imapext-2007

annotate src/osdep/amiga/mh.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: MH mail routines
yuuji@0 16 *
yuuji@0 17 * Author(s): 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: 23 February 1992
yuuji@0 26 * Last Edited: 11 October 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 <pwd.h>
yuuji@0 37 #include <sys/stat.h>
yuuji@0 38 #include <sys/time.h>
yuuji@0 39 #include "misc.h"
yuuji@0 40 #include "dummy.h"
yuuji@0 41 #include "fdstring.h"
yuuji@0 42
yuuji@0 43
yuuji@0 44 /* Build parameters */
yuuji@0 45
yuuji@0 46 #define MHINBOX "#mhinbox" /* corresponds to namespace in env_unix.c */
yuuji@0 47 #define MHINBOXDIR "inbox"
yuuji@0 48 #define MHPROFILE ".mh_profile"
yuuji@0 49 #define MHCOMMA ','
yuuji@0 50 #define MHSEQUENCE ".mh_sequence"
yuuji@0 51 #define MHSEQUENCES ".mh_sequences"
yuuji@0 52 #define MHPATH "Mail"
yuuji@0 53
yuuji@0 54
yuuji@0 55 /* mh_load_message() flags */
yuuji@0 56
yuuji@0 57 #define MLM_HEADER 0x1 /* load message text */
yuuji@0 58 #define MLM_TEXT 0x2 /* load message text */
yuuji@0 59
yuuji@0 60 /* MH I/O stream local data */
yuuji@0 61
yuuji@0 62 typedef struct mh_local {
yuuji@0 63 char *dir; /* spool directory name */
yuuji@0 64 unsigned char buf[CHUNKSIZE]; /* temporary buffer */
yuuji@0 65 unsigned long cachedtexts; /* total size of all cached texts */
yuuji@0 66 time_t scantime; /* last time directory scanned */
yuuji@0 67 } MHLOCAL;
yuuji@0 68
yuuji@0 69
yuuji@0 70 /* Convenient access to local data */
yuuji@0 71
yuuji@0 72 #define LOCAL ((MHLOCAL *) stream->local)
yuuji@0 73
yuuji@0 74
yuuji@0 75 /* Function prototypes */
yuuji@0 76
yuuji@0 77 DRIVER *mh_valid (char *name);
yuuji@0 78 int mh_isvalid (char *name,char *tmp,long synonly);
yuuji@0 79 int mh_namevalid (char *name);
yuuji@0 80 char *mh_path (char *tmp);
yuuji@0 81 void *mh_parameters (long function,void *value);
yuuji@0 82 long mh_dirfmttest (char *name);
yuuji@0 83 void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
yuuji@0 84 void mh_list (MAILSTREAM *stream,char *ref,char *pat);
yuuji@0 85 void mh_lsub (MAILSTREAM *stream,char *ref,char *pat);
yuuji@0 86 void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level);
yuuji@0 87 long mh_subscribe (MAILSTREAM *stream,char *mailbox);
yuuji@0 88 long mh_unsubscribe (MAILSTREAM *stream,char *mailbox);
yuuji@0 89 long mh_create (MAILSTREAM *stream,char *mailbox);
yuuji@0 90 long mh_delete (MAILSTREAM *stream,char *mailbox);
yuuji@0 91 long mh_rename (MAILSTREAM *stream,char *old,char *newname);
yuuji@0 92 MAILSTREAM *mh_open (MAILSTREAM *stream);
yuuji@0 93 void mh_close (MAILSTREAM *stream,long options);
yuuji@0 94 void mh_fast (MAILSTREAM *stream,char *sequence,long flags);
yuuji@0 95 void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags);
yuuji@0 96 char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
yuuji@0 97 long flags);
yuuji@0 98 long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
yuuji@0 99 long mh_ping (MAILSTREAM *stream);
yuuji@0 100 void mh_check (MAILSTREAM *stream);
yuuji@0 101 long mh_expunge (MAILSTREAM *stream,char *sequence,long options);
yuuji@0 102 long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
yuuji@0 103 long options);
yuuji@0 104 long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
yuuji@0 105
yuuji@0 106 int mh_select (struct direct *name);
yuuji@0 107 int mh_numsort (const void *d1,const void *d2);
yuuji@0 108 char *mh_file (char *dst,char *name);
yuuji@0 109 long mh_canonicalize (char *pattern,char *ref,char *pat);
yuuji@0 110 void mh_setdate (char *file,MESSAGECACHE *elt);
yuuji@0 111
yuuji@0 112 /* MH mail routines */
yuuji@0 113
yuuji@0 114
yuuji@0 115 /* Driver dispatch used by MAIL */
yuuji@0 116
yuuji@0 117 DRIVER mhdriver = {
yuuji@0 118 "mh", /* driver name */
yuuji@0 119 /* driver flags */
yuuji@0 120 DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE|DR_NOSTICKY|DR_DIRFMT,
yuuji@0 121 (DRIVER *) NIL, /* next driver */
yuuji@0 122 mh_valid, /* mailbox is valid for us */
yuuji@0 123 mh_parameters, /* manipulate parameters */
yuuji@0 124 mh_scan, /* scan mailboxes */
yuuji@0 125 mh_list, /* find mailboxes */
yuuji@0 126 mh_lsub, /* find subscribed mailboxes */
yuuji@0 127 mh_subscribe, /* subscribe to mailbox */
yuuji@0 128 mh_unsubscribe, /* unsubscribe from mailbox */
yuuji@0 129 mh_create, /* create mailbox */
yuuji@0 130 mh_delete, /* delete mailbox */
yuuji@0 131 mh_rename, /* rename mailbox */
yuuji@0 132 mail_status_default, /* status of mailbox */
yuuji@0 133 mh_open, /* open mailbox */
yuuji@0 134 mh_close, /* close mailbox */
yuuji@0 135 mh_fast, /* fetch message "fast" attributes */
yuuji@0 136 NIL, /* fetch message flags */
yuuji@0 137 NIL, /* fetch overview */
yuuji@0 138 NIL, /* fetch message envelopes */
yuuji@0 139 mh_header, /* fetch message header */
yuuji@0 140 mh_text, /* fetch message body */
yuuji@0 141 NIL, /* fetch partial message text */
yuuji@0 142 NIL, /* unique identifier */
yuuji@0 143 NIL, /* message number */
yuuji@0 144 NIL, /* modify flags */
yuuji@0 145 NIL, /* per-message modify flags */
yuuji@0 146 NIL, /* search for message based on criteria */
yuuji@0 147 NIL, /* sort messages */
yuuji@0 148 NIL, /* thread messages */
yuuji@0 149 mh_ping, /* ping mailbox to see if still alive */
yuuji@0 150 mh_check, /* check for new messages */
yuuji@0 151 mh_expunge, /* expunge deleted messages */
yuuji@0 152 mh_copy, /* copy messages to another mailbox */
yuuji@0 153 mh_append, /* append string message to mailbox */
yuuji@0 154 NIL /* garbage collect stream */
yuuji@0 155 };
yuuji@0 156
yuuji@0 157 /* prototype stream */
yuuji@0 158 MAILSTREAM mhproto = {&mhdriver};
yuuji@0 159
yuuji@0 160
yuuji@0 161 static char *mh_profile = NIL; /* holds MH profile */
yuuji@0 162 static char *mh_pathname = NIL; /* holds MH path name */
yuuji@0 163 static long mh_once = 0; /* already snarled once */
yuuji@0 164 static long mh_allow_inbox =NIL;/* allow INBOX as well as MHINBOX */
yuuji@0 165
yuuji@0 166 /* MH mail validate mailbox
yuuji@0 167 * Accepts: mailbox name
yuuji@0 168 * Returns: our driver if name is valid, NIL otherwise
yuuji@0 169 */
yuuji@0 170
yuuji@0 171 DRIVER *mh_valid (char *name)
yuuji@0 172 {
yuuji@0 173 char tmp[MAILTMPLEN];
yuuji@0 174 return mh_isvalid (name,tmp,T) ? &mhdriver : NIL;
yuuji@0 175 }
yuuji@0 176
yuuji@0 177
yuuji@0 178 /* MH mail test for valid mailbox
yuuji@0 179 * Accepts: mailbox name
yuuji@0 180 * temporary buffer to use
yuuji@0 181 * syntax only test flag
yuuji@0 182 * Returns: T if valid, NIL otherwise
yuuji@0 183 */
yuuji@0 184
yuuji@0 185 int mh_isvalid (char *name,char *tmp,long synonly)
yuuji@0 186 {
yuuji@0 187 struct stat sbuf;
yuuji@0 188 char *s,*t,altname[MAILTMPLEN];
yuuji@0 189 unsigned long i;
yuuji@0 190 int ret = NIL;
yuuji@0 191 errno = NIL; /* zap any error condition */
yuuji@0 192 /* mh name? */
yuuji@0 193 if ((mh_allow_inbox && !compare_cstring (name,"INBOX")) ||
yuuji@0 194 !compare_cstring (name,MHINBOX) ||
yuuji@0 195 ((name[0] == '#') && ((name[1] == 'm') || (name[1] == 'M')) &&
yuuji@0 196 ((name[2] == 'h') || (name[2] == 'H')) && (name[3] == '/') && name[4])){
yuuji@0 197 if (mh_path (tmp)) /* validate name if INBOX or not synonly */
yuuji@0 198 ret = (synonly && compare_cstring (name,"INBOX")) ?
yuuji@0 199 T : ((stat (mh_file (tmp,name),&sbuf) == 0) &&
yuuji@0 200 (sbuf.st_mode & S_IFMT) == S_IFDIR);
yuuji@0 201 else if (!mh_once++) { /* only report error once */
yuuji@0 202 sprintf (tmp,"%.900s not found, mh format names disabled",mh_profile);
yuuji@0 203 mm_log (tmp,WARN);
yuuji@0 204 }
yuuji@0 205 }
yuuji@0 206 /* see if non-NS name within mh hierarchy */
yuuji@0 207 else if ((name[0] != '#') && (s = mh_path (tmp)) && (i = strlen (s)) &&
yuuji@0 208 (t = mailboxfile (tmp,name)) && !strncmp (t,s,i) &&
yuuji@0 209 (tmp[i] == '/') && tmp[i+1]) {
yuuji@0 210 sprintf (altname,"#mh%.900s",tmp+i);
yuuji@0 211 /* can't do synonly here! */
yuuji@0 212 ret = mh_isvalid (altname,tmp,NIL);
yuuji@0 213 }
yuuji@0 214 else errno = EINVAL; /* bogus name */
yuuji@0 215 return ret;
yuuji@0 216 }
yuuji@0 217
yuuji@0 218 /* MH mail test for valid mailbox
yuuji@0 219 * Accepts: mailbox name
yuuji@0 220 * Returns: T if valid, NIL otherwise
yuuji@0 221 */
yuuji@0 222
yuuji@0 223 int mh_namevalid (char *name)
yuuji@0 224 {
yuuji@0 225 char *s;
yuuji@0 226 if (name[0] == '#' && (name[1] == 'm' || name[1] == 'M') &&
yuuji@0 227 (name[2] == 'h' || name[2] == 'H') && name[3] == '/')
yuuji@0 228 for (s = name; s && *s;) { /* make sure no all-digit nodes */
yuuji@0 229 if (isdigit (*s)) s++; /* digit, check this node further... */
yuuji@0 230 else if (*s == '/') break;/* all digit node, barf */
yuuji@0 231 /* non-digit, skip to next node or return */
yuuji@0 232 else if (!((s = strchr (s+1,'/')) && *++s)) return T;
yuuji@0 233 }
yuuji@0 234 return NIL; /* all numeric or empty node */
yuuji@0 235 }
yuuji@0 236
yuuji@0 237 /* Return MH path
yuuji@0 238 * Accepts: temporary buffer
yuuji@0 239 * Returns: MH path or NIL if MH disabled
yuuji@0 240 */
yuuji@0 241
yuuji@0 242 char *mh_path (char *tmp)
yuuji@0 243 {
yuuji@0 244 char *s,*t,*v,*r;
yuuji@0 245 int fd;
yuuji@0 246 struct stat sbuf;
yuuji@0 247 if (!mh_profile) { /* build mh_profile and mh_pathname now */
yuuji@0 248 sprintf (tmp,"%s/%s",myhomedir (),MHPROFILE);
yuuji@0 249 if ((fd = open (mh_profile = cpystr (tmp),O_RDONLY,NIL)) >= 0) {
yuuji@0 250 fstat (fd,&sbuf); /* yes, get size and read file */
yuuji@0 251 read (fd,(t = (char *) fs_get (sbuf.st_size + 1)),sbuf.st_size);
yuuji@0 252 close (fd); /* don't need the file any more */
yuuji@0 253 t[sbuf.st_size] = '\0'; /* tie it off */
yuuji@0 254 /* parse profile file */
yuuji@0 255 for (s = strtok_r (t,"\r\n",&r); s && *s; s = strtok_r (NIL,"\r\n",&r)) {
yuuji@0 256 /* found space in line? */
yuuji@0 257 if (v = strpbrk (s," \t")) {
yuuji@0 258 *v++ = '\0'; /* tie off, is keyword "Path:"? */
yuuji@0 259 if (!compare_cstring (s,"Path:")) {
yuuji@0 260 /* skip whitespace */
yuuji@0 261 while ((*v == ' ') || (*v == '\t')) ++v;
yuuji@0 262 /* absolute path? */
yuuji@0 263 if (*v == '/') s = v;
yuuji@0 264 else sprintf (s = tmp,"%s/%s",myhomedir (),v);
yuuji@0 265 /* copy name */
yuuji@0 266 mh_pathname = cpystr (s);
yuuji@0 267 break; /* don't need to look at rest of file */
yuuji@0 268 }
yuuji@0 269 }
yuuji@0 270 }
yuuji@0 271 fs_give ((void **) &t); /* flush profile text */
yuuji@0 272 if (!mh_pathname) { /* default path if not in the profile */
yuuji@0 273 sprintf (tmp,"%s/%s",myhomedir (),MHPATH);
yuuji@0 274 mh_pathname = cpystr (tmp);
yuuji@0 275 }
yuuji@0 276 }
yuuji@0 277 }
yuuji@0 278 return mh_pathname;
yuuji@0 279 }
yuuji@0 280
yuuji@0 281 /* MH manipulate driver parameters
yuuji@0 282 * Accepts: function code
yuuji@0 283 * function-dependent value
yuuji@0 284 * Returns: function-dependent return value
yuuji@0 285 */
yuuji@0 286
yuuji@0 287 void *mh_parameters (long function,void *value)
yuuji@0 288 {
yuuji@0 289 void *ret = NIL;
yuuji@0 290 switch ((int) function) {
yuuji@0 291 case GET_INBOXPATH:
yuuji@0 292 if (value) ret = mh_file ((char *) value,"INBOX");
yuuji@0 293 break;
yuuji@0 294 case GET_DIRFMTTEST:
yuuji@0 295 ret = (void *) mh_dirfmttest;
yuuji@0 296 break;
yuuji@0 297 case SET_MHPROFILE:
yuuji@0 298 if (mh_profile) fs_give ((void **) &mh_profile);
yuuji@0 299 mh_profile = cpystr ((char *) value);
yuuji@0 300 case GET_MHPROFILE:
yuuji@0 301 ret = (void *) mh_profile;
yuuji@0 302 break;
yuuji@0 303 case SET_MHPATH:
yuuji@0 304 if (mh_pathname) fs_give ((void **) &mh_pathname);
yuuji@0 305 mh_pathname = cpystr ((char *) value);
yuuji@0 306 case GET_MHPATH:
yuuji@0 307 ret = (void *) mh_pathname;
yuuji@0 308 break;
yuuji@0 309 case SET_MHALLOWINBOX:
yuuji@0 310 mh_allow_inbox = value ? T : NIL;
yuuji@0 311 case GET_MHALLOWINBOX:
yuuji@0 312 ret = (void *) (mh_allow_inbox ? VOIDT : NIL);
yuuji@0 313 }
yuuji@0 314 return ret;
yuuji@0 315 }
yuuji@0 316
yuuji@0 317
yuuji@0 318 /* MH test for directory format internal node
yuuji@0 319 * Accepts: candidate node name
yuuji@0 320 * Returns: T if internal name, NIL otherwise
yuuji@0 321 */
yuuji@0 322
yuuji@0 323 long mh_dirfmttest (char *s)
yuuji@0 324 {
yuuji@0 325 int c;
yuuji@0 326 /* sequence(s) file is an internal name */
yuuji@0 327 if (strcmp (s,MHSEQUENCE) && strcmp (s,MHSEQUENCES)) {
yuuji@0 328 if (*s == MHCOMMA) ++s; /* else comma + all numeric name */
yuuji@0 329 /* success if all-numeric */
yuuji@0 330 while (c = *s++) if (!isdigit (c)) return NIL;
yuuji@0 331 }
yuuji@0 332 return LONGT;
yuuji@0 333 }
yuuji@0 334
yuuji@0 335 /* MH scan mailboxes
yuuji@0 336 * Accepts: mail stream
yuuji@0 337 * reference
yuuji@0 338 * pattern to search
yuuji@0 339 * string to scan
yuuji@0 340 */
yuuji@0 341
yuuji@0 342 void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
yuuji@0 343 {
yuuji@0 344 char *s,test[MAILTMPLEN],file[MAILTMPLEN];
yuuji@0 345 long i = 0;
yuuji@0 346 if (!pat || !*pat) { /* empty pattern? */
yuuji@0 347 if (mh_canonicalize (test,ref,"*")) {
yuuji@0 348 /* tie off name at root */
yuuji@0 349 if (s = strchr (test,'/')) *++s = '\0';
yuuji@0 350 else test[0] = '\0';
yuuji@0 351 mm_list (stream,'/',test,LATT_NOSELECT);
yuuji@0 352 }
yuuji@0 353 }
yuuji@0 354 /* get canonical form of name */
yuuji@0 355 else if (mh_canonicalize (test,ref,pat)) {
yuuji@0 356 if (contents) { /* maybe I'll implement this someday */
yuuji@0 357 mm_log ("Scan not valid for mh mailboxes",ERROR);
yuuji@0 358 return;
yuuji@0 359 }
yuuji@0 360 if (test[3] == '/') { /* looking down levels? */
yuuji@0 361 /* yes, found any wildcards? */
yuuji@0 362 if (s = strpbrk (test,"%*")) {
yuuji@0 363 /* yes, copy name up to that point */
yuuji@0 364 strncpy (file,test+4,i = s - (test+4));
yuuji@0 365 file[i] = '\0'; /* tie off */
yuuji@0 366 }
yuuji@0 367 else strcpy (file,test+4);/* use just that name then */
yuuji@0 368 /* find directory name */
yuuji@0 369 if (s = strrchr (file,'/')) {
yuuji@0 370 *s = '\0'; /* found, tie off at that point */
yuuji@0 371 s = file;
yuuji@0 372 }
yuuji@0 373 /* do the work */
yuuji@0 374 mh_list_work (stream,s,test,0);
yuuji@0 375 }
yuuji@0 376 /* always an INBOX */
yuuji@0 377 if (!compare_cstring (test,MHINBOX))
yuuji@0 378 mm_list (stream,NIL,MHINBOX,LATT_NOINFERIORS);
yuuji@0 379 }
yuuji@0 380 }
yuuji@0 381
yuuji@0 382 /* MH list mailboxes
yuuji@0 383 * Accepts: mail stream
yuuji@0 384 * reference
yuuji@0 385 * pattern to search
yuuji@0 386 */
yuuji@0 387
yuuji@0 388 void mh_list (MAILSTREAM *stream,char *ref,char *pat)
yuuji@0 389 {
yuuji@0 390 mh_scan (stream,ref,pat,NIL);
yuuji@0 391 }
yuuji@0 392
yuuji@0 393
yuuji@0 394 /* MH list subscribed mailboxes
yuuji@0 395 * Accepts: mail stream
yuuji@0 396 * reference
yuuji@0 397 * pattern to search
yuuji@0 398 */
yuuji@0 399
yuuji@0 400 void mh_lsub (MAILSTREAM *stream,char *ref,char *pat)
yuuji@0 401 {
yuuji@0 402 void *sdb = NIL;
yuuji@0 403 char *s,test[MAILTMPLEN];
yuuji@0 404 /* get canonical form of name */
yuuji@0 405 if (mh_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) {
yuuji@0 406 do if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL);
yuuji@0 407 while (s = sm_read (&sdb)); /* until no more subscriptions */
yuuji@0 408 }
yuuji@0 409 }
yuuji@0 410
yuuji@0 411 /* MH list mailboxes worker routine
yuuji@0 412 * Accepts: mail stream
yuuji@0 413 * directory name to search
yuuji@0 414 * search pattern
yuuji@0 415 * search level
yuuji@0 416 */
yuuji@0 417
yuuji@0 418 void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level)
yuuji@0 419 {
yuuji@0 420 DIR *dp;
yuuji@0 421 struct direct *d;
yuuji@0 422 struct stat sbuf;
yuuji@0 423 char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN];
yuuji@0 424 /* build MH name to search */
yuuji@0 425 if (dir) sprintf (name,"#mh/%s/",dir);
yuuji@0 426 else strcpy (name,"#mh/");
yuuji@0 427 /* make directory name, punt if bogus */
yuuji@0 428 if (!mh_file (curdir,name)) return;
yuuji@0 429 cp = curdir + strlen (curdir);/* end of directory name */
yuuji@0 430 np = name + strlen (name); /* end of MH name */
yuuji@0 431 if (dp = opendir (curdir)) { /* open directory */
yuuji@0 432 while (d = readdir (dp)) /* scan, ignore . and numeric names */
yuuji@0 433 if ((d->d_name[0] != '.') && !mh_select (d)) {
yuuji@0 434 strcpy (cp,d->d_name); /* make directory name */
yuuji@0 435 if (!stat (curdir,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
yuuji@0 436 strcpy (np,d->d_name);/* make mh name of directory name */
yuuji@0 437 /* yes, an MH name if full match */
yuuji@0 438 if (pmatch_full (name,pat,'/')) mm_list (stream,'/',name,NIL);
yuuji@0 439 /* check if should recurse */
yuuji@0 440 if (dmatch (name,pat,'/') &&
yuuji@0 441 (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
yuuji@0 442 mh_list_work (stream,name+4,pat,level+1);
yuuji@0 443 }
yuuji@0 444 }
yuuji@0 445 closedir (dp); /* all done, flush directory */
yuuji@0 446 }
yuuji@0 447 }
yuuji@0 448
yuuji@0 449 /* MH mail subscribe to mailbox
yuuji@0 450 * Accepts: mail stream
yuuji@0 451 * mailbox to add to subscription list
yuuji@0 452 * Returns: T on success, NIL on failure
yuuji@0 453 */
yuuji@0 454
yuuji@0 455 long mh_subscribe (MAILSTREAM *stream,char *mailbox)
yuuji@0 456 {
yuuji@0 457 return sm_subscribe (mailbox);
yuuji@0 458 }
yuuji@0 459
yuuji@0 460
yuuji@0 461 /* MH mail unsubscribe to mailbox
yuuji@0 462 * Accepts: mail stream
yuuji@0 463 * mailbox to delete from subscription list
yuuji@0 464 * Returns: T on success, NIL on failure
yuuji@0 465 */
yuuji@0 466
yuuji@0 467 long mh_unsubscribe (MAILSTREAM *stream,char *mailbox)
yuuji@0 468 {
yuuji@0 469 return sm_unsubscribe (mailbox);
yuuji@0 470 }
yuuji@0 471
yuuji@0 472 /* MH mail create mailbox
yuuji@0 473 * Accepts: mail stream
yuuji@0 474 * mailbox name to create
yuuji@0 475 * Returns: T on success, NIL on failure
yuuji@0 476 */
yuuji@0 477
yuuji@0 478 long mh_create (MAILSTREAM *stream,char *mailbox)
yuuji@0 479 {
yuuji@0 480 char tmp[MAILTMPLEN];
yuuji@0 481 if (!mh_namevalid (mailbox)) /* validate name */
yuuji@0 482 sprintf (tmp,"Can't create mailbox %.80s: invalid MH-format name",mailbox);
yuuji@0 483 /* must not already exist */
yuuji@0 484 else if (mh_isvalid (mailbox,tmp,NIL))
yuuji@0 485 sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
yuuji@0 486 else if (!mh_path (tmp)) return NIL;
yuuji@0 487 /* try to make it */
yuuji@0 488 else if (!(mh_file (tmp,mailbox) &&
yuuji@0 489 dummy_create_path (stream,strcat (tmp,"/"),
yuuji@0 490 get_dir_protection (mailbox))))
yuuji@0 491 sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno));
yuuji@0 492 else return LONGT; /* success */
yuuji@0 493 mm_log (tmp,ERROR);
yuuji@0 494 return NIL;
yuuji@0 495 }
yuuji@0 496
yuuji@0 497 /* MH mail delete mailbox
yuuji@0 498 * mailbox name to delete
yuuji@0 499 * Returns: T on success, NIL on failure
yuuji@0 500 */
yuuji@0 501
yuuji@0 502 long mh_delete (MAILSTREAM *stream,char *mailbox)
yuuji@0 503 {
yuuji@0 504 DIR *dirp;
yuuji@0 505 struct direct *d;
yuuji@0 506 int i;
yuuji@0 507 char tmp[MAILTMPLEN];
yuuji@0 508 /* is mailbox valid? */
yuuji@0 509 if (!mh_isvalid (mailbox,tmp,NIL)) {
yuuji@0 510 sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
yuuji@0 511 mm_log (tmp,ERROR);
yuuji@0 512 return NIL;
yuuji@0 513 }
yuuji@0 514 /* get name of directory */
yuuji@0 515 i = strlen (mh_file (tmp,mailbox));
yuuji@0 516 if (dirp = opendir (tmp)) { /* open directory */
yuuji@0 517 tmp[i++] = '/'; /* now apply trailing delimiter */
yuuji@0 518 /* massacre all mh owned files */
yuuji@0 519 while (d = readdir (dirp)) if (mh_dirfmttest (d->d_name)) {
yuuji@0 520 strcpy (tmp + i,d->d_name);
yuuji@0 521 unlink (tmp); /* sayonara */
yuuji@0 522 }
yuuji@0 523 closedir (dirp); /* flush directory */
yuuji@0 524 }
yuuji@0 525 /* try to remove the directory */
yuuji@0 526 if (rmdir (mh_file (tmp,mailbox))) {
yuuji@0 527 sprintf (tmp,"Can't delete mailbox %.80s: %s",mailbox,strerror (errno));
yuuji@0 528 mm_log (tmp,WARN);
yuuji@0 529 }
yuuji@0 530 return T; /* return success */
yuuji@0 531 }
yuuji@0 532
yuuji@0 533 /* MH mail rename mailbox
yuuji@0 534 * Accepts: MH mail stream
yuuji@0 535 * old mailbox name
yuuji@0 536 * new mailbox name
yuuji@0 537 * Returns: T on success, NIL on failure
yuuji@0 538 */
yuuji@0 539
yuuji@0 540 long mh_rename (MAILSTREAM *stream,char *old,char *newname)
yuuji@0 541 {
yuuji@0 542 char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
yuuji@0 543 struct stat sbuf;
yuuji@0 544 /* old mailbox name must be valid */
yuuji@0 545 if (!mh_isvalid (old,tmp,NIL))
yuuji@0 546 sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
yuuji@0 547 else if (!mh_namevalid (newname))
yuuji@0 548 sprintf (tmp,"Can't rename to mailbox %.80s: invalid MH-format name",
yuuji@0 549 newname);
yuuji@0 550 /* new mailbox name must not be valid */
yuuji@0 551 else if (mh_isvalid (newname,tmp,NIL))
yuuji@0 552 sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
yuuji@0 553 newname);
yuuji@0 554 /* success if can rename the directory */
yuuji@0 555 else { /* found superior to destination name? */
yuuji@0 556 if (s = strrchr (mh_file (tmp1,newname),'/')) {
yuuji@0 557 c = *++s; /* remember first character of inferior */
yuuji@0 558 *s = '\0'; /* tie off to get just superior */
yuuji@0 559 /* name doesn't exist, create it */
yuuji@0 560 if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
yuuji@0 561 !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
yuuji@0 562 return NIL;
yuuji@0 563 *s = c; /* restore full name */
yuuji@0 564 }
yuuji@0 565 if (!rename (mh_file (tmp,old),tmp1)) return T;
yuuji@0 566 sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",
yuuji@0 567 old,newname,strerror (errno));
yuuji@0 568 }
yuuji@0 569 mm_log (tmp,ERROR); /* something failed */
yuuji@0 570 return NIL;
yuuji@0 571 }
yuuji@0 572
yuuji@0 573 /* MH mail open
yuuji@0 574 * Accepts: stream to open
yuuji@0 575 * Returns: stream on success, NIL on failure
yuuji@0 576 */
yuuji@0 577
yuuji@0 578 MAILSTREAM *mh_open (MAILSTREAM *stream)
yuuji@0 579 {
yuuji@0 580 char tmp[MAILTMPLEN];
yuuji@0 581 if (!stream) return &mhproto; /* return prototype for OP_PROTOTYPE call */
yuuji@0 582 if (stream->local) fatal ("mh recycle stream");
yuuji@0 583 stream->local = fs_get (sizeof (MHLOCAL));
yuuji@0 584 /* INBOXness is one of the following:
yuuji@0 585 * #mhinbox (case-independent)
yuuji@0 586 * #mh/inbox (mh is case-independent, inbox is case-dependent)
yuuji@0 587 * INBOX (case-independent
yuuji@0 588 */
yuuji@0 589 stream->inbox = /* note if an INBOX or not */
yuuji@0 590 (!compare_cstring (stream->mailbox,MHINBOX) ||
yuuji@0 591 ((stream->mailbox[0] == '#') &&
yuuji@0 592 ((stream->mailbox[1] == 'm') || (stream->mailbox[1] == 'M')) &&
yuuji@0 593 ((stream->mailbox[2] == 'h') || (stream->mailbox[2] == 'H')) &&
yuuji@0 594 (stream->mailbox[3] == '/') && !strcmp (stream->mailbox+4,MHINBOXDIR)) ||
yuuji@0 595 !compare_cstring (stream->mailbox,"INBOX")) ? T : NIL;
yuuji@0 596 mh_file (tmp,stream->mailbox);/* get directory name */
yuuji@0 597 LOCAL->dir = cpystr (tmp); /* copy directory name for later */
yuuji@0 598 LOCAL->scantime = 0; /* not scanned yet */
yuuji@0 599 LOCAL->cachedtexts = 0; /* no cached texts */
yuuji@0 600 stream->sequence++; /* bump sequence number */
yuuji@0 601 /* parse mailbox */
yuuji@0 602 stream->nmsgs = stream->recent = 0;
yuuji@0 603 if (!mh_ping (stream)) return NIL;
yuuji@0 604 if (!(stream->nmsgs || stream->silent))
yuuji@0 605 mm_log ("Mailbox is empty",(long) NIL);
yuuji@0 606 return stream; /* return stream to caller */
yuuji@0 607 }
yuuji@0 608
yuuji@0 609 /* MH mail close
yuuji@0 610 * Accepts: MAIL stream
yuuji@0 611 * close options
yuuji@0 612 */
yuuji@0 613
yuuji@0 614 void mh_close (MAILSTREAM *stream,long options)
yuuji@0 615 {
yuuji@0 616 if (LOCAL) { /* only if a file is open */
yuuji@0 617 int silent = stream->silent;
yuuji@0 618 stream->silent = T; /* note this stream is dying */
yuuji@0 619 if (options & CL_EXPUNGE) mh_expunge (stream,NIL,NIL);
yuuji@0 620 if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
yuuji@0 621 /* nuke the local data */
yuuji@0 622 fs_give ((void **) &stream->local);
yuuji@0 623 stream->dtb = NIL; /* log out the DTB */
yuuji@0 624 stream->silent = silent; /* reset silent state */
yuuji@0 625 }
yuuji@0 626 }
yuuji@0 627
yuuji@0 628
yuuji@0 629 /* MH mail fetch fast information
yuuji@0 630 * Accepts: MAIL stream
yuuji@0 631 * sequence
yuuji@0 632 * option flags
yuuji@0 633 */
yuuji@0 634
yuuji@0 635 void mh_fast (MAILSTREAM *stream,char *sequence,long flags)
yuuji@0 636 {
yuuji@0 637 MESSAGECACHE *elt;
yuuji@0 638 unsigned long i;
yuuji@0 639 /* set up metadata for all messages */
yuuji@0 640 if (stream && LOCAL && ((flags & FT_UID) ?
yuuji@0 641 mail_uid_sequence (stream,sequence) :
yuuji@0 642 mail_sequence (stream,sequence)))
yuuji@0 643 for (i = 1; i <= stream->nmsgs; i++)
yuuji@0 644 if ((elt = mail_elt (stream,i))->sequence &&
yuuji@0 645 !(elt->day && elt->rfc822_size)) mh_load_message (stream,i,NIL);
yuuji@0 646 }
yuuji@0 647
yuuji@0 648 /* MH load message into cache
yuuji@0 649 * Accepts: MAIL stream
yuuji@0 650 * message #
yuuji@0 651 * option flags
yuuji@0 652 */
yuuji@0 653
yuuji@0 654 void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags)
yuuji@0 655 {
yuuji@0 656 unsigned long i,j,nlseen;
yuuji@0 657 int fd;
yuuji@0 658 unsigned char c,*t;
yuuji@0 659 struct stat sbuf;
yuuji@0 660 MESSAGECACHE *elt;
yuuji@0 661 FDDATA d;
yuuji@0 662 STRING bs;
yuuji@0 663 elt = mail_elt (stream,msgno);/* get elt */
yuuji@0 664 /* build message file name */
yuuji@0 665 sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
yuuji@0 666 /* anything we need not currently cached? */
yuuji@0 667 if ((!elt->day || !elt->rfc822_size ||
yuuji@0 668 ((flags & MLM_HEADER) && !elt->private.msg.header.text.data) ||
yuuji@0 669 ((flags & MLM_TEXT) && !elt->private.msg.text.text.data)) &&
yuuji@0 670 ((fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0)) {
yuuji@0 671 fstat (fd,&sbuf); /* get file metadata */
yuuji@0 672 d.fd = fd; /* set up file descriptor */
yuuji@0 673 d.pos = 0; /* start of file */
yuuji@0 674 d.chunk = LOCAL->buf;
yuuji@0 675 d.chunksize = CHUNKSIZE;
yuuji@0 676 INIT (&bs,fd_string,&d,sbuf.st_size);
yuuji@0 677 if (!elt->day) { /* set internaldate to file date */
yuuji@0 678 struct tm *tm = gmtime (&sbuf.st_mtime);
yuuji@0 679 elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
yuuji@0 680 elt->year = tm->tm_year + 1900 - BASEYEAR;
yuuji@0 681 elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
yuuji@0 682 elt->seconds = tm->tm_sec;
yuuji@0 683 elt->zhours = 0; elt->zminutes = 0;
yuuji@0 684 }
yuuji@0 685
yuuji@0 686 if (!elt->rfc822_size) { /* know message size yet? */
yuuji@0 687 for (i = 0, j = SIZE (&bs), nlseen = 0; j--; ) switch (SNX (&bs)) {
yuuji@0 688 case '\015': /* unlikely carriage return */
yuuji@0 689 if (!j || (CHR (&bs) != '\012')) {
yuuji@0 690 i++; /* ugh, raw CR */
yuuji@0 691 nlseen = NIL;
yuuji@0 692 break;
yuuji@0 693 }
yuuji@0 694 SNX (&bs); /* eat the line feed, drop in */
yuuji@0 695 --j;
yuuji@0 696 case '\012': /* line feed? */
yuuji@0 697 i += 2; /* count a CRLF */
yuuji@0 698 /* header size known yet? */
yuuji@0 699 if (!elt->private.msg.header.text.size && nlseen) {
yuuji@0 700 /* note position in file */
yuuji@0 701 elt->private.special.text.size = GETPOS (&bs);
yuuji@0 702 /* and CRLF-adjusted size */
yuuji@0 703 elt->private.msg.header.text.size = i;
yuuji@0 704 }
yuuji@0 705 nlseen = T; /* note newline seen */
yuuji@0 706 break;
yuuji@0 707 default: /* ordinary chararacter */
yuuji@0 708 i++;
yuuji@0 709 nlseen = NIL;
yuuji@0 710 break;
yuuji@0 711 }
yuuji@0 712 SETPOS (&bs,0); /* restore old position */
yuuji@0 713 elt->rfc822_size = i; /* note that we have size now */
yuuji@0 714 /* header is entire message if no delimiter */
yuuji@0 715 if (!elt->private.msg.header.text.size)
yuuji@0 716 elt->private.msg.header.text.size = elt->rfc822_size;
yuuji@0 717 /* text is remainder of message */
yuuji@0 718 elt->private.msg.text.text.size =
yuuji@0 719 elt->rfc822_size - elt->private.msg.header.text.size;
yuuji@0 720 }
yuuji@0 721 /* need to load cache with message data? */
yuuji@0 722 if (((flags & MLM_HEADER) && !elt->private.msg.header.text.data) ||
yuuji@0 723 ((flags & MLM_TEXT) && !elt->private.msg.text.text.data)) {
yuuji@0 724 /* purge cache if too big */
yuuji@0 725 if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
yuuji@0 726 /* just can't keep that much */
yuuji@0 727 mail_gc (stream,GC_TEXTS);
yuuji@0 728 LOCAL->cachedtexts = 0;
yuuji@0 729 }
yuuji@0 730
yuuji@0 731 if ((flags & MLM_HEADER) && !elt->private.msg.header.text.data) {
yuuji@0 732 t = elt->private.msg.header.text.data =
yuuji@0 733 (unsigned char *) fs_get (elt->private.msg.header.text.size + 1);
yuuji@0 734 LOCAL->cachedtexts += elt->private.msg.header.text.size;
yuuji@0 735 /* read in message header */
yuuji@0 736 for (i = 0; i < elt->private.msg.header.text.size; i++)
yuuji@0 737 switch (c = SNX (&bs)) {
yuuji@0 738 case '\015': /* unlikely carriage return */
yuuji@0 739 *t++ = c;
yuuji@0 740 if ((CHR (&bs) == '\012')) {
yuuji@0 741 *t++ = SNX (&bs);
yuuji@0 742 i++;
yuuji@0 743 }
yuuji@0 744 break;
yuuji@0 745 case '\012': /* line feed? */
yuuji@0 746 *t++ = '\015';
yuuji@0 747 i++;
yuuji@0 748 default:
yuuji@0 749 *t++ = c;
yuuji@0 750 break;
yuuji@0 751 }
yuuji@0 752 *t = '\0'; /* tie off string */
yuuji@0 753 if ((t - elt->private.msg.header.text.data) !=
yuuji@0 754 elt->private.msg.header.text.size) fatal ("mh hdr size mismatch");
yuuji@0 755 }
yuuji@0 756 if ((flags & MLM_TEXT) && !elt->private.msg.text.text.data) {
yuuji@0 757 t = elt->private.msg.text.text.data =
yuuji@0 758 (unsigned char *) fs_get (elt->private.msg.text.text.size + 1);
yuuji@0 759 SETPOS (&bs,elt->private.special.text.size);
yuuji@0 760 LOCAL->cachedtexts += elt->private.msg.text.text.size;
yuuji@0 761 /* read in message text */
yuuji@0 762 for (i = 0; i < elt->private.msg.text.text.size; i++)
yuuji@0 763 switch (c = SNX (&bs)) {
yuuji@0 764 case '\015': /* unlikely carriage return */
yuuji@0 765 *t++ = c;
yuuji@0 766 if ((CHR (&bs) == '\012')) {
yuuji@0 767 *t++ = SNX (&bs);
yuuji@0 768 i++;
yuuji@0 769 }
yuuji@0 770 break;
yuuji@0 771 case '\012': /* line feed? */
yuuji@0 772 *t++ = '\015';
yuuji@0 773 i++;
yuuji@0 774 default:
yuuji@0 775 *t++ = c;
yuuji@0 776 break;
yuuji@0 777 }
yuuji@0 778 *t = '\0'; /* tie off string */
yuuji@0 779 if ((t - elt->private.msg.text.text.data) !=
yuuji@0 780 elt->private.msg.text.text.size) fatal ("mh txt size mismatch");
yuuji@0 781 }
yuuji@0 782 }
yuuji@0 783 close (fd); /* flush message file */
yuuji@0 784 }
yuuji@0 785 }
yuuji@0 786
yuuji@0 787 /* MH mail fetch message header
yuuji@0 788 * Accepts: MAIL stream
yuuji@0 789 * message # to fetch
yuuji@0 790 * pointer to returned header text length
yuuji@0 791 * option flags
yuuji@0 792 * Returns: message header in RFC822 format
yuuji@0 793 */
yuuji@0 794
yuuji@0 795 char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
yuuji@0 796 long flags)
yuuji@0 797 {
yuuji@0 798 MESSAGECACHE *elt;
yuuji@0 799 *length = 0; /* default to empty */
yuuji@0 800 if (flags & FT_UID) return "";/* UID call "impossible" */
yuuji@0 801 elt = mail_elt (stream,msgno);/* get elt */
yuuji@0 802 if (!elt->private.msg.header.text.data)
yuuji@0 803 mh_load_message (stream,msgno,MLM_HEADER);
yuuji@0 804 *length = elt->private.msg.header.text.size;
yuuji@0 805 return (char *) elt->private.msg.header.text.data;
yuuji@0 806 }
yuuji@0 807
yuuji@0 808
yuuji@0 809 /* MH mail fetch message text (body only)
yuuji@0 810 * Accepts: MAIL stream
yuuji@0 811 * message # to fetch
yuuji@0 812 * pointer to returned stringstruct
yuuji@0 813 * option flags
yuuji@0 814 * Returns: T on success, NIL on failure
yuuji@0 815 */
yuuji@0 816
yuuji@0 817 long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
yuuji@0 818 {
yuuji@0 819 MESSAGECACHE *elt;
yuuji@0 820 /* UID call "impossible" */
yuuji@0 821 if (flags & FT_UID) return NIL;
yuuji@0 822 elt = mail_elt (stream,msgno);/* get elt */
yuuji@0 823 /* snarf message if don't have it yet */
yuuji@0 824 if (!elt->private.msg.text.text.data) {
yuuji@0 825 mh_load_message (stream,msgno,MLM_TEXT);
yuuji@0 826 if (!elt->private.msg.text.text.data) return NIL;
yuuji@0 827 }
yuuji@0 828 if (!(flags & FT_PEEK)) { /* mark as seen */
yuuji@0 829 mail_elt (stream,msgno)->seen = T;
yuuji@0 830 mm_flags (stream,msgno);
yuuji@0 831 }
yuuji@0 832 INIT (bs,mail_string,elt->private.msg.text.text.data,
yuuji@0 833 elt->private.msg.text.text.size);
yuuji@0 834 return T;
yuuji@0 835 }
yuuji@0 836
yuuji@0 837 /* MH mail ping mailbox
yuuji@0 838 * Accepts: MAIL stream
yuuji@0 839 * Returns: T if stream alive, else NIL
yuuji@0 840 */
yuuji@0 841
yuuji@0 842 long mh_ping (MAILSTREAM *stream)
yuuji@0 843 {
yuuji@0 844 MAILSTREAM *sysibx = NIL;
yuuji@0 845 MESSAGECACHE *elt,*selt;
yuuji@0 846 struct stat sbuf;
yuuji@0 847 char *s,tmp[MAILTMPLEN];
yuuji@0 848 int fd;
yuuji@0 849 unsigned long i,j,r;
yuuji@0 850 unsigned long old = stream->uid_last;
yuuji@0 851 long nmsgs = stream->nmsgs;
yuuji@0 852 long recent = stream->recent;
yuuji@0 853 int silent = stream->silent;
yuuji@0 854 if (stat (LOCAL->dir,&sbuf)) {/* directory exists? */
yuuji@0 855 if (stream->inbox && /* no, create if INBOX */
yuuji@0 856 dummy_create_path (stream,strcat (mh_file (tmp,MHINBOX),"/"),
yuuji@0 857 get_dir_protection ("INBOX"))) return T;
yuuji@0 858 sprintf (tmp,"Can't open mailbox %.80s: no such mailbox",stream->mailbox);
yuuji@0 859 mm_log (tmp,ERROR);
yuuji@0 860 return NIL;
yuuji@0 861 }
yuuji@0 862 stream->silent = T; /* don't pass up mm_exists() events yet */
yuuji@0 863 if (sbuf.st_ctime != LOCAL->scantime) {
yuuji@0 864 struct direct **names = NIL;
yuuji@0 865 long nfiles = scandir (LOCAL->dir,&names,mh_select,mh_numsort);
yuuji@0 866 if (nfiles < 0) nfiles = 0; /* in case error */
yuuji@0 867 /* note scanned now */
yuuji@0 868 LOCAL->scantime = sbuf.st_ctime;
yuuji@0 869 /* scan directory */
yuuji@0 870 for (i = 0; i < nfiles; ++i) {
yuuji@0 871 /* if newly seen, add to list */
yuuji@0 872 if ((j = atoi (names[i]->d_name)) > old) {
yuuji@0 873 mail_exists (stream,++nmsgs);
yuuji@0 874 stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j;
yuuji@0 875 elt->valid = T; /* note valid flags */
yuuji@0 876 if (old) { /* other than the first pass? */
yuuji@0 877 elt->recent = T; /* yup, mark as recent */
yuuji@0 878 recent++; /* bump recent count */
yuuji@0 879 }
yuuji@0 880 else { /* see if already read */
yuuji@0 881 sprintf (tmp,"%s/%s",LOCAL->dir,names[i]->d_name);
yuuji@0 882 if (!stat (tmp,&sbuf) && (sbuf.st_atime > sbuf.st_mtime))
yuuji@0 883 elt->seen = T;
yuuji@0 884 }
yuuji@0 885 }
yuuji@0 886 fs_give ((void **) &names[i]);
yuuji@0 887 }
yuuji@0 888 /* free directory */
yuuji@0 889 if (s = (void *) names) fs_give ((void **) &s);
yuuji@0 890 }
yuuji@0 891
yuuji@0 892 /* if INBOX, snarf from system INBOX */
yuuji@0 893 if (stream->inbox && strcmp (sysinbox (),stream->mailbox)) {
yuuji@0 894 old = stream->uid_last;
yuuji@0 895 mm_critical (stream); /* go critical */
yuuji@0 896 /* see if anything in system inbox */
yuuji@0 897 if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
yuuji@0 898 (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
yuuji@0 899 !sysibx->rdonly && (r = sysibx->nmsgs)) {
yuuji@0 900 for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */
yuuji@0 901 /* build file name we will use */
yuuji@0 902 sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old);
yuuji@0 903 /* snarf message from Berkeley mailbox */
yuuji@0 904 selt = mail_elt (sysibx,i);
yuuji@0 905 if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL,
yuuji@0 906 (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
yuuji@0 907 >= 0) &&
yuuji@0 908 (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_INTERNAL)) &&
yuuji@0 909 (write (fd,s,j) == j) &&
yuuji@0 910 (s = mail_fetchtext_full (sysibx,i,&j,FT_INTERNAL|FT_PEEK)) &&
yuuji@0 911 (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) {
yuuji@0 912 /* swell the cache */
yuuji@0 913 mail_exists (stream,++nmsgs);
yuuji@0 914 stream->uid_last = /* create new elt, note its file number */
yuuji@0 915 (elt = mail_elt (stream,nmsgs))->private.uid = old;
yuuji@0 916 recent++; /* bump recent count */
yuuji@0 917 /* set up initial flags and date */
yuuji@0 918 elt->valid = elt->recent = T;
yuuji@0 919 elt->seen = selt->seen;
yuuji@0 920 elt->deleted = selt->deleted;
yuuji@0 921 elt->flagged = selt->flagged;
yuuji@0 922 elt->answered = selt->answered;
yuuji@0 923 elt->draft = selt->draft;
yuuji@0 924 elt->day = selt->day;elt->month = selt->month;elt->year = selt->year;
yuuji@0 925 elt->hours = selt->hours;elt->minutes = selt->minutes;
yuuji@0 926 elt->seconds = selt->seconds;
yuuji@0 927 elt->zhours = selt->zhours; elt->zminutes = selt->zminutes;
yuuji@0 928 elt->zoccident = selt->zoccident;
yuuji@0 929 mh_setdate (LOCAL->buf,elt);
yuuji@0 930 sprintf (tmp,"%lu",i);/* delete it from the sysinbox */
yuuji@0 931 mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
yuuji@0 932 }
yuuji@0 933
yuuji@0 934 else { /* failed to snarf */
yuuji@0 935 if (fd) { /* did it ever get opened? */
yuuji@0 936 close (fd); /* close descriptor */
yuuji@0 937 unlink (LOCAL->buf);/* flush this file */
yuuji@0 938 }
yuuji@0 939 sprintf (tmp,"Message copy to MH mailbox failed: %.80s",
yuuji@0 940 s,strerror (errno));
yuuji@0 941 mm_log (tmp,ERROR);
yuuji@0 942 r = 0; /* stop the snarf in its tracks */
yuuji@0 943 }
yuuji@0 944 }
yuuji@0 945 /* update scan time */
yuuji@0 946 if (!stat (LOCAL->dir,&sbuf)) LOCAL->scantime = sbuf.st_ctime;
yuuji@0 947 mail_expunge (sysibx); /* now expunge all those messages */
yuuji@0 948 }
yuuji@0 949 if (sysibx) mail_close (sysibx);
yuuji@0 950 mm_nocritical (stream); /* release critical */
yuuji@0 951 }
yuuji@0 952 stream->silent = silent; /* can pass up events now */
yuuji@0 953 mail_exists (stream,nmsgs); /* notify upper level of mailbox size */
yuuji@0 954 mail_recent (stream,recent);
yuuji@0 955 return T; /* return that we are alive */
yuuji@0 956 }
yuuji@0 957
yuuji@0 958 /* MH mail check mailbox
yuuji@0 959 * Accepts: MAIL stream
yuuji@0 960 */
yuuji@0 961
yuuji@0 962 void mh_check (MAILSTREAM *stream)
yuuji@0 963 {
yuuji@0 964 /* Perhaps in the future this will preserve flags */
yuuji@0 965 if (mh_ping (stream)) mm_log ("Check completed",(long) NIL);
yuuji@0 966 }
yuuji@0 967
yuuji@0 968
yuuji@0 969 /* MH mail expunge mailbox
yuuji@0 970 * Accepts: MAIL stream
yuuji@0 971 * sequence to expunge if non-NIL
yuuji@0 972 * expunge options
yuuji@0 973 * Returns: T, always
yuuji@0 974 */
yuuji@0 975
yuuji@0 976 long mh_expunge (MAILSTREAM *stream,char *sequence,long options)
yuuji@0 977 {
yuuji@0 978 long ret;
yuuji@0 979 MESSAGECACHE *elt;
yuuji@0 980 unsigned long i = 1;
yuuji@0 981 unsigned long n = 0;
yuuji@0 982 unsigned long recent = stream->recent;
yuuji@0 983 if (ret = sequence ? ((options & EX_UID) ?
yuuji@0 984 mail_uid_sequence (stream,sequence) :
yuuji@0 985 mail_sequence (stream,sequence)) : LONGT) {
yuuji@0 986 mm_critical (stream); /* go critical */
yuuji@0 987 while (i <= stream->nmsgs) {/* for each message */
yuuji@0 988 elt = mail_elt (stream,i);/* if deleted, need to trash it */
yuuji@0 989 if (elt->deleted && (sequence ? elt->sequence : T)) {
yuuji@0 990 sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
yuuji@0 991 if (unlink (LOCAL->buf)) {/* try to delete the message */
yuuji@0 992 sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i,
yuuji@0 993 strerror (errno));
yuuji@0 994 mm_log (LOCAL->buf,(long) NIL);
yuuji@0 995 break;
yuuji@0 996 }
yuuji@0 997 /* note uncached */
yuuji@0 998 LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ?
yuuji@0 999 elt->private.msg.header.text.size : 0) +
yuuji@0 1000 (elt->private.msg.text.text.data ?
yuuji@0 1001 elt->private.msg.text.text.size : 0));
yuuji@0 1002 mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS);
yuuji@0 1003 /* if recent, note one less recent message */
yuuji@0 1004 if (elt->recent) --recent;
yuuji@0 1005 /* notify upper levels */
yuuji@0 1006 mail_expunged (stream,i);
yuuji@0 1007 n++; /* count up one more expunged message */
yuuji@0 1008 }
yuuji@0 1009 else i++; /* otherwise try next message */
yuuji@0 1010 }
yuuji@0 1011 if (n) { /* output the news if any expunged */
yuuji@0 1012 sprintf (LOCAL->buf,"Expunged %lu messages",n);
yuuji@0 1013 mm_log (LOCAL->buf,(long) NIL);
yuuji@0 1014 }
yuuji@0 1015 else mm_log ("No messages deleted, so no update needed",(long) NIL);
yuuji@0 1016 mm_nocritical (stream); /* release critical */
yuuji@0 1017 /* notify upper level of new mailbox size */
yuuji@0 1018 mail_exists (stream,stream->nmsgs);
yuuji@0 1019 mail_recent (stream,recent);
yuuji@0 1020 }
yuuji@0 1021 return ret;
yuuji@0 1022 }
yuuji@0 1023
yuuji@0 1024 /* MH mail copy message(s)
yuuji@0 1025 * Accepts: MAIL stream
yuuji@0 1026 * sequence
yuuji@0 1027 * destination mailbox
yuuji@0 1028 * copy options
yuuji@0 1029 * Returns: T if copy successful, else NIL
yuuji@0 1030 */
yuuji@0 1031
yuuji@0 1032 long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
yuuji@0 1033 {
yuuji@0 1034 FDDATA d;
yuuji@0 1035 STRING st;
yuuji@0 1036 MESSAGECACHE *elt;
yuuji@0 1037 struct stat sbuf;
yuuji@0 1038 int fd;
yuuji@0 1039 unsigned long i;
yuuji@0 1040 char flags[MAILTMPLEN],date[MAILTMPLEN];
yuuji@0 1041 appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
yuuji@0 1042 long ret = NIL;
yuuji@0 1043 /* copy the messages */
yuuji@0 1044 if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
yuuji@0 1045 mail_sequence (stream,sequence))
yuuji@0 1046 for (i = 1; i <= stream->nmsgs; i++)
yuuji@0 1047 if ((elt = mail_elt (stream,i))->sequence) {
yuuji@0 1048 sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
yuuji@0 1049 if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return NIL;
yuuji@0 1050 fstat (fd,&sbuf); /* get size of message */
yuuji@0 1051 if (!elt->day) { /* set internaldate to file date if needed */
yuuji@0 1052 struct tm *tm = gmtime (&sbuf.st_mtime);
yuuji@0 1053 elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
yuuji@0 1054 elt->year = tm->tm_year + 1900 - BASEYEAR;
yuuji@0 1055 elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
yuuji@0 1056 elt->seconds = tm->tm_sec;
yuuji@0 1057 elt->zhours = 0; elt->zminutes = 0;
yuuji@0 1058 }
yuuji@0 1059 d.fd = fd; /* set up file descriptor */
yuuji@0 1060 d.pos = 0; /* start of file */
yuuji@0 1061 d.chunk = LOCAL->buf;
yuuji@0 1062 d.chunksize = CHUNKSIZE;
yuuji@0 1063 /* kludge; mh_append would just strip CRs */
yuuji@0 1064 INIT (&st,fd_string,&d,sbuf.st_size);
yuuji@0 1065 /* init flag string */
yuuji@0 1066 flags[0] = flags[1] = '\0';
yuuji@0 1067 if (elt->seen) strcat (flags," \\Seen");
yuuji@0 1068 if (elt->deleted) strcat (flags," \\Deleted");
yuuji@0 1069 if (elt->flagged) strcat (flags," \\Flagged");
yuuji@0 1070 if (elt->answered) strcat (flags," \\Answered");
yuuji@0 1071 if (elt->draft) strcat (flags," \\Draft");
yuuji@0 1072 flags[0] = '('; /* open list */
yuuji@0 1073 strcat (flags,")"); /* close list */
yuuji@0 1074 mail_date (date,elt); /* generate internal date */
yuuji@0 1075 if (au) mail_parameters (NIL,SET_APPENDUID,NIL);
yuuji@0 1076 if ((ret = mail_append_full (NIL,mailbox,flags,date,&st)) &&
yuuji@0 1077 (options & CP_MOVE)) elt->deleted = T;
yuuji@0 1078 if (au) mail_parameters (NIL,SET_APPENDUID,(void *) au);
yuuji@0 1079 close (fd);
yuuji@0 1080 }
yuuji@0 1081 if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
yuuji@0 1082 mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
yuuji@0 1083 return ret; /* return success */
yuuji@0 1084 }
yuuji@0 1085
yuuji@0 1086 /* MH mail append message from stringstruct
yuuji@0 1087 * Accepts: MAIL stream
yuuji@0 1088 * destination mailbox
yuuji@0 1089 * append callback
yuuji@0 1090 * data for callback
yuuji@0 1091 * Returns: T if append successful, else NIL
yuuji@0 1092 */
yuuji@0 1093
yuuji@0 1094 long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
yuuji@0 1095 {
yuuji@0 1096 struct direct **names = NIL;
yuuji@0 1097 int fd;
yuuji@0 1098 char c,*flags,*date,*s,tmp[MAILTMPLEN];
yuuji@0 1099 STRING *message;
yuuji@0 1100 MESSAGECACHE elt;
yuuji@0 1101 FILE *df;
yuuji@0 1102 long i,size,last,nfiles;
yuuji@0 1103 long ret = LONGT;
yuuji@0 1104 /* default stream to prototype */
yuuji@0 1105 if (!stream) stream = &mhproto;
yuuji@0 1106 /* make sure valid mailbox */
yuuji@0 1107 if (!mh_isvalid (mailbox,tmp,NIL)) switch (errno) {
yuuji@0 1108 case ENOENT: /* no such file? */
yuuji@0 1109 if (!((!compare_cstring (mailbox,MHINBOX) ||
yuuji@0 1110 !compare_cstring (mailbox,"INBOX")) &&
yuuji@0 1111 (mh_file (tmp,MHINBOX) &&
yuuji@0 1112 dummy_create_path (stream,strcat (tmp,"/"),
yuuji@0 1113 get_dir_protection (mailbox))))) {
yuuji@0 1114 mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
yuuji@0 1115 return NIL;
yuuji@0 1116 }
yuuji@0 1117 /* falls through */
yuuji@0 1118 case 0: /* merely empty file? */
yuuji@0 1119 break;
yuuji@0 1120 case EINVAL:
yuuji@0 1121 sprintf (tmp,"Invalid MH-format mailbox name: %.80s",mailbox);
yuuji@0 1122 mm_log (tmp,ERROR);
yuuji@0 1123 return NIL;
yuuji@0 1124 default:
yuuji@0 1125 sprintf (tmp,"Not a MH-format mailbox: %.80s",mailbox);
yuuji@0 1126 mm_log (tmp,ERROR);
yuuji@0 1127 return NIL;
yuuji@0 1128 }
yuuji@0 1129 /* get first message */
yuuji@0 1130 if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
yuuji@0 1131 if ((nfiles = scandir (tmp,&names,mh_select,mh_numsort)) > 0) {
yuuji@0 1132 /* largest number */
yuuji@0 1133 last = atoi (names[nfiles-1]->d_name);
yuuji@0 1134 for (i = 0; i < nfiles; ++i) /* free directory */
yuuji@0 1135 fs_give ((void **) &names[i]);
yuuji@0 1136 }
yuuji@0 1137 else last = 0; /* no messages here yet */
yuuji@0 1138 if (s = (void *) names) fs_give ((void **) &s);
yuuji@0 1139
yuuji@0 1140 mm_critical (stream); /* go critical */
yuuji@0 1141 do {
yuuji@0 1142 if (!SIZE (message)) { /* guard against zero-length */
yuuji@0 1143 mm_log ("Append of zero-length message",ERROR);
yuuji@0 1144 ret = NIL;
yuuji@0 1145 break;
yuuji@0 1146 }
yuuji@0 1147 if (date) { /* want to preserve date? */
yuuji@0 1148 /* yes, parse date into an elt */
yuuji@0 1149 if (!mail_parse_date (&elt,date)) {
yuuji@0 1150 sprintf (tmp,"Bad date in append: %.80s",date);
yuuji@0 1151 mm_log (tmp,ERROR);
yuuji@0 1152 ret = NIL;
yuuji@0 1153 break;
yuuji@0 1154 }
yuuji@0 1155 }
yuuji@0 1156 mh_file (tmp,mailbox); /* build file name we will use */
yuuji@0 1157 sprintf (tmp + strlen (tmp),"/%ld",++last);
yuuji@0 1158 if (((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,
yuuji@0 1159 (long)mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0)||
yuuji@0 1160 !(df = fdopen (fd,"ab"))) {
yuuji@0 1161 sprintf (tmp,"Can't open append message: %s",strerror (errno));
yuuji@0 1162 mm_log (tmp,ERROR);
yuuji@0 1163 ret = NIL;
yuuji@0 1164 break;
yuuji@0 1165 }
yuuji@0 1166 /* copy the data w/o CR's */
yuuji@0 1167 for (size = 0,i = SIZE (message); i && ret; --i)
yuuji@0 1168 if (((c = SNX (message)) != '\015') && (putc (c,df) == EOF)) ret = NIL;
yuuji@0 1169 /* close the file */
yuuji@0 1170 if (!ret || fclose (df)) {
yuuji@0 1171 unlink (tmp); /* delete message */
yuuji@0 1172 sprintf (tmp,"Message append failed: %s",strerror (errno));
yuuji@0 1173 mm_log (tmp,ERROR);
yuuji@0 1174 ret = NIL;
yuuji@0 1175 }
yuuji@0 1176 if (ret) { /* set the date for this message */
yuuji@0 1177 if (date) mh_setdate (tmp,&elt);
yuuji@0 1178 /* get next message */
yuuji@0 1179 if (!(*af) (stream,data,&flags,&date,&message)) ret = NIL;
yuuji@0 1180 }
yuuji@0 1181 } while (ret && message);
yuuji@0 1182 mm_nocritical (stream); /* release critical */
yuuji@0 1183 if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
yuuji@0 1184 mm_log ("Can not return meaningful APPENDUID with this mailbox format",
yuuji@0 1185 WARN);
yuuji@0 1186 return ret;
yuuji@0 1187 }
yuuji@0 1188
yuuji@0 1189 /* Internal routines */
yuuji@0 1190
yuuji@0 1191
yuuji@0 1192 /* MH file name selection test
yuuji@0 1193 * Accepts: candidate directory entry
yuuji@0 1194 * Returns: T to use file name, NIL to skip it
yuuji@0 1195 */
yuuji@0 1196
yuuji@0 1197 int mh_select (struct direct *name)
yuuji@0 1198 {
yuuji@0 1199 char c;
yuuji@0 1200 char *s = name->d_name;
yuuji@0 1201 while (c = *s++) if (!isdigit (c)) return NIL;
yuuji@0 1202 return T;
yuuji@0 1203 }
yuuji@0 1204
yuuji@0 1205
yuuji@0 1206 /* MH file name comparision
yuuji@0 1207 * Accepts: first candidate directory entry
yuuji@0 1208 * second candidate directory entry
yuuji@0 1209 * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
yuuji@0 1210 */
yuuji@0 1211
yuuji@0 1212 int mh_numsort (const void *d1,const void *d2)
yuuji@0 1213 {
yuuji@0 1214 return atoi ((*(struct direct **) d1)->d_name) -
yuuji@0 1215 atoi ((*(struct direct **) d2)->d_name);
yuuji@0 1216 }
yuuji@0 1217
yuuji@0 1218
yuuji@0 1219 /* MH mail build file name
yuuji@0 1220 * Accepts: destination string
yuuji@0 1221 * source
yuuji@0 1222 * Returns: destination
yuuji@0 1223 */
yuuji@0 1224
yuuji@0 1225 char *mh_file (char *dst,char *name)
yuuji@0 1226 {
yuuji@0 1227 char *s;
yuuji@0 1228 char *path = mh_path (dst);
yuuji@0 1229 if (!path) fatal ("No mh path in mh_file()!");
yuuji@0 1230 /* INBOX becomes "inbox" in the MH path */
yuuji@0 1231 if (!compare_cstring (name,MHINBOX) || !compare_cstring (name,"INBOX"))
yuuji@0 1232 sprintf (dst,"%.900s/%.80s",path,MHINBOXDIR);
yuuji@0 1233 /* #mh names skip past prefix */
yuuji@0 1234 else if (*name == '#') sprintf (dst,"%.100s/%.900s",path,name + 4);
yuuji@0 1235 else mailboxfile (dst,name); /* all other names */
yuuji@0 1236 /* tie off unnecessary trailing / */
yuuji@0 1237 if ((s = strrchr (dst,'/')) && !s[1] && (s[-1] == '/')) *s = '\0';
yuuji@0 1238 return dst;
yuuji@0 1239 }
yuuji@0 1240
yuuji@0 1241 /* MH canonicalize name
yuuji@0 1242 * Accepts: buffer to write name
yuuji@0 1243 * reference
yuuji@0 1244 * pattern
yuuji@0 1245 * Returns: T if success, NIL if failure
yuuji@0 1246 */
yuuji@0 1247
yuuji@0 1248 long mh_canonicalize (char *pattern,char *ref,char *pat)
yuuji@0 1249 {
yuuji@0 1250 unsigned long i;
yuuji@0 1251 char *s,tmp[MAILTMPLEN];
yuuji@0 1252 if (ref && *ref) { /* have a reference */
yuuji@0 1253 strcpy (pattern,ref); /* copy reference to pattern */
yuuji@0 1254 /* # overrides mailbox field in reference */
yuuji@0 1255 if (*pat == '#') strcpy (pattern,pat);
yuuji@0 1256 /* pattern starts, reference ends, with / */
yuuji@0 1257 else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/'))
yuuji@0 1258 strcat (pattern,pat + 1); /* append, omitting one of the period */
yuuji@0 1259 else strcat (pattern,pat); /* anything else is just appended */
yuuji@0 1260 }
yuuji@0 1261 else strcpy (pattern,pat); /* just have basic name */
yuuji@0 1262 if (mh_isvalid (pattern,tmp,T)) {
yuuji@0 1263 /* count wildcards */
yuuji@0 1264 for (i = 0, s = pattern; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
yuuji@0 1265 /* success if not too many */
yuuji@0 1266 if (i <= MAXWILDCARDS) return LONGT;
yuuji@0 1267 mm_log ("Excessive wildcards in LIST/LSUB",ERROR);
yuuji@0 1268 }
yuuji@0 1269 return NIL;
yuuji@0 1270 }
yuuji@0 1271
yuuji@0 1272 /* Set date for message
yuuji@0 1273 * Accepts: file name
yuuji@0 1274 * elt containing date
yuuji@0 1275 */
yuuji@0 1276
yuuji@0 1277 void mh_setdate (char *file,MESSAGECACHE *elt)
yuuji@0 1278 {
yuuji@0 1279 time_t tp[2];
yuuji@0 1280 tp[0] = time (0); /* atime is now */
yuuji@0 1281 tp[1] = mail_longdate (elt); /* modification time */
yuuji@0 1282 utime (file,tp); /* set the times */
yuuji@0 1283 }

UW-IMAP'd extensions by yuuji