diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5901993 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "imap"] + path = imap + url = https://git.iohub.dev/dany/imap.git diff --git a/imap b/imap new file mode 160000 index 0000000..cab1094 --- /dev/null +++ b/imap @@ -0,0 +1 @@ +Subproject commit cab109466534e206a3652ef1c68fe88101b68bda diff --git a/limap.c b/limap.c new file mode 100644 index 0000000..65937dc --- /dev/null +++ b/limap.c @@ -0,0 +1,403 @@ +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + + +#include "c-client.h" +#include "imap4r1.h" +#include +#include +extern int errno; /* just in case */ + +#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__, \ + __LINE__, ##__VA_ARGS__) +#define M_ERROR(a,...) syslog (LOG_ERR, "limap@[%s: %d]: " a "\n", __FILE__, \ + __LINE__, ##__VA_ARGS__) + +/* Co-routines from MAIL library */ + + +/* Message matches a search + * Accepts: MAIL stream + * message number + */ + +void mm_searched (MAILSTREAM *stream,unsigned long msgno) +{ + /* dummy routine */ +} + + +/* Message exists (i.e. there are that many messages in the mailbox) + * Accepts: MAIL stream + * message number + */ + +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; +} + + +/* Message expunged + * Accepts: MAIL stream + * message number + */ + +void mm_expunged (MAILSTREAM *stream,unsigned long number) +{ + /* dummy routine */ +} + + +/* Message flags update seen + * Accepts: MAIL stream + * message number + */ + +void mm_flags (MAILSTREAM *stream,unsigned long number) +{ + /* dummy routine */ +} +/* Mailbox found + * Accepts: MAIL stream + * hierarchy delimiter + * mailbox name + * mailbox attributes + */ + +void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes) +{ + lua_State *L = stream->sparep; + if(!L) + return; + if(attributes & LATT_NOSELECT) + { + return; + } + size_t cnt = lua_rawlen(L,-1) + 1; + lua_pushnumber(L,cnt); + lua_newtable(L); + char delim[2]; + delim[0] = (char) delimiter; + delim[1] = '\0'; + if(!name) + return; + lua_pushstring(L, "delimiter"); + lua_pushstring(L, delim); + lua_settable(L, -3); + + lua_pushstring(L, "name"); + lua_pushstring(L, name); + lua_settable(L, -3); + + lua_pushstring(L, "LATT_NOINFERIORS"); + lua_pushboolean(L, attributes & LATT_NOINFERIORS); + lua_settable(L, -3); + + lua_pushstring(L, "LATT_NOSELECT"); + lua_pushboolean(L, attributes & LATT_NOSELECT); + lua_settable(L, -3); + + lua_pushstring(L, "LATT_MARKED"); + lua_pushboolean(L, attributes & LATT_MARKED); + lua_settable(L, -3); + + lua_pushstring(L, "LATT_UNMARKED"); + lua_pushboolean(L, attributes & LATT_UNMARKED); + lua_settable(L, -3); + lua_settable(L, -3); +} + + +/* Subscribe mailbox found + * Accepts: MAIL stream + * hierarchy delimiter + * mailbox name + * mailbox attributes + */ + +void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes) +{ + /* dummy routine */ +} + + +/* Mailbox status + * Accepts: MAIL stream + * mailbox name + * mailbox status + */ + +void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status) +{ + if (status->recent || status->unseen) + printf ("%lu new message(s) (%lu unseen),",status->recent,status->unseen); + else fputs ("No new messages,",stdout); + printf (" %lu total in %s\n",status->messages,mailbox); +} +/* Notification event + * Accepts: MAIL stream + * string to log + * error flag + */ + +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 */ +} + +/* Log an event for the user to see + * Accepts: string to log + * error flag + */ + +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; + } +} + + +/* Log an event to debugging telemetry + * Accepts: string to log + */ + +void mm_dlog (char *string) +{ + M_LOG ("%s",string); +} +/* Get user name and password for this host + * Accepts: parse of network mailbox name + * where to return user name + * where to return password + * trial count + */ + +void mm_login (NETMBX *mb,char *username,char *password,long trial) +{ + if(mb->user) + { + 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); + } +} + + +/* About to enter critical code + * Accepts: stream + */ + +void mm_critical (MAILSTREAM *stream) +{ + /* note in critical code */ +} + + +/* About to exit critical code + * Accepts: stream + */ + +void mm_nocritical (MAILSTREAM *stream) +{ + /* note not in critical code */ +} + + +/* Disk error found + * Accepts: stream + * system error code + * flag indicating that mailbox may be clobbered + * Returns: T if user wants to abort + */ + +long mm_diskerror (MAILSTREAM *stream,long errcode,long serious) +{ + return T; +} + + +/* Log a fatal error event + * Accepts: string to log + */ + +void mm_fatal (char *string) +{ + M_ERROR ("FATAL: %s",string); +} + + + +static int l_mail_open(lua_State *L) +{ + const char* mailbox = luaL_checkstring(L,1); + long flag = 0; + if( lua_isnumber(L,2)) + { + flag = flag | (long)luaL_checknumber(L,2); + } + MAILSTREAM *stream = mail_open (NIL,(char*)mailbox,flag); + if(!stream) + { + lua_pushnil(L); + return 1; + } + //strncpy(stream->original_mailbox, stream->mailbox, strlen(stream->mailbox)); + if(stream->original_mailbox) + { + free(stream->original_mailbox); + } + stream->original_mailbox = strdup(stream->mailbox); + M_LOG("mailbox: %s", stream->mailbox); + M_LOG("original_mailbox: %s", stream->original_mailbox); + lua_pushlightuserdata(L, stream); + lua_pushstring(L,stream->mailbox); + return 2; +} + +static int l_mail_close(lua_State *L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + if(!stream) + { + lua_pushboolean(L,0); + } + else + { + (void) mail_close(stream); + lua_pushboolean(L,1); + } + return 1; +} + +static int l_get_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); + if(!stream) + { + lua_pushboolean(L,0); + return 1; + } + stream->sparep = (void*)L; + lua_newtable(L); + mail_list(stream,(char*)reference, (char*)pattern); + return 1; +} + +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); + } + else + { + lua_pushboolean(L,1); + } + lua_pushnumber(L, new_mail); + return 2; +} + +static int l_mail_check(lua_State *L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + if(!stream) + { + return 0; + } + mail_check(stream); + return 0; +} + +static int l_reopen(lua_State *L) +{ + MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); + const char* mailbox = luaL_checkstring(L,2); + long flag = 0; + if( lua_isnumber(L,3)) + { + flag = flag | (long)luaL_checknumber(L,3); + } + if(!stream) + { + lua_pushboolean(L,0); + return 1; + } + stream = mail_open (stream,(char*)mailbox,flag); + if(!stream) + { + lua_pushboolean(L,0); + return 1; + } + if(stream->original_mailbox) + { + free(stream->original_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); + lua_pushstring(L,stream->mailbox); + return 2; +} + + +static const struct luaL_Reg _lib [] = { + {"open", l_mail_open}, + {"close", l_mail_close}, + {"get_mail_boxes", l_get_mailboxes}, + {"reopen", l_reopen}, + {"ping", l_mail_ping}, + {"check", l_mail_check}, + {NULL,NULL} +}; + +int luaopen_limap(lua_State *L) +{ + #include "linkage.c" + luaL_newlib(L, _lib); + return 1; +} \ No newline at end of file diff --git a/test.lua b/test.lua new file mode 100644 index 0000000..26a87a1 --- /dev/null +++ b/test.lua @@ -0,0 +1,44 @@ +local OP_DEBUG = 0x1 +local OP_READONLY = 0x2 +local OP_ANONYMOUS = 0x4 --/* anonymous open of newsgroup */ +local OP_SHORTCACHE = 0x8 --/* short (elt-only) caching */ +local OP_SILENT = 0x10 --/* don't pass up events (internal use) */ +local OP_PROTOTYPE = 0x20 --/* return driver prototype */ +local OP_HALFOPEN = 0x40 --/* half-open (IMAP connect but no select) */ +local OP_EXPUNGE= 0x80 --/* silently expunge recycle stream */ + + +local imap = require("limap") +local passwd = os.getenv("IMAP_PWD") or "!x$@n9ph" +local url = string.format("{iohub.dev:143/imap/tls-sslv23/novalidate-cert/user=mrsang;%s}",passwd); +local handle, box = imap.open(url,OP_HALFOPEN|OP_DEBUG) + +local r,v = false,nil; + +if(handle) then + print("mailbox openned") + + local boxes = imap.get_mail_boxes(handle, box:gsub("[^}]*$",""),"*"); + + for k, v in ipairs(boxes) do + print("=================") + for k1, v1 in pairs(v) do + print(k1,v1) + end + end + r, box = imap.reopen(handle,box:gsub("[^}]*$","INBOX"), OP_DEBUG) + if r then + print("new box opened", box) + else + print("reopen failed") + end + r,v = imap.ping(handle) + if r then + print("Alive", v) + else + print("Unable to ping") + end + imap.check(handle); + + imap.close(handle) +end \ No newline at end of file