#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <sys/file.h>
#include <fcntl.h>

#include "jamlib/jam.h"
#include "lua/lua.h"
#include "lua/lualib.h"
#include "lua/lauxlib.h"
#include "libuuid/uuid.h"

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

#ifdef __sun
#include "os/sunos.h"
#endif
extern struct bbs_config conf;
extern struct user_record *gUser;
extern int mynode;

time_t utc_to_local(time_t utc) {
	time_t local;
	struct tm date_time;

	localtime_r(&utc, &date_time);

#ifdef __sun
	local = utc + gmtoff(utc);
#else
	local = utc + date_time.tm_gmtoff;
#endif
	return local;
}

s_JamBase *open_jam_base(char *path) {
	int ret;
	s_JamBase *jb;

	ret = JAM_OpenMB((char *)path, &jb);

	if (ret != 0) {
		if (ret == JAM_IO_ERROR) {
			free(jb);
			ret = JAM_CreateMB((char *)path, 1, &jb);
			if (ret != 0) {
				free(jb);
				return NULL;
			}
		} else {
			free(jb);
			dolog("Got %d", ret);
			return NULL;
		}
	}
	return jb;
}

unsigned long generate_msgid() {

	char buffer[1024];
	time_t unixtime;

	unsigned long msgid;
	unsigned long lastid;
	FILE *fptr;

	snprintf(buffer, 1024, "%s/msgserial", conf.bbs_path);

	unixtime = time(NULL);

	fptr = fopen(buffer, "r+");
	if (fptr) {
		flock(fileno(fptr), LOCK_EX);
		fread(&lastid, sizeof(unsigned long), 1, fptr);

		if (unixtime > lastid) {
			lastid = unixtime;
		} else {
			lastid++;
		}
		rewind(fptr);
		fwrite(&lastid, sizeof(unsigned long), 1, fptr);
		flock(fileno(fptr), LOCK_UN);
		fclose(fptr);
	} else {
		fptr = fopen(buffer, "w");
		if (fptr) {
			lastid = unixtime;
			flock(fileno(fptr), LOCK_EX);
			fwrite(&lastid, sizeof(unsigned long), 1, fptr);
			flock(fileno(fptr), LOCK_UN);
			fclose(fptr);
		} else {
			lastid = unixtime;
			dolog("Unable to open message id log");
		}
	}
	snprintf(buffer, sizeof buffer, "%lX", lastid);
	return strtoul(&buffer[strlen(buffer) - 8], NULL, 16);
}

void free_message_headers(struct msg_headers *msghs) {
	int i;

	for (i = 0; i < msghs->msg_count; i++) {
		free(msghs->msgs[i]->msg_h);
		free(msghs->msgs[i]->from);
		free(msghs->msgs[i]->to);
		free(msghs->msgs[i]->subject);
		free(msghs->msgs[i]->oaddress);
		free(msghs->msgs[i]->daddress);
		free(msghs->msgs[i]->msgid);
		free(msghs->msgs[i]->replyid);
		free(msghs->msgs[i]);
	}
	if (msghs->msg_count > 0) {
		free(msghs->msgs);
	}
	free(msghs);
}

int msg_is_to(struct user_record *user, char *addressed_to, char *address, int type, int rn, int msgconf) {
	char *myname;
	if (rn) {
		myname = str3dup(user->firstname, " ", user->lastname);
	} else {
		myname = strdup(user->loginname);
	}
	struct mail_conference *mc = ptr_vector_get(&conf.mail_conferences, msgconf);
	assert(mc != NULL);
	if (type == NETWORK_FIDO && address != NULL) {
		if (strcasecmp(myname, addressed_to) == 0) {
			struct fido_addr *dest = parse_fido_addr(address);
			if (mc->fidoaddr->zone == dest->zone &&
			    mc->fidoaddr->net == dest->net &&
			    mc->fidoaddr->node == dest->node &&
			    mc->fidoaddr->point == dest->point) {
				free(dest);
				free(myname);
				return 1;
			}
			free(dest);
		}
		free(myname);
		return 0;
	} else if (type == NETWORK_MAGI && address != NULL) {
		if (strcasecmp(myname, addressed_to) == 0) {
			int magi_dest = atoi(address);
			if (magi_dest == mc->maginode) {
				free(myname);
				return 1;
			}
		}
		free(myname);
		return 0;
	} else {
		if (strcasecmp(myname, addressed_to) == 0) {
			free(myname);
			return 1;
		}
		free(myname);
		return 0;
	}
}

int msg_is_from(struct user_record *user, char *addressed_from, char *address, int type, int rn, int msgconf) {
	char *myname;

	if (rn) {
		myname = str3dup(user->firstname, " ", user->lastname);
	} else {
		myname = strdup(user->loginname);
	}
	struct mail_conference *mc = ptr_vector_get(&conf.mail_conferences, msgconf);
	assert(mc != NULL);
	if (type == NETWORK_FIDO && address != NULL) {
		if (strcasecmp(myname, addressed_from) == 0) {
			struct fido_addr *orig = parse_fido_addr(address);
			if (mc->fidoaddr->zone == orig->zone &&
			    mc->fidoaddr->net == orig->net &&
			    mc->fidoaddr->node == orig->node &&
			    mc->fidoaddr->point == orig->point) {
				free(orig);
				free(myname);
				return 1;
			}
			free(orig);
		}
		free(myname);
		return 0;
	} else if (type == NETWORK_MAGI && address != NULL) {
		if (strcasecmp(myname, addressed_from) == 0) {
			int magi_orig = atoi(address);
			if (magi_orig == mc->maginode) {
				free(myname);
				return 1;
			}
		}
		free(myname);
		return 0;
	} else {
		if (strcasecmp(myname, addressed_from) == 0) {
			free(myname);
			return 1;
		}
		free(myname);
		return 0;
	}
}

struct msg_headers *read_message_headers(int msgconf, int msgarea, struct user_record *user, int personal) {
	s_JamBase *jb;
	s_JamBaseHeader jbh;
	s_JamMsgHeader jmh;
	s_JamSubPacket *jsp;
	struct jam_msg *jamm;
	struct ptr_vector vec;
	int to_us;
	int i;
	int z;
	int j;
	int k;

	struct fido_addr *dest;
	struct msg_headers *msghs = NULL;
	struct mail_conference *mc = ptr_vector_get(&conf.mail_conferences, msgconf);
	assert(mc != NULL);
	struct mail_area *area = ptr_vector_get(&mc->mail_areas, msgarea);
	assert(area != NULL);

	jb = open_jam_base(area->path);
	if (!jb) {
		dolog("Error opening JAM base.. %s", area->path);
		return NULL;
	}

	JAM_ReadMBHeader(jb, &jbh);

	if (jbh.ActiveMsgs <= 0) {
		JAM_CloseMB(jb);
		free(jb);
		return NULL;
	}
	init_ptr_vector(&vec);
	msghs = (struct msg_headers *)malloz(sizeof(struct msg_headers));
	msghs->msg_count = 0;
	k = 0;
	for (i = 0; k < jbh.ActiveMsgs; i++) {
		memset(&jmh, 0, sizeof(s_JamMsgHeader));
		z = JAM_ReadMsgHeader(jb, i, &jmh, &jsp);
		if (z != 0) {
			dolog("Failed to read msg header: %d Erro %d", z, JAM_Errno(jb));
			continue;
		}

		if (jmh.Attribute & JAM_MSG_DELETED) {
			JAM_DelSubPacket(jsp);
			continue;
		}

		jamm = (struct jam_msg *)malloz(sizeof(struct jam_msg));
		jamm->msg_no = i;
		jamm->msg_h = (s_JamMsgHeader *)malloz(sizeof(s_JamMsgHeader));
		memcpy(jamm->msg_h, &jmh, sizeof(s_JamMsgHeader));
		jamm->from = NULL;
		jamm->to = NULL;
		jamm->subject = NULL;
		jamm->oaddress = NULL;
		jamm->daddress = NULL;
		jamm->msgid = NULL;
		jamm->replyid = NULL;

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

		if (jamm->subject == NULL) {
			jamm->subject = strdup("(No Subject)");
		}
		if (jamm->from == NULL) {
			jamm->from = strdup("(No Sender)");
		}
		if (jamm->to == NULL) {
			jamm->to = strdup("(No Recipient)");
		}
		if (jmh.Attribute & JAM_MSG_PRIVATE) {
			if (!msg_is_to(user, jamm->to, jamm->daddress, mc->nettype, mc->realnames, msgconf) &&
			    !msg_is_from(user, jamm->from, jamm->oaddress, mc->nettype, mc->realnames, msgconf) &&
			    !msg_is_to(user, jamm->to, jamm->daddress, mc->nettype, !mc->realnames, msgconf) &&
			    !msg_is_from(user, jamm->from, jamm->oaddress, mc->nettype, !mc->realnames, msgconf)) {
				free(jamm->subject);
				free(jamm->from);
				free(jamm->to);
				free(jamm->oaddress);
				free(jamm->daddress);
				free(jamm->msgid);
				free(jamm->replyid);
				free(jamm->msg_h);
				free(jamm);
				k++;
				continue;
			}
		} else if (personal) {
			if (!msg_is_to(user, jamm->to, jamm->daddress, mc->nettype, mc->realnames, msgconf) &&
			    !msg_is_to(user, jamm->to, jamm->daddress, mc->nettype, !mc->realnames, msgconf)) {

				free(jamm->subject);
				free(jamm->from);
				free(jamm->to);
				free(jamm->oaddress);
				free(jamm->daddress);
				free(jamm->msgid);
				free(jamm->replyid);
				free(jamm->msg_h);
				free(jamm);
				k++;
				continue;
			}
		}

		ptr_vector_append(&vec, jamm);
		k++;
	}
	msghs->msg_count = ptr_vector_len(&vec);
	msghs->msgs = (struct jam_msg **)consume_ptr_vector(&vec);

	JAM_CloseMB(jb);
	free(jb);
	return msghs;
}

