diff --git a/web/root/sajax-forum/body.xjs b/web/root/sajax-forum/body.xjs
index 474d68a2215cdfc79649ce04bf3e092ea2418d6c..2ecb8b3fb4cfa32acba40f8bb86c1b0b38c69b06 100644
--- a/web/root/sajax-forum/body.xjs
+++ b/web/root/sajax-forum/body.xjs
@@ -20,28 +20,131 @@ var body;
 if((!msg_area.sub[sub_code.toLowerCase()].ismoderated) || (hdr.attr & MSG_VALIDATED)!=0) {
 	if((hdr.attr & (MSG_PRIVATE|MSG_DELETE))==0) {
 		body=msgbase.get_msg_body(true,hdr.offset);
-		body=html_encode(body, true, false, true, true);
 	}
 }
-?><pre><?xjs
 msgbase.close();
 
-body=body.replace(/\r?\n+(<\/span>)?$/,'$1');
+var BBS_formatted=false;
+if(BBS_formatted) {
+	body=html_encode(body, true, false, true, true);
+	body=body.replace(/\r?\n+(<\/span>)?$/,'$1');
 
-// Get the last line
-var body_m=body.match(/\n([^\n]*)$/);
-if(body_m != null) {
-	write(body);
-	body_m[1]=body_m[1].replace(/&[^;]*;/g,".");
-	body_m[1]=body_m[1].replace(/<[^>]*>/g,"");
-	var lenremain=80-body_m[1].length;
-	while(lenremain > 0) {
-		write("&nbsp;");
-		lenremain--;
+	// Get the last line
+	var body_m=body.match(/\n([^\n]*)$/);
+	if(body_m != null) {
+		write(body);
+		body_m[1]=body_m[1].replace(/&[^;]*;/g,".");
+		body_m[1]=body_m[1].replace(/<[^>]*>/g,"");
+		var lenremain=80-body_m[1].length;
+		while(lenremain > 0) {
+			write("&nbsp;");
+			lenremain--;
+		}
+	}
+	else {
+		/* If we couldn't get the last line, add a line of 80 columns */
+		writeln("<pre>"+body+":-(");
+		?>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</pre><?xjs
 	}
 }
 else {
-	/* If we couldn't get the last line, add a line of 80 columns */
-	writeln(body+":-(");
-	?>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</pre><?xjs
+	// Strip CTRL-A
+	body=body.replace(/\1./g,'');
+	// Strip ANSI
+	body=body.replace(/\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/,'');
+	body=body.replace(/\x1b[\x40-\x7e]/,'');
+	// Strip unprintable control chars (NULL, BEL, DEL, ESC)
+	body=body.replace(/[\x00\x07\x1b\x7f]/g,'');
+
+	// Wrap and encode
+	body=word_wrap(body, body.length);
+	body=html_encode(body, true, false, false, false);
+
+	// Magical quoting stuff!
+	/*
+	 * Each /[^ ]{0,3}> / is a quote block
+	 */
+	var lines=body.split(/\r?\n/);
+	var quote_depth=0;
+	var prefixes=new Array();
+	body='';
+	for (l in lines) {
+		var line_prefix='';
+		var m=lines[l].match(/^((?:\s?[^\s]{0,3}&gt;\s?)+)/);
+
+		if(m!=null) {
+			var new_prefixes=m[1].match(/\s?[^\s]{0,3}&gt;\s?/g);
+			var p;
+			var broken=false;
+
+			line=lines[l];
+			
+			// If the new length is smaller than the old one, close the extras
+			for(p=new_prefixes.length; p<prefixes.length; p++) {
+				if(quote_depth > 0) {
+					line_prefix = line_prefix + '</blockquote>';
+					quote_depth--;
+				}
+				else {
+					log("BODY: Depth problem 1");
+				}
+			}
+			for(p in new_prefixes) {
+				// Remove prefix from start of line
+				line=line.substr(new_prefixes[p].length);
+
+				if(prefixes[p]==undefined) {
+					/* New depth */
+					line_prefix = line_prefix + '<blockquote>';
+					quote_depth++;
+				}
+				else if(broken) {
+					line_prefix = line_prefix + '<blockquote>';
+					quote_depth++;
+				}
+				else if(prefixes[p].replace(/^\s*(.*?)\s*$/,"$1") != new_prefixes[p].replace(/^\s*(.*?)\s*$/,"$1")) {
+					// Close all remaining old prefixes and start one new one
+					var o;
+					for(o=p; o<prefixes.length && o<new_prefixes.length; o++) {
+						if(quote_depth > 0) {
+							line_prefix = '</blockquote>'+line_prefix;
+							quote_depth--;
+						}
+						else {
+							log("BODY: Depth problem 2");
+						}
+					}
+					line_prefix = '<blockquote>'+line_prefix;
+					quote_depth++;
+					broken=true;
+				}
+			}
+			prefixes=new_prefixes.slice();
+			line=line_prefix+line;
+		}
+		else {
+			for(p=0; p<prefixes.length; p++) {
+				if(quote_depth > 0) {
+					line_prefix = line_prefix + '</blockquote>';
+					quote_depth--;
+				}
+				else {
+					log("BODY: Depth problem 3");
+				}
+			}
+			prefixes=new Array();
+			line=line_prefix + lines[l];
+		}
+		body = body + line + "\r\n";
+	}
+	if(quote_depth != 0) {
+		log("BODY: Depth problem 4");
+		for(;quote_depth > 0; quote_depth--) {
+			body += "</blockquote>";
+		}
+	}
+
+	body=body.replace(/\r\n$/,'');
+	body=body.replace(/(\r?\n)/g, "<br>$1");
+	write(body);
 }?>