diff --git a/exec/imapservice.js b/exec/imapservice.js
index bd848709c3b1307e6df4baf282cfe8f3d9732930..5309f9e89f78b5bae5ee9bd1454b962e43a0931e 100644
--- a/exec/imapservice.js
+++ b/exec/imapservice.js
@@ -9,6 +9,7 @@
  */
 
 load("sbbsdefs.js");
+load("mime.js");
 const RFC822HEADER = 0xb0;  // from smbdefs.h
 
 var sepchar="|";
@@ -63,15 +64,22 @@ function encode_binary(str)
 	return '{'+str.length+'}\r\n'+str;
 }
 
+function encode_token(str)
+{
+	if(str=='')
+		return(encode_string(str));
+
+	if(str.search(/[\(\)\{ \x00-\x1F\*\%\"\\\]]/)==-1)
+		return str;
+
+	return(encode_string(str));
+}
+
 function encode_string(str)
 {
 	if(str=='')
 		return('""');
 
-	// An Atom
-	//if(str.search(/[\(\)\{ \x00-\x1F\*\%\"\\\]]*/)==-1)
-	//	return str;
-
 	if(str.search(/[\r\n\x80-\xff]/)==-1) {
 		str=str.replace(/[\\\"]/, "\\$1");
 		return '"'+str+'"';
@@ -92,6 +100,18 @@ function get_from(hdr)
 	return(hdr.from+" <"+hdr.from.replace(/ /g,".").toLowerCase()+"@"+hdr.from_net_addr+">");
 }
 
+function dump_obj(obj, name)
+{
+	var i;
+
+	for(i in obj) {
+		if(typeof(obj[i])=='object')
+			dump_obj(obj[i], name+'['+i+']');
+		else
+			log(name+'['+i+']="'+obj[i]+'"');
+	}
+}
+
 function send_fetch_response(msgnum, fmat, uid)
 {
 	var idx;
@@ -107,9 +127,18 @@ function send_fetch_response(msgnum, fmat, uid)
 	var sent_flags=false;
 	var seen_changed=false;
 	var envelope;
+	var mime;
+	var part;
 
 	idx=index.idx[msgnum];
 
+	function get_mime() {
+		if(mime==undefined) {
+			get_rfc822();
+			mime=parse_message(rfc822.header+rfc822.text);
+		}
+	}
+
 	function get_header() {
 		if(hdr == undefined) {
 			hdr=base.get_msg_header(msgnum);
@@ -308,71 +337,136 @@ function send_fetch_response(msgnum, fmat, uid)
 			// We already handled this I hope...
 			if(objtype == undefined)
 				continue;
-			switch(objtype) {
-				case 'BODY[HEADER.FIELDS':
-					tmp='';
-					get_rfc822_header();
-					resp += objtype+" (";
-					for(j in fmat[i]) {
-						resp+=fmat[i][j]+" ";
-						re=new RegExp("^("+fmat[i][j]+":.*)$", "im");
-						m=re.exec(rfc822.header);
-						if(m!=null) {
-							tmp += m[1]+"\r\n";
-						}
-					}
-					tmp += "\r\n";
-					resp=resp.substr(0,resp.length-1)+")] "+encode_binary(tmp)+" ";
-					break;
+			if(objtype.search(/^BODY\[[0-9.]*HEADER\.FIELDS$/)==0) {
+				tmp='';
+				for(j in fmat[i]) {
+					if(part.headers[fmat[i][j].toLowerCase()]!=undefined)
+						tmp += part.headers[fmat[i][j].toLowerCase()];
+				}
+
+				resp += objtype+" ("+fmat[i].join(" ")+")] "+encode_binary(tmp+"\r\n")+" ";
+			}
+			if(objtype.search(/^BODY\[[0-9.]*HEADER\.FIELDS\.NOT$/)==0) {
+				tmp=eval(part.headers.toSource());
+				delete tmp['::'];
+				delete tmp[':mime:'];
+				for(j in fmat[i]) {
+					if(tmp[fmat[i][j].toLowerCase()]!=undefined)
+						delete tmp[fmat[i][j].toLowerCase()];
+				}
+				tmp2='';
+				for(j in tmp)
+					tmp2 += tmp[j];
+
+				resp += objtype+" ("+fmat[i].join(" ")+")] "+encode_binary(tmp2+"\r\n")+" ";
 			}
 			continue;
 		}
 		if(fmat[i].toUpperCase().substr(0,4)=='BODY') {
-			// TODO: Handle BODY* stuff correctly (MIME Decode)
-			switch(fmat[i].toUpperCase()) {
-				case 'BODY[TEXT]':
+			function get_mime_part(fmat) {
+				var m=fmat.match(/^BODY((?:\.PEEK)?)\[([^[\]]*)/i);
+				var specifiers;
+				var i;
+				var tmp;
+				var part_name='';
+
+				part=mime;
+				if(m==null)
+					return(undefined);
+				if(m[1].toUpperCase()!='.PEEK')
 					set_seen_flag();
-					// fall-through
-				case 'BODY.PEEK[TEXT]':
-					get_rfc822_text();
-					resp += fmat[i].replace(/\.PEEK/,"").toUpperCase()+" "+encode_binary(rfc822.text)+" ";
-					break;
-
-				case 'BODY[HEADER]':
-					set_seen_flag();
-					// fall-through
-				case 'BODY.PEEK[HEADER]':
-					get_rfc822_header();
-					resp += fmat[i].replace(/\.PEEK/,"").toUpperCase()+" "+encode_binary(rfc822.header)+" ";
-					break;
-
-				case 'BODY[]':
-					set_seen_flag();
-					// fall-through
-				case 'BODY.PEEK[]':
-					get_rfc822();
-					resp += fmat[i].replace(/\.PEEK/,"").toUpperCase()+" "+encode_binary(rfc822.header+rfc822.text)+" ";
-					break;
-
-				case 'BODY[HEADER.FIELDS':
-					set_seen_flag();
-				case 'BODY.PEEK[HEADER.FIELDS':
-					objtype=fmat[i].replace(/\.PEEK/,"").toUpperCase();
-					break;
+				part_name='BODY['+m[2]+']';
+				specifiers=m[2].split('.');
+				for(i=0; i<specifiers.length; i++) {
+					tmp=parseInt(specifiers[i], 10);
+					if(tmp > 0) {
+						if(part.mime != undefined && part.mime.parts != undefined && part.mime.parts[tmp-1]!=undefined) {
+							part=part.mime.parts[tmp-1];
+						}
+					}
+					else
+						break;
+				}
+				switch(specifiers[i]) {
+					case 'HEADER':
+						if(specifiers[i+1]!=undefined) {
+							objtype='BODY['+specifiers.join('.');
+							return undefined;
+						}
+						else
+							return(part_name+" "+encode_binary(part.headers['::'].join('')+"\r\n")+' ');
+					case 'MIME':
+						return(part_name+" "+encode_binary(part.headers[':mime:'].join('')+"\r\n")+' ');
+					case '':
+						if(specifiers.length==1)
+							return(part_name+" "+encode_binary(part.headers['::'].join('')+'\r\n'+part.text)+' ');
+						// Fall-through
+					case undefined:
+					case 'TEXT':
+						return(part_name+' '+encode_binary(part.text)+' ');
+				}
+			}
 
-				case 'BODYSTRUCTURE':
-					get_rfc822_size();
-					get_rfc822_text();
-					resp += 'BODYSTRUCTURE ("TEXT" "PLAIN" ("CHARSET" "IBM437") NIL NIL "8BIT" '+rfc822.text.length+' '+rfc822.text.split(/\r\n/).length+" NIL NIL NIL) ";
-					break;
+			get_mime();
+
+			if((tmp=get_mime_part(fmat[i].toUpperCase()))==undefined) {
+				switch(fmat[i].toUpperCase()) {
+					case 'BODY':
+					case 'BODYSTRUCTURE':
+						function add_part(mime) {
+							var i;
+							var ret='(';
+
+							if(mime.mime.parts != undefined) {
+								for(i in mime.mime.parts)
+									ret += add_part(mime.mime.parts[i]);
+							}
+							else
+								ret += encode_string(mime.mime.parsed['content-type'].vals[0])+" ";
+
+							ret += encode_string(mime.mime.parsed['content-type'].vals[1])+" ";
+							if(mime.mime.parsed['content-type'].attrs==undefined)
+								ret += 'NIL ';
+							else {
+								ret += '(';
+								for(i in mime.mime.parsed['content-type'].attrs) {
+									ret += encode_string(i)+" ";
+									ret += encode_string(mime.mime.parsed['content-type'].attrs[i])+" ";
+								}
+								ret=ret.replace(/ $/, ") ");
+							}
+							if(mime.mime.parsed['content-id']==undefined)
+								ret += 'NIL ';
+							else
+								ret += encode_string(mime.mime.parsed['content-id'].vals[0])+' ';
+							if(mime.mime.parsed['content-description']==undefined)
+								ret += 'NIL ';
+							else
+								ret += encode_string(mime.mime.parsed['content-description'].vals[0])+' ';
+
+							if(mime.mime.parsed['content-type'].vals[0]!='multipart') {
+								if(mime.mime.parsed['content-transfer-encoding']==undefined)
+									ret += 'NIL ';
+								else
+									ret += encode_string(mime.mime.parsed['content-transfer-encoding'].vals[0])+' ';
+
+								ret += encode_token(mime.text.length.toString())+' ';
+							}
+
+							if(mime.mime.parsed['content-type'].vals[0]=='text' || mime.mime.parsed['content-type'].vals[0]=='message') {
+								i=mime.text.split(/\x0d\x0a/);
+								ret=ret+encode_token(i.length.toString())+' ';
+							}
+							ret=ret.replace(/ $/, ') ');
+							return ret;
+						}
 
-				case 'BODY[1]':
-					set_seen_flag();
-				case 'BODY.PEEK[1]':
-					get_rfc822_text();
-					resp += 'BODY[1] '+encode_binary(rfc822.text)+' ';
-					break;
+						resp += 'BODYSTRUCTURE '+add_part(mime);
+						break;
+				}
 			}
+			else
+				resp += tmp;
 		}
 		else {
 			switch(fmat[i].toUpperCase()) {
@@ -387,7 +481,7 @@ function send_fetch_response(msgnum, fmat, uid)
 					break;
 				case 'INTERNALDATE':
 					get_header();
-					resp += 'INTERNALDATE '+strftime('"%d-%b-%C%y %H:%M:%S ', hdr.when_imported_time)+format('%+05d " ', hdr.when_imported_zone_offset);
+					resp += 'INTERNALDATE '+strftime('"%d-%b-%C%y %H:%M:%S ', hdr.when_imported_time)+format('%+05d" ', hdr.when_imported_zone_offset);
 					break;
 				case 'RFC822.SIZE':
 					get_rfc822_size();