imapext-2007

diff 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 diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/osdep/unix/news.c	Mon Sep 14 15:17:45 2009 +0900
     1.3 @@ -0,0 +1,738 @@
     1.4 +/* ========================================================================
     1.5 + * Copyright 1988-2007 University of Washington
     1.6 + *
     1.7 + * Licensed under the Apache License, Version 2.0 (the "License");
     1.8 + * you may not use this file except in compliance with the License.
     1.9 + * You may obtain a copy of the License at
    1.10 + *
    1.11 + *     http://www.apache.org/licenses/LICENSE-2.0
    1.12 + *
    1.13 + * 
    1.14 + * ========================================================================
    1.15 + */
    1.16 +
    1.17 +/*
    1.18 + * Program:	News routines
    1.19 + *
    1.20 + * Author:	Mark Crispin
    1.21 + *		Networks and Distributed Computing
    1.22 + *		Computing & Communications
    1.23 + *		University of Washington
    1.24 + *		Administration Building, AG-44
    1.25 + *		Seattle, WA  98195
    1.26 + *		Internet: MRC@CAC.Washington.EDU
    1.27 + *
    1.28 + * Date:	4 September 1991
    1.29 + * Last Edited:	30 January 2007
    1.30 + */
    1.31 +
    1.32 +
    1.33 +#include <stdio.h>
    1.34 +#include <ctype.h>
    1.35 +#include <errno.h>
    1.36 +extern int errno;		/* just in case */
    1.37 +#include "mail.h"
    1.38 +#include "osdep.h"
    1.39 +#include <sys/stat.h>
    1.40 +#include <sys/time.h>
    1.41 +#include "misc.h"
    1.42 +#include "newsrc.h"
    1.43 +#include "fdstring.h"
    1.44 +
    1.45 +
    1.46 +/* news_load_message() flags */
    1.47 +
    1.48 +#define NLM_HEADER 0x1		/* load message text */
    1.49 +#define NLM_TEXT 0x2		/* load message text */
    1.50 +
    1.51 +/* NEWS I/O stream local data */
    1.52 +	
    1.53 +typedef struct news_local {
    1.54 +  unsigned int dirty : 1;	/* disk copy of .newsrc needs updating */
    1.55 +  char *dir;			/* spool directory name */
    1.56 +  char *name;			/* local mailbox name */
    1.57 +  unsigned char buf[CHUNKSIZE];	/* scratch buffer */
    1.58 +  unsigned long cachedtexts;	/* total size of all cached texts */
    1.59 +} NEWSLOCAL;
    1.60 +
    1.61 +
    1.62 +/* Convenient access to local data */
    1.63 +
    1.64 +#define LOCAL ((NEWSLOCAL *) stream->local)
    1.65 +
    1.66 +
    1.67 +/* Function prototypes */
    1.68 +
    1.69 +DRIVER *news_valid (char *name);
    1.70 +DRIVER *news_isvalid (char *name,char *mbx);
    1.71 +void *news_parameters (long function,void *value);
    1.72 +void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
    1.73 +void news_list (MAILSTREAM *stream,char *ref,char *pat);
    1.74 +void news_lsub (MAILSTREAM *stream,char *ref,char *pat);
    1.75 +long news_canonicalize (char *ref,char *pat,char *pattern);
    1.76 +long news_subscribe (MAILSTREAM *stream,char *mailbox);
    1.77 +long news_unsubscribe (MAILSTREAM *stream,char *mailbox);
    1.78 +long news_create (MAILSTREAM *stream,char *mailbox);
    1.79 +long news_delete (MAILSTREAM *stream,char *mailbox);
    1.80 +long news_rename (MAILSTREAM *stream,char *old,char *newname);
    1.81 +MAILSTREAM *news_open (MAILSTREAM *stream);
    1.82 +int news_select (struct direct *name);
    1.83 +int news_numsort (const void *d1,const void *d2);
    1.84 +void news_close (MAILSTREAM *stream,long options);
    1.85 +void news_fast (MAILSTREAM *stream,char *sequence,long flags);
    1.86 +void news_flags (MAILSTREAM *stream,char *sequence,long flags);
    1.87 +void news_load_message (MAILSTREAM *stream,unsigned long msgno,long flags);
    1.88 +char *news_header (MAILSTREAM *stream,unsigned long msgno,
    1.89 +		   unsigned long *length,long flags);
    1.90 +long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
    1.91 +void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
    1.92 +long news_ping (MAILSTREAM *stream);
    1.93 +void news_check (MAILSTREAM *stream);
    1.94 +long news_expunge (MAILSTREAM *stream,char *sequence,long options);
    1.95 +long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
    1.96 +long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
    1.97 +
    1.98 +/* News routines */
    1.99 +
   1.100 +
   1.101 +/* Driver dispatch used by MAIL */
   1.102 +
   1.103 +DRIVER newsdriver = {
   1.104 +  "news",			/* driver name */
   1.105 +				/* driver flags */
   1.106 +  DR_NEWS|DR_READONLY|DR_NOFAST|DR_NAMESPACE|DR_NONEWMAIL|DR_DIRFMT,
   1.107 +  (DRIVER *) NIL,		/* next driver */
   1.108 +  news_valid,			/* mailbox is valid for us */
   1.109 +  news_parameters,		/* manipulate parameters */
   1.110 +  news_scan,			/* scan mailboxes */
   1.111 +  news_list,			/* find mailboxes */
   1.112 +  news_lsub,			/* find subscribed mailboxes */
   1.113 +  news_subscribe,		/* subscribe to mailbox */
   1.114 +  news_unsubscribe,		/* unsubscribe from mailbox */
   1.115 +  news_create,			/* create mailbox */
   1.116 +  news_delete,			/* delete mailbox */
   1.117 +  news_rename,			/* rename mailbox */
   1.118 +  mail_status_default,		/* status of mailbox */
   1.119 +  news_open,			/* open mailbox */
   1.120 +  news_close,			/* close mailbox */
   1.121 +  news_fast,			/* fetch message "fast" attributes */
   1.122 +  news_flags,			/* fetch message flags */
   1.123 +  NIL,				/* fetch overview */
   1.124 +  NIL,				/* fetch message envelopes */
   1.125 +  news_header,			/* fetch message header */
   1.126 +  news_text,			/* fetch message body */
   1.127 +  NIL,				/* fetch partial message text */
   1.128 +  NIL,				/* unique identifier */
   1.129 +  NIL,				/* message number */
   1.130 +  NIL,				/* modify flags */
   1.131 +  news_flagmsg,			/* per-message modify flags */
   1.132 +  NIL,				/* search for message based on criteria */
   1.133 +  NIL,				/* sort messages */
   1.134 +  NIL,				/* thread messages */
   1.135 +  news_ping,			/* ping mailbox to see if still alive */
   1.136 +  news_check,			/* check for new messages */
   1.137 +  news_expunge,			/* expunge deleted messages */
   1.138 +  news_copy,			/* copy messages to another mailbox */
   1.139 +  news_append,			/* append string message to mailbox */
   1.140 +  NIL				/* garbage collect stream */
   1.141 +};
   1.142 +
   1.143 +				/* prototype stream */
   1.144 +MAILSTREAM newsproto = {&newsdriver};
   1.145 +
   1.146 +/* News validate mailbox
   1.147 + * Accepts: mailbox name
   1.148 + * Returns: our driver if name is valid, NIL otherwise
   1.149 + */
   1.150 +
   1.151 +DRIVER *news_valid (char *name)
   1.152 +{
   1.153 +  int fd;
   1.154 +  char *s,*t,*u;
   1.155 +  struct stat sbuf;
   1.156 +  if ((name[0] == '#') && (name[1] == 'n') && (name[2] == 'e') &&
   1.157 +      (name[3] == 'w') && (name[4] == 's') && (name[5] == '.') &&
   1.158 +      !strchr (name,'/') &&
   1.159 +      !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
   1.160 +      ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),O_RDONLY,
   1.161 +		   NIL)) >= 0)) {
   1.162 +    fstat (fd,&sbuf);		/* get size of active file */
   1.163 +				/* slurp in active file */
   1.164 +    read (fd,t = s = (char *) fs_get (sbuf.st_size+1),sbuf.st_size);
   1.165 +    s[sbuf.st_size] = '\0';	/* tie off file */
   1.166 +    close (fd);			/* flush file */
   1.167 +    while (*t && (u = strchr (t,' '))) {
   1.168 +      *u++ = '\0';		/* tie off at end of name */
   1.169 +      if (!strcmp (name+6,t)) {
   1.170 +	fs_give ((void **) &s);	/* flush data */
   1.171 +	return &newsdriver;
   1.172 +      }
   1.173 +      t = 1 + strchr (u,'\n');	/* next line */
   1.174 +    }
   1.175 +    fs_give ((void **) &s);	/* flush data */
   1.176 +  }
   1.177 +  return NIL;			/* return status */
   1.178 +}
   1.179 +
   1.180 +/* News manipulate driver parameters
   1.181 + * Accepts: function code
   1.182 + *	    function-dependent value
   1.183 + * Returns: function-dependent return value
   1.184 + */
   1.185 +
   1.186 +void *news_parameters (long function,void *value)
   1.187 +{
   1.188 +  return (function == GET_NEWSRC) ? env_parameters (function,value) : NIL;
   1.189 +}
   1.190 +
   1.191 +
   1.192 +/* News scan mailboxes
   1.193 + * Accepts: mail stream
   1.194 + *	    reference
   1.195 + *	    pattern to search
   1.196 + *	    string to scan
   1.197 + */
   1.198 +
   1.199 +void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
   1.200 +{
   1.201 +  char tmp[MAILTMPLEN];
   1.202 +  if (news_canonicalize (ref,pat,tmp))
   1.203 +    mm_log ("Scan not valid for news mailboxes",ERROR);
   1.204 +}
   1.205 +
   1.206 +/* News find list of newsgroups
   1.207 + * Accepts: mail stream
   1.208 + *	    reference
   1.209 + *	    pattern to search
   1.210 + */
   1.211 +
   1.212 +void news_list (MAILSTREAM *stream,char *ref,char *pat)
   1.213 +{
   1.214 +  int fd;
   1.215 +  int i;
   1.216 +  char *s,*t,*u,*r,pattern[MAILTMPLEN],name[MAILTMPLEN];
   1.217 +  struct stat sbuf;
   1.218 +  if (!pat || !*pat) {		/* empty pattern? */
   1.219 +    if (news_canonicalize (ref,"*",pattern)) {
   1.220 +				/* tie off name at root */
   1.221 +      if (s = strchr (pattern,'.')) *++s = '\0';
   1.222 +      else pattern[0] = '\0';
   1.223 +      mm_list (stream,'.',pattern,LATT_NOSELECT);
   1.224 +    }
   1.225 +  }
   1.226 +  else if (news_canonicalize (ref,pat,pattern) &&
   1.227 +	   !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
   1.228 +	   ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),
   1.229 +			O_RDONLY,NIL)) >= 0)) {
   1.230 +    fstat (fd,&sbuf);		/* get file size and read data */
   1.231 +    read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
   1.232 +    close (fd);			/* close file */
   1.233 +    s[sbuf.st_size] = '\0';	/* tie off string */
   1.234 +    strcpy (name,"#news.");	/* write initial prefix */
   1.235 +    i = strlen (pattern);	/* length of pattern */
   1.236 +    if (pattern[--i] != '%') i = 0;
   1.237 +    if (t = strtok_r (s,"\n",&r)) do if (u = strchr (t,' ')) {
   1.238 +      *u = '\0';		/* tie off at end of name */
   1.239 +      strcpy (name + 6,t);	/* make full form of name */
   1.240 +      if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,NIL);
   1.241 +      else if (i && (u = strchr (name + i,'.'))) {
   1.242 +	*u = '\0';		/* tie off at delimiter, see if matches */
   1.243 +	if (pmatch_full (name,pattern,'.'))
   1.244 +	  mm_list (stream,'.',name,LATT_NOSELECT);
   1.245 +      }
   1.246 +    } while (t = strtok_r (NIL,"\n",&r));
   1.247 +    fs_give ((void **) &s);
   1.248 +  }
   1.249 +}
   1.250 +
   1.251 +/* News find list of subscribed newsgroups
   1.252 + * Accepts: mail stream
   1.253 + *	    reference
   1.254 + *	    pattern to search
   1.255 + */
   1.256 +
   1.257 +void news_lsub (MAILSTREAM *stream,char *ref,char *pat)
   1.258 +{
   1.259 +  char pattern[MAILTMPLEN];
   1.260 +				/* return data from newsrc */
   1.261 +  if (news_canonicalize (ref,pat,pattern)) newsrc_lsub (stream,pattern);
   1.262 +}
   1.263 +
   1.264 +
   1.265 +/* News canonicalize newsgroup name
   1.266 + * Accepts: reference
   1.267 + *	    pattern
   1.268 + *	    returned single pattern
   1.269 + * Returns: T on success, NIL on failure
   1.270 + */
   1.271 +
   1.272 +long news_canonicalize (char *ref,char *pat,char *pattern)
   1.273 +{
   1.274 +  unsigned long i;
   1.275 +  char *s;
   1.276 +  if (ref && *ref) {		/* have a reference */
   1.277 +    strcpy (pattern,ref);	/* copy reference to pattern */
   1.278 +				/* # overrides mailbox field in reference */
   1.279 +    if (*pat == '#') strcpy (pattern,pat);
   1.280 +				/* pattern starts, reference ends, with . */
   1.281 +    else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
   1.282 +      strcat (pattern,pat + 1);	/* append, omitting one of the period */
   1.283 +    else strcat (pattern,pat);	/* anything else is just appended */
   1.284 +  }
   1.285 +  else strcpy (pattern,pat);	/* just have basic name */
   1.286 +  if ((pattern[0] == '#') && (pattern[1] == 'n') && (pattern[2] == 'e') &&
   1.287 +      (pattern[3] == 'w') && (pattern[4] == 's') && (pattern[5] == '.') &&
   1.288 +      !strchr (pattern,'/')) {	/* count wildcards */
   1.289 +    for (i = 0, s = pattern; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
   1.290 +				/* success if not too many */
   1.291 +    if (i <= MAXWILDCARDS) return LONGT;
   1.292 +    MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
   1.293 +  }
   1.294 +  return NIL;
   1.295 +}
   1.296 +
   1.297 +/* News subscribe to mailbox
   1.298 + * Accepts: mail stream
   1.299 + *	    mailbox to add to subscription list
   1.300 + * Returns: T on success, NIL on failure
   1.301 + */
   1.302 +
   1.303 +long news_subscribe (MAILSTREAM *stream,char *mailbox)
   1.304 +{
   1.305 +  return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,':') : NIL;
   1.306 +}
   1.307 +
   1.308 +
   1.309 +/* NEWS unsubscribe to mailbox
   1.310 + * Accepts: mail stream
   1.311 + *	    mailbox to delete from subscription list
   1.312 + * Returns: T on success, NIL on failure
   1.313 + */
   1.314 +
   1.315 +long news_unsubscribe (MAILSTREAM *stream,char *mailbox)
   1.316 +{
   1.317 +  return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,'!') : NIL;
   1.318 +}
   1.319 +
   1.320 +/* News create mailbox
   1.321 + * Accepts: mail stream
   1.322 + *	    mailbox name to create
   1.323 + * Returns: T on success, NIL on failure
   1.324 + */
   1.325 +
   1.326 +long news_create (MAILSTREAM *stream,char *mailbox)
   1.327 +{
   1.328 +  return NIL;			/* never valid for News */
   1.329 +}
   1.330 +
   1.331 +
   1.332 +/* News delete mailbox
   1.333 + *	    mailbox name to delete
   1.334 + * Returns: T on success, NIL on failure
   1.335 + */
   1.336 +
   1.337 +long news_delete (MAILSTREAM *stream,char *mailbox)
   1.338 +{
   1.339 +  return NIL;			/* never valid for News */
   1.340 +}
   1.341 +
   1.342 +
   1.343 +/* News rename mailbox
   1.344 + * Accepts: mail stream
   1.345 + *	    old mailbox name
   1.346 + *	    new mailbox name
   1.347 + * Returns: T on success, NIL on failure
   1.348 + */
   1.349 +
   1.350 +long news_rename (MAILSTREAM *stream,char *old,char *newname)
   1.351 +{
   1.352 +  return NIL;			/* never valid for News */
   1.353 +}
   1.354 +
   1.355 +/* News open
   1.356 + * Accepts: stream to open
   1.357 + * Returns: stream on success, NIL on failure
   1.358 + */
   1.359 +
   1.360 +MAILSTREAM *news_open (MAILSTREAM *stream)
   1.361 +{
   1.362 +  long i,nmsgs;
   1.363 +  char *s,tmp[MAILTMPLEN];
   1.364 +  struct direct **names = NIL;
   1.365 +  				/* return prototype for OP_PROTOTYPE call */
   1.366 +  if (!stream) return &newsproto;
   1.367 +  if (stream->local) fatal ("news recycle stream");
   1.368 +				/* build directory name */
   1.369 +  sprintf (s = tmp,"%s/%s",(char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),
   1.370 +	   stream->mailbox + 6);
   1.371 +  while (s = strchr (s,'.')) *s = '/';
   1.372 +				/* scan directory */
   1.373 +  if ((nmsgs = scandir (tmp,&names,news_select,news_numsort)) >= 0) {
   1.374 +    mail_exists (stream,nmsgs);	/* notify upper level that messages exist */
   1.375 +    stream->local = fs_get (sizeof (NEWSLOCAL));
   1.376 +    LOCAL->dirty = NIL;		/* no update to .newsrc needed yet */
   1.377 +    LOCAL->dir = cpystr (tmp);	/* copy directory name for later */
   1.378 +    LOCAL->name = cpystr (stream->mailbox + 6);
   1.379 +    for (i = 0; i < nmsgs; ++i) {
   1.380 +      stream->uid_last = mail_elt (stream,i+1)->private.uid =
   1.381 +	atoi (names[i]->d_name);
   1.382 +      fs_give ((void **) &names[i]);
   1.383 +    }
   1.384 +    s = (void *) names;		/* stupid language */
   1.385 +    fs_give ((void **) &s);	/* free directory */
   1.386 +    LOCAL->cachedtexts = 0;	/* no cached texts */
   1.387 +    stream->sequence++;		/* bump sequence number */
   1.388 +    stream->rdonly = stream->perm_deleted = T;
   1.389 +				/* UIDs are always valid */
   1.390 +    stream->uid_validity = 0xbeefface;
   1.391 +				/* read .newsrc entries */
   1.392 +    mail_recent (stream,newsrc_read (LOCAL->name,stream));
   1.393 +				/* notify if empty newsgroup */
   1.394 +    if (!(stream->nmsgs || stream->silent)) {
   1.395 +      sprintf (tmp,"Newsgroup %s is empty",LOCAL->name);
   1.396 +      mm_log (tmp,WARN);
   1.397 +    }
   1.398 +  }
   1.399 +  else mm_log ("Unable to scan newsgroup spool directory",ERROR);
   1.400 +  return LOCAL ? stream : NIL;	/* if stream is alive, return to caller */
   1.401 +}
   1.402 +
   1.403 +/* News file name selection test
   1.404 + * Accepts: candidate directory entry
   1.405 + * Returns: T to use file name, NIL to skip it
   1.406 + */
   1.407 +
   1.408 +int news_select (struct direct *name)
   1.409 +{
   1.410 +  char c;
   1.411 +  char *s = name->d_name;
   1.412 +  while (c = *s++) if (!isdigit (c)) return NIL;
   1.413 +  return T;
   1.414 +}
   1.415 +
   1.416 +
   1.417 +/* News file name comparision
   1.418 + * Accepts: first candidate directory entry
   1.419 + *	    second candidate directory entry
   1.420 + * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
   1.421 + */
   1.422 +
   1.423 +int news_numsort (const void *d1,const void *d2)
   1.424 +{
   1.425 +  return atoi ((*(struct direct **) d1)->d_name) -
   1.426 +    atoi ((*(struct direct **) d2)->d_name);
   1.427 +}
   1.428 +
   1.429 +
   1.430 +/* News close
   1.431 + * Accepts: MAIL stream
   1.432 + *	    option flags
   1.433 + */
   1.434 +
   1.435 +void news_close (MAILSTREAM *stream,long options)
   1.436 +{
   1.437 +  if (LOCAL) {			/* only if a file is open */
   1.438 +    news_check (stream);	/* dump final checkpoint */
   1.439 +    if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
   1.440 +    if (LOCAL->name) fs_give ((void **) &LOCAL->name);
   1.441 +				/* nuke the local data */
   1.442 +    fs_give ((void **) &stream->local);
   1.443 +    stream->dtb = NIL;		/* log out the DTB */
   1.444 +  }
   1.445 +}
   1.446 +
   1.447 +/* News fetch fast information
   1.448 + * Accepts: MAIL stream
   1.449 + *	    sequence
   1.450 + *	    option flags
   1.451 + */
   1.452 +
   1.453 +void news_fast (MAILSTREAM *stream,char *sequence,long flags)
   1.454 +{
   1.455 +  MESSAGECACHE *elt;
   1.456 +  unsigned long i;
   1.457 +				/* set up metadata for all messages */
   1.458 +  if (stream && LOCAL && ((flags & FT_UID) ?
   1.459 +			  mail_uid_sequence (stream,sequence) :
   1.460 +			  mail_sequence (stream,sequence)))
   1.461 +    for (i = 1; i <= stream->nmsgs; i++)
   1.462 +      if ((elt = mail_elt (stream,i))->sequence &&
   1.463 +	  !(elt->day && elt->rfc822_size)) news_load_message (stream,i,NIL);
   1.464 +}
   1.465 +
   1.466 +
   1.467 +/* News fetch flags
   1.468 + * Accepts: MAIL stream
   1.469 + *	    sequence
   1.470 + *	    option flags
   1.471 + */
   1.472 +
   1.473 +void news_flags (MAILSTREAM *stream,char *sequence,long flags)
   1.474 +{
   1.475 +  unsigned long i;
   1.476 +  if ((flags & FT_UID) ?	/* validate all elts */
   1.477 +      mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))
   1.478 +    for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->valid = T;
   1.479 +}
   1.480 +
   1.481 +/* News load message into cache
   1.482 + * Accepts: MAIL stream
   1.483 + *	    message #
   1.484 + *	    option flags
   1.485 + */
   1.486 +
   1.487 +void news_load_message (MAILSTREAM *stream,unsigned long msgno,long flags)
   1.488 +{
   1.489 +  unsigned long i,j,nlseen;
   1.490 +  int fd;
   1.491 +  unsigned char c,*t;
   1.492 +  struct stat sbuf;
   1.493 +  MESSAGECACHE *elt;
   1.494 +  FDDATA d;
   1.495 +  STRING bs;
   1.496 +  elt = mail_elt (stream,msgno);/* get elt */
   1.497 +				/* build message file name */
   1.498 +  sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
   1.499 +				/* anything we need not currently cached? */
   1.500 +  if ((!elt->day || !elt->rfc822_size ||
   1.501 +       ((flags & NLM_HEADER) && !elt->private.msg.header.text.data) ||
   1.502 +       ((flags & NLM_TEXT) && !elt->private.msg.text.text.data)) &&
   1.503 +      ((fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0)) {
   1.504 +    fstat (fd,&sbuf);		/* get file metadata */
   1.505 +    d.fd = fd;			/* set up file descriptor */
   1.506 +    d.pos = 0;			/* start of file */
   1.507 +    d.chunk = LOCAL->buf;
   1.508 +    d.chunksize = CHUNKSIZE;
   1.509 +    INIT (&bs,fd_string,&d,sbuf.st_size);
   1.510 +    if (!elt->day) {		/* set internaldate to file date */
   1.511 +      struct tm *tm = gmtime (&sbuf.st_mtime);
   1.512 +      elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
   1.513 +      elt->year = tm->tm_year + 1900 - BASEYEAR;
   1.514 +      elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
   1.515 +      elt->seconds = tm->tm_sec;
   1.516 +      elt->zhours = 0; elt->zminutes = 0;
   1.517 +    }
   1.518 +
   1.519 +    if (!elt->rfc822_size) {	/* know message size yet? */
   1.520 +      for (i = 0, j = SIZE (&bs), nlseen = 0; j--; ) switch (SNX (&bs)) {
   1.521 +      case '\015':		/* unlikely carriage return */
   1.522 +	if (!j || (CHR (&bs) != '\012')) {
   1.523 +	  i++;			/* ugh, raw CR */
   1.524 +	  nlseen = NIL;
   1.525 +	  break;
   1.526 +	}
   1.527 +	SNX (&bs);		/* eat the line feed, drop in */
   1.528 +      case '\012':		/* line feed? */
   1.529 +	i += 2;			/* count a CRLF */
   1.530 +				/* header size known yet? */
   1.531 +	if (!elt->private.msg.header.text.size && nlseen) {
   1.532 +				/* note position in file */
   1.533 +	  elt->private.special.text.size = GETPOS (&bs);
   1.534 +				/* and CRLF-adjusted size */
   1.535 +	  elt->private.msg.header.text.size = i;
   1.536 +	}
   1.537 +	nlseen = T;		/* note newline seen */
   1.538 +	break;
   1.539 +      default:			/* ordinary chararacter */
   1.540 +	i++;
   1.541 +	nlseen = NIL;
   1.542 +	break;
   1.543 +      }
   1.544 +      SETPOS (&bs,0);		/* restore old position */
   1.545 +      elt->rfc822_size = i;	/* note that we have size now */
   1.546 +				/* header is entire message if no delimiter */
   1.547 +      if (!elt->private.msg.header.text.size)
   1.548 +	elt->private.msg.header.text.size = elt->rfc822_size;
   1.549 +				/* text is remainder of message */
   1.550 +      elt->private.msg.text.text.size =
   1.551 +	elt->rfc822_size - elt->private.msg.header.text.size;
   1.552 +    }
   1.553 +
   1.554 +				/* need to load cache with message data? */
   1.555 +    if (((flags & NLM_HEADER) && !elt->private.msg.header.text.data) ||
   1.556 +	((flags & NLM_TEXT) && !elt->private.msg.text.text.data)) {
   1.557 +				/* purge cache if too big */
   1.558 +      if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
   1.559 +				/* just can't keep that much */
   1.560 +	mail_gc (stream,GC_TEXTS);
   1.561 +	LOCAL->cachedtexts = 0;
   1.562 +      }
   1.563 +      if ((flags & NLM_HEADER) && !elt->private.msg.header.text.data) {
   1.564 +	t = elt->private.msg.header.text.data =
   1.565 +	  (unsigned char *) fs_get (elt->private.msg.header.text.size + 1);
   1.566 +	LOCAL->cachedtexts += elt->private.msg.header.text.size;
   1.567 +				/* read in message header */
   1.568 +	for (i = 0; i <= elt->private.msg.header.text.size; i++)
   1.569 +	  switch (c = SNX (&bs)) {
   1.570 +	  case '\015':		/* unlikely carriage return */
   1.571 +	    *t++ = c;
   1.572 +	    if ((CHR (&bs) == '\012')) *t++ = SNX (&bs);
   1.573 +	    break;
   1.574 +	  case '\012':		/* line feed? */
   1.575 +	    *t++ = '\015';
   1.576 +	  default:
   1.577 +	    *t++ = c;
   1.578 +	    break;
   1.579 +	  }
   1.580 +	*t = '\0';		/* tie off string */
   1.581 +      }
   1.582 +      if ((flags & NLM_TEXT) && !elt->private.msg.text.text.data) {
   1.583 +	t = elt->private.msg.text.text.data =
   1.584 +	  (unsigned char *) fs_get (elt->private.msg.text.text.size + 1);
   1.585 +	SETPOS (&bs,elt->private.msg.header.text.size);
   1.586 +	LOCAL->cachedtexts += elt->private.msg.text.text.size;
   1.587 +				/* read in message text */
   1.588 +	for (i = 0; i <= elt->private.msg.text.text.size; i++)
   1.589 +	  switch (c = SNX (&bs)) {
   1.590 +	  case '\015':		/* unlikely carriage return */
   1.591 +	    *t++ = c;
   1.592 +	    if ((CHR (&bs) == '\012')) *t++ = SNX (&bs);
   1.593 +	    break;
   1.594 +	  case '\012':		/* line feed? */
   1.595 +	    *t++ = '\015';
   1.596 +	  default:
   1.597 +	    *t++ = c;
   1.598 +	    break;
   1.599 +	  }
   1.600 +	*t = '\0';		/* tie off string */
   1.601 +      }
   1.602 +    }
   1.603 +    close (fd);			/* flush message file */
   1.604 +  }
   1.605 +}
   1.606 +
   1.607 +/* News fetch message header
   1.608 + * Accepts: MAIL stream
   1.609 + *	    message # to fetch
   1.610 + *	    pointer to returned header text length
   1.611 + *	    option flags
   1.612 + * Returns: message header in RFC822 format
   1.613 + */
   1.614 +
   1.615 +char *news_header (MAILSTREAM *stream,unsigned long msgno,
   1.616 +		   unsigned long *length,long flags)
   1.617 +{
   1.618 +  MESSAGECACHE *elt;
   1.619 +  *length = 0;			/* default to empty */
   1.620 +  if (flags & FT_UID) return "";/* UID call "impossible" */
   1.621 +  elt = mail_elt (stream,msgno);/* get elt */
   1.622 +  if (!elt->private.msg.header.text.data)
   1.623 +    news_load_message (stream,msgno,NLM_HEADER);
   1.624 +  *length = elt->private.msg.header.text.size;
   1.625 +  return (char *) elt->private.msg.header.text.data;
   1.626 +}
   1.627 +
   1.628 +
   1.629 +/* News fetch message text (body only)
   1.630 + * Accepts: MAIL stream
   1.631 + *	    message # to fetch
   1.632 + *	    pointer to returned stringstruct
   1.633 + *	    option flags
   1.634 + * Returns: T on success, NIL on failure
   1.635 + */
   1.636 +
   1.637 +long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
   1.638 +{
   1.639 +  MESSAGECACHE *elt;
   1.640 +				/* UID call "impossible" */
   1.641 +  if (flags & FT_UID) return NIL;
   1.642 +  elt = mail_elt (stream,msgno);/* get elt */
   1.643 +				/* snarf message if don't have it yet */
   1.644 +  if (!elt->private.msg.text.text.data) {
   1.645 +    news_load_message (stream,msgno,NLM_TEXT);
   1.646 +    if (!elt->private.msg.text.text.data) return NIL;
   1.647 +  }
   1.648 +  if (!(flags & FT_PEEK)) {	/* mark as seen */
   1.649 +    mail_elt (stream,msgno)->seen = T;
   1.650 +    mm_flags (stream,msgno);
   1.651 +  }
   1.652 +  INIT (bs,mail_string,elt->private.msg.text.text.data,
   1.653 +	elt->private.msg.text.text.size);
   1.654 +  return T;
   1.655 +}
   1.656 +
   1.657 +/* News per-message modify flag
   1.658 + * Accepts: MAIL stream
   1.659 + *	    message cache element
   1.660 + */
   1.661 +
   1.662 +void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
   1.663 +{
   1.664 +  if (!LOCAL->dirty) {		/* only bother checking if not dirty yet */
   1.665 +    if (elt->valid) {		/* if done, see if deleted changed */
   1.666 +      if (elt->sequence != elt->deleted) LOCAL->dirty = T;
   1.667 +      elt->sequence = T;	/* leave the sequence set */
   1.668 +    }
   1.669 +				/* note current setting of deleted flag */
   1.670 +    else elt->sequence = elt->deleted;
   1.671 +  }
   1.672 +}
   1.673 +
   1.674 +
   1.675 +/* News ping mailbox
   1.676 + * Accepts: MAIL stream
   1.677 + * Returns: T if stream alive, else NIL
   1.678 + */
   1.679 +
   1.680 +long news_ping (MAILSTREAM *stream)
   1.681 +{
   1.682 +  return T;			/* always alive */
   1.683 +}
   1.684 +
   1.685 +
   1.686 +/* News check mailbox
   1.687 + * Accepts: MAIL stream
   1.688 + */
   1.689 +
   1.690 +void news_check (MAILSTREAM *stream)
   1.691 +{
   1.692 +				/* never do if no updates */
   1.693 +  if (LOCAL->dirty) newsrc_write (LOCAL->name,stream);
   1.694 +  LOCAL->dirty = NIL;
   1.695 +}
   1.696 +
   1.697 +
   1.698 +/* News expunge mailbox
   1.699 + * Accepts: MAIL stream
   1.700 + *	    sequence to expunge if non-NIL
   1.701 + *	    expunge options
   1.702 + * Returns: T if success, NIL if failure
   1.703 + */
   1.704 +
   1.705 +long news_expunge (MAILSTREAM *stream,char *sequence,long options)
   1.706 +{
   1.707 +  if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
   1.708 +  return LONGT;
   1.709 +}
   1.710 +
   1.711 +/* News copy message(s)
   1.712 + * Accepts: MAIL stream
   1.713 + *	    sequence
   1.714 + *	    destination mailbox
   1.715 + *	    option flags
   1.716 + * Returns: T if copy successful, else NIL
   1.717 + */
   1.718 +
   1.719 +long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
   1.720 +{
   1.721 +  mailproxycopy_t pc =
   1.722 +    (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
   1.723 +  if (pc) return (*pc) (stream,sequence,mailbox,options);
   1.724 +  mm_log ("Copy not valid for News",ERROR);
   1.725 +  return NIL;
   1.726 +}
   1.727 +
   1.728 +
   1.729 +/* News append message from stringstruct
   1.730 + * Accepts: MAIL stream
   1.731 + *	    destination mailbox
   1.732 + *	    append callback function
   1.733 + *	    data for callback
   1.734 + * Returns: T if append successful, else NIL
   1.735 + */
   1.736 +
   1.737 +long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
   1.738 +{
   1.739 +  mm_log ("Append not valid for news",ERROR);
   1.740 +  return NIL;
   1.741 +}

UW-IMAP'd extensions by yuuji