/*
 * asmail is the AfterStep mailbox monitor
 * Copyright (c) 2002-2007 Albert Dorofeev <albert@tigr.net>
 * For the updates see http://www.tigr.net/
 *
 * This software is distributed under GPL. For details see LICENSE file.
 */

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include <errno.h>

#include "globals.h"
#include "mbox.h"
#include "maildir.h"
#include "mh.h"
#include "pop3.h"
#include "imap.h"
#include "gui.h"
#include "config.h"


int passwords_in_config = 0;

void version() {
	printf("asmail : AfterStep e-mail monitor version %s\n", VERSION);
}

void usage() {
	version();
	printf("Usage : asmail [-options...]\n");
	printf("\n");
	printf("-V		print version and exit\n");
	printf("-h		print this help screen and exit\n");
	printf("-v		verbose: print mailbox stats to STDOUT\n");
	printf("-insecure	allow config file to be readable by others\n");
	printf("-f <filename>	name of the configuration file to use\n");
	printf("-geometry <xy>	X geometry specification (position/size)\n");
	printf("-nox		don't use X11 interface (implies -v)\n");
	printf("-noconfig	don't read config file (run default settings)\n");
	printf("-iconic		start as an icon rather than a window\n");
	printf("-withdrawn	\"withdrawn\" mode for WindowMaker\n");
	printf("\n");
	printf("Use $DISPLAY environment variable to run asmail on a different display.\n");
	printf("\n");
}

static void err_printf(char const *tmpl, ...) {
	va_list val;
	int errno_;

	errno_ = errno;
	
	va_start(val, tmpl);
	vprintf(tmpl, val);
	va_end(val);

	errno = errno_;
}

/*
 * Set up the defaults that cannot be determined at compile time.
 */
void defaults() {
	strncpy(config_file_name, (char *) getenv("HOME"), MAX_INPUT_LENGTH);
	strncat(config_file_name, "/", MAX_INPUT_LENGTH-strlen(config_file_name));
	strncat(config_file_name, RCFILE, MAX_INPUT_LENGTH-strlen(config_file_name));
	/*
	 * Just in case, prevent the strings from overrunning
	 */
	config_file_name[MAX_INPUT_LENGTH] = '\0';
	x11_set.geometry[MAX_INPUT_LENGTH] = '\0';
	x11_set.title[MAX_INPUT_LENGTH] = '\0';
	x11_set.font[MAX_INPUT_LENGTH] = '\0';
	x11_set.on_left[MAX_INPUT_LENGTH] = '\0';
	x11_set.on_middle[MAX_INPUT_LENGTH] = '\0';
	x11_set.on_right[MAX_INPUT_LENGTH] = '\0';
	x11_set.on_new_mail[MAX_INPUT_LENGTH] = '\0';
	x11_set.beep = 1;
	x11_set.shape = 0;
	x11_set.use_frame = 1;
	x11_set.withdrawn = 0;
	x11_set.iconic = 0;
	strcpy(x11_set.geometry, "");
	strcpy(x11_set.title, "asmail");
	strcpy(x11_set.on_left, "");
	strcpy(x11_set.on_middle, "");
	strcpy(x11_set.on_right, "");
	strcpy(x11_set.on_new_mail, "");
	x11_set.each = 0;
	x11_set.total = 1;
	x11_set.status = 1;
	x11_set.old = 1;
	x11_set.new = 1;
	x11_set.x = 0;
	x11_set.y = 52;
	strcpy(x11_set.delimiter, "/");
	strcpy(x11_set.font, "-*-*-medium-r-normal--10-*-*-*-*-*-*-*");
	strcpy(x11_set.color, "black");
	x11_set.refresh = 10;
	x11_set.nomail = NULL;
	x11_set.oldmail = NULL;
	x11_set.newmail = NULL;
	x11_set.frame = NULL;
}

/*
 * The function parses the command line switches and sets
 * global variables accordingly.
 * Returns 0 on success, -1 = error.
 */
