limap/limap.c
2023-06-14 15:20:16 +02:00

1628 lines
44 KiB
C

#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "c-client.h"
#include "imap4r1.h"
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <pthread.h>
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__, \
__LINE__, ##__VA_ARGS__)
#define M_ERROR(a,...) syslog (LOG_ERR, "limap@[%s: %d]: " a "\n", __FILE__, \
__LINE__, ##__VA_ARGS__)
#define add_property_string(L,k,v) \
lua_pushstring(L,k); \
lua_pushstring(L,v); \
lua_settable(L,-3);
#define add_property_long(L,k,v) \
lua_pushstring(L,k); \
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) {
char *p = dst;
while (n != 0 && *p != '\0') {
p++;
n--;
}
if (n != 0) {
for (; --n != 0; p++, src++) {
if ((*p = *src) == '\0')
return p - dst;
}
*p = '\0';
}
return (p - dst) + strlen(src);
}
static void str_trim(char* str, const char delim)
{
if(!str || strlen(str) == 0) return;
char * p = str;
int l = strlen(p);
while(l > 0 && p[l - 1] == delim)
p[--l] = 0;
while(* p && (* p) == delim ) ++p, --l;
memmove(str, p, l + 1);
}
/* 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);
lua_State * L = stream->sparep;
if(!L) return;
lua_pushnumber(L, 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)
{
mm_list(stream,delimiter, name,attributes);
}
/* 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)
{
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
* 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)
{
// user name
if(!lua_isstring(__L__,2))
{
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
*/
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_toboolean(L,4))
{
/*half open*/
flag = flag | OP_HALFOPEN;
}
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)
{
free(stream->original_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);
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;
}
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);
if(!stream || mail_ping(stream) == NIL)
{
lua_pushboolean(L,0);
}
else
{
lua_pushboolean(L,1);
}
return 2;
}
static int l_mail_check(lua_State *L)
{
MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1);
if(!stream)
{
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;
}
}
static int l_mail_get_raw_header(lua_State* L)
{
MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1);
if(!stream)
{
lua_pushnil(L);
return 1;
}
const int id = luaL_checknumber(L,2);
char* msg_header = mail_fetchheader_full(stream, id, NIL, NIL, 0);
lua_pushstring(L, msg_header);
return 1;
}
static int l_mail_getnumber(lua_State* L)
{
MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1);
if(!stream)
{
lua_pushnumber(L, -1);
return 1;
}
lua_pushnumber(L, stream->nmsgs);
return 1;
}
static void _imap_parse_address (lua_State* L, ADDRESS *addresslist)
{
ADDRESS *addresstmp;
addresstmp = addresslist;
int count = 1;
lua_newtable(L);
do {
lua_pushnumber(L,count);
lua_newtable(L);
if (addresstmp->personal)
{
add_property_string(L, "personal", addresstmp->personal);
}
if (addresstmp->adl)
{
add_property_string(L, "adl", addresstmp->adl);
}
if (addresstmp->mailbox)
{
add_property_string(L, "mailbox", addresstmp->mailbox);
}
if (addresstmp->host)
{
add_property_string(L, "host", addresstmp->host);
}
lua_settable(L, -3);
count++;
} while ((addresstmp = addresstmp->next));
}
static void _make_header_object(lua_State* L, ENVELOPE *en)
{
lua_newtable(L);
if (en->remail)
{
add_property_string(L, "remail", en->remail);
}
if(en->date)
{
add_property_string(L, "date", (char*)en->date);
}
if (en->subject)
{
add_property_string(L, "subject", en->subject);
}
if (en->in_reply_to)
{
add_property_string(L, "in_reply_to", en->in_reply_to);
}
if (en->message_id)
{
add_property_string(L, "message_id", en->message_id);
}
if (en->newsgroups)
{
add_property_string(L, "newsgroups", en->newsgroups);
}
if (en->followup_to)
{
add_property_string(L, "followup_to", en->followup_to);
}
if (en->references)
{
add_property_string(L, "references", en->references);
}
if (en->to) {
lua_pushstring(L, "to");
_imap_parse_address(L, en->to);
lua_settable(L, -3);
}
if (en->from) {
lua_pushstring(L, "from");
_imap_parse_address(L, en->from);
lua_settable(L, -3);
}
if (en->cc) {
lua_pushstring(L, "cc");
_imap_parse_address(L, en->cc);
lua_settable(L, -3);
}
if (en->bcc) {
lua_pushstring(L, "bcc");
_imap_parse_address(L, en->bcc);
lua_settable(L, -3);
}
if (en->reply_to) {
lua_pushstring(L, "reply_to");
_imap_parse_address(L, en->reply_to);
lua_settable(L, -3);
}
if (en->sender) {
lua_pushstring(L, "sender");
_imap_parse_address(L, en->sender);
lua_settable(L, -3);
}
if (en->return_path) {
lua_pushstring(L, "return_path");
_imap_parse_address(L, en->return_path);
lua_settable(L, -3);
}
}
static int l_rfc822_parse_header(lua_State* L)
{
ENVELOPE *en = NULL;
const char* headers = luaL_checkstring(L,1);
rfc822_parse_msg(&en, NULL, (char*)headers, strlen(headers), NULL, "UNKNOWN", NIL);
if(en)
{
_make_header_object(L, en);
mail_free_envelope(&en);
}
else
{
lua_pushnil(L);
}
return 1;
}
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_toboolean(L,3))
{
/*half open*/
flag = flag | OP_HALFOPEN;
}
if( lua_toboolean(L,4))
{
/*debug*/
flag = flag | OP_DEBUG;
}
if(!stream)
{
lua_pushboolean(L,0);
return 1;
}
stream = mail_open (stream,(char*)mailbox,flag);
if(!stream)
{
lua_pushboolean(L,0);
return 1;
}
stream->sparep = (void*) L;
/*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 int l_mail_get_header(lua_State* L)
{
char tmp[MAILTMPLEN + 1];
MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1);
if(!stream)
{
lua_pushnil(L);
return 1;
}
const int msgno = luaL_checknumber(L,2);
MESSAGECACHE *cache;
ENVELOPE *en;
if (mail_fetchstructure(stream, msgno, NIL)) {
cache = mail_elt(stream, msgno);
} else {
lua_pushnil(L);
return 1;
}
en = mail_fetchenvelope(stream, msgno);
/* call a function to parse all the text, so that we can use the
same function to parse text from other sources */
_make_header_object(L, en);
//free(en);
/* now run through properties that are only going to be returned
from a server, not text headers */
add_property_string(L, "recent", cache->recent ? (cache->seen ? "R": "N") : " ");
add_property_string(L, "unseen", (cache->recent | cache->seen) ? " " : "U");
add_property_string(L, "flagged", cache->flagged ? "F" : " ");
add_property_string(L, "answered", cache->answered ? "A" : " ");
add_property_string(L, "deleted", cache->deleted ? "D" : " ");
add_property_string(L, "draft", cache->draft ? "X" : " ");
add_property_long(L, "msgno", cache->msgno);
mail_date(tmp, cache);
add_property_string(L, "mail_date", tmp);
add_property_long(L, "size", cache->rfc822_size);
add_property_long(L, "udate", mail_longdate(cache));
if (en->from) {
tmp[0] = 0x00;
mail_fetchfrom(tmp, stream, msgno, sizeof(tmp));
str_trim(tmp, ' ');
add_property_string(L, "fetchfrom", tmp);
}
if (en->subject) {
tmp[0] = 0x00;
mail_fetchsubject(tmp, stream, msgno, sizeof(tmp));
str_trim(tmp, ' ');
add_property_string(L, "fetchsubject", tmp);
}
return 1;
}
static int l_mail_get_headers(lua_State* L)
{
MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1);
if(!stream)
{
lua_pushnil(L);
return 1;
}
char tmp[MAILTMPLEN];
unsigned int msgno;
char *t;
unsigned long i;
lua_newtable(L);
for (msgno = 1; msgno <= stream->nmsgs; msgno++) {
lua_pushnumber(L, msgno);
MESSAGECACHE * cache = mail_elt (stream, msgno);
mail_fetchstructure(stream, msgno, NIL);
tmp[0] = cache->recent ? (cache->seen ? 'R': 'N') : ' ';
tmp[1] = (cache->recent | cache->seen) ? ' ' : 'U';
tmp[2] = cache->flagged ? 'F' : ' ';
tmp[3] = cache->answered ? 'A' : ' ';
tmp[4] = cache->deleted ? 'D' : ' ';
tmp[5] = cache->draft ? 'X' : ' ';
snprintf(tmp + 6, sizeof(tmp) - 6, "%4ld) ", cache->msgno);
mail_date(tmp+11, cache);
tmp[22] = ' ';
tmp[23] = '\0';
mail_fetchfrom(tmp+23, stream, msgno, (long)20);
strcat(tmp, " ");
if ((i = cache->user_flags)) {
strcat(tmp, "{");
while (i) {
strlcat(tmp, stream->user_flags[find_rightmost_bit (&i)], sizeof(tmp));
if (i) strlcat(tmp, " ", sizeof(tmp));
}
strlcat(tmp, "} ", sizeof(tmp));
}
mail_fetchsubject(t = tmp + strlen(tmp), stream, msgno, (long)25);
snprintf(t += strlen(t), sizeof(tmp) - strlen(tmp), " (%ld chars)", cache->rfc822_size);
lua_pushstring(L, tmp);
lua_settable(L,-3);
}
return 1;
}
static int l_get_raw_body(lua_State* L)
{
char tmp[MAILTMPLEN + 1];
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);
if(lua_toboolean(L,3))
{
flags |= FT_PEEK;
}
char *body;
unsigned long body_len = 0;
body = mail_fetchtext_full(stream, msgno, &body_len, flags);
if (body_len == 0) {
lua_pushnil(L);
} else {
lua_pushstring(L, body);
}
return 1;
}
static void _imap_add_body(lua_State* L, BODY *body)
{
PARAMETER *par, *dpar;
PART *part;
int count = 1;
lua_newtable(L);
if (body->type <= TYPEMAX) {
add_property_long(L, "type", body->type);
switch(body->type)
{
case TYPETEXT: /* unformatted text */
add_property_string(L, "type_text", "TEXT");
break;
case TYPEMULTIPART: /* multiple part */
add_property_string(L, "type_text", "MULTIPART");
break;
case TYPEMESSAGE: /* encapsulated message */
add_property_string(L, "type_text", "MESSAGE");
break;
case TYPEAPPLICATION: /* application data */
add_property_string(L, "type_text", "APPLICATION");
break;
case TYPEAUDIO: /* audio */
add_property_string(L, "type_text", "AUDIO");
break;
case TYPEIMAGE: /* static image */
add_property_string(L, "type_text", "IMAGE");
break;
case TYPEVIDEO: /* video */
add_property_string(L, "type_text", "VIDEO");
break;
case TYPEMODEL: /* model */
add_property_string(L, "type_text", "MODEL");
break;
default:
add_property_string(L, "type_text", "OTHER");
break;
}
}
if (body->encoding <= ENCMAX) {
add_property_long(L, "encoding", body->encoding);
switch(body->encoding)
{
case ENC7BIT: /* 7 bit SMTP semantic data */
add_property_string(L, "encoding_text", "7BIT");
break;
case ENC8BIT: /* 8 bit SMTP semantic data */
add_property_string(L, "encoding_text", "8BIT");
break;
case ENCBINARY: /* 8 bit binary data */
add_property_string(L, "encoding_text", "BINARY");
break;
case ENCBASE64: /* base-64 encoded data */
add_property_string(L, "encoding_text", "BASE64");
break;
case ENCQUOTEDPRINTABLE: /* human-readable 8-as-7 bit data */
add_property_string(L, "encoding_text", "QUOTEDPRINTABLE");
break;
default:
add_property_string(L, "encoding_text", "OTHER");
break;
}
}
if (body->subtype) {
add_property_string(L, "subtype", body->subtype);
}
if (body->description) {
add_property_string(L, "description", body->description);
}
if (body->id) {
add_property_string(L, "id", body->id);
}
if (body->size.lines) {
add_property_long(L, "lines", body->size.lines);
}
if (body->size.bytes) {
add_property_long(L, "bytes", body->size.bytes);
}
if (body->md5) {
add_property_string(L, "md5", body->md5);
}
if (body->location) {
add_property_string(L, "location", body->location);
}
if (body->disposition.type) {
add_property_string(L, "disposition", body->disposition.type);
}
if (body->disposition.parameter) {
dpar = body->disposition.parameter;
lua_pushstring(L, "dparameters");
lua_newtable(L);
//count = 1;
do {
//lua_pushnumber(L, count);
//lua_newtable(L);
if(dpar->attribute && dpar->value)
{
add_property_string(L, dpar->attribute, dpar->value);
}
//lua_settable(L,-3);
//count++;
} while ((dpar = dpar->next));
lua_settable(L, -3);
}
if ((par = body->parameter)) {
lua_pushstring(L, "parameters");
lua_newtable(L);
//count = 1;
do {
//lua_pushnumber(L, count);
//lua_newtable(L);
if (par->attribute && par->value) {
add_property_string(L, par->attribute, par->value);
}
//lua_settable(L,-3);
//count++;
} while ((par = par->next));
lua_settable(L, -3);
}
/* multipart message ? */
if (body->type == TYPEMULTIPART) {
lua_pushstring(L, "parts");
lua_newtable(L);
count = 1;
for (part = body->nested.part; part; part = part->next) {
lua_pushnumber(L, count);
_imap_add_body(L, &part->body);
lua_settable(L, -3);
count++;
}
lua_settable(L, -3);
}
/* encapsulated message ? */
if ((body->type == TYPEMESSAGE) && (!strcasecmp(body->subtype, "rfc822"))) {
lua_pushstring(L, "message");
lua_newtable(L);
lua_pushstring(L, "header");
_make_header_object(L,body->nested.msg->env);
lua_settable(L, -3);
lua_pushstring(L, "body");
_imap_add_body(L, body->nested.msg->body);
lua_settable(L, -3);
lua_settable(L, -3);
}
}
static int l_get_structure(lua_State* L)
{
MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1);
BODY *body;
if(!stream)
{
lua_pushnil(L);
return 1;
}
const int msgno = luaL_checknumber(L,2);
mail_fetchstructure_full(stream, msgno, &body , 0);
if (!body) {
lua_pushnil(L);
return 1;
}
_imap_add_body(L, body);
return 1;
}
static int l_get_body_part(lua_State* L)
{
MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1);
if(!stream)
{
lua_pushnil(L);
return 1;
}
const int msgno = luaL_checknumber(L,2);
const char* part_name = luaL_checkstring(L,3);
unsigned int flags = 0;
if(lua_toboolean(L,3))
{
flags |= FT_PEEK;
}
char *body;
unsigned long len;
body = mail_fetchbody_full(stream, msgno, (char*)part_name, &len, flags);
if (!body) {
lua_pushnil(L);
return 1;
}
lua_pushstring(L, body);
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},// = PHP imap_getmailboxes, imap_list
{"reopen", l_reopen},
{"ping", l_mail_ping},
{"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}, // = php imap_header()
{"rfc822_parse_header", l_rfc822_parse_header},
{"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;
}