char *external_editor(struct user_record *user, char *to, char *from, char *quote, int qlen, char *qfrom, char *subject, int email, int sig) {
	char c;
	FILE *fptr;
	char *body = NULL;
	char buffer[256];
	int len;
	int totlen;
	char *body2 = NULL;
	char *tagline;
	int i;
	int j;
	struct stat s;
	struct utsname name;

	if (conf.external_editor_cmd != NULL && user->exteditor != 0) {
		if (user->exteditor == 2) {
			s_printf(get_string(85));
			c = s_getc();
		} else {
			c = 'y';
		}
		if (tolower(c) == 'y') {
			snprintf(buffer, sizeof buffer, "%s/node%d", conf.bbs_path, mynode);
			if (stat(buffer, &s) != 0) {
				mkdir(buffer, 0755);
			}
			strlcat(buffer, "/MSGTMP", sizeof buffer);
			if (stat(buffer, &s) == 0) {
				remove(buffer);
			}

			// write msgtemp
			if (quote != NULL) {
				fptr = fopen(buffer, "w");
				for (i = 0; i < qlen; i++) {
					if (quote[i] == '\r') {
						fprintf(fptr, "\r\n");
					} else if (quote[i] == 0x1) {
						continue;
					} else if (quote[i] == '\e' && quote[i + 1] == '[') {
						while (strchr("ABCDEFGHIGJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", quote[i]) == NULL)
							i++;
					} else if (quote[i] != '\n') {
						fprintf(fptr, "%c", quote[i]);
					}
				}
				fclose(fptr);
			}
			snprintf(buffer, sizeof buffer, "%s/node%d/MSGINF", conf.bbs_path, mynode);
			fptr = fopen(buffer, "w");
			fprintf(fptr, "%s\r\n", user->loginname);
			if (qfrom != NULL) {
				fprintf(fptr, "%s\r\n", qfrom);
			} else {
				fprintf(fptr, "%s\r\n", to);
			}
			fprintf(fptr, "%s\r\n", subject);
			fprintf(fptr, "0\r\n");
			if (email == 1) {
				fprintf(fptr, "E-Mail\r\n");
				fprintf(fptr, "YES\r\n");
			} else {
				if (!sig) {
					struct mail_area *area = get_user_area(user);
					fprintf(fptr, "%s\r\n", area->name);
					if (area->type == TYPE_NETMAIL_AREA) {
						fprintf(fptr, "YES\r\n");
					} else {
						fprintf(fptr, "NO\r\n");
					}
				} else {
					fprintf(fptr, "None\r\n");
					fprintf(fptr, "NO\r\n");
				}
			}
			fclose(fptr);

			rundoor(user, conf.external_editor_cmd, conf.external_editor_stdio, conf.external_editor_codepage);

			// readin msgtmp
			snprintf(buffer, sizeof buffer, "%s/node%d/MSGTMP", conf.bbs_path, mynode);
			body = file2str(buffer);

			if (body == NULL) {
				return NULL;
			}

			totlen = strlen(body);

			if (email == 1) {
				tagline = conf.default_tagline;
			} else {
				tagline = conf.default_tagline;
				struct mail_conference *mc = get_user_conf(user);
				if (mc->tagline != NULL) {
					tagline = mc->tagline;
				}
			}

			if (!sig) {
				uname(&name);

				struct mail_conference *mc = get_user_conf(user);
				if (mc->nettype == NETWORK_FIDO && !email) {
					if (mc->fidoaddr->point == 0) {
						snprintf(buffer, sizeof buffer, "\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--- 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 && !email) {
					snprintf(buffer, sizeof buffer, "\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 if (mc->nettype == NETWORK_QWK && !email) {
					snprintf(buffer, sizeof buffer, "\r--- MagickaBBS v%d.%d%s (%s/%s)\r * Origin: %s (%s)\r",
						 VERSION_MAJOR, VERSION_MINOR, VERSION_STR, name.sysname, name.machine, tagline,
						 conf.bwave_name);
				} else {
					snprintf(buffer, sizeof buffer, "\r");
				}
				if (user->autosig) {
					body2 = (char *)malloz(totlen + 3 + strlen(buffer) + strlen(user->signature));
					totlen += strlen(buffer) + strlen(user->signature) + 3;
				} else {
					body2 = (char *)malloz(totlen + 2 + strlen(buffer));
					totlen += strlen(buffer) + 2;
				}
			} else {
				body2 = (char *)malloz(totlen + 1);
			}

			j = 0;
			for (i = 0; i < totlen; i++) {
				if (body[i] == '\n') {
					continue;
				} else if (body[i] == '\0') {
					break;
				}
				body2[j++] = body[i];
				body2[j] = '\0';
			}

			if (!sig) {
				if (user->autosig) {
					strlcat(body2, "\r", totlen + 1);
					strlcat(body2, user->signature, totlen + 1);
				}
				strlcat(body2, buffer, totlen + 1);
			}

			free(body);

			return body2;
		}
	}
	return editor(user, quote, qlen, qfrom, email, sig);
}

char *editor(struct user_record *user, char *quote, int quotelen, char *from, int email, int sig) {
	char buffer[256];
	char linebuffer[80];
	int doquit = 0;
	int i;
	char *msg;
	int size = 0;
	int lineat = 0;
	int qfrom, qto;
	int z;
	char *tagline;
	struct utsname name;
	char next_line_buffer[80];
	struct ptr_vector content;
	struct ptr_vector quotecontent;

	memset(next_line_buffer, 0, 80);
	init_ptr_vector(&quotecontent);
	init_ptr_vector(&content);

	if (quote != NULL) {
		for (i = 0; i < quotelen; i++) {
			if (quote[i] == '\r' || lineat == 67) {
				char prefix[] = {from[0], '>', '\0'};
				ptr_vector_append(&quotecontent, str3dup(prefix, " ", linebuffer));
				lineat = 0;
				linebuffer[0] = '\0';
				if (quote[i] != '\r') {
					i--;
				}
			} else {
				if (quote[i] == 27) {
					while (strchr("ABCDEFGHIGJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", quote[i]) == NULL)
						i++;
					continue;
				}

				linebuffer[lineat++] = quote[i];
				linebuffer[lineat] = '\0';
			}
		}
	}

	s_printf(get_string(86));
	s_printf(get_string(87));

	while (!doquit) {
		s_printf(get_string(88), ptr_vector_len(&content), "");
		strlcpy(linebuffer, next_line_buffer, sizeof(linebuffer));
		s_readstring_inject(linebuffer, 70, next_line_buffer);
		memset(next_line_buffer, 0, 70);

		if (strlen(linebuffer) == 70 && linebuffer[69] != ' ') {
			for (i = strlen(linebuffer) - 1; i > 15; i--) {
				if (linebuffer[i] == ' ') {
					linebuffer[i] = '\0';
					strlcpy(next_line_buffer, &linebuffer[i + 1], sizeof next_line_buffer);
					s_printf("\e[%dD\e[0K", 70 - i);
					break;
				}
			}
		}

		if (linebuffer[0] == '/' && strlen(linebuffer) == 2) {
			if (toupper(linebuffer[1]) == 'S') {
				for (i = 0; i < ptr_vector_len(&content); i++) {
					size += strlen(ptr_vector_get(&content, i)) + 1;
				}
				size++;

				tagline = conf.default_tagline;
				struct mail_conference *mc = get_user_conf(user);
				if (mc->tagline != NULL) {
					tagline = mc->tagline;
				}
				if (!sig) {
					uname(&name);
					if (mc->nettype == NETWORK_FIDO && !email) {
						if (mc->fidoaddr->point == 0) {
							snprintf(buffer, sizeof buffer,
								 "\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--- 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 && !email) {
						snprintf(buffer, sizeof buffer,
						         "\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 if (mc->nettype == NETWORK_QWK && !email) {
						snprintf(buffer, sizeof buffer,
							 "\r--- MagickaBBS v%d.%d%s (%s/%s)\r * Origin: %s (%s)\r",
							 VERSION_MAJOR, VERSION_MINOR, VERSION_STR, name.sysname, name.machine, tagline,
							 conf.bwave_name);
					} else {
						strlcpy(buffer, "\r", sizeof buffer);
					}
					if (user->autosig) {
						size += 3;
						size += strlen(buffer) + strlen(user->signature);
					} else {
						size += 2;
						size += strlen(buffer);
					}
				} else {
					size += 1;
				}

				msg = (char *)malloz(size);
				for (i = 0; i < ptr_vector_len(&content); i++) {
					strlcat(msg, ptr_vector_get(&content, i), size);
					strlcat(msg, "\r", size);
				}
				ptr_vector_apply(&content, free);
				destroy_ptr_vector(&content);

				if (!sig) {
					if (user->autosig) {
						strlcat(msg, "\r", size);
						strlcat(msg, user->signature, size);
					}
					strlcat(msg, buffer, size);
				}

				if (quote != NULL) {
					ptr_vector_apply(&quotecontent, free);
					destroy_ptr_vector(&quotecontent);
				}
				return msg;
			} else if (toupper(linebuffer[1]) == 'A') {
				ptr_vector_apply(&content, free);
				destroy_ptr_vector(&content);
				if (quote != NULL) {
					ptr_vector_apply(&quotecontent, free);
					destroy_ptr_vector(&quotecontent);
				}
				return NULL;
			} else if (toupper(linebuffer[1]) == 'Q') {
				if (quote == NULL) {
					s_printf(get_string(89));
				} else {

					s_printf("\r\n");
					for (i = 0; i < ptr_vector_len(&quotecontent); i++) {
						s_printf(get_string(88), i, ptr_vector_get(&quotecontent, i));
					}

					s_printf(get_string(90));
					s_readstring(buffer, 5);
					qfrom = atoi(buffer);
					s_printf(get_string(91));
					s_readstring(buffer, 5);
					qto = atoi(buffer);
					s_printf("\r\n");

					if (qto > ptr_vector_len(&quotecontent)) {
						qto = ptr_vector_len(&quotecontent);
					}
					if (qfrom < 0) {
						qfrom = 0;
					}
					if (qfrom > qto) {
						s_printf(get_string(92));
					}

					for (i = qfrom; i <= qto; i++) {
						char *copy = strdup(ptr_vector_get(&quotecontent, i));
						ptr_vector_append(&content, copy);
					}

					s_printf(get_string(86));
					s_printf(get_string(87));

					for (i = 0; i < ptr_vector_len(&content); i++) {
						s_printf(get_string(88), i, ptr_vector_get(&content, i));
					}
				}
			} else if (toupper(linebuffer[1]) == 'L') {
				s_printf(get_string(86));
				s_printf(get_string(87));

				for (i = 0; i < ptr_vector_len(&content); i++) {
					s_printf(get_string(88), i, ptr_vector_get(&content, i));
				}
			} else if (linebuffer[1] == '?') {
				s_printf(get_string(93));
				s_printf(get_string(94));
				s_printf(get_string(95));
				s_printf(get_string(96));
				s_printf(get_string(97));
				s_printf(get_string(98));
				s_printf(get_string(99));
				s_printf(get_string(100));
			} else if (toupper(linebuffer[1]) == 'D') {
				s_printf(get_string(101));
				s_readstring(buffer, 6);
				if (strlen(buffer) == 0) {
					s_printf(get_string(39));
				} else {
					z = atoi(buffer);
					if (z < 0 || z >= ptr_vector_len(&content)) {
						s_printf(get_string(39));
					} else {
						free(ptr_vector_del(&content, i));
					}
				}
			} else if (toupper(linebuffer[1]) == 'E') {
				s_printf(get_string(102));
				s_readstring(buffer, 6);
				if (strlen(buffer) == 0) {
					s_printf(get_string(39));
				} else {
					z = atoi(buffer);
					if (z < 0 || z >= ptr_vector_len(&content)) {
						s_printf(get_string(39));
					} else {
						s_printf(get_string(88), z, ptr_vector_get(&content, z));
						s_printf(get_string(103), z);
						s_readstring(linebuffer, 70);
						free(ptr_vector_get(&content, z));
						ptr_vector_put(&content, strdup(linebuffer), z);
					}
				}
			} else if (toupper(linebuffer[1]) == 'I') {
				s_printf(get_string(104));
				s_readstring(buffer, 6);
				if (strlen(buffer) == 0) {
					s_printf(get_string(39));
				} else {
					z = atoi(buffer);
					if (z < 0 || z >= ptr_vector_len(&content)) {
						s_printf(get_string(39));
					} else {
						s_printf(get_string(103), z);
						s_readstring(linebuffer, 70);
						ptr_vector_ins(&content, strdup(linebuffer), z);
					}
				}
			}
		} else {
			ptr_vector_append(&content, strdup(linebuffer));
		}
	}
	if (quote != NULL) {
		ptr_vector_apply(&quotecontent, free);
		destroy_ptr_vector(&quotecontent);
	}
	return NULL;
}

struct character_t {
	char c;
	int fg;
	int bg;
};

void unmangle_ansi(char *body, int len, char **body_out, int *body_len) {
	// count lines
	int line_count = 1;
	int line_at = 1;
	int char_at = 1;
	int fg = 0x07;
	int bg = 0x00;
	int state = 0;
	int save_char_at = 0;
	int save_line_at = 0;
	int params[16];
	int param_count = 0;
	int bold = 0;
	stralloc out = EMPTY_STRALLOC;
	char buffer[1024];
	int buf_at;
	int i, j, k;
	struct character_t ***fake_screen;
	int ansi;
	int tab;

	line_at = 1;
	char_at = 1;

	for (i = 0; i < len; i++) {

		if (state == 0) {
			if (body[i] == 27) {
				state = 1;
				continue;
			} else {
				if (body[i] == '\r') {
					char_at = 1;
					line_at++;
				} else if (body[i] == '\t') {
					char_at += 8;
					while (char_at > 80) {
						line_at++;
						char_at -= 80;
					}
				} else {
					char_at++;
					while (char_at > 80) {
						line_at++;
						char_at -= 80;
					}
				}

				if (line_at > line_count) {
					line_count = line_at;
				}
			}
		} else if (state == 1) {
			if (body[i] == '[') {
				state = 2;
				continue;
			} else {
				state = 0;
				continue;
			}
		} else if (state == 2) {
			param_count = 0;
			for (j = 0; j < 16; j++) {
				params[j] = 0;
			}
			state = 3;
		}
		if (state == 3) {
			if (body[i] == ';') {
				if (param_count < 15) {
					param_count++;
				}
				continue;
			} else if (body[i] >= '0' && body[i] <= '9') {
				if (!param_count) param_count = 1;
				params[param_count - 1] = params[param_count - 1] * 10 + (body[i] - '0');
				continue;
			} else {
				state = 4;
			}
		}

		if (state == 4) {
			switch (body[i]) {
				case 'H':
				case 'f':
					if (params[0]) params[0]--;
					if (params[1]) params[1]--;
					line_at = params[0] + 1;
					char_at = params[1] + 1;

					if (char_at > 80) {
						char_at = 80;
					}

					if (line_at > line_count) {
						line_count = line_at;
					}

					state = 0;
					break;
				case 'A':
					if (param_count > 0) {
						line_at = line_at - params[0];
					} else {
						line_at--;
					}
					if (line_at < 1) {
						line_at = 1;
					}
					state = 0;
					break;
				case 'B':
					if (param_count > 0) {
						line_at = line_at + params[0];
					} else {
						line_at++;
					}
					if (line_at > line_count) {
						line_count = line_at;
					}
					state = 0;
					break;
				case 'C':
					if (param_count > 0) {
						char_at = char_at + params[0];
					} else {
						char_at++;
					}
					if (char_at > 80) {
						char_at = 80;
					}
					state = 0;
					break;
				case 'D':
					if (param_count > 0) {
						char_at = char_at - params[0];
					} else {
						char_at--;
					}
					if (char_at < 1) {
						char_at = 1;
					}
					state = 0;
					break;
				case 's':
					save_char_at = char_at;
					save_line_at = line_at;
					state = 0;
					break;
				case 'u':
					char_at = save_char_at;
					line_at = save_line_at;
					state = 0;
					break;
				default:
					state = 0;
					break;
			}
		}
	}

	fake_screen = (struct character_t ***)malloz(sizeof(struct character_t **) * line_count);
	for (i = 0; i < line_count; i++) {
		fake_screen[i] = (struct character_t **)malloz(sizeof(struct character_t *) * 80);
		for (j = 0; j < 80; j++) {
			fake_screen[i][j] = (struct character_t *)malloz(sizeof(struct character_t));
			fake_screen[i][j]->c = ' ';
			fake_screen[i][j]->fg = fg;
			fake_screen[i][j]->bg = bg;
		}
	}

	line_at = 1;
	char_at = 1;

	for (i = 0; i < len; i++) {

		if (state == 0) {
			if (body[i] == 27) {
				state = 1;
				continue;
			} else {
				if (body[i] == '\r') {
					char_at = 1;
					line_at++;
				} else if (body[i] == '\t') {
					for (tab = 0; tab < 8; tab++) {
						if (line_at > line_count) line_at = line_count;
						fake_screen[line_at - 1][char_at - 1]->c = ' ';
						fake_screen[line_at - 1][char_at - 1]->fg = fg;
						fake_screen[line_at - 1][char_at - 1]->bg = bg;
						char_at++;
						while (char_at > 80) {
							line_at++;
							char_at -= 80;
						}
					}
				} else {
					if (line_at > line_count) line_at = line_count;
					fake_screen[line_at - 1][char_at - 1]->c = body[i];
					fake_screen[line_at - 1][char_at - 1]->fg = fg;
					fake_screen[line_at - 1][char_at - 1]->bg = bg;
					char_at++;
					while (char_at > 80) {
						line_at++;
						char_at -= 80;
					}
				}
			}
		} else if (state == 1) {
			if (body[i] == '[') {
				state = 2;
				continue;
			} else {
				state = 0;
				continue;
			}
		} else if (state == 2) {
			param_count = 0;
			for (j = 0; j < 16; j++) {
				params[j] = 0;
			}
			state = 3;
		}
		if (state == 3) {
			if (body[i] == ';') {
				if (param_count < 15) {
					param_count++;
				}
				continue;
			} else if (body[i] >= '0' && body[i] <= '9') {
				if (!param_count) param_count = 1;
				params[param_count - 1] = params[param_count - 1] * 10 + (body[i] - '0');
				continue;
			} else {
				state = 4;
			}
		}

		if (state == 4) {
			switch (body[i]) {
				case 'H':
				case 'f':
					if (params[0]) params[0]--;
					if (params[1]) params[1]--;
					line_at = params[0] + 1;
					char_at = params[1] + 1;
					state = 0;
					break;
				case 'A':
					if (param_count > 0) {
						line_at = line_at - params[0];
					} else {
						line_at--;
					}
					if (line_at < 1) {
						line_at = 1;
					}
					state = 0;
					break;
				case 'B':
					if (param_count > 0) {
						line_at = line_at + params[0];
					} else {
						line_at++;
					}
					if (line_at > line_count) {
						line_at = line_count;
					}
					state = 0;
					break;
				case 'C':
					if (param_count > 0) {
						char_at = char_at + params[0];
					} else {
						char_at++;
					}
					if (char_at > 80) {
						char_at = 80;
					}
					state = 0;
					break;
				case 'D':
					if (param_count > 0) {
						char_at = char_at - params[0];
					} else {
						char_at--;
					}
					if (char_at < 1) {
						char_at = 1;
					}
					state = 0;
					break;
				case 's':
					save_char_at = char_at;
					save_line_at = line_at;
					state = 0;
					break;
				case 'u':
					char_at = save_char_at;
					line_at = save_line_at;
					state = 0;
					break;
				case 'm':
					for (j = 0; j < param_count; j++) {
						switch (params[j]) {
							case 0:
								fg = 0x07;
								bg = 0x00;
								bold = 0;
								break;
							case 1:
								bold = 1;
								if (fg < 0x08) {
									fg += 0x08;
								}
								break;
							case 2:
								bold = 0;
								if (fg > 0x07) {
									fg -= 0x08;
								}
								break;
							case 30:
								if (bold) {
									fg = 0x08;
								} else {
									fg = 0x00;
								}
								break;
							case 31:
								if (bold) {
									fg = 0x0C;
								} else {
									fg = 0x04;
								}
								break;
							case 32:
								if (bold) {
									fg = 0x0A;
								} else {
									fg = 0x02;
								}
								break;
							case 33:
								if (bold) {
									fg = 0x0E;
								} else {
									fg = 0x06;
								}
								break;
							case 34:
								if (bold) {
									fg = 0x09;
								} else {
									fg = 0x01;
								}
								break;
							case 35:
								if (bold) {
									fg = 0x0D;
								} else {
									fg = 0x05;
								}
								break;
							case 36:
								if (bold) {
									fg = 0x0B;
								} else {
									fg = 0x03;
								}
								break;
							case 37:
								if (bold) {
									fg = 0x0F;
								} else {
									fg = 0x07;
								}
								break;
							case 40:
								bg = 0x00;
								break;
							case 41:
								bg = 0x04;
								break;
							case 42:
								bg = 0x02;
								break;
							case 43:
								bg = 0x06;
								break;
							case 44:
								bg = 0x01;
								break;
							case 45:
								bg = 0x05;
								break;
							case 46:
								bg = 0x03;
								break;
							case 47:
								bg = 0x07;
								break;
						}
					}
					state = 0;
					break;
				case 'K':
					if (params[0] == 0) {
						for (k = char_at - 1; k < 80; k++) {
							fake_screen[line_at - 1][k]->c = ' ';
							fake_screen[line_at - 1][k]->fg = fg;
							fake_screen[line_at - 1][k]->bg = bg;
						}
					} else if (params[0] == 1) {
						for (k = 0; k < char_at; k++) {
							fake_screen[line_at - 1][k]->c = ' ';
							fake_screen[line_at - 1][k]->fg = fg;
							fake_screen[line_at - 1][k]->bg = bg;
						}
					} else if (params[0] == 2) {
						for (k = 0; k < 80; k++) {
							fake_screen[line_at - 1][k]->c = ' ';
							fake_screen[line_at - 1][k]->fg = fg;
							fake_screen[line_at - 1][k]->bg = bg;
						}
					}
					state = 0;
					break;
				case 'J':
					if (params[0] == 0) {
						for (k = char_at - 1; k < 80; k++) {
							fake_screen[line_at - 1][k]->c = ' ';
							fake_screen[line_at - 1][k]->fg = fg;
							fake_screen[line_at - 1][k]->bg = bg;
						}

						for (k = line_at; k < line_count; k++) {
							for (j = 0; j < 80; j++) {
								fake_screen[k][j]->c = ' ';
								fake_screen[k][j]->fg = fg;
								fake_screen[k][j]->bg = bg;
							}
						}
					} else if (params[0] == 1) {
						for (k = 0; k < char_at; k++) {
							fake_screen[line_at - 1][k]->c = ' ';
							fake_screen[line_at - 1][k]->fg = fg;
							fake_screen[line_at - 1][k]->bg = bg;
						}

						for (k = line_at - 2; k >= 0; k--) {
							for (j = 0; j < 80; j++) {
								fake_screen[k][j]->c = ' ';
								fake_screen[k][j]->fg = fg;
								fake_screen[k][j]->bg = bg;
							}
						}
					} else if (params[0] == 2) {
						for (k = 0; k < line_count; k++) {
							for (j = 0; j < 80; j++) {
								fake_screen[k][j]->c = ' ';
								fake_screen[k][j]->fg = fg;
								fake_screen[k][j]->bg = bg;
							}
						}
					}
					state = 0;
					break;
				default:
					// bad ansi
					state = 0;
					break;
			}
		}
	}

	fg = 0x07;
	bg = 0x00;

	for (i = 0; i < line_count; i++) {
		buf_at = 0;
		for (j = 0; j < 79; j++) {
			if (fake_screen[i][j]->fg != fg || fake_screen[i][j]->bg != bg) {
				buffer[buf_at++] = 27;
				buffer[buf_at++] = '[';
				fg = fake_screen[i][j]->fg;
				if (fg < 0x08) {
					buffer[buf_at++] = '0';
					buffer[buf_at++] = ';';
					buffer[buf_at++] = '3';
					switch (fg) {
						case 0x00:
							buffer[buf_at++] = '0';
							break;
						case 0x04:
							buffer[buf_at++] = '1';
							break;
						case 0x02:
							buffer[buf_at++] = '2';
							break;
						case 0x06:
							buffer[buf_at++] = '3';
							break;
						case 0x01:
							buffer[buf_at++] = '4';
							break;
						case 0x05:
							buffer[buf_at++] = '5';
							break;
						case 0x03:
							buffer[buf_at++] = '6';
							break;
						case 0x07:
							buffer[buf_at++] = '7';
							break;
					}
				} else {
					buffer[buf_at++] = '1';
					buffer[buf_at++] = ';';
					buffer[buf_at++] = '3';
					switch (fg) {
						case 0x08:
							buffer[buf_at++] = '0';
							break;
						case 0x0C:
							buffer[buf_at++] = '1';
							break;
						case 0x0A:
							buffer[buf_at++] = '2';
							break;
						case 0x0E:
							buffer[buf_at++] = '3';
							break;
						case 0x09:
							buffer[buf_at++] = '4';
							break;
						case 0x0D:
							buffer[buf_at++] = '5';
							break;
						case 0x0B:
							buffer[buf_at++] = '6';
							break;
						case 0x0F:
							buffer[buf_at++] = '7';
							break;
					}
				}

				bg = fake_screen[i][j]->bg;
				buffer[buf_at++] = ';';
				buffer[buf_at++] = '4';
				switch (bg) {
					case 0x00:
						buffer[buf_at++] = '0';
						break;
					case 0x04:
						buffer[buf_at++] = '1';
						break;
					case 0x02:
						buffer[buf_at++] = '2';
						break;
					case 0x06:
						buffer[buf_at++] = '3';
						break;
					case 0x01:
						buffer[buf_at++] = '4';
						break;
					case 0x05:
						buffer[buf_at++] = '5';
						break;
					case 0x03:
						buffer[buf_at++] = '6';
						break;
					case 0x07:
						buffer[buf_at++] = '7';
						break;
				}
				buffer[buf_at++] = 'm';
			}
			buffer[buf_at++] = fake_screen[i][j]->c;
		}

		while (buf_at > 0 && buffer[buf_at - 1] == ' ') {
			buf_at--;
		}

		buffer[buf_at++] = '\r';
		stralloc_catb(&out, buffer, buf_at);
	}

	for (i = 0; i < line_count; i++) {
		for (j = 0; j < 80; j++) {
			free(fake_screen[i][j]);
		}
		free(fake_screen[i]);
	}
	free(fake_screen);

	while (out.s[out.len - 2] == '\r') {
		out.len--;
	}

	*body_out = out.s;
	*body_len = out.len;
}

int read_message(struct user_record *user, struct msg_headers *msghs, int mailno, int newscan) {
	s_JamBase *jb;
	s_JamMsgHeader jmh;
	s_JamSubPacket *jsp;
	s_JamSubfield jsf;
	s_JamLastRead jlr;

	char buffer[256];
	int z, z2;
	struct tm msg_date;

	char *subject = NULL;
	char *from = NULL;
	char *to = NULL;
	char *body = NULL;
	char *body2 = NULL;
	int lines = 0;
	char c;
	char *replybody;
	struct fido_addr *from_addr = NULL;
	int i, j;
	int doquit = 0;
	int skip_line = 0;
	int chars = 0;
	int ansi;
	int sem_fd;
	int start_line;
	int should_break;
	int position;
	int y;
	uuid_t magi_msgid;
	struct ptr_vector msg_lines;

	init_ptr_vector(&msg_lines);

	struct mail_area *ma = get_user_area(user);
	jb = open_jam_base(ma->path);
	if (!jb) {
		dolog("Error opening JAM base.. %s", ma->path);
		return 0;
	}

	while (doquit == 0) {

		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 = msghs->msgs[mailno]->msg_h->MsgNum;
		}

		jlr.LastReadMsg = msghs->msgs[mailno]->msg_h->MsgNum;
		if (jlr.HighReadMsg < msghs->msgs[mailno]->msg_h->MsgNum) {
			jlr.HighReadMsg = msghs->msgs[mailno]->msg_h->MsgNum;
		}

		struct mail_conference *mc = get_user_conf(user);
		if (msghs->msgs[mailno]->oaddress != NULL && mc->nettype == NETWORK_FIDO) {
			from_addr = parse_fido_addr(msghs->msgs[mailno]->oaddress);
			s_printf(get_string(105), msghs->msgs[mailno]->from, from_addr->zone, from_addr->net, from_addr->node, from_addr->point);
			free(from_addr);
		} else if (msghs->msgs[mailno]->oaddress != NULL && mc->nettype == NETWORK_MAGI) {
			s_printf(get_string(288), msghs->msgs[mailno]->from, atoi(msghs->msgs[mailno]->oaddress));
		} else if (msghs->msgs[mailno]->oaddress != NULL && mc->nettype == NETWORK_QWK) {
			s_printf(get_string(289), msghs->msgs[mailno]->from, msghs->msgs[mailno]->oaddress);
		} else {
			s_printf(get_string(106), msghs->msgs[mailno]->from);
		}
		struct mail_area *ma = get_user_area(user);
		s_printf(get_string(107), msghs->msgs[mailno]->to, mc->name);
		s_printf(get_string(108), msghs->msgs[mailno]->subject, ma->name);
		gmtime_r((time_t *)&msghs->msgs[mailno]->msg_h->DateWritten, &msg_date);
		snprintf(buffer, sizeof buffer, "%s", asctime(&msg_date));
		buffer[strlen(buffer) - 1] = '\0';
		s_printf(get_string(109), buffer, mailno + 1, msghs->msg_count);
		s_printf(get_string(110), (msghs->msgs[mailno]->msg_h->Attribute & JAM_MSG_SENT ? "SENT" : ""),
			 (msgbase_is_flagged(user, user->cur_mail_conf, user->cur_mail_area, msghs->msgs[mailno]->msg_h->MsgNum) ? "FLAGGED" : ""));
		s_printf(get_string(111));

		body = (char *)malloz(msghs->msgs[mailno]->msg_h->TxtLen);

		JAM_ReadMsgText(jb, msghs->msgs[mailno]->msg_h->TxtOffset, msghs->msgs[mailno]->msg_h->TxtLen, (char *)body);
		if (!newscan) {
			JAM_WriteLastRead(jb, user->id, &jlr);
		}

		z2 = msghs->msgs[mailno]->msg_h->TxtLen;

		lines = 0;
		chars = 0;

		body2 = body;
		z = z2;

		unmangle_ansi(body2, z, &body, &z2);
		free(body2);
		start_line = 0;

		// count the number of lines...
		for (z = 0; z < z2; z++) {
			if (body[z] == '\r' || chars == 80) {
				char *msg_line = (char *)malloz(z - start_line + 1);
				ptr_vector_append(&msg_lines, msg_line);
				if (z == start_line) {
					msg_line[0] = '\0';
				} else {
					strlcpy(msg_line, &body[start_line], z - start_line + 1);
					msg_line[z - start_line] = '\0';
				}
				if (body[z] == '\r') {
					start_line = z + 1;
				} else {
					start_line = z;
				}
				chars = 0;
			} else {
				if (body[z] == 27) {
					ansi = z;
					while (strchr("ABCDEFGHIGJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", body[z]) == NULL)
						z++;
					if (body[z] == 'm') {
						// do nothing
					} else {
						y = ansi;
						for (j = z + 1; j < z2; j++) {
							body[y] = body[j];
							y++;
						}
						z2 = z2 - (z2 - y);
						z = ansi - 1;
					}
				} else {
					chars++;
				}
			}
		}

		lines = 0;

		position = 0;
		should_break = 0;

		while (!should_break) {
			s_printf("\e[7;1H");
			for (z = position; z < ptr_vector_len(&msg_lines); z++) {
				s_printf("%s\e[K\r\n", ptr_vector_get(&msg_lines, z));
				if (z - position >= 15) {
					break;
				}
			}

			s_printf(get_string(187));
			if (newscan) {
				s_printf(get_string(234));
			} else {
				s_printf(get_string(186));
			}
			c = s_getc();

			if (tolower(c) == 'r') {
				should_break = 1;
			} else if (tolower(c) == 'q') {
				should_break = 1;
			} else if (tolower(c) == 'j' && newscan == 1) {
				should_break = 1;
			} else if (tolower(c) == 'f') {
				should_break = 1;
			} else if (c == '\e') {
				c = s_getc();
				if (c == 91) {
					c = s_getc();
					if (c == 65) {
						position--;
						if (position < 0) {
							position = 0;
						}
					} else if (c == 66) {
						position++;
						if (position + 15 >= ptr_vector_len(&msg_lines)) {
							position--;
						}
					} else if (c == 67) {
						c = ' ';
						should_break = 1;
					} else if (c == 68) {
						c = 'b';
						should_break = 1;
					}
				}
			}
		}

		if (tolower(c) == 'r') {
			JAM_CloseMB(jb);
			free(jb);
			jb = NULL;
			struct mail_area *ma = get_user_area(user);
			if (user->sec_level < ma->write_sec_level) {
				s_printf(get_string(113));
				jb = open_jam_base(ma->path);
			} else {
				const char *old_subject = msghs->msgs[mailno]->subject;
				if (old_subject != NULL) {
					*buffer = '\0';
					if (strncasecmp(old_subject, "RE:", 3) != 0)
						strlcpy(buffer, "RE: ", sizeof buffer);
					strlcat(buffer, old_subject, sizeof buffer);
				}
				subject = strdup(buffer);

				s_printf(get_string(114));
				s_readstring_inject(buffer, 32, msghs->msgs[mailno]->from);
				to = strdup(buffer);
				s_printf(get_string(115));
				s_readstring_inject(buffer, 64, subject);
				free(subject);
				subject = strdup(buffer);

				s_printf("\r\n");

				if (msghs->msgs[mailno]->from != NULL) {
					strlcpy(buffer, msghs->msgs[mailno]->from, sizeof buffer);
				}
				struct mail_conference *mc = get_user_conf(user);
				if (mc->realnames == 0) {
					from = strdup(user->loginname);
				} else {
					from = str3dup(user->firstname, " ", user->lastname);
				}
				if (ma->type == TYPE_NEWSGROUP_AREA) {
					free(to);
					to = strdup("ALL");
				}
				replybody = external_editor(user, to, from, body, z2, msghs->msgs[mailno]->from, subject, 0, 0);
				if (replybody != NULL) {
					jb = open_jam_base(ma->path);
					if (!jb) {
						dolog("Error opening JAM base.. %s", ma->path);
						free(replybody);
						free(body);
						free(subject);
						free(to);
						free(from);
						ptr_vector_apply(&msg_lines, free);
						destroy_ptr_vector(&msg_lines);
						return 0;
					}

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

					jsp = JAM_NewSubPacket();
					jsf.LoID = JAMSFLD_SENDERNAME;
					jsf.HiID = 0;
					jsf.DatLen = strlen(from);
					jsf.Buffer = (char *)from;
					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(subject);
					jsf.Buffer = (char *)subject;
					JAM_PutSubfield(jsp, &jsf);

					if (ma->type == TYPE_ECHOMAIL_AREA || ma->type == TYPE_NEWSGROUP_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);

							if (msghs->msgs[mailno]->msgid != NULL) {
								strlcpy(buffer, msghs->msgs[mailno]->msgid, sizeof buffer);
								jsf.LoID = JAMSFLD_REPLYID;
								jsf.HiID = 0;
								jsf.DatLen = strlen(buffer);
								jsf.Buffer = (char *)buffer;
								JAM_PutSubfield(jsp, &jsf);
								jmh.ReplyCRC = JAM_Crc32(buffer, strlen(buffer));
							}

						} 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);
							if (msghs->msgs[mailno]->msgid != NULL) {
								strlcpy(buffer, msghs->msgs[mailno]->msgid, sizeof buffer);
								jsf.LoID = JAMSFLD_REPLYID;
								jsf.HiID = 0;
								jsf.DatLen = strlen(buffer);
								jsf.Buffer = (char *)buffer;
								JAM_PutSubfield(jsp, &jsf);
								jmh.ReplyCRC = JAM_Crc32(buffer, strlen(buffer));
							}
						} else if (mc->nettype == NETWORK_QWK) {
							jsf.LoID = JAMSFLD_OADDRESS;
							jsf.HiID = 0;
							jsf.DatLen = strlen(conf.bwave_name);
							jsf.Buffer = (char *)conf.bwave_name;
							JAM_PutSubfield(jsp, &jsf);
						}
					} else if (ma->type == TYPE_NETMAIL_AREA) {
						jmh.Attribute |= JAM_MSG_TYPENET;
						jmh.Attribute |= JAM_MSG_PRIVATE;

						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);
							jmh.MsgIdCRC = JAM_Crc32(buffer, strlen(buffer));
							from_addr = parse_fido_addr(msghs->msgs[mailno]->oaddress);
							if (from_addr != NULL) {
								if (from_addr->point) {
									snprintf(buffer, sizeof buffer, "%d:%d/%d.%d",
										 from_addr->zone, from_addr->net, from_addr->node,
									         from_addr->point);
								} else {
									snprintf(buffer, sizeof buffer, "%d:%d/%d",
										 from_addr->zone, from_addr->net, from_addr->node);
								}
								jsf.LoID = JAMSFLD_DADDRESS;
								jsf.HiID = 0;
								jsf.DatLen = strlen(buffer);
								jsf.Buffer = (char *)buffer;
								JAM_PutSubfield(jsp, &jsf);
								free(from_addr);
							}

							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 (msghs->msgs[mailno]->msgid != NULL) {
								strlcpy(buffer, msghs->msgs[mailno]->msgid, sizeof buffer);
								jsf.LoID = JAMSFLD_REPLYID;
								jsf.HiID = 0;
								jsf.DatLen = strlen(buffer);
								jsf.Buffer = (char *)buffer;
								JAM_PutSubfield(jsp, &jsf);
								jmh.ReplyCRC = JAM_Crc32(buffer, strlen(buffer));
							}
						}
					}

					while (1) {
						z = JAM_LockMB(jb, 100);
						if (z == 0) {
							break;
						} else if (z == JAM_LOCK_FAILED) {
							sleep(1);
						} else {
							free(replybody);
							free(body);
							free(subject);
							free(to);
							free(from);
							dolog("Failed to lock msg base!");
							ptr_vector_apply(&msg_lines, free);
							destroy_ptr_vector(&msg_lines);
							return 0;
						}
					}
					if (JAM_AddMessage(jb, &jmh, jsp, (char *)replybody, strlen(replybody))) {
						dolog("Failed to add message");
					} else {
						if (ma->type == TYPE_NETMAIL_AREA) {
							if (conf.netmail_sem != NULL) {
								sem_fd = open(conf.netmail_sem, O_RDWR | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
								close(sem_fd);
							}
						} else if (ma->type == TYPE_ECHOMAIL_AREA || ma->type == TYPE_NEWSGROUP_AREA) {
							if (conf.echomail_sem != NULL) {
								sem_fd = open(conf.echomail_sem, O_RDWR | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
								close(sem_fd);
							}
						}
					}

					JAM_UnlockMB(jb);

					JAM_DelSubPacket(jsp);
					free(replybody);
					// JAM_CloseMB(jb);
					// doquit = 1;
				} else {
					jb = open_jam_base(ma->path);
				}
			}
			free(body);
			free(from);
			free(to);
			free(subject);
		} else if (tolower(c) == 'j' && newscan == 1) {
			free(body);
			doquit = 1;
		} else if (tolower(c) == 'q') {
			free(body);
			doquit = 2;
		} else if (c == ' ') {
			mailno++;
			if (mailno >= msghs->msg_count) {
				s_printf(get_string(118));
				doquit = 1;
			}
			free(body);
		} else if (tolower(c) == 'b') {
			if (mailno > 0) {
				mailno--;
			}
			free(body);
		} else if (tolower(c) == 'f') {
			msgbase_flag_unflag(user, user->cur_mail_conf, user->cur_mail_area, msghs->msgs[mailno]->msg_h->MsgNum);
			free(body);
		} else {
			free(body);
		}
		ptr_vector_apply(&msg_lines, free);
		destroy_ptr_vector(&msg_lines);
	}

	if (jb != NULL)
		JAM_CloseMB(jb);
	free(jb);

	if (doquit == 2) {
		return 1;
	}

	return 0;
}

int read_new_msgs(struct user_record *user, struct msg_headers *msghs) {
	s_JamBase *jb;
	s_JamLastRead jlr;
	int all_unread;
	int i;
	int k;
	char buffer[7];
	int res;

	// list mail in message base
	if (msghs != NULL && msghs->msg_count > 0) {
		struct mail_area *ma = get_user_area(user);
		jb = open_jam_base(ma->path);
		if (!jb) {
			dolog("Error opening JAM base.. %s", ma->path);
			return 0;
		} else {
			all_unread = 0;
			if (JAM_ReadLastRead(jb, user->id, &jlr) == JAM_NO_USER) {
				jlr.LastReadMsg = 0;
				jlr.HighReadMsg = 0;
				all_unread = 1;
			}
			if (jlr.LastReadMsg == 0 && jlr.HighReadMsg == 0) {
				all_unread = 1;
			}
			JAM_CloseMB(jb);
			free(jb);
			if (all_unread == 0) {
				k = jlr.HighReadMsg;
				for (i = 0; i < msghs->msg_count; i++) {
					if (msghs->msgs[i]->msg_h->MsgNum == k) {
						i += 2;
						break;
					}
					if (msghs->msgs[i]->msg_h->MsgNum > k) {
						i++;
						break;
					}
				}

			} else {
				i = 1;
			}

			if (i > 0 && i <= msghs->msg_count) {
				res = read_message(user, msghs, i - 1, 1);
				s_printf("\r\n");
				return res;
			}
		}
	}
	return 0;
}

void read_mail(struct user_record *user) {
	struct msg_headers *msghs;
	s_JamBase *jb;
	s_JamLastRead jlr;
	int all_unread;
	int i;
	int k;
	char buffer[7];

	s_printf("\r\n");
	// list mail in message base
	msghs = read_message_headers(user->cur_mail_conf, user->cur_mail_area, user, 0);
	if (msghs == NULL)
		return;
	if (msghs->msg_count <= 0) {
		free_message_headers(msghs);
		return;
	}
	struct mail_area *ma = get_user_area(user);
	jb = open_jam_base(ma->path);
	if (!jb) {
		dolog("Error opening JAM base.. %s", ma->path);
		return;
	}
	all_unread = 0;
	if (JAM_ReadLastRead(jb, user->id, &jlr) == JAM_NO_USER) {
		jlr.LastReadMsg = 0;
		jlr.HighReadMsg = 0;
		all_unread = 1;
	} else if (jlr.LastReadMsg == 0 && jlr.HighReadMsg == 0) {
		all_unread = 1;
	}
	JAM_CloseMB(jb);
	free(jb);
	s_printf(get_string(120), msghs->msg_count);

	s_readstring(buffer, 6);

	if (tolower(buffer[0]) == 'n') {
		if (all_unread == 0) {
			k = jlr.HighReadMsg;
			for (i = 0; i < msghs->msg_count; i++) {
				if (msghs->msgs[i]->msg_h->MsgNum == k) {
					break;
				}
			}
			i += 2;
		} else {
			i = 1;
		}
	} else {
		i = atoi(buffer);
	}

	if (i > 0 && i <= msghs->msg_count) {
		read_message(user, msghs, i - 1, 0);
	}
	if (msghs != NULL) {
		free_message_headers(msghs);
	}
}

void post_message(struct user_record *user) {
	char *subject;
	char *from;
	char *to;
	char *msg;
	int closed;
	struct fido_addr *from_addr = NULL;
	char buffer[256];
	char buffer2[256];
	int z;
	int sem_fd;
	char *bbsname;
	uuid_t magi_msgid;

	s_JamBase *jb;
	s_JamMsgHeader jmh;
	s_JamSubPacket *jsp;
	s_JamSubfield jsf;
	s_JamLastRead jlr;

	struct mail_area *ma = get_user_area(user);
	if (user->sec_level < ma->write_sec_level) {
		s_printf(get_string(113));
		return;
	}
	if (ma->type == TYPE_NEWSGROUP_AREA) {
		strlcpy(buffer, "ALL", sizeof buffer);
	} else {
		s_printf(get_string(54));
		s_readstring(buffer, 32);
	}
	if (strlen(buffer) == 0) {
		strlcpy(buffer, "ALL", sizeof(buffer));
	}

	if (ma->type == TYPE_NETMAIL_AREA) {
		s_printf(get_string(121));
		s_readstring(buffer2, 32);
		struct mail_conference *mc = get_user_conf(user);
		if (mc->nettype == NETWORK_FIDO) {
			from_addr = parse_fido_addr(buffer2);
			if (!from_addr) {
				s_printf(get_string(122));
				return;
			} else {
				if (from_addr->zone == 0 && from_addr->net == 0 && from_addr->node == 0 && from_addr->point == 0) {
					free(from_addr);
					s_printf(get_string(122));
					return;
				}
				// TODO: Is this different than `user`?
				struct mail_conference *gmc = get_user_conf(gUser);
				if (gmc->domain != NULL) {
					bbsname = nl_get_bbsname(from_addr, gmc->domain);
				} else {
					bbsname = strdup("Unknown");
				}
				s_printf(get_string(123), from_addr->zone, from_addr->net, from_addr->node, from_addr->point, bbsname);

				free(bbsname);
			}
		}
	}
	to = strdup(buffer);
	s_printf(get_string(56));
	s_readstring(buffer, 25);
	if (strlen(buffer) == 0) {
		s_printf(get_string(39));
		free(to);
		if (from_addr != NULL) {
			free(from_addr);
		}
		return;
	}
	subject = strdup(buffer);

	// post a message
	struct mail_conference *mc = get_user_conf(user);
	if (mc->realnames == 0) {
		from = strdup(user->loginname);
	} else {
		from = str3dup(user->firstname, " ", user->lastname);
	}
	msg = external_editor(user, to, from, NULL, 0, NULL, subject, 0, 0);

	free(from);

	if (msg != NULL) {
		jb = open_jam_base(ma->path);
		if (!jb) {
			dolog("Error opening JAM base.. %s", ma->path);
			free(msg);
			free(to);
			free(subject);
			return;
		}

		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(subject);
		jsf.Buffer = (char *)subject;
		JAM_PutSubfield(jsp, &jsf);

		if (ma->type == TYPE_ECHOMAIL_AREA || ma->type == TYPE_NEWSGROUP_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));
			} 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));

			} else if (mc->nettype == NETWORK_QWK) {
				jsf.LoID = JAMSFLD_OADDRESS;
				jsf.HiID = 0;
				jsf.DatLen = strlen(conf.bwave_name);
				jsf.Buffer = (char *)conf.bwave_name;
				JAM_PutSubfield(jsp, &jsf);
			}
		} else if (ma->type == TYPE_NETMAIL_AREA) {
			jmh.Attribute |= JAM_MSG_TYPENET;
			jmh.Attribute |= JAM_MSG_PRIVATE;
			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);

				if (from_addr != NULL) {
					if (from_addr->point) {
						snprintf(buffer, sizeof buffer, "%d:%d/%d.%d",
							 from_addr->zone, from_addr->net, from_addr->node,
						         from_addr->point);
					} else {
						snprintf(buffer, sizeof buffer, "%d:%d/%d",
							 from_addr->zone, from_addr->net, from_addr->node);
					}
					jsf.LoID = JAMSFLD_DADDRESS;
					jsf.HiID = 0;
					jsf.DatLen = strlen(buffer);
					jsf.Buffer = (char *)buffer;
					JAM_PutSubfield(jsp, &jsf);
					free(from_addr);
					from_addr = NULL;
				}

				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));
			}
		}

		while (1) {
			z = JAM_LockMB(jb, 100);
			if (z == 0) {
				break;
			} else if (z == JAM_LOCK_FAILED) {
				sleep(1);
			} else {
				free(msg);
				free(to);
				free(subject);
				dolog("Failed to lock msg base!");
				break;
			}
		}
		if (z != 0) {
			JAM_CloseMB(jb);
			free(jb);
			return;
		}

		if (JAM_AddMessage(jb, &jmh, jsp, (char *)msg, strlen(msg))) {
			dolog("Failed to add message");
		} else {
			if (ma->type == TYPE_NETMAIL_AREA) {
				if (conf.netmail_sem != NULL) {
					sem_fd = open(conf.netmail_sem, O_RDWR | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
					close(sem_fd);
				}
			} else if (ma->type == TYPE_ECHOMAIL_AREA || ma->type == TYPE_NEWSGROUP_AREA) {
				if (conf.echomail_sem != NULL) {
					sem_fd = open(conf.echomail_sem, O_RDWR | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
					close(sem_fd);
				}
			}
		}

		JAM_UnlockMB(jb);

		JAM_DelSubPacket(jsp);
		free(msg);
		JAM_CloseMB(jb);
		free(jb);
	}
	free(to);
	free(subject);
}