int parse_cmd(int argc, char *argv[]) {
	int i;

	for (i=1; i<argc; i++) {
		if ( ! strcmp(argv[i], "-geometry") ) {
			if ( ++i >= argc ) {
				usage();
				return(-1);
			}
			strncpy(x11_set.geometry, argv[i], MAX_INPUT_LENGTH);
		} else if ( ! strcmp(argv[i], "-title") ) {
			if ( ++i >= argc ) {
				usage();
				return(-1);
			}
			strncpy(x11_set.title, argv[i], MAX_INPUT_LENGTH);
		} else if ( ! strcmp(argv[i], "-f") ) {
			if ( ++i >= argc ) {
				usage();
				return(-1);
			}
			strncpy(config_file_name, argv[i], MAX_INPUT_LENGTH);
			flag_config_specified = 1;
		} else if ( ! strcmp(argv[i], "-V") ) {
			version();
			return(-1);
		} else if ( ! strcmp(argv[i], "-h") ) {
			usage();
			return(-1);
		} else if ( ! strcmp(argv[i], "-v") ) {
			flag_verbose = 1;
		} else if ( ! strcmp(argv[i], "-nox") ) {
			flag_verbose = 1;
			flag_no_x = 1;
		} else if ( ! strcmp(argv[i], "-noconfig") ) {
			flag_no_config = 1;
		} else if ( ! strcmp(argv[i], "-iconic") ) {
			x11_set.iconic = 1;
		} else if ( ! strcmp(argv[i], "-withdrawn") ) {
			x11_set.withdrawn = 1;
		} else if ( ! strcmp(argv[i], "-insecure") ) {
			flag_allow_insecure = 1;
		/*
		 * Unrecognised options
		 */
		} else if ( argv[i][0] == '-' ) {
			printf("asmail: Unknown option: %s\n", argv[i]);
			usage();
			return(-1);
		/*
		 * Old-style configuration file name
		 */
		} else {
			strncpy(config_file_name, argv[i], MAX_INPUT_LENGTH);
			flag_config_specified = 1;
		}
	}
	return(0);
}

/*
 * Remove the blanks at the beginning and the end of the line
 */
void shorten(char * line) {
	int i = 0, j = 0;

	if (line[strlen(line)-1] == '\n')
		line[strlen(line)-1] = '\0';
    
	while (isspace(line[i]) && line[i]) 
		i++;
	if (i == 0 || i == strlen(line))
		return;
	while (line[i])
		line[j++] = line[i++];
	line[j] = '\0';

	i = strlen(line);
	while (isspace(line[i]) && i > 0)
		i--;
	if (line[i] == '\n')
		i--;
	line[++i] = '\0';
}

/*
 * Strips the outside quotes (' or ") from the given line.
 * If there are no matching quotes - nothing is done.
 */
void strip_quotes(char * line) {
	char tmp1[MAX_INPUT_LENGTH+1];
	int len;

	if ( strlen(line) < 2 )
		return;
	strcpy(tmp1, line);
	shorten(tmp1);
	if ( ( tmp1[0] != '\'' ) && ( tmp1[0] != '"' ) ) {
		return;
	}
	/*
	 * Now the first character is either ' or ",
	 * find the terminating one.
	 */
	len = strlen(tmp1);
	if ( tmp1[len-1] != tmp1[0] )
		return;
	tmp1[len-1] = '\0';
	strcpy(line, &tmp1[1]);
}

/*
 * Clean the config line, check and prepare for parsing
 * Returns 0 if ok
 *        -1 if error
 */
int parse_cfg_check(char * line) {
	if (line[strlen(line)-1] != '\n') {
		/* The line does not end in EOL - too long */
		printf("asmail: a line in configuration is too long, maximum length is %d.\n",
				MAX_INPUT_LENGTH);
		printf("asmail: Offending line (%d chars): %s\n", 
				(unsigned)strlen(line), line);
		return(-1);
	}
	/* Leading and trailing space are removed */
	shorten(line);
	return(0);
}

/* 
 * Inside the X11 settings, parse the block with animation settings
 * and picture file names.
 */
