Add binding to some basic mail API

This commit is contained in:
Dany LE 2023-01-10 00:34:20 +01:00
parent 19e201d447
commit 115a068ac0
3 changed files with 233 additions and 37 deletions

104
Notes.md Normal file
View File

@ -0,0 +1,104 @@
# How to identify partno string
imap-fetchbody() will decode attached email messages inline with the rest of the email parts, however the way it works when handling attached email messages is inconsistent with the main email message.
With an email message that only has a text body and does not have any mime attachments, imap-fetchbody() will return the following for each requested part number:
(empty) - Entire message
0 - Message header
1 - Body text
With an email message that is a multi-part message in MIME format, and contains the message text in plain text and HTML, and has a file.ext attachment, imap-fetchbody() will return something like the following for each requested part number:
(empty) - Entire message
0 - Message header
1 - MULTIPART/ALTERNATIVE
1.1 - TEXT/PLAIN
1.2 - TEXT/HTML
2 - file.ext
Now if you attach the above email to an email with the message text in plain text and HTML, imap_fetchbody() will use this type of part number system:
(empty) - Entire message
0 - Message header
1 - MULTIPART/ALTERNATIVE
1.1 - TEXT/PLAIN
1.2 - TEXT/HTML
2 - MESSAGE/RFC822 (entire attached message)
2.0 - Attached message header
2.1 - TEXT/PLAIN
2.2 - TEXT/HTML
2.3 - file.ext
Note that the file.ext is on the same level now as the plain text and HTML, and that there is no way to access the MULTIPART/ALTERNATIVE in the attached message.
Here is a modified version of some of the code from previous posts that will build an easily accessible array that includes accessible attached message parts and the message body if there aren't multipart mimes. The $structure variable is the output of the imap_fetchstructure() function. The returned $part_array has the field 'part_number' which contains the part number to be fed directly into the imap_fetchbody() function.
<?php
function create_part_array($structure, $prefix="") {
//print_r($structure);
if (sizeof($structure->parts) > 0) { // There some sub parts
foreach ($structure->parts as $count => $part) {
add_part_to_array($part, $prefix.($count+1), $part_array);
}
}else{ // Email does not have a seperate mime attachment for text
$part_array[] = array('part_number' => $prefix.'1', 'part_object' => $obj);
}
return $part_array;
}
// Sub function for create_part_array(). Only called by create_part_array() and itself.
function add_part_to_array($obj, $partno, & $part_array) {
$part_array[] = array('part_number' => $partno, 'part_object' => $obj);
if ($obj->type == 2) { // Check to see if the part is an attached email message, as in the RFC-822 type
//print_r($obj);
if (sizeof($obj->parts) > 0) { // Check to see if the email has parts
foreach ($obj->parts as $count => $part) {
// Iterate here again to compensate for the broken way that imap_fetchbody() handles attachments
if (sizeof($part->parts) > 0) {
foreach ($part->parts as $count2 => $part2) {
add_part_to_array($part2, $partno.".".($count2+1), $part_array);
}
}else{ // Attached email does not have a seperate mime attachment for text
$part_array[] = array('part_number' => $partno.'.'.($count+1), 'part_object' => $obj);
}
}
}else{ // Not sure if this is possible
$part_array[] = array('part_number' => $prefix.'.1', 'part_object' => $obj);
}
}else{ // If there are more sub-parts, expand them out.
if (sizeof($obj->parts) > 0) {
foreach ($obj->parts as $count => $p) {
add_part_to_array($p, $partno.".".($count+1), $part_array);
}
}
}
}
?>
# body type
```c
#define TYPETEXT 0 /* unformatted text */
#define TYPEMULTIPART 1 /* multiple part */
#define TYPEMESSAGE 2 /* encapsulated message */
#define TYPEAPPLICATION 3 /* application data */
#define TYPEAUDIO 4 /* audio */
#define TYPEIMAGE 5 /* static image */
#define TYPEVIDEO 6 /* video */
#define TYPEMODEL 7 /* model */
#define TYPEOTHER 8 /* unknown */
#define TYPEMAX 15 /* maximum type code */
```
# stream flags
```lua
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 */
```

