#if defined(ENABLE_WWW)
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <iconv.h>

#include "jamlib/jam.h"
#include "libuuid/uuid.h"

#include "bbs.h"
#include "mail_utils.h"

#define IN 0
#define OUT 1
extern char *aha(char *input);
extern struct bbs_config conf;

static char *www_wordwrap(char *content, int cutoff);
char *www_sanitize(char *inp) {
	int i;
	char *result;
	int len = 0;
	for (i = 0; i < strlen(inp); i++) {
		if ((inp[i] == '<') || (inp[i] == '>')) {
			len += 4;
		} else if (inp[i] == '&') {
			len += 5;
		} else {
			len++;
		}
	}

	result = (char *)malloz(len + 1);
	len = 0;
	for (i = 0; i < strlen(inp); i++) {
		if (inp[i] == '<') {
			result[len++] = '&';
			result[len++] = 'l';
			result[len++] = 't';
			result[len++] = ';';
		} else if (inp[i] == '>') {
			result[len++] = '&';
			result[len++] = 'g';
			result[len++] = 't';
			result[len++] = ';';
		} else if (inp[i] == '&') {
			result[len++] = '&';
			result[len++] = 'a';
			result[len++] = 'm';
			result[len++] = 'p';
			result[len++] = ';';
		} else {
			result[len++] = inp[i];
		}
	}

	return result;
}

char *www_msgs_arealist(struct user_record *user) {
	stralloc page = EMPTY_STRALLOC;

	stralloc_copys(&page, "<div class=\"content-header\"><h2>Message Conferences</h2></div>\n");
	for (size_t i = 0; i < ptr_vector_len(&conf.mail_conferences); i++) {
		struct mail_conference *mc = get_conf(i);
		if (mc->sec_level <= user->sec_level) {
			stralloc_cats(&page, "<div class=\"conference-list-item\">");
			stralloc_cats(&page, mc->name);
			stralloc_cats(&page, "</div>\n");
			for (size_t j = 0; j < ptr_vector_len(&mc->mail_areas); j++) {
				struct mail_area *ma = get_area(i, j);
				if (ma->read_sec_level > user->sec_level) {
					continue;
				}
				stralloc_cats(&page, "<div class=\"area-list-");
				stralloc_cats(&page, (new_messages(user, i, j) > 0) ? "new" : "item");
				stralloc_cats(&page, "\"><a href=\"");
				stralloc_cats(&page, conf.www_url);
				stralloc_cats(&page, "msgs/");
				stralloc_cat_long(&page, i);
				stralloc_append1(&page, '/');
				stralloc_cat_long(&page, j);
				stralloc_cats(&page, "/\">");
				stralloc_cats(&page, ma->name);
				stralloc_cats(&page, "</a></div>\n");
			}
		}
	}
	stralloc_0(&page);
	return page.s;
}

