aboutsummaryrefslogtreecommitdiffstats
path: root/uid.c
blob: 06acd5a9820c46e56a4b5cba97dc0987f4c1e04a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/* Copyright 1993-95 by Carl Harris, Jr. Copyright 1996 by Eric S. Raymond
 * All rights reserved.
 * For license terms, see the file COPYING in this directory.
 */

/***********************************************************************
  module:       uid.c
  project:      fetchmail
  programmer:   Eric S. Raymond
  description:	UID list handling

 ***********************************************************************/

#include <config.h>

#include <stdio.h>

#if defined(STDC_HEADERS)
#include <stdlib.h>
#include <string.h>
#endif

#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif

#include "fetchmail.h"

/*
 * Machinery for handling UID lists live here.  This is mainly to support
 * RFC1725-conformant POP3 servers without a LAST command, but may also be
 * useful for making the IMAP4 querying logic UID-oriented, if I feel
 * sufficiently motivated at some point.
 *
 * Here's the theory:
 *
 * At start of a query, we have a (possibly empty) list of UIDs to be
 * considered `already seen'.  These are messages that were left in
 * the mailbox and *not deleted* on previous queries (we don't need to
 * remember the UIDs of deleted messages because ... well, they're gone!).
 * This list is set up by initialized_saved_list() from the .fetchids 
 * file and hangs off the host's `saved' member.
 *
 * Early in the query, during the execution of the protocol-specific 
 * getrange code, the driver expects that the host's `listed' member
 * will be filled with a list of UIDs and message numbers representing
 * the current mailbox state.  If this list is empty, the server did
 * not respond to the request for a UID listing.
 *
 * Each time a message is fetched, we can check its UID against the
 * `saved' list to see if it is old.  If not, it should be downloaded
 * (and possibly deleted).  It should be downloaded anyway if --all
 * is on.  It should not be deleted if --keep is on.
 *
 * Each time a message is deleted, we remove its id from the `listed'
 * member.
 *
 * At the end of the query, whatever remains in the `listed' member
 * (because it was not deleted) becomes the `saved' list.  The old
 * `saved' list is freed.
 */

/* UIDs associated with un-queried hosts */
static struct idlist *scratchlist;

void initialize_saved_lists(hostlist, idfile)
/* read file of saved IDs and attach to each host */
struct hostrec *hostlist;
char *idfile;
{
    int	st;
    FILE	*tmpfp;
    struct hostrec *hostp;

    /* make sure lists are initially empty */
    for (hostp = hostlist; hostp; hostp = hostp->next)
	hostp->saved = hostp->mailbox = (struct idlist *)NULL;

    /* let's get stored message UIDs from previous queries */
    if ((tmpfp = fopen(idfile, "r")) != (FILE *)NULL) {
	char buf[POPBUFSIZE+1], host[HOSTLEN+1], id[IDLEN+1];

	while (fgets(buf, POPBUFSIZE, tmpfp) != (char *)NULL)
	{
	    if ((st = sscanf(buf, "%s %s\n", host, id)) == 2)
	    {
		for (hostp = hostlist; hostp; hostp = hostp->next)
		{
		    if (strcmp(host, hostp->servername) == 0)
		    {
			save_uid(&hostp->saved, -1, id);
			break;
		    }
		}

		/* if it's not in a host we're querying, save it anyway */
		if (hostp == (struct hostrec *)NULL)
		    save_uid(&scratchlist, -1, buf);
	    }
	}
	fclose(tmpfp);
    }
}

void save_uid(idl, num, str)
/* save a number/UID pair on the given UID list */
struct idlist **idl;
int num;
char *str;
{
    struct idlist *new;

    new = (struct idlist *)xmalloc(sizeof(struct idlist));
    new->num = num;
    new->id = strdup(str);
    new->next = *idl;
    *idl = new;
}

void free_uid_list(idl)
/* free the given UID list */
struct idlist **idl;
{
    if (*idl == (struct idlist *)NULL)
	return;

    free_uid_list(&(*idl)->next);
    free ((*idl)->id);
    free(*idl);
    *idl = (struct idlist *)NULL;
}

int uid_in_list(idl, str)
/* is a given ID in the given list? */
struct idlist **idl;
char *str;
{
    if (*idl == (struct idlist *)NULL)
	return(0);
    else if (strcmp(str, (*idl)->id) == 0)
	return(1);
    else
	return(uid_in_list(&(*idl)->next, str));
}

int delete_uid(idl, str)
/* delete given UID from given list */
struct idlist **idl;
char *str;
{
    if (*idl == (struct idlist *)NULL)
	return(0);
    else if (strcmp((*idl)->id, str) == 0)
    {
	struct idlist	*next = (*idl)->next;

	free ((*idl)->id);
	free(*idl);
	*idl = next;
	return(1);
    }
    else
	return(delete_uid(&(*idl)->next, str));
    return(0);
}

void update_uid_lists(hostp)
/* perform end-of-query actions on UID lists */
struct hostrec *hostp;
{
    /*
     * Replace `saved' list with `mailbox' list as modified by deletions.
     */
    free_uid_list(&hostp->saved);
    hostp->saved = hostp->mailbox;
}

void write_saved_lists(hostlist, idfile)
struct hostrec *hostlist;
char *idfile;
{
    int	st, idcount;
    FILE	*tmpfp;
    struct hostrec *hostp;
    struct idlist *idp;

    /* if all lists are empty, nuke the file */
    idcount = 0;
    for (hostp = hostlist; hostp; hostp = hostp->next) {
	if (hostp->saved)
	    idcount++;
    }

    /* either nuke the file or write updated last-seen IDs */
    if (!idcount)
	unlink(idfile);
    else
	if ((tmpfp = fopen(idfile, "w")) != (FILE *)NULL) {
	    for (hostp = hostlist; hostp; hostp = hostp->next) {
		for (idp = hostp->saved; idp; idp = idp->next)
		    fprintf(tmpfp, "%s %s\n", hostp->servername, idp->id);
	    }
	    for (idp = scratchlist; idp; idp = idp->next)
		fputs(idp->id, tmpfp);
	    fclose(tmpfp);
	}
}