152
limap.c
View File

@ -308,9 +308,15 @@ static int l_mail_open(lua_State *L)
{ {
const char* mailbox = luaL_checkstring(L,1); const char* mailbox = luaL_checkstring(L,1);
long flag = 0; long flag = 0;
if( lua_isnumber(L,2)) if( lua_toboolean(L,2))
{ {
flag = flag | (long)luaL_checknumber(L,2); /*half open*/
flag = flag | OP_HALFOPEN;
}
if( lua_toboolean(L,3))
{
/*debug*/
flag = flag | OP_DEBUG;
} }
MAILSTREAM *stream = mail_open (NIL,(char*)mailbox,flag); MAILSTREAM *stream = mail_open (NIL,(char*)mailbox,flag);
if(!stream) if(!stream)
@ -548,9 +554,15 @@ static int l_reopen(lua_State *L)
MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1); MAILSTREAM *stream = (MAILSTREAM *) lua_touserdata(L, 1);
const char* mailbox = luaL_checkstring(L,2); const char* mailbox = luaL_checkstring(L,2);
long flag = 0; long flag = 0;
if( lua_isnumber(L,3)) if( lua_toboolean(L,3))
{ {
flag = flag | (long)luaL_checknumber(L,3); /*half open*/
flag = flag | OP_HALFOPEN;
}
if( lua_toboolean(L,4))
{
/*debug*/
flag = flag | OP_DEBUG;
} }
if(!stream) if(!stream)
{ {
@ -604,16 +616,16 @@ static int l_mail_get_header(lua_State* L)
/* now run through properties that are only going to be returned /* now run through properties that are only going to be returned
from a server, not text headers */ from a server, not text headers */
add_property_string(L, "Recent", cache->recent ? (cache->seen ? "R": "N") : " "); 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, "unseen", (cache->recent | cache->seen) ? " " : "U");
add_property_string(L, "Flagged", cache->flagged ? "F" : " "); add_property_string(L, "flagged", cache->flagged ? "F" : " ");
add_property_string(L, "Answered", cache->answered ? "A" : " "); add_property_string(L, "answered", cache->answered ? "A" : " ");
add_property_string(L, "Deleted", cache->deleted ? "D" : " "); add_property_string(L, "deleted", cache->deleted ? "D" : " ");
add_property_string(L, "Draft", cache->draft ? "X" : " "); add_property_string(L, "draft", cache->draft ? "X" : " ");
add_property_long(L, "Msgno", cache->msgno); add_property_long(L, "msgno", cache->msgno);
mail_date(tmp, cache); mail_date(tmp, cache);
add_property_string(L, "MailDate", tmp); add_property_string(L, "mail_date", tmp);
add_property_long(L, "Size", cache->rfc822_size); add_property_long(L, "size", cache->rfc822_size);
add_property_long(L, "udate", mail_longdate(cache)); add_property_long(L, "udate", mail_longdate(cache));
if (en->from) { if (en->from) {
@ -710,10 +722,63 @@ static void _imap_add_body(lua_State* L, BODY *body)
if (body->type <= TYPEMAX) { if (body->type <= TYPEMAX) {
add_property_long(L, "type", body->type); 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) { if (body->encoding <= ENCMAX) {
add_property_long(L, "encoding", body->encoding); 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) { if (body->subtype) {
@ -751,14 +816,16 @@ static void _imap_add_body(lua_State* L, BODY *body)
dpar = body->disposition.parameter; dpar = body->disposition.parameter;
lua_pushstring(L, "dparameters"); lua_pushstring(L, "dparameters");
lua_newtable(L); lua_newtable(L);
count = 1; //count = 1;
do { do {
lua_pushnumber(L, count); //lua_pushnumber(L, count);
lua_newtable(L); //lua_newtable(L);
add_property_string(L, "attribute", dpar->attribute); if(dpar->attribute && dpar->value)
add_property_string(L, "value", dpar->value); {
lua_settable(L,-3); add_property_string(L, dpar->attribute, dpar->value);
count++; }
//lua_settable(L,-3);
//count++;
} while ((dpar = dpar->next)); } while ((dpar = dpar->next));
lua_settable(L, -3); lua_settable(L, -3);
} }
@ -766,19 +833,16 @@ static void _imap_add_body(lua_State* L, BODY *body)
if ((par = body->parameter)) { if ((par = body->parameter)) {
lua_pushstring(L, "parameters"); lua_pushstring(L, "parameters");
lua_newtable(L); lua_newtable(L);
count = 1; //count = 1;
do { do {
lua_pushnumber(L, count); //lua_pushnumber(L, count);
lua_newtable(L); //lua_newtable(L);
if (par->attribute) { if (par->attribute && par->value) {
add_property_string(L, "attribute", par->attribute); add_property_string(L, par->attribute, par->value);
}
if (par->value) {
add_property_string(L, "value", par->value);
} }
lua_settable(L,-3); //lua_settable(L,-3);
count++; //count++;
} while ((par = par->next)); } while ((par = par->next));
lua_settable(L, -3); lua_settable(L, -3);
} }
@ -833,6 +897,33 @@ static int l_get_structure(lua_State* L)
return 1; 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 const struct luaL_Reg _lib [] = { static const struct luaL_Reg _lib [] = {
{"open", l_mail_open}, {"open", l_mail_open},
{"close", l_mail_close}, {"close", l_mail_close},
@ -847,6 +938,7 @@ static const struct luaL_Reg _lib [] = {
{"rfc822_parse_header", l_rfc822_parse_header}, {"rfc822_parse_header", l_rfc822_parse_header},
{"get_raw_body", l_get_raw_body}, {"get_raw_body", l_get_raw_body},
{"get_structure", l_get_structure}, {"get_structure", l_get_structure},
{"get_body_part", l_get_body_part},
{NULL,NULL} {NULL,NULL}
}; };

View File

@ -9,9 +9,9 @@ local OP_EXPUNGE= 0x80 --/* silently expunge recycle stream */
local imap = require("limap") local imap = require("limap")
local passwd = os.getenv("IMAP_PWD") or "" local passwd = os.getenv("IMAP_PWD")
local url = string.format("{iohub.dev:143/imap/tls-sslv23/novalidate-cert/user=mrsang;%s}",passwd); 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 handle, box = imap.open(url,true, true) -- half open and debug on
local r,v = false,nil; local r,v = false,nil;
@ -37,7 +37,7 @@ if(handle) then
print(k1,v1) print(k1,v1)
end end
end end
r, box = imap.reopen(handle,box:gsub("[^}]*$","INBOX"), OP_DEBUG) r, box = imap.reopen(handle,box:gsub("[^}]*$","INBOX"), false, true) -- half open off, debug on
if r then if r then
print("new box opened", box) print("new box opened", box)
else else
@ -59,13 +59,13 @@ if(handle) then
dump(headers) dump(headers)
end end
print("===============================") print("===============================")
local header = imap.get_header(handle, nmail-3) local header = imap.get_header(handle, nmail)
dump(header) dump(header)
print("===============================") print("===============================")
local raw_body = imap.get_raw_body(handle, nmail-3, true) -- peak the message without set it to seen --local raw_body = imap.get_raw_body(handle, nmail, true) -- peak the message without set it to seen
print(raw_body) --print(raw_body)
print("===============================") print("===============================")
local structure = imap.get_structure(handle, nmail-3) local structure = imap.get_structure(handle, nmail)
dump(structure) dump(structure)
imap.close(handle) imap.close(handle)