|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pike __REAL_VERSION__ |
inherit ___MIME; |
|
|
|
|
|
protected class StringRange |
{ |
string data; |
int start; |
int end; |
protected void create(string|StringRange s, int start, int end) |
{ |
if (start == end) { |
data = ""; |
this::start = this::end = 0; |
return; |
} |
if (start < 0) start = 0; |
if (end < 0) end = 0; |
if (objectp(s)) { |
start += s->start; |
if (start > s->end) start = s->end; |
end += s->start; |
if (end > s->end) end = s->end; |
s = s->data; |
} |
if ((end - start)*16 < sizeof(s)) { |
s = s[start..end-1]; |
end -= start; |
start = 0; |
} |
data = s; |
this::start = start; |
this::end = end; |
} |
protected int _sizeof() |
{ |
return end-start; |
} |
protected string|StringRange `[..](int low, int ltype, int high, int htype) |
{ |
int len = end - start; |
if (ltype == Pike.INDEX_FROM_END) { |
low = len - (low + 1); |
} |
high += 1; |
if (htype == Pike.INDEX_FROM_END) { |
high = len - high; |
} else if (htype == Pike.OPEN_BOUND) { |
high = len; |
} |
if (low < 0) low = 0; |
if (high < 0) high = 0; |
if (low > len) low = len; |
if (high > len) high = len; |
if (!low && (high == len)) return this_object(); |
if ((high - low) < 65536) return data[start+low..start+high-1]; |
return StringRange(this_object(), low, high); |
} |
protected int `[](int pos) |
{ |
int npos = pos; |
if (npos < 0) { |
npos += end; |
if (npos < start) { |
error("Index out of range [-%d..%d]\n", 1 + end-start, end-start); |
} |
} else { |
npos += start; |
if (npos >= end) { |
error("Index out of range [-%d..%d]\n", 1 + end-start, end-start); |
} |
} |
return data[npos]; |
} |
protected mixed cast(string type) |
{ |
if( type == "string" ) |
return data[start..end-1]; |
return UNDEFINED; |
} |
protected int _search(string frag, int|void pos) |
{ |
if (pos < 0) |
error("Start must be greater or equal to zero.\n"); |
int npos = pos + start; |
if (npos > end) |
error("Start must not be greater than the length of the string.\n"); |
if ((npos + sizeof(frag)) > end) return -1; |
npos = search(data, frag, npos); |
if (npos < 0) return npos; |
if ((npos + sizeof(frag)) > end) return -1; |
return npos - start; |
} |
protected string _sprintf(int c) |
{ |
if (c == 'O') |
return sprintf("StringRange(%d bytes[%d..%d] %O)", |
data && sizeof(data), start, end-1, data && data[..40]); |
return (string)this_object(); |
} |
} |
|
#if (__REAL_VERSION__ < 7.8) || ((__REAL_VERSION__) < 7.9 && (__REAL_BUILD__ < 413)) |
|
|
|
protected int(0..1) has_prefix(string|object s, string prefix) |
{ |
if (!objectp(s)) return predef::has_prefix(s, prefix); |
for(int i = 0; i < sizeof(prefix); i++) { |
if (s[i] != prefix[i]) return 0; |
} |
return 1; |
} |
|
#endif |
|
|
|
|
|
|
|
|
string generate_boundary( ) |
{ |
return "'ThIs-RaNdOm-StRiNg-/=_."+random( 1000000000 )+":"; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string|StringRange decode( string|StringRange data, string encoding ) |
{ |
switch (lower_case( encoding || "binary" )) { |
case "base64": |
return decode_base64( (string)data ); |
case "quoted-printable": |
return decode_qp( (string)data ); |
case "x-uue": |
case "x-uuencode": |
return decode_uue( (string)data ); |
case "7bit": |
case "8bit": |
case "binary": |
return data; |
default: |
error("Unknown transfer encoding %s.\n", encoding); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string encode( string data, string encoding, void|string filename, |
void|int no_linebreaks ) |
{ |
switch (lower_case( encoding || "binary" )) { |
case "base64": |
return encode_base64( data, no_linebreaks ); |
case "quoted-printable": |
return encode_qp( data, no_linebreaks ); |
case "x-uue": |
case "x-uuencode": |
return encode_uue( data, filename ); |
case "7bit": |
case "8bit": |
case "binary": |
return data; |
default: |
error("Unknown transfer encoding %s.\n", encoding); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
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: |
error( "Invalid rfc1522 encoding %s.\n", encoding ); |
} |
return ({ decode( replace( encoded_text, "_", " " ), encoding ), |
lower_case( charset ) }); |
} else |
return ({ word, 0 }); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string encode_word( string|array(string) word, string encoding ) |
{ |
if (stringp(word)) |
return word; |
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: |
error( "Invalid rfc1522 encoding %s.\n", encoding); |
} |
string enc = encode( word[0], encoding, 0, 1 ); |
if (encoding == "quoted-printable") |
enc = replace( enc, ({ "?", "_", "(", ")", "\\", "\"" }), |
({ "=3F", "=5F", "=28", "=29", "=5C", "=22" }) ); |
return "=?"+word[1]+"?"+encoding[0..0]+"?"+ enc +"?="; |
} |
|
protected string remap(array(string) item) |
{ |
if (sizeof(item)>1 && item[1]) |
return Charset.decoder(item[1])->feed(item[0])->drain(); |
else |
return item[0]; |
} |
|
protected array(string) reremap(string word, string|function(string:string) selector, |
string|void replacement,function(string:string)|void repcb) |
{ |
if(max(@values(word))<128) |
return ({ word,0 }); |
string s = stringp(selector)? selector : selector(word); |
return s? |
({ Charset.encoder(s,replacement,repcb)->feed(word)->drain(), s }) : |
({ word,0 }); |
} |
|
|
|
|
|
|
|
|
|
array(array(string)) decode_words_text( string txt ) |
{ |
object r = Regexp("^(.*[ \t\n\r]|)(=\\?[^\1- ?]*\\?[^\1- ?]*\\?" |
"[^\1- ?]*\\?=)(([ \t\n\r]+)(.*)|)$"); |
array a, res = ({}); |
while ((a = r->split(txt))) |
{ |
if(!sizeof(a[2])) a = a[..2]+({"",""}); |
txt = a[0]||""; |
if(!sizeof(res) || sizeof(a[4])) a[4]=a[3]+a[4]; |
array w = decode_word(a[1]); |
if (sizeof(a[4])) |
res = ({ w, ({ a[4], 0 }) }) + res; |
else |
res = ({ w }) + res; |
} |
a = res; |
res = ({}); |
if (sizeof(txt)) res = ({ ({ txt, 0 }) }); |
foreach(a, array(string) word) { |
if (sizeof(res) && res[-1][1] && (res[-1][1] == word[1])) { |
|
|
|
|
res[-1][0] += word[0]; |
} else { |
res += ({ word }); |
} |
} |
return res; |
} |
|
|
|
|
|
|
|
|
|
|
string decode_words_text_remapped( string txt ) |
{ |
return Array.map(decode_words_text(txt), remap)*""; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
array(array(string)|int) decode_words_tokenized( string phrase, int|void flags ) |
{ |
return Array.map(tokenize(phrase, flags), |
lambda(string|int item) { |
return intp(item)? item : decode_word(item); |
}); |
} |
|
|
|
|
|
|
|
|
|
|
array(string|int) decode_words_tokenized_remapped( string phrase, |
int|void flags ) |
{ |
return Array.map(decode_words_tokenized(phrase, flags), |
lambda(array(string)|int item) { |
return intp(item)? item : remap(item); |
}); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
array(array(string|int|array(array(string)))) |
decode_words_tokenized_labled( string phrase, int|void flags ) |
{ |
return Array.map( tokenize_labled( phrase, flags ), |
lambda(array(string|int) item) { |
switch(item[0]) { |
case "encoded-word": |
return ({ "word", @decode_word(item[1]) }); |
case "word": |
return item + ({ 0 }); |
case "comment": |
return ({ "comment", decode_words_text(item[1]) }); |
default: |
return item; |
} |
}); |
} |
|
|
|
|
|
|
array(array(string|int)) |
decode_words_tokenized_labled_remapped(string phrase, int|void flags) |
{ |
return Array.map(decode_words_tokenized_labled(phrase, flags), |
lambda(array(string|int|array(array(string|int))) item) { |
switch(item[0]) { |
case "word": |
return ({ "word", remap(item[1..]) }); |
case "comment": |
return ({ "comment", Array.map(item[1], remap)*"" }); |
default: |
return item; |
} |
}); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
string encode_words_text(array(string|array(string)) phrase, string encoding) |
{ |
phrase = filter(phrase, lambda(string|array(string) w) { |
return stringp(w)? sizeof(w) : |
sizeof(w[0]) || w[1]; |
}); |
array(string) ephrase = map(phrase, encode_word, encoding); |
if(!encoding) return ephrase*""; |
string res=""; |
for(int i=0; i<sizeof(ephrase); i++) |
if(ephrase[i] != (stringp(phrase[i])? phrase[i] : phrase[i][0])) { |
if(sizeof(res) && !(<' ','\t','\n','\r'>)[res[-1]]) |
res += " "; |
res += ephrase[i]; |
if(i+1<sizeof(ephrase) && !(<' ','\t','\n','\r'>)[ephrase[i+1][0]]) |
res += " "; |
} else |
res += ephrase[i]; |
return res; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string encode_words_text_remapped(string text, string encoding, |
string|function(string:string) charset, |
string|void replacement, |
function(string:string)|void repcb) |
{ |
array(array(string)) out = ({}); |
string lastword = ""; |
while(sizeof(text)) { |
sscanf(text, "%[ \t\n\r]%[^ \t\n\r]%s", string ws, string word, text); |
array(string) ww = reremap(word, charset, replacement, repcb); |
if(sizeof(ws)) |
if(!ww[1]) |
ww[0] = ws + ww[0]; |
else if(!sizeof(out)) |
out = ({({ws,0})}); |
else if(!out[-1][1]) |
out[-1][0] += ws; |
else { |
|
word = lastword+ws+word; |
ww = reremap(word, charset, replacement, repcb); |
out = out[..<1]; |
} |
lastword = word; |
out += ({ ww }); |
} |
return encode_words_text(out, encoding); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string encode_words_quoted(array(array(string)|int) phrase, string encoding) |
{ |
return quote(Array.map(phrase, lambda(array(string)|int item) { |
return intp(item)? item : |
encode_word(item, encoding); |
})); |
} |
|
|
|
|
|
|
|
|
|
|
string encode_words_quoted_remapped(array(string|int) phrase, string encoding, |
string|function(string:string) charset, |
string|void replacement, |
function(string:string)|void repcb) |
{ |
return encode_words_quoted(map(phrase, lambda(string|int item) { |
return intp(item)? item : |
reremap(item, charset, |
replacement, repcb); |
}), encoding); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string encode_words_quoted_labled(array(array(string|int|array(string|array(string)))) phrase, string encoding) |
{ |
return |
quote_labled(Array.map(phrase, |
lambda(array(string|int|array(string)) item) { |
switch(item[0]) { |
case "word": |
if(sizeof(item)>2 && item[2]) |
return ({ |
"encoded-word", |
encode_word(item[1..], encoding) }); |
else |
return item; |
case "comment": |
return ({ |
"comment", |
encode_words_text(item[1], encoding) }); |
default: |
return item; |
} |
})); |
} |
|
|
|
|
|
|
string encode_words_quoted_labled_remapped(array(array(string|int)) phrase, |
string encoding, |
string|function(string:string) charset, |
string|void replacement, |
function(string:string)|void repcb) |
{ |
return quote_labled(map(phrase, lambda(array(string|int) item) { |
switch(item[0]) { |
case "word": |
item = item[..0]+reremap(item[1], |
charset, |
replacement, |
repcb); |
if(sizeof(item)>2 && item[2]) |
return ({ |
"encoded-word", |
encode_word(item[1..], encoding) }); |
else |
return item; |
case "comment": |
return ({ |
"comment", |
encode_words_text_remapped(item[1], |
encoding, |
charset, |
replacement, |
repcb) }); |
default: |
return item; |
} |
})); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string guess_subtype( string type ) |
{ |
switch (type) { |
case "text": |
return "plain"; |
case "message": |
return "rfc822"; |
case "multipart": |
return "mixed"; |
} |
return 0; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
array(mapping(string:string|array(string))|string|StringRange) |
parse_headers(string|StringRange message, void|int(1..1) use_multiple) |
{ |
string head, header, hname, hcontents; |
string|StringRange body; |
int mesgsep; |
if (has_prefix(message, "\r\n") || has_prefix(message, "\n")) { |
|
return ({ ([]), message[1 + (message[0] == '\r')..] }); |
} else { |
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 = (string)message; |
body = ""; |
} else if (mesgsep) { |
head = (string)(mesgsep>0? message[..mesgsep-1]:""); |
body = message[mesgsep+(message[mesgsep]=='\r'? 4:2)..]; |
} |
} |
mapping(string:string|array) headers = ([ ]); |
foreach( replace(head, ({"\r", "\n ", "\n\t"}), |
({"", " ", " "}))/"\n", header ) |
{ |
if(4==sscanf(header, "%[!-9;-~]%*[ \t]:%*[ \t]%s", hname, hcontents)) |
{ |
hname = lower_case(hname); |
if (use_multiple) |
headers[hname] += ({hcontents}); |
else |
if(headers[hname]) |
headers[hname] += "\0"+hcontents; |
else |
headers[hname] = hcontents; |
} |
} |
|
if( mesgsep<0 && !sizeof(headers) ) |
return ({ ([]), (string)message }); |
return ({ headers, body }); |
} |
|
|
|
class Message { |
|
import Array; |
|
protected string|StringRange encoded_data; |
protected string|StringRange decoded_data; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mapping(string:string) headers; |
|
|
|
|
|
|
|
|
array(object) body_parts; |
|
|
|
|
|
|
|
|
|
string boundary; |
|
|
|
|
|
|
|
|
|
|
|
string charset; |
|
|
|
|
|
|
|
|
|
|
|
string type; |
|
|
|
|
|
|
|
|
|
|
|
string subtype; |
|
|
|
|
|
|
|
|
|
|
|
|
|
string transfer_encoding; |
|
|
|
|
|
|
|
|
|
|
mapping (string:string) params; |
|
|
|
|
|
|
|
|
string disposition; |
|
|
|
|
|
|
|
mapping (string:string) disp_params; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string get_filename( ) |
{ |
string fn = disp_params["filename"] || params["name"]; |
return fn && decode_words_text_remapped(fn); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
array(string|int) is_partial( ) |
{ |
return (type == "message" && subtype == "partial") && |
({ params["id"], (int)params["number"], (int)(params["total"]||"0") }); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
void setdata( string data ) |
{ |
if (data != decoded_data) { |
decoded_data = data; |
encoded_data = 0; |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void `->data=(string data) |
{ |
setdata(data); |
} |
|
|
|
|
|
|
|
|
|
string getdata( ) |
{ |
if (encoded_data && !decoded_data) |
decoded_data = decode( encoded_data, transfer_encoding ); |
return decoded_data = (string)decoded_data; |
} |
|
string `->data() |
{ |
return getdata(); |
} |
|
|
|
|
|
|
|
|
|
string getencoded( ) |
{ |
if (decoded_data && !encoded_data) |
encoded_data = encode( (string)decoded_data, transfer_encoding, |
get_filename() ); |
return (string)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" && transfer_encoding != "x-uuencode") |
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" && transfer_encoding != "x-uuencode") |
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 ); |
} |
|
|
|
|
|
|
|
|
|
|
|
protected string cast( string dest_type ) |
{ |
string data; |
object body_part; |
|
if (dest_type != "string") |
return UNDEFINED; |
|
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"] = ""+sizeof(data); |
|
return map( indices(headers), |
lambda(string hname){ |
return map(arrayp(headers[hname]) ? headers[hname] : |
headers[hname]/"\0", |
lambda(string header,string hname) { |
return hname+": "+header; |
}, |
replace(map(hname/"-", |
String.capitalize)*"-", |
"Mime","MIME"))*"\r\n"; |
} )*"\r\n" + "\r\n\r\n" + data; |
} |
|
protected string token_to_string(string|int token) |
{ |
return intp(token) ? sprintf("%c", token) : token; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void parse_param(mapping(string:string) params, |
array(string|int) entry, |
string header, |
int|void guess, |
array(string|int)|void entry2) |
{ |
if(sizeof(entry)) { |
if(sizeof(entry)<3 || entry[1]!='=' || !stringp(entry[0])) |
if(guess) { |
if ((sizeof(entry) == 1) && stringp(entry[0])) { |
if (sizeof(entry = (entry[0]/"-")) > 1) { |
|
|
entry = ({ entry[0], '=', entry[1..]*"-" }); |
} |
|
} |
else |
return; |
} else |
error("invalid parameter %O in %s %O (%O)\n", |
entry[0], header, headers[lower_case(header)], guess); |
string param = lower_case(entry[0]); |
string val; |
if (guess) { |
val = map(entry[2..], token_to_string) * ""; |
} else if (sizeof(filter(entry[2..], intp))) { |
error("invalid quoting of parameter %O in %s %O (%O)\n", |
entry[0], header, headers[lower_case(header)], guess); |
} else { |
val = entry[2..]*""; |
} |
|
params[param] = val; |
|
|
|
|
|
|
|
|
|
|
|
|
if ((param == "filename") && guess && entry2 && |
!has_value(val, "/") && !has_value(val[1..], "\\") && |
(sizeof(entry2) >= 3) && (entry2[1] == '=') && |
(lower_case(entry2[0]) == param)) { |
val = map(entry2[2..], token_to_string) * ""; |
if (has_value(val, "\\")) |
params[param] = val; |
} |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void create(void | string|StringRange message, |
void | mapping(string:string|array(string)) hdrs, |
void | array(object) parts, |
void | int guess) |
{ |
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 (message && stringp(message) && (sizeof(message) > 0x100000)) { |
|
|
message = StringRange(message, 0, sizeof(message)); |
} |
if (hdrs || parts) { |
string|array(string) hname; |
decoded_data = message; |
if (hdrs) |
foreach( indices(hdrs), hname ) |
headers[lower_case(hname)] = hdrs[hname]; |
body_parts = parts; |
} else if (message) |
[ headers, encoded_data ] = parse_headers(message); |
|
if (headers["content-type"]) { |
array(array(string|int)) arr = |
tokenize(headers["content-type"]) / ({';'}); |
array(string|int) p; |
if (guess && sizeof(arr[0]) > 3) { |
|
|
|
|
arr[0] = arr[0][..2]; |
} |
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 if(!guess) |
error("invalid Content-Type %O\n", headers["content-type"]); |
else |
arr = ({ ({ "application", '/', "octet-stream" }) }) + arr[1..]; |
type = lower_case(arr[0][0]); |
subtype = lower_case(arr[0][2]); |
foreach( arr[1..], p ) |
parse_param(params, p, "Content-Type", guess); |
charset = lower_case(params["charset"] || charset); |
boundary = params["boundary"]; |
} |
if (headers["content-disposition"]) { |
array(array(string|int)) arr; |
array(array(string|int)) arr2; |
mixed err = catch { |
arr = tokenize(headers["content-disposition"]) / ({';'}); |
}; |
mixed err2 = catch { |
arr2 = tokenize(headers["content-disposition"], |
MIME.TOKENIZE_KEEP_ESCAPES) / ({';'}); |
}; |
if (err) { |
if (!guess || err2) throw(err); |
|
arr = arr2; |
arr2 = 0; |
} |
|
array(string|int) p; |
if(sizeof(arr[0])!=1 || !stringp(arr[0][0])) |
{ |
if(!guess) |
error("invalid Content-Disposition in message\n"); |
} else |
{ |
disposition = lower_case(arr[0][0]); |
foreach( arr[1..]; int i; p ) |
parse_param(disp_params, p, "Content-Disposition", guess, |
arr2 && ((i+1) < sizeof(arr2)) && arr2[i+1]); |
} |
} |
if (headers["content-transfer-encoding"]) { |
array(string) arr=tokenize(headers["content-transfer-encoding"]); |
if(sizeof(arr)!=1 || !stringp(arr[0])) |
{ |
if(!guess) |
error("invalid Content-Transfer-Encoding %O\n", |
headers["content-transfer-encoding"]); |
} else |
transfer_encoding = lower_case(arr[0]); |
} |
if (boundary && type=="multipart" && !body_parts && |
(encoded_data || decoded_data)) { |
|
string|StringRange data = decoded_data || getdata(); |
string separator = "--" + boundary; |
array(string) parts = ({}); |
int start = 0; |
int found = 0; |
encoded_data = 0; |
decoded_data = 0; |
while ((found = search(data, separator, found)) != -1) { |
if (found) { |
if (data[found-1] != '\n') { |
found += sizeof(separator); |
continue; |
} |
string part; |
|
|
if ((found > 1) && (data[found - 2] == '\r')) { |
part = data[start..found-3]; |
} else { |
part = data[start..found-2]; |
} |
if (start) { |
parts += ({ part }); |
} else { |
decoded_data = part; |
} |
} else { |
decoded_data = ""; |
} |
|
|
found += sizeof(separator); |
string terminator = data[found..found+1]; |
if (terminator == "--") { |
found += 2; |
} else { |
terminator = 0; |
} |
while ((found < sizeof(data)) && |
((data[found] == ' ') || (data[found] == '\t'))) { |
found++; |
} |
if ((found < sizeof(data)) && (data[found] == '\n')) { |
found++; |
} else if ((found < sizeof(data)) && |
(data[found..found+1] == "\r\n")) { |
found += 2; |
} else if (!guess && !terminator) { |
error("newline missing after multipart boundary\n"); |
} |
|
start = found; |
if (terminator) break; |
} |
string epilogue = data[start..]; |
if (!decoded_data) { |
if (guess) { |
decoded_data = epilogue; |
epilogue = ""; |
} else |
error("boundary missing from multipart-body\n"); |
} |
if ((epilogue != "") && !guess) { |
error("multipart message improperly terminated (%O%s)\n", |
epilogue[..200], |
sizeof(epilogue) > 201 ? "[...]" : ""); |
} |
body_parts = map(parts, this_program, 0, 0, guess); |
} |
if((hdrs || parts) && !decoded_data) { |
decoded_data = (parts? |
"This is a multi-part message in MIME format.\r\n": |
""); |
} |
} |
|
protected string _sprintf(int c) |
{ |
if (c == 'O') |
return sprintf("Message(%O)", disp_params); |
return (string)this_object(); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int|object reconstruct_partial(array(object) collection) |
{ |
int got = 0, maxgot = 0, top = sizeof(collection), total = 0; |
mapping(int:object) parts = ([ ]); |
string id; |
|
if(!top) |
return 0; |
|
if(!(id = (collection[0]->is_partial()||({0}))[0])) |
return 0; |
|
foreach(collection, object m) { |
array(int|string) p = m->is_partial(); |
if(!(p && p[0] == id)) |
return 0; |
if((!total || p[1]==p[2]) && p[2]) |
total = p[2]; |
if(p[1]>maxgot) |
maxgot = p[1]; |
if(p[1]>0 && !parts[p[1]]) { |
parts[p[1]] = m; |
got++; |
} |
} |
|
if(!total) |
return -1; |
|
if(got == total && maxgot == total) { |
mapping(string:string) enclosing_headers = parts[1]->headers; |
|
object reconstructed = |
Message(`+(@Array.map(sort(indices(parts)), |
lambda(int i, mapping(int:object) parts){ |
return parts[i]->getencoded(); |
}, parts))); |
foreach(indices(reconstructed->headers), string h) { |
if(h != "message-id" && h != "encrypted" && h != "mime-version" && |
h != "subject" && (sizeof(h)<8 || h[0..7] != "content-")) |
m_delete(reconstructed->headers, h); |
} |
foreach(indices(enclosing_headers), string h) { |
if(h != "message-id" && h != "encrypted" && h != "mime-version" && |
h != "subject" && (sizeof(h)<8 || h[0..7] != "content-")) |
reconstructed->headers[h] = enclosing_headers[h]; |
} |
return reconstructed; |
} else return (maxgot>total? -1 : total-got); |
} |
|
|