imapext-2007

annotate src/osdep/unix/news.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
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 }

UW-IMAP'd extensions by yuuji