imapext-2007
diff src/osdep/amiga/mh.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/amiga/mh.c Mon Sep 14 15:17:45 2009 +0900 1.3 @@ -0,0 +1,1283 @@ 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: MH mail routines 1.19 + * 1.20 + * Author(s): 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: 23 February 1992 1.29 + * Last Edited: 11 October 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 <pwd.h> 1.40 +#include <sys/stat.h> 1.41 +#include <sys/time.h> 1.42 +#include "misc.h" 1.43 +#include "dummy.h" 1.44 +#include "fdstring.h" 1.45 + 1.46 + 1.47 +/* Build parameters */ 1.48 + 1.49 +#define MHINBOX "#mhinbox" /* corresponds to namespace in env_unix.c */ 1.50 +#define MHINBOXDIR "inbox" 1.51 +#define MHPROFILE ".mh_profile" 1.52 +#define MHCOMMA ',' 1.53 +#define MHSEQUENCE ".mh_sequence" 1.54 +#define MHSEQUENCES ".mh_sequences" 1.55 +#define MHPATH "Mail" 1.56 + 1.57 + 1.58 +/* mh_load_message() flags */ 1.59 + 1.60 +#define MLM_HEADER 0x1 /* load message text */ 1.61 +#define MLM_TEXT 0x2 /* load message text */ 1.62 + 1.63 +/* MH I/O stream local data */ 1.64 + 1.65 +typedef struct mh_local { 1.66 + char *dir; /* spool directory name */ 1.67 + unsigned char buf[CHUNKSIZE]; /* temporary buffer */ 1.68 + unsigned long cachedtexts; /* total size of all cached texts */ 1.69 + time_t scantime; /* last time directory scanned */ 1.70 +} MHLOCAL; 1.71 + 1.72 + 1.73 +/* Convenient access to local data */ 1.74 + 1.75 +#define LOCAL ((MHLOCAL *) stream->local) 1.76 + 1.77 + 1.78 +/* Function prototypes */ 1.79 + 1.80 +DRIVER *mh_valid (char *name); 1.81 +int mh_isvalid (char *name,char *tmp,long synonly); 1.82 +int mh_namevalid (char *name); 1.83 +char *mh_path (char *tmp); 1.84 +void *mh_parameters (long function,void *value); 1.85 +long mh_dirfmttest (char *name); 1.86 +void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents); 1.87 +void mh_list (MAILSTREAM *stream,char *ref,char *pat); 1.88 +void mh_lsub (MAILSTREAM *stream,char *ref,char *pat); 1.89 +void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level); 1.90 +long mh_subscribe (MAILSTREAM *stream,char *mailbox); 1.91 +long mh_unsubscribe (MAILSTREAM *stream,char *mailbox); 1.92 +long mh_create (MAILSTREAM *stream,char *mailbox); 1.93 +long mh_delete (MAILSTREAM *stream,char *mailbox); 1.94 +long mh_rename (MAILSTREAM *stream,char *old,char *newname); 1.95 +MAILSTREAM *mh_open (MAILSTREAM *stream); 1.96 +void mh_close (MAILSTREAM *stream,long options); 1.97 +void mh_fast (MAILSTREAM *stream,char *sequence,long flags); 1.98 +void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags); 1.99 +char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, 1.100 + long flags); 1.101 +long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags); 1.102 +long mh_ping (MAILSTREAM *stream); 1.103 +void mh_check (MAILSTREAM *stream); 1.104 +long mh_expunge (MAILSTREAM *stream,char *sequence,long options); 1.105 +long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox, 1.106 + long options); 1.107 +long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data); 1.108 + 1.109 +int mh_select (struct direct *name); 1.110 +int mh_numsort (const void *d1,const void *d2); 1.111 +char *mh_file (char *dst,char *name); 1.112 +long mh_canonicalize (char *pattern,char *ref,char *pat); 1.113 +void mh_setdate (char *file,MESSAGECACHE *elt); 1.114 + 1.115 +/* MH mail routines */ 1.116 + 1.117 + 1.118 +/* Driver dispatch used by MAIL */ 1.119 + 1.120 +DRIVER mhdriver = { 1.121 + "mh", /* driver name */ 1.122 + /* driver flags */ 1.123 + DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE|DR_NOSTICKY|DR_DIRFMT, 1.124 + (DRIVER *) NIL, /* next driver */ 1.125 + mh_valid, /* mailbox is valid for us */ 1.126 + mh_parameters, /* manipulate parameters */ 1.127 + mh_scan, /* scan mailboxes */ 1.128 + mh_list, /* find mailboxes */ 1.129 + mh_lsub, /* find subscribed mailboxes */ 1.130 + mh_subscribe, /* subscribe to mailbox */ 1.131 + mh_unsubscribe, /* unsubscribe from mailbox */ 1.132 + mh_create, /* create mailbox */ 1.133 + mh_delete, /* delete mailbox */ 1.134 + mh_rename, /* rename mailbox */ 1.135 + mail_status_default, /* status of mailbox */ 1.136 + mh_open, /* open mailbox */ 1.137 + mh_close, /* close mailbox */ 1.138 + mh_fast, /* fetch message "fast" attributes */ 1.139 + NIL, /* fetch message flags */ 1.140 + NIL, /* fetch overview */ 1.141 + NIL, /* fetch message envelopes */ 1.142 + mh_header, /* fetch message header */ 1.143 + mh_text, /* fetch message body */ 1.144 + NIL, /* fetch partial message text */ 1.145 + NIL, /* unique identifier */ 1.146 + NIL, /* message number */ 1.147 + NIL, /* modify flags */ 1.148 + NIL, /* per-message modify flags */ 1.149 + NIL, /* search for message based on criteria */ 1.150 + NIL, /* sort messages */ 1.151 + NIL, /* thread messages */ 1.152 + mh_ping, /* ping mailbox to see if still alive */ 1.153 + mh_check, /* check for new messages */ 1.154 + mh_expunge, /* expunge deleted messages */ 1.155 + mh_copy, /* copy messages to another mailbox */ 1.156 + mh_append, /* append string message to mailbox */ 1.157 + NIL /* garbage collect stream */ 1.158 +}; 1.159 + 1.160 + /* prototype stream */ 1.161 +MAILSTREAM mhproto = {&mhdriver}; 1.162 + 1.163 + 1.164 +static char *mh_profile = NIL; /* holds MH profile */ 1.165 +static char *mh_pathname = NIL; /* holds MH path name */ 1.166 +static long mh_once = 0; /* already snarled once */ 1.167 +static long mh_allow_inbox =NIL;/* allow INBOX as well as MHINBOX */ 1.168 + 1.169 +/* MH mail validate mailbox 1.170 + * Accepts: mailbox name 1.171 + * Returns: our driver if name is valid, NIL otherwise 1.172 + */ 1.173 + 1.174 +DRIVER *mh_valid (char *name) 1.175 +{ 1.176 + char tmp[MAILTMPLEN]; 1.177 + return mh_isvalid (name,tmp,T) ? &mhdriver : NIL; 1.178 +} 1.179 + 1.180 + 1.181 +/* MH mail test for valid mailbox 1.182 + * Accepts: mailbox name 1.183 + * temporary buffer to use 1.184 + * syntax only test flag 1.185 + * Returns: T if valid, NIL otherwise 1.186 + */ 1.187 + 1.188 +int mh_isvalid (char *name,char *tmp,long synonly) 1.189 +{ 1.190 + struct stat sbuf; 1.191 + char *s,*t,altname[MAILTMPLEN]; 1.192 + unsigned long i; 1.193 + int ret = NIL; 1.194 + errno = NIL; /* zap any error condition */ 1.195 + /* mh name? */ 1.196 + if ((mh_allow_inbox && !compare_cstring (name,"INBOX")) || 1.197 + !compare_cstring (name,MHINBOX) || 1.198 + ((name[0] == '#') && ((name[1] == 'm') || (name[1] == 'M')) && 1.199 + ((name[2] == 'h') || (name[2] == 'H')) && (name[3] == '/') && name[4])){ 1.200 + if (mh_path (tmp)) /* validate name if INBOX or not synonly */ 1.201 + ret = (synonly && compare_cstring (name,"INBOX")) ? 1.202 + T : ((stat (mh_file (tmp,name),&sbuf) == 0) && 1.203 + (sbuf.st_mode & S_IFMT) == S_IFDIR); 1.204 + else if (!mh_once++) { /* only report error once */ 1.205 + sprintf (tmp,"%.900s not found, mh format names disabled",mh_profile); 1.206 + mm_log (tmp,WARN); 1.207 + } 1.208 + } 1.209 + /* see if non-NS name within mh hierarchy */ 1.210 + else if ((name[0] != '#') && (s = mh_path (tmp)) && (i = strlen (s)) && 1.211 + (t = mailboxfile (tmp,name)) && !strncmp (t,s,i) && 1.212 + (tmp[i] == '/') && tmp[i+1]) { 1.213 + sprintf (altname,"#mh%.900s",tmp+i); 1.214 + /* can't do synonly here! */ 1.215 + ret = mh_isvalid (altname,tmp,NIL); 1.216 + } 1.217 + else errno = EINVAL; /* bogus name */ 1.218 + return ret; 1.219 +} 1.220 + 1.221 +/* MH mail test for valid mailbox 1.222 + * Accepts: mailbox name 1.223 + * Returns: T if valid, NIL otherwise 1.224 + */ 1.225 + 1.226 +int mh_namevalid (char *name) 1.227 +{ 1.228 + char *s; 1.229 + if (name[0] == '#' && (name[1] == 'm' || name[1] == 'M') && 1.230 + (name[2] == 'h' || name[2] == 'H') && name[3] == '/') 1.231 + for (s = name; s && *s;) { /* make sure no all-digit nodes */ 1.232 + if (isdigit (*s)) s++; /* digit, check this node further... */ 1.233 + else if (*s == '/') break;/* all digit node, barf */ 1.234 + /* non-digit, skip to next node or return */ 1.235 + else if (!((s = strchr (s+1,'/')) && *++s)) return T; 1.236 + } 1.237 + return NIL; /* all numeric or empty node */ 1.238 +} 1.239 + 1.240 +/* Return MH path 1.241 + * Accepts: temporary buffer 1.242 + * Returns: MH path or NIL if MH disabled 1.243 + */ 1.244 + 1.245 +char *mh_path (char *tmp) 1.246 +{ 1.247 + char *s,*t,*v,*r; 1.248 + int fd; 1.249 + struct stat sbuf; 1.250 + if (!mh_profile) { /* build mh_profile and mh_pathname now */ 1.251 + sprintf (tmp,"%s/%s",myhomedir (),MHPROFILE); 1.252 + if ((fd = open (mh_profile = cpystr (tmp),O_RDONLY,NIL)) >= 0) { 1.253 + fstat (fd,&sbuf); /* yes, get size and read file */ 1.254 + read (fd,(t = (char *) fs_get (sbuf.st_size + 1)),sbuf.st_size); 1.255 + close (fd); /* don't need the file any more */ 1.256 + t[sbuf.st_size] = '\0'; /* tie it off */ 1.257 + /* parse profile file */ 1.258 + for (s = strtok_r (t,"\r\n",&r); s && *s; s = strtok_r (NIL,"\r\n",&r)) { 1.259 + /* found space in line? */ 1.260 + if (v = strpbrk (s," \t")) { 1.261 + *v++ = '\0'; /* tie off, is keyword "Path:"? */ 1.262 + if (!compare_cstring (s,"Path:")) { 1.263 + /* skip whitespace */ 1.264 + while ((*v == ' ') || (*v == '\t')) ++v; 1.265 + /* absolute path? */ 1.266 + if (*v == '/') s = v; 1.267 + else sprintf (s = tmp,"%s/%s",myhomedir (),v); 1.268 + /* copy name */ 1.269 + mh_pathname = cpystr (s); 1.270 + break; /* don't need to look at rest of file */ 1.271 + } 1.272 + } 1.273 + } 1.274 + fs_give ((void **) &t); /* flush profile text */ 1.275 + if (!mh_pathname) { /* default path if not in the profile */ 1.276 + sprintf (tmp,"%s/%s",myhomedir (),MHPATH); 1.277 + mh_pathname = cpystr (tmp); 1.278 + } 1.279 + } 1.280 + } 1.281 + return mh_pathname; 1.282 +} 1.283 + 1.284 +/* MH manipulate driver parameters 1.285 + * Accepts: function code 1.286 + * function-dependent value 1.287 + * Returns: function-dependent return value 1.288 + */ 1.289 + 1.290 +void *mh_parameters (long function,void *value) 1.291 +{ 1.292 + void *ret = NIL; 1.293 + switch ((int) function) { 1.294 + case GET_INBOXPATH: 1.295 + if (value) ret = mh_file ((char *) value,"INBOX"); 1.296 + break; 1.297 + case GET_DIRFMTTEST: 1.298 + ret = (void *) mh_dirfmttest; 1.299 + break; 1.300 + case SET_MHPROFILE: 1.301 + if (mh_profile) fs_give ((void **) &mh_profile); 1.302 + mh_profile = cpystr ((char *) value); 1.303 + case GET_MHPROFILE: 1.304 + ret = (void *) mh_profile; 1.305 + break; 1.306 + case SET_MHPATH: 1.307 + if (mh_pathname) fs_give ((void **) &mh_pathname); 1.308 + mh_pathname = cpystr ((char *) value); 1.309 + case GET_MHPATH: 1.310 + ret = (void *) mh_pathname; 1.311 + break; 1.312 + case SET_MHALLOWINBOX: 1.313 + mh_allow_inbox = value ? T : NIL; 1.314 + case GET_MHALLOWINBOX: 1.315 + ret = (void *) (mh_allow_inbox ? VOIDT : NIL); 1.316 + } 1.317 + return ret; 1.318 +} 1.319 + 1.320 + 1.321 +/* MH test for directory format internal node 1.322 + * Accepts: candidate node name 1.323 + * Returns: T if internal name, NIL otherwise 1.324 + */ 1.325 + 1.326 +long mh_dirfmttest (char *s) 1.327 +{ 1.328 + int c; 1.329 + /* sequence(s) file is an internal name */ 1.330 + if (strcmp (s,MHSEQUENCE) && strcmp (s,MHSEQUENCES)) { 1.331 + if (*s == MHCOMMA) ++s; /* else comma + all numeric name */ 1.332 + /* success if all-numeric */ 1.333 + while (c = *s++) if (!isdigit (c)) return NIL; 1.334 + } 1.335 + return LONGT; 1.336 +} 1.337 + 1.338 +/* MH scan mailboxes 1.339 + * Accepts: mail stream 1.340 + * reference 1.341 + * pattern to search 1.342 + * string to scan 1.343 + */ 1.344 + 1.345 +void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents) 1.346 +{ 1.347 + char *s,test[MAILTMPLEN],file[MAILTMPLEN]; 1.348 + long i = 0; 1.349 + if (!pat || !*pat) { /* empty pattern? */ 1.350 + if (mh_canonicalize (test,ref,"*")) { 1.351 + /* tie off name at root */ 1.352 + if (s = strchr (test,'/')) *++s = '\0'; 1.353 + else test[0] = '\0'; 1.354 + mm_list (stream,'/',test,LATT_NOSELECT); 1.355 + } 1.356 + } 1.357 + /* get canonical form of name */ 1.358 + else if (mh_canonicalize (test,ref,pat)) { 1.359 + if (contents) { /* maybe I'll implement this someday */ 1.360 + mm_log ("Scan not valid for mh mailboxes",ERROR); 1.361 + return; 1.362 + } 1.363 + if (test[3] == '/') { /* looking down levels? */ 1.364 + /* yes, found any wildcards? */ 1.365 + if (s = strpbrk (test,"%*")) { 1.366 + /* yes, copy name up to that point */ 1.367 + strncpy (file,test+4,i = s - (test+4)); 1.368 + file[i] = '\0'; /* tie off */ 1.369 + } 1.370 + else strcpy (file,test+4);/* use just that name then */ 1.371 + /* find directory name */ 1.372 + if (s = strrchr (file,'/')) { 1.373 + *s = '\0'; /* found, tie off at that point */ 1.374 + s = file; 1.375 + } 1.376 + /* do the work */ 1.377 + mh_list_work (stream,s,test,0); 1.378 + } 1.379 + /* always an INBOX */ 1.380 + if (!compare_cstring (test,MHINBOX)) 1.381 + mm_list (stream,NIL,MHINBOX,LATT_NOINFERIORS); 1.382 + } 1.383 +} 1.384 + 1.385 +/* MH list mailboxes 1.386 + * Accepts: mail stream 1.387 + * reference 1.388 + * pattern to search 1.389 + */ 1.390 + 1.391 +void mh_list (MAILSTREAM *stream,char *ref,char *pat) 1.392 +{ 1.393 + mh_scan (stream,ref,pat,NIL); 1.394 +} 1.395 + 1.396 + 1.397 +/* MH list subscribed mailboxes 1.398 + * Accepts: mail stream 1.399 + * reference 1.400 + * pattern to search 1.401 + */ 1.402 + 1.403 +void mh_lsub (MAILSTREAM *stream,char *ref,char *pat) 1.404 +{ 1.405 + void *sdb = NIL; 1.406 + char *s,test[MAILTMPLEN]; 1.407 + /* get canonical form of name */ 1.408 + if (mh_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) { 1.409 + do if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL); 1.410 + while (s = sm_read (&sdb)); /* until no more subscriptions */ 1.411 + } 1.412 +} 1.413 + 1.414 +/* MH list mailboxes worker routine 1.415 + * Accepts: mail stream 1.416 + * directory name to search 1.417 + * search pattern 1.418 + * search level 1.419 + */ 1.420 + 1.421 +void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level) 1.422 +{ 1.423 + DIR *dp; 1.424 + struct direct *d; 1.425 + struct stat sbuf; 1.426 + char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN]; 1.427 + /* build MH name to search */ 1.428 + if (dir) sprintf (name,"#mh/%s/",dir); 1.429 + else strcpy (name,"#mh/"); 1.430 + /* make directory name, punt if bogus */ 1.431 + if (!mh_file (curdir,name)) return; 1.432 + cp = curdir + strlen (curdir);/* end of directory name */ 1.433 + np = name + strlen (name); /* end of MH name */ 1.434 + if (dp = opendir (curdir)) { /* open directory */ 1.435 + while (d = readdir (dp)) /* scan, ignore . and numeric names */ 1.436 + if ((d->d_name[0] != '.') && !mh_select (d)) { 1.437 + strcpy (cp,d->d_name); /* make directory name */ 1.438 + if (!stat (curdir,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) { 1.439 + strcpy (np,d->d_name);/* make mh name of directory name */ 1.440 + /* yes, an MH name if full match */ 1.441 + if (pmatch_full (name,pat,'/')) mm_list (stream,'/',name,NIL); 1.442 + /* check if should recurse */ 1.443 + if (dmatch (name,pat,'/') && 1.444 + (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))) 1.445 + mh_list_work (stream,name+4,pat,level+1); 1.446 + } 1.447 + } 1.448 + closedir (dp); /* all done, flush directory */ 1.449 + } 1.450 +} 1.451 + 1.452 +/* MH mail subscribe to mailbox 1.453 + * Accepts: mail stream 1.454 + * mailbox to add to subscription list 1.455 + * Returns: T on success, NIL on failure 1.456 + */ 1.457 + 1.458 +long mh_subscribe (MAILSTREAM *stream,char *mailbox) 1.459 +{ 1.460 + return sm_subscribe (mailbox); 1.461 +} 1.462 + 1.463 + 1.464 +/* MH mail unsubscribe to mailbox 1.465 + * Accepts: mail stream 1.466 + * mailbox to delete from subscription list 1.467 + * Returns: T on success, NIL on failure 1.468 + */ 1.469 + 1.470 +long mh_unsubscribe (MAILSTREAM *stream,char *mailbox) 1.471 +{ 1.472 + return sm_unsubscribe (mailbox); 1.473 +} 1.474 + 1.475 +/* MH mail create mailbox 1.476 + * Accepts: mail stream 1.477 + * mailbox name to create 1.478 + * Returns: T on success, NIL on failure 1.479 + */ 1.480 + 1.481 +long mh_create (MAILSTREAM *stream,char *mailbox) 1.482 +{ 1.483 + char tmp[MAILTMPLEN]; 1.484 + if (!mh_namevalid (mailbox)) /* validate name */ 1.485 + sprintf (tmp,"Can't create mailbox %.80s: invalid MH-format name",mailbox); 1.486 + /* must not already exist */ 1.487 + else if (mh_isvalid (mailbox,tmp,NIL)) 1.488 + sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox); 1.489 + else if (!mh_path (tmp)) return NIL; 1.490 + /* try to make it */ 1.491 + else if (!(mh_file (tmp,mailbox) && 1.492 + dummy_create_path (stream,strcat (tmp,"/"), 1.493 + get_dir_protection (mailbox)))) 1.494 + sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno)); 1.495 + else return LONGT; /* success */ 1.496 + mm_log (tmp,ERROR); 1.497 + return NIL; 1.498 +} 1.499 + 1.500 +/* MH mail delete mailbox 1.501 + * mailbox name to delete 1.502 + * Returns: T on success, NIL on failure 1.503 + */ 1.504 + 1.505 +long mh_delete (MAILSTREAM *stream,char *mailbox) 1.506 +{ 1.507 + DIR *dirp; 1.508 + struct direct *d; 1.509 + int i; 1.510 + char tmp[MAILTMPLEN]; 1.511 + /* is mailbox valid? */ 1.512 + if (!mh_isvalid (mailbox,tmp,NIL)) { 1.513 + sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox); 1.514 + mm_log (tmp,ERROR); 1.515 + return NIL; 1.516 + } 1.517 + /* get name of directory */ 1.518 + i = strlen (mh_file (tmp,mailbox)); 1.519 + if (dirp = opendir (tmp)) { /* open directory */ 1.520 + tmp[i++] = '/'; /* now apply trailing delimiter */ 1.521 + /* massacre all mh owned files */ 1.522 + while (d = readdir (dirp)) if (mh_dirfmttest (d->d_name)) { 1.523 + strcpy (tmp + i,d->d_name); 1.524 + unlink (tmp); /* sayonara */ 1.525 + } 1.526 + closedir (dirp); /* flush directory */ 1.527 + } 1.528 + /* try to remove the directory */ 1.529 + if (rmdir (mh_file (tmp,mailbox))) { 1.530 + sprintf (tmp,"Can't delete mailbox %.80s: %s",mailbox,strerror (errno)); 1.531 + mm_log (tmp,WARN); 1.532 + } 1.533 + return T; /* return success */ 1.534 +} 1.535 + 1.536 +/* MH mail rename mailbox 1.537 + * Accepts: MH mail stream 1.538 + * old mailbox name 1.539 + * new mailbox name 1.540 + * Returns: T on success, NIL on failure 1.541 + */ 1.542 + 1.543 +long mh_rename (MAILSTREAM *stream,char *old,char *newname) 1.544 +{ 1.545 + char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN]; 1.546 + struct stat sbuf; 1.547 + /* old mailbox name must be valid */ 1.548 + if (!mh_isvalid (old,tmp,NIL)) 1.549 + sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old); 1.550 + else if (!mh_namevalid (newname)) 1.551 + sprintf (tmp,"Can't rename to mailbox %.80s: invalid MH-format name", 1.552 + newname); 1.553 + /* new mailbox name must not be valid */ 1.554 + else if (mh_isvalid (newname,tmp,NIL)) 1.555 + sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists", 1.556 + newname); 1.557 + /* success if can rename the directory */ 1.558 + else { /* found superior to destination name? */ 1.559 + if (s = strrchr (mh_file (tmp1,newname),'/')) { 1.560 + c = *++s; /* remember first character of inferior */ 1.561 + *s = '\0'; /* tie off to get just superior */ 1.562 + /* name doesn't exist, create it */ 1.563 + if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) && 1.564 + !dummy_create_path (stream,tmp1,get_dir_protection (newname))) 1.565 + return NIL; 1.566 + *s = c; /* restore full name */ 1.567 + } 1.568 + if (!rename (mh_file (tmp,old),tmp1)) return T; 1.569 + sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s", 1.570 + old,newname,strerror (errno)); 1.571 + } 1.572 + mm_log (tmp,ERROR); /* something failed */ 1.573 + return NIL; 1.574 +} 1.575 + 1.576 +/* MH mail open 1.577 + * Accepts: stream to open 1.578 + * Returns: stream on success, NIL on failure 1.579 + */ 1.580 + 1.581 +MAILSTREAM *mh_open (MAILSTREAM *stream) 1.582 +{ 1.583 + char tmp[MAILTMPLEN]; 1.584 + if (!stream) return &mhproto; /* return prototype for OP_PROTOTYPE call */ 1.585 + if (stream->local) fatal ("mh recycle stream"); 1.586 + stream->local = fs_get (sizeof (MHLOCAL)); 1.587 + /* INBOXness is one of the following: 1.588 + * #mhinbox (case-independent) 1.589 + * #mh/inbox (mh is case-independent, inbox is case-dependent) 1.590 + * INBOX (case-independent 1.591 + */ 1.592 + stream->inbox = /* note if an INBOX or not */ 1.593 + (!compare_cstring (stream->mailbox,MHINBOX) || 1.594 + ((stream->mailbox[0] == '#') && 1.595 + ((stream->mailbox[1] == 'm') || (stream->mailbox[1] == 'M')) && 1.596 + ((stream->mailbox[2] == 'h') || (stream->mailbox[2] == 'H')) && 1.597 + (stream->mailbox[3] == '/') && !strcmp (stream->mailbox+4,MHINBOXDIR)) || 1.598 + !compare_cstring (stream->mailbox,"INBOX")) ? T : NIL; 1.599 + mh_file (tmp,stream->mailbox);/* get directory name */ 1.600 + LOCAL->dir = cpystr (tmp); /* copy directory name for later */ 1.601 + LOCAL->scantime = 0; /* not scanned yet */ 1.602 + LOCAL->cachedtexts = 0; /* no cached texts */ 1.603 + stream->sequence++; /* bump sequence number */ 1.604 + /* parse mailbox */ 1.605 + stream->nmsgs = stream->recent = 0; 1.606 + if (!mh_ping (stream)) return NIL; 1.607 + if (!(stream->nmsgs || stream->silent)) 1.608 + mm_log ("Mailbox is empty",(long) NIL); 1.609 + return stream; /* return stream to caller */ 1.610 +} 1.611 + 1.612 +/* MH mail close 1.613 + * Accepts: MAIL stream 1.614 + * close options 1.615 + */ 1.616 + 1.617 +void mh_close (MAILSTREAM *stream,long options) 1.618 +{ 1.619 + if (LOCAL) { /* only if a file is open */ 1.620 + int silent = stream->silent; 1.621 + stream->silent = T; /* note this stream is dying */ 1.622 + if (options & CL_EXPUNGE) mh_expunge (stream,NIL,NIL); 1.623 + if (LOCAL->dir) fs_give ((void **) &LOCAL->dir); 1.624 + /* nuke the local data */ 1.625 + fs_give ((void **) &stream->local); 1.626 + stream->dtb = NIL; /* log out the DTB */ 1.627 + stream->silent = silent; /* reset silent state */ 1.628 + } 1.629 +} 1.630 + 1.631 + 1.632 +/* MH mail fetch fast information 1.633 + * Accepts: MAIL stream 1.634 + * sequence 1.635 + * option flags 1.636 + */ 1.637 + 1.638 +void mh_fast (MAILSTREAM *stream,char *sequence,long flags) 1.639 +{ 1.640 + MESSAGECACHE *elt; 1.641 + unsigned long i; 1.642 + /* set up metadata for all messages */ 1.643 + if (stream && LOCAL && ((flags & FT_UID) ? 1.644 + mail_uid_sequence (stream,sequence) : 1.645 + mail_sequence (stream,sequence))) 1.646 + for (i = 1; i <= stream->nmsgs; i++) 1.647 + if ((elt = mail_elt (stream,i))->sequence && 1.648 + !(elt->day && elt->rfc822_size)) mh_load_message (stream,i,NIL); 1.649 +} 1.650 + 1.651 +/* MH load message into cache 1.652 + * Accepts: MAIL stream 1.653 + * message # 1.654 + * option flags 1.655 + */ 1.656 + 1.657 +void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags) 1.658 +{ 1.659 + unsigned long i,j,nlseen; 1.660 + int fd; 1.661 + unsigned char c,*t; 1.662 + struct stat sbuf; 1.663 + MESSAGECACHE *elt; 1.664 + FDDATA d; 1.665 + STRING bs; 1.666 + elt = mail_elt (stream,msgno);/* get elt */ 1.667 + /* build message file name */ 1.668 + sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); 1.669 + /* anything we need not currently cached? */ 1.670 + if ((!elt->day || !elt->rfc822_size || 1.671 + ((flags & MLM_HEADER) && !elt->private.msg.header.text.data) || 1.672 + ((flags & MLM_TEXT) && !elt->private.msg.text.text.data)) && 1.673 + ((fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0)) { 1.674 + fstat (fd,&sbuf); /* get file metadata */ 1.675 + d.fd = fd; /* set up file descriptor */ 1.676 + d.pos = 0; /* start of file */ 1.677 + d.chunk = LOCAL->buf; 1.678 + d.chunksize = CHUNKSIZE; 1.679 + INIT (&bs,fd_string,&d,sbuf.st_size); 1.680 + if (!elt->day) { /* set internaldate to file date */ 1.681 + struct tm *tm = gmtime (&sbuf.st_mtime); 1.682 + elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; 1.683 + elt->year = tm->tm_year + 1900 - BASEYEAR; 1.684 + elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; 1.685 + elt->seconds = tm->tm_sec; 1.686 + elt->zhours = 0; elt->zminutes = 0; 1.687 + } 1.688 + 1.689 + if (!elt->rfc822_size) { /* know message size yet? */ 1.690 + for (i = 0, j = SIZE (&bs), nlseen = 0; j--; ) switch (SNX (&bs)) { 1.691 + case '\015': /* unlikely carriage return */ 1.692 + if (!j || (CHR (&bs) != '\012')) { 1.693 + i++; /* ugh, raw CR */ 1.694 + nlseen = NIL; 1.695 + break; 1.696 + } 1.697 + SNX (&bs); /* eat the line feed, drop in */ 1.698 + --j; 1.699 + case '\012': /* line feed? */ 1.700 + i += 2; /* count a CRLF */ 1.701 + /* header size known yet? */ 1.702 + if (!elt->private.msg.header.text.size && nlseen) { 1.703 + /* note position in file */ 1.704 + elt->private.special.text.size = GETPOS (&bs); 1.705 + /* and CRLF-adjusted size */ 1.706 + elt->private.msg.header.text.size = i; 1.707 + } 1.708 + nlseen = T; /* note newline seen */ 1.709 + break; 1.710 + default: /* ordinary chararacter */ 1.711 + i++; 1.712 + nlseen = NIL; 1.713 + break; 1.714 + } 1.715 + SETPOS (&bs,0); /* restore old position */ 1.716 + elt->rfc822_size = i; /* note that we have size now */ 1.717 + /* header is entire message if no delimiter */ 1.718 + if (!elt->private.msg.header.text.size) 1.719 + elt->private.msg.header.text.size = elt->rfc822_size; 1.720 + /* text is remainder of message */ 1.721 + elt->private.msg.text.text.size = 1.722 + elt->rfc822_size - elt->private.msg.header.text.size; 1.723 + } 1.724 + /* need to load cache with message data? */ 1.725 + if (((flags & MLM_HEADER) && !elt->private.msg.header.text.data) || 1.726 + ((flags & MLM_TEXT) && !elt->private.msg.text.text.data)) { 1.727 + /* purge cache if too big */ 1.728 + if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) { 1.729 + /* just can't keep that much */ 1.730 + mail_gc (stream,GC_TEXTS); 1.731 + LOCAL->cachedtexts = 0; 1.732 + } 1.733 + 1.734 + if ((flags & MLM_HEADER) && !elt->private.msg.header.text.data) { 1.735 + t = elt->private.msg.header.text.data = 1.736 + (unsigned char *) fs_get (elt->private.msg.header.text.size + 1); 1.737 + LOCAL->cachedtexts += elt->private.msg.header.text.size; 1.738 + /* read in message header */ 1.739 + for (i = 0; i < elt->private.msg.header.text.size; i++) 1.740 + switch (c = SNX (&bs)) { 1.741 + case '\015': /* unlikely carriage return */ 1.742 + *t++ = c; 1.743 + if ((CHR (&bs) == '\012')) { 1.744 + *t++ = SNX (&bs); 1.745 + i++; 1.746 + } 1.747 + break; 1.748 + case '\012': /* line feed? */ 1.749 + *t++ = '\015'; 1.750 + i++; 1.751 + default: 1.752 + *t++ = c; 1.753 + break; 1.754 + } 1.755 + *t = '\0'; /* tie off string */ 1.756 + if ((t - elt->private.msg.header.text.data) != 1.757 + elt->private.msg.header.text.size) fatal ("mh hdr size mismatch"); 1.758 + } 1.759 + if ((flags & MLM_TEXT) && !elt->private.msg.text.text.data) { 1.760 + t = elt->private.msg.text.text.data = 1.761 + (unsigned char *) fs_get (elt->private.msg.text.text.size + 1); 1.762 + SETPOS (&bs,elt->private.special.text.size); 1.763 + LOCAL->cachedtexts += elt->private.msg.text.text.size; 1.764 + /* read in message text */ 1.765 + for (i = 0; i < elt->private.msg.text.text.size; i++) 1.766 + switch (c = SNX (&bs)) { 1.767 + case '\015': /* unlikely carriage return */ 1.768 + *t++ = c; 1.769 + if ((CHR (&bs) == '\012')) { 1.770 + *t++ = SNX (&bs); 1.771 + i++; 1.772 + } 1.773 + break; 1.774 + case '\012': /* line feed? */ 1.775 + *t++ = '\015'; 1.776 + i++; 1.777 + default: 1.778 + *t++ = c; 1.779 + break; 1.780 + } 1.781 + *t = '\0'; /* tie off string */ 1.782 + if ((t - elt->private.msg.text.text.data) != 1.783 + elt->private.msg.text.text.size) fatal ("mh txt size mismatch"); 1.784 + } 1.785 + } 1.786 + close (fd); /* flush message file */ 1.787 + } 1.788 +} 1.789 + 1.790 +/* MH mail fetch message header 1.791 + * Accepts: MAIL stream 1.792 + * message # to fetch 1.793 + * pointer to returned header text length 1.794 + * option flags 1.795 + * Returns: message header in RFC822 format 1.796 + */ 1.797 + 1.798 +char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length, 1.799 + long flags) 1.800 +{ 1.801 + MESSAGECACHE *elt; 1.802 + *length = 0; /* default to empty */ 1.803 + if (flags & FT_UID) return "";/* UID call "impossible" */ 1.804 + elt = mail_elt (stream,msgno);/* get elt */ 1.805 + if (!elt->private.msg.header.text.data) 1.806 + mh_load_message (stream,msgno,MLM_HEADER); 1.807 + *length = elt->private.msg.header.text.size; 1.808 + return (char *) elt->private.msg.header.text.data; 1.809 +} 1.810 + 1.811 + 1.812 +/* MH mail fetch message text (body only) 1.813 + * Accepts: MAIL stream 1.814 + * message # to fetch 1.815 + * pointer to returned stringstruct 1.816 + * option flags 1.817 + * Returns: T on success, NIL on failure 1.818 + */ 1.819 + 1.820 +long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags) 1.821 +{ 1.822 + MESSAGECACHE *elt; 1.823 + /* UID call "impossible" */ 1.824 + if (flags & FT_UID) return NIL; 1.825 + elt = mail_elt (stream,msgno);/* get elt */ 1.826 + /* snarf message if don't have it yet */ 1.827 + if (!elt->private.msg.text.text.data) { 1.828 + mh_load_message (stream,msgno,MLM_TEXT); 1.829 + if (!elt->private.msg.text.text.data) return NIL; 1.830 + } 1.831 + if (!(flags & FT_PEEK)) { /* mark as seen */ 1.832 + mail_elt (stream,msgno)->seen = T; 1.833 + mm_flags (stream,msgno); 1.834 + } 1.835 + INIT (bs,mail_string,elt->private.msg.text.text.data, 1.836 + elt->private.msg.text.text.size); 1.837 + return T; 1.838 +} 1.839 + 1.840 +/* MH mail ping mailbox 1.841 + * Accepts: MAIL stream 1.842 + * Returns: T if stream alive, else NIL 1.843 + */ 1.844 + 1.845 +long mh_ping (MAILSTREAM *stream) 1.846 +{ 1.847 + MAILSTREAM *sysibx = NIL; 1.848 + MESSAGECACHE *elt,*selt; 1.849 + struct stat sbuf; 1.850 + char *s,tmp[MAILTMPLEN]; 1.851 + int fd; 1.852 + unsigned long i,j,r; 1.853 + unsigned long old = stream->uid_last; 1.854 + long nmsgs = stream->nmsgs; 1.855 + long recent = stream->recent; 1.856 + int silent = stream->silent; 1.857 + if (stat (LOCAL->dir,&sbuf)) {/* directory exists? */ 1.858 + if (stream->inbox && /* no, create if INBOX */ 1.859 + dummy_create_path (stream,strcat (mh_file (tmp,MHINBOX),"/"), 1.860 + get_dir_protection ("INBOX"))) return T; 1.861 + sprintf (tmp,"Can't open mailbox %.80s: no such mailbox",stream->mailbox); 1.862 + mm_log (tmp,ERROR); 1.863 + return NIL; 1.864 + } 1.865 + stream->silent = T; /* don't pass up mm_exists() events yet */ 1.866 + if (sbuf.st_ctime != LOCAL->scantime) { 1.867 + struct direct **names = NIL; 1.868 + long nfiles = scandir (LOCAL->dir,&names,mh_select,mh_numsort); 1.869 + if (nfiles < 0) nfiles = 0; /* in case error */ 1.870 + /* note scanned now */ 1.871 + LOCAL->scantime = sbuf.st_ctime; 1.872 + /* scan directory */ 1.873 + for (i = 0; i < nfiles; ++i) { 1.874 + /* if newly seen, add to list */ 1.875 + if ((j = atoi (names[i]->d_name)) > old) { 1.876 + mail_exists (stream,++nmsgs); 1.877 + stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j; 1.878 + elt->valid = T; /* note valid flags */ 1.879 + if (old) { /* other than the first pass? */ 1.880 + elt->recent = T; /* yup, mark as recent */ 1.881 + recent++; /* bump recent count */ 1.882 + } 1.883 + else { /* see if already read */ 1.884 + sprintf (tmp,"%s/%s",LOCAL->dir,names[i]->d_name); 1.885 + if (!stat (tmp,&sbuf) && (sbuf.st_atime > sbuf.st_mtime)) 1.886 + elt->seen = T; 1.887 + } 1.888 + } 1.889 + fs_give ((void **) &names[i]); 1.890 + } 1.891 + /* free directory */ 1.892 + if (s = (void *) names) fs_give ((void **) &s); 1.893 + } 1.894 + 1.895 + /* if INBOX, snarf from system INBOX */ 1.896 + if (stream->inbox && strcmp (sysinbox (),stream->mailbox)) { 1.897 + old = stream->uid_last; 1.898 + mm_critical (stream); /* go critical */ 1.899 + /* see if anything in system inbox */ 1.900 + if (!stat (sysinbox (),&sbuf) && sbuf.st_size && 1.901 + (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) && 1.902 + !sysibx->rdonly && (r = sysibx->nmsgs)) { 1.903 + for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */ 1.904 + /* build file name we will use */ 1.905 + sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old); 1.906 + /* snarf message from Berkeley mailbox */ 1.907 + selt = mail_elt (sysibx,i); 1.908 + if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL, 1.909 + (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) 1.910 + >= 0) && 1.911 + (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_INTERNAL)) && 1.912 + (write (fd,s,j) == j) && 1.913 + (s = mail_fetchtext_full (sysibx,i,&j,FT_INTERNAL|FT_PEEK)) && 1.914 + (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) { 1.915 + /* swell the cache */ 1.916 + mail_exists (stream,++nmsgs); 1.917 + stream->uid_last = /* create new elt, note its file number */ 1.918 + (elt = mail_elt (stream,nmsgs))->private.uid = old; 1.919 + recent++; /* bump recent count */ 1.920 + /* set up initial flags and date */ 1.921 + elt->valid = elt->recent = T; 1.922 + elt->seen = selt->seen; 1.923 + elt->deleted = selt->deleted; 1.924 + elt->flagged = selt->flagged; 1.925 + elt->answered = selt->answered; 1.926 + elt->draft = selt->draft; 1.927 + elt->day = selt->day;elt->month = selt->month;elt->year = selt->year; 1.928 + elt->hours = selt->hours;elt->minutes = selt->minutes; 1.929 + elt->seconds = selt->seconds; 1.930 + elt->zhours = selt->zhours; elt->zminutes = selt->zminutes; 1.931 + elt->zoccident = selt->zoccident; 1.932 + mh_setdate (LOCAL->buf,elt); 1.933 + sprintf (tmp,"%lu",i);/* delete it from the sysinbox */ 1.934 + mail_flag (sysibx,tmp,"\\Deleted",ST_SET); 1.935 + } 1.936 + 1.937 + else { /* failed to snarf */ 1.938 + if (fd) { /* did it ever get opened? */ 1.939 + close (fd); /* close descriptor */ 1.940 + unlink (LOCAL->buf);/* flush this file */ 1.941 + } 1.942 + sprintf (tmp,"Message copy to MH mailbox failed: %.80s", 1.943 + s,strerror (errno)); 1.944 + mm_log (tmp,ERROR); 1.945 + r = 0; /* stop the snarf in its tracks */ 1.946 + } 1.947 + } 1.948 + /* update scan time */ 1.949 + if (!stat (LOCAL->dir,&sbuf)) LOCAL->scantime = sbuf.st_ctime; 1.950 + mail_expunge (sysibx); /* now expunge all those messages */ 1.951 + } 1.952 + if (sysibx) mail_close (sysibx); 1.953 + mm_nocritical (stream); /* release critical */ 1.954 + } 1.955 + stream->silent = silent; /* can pass up events now */ 1.956 + mail_exists (stream,nmsgs); /* notify upper level of mailbox size */ 1.957 + mail_recent (stream,recent); 1.958 + return T; /* return that we are alive */ 1.959 +} 1.960 + 1.961 +/* MH mail check mailbox 1.962 + * Accepts: MAIL stream 1.963 + */ 1.964 + 1.965 +void mh_check (MAILSTREAM *stream) 1.966 +{ 1.967 + /* Perhaps in the future this will preserve flags */ 1.968 + if (mh_ping (stream)) mm_log ("Check completed",(long) NIL); 1.969 +} 1.970 + 1.971 + 1.972 +/* MH mail expunge mailbox 1.973 + * Accepts: MAIL stream 1.974 + * sequence to expunge if non-NIL 1.975 + * expunge options 1.976 + * Returns: T, always 1.977 + */ 1.978 + 1.979 +long mh_expunge (MAILSTREAM *stream,char *sequence,long options) 1.980 +{ 1.981 + long ret; 1.982 + MESSAGECACHE *elt; 1.983 + unsigned long i = 1; 1.984 + unsigned long n = 0; 1.985 + unsigned long recent = stream->recent; 1.986 + if (ret = sequence ? ((options & EX_UID) ? 1.987 + mail_uid_sequence (stream,sequence) : 1.988 + mail_sequence (stream,sequence)) : LONGT) { 1.989 + mm_critical (stream); /* go critical */ 1.990 + while (i <= stream->nmsgs) {/* for each message */ 1.991 + elt = mail_elt (stream,i);/* if deleted, need to trash it */ 1.992 + if (elt->deleted && (sequence ? elt->sequence : T)) { 1.993 + sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); 1.994 + if (unlink (LOCAL->buf)) {/* try to delete the message */ 1.995 + sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i, 1.996 + strerror (errno)); 1.997 + mm_log (LOCAL->buf,(long) NIL); 1.998 + break; 1.999 + } 1.1000 + /* note uncached */ 1.1001 + LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ? 1.1002 + elt->private.msg.header.text.size : 0) + 1.1003 + (elt->private.msg.text.text.data ? 1.1004 + elt->private.msg.text.text.size : 0)); 1.1005 + mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS); 1.1006 + /* if recent, note one less recent message */ 1.1007 + if (elt->recent) --recent; 1.1008 + /* notify upper levels */ 1.1009 + mail_expunged (stream,i); 1.1010 + n++; /* count up one more expunged message */ 1.1011 + } 1.1012 + else i++; /* otherwise try next message */ 1.1013 + } 1.1014 + if (n) { /* output the news if any expunged */ 1.1015 + sprintf (LOCAL->buf,"Expunged %lu messages",n); 1.1016 + mm_log (LOCAL->buf,(long) NIL); 1.1017 + } 1.1018 + else mm_log ("No messages deleted, so no update needed",(long) NIL); 1.1019 + mm_nocritical (stream); /* release critical */ 1.1020 + /* notify upper level of new mailbox size */ 1.1021 + mail_exists (stream,stream->nmsgs); 1.1022 + mail_recent (stream,recent); 1.1023 + } 1.1024 + return ret; 1.1025 +} 1.1026 + 1.1027 +/* MH mail copy message(s) 1.1028 + * Accepts: MAIL stream 1.1029 + * sequence 1.1030 + * destination mailbox 1.1031 + * copy options 1.1032 + * Returns: T if copy successful, else NIL 1.1033 + */ 1.1034 + 1.1035 +long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options) 1.1036 +{ 1.1037 + FDDATA d; 1.1038 + STRING st; 1.1039 + MESSAGECACHE *elt; 1.1040 + struct stat sbuf; 1.1041 + int fd; 1.1042 + unsigned long i; 1.1043 + char flags[MAILTMPLEN],date[MAILTMPLEN]; 1.1044 + appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL); 1.1045 + long ret = NIL; 1.1046 + /* copy the messages */ 1.1047 + if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) : 1.1048 + mail_sequence (stream,sequence)) 1.1049 + for (i = 1; i <= stream->nmsgs; i++) 1.1050 + if ((elt = mail_elt (stream,i))->sequence) { 1.1051 + sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid); 1.1052 + if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return NIL; 1.1053 + fstat (fd,&sbuf); /* get size of message */ 1.1054 + if (!elt->day) { /* set internaldate to file date if needed */ 1.1055 + struct tm *tm = gmtime (&sbuf.st_mtime); 1.1056 + elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1; 1.1057 + elt->year = tm->tm_year + 1900 - BASEYEAR; 1.1058 + elt->hours = tm->tm_hour; elt->minutes = tm->tm_min; 1.1059 + elt->seconds = tm->tm_sec; 1.1060 + elt->zhours = 0; elt->zminutes = 0; 1.1061 + } 1.1062 + d.fd = fd; /* set up file descriptor */ 1.1063 + d.pos = 0; /* start of file */ 1.1064 + d.chunk = LOCAL->buf; 1.1065 + d.chunksize = CHUNKSIZE; 1.1066 + /* kludge; mh_append would just strip CRs */ 1.1067 + INIT (&st,fd_string,&d,sbuf.st_size); 1.1068 + /* init flag string */ 1.1069 + flags[0] = flags[1] = '\0'; 1.1070 + if (elt->seen) strcat (flags," \\Seen"); 1.1071 + if (elt->deleted) strcat (flags," \\Deleted"); 1.1072 + if (elt->flagged) strcat (flags," \\Flagged"); 1.1073 + if (elt->answered) strcat (flags," \\Answered"); 1.1074 + if (elt->draft) strcat (flags," \\Draft"); 1.1075 + flags[0] = '('; /* open list */ 1.1076 + strcat (flags,")"); /* close list */ 1.1077 + mail_date (date,elt); /* generate internal date */ 1.1078 + if (au) mail_parameters (NIL,SET_APPENDUID,NIL); 1.1079 + if ((ret = mail_append_full (NIL,mailbox,flags,date,&st)) && 1.1080 + (options & CP_MOVE)) elt->deleted = T; 1.1081 + if (au) mail_parameters (NIL,SET_APPENDUID,(void *) au); 1.1082 + close (fd); 1.1083 + } 1.1084 + if (ret && mail_parameters (NIL,GET_COPYUID,NIL)) 1.1085 + mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN); 1.1086 + return ret; /* return success */ 1.1087 +} 1.1088 + 1.1089 +/* MH mail append message from stringstruct 1.1090 + * Accepts: MAIL stream 1.1091 + * destination mailbox 1.1092 + * append callback 1.1093 + * data for callback 1.1094 + * Returns: T if append successful, else NIL 1.1095 + */ 1.1096 + 1.1097 +long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) 1.1098 +{ 1.1099 + struct direct **names = NIL; 1.1100 + int fd; 1.1101 + char c,*flags,*date,*s,tmp[MAILTMPLEN]; 1.1102 + STRING *message; 1.1103 + MESSAGECACHE elt; 1.1104 + FILE *df; 1.1105 + long i,size,last,nfiles; 1.1106 + long ret = LONGT; 1.1107 + /* default stream to prototype */ 1.1108 + if (!stream) stream = &mhproto; 1.1109 + /* make sure valid mailbox */ 1.1110 + if (!mh_isvalid (mailbox,tmp,NIL)) switch (errno) { 1.1111 + case ENOENT: /* no such file? */ 1.1112 + if (!((!compare_cstring (mailbox,MHINBOX) || 1.1113 + !compare_cstring (mailbox,"INBOX")) && 1.1114 + (mh_file (tmp,MHINBOX) && 1.1115 + dummy_create_path (stream,strcat (tmp,"/"), 1.1116 + get_dir_protection (mailbox))))) { 1.1117 + mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL); 1.1118 + return NIL; 1.1119 + } 1.1120 + /* falls through */ 1.1121 + case 0: /* merely empty file? */ 1.1122 + break; 1.1123 + case EINVAL: 1.1124 + sprintf (tmp,"Invalid MH-format mailbox name: %.80s",mailbox); 1.1125 + mm_log (tmp,ERROR); 1.1126 + return NIL; 1.1127 + default: 1.1128 + sprintf (tmp,"Not a MH-format mailbox: %.80s",mailbox); 1.1129 + mm_log (tmp,ERROR); 1.1130 + return NIL; 1.1131 + } 1.1132 + /* get first message */ 1.1133 + if (!(*af) (stream,data,&flags,&date,&message)) return NIL; 1.1134 + if ((nfiles = scandir (tmp,&names,mh_select,mh_numsort)) > 0) { 1.1135 + /* largest number */ 1.1136 + last = atoi (names[nfiles-1]->d_name); 1.1137 + for (i = 0; i < nfiles; ++i) /* free directory */ 1.1138 + fs_give ((void **) &names[i]); 1.1139 + } 1.1140 + else last = 0; /* no messages here yet */ 1.1141 + if (s = (void *) names) fs_give ((void **) &s); 1.1142 + 1.1143 + mm_critical (stream); /* go critical */ 1.1144 + do { 1.1145 + if (!SIZE (message)) { /* guard against zero-length */ 1.1146 + mm_log ("Append of zero-length message",ERROR); 1.1147 + ret = NIL; 1.1148 + break; 1.1149 + } 1.1150 + if (date) { /* want to preserve date? */ 1.1151 + /* yes, parse date into an elt */ 1.1152 + if (!mail_parse_date (&elt,date)) { 1.1153 + sprintf (tmp,"Bad date in append: %.80s",date); 1.1154 + mm_log (tmp,ERROR); 1.1155 + ret = NIL; 1.1156 + break; 1.1157 + } 1.1158 + } 1.1159 + mh_file (tmp,mailbox); /* build file name we will use */ 1.1160 + sprintf (tmp + strlen (tmp),"/%ld",++last); 1.1161 + if (((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL, 1.1162 + (long)mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0)|| 1.1163 + !(df = fdopen (fd,"ab"))) { 1.1164 + sprintf (tmp,"Can't open append message: %s",strerror (errno)); 1.1165 + mm_log (tmp,ERROR); 1.1166 + ret = NIL; 1.1167 + break; 1.1168 + } 1.1169 + /* copy the data w/o CR's */ 1.1170 + for (size = 0,i = SIZE (message); i && ret; --i) 1.1171 + if (((c = SNX (message)) != '\015') && (putc (c,df) == EOF)) ret = NIL; 1.1172 + /* close the file */ 1.1173 + if (!ret || fclose (df)) { 1.1174 + unlink (tmp); /* delete message */ 1.1175 + sprintf (tmp,"Message append failed: %s",strerror (errno)); 1.1176 + mm_log (tmp,ERROR); 1.1177 + ret = NIL; 1.1178 + } 1.1179 + if (ret) { /* set the date for this message */ 1.1180 + if (date) mh_setdate (tmp,&elt); 1.1181 + /* get next message */ 1.1182 + if (!(*af) (stream,data,&flags,&date,&message)) ret = NIL; 1.1183 + } 1.1184 + } while (ret && message); 1.1185 + mm_nocritical (stream); /* release critical */ 1.1186 + if (ret && mail_parameters (NIL,GET_APPENDUID,NIL)) 1.1187 + mm_log ("Can not return meaningful APPENDUID with this mailbox format", 1.1188 + WARN); 1.1189 + return ret; 1.1190 +} 1.1191 + 1.1192 +/* Internal routines */ 1.1193 + 1.1194 + 1.1195 +/* MH file name selection test 1.1196 + * Accepts: candidate directory entry 1.1197 + * Returns: T to use file name, NIL to skip it 1.1198 + */ 1.1199 + 1.1200 +int mh_select (struct direct *name) 1.1201 +{ 1.1202 + char c; 1.1203 + char *s = name->d_name; 1.1204 + while (c = *s++) if (!isdigit (c)) return NIL; 1.1205 + return T; 1.1206 +} 1.1207 + 1.1208 + 1.1209 +/* MH file name comparision 1.1210 + * Accepts: first candidate directory entry 1.1211 + * second candidate directory entry 1.1212 + * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2 1.1213 + */ 1.1214 + 1.1215 +int mh_numsort (const void *d1,const void *d2) 1.1216 +{ 1.1217 + return atoi ((*(struct direct **) d1)->d_name) - 1.1218 + atoi ((*(struct direct **) d2)->d_name); 1.1219 +} 1.1220 + 1.1221 + 1.1222 +/* MH mail build file name 1.1223 + * Accepts: destination string 1.1224 + * source 1.1225 + * Returns: destination 1.1226 + */ 1.1227 + 1.1228 +char *mh_file (char *dst,char *name) 1.1229 +{ 1.1230 + char *s; 1.1231 + char *path = mh_path (dst); 1.1232 + if (!path) fatal ("No mh path in mh_file()!"); 1.1233 + /* INBOX becomes "inbox" in the MH path */ 1.1234 + if (!compare_cstring (name,MHINBOX) || !compare_cstring (name,"INBOX")) 1.1235 + sprintf (dst,"%.900s/%.80s",path,MHINBOXDIR); 1.1236 + /* #mh names skip past prefix */ 1.1237 + else if (*name == '#') sprintf (dst,"%.100s/%.900s",path,name + 4); 1.1238 + else mailboxfile (dst,name); /* all other names */ 1.1239 + /* tie off unnecessary trailing / */ 1.1240 + if ((s = strrchr (dst,'/')) && !s[1] && (s[-1] == '/')) *s = '\0'; 1.1241 + return dst; 1.1242 +} 1.1243 + 1.1244 +/* MH canonicalize name 1.1245 + * Accepts: buffer to write name 1.1246 + * reference 1.1247 + * pattern 1.1248 + * Returns: T if success, NIL if failure 1.1249 + */ 1.1250 + 1.1251 +long mh_canonicalize (char *pattern,char *ref,char *pat) 1.1252 +{ 1.1253 + unsigned long i; 1.1254 + char *s,tmp[MAILTMPLEN]; 1.1255 + if (ref && *ref) { /* have a reference */ 1.1256 + strcpy (pattern,ref); /* copy reference to pattern */ 1.1257 + /* # overrides mailbox field in reference */ 1.1258 + if (*pat == '#') strcpy (pattern,pat); 1.1259 + /* pattern starts, reference ends, with / */ 1.1260 + else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/')) 1.1261 + strcat (pattern,pat + 1); /* append, omitting one of the period */ 1.1262 + else strcat (pattern,pat); /* anything else is just appended */ 1.1263 + } 1.1264 + else strcpy (pattern,pat); /* just have basic name */ 1.1265 + if (mh_isvalid (pattern,tmp,T)) { 1.1266 + /* count wildcards */ 1.1267 + for (i = 0, s = pattern; *s; *s++) if ((*s == '*') || (*s == '%')) ++i; 1.1268 + /* success if not too many */ 1.1269 + if (i <= MAXWILDCARDS) return LONGT; 1.1270 + mm_log ("Excessive wildcards in LIST/LSUB",ERROR); 1.1271 + } 1.1272 + return NIL; 1.1273 +} 1.1274 + 1.1275 +/* Set date for message 1.1276 + * Accepts: file name 1.1277 + * elt containing date 1.1278 + */ 1.1279 + 1.1280 +void mh_setdate (char *file,MESSAGECACHE *elt) 1.1281 +{ 1.1282 + time_t tp[2]; 1.1283 + tp[0] = time (0); /* atime is now */ 1.1284 + tp[1] = mail_longdate (elt); /* modification time */ 1.1285 + utime (file,tp); /* set the times */ 1.1286 +}