/*
    Copyright (C) 2001-2006  Ben Kibbey <bjk@luxsci.net>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "mail.h"

#ifndef HAVE_ERR_H
#include "../err.c"
#endif

#ifndef HAVE_STRSEP
#include "../strsep.c"
#endif

void ui_module_init(int *chainable)
{
#ifdef DEBUG
    fprintf(stderr, "%s: ui_module_init()\n", __FILE__);
#endif

    *chainable = 0;
    return;
}

void ui_module_exit()
{
#ifdef DEBUG
    fprintf(stderr, "%s: ui_module_exit()\n", __FILE__);
#endif

    if (aliasbuf)
	munmap(aliasbuf, strlen(aliasbuf));

    aliasbuf = NULL;
    return;
}

/* Remove characters (rm) from string (str). */
static char *stripstr(char *str, char *rm)
{
    static char buf[LINE_MAX];
    char *orm;
    int i = 0, c;

    if (rm == NULL || str == NULL)
	return str;

    while (*str) {
	orm = rm;

	while (*orm) {
	    if (*str == *orm) {
		c = *str++;
		continue;
	    }

	    c = *orm++;
	}

	buf[i++] = *str++;
    }

    buf[i] = '\0';
    return buf;
}

/* Return a string of mail aliases for the user. Looks in /etc/aliases (or
 * whatever was specified at compile-time). The file is read into a buffer
 * only once (mmap(2)). */
static char *mail_aliases(const char *user, const int multi)
{
    char t[LINE_MAX];
    static char aliases[LINE_MAX], *p;
    static int firstrun;
    int i, n;
    struct stat st;
    char m[2] = { multi, '\0' };
    int fd;

    aliases[0] = '\0';

    if ((!aliasbuf && firstrun) || aliasbuf == MAP_FAILED)
	return "!";

    if (!aliasbuf) {
	firstrun = 1;

	if (stat(ALIAS_FILE, &st) == -1)
	    return "!";

	if ((fd = open(ALIAS_FILE, O_RDONLY)) == -1)
	    return "!";

	if ((aliasbuf = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd,
			     0)) == MAP_FAILED) {
	    warn("%s", "mmap()");
	    return "!";
	}

	close(fd);
    }

    for (i = n = 0; aliasbuf[i]; i++) {
	char *last, *name, *tmp;

	while (aliasbuf[i] != '\n')
	    t[n++] = aliasbuf[i++];

	t[n] = 0;
	n = 0;

	if (t[0] == '#' || t[0] == '\0')
	    continue;

	last = t;

	if ((name = strsep(&last, ":")) == NULL)
	    continue;

	if (strcmp(user, name) == 0) {
	    while ((tmp = strsep(&last, ",")) != NULL) {
		tmp = stripstr(tmp, " \n\t");

		strncat(aliases, tmp, sizeof(aliases));
		strncat(aliases, m, sizeof(aliases));
	    }

	    continue;
	}

	while ((tmp = strsep(&last, ",")) != NULL) {
	    tmp = stripstr(tmp, " \n\t");

	    if (strcmp(user, tmp) == 0) {
		strncat(aliases, name, sizeof(aliases));
		strncat(aliases, m, sizeof(aliases));
	    }
	}
    }

    if (aliases[0] == '\0')
	return "-";
    else
	aliases[strlen(aliases) - 1] = '\0';

    p = aliases;
    return p;
}

/* Returns a string of forward aliases for the user. Reads ~/.forward if it
 * exists and is readable. */
static char *forwards(const char *dir, const int multi)
{
    FILE *fp;
    char buf[LINE_MAX], *s;
    static char buf2[FILENAME_MAX];
    int n = 0;
    char m[2] = { multi, '\0' };

    buf[0] = buf2[0] = '\0';
    snprintf(buf2, sizeof(buf2), "%s/.forward", dir);

    if ((fp = fopen(buf2, "r")) == NULL) {
	if (errno == ENOENT)
	    return "-";
	else
	    return "!";
    }

    buf2[0] = '\0';

    while ((s = fgets(buf, sizeof(buf), fp)) != NULL) {
	if (buf[0] == '\n')
	    continue;

	if (buf[strlen(buf) - 1] == '\n')
	    buf[strlen(buf) - 1] = '\0';

	if (n++)
	    strncat(buf2, m, sizeof(buf2));

	strncat(buf2, buf, sizeof(buf2));
    }

    fclose(fp);

    if (!n)
	return "-";

    s = buf2;
    return s;
}

/* /var/mail/username folder size in bytes. */
static char *foldersize(struct stat st)
{
    static char str[33], *p;

    str[0] = '\0';

    snprintf(str, sizeof(str), "%lu", (unsigned long) st.st_size);
    p = str;
    return p;
}

/* This is output if the -h command line option is passed to the main program.
 */
void ui_module_help()
{
#ifdef DEBUG
    fprintf(stderr, "%s: ui_module_help()\n", __FILE__);
#endif

    printf("  Mail information [-M (-%s)]:\n", MAIL_OPTION_ORDER);
    printf("\t-f  forwarding addresses\t");
    printf("-a  mail aliases\n");
    printf("\t-r  folder access (read) time\t");
    printf("-m  folder modification time\n");
    printf("\t-s  folder size\n\n");
    return;
}

/* This is the equivalent to main() only without argc and argv available. */
int ui_module_exec(char ***s, const struct passwd *pw, const int multi_char,
	      const int verbose, char *tf)
{
    char *p = options;
    int gotstat = 0;
    struct stat st;
    char folder[FILENAME_MAX];

    strings = *s;
    folder[0] = '\0';
    snprintf(folder, sizeof(folder), "%s/%s", _PATH_MAILDIR, pw->pw_name);

    if (stat(folder, &st) != -1)
	gotstat = 1;

    while (*p) {
	switch (*p) {
	    case 's':
		add_string(&strings, (gotstat) ? foldersize(st) : "!");
		break;
	    case 'r':
		add_string(&strings, (gotstat) ? stamp(st.st_atime, tf) : "!");
		break;
	    case 'm':
		add_string(&strings, (gotstat) ? stamp(st.st_mtime, tf) : "!");
		break;
	    case 'f':
		add_string(&strings, forwards(pw->pw_dir, multi_char));
		break;
	    case 'a':
		add_string(&strings, mail_aliases(pw->pw_name, multi_char));
		break;
	    default:
		break;
	}

	p++;
    }

    *s = strings;
    return EXIT_SUCCESS;
}

char *ui_module_options_init(char **defaults)
{
    *defaults = "M";
    return MAIL_OPTION_STRING;
}

/* Check module option validity. */
int ui_module_options(int argc, char **argv)
{
    int opt;
    char *p = options;

#ifdef DEBUG
    fprintf(stderr, "%s: ui_module_options()\n", __FILE__);
#endif

    while ((opt = getopt(argc, argv, MAIL_OPTION_STRING)) != -1) {
	switch (opt) {
	    case 'f':
	    case 's':
	    case 'r':
	    case 'm':
	    case 'a':
	    case 'M':
		break;
	    case '?':
		warnx("mail: invalid option -- %c", optopt);
	    default:
		return 1;
	}

	/*
	 * This option '-M' sets all available options for this module. 
	 */
	if (opt == 'M') {
	    strncpy(options, MAIL_OPTION_ORDER, sizeof(options));
	    break;
	}

	*p++ = opt;
	*p = '\0';
    }

    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1