int parse_cfg_animate(FILE * f, int * line_counter, char * filename) {
	char line[MAX_INPUT_LENGTH+1];
	char key[MAX_INPUT_LENGTH+1];
	struct pixfile * tmp_pixfile;

	while ( fgets(line, MAX_INPUT_LENGTH, f) ) {
		++(*line_counter);
		if ( parse_cfg_check(line) ) {
			return(-1);
		}
		/* Weed out comments and empty lines */
		if ( (line[0] == '#') || (line[0] == '\0') ) {
			continue;
		}
		/* Read the command */
		sscanf( line, "%s", key );
		if (!strcasecmp(key, "refresh")) {
			sscanf(line, "%s %d", key, &x11_set.refresh);
		} else if (!strcasecmp(key, "nomail")) {
			if ( strlen(line) > (strlen(key) + 1) ) {
				tmp_pixfile = (struct pixfile *) 
					malloc(sizeof(struct pixfile));
				if ( ! tmp_pixfile ) {
					err_printf("asmail: parse_cfg_animate: failed to allocate memory for the pixfile\n");
					perror("asmail: parse_cfg_animate: ");
					return(-1);
				}
				strcpy(tmp_pixfile->name, &line[strlen(key)+1]);
				tmp_pixfile->next = x11_set.nomail;
				x11_set.nomail = tmp_pixfile;
			}
		} else if (!strcasecmp(key, "old")) {
			if ( strlen(line) > (strlen(key) + 1) ) {
				tmp_pixfile = (struct pixfile *) 
					malloc(sizeof(struct pixfile));
				if ( ! tmp_pixfile ) {
					err_printf("asmail: parse_cfg_animate: failed to allocate memory for the pixfile\n");
					perror("asmail: parse_cfg_animate: ");
					return(-1);
				}
				strcpy(tmp_pixfile->name, &line[strlen(key)+1]);
				tmp_pixfile->next = x11_set.oldmail;
				x11_set.oldmail = tmp_pixfile;
			}
		} else if (!strcasecmp(key, "new")) {
			if ( strlen(line) > (strlen(key) + 1) ) {
				tmp_pixfile = (struct pixfile *) 
					malloc(sizeof(struct pixfile));
				if ( ! tmp_pixfile ) {
					err_printf("asmail: parse_cfg_animate: failed to allocate memory for the pixfile\n");
					perror("asmail: parse_cfg_animate: ");
					return(-1);
				}
				strcpy(tmp_pixfile->name, &line[strlen(key)+1]);
				tmp_pixfile->next = x11_set.newmail;
				x11_set.newmail = tmp_pixfile;
			}
		} else if (!strcasecmp(key, "frame")) {
			if ( strlen(line) > (strlen(key) + 1) ) {
				if ( !strcasecmp(&line[strlen(key)+1], "none") ) {
					x11_set.use_frame = 0;
				} else {
					tmp_pixfile = (struct pixfile *) 
						malloc(sizeof(struct pixfile));
					if ( ! tmp_pixfile ) {
						err_printf("asmail: parse_cfg_animate: failed to allocate memory for the pixfile\n");
						perror("asmail: parse_cfg_animate: ");
						return(-1);
					}
					strcpy(tmp_pixfile->name, &line[strlen(key)+1]);
					tmp_pixfile->next = x11_set.frame;
					x11_set.frame = tmp_pixfile;
					x11_set.use_frame = 1;
				}
			}
		} else if ( !strcmp(key, "}") ) {
			/* config block terminator */
			return(0);
		} else {
			printf("asmail: cannot understand config file %s\n",
					filename);
			printf("asmail: Offending line (%d): %s\n", 
					(*line_counter), line);
			return(-1);
		}
	}
	/*
	 * If we reach here, we did not find the terminating '}'
	 */
	printf("asmail: Did not find terminating '}' for animate configuration block.\n");
	return(-1);
}

/*
 * Inside the X11 settings, parse the block related to the
 * display of message statistics.
 */
