/* trn-artchk.c
*/
/* This software is copyrighted as detailed in the LICENSE file. */


/* A program to check an article's validity and print warnings if problems
** are found.
**
** Usage: trn-artchk <article> <maxLineLen> <newsgroupsFile> <activeFile>
*/

#include "EXTERN.h"
#include "common.h"
#include "util2.h"
#include "util3.h"
#ifdef SUPPORT_NNTP
#include "nntpclient.h"
#include "nntpinit.h"
#endif

#define MAXNGS 100

#ifdef SUPPORT_NNTP
int server_connection _((void));
int nntp_handle_timeout _((void));

char* server_name;
char* nntp_auth_file;
#endif /* SUPPORT_NNTP */

int debug = 0;

char* homedir;
char* dotdir;

char nullstr[1] = "";
char ess[2] = "s";

int
main(argc, argv)
int argc;
char* argv[];
{
    FILE* fp;
    FILE* fp_active = NULL;
    FILE* fp_ng = NULL;
    bool check_active = FALSE;
    bool check_ng = FALSE;
    char buff[LBUFLEN];
    char* cp;
    char* cp2;
    char* ngptrs[MAXNGS];
    int nglens[MAXNGS];
    int foundactive[MAXNGS];
    int i, col, max_col_len, line_num = 0, ngcnt = 0, ngleft;
    int found_newsgroups = 0;

    homedir = getenv("HOME");
    if (homedir == NULL)
	homedir = getenv("LOGDIR");
    dotdir = getenv("DOTDIR");
    if (!dotdir)
	dotdir = homedir;

    if (argc != 5 || !(max_col_len = atoi(argv[2]))) {
	fprintf(stderr, "\
Usage: trn-artchk <article> <maxLineLen> <newsgroupsFile> <activeFile>\n");
	exit(1);
    }

    if ((fp = fopen(argv[1], "r")) == NULL) {
	fprintf(stderr, "trn-artchk: unable to open article `%s'.\n", argv[1]);
	exit(1);
    }

    /* Check the header for proper format and report on the newsgroups */
    while (fgets(buff, LBUFLEN, fp)) {
	line_num++;
	buff[strlen(buff)-1] = '\0';
	if (!*buff)
	    break;
	if (*buff == ' ' || *buff == '\t')
	    continue;
	if (!(cp = index(buff, ':'))) {
	    printf("\nERROR: line %d is an invalid header line:\n%s\n",
		   line_num, buff);
	    break;
	}
	if (cp[1] != ' ' && cp[1] != '\0') {
	    printf("\n\
ERROR: header on line %d does not have a space after the colon:\n%s\n",
		   line_num, buff);
	}
	if (cp - buff == 10 && strnEQ(buff, "Newsgroups", 10)) {
	    found_newsgroups = 1;
	    for (cp = buff + 11; *cp == ' '; cp++)
		;
	    if (index(cp, ' ')) {
		printf("\n\
ERROR: the \"Newsgroups:\" line has spaces in it that MUST be removed. The\n\
only allowable space is the one separating the colon (:) from the contents.\n\
Use a comma (,) to separate multiple newsgroup names.\n");
		continue;
	    }
	    while (*cp) {
		if (!(cp2 = index(cp, ',')))
		    cp2 = cp + strlen(cp);
		else
		    *cp2++ = '\0';
		if (ngcnt < MAXNGS) {
		    nglens[ngcnt] = strlen(cp);
		    foundactive[ngcnt] = 0;
		    ngptrs[ngcnt] = safemalloc(nglens[ngcnt]+1);
		    strcpy(ngptrs[ngcnt], cp);
		    ngcnt++;
		}
		cp = cp2;
	    }
	    if (!ngcnt) {
		printf("\n\
ERROR: the \"Newsgroups:\" line lists no newsgroups.\n");
		continue;
	    }
	}
    }
    if (!found_newsgroups) {
	printf("\nERROR: the \"Newsgroups:\" line is missing from the header.\n");
    }

    /* Check the body of the article for long lines */
    while (fgets(buff, LBUFLEN, fp)) {
	line_num++;
	col = strlen(buff)-1;
	if (buff[col] != '\n')
	    printf("\n\
Warning: line %d has no trailing newline character and may get lost.\n",
		   line_num);
	else
	    buff[col] = '\0';
	col = 0;
	for (cp = buff; *cp; cp++) {
	    if (*cp == '\t')
		col += 8 - (col%8);
	    else
		col++;
	}
	if (col > max_col_len) {
	    printf("\n\
Warning: posting exceeds %d columns.  Line %d is the first long one:\n%s\n",
		   max_col_len, line_num, buff);
	    break;
	}
    }
#ifdef SUPPORT_NNTP
    cp = getenv("NNTPSERVER");
    if (!cp) {
	cp = filexp(SERVER_NAME);
	if (FILE_REF(cp))
	    cp = nntp_servername(cp);
    }
    if (strNE(cp,"local")) {
	server_name = savestr(cp);
	cp = index(server_name, ';');
#ifndef DECNET
	if (!cp)
	    cp = index(server_name, ':');
#endif
	if (cp) {
	    *cp = '\0';
	    nntplink.port_number = atoi(cp+1);
	}
	nntp_auth_file = filexp(NNTP_AUTH_FILE);
	if ((cp = getenv("NNTP_FORCE_AUTH")) != NULL
	 && (*cp == 'y' || *cp == 'Y'))
	    nntplink.flags |= NNTP_FORCE_AUTH_NEEDED;
	if (init_nntp() < 0)
	    server_name = NULL;
    }
#endif
    if (ngcnt) {
	struct stat st;
	if (stat(argv[3], &st) != -1)
	    check_ng = st.st_size > 0 && (fp_ng = fopen(argv[3], "r")) != NULL;
#ifdef SUPPORT_NNTP
	else if (server_name && server_connection())
	    check_ng = TRUE;
#endif
	if (stat(argv[4], &st) != -1)
	    check_active = st.st_size > 0 && (fp_active = fopen(argv[4], "r")) != NULL;
#ifdef SUPPORT_NNTP
	else if (server_name && server_connection())
	    check_active = TRUE;
#endif
    }
    if (ngcnt && (check_ng || check_active)) {
	/* Print a note about each newsgroup */
	printf("\nYour article's newsgroup%s:\n", PLURAL(ngcnt));
	if (!check_active) {
	    for (i = 0; i < ngcnt; i++) {
		foundactive[i] = 1;
	    }
	}
	else if (fp_active != NULL) {
	    ngleft = ngcnt;
	    while (fgets(buff, LBUFLEN, fp_active)) {
		if (!ngleft)
		    break;
		for (i = 0; i < ngcnt; i++) {
		    if (!foundactive[i]) {
			if ((buff[nglens[i]] == '\t' || buff[nglens[i]] == ' ')
			  && strnEQ(ngptrs[i], buff, nglens[i])) {
			    foundactive[i] = 1;
			    ngleft--;
			}
		    }
		}
	    }
	    fclose(fp_active);
	}
#ifdef SUPPORT_NNTP
	else if (server_name) {
	    int listactive_works = 1;
	    for (i = 0; i < ngcnt; i++) {
		if (listactive_works) {
		    sprintf(ser_line, "list active %s", ngptrs[i]);
		    if (nntp_command(ser_line) <= 0)
			break;
		    if (nntp_check() > 0) {
			while (nntp_gets(ser_line, sizeof ser_line) >= 0) {
			    if (nntp_at_list_end(ser_line))
				break;
			    foundactive[i] = 1;
			}
		    }
		    else if (*ser_line == NNTP_CLASS_FATAL) {
			listactive_works = FALSE;
			i--;
		    }
		}
		else {
		    sprintf(ser_line, "GROUP %s", ngptrs[i]);
		    if (nntp_command(ser_line) <= 0)
			break;
		    if (nntp_check() > 0)
			foundactive[i] = 1;
		}
	    }
	}
	if (check_ng && fp_ng == NULL) {
	    fp_ng = fopen(argv[3], "w+");
	    unlink(argv[3]);
	    if (fp_ng != NULL) {
		for (i = 0; i < ngcnt; i++) {
		    /* issue a description list command */
		    sprintf(ser_line, "XGTITLE %s", ngptrs[i]);
		    if (nntp_command(ser_line) <= 0)
			break;
		    /*$$ use list newsgroups if this fails...? */
		    if (nntp_check() > 0) {
			/* write results to fp_ng */
			while (nntp_gets(ser_line, sizeof ser_line) >= 0) {
			    if (nntp_at_list_end(ser_line))
				break;
			    fprintf(fp_ng, "%s\n", ser_line);
			}
		    }
		}
		fseek(fp_ng, 0L, 0);
	    }
	}
#endif
	if (fp_ng != NULL) {
	    ngleft = ngcnt;
	    while (fgets(buff, LBUFLEN, fp_ng)) {
		if (!ngleft)
		    break;
		for (i = 0; i < ngcnt; i++) {
		    if (foundactive[i] && ngptrs[i]) {
			if ((buff[nglens[i]] == '\t' || buff[nglens[i]] == ' ')
			  && strnEQ(ngptrs[i], buff, nglens[i])) {
			    cp = &buff[nglens[i]];
			    *cp++ = '\0';
			    while (*cp == ' ' || *cp == '\t')
				cp++;
			    if (cp[0] == '?' && cp[1] == '?')
				cp = "[no description available]\n";
			    printf("%-23s %s", buff, cp);
			    free(ngptrs[i]);
			    ngptrs[i] = 0;
			    ngleft--;
			}
		    }
		}
	    }
	    fclose(fp_ng);
	}
	for (i = 0; i < ngcnt; i++) {
	    if (!foundactive[i]) {
		printf("%-23s ** invalid news group -- check spelling **\n",
		   ngptrs[i]);
		free(ngptrs[i]);
	    }
	    else if (ngptrs[i]) {
		printf("%-23s [no description available]\n", ngptrs[i]);
		free(ngptrs[i]);
	    }
	}
    }

#ifdef SUPPORT_NNTP
    nntp_close(TRUE);
    if (server_name)
	cleanup_nntp();
#endif

    return 0;
}

#ifdef SUPPORT_NNTP
int
server_connection()
{
    static int server_stat = 0;
    if (!server_stat) {
	if (nntp_connect(server_name,0) > 0)
	    server_stat = 1;
	else
	    server_stat = -1;
    }
    return server_stat == 1;
}
#endif

#ifdef SUPPORT_NNTP
int
nntp_handle_timeout()
{
    fputs("\n503 Server timed out.\n",stderr);
    return -2;
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1