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