int parse_cfg_stat(FILE * f, int * line_counter, char * filename) {
	char line[MAX_INPUT_LENGTH+1];
	char key[MAX_INPUT_LENGTH+1];
	char rest[MAX_INPUT_LENGTH+1];

	while ( fgets(line, MAX_INPUT_LENGTH, f) ) {
		++(*line_counter);
		if ( parse_cfg_check(line) ) {
			return(-1);
		}
		/* Weed out comments and empty lines */
		if ( (line[0] == '#') || (line[0] == '\0') ) {
			continue;
		}
		/* Read the command */
		sscanf( line, "%s", key );
		if (!strcasecmp(key, "total")) {
			sscanf(line, "%s %s", key, rest);
			x11_set.total = strcasecmp(rest, "yes") ? 0 : 1;
		} else if (!strcasecmp(key, "each")) {
			sscanf(line, "%s %s", key, rest);
			x11_set.each = strcasecmp(rest, "yes") ? 0 : 1;
		} else if (!strcasecmp(key, "status")) {
			sscanf(line, "%s %s", key, rest);
			x11_set.status = strcasecmp(rest, "yes") ? 0 : 1;
		} else if (!strcasecmp(key, "old")) {
			sscanf(line, "%s %s", key, rest);
			x11_set.old = strcasecmp(rest, "yes") ? 0 : 1;
		} else if (!strcasecmp(key, "new")) {
			sscanf(line, "%s %s", key, rest);
			x11_set.new = strcasecmp(rest, "yes") ? 0 : 1;
		} else if (!strcasecmp(key, "x")) {
			sscanf(line, "%s %d", key, &x11_set.x);
		} else if (!strcasecmp(key, "y")) {
			sscanf(line, "%s %d", key, &x11_set.y);
		} else if (!strcasecmp(key, "delimiter")) {
			if ( strlen(line) > (strlen(key) + 1) ) {
				strcpy(x11_set.delimiter, &line[strlen(key)+1]);
				strip_quotes(x11_set.delimiter);
			}
		} else if (!strcasecmp(key, "font")) {
			sscanf(line, "%s %s", key, x11_set.font);
		} else if (!strcasecmp(key, "color")) {
			if ( strlen(line) > (strlen(key) + 1) ) {
				strcpy(x11_set.color, &line[strlen(key)+1]);
				strip_quotes(x11_set.color);
			}
		} else if ( !strcmp(key, "}") ) {
			/* config block terminator */
			return(0);
		} else {
			printf("asmail: cannot understand config file %s\n",
					filename);
			printf("asmail: Offending line (%d): %s\n", 
					(*line_counter), line);
			return(-1);
		}
	}
	/*
	 * If we reach here, we did not find the terminating '}'
	 */
	printf("asmail: Did not find terminating '}' for stat configuration block.\n");
	return(-1);
}

/*
 * Parse the X11 settings (were global definitions)
 */
int parse_cfg_x11(FILE * f, int * line_counter, char * filename) {
	char line[MAX_INPUT_LENGTH+1];
	char key[MAX_INPUT_LENGTH+1];
	char rest[MAX_INPUT_LENGTH+1];

	while ( fgets(line, MAX_INPUT_LENGTH, f) ) {
		++(*line_counter);
		if ( parse_cfg_check(line) ) {
			return(-1);
		}
		/* Weed out comments and empty lines */
		if ( (line[0] == '#') || (line[0] == '\0') ) {
			continue;
		}
		/* Read the command */
		sscanf( line, "%s", key );
		if (!strcasecmp(key, "beep")) {
			sscanf(line, "%s %s", key, rest);
			x11_set.beep = strcasecmp(rest, "yes") ? 0 : 1;
		} else if (!strcasecmp(key, "shape")) {
			sscanf(line, "%s %s", key, rest);
			x11_set.shape = strcasecmp(rest, "yes") ? 0 : 1;
		} else if (!strcasecmp(key, "on_mouse_left")) {
			if ( strlen(line) > (strlen(key) + 1) ) {
				strcpy(x11_set.on_left, &line[strlen(key)+1]);
			}
		} else if (!strcasecmp(key, "on_mouse_middle")) {
			if ( strlen(line) > (strlen(key) + 1) ) {
				strcpy(x11_set.on_middle, &line[strlen(key)+1]);
			}
		} else if (!strcasecmp(key, "on_mouse_right")) {
			if ( strlen(line) > (strlen(key) + 1) ) {
				strcpy(x11_set.on_right, &line[strlen(key)+1]);
			}
		} else if (!strcasecmp(key, "on_new_mail")) {
			if ( strlen(line) > (strlen(key) + 1) ) {
				strcpy(x11_set.on_new_mail, &line[strlen(key)+1]);
			}
		} else if (!strcasecmp(key, "font")) {
			if ( strlen(line) > (strlen(key) + 1) ) {
				strcpy(x11_set.font, &line[strlen(key)+1]);
			}
		} else if (!strcasecmp(key, "stat")) {
			sscanf( line, "%s %s", key, rest);
			if ( !strcmp(rest, "{") ) {
				if ( parse_cfg_stat(f, line_counter, filename) ) {
					return(-1);
				}
			}
		} else if (!strcasecmp(key, "animate")) {
			sscanf( line, "%s %s", key, rest);
			if ( !strcmp(rest, "{") ) {
				if ( parse_cfg_animate(f, line_counter, 
							filename) ) {
					return(-1);
				}
			}
		} else if ( !strcmp(key, "}") ) {
			/* config block terminator */
			return(0);
		} else {
			printf("asmail: cannot understand config file %s\n",
					filename);
			printf("asmail: Offending line (%d): %s\n", 
					(*line_counter), line);
			return(-1);
		}
	}
	/*
	 * If we reach here, we did not find the terminating '}'
	 */
	printf("asmail: Did not find terminating '}' for X11 configuration block.\n");
	return(-1);
}

