1 /* ========================================================================
2 * Copyright 2008-2011 Mark Crispin
3 * ========================================================================
4 */
5
6 /*
7 * Program: Dummy routines
8 *
9 * Author: Mark Crispin
10 *
11 * Date: 9 May 1991
12 * Last Edited: 8 April 2011
13 *
14 * Previous versions of this file were:
15 *
16 * Copyright 1988-2007 University of Washington
17 *
18 * Licensed under the Apache License, Version 2.0 (the "License");
19 * you may not use this file except in compliance with the License.
20 * You may obtain a copy of the License at
21 *
22 * http://www.apache.org/licenses/LICENSE-2.0
23 *
24 */
25
26
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <errno.h>
30 extern int errno; /* just in case */
31 #include "mail.h"
32 #include "osdep.h"
33 #include <pwd.h>
34 #include <sys/stat.h>
35 #include "dummy.h"
36 #include "misc.h"
37
38 /* Function prototypes */
39
40 DRIVER *dummy_valid (char *name);
41 void *dummy_parameters (long function,void *value);
42 void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
43 long level);
44 long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
45 long attributes,char *contents);
46 long dummy_subscribe (MAILSTREAM *stream,char *mailbox);
47 MAILSTREAM *dummy_open (MAILSTREAM *stream);
48 void dummy_close (MAILSTREAM *stream,long options);
49 long dummy_ping (MAILSTREAM *stream);
50 void dummy_check (MAILSTREAM *stream);
51 long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
52 long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
53 long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
54
55 /* Dummy routines */
56
57
58 /* Driver dispatch used by MAIL */
59
60 DRIVER dummydriver = {
61 "dummy", /* driver name */
62 DR_LOCAL|DR_MAIL, /* driver flags */
63 (DRIVER *) NIL, /* next driver */
64 dummy_valid, /* mailbox is valid for us */
65 dummy_parameters, /* manipulate parameters */
66 dummy_scan, /* scan mailboxes */
67 dummy_list, /* list mailboxes */
68 dummy_lsub, /* list subscribed mailboxes */
69 dummy_subscribe, /* subscribe to mailbox */
70 NIL, /* unsubscribe from mailbox */
71 dummy_create, /* create mailbox */
72 dummy_delete, /* delete mailbox */
73 dummy_rename, /* rename mailbox */
74 mail_status_default, /* status of mailbox */
75 dummy_open, /* open mailbox */
76 dummy_close, /* close mailbox */
77 NIL, /* fetch message "fast" attributes */
78 NIL, /* fetch message flags */
79 NIL, /* fetch overview */
80 NIL, /* fetch message structure */
81 NIL, /* fetch header */
82 NIL, /* fetch text */
83 NIL, /* fetch message data */
84 NIL, /* unique identifier */
85 NIL, /* message number from UID */
86 NIL, /* modify flags */
87 NIL, /* per-message modify flags */
88 NIL, /* search for message based on criteria */
89 NIL, /* sort messages */
90 NIL, /* thread messages */
91 dummy_ping, /* ping mailbox to see if still alive */
92 dummy_check, /* check for new messages */
93 dummy_expunge, /* expunge deleted messages */
94 dummy_copy, /* copy messages to another mailbox */
95 dummy_append, /* append string message to mailbox */
96 NIL /* garbage collect stream */
97 };
98
99 /* prototype stream */
100 MAILSTREAM dummyproto = {&dummydriver};
101
102 /* Dummy validate mailbox
103 * Accepts: mailbox name
104 * Returns: our driver if name is valid, NIL otherwise
105 */
106
dummy_valid(char * name)107 DRIVER *dummy_valid (char *name)
108 {
109 char *s,tmp[MAILTMPLEN];
110 struct stat sbuf;
111 /* must be valid local mailbox */
112 if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) {
113 /* indeterminate clearbox INBOX */
114 if (!*s) return &dummydriver;
115 else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) {
116 case S_IFREG:
117 case S_IFDIR:
118 return &dummydriver;
119 }
120 /* blackbox INBOX does not exist yet */
121 else if (!compare_cstring (name,"INBOX")) return &dummydriver;
122 }
123 return NIL;
124 }
125
126
127 /* Dummy manipulate driver parameters
128 * Accepts: function code
129 * function-dependent value
130 * Returns: function-dependent return value
131 */
132
dummy_parameters(long function,void * value)133 void *dummy_parameters (long function,void *value)
134 {
135 void *ret = NIL;
136 switch ((int) function) {
137 case GET_INBOXPATH:
138 if (value) ret = dummy_file ((char *) value,"INBOX");
139 break;
140 }
141 return ret;
142 }
143
144 /* Dummy scan mailboxes
145 * Accepts: mail stream
146 * reference
147 * pattern to search
148 * string to scan
149 */
150
dummy_scan(MAILSTREAM * stream,char * ref,char * pat,char * contents)151 void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
152 {
153 DRIVER *drivers;
154 char *s,test[MAILTMPLEN],file[MAILTMPLEN];
155 long i;
156 if (!pat || !*pat) { /* empty pattern? */
157 if (dummy_canonicalize (test,ref,"*")) {
158 /* tie off name at root */
159 if (s = strchr (test,'/')) *++s = '\0';
160 else test[0] = '\0';
161 dummy_listed (stream,'/',test,LATT_NOSELECT,NIL);
162 }
163 }
164 /* get canonical form of name */
165 else if (dummy_canonicalize (test,ref,pat)) {
166 /* found any wildcards? */
167 if (s = strpbrk (test,"%*")) {
168 /* yes, copy name up to that point */
169 strncpy (file,test,i = s - test);
170 file[i] = '\0'; /* tie off */
171 }
172 else strcpy (file,test); /* use just that name then */
173 if (s = strrchr (file,'/')){/* find directory name */
174 *++s = '\0'; /* found, tie off at that point */
175 s = file;
176 }
177 /* silly case */
178 else if ((file[0] == '~') || (file[0] == '#')) s = file;
179 /* do the work */
180 dummy_list_work (stream,s,test,contents,0);
181 /* always an INBOX */
182 if (pmatch ("INBOX",ucase (test))) {
183 /* done if have a dirfmt INBOX */
184 for (drivers = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
185 drivers && !(!(drivers->flags & DR_DISABLE) &&
186 (drivers->flags & DR_DIRFMT) &&
187 (*drivers->valid) ("INBOX")); drivers = drivers->next);
188 /* list INBOX appropriately */
189 dummy_listed (stream,drivers ? '/' : NIL,"INBOX",
190 drivers ? NIL : LATT_NOINFERIORS,contents);
191 }
192 }
193 }
194
195
196 /* Dummy list mailboxes
197 * Accepts: mail stream
198 * reference
199 * pattern to search
200 */
201
dummy_list(MAILSTREAM * stream,char * ref,char * pat)202 void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
203 {
204 dummy_scan (stream,ref,pat,NIL);
205 }
206
207 /* Dummy list subscribed mailboxes
208 * Accepts: mail stream
209 * reference
210 * pattern to search
211 */
212
dummy_lsub(MAILSTREAM * stream,char * ref,char * pat)213 void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
214 {
215 void *sdb = NIL;
216 char *s,*t,test[MAILTMPLEN],tmp[MAILTMPLEN],tmpx[MAILTMPLEN];
217 int showuppers = pat[strlen (pat) - 1] == '%';
218 /* get canonical form of name */
219 if (dummy_canonicalize (test,ref,pat) && (s = sm_read (tmpx,&sdb))) do
220 if (*s != '{') {
221 if (!compare_cstring (s,"INBOX") &&
222 pmatch ("INBOX",ucase (strcpy (tmp,test))))
223 mm_lsub (stream,NIL,s,LATT_NOINFERIORS);
224 else if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL);
225 else while (showuppers && (t = strrchr (s,'/'))) {
226 *t = '\0'; /* tie off the name */
227 if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,LATT_NOSELECT);
228 }
229 }
230 /* until no more subscriptions */
231 while (s = sm_read (tmpx,&sdb));
232 }
233
234
235 /* Dummy subscribe to mailbox
236 * Accepts: mail stream
237 * mailbox to add to subscription list
238 * Returns: T on success, NIL on failure
239 */
240
dummy_subscribe(MAILSTREAM * stream,char * mailbox)241 long dummy_subscribe (MAILSTREAM *stream,char *mailbox)
242 {
243 char *s,tmp[MAILTMPLEN];
244 struct stat sbuf;
245 /* must be valid local mailbox */
246 if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf))
247 switch (sbuf.st_mode & S_IFMT) {
248 case S_IFDIR: /* allow but snarl */
249 sprintf (tmp,"CLIENT BUG DETECTED: subscribe of non-mailbox directory %.80s",
250 mailbox);
251 MM_NOTIFY (stream,tmp,WARN);
252 case S_IFREG:
253 return sm_subscribe (mailbox);
254 }
255 sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox);
256 MM_LOG (tmp,ERROR);
257 return NIL;
258 }
259
260 /* Dummy list mailboxes worker routine
261 * Accepts: mail stream
262 * directory name to search
263 * search pattern
264 * string to scan
265 * search level
266 */
267
dummy_list_work(MAILSTREAM * stream,char * dir,char * pat,char * contents,long level)268 void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
269 long level)
270 {
271 DRIVER *drivers;
272 dirfmttest_t dt;
273 DIR *dp;
274 struct direct *d;
275 struct stat sbuf;
276 char tmp[MAILTMPLEN],path[MAILTMPLEN];
277 size_t len = 0;
278 /* punt if bogus name */
279 if (!mailboxdir (tmp,dir,NIL)) return;
280 if (dp = opendir (tmp)) { /* do nothing if can't open directory */
281 /* see if a non-namespace directory format */
282 for (drivers = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL), dt = NIL;
283 dir && !dt && drivers; drivers = drivers->next)
284 if (!(drivers->flags & DR_DISABLE) && (drivers->flags & DR_DIRFMT) &&
285 (*drivers->valid) (dir))
286 dt = mail_parameters ((*drivers->open) (NIL),GET_DIRFMTTEST,NIL);
287 /* list it if at top-level */
288 if (!level && dir && pmatch_full (dir,pat,'/') && !pmatch (dir,"INBOX"))
289 dummy_listed (stream,'/',dir,dt ? NIL : LATT_NOSELECT,contents);
290
291 /* scan directory, ignore . and .. */
292 if (!dir || dir[(len = strlen (dir)) - 1] == '/') while (d = readdir (dp))
293 if ((!(dt && (*dt) (d->d_name))) &&
294 ((d->d_name[0] != '.') ||
295 (((long) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL :
296 (d->d_name[1] && (((d->d_name[1] != '.') || d->d_name[2]))))) &&
297 ((len + strlen (d->d_name)) <= NETMAXMBX)) {
298 /* see if name is useful */
299 if (dir) sprintf (tmp,"%s%s",dir,d->d_name);
300 else strcpy (tmp,d->d_name);
301 /* make sure useful and can get info */
302 if ((pmatch_full (strcpy (path,tmp),pat,'/') ||
303 pmatch_full (strcat (path,"/"),pat,'/') ||
304 dmatch (path,pat,'/')) &&
305 mailboxdir (path,dir,"x") && (len = strlen (path)) &&
306 strcpy (path+len-1,d->d_name) && !stat (path,&sbuf)) {
307 /* only interested in file type */
308 switch (sbuf.st_mode & S_IFMT) {
309 case S_IFDIR: /* directory? */
310 /* form with trailing / */
311 sprintf (path,"%s/",tmp);
312 /* skip listing if INBOX */
313 if (!pmatch (tmp,"INBOX")) {
314 if (pmatch_full (tmp,pat,'/')) {
315 if (!dummy_listed (stream,'/',tmp,LATT_NOSELECT,contents))
316 break;
317 }
318 /* try again with trailing / */
319 else if (pmatch_full (path,pat,'/') &&
320 !dummy_listed (stream,'/',path,LATT_NOSELECT,contents))
321 break;
322 }
323 if (dmatch (path,pat,'/') &&
324 (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
325 dummy_list_work (stream,path,pat,contents,level+1);
326 break;
327 case S_IFREG: /* ordinary name */
328 /* Must use ctime for systems that don't update mtime properly */
329 if (pmatch_full (tmp,pat,'/') && compare_cstring (tmp,"INBOX"))
330 dummy_listed (stream,'/',tmp,LATT_NOINFERIORS +
331 ((sbuf.st_size && (sbuf.st_atime < sbuf.st_ctime))?
332 LATT_MARKED : LATT_UNMARKED),contents);
333 break;
334 }
335 }
336 }
337 closedir (dp); /* all done, flush directory */
338 }
339 }
340
341 /* Scan file for contents
342 * Accepts: driver to use
343 * file name
344 * desired contents
345 * length of contents
346 * size of file
347 * Returns: NIL if contents not found, T if found
348 */
349
scan_contents(DRIVER * dtb,char * name,char * contents,unsigned long csiz,unsigned long fsiz)350 long scan_contents (DRIVER *dtb,char *name,char *contents,
351 unsigned long csiz,unsigned long fsiz)
352 {
353 scancontents_t sc = dtb ?
354 (scancontents_t) (*dtb->parameters) (GET_SCANCONTENTS,NIL) : NIL;
355 return (*(sc ? sc : dummy_scan_contents)) (name,contents,csiz,fsiz);
356 }
357
358
359 /* Scan file for contents
360 * Accepts: file name
361 * desired contents
362 * length of contents
363 * size of file
364 * Returns: NIL if contents not found, T if found
365 */
366
367 #define BUFSIZE 4*MAILTMPLEN
368
dummy_scan_contents(char * name,char * contents,unsigned long csiz,unsigned long fsiz)369 long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
370 unsigned long fsiz)
371 {
372 int fd;
373 unsigned long ssiz,bsiz;
374 char *buf;
375 /* forget it if can't select or open */
376 if ((fd = open (name,O_RDONLY,NIL)) >= 0) {
377 /* get buffer including slop */
378 buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1);
379 memset (buf,'\0',ssiz); /* no slop area the first time */
380 while (fsiz) { /* until end of file */
381 read (fd,buf+ssiz,bsiz = min (fsiz,BUFSIZE));
382 if (search ((unsigned char *) buf,bsiz+ssiz,
383 (unsigned char *) contents,csiz)) break;
384 memcpy (buf,buf+BUFSIZE,ssiz);
385 fsiz -= bsiz; /* note that we read that much */
386 }
387 fs_give ((void **) &buf); /* flush buffer */
388 close (fd); /* finished with file */
389 if (fsiz) return T; /* found */
390 }
391 return NIL; /* not found */
392 }
393
394 /* Mailbox found
395 * Accepts: MAIL stream
396 * hierarchy delimiter
397 * mailbox name
398 * attributes
399 * contents to search before calling mm_list()
400 * Returns: NIL if should abort hierarchy search, else T (currently always)
401 */
402
dummy_listed(MAILSTREAM * stream,char delimiter,char * name,long attributes,char * contents)403 long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
404 long attributes,char *contents)
405 {
406 DRIVER *d;
407 DIR *dp;
408 struct direct *dr;
409 dirfmttest_t dt;
410 unsigned long csiz;
411 struct stat sbuf;
412 int nochild;
413 char *s,tmp[MAILTMPLEN];
414 if (!(attributes & LATT_NOINFERIORS) && mailboxdir (tmp,name,NIL) &&
415 (dp = opendir (tmp))) { /* if not \NoInferiors */
416 /* locate dirfmttest if any */
417 for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL), dt = NIL;
418 !dt && d; d = d->next)
419 if (!(d->flags & DR_DISABLE) && (d->flags & DR_DIRFMT) &&
420 (*d->valid) (name))
421 dt = mail_parameters ((*d->open) (NIL),GET_DIRFMTTEST,NIL);
422 /* scan directory for children */
423 for (nochild = T; nochild && (dr = readdir (dp)); )
424 if ((!(dt && (*dt) (dr->d_name))) &&
425 ((dr->d_name[0] != '.') ||
426 (((long) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL :
427 (dr->d_name[1] && ((dr->d_name[1] != '.') || dr->d_name[2])))))
428 nochild = NIL;
429 attributes |= nochild ? LATT_HASNOCHILDREN : LATT_HASCHILDREN;
430 closedir (dp); /* all done, flush directory */
431 }
432 d = NIL; /* don't \NoSelect dir if it has a driver */
433 if ((attributes & LATT_NOSELECT) && (d = mail_valid (NIL,name,NIL)) &&
434 (d != &dummydriver)) attributes &= ~LATT_NOSELECT;
435 if (!contents || /* notify main program */
436 (!(attributes & LATT_NOSELECT) && (csiz = strlen (contents)) &&
437 (s = mailboxfile (tmp,name)) &&
438 (*s || (s = mail_parameters (NIL,GET_INBOXPATH,tmp))) &&
439 !stat (s,&sbuf) && (d || (csiz <= sbuf.st_size)) &&
440 SAFE_SCAN_CONTENTS (d,tmp,contents,csiz,sbuf.st_size)))
441 mm_list (stream,delimiter,name,attributes);
442 return T;
443 }
444
445 /* Dummy create mailbox
446 * Accepts: mail stream
447 * mailbox name to create
448 * Returns: T on success, NIL on failure
449 */
450
dummy_create(MAILSTREAM * stream,char * mailbox)451 long dummy_create (MAILSTREAM *stream,char *mailbox)
452 {
453 char *s,tmp[MAILTMPLEN];
454 long ret = NIL;
455 /* validate name */
456 if (!(compare_cstring (mailbox,"INBOX") && (s = dummy_file (tmp,mailbox)))) {
457 sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
458 MM_LOG (tmp,ERROR);
459 }
460 /* create the name, done if made directory */
461 else if ((ret = dummy_create_path (stream,tmp,get_dir_protection(mailbox)))&&
462 (s = strrchr (s,'/')) && !s[1]) return T;
463 return ret ? set_mbx_protections (mailbox,tmp) : NIL;
464 }
465
466 /* Dummy create path
467 * Accepts: mail stream
468 * path name to create
469 * directory mode
470 * Returns: T on success, NIL on failure
471 */
472
dummy_create_path(MAILSTREAM * stream,char * path,long dirmode)473 long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode)
474 {
475 struct stat sbuf;
476 char c,*s,tmp[MAILTMPLEN];
477 int fd;
478 long ret = NIL;
479 char *t = strrchr (path,'/');
480 int wantdir = t && !t[1];
481 int mask = umask (0);
482 if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */
483 if (s = strrchr (path,'/')) { /* found superior to this name? */
484 c = *++s; /* remember first character of inferior */
485 *s = '\0'; /* tie off to get just superior */
486 /* name doesn't exist, create it */
487 if ((stat (path,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
488 !dummy_create_path (stream,path,dirmode)) {
489 umask (mask); /* restore mask */
490 return NIL;
491 }
492 *s = c; /* restore full name */
493 }
494 if (wantdir) { /* want to create directory? */
495 ret = !mkdir (path,(int) dirmode);
496 *t = '/'; /* restore directory delimiter */
497 }
498 /* create file */
499 else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,
500 (long) mail_parameters(NIL,GET_MBXPROTECTION,NIL))) >=0)
501 ret = !close (fd);
502 if (!ret) { /* error? */
503 sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path,strerror (errno));
504 MM_LOG (tmp,ERROR);
505 }
506 umask (mask); /* restore mask */
507 return ret; /* return status */
508 }
509
510 /* Dummy delete mailbox
511 * Accepts: mail stream
512 * mailbox name to delete
513 * Returns: T on success, NIL on failure
514 */
515
dummy_delete(MAILSTREAM * stream,char * mailbox)516 long dummy_delete (MAILSTREAM *stream,char *mailbox)
517 {
518 struct stat sbuf;
519 char *s,tmp[MAILTMPLEN];
520 if (!(s = dummy_file (tmp,mailbox))) {
521 sprintf (tmp,"Can't delete - invalid name: %.80s",s);
522 MM_LOG (tmp,ERROR);
523 }
524 /* no trailing / (workaround BSD kernel bug) */
525 if ((s = strrchr (tmp,'/')) && !s[1]) *s = '\0';
526 if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ?
527 rmdir (tmp) : unlink (tmp)) {
528 sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno));
529 MM_LOG (tmp,ERROR);
530 return NIL;
531 }
532 return T; /* return success */
533 }
534
535 /* Mail rename mailbox
536 * Accepts: mail stream
537 * old mailbox name
538 * new mailbox name
539 * Returns: T on success, NIL on failure
540 */
541
dummy_rename(MAILSTREAM * stream,char * old,char * newname)542 long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
543 {
544 struct stat sbuf;
545 char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN];
546 /* no trailing / allowed */
547 if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) ||
548 stat (oldname,&sbuf) || ((s = strrchr (s,'/')) && !s[1] &&
549 ((sbuf.st_mode & S_IFMT) != S_IFDIR))) {
550 sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname);
551 MM_LOG (mbx,ERROR);
552 return NIL;
553 }
554 if (s) { /* found a directory delimiter? */
555 if (!s[1]) *s = '\0'; /* ignore trailing delimiter */
556 else { /* found superior to destination name? */
557 c = *++s; /* remember first character of inferior */
558 *s = '\0'; /* tie off to get just superior */
559 /* name doesn't exist, create it */
560 if ((stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
561 !dummy_create (stream,mbx)) return NIL;
562 *s = c; /* restore full name */
563 }
564 }
565 /* rename of non-ex INBOX creates dest */
566 if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf))
567 return dummy_create (NIL,mbx);
568 if (rename (oldname,mbx)) {
569 sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname,
570 strerror (errno));
571 MM_LOG (tmp,ERROR);
572 return NIL;
573 }
574 return T; /* return success */
575 }
576
577 /* Dummy open
578 * Accepts: stream to open
579 * Returns: stream on success, NIL on failure
580 */
581
dummy_open(MAILSTREAM * stream)582 MAILSTREAM *dummy_open (MAILSTREAM *stream)
583 {
584 int fd;
585 char err[MAILTMPLEN],tmp[MAILTMPLEN];
586 struct stat sbuf;
587 /* OP_PROTOTYPE call */
588 if (!stream) return &dummyproto;
589 err[0] = '\0'; /* no error message yet */
590 /* can we open the file? */
591 if (!dummy_file (tmp,stream->mailbox))
592 sprintf (err,"Can't open this name: %.80s",stream->mailbox);
593 else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
594 /* no, error unless INBOX */
595 if (compare_cstring (stream->mailbox,"INBOX"))
596 sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox);
597 }
598 else { /* file had better be empty then */
599 fstat (fd,&sbuf); /* sniff at its size */
600 close (fd);
601 if ((sbuf.st_mode & S_IFMT) != S_IFREG)
602 sprintf (err,"Can't open %.80s: not a selectable mailbox",
603 stream->mailbox);
604 else if (sbuf.st_size) /* bogus format if non-empty */
605 sprintf (err,"Can't open %.80s (file %.80s): not in valid mailbox format",
606 stream->mailbox,tmp);
607 }
608 if (err[0]) { /* if an error happened */
609 MM_LOG (err,stream->silent ? WARN : ERROR);
610 return NIL;
611 }
612 else if (!stream->silent) { /* only if silence not requested */
613 mail_exists (stream,0); /* say there are 0 messages */
614 mail_recent (stream,0); /* and certainly no recent ones! */
615 stream->uid_validity = time (0);
616 }
617 stream->inbox = T; /* note that it's an INBOX */
618 return stream; /* return success */
619 }
620
621
622 /* Dummy close
623 * Accepts: MAIL stream
624 * options
625 */
626
dummy_close(MAILSTREAM * stream,long options)627 void dummy_close (MAILSTREAM *stream,long options)
628 {
629 /* return silently */
630 }
631
632 /* Dummy ping mailbox
633 * Accepts: MAIL stream
634 * Returns: T if stream alive, else NIL
635 */
636
dummy_ping(MAILSTREAM * stream)637 long dummy_ping (MAILSTREAM *stream)
638 {
639 MAILSTREAM *test;
640 if (time (0) >= /* time to do another test? */
641 ((time_t) (stream->gensym +
642 (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL)))) {
643 /* has mailbox format changed? */
644 if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) &&
645 (test->dtb != stream->dtb) &&
646 (test = mail_open (NIL,stream->mailbox,NIL))) {
647 /* preserve some resources */
648 test->original_mailbox = stream->original_mailbox;
649 stream->original_mailbox = NIL;
650 test->sparep = stream->sparep;
651 stream->sparep = NIL;
652 test->sequence = stream->sequence;
653 mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */
654 memcpy (fs_get (sizeof (MAILSTREAM)),stream,
655 sizeof (MAILSTREAM)));
656 /* swap the streams */
657 memcpy (stream,test,sizeof (MAILSTREAM));
658 fs_give ((void **) &test);/* flush test now that copied */
659 /* make sure application knows */
660 mail_exists (stream,stream->recent = stream->nmsgs);
661 }
662 /* still hasn't changed */
663 else stream->gensym = time (0);
664 }
665 return T;
666 }
667
668
669 /* Dummy check mailbox
670 * Accepts: MAIL stream
671 * No-op for readonly files, since read/writer can expunge it from under us!
672 */
673
dummy_check(MAILSTREAM * stream)674 void dummy_check (MAILSTREAM *stream)
675 {
676 dummy_ping (stream); /* invoke ping */
677 }
678
679
680 /* Dummy expunge mailbox
681 * Accepts: MAIL stream
682 * sequence to expunge if non-NIL
683 * expunge options
684 * Returns: T, always
685 */
686
dummy_expunge(MAILSTREAM * stream,char * sequence,long options)687 long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
688 {
689 return LONGT;
690 }
691
692 /* Dummy copy message(s)
693 * Accepts: MAIL stream
694 * sequence
695 * destination mailbox
696 * options
697 * Returns: T if copy successful, else NIL
698 */
699
dummy_copy(MAILSTREAM * stream,char * sequence,char * mailbox,long options)700 long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
701 {
702 if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
703 mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
704 return NIL;
705 }
706
707
708 /* Dummy append message string
709 * Accepts: mail stream
710 * destination mailbox
711 * append callback function
712 * data for callback
713 * Returns: T on success, NIL on failure
714 */
715
dummy_append(MAILSTREAM * stream,char * mailbox,append_t af,void * data)716 long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
717 {
718 struct stat sbuf;
719 int fd = -1;
720 int e;
721 char tmp[MAILTMPLEN];
722 MAILSTREAM *ts = default_proto (T);
723 /* append to INBOX? */
724 if (!compare_cstring (mailbox,"INBOX")) {
725 /* yes, if no empty proto try creating */
726 if (!ts && !(*(ts = default_proto (NIL))->dtb->create) (ts,"INBOX"))
727 ts = NIL;
728 }
729 else if (dummy_file (tmp,mailbox) && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) {
730 if ((e = errno) == ENOENT) /* failed, was it no such file? */
731 MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
732 sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox);
733 MM_LOG (tmp,ERROR); /* pass up error */
734 return NIL; /* always fails */
735 }
736 else if (fd >= 0) { /* found file? */
737 fstat (fd,&sbuf); /* get its size */
738 close (fd); /* toss out the fd */
739 if (sbuf.st_size) ts = NIL; /* non-empty file? */
740 }
741 if (ts) return (*ts->dtb->append) (stream,mailbox,af,data);
742 sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox);
743 MM_LOG (tmp,ERROR);
744 return NIL;
745 }
746
747 /* Dummy mail generate file string
748 * Accepts: temporary buffer to write into
749 * mailbox name string
750 * Returns: local file string or NIL if failure
751 */
752
dummy_file(char * dst,char * name)753 char *dummy_file (char *dst,char *name)
754 {
755 char *s = mailboxfile (dst,name);
756 /* return our standard inbox */
757 return (s && !*s) ? strcpy (dst,sysinbox ()) : s;
758 }
759
760
761 /* Dummy canonicalize name
762 * Accepts: buffer to write name
763 * reference
764 * pattern
765 * Returns: T if success, NIL if failure
766 */
767
dummy_canonicalize(char * tmp,char * ref,char * pat)768 long dummy_canonicalize (char *tmp,char *ref,char *pat)
769 {
770 unsigned long i;
771 char *s;
772 if (ref) { /* preliminary reference check */
773 if (*ref == '{') return NIL;/* remote reference not allowed */
774 else if (!*ref) ref = NIL; /* treat empty reference as no reference */
775 }
776 switch (*pat) {
777 case '#': /* namespace name */
778 if (mailboxfile (tmp,pat)) strcpy (tmp,pat);
779 else return NIL; /* unknown namespace */
780 break;
781 case '{': /* remote names not allowed */
782 return NIL;
783 case '/': /* rooted name */
784 case '~': /* home directory name */
785 if (!ref || (*ref != '#')) {/* non-namespace reference? */
786 strcpy (tmp,pat); /* yes, ignore */
787 break;
788 }
789 /* fall through */
790 default: /* apply reference for all other names */
791 if (!ref) strcpy (tmp,pat); /* just copy if no namespace */
792 else if ((*ref != '#') || mailboxfile (tmp,ref)) {
793 /* wants root of name? */
794 if (*pat == '/') strcpy (strchr (strcpy (tmp,ref),'/'),pat);
795 /* otherwise just append */
796 else sprintf (tmp,"%s%s",ref,pat);
797 }
798 else return NIL; /* unknown namespace */
799 }
800 /* count wildcards */
801 for (i = 0, s = tmp; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
802 if (i > MAXWILDCARDS) { /* ridiculous wildcarding? */
803 MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
804 return NIL;
805 }
806 return T;
807 }
808