limap/limap.c
2022-08-31 20:47:06 +02:00

627 lines
15 KiB
C

#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "c-client.h"
#include "imap4r1.h"
#include <stdio.h>
#include <errno.h>
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__)
#define add_property_string(L,k,v) \
lua_pushstring(L,k); \
lua_pushstring(L,v); \
lua_settable(L,-3);
unsigned long find_rightmost_bit(unsigned long *valptr);
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);
}
/* 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_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);
free(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_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 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 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},
{"get_raw_header", l_mail_get_raw_header},
{"get_nmail", l_mail_getnumber},
{"get_headers", l_mail_get_headers},
{"rfc822_parse_header", l_rfc822_parse_header},
{NULL,NULL}
};
int luaopen_limap(lua_State *L)
{
#include "linkage.c"
luaL_newlib(L, _lib);
return 1;
}