void list_messages(struct user_record *user) {
	struct msg_headers *msghs;
	s_JamBase *jb;
	int all_unread;
	s_JamLastRead jlr;
	char buffer[256];
	int i;
	int k;
	int j;
	int start;
	int closed;
	int redraw;
	struct tm msg_date;
	char c;
	int offset = 2;
	int height = 22;

	struct mail_conference *gmc = get_conf(gUser->cur_mail_conf);
	if (gmc->header != NULL) {
		offset = 8;
		height = 16;
	}

	struct mail_conference *mc = get_user_conf(user);

	s_printf("\r\n");
	// list mail in message base
	msghs = read_message_headers(user->cur_mail_conf, user->cur_mail_area, user, 0);
	if (msghs != NULL && msghs->msg_count > 0) {
		struct mail_area *ma = get_user_area(user);
		jb = open_jam_base(ma->path);
		if (!jb) {
			dolog("Error opening JAM base.. %s", ma->path);
			return;
		} else {
			all_unread = 0;
			if (JAM_ReadLastRead(jb, user->id, &jlr) == JAM_NO_USER) {
				jlr.LastReadMsg = 0;
				jlr.HighReadMsg = 0;
				all_unread = 1;
			} else if (jlr.LastReadMsg == 0 && jlr.HighReadMsg == 0) {
				all_unread = 1;
			}
			JAM_CloseMB(jb);
			free(jb);
			s_printf(get_string(125), msghs->msg_count);

			s_readstring(buffer, 6);
			if (tolower(buffer[0]) == 'n') {
				if (all_unread == 0) {
					k = jlr.HighReadMsg;
					for (i = 0; i < msghs->msg_count; i++) {
						if (msghs->msgs[i]->msg_h->MsgNum == k) {
							break;
						}
					}
					if (i == msghs->msg_count - 1) {
						i = 1;
					} else {
						i += 2;
					}

				} else {
					i = 1;
				}
			} else {
				i = atoi(buffer);
				if (i <= 0) {
					i = 1;
				}
			}
			closed = 0;

			redraw = 1;
			start = i - 1;
			while (!closed) {
				if (redraw) {
					if (mc->header != NULL) {
						s_printf("\e[2J\e[1;1H");
						s_displayansi(mc->header);
						s_printf("\e[7;1H");
					} else {
						s_printf("\e[2J\e[1;1H");
					}
					s_printf(get_string(126));
					for (j = start; j < start + height && j < msghs->msg_count; j++) {
						gmtime_r((time_t *)&msghs->msgs[j]->msg_h->DateWritten, &msg_date);
						if (j == i - 1) {
							if (msgbase_is_flagged(user, user->cur_mail_conf, user->cur_mail_area, msghs->msgs[j]->msg_h->MsgNum)) {
								if (conf.date_style == 1) {
									s_printf(get_string(286), j + 1, msghs->msgs[j]->subject, msghs->msgs[j]->from, msghs->msgs[j]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
								} else {
									s_printf(get_string(286), j + 1, msghs->msgs[j]->subject, msghs->msgs[j]->from, msghs->msgs[j]->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 (msghs->msgs[j]->msg_h->MsgNum > jlr.HighReadMsg || all_unread) {
								if (conf.date_style == 1) {
									s_printf(get_string(188), j + 1, msghs->msgs[j]->subject, msghs->msgs[j]->from, msghs->msgs[j]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
								} else {
									s_printf(get_string(188), j + 1, msghs->msgs[j]->subject, msghs->msgs[j]->from, msghs->msgs[j]->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) {
									s_printf(get_string(189), j + 1, msghs->msgs[j]->subject, msghs->msgs[j]->from, msghs->msgs[j]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
								} else {
									s_printf(get_string(189), j + 1, msghs->msgs[j]->subject, msghs->msgs[j]->from, msghs->msgs[j]->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 (msgbase_is_flagged(user, user->cur_mail_conf, user->cur_mail_area, msghs->msgs[j]->msg_h->MsgNum)) {
								if (conf.date_style == 1) {
									s_printf(get_string(287), j + 1, msghs->msgs[j]->subject, msghs->msgs[j]->from, msghs->msgs[j]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
								} else {
									s_printf(get_string(287), j + 1, msghs->msgs[j]->subject, msghs->msgs[j]->from, msghs->msgs[j]->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 (msghs->msgs[j]->msg_h->MsgNum > jlr.HighReadMsg || all_unread) {
								if (conf.date_style == 1) {
									s_printf(get_string(127), j + 1, msghs->msgs[j]->subject, msghs->msgs[j]->from, msghs->msgs[j]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
								} else {
									s_printf(get_string(127), j + 1, msghs->msgs[j]->subject, msghs->msgs[j]->from, msghs->msgs[j]->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) {
									s_printf(get_string(128), j + 1, msghs->msgs[j]->subject, msghs->msgs[j]->from, msghs->msgs[j]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
								} else {
									s_printf(get_string(128), j + 1, msghs->msgs[j]->subject, msghs->msgs[j]->from, msghs->msgs[j]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mday, msg_date.tm_mon + 1, msg_date.tm_year - 100);
								}
							}
						}
					}
					s_printf(get_string(190));
					s_printf("\e[%d;5H", i - start + offset - 1);
					redraw = 0;
				}
				c = s_getchar();
				if (tolower(c) == 'q') {
					closed = 1;
				} else if (c == 27) {
					c = s_getchar();
					if (c == 91) {
						c = s_getchar();
						if (c == 66) {
							// down
							i++;
							if (i > start + height) {
								start += height;
								if (start > msghs->msg_count) {
									start = msghs->msg_count - height;
								}
								redraw = 1;
							}
							if (i - 1 == msghs->msg_count) {
								i--;
								s_printf("\e[%d;5H", i - start + offset - 1);
							} else if (!redraw) {
								s_printf("\e[%d;1H", i - start + offset - 2);
								gmtime_r((time_t *)&msghs->msgs[i - 2]->msg_h->DateWritten, &msg_date);
								if (msgbase_is_flagged(user, user->cur_mail_conf, user->cur_mail_area, msghs->msgs[i - 2]->msg_h->MsgNum)) {
									if (conf.date_style == 1) {
										s_printf(get_string(287), i - 1, msghs->msgs[i - 2]->subject, msghs->msgs[i - 2]->from, msghs->msgs[i - 2]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
									} else {
										s_printf(get_string(287), i - 1, msghs->msgs[i - 2]->subject, msghs->msgs[i - 2]->from, msghs->msgs[i - 2]->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 (msghs->msgs[i - 2]->msg_h->MsgNum > jlr.HighReadMsg || all_unread) {
									if (conf.date_style == 1) {
										s_printf(get_string(127), i - 1, msghs->msgs[i - 2]->subject, msghs->msgs[i - 2]->from, msghs->msgs[i - 2]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
									} else {
										s_printf(get_string(127), i - 1, msghs->msgs[i - 2]->subject, msghs->msgs[i - 2]->from, msghs->msgs[i - 2]->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) {
										s_printf(get_string(128), i - 1, msghs->msgs[i - 2]->subject, msghs->msgs[i - 2]->from, msghs->msgs[i - 2]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
									} else {
										s_printf(get_string(128), i - 1, msghs->msgs[i - 2]->subject, msghs->msgs[i - 2]->from, msghs->msgs[i - 2]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mday, msg_date.tm_mon + 1, msg_date.tm_year - 100);
									}
								}
								s_printf("\e[%d;1H", i - start + offset - 1);
								gmtime_r((time_t *)&msghs->msgs[i - 1]->msg_h->DateWritten, &msg_date);
								if (msgbase_is_flagged(user, user->cur_mail_conf, user->cur_mail_area, msghs->msgs[i - 1]->msg_h->MsgNum)) {
									if (conf.date_style == 1) {
										s_printf(get_string(286), i, msghs->msgs[i - 1]->subject, msghs->msgs[i - 1]->from, msghs->msgs[i - 1]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
									} else {
										s_printf(get_string(286), i, msghs->msgs[i - 1]->subject, msghs->msgs[i - 1]->from, msghs->msgs[i - 1]->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 (msghs->msgs[i - 1]->msg_h->MsgNum > jlr.HighReadMsg || all_unread) {
									if (conf.date_style == 1) {
										s_printf(get_string(188), i, msghs->msgs[i - 1]->subject, msghs->msgs[i - 1]->from, msghs->msgs[i - 1]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
									} else {
										s_printf(get_string(188), i, msghs->msgs[i - 1]->subject, msghs->msgs[i - 1]->from, msghs->msgs[i - 1]->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) {
										s_printf(get_string(189), i, msghs->msgs[i - 1]->subject, msghs->msgs[i - 1]->from, msghs->msgs[i - 1]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
									} else {
										s_printf(get_string(189), i, msghs->msgs[i - 1]->subject, msghs->msgs[i - 1]->from, msghs->msgs[i - 1]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mday, msg_date.tm_mon + 1, msg_date.tm_year - 100);
									}
								}
								s_printf("\e[%d;5H", i - start + offset - 1);
							}
						} else if (c == 65) {
							// up
							i--;
							if (i - 1 < start) {
								start -= height;
								if (start < 0) {
									start = 0;
								}
								redraw = 1;
							}
							if (i <= 1) {
								start = 0;
								i = 1;
								redraw = 1;
							} else if (!redraw) {
								s_printf("\e[%d;1H", i - start + offset);
								gmtime_r((time_t *)&msghs->msgs[i]->msg_h->DateWritten, &msg_date);
								if (msgbase_is_flagged(user, user->cur_mail_conf, user->cur_mail_area, msghs->msgs[i]->msg_h->MsgNum)) {
									if (conf.date_style == 1) {
										s_printf(get_string(287), i + 1, msghs->msgs[i]->subject, msghs->msgs[i]->from, msghs->msgs[i]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
									} else {
										s_printf(get_string(287), i + 1, msghs->msgs[i]->subject, msghs->msgs[i]->from, msghs->msgs[i]->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 (msghs->msgs[i]->msg_h->MsgNum > jlr.HighReadMsg || all_unread) {
									if (conf.date_style == 1) {
										s_printf(get_string(127), i + 1, msghs->msgs[i]->subject, msghs->msgs[i]->from, msghs->msgs[i]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
									} else {
										s_printf(get_string(127), i + 1, msghs->msgs[i]->subject, msghs->msgs[i]->from, msghs->msgs[i]->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) {
										s_printf(get_string(128), i + 1, msghs->msgs[i]->subject, msghs->msgs[i]->from, msghs->msgs[i]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
									} else {
										s_printf(get_string(128), i + 1, msghs->msgs[i]->subject, msghs->msgs[i]->from, msghs->msgs[i]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mday, msg_date.tm_mon + 1, msg_date.tm_year - 100);
									}
								}
								s_printf("\e[%d;1H", i - start + offset - 1);
								gmtime_r((time_t *)&msghs->msgs[i - 1]->msg_h->DateWritten, &msg_date);
								if (msgbase_is_flagged(user, user->cur_mail_conf, user->cur_mail_area, msghs->msgs[i - 1]->msg_h->MsgNum)) {
									if (conf.date_style == 1) {
										s_printf(get_string(286), i, msghs->msgs[i - 1]->subject, msghs->msgs[i - 1]->from, msghs->msgs[i - 1]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
									} else {
										s_printf(get_string(286), i, msghs->msgs[i - 1]->subject, msghs->msgs[i - 1]->from, msghs->msgs[i - 1]->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 (msghs->msgs[i - 1]->msg_h->MsgNum > jlr.HighReadMsg || all_unread) {
									if (conf.date_style == 1) {
										s_printf(get_string(188), i, msghs->msgs[i - 1]->subject, msghs->msgs[i - 1]->from, msghs->msgs[i - 1]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
									} else {
										s_printf(get_string(188), i, msghs->msgs[i - 1]->subject, msghs->msgs[i - 1]->from, msghs->msgs[i - 1]->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) {
										s_printf(get_string(189), i, msghs->msgs[i - 1]->subject, msghs->msgs[i - 1]->from, msghs->msgs[i - 1]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mon + 1, msg_date.tm_mday, msg_date.tm_year - 100);
									} else {
										s_printf(get_string(189), i, msghs->msgs[i - 1]->subject, msghs->msgs[i - 1]->from, msghs->msgs[i - 1]->to, msg_date.tm_hour, msg_date.tm_min, msg_date.tm_mday, msg_date.tm_mon + 1, msg_date.tm_year - 100);
									}
								}
								s_printf("\e[%d;5H", i - start + offset - 1);
							}
						} else if (c == 75) {
							// END KEY
							i = msghs->msg_count;
							start = i - height;
							if (start < 0) {
								start = 0;
							}
							redraw = 1;
						} else if (c == 72) {
							// HOME KEY
							i = 1;
							start = 0;
							redraw = 1;
						} else if (c == 86 || c == '5') {
							if (c == '5') {
								s_getchar();
							}
							// PAGE UP
							i = i - height;
							if (i <= 0) {
								i = 1;
							}
							start = i - 1;
							redraw = 1;
						} else if (c == 85 || c == '6') {
							if (c == '6') {
								s_getchar();
							}
							// PAGE DOWN
							i = i + height;
							if (i > msghs->msg_count) {
								i = msghs->msg_count;
							}
							start = i - 1;
							redraw = 1;
						}
					}
				} else if (c == 13) {
					redraw = 1;
					read_message(user, msghs, i - 1, 0);
					free_message_headers(msghs);
					msghs = read_message_headers(user->cur_mail_conf, user->cur_mail_area, user, 0);
					struct mail_area *ma = get_user_area(user);
					jb = open_jam_base(ma->path);
					if (!jb) {
						dolog("Error opening JAM base.. %s", ma->path);
						if (msghs != NULL) {
							free_message_headers(msghs);
						}
						return;
					} else {
						all_unread = 0;
						if (JAM_ReadLastRead(jb, user->id, &jlr) == JAM_NO_USER) {
							jlr.LastReadMsg = 0;
							jlr.HighReadMsg = 0;
							all_unread = 1;
						}
						JAM_CloseMB(jb);
						free(jb);
					}
				} else if (tolower(c) == 'f') {
					redraw = 1;
					msgbase_flag_unflag(user, user->cur_mail_conf, user->cur_mail_area, msghs->msgs[i - 1]->msg_h->MsgNum);
				}
			}
		}

		if (msghs != NULL) {
			free_message_headers(msghs);
		}
	} else {
		s_printf(get_string(130));
	}
}

struct conf_tmp_t {
	struct mail_conference *conference;
	int index;
};

void choose_conference() {
	int i;
	int list_tmp = 0;
	struct conf_tmp_t **conf_tmp;
	int redraw = 1;
	int start = 0;
	int selected = 0;
	char c;
	struct ptr_vector vec;

	init_ptr_vector(&vec);
	for (i = 0; i < ptr_vector_len(&conf.mail_conferences); ++i) {
		struct mail_conference *mc = ptr_vector_get(&conf.mail_conferences, i);
		if (mc->sec_level <= gUser->sec_level) {
			struct conf_tmp_t *c = (struct conf_tmp_t *)malloz(sizeof(struct conf_tmp_t));
			c->conference = mc;
			c->index = i;
			ptr_vector_append(&vec, c);
		}
	}
	list_tmp = ptr_vector_len(&vec);
	conf_tmp = (struct conf_tmp_t **)consume_ptr_vector(&vec);

	while (1) {
		if (redraw) {
			s_printf("\e[2J\e[1;1H");
			s_printf(get_string(247));
			s_printf(get_string(248));
			for (i = start; i < start + 22 && i < list_tmp; i++) {
				if (i == selected) {
					s_printf(get_string(249), i - start + 2, conf_tmp[i]->index, conf_tmp[i]->conference->name);
				} else {
					s_printf(get_string(250), i - start + 2, conf_tmp[i]->index, conf_tmp[i]->conference->name);
				}
			}
			s_printf("\e[%d;5H", selected - start + 2);
			redraw = 0;
		}
		c = s_getchar();
		if (tolower(c) == 'q') {
			break;
		} else if (c == 27) {
			c = s_getchar();
			if (c == 91) {
				c = s_getchar();
				if (c == 66) {
					// down
					if (selected + 1 >= start + 22) {
						start += 22;
						if (start >= list_tmp) {
							start = list_tmp - 22;
						}
						redraw = 1;
					}
					selected++;
					if (selected >= list_tmp) {
						selected = list_tmp - 1;
					} else {
						if (!redraw) {
							s_printf(get_string(250), selected - start + 1, conf_tmp[selected - 1]->index, conf_tmp[selected - 1]->conference->name);
							s_printf(get_string(249), selected - start + 2, conf_tmp[selected]->index, conf_tmp[selected]->conference->name);
							s_printf("\e[%d;5H", selected - start + 2);
						}
					}
				} else if (c == 65) {
					// up
					if (selected - 1 < start) {
						start -= 22;
						if (start < 0) {
							start = 0;
						}
						redraw = 1;
					}
					selected--;
					if (selected < 0) {
						selected = 0;
					} else {
						if (!redraw) {
							s_printf(get_string(249), selected - start + 2, conf_tmp[selected]->index, conf_tmp[selected]->conference->name);
							s_printf(get_string(250), selected - start + 3, conf_tmp[selected + 1]->index, conf_tmp[selected + 1]->conference->name);
							s_printf("\e[%d;5H", selected - start + 2);
						}
					}
				} else if (c == 75) {
					// END KEY
					selected = list_tmp - 1;
					start = list_tmp - 22;
					if (start < 0) {
						start = 0;
					}
					redraw = 1;
				} else if (c == 72) {
					// HOME KEY
					selected = 0;
					start = 0;
					redraw = 1;
				} else if (c == 86 || c == '5') {
					if (c == '5') {
						s_getchar();
					}
					// PAGE UP
					selected = selected - 22;
					if (selected < 0) {
						selected = 0;
					}
					start = selected;
					redraw = 1;
				} else if (c == 85 || c == '6') {
					if (c == '6') {
						s_getchar();
					}
					// PAGE DOWN
					selected = selected + 22;
					if (selected >= list_tmp) {
						selected = list_tmp - 1;
					}
					start = selected;
					redraw = 1;
				}
			}
		} else if (c == 13) {
			gUser->cur_mail_conf = conf_tmp[selected]->index;
			gUser->cur_mail_area = 0;
			break;
		}
	}

	for (i = 0; i < list_tmp; i++) {
		free(conf_tmp[i]);
	}
	free(conf_tmp);
}

struct area_tmp_t {
	struct mail_area *area;
	int index;
};

void choose_area() {
	int i;
	int list_tmp = 0;
	struct area_tmp_t **area_tmp;
	int redraw = 1;
	int start = 0;
	int selected = 0;
	char c;
	int offset = 2;
	int height = 22;
	struct ptr_vector vec;

	struct mail_conference *gmc = get_user_conf(gUser);
	if (gmc->header != NULL) {
		offset = 8;
		height = 16;
	}

	init_ptr_vector(&vec);
	for (i = 0; i < ptr_vector_len(&gmc->mail_areas); ++i) {
		struct mail_area *ma = ptr_vector_get(&gmc->mail_areas, i);
		if (ma->read_sec_level <= gUser->sec_level) {
			struct area_tmp_t *area = (struct area_tmp_t *)malloz(sizeof(struct area_tmp_t));
			area->area = ma;
			area->index = i;
			ptr_vector_append(&vec, area);
		}
	}
	list_tmp = ptr_vector_len(&vec);
	area_tmp = (struct area_tmp_t **)consume_ptr_vector(&vec);

	while (1) {
		if (redraw) {
			struct mail_conference *gmc = get_user_conf(gUser);
			if (gmc->header != NULL) {
				s_printf("\e[2J\e[1;1H");
				s_displayansi(gmc->header);
				s_printf("\e[7;1H");
			} else {
				s_printf("\e[2J\e[1;1H");
			}
			s_printf(get_string(251), gmc->name);
			s_printf(get_string(248));
			for (i = start; i < start + height && i < list_tmp; i++) {
				if (i == selected) {
					if (new_messages(gUser, gUser->cur_mail_conf, area_tmp[i]->index)) {
						s_printf(get_string(259), i - start + offset, area_tmp[i]->index, area_tmp[i]->area->name);
					} else {
						s_printf(get_string(249), i - start + offset, area_tmp[i]->index, area_tmp[i]->area->name);
					}
				} else {
					if (new_messages(gUser, gUser->cur_mail_conf, area_tmp[i]->index)) {
						s_printf(get_string(260), i - start + offset, area_tmp[i]->index, area_tmp[i]->area->name);
					} else {
						s_printf(get_string(250), i - start + offset, area_tmp[i]->index, area_tmp[i]->area->name);
					}
				}
			}
			s_printf("\e[%d;5H", selected - start + offset);
			redraw = 0;
		}
		c = s_getchar();
		if (tolower(c) == 'q') {
			break;
		} else if (c == 27) {
			c = s_getchar();
			if (c == 91) {
				c = s_getchar();
				if (c == 66) {
					// down
					if (selected + 1 >= start + height) {
						start += height;
						if (start >= list_tmp) {
							start = list_tmp - height;
						}
						redraw = 1;
					}
					selected++;
					if (selected >= list_tmp) {
						selected = list_tmp - 1;
					} else {
						if (!redraw) {
							if (new_messages(gUser, gUser->cur_mail_conf, area_tmp[selected - 1]->index)) {
								s_printf(get_string(260), selected - start + (offset - 1), area_tmp[selected - 1]->index, area_tmp[selected - 1]->area->name);
							} else {
								s_printf(get_string(250), selected - start + (offset - 1), area_tmp[selected - 1]->index, area_tmp[selected - 1]->area->name);
							}
							if (new_messages(gUser, gUser->cur_mail_conf, area_tmp[selected]->index)) {
								s_printf(get_string(259), selected - start + offset, area_tmp[selected]->index, area_tmp[selected]->area->name);
							} else {
								s_printf(get_string(249), selected - start + offset, area_tmp[selected]->index, area_tmp[selected]->area->name);
							}
							s_printf("\e[%d;5H", selected - start + offset);
						}
					}
				} else if (c == 65) {
					// up
					if (selected - 1 < start) {
						start -= height;
						if (start < 0) {
							start = 0;
						}
						redraw = 1;
					}
					selected--;
					if (selected < 0) {
						selected = 0;
					} else {
						if (!redraw) {
							if (new_messages(gUser, gUser->cur_mail_conf, area_tmp[selected]->index)) {
								s_printf(get_string(259), selected - start + offset, area_tmp[selected]->index, area_tmp[selected]->area->name);
							} else {
								s_printf(get_string(249), selected - start + offset, area_tmp[selected]->index, area_tmp[selected]->area->name);
							}
							if (new_messages(gUser, gUser->cur_mail_conf, area_tmp[selected + 1]->index)) {
								s_printf(get_string(260), selected - start + (offset + 1), area_tmp[selected + 1]->index, area_tmp[selected + 1]->area->name);
							} else {
								s_printf(get_string(250), selected - start + (offset + 1), area_tmp[selected + 1]->index, area_tmp[selected + 1]->area->name);
							}
							s_printf("\e[%d;5H", selected - start + offset);
						}
					}
				} else if (c == 75) {
					// END KEY
					selected = list_tmp - 1;
					start = list_tmp - height;
					if (start < 0) {
						start = 0;
					}
					redraw = 1;
				} else if (c == 72) {
					// HOME KEY
					selected = 0;
					start = 0;
					redraw = 1;
				} else if (c == 86 || c == '5') {
					if (c == '5') {
						s_getchar();
					}
					// PAGE UP
					selected = selected - height;
					if (selected < 0) {
						selected = 0;
					}
					start = selected;
					redraw = 1;
				} else if (c == 85 || c == '6') {
					if (c == '6') {
						s_getchar();
					}
					// PAGE DOWN
					selected = selected + height;
					if (selected >= list_tmp) {
						selected = list_tmp - 1;
					}
					start = selected;
					redraw = 1;
				}
			}
		} else if (c == 13) {
			gUser->cur_mail_area = area_tmp[selected]->index;
			break;
		}
	}

	for (i = 0; i < list_tmp; i++) {
		free(area_tmp[i]);
	}
	free(area_tmp);
}

void next_mail_conf(struct user_record *user) {
	size_t n = ptr_vector_len(&conf.mail_conferences);
	size_t start = user->cur_mail_conf;
	size_t i;
	for (i = (start + 1) % n; i != start; i = (i + 1) % n) {
		struct mail_conference *mc = get_conf(i);
		if (mc->sec_level <= user->sec_level) {
			break;
		}
	}
	user->cur_mail_conf = i;
	user->cur_mail_area = 0;
}

void prev_mail_conf(struct user_record *user) {
	size_t n = ptr_vector_len(&conf.mail_conferences);
	size_t start = user->cur_mail_conf;
	size_t i;
	for (i = (start + n - 1) % n; i != start; i = (i + n - 1) % n) {
		struct mail_conference *mc = get_conf(i);
		if (mc->sec_level <= user->sec_level) {
			break;
		}
	}
	user->cur_mail_conf = i;
	user->cur_mail_area = 0;
}

void next_mail_area(struct user_record *user) {
	struct mail_conference *mc = get_user_conf(user);
	size_t n = ptr_vector_len(&mc->mail_areas);
	size_t start = user->cur_mail_area;
	for (size_t i = (start + 1) % n; i != start; i = (i + 1) % n) {
		struct mail_area *ma = get_area(user->cur_mail_conf, i);
		if (ma->read_sec_level <= user->sec_level) {
			user->cur_mail_area = i;
			break;
		}
	}
}

void prev_mail_area(struct user_record *user) {
	struct mail_conference *mc = get_user_conf(user);
	size_t n = ptr_vector_len(&mc->mail_areas);
	size_t start = user->cur_mail_area;
	for (size_t i = (start + n - 1) % n; i != start; i = (i + n - 1) % n) {
		struct mail_area *ma = get_area(user->cur_mail_conf, i);
		if (ma->read_sec_level <= user->sec_level) {
			user->cur_mail_area = i;
			break;
		}
	}
}

void do_mail_scan(struct user_record *user, int oldscan, int personal) {
	s_JamBase *jb;
	s_JamBaseHeader jbh;
	s_JamLastRead jlr;
	struct msg_headers *msghs;
	char c;
	int i;
	int j;
	int lines = 0;
	int orig_conf;
	int orig_area;
	int res = 0;
	char ch;
	int unread_count;
	int k;

	if (personal) {
		s_printf(get_string(276));
	} else {
		s_printf(get_string(139));
	}
	c = s_getc();

	if (tolower(c) == 'y' || tolower(c) == 's') {
		for (i = 0; i < ptr_vector_len(&conf.mail_conferences); i++) {
			struct mail_conference *mc = get_conf(i);
			if (mc->sec_level > user->sec_level) {
				continue;
			}
			if (oldscan) {
				s_printf(get_string(140), i, mc->name);

				lines += 2;
				if (lines == 22) {
					s_printf(get_string(6));
					s_getc();
					lines = 0;
				}
			}
			for (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;
				}

				if (tolower(c) == 's' && !msgbase_is_subscribed(i, j)) {
					continue;
				}

				jb = open_jam_base(ma->path);
				if (!jb) {
					dolog("Unable to open message base");
					continue;
				}

				if (JAM_ReadMBHeader(jb, &jbh) != 0) {
					JAM_CloseMB(jb);
					free(jb);
					continue;
				}

				if (JAM_ReadLastRead(jb, user->id, &jlr) == JAM_NO_USER) {
					if (jbh.ActiveMsgs == 0) {
						JAM_CloseMB(jb);
						free(jb);
						continue;
					}
					if (ma->type == TYPE_NETMAIL_AREA) {
						msghs = read_message_headers(i, j, user, personal);
						if (msghs != NULL) {
							if (msghs->msg_count > 0) {
								if (oldscan) {
									s_printf(get_string(141), j, ma->name, msghs->msg_count);
									lines++;
									if (lines == 22) {
										s_printf(get_string(6));
										s_getc();
										lines = 0;
									}
								} else {
									s_printf("\e[2J\e[1;1H");
									s_printf(get_string(277), i, mc->name);
									s_printf(get_string(278), j, ma->name, msghs->msg_count);
									s_printf(get_string(279));

									ch = s_getchar();
									s_printf("\r\n");
									if (tolower(ch) == 'y') {
										orig_conf = user->cur_mail_conf;
										orig_area = user->cur_mail_area;

										user->cur_mail_conf = i;
										user->cur_mail_area = j;

										res = read_new_msgs(user, msghs);

										user->cur_mail_conf = orig_conf;
										user->cur_mail_area = orig_area;
									}
								}
							}
							free_message_headers(msghs);
						}
					} else {
						if (oldscan) {
							s_printf(get_string(141), j, ma->name, jbh.ActiveMsgs);
							lines++;
							if (lines == 22) {
								s_printf(get_string(6));
								s_getc();
								lines = 0;
							}
						} else {
							msghs = read_message_headers(i, j, user, personal);
							if (msghs != NULL) {

								if (msghs->msg_count > 0) {
									s_printf("\e[2J\e[1;1H");
									s_printf(get_string(277), i, mc->name);
									s_printf(get_string(278), j, ma->name, msghs->msg_count);
									s_printf(get_string(279));

									ch = s_getchar();
									s_printf("\r\n");
									if (tolower(ch) == 'y') {
										orig_conf = user->cur_mail_conf;
										orig_area = user->cur_mail_area;

										user->cur_mail_conf = i;
										user->cur_mail_area = j;

										res = read_new_msgs(user, msghs);

										user->cur_mail_conf = orig_conf;
										user->cur_mail_area = orig_area;
									}
								}
								free_message_headers(msghs);
							}
						}
					}
				} else {
					if (jlr.HighReadMsg < jbh.ActiveMsgs) {
						if (ma->type == TYPE_NETMAIL_AREA) {
							msghs = read_message_headers(i, j, user, personal);
							if (msghs != NULL) {
								if (msghs->msg_count > 0) {
									unread_count = 0;

									for (k = msghs->msg_count - 1; k >= 0; k--) {
										if (msghs->msgs[k]->msg_no < jlr.HighReadMsg) {
											break;
										}
										unread_count++;
									}
									if (unread_count > 0) {
										if (oldscan) {
											s_printf(get_string(141), j, ma->name, unread_count);
											lines++;
											if (lines == 22) {
												s_printf(get_string(6));
												s_getc();
												lines = 0;
											}
										} else {
											s_printf("\e[2J\e[1;1H");
											s_printf(get_string(277), i, mc->name);
											s_printf(get_string(278), j, ma->name, unread_count);
											s_printf(get_string(279));

											ch = s_getchar();
											s_printf("\r\n");
											if (tolower(ch) == 'y') {
												orig_conf = user->cur_mail_conf;
												orig_area = user->cur_mail_area;

												user->cur_mail_conf = i;
												user->cur_mail_area = j;

												res = read_new_msgs(user, msghs);

												user->cur_mail_conf = orig_conf;
												user->cur_mail_area = orig_area;
											}
										}
									}
								}
								free_message_headers(msghs);
							}
						} else {
							if (oldscan) {
								s_printf(get_string(141), j, ma->name, jbh.ActiveMsgs - jlr.HighReadMsg);
								lines++;
								if (lines == 22) {
									s_printf(get_string(6));
									s_getc();
									lines = 0;
								}
							} else {
								msghs = read_message_headers(i, j, user, personal);
								if (msghs != NULL) {
									if (msghs->msg_count > 0) {
										unread_count = 0;

										for (k = msghs->msg_count - 1; k >= 0; k--) {
											if (msghs->msgs[k]->msg_no < jlr.HighReadMsg) {
												break;
											}
											unread_count++;
										}
										if (unread_count > 0) {
											s_printf("\e[2J\e[1;1H");
											s_printf(get_string(277), i, mc->name);
											s_printf(get_string(278), j, ma->name, unread_count);
											s_printf(get_string(279));

											ch = s_getchar();
											s_printf("\r\n");
											if (tolower(ch) == 'y') {
												orig_conf = user->cur_mail_conf;
												orig_area = user->cur_mail_area;

												user->cur_mail_conf = i;
												user->cur_mail_area = j;

												res = read_new_msgs(user, msghs);

												user->cur_mail_conf = orig_conf;
												user->cur_mail_area = orig_area;
											}
										}
									}
									free_message_headers(msghs);
								}
							}
						}
					} else {
						JAM_CloseMB(jb);
						free(jb);
						continue;
					}
				}
				JAM_CloseMB(jb);
				free(jb);
				if (res) {
					break;
				}
			}
			if (res) {
				break;
			}
		}

		s_printf(get_string(6));
		s_getc();
	}
}

void full_mail_scan_personal(struct user_record *user) {
	do_mail_scan(user, 0, 1);
}

void full_mail_scan(struct user_record *user) {
	do_mail_scan(user, 0, 0);
}

void mail_scan(struct user_record *user) {
	do_mail_scan(user, 1, 0);
}

void msg_conf_sub_bases() {
	int i;
	int lines = 0;
	char buffer[10];
	int toggle_area;
	int done = 0;
	int j;
	s_printf("\e[1;1H\e[2J");

	do {
		struct mail_conference *gmc = get_user_conf(gUser);
		for (i = 0; i < ptr_vector_len(&gmc->mail_areas); i++) {
			struct mail_area *ma = ptr_vector_get(&gmc->mail_areas, i);
			if (ma->read_sec_level <= gUser->sec_level) {
				s_printf(get_string(226), i, (msgbase_is_subscribed(gUser->cur_mail_conf, i) ? get_string(227) : get_string(228)), ma->name);
				lines++;
			}

			if (lines == 23) {
				s_printf(get_string(225));
				s_readstring(buffer, 9);
				s_printf("\r\n");
				if (strlen(buffer) > 0) {
					if (buffer[0] >= '0' && buffer[0] <= '9') {
						toggle_area = atoi(buffer);
						struct mail_area *ma = ptr_vector_get(&gmc->mail_areas, toggle_area);
						if (ma->read_sec_level <= gUser->sec_level) {
							msgbase_sub_unsub(gUser->cur_mail_conf, toggle_area);
						}
						lines = 0;
						break;
					}
					if (buffer[0] == 'a' || buffer[0] == 'A') {
						for (j = 0; j < ptr_vector_len(&gmc->mail_areas); j++) {
							struct mail_area *ma = ptr_vector_get(&gmc->mail_areas, j);
							if (ma->read_sec_level <= gUser->sec_level) {
								if (!msgbase_is_subscribed(gUser->cur_mail_conf, j)) {
									msgbase_sub_unsub(gUser->cur_mail_conf, j);
								}
							}
						}
						break;
					}
					if (buffer[0] == 'n' || buffer[0] == 'N') {
						for (j = 0; j < ptr_vector_len(&gmc->mail_areas); j++) {
							struct mail_area *ma = ptr_vector_get(&gmc->mail_areas, j);
							if (ma->read_sec_level <= gUser->sec_level) {
								if (msgbase_is_subscribed(gUser->cur_mail_conf, j)) {
									msgbase_sub_unsub(gUser->cur_mail_conf, j);
								}
							}
						}
						break;
					}
				}
				lines = 0;
			}
		}

		if (lines > 0) {
			s_printf(get_string(225));
			s_readstring(buffer, 9);
			s_printf("\r\n");
			if (strlen(buffer) > 0) {
				if (buffer[0] >= '0' && buffer[0] <= '9') {
					toggle_area = atoi(buffer);
					struct mail_area *ma = ptr_vector_get(&gmc->mail_areas, toggle_area);
					if (ma->read_sec_level <= gUser->sec_level) {
						msgbase_sub_unsub(gUser->cur_mail_conf, toggle_area);
					}
					lines = 0;
				} else if (buffer[0] == 'a' || buffer[0] == 'A') {
					for (j = 0; j < ptr_vector_len(&gmc->mail_areas); j++) {
						struct mail_area *ma = ptr_vector_get(&gmc->mail_areas, j);
						if (ma->read_sec_level <= gUser->sec_level) {
							if (!msgbase_is_subscribed(gUser->cur_mail_conf, j)) {
								msgbase_sub_unsub(gUser->cur_mail_conf, j);
							}
						}
					}
				} else if (buffer[0] == 'n' || buffer[0] == 'N') {
					for (j = 0; j < ptr_vector_len(&gmc->mail_areas); j++) {
						struct mail_area *ma = ptr_vector_get(&gmc->mail_areas, j);
						if (ma->read_sec_level <= gUser->sec_level) {
							if (msgbase_is_subscribed(gUser->cur_mail_conf, j)) {
								msgbase_sub_unsub(gUser->cur_mail_conf, j);
							}
						}
					}
				} else {
					done = 1;
				}
			} else {
				done = 1;
			}
		} else {
			done = 1;
		}
	} while (!done);
}

void msgbase_reset_pointers(int conference, int msgarea, int readm, int msgno) {
	s_JamBase *jb;
	s_JamBaseHeader jbh;
	s_JamLastRead jlr;
	s_JamMsgHeader jmh;

	int max_msg;
	int active_msgs;
	int i, j, k;

	struct mail_area *ma = get_area(conference, msgarea);
	jb = open_jam_base(ma->path);
	if (!jb) {
		dolog("Unable to open message base");
		return;
	}

	if (JAM_ReadMBHeader(jb, &jbh) != 0) {
		JAM_CloseMB(jb);
		free(jb);
		return;
	}

	j = 0;

	if (msgno == -1 && readm) {
		k = jbh.ActiveMsgs;
	} else if (msgno == -1 && !readm) {
		k = 0;
	} else {
		if (msgno > jbh.ActiveMsgs) {
			k = jbh.ActiveMsgs;
		} else {
			k = msgno;
		}
	}

	for (i = 0; j < k; i++) {
		memset(&jmh, 0, sizeof(s_JamMsgHeader));
		if (JAM_ReadMsgHeader(jb, i, &jmh, NULL) != 0) {
			dolog("Failed to read msg header: Erro %d", JAM_Errno(jb));
			return;
		}

		if (jmh.Attribute & JAM_MSG_DELETED) {
			continue;
		}
		j++;
	}

	max_msg = i;

	if (JAM_ReadLastRead(jb, gUser->id, &jlr) != JAM_NO_USER) {
		jlr.LastReadMsg = max_msg;
		jlr.HighReadMsg = max_msg;
		JAM_WriteLastRead(jb, gUser->id, &jlr);
	} else {
		jlr.LastReadMsg = max_msg;
		jlr.HighReadMsg = max_msg;
		jlr.UserCRC = JAM_Crc32(gUser->loginname, strlen(gUser->loginname));
		jlr.UserID = gUser->id;
		JAM_WriteLastRead(jb, gUser->id, &jlr);
	}
	JAM_CloseMB(jb);
	free(jb);
}

void msgbase_reset_all_pointers(int readm) {
	for (size_t i = 0; i < ptr_vector_len(&conf.mail_conferences); ++i) {
		struct mail_conference *mc = ptr_vector_get(&conf.mail_conferences, i);
		for (size_t j = 0; j < ptr_vector_len(&mc->mail_areas); ++j)
			msgbase_reset_pointers(i, j, readm, -1);
	}
}

int new_messages(struct user_record *user, int conference, int area) {
	int count = 0;
	s_JamBase *jb;
	s_JamBaseHeader jbh;
	s_JamLastRead jlr;
	struct msg_headers *msghs;

	struct mail_area *ma = get_area(conference, area);
	jb = open_jam_base(ma->path);
	if (!jb) {
		return 0;
	}
	if (JAM_ReadMBHeader(jb, &jbh) != 0) {
		JAM_CloseMB(jb);
		free(jb);
		return 0;
	}
	if (JAM_ReadLastRead(jb, user->id, &jlr) == JAM_NO_USER) {
		if (jbh.ActiveMsgs == 0) {
			JAM_CloseMB(jb);
			free(jb);
			return 0;
		}
		msghs = read_message_headers(conference, area, user, 0);
		if (msghs != NULL) {
			if (msghs->msg_count > 0) {
				count = msghs->msg_count;
			}
			free_message_headers(msghs);
		}
	} else {
		msghs = read_message_headers(conference, area, user, 0);
		if (msghs != NULL) {
			if (msghs->msg_count > 0) {
				if (msghs->msgs[msghs->msg_count - 1]->msg_h->MsgNum > jlr.HighReadMsg) {
					count = msghs->msgs[msghs->msg_count - 1]->msg_h->MsgNum - jlr.HighReadMsg;
				}
			}
			free_message_headers(msghs);
		}
	}
	JAM_CloseMB(jb);
	free(jb);
	return count;
}