char *www_msgs_messagelist(struct user_record *user, int conference, int area, int skip) {
	struct msg_headers *mhrs;
	stralloc page = EMPTY_STRALLOC;
	char buffer[4096];
	int i;
	struct tm msg_date;
	time_t date;
	s_JamBase *jb;
	s_JamLastRead jlr;
	int skip_f;
	int skip_t;
	char *to;
	char *from;
	char *subject;

	if (conference < 0 || conference >= ptr_vector_len(&conf.mail_conferences))
		return NULL;
	struct mail_conference *mc = get_conf(conference);
	if (area < 0 || area >= ptr_vector_len(&mc->mail_areas))
		return NULL;
	struct mail_area *ma = get_area(conference, area);

	stralloc_copys(&page, "<div class=\"content-header\"><h2>");
	stralloc_cats(&page, mc->name);
	stralloc_cats(&page, " - ");
	stralloc_cats(&page, ma->name);
	stralloc_cats(&page, "</h2></div>\n");

	if (ma->type != TYPE_NETMAIL_AREA) {
		stralloc_cats(&page, "<div class=\"button\"><a href=\"");
		stralloc_cats(&page, conf.www_url);
		stralloc_cats(&page, "msgs/new/");
		stralloc_cat_long(&page, conference);
		stralloc_append1(&page, '/');
		stralloc_cat_long(&page, area);
		stralloc_cats(&page, "\">New Message</a></div>\n");
	}
	mhrs = read_message_headers(conference, area, user, 0);

	if (mhrs == NULL) {
		stralloc_cats(&page, "<h3>No Messages</h3>\n");
	}

	stralloc_cats(&page, "<div class=\"div-table\">\n");

	jb = open_jam_base(ma->path);
	if (!jb) {
		free(page.s);
		free_message_headers(mhrs);
		return NULL;
	}
	if (JAM_ReadLastRead(jb, user->id, &jlr) == JAM_NO_USER) {
		jlr.LastReadMsg = 0;
		jlr.HighReadMsg = 0;
	}
	JAM_CloseMB(jb);
	free(jb);
	skip_f = mhrs->msg_count - skip;
	skip_t = mhrs->msg_count - skip - 50;
	if (skip_t < 0) {
		skip_t = 0;
	}

	for (i = skip_f - 1; i >= skip_t; i--) {
		date = (time_t)mhrs->msgs[i]->msg_h->DateWritten;
		gmtime_r(&date, &msg_date);
		to = www_sanitize(mhrs->msgs[i]->to);
		from = www_sanitize(mhrs->msgs[i]->from);
		subject = www_sanitize(mhrs->msgs[i]->subject);
		if (msgbase_is_flagged(user, conference, area, mhrs->msgs[i]->msg_h->MsgNum)) {
			if (conf.date_style == 1) {
				snprintf(buffer, sizeof buffer, "<div class=\"msg-summary-flag\"><div class=\"msg-summary-id\">%d</div><div class=\"msg-summary-subject\"><a href=\"%smsgs/%d/%d/%d\">%s</a></div><div class=\"msg-summary-from\">%s</div><div class=\"msg-summary-to\">%s</div><div class=\"msg-summary-date\">%.2d:%.2d %.2d-%.2d-%.2d</div></div>\n", mhrs->msgs[i]->msg_no + 1, conf.www_url, conference, area, mhrs->msgs[i]->msg_h->MsgNum, subject, from, to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
			} else {
				snprintf(buffer, sizeof buffer, "<div class=\"msg-summary-flag\"><div class=\"msg-summary-id\">%d</div><div class=\"msg-summary-subject\"><a href=\"%smsgs/%d/%d/%d\">%s</a></div><div class=\"msg-summary-from\">%s</div><div class=\"msg-summary-to\">%s</div><div class=\"msg-summary-date\">%.2d:%.2d %.2d-%.2d-%.2d</div></div>\n", mhrs->msgs[i]->msg_no + 1, conf.www_url, conference, area, mhrs->msgs[i]->msg_h->MsgNum, subject, from, to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mday, msg_date.tm_mon + 1, msg_date.tm_year - 100);
			}
		} else if (mhrs->msgs[i]->msg_h->MsgNum > jlr.HighReadMsg) {
			if (conf.date_style == 1) {
				snprintf(buffer, sizeof buffer, "<div class=\"msg-summary\"><div class=\"msg-summary-id\">%d</div><div class=\"msg-summary-subject\"><a href=\"%smsgs/%d/%d/%d\">%s</a></div><div class=\"msg-summary-from\">%s</div><div class=\"msg-summary-to\">%s</div><div class=\"msg-summary-date\">%.2d:%.2d %.2d-%.2d-%.2d</div></div>\n", mhrs->msgs[i]->msg_no + 1, conf.www_url, conference, area, mhrs->msgs[i]->msg_h->MsgNum, subject, from, to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
			} else {
				snprintf(buffer, sizeof buffer, "<div class=\"msg-summary\"><div class=\"msg-summary-id\">%d</div><div class=\"msg-summary-subject\"><a href=\"%smsgs/%d/%d/%d\">%s</a></div><div class=\"msg-summary-from\">%s</div><div class=\"msg-summary-to\">%s</div><div class=\"msg-summary-date\">%.2d:%.2d %.2d-%.2d-%.2d</div></div>\n", mhrs->msgs[i]->msg_no + 1, conf.www_url, conference, area, mhrs->msgs[i]->msg_h->MsgNum, subject, from, to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mday, msg_date.tm_mon + 1, msg_date.tm_year - 100);
			}
		} else {
			if (conf.date_style == 1) {
				snprintf(buffer, sizeof buffer, "<div class=\"msg-summary-seen\"><div class=\"msg-summary-id\">%d</div><div class=\"msg-summary-subject\"><a href=\"%smsgs/%d/%d/%d\">%s</a></div><div class=\"msg-summary-from\">%s</div><div class=\"msg-summary-to\">%s</div><div class=\"msg-summary-date\">%.2d:%.2d %.2d-%.2d-%.2d</div></div>\n", mhrs->msgs[i]->msg_no + 1, conf.www_url, conference, area, mhrs->msgs[i]->msg_h->MsgNum, subject, from, to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
			} else {
				snprintf(buffer, sizeof buffer, "<div class=\"msg-summary-seen\"><div class=\"msg-summary-id\">%d</div><div class=\"msg-summary-subject\"><a href=\"%smsgs/%d/%d/%d\">%s</a></div><div class=\"msg-summary-from\">%s</div><div class=\"msg-summary-to\">%s</div><div class=\"msg-summary-date\">%.2d:%.2d %.2d-%.2d-%.2d</div></div>\n", mhrs->msgs[i]->msg_no + 1, conf.www_url, conference, area, mhrs->msgs[i]->msg_h->MsgNum, subject, from, to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mday, msg_date.tm_mon + 1, msg_date.tm_year - 100);
			}
		}

		free(to);
		free(from);
		free(subject);

		stralloc_cats(&page, buffer);
	}
	stralloc_cats(&page, "</div>\n");
	if (skip + 50 <= mhrs->msg_count) {
		snprintf(buffer, sizeof buffer,
		         "<div class=\"msg-summary-next\"><a href=\"%smsgs/%d/%d/?skip=%d\">Next</a></div>\n",
		         conf.www_url, conference, area, skip + 50);
		stralloc_cats(&page, buffer);
	}

	if (skip > 0) {
		if (skip - 50 < 0) {
			snprintf(buffer, sizeof buffer, "<div class=\"msg-summary-prev\"><a href=\"%smsgs/%d/%d/\">Prev</a></div>\n", conf.www_url, conference, area);
		} else {
			snprintf(buffer, sizeof buffer, "<div class=\"msg-summary-prev\"><a href=\"%smsgs/%d/%d/?skip=%d\">Prev</a></div>\n", conf.www_url, conference, area, skip - 50);
		}
		stralloc_cats(&page, buffer);
	}
	stralloc_0(&page);

	free_message_headers(mhrs);
	return page.s;
}

char *www_msgs_messageview(struct user_record *user, int conference, int area, int msg) {
	s_JamBase *jb;
	s_JamMsgHeader jmh;
	s_JamSubPacket *jsp;
	s_JamSubfield jsf;
	s_JamLastRead jlr;
	s_JamBaseHeader jbh;

	char *subject = NULL;
	char *from = NULL;
	char *to = NULL;
	char *daddress = NULL;
	char *oaddress = NULL;
	char *msgid = NULL;
	char *replyid = NULL;
	char *body = NULL;
	char *body2 = NULL;
	char *replybody = NULL;
	int z;
	struct tm msg_date;
	time_t date;
	stralloc page = EMPTY_STRALLOC;
	char buffer[4096];
	int chars;
	int i;
	char *from_s;
	char *subject_s;
	char *to_s;
	int l1, l2;
	char *aha_text;
	char *aha_out;
	char *nodename;
	struct fido_addr *nodeno;

	if (conference < 0 || conference >= ptr_vector_len(&conf.mail_conferences))
		return NULL;
	struct mail_conference *mc = get_conf(conference);
	if (area < 0 || area >= ptr_vector_len(&mc->mail_areas))
		return NULL;
	struct mail_area *ma = get_area(conference, area);

	jb = open_jam_base(ma->path);
	if (!jb) {
		return NULL;
	}

	JAM_ReadMBHeader(jb, &jbh);

	memset(&jmh, 0, sizeof(s_JamMsgHeader));
	z = JAM_ReadMsgHeader(jb, msg - 1, &jmh, &jsp);
	if (z != 0) {
		JAM_CloseMB(jb);
		free(jb);
		return NULL;
	}
	if (jmh.Attribute & JAM_MSG_DELETED) {
		JAM_DelSubPacket(jsp);
		JAM_CloseMB(jb);
		free(jb);
		return NULL;
	}

	for (z = 0; z < jsp->NumFields; z++) {
		if (jsp->Fields[z]->LoID == JAMSFLD_SUBJECT) {
			subject = strndup(jsp->Fields[z]->Buffer, jsp->Fields[z]->DatLen);
		} else if (jsp->Fields[z]->LoID == JAMSFLD_SENDERNAME) {
			from = strndup(jsp->Fields[z]->Buffer, jsp->Fields[z]->DatLen);
		} else if (jsp->Fields[z]->LoID == JAMSFLD_RECVRNAME) {
			to = strndup(jsp->Fields[z]->Buffer, jsp->Fields[z]->DatLen);
		} else if (jsp->Fields[z]->LoID == JAMSFLD_DADDRESS) {
			daddress = strndup(jsp->Fields[z]->Buffer, jsp->Fields[z]->DatLen);
		} else if (jsp->Fields[z]->LoID == JAMSFLD_OADDRESS) {
			oaddress = strndup(jsp->Fields[z]->Buffer, jsp->Fields[z]->DatLen);
		} else if (jsp->Fields[z]->LoID == JAMSFLD_MSGID) {
			msgid = strndup(jsp->Fields[z]->Buffer, jsp->Fields[z]->DatLen);
		} else if (jsp->Fields[z]->LoID == JAMSFLD_REPLYID) {
			replyid = strndup(jsp->Fields[z]->Buffer, jsp->Fields[z]->DatLen);
		}
	}
	JAM_DelSubPacket(jsp);

	if (subject == NULL) {
		subject = strdup("(No Subject)");
	}

	if (from == NULL) {
		from = strdup("(No Sender)");
	}

	if (to == NULL) {
		to = strdup("(No Recipient)");
	}

	if (jmh.Attribute & JAM_MSG_PRIVATE) {
		if (!msg_is_to(user, to, daddress, mc->nettype, mc->realnames, conference) &&
		    !msg_is_from(user, from, oaddress, mc->nettype, mc->realnames, conference) &&
		    !msg_is_to(user, to, daddress, mc->nettype, !mc->realnames, conference) &&
		    !msg_is_from(user, from, oaddress, mc->nettype, !mc->realnames, conference)) {
			free(subject);
			free(from);
			free(to);
			free(oaddress);
			free(daddress);
			free(msgid);
			free(replyid);
			JAM_CloseMB(jb);
			free(jb);
			return NULL;
		}
	}
	body = (char *)malloz(jmh.TxtLen + 1);

	JAM_ReadMsgText(jb, jmh.TxtOffset, jmh.TxtLen, (char *)body);

	if (JAM_ReadLastRead(jb, user->id, &jlr) == JAM_NO_USER) {
		jlr.UserCRC = JAM_Crc32(user->loginname, strlen(user->loginname));
		jlr.UserID = user->id;
		jlr.HighReadMsg = msg;
	}

	jlr.LastReadMsg = msg;
	if (jlr.HighReadMsg < msg) {
		jlr.HighReadMsg = msg;
	}

	JAM_WriteLastRead(jb, user->id, &jlr);
	JAM_CloseMB(jb);
	free(jb);

	stralloc_copys(&page, "<div class=\"content-header\"><a href=\"");
	stralloc_cats(&page, conf.www_url);
	stralloc_cats(&page, "msgs/");
	stralloc_cat_long(&page, conference);
	stralloc_append1(&page, '/');
	stralloc_cat_long(&page, area);
	stralloc_cats(&page, "\"><h2>");
	stralloc_cats(&page, mc->name);
	stralloc_cats(&page, " - ");
	stralloc_cats(&page, ma->name);
	stralloc_cats(&page, "</h2></a></div>\n");

	if (msgbase_is_flagged(user, conference, area, msg)) {
		stralloc_cats(&page, "<div class=\"msg-view-header-flagged\">\n");
	} else {
		stralloc_cats(&page, "<div class=\"msg-view-header\">\n");
	}

	subject_s = www_sanitize(subject);
	stralloc_cats(&page, "<div class=\"msg-view-subject\">");
	stralloc_cats(&page, subject_s);
	stralloc_cats(&page, "</div>\n");
	free(subject_s);

	from_s = www_sanitize(from);
	if (ma->type != TYPE_LOCAL_AREA && oaddress != NULL) {
		if (mc->nettype == NETWORK_MAGI) {
			snprintf(buffer, sizeof buffer, "<div class=\"msg-view-from\">From: %s (@%s)</div>\n", from_s, oaddress);
		} else if (mc->nettype == NETWORK_FIDO) {
			nodeno = parse_fido_addr(oaddress);
			if (nodeno != NULL) {
				nodename = nl_get_bbsname(nodeno, mc->domain);
				if (strcmp(nodename, "Unknown") == 0) {
					snprintf(buffer, sizeof buffer, "<div class=\"msg-view-from\">From: %s (%s)</div>\n", from_s, oaddress);
				} else {
					snprintf(buffer, sizeof buffer,
					         "<div class=\"msg-view-from\">From: %s (<span class=\"bbsname\">%s</span> - %s)</div>\n",
					         from_s, nodename, oaddress);
				}
				free(nodename);
				free(nodeno);
			} else {
				snprintf(buffer, sizeof buffer, "<div class=\"msg-view-from\">From: %s (%s)</div>\n", from_s, oaddress);
			}
		} else {
			snprintf(buffer, sizeof buffer, "<div class=\"msg-view-from\">From: %s (%s)</div>\n", from_s, oaddress);
		}
	} else {
		snprintf(buffer, sizeof buffer, "<div class=\"msg-view-from\">From: %s</div>\n", from_s);
	}
	free(from_s);

	stralloc_cats(&page, buffer);

	to_s = www_sanitize(to);
	stralloc_cats(&page, "<div class=\"msg-view-to\">To: ");
	stralloc_cats(&page, to_s);
	stralloc_cats(&page, "</div>\n");
	free(to_s);

	date = (time_t)jmh.DateWritten;
	gmtime_r(&date, &msg_date);
	if (conf.date_style == 1) {
		snprintf(buffer, sizeof buffer, "<div class=\"msg-view-date\">Date: %.2d:%.2d %.2d-%.2d-%.2d</div>\n",
		         msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
	} else {
		snprintf(buffer, sizeof buffer, "<div class=\"msg-view-date\">Date: %.2d:%.2d %.2d-%.2d-%.2d</div>\n",
		         msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mday, msg_date.tm_mon + 1, msg_date.tm_year - 100);
	}

	stralloc_cats(&page, buffer);

	snprintf(buffer, sizeof buffer,
	         "<div class=\"msg-view-options\"><a href=\"%smsgs/flag/%d/%d/%d\"><img src=\"%sstatic/flag.png\" /></a></div>",
	         conf.www_url, conference, area, msg, conf.www_url);
	stralloc_cats(&page, buffer);

	stralloc_cats(&page, "</div>\n");
	stralloc_cats(&page, "<div id=\"msgbody\">\n");

	aha_text = strndup(body, jmh.TxtLen);
	aha_out = aha(aha_text);
	stralloc_cats(&page, aha_out);

	free(aha_out);
	free(aha_text);

	stralloc_cats(&page, "</div>\n");

	stralloc_cats(&page, "<div class=\"msg-reply-form\">\n");
	if (ma->write_sec_level <= user->sec_level && ma->type != TYPE_NETMAIL_AREA) {
		stralloc_cats(&page, "<h3>Reply</h3>\n");
		stralloc_cats(&page, "<form action=\"");
		stralloc_cats(&page, conf.www_url);
		stralloc_cats(&page, "msgs/\" method=\"POST\" enctype=\"application/x-www-form-urlencoded;charset=UTF-8\">\n");

		stralloc_cats(&page, "<input type=\"hidden\" name=\"conference\" value=\"");
		stralloc_cat_long(&page, conference);
		stralloc_cats(&page, "\" />\n");

		stralloc_cats(&page, "<input type=\"hidden\" name=\"area\" value=\"");
		stralloc_cat_long(&page, area);
		stralloc_cats(&page, "\" />\n");

		stralloc_cats(&page, "<input type=\"hidden\" name=\"replyid\" value=\"");
		if (msgid == NULL) {
			stralloc_cats(&page, "NULL");
		} else {
			stralloc_cats(&page, msgid);
		}
		stralloc_cats(&page, "\" />\n");

		stralloc_cats(&page, "To : <input type=\"text\" name=\"recipient\" value=\"");
		stralloc_cats(&page, from);
		stralloc_cats(&page, "\" /><br />\n");

		stralloc_cats(&page, "Subject : <input type=\"text\" name=\"subject\" value=\"");
		if (strncasecmp(subject, "re:", 3) != 0) {
			stralloc_cats(&page, "RE: ");
		}
		stralloc_cats(&page, subject);
		stralloc_cats(&page, "\" /><br />\n");

		stralloc_cats(&page, "<textarea name=\"body\" rows=\"25\" cols=\"79\" wrap=\"soft\" id=\"replybody\">");

		stralloc_append1(&page, ' ');
		stralloc_append1(&page, from[0]);
		stralloc_cats(&page, "> ");

		replybody = (char *)malloz(strlen(body) + 1);

		l2 = 0;
		for (l1 = 0; l1 < strlen(body); l1++) {
			if (body[l1] == '\e' && body[l1 + 1] == '[') {
				while (strchr("ABCDEFGHIGJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", body[l1]) == NULL && l1 < strlen(body))
					l1++;
			} else {
				replybody[l2++] = body[l1];
				replybody[l2] = '\0';
			}
		}

		free(body);
		chars = 0;
		body2 = www_wordwrap(replybody, 69);

		if (body2 == NULL) {
			body2 = replybody;
		} else {
			free(replybody);
		}

		for (i = 0; i < strlen(body2); i++) {
			if (body2[i] == '\r') {
				snprintf(buffer, sizeof buffer, "\n %c> ", from[0]);
				chars = 0;
			} else if (chars == 73) {
				snprintf(buffer, sizeof buffer, "\n %c> %c", from[0], body2[i]);
				chars = 1;
			} else {
				snprintf(buffer, sizeof buffer, "%c", body2[i]);
				chars++;
			}
			stralloc_cats(&page, buffer);
		}
		free(body2);
		stralloc_cats(&page, "</textarea>\n<br />");
		stralloc_cats(&page, "<input type=\"submit\" name=\"submit\" value=\"Reply\" />\n<br />");
		stralloc_cats(&page, "</form>\n");
		stralloc_cats(&page, "</div>\n");
	}
	stralloc_0(&page);

	free(subject);
	free(from);
	free(to);
	free(oaddress);
	free(daddress);
	free(msgid);
	free(replyid);

	return page.s;
}

static char *www_wordwrap(char *content, int cutoff) {
	int len = strlen(content);
	int i;
	int line_count = 0;
	char *last_space = NULL;
	char *ret;
	int at = 0;
	int extra = 0;
	int quote_line = 0;
	int z;

	for (i = 0; i < len; i++) {
		if (content[i] == '\n') {
			continue;
		}
		content[at++] = content[i];
	}

	content[at] = '\0';
	at = 0;
	len = strlen(content);

	for (i = 0; i < len - 1; i++) {
		if (content[i] == '>' && line_count < 4) {
			quote_line = 1;
		}

		if (content[i] == '\r' && content[i + 1] != '\r') {
			if (content[i + 1] == ' ' && quote_line != 1) {
				content[at++] = '\r';
				line_count = 0;
				quote_line = 0;
			} else if (quote_line != 1) {
				for (z = i + 1; content[z] != ' ' && z < len; z++)
					;
				if (at > 0 && content[at - 1] != '\r' && content[at - 1] != ' ' && cutoff - line_count < z - i) {
					content[at++] = ' ';
					line_count++;
				} else {
					content[at++] = '\r';
					line_count = 0;
					quote_line = 0;
				}
			} else if (quote_line == 1) {
				content[at++] = '\r';
				line_count = 0;
				quote_line = 0;
			}
		} else if (content[i] == '\r' && content[i + 1] == '\r') {
			content[at++] = '\r';
			content[at++] = '\r';
			line_count = 0;
			quote_line = 0;
			i++;
		} else {
			content[at++] = content[i];
			line_count++;
		}
	}
	content[at++] = content[i];
	content[at] = '\0';

	at = 0;

	len = strlen(content);
	ret = (char *)malloz(len + 1);

	line_count = 0;
	quote_line = 0;

	for (i = 0; i < len; i++) {
		if (content[i] != '\r') {
			ret[at] = content[i];
			if (content[i] == ' ') {
				last_space = &ret[at];
			}
			at++;
			if (content[i] == '>' && line_count < 4) {
				quote_line = 1;
			}
		} else {
			ret[at++] = content[i];
		}
		ret[at] = '\0';

		if (content[i] == '\r') {
			line_count = 0;
			last_space = NULL;
			quote_line = 0;
		} else if (line_count == cutoff) {
			// wrap
			if (quote_line == 1) {
				while (i < len - 1 && content[i + 1] != '\r') {
					i++;
				}
				last_space = NULL;
				line_count = 0;
				quote_line = 0;
			} else if (last_space != NULL) {
				*last_space = '\r';
				line_count = strlen(&last_space[1]);
				last_space = NULL;
				quote_line = 0;
			} else {
				extra++;
				ret = (char *)realloc(ret, strlen(content) + extra + 1);
				if (ret == NULL) {
					return NULL;
				}
				ret[at++] = '\r';
				ret[at] = '\0';
				last_space = NULL;
				line_count = 0;
				quote_line = 0;
			}
		} else {
			line_count++;
		}
	}
	return ret;
}

int www_send_msg(struct user_record *user, char *to, char *subj, int conference, int area, char *replyid, char *body) {
	s_JamBase *jb;
	s_JamMsgHeader jmh;
	s_JamSubPacket *jsp;
	s_JamSubfield jsf;
	s_JamLastRead jlr;
	s_JamBaseHeader jbh;
	int z;
	int sem_fd;
	char *page;
	int max_len;
	int len;
	char buffer[256];
	char *body2;
	char *tagline;
	struct utsname name;
	char *body3;

	iconv_t ic;
	size_t inc;
	size_t ouc;
	size_t sz;
	char *inbuf, *oubuf;

	uuid_t magi_msgid;

	if (subj == NULL || to == NULL || body == NULL) {
		return 0;
	}

	if (conference < 0 || conference >= ptr_vector_len(&conf.mail_conferences))
		return 0;
	struct mail_conference *mc = get_conf(conference);
	if (area < 0 || area >= ptr_vector_len(&mc->mail_areas))
		return 0;
	struct mail_area *ma = get_area(conference, area);

	if (ma->write_sec_level <= user->sec_level && ma->type != TYPE_NETMAIL_AREA) {
		jb = open_jam_base(ma->path);
		if (!jb) {
			return 0;
		}

		JAM_ClearMsgHeader(&jmh);
		jmh.DateWritten = (uint32_t)utc_to_local(time(NULL));
		jmh.Attribute |= JAM_MSG_LOCAL;

		if (mc->realnames == 0) {
			strlcpy(buffer, user->loginname, sizeof buffer);
		} else {
			snprintf(buffer, sizeof buffer, "%s %s", user->firstname, user->lastname);
		}
		jsp = JAM_NewSubPacket();

		jsf.LoID = JAMSFLD_SENDERNAME;
		jsf.HiID = 0;
		jsf.DatLen = strlen(buffer);
		jsf.Buffer = (char *)buffer;
		JAM_PutSubfield(jsp, &jsf);

		jsf.LoID = JAMSFLD_RECVRNAME;
		jsf.HiID = 0;
		jsf.DatLen = strlen(to);
		jsf.Buffer = (char *)to;
		JAM_PutSubfield(jsp, &jsf);

		jsf.LoID = JAMSFLD_SUBJECT;
		jsf.HiID = 0;
		jsf.DatLen = strlen(subj);
		jsf.Buffer = (char *)subj;
		JAM_PutSubfield(jsp, &jsf);

		if (ma->type == TYPE_ECHOMAIL_AREA) {
			jmh.Attribute |= JAM_MSG_TYPEECHO;

			if (mc->nettype == NETWORK_FIDO) {
				if (mc->fidoaddr->point) {
					snprintf(buffer, sizeof buffer, "%d:%d/%d.%d",
					         mc->fidoaddr->zone,
					         mc->fidoaddr->net,
					         mc->fidoaddr->node,
					         mc->fidoaddr->point);
				} else {
					snprintf(buffer, sizeof buffer, "%d:%d/%d",
					         mc->fidoaddr->zone,
					         mc->fidoaddr->net,
					         mc->fidoaddr->node);
				}
				jsf.LoID = JAMSFLD_OADDRESS;
				jsf.HiID = 0;
				jsf.DatLen = strlen(buffer);
				jsf.Buffer = (char *)buffer;
				JAM_PutSubfield(jsp, &jsf);

				snprintf(buffer, sizeof buffer, "%d:%d/%d.%d %08lx",
				         mc->fidoaddr->zone,
				         mc->fidoaddr->net,
				         mc->fidoaddr->node,
				         mc->fidoaddr->point,
				         generate_msgid());

				jsf.LoID = JAMSFLD_MSGID;
				jsf.HiID = 0;
				jsf.DatLen = strlen(buffer);
				jsf.Buffer = (char *)buffer;
				JAM_PutSubfield(jsp, &jsf);
				jmh.MsgIdCRC = JAM_Crc32(buffer, strlen(buffer));

				if (strcasecmp(replyid, "NULL") != 0) {
					jsf.LoID = JAMSFLD_REPLYID;
					jsf.HiID = 0;
					jsf.DatLen = strlen(replyid);
					jsf.Buffer = (char *)replyid;
					JAM_PutSubfield(jsp, &jsf);
					jmh.ReplyCRC = JAM_Crc32(buffer, strlen(replyid));
				}
			} else if (mc->nettype == NETWORK_MAGI) {
				snprintf(buffer, sizeof buffer, "%d", mc->maginode);
				jsf.LoID = JAMSFLD_OADDRESS;
				jsf.HiID = 0;
				jsf.DatLen = strlen(buffer);
				jsf.Buffer = (char *)buffer;
				JAM_PutSubfield(jsp, &jsf);

				uuid_generate(magi_msgid);
				uuid_unparse_lower(magi_msgid, buffer);

				jsf.LoID = JAMSFLD_MSGID;
				jsf.HiID = 0;
				jsf.DatLen = strlen(buffer);
				jsf.Buffer = (char *)buffer;
				JAM_PutSubfield(jsp, &jsf);
				jmh.MsgIdCRC = JAM_Crc32(buffer, strlen(buffer));

				if (strcasecmp(replyid, "NULL") != 0) {
					jsf.LoID = JAMSFLD_REPLYID;
					jsf.HiID = 0;
					jsf.DatLen = strlen(replyid);
					jsf.Buffer = (char *)replyid;
					JAM_PutSubfield(jsp, &jsf);
					jmh.ReplyCRC = JAM_Crc32(buffer, strlen(replyid));
				}
			}
		}
		while (1) {
			z = JAM_LockMB(jb, 100);
			if (z == 0) {
				break;
			} else if (z == JAM_LOCK_FAILED) {
				sleep(1);
			} else {
				JAM_CloseMB(jb);
				free(jb);
				return 0;
			}
		}
		if (z != 0) {
			JAM_CloseMB(jb);
			free(jb);
			return 0;
		}

		tagline = conf.default_tagline;
		if (mc->tagline != NULL) {
			tagline = mc->tagline;
		}

		uname(&name);

		if (mc->nettype == NETWORK_FIDO) {
			if (mc->fidoaddr->point == 0) {
				snprintf(buffer, sizeof buffer, "\r\r--- MagickaBBS v%d.%d%s (%s/%s)\r * Origin: %s (%d:%d/%d)\r",
				         VERSION_MAJOR, VERSION_MINOR, VERSION_STR, name.sysname, name.machine, tagline,
				         mc->fidoaddr->zone, mc->fidoaddr->net, mc->fidoaddr->node);
			} else {
				snprintf(buffer, sizeof buffer, "\r\r--- MagickaBBS v%d.%d%s (%s/%s)\r * Origin: %s (%d:%d/%d.%d)\r",
				         VERSION_MAJOR, VERSION_MINOR, VERSION_STR, name.sysname, name.machine, tagline,
				         mc->fidoaddr->zone, mc->fidoaddr->net, mc->fidoaddr->node, mc->fidoaddr->point);
			}
		} else if (mc->nettype == NETWORK_MAGI) {
			snprintf(buffer, sizeof buffer, "\r\r--- MagickaBBS v%d.%d%s (%s/%s)\r * Origin: %s (@%d)\r",
			         VERSION_MAJOR, VERSION_MINOR, VERSION_STR, name.sysname, name.machine, tagline, mc->maginode);
		} else {
			snprintf(buffer, sizeof buffer, "\r");
		}
		body2 = www_wordwrap(body, 73);
		if (body2 == NULL) {
			JAM_UnlockMB(jb);
			JAM_DelSubPacket(jsp);
			JAM_CloseMB(jb);
			free(jb);
			return 0;
		}
		body3 = str2dup(body2, buffer);
		if (body3 == NULL) {
			free(body2);
			JAM_UnlockMB(jb);
			JAM_DelSubPacket(jsp);
			JAM_CloseMB(jb);
			free(jb);
			return 0;
		}

		free(body2);
		body2 = (char *)malloz(strlen(body3) + 1);

		ic = iconv_open("CP437", "UTF-8");

		inc = strlen(body3);
		ouc = strlen(body3);

		inbuf = body3;
		oubuf = body2;
		sz = iconv(ic, &inbuf, &inc, &oubuf, &ouc);
		free(body3);

		if (JAM_AddMessage(jb, &jmh, jsp, (char *)body2, oubuf - body2)) {
			free(body2);
			JAM_UnlockMB(jb);
			JAM_DelSubPacket(jsp);
			JAM_CloseMB(jb);
			free(jb);
			iconv_close(ic);
			return 0;
		} else {
			if (ma->type == TYPE_ECHOMAIL_AREA) {
				if (conf.echomail_sem != NULL) {
					sem_fd = open(conf.echomail_sem, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
					close(sem_fd);
				}
			}
		}

		free(body2);

		JAM_UnlockMB(jb);

		JAM_DelSubPacket(jsp);
		JAM_CloseMB(jb);
		free(jb);
		iconv_close(ic);
		return 1;
	}
	return 0;
}

char *www_new_msg(struct user_record *user, int conference, int area) {
	stralloc page = EMPTY_STRALLOC;

	stralloc_copys(&page, "<div class=\"content-header\"><h2>New Message</h2></div>\n");
	stralloc_cats(&page, "<form action=\"");
	stralloc_cats(&page, conf.www_url);
	stralloc_cats(&page, "msgs/\" method=\"POST\" onsubmit=\"return validate()\" "
	                     "enctype=\"application/x-www-form-urlencoded;charset=UTF-8\">\n");
	stralloc_cats(&page, "<input type=\"hidden\" name=\"conference\" value=\"");
	stralloc_cat_long(&page, conference);
	stralloc_cats(&page, "\" />\n");
	stralloc_cats(&page, "<input type=\"hidden\" name=\"area\" value=\"");
	stralloc_cat_long(&page, area);
	stralloc_cats(&page, "\" />\n");
	stralloc_cats(&page, "<input type=\"hidden\" name=\"replyid\" value=\"NULL\" />\n");
	stralloc_cats(&page, "To : <input type=\"text\" name=\"recipient\" value=\"All\" id=\"recipient\"/><br />\n");
	stralloc_cats(&page, "Subject : <input type=\"text\" name=\"subject\" id=\"subject\" /><br />\n");
	stralloc_cats(&page, "<textarea name=\"body\" id=\"body\" rows=\"25\" cols=\"79\" wrap=\"soft\"></textarea>\n<br />");
	stralloc_cats(&page, "<input type=\"submit\" name=\"submit\" value=\"Send\" />\n<br />");
	stralloc_cats(&page, "</form>\n");
	stralloc_0(&page);

	return page.s;
}

#endif
