imapext-2007

view src/c-client/newsrc.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
line source
1 /* ========================================================================
2 * Copyright 1988-2006 University of Washington
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *
11 * ========================================================================
12 */
14 /*
15 * Program: Newsrc manipulation routines
16 *
17 * Author: Mark Crispin
18 * Networks and Distributed Computing
19 * Computing & Communications
20 * University of Washington
21 * Administration Building, AG-44
22 * Seattle, WA 98195
23 * Internet: MRC@CAC.Washington.EDU
24 *
25 * Date: 12 September 1994
26 * Last Edited: 30 August 2006
27 */
30 #include <ctype.h>
31 #include <stdio.h>
32 #include "c-client.h"
33 #include "newsrc.h"
35 #ifndef OLDFILESUFFIX
36 #define OLDFILESUFFIX ".old"
37 #endif
39 /* Error message
40 * Accepts: message format
41 * additional message string
42 * message level
43 * Returns: NIL, always
44 */
46 long newsrc_error (char *fmt,char *text,long errflg)
47 {
48 char tmp[MAILTMPLEN];
49 sprintf (tmp,fmt,text);
50 MM_LOG (tmp,errflg);
51 return NIL;
52 }
55 /* Write error message
56 * Accepts: newsrc name
57 * file designator
58 * file designator
59 * Returns: NIL, always
60 */
62 long newsrc_write_error (char *name,FILE *f1,FILE *f2)
63 {
64 if (f1) fclose (f1); /* close file designators */
65 if (f2) fclose (f2);
66 return newsrc_error ("Error writing to %.80s",name,ERROR);
67 }
70 /* Create newsrc file in local form
71 * Accepts: MAIL stream
72 * notification flag
73 * Returns: file designator of newsrc
74 */
76 FILE *newsrc_create (MAILSTREAM *stream,int notify)
77 {
78 char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,stream);
79 FILE *f = fopen (newsrc,"wb");
80 if (!f) newsrc_error ("Unable to create news state %.80s",newsrc,ERROR);
81 else if (notify) newsrc_error ("Creating news state %.80s",newsrc,WARN);
82 return f;
83 }
85 /* Write new state in newsrc
86 * Accepts: file designator of newsrc
87 * group
88 * new subscription status character
89 * newline convention
90 * Returns: T if successful, NIL otherwise
91 */
93 long newsrc_newstate (FILE *f,char *group,char state,char *nl)
94 {
95 long ret = (f && (fputs (group,f) != EOF) && ((putc (state,f)) != EOF) &&
96 ((putc (' ',f)) != EOF) && (fputs (nl,f) != EOF)) ? LONGT : NIL;
97 if (fclose (f) == EOF) ret = NIL;
98 return ret;
99 }
102 /* Write messages in newsrc
103 * Accepts: file designator of newsrc
104 * MAIL stream
105 * message number/newsgroup message map
106 * newline convention
107 * Returns: T if successful, NIL otherwise
108 */
110 long newsrc_newmessages (FILE *f,MAILSTREAM *stream,char *nl)
111 {
112 unsigned long i,j,k;
113 char tmp[MAILTMPLEN];
114 MESSAGECACHE *elt;
115 int c = ' ';
116 if (stream->nmsgs) { /* have any messages? */
117 for (i = 1,j = k = (mail_elt (stream,i)->private.uid > 1) ? 1 : 0;
118 i <= stream->nmsgs; ++i) {
119 /* deleted message? */
120 if ((elt = mail_elt (stream,i))->deleted) {
121 k = elt->private.uid; /* this is the top of the current range */
122 if (!j) j = k; /* if no range in progress, start one */
123 }
124 else if (j) { /* unread message, ending a range */
125 /* calculate end of range */
126 if (k = elt->private.uid - 1) {
127 /* dump range */
128 sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
129 if (fputs (tmp,f) == EOF) return NIL;
130 c = ','; /* need a comma after the first time */
131 }
132 j = 0; /* no more range in progress */
133 }
134 }
135 if (j) { /* dump trailing range */
136 sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
137 if (fputs (tmp,f) == EOF) return NIL;
138 }
139 }
140 /* write trailing newline, return */
141 return (fputs (nl,f) == EOF) ? NIL : LONGT;
142 }
144 /* List subscribed newsgroups
145 * Accepts: MAIL stream
146 * prefix to append name
147 * pattern to search
148 */
150 void newsrc_lsub (MAILSTREAM *stream,char *pattern)
151 {
152 char *s,*t,*lcl,name[MAILTMPLEN];
153 int c = ' ';
154 int showuppers = pattern[strlen (pattern) - 1] == '%';
155 FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,stream),"rb");
156 if (f) { /* got file? */
157 /* remote name? */
158 if (*(lcl = strcpy (name,pattern)) == '{') lcl = strchr (lcl,'}') + 1;
159 if (*lcl == '#') lcl += 6; /* namespace format name? */
160 while (c != EOF) { /* yes, read newsrc */
161 for (s = lcl; (s < (name + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
162 (c != ':') && (c != '!') && (c != '\015') && (c != '\012');
163 *s++ = c);
164 if (c == ':') { /* found a subscribed newsgroup? */
165 *s = '\0'; /* yes, tie off name */
166 /* report if match */
167 if (pmatch_full (name,pattern,'.')) mm_lsub (stream,'.',name,NIL);
168 else while (showuppers && (t = strrchr (lcl,'.'))) {
169 *t = '\0'; /* tie off the name */
170 if (pmatch_full (name,pattern,'.'))
171 mm_lsub (stream,'.',name,LATT_NOSELECT);
172 }
173 }
174 while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
175 }
176 fclose (f);
177 }
178 }
180 /* Update subscription status of newsrc
181 * Accepts: MAIL stream
182 * group
183 * new subscription status character
184 * Returns: T if successful, NIL otherwise
185 */
187 long newsrc_update (MAILSTREAM *stream,char *group,char state)
188 {
189 char tmp[MAILTMPLEN];
190 char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,stream);
191 long ret = NIL;
192 FILE *f = fopen (newsrc,"r+b");
193 if (f) { /* update existing file */
194 int c = 0;
195 char *s,nl[3];
196 long pos = 0;
197 nl[0] = nl[1] = nl[2]='\0'; /* no newline known yet */
198 do { /* read newsrc */
199 for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
200 (c != ':') && (c != '!') && (c != '\015') && (c != '\012');
201 *s++ = c) pos = ftell (f);
202 *s = '\0'; /* tie off name */
203 /* found the newsgroup? */
204 if (((c == ':') || (c == '!')) && !strcmp (tmp,group)) {
205 if (c == state) { /* already at that state? */
206 if (c == ':') newsrc_error("Already subscribed to %.80s",group,WARN);
207 ret = LONGT; /* noop the update */
208 }
209 /* write the character */
210 else if (!fseek (f,pos,0)) ret = ((putc (state,f)) == EOF) ? NIL:LONGT;
211 if (fclose (f) == EOF) ret = NIL;
212 f = NIL; /* done with file */
213 break;
214 }
215 /* gobble remainder of this line */
216 while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
217 /* need to know about newlines and found it? */
218 if (!nl[0] && ((c == '\015') || (c == '\012')) && ((nl[0]=c) == '\015')){
219 /* sniff and see if an LF */
220 if ((c = getc (f)) == '\012') nl[1] = c;
221 else ungetc (c,f); /* nope, push it back */
222 }
223 } while (c != EOF);
225 if (f) { /* still haven't written it yet? */
226 if (nl[0]) { /* know its newline convention? */
227 fseek (f,0L,2); /* yes, seek to end of file */
228 ret = newsrc_newstate (f,group,state,nl);
229 }
230 else { /* can't find a newline convention */
231 fclose (f); /* punt the file */
232 /* can't win if read something */
233 if (pos) newsrc_error ("Unknown newline convention in %.80s",
234 newsrc,ERROR);
235 /* file must have been empty, rewrite it */
236 else ret = newsrc_newstate(newsrc_create(stream,NIL),group,state,"\n");
237 }
238 }
239 }
240 /* create new file */
241 else ret = newsrc_newstate (newsrc_create (stream,T),group,state,"\n");
242 return ret; /* return with update status */
243 }
245 /* Update newsgroup status in stream
246 * Accepts: newsgroup name
247 * MAIL stream
248 * Returns: number of recent messages
249 */
251 long newsrc_read (char *group,MAILSTREAM *stream)
252 {
253 int c = 0;
254 char *s,tmp[MAILTMPLEN];
255 unsigned long i,j;
256 MESSAGECACHE *elt;
257 unsigned long m = 1,recent = 0,unseen = 0;
258 FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,stream),"rb");
259 if (f) do { /* read newsrc */
260 for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
261 (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
262 *s = '\0'; /* tie off name */
263 if ((c==':') || (c=='!')) { /* found newsgroup? */
264 if (strcmp (tmp,group)) /* group name match? */
265 while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
266 else { /* yes, skip leading whitespace */
267 while ((c = getc (f)) == ' ');
268 /* only if unprocessed messages */
269 if (stream->nmsgs) while (f && (m <= stream->nmsgs)) {
270 /* collect a number */
271 if (isdigit (c)) { /* better have a number */
272 for (i = 0,j = 0; isdigit (c); c = getc (f)) i = i*10 + (c-'0');
273 if (c == '-') for (c = getc (f); isdigit (c); c = getc (f))
274 j = j*10 +(c-'0');/* collect second value if range */
275 if (!unseen && (mail_elt (stream,m)->private.uid < i)) unseen = m;
276 /* skip messages before first value */
277 while ((m <= stream->nmsgs) &&
278 ((elt = mail_elt (stream,m))->private.uid < i) && m++)
279 elt->valid = T;
280 /* do all messages in range */
281 while ((m <= stream->nmsgs) && (elt = mail_elt (stream,m)) &&
282 (j ? ((elt->private.uid >= i) && (elt->private.uid <= j)) :
283 (elt->private.uid == i)) && m++)
284 elt->valid = elt->deleted = T;
285 }
287 switch (c) { /* what is the delimiter? */
288 case ',': /* more to come */
289 c = getc (f); /* get first character of number */
290 break;
291 default: /* bogus character */
292 sprintf (tmp,"Bogus character 0x%x in news state",(unsigned int)c);
293 MM_LOG (tmp,ERROR);
294 case EOF: case '\015': case '\012':
295 fclose (f); /* all done - close the file */
296 f = NIL;
297 break;
298 }
299 }
300 else { /* empty newsgroup */
301 while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
302 fclose (f); /* all done - close the file */
303 f = NIL;
304 }
305 }
306 }
307 } while (f && (c != EOF)); /* until file closed or EOF */
308 if (f) { /* still have file open? */
309 sprintf (tmp,"No state for newsgroup %.80s found, reading as new",group);
310 MM_LOG (tmp,WARN);
311 fclose (f); /* close the file */
312 }
313 if (m <= stream->nmsgs) { /* any messages beyond newsrc range? */
314 if (!unseen) unseen = m; /* then this must be the first unseen one */
315 do {
316 elt = mail_elt (stream,m++);
317 elt->valid = elt->recent = T;
318 ++recent; /* count another recent message */
319 }
320 while (m <= stream->nmsgs);
321 }
322 if (unseen) { /* report first unseen message */
323 sprintf (tmp,"[UNSEEN] %lu is first unseen message in %.80s",unseen,group);
324 MM_NOTIFY (stream,tmp,(long) NIL);
325 }
326 return recent;
327 }
329 /* Update newsgroup entry in newsrc
330 * Accepts: newsgroup name
331 * MAIL stream
332 * Returns: T if successful, NIL otherwise
333 */
335 long newsrc_write (char *group,MAILSTREAM *stream)
336 {
337 long ret = NIL;
338 int c = 0,d = EOF;
339 char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,stream);
340 char *s,tmp[MAILTMPLEN],backup[MAILTMPLEN],nl[3];
341 FILE *f,*bf;
342 nl[0] = nl[1] = nl[2] = '\0'; /* no newline known yet */
343 if (f = fopen (newsrc,"rb")) {/* have existing newsrc file? */
344 if (!(bf = fopen ((strcat (strcpy (backup,newsrc),OLDFILESUFFIX)),"wb"))) {
345 fclose (f); /* punt input file */
346 return newsrc_error("Can't create backup news state %.80s",backup,ERROR);
347 }
348 /* copy to backup file */
349 while ((c = getc (f)) != EOF) {
350 /* need to know about newlines and found it? */
351 if (!nl[0] && ((c == '\015') || (c == '\012')) && ((nl[0]=c) == '\015')){
352 /* sniff and see if an LF */
353 if ((c = getc (f)) == '\012') nl[1] = c;
354 ungetc (c,f); /* push it back */
355 }
356 /* write to backup file */
357 if ((d = putc (c,bf)) == EOF) {
358 fclose (f); /* punt input file */
359 return newsrc_error ("Error writing backup news state %.80s",
360 newsrc,ERROR);
361 }
362 }
363 fclose (f); /* close existing file */
364 if (fclose (bf) == EOF) /* and backup file */
365 return newsrc_error ("Error closing backup news state %.80s",
366 newsrc,ERROR);
367 if (d == EOF) { /* open for write if empty file */
368 if (f = newsrc_create (stream,NIL)) bf = NIL;
369 else return NIL;
370 }
371 else if (!nl[0]) /* make sure newlines valid */
372 return newsrc_error ("Unknown newline convention in %.80s",newsrc,ERROR);
373 /* now read backup file */
374 else if (!(bf = fopen (backup,"rb")))
375 return newsrc_error ("Error reading backup news state %.80s",
376 backup,ERROR);
377 /* open newsrc for writing */
378 else if (!(f = fopen (newsrc,"wb"))) {
379 fclose (bf); /* punt backup */
380 return newsrc_error ("Can't rewrite news state %.80s",newsrc,ERROR);
381 }
382 }
383 else { /* create new newsrc file */
384 if (f = newsrc_create (stream,T)) bf = NIL;
385 else return NIL; /* can't create newsrc */
386 }
388 while (bf) { /* read newsrc */
389 for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (bf)) != EOF) &&
390 (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
391 *s = '\0'; /* tie off name */
392 /* saw correct end of group delimiter? */
393 if (tmp[0] && ((c == ':') || (c == '!'))) {
394 /* yes, write newsgroup name and delimiter */
395 if ((tmp[0] && (fputs (tmp,f) == EOF)) || ((putc (c,f)) == EOF))
396 return newsrc_write_error (newsrc,bf,f);
397 if (!strcmp (tmp,group)) {/* found correct group? */
398 /* yes, write new status */
399 if (!newsrc_newmessages (f,stream,nl[0] ? nl : "\n"))
400 return newsrc_write_error (newsrc,bf,f);
401 /* skip past old data */
402 while (((c = getc (bf)) != EOF) && (c != '\015') && (c != '\012'));
403 /* skip past newline */
404 while ((c == '\015') || (c == '\012')) c = getc (bf);
405 while (c != EOF) { /* copy remainder of file */
406 if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
407 c = getc (bf); /* get next character */
408 }
409 /* done with file */
410 if (fclose (f) == EOF) return newsrc_write_error (newsrc,bf,NIL);
411 f = NIL;
412 }
413 /* copy remainder of line */
414 else while (((c = getc (bf)) != EOF) && (c != '\015') && (c != '\012'))
415 if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
416 if (c == '\015') { /* write CR if seen */
417 if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
418 /* sniff to see if LF */
419 if (((c = getc (bf)) != EOF) && (c != '\012')) ungetc (c,bf);
420 }
421 /* write LF if seen */
422 if ((c == '\012') && (putc (c,f) == EOF))
423 return newsrc_write_error (newsrc,bf,f);
424 }
425 if (c == EOF) { /* hit end of file? */
426 fclose (bf); /* yup, close the file */
427 bf = NIL;
428 }
429 }
430 if (f) { /* still have newsrc file open? */
431 ret = ((fputs (group,f) != EOF) && ((putc (':',f)) != EOF) &&
432 newsrc_newmessages (f,stream,nl[0] ? nl : "\n")) ? LONGT : NIL;
433 if (fclose (f) == EOF) ret = newsrc_write_error (newsrc,NIL,NIL);
434 }
435 else ret = LONGT;
436 return ret;
437 }
439 /* Get newsgroup state as text stream
440 * Accepts: MAIL stream
441 * newsgroup name
442 * Returns: string containing newsgroup state, or NIL if not found
443 */
445 char *newsrc_state (MAILSTREAM *stream,char *group)
446 {
447 int c = 0;
448 char *s,tmp[MAILTMPLEN];
449 long pos;
450 size_t size;
451 FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,stream),"rb");
452 if (f) do { /* read newsrc */
453 for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
454 (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
455 *s = '\0'; /* tie off name */
456 if ((c==':') || (c=='!')) { /* found newsgroup? */
457 if (strcmp (tmp,group)) /* group name match? */
458 while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
459 else { /* yes, skip leading whitespace */
460 do pos = ftell (f);
461 while ((c = getc (f)) == ' ');
462 /* count characters in state */
463 for (size = 0; (c != '\015') && (c != '\012') && (c != EOF); size++)
464 c = getc (f);
465 /* now copy it */
466 s = (char *) fs_get (size + 1);
467 fseek (f,pos,SEEK_SET);
468 fread (s,(size_t) 1,size,f);
469 s[size] = '\0'; /* tie off string */
470 fclose (f); /* all done - close the file */
471 return s;
472 }
473 }
474 } while (f && (c != EOF)); /* until file closed or EOF */
475 sprintf (tmp,"No state for newsgroup %.80s found",group);
476 MM_LOG (tmp,WARN);
477 if (f) fclose (f); /* close the file */
478 return NIL; /* not found return */
479 }
481 /* Check UID in newsgroup state
482 * Accepts: newsgroup state string
483 * uid
484 * returned recent count
485 * returned unseen count
486 */
488 void newsrc_check_uid (unsigned char *state,unsigned long uid,
489 unsigned long *recent,unsigned long *unseen)
490 {
491 unsigned long i,j;
492 while (*state) { /* until run out of state string */
493 /* collect a number */
494 for (i = 0; isdigit (*state); i = i*10 + (*state++ - '0'));
495 if (*state != '-') j = i; /* coerce single mesage into range */
496 else { /* have a range */
497 for (j = 0; isdigit (*++state); j = j*10 + (*state - '0'));
498 if (!j) j = i; /* guard against -0 */
499 if (j < i) return; /* bogon if end less than start */
500 }
501 if (*state == ',') state++; /* skip past comma */
502 else if (*state) return; /* otherwise it's a bogon */
503 if (uid <= j) { /* covered by upper bound? */
504 if (uid < i) ++*unseen; /* unseen if not covered by lower bound */
505 return; /* don't need to look further */
506 }
507 }
508 ++*unseen; /* not found in any range, message is unseen */
509 ++*recent; /* and recent */
510 }

UW-IMAP'd extensions by yuuji