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