/*
 * Copyright (c) 2005 Peter Postma <peter@pointless.nl>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/stat.h>

#include <err.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* Folders to skip. */
static const char * const skip[] = {
	".Spam", NULL
};

struct folder {
	char		*name;
	int		 newmsgs;
	struct folder	*next;
	struct folder	*prev;
};

static int
check_skip(const char *name)
{
	const char * const *p;

	for (p = skip; *p != NULL; p++)
		if (strcmp(name, *p) == 0)
			return (1);
	return (0);
}

static int
folder_count_new(const char *folder)
{
	struct dirent	*dp;
	DIR		*dirp;
	char		*p, buf[MAXPATHLEN];
	int		 count = 0;

	/* Count messages in new. */
	snprintf(buf, sizeof(buf), "%s/new", folder);
	if ((dirp = opendir(buf)) == NULL) {
		warn("Can't open directory `%s'", buf);
		return (0);
	}
	while ((dp = readdir(dirp)) != NULL) {
		if (dp->d_name[0] == '.')
			continue;
		count++;
	}
	closedir(dirp);

	/* Count unread messages in cur. */
	snprintf(buf, sizeof(buf), "%s/cur", folder);
	if ((dirp = opendir(buf)) == NULL) {
		warn("Can't open directory `%s'", buf);
		return (0);
	}
	while ((dp = readdir(dirp)) != NULL) {
		if (dp->d_name[0] == '.')
			continue;
		if ((p = strchr(dp->d_name, ':')) == NULL)
			continue;
		for (; *p != '\0' && *p != 'S'; p++)
			continue;
		if (*p == '\0')
			count++;
	}
	closedir(dirp);

	return (count);
}

static struct folder *
folder_scan(const char *dir)
{
	struct folder	*fp, *n, *last = NULL, *head = NULL;
	struct dirent	*dp;
	struct stat	 sb;
	DIR		*dirp;
	char		 buf[MAXPATHLEN];

	/* Scan for folders. */
	if ((dirp = opendir(dir)) == NULL) {
		warn("Can't open directory `%s'", dir);
		return (NULL);
	}
	while ((dp = readdir(dirp)) != NULL) {
		if (strcmp(dp->d_name, ".") == 0 ||
		    strcmp(dp->d_name, "..") == 0)
			continue;
		if (dp->d_name[0] != '.')
			continue;
		if (check_skip(dp->d_name))
			continue;
		snprintf(buf, sizeof(buf), "%s/%s", dir, dp->d_name);
		if (stat(buf, &sb) < 0 || !S_ISDIR(sb.st_mode))
			continue;
		fp = malloc(sizeof(struct folder));
		if (fp == NULL) {
			warn("Failed to allocate memory");
			break;
		}
		fp->name = strdup(dp->d_name + 1);
		if (fp->name == NULL) {
			warn("Failed to allocate memory");
			break;
		}
		fp->newmsgs = folder_count_new(buf);

		/* Keep the list ordered alphabetically. */
		for (n = head; n != NULL; last = n, n = n->next)
			if (strcmp(fp->name, n->name) < 0)
				break;
		if (n == NULL) {
			if (head == NULL || last == NULL) {
				head = fp;
				fp->prev = NULL;
				fp->next = NULL;
			} else {
				fp->prev = last;
				fp->next = last->next;
				if (last->next != NULL)
					last->next->prev = fp;
				last->next = fp;
			}
		} else {
			fp->prev = n->prev;
			fp->next = n;
			if (n->prev == NULL)
				head = fp;
			else
				n->prev->next = fp;
			n->prev = fp;
		}
	}
	closedir(dirp);

	/* Add Inbox to the begin. */
	fp = malloc(sizeof(struct folder));
	if (fp == NULL) {
		warn("Failed to allocate memory");
		return (NULL);
	}
	fp->name = strdup("Inbox");
	if (fp->name == NULL) {
		warn("Failed to allocate memory");
		return (NULL);
	}
	fp->newmsgs = folder_count_new(dir);
	fp->next = head;
	head = fp;

	return (head);
}

int
main(void)
{
	struct folder	*fp, *head;
	const char	*home;
	char		 buf[MAXPATHLEN];
	int		 total = 0;

	if ((home = getenv("HOME")) == NULL)
		err(EXIT_FAILURE, "Can't get `HOME' from environment");

        snprintf(buf, sizeof(buf), "%s/Maildir", home);

	head = folder_scan(buf);
	if (head == NULL) {
		errx(EXIT_FAILURE, "Failed to scan folders in `%s'", buf);
	} else {
		for (fp = head; fp != NULL; fp = fp->next)
			total += fp->newmsgs;
		if (total > 0) {
			printf("You've %d new mail%s.\n\n", total,
			    (total != 1) ? "s" : "");
			for (fp = head; fp != NULL; fp = fp->next)
				if (fp->newmsgs > 0)
					printf("%s: %d\n", fp->name, fp->newmsgs);
		} else
			printf("No new mail.\n");
	}

	return (0);
}