/*
 * Initialize the new mailbox record and return it
 */
int init_mbox( struct mbox_struct ** mb ) {
	struct mbox_struct * new_mbox;
	new_mbox = (struct mbox_struct *) malloc(sizeof(struct mbox_struct));
	if ( ! new_mbox ) {
		printf("asmail: failed to allocate memory for the mailbox control structure.\n");
		return(-1);
	}
	if ( pthread_mutex_init(&new_mbox->mutex, NULL) ) {
		printf("asmail: parse_cfg_mbox: failed to initialize mutex.\n");
		return(-1);
	}
	new_mbox->type = 0;
	strcpy(new_mbox->file, "");
	strcpy(new_mbox->server, "");
	strcpy(new_mbox->user, "");
	strcpy(new_mbox->pass, "");
	strcpy(new_mbox->mbox, "");
	new_mbox->auth = AUTH_PLAIN | AUTH_MD5;
	new_mbox->port = 0;
	new_mbox->timeout = 60; /* Default timeout for IP connections, sec */
	new_mbox->update = 10;
	new_mbox->flags = FLAG_DEFAULT;
	new_mbox->status = STAT_IDLE;
	new_mbox->mail = MAIL_NONE;
	new_mbox->ctotal = 0;
	new_mbox->cnew = 0;
#ifdef HAVE_OPENSSL_SSL_H
	strcpy(new_mbox->trustedCaDir, "");
#endif
	new_mbox->next = NULL;
	*mb = new_mbox;
	return(0);
}

/*
 * Parse the configuration block related to a mailbox
 */
