4d7cb11997-03-26Henrik Grubbström (Grubba) /* * $Id: MIME.pmod,v 1.3 1997/03/26 21:11:07 grubba Exp $ * * RFC1521 functionality for Pike * * Marcus Comstedt 1996-1997 */
5565b61997-03-08Marcus Comstedt  class support { inherit "MIME"; string generate_boundary( ) {
9230b41997-03-13Marcus Comstedt  return "'ThIs-RaNdOm-StRiNg-/=_."+random( 1000000000 )+":";
5565b61997-03-08Marcus Comstedt  } string decode( string data, string encoding ) {
9230b41997-03-13Marcus Comstedt  switch (lower_case( encoding || "binary" )) {
5565b61997-03-08Marcus Comstedt  case "base64": return decode_base64( data ); case "quoted-printable": return decode_qp( data ); case "x-uue": return decode_uue( data );
9230b41997-03-13Marcus Comstedt  case "7bit": case "8bit": case "binary":
5565b61997-03-08Marcus Comstedt  return data; default: throw(({ "unknown transfer encoding "+encoding+"\n", backtrace() })); } } string encode( string data, string encoding, void|string filename ) {
9230b41997-03-13Marcus Comstedt  switch (lower_case( encoding || "binary" )) {
5565b61997-03-08Marcus Comstedt  case "base64": return encode_base64( data ); case "quoted-printable": return encode_qp( data ); case "x-uue": return encode_uue( data, filename );
9230b41997-03-13Marcus Comstedt  case "7bit": case "8bit": case "binary":
5565b61997-03-08Marcus Comstedt  return data; default: throw(({ "unknown transfer encoding "+encoding+"\n", backtrace() })); } } array(string) decode_word( string word ) { string charset, encoding, encoded_text;
9230b41997-03-13Marcus Comstedt  if (sscanf( word, "=?%[^][ \t()<>@,;:\"\\/?.=]?%[^][ \t()<>@,;:\"\\/?.=]?%s?=", charset, encoding, encoded_text) == 3 ) { switch (lower_case( encoding )) {
5565b61997-03-08Marcus Comstedt  case "b":
9230b41997-03-13Marcus Comstedt  encoding = "base64"; break;
5565b61997-03-08Marcus Comstedt  case "q":
9230b41997-03-13Marcus Comstedt  encoding = "quoted-printable"; break;
5565b61997-03-08Marcus Comstedt  default:
9230b41997-03-13Marcus Comstedt  throw (({ "invalid rfc1522 encoding "+encoding+"\n", backtrace() }));
5565b61997-03-08Marcus Comstedt  }
9230b41997-03-13Marcus Comstedt  return ({ decode( replace( encoded_text, "_", " " ), encoding ), lower_case( charset ) });
5565b61997-03-08Marcus Comstedt  } else return ({ word, 0 }); } string encode_word( array(string) word, string encoding ) {
9230b41997-03-13Marcus Comstedt  if (!encoding || !word[1])
5565b61997-03-08Marcus Comstedt  return word[0];
9230b41997-03-13Marcus Comstedt  switch (lower_case(encoding)) {
5565b61997-03-08Marcus Comstedt  case "b": case "base64":
9230b41997-03-13Marcus Comstedt  encoding = "base64"; break;
5565b61997-03-08Marcus Comstedt  case "q": case "quoted-printable":
9230b41997-03-13Marcus Comstedt  encoding = "quoted-printable"; break;
5565b61997-03-08Marcus Comstedt  default:
9230b41997-03-13Marcus Comstedt  throw (({ "invalid rfc1522 encoding "+encoding+"\n", backtrace() }));
5565b61997-03-08Marcus Comstedt  } return "=?"+word[1]+"?"+encoding[0..0]+"?"+ replace( encode( word[0], encoding ), ({ "?", "_" }), ({ "=3F", "=5F" }))+"?="; }
9230b41997-03-13Marcus Comstedt  string guess_subtype( string typ )
5565b61997-03-08Marcus Comstedt  {
9230b41997-03-13Marcus Comstedt  switch (typ) {
5565b61997-03-08Marcus Comstedt  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 ) {
9230b41997-03-13Marcus Comstedt  if (data != decoded_data) {
5565b61997-03-08Marcus Comstedt  decoded_data = data; encoded_data = 0; } } string getdata( ) {
9230b41997-03-13Marcus Comstedt  if (encoded_data && !decoded_data) decoded_data = decode( encoded_data, transfer_encoding );
5565b61997-03-08Marcus Comstedt  return decoded_data; } string getencoded( ) {
9230b41997-03-13Marcus Comstedt  if (decoded_data && !encoded_data) encoded_data = encode( decoded_data, transfer_encoding, get_filename() );
5565b61997-03-08Marcus Comstedt  return encoded_data; } void setencoding( string encoding ) { if(encoded_data && !decoded_data) decoded_data = getdata( );
9230b41997-03-13Marcus Comstedt  headers["content-transfer-encoding"] = transfer_encoding = lower_case( encoding );
5565b61997-03-08Marcus Comstedt  encoded_data = 0; } void setparam( string param, string value ) { param = lower_case(param); params[param] = value; switch(param) {
9230b41997-03-13Marcus Comstedt  case "charset": charset = value; break; case "boundary": boundary = value; break;
5565b61997-03-08Marcus Comstedt  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 ) {
9230b41997-03-13Marcus Comstedt  param = lower_case( param );
5565b61997-03-08Marcus Comstedt  disp_params[param] = value;
9230b41997-03-13Marcus Comstedt  switch (param) {
5565b61997-03-08Marcus Comstedt  case "filename":
9230b41997-03-13Marcus Comstedt  if (transfer_encoding != "x-uue")
5565b61997-03-08Marcus Comstedt  break;
9230b41997-03-13Marcus Comstedt  if (encoded_data && !decoded_data)
5565b61997-03-08Marcus Comstedt  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;
9230b41997-03-13Marcus Comstedt  if (dest_type != "string")
5565b61997-03-08Marcus Comstedt  throw(({ "can't cast Message to "+dest_type+"\n", backtrace() })); data = getencoded( );
9230b41997-03-13Marcus Comstedt  if (body_parts) {
5565b61997-03-08Marcus Comstedt 
9230b41997-03-13Marcus Comstedt  if (!boundary) { if (type != "multipart") { type = "multipart"; subtype = "mixed"; } setboundary( generate_boundary( ) );
5565b61997-03-08Marcus Comstedt  } 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);
9230b41997-03-13Marcus Comstedt  return map( indices(headers), lambda(string hname){
5565b61997-03-08Marcus Comstedt  return replace(map(hname/"-", String.capitalize)*"-", "Mime", "MIME")+ ": "+headers[hname];
9230b41997-03-13Marcus Comstedt  } )*"\r\n" + "\r\n\r\n" + data;
5565b61997-03-08Marcus Comstedt  } 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;
9230b41997-03-13Marcus Comstedt  if (message)
5565b61997-03-08Marcus Comstedt  decoded_data = message; else decoded_data = (parts? "This is a multi-part message in MIME format.\r\n": "");
9230b41997-03-13Marcus Comstedt  if (hdrs)
5565b61997-03-08Marcus Comstedt  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))); }
9230b41997-03-13Marcus Comstedt  if (mesgsep<0) {
5565b61997-03-08Marcus Comstedt  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; }
9230b41997-03-13Marcus Comstedt  if (headers["content-type"]) {
5565b61997-03-08Marcus Comstedt  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"]; }
9230b41997-03-13Marcus Comstedt  if (headers["content-disposition"]) {
5565b61997-03-08Marcus Comstedt  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]))
9230b41997-03-13Marcus Comstedt  throw(({ "invalid parameter in Content-Disposition\n", backtrace() }));
5565b61997-03-08Marcus Comstedt  disp_params[ lower_case(p[0]) ] = p[2..]*""; } }
9230b41997-03-13Marcus Comstedt  if (headers["content-transfer-encoding"]) {
5565b61997-03-08Marcus Comstedt  array(string) arr=tokenize(headers["content-transfer-encoding"]);
9230b41997-03-13Marcus Comstedt  if (sizeof(arr)!=1 || !stringp(arr[0])) throw (({ "invalid Content-Transfer-Encoding in message\n", backtrace() }));
5565b61997-03-08Marcus Comstedt  transfer_encoding = lower_case(arr[0]); }
9230b41997-03-13Marcus Comstedt  if (boundary && type=="multipart" && !body_parts &&
5565b61997-03-08Marcus Comstedt  (encoded_data || decoded_data)) { array(string) parts = ("\n"+getdata())/("\n--"+boundary);
9230b41997-03-13Marcus Comstedt  if (parts[-1][0..3]!="--\n" && parts[-1][0..3]!="--\r\n")
5565b61997-03-08Marcus Comstedt  throw(({ "multipart message improperly terminated\n", backtrace() })); encoded_data = 0; decoded_data = parts[0][1..];
9230b41997-03-13Marcus Comstedt  body_parts = map(parts[1..sizeof(parts)-2], lambda(string part){ return object_program(this_object())(part[1..]); });
5565b61997-03-08Marcus Comstedt  } } }