diff --git a/.gitattributes b/.gitattributes index 86550f50eec2ea9ca1de872cc43904cf918919ec..c0303b3e70ea3e9d2d7575b1a42cc6e7bf0a46b2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -47,6 +47,7 @@ testfont binary /src/modules/Image/pnm.c foreign_ident /src/modules/Image/quant.c foreign_ident /src/modules/Image/togif.c foreign_ident +/src/modules/MIME/mime.c foreign_ident /src/modules/Mysql/Makefile.in foreign_ident /src/modules/Mysql/acconfig.h foreign_ident /src/modules/Mysql/configure.in foreign_ident diff --git a/lib/modules/MIME.pmod b/lib/modules/MIME.pmod new file mode 100644 index 0000000000000000000000000000000000000000..30cd633bd7e7fba1b0322a2b01870e4860e48716 --- /dev/null +++ b/lib/modules/MIME.pmod @@ -0,0 +1,342 @@ + +class support { + + inherit "MIME"; + + string generate_boundary( ) + { + return "'ThIs-RaNdOm-StRiNg-/=_."+random(1000000000)+":"; + } + + string decode( string data, string encoding ) + { + switch(lower_case(encoding||"binary")) { + case "base64": + return decode_base64( data ); + case "quoted-printable": + return decode_qp( data ); + case "x-uue": + return decode_uue( data ); + case "7bit": case "8bit": case "binary": + return data; + default: + throw(({ "unknown transfer encoding "+encoding+"\n", + backtrace() })); + } + } + + string encode( string data, string encoding, void|string filename ) + { + switch(lower_case(encoding||"binary")) { + case "base64": + return encode_base64( data ); + case "quoted-printable": + return encode_qp( data ); + case "x-uue": + return encode_uue( data, filename ); + case "7bit": case "8bit": case "binary": + return data; + default: + throw(({ "unknown transfer encoding "+encoding+"\n", + backtrace() })); + } + } + + + + /* rfc1522 */ + + array(string) decode_word( string word ) + { + string charset, encoding, encoded_text; + if(sscanf(word, "=?%[^][ \t()<>@,;:\"\\/?.=]?%[^][ \t()<>@,;:\"\\/?.=]?%s?=", + charset, encoding, encoded_text) == 3) { + switch(lower_case(encoding)) { + case "b": + encoding = "base64"; break; + case "q": + encoding = "quoted-printable"; break; + default: + throw(({ "invalid rfc1522 encoding "+encoding+"\n", backtrace() })); + } + return ({ decode( replace(encoded_text, "_", " "), encoding ), + lower_case(charset) }); + } else + return ({ word, 0 }); + } + + string encode_word( array(string) word, string encoding ) + { + if(!encoding || !word[1]) + return word[0]; + switch(lower_case(encoding)) { + case "b": + case "base64": + encoding = "base64"; break; + case "q": + case "quoted-printable": + encoding = "quoted-printable"; break; + default: + throw(({ "invalid rfc1522 encoding "+encoding+"\n", backtrace() })); + } + return "=?"+word[1]+"?"+encoding[0..0]+"?"+ + replace( encode( word[0], encoding ), + ({ "?", "_" }), ({ "=3F", "=5F" }))+"?="; + } + + string guess_subtype(string typ) + { + switch(typ) { + case "text": + return "plain"; + case "message": + return "rfc822"; + case "multipart": + return "mixed"; + } + return 0; + } + +}; + +inherit support; + +class Message { + + inherit support; + import Array; + + string encoded_data; + string decoded_data; + mapping(string:string) headers; + array(object) body_parts; + + string type, subtype, charset, boundary, transfer_encoding; + mapping (string:string) params; + + string disposition; + mapping (string:string) disp_params; + + + string get_filename( ) + { + return disp_params["filename"] || params["name"]; + } + + void setdata( string data ) + { + if(data != decoded_data) { + decoded_data = data; + encoded_data = 0; + } + } + + string getdata( ) + { + if(encoded_data && !decoded_data) + decoded_data=decode(encoded_data, transfer_encoding); + return decoded_data; + } + + string getencoded( ) + { + if(decoded_data && !encoded_data) + encoded_data=encode(decoded_data, transfer_encoding, get_filename( )); + return encoded_data; + } + + void setencoding( string encoding ) + { + if(encoded_data && !decoded_data) + decoded_data = getdata( ); + headers["content-transfer-encoding"]=transfer_encoding=lower_case(encoding); + encoded_data = 0; + } + + void setparam( string param, string value ) + { + param = lower_case(param); + params[param] = value; + switch(param) { + case "charset": charset = value; break; + case "boundary": boundary = value; break; + case "name": + if(transfer_encoding != "x-uue") + break; + if(encoded_data && !decoded_data) + decoded_data = getdata( ); + encoded_data = 0; + break; + } + headers["content-type"] = + quote(({ type, '/', subtype })+ + `+(@map(indices(params), lambda(string param) { + return ({ ';', param, '=', params[param] }); + }))); + } + + void setdisp_param( string param, string value ) + { + param = lower_case(param); + disp_params[param] = value; + switch(param) { + case "filename": + if(transfer_encoding != "x-uue") + break; + if(encoded_data && !decoded_data) + decoded_data = getdata( ); + encoded_data = 0; + break; + } + headers["content-disposition"] = + quote(({ disposition || "attachment" })+ + `+(@map(indices(disp_params), lambda(string param) { + return ({ ';', param, '=', disp_params[param] }); + }))); + } + + void setcharset( string charset ) + { + setparam( "charset", charset ); + } + + void setboundary( string boundary ) + { + setparam( "boundary", boundary ); + } + + string cast( string dest_type ) + { + string data; + object body_part; + + if(dest_type != "string") + throw(({ "can't cast Message to "+dest_type+"\n", backtrace() })); + + data = getencoded( ); + + if(body_parts) { + + if(!boundary) { + if(type != "multipart") { type="multipart"; subtype="mixed"; } + setboundary(generate_boundary()); + } + + data += "\r\n"; + foreach( body_parts, body_part ) + data += "--"+boundary+"\r\n"+((string)body_part)+"\r\n"; + data += "--"+boundary+"--\r\n"; + } + + headers["content-length"] = ""+strlen(data); + + return map(indices(headers), + lambda(string hname){ + return replace(map(hname/"-", String.capitalize)*"-", "Mime", "MIME")+ + ": "+headers[hname]; + })*"\r\n"+"\r\n\r\n"+data; + } + + void create(void | string message, + void | mapping(string:string) hdrs, + void | array(object) parts) + { + encoded_data = 0; + decoded_data = 0; + headers = ([ ]); + params = ([ ]); + disp_params = ([ ]); + body_parts = 0; + type = "text"; + subtype = "plain"; + charset = "us-ascii"; + boundary = 0; + disposition = 0; + if (hdrs || parts) { + string hname; + if(message) + decoded_data = message; + else + decoded_data = (parts? + "This is a multi-part message in MIME format.\r\n": + ""); + if(hdrs) + foreach( indices(hdrs), hname ) + headers[lower_case(hname)] = hdrs[hname]; + body_parts = parts; + } else if (message) { + string head, body, header, hname, hcontents; + int mesgsep; + { + int mesgsep1 = search(message, "\r\n\r\n"); + int mesgsep2 = search(message, "\n\n"); + mesgsep = (mesgsep1<0? mesgsep2 : + (mesgsep2<0? mesgsep1 : + (mesgsep1<mesgsep2? mesgsep1 : mesgsep2))); + } + if(mesgsep<0) { + head = message; + body = ""; + } else { + head = (mesgsep>0? message[..mesgsep-1]:""); + body = message[mesgsep+(message[mesgsep]=='\r'? 4:2)..]; + } + foreach( replace(head, ({"\r", "\n ", "\n\t"}), + ({"", " ", " "}))/"\n", header ) { + if(4==sscanf(header, "%[!-9;-~]%*[ \t]:%*[ \t]%s", hname, hcontents)) + headers[lower_case(hname)] = hcontents; + } + encoded_data = body; + } + if(headers["content-type"]) { + array(array(string|int)) arr = + tokenize(headers["content-type"]) / ({';'}); + array(string|int) p; + if(sizeof(arr[0])!=3 || arr[0][1]!='/' || + !stringp(arr[0][0]) || !stringp(arr[0][2])) + if(sizeof(arr[0])==1 && stringp(arr[0][0]) && + (subtype = guess_subtype(lower_case(type = arr[0][0])))) + arr = ({ ({ type, '/', subtype }) }) + arr[1..]; + else + throw(({ "invalid Content-Type in message\n", backtrace() })); + type = lower_case(arr[0][0]); + subtype = lower_case(arr[0][2]); + foreach( arr[1..], p ) { + if(sizeof(p)<3 || p[1]!='=' || !stringp(p[0])) + throw(({ "invalid parameter in Content-Type\n", backtrace() })); + params[ lower_case(p[0]) ] = p[2..]*""; + } + charset = lower_case(params["charset"] || charset); + boundary = params["boundary"]; + } + if(headers["content-disposition"]) { + array(array(string|int)) arr = + tokenize(headers["content-disposition"]) / ({';'}); + array(string|int) p; + if(sizeof(arr[0])!=1 || !stringp(arr[0][0])) + throw(({ "invalid Content-Disposition in message\n", backtrace() })); + disposition = lower_case(arr[0][0]); + foreach( arr[1..], p ) { + if(sizeof(p)<3 || p[1]!='=' || !stringp(p[0])) + throw(({ "invalid parameter in Content-Disposition\n", backtrace() })); + disp_params[ lower_case(p[0]) ] = p[2..]*""; + } + } + if(headers["content-transfer-encoding"]) { + array(string) arr=tokenize(headers["content-transfer-encoding"]); + if(sizeof(arr)!=1 || !stringp(arr[0])) + throw(({"invalid Content-Transfer-Encoding in message\n", backtrace()})); + transfer_encoding = lower_case(arr[0]); + } + if(boundary && type=="multipart" && !body_parts && + (encoded_data || decoded_data)) { + array(string) parts = ("\n"+getdata())/("\n--"+boundary); + if(parts[-1][0..3]!="--\n" && parts[-1][0..3]!="--\r\n") + throw(({ "multipart message improperly terminated\n", backtrace() })); + encoded_data = 0; + decoded_data = parts[0][1..]; + body_parts = map(parts[1..sizeof(parts)-2], lambda(string part){ return object_program(this_object())(part[1..]); }); + } + } + +} diff --git a/src/modules/MIME/.cvsignore b/src/modules/MIME/.cvsignore new file mode 100644 index 0000000000000000000000000000000000000000..66d35f0a032e2d62628e6095389746641e62dec2 --- /dev/null +++ b/src/modules/MIME/.cvsignore @@ -0,0 +1,10 @@ +.pure +Makefile +config.log +config.status +config.h.in +configure +dependencies +linker_options +stamp-h +stamp-h.in diff --git a/src/modules/MIME/.gitignore b/src/modules/MIME/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..74cf2e941ed8a052aef5431084df6d70853912f8 --- /dev/null +++ b/src/modules/MIME/.gitignore @@ -0,0 +1,10 @@ +/.pure +/Makefile +/config.log +/config.status +/config.h.in +/configure +/dependencies +/linker_options +/stamp-h +/stamp-h.in diff --git a/src/modules/MIME/Makefile.in b/src/modules/MIME/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..63f00eeaa58b4ebb61d0b2e365d6ba2cae658545 --- /dev/null +++ b/src/modules/MIME/Makefile.in @@ -0,0 +1,7 @@ +SRCDIR=@srcdir@ +VPATH=@srcdir@:@srcdir@/../..:../.. +OBJS=mime.o +MODULE_LDFLAGS=@LDFLAGS@ @LIBS@ + +@dynamic_module_makefile@ +@dependencies@ diff --git a/src/modules/MIME/acconfig.h b/src/modules/MIME/acconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/modules/MIME/configure.in b/src/modules/MIME/configure.in new file mode 100644 index 0000000000000000000000000000000000000000..e0d24b4b20f95e755849c64931646287c76ff9e6 --- /dev/null +++ b/src/modules/MIME/configure.in @@ -0,0 +1,6 @@ +AC_INIT(mime.c) +AC_CONFIG_HEADER(config.h) + +sinclude(../module_configure.in) + +AC_OUTPUT(Makefile,echo FOO >stamp-h ) diff --git a/src/modules/MIME/mime.c b/src/modules/MIME/mime.c new file mode 100644 index 0000000000000000000000000000000000000000..612736975b473c1e5af34925760663419239d96c --- /dev/null +++ b/src/modules/MIME/mime.c @@ -0,0 +1,535 @@ +#include "config.h" + +#include "global.h" +RCSID("$Id: mime.c,v 1.1 1997/03/08 21:09:51 marcus Exp $"); +#include "stralloc.h" +#include "types.h" +#include "macros.h" +#include "object.h" +#include "program.h" +#include "interpret.h" +#include "builtin_functions.h" +#include "error.h" + +static void f_decode_base64(INT32 args); +static void f_encode_base64(INT32 args); +static void f_decode_qp(INT32 args); +static void f_encode_qp(INT32 args); +static void f_decode_uue(INT32 args); +static void f_encode_uue(INT32 args); + +static void f_tokenize(INT32 args); +static void f_quote(INT32 args); + +static char base64tab[64]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static char base64rtab[0x80-' ']; +static char qptab[16]="0123456789ABCDEF"; +static char qprtab[0x80-'0']; + +#define CT_CTL 0 +#define CT_WHITE 1 +#define CT_ATOM 2 +#define CT_SPECIAL 3 +#define CT_LPAR 4 +#define CT_RPAR 5 +#define CT_LBRACK 6 +#define CT_RBRACK 7 +#define CT_QUOTE 8 +unsigned char rfc822ctype[256]; + +/* Initialize and start module */ +void pike_module_init(void) +{ + int i; + memset(base64rtab, -1, sizeof(base64rtab)); + for(i=0; i<64; i++) base64rtab[base64tab[i]-' ']=i; + memset(qprtab, -1, sizeof(qprtab)); + for(i=0; i<16; i++) qprtab[qptab[i]-'0']=i; + for(i=10; i<16; i++) qprtab[qptab[i]-('0'+'A'-'a')]=i; + + memset(rfc822ctype, CT_ATOM, sizeof(rfc822ctype)); + for(i=0; i<32; i++) rfc822ctype[i]=CT_CTL; + rfc822ctype[127]=CT_CTL; + rfc822ctype[' ']=CT_WHITE; + rfc822ctype['\t']=CT_WHITE; + rfc822ctype['(']=CT_LPAR; + rfc822ctype[')']=CT_RPAR; + rfc822ctype['[']=CT_LBRACK; + rfc822ctype[']']=CT_LBRACK; + rfc822ctype['"']=CT_QUOTE; + for(i=0; i<10; i++) rfc822ctype[(int)"<>@,;:\\/?="[i]]=CT_SPECIAL; + + add_function_constant("decode_base64", f_decode_base64, "function(string:string)", OPT_TRY_OPTIMIZE); + add_function_constant("encode_base64", f_encode_base64, "function(string:string)", OPT_TRY_OPTIMIZE); + add_function_constant("decode_qp", f_decode_qp, "function(string:string)", OPT_TRY_OPTIMIZE); + add_function_constant("encode_qp", f_encode_qp, "function(string:string)", OPT_TRY_OPTIMIZE); + add_function_constant("decode_uue", f_decode_uue, "function(string:string)", OPT_TRY_OPTIMIZE); + add_function_constant("encode_uue", f_encode_uue, "function(string,void|string:string)", OPT_TRY_OPTIMIZE); + + add_function_constant("tokenize", f_tokenize, "function(string:array(string|int))", OPT_TRY_OPTIMIZE); + add_function_constant("quote", f_quote, "function(array(string|int):string)", OPT_TRY_OPTIMIZE); + +} + +/* Restore and exit module */ +void pike_module_exit(void) +{ +} + +static void f_decode_base64(INT32 args) +{ + if(args != 1) + error("Wrong number of arguments to MIME.decode_base64()\n"); + else if(sp[-1].type != T_STRING) + error("Wrong type of argument to MIME.decode_base64()\n"); + else { + dynamic_buffer buf; + char *src; + INT32 cnt, d=1; + int pads=0; + + buf.s.str=NULL; + initialize_buf(&buf); + + for(src = sp[-1].u.string->str, cnt = sp[-1].u.string->len; cnt--; src++) + if(*src>=' ' && base64rtab[*src-' ']>=0) + if((d=(d<<6)|base64rtab[*src-' '])>=0x1000000) { + low_my_putchar( d>>16, &buf ); + low_my_putchar( d>>8, &buf ); + low_my_putchar( d, &buf ); + d=1; + } + else ; + else if(*src=='=') { pads++; d>>=2; } + + switch(pads) { + case 1: + low_my_putchar( d>>8, &buf ); + case 2: + low_my_putchar( d, &buf ); + } + + pop_n_elems(1); + push_string(low_free_buf(&buf)); + } +} + +static int do_b64_encode(INT32 groups, unsigned char **srcp, char **destp) +{ + unsigned char *src=*srcp; + char *dest=*destp; + int g=0; + while(groups--) { + INT32 d = *src++<<8; + d = (*src++|d)<<8; + d |= *src++; + *dest++ = base64tab[d>>18]; + *dest++ = base64tab[(d>>12)&63]; + *dest++ = base64tab[(d>>6)&63]; + *dest++ = base64tab[d&63]; + if(++g == 19) { + *dest++ = 13; + *dest++ = 10; + g=0; + } + } + *srcp = src; + *destp = dest; + return g; +} + +static void f_encode_base64(INT32 args) +{ + if(args != 1) + error("Wrong number of arguments to MIME.encode_base64()\n"); + else if(sp[-1].type != T_STRING) + error("Wrong type of argument to MIME.encode_base64()\n"); + else { + INT32 groups=(sp[-1].u.string->len+2)/3; + int last=(sp[-1].u.string->len-1)%3+1; + struct pike_string *str = begin_shared_string(groups*4+(groups/19)*2); + unsigned char *src=(unsigned char *)sp[-1].u.string->str; + char *dest=str->str; + + if(groups) { + unsigned char tmp[3], *tmpp=tmp; + int i; + + if(do_b64_encode(groups-1, &src, &dest)==18) + str->len-=2; + + tmp[1]=tmp[2]=0; + for(i=0; i<last; i++) tmp[i]=*src++; + do_b64_encode(1, &tmpp, &dest); + switch(last) { + case 1: + *--dest='='; + case 2: + *--dest='='; + } + } + pop_n_elems(1); + push_string(end_shared_string(str)); + } +} + +static void f_decode_qp(INT32 args) +{ + if(args != 1) + error("Wrong number of arguments to MIME.decode_qp()\n"); + else if(sp[-1].type != T_STRING) + error("Wrong type of argument to MIME.decode_qp()\n"); + else { + dynamic_buffer buf; + char *src; + INT32 cnt; + + buf.s.str=NULL; + initialize_buf(&buf); + + for(src = sp[-1].u.string->str, cnt = sp[-1].u.string->len; cnt--; src++) + if(*src == '=') + if(cnt>0 && (src[1]==10 || src[1]==13)) { + if(src[1]==13) { --cnt; src++; } + if(cnt>0 && src[1]==10) { --cnt; src++; } + } else if(cnt>=2 && src[1]>='0' && src[2]>='0' && + qprtab[src[1]-'0']>=0 && qprtab[src[2]-'0']>=0) { + low_my_putchar( (qprtab[src[1]-'0']<<4)|qprtab[src[2]-'0'], &buf ); + cnt -= 2; + src += 2; + } else ; + else + low_my_putchar( *src, &buf ); + + pop_n_elems(1); + push_string(low_free_buf(&buf)); + } +} + +static void f_encode_qp(INT32 args) +{ + if(args != 1) + error("Wrong number of arguments to MIME.encode_qp()\n"); + else if(sp[-1].type != T_STRING) + error("Wrong type of argument to MIME.encode_qp()\n"); + else { + dynamic_buffer buf; + unsigned char *src = (unsigned char *)sp[-1].u.string->str; + INT32 cnt; + int col=0; + + buf.s.str=NULL; + initialize_buf(&buf); + + for(cnt = sp[-1].u.string->len; cnt--; src++) { + if((*src >= 33 && *src <= 60) || + (*src >= 62 && *src <= 126)) + low_my_putchar( *src, &buf ); + else { + low_my_putchar( '=', &buf ); + low_my_putchar( qptab[(*src)>>4], &buf ); + low_my_putchar( qptab[(*src)&15], &buf ); + col += 2; + } + if(++col >= 73) { + low_my_putchar( '=', &buf ); + low_my_putchar( 13, &buf ); + low_my_putchar( 10, &buf ); + col = 0; + } + } + + pop_n_elems(1); + push_string(low_free_buf(&buf)); + } +} + + +static void f_decode_uue(INT32 args) +{ + if(args != 1) + error("Wrong number of arguments to MIME.decode_uue()\n"); + else if(sp[-1].type != T_STRING) + error("Wrong type of argument to MIME.decode_uue()\n"); + else { + dynamic_buffer buf; + char *src; + INT32 cnt; + + buf.s.str=NULL; + initialize_buf(&buf); + + src = sp[-1].u.string->str; + cnt = sp[-1].u.string->len; + + while(cnt--) + if(*src++=='b' && cnt>5 && !memcmp(src, "egin ", 5)) + break; + if(cnt>=0) + while(cnt--) + if(*src++=='\n') + break; + if(cnt<0) { + pop_n_elems(1); + push_int(0); + return; + } + for(;;) { + int l, g; + if(cnt<=0 || (l=(*src++)-' ')>=64) + break; + --cnt; + g=(l+2)/3; + l-=g*3; + if((cnt-=g*4)<0) + break; + while(g--) { + INT32 d = ((*src++-' ')&63)<<18; + d |= ((*src++-' ')&63)<<12; + d |= ((*src++-' ')&63)<<6; + d |= ((*src++-' ')&63); + low_my_putchar( d>>16, &buf ); + low_my_putchar( d>>8, &buf ); + low_my_putchar( d, &buf ); + } + while(l++) + low_make_buf_space( -1, &buf ); + while(cnt-- && *src++!=10); + } + pop_n_elems(1); + push_string(low_free_buf(&buf)); + } +} + +static void do_uue_encode(INT32 groups, unsigned char **srcp, char **destp, + INT32 last) +{ + unsigned char *src=*srcp; + char *dest=*destp; + + while(groups || last) { + int g=(groups>=15? 15:groups); + if(g<15) { + *dest++ = ' '+(3*g+last); + last = 0; + } else + *dest++ = ' '+(3*g); + groups -= g; + + while(g--) { + INT32 d = *src++<<8; + d = (*src++|d)<<8; + d |= *src++; + *dest++ = ' '+(d>>18); + *dest++ = ' '+((d>>12)&63); + *dest++ = ' '+((d>>6)&63); + *dest++ = ' '+(d&63); + } + + if(groups || last) { + *dest++ = 13; + *dest++ = 10; + } + } + *srcp = src; + *destp = dest; +} + +static void f_encode_uue(INT32 args) +{ + if(args != 1 && args != 2) + error("Wrong number of arguments to MIME.encode_uue()\n"); + else if(sp[-args].type != T_STRING || + (args == 2 && sp[-1].type != T_VOID && sp[-1].type != T_STRING && + sp[-1].type != T_INT)) + error("Wrong type of argument to MIME.encode_uue()\n"); + else { + struct pike_string *str; + unsigned char *src = (unsigned char *) sp[-args].u.string->str; + INT32 groups=(sp[-args].u.string->len+2)/3; + int last=(sp[-args].u.string->len-1)%3+1; + char *dest, *filename = "attachment"; + if(args == 2 && sp[-1].type == T_STRING) + filename = sp[-1].u.string->str; + str = begin_shared_string(groups*4+((groups+14)/15)*3+strlen(filename)+20); + dest = str->str; + sprintf(dest, "begin 644 %s\r\n", filename); + dest += 12 + strlen(filename); + if(groups) { + unsigned char tmp[3], *tmpp=tmp; + char *kp, k; + int i; + + do_uue_encode(groups-1, &src, &dest, last); + + tmp[1]=tmp[2]=0; + for(i=0; i<last; i++) tmp[i]=*src++; + k = *--dest; + kp = dest; + do_uue_encode(1, &tmpp, &dest, 0); + *kp = k; + switch(last) { + case 1: + dest[-2]='`'; + case 2: + dest[-1]='`'; + } + *dest++ = 13; + *dest++ = 10; + } + memcpy(dest, "`\r\nend\r\n", 8); + pop_n_elems(args); + push_string(end_shared_string(str)); + } +} + +static void f_tokenize(INT32 args) +{ + if(args != 1) + error("Wrong number of arguments to MIME.tokenize()\n"); + else if(sp[-1].type != T_STRING) + error("Wrong type of argument to MIME.tokenize()\n"); + else { + unsigned char *src = (unsigned char *)sp[-1].u.string->str; + struct array *arr; + struct pike_string *str; + INT32 cnt = sp[-1].u.string->len, n = 0, l, e; + char *p; + + while(cnt>0) + switch(rfc822ctype[*src]) { + case CT_SPECIAL: + case CT_RBRACK: + case CT_RPAR: + /* individual special character */ + push_int(*src++); + n++; + --cnt; + break; + case CT_ATOM: + /* atom */ + for(l=1; l<cnt; l++) + if(rfc822ctype[src[l]] != CT_ATOM) + break; + push_string(make_shared_binary_string(src, l)); + n++; + src += l; + cnt -= l; + break; + case CT_QUOTE: + /* quoted-string */ + for(e=0, l=1; l<cnt; l++) + if(src[l] == '"') + break; + else if(src[l] == '\\') { e++; l++; } + str = begin_shared_string( l-e-1 ); + for(p=str->str, e=1; e<l; e++) + *p++=(src[e]=='\\'? src[++e]:src[e]); + push_string(end_shared_string(str)); + n++; + src += l+1; + cnt -= l+1; + break; + case CT_LBRACK: + /* domain literal */ + for(e=0, l=1; l<cnt; l++) + if(src[l] == ']') + break; + else if(src[l] == '\\') { e++; l++; } + str = begin_shared_string( l-e+1 ); + for(p=str->str, e=0; e<=l; e++) + *p++=(src[e]=='\\'? src[++e]:src[e]); + push_string(end_shared_string(str)); + n++; + src += l+1; + cnt -= l+1; + break; + case CT_LPAR: + /* comment */ + for(e=1, l=1; l<cnt; l++) + if(src[l] == '(') + e++; + else if(src[l] == ')') + if(!--e) + break; + else ; + else if(src[l] == '\\') l++; + src += l+1; + cnt -= l+1; + break; + case CT_WHITE: + /* whitespace */ + src++; + --cnt; + break; + default: + error("invalid character in header field\n"); + } + + arr = aggregate_array(n); + pop_n_elems(1); + push_array(arr); + } +} + +static int check_atom_chars(unsigned char *str, INT32 len) +{ + /* Atoms must contain at least 1 character... */ + if(len<1) return 0; + + while(len--) + if(*str>=0x80 || rfc822ctype[*str]!=CT_ATOM) + return 0; + else + str++; + + return 1; +} + +static void f_quote(INT32 args) +{ + struct svalue *item; + INT32 cnt; + dynamic_buffer buf; + int prev_atom = 0; + + if(args != 1) + error("Wrong number of arguments to MIME.quote()\n"); + else if(sp[-1].type != T_ARRAY) + error("Wrong type of argument to MIME.quote()\n"); + + buf.s.str=NULL; + initialize_buf(&buf); + for(cnt=sp[-1].u.array->size, item=sp[-1].u.array->item; cnt--; item++) { + if(item->type == T_INT) { + /* single special character */ + low_my_putchar( item->u.integer, &buf ); + prev_atom=0; + } else if(item->type != T_STRING) { + toss_buffer(&buf); + error("Wrong type of argument to MIME.quote()\n"); + } else { + struct pike_string *str=item->u.string; + if(prev_atom) + low_my_putchar( ' ', &buf ); + if(check_atom_chars((unsigned char *)str->str, str->len)) { + /* valid atom without quotes... */ + low_my_binary_strcat(str->str, str->len, &buf); + } else { + /* quote string */ + INT32 len=str->len; + char *src=str->str; + low_my_putchar( '"', &buf ); + while(len--) { + if(*src=='"' || *src=='\\' || *src=='\r') + low_my_putchar( '\\', &buf ); + low_my_putchar( *src++, &buf ); + } + low_my_putchar( '"', &buf ); + } + prev_atom=1; + } + } + pop_n_elems(1); + push_string(low_free_buf(&buf)); +} diff --git a/src/modules/MIME/testsuite.in b/src/modules/MIME/testsuite.in new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391