int parse_cfg_mbox(FILE * f, int * line_counter, char * filename) {
	char line[MAX_INPUT_LENGTH+1];
	char key[MAX_INPUT_LENGTH+1];
	char rest[MAX_INPUT_LENGTH+1];
	struct mbox_struct * new_mbox;
	char * str_ptr;

	if ( init_mbox(&new_mbox) ) {
		return(-1);
	}
	while ( fgets(line, MAX_INPUT_LENGTH, f) ) {
		++(*line_counter);
		if ( parse_cfg_check(line) ) {
			return(-1);
		}
		/* Weed out comments and empty lines */
		if ( (line[0] == '#') || (line[0] == '\0') ) {
			continue;
		}
		/* Read the command */
		sscanf( line, "%s", key );
		if (!strcasecmp(key, "type")) {
			sscanf(line, "%s %s", key, rest);
			if ( !strcasecmp(rest, "mbox") ) {
				new_mbox->type = MBOX_FILE;
			} else if ( !strcasecmp(rest, "maildir") ) {
				new_mbox->type = MBOX_DIR;
			} else if ( !strcasecmp(rest, "mh") ) {
				new_mbox->type = MBOX_MH;
			} else if ( !strcasecmp(rest, "pop3") ) {
				new_mbox->type = MBOX_POP3;
			} else if ( !strcasecmp(rest, "imap") ) {
				new_mbox->type = MBOX_IMAP;
			} else {
				printf("asmail: don't know how to handle mailbox type '%s'\n",
						rest);
				printf("asmail: Offending line (%d): %s\n", 
						(*line_counter), line);
			}
		} else if (!strcasecmp(key, "update")) {
			sscanf(line, "%s %d", key, &new_mbox->update);
		} else if (!strcasecmp(key, "file")) {
			sscanf(line, "%s %s", key, new_mbox->file);
		} else if (!strcasecmp(key, "server")) {
			sscanf(line, "%s %s", key, new_mbox->server);
		} else if (!strcasecmp(key, "user")) {
			sscanf(line, "%s %s", key, new_mbox->user);
		} else if (!strcasecmp(key, "password")) {
			sscanf(line, "%s %s", key, new_mbox->pass);
			passwords_in_config = 1;
		} else if (!strcasecmp(key, "auth")) {
			/* Watch the magic: empty list (just the keyword
			 * without parameters) resets the settings to
			 * none */
			new_mbox->auth = AUTH_NONE;
			if ( strlen(line) > (strlen(key) + 1) ) {
				new_mbox->auth = 0;
				strcpy(rest, &line[strlen(key)+1]);
				str_ptr = strtok(rest, " \t\n");
				while ( str_ptr ) {
					sscanf(str_ptr, "%s", key);
					if ( !strcasecmp(key, "plain")) {
						new_mbox->auth |= AUTH_PLAIN;
					} else if (!strcasecmp(key, "md5")) {
						new_mbox->auth |= AUTH_MD5;
					} else {
						printf("asmail: cannot understand authorisation scheme '%s'\n", key);
						printf("asmail: Offending configuration line (%d): %s\n", (*line_counter), line);
						return(-1);
					}
					str_ptr = strtok(NULL, " \t\n");
				}
			}
		} else if (!strcasecmp(key, "port")) {
			sscanf(line, "%s %d", key, &new_mbox->port);
		} else if (!strcasecmp(key, "timeout")) {
			sscanf(line, "%s %d", key, &new_mbox->timeout);
		} else if (!strcasecmp(key, "mailbox")) {
			sscanf(line, "%s %s", key, new_mbox->mbox);
		} else if (!strcasecmp(key, "unread-is-new")) {
			sscanf(line, "%s %s", key, rest);
			if ( ! strcasecmp(rest, "yes") ) {
				new_mbox->flags |= FLAG_UNREAD_AS_NEW;
			} else {
				new_mbox->flags &= ~FLAG_UNREAD_AS_NEW;
			}
		} else if (!strcasecmp(key, "use-mh-sequences")) {
			sscanf(line, "%s %s", key, rest);
			if ( ! strcasecmp(rest, "yes") ) {
				new_mbox->flags |= FLAG_USE_MH_SEQ;
			} else {
				new_mbox->flags &= ~FLAG_USE_MH_SEQ;
			}
		} else if (!strcasecmp(key, "persistent")) {
			sscanf(line, "%s %s", key, rest);
			if ( ! strcasecmp(rest, "yes") ) {
				new_mbox->flags |= FLAG_PERSISTENT_CONN;
			} else {
				new_mbox->flags &= ~FLAG_PERSISTENT_CONN;
			}
		} else if (!strcasecmp(key, "ssl")) {
#ifdef HAVE_OPENSSL_SSL_H
			sscanf(line, "%s %s", key, rest);
			if ( ! strcasecmp(rest, "yes") ) {
				new_mbox->flags |= FLAG_SSL;
			} else {
				new_mbox->flags &= ~FLAG_SSL;
			}
#else
			printf("asmail: ssl support is not available, ignoring ssl statement\n");
#endif
		} else if (!strcasecmp(key, "trustedCaDir")) {
#ifdef HAVE_OPENSSL_SSL_H
			sscanf(line, "%s %s", key, new_mbox->trustedCaDir);
#else
			printf("asmail: ssl support is not available, ignoring trustedCaDir statement\n");
#endif
		} else if ( !strcmp(key, "}") ) {
         if ((new_mbox->flags & FLAG_SSL) && (new_mbox->type != MBOX_IMAP))
            printf("asmail: ssl is only supported with imap mailboxes, ignoring ssl statement\n");
			/* config block terminator */
			new_mbox->next = mbox;
			mbox = new_mbox;
			return(0);
		} else {
			printf("asmail: cannot understand config file %s\n",
					filename);
			printf("asmail: Offending line (%d): %s\n", 
					(*line_counter), line);
			return(-1);
		}
	}
	/*
	 * If we reach here, we did not find the terminating '}'
	 */
	printf("asmail: Did not find terminating '}' for mailbox configuration block.\n");
	return(-1);
}

