imapext-2007

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

UW-IMAP'd extensions by yuuji