From af3a063299c262a40b527906f260f5b03d258da1 Mon Sep 17 00:00:00 2001 From: Dany LE Date: Wed, 14 Jun 2023 15:20:16 +0200 Subject: [PATCH] add more wrapper APIs to the module --- limap.c | 804 ++++++++++++++++++++++++++++++++++++++++++++++++++----- test.lua | 66 ++++- 2 files changed, 801 insertions(+), 69 deletions(-) diff --git a/limap.c b/limap.c index 5378633..f31e621 100644 --- a/limap.c +++ b/limap.c @@ -5,8 +5,18 @@ #include "imap4r1.h" #include #include +#include +#include extern int errno; /* just in case */ +#define IMAP_MT "limap" + +#define IMAP_ADDRESS_SIZE_BUF 10 + +#ifndef SENDBUFLEN +#define SENDBUFLEN 16385 +#endif + #define M_LOG(a,...) syslog (LOG_NOTICE,"limap@[%s: %d]: " a "\n", __FILE__, \ __LINE__, ##__VA_ARGS__) #define M_WARN(a,...) syslog (LOG_WARNING, "limap@[%s: %d]: " a "\n", __FILE__, \ @@ -24,6 +34,10 @@ extern int errno; /* just in case */ lua_pushnumber(L,v); \ lua_settable(L,-3); + +static pthread_mutex_t _limap_lock; +static lua_State* __L__; + unsigned long find_rightmost_bit(unsigned long *valptr); static size_t strlcat(char *dst, const char *src, size_t n) { @@ -76,9 +90,9 @@ void mm_searched (MAILSTREAM *stream,unsigned long msgno) void mm_exists (MAILSTREAM *stream,unsigned long number) { M_LOG("New emails found in %s: %d", stream->mailbox, number); - int * n = stream->sparep; - if(!n) return; - *n = number; + lua_State * L = stream->sparep; + if(!L) return; + lua_pushnumber(L, number); } @@ -162,7 +176,7 @@ void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes) void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes) { - /* dummy routine */ + mm_list(stream,delimiter, name,attributes); } @@ -187,18 +201,33 @@ void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status) void mm_notify (MAILSTREAM *stream,char *string,long errflg) { - if (!errflg && (string[0] == '[') && - ((string[1] == 'T') || (string[1] == 't')) && - ((string[2] == 'R') || (string[2] == 'r')) && - ((string[3] == 'Y') || (string[3] == 'y')) && - ((string[4] == 'C') || (string[4] == 'c')) && - ((string[5] == 'R') || (string[5] == 'r')) && - ((string[6] == 'E') || (string[6] == 'e')) && - ((string[7] == 'A') || (string[7] == 'a')) && - ((string[8] == 'T') || (string[8] == 't')) && - ((string[9] == 'E') || (string[9] == 'e')) && - (string[10] == ']')) - mm_log (string,errflg); /* just do mm_log action */ + lua_State* L = stream->sparep; + if (!errflg && (string[0] == '[') && + ((string[1] == 'T') || (string[1] == 't')) && + ((string[2] == 'R') || (string[2] == 'r')) && + ((string[3] == 'Y') || (string[3] == 'y')) && + ((string[4] == 'C') || (string[4] == 'c')) && + ((string[5] == 'R') || (string[5] == 'r')) && + ((string[6] == 'E') || (string[6] == 'e')) && + ((string[7] == 'A') || (string[7] == 'a')) && + ((string[8] == 'T') || (string[8] == 't')) && + ((string[9] == 'E') || (string[9] == 'e')) && + (string[10] == ']')) + { + switch (errflg) { + case BYE: + case NIL: /* no error */ + break; + default: + lua_getglobal(L, "limap_errors"); + size_t len = lua_rawlen(L,-1); + lua_pushnumber(L,len+1); + lua_pushstring(L,string); + lua_settable(L,-3); + break; + } + mm_log (string,errflg); /* just do mm_log action */ + } } /* Log an event for the user to see @@ -208,20 +237,20 @@ void mm_notify (MAILSTREAM *stream,char *string,long errflg) void mm_log (char *string,long errflg) { - switch (errflg) { - case BYE: - case NIL: /* no error */ - M_LOG ("[%s]",string); - break; - case PARSE: /* parsing problem */ - case WARN: /* warning */ - M_WARN("warning: %s",string); - break; - case ERROR: /* error */ - default: - M_ERROR ("%s",string); - break; - } + switch (errflg) { + case BYE: + case NIL: /* no error */ + M_LOG ("[%s]",string); + break; + case PARSE: /* parsing problem */ + case WARN: /* warning */ + M_WARN("warning: %s",string); + break; + case ERROR: /* error */ + default: + M_ERROR ("%s",string); + break; + } } @@ -242,23 +271,56 @@ void mm_dlog (char *string) void mm_login (NETMBX *mb,char *username,char *password,long trial) { - if(mb->user) + // user name + if(!lua_isstring(__L__,2)) { - char * tmp = mb->user; - (void)strsep(&tmp, ";"); - strncpy(username, mb->user, NETMAXUSER); - if(tmp) - { - strncpy (password,tmp,MAILTMPLEN); - } - M_LOG("host: %s", mb->host); - M_LOG("orighost: %s", mb->orighost); - M_LOG("user: %s", mb->user); - M_LOG("mailbox: %s", mb->mailbox); - M_LOG("service: %s", mb->service); + return; + } + // password + if(!lua_isstring(__L__,3)) + { + return; + } + char * tmp = (char*)lua_tostring(__L__,2); + strncpy(username, tmp, NETMAXUSER); + tmp = (char*)lua_tostring(__L__,3); + strncpy (password,tmp,MAILTMPLEN); + + M_LOG("host: %s", mb->host); + M_LOG("orighost: %s", mb->orighost); + M_LOG("user: %s", username); + M_LOG("mailbox: %s", mb->mailbox); + M_LOG("service: %s", mb->service); +} + +void mm_getacl(MAILSTREAM *stream, char *mailbox, ACLLIST *alist) +{ + + /* walk through the ACLLIST */ + lua_State* L = stream->sparep; + lua_newtable(L); + for(; alist; alist = alist->next) { + add_property_string(L,alist->identifier, alist->rights); } } +void mm_getquota(MAILSTREAM *stream, char *qroot, QUOTALIST *qlist) +{ + /* put parsing code here */ + lua_State* L = stream->sparep; + M_LOG("querying quota:"); + lua_newtable(L); + for(; qlist; qlist = qlist->next) { + lua_pushstring(L, qlist->name); + lua_newtable(L); + add_property_long(L, "usage", qlist->usage); + add_property_long(L, "limit", qlist->limit); + lua_settable(L,-3); + M_LOG("Quota name %s: ", qlist->name); + M_LOG("Usage: %d", qlist->usage); + M_LOG("Limit: %d",qlist->limit); + } +} /* About to enter critical code * Accepts: stream @@ -308,28 +370,33 @@ static int l_mail_open(lua_State *L) { const char* mailbox = luaL_checkstring(L,1); long flag = 0; - if( lua_toboolean(L,2)) + if( lua_toboolean(L,4)) { /*half open*/ flag = flag | OP_HALFOPEN; } - if( lua_toboolean(L,3)) + if( lua_toboolean(L,5)) { /*debug*/ flag = flag | OP_DEBUG; } + pthread_mutex_lock(&_limap_lock); + __L__ = L; MAILSTREAM *stream = mail_open (NIL,(char*)mailbox,flag); + __L__ = NULL; + pthread_mutex_unlock(&_limap_lock); if(!stream) { lua_pushnil(L); return 1; } //strncpy(stream->original_mailbox, stream->mailbox, strlen(stream->mailbox)); - if(stream->original_mailbox) + /*if(stream->original_mailbox) { free(stream->original_mailbox); } - stream->original_mailbox = strdup(stream->mailbox); + stream->original_mailbox = strdup(stream->mailbox);*/ + stream->sparep = (void*)L; M_LOG("mailbox: %s", stream->mailbox); M_LOG("original_mailbox: %s", stream->original_mailbox); lua_pushlightuserdata(L, stream); @@ -362,7 +429,6 @@ static int l_get_mailboxes(lua_State *L) lua_pushboolean(L,0); return 1; } - stream->sparep = (void*)L; lua_newtable(L); mail_list(stream,(char*)reference, (char*)pattern); return 1; @@ -371,8 +437,6 @@ static int l_get_mailboxes(lua_State *L) static int l_mail_ping(lua_State *L) { MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); - int new_mail = 0; - stream->sparep = (void*)&new_mail; if(!stream || mail_ping(stream) == NIL) { lua_pushboolean(L,0); @@ -381,7 +445,6 @@ static int l_mail_ping(lua_State *L) { lua_pushboolean(L,1); } - lua_pushnumber(L, new_mail); return 2; } @@ -390,10 +453,30 @@ static int l_mail_check(lua_State *L) MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); if(!stream) { - return 0; + lua_pushnil(L); + return 1; + } + + char date[100]; + + if (mail_ping (stream) == NIL) { + lua_pushnil(L); + return 1; + } + + if (stream->mailbox) { + rfc822_date(date); + lua_newtable(L); + add_property_string(L, "Date", date); + add_property_string(L, "Driver", stream->dtb->name); + add_property_string(L, "Mailbox", stream->mailbox); + add_property_long(L, "Nmsgs", stream->nmsgs); + add_property_long(L, "Recent", stream->recent); + return 1; + } else { + lua_pushnil(L); + return 1; } - mail_check(stream); - return 0; } static int l_mail_get_raw_header(lua_State* L) @@ -575,11 +658,12 @@ static int l_reopen(lua_State *L) lua_pushboolean(L,0); return 1; } - if(stream->original_mailbox) + stream->sparep = (void*) L; + /*if(stream->original_mailbox) { free(stream->original_mailbox); } - stream->original_mailbox = strdup(stream->mailbox); + stream->original_mailbox = strdup(stream->mailbox);*/ M_LOG("mailbox: %s", stream->mailbox); M_LOG("original_mailbox: %s", stream->original_mailbox); lua_pushboolean(L,1); @@ -924,27 +1008,621 @@ static int l_get_body_part(lua_State* L) return 1; } +static int l_append(lua_State* L) +{ + STRING s; + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + char* folder = (char*)luaL_checkstring(L,2); + char* message = (char*)luaL_checkstring(L,3); + INIT (&s, mail_string, message,strlen(message)); + if (mail_append(stream, folder, &s)) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + return 1; +} + +static int l_setflag_full(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + + char *sequence = (char*)luaL_checkstring(L,2); + char *flag = (char*)luaL_checkstring(L,3); + + unsigned int flags = 0; + if(lua_toboolean(L,4)) + { + flags |= ST_UID; + } + + mail_setflag_full(stream, sequence, flag, flags); + lua_pushboolean(L,1); + return 1; +} + +static int l_create_mailbox(lua_State * L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + char *folder = (char*)luaL_checkstring(L,2); + + if (mail_create(stream, folder) == T) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + + return 1; +} + +static int l_delete_mailbox(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + char *folder = (char*)luaL_checkstring(L,2); + + if (mail_delete(stream, folder) == T) { + lua_pushboolean(L, 1); + } else { + lua_pushboolean(L, 0); + } + return 1; +} + +static int l_8bit(lua_State* L) +{ + char* text = (char*) luaL_checkstring(L,1); + char *decode; + unsigned long newlength; + + decode = (char *) rfc822_8bit((unsigned char *) text, strlen(text), &newlength); + + if (decode == NULL) { + lua_pushnil(L); + } + + decode[newlength] = '\0'; + lua_pushstring(L, decode); + fs_give((void**) &decode); + return 1; +} + +static int l_binary(lua_State* L) +{ + char* text = (char*) luaL_checkstring(L,1); + char *decode; + unsigned long newlength; + + decode = (char*)rfc822_binary(text, strlen(text), &newlength); + + if (decode == NULL) { + lua_pushnil(L); + } + + decode[newlength] = '\0'; + lua_pushstring(L, decode); + fs_give((void**) &decode); + return 1; +} + +static int l_clearflag_full(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + + char *sequence = (char*)luaL_checkstring(L,2); + char *flag = (char*)luaL_checkstring(L,3); + + unsigned int flags = 0; + if(lua_toboolean(L,4)) + { + flags |= ST_UID; + } + + mail_clearflag_full(stream, sequence, flag, flags); + lua_pushboolean(L,1); + return 1; +} + +static int l_get_body_struct(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + unsigned int msgno = luaL_checknumber(L, 2); + char* section = (char*)luaL_checkstring(L,3); + + PARAMETER *par, *dpar; + BODY *body; + + body=mail_body(stream, msgno, (unsigned char*)section); + if (body == NULL) { + lua_pushnil(L); + return 1; + } + + lua_newtable(L); + if (body->type <= TYPEMAX) { + add_property_long(L, "type", body->type); + } + if (body->encoding <= ENCMAX) { + add_property_long(L, "encoding", body->encoding); + } + + if (body->subtype) { + add_property_long(L, "ifsubtype", 1); + add_property_string(L, "subtype", body->subtype); + } else { + add_property_long(L, "ifsubtype", 0); + } + + if (body->description) { + add_property_long(L, "ifdescription", 1); + add_property_string(L, "description", body->description); + } else { + add_property_long(L, "ifdescription", 0); + } + if (body->id) { + add_property_long(L, "ifid", 1); + add_property_string(L, "id", body->id); + } else { + add_property_long(L, "ifid", 0); + } + + if (body->size.lines) { + add_property_long(L, "lines", body->size.lines); + } + if (body->size.bytes) { + add_property_long(L, "bytes", body->size.bytes); + } +#ifdef IMAP41 + if (body->disposition.type) { + add_property_long(L, "ifdisposition", 1); + add_property_string(L, "disposition", body->disposition.type); + } else { + add_property_long(L, "ifdisposition", 0); + } + + if (body->disposition.parameter) { + dpar = body->disposition.parameter; + add_property_long(L, "ifdparameters", 1); + lua_pushstring(L,"dparameters"); + lua_newtable(L); + int count = 1; + do { + lua_pushnumber(L,count); + lua_newtable(L); + add_property_string(L, "attribute", dpar->attribute); + add_property_string(L, "value", dpar->value); + lua_settable(L,-3); + count++; + } while ((dpar = dpar->next)); + lua_settable(L,-3); + } else { + add_property_long(L, "ifdparameters", 0); + } +#endif + + if ((par = body->parameter)) { + add_property_long(L, "ifparameters", 1); + lua_pushstring(L,"parameters"); + lua_newtable(L); + int count = 1; + do { + lua_pushnumber(L,count); + lua_newtable(L); + if (par->attribute) { + add_property_string(L, "attribute", par->attribute); + } + if (par->value) { + add_property_string(L, "value", par->value); + } + count++; + lua_settable(L,-3); + } while ((par = par->next)); + lua_settable(L,-3); + } else { + add_property_long(L, "ifparameters", 0); + } + return 1; +} + +static int l_expunge(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + + mail_expunge (stream); + + return 0; +} + +/* {{{ _rfc822_len + * Calculate string length based on imap's rfc822_cat function. + */ +static int _rfc822_len(char *str) +{ + int len; + char *p; + + if (!str || !*str) { + return 0; + } + + /* strings with special characters will need to be quoted, as a safety measure we + * add 2 bytes for the quotes just in case. + */ + len = strlen(str) + 2; + p = str; + /* rfc822_cat() will escape all " and \ characters, therefore we need to increase + * our buffer length to account for these characters. + */ + while ((p = strpbrk(p, "\\\""))) { + p++; + len++; + } + + return len; +} +/* }}} */ + +/* {{{ _imap_get_address_size */ +static int _imap_address_size (ADDRESS *addresslist) +{ + ADDRESS *tmp; + int ret=0, num_ent=0; + + tmp = addresslist; + + if (tmp) do { + ret += _rfc822_len(tmp->personal); + ret += _rfc822_len(tmp->adl); + ret += _rfc822_len(tmp->mailbox); + ret += _rfc822_len(tmp->host); + num_ent++; + } while ((tmp = tmp->next)); + + /* + * rfc822_write_address_full() needs some extra space for '<>,', etc. + * for this perpouse we allocate additional IMAP_ADDRESS_SIZE_BUF bytes + * by default this buffer is 10 bytes long + */ + ret += (ret) ? num_ent*IMAP_ADDRESS_SIZE_BUF : 0; + + return ret; +} + +/* }}} */ + +/* {{{ _rfc822_write_address */ +static void _rfc822_write_address(lua_State* L,const char* key, ADDRESS *addresslist) +{ + char address[SENDBUFLEN]; + + if (_imap_address_size(addresslist) >= SENDBUFLEN) { + //throw_error(NULL, "Address buffer overflow"); + return; + } + address[0] = 0; + rfc822_write_address(address, addresslist); + add_property_string(L, key, address); +} +/* }}} */ + +static int l_get_overview(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + unsigned int status, flags = 0L; + + char *sequence = (char*)luaL_checkstring(L,2); + + if(lua_toboolean(L,3)) + { + flags |= ST_UID; + } + + + status = (flags & FT_UID) + ? mail_uid_sequence(stream, (unsigned char*)sequence) + : mail_sequence(stream, (unsigned char*)sequence); + + if (status) { + MESSAGECACHE *elt; + ENVELOPE *env; + unsigned long i; + lua_newtable(L); + for (i = 1; i <= stream->nmsgs; i++) { + if (((elt = mail_elt (stream, i))->sequence) && + (env = mail_fetch_structure (stream, i, NIL, NIL))) { + lua_pushnumber(L,i); + lua_newtable(L); + if (env->subject) { + add_property_string(L, "subject", env->subject); + } + if (env->from) { + env->from->next=NULL; + _rfc822_write_address(L,"from",env->from); + } + if (env->to) { + env->to->next = NULL; + _rfc822_write_address(L,"to",env->to); + } + if (env->date) { + add_property_string(L, "date", (char*)env->date); + } + if (env->message_id) { + add_property_string(L, "message_id", env->message_id); + } + if (env->references) { + add_property_string(L, "references", env->references); + } + if (env->in_reply_to) { + add_property_string(L, "in_reply_to", env->in_reply_to); + } + add_property_long(L, "size", elt->rfc822_size); + add_property_long(L, "uid", mail_uid(stream, i)); + add_property_long(L, "msgno", i); + add_property_long(L, "recent", elt->recent); + add_property_long(L, "flagged", elt->flagged); + add_property_long(L, "answered", elt->answered); + add_property_long(L, "deleted", elt->deleted); + add_property_long(L, "seen", elt->seen); + add_property_long(L, "draft", elt->draft); + add_property_long(L, "udate", mail_longdate(elt)); + lua_settable(L,-3); + } + } + } + else + { + lua_pushnil(L); + } + return 1; +} + +static int l_get_mime(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + if(!stream) + { + lua_pushnil(L); + return 1; + } + unsigned int flags = 0; + const int msgno = luaL_checknumber(L,2); + char* section = (char*)luaL_checkstring(L,3); + + if(lua_toboolean(L,4)) + { + flags |= FT_PEEK; + } + + char *body; + unsigned long len; + + body = mail_fetch_mime(stream, msgno, section, &len, flags); + + if (!body) { + lua_pushnil(L); + return 1; + } + lua_pushstring(L,body); + return 1; +} + +static int l_gc(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + if(!stream) + { + lua_pushboolean(L,0); + return 1; + } + unsigned int flags = GC_TEXTS | GC_ELT | GC_ENV; + + mail_gc(stream, flags); + + lua_pushboolean(L,1); + return 1; +} + +static int l_get_quota(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + if(!stream) + { + lua_pushboolean(L,0); + return 1; + } + char * qroot = (char*) luaL_checkstring(L,2); + + // set the callback for the GET_QUOTA function + mail_parameters(NIL, SET_QUOTA, (void *) mm_getquota); + if (!imap_getquota(stream,qroot)) { + M_ERROR("C-client imap_getquota failed"); + } + if(lua_isstring(L,-1)) + { + lua_pushnil(L); + } + return 1; +} + +static int l_get_quotaroot(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + if(!stream) + { + lua_pushboolean(L,0); + return 1; + } + char * qroot = (char*) luaL_checkstring(L,2); + + // set the callback for the GET_QUOTA function + mail_parameters(NIL, SET_QUOTA, (void *) mm_getquota); + if (!imap_getquotaroot(stream,qroot)) { + M_ERROR("C-client imap_getquotaroot failed"); + } + if(lua_isstring(L,-1)) + { + lua_pushnil(L); + } + + return 1; +} + +static int l_getacl(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + if(!stream) + { + lua_pushboolean(L,0); + return 1; + } + char * mailbox = (char*) luaL_checkstring(L,2); + + /* set the callback for the GET_ACL function */ + mail_parameters(NIL, SET_ACL, (void *) mm_getacl); + if (!imap_getacl(stream, mailbox)) + { + M_ERROR("c-client imap_getacl failed"); + } + if(lua_isstring(L,-1)) + { + lua_pushnil(L); + } + + return 1; +} + +static int l_get_subscribed(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + const char* reference = luaL_checkstring(L,2); + const char* pattern = luaL_checkstring(L,3); + if(!stream) + { + lua_pushboolean(L,0); + return 1; + } + lua_newtable(L); + mail_lsub(stream,(char*)reference, (char*)pattern); + return 1; +} + +static int l_scan_mailboxes(lua_State* L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + const char* reference = luaL_checkstring(L,2); + const char* pattern = luaL_checkstring(L,3); + const char* content = luaL_checkstring(L,4); + if(!stream) + { + lua_pushboolean(L,0); + return 1; + } + lua_newtable(L); + mail_scan(stream, (char*)reference, (char*)pattern, (char*)content); + return 1; +} + +static int l_mail_compose(lua_State* L) +{ + +} + +static int l_lua_gc(lua_State* L) +{ + M_LOG("Imap module unloaded"); + pthread_mutex_destroy(&_limap_lock); + return 0; +} + static const struct luaL_Reg _lib [] = { {"open", l_mail_open}, {"close", l_mail_close}, - {"get_mail_boxes", l_get_mailboxes}, + {"get_mail_boxes", l_get_mailboxes},// = PHP imap_getmailboxes, imap_list {"reopen", l_reopen}, {"ping", l_mail_ping}, - {"check", l_mail_check}, - {"get_raw_header", l_mail_get_raw_header}, + {"check", l_mail_check}, // PHP imap_check + {"get_raw_header", l_mail_get_raw_header}, // = PHP imap_fetchheader() {"get_nmail", l_mail_getnumber}, {"get_headers", l_mail_get_headers}, - {"get_header", l_mail_get_header}, + {"get_header", l_mail_get_header}, // = php imap_header() {"rfc822_parse_header", l_rfc822_parse_header}, - {"get_raw_body", l_get_raw_body}, - {"get_structure", l_get_structure}, - {"get_body_part", l_get_body_part}, + {"get_raw_body", l_get_raw_body}, // = PHP imap_body + {"get_structure", l_get_structure}, // = PHP imap_fetchstructure() + {"get_body_part", l_get_body_part}, // = PHP imap_fetchbody() + {"append", l_append}, + {"setflag_full", l_setflag_full}, // = PHP imap_setflag_full + {"clearflag_full", l_clearflag_full}, // = PHP clearflag_full + {"create_mailbox", l_create_mailbox}, // = PHP imap_createmailbox + {"delete_mailbox", l_delete_mailbox}, // = PHP imap_deletemailbox + {"_8bit", l_8bit}, // = PHP imap_8bit + {"binary", l_binary}, // = PHP imap_binary + {"get_body_struct", l_get_body_struct }, // = PHP imap_bodystruct + {"expunge" , l_expunge}, // = PHP imap_expunge + {"get_overview", l_get_overview}, // = PHP imap_fetch_overview + {"get_mime", l_get_mime}, // = PHP imap_fetchmime + {"gc", l_gc}, // = PHP imap_gc + {"get_quota", l_get_quota}, // = PHP imap_get_quota + {"get_quotaroot", l_get_quotaroot}, // = PHP imap_get_quotaroot + {"get_acl", l_getacl}, // = PHP imap_getacl, + {"get_subscribed", l_get_subscribed}, // = PHP imap_getsubscribed, imap_listsubscribed, imap_lsub + {"scan_mailboxes", l_scan_mailboxes}, // = PHP imap_listscan, + // {"mail_compose", l_mail_compose}, TODO + {NULL,NULL} }; + +/* +cleanup in a c module + +https://lua-l.lua.narkive.com/09DuVpEl/cleanup-in-a-c-module +The sentinel userdata above will call the cleanup code as soon as it is collected; +to prevent collection until the module is destroyed, we need to add a reference that links the module to the sentinel. +We're using the LUA_REGISTRYINDEX to store this in a way that is hidden from Lua scripts. +We create a weak-valued table in the registry called "__gc_sentinels" (if it doesn't already exist), +and use weak-referencing so that existence of the module in this table will not stop it from being collected. +For most uses of Lua, this won't matter since modules usually only get unloaded when the lua_State is closed; +but we had a situation where we needed to manually unload modules sometimes. +*/ +static void gc_sentinel(lua_State * L, int idx, lua_CFunction callback) { + + lua_pushvalue(L, idx); // value @idx + lua_newuserdata(L, sizeof(void *)); // sentinel userdata + lua_newtable(L); // userdata metatable with __gc = callback + lua_pushcfunction(L, callback); + lua_setfield(L, -2, "__gc"); + lua_setmetatable(L, -2); + + /* check for (weak-valued) sentinel table; create if needed */ + lua_getfield(L, LUA_REGISTRYINDEX, "__gc_limap_sentinels"); + if (lua_isnoneornil(L, -1)) { + lua_pop(L, 1); + lua_newtable(L); + // make weak-keyed + lua_pushstring(L, "v"); + lua_setfield(L, -2, "__mode"); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pushvalue(L, -1); + lua_setmetatable(L, -2); + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, "__gc_limap_sentinels"); + } + lua_insert(L, -3); + lua_insert(L, -2); + lua_settable(L, -3); // lua::sentinel[value @idx] = sentinel userdata + lua_pop(L, 1); // lua::sentinel +} + int luaopen_limap(lua_State *L) { + M_LOG("Imap module loaded"); #include "linkage.c" + pthread_mutex_init(&_limap_lock, NULL); + __L__ = NULL; + // create error stack + lua_newtable( L ); + lua_setglobal( L, "limap_errors" ); luaL_newlib(L, _lib); + gc_sentinel(L, -1, l_lua_gc); return 1; } \ No newline at end of file diff --git a/test.lua b/test.lua index 20f25e5..26bf9ff 100644 --- a/test.lua +++ b/test.lua @@ -10,13 +10,15 @@ local OP_EXPUNGE= 0x80 --/* silently expunge recycle stream */ local imap = require("limap") local passwd = os.getenv("IMAP_PWD") -local url = string.format("{iohub.dev:143/imap/tls-sslv23/novalidate-cert/user=mrsang;%s}",passwd); -local handle, box = imap.open(url,true, true) -- half open and debug on +local url = "{iohub.dev:993/imap/ssl/novalidate-cert}" +--local url = string.format("{iohub.dev:993/imap/ssl/novalidate-cert/user=dany@iohub.dev;%s}",passwd); +local handle, box = imap.open(url,"dany@iohub.dev", passwd, true, true) -- half open and debug on local r,v = false,nil; function dump(obj) if(type(obj) == "table") then + print("dump", obj, " size: ", #obj) for k,v in pairs(obj) do print(k) dump(v) @@ -38,21 +40,24 @@ if(handle) then end end r, box = imap.reopen(handle,box:gsub("[^}]*$","INBOX"), false, true) -- half open off, debug on + print(box) if r then print("new box opened", box) else print("reopen failed") end - r,v = imap.ping(handle) + v,r = imap.ping(handle) if r then print("Alive", v) else print("Unable to ping") end - imap.check(handle); + + local mbdata = imap.check(handle) + dump(mbdata) -- get nmail - local nmail = imap.get_nmail(handle); - print("umber of mail", nmail); + local nmail = imap.get_nmail(handle) + print("umber of mail", nmail) if nmail > 0 then local headers = imap.get_headers(handle) @@ -68,5 +73,54 @@ if(handle) then local structure = imap.get_structure(handle, nmail) dump(structure) + -- append message to a mail box + --if imap.append(handle, box:gsub("[^}]*$","Drafts"), + -- "From: dany@iohub.dev\r\n" + -- .. "To: xsang.le@gmail.com\r\n" + -- .. "Subject: test\r\n" + -- .. "\r\n" + -- .. "this is a test message, please ignore\r\n") then + -- print("Message appended") + --else + -- print("Error append message") + --end + + -- get body structure + print("########## Get body section 1 structure") + local bstruct = imap.get_body_struct(handle, nmail,"1" ) + dump(bstruct) + + print("########## fetch overview messages") + local ov = imap.get_overview(handle,string.format("1:%d", nmail), false) + dump(ov) + + print("########## fetch mime") + local mime = imap.get_mime(handle,nmail,"1", true) -- peak the message without set it to seen + dump(mime) + + --print("########## get quota") + --local quota = imap.get_quota(handle,"user.dany") + --dump(quota) + + --print("########## get quota root") + --local quota = imap.get_quotaroot(handle,"INBOX") + --dump(quota) + + --print("########## get ACL") + --local acl = imap.get_acl(handle,box) + --dump(acl) + + print("#mail subscriber") + local sub = imap.get_subscribed(handle, box:gsub("[^}]*$",""), "*") + dump(sub) + + --print("#mailbox scan") + --local sub = imap.scan_mailboxes(handle, box:gsub("[^}]*$",""), "*", "Junk") + --dump(sub) + + print("######### ERROR stack") + dump(limap_errors) + + print("Close handle") imap.close(handle) end \ No newline at end of file