]> Pileus Git - ~andy/fetchmail/blob - uid.c
Comment fix.
[~andy/fetchmail] / uid.c
1 /* Copyright 1993-95 by Carl Harris, Jr. Copyright 1996 by Eric S. Raymond
2  * All rights reserved.
3  * For license terms, see the file COPYING in this directory.
4  */
5
6 /***********************************************************************
7   module:       uid.c
8   project:      fetchmail
9   programmer:   Eric S. Raymond
10   description:  UID list handling
11
12  ***********************************************************************/
13
14 #include <config.h>
15
16 #include <stdio.h>
17
18 #if defined(STDC_HEADERS)
19 #include <stdlib.h>
20 #include <string.h>
21 #endif
22
23 #if defined(HAVE_UNISTD_H)
24 #include <unistd.h>
25 #endif
26
27 #include "fetchmail.h"
28
29 /*
30  * Machinery for handling UID lists live here.  This is mainly to support
31  * RFC1725-conformant POP3 servers without a LAST command, but may also be
32  * useful for making the IMAP4 querying logic UID-oriented, if a future
33  * revision of IMAP forces me to.  (This would be bad.  Server-side 
34  * seen bits are better than UIDs, because they track messages seen by
35  * *all* clients.)
36  *
37  * Here's the theory:
38  *
39  * At start of a query, we have a (possibly empty) list of UIDs to be
40  * considered `already seen'.  These are messages that were left in
41  * the mailbox and *not deleted* on previous queries (we don't need to
42  * remember the UIDs of deleted messages because ... well, they're gone!).
43  * This list is set up by initialized_saved_list() from the .fetchids 
44  * file and hangs off the host's `saved' member.
45  *
46  * Early in the query, during the execution of the protocol-specific 
47  * getrange code, the driver expects that the host's `unseen' member
48  * will be filled with a list of UIDs and message numbers representing
49  * the unseen mailbox state.  If this list is empty, the server did
50  * not respond to the request for a UID listing.
51  *
52  * Each time a message is fetched, we can check its UID against the
53  * `saved' list to see if it is old.  If not, it should be downloaded
54  * (and possibly deleted).  It should be downloaded anyway if --all
55  * is on.  It should not be deleted if --keep is on.
56  *
57  * Each time a message is read, we remove its id from the `unseen'
58  * member.
59  *
60  * At the end of the query, whatever remains in the `unseen' member
61  * (because it was not deleted) becomes the `saved' list.  The old
62  * `saved' list is freed.
63  */
64
65 /* UIDs associated with un-queried hosts */
66 static struct idlist *scratchlist;
67
68 void initialize_saved_lists(hostlist, idfile)
69 /* read file of saved IDs and attach to each host */
70 struct hostrec *hostlist;
71 char *idfile;
72 {
73     int st;
74     FILE        *tmpfp;
75     struct hostrec *hostp;
76
77     /* make sure lists are initially empty */
78     for (hostp = hostlist; hostp; hostp = hostp->next)
79         hostp->saved = hostp->unseen = (struct idlist *)NULL;
80
81     /* let's get stored message UIDs from previous queries */
82     if ((tmpfp = fopen(idfile, "r")) != (FILE *)NULL) {
83         char buf[POPBUFSIZE+1], host[HOSTLEN+1], id[IDLEN+1];
84
85         while (fgets(buf, POPBUFSIZE, tmpfp) != (char *)NULL)
86         {
87             if ((st = sscanf(buf, "%s %s\n", host, id)) == 2)
88             {
89                 for (hostp = hostlist; hostp; hostp = hostp->next)
90                 {
91                     if (strcmp(host, hostp->servername) == 0)
92                     {
93                         save_uid(&hostp->saved, -1, id);
94                         break;
95                     }
96                 }
97
98                 /* if it's not in a host we're querying, save it anyway */
99                 if (hostp == (struct hostrec *)NULL)
100                     save_uid(&scratchlist, -1, buf);
101             }
102         }
103         fclose(tmpfp);
104     }
105 }
106
107 void save_uid(idl, num, str)
108 /* save a number/UID pair on the given UID list */
109 struct idlist **idl;
110 int num;
111 char *str;
112 {
113     struct idlist *new;
114
115     new = (struct idlist *)xmalloc(sizeof(struct idlist));
116     new->num = num;
117     new->id = strdup(str);
118     new->next = *idl;
119     *idl = new;
120 }
121
122 void free_uid_list(idl)
123 /* free the given UID list */
124 struct idlist **idl;
125 {
126     if (*idl == (struct idlist *)NULL)
127         return;
128
129     free_uid_list(&(*idl)->next);
130     free ((*idl)->id);
131     free(*idl);
132     *idl = (struct idlist *)NULL;
133 }
134
135 int uid_in_list(idl, str)
136 /* is a given ID in the given list? */
137 struct idlist **idl;
138 char *str;
139 {
140     if (*idl == (struct idlist *)NULL)
141         return(0);
142     else if (strcmp(str, (*idl)->id) == 0)
143         return(1);
144     else
145         return(uid_in_list(&(*idl)->next, str));
146 }
147
148 int delete_uid(idl, num)
149 /* delete given message from given list */
150 struct idlist **idl;
151 int num;
152 {
153     if (*idl == (struct idlist *)NULL)
154         return(0);
155     else if ((*idl)->num == num)
156     {
157         struct idlist   *next = (*idl)->next;
158
159         free ((*idl)->id);
160         free(*idl);
161         *idl = next;
162         return(1);
163     }
164     else
165         return(delete_uid(&(*idl)->next, num));
166     return(0);
167 }
168
169 void update_uid_lists(hostp)
170 /* perform end-of-query actions on UID lists */
171 struct hostrec *hostp;
172 {
173     /*
174      * Replace `saved' list with `unseen' list as modified by deletions.
175      */
176     free_uid_list(&hostp->saved);
177     hostp->saved = hostp->unseen;
178 }
179
180 void write_saved_lists(hostlist, idfile)
181 /* perform end-of-run write of seen-messages list */
182 struct hostrec *hostlist;
183 char *idfile;
184 {
185     int st, idcount;
186     FILE        *tmpfp;
187     struct hostrec *hostp;
188     struct idlist *idp;
189
190     /* if all lists are empty, nuke the file */
191     idcount = 0;
192     for (hostp = hostlist; hostp; hostp = hostp->next) {
193         if (hostp->saved)
194             idcount++;
195     }
196
197     /* either nuke the file or write updated last-seen IDs */
198     if (!idcount)
199         unlink(idfile);
200     else
201         if ((tmpfp = fopen(idfile, "w")) != (FILE *)NULL) {
202             for (hostp = hostlist; hostp; hostp = hostp->next) {
203                 for (idp = hostp->saved; idp; idp = idp->next)
204                     fprintf(tmpfp, "%s %s\n", hostp->servername, idp->id);
205             }
206             for (idp = scratchlist; idp; idp = idp->next)
207                 fputs(idp->id, tmpfp);
208             fclose(tmpfp);
209         }
210 }