rev |
line source |
yuuji@0
|
1 /* ========================================================================
|
yuuji@0
|
2 * Copyright 1988-2007 University of Washington
|
yuuji@0
|
3 *
|
yuuji@0
|
4 * Licensed under the Apache License, Version 2.0 (the "License");
|
yuuji@0
|
5 * you may not use this file except in compliance with the License.
|
yuuji@0
|
6 * You may obtain a copy of the License at
|
yuuji@0
|
7 *
|
yuuji@0
|
8 * http://www.apache.org/licenses/LICENSE-2.0
|
yuuji@0
|
9 *
|
yuuji@0
|
10 *
|
yuuji@0
|
11 * ========================================================================
|
yuuji@0
|
12 */
|
yuuji@0
|
13
|
yuuji@0
|
14 /*
|
yuuji@0
|
15 * Program: News routines
|
yuuji@0
|
16 *
|
yuuji@0
|
17 * Author: Mark Crispin
|
yuuji@0
|
18 * Networks and Distributed Computing
|
yuuji@0
|
19 * Computing & Communications
|
yuuji@0
|
20 * University of Washington
|
yuuji@0
|
21 * Administration Building, AG-44
|
yuuji@0
|
22 * Seattle, WA 98195
|
yuuji@0
|
23 * Internet: MRC@CAC.Washington.EDU
|
yuuji@0
|
24 *
|
yuuji@0
|
25 * Date: 4 September 1991
|
yuuji@0
|
26 * Last Edited: 30 January 2007
|
yuuji@0
|
27 */
|
yuuji@0
|
28
|
yuuji@0
|
29
|
yuuji@0
|
30 #include <stdio.h>
|
yuuji@0
|
31 #include <ctype.h>
|
yuuji@0
|
32 #include <errno.h>
|
yuuji@0
|
33 extern int errno; /* just in case */
|
yuuji@0
|
34 #include "mail.h"
|
yuuji@0
|
35 #include "osdep.h"
|
yuuji@0
|
36 #include <sys/stat.h>
|
yuuji@0
|
37 #include <sys/time.h>
|
yuuji@0
|
38 #include "misc.h"
|
yuuji@0
|
39 #include "newsrc.h"
|
yuuji@0
|
40 #include "fdstring.h"
|
yuuji@0
|
41
|
yuuji@0
|
42
|
yuuji@0
|
43 /* news_load_message() flags */
|
yuuji@0
|
44
|
yuuji@0
|
45 #define NLM_HEADER 0x1 /* load message text */
|
yuuji@0
|
46 #define NLM_TEXT 0x2 /* load message text */
|
yuuji@0
|
47
|
yuuji@0
|
48 /* NEWS I/O stream local data */
|
yuuji@0
|
49
|
yuuji@0
|
50 typedef struct news_local {
|
yuuji@0
|
51 unsigned int dirty : 1; /* disk copy of .newsrc needs updating */
|
yuuji@0
|
52 char *dir; /* spool directory name */
|
yuuji@0
|
53 char *name; /* local mailbox name */
|
yuuji@0
|
54 unsigned char buf[CHUNKSIZE]; /* scratch buffer */
|
yuuji@0
|
55 unsigned long cachedtexts; /* total size of all cached texts */
|
yuuji@0
|
56 } NEWSLOCAL;
|
yuuji@0
|
57
|
yuuji@0
|
58
|
yuuji@0
|
59 /* Convenient access to local data */
|
yuuji@0
|
60
|
yuuji@0
|
61 #define LOCAL ((NEWSLOCAL *) stream->local)
|
yuuji@0
|
62
|
yuuji@0
|
63
|
yuuji@0
|
64 /* Function prototypes */
|
yuuji@0
|
65
|
yuuji@0
|
66 DRIVER *news_valid (char *name);
|
yuuji@0
|
67 DRIVER *news_isvalid (char *name,char *mbx);
|
yuuji@0
|
68 void *news_parameters (long function,void *value);
|
yuuji@0
|
69 void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
|
yuuji@0
|
70 void news_list (MAILSTREAM *stream,char *ref,char *pat);
|
yuuji@0
|
71 void news_lsub (MAILSTREAM *stream,char *ref,char *pat);
|
yuuji@0
|
72 long news_canonicalize (char *ref,char *pat,char *pattern);
|
yuuji@0
|
73 long news_subscribe (MAILSTREAM *stream,char *mailbox);
|
yuuji@0
|
74 long news_unsubscribe (MAILSTREAM *stream,char *mailbox);
|
yuuji@0
|
75 long news_create (MAILSTREAM *stream,char *mailbox);
|
yuuji@0
|
76 long news_delete (MAILSTREAM *stream,char *mailbox);
|
yuuji@0
|
77 long news_rename (MAILSTREAM *stream,char *old,char *newname);
|
yuuji@0
|
78 MAILSTREAM *news_open (MAILSTREAM *stream);
|
yuuji@0
|
79 int news_select (struct direct *name);
|
yuuji@0
|
80 int news_numsort (const void *d1,const void *d2);
|
yuuji@0
|
81 void news_close (MAILSTREAM *stream,long options);
|
yuuji@0
|
82 void news_fast (MAILSTREAM *stream,char *sequence,long flags);
|
yuuji@0
|
83 void news_flags (MAILSTREAM *stream,char *sequence,long flags);
|
yuuji@0
|
84 void news_load_message (MAILSTREAM *stream,unsigned long msgno,long flags);
|
yuuji@0
|
85 char *news_header (MAILSTREAM *stream,unsigned long msgno,
|
yuuji@0
|
86 unsigned long *length,long flags);
|
yuuji@0
|
87 long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
|
yuuji@0
|
88 void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
|
yuuji@0
|
89 long news_ping (MAILSTREAM *stream);
|
yuuji@0
|
90 void news_check (MAILSTREAM *stream);
|
yuuji@0
|
91 long news_expunge (MAILSTREAM *stream,char *sequence,long options);
|
yuuji@0
|
92 long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
|
yuuji@0
|
93 long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
|
yuuji@0
|
94
|
yuuji@0
|
95 /* News routines */
|
yuuji@0
|
96
|
yuuji@0
|
97
|
yuuji@0
|
98 /* Driver dispatch used by MAIL */
|
yuuji@0
|
99
|
yuuji@0
|
100 DRIVER newsdriver = {
|
yuuji@0
|
101 "news", /* driver name */
|
yuuji@0
|
102 /* driver flags */
|
yuuji@0
|
103 DR_NEWS|DR_READONLY|DR_NOFAST|DR_NAMESPACE|DR_NONEWMAIL|DR_DIRFMT,
|
yuuji@0
|
104 (DRIVER *) NIL, /* next driver */
|
yuuji@0
|
105 news_valid, /* mailbox is valid for us */
|
yuuji@0
|
106 news_parameters, /* manipulate parameters */
|
yuuji@0
|
107 news_scan, /* scan mailboxes */
|
yuuji@0
|
108 news_list, /* find mailboxes */
|
yuuji@0
|
109 news_lsub, /* find subscribed mailboxes */
|
yuuji@0
|
110 news_subscribe, /* subscribe to mailbox */
|
yuuji@0
|
111 news_unsubscribe, /* unsubscribe from mailbox */
|
yuuji@0
|
112 news_create, /* create mailbox */
|
yuuji@0
|
113 news_delete, /* delete mailbox */
|
yuuji@0
|
114 news_rename, /* rename mailbox */
|
yuuji@0
|
115 mail_status_default, /* status of mailbox */
|
yuuji@0
|
116 news_open, /* open mailbox */
|
yuuji@0
|
117 news_close, /* close mailbox */
|
yuuji@0
|
118 news_fast, /* fetch message "fast" attributes */
|
yuuji@0
|
119 news_flags, /* fetch message flags */
|
yuuji@0
|
120 NIL, /* fetch overview */
|
yuuji@0
|
121 NIL, /* fetch message envelopes */
|
yuuji@0
|
122 news_header, /* fetch message header */
|
yuuji@0
|
123 news_text, /* fetch message body */
|
yuuji@0
|
124 NIL, /* fetch partial message text */
|
yuuji@0
|
125 NIL, /* unique identifier */
|
yuuji@0
|
126 NIL, /* message number */
|
yuuji@0
|
127 NIL, /* modify flags */
|
yuuji@0
|
128 news_flagmsg, /* per-message modify flags */
|
yuuji@0
|
129 NIL, /* search for message based on criteria */
|
yuuji@0
|
130 NIL, /* sort messages */
|
yuuji@0
|
131 NIL, /* thread messages */
|
yuuji@0
|
132 news_ping, /* ping mailbox to see if still alive */
|
yuuji@0
|
133 news_check, /* check for new messages */
|
yuuji@0
|
134 news_expunge, /* expunge deleted messages */
|
yuuji@0
|
135 news_copy, /* copy messages to another mailbox */
|
yuuji@0
|
136 news_append, /* append string message to mailbox */
|
yuuji@0
|
137 NIL /* garbage collect stream */
|
yuuji@0
|
138 };
|
yuuji@0
|
139
|
yuuji@0
|
140 /* prototype stream */
|
yuuji@0
|
141 MAILSTREAM newsproto = {&newsdriver};
|
yuuji@0
|
142
|
yuuji@0
|
143 /* News validate mailbox
|
yuuji@0
|
144 * Accepts: mailbox name
|
yuuji@0
|
145 * Returns: our driver if name is valid, NIL otherwise
|
yuuji@0
|
146 */
|
yuuji@0
|
147
|
yuuji@0
|
148 DRIVER *news_valid (char *name)
|
yuuji@0
|
149 {
|
yuuji@0
|
150 int fd;
|
yuuji@0
|
151 char *s,*t,*u;
|
yuuji@0
|
152 struct stat sbuf;
|
yuuji@0
|
153 if ((name[0] == '#') && (name[1] == 'n') && (name[2] == 'e') &&
|
yuuji@0
|
154 (name[3] == 'w') && (name[4] == 's') && (name[5] == '.') &&
|
yuuji@0
|
155 !strchr (name,'/') &&
|
yuuji@0
|
156 !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
|
yuuji@0
|
157 ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),O_RDONLY,
|
yuuji@0
|
158 NIL)) >= 0)) {
|
yuuji@0
|
159 fstat (fd,&sbuf); /* get size of active file */
|
yuuji@0
|
160 /* slurp in active file */
|
yuuji@0
|
161 read (fd,t = s = (char *) fs_get (sbuf.st_size+1),sbuf.st_size);
|
yuuji@0
|
162 s[sbuf.st_size] = '\0'; /* tie off file */
|
yuuji@0
|
163 close (fd); /* flush file */
|
yuuji@0
|
164 while (*t && (u = strchr (t,' '))) {
|
yuuji@0
|
165 *u++ = '\0'; /* tie off at end of name */
|
yuuji@0
|
166 if (!strcmp (name+6,t)) {
|
yuuji@0
|
167 fs_give ((void **) &s); /* flush data */
|
yuuji@0
|
168 return &newsdriver;
|
yuuji@0
|
169 }
|
yuuji@0
|
170 t = 1 + strchr (u,'\n'); /* next line */
|
yuuji@0
|
171 }
|
yuuji@0
|
172 fs_give ((void **) &s); /* flush data */
|
yuuji@0
|
173 }
|
yuuji@0
|
174 return NIL; /* return status */
|
yuuji@0
|
175 }
|
yuuji@0
|
176
|
yuuji@0
|
177 /* News manipulate driver parameters
|
yuuji@0
|
178 * Accepts: function code
|
yuuji@0
|
179 * function-dependent value
|
yuuji@0
|
180 * Returns: function-dependent return value
|
yuuji@0
|
181 */
|
yuuji@0
|
182
|
yuuji@0
|
183 void *news_parameters (long function,void *value)
|
yuuji@0
|
184 {
|
yuuji@0
|
185 return (function == GET_NEWSRC) ? env_parameters (function,value) : NIL;
|
yuuji@0
|
186 }
|
yuuji@0
|
187
|
yuuji@0
|
188
|
yuuji@0
|
189 /* News scan mailboxes
|
yuuji@0
|
190 * Accepts: mail stream
|
yuuji@0
|
191 * reference
|
yuuji@0
|
192 * pattern to search
|
yuuji@0
|
193 * string to scan
|
yuuji@0
|
194 */
|
yuuji@0
|
195
|
yuuji@0
|
196 void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
|
yuuji@0
|
197 {
|
yuuji@0
|
198 char tmp[MAILTMPLEN];
|
yuuji@0
|
199 if (news_canonicalize (ref,pat,tmp))
|
yuuji@0
|
200 mm_log ("Scan not valid for news mailboxes",ERROR);
|
yuuji@0
|
201 }
|
yuuji@0
|
202
|
yuuji@0
|
203 /* News find list of newsgroups
|
yuuji@0
|
204 * Accepts: mail stream
|
yuuji@0
|
205 * reference
|
yuuji@0
|
206 * pattern to search
|
yuuji@0
|
207 */
|
yuuji@0
|
208
|
yuuji@0
|
209 void news_list (MAILSTREAM *stream,char *ref,char *pat)
|
yuuji@0
|
210 {
|
yuuji@0
|
211 int fd;
|
yuuji@0
|
212 int i;
|
yuuji@0
|
213 char *s,*t,*u,*r,pattern[MAILTMPLEN],name[MAILTMPLEN];
|
yuuji@0
|
214 struct stat sbuf;
|
yuuji@0
|
215 if (!pat || !*pat) { /* empty pattern? */
|
yuuji@0
|
216 if (news_canonicalize (ref,"*",pattern)) {
|
yuuji@0
|
217 /* tie off name at root */
|
yuuji@0
|
218 if (s = strchr (pattern,'.')) *++s = '\0';
|
yuuji@0
|
219 else pattern[0] = '\0';
|
yuuji@0
|
220 mm_list (stream,'.',pattern,LATT_NOSELECT);
|
yuuji@0
|
221 }
|
yuuji@0
|
222 }
|
yuuji@0
|
223 else if (news_canonicalize (ref,pat,pattern) &&
|
yuuji@0
|
224 !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
|
yuuji@0
|
225 ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),
|
yuuji@0
|
226 O_RDONLY,NIL)) >= 0)) {
|
yuuji@0
|
227 fstat (fd,&sbuf); /* get file size and read data */
|
yuuji@0
|
228 read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
|
yuuji@0
|
229 close (fd); /* close file */
|
yuuji@0
|
230 s[sbuf.st_size] = '\0'; /* tie off string */
|
yuuji@0
|
231 strcpy (name,"#news."); /* write initial prefix */
|
yuuji@0
|
232 i = strlen (pattern); /* length of pattern */
|
yuuji@0
|
233 if (pattern[--i] != '%') i = 0;
|
yuuji@0
|
234 if (t = strtok_r (s,"\n",&r)) do if (u = strchr (t,' ')) {
|
yuuji@0
|
235 *u = '\0'; /* tie off at end of name */
|
yuuji@0
|
236 strcpy (name + 6,t); /* make full form of name */
|
yuuji@0
|
237 if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,NIL);
|
yuuji@0
|
238 else if (i && (u = strchr (name + i,'.'))) {
|
yuuji@0
|
239 *u = '\0'; /* tie off at delimiter, see if matches */
|
yuuji@0
|
240 if (pmatch_full (name,pattern,'.'))
|
yuuji@0
|
241 mm_list (stream,'.',name,LATT_NOSELECT);
|
yuuji@0
|
242 }
|
yuuji@0
|
243 } while (t = strtok_r (NIL,"\n",&r));
|
yuuji@0
|
244 fs_give ((void **) &s);
|
yuuji@0
|
245 }
|
yuuji@0
|
246 }
|
yuuji@0
|
247
|
yuuji@0
|
248 /* News find list of subscribed newsgroups
|
yuuji@0
|
249 * Accepts: mail stream
|
yuuji@0
|
250 * reference
|
yuuji@0
|
251 * pattern to search
|
yuuji@0
|
252 */
|
yuuji@0
|
253
|
yuuji@0
|
254 void news_lsub (MAILSTREAM *stream,char *ref,char *pat)
|
yuuji@0
|
255 {
|
yuuji@0
|
256 char pattern[MAILTMPLEN];
|
yuuji@0
|
257 /* return data from newsrc */
|
yuuji@0
|
258 if (news_canonicalize (ref,pat,pattern)) newsrc_lsub (stream,pattern);
|
yuuji@0
|
259 }
|
yuuji@0
|
260
|
yuuji@0
|
261
|
yuuji@0
|
262 /* News canonicalize newsgroup name
|
yuuji@0
|
263 * Accepts: reference
|
yuuji@0
|
264 * pattern
|
yuuji@0
|
265 * returned single pattern
|
yuuji@0
|
266 * Returns: T on success, NIL on failure
|
yuuji@0
|
267 */
|
yuuji@0
|
268
|
yuuji@0
|
269 long news_canonicalize (char *ref,char *pat,char *pattern)
|
yuuji@0
|
270 {
|
yuuji@0
|
271 unsigned long i;
|
yuuji@0
|
272 char *s;
|
yuuji@0
|
273 if (ref && *ref) { /* have a reference */
|
yuuji@0
|
274 strcpy (pattern,ref); /* copy reference to pattern */
|
yuuji@0
|
275 /* # overrides mailbox field in reference */
|
yuuji@0
|
276 if (*pat == '#') strcpy (pattern,pat);
|
yuuji@0
|
277 /* pattern starts, reference ends, with . */
|
yuuji@0
|
278 else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
|
yuuji@0
|
279 strcat (pattern,pat + 1); /* append, omitting one of the period */
|
yuuji@0
|
280 else strcat (pattern,pat); /* anything else is just appended */
|
yuuji@0
|
281 }
|
yuuji@0
|
282 else strcpy (pattern,pat); /* just have basic name */
|
yuuji@0
|
283 if ((pattern[0] == '#') && (pattern[1] == 'n') && (pattern[2] == 'e') &&
|
yuuji@0
|
284 (pattern[3] == 'w') && (pattern[4] == 's') && (pattern[5] == '.') &&
|
yuuji@0
|
285 !strchr (pattern,'/')) { /* count wildcards */
|
yuuji@0
|
286 for (i = 0, s = pattern; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
|
yuuji@0
|
287 /* success if not too many */
|
yuuji@0
|
288 if (i <= MAXWILDCARDS) return LONGT;
|
yuuji@0
|
289 MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
|
yuuji@0
|
290 }
|
yuuji@0
|
291 return NIL;
|
yuuji@0
|
292 }
|
yuuji@0
|
293
|
yuuji@0
|
294 /* News subscribe to mailbox
|
yuuji@0
|
295 * Accepts: mail stream
|
yuuji@0
|
296 * mailbox to add to subscription list
|
yuuji@0
|
297 * Returns: T on success, NIL on failure
|
yuuji@0
|
298 */
|
yuuji@0
|
299
|
yuuji@0
|
300 long news_subscribe (MAILSTREAM *stream,char *mailbox)
|
yuuji@0
|
301 {
|
yuuji@0
|
302 return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,':') : NIL;
|
yuuji@0
|
303 }
|
yuuji@0
|
304
|
yuuji@0
|
305
|
yuuji@0
|
306 /* NEWS unsubscribe to mailbox
|
yuuji@0
|
307 * Accepts: mail stream
|
yuuji@0
|
308 * mailbox to delete from subscription list
|
yuuji@0
|
309 * Returns: T on success, NIL on failure
|
yuuji@0
|
310 */
|
yuuji@0
|
311
|
yuuji@0
|
312 long news_unsubscribe (MAILSTREAM *stream,char *mailbox)
|
yuuji@0
|
313 {
|
yuuji@0
|
314 return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,'!') : NIL;
|
yuuji@0
|
315 }
|
yuuji@0
|
316
|
yuuji@0
|
317 /* News create mailbox
|
yuuji@0
|
318 * Accepts: mail stream
|
yuuji@0
|
319 * mailbox name to create
|
yuuji@0
|
320 * Returns: T on success, NIL on failure
|
yuuji@0
|
321 */
|
yuuji@0
|
322
|
yuuji@0
|
323 long news_create (MAILSTREAM *stream,char *mailbox)
|
yuuji@0
|
324 {
|
yuuji@0
|
325 return NIL; /* never valid for News */
|
yuuji@0
|
326 }
|
yuuji@0
|
327
|
yuuji@0
|
328
|
yuuji@0
|
329 /* News delete mailbox
|
yuuji@0
|
330 * mailbox name to delete
|
yuuji@0
|
331 * Returns: T on success, NIL on failure
|
yuuji@0
|
332 */
|
yuuji@0
|
333
|
yuuji@0
|
334 long news_delete (MAILSTREAM *stream,char *mailbox)
|
yuuji@0
|
335 {
|
yuuji@0
|
336 return NIL; /* never valid for News */
|
yuuji@0
|
337 }
|
yuuji@0
|
338
|
yuuji@0
|
339
|
yuuji@0
|
340 /* News rename mailbox
|
yuuji@0
|
341 * Accepts: mail stream
|
yuuji@0
|
342 * old mailbox name
|
yuuji@0
|
343 * new mailbox name
|
yuuji@0
|
344 * Returns: T on success, NIL on failure
|
yuuji@0
|
345 */
|
yuuji@0
|
346
|
yuuji@0
|
347 long news_rename (MAILSTREAM *stream,char *old,char *newname)
|
yuuji@0
|
348 {
|
yuuji@0
|
349 return NIL; /* never valid for News */
|
yuuji@0
|
350 }
|
yuuji@0
|
351
|
yuuji@0
|
352 /* News open
|
yuuji@0
|
353 * Accepts: stream to open
|
yuuji@0
|
354 * Returns: stream on success, NIL on failure
|
yuuji@0
|
355 */
|
yuuji@0
|
356
|
yuuji@0
|
357 MAILSTREAM *news_open (MAILSTREAM *stream)
|
yuuji@0
|
358 {
|
yuuji@0
|
359 long i,nmsgs;
|
yuuji@0
|
360 char *s,tmp[MAILTMPLEN];
|
yuuji@0
|
361 struct direct **names = NIL;
|
yuuji@0
|
362 /* return prototype for OP_PROTOTYPE call */
|
yuuji@0
|
363 if (!stream) return &newsproto;
|
yuuji@0
|
364 if (stream->local) fatal ("news recycle stream");
|
yuuji@0
|
365 /* build directory name */
|
yuuji@0
|
366 sprintf (s = tmp,"%s/%s",(char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),
|
yuuji@0
|
367 stream->mailbox + 6);
|
yuuji@0
|
368 while (s = strchr (s,'.')) *s = '/';
|
yuuji@0
|
369 /* scan directory */
|
yuuji@0
|
370 if ((nmsgs = scandir (tmp,&names,news_select,news_numsort)) >= 0) {
|
yuuji@0
|
371 mail_exists (stream,nmsgs); /* notify upper level that messages exist */
|
yuuji@0
|
372 stream->local = fs_get (sizeof (NEWSLOCAL));
|
yuuji@0
|
373 LOCAL->dirty = NIL; /* no update to .newsrc needed yet */
|
yuuji@0
|
374 LOCAL->dir = cpystr (tmp); /* copy directory name for later */
|
yuuji@0
|
375 LOCAL->name = cpystr (stream->mailbox + 6);
|
yuuji@0
|
376 for (i = 0; i < nmsgs; ++i) {
|
yuuji@0
|
377 stream->uid_last = mail_elt (stream,i+1)->private.uid =
|
yuuji@0
|
378 atoi (names[i]->d_name);
|
yuuji@0
|
379 fs_give ((void **) &names[i]);
|
yuuji@0
|
380 }
|
yuuji@0
|
381 s = (void *) names; /* stupid language */
|
yuuji@0
|
382 fs_give ((void **) &s); /* free directory */
|
yuuji@0
|
383 LOCAL->cachedtexts = 0; /* no cached texts */
|
yuuji@0
|
384 stream->sequence++; /* bump sequence number */
|
yuuji@0
|
385 stream->rdonly = stream->perm_deleted = T;
|
yuuji@0
|
386 /* UIDs are always valid */
|
yuuji@0
|
387 stream->uid_validity = 0xbeefface;
|
yuuji@0
|
388 /* read .newsrc entries */
|
yuuji@0
|
389 mail_recent (stream,newsrc_read (LOCAL->name,stream));
|
yuuji@0
|
390 /* notify if empty newsgroup */
|
yuuji@0
|
391 if (!(stream->nmsgs || stream->silent)) {
|
yuuji@0
|
392 sprintf (tmp,"Newsgroup %s is empty",LOCAL->name);
|
yuuji@0
|
393 mm_log (tmp,WARN);
|
yuuji@0
|
394 }
|
yuuji@0
|
395 }
|
yuuji@0
|
396 else mm_log ("Unable to scan newsgroup spool directory",ERROR);
|
yuuji@0
|
397 return LOCAL ? stream : NIL; /* if stream is alive, return to caller */
|
yuuji@0
|
398 }
|
yuuji@0
|
399
|
yuuji@0
|
400 /* News file name selection test
|
yuuji@0
|
401 * Accepts: candidate directory entry
|
yuuji@0
|
402 * Returns: T to use file name, NIL to skip it
|
yuuji@0
|
403 */
|
yuuji@0
|
404
|
yuuji@0
|
405 int news_select (struct direct *name)
|
yuuji@0
|
406 {
|
yuuji@0
|
407 char c;
|
yuuji@0
|
408 char *s = name->d_name;
|
yuuji@0
|
409 while (c = *s++) if (!isdigit (c)) return NIL;
|
yuuji@0
|
410 return T;
|
yuuji@0
|
411 }
|
yuuji@0
|
412
|
yuuji@0
|
413
|
yuuji@0
|
414 /* News file name comparision
|
yuuji@0
|
415 * Accepts: first candidate directory entry
|
yuuji@0
|
416 * second candidate directory entry
|
yuuji@0
|
417 * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
|
yuuji@0
|
418 */
|
yuuji@0
|
419
|
yuuji@0
|
420 int news_numsort (const void *d1,const void *d2)
|
yuuji@0
|
421 {
|
yuuji@0
|
422 return atoi ((*(struct direct **) d1)->d_name) -
|
yuuji@0
|
423 atoi ((*(struct direct **) d2)->d_name);
|
yuuji@0
|
424 }
|
yuuji@0
|
425
|
yuuji@0
|
426
|
yuuji@0
|
427 /* News close
|
yuuji@0
|
428 * Accepts: MAIL stream
|
yuuji@0
|
429 * option flags
|
yuuji@0
|
430 */
|
yuuji@0
|
431
|
yuuji@0
|
432 void news_close (MAILSTREAM *stream,long options)
|
yuuji@0
|
433 {
|
yuuji@0
|
434 if (LOCAL) { /* only if a file is open */
|
yuuji@0
|
435 news_check (stream); /* dump final checkpoint */
|
yuuji@0
|
436 if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
|
yuuji@0
|
437 if (LOCAL->name) fs_give ((void **) &LOCAL->name);
|
yuuji@0
|
438 /* nuke the local data */
|
yuuji@0
|
439 fs_give ((void **) &stream->local);
|
yuuji@0
|
440 stream->dtb = NIL; /* log out the DTB */
|
yuuji@0
|
441 }
|
yuuji@0
|
442 }
|
yuuji@0
|
443
|
yuuji@0
|
444 /* News fetch fast information
|
yuuji@0
|
445 * Accepts: MAIL stream
|
yuuji@0
|
446 * sequence
|
yuuji@0
|
447 * option flags
|
yuuji@0
|
448 */
|
yuuji@0
|
449
|
yuuji@0
|
450 void news_fast (MAILSTREAM *stream,char *sequence,long flags)
|
yuuji@0
|
451 {
|
yuuji@0
|
452 MESSAGECACHE *elt;
|
yuuji@0
|
453 unsigned long i;
|
yuuji@0
|
454 /* set up metadata for all messages */
|
yuuji@0
|
455 if (stream && LOCAL && ((flags & FT_UID) ?
|
yuuji@0
|
456 mail_uid_sequence (stream,sequence) :
|
yuuji@0
|
457 mail_sequence (stream,sequence)))
|
yuuji@0
|
458 for (i = 1; i <= stream->nmsgs; i++)
|
yuuji@0
|
459 if ((elt = mail_elt (stream,i))->sequence &&
|
yuuji@0
|
460 !(elt->day && elt->rfc822_size)) news_load_message (stream,i,NIL);
|
yuuji@0
|
461 }
|
yuuji@0
|
462
|
yuuji@0
|
463
|
yuuji@0
|
464 /* News fetch flags
|
yuuji@0
|
465 * Accepts: MAIL stream
|
yuuji@0
|
466 * sequence
|
yuuji@0
|
467 * option flags
|
yuuji@0
|
468 */
|
yuuji@0
|
469
|
yuuji@0
|
470 void news_flags (MAILSTREAM *stream,char *sequence,long flags)
|
yuuji@0
|
471 {
|
yuuji@0
|
472 unsigned long i;
|
yuuji@0
|
473 if ((flags & FT_UID) ? /* validate all elts */
|
yuuji@0
|
474 mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))
|
yuuji@0
|
475 for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->valid = T;
|
yuuji@0
|
476 }
|
yuuji@0
|
477
|
yuuji@0
|
478 /* News load message into cache
|
yuuji@0
|
479 * Accepts: MAIL stream
|
yuuji@0
|
480 * message #
|
yuuji@0
|
481 * option flags
|
yuuji@0
|
482 */
|
yuuji@0
|
483
|
yuuji@0
|
484 void news_load_message (MAILSTREAM *stream,unsigned long msgno,long flags)
|
yuuji@0
|
485 {
|
yuuji@0
|
486 unsigned long i,j,nlseen;
|
yuuji@0
|
487 int fd;
|
yuuji@0
|
488 unsigned char c,*t;
|
yuuji@0
|
489 struct stat sbuf;
|
yuuji@0
|
490 MESSAGECACHE *elt;
|
yuuji@0
|
491 FDDATA d;
|
yuuji@0
|
492 STRING bs;
|
yuuji@0
|
493 elt = mail_elt (stream,msgno);/* get elt */
|
yuuji@0
|
494 /* build message file name */
|
yuuji@0
|
495 sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
|
yuuji@0
|
496 /* anything we need not currently cached? */
|
yuuji@0
|
497 if ((!elt->day || !elt->rfc822_size ||
|
yuuji@0
|
498 ((flags & NLM_HEADER) && !elt->private.msg.header.text.data) ||
|
yuuji@0
|
499 ((flags & NLM_TEXT) && !elt->private.msg.text.text.data)) &&
|
yuuji@0
|
500 ((fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0)) {
|
yuuji@0
|
501 fstat (fd,&sbuf); /* get file metadata */
|
yuuji@0
|
502 d.fd = fd; /* set up file descriptor */
|
yuuji@0
|
503 d.pos = 0; /* start of file */
|
yuuji@0
|
504 d.chunk = LOCAL->buf;
|
yuuji@0
|
505 d.chunksize = CHUNKSIZE;
|
yuuji@0
|
506 INIT (&bs,fd_string,&d,sbuf.st_size);
|
yuuji@0
|
507 if (!elt->day) { /* set internaldate to file date */
|
yuuji@0
|
508 struct tm *tm = gmtime (&sbuf.st_mtime);
|
yuuji@0
|
509 elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
|
yuuji@0
|
510 elt->year = tm->tm_year + 1900 - BASEYEAR;
|
yuuji@0
|
511 elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
|
yuuji@0
|
512 elt->seconds = tm->tm_sec;
|
yuuji@0
|
513 elt->zhours = 0; elt->zminutes = 0;
|
yuuji@0
|
514 }
|
yuuji@0
|
515
|
yuuji@0
|
516 if (!elt->rfc822_size) { /* know message size yet? */
|
yuuji@0
|
517 for (i = 0, j = SIZE (&bs), nlseen = 0; j--; ) switch (SNX (&bs)) {
|
yuuji@0
|
518 case '\015': /* unlikely carriage return */
|
yuuji@0
|
519 if (!j || (CHR (&bs) != '\012')) {
|
yuuji@0
|
520 i++; /* ugh, raw CR */
|
yuuji@0
|
521 nlseen = NIL;
|
yuuji@0
|
522 break;
|
yuuji@0
|
523 }
|
yuuji@0
|
524 SNX (&bs); /* eat the line feed, drop in */
|
yuuji@0
|
525 case '\012': /* line feed? */
|
yuuji@0
|
526 i += 2; /* count a CRLF */
|
yuuji@0
|
527 /* header size known yet? */
|
yuuji@0
|
528 if (!elt->private.msg.header.text.size && nlseen) {
|
yuuji@0
|
529 /* note position in file */
|
yuuji@0
|
530 elt->private.special.text.size = GETPOS (&bs);
|
yuuji@0
|
531 /* and CRLF-adjusted size */
|
yuuji@0
|
532 elt->private.msg.header.text.size = i;
|
yuuji@0
|
533 }
|
yuuji@0
|
534 nlseen = T; /* note newline seen */
|
yuuji@0
|
535 break;
|
yuuji@0
|
536 default: /* ordinary chararacter */
|
yuuji@0
|
537 i++;
|
yuuji@0
|
538 nlseen = NIL;
|
yuuji@0
|
539 break;
|
yuuji@0
|
540 }
|
yuuji@0
|
541 SETPOS (&bs,0); /* restore old position */
|
yuuji@0
|
542 elt->rfc822_size = i; /* note that we have size now */
|
yuuji@0
|
543 /* header is entire message if no delimiter */
|
yuuji@0
|
544 if (!elt->private.msg.header.text.size)
|
yuuji@0
|
545 elt->private.msg.header.text.size = elt->rfc822_size;
|
yuuji@0
|
546 /* text is remainder of message */
|
yuuji@0
|
547 elt->private.msg.text.text.size =
|
yuuji@0
|
548 elt->rfc822_size - elt->private.msg.header.text.size;
|
yuuji@0
|
549 }
|
yuuji@0
|
550
|
yuuji@0
|
551 /* need to load cache with message data? */
|
yuuji@0
|
552 if (((flags & NLM_HEADER) && !elt->private.msg.header.text.data) ||
|
yuuji@0
|
553 ((flags & NLM_TEXT) && !elt->private.msg.text.text.data)) {
|
yuuji@0
|
554 /* purge cache if too big */
|
yuuji@0
|
555 if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
|
yuuji@0
|
556 /* just can't keep that much */
|
yuuji@0
|
557 mail_gc (stream,GC_TEXTS);
|
yuuji@0
|
558 LOCAL->cachedtexts = 0;
|
yuuji@0
|
559 }
|
yuuji@0
|
560 if ((flags & NLM_HEADER) && !elt->private.msg.header.text.data) {
|
yuuji@0
|
561 t = elt->private.msg.header.text.data =
|
yuuji@0
|
562 (unsigned char *) fs_get (elt->private.msg.header.text.size + 1);
|
yuuji@0
|
563 LOCAL->cachedtexts += elt->private.msg.header.text.size;
|
yuuji@0
|
564 /* read in message header */
|
yuuji@0
|
565 for (i = 0; i <= elt->private.msg.header.text.size; i++)
|
yuuji@0
|
566 switch (c = SNX (&bs)) {
|
yuuji@0
|
567 case '\015': /* unlikely carriage return */
|
yuuji@0
|
568 *t++ = c;
|
yuuji@0
|
569 if ((CHR (&bs) == '\012')) *t++ = SNX (&bs);
|
yuuji@0
|
570 break;
|
yuuji@0
|
571 case '\012': /* line feed? */
|
yuuji@0
|
572 *t++ = '\015';
|
yuuji@0
|
573 default:
|
yuuji@0
|
574 *t++ = c;
|
yuuji@0
|
575 break;
|
yuuji@0
|
576 }
|
yuuji@0
|
577 *t = '\0'; /* tie off string */
|
yuuji@0
|
578 }
|
yuuji@0
|
579 if ((flags & NLM_TEXT) && !elt->private.msg.text.text.data) {
|
yuuji@0
|
580 t = elt->private.msg.text.text.data =
|
yuuji@0
|
581 (unsigned char *) fs_get (elt->private.msg.text.text.size + 1);
|
yuuji@0
|
582 SETPOS (&bs,elt->private.msg.header.text.size);
|
yuuji@0
|
583 LOCAL->cachedtexts += elt->private.msg.text.text.size;
|
yuuji@0
|
584 /* read in message text */
|
yuuji@0
|
585 for (i = 0; i <= elt->private.msg.text.text.size; i++)
|
yuuji@0
|
586 switch (c = SNX (&bs)) {
|
yuuji@0
|
587 case '\015': /* unlikely carriage return */
|
yuuji@0
|
588 *t++ = c;
|
yuuji@0
|
589 if ((CHR (&bs) == '\012')) *t++ = SNX (&bs);
|
yuuji@0
|
590 break;
|
yuuji@0
|
591 case '\012': /* line feed? */
|
yuuji@0
|
592 *t++ = '\015';
|
yuuji@0
|
593 default:
|
yuuji@0
|
594 *t++ = c;
|
yuuji@0
|
595 break;
|
yuuji@0
|
596 }
|
yuuji@0
|
597 *t = '\0'; /* tie off string */
|
yuuji@0
|
598 }
|
yuuji@0
|
599 }
|
yuuji@0
|
600 close (fd); /* flush message file */
|
yuuji@0
|
601 }
|
yuuji@0
|
602 }
|
yuuji@0
|
603
|
yuuji@0
|
604 /* News fetch message header
|
yuuji@0
|
605 * Accepts: MAIL stream
|
yuuji@0
|
606 * message # to fetch
|
yuuji@0
|
607 * pointer to returned header text length
|
yuuji@0
|
608 * option flags
|
yuuji@0
|
609 * Returns: message header in RFC822 format
|
yuuji@0
|
610 */
|
yuuji@0
|
611
|
yuuji@0
|
612 char *news_header (MAILSTREAM *stream,unsigned long msgno,
|
yuuji@0
|
613 unsigned long *length,long flags)
|
yuuji@0
|
614 {
|
yuuji@0
|
615 MESSAGECACHE *elt;
|
yuuji@0
|
616 *length = 0; /* default to empty */
|
yuuji@0
|
617 if (flags & FT_UID) return "";/* UID call "impossible" */
|
yuuji@0
|
618 elt = mail_elt (stream,msgno);/* get elt */
|
yuuji@0
|
619 if (!elt->private.msg.header.text.data)
|
yuuji@0
|
620 news_load_message (stream,msgno,NLM_HEADER);
|
yuuji@0
|
621 *length = elt->private.msg.header.text.size;
|
yuuji@0
|
622 return (char *) elt->private.msg.header.text.data;
|
yuuji@0
|
623 }
|
yuuji@0
|
624
|
yuuji@0
|
625
|
yuuji@0
|
626 /* News fetch message text (body only)
|
yuuji@0
|
627 * Accepts: MAIL stream
|
yuuji@0
|
628 * message # to fetch
|
yuuji@0
|
629 * pointer to returned stringstruct
|
yuuji@0
|
630 * option flags
|
yuuji@0
|
631 * Returns: T on success, NIL on failure
|
yuuji@0
|
632 */
|
yuuji@0
|
633
|
yuuji@0
|
634 long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
|
yuuji@0
|
635 {
|
yuuji@0
|
636 MESSAGECACHE *elt;
|
yuuji@0
|
637 /* UID call "impossible" */
|
yuuji@0
|
638 if (flags & FT_UID) return NIL;
|
yuuji@0
|
639 elt = mail_elt (stream,msgno);/* get elt */
|
yuuji@0
|
640 /* snarf message if don't have it yet */
|
yuuji@0
|
641 if (!elt->private.msg.text.text.data) {
|
yuuji@0
|
642 news_load_message (stream,msgno,NLM_TEXT);
|
yuuji@0
|
643 if (!elt->private.msg.text.text.data) return NIL;
|
yuuji@0
|
644 }
|
yuuji@0
|
645 if (!(flags & FT_PEEK)) { /* mark as seen */
|
yuuji@0
|
646 mail_elt (stream,msgno)->seen = T;
|
yuuji@0
|
647 mm_flags (stream,msgno);
|
yuuji@0
|
648 }
|
yuuji@0
|
649 INIT (bs,mail_string,elt->private.msg.text.text.data,
|
yuuji@0
|
650 elt->private.msg.text.text.size);
|
yuuji@0
|
651 return T;
|
yuuji@0
|
652 }
|
yuuji@0
|
653
|
yuuji@0
|
654 /* News per-message modify flag
|
yuuji@0
|
655 * Accepts: MAIL stream
|
yuuji@0
|
656 * message cache element
|
yuuji@0
|
657 */
|
yuuji@0
|
658
|
yuuji@0
|
659 void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
|
yuuji@0
|
660 {
|
yuuji@0
|
661 if (!LOCAL->dirty) { /* only bother checking if not dirty yet */
|
yuuji@0
|
662 if (elt->valid) { /* if done, see if deleted changed */
|
yuuji@0
|
663 if (elt->sequence != elt->deleted) LOCAL->dirty = T;
|
yuuji@0
|
664 elt->sequence = T; /* leave the sequence set */
|
yuuji@0
|
665 }
|
yuuji@0
|
666 /* note current setting of deleted flag */
|
yuuji@0
|
667 else elt->sequence = elt->deleted;
|
yuuji@0
|
668 }
|
yuuji@0
|
669 }
|
yuuji@0
|
670
|
yuuji@0
|
671
|
yuuji@0
|
672 /* News ping mailbox
|
yuuji@0
|
673 * Accepts: MAIL stream
|
yuuji@0
|
674 * Returns: T if stream alive, else NIL
|
yuuji@0
|
675 */
|
yuuji@0
|
676
|
yuuji@0
|
677 long news_ping (MAILSTREAM *stream)
|
yuuji@0
|
678 {
|
yuuji@0
|
679 return T; /* always alive */
|
yuuji@0
|
680 }
|
yuuji@0
|
681
|
yuuji@0
|
682
|
yuuji@0
|
683 /* News check mailbox
|
yuuji@0
|
684 * Accepts: MAIL stream
|
yuuji@0
|
685 */
|
yuuji@0
|
686
|
yuuji@0
|
687 void news_check (MAILSTREAM *stream)
|
yuuji@0
|
688 {
|
yuuji@0
|
689 /* never do if no updates */
|
yuuji@0
|
690 if (LOCAL->dirty) newsrc_write (LOCAL->name,stream);
|
yuuji@0
|
691 LOCAL->dirty = NIL;
|
yuuji@0
|
692 }
|
yuuji@0
|
693
|
yuuji@0
|
694
|
yuuji@0
|
695 /* News expunge mailbox
|
yuuji@0
|
696 * Accepts: MAIL stream
|
yuuji@0
|
697 * sequence to expunge if non-NIL
|
yuuji@0
|
698 * expunge options
|
yuuji@0
|
699 * Returns: T if success, NIL if failure
|
yuuji@0
|
700 */
|
yuuji@0
|
701
|
yuuji@0
|
702 long news_expunge (MAILSTREAM *stream,char *sequence,long options)
|
yuuji@0
|
703 {
|
yuuji@0
|
704 if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
|
yuuji@0
|
705 return LONGT;
|
yuuji@0
|
706 }
|
yuuji@0
|
707
|
yuuji@0
|
708 /* News copy message(s)
|
yuuji@0
|
709 * Accepts: MAIL stream
|
yuuji@0
|
710 * sequence
|
yuuji@0
|
711 * destination mailbox
|
yuuji@0
|
712 * option flags
|
yuuji@0
|
713 * Returns: T if copy successful, else NIL
|
yuuji@0
|
714 */
|
yuuji@0
|
715
|
yuuji@0
|
716 long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
|
yuuji@0
|
717 {
|
yuuji@0
|
718 mailproxycopy_t pc =
|
yuuji@0
|
719 (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
|
yuuji@0
|
720 if (pc) return (*pc) (stream,sequence,mailbox,options);
|
yuuji@0
|
721 mm_log ("Copy not valid for News",ERROR);
|
yuuji@0
|
722 return NIL;
|
yuuji@0
|
723 }
|
yuuji@0
|
724
|
yuuji@0
|
725
|
yuuji@0
|
726 /* News append message from stringstruct
|
yuuji@0
|
727 * Accepts: MAIL stream
|
yuuji@0
|
728 * destination mailbox
|
yuuji@0
|
729 * append callback function
|
yuuji@0
|
730 * data for callback
|
yuuji@0
|
731 * Returns: T if append successful, else NIL
|
yuuji@0
|
732 */
|
yuuji@0
|
733
|
yuuji@0
|
734 long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
|
yuuji@0
|
735 {
|
yuuji@0
|
736 mm_log ("Append not valid for news",ERROR);
|
yuuji@0
|
737 return NIL;
|
yuuji@0
|
738 }
|