Esempio n. 1
0
/** Finish a successful query */
void uid_swap_lists(struct query *ctl)
{
    /* debugging code */
    if (outlevel >= O_DEBUG)
    {
	if (dofastuidl) {
	    report_build(stdout, GT_("Merged UID list from %s:"), ctl->server.pollname);
	    dump_uid_db(&ctl->oldsaved);
	} else {
	    report_build(stdout, GT_("New UID list from %s:"), ctl->server.pollname);
	    dump_uid_db(&ctl->newsaved);
	}
	report_complete(stdout, "\n");
    }

    /*
     * Don't swap UID lists unless we've actually seen UIDLs.
     * This is necessary in order to keep UIDL information
     * from being heedlessly deleted later on.
     *
     * Older versions of fetchmail did
     *
     *     free_str_list(&scratchlist);
     *
     * after swap.  This was wrong; we need to preserve the UIDL information
     * from unqueried hosts.  Unfortunately, not doing this means that
     * under some circumstances UIDLs can end up being stored forever --
     * specifically, if a user description is removed from .fetchmailrc
     * with UIDLs from that account in .fetchids, there is no way for
     * them to ever get garbage-collected.
     */
    if (uid_db_n_records(&ctl->newsaved))
    {
	swap_uid_db_data(&ctl->newsaved, &ctl->oldsaved);
	clear_uid_db(&ctl->newsaved);
    }
    /* in fast uidl, there is no need to swap lists: the old state of
     * mailbox cannot be discarded! */
    else if (outlevel >= O_DEBUG && !dofastuidl)
	report(stdout, GT_("not swapping UID lists, no UIDs seen this query\n"));
}
Esempio n. 2
0
static void dump_uid_db(struct uid_db *db)
{
	unsigned n_recs;

	n_recs = uid_db_n_records(db);
	if (!n_recs) {
		report_build(stdout, GT_(" <empty>"));
		return;
	}

	traverse_uid_db(db, dump_uid_db_record, &n_recs);
}
Esempio n. 3
0
static int dump_saved_uid(struct uid_db_record *rec, void *unused)
{
    char *t;

    (void)unused;

    t = sdump(rec->id, rec->id_len);
    report_build(stdout, " %s", t);
    free(t);

    return 0;
}
Esempio n. 4
0
static int dump_uid_db_record(struct uid_db_record *rec, void *arg)
{
	unsigned *n_recs;
	char *t;

	n_recs = (unsigned int *)arg;
	--*n_recs;

	t = sdump(rec->id, rec->id_len);
	report_build(stdout, " %s = %s%s", t, str_uidmark(rec->status), *n_recs ? "," : "");
	free(t);

	return 0;
}
Esempio n. 5
0
/** Finish a query which had errors */
void uid_discard_new_list(struct query *ctl)
{
    /* debugging code */
    if (outlevel >= O_DEBUG)
    {
	/* this is now a merged list! the mails which were seen in this
	 * poll are marked here. */
	report_build(stdout, GT_("Merged UID list from %s:"), ctl->server.pollname);
	dump_uid_db(&ctl->oldsaved);
	report_complete(stdout, "\n");
    }

    if (uid_db_n_records(&ctl->newsaved))
    {
	/* new state of mailbox is not reliable */
	if (outlevel >= O_DEBUG)
	    report(stdout, GT_("discarding new UID list\n"));
	clear_uid_db(&ctl->newsaved);
    }
}
Esempio n. 6
0
/** Read saved IDs from \a idfile and attach to each host in \a hostlist. */
void initialize_saved_lists(struct query *hostlist, const char *idfile)
{
    struct stat statbuf;
    FILE	*tmpfp;
    struct query *ctl;

    /* make sure lists are initially empty */
    for (ctl = hostlist; ctl; ctl = ctl->next) {
	ctl->skipped = (struct idlist *)NULL;

	init_uid_db(&ctl->oldsaved);
	init_uid_db(&ctl->newsaved);
    }

    errno = 0;

    /*
     * Croak if the uidl directory does not exist.
     * This probably means an NFS mount failed and we can't
     * see a uidl file that ought to be there.
     * Question: is this a portable check? It's not clear
     * that all implementations of lstat() will return ENOTDIR
     * rather than plain ENOENT in this case...
     */
    if (lstat(idfile, &statbuf) < 0) {
	if (errno == ENOTDIR)
	{
	    report(stderr, "lstat: %s: %s\n", idfile, strerror(errno));
	    exit(PS_IOERR);
	}
    }

    /* let's get stored message UIDs from previous queries */
    if ((tmpfp = fopen(idfile, "r")) != (FILE *)NULL)
    {
	char buf[POPBUFSIZE+1];
	char *host = NULL;	/* pacify -Wall */
	char *user;
	char *id;
	char *atsign;	/* temp pointer used in parsing user and host */
	char *delimp1;
	char saveddelim1;
	char *delimp2;
	char saveddelim2 = '\0';	/* pacify -Wall */

	while (fgets(buf, POPBUFSIZE, tmpfp) != (char *)NULL)
	{
	    /*
	     * At this point, we assume the bug has two fields -- a user@host
	     * part, and an ID part. Either field may contain spurious @ signs.
	     * The previous version of this code presumed one could split at
	     * the rightmost '@'.  This is not correct, as InterMail puts an
	     * '@' in the UIDL.
	     */

	    /* first, skip leading spaces */
	    user = buf + strspn(buf, " \t");

	    /*
	     * First, we split the buf into a userhost part and an id
	     * part ... but id doesn't necessarily start with a '<',
	     * espescially if the POP server returns an X-UIDL header
	     * instead of a Message-ID, as GMX's (www.gmx.net) POP3
	     * StreamProxy V1.0 does.
	     *
	     * this is one other trick. The userhost part
	     * may contain ' ' in the user part, at least in
	     * the lotus notes case.
	     * So we start looking for the '@' after which the
	     * host will follow with the ' ' separator with the id.
	     *
	     * XXX FIXME: There is a case this code cannot handle:
	     * the user name cannot have blanks after a '@'.
	     */
	    if ((delimp1 = strchr(user, '@')) != NULL &&
		(id = strchr(delimp1,' ')) != NULL)
	    {
	        for (delimp1 = id; delimp1 >= user; delimp1--)
		    if ((*delimp1 != ' ') && (*delimp1 != '\t'))
			break;

		/*
		 * It should be safe to assume that id starts after
		 * the " " - after all, we're writing the " "
		 * ourselves in write_saved_lists() :-)
		 */
		id = id + strspn(id, " ");

		delimp1++; /* but what if there is only white space ?!? */
		/* we have at least one @, else we are not in this branch */
		saveddelim1 = *delimp1;		/* save char after token */
		*delimp1 = '\0';		/* delimit token with \0 */

		/* now remove trailing white space chars from id */
		if ((delimp2 = strpbrk(id, " \t\n")) != NULL ) {
		    saveddelim2 = *delimp2;
		    *delimp2 = '\0';
		}

		atsign = strrchr(user, '@');
		/* we have at least one @, else we are not in this branch */
		*atsign = '\0';
		host = atsign + 1;

		/* find uidl db and save it */
		for (ctl = hostlist; ctl; ctl = ctl->next) {
		    if (strcasecmp(host, ctl->server.queryname) == 0
			    && strcasecmp(user, ctl->remotename) == 0) {
			uid_db_insert(&ctl->oldsaved, id, UID_SEEN);
			break;
		    }
		}
		/*
		 * If it's not in a host we're querying,
		 * save it anyway.  Otherwise we'd lose UIDL
		 * information any time we queried an explicit
		 * subset of hosts.
		 */
		if (ctl == (struct query *)NULL) {
		    /* restore string */
		    *delimp1 = saveddelim1;
		    *atsign = '@';
		    if (delimp2 != NULL) {
			*delimp2 = saveddelim2;
		    }
		    save_str(&scratchlist, buf, UID_SEEN);
		}
	    }
	}
	fclose(tmpfp);	/* not checking should be safe, mode was "r" */
    }

    if (outlevel >= O_DEBUG)
    {
	struct idlist	*idp;

	for (ctl = hostlist; ctl; ctl = ctl->next)
	    {
		report_build(stdout, GT_("Old UID list from %s:"),
			     ctl->server.pollname);

		if (!uid_db_n_records(&ctl->oldsaved))
		    report_build(stdout, GT_(" <empty>"));
		else
		    traverse_uid_db(&ctl->oldsaved, dump_saved_uid, NULL);

		report_complete(stdout, "\n");
	    }

	report_build(stdout, GT_("Scratch list of UIDs:"));
	if (!scratchlist)
		report_build(stdout, GT_(" <empty>"));
	else for (idp = scratchlist; idp; idp = idp->next) {
		char *t = sdump(idp->id, strlen(idp->id)-1);
		report_build(stdout, " %s\n", t);
		free(t);
	}
	report_complete(stdout, "\n");
    }
}
Esempio n. 7
0
char *reply_hack(
	char *buf		/* header to be hacked */,
	const char *host	/* server hostname */,
	size_t *length)