/*
 * Parse the specified config file. Return 0 on success, -1 = error.
 */
int parse_cfg(char * filename) {
	FILE * f;
	struct stat f_stat;
	int line_counter = 0;
	char line[MAX_INPUT_LENGTH+1];
	char key[MAX_INPUT_LENGTH+1];
	char rest[MAX_INPUT_LENGTH+1];

	if ( stat(filename, &f_stat) ) {
		err_printf("asmail: Cannot stat the config file (%s).\n", filename);
		/* if the file does not exist, we still can run with
		 * the default settings using $MAIL unless a config
		 * file was given on command line. */
		if ( ! flag_config_specified )
			if ( ENOENT == errno )
				return(-2);
		perror("asmail: parse_cfg");
		return(-1);
	}
	if ( ( f = fopen(filename, "r") ) == NULL ) {
		err_printf("asmail: Cannot open the config file (%s).\n", filename);
		perror("asmail: parse_cfg");
		return(-1);
	}

	while ( fgets(line, MAX_INPUT_LENGTH, f) ) {
		++line_counter;
		if ( parse_cfg_check(line) ) {
			return(-1);
		}
		/* Weed out comments and empty lines */
		if ( (line[0] == '#') || (line[0] == '\0') ) {
			continue;
		}
		/* Read the command */
		sscanf( line, "%s", key );
		if (!strcasecmp(key, "x11")) {
			sscanf( line, "%s %s", key, rest);
			if ( !strcmp(rest, "{") ) {
				if ( parse_cfg_x11(f, &line_counter, filename) ) {
					return(-1);
				}
			}
		} else if (!strcasecmp(key, "mailbox")) {
			sscanf( line, "%s %s", key, rest);
			if ( !strcmp(rest, "{") ) {
				if ( parse_cfg_mbox(f, &line_counter, filename) ) {
					return(-1);
				}
			}
		} else {
			printf("asmail: cannot understand config file %s\n",
					filename);
			printf("asmail: Offending line (%d): %s\n", 
					line_counter, line);
			return(-1);
		}
	}

	if ( fclose(f) ) {
		err_printf("asmail: Cannot close the config file (%s).\n", filename);
		perror("asmail: parse_cfg");
		return(-1);
	}
	if ( ( ((f_stat.st_mode & S_IRWXG) != 0) || 
			((f_stat.st_mode & S_IRWXO) != 0) ) &&
			passwords_in_config ) {
		if ( flag_allow_insecure ) {
			printf("asmail: configuration file mode is insecure but '-insecure' was given.\n");
		} else {
			printf("asmail: The configuration file has permissions for group and/or others set.\n");
			printf("asmail: Configuration file : '%s'.\n", filename);
			printf("asmail: Make sure the permissions are set to 0600 before proceeding.\n");
			return(-1);
		}
	}
	return 0;
}

void mbox_thread( void * ptr ) {
	struct mbox_struct * mb;
	mb = (struct mbox_struct *) ptr;
	if ( mb->type == MBOX_FILE ) {
		mbox_handle( mb );
	} else if ( mb->type == MBOX_DIR ) {
		maildir_handle( mb );
	} else if ( mb->type == MBOX_MH ) {
		mh_handle( mb );
	} else if ( mb->type == MBOX_POP3 ) {
		pop3_handle( mb );
	} else if ( mb->type == MBOX_IMAP ) {
		imap_handle( mb );
	} else {
		printf("Don't know how to handle the mailbox of format '%d'\n",
				mb->type);
		mb->status = STAT_FAIL;
		signal_update();
	}
	pthread_exit(NULL);
}

