/*
 * Copyright (c) 1998,1999,2000  Kazushi (Jam) Marukawa
 * All rights of my changes are 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 in the documentation and/or other materials provided with
 *    the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
 */

#ifdef SOCKS
#include <socks.h>
#endif

#include <sys/types.h>
#ifdef BSD
#include <sys/errno.h>
#endif
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pwd.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>

#include "leafnode.h"


static void getactive(void)
{
    const char* s;

    nntpactive();

    s = getactivefname(servers->servername);
    unlink(s);
    close(open(s, O_WRONLY|O_CREAT, 0664));
    writeactive();		/* write groupinfo file */
}

static void fixgroup(struct newsgroup* g)
{
    DIR* d;
    struct dirent* de;
    char* p;
    unsigned long art;
    unsigned long xfirst, xlast;

    /* eliminate empty groups */
    if (!chdirgroup(g, 0))
	return;

    /* find article range, without locking problems */
    d = opendir(".");
    if (!d) {
	mysyslog(LOG_ERR, "opendir: %s", strerror(errno));
	return;
    }

    xfirst = ULONG_MAX;
    xlast = 0;
    while ((de=readdir(d))) {
	art = strtoul(de->d_name, &p, 10);
	if (p && !*p) {
	    if (art < xfirst)
		xfirst = art;
	    if (art > xlast)
		xlast = art;
	}
    }
    closedir(d);

    if (xlast < xfirst)
	return;

    if (xfirst > g->first || xlast > g->last) {
	unsigned long oldxfirst = g->first;
	unsigned long oldxlast = g->last;

	if (xfirst > g->first)
	    g->first = xfirst;
	if (xlast > g->last)
	    g->last = xlast;

	mysyslog(LOG_INFO, "fixed %s: %lu, %lu -> %lu, %lu",
		 g->name, oldxfirst, oldxlast, g->first, g->last);
	if (verbose) {
	    fprintf(stderr, "fixed %s: %lu, %lu -> %lu, %lu\n",
		    g->name, oldxfirst, oldxlast, g->first, g->last);
	}
    }
}


static void fixgroupinfo(struct newsgroup* g)
{
    if (g) {
	fixgroupinfo(g->right);
	fixgroup(g);
	fixgroupinfo(g->left);
    }
}


static void usage(void)
{
    fprintf(stderr, "Usage: leaftool [-hviu] [-rl <msg-id>...]\n"
	    "  -h: show this usage\n"
	    "  -v: more verbose (may be repeated)\n"
	    "  -i: collect news group information via NNTP\n"
	    "  -u: correct actives by checking all articles\n"
	    "  -r: remove all articles containing message-id\n"
	    "  -l: make a list of articles containing message-id "
	    "(conflicts with -r)\n");
    exit(1);
}


int main(int argc, char** argv)
{
    struct passwd* pw;
    int option;
    extern int optind;
    int opti = 0;
    int optu = 0;
    int optr = 0;
    int optl = 0;

    verbose = 0;

    pw = getpwnam("news");
    if (!pw) {
	fprintf(stderr, "no such user: news\n");
	exit(1);
    }

    setregid(-1, pw->pw_gid);
    setreuid(-1, pw->pw_uid);

    if (geteuid() != pw->pw_uid || getegid() != pw->pw_gid) {
	fprintf(stderr, "%s: must be run as news or root\n", argv[0]);
	exit(1);
    }

    while ((option = getopt(argc, argv, "hviurl")) != -1) {
	if (option == 'h')
	    usage();
	else if (option == 'v')
	    verbose++;
	else if (option == 'i')
	    opti = 1;
	else if (option == 'u')
	    optu = 1;
	else if (option == 'r')
	    optr = 1;
	else if (option == 'l')
	    optl = 1;
    }
    argc -= optind - 1;
    argv += optind - 1;

    if (argc == 1 && ((!opti && !optu) || optr || optl))
	usage();
    else if (argc != 1 && !optr && !optl)
	usage();
    else if (optr && optl)
	usage();

    if (verbose) {
	printf("%s: verbosity level is %d\n", version, verbose);
    }

    whoami();

    umask(2);

    openlog("leaftool", LOG_PID|LOG_CONS, LOG_NEWS);

    readconfig();

    if (opti) {
	/*
	 * Force read all acrive and re-create groupinfo file.
	 */
	if (verbose)
	    printf("update all information\n");
	lockactive();
	readactive();
	correctnewsdesc();
	while (servers) {
	    if (verbose)
		printf("Trying to connect to %s ... ", servers->servername);
	    if (nntpreconnect()) {
		if (verbose)
		    printf("connected.\n");
		getactive();	/* get active from server */
	    } else {
		if (verbose)
		    printf("failed.\n");
	    }
	    servers = servers->next;
	}
	writeactive();
	unlockactive();
    }
    if (optu) {
	/*
	 * Update first and last value in active file.
	 * When fetchnews program halted after writing some article and before
	 * writing active file, the number may be corrupted.
	 */
	if (verbose)
	    printf("update index information\n");
	lockactive();
	readactive();
	fixgroupinfo(active);
	writeactive();
	unlockactive();
    }
    if (optr) {
	int i;

	if (verbose)
	    printf("remove articles\n");
	readactive();
	for (i = 1; i < argc; i++) {
	    removearts(argv[i]);
	}
    }
    if (optl) {
	int i;

	if (verbose)
	    printf("list articles\n");
	readactive();
	for (i = 1; i < argc; i++) {
	    struct articles* p = findarticles(argv[i]);
	    int j;
	    if (p) {
		printf("articles containing %s are...\n", argv[i]);
		for (j = 0; j < p->num; j++)
		    printf("%s:%lu\n", p->arts[j].ng->name, p->arts[j].art);
	    } else {
		printf("cannot find any articles containing %s\n", argv[i]);
	    }
	}
    }

    mysyslog(LOG_INFO, "done");

    exit(0);
}


syntax highlighted by Code2HTML, v. 0.9.1