/* hack message headers so replies will work properly */
{
    char *from, *cp, last_nws = '\0', *parens_from = NULL;
    int parendepth, state, has_bare_name_part, has_host_part;
#ifndef MAIN
    int addresscount = 1;
#endif /* MAIN */

    if (strncasecmp("From:", buf, 5)
	&& strncasecmp("To:", buf, 3)
	&& strncasecmp("Reply-To:", buf, 9)
	&& strncasecmp("Return-Path:", buf, 12)
	&& strncasecmp("Cc:", buf, 3)
	&& strncasecmp("Bcc:", buf, 4)
	&& strncasecmp("Resent-From:", buf, 12)
	&& strncasecmp("Resent-To:", buf, 10)
	&& strncasecmp("Resent-Cc:", buf, 10)
	&& strncasecmp("Resent-Bcc:", buf, 11)
	&& strncasecmp("Apparently-From:", buf, 16)
	&& strncasecmp("Apparently-To:", buf, 14)
	&& strncasecmp("Sender:", buf, 7)
	&& strncasecmp("Resent-Sender:", buf, 14)
       ) {
	return(buf);
    }

#ifndef MAIN
    if (outlevel >= O_DEBUG) {
	report_build(stdout, GT_("About to rewrite %s...\n"), (cp = sdump(buf, BEFORE_EOL(buf))));
	xfree(cp);
    }

    /* make room to hack the address; buf must be malloced */
    for (cp = buf; *cp; cp++)
	if (*cp == ',' || isspace((unsigned char)*cp))
	    addresscount++;
    buf = (char *)xrealloc(buf, strlen(buf) + addresscount * (strlen(host) + 1) + 1);
#endif /* MAIN */

    /*
     * This is going to foo up on some ill-formed addresses.
     * Note that we don't rewrite the fake address <> in order to
     * avoid screwing up bounce suppression with a null Return-Path.
     */

    parendepth = state = 0;
    has_host_part = has_bare_name_part = FALSE;
    for (from = buf; *from; from++)
    {
#ifdef MAIN
	if (verbose)
	{
	    printf("state %d: %s", state, buf);
	    printf("%*s^\n", (int)(from - buf + 10), " ");
	}
#endif /* MAIN */
	if (state != 2)
	{
	    if (*from == '(')
		++parendepth;
	    else if (*from == ')')
		--parendepth;
	}

	if (!parendepth && !has_host_part)
	    switch (state)
	    {
	    case 0:	/* before header colon */
		if (*from == ':')
		    state = 1;
		break;

	    case 1:	/* we've seen the colon, we're looking for addresses */
		if (!isspace((unsigned char)*from))
		    last_nws = *from;
		if (*from == '<')
		    state = 3;
		else if (*from == '@' || *from == '!')
		    has_host_part = TRUE;
		else if (*from == '"')
		    state = 2;
		/*
		 * Not expanding on last non-WS == ';' deals with groupnames,
		 * an obscure misfeature described in sections
		 * 6.1, 6.2.6, and A.1.5 of the RFC822 standard.
		 */
		else if ((*from == ',' || HEADER_END(from))
			 && has_bare_name_part
			 && !has_host_part
			 && last_nws != ';')
		{
		    int hostlen;
		    char *p;

		    p = from;
		    if (parens_from)
			from = parens_from;
		    while (isspace((unsigned char)*from) || (*from == ','))
			--from;
		    from++;
		    hostlen = strlen(host);
		    for (cp = from + strlen(from); cp >= from; --cp)
			cp[hostlen+1] = *cp;
		    *from++ = '@';
		    memcpy(from, host, hostlen);
		    from = p + hostlen + 1;
		    has_host_part = TRUE;
		} 
		else if (from[1] == '('
			 && has_bare_name_part
			 && !has_host_part
			 && last_nws != ';' && last_nws != ')')
		{
		    parens_from = from;
		} 
		else if (!isspace((unsigned char)*from))
		    has_bare_name_part = TRUE;
		break;

	    case 2:	/* we're in a string */
		if (*from == '"')
		{
		    char	*bp;
		    int		bscount;

		    bscount = 0;
		    for (bp = from - 1; *bp == '\\'; bp--)
			bscount++;
		    if (!(bscount % 2))
			state = 1;
		}
		break;

	    case 3:	/* we're in a <>-enclosed address */
		if (*from == '@' || *from == '!')
		    has_host_part = TRUE;
		else if (*from == '>' && (from > buf && from[-1] != '<'))
		{
		    state = 1;
		    if (!has_host_part)
		    {
			int hostlen;

			hostlen = strlen(host);
			for (cp = from + strlen(from); cp >= from; --cp)
			    cp[hostlen+1] = *cp;
			*from++ = '@';
			memcpy(from, host, hostlen);
			from += hostlen;
			has_host_part = TRUE;
		    }
		}
		break;
	    }

	/*
	 * If we passed a comma, reset everything.
	 */
	if ((from > buf && from[-1] == ',') && !parendepth) {
	  has_host_part = has_bare_name_part = FALSE;
	  parens_from = NULL;
	}
    }

#ifndef MAIN
    if (outlevel >= O_DEBUG) {
	report_complete(stdout, GT_("...rewritten version is %s.\n"),
			(cp = sdump(buf, BEFORE_EOL(buf))));
	xfree(cp)
    }