int main(int argc, char *argv[]) {
	struct mbox_struct * tmp_mbox;
	char * tmp_ptr;

	x11_set.argc = argc;
	x11_set.argv = argv;

	if ( pthread_mutex_init(&update_lock, NULL) ) {
		printf("asmail: main: failed to initialize update mutex.\n");
		exit(-1);
	}
	if ( pthread_cond_init(&update_cv, NULL) ) {
		printf("asmail: main: failed to initialize update condition variable.\n");
		exit(-1);
	}
	if ( pthread_mutex_init(&md5_lock, NULL) ) {
		printf("asmail: main: failed to initialize MD5 mutex.\n");
		exit(-1);
	}
	if ( pthread_mutex_init(&check_lock, NULL) ) {
		printf("asmail: main: failed to initialize check mutex.\n");
		exit(-1);
	}
	if ( pthread_cond_init(&check_cv, NULL) ) {
		printf("asmail: main: failed to initialize check condition variable.\n");
		exit(-1);
	}
	defaults();
	if ( parse_cmd(argc, argv) )
		exit(-1);
	if ( ! flag_no_config ) {
		if ( parse_cfg(config_file_name) == -1 )
			exit(-1);
	}
	if ( ! mbox ) {
		/* no mailbox definitions, try to recover */
		tmp_ptr = getenv("MAIL");
		if ( tmp_ptr ) {
			printf("asmail: no mailbox defnitions, using $MAIL (%s)\n", tmp_ptr);
			if ( init_mbox(&tmp_mbox) ) {
				return(-1);
			}
			strncpy(tmp_mbox->file, tmp_ptr, MAX_INPUT_LENGTH);
			tmp_mbox->type = MBOX_FILE;
			tmp_mbox->next = mbox;
			mbox = tmp_mbox;
		}
	}
	if ( ! mbox ) {
		printf("asmail: no mailboxes to check, cowardly quitting.\n");
		return(-1);
	}
	tmp_mbox = mbox;
	while (tmp_mbox) {
		pthread_create( &tmp_mbox->thread, pthread_attr_default,
			(void*)&mbox_thread, (void*) tmp_mbox);
		pthread_detach( tmp_mbox->thread );
		tmp_mbox = tmp_mbox->next;
	}

	if ( ! flag_no_x ) {
		pthread_create( &x11_set.thread, pthread_attr_default,
			(void*)&startx, (void*) &x11_set);
		pthread_detach( x11_set.thread );
	}

	while (1) {
		pthread_mutex_lock(&update_lock);
		while( update_count <= 0 ) {
			pthread_cond_wait(&update_cv, &update_lock);
		}
		if ( flag_verbose )
			printf("%d > ", update_count);
		--update_count;
		pthread_mutex_unlock(&update_lock);
		tmp_mbox = mbox;
		while ( tmp_mbox ) {
			if ( flag_verbose ) {
				printf("[%d] ", (unsigned)tmp_mbox->thread);
				if ( tmp_mbox->status & STAT_RUN )
					printf("R");
				else
					printf(" ");
				if ( tmp_mbox->status & STAT_FAIL )
					printf("F");
				else
					printf(" ");
				if ( tmp_mbox->status & STAT_CONN )
					printf("C");
				else
					printf(" ");
				if ( tmp_mbox->status & STAT_LOGIN )
					printf("L");
				else
					printf(" ");
				if ( tmp_mbox->status & STAT_TIMEOUT )
					printf("T");
				else
					printf(" ");
				printf(" ");
				switch (tmp_mbox->mail) {
					case MAIL_NONE : printf(" ");
						 break;
					case MAIL_OLD : printf("O");
						 break;
					case MAIL_NEW : printf("N");
				}
				printf(" %d/%d  ", tmp_mbox->cnew, tmp_mbox->ctotal);
			}
			tmp_mbox = tmp_mbox->next;
		}
		if ( flag_verbose )
			printf("\n");
	}
	exit(0);
}
