1 /* ========================================================================
2 * Copyright 2008-2011 Mark Crispin
3 * ========================================================================
7 * Program: Dummy routines
12 * Last Edited: 8 April 2011
14 * Previous versions of this file were:
16 * Copyright 1988-2007 University of Washington
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
22 * http://www.apache.org/licenses/LICENSE-2.0
31 extern int errno
; /* just in case */
40 /* Function prototypes */
42 DRIVER
*dummy_valid (char *name
);
43 void *dummy_parameters (long function
,void *value
);
44 void dummy_list_work (MAILSTREAM
*stream
,char *dir
,char *pat
,char *contents
,
46 long dummy_listed (MAILSTREAM
*stream
,char delimiter
,char *name
,
47 long attributes
,char *contents
);
48 long dummy_subscribe (MAILSTREAM
*stream
,char *mailbox
);
49 MAILSTREAM
*dummy_open (MAILSTREAM
*stream
);
50 void dummy_close (MAILSTREAM
*stream
,long options
);
51 long dummy_ping (MAILSTREAM
*stream
);
52 void dummy_check (MAILSTREAM
*stream
);
53 long dummy_expunge (MAILSTREAM
*stream
,char *sequence
,long options
);
54 long dummy_copy (MAILSTREAM
*stream
,char *sequence
,char *mailbox
,long options
);
55 long dummy_append (MAILSTREAM
*stream
,char *mailbox
,append_t af
,void *data
);
60 /* Driver dispatch used by MAIL */
62 DRIVER dummydriver
= {
63 "dummy", /* driver name */
64 DR_LOCAL
|DR_MAIL
, /* driver flags */
65 (DRIVER
*) NIL
, /* next driver */
66 dummy_valid
, /* mailbox is valid for us */
67 dummy_parameters
, /* manipulate parameters */
68 dummy_scan
, /* scan mailboxes */
69 dummy_list
, /* list mailboxes */
70 dummy_lsub
, /* list subscribed mailboxes */
71 dummy_subscribe
, /* subscribe to mailbox */
72 NIL
, /* unsubscribe from mailbox */
73 dummy_create
, /* create mailbox */
74 dummy_delete
, /* delete mailbox */
75 dummy_rename
, /* rename mailbox */
76 mail_status_default
, /* status of mailbox */
77 dummy_open
, /* open mailbox */
78 dummy_close
, /* close mailbox */
79 NIL
, /* fetch message "fast" attributes */
80 NIL
, /* fetch message flags */
81 NIL
, /* fetch overview */
82 NIL
, /* fetch message structure */
83 NIL
, /* fetch header */
85 NIL
, /* fetch message data */
86 NIL
, /* unique identifier */
87 NIL
, /* message number from UID */
88 NIL
, /* modify flags */
89 NIL
, /* per-message modify flags */
90 NIL
, /* search for message based on criteria */
91 NIL
, /* sort messages */
92 NIL
, /* thread messages */
93 dummy_ping
, /* ping mailbox to see if still alive */
94 dummy_check
, /* check for new messages */
95 dummy_expunge
, /* expunge deleted messages */
96 dummy_copy
, /* copy messages to another mailbox */
97 dummy_append
, /* append string message to mailbox */
98 NIL
/* garbage collect stream */
101 /* prototype stream */
102 MAILSTREAM dummyproto
= {&dummydriver
};
104 /* Dummy validate mailbox
105 * Accepts: mailbox name
106 * Returns: our driver if name is valid, NIL otherwise
109 DRIVER
*dummy_valid (char *name
)
111 char *s
,tmp
[MAILTMPLEN
];
113 /* must be valid local mailbox */
114 if (name
&& *name
&& (*name
!= '{') && (s
= mailboxfile (tmp
,name
))) {
115 /* indeterminate clearbox INBOX */
116 if (!*s
) return &dummydriver
;
117 else if (!stat (s
,&sbuf
)) switch (sbuf
.st_mode
& S_IFMT
) {
122 /* blackbox INBOX does not exist yet */
123 else if (!compare_cstring (name
,"INBOX")) return &dummydriver
;
129 /* Dummy manipulate driver parameters
130 * Accepts: function code
131 * function-dependent value
132 * Returns: function-dependent return value
135 void *dummy_parameters (long function
,void *value
)
138 switch ((int) function
) {
140 if (value
) ret
= dummy_file ((char *) value
,"INBOX");
146 /* Dummy scan mailboxes
147 * Accepts: mail stream
153 void dummy_scan (MAILSTREAM
*stream
,char *ref
,char *pat
,char *contents
)
156 char *s
,test
[MAILTMPLEN
],file
[MAILTMPLEN
];
158 if (!pat
|| !*pat
) { /* empty pattern? */
159 if (dummy_canonicalize (test
,ref
,"*")) {
160 /* tie off name at root */
161 if (s
= strchr (test
,'/')) *++s
= '\0';
163 dummy_listed (stream
,'/',test
,LATT_NOSELECT
,NIL
);
166 /* get canonical form of name */
167 else if (dummy_canonicalize (test
,ref
,pat
)) {
168 /* found any wildcards? */
169 if (s
= strpbrk (test
,"%*")) {
170 /* yes, copy name up to that point */
171 strncpy (file
,test
,i
= s
- test
);
172 file
[i
] = '\0'; /* tie off */
174 else strcpy (file
,test
); /* use just that name then */
175 if (s
= strrchr (file
,'/')){/* find directory name */
176 *++s
= '\0'; /* found, tie off at that point */
180 else if ((file
[0] == '~') || (file
[0] == '#')) s
= file
;
182 dummy_list_work (stream
,s
,test
,contents
,0);
183 /* always an INBOX */
184 if (pmatch ("INBOX",ucase (test
))) {
185 /* done if have a dirfmt INBOX */
186 for (drivers
= (DRIVER
*) mail_parameters (NIL
,GET_DRIVERS
,NIL
);
187 drivers
&& !(!(drivers
->flags
& DR_DISABLE
) &&
188 (drivers
->flags
& DR_DIRFMT
) &&
189 (*drivers
->valid
) ("INBOX")); drivers
= drivers
->next
);
190 /* list INBOX appropriately */
191 dummy_listed (stream
,drivers
? '/' : NIL
,"INBOX",
192 drivers
? NIL
: LATT_NOINFERIORS
,contents
);
198 /* Dummy list mailboxes
199 * Accepts: mail stream
204 void dummy_list (MAILSTREAM
*stream
,char *ref
,char *pat
)
206 dummy_scan (stream
,ref
,pat
,NIL
);
209 /* Dummy list subscribed mailboxes
210 * Accepts: mail stream
215 void dummy_lsub (MAILSTREAM
*stream
,char *ref
,char *pat
)
218 char *s
,*t
,test
[MAILTMPLEN
],tmp
[MAILTMPLEN
],tmpx
[MAILTMPLEN
];
219 int showuppers
= pat
[strlen (pat
) - 1] == '%';
220 /* get canonical form of name */
221 if (dummy_canonicalize (test
,ref
,pat
) && (s
= sm_read (tmpx
,&sdb
))) do
223 if (!compare_cstring (s
,"INBOX") &&
224 pmatch ("INBOX",ucase (strcpy (tmp
,test
))))
225 mm_lsub (stream
,NIL
,s
,LATT_NOINFERIORS
);
226 else if (pmatch_full (s
,test
,'/')) mm_lsub (stream
,'/',s
,NIL
);
227 else while (showuppers
&& (t
= strrchr (s
,'/'))) {
228 *t
= '\0'; /* tie off the name */
229 if (pmatch_full (s
,test
,'/')) mm_lsub (stream
,'/',s
,LATT_NOSELECT
);
232 /* until no more subscriptions */
233 while (s
= sm_read (tmpx
,&sdb
));
237 /* Dummy subscribe to mailbox
238 * Accepts: mail stream
239 * mailbox to add to subscription list
240 * Returns: T on success, NIL on failure
243 long dummy_subscribe (MAILSTREAM
*stream
,char *mailbox
)
245 char *s
,tmp
[MAILTMPLEN
];
247 /* must be valid local mailbox */
248 if ((s
= mailboxfile (tmp
,mailbox
)) && *s
&& !stat (s
,&sbuf
))
249 switch (sbuf
.st_mode
& S_IFMT
) {
250 case S_IFDIR
: /* allow but snarl */
251 sprintf (tmp
,"CLIENT BUG DETECTED: subscribe of non-mailbox directory %.80s",
253 MM_NOTIFY (stream
,tmp
,WARN
);
255 return sm_subscribe (mailbox
);
257 sprintf (tmp
,"Can't subscribe %.80s: not a mailbox",mailbox
);
262 /* Dummy list mailboxes worker routine
263 * Accepts: mail stream
264 * directory name to search
270 void dummy_list_work (MAILSTREAM
*stream
,char *dir
,char *pat
,char *contents
,
278 char tmp
[MAILTMPLEN
],path
[MAILTMPLEN
];
280 /* punt if bogus name */
281 if (!mailboxdir (tmp
,dir
,NIL
)) return;
282 if (dp
= opendir (tmp
)) { /* do nothing if can't open directory */
283 /* see if a non-namespace directory format */
284 for (drivers
= (DRIVER
*) mail_parameters (NIL
,GET_DRIVERS
,NIL
), dt
= NIL
;
285 dir
&& !dt
&& drivers
; drivers
= drivers
->next
)
286 if (!(drivers
->flags
& DR_DISABLE
) && (drivers
->flags
& DR_DIRFMT
) &&
287 (*drivers
->valid
) (dir
))
288 dt
= mail_parameters ((*drivers
->open
) (NIL
),GET_DIRFMTTEST
,NIL
);
289 /* list it if at top-level */
290 if (!level
&& dir
&& pmatch_full (dir
,pat
,'/') && !pmatch (dir
,"INBOX"))
291 dummy_listed (stream
,'/',dir
,dt
? NIL
: LATT_NOSELECT
,contents
);
293 /* scan directory, ignore . and .. */
294 if (!dir
|| dir
[(len
= strlen (dir
)) - 1] == '/') while (d
= readdir (dp
))
295 if ((!(dt
&& (*dt
) (d
->d_name
))) &&
296 ((d
->d_name
[0] != '.') ||
297 (((long) mail_parameters (NIL
,GET_HIDEDOTFILES
,NIL
)) ? NIL
:
298 (d
->d_name
[1] && (((d
->d_name
[1] != '.') || d
->d_name
[2]))))) &&
299 ((len
+ strlen (d
->d_name
)) <= NETMAXMBX
)) {
300 /* see if name is useful */
301 if (dir
) sprintf (tmp
,"%s%s",dir
,d
->d_name
);
302 else strcpy (tmp
,d
->d_name
);
303 /* make sure useful and can get info */
304 if ((pmatch_full (strcpy (path
,tmp
),pat
,'/') ||
305 pmatch_full (strcat (path
,"/"),pat
,'/') ||
306 dmatch (path
,pat
,'/')) &&
307 mailboxdir (path
,dir
,"x") && (len
= strlen (path
)) &&
308 strcpy (path
+len
-1,d
->d_name
) && !stat (path
,&sbuf
)) {
309 /* only interested in file type */
310 switch (sbuf
.st_mode
& S_IFMT
) {
311 case S_IFDIR
: /* directory? */
312 /* form with trailing / */
313 sprintf (path
,"%s/",tmp
);
314 /* skip listing if INBOX */
315 if (!pmatch (tmp
,"INBOX")) {
316 if (pmatch_full (tmp
,pat
,'/')) {
317 if (!dummy_listed (stream
,'/',tmp
,LATT_NOSELECT
,contents
))
320 /* try again with trailing / */
321 else if (pmatch_full (path
,pat
,'/') &&
322 !dummy_listed (stream
,'/',path
,LATT_NOSELECT
,contents
))
325 if (dmatch (path
,pat
,'/') &&
326 (level
< (long) mail_parameters (NIL
,GET_LISTMAXLEVEL
,NIL
)))
327 dummy_list_work (stream
,path
,pat
,contents
,level
+1);
329 case S_IFREG
: /* ordinary name */
330 /* Must use ctime for systems that don't update mtime properly */
331 if (pmatch_full (tmp
,pat
,'/') && compare_cstring (tmp
,"INBOX"))
332 dummy_listed (stream
,'/',tmp
,LATT_NOINFERIORS
+
333 ((sbuf
.st_size
&& (sbuf
.st_atime
< sbuf
.st_ctime
))?
334 LATT_MARKED
: LATT_UNMARKED
),contents
);
339 closedir (dp
); /* all done, flush directory */
343 /* Scan file for contents
344 * Accepts: driver to use
349 * Returns: NIL if contents not found, T if found
352 long scan_contents (DRIVER
*dtb
,char *name
,char *contents
,
353 unsigned long csiz
,unsigned long fsiz
)
355 scancontents_t sc
= dtb
?
356 (scancontents_t
) (*dtb
->parameters
) (GET_SCANCONTENTS
,NIL
) : NIL
;
357 return (*(sc
? sc
: dummy_scan_contents
)) (name
,contents
,csiz
,fsiz
);
361 /* Scan file for contents
366 * Returns: NIL if contents not found, T if found
369 #define BUFSIZE 4*MAILTMPLEN
371 long dummy_scan_contents (char *name
,char *contents
,unsigned long csiz
,
375 unsigned long ssiz
,bsiz
;
377 /* forget it if can't select or open */
378 if ((fd
= open (name
,O_RDONLY
,NIL
)) >= 0) {
379 /* get buffer including slop */
380 buf
= (char *) fs_get (BUFSIZE
+ (ssiz
= 4 * ((csiz
/ 4) + 1)) + 1);
381 memset (buf
,'\0',ssiz
); /* no slop area the first time */
382 while (fsiz
) { /* until end of file */
383 read (fd
,buf
+ssiz
,bsiz
= min (fsiz
,BUFSIZE
));
384 if (search ((unsigned char *) buf
,bsiz
+ssiz
,
385 (unsigned char *) contents
,csiz
)) break;
386 memcpy (buf
,buf
+BUFSIZE
,ssiz
);
387 fsiz
-= bsiz
; /* note that we read that much */
389 fs_give ((void **) &buf
); /* flush buffer */
390 close (fd
); /* finished with file */
391 if (fsiz
) return T
; /* found */
393 return NIL
; /* not found */
397 * Accepts: MAIL stream
398 * hierarchy delimiter
401 * contents to search before calling mm_list()
402 * Returns: NIL if should abort hierarchy search, else T (currently always)
405 long dummy_listed (MAILSTREAM
*stream
,char delimiter
,char *name
,
406 long attributes
,char *contents
)
415 char *s
,tmp
[MAILTMPLEN
];
416 if (!(attributes
& LATT_NOINFERIORS
) && mailboxdir (tmp
,name
,NIL
) &&
417 (dp
= opendir (tmp
))) { /* if not \NoInferiors */
418 /* locate dirfmttest if any */
419 for (d
= (DRIVER
*) mail_parameters (NIL
,GET_DRIVERS
,NIL
), dt
= NIL
;
420 !dt
&& d
; d
= d
->next
)
421 if (!(d
->flags
& DR_DISABLE
) && (d
->flags
& DR_DIRFMT
) &&
423 dt
= mail_parameters ((*d
->open
) (NIL
),GET_DIRFMTTEST
,NIL
);
424 /* scan directory for children */
425 for (nochild
= T
; nochild
&& (dr
= readdir (dp
)); )
426 if ((!(dt
&& (*dt
) (dr
->d_name
))) &&
427 ((dr
->d_name
[0] != '.') ||
428 (((long) mail_parameters (NIL
,GET_HIDEDOTFILES
,NIL
)) ? NIL
:
429 (dr
->d_name
[1] && ((dr
->d_name
[1] != '.') || dr
->d_name
[2])))))
431 attributes
|= nochild
? LATT_HASNOCHILDREN
: LATT_HASCHILDREN
;
432 closedir (dp
); /* all done, flush directory */
434 d
= NIL
; /* don't \NoSelect dir if it has a driver */
435 if ((attributes
& LATT_NOSELECT
) && (d
= mail_valid (NIL
,name
,NIL
)) &&
436 (d
!= &dummydriver
)) attributes
&= ~LATT_NOSELECT
;
437 if (!contents
|| /* notify main program */
438 (!(attributes
& LATT_NOSELECT
) && (csiz
= strlen (contents
)) &&
439 (s
= mailboxfile (tmp
,name
)) &&
440 (*s
|| (s
= mail_parameters (NIL
,GET_INBOXPATH
,tmp
))) &&
441 !stat (s
,&sbuf
) && (d
|| (csiz
<= sbuf
.st_size
)) &&
442 SAFE_SCAN_CONTENTS (d
,tmp
,contents
,csiz
,sbuf
.st_size
)))
443 mm_list (stream
,delimiter
,name
,attributes
);
447 /* Dummy create mailbox
448 * Accepts: mail stream
449 * mailbox name to create
450 * Returns: T on success, NIL on failure
453 long dummy_create (MAILSTREAM
*stream
,char *mailbox
)
455 char *s
,tmp
[MAILTMPLEN
];
458 if (!(compare_cstring (mailbox
,"INBOX") && (s
= dummy_file (tmp
,mailbox
)))) {
459 sprintf (tmp
,"Can't create %.80s: invalid name",mailbox
);
462 /* create the name, done if made directory */
463 else if ((ret
= dummy_create_path (stream
,tmp
,get_dir_protection(mailbox
)))&&
464 (s
= strrchr (s
,'/')) && !s
[1]) return T
;
465 return ret
? set_mbx_protections (mailbox
,tmp
) : NIL
;
469 * Accepts: mail stream
470 * path name to create
472 * Returns: T on success, NIL on failure
475 long dummy_create_path (MAILSTREAM
*stream
,char *path
,long dirmode
)
478 char c
,*s
,tmp
[MAILTMPLEN
];
481 char *t
= strrchr (path
,'/');
482 int wantdir
= t
&& !t
[1];
483 int mask
= umask (0);
484 if (wantdir
) *t
= '\0'; /* flush trailing delimiter for directory */
485 if (s
= strrchr (path
,'/')) { /* found superior to this name? */
486 c
= *++s
; /* remember first character of inferior */
487 *s
= '\0'; /* tie off to get just superior */
488 /* name doesn't exist, create it */
489 if ((stat (path
,&sbuf
) || ((sbuf
.st_mode
& S_IFMT
) != S_IFDIR
)) &&
490 !dummy_create_path (stream
,path
,dirmode
)) {
491 umask (mask
); /* restore mask */
494 *s
= c
; /* restore full name */
496 if (wantdir
) { /* want to create directory? */
497 ret
= !mkdir (path
,(int) dirmode
);
498 *t
= '/'; /* restore directory delimiter */
501 else if ((fd
= open (path
,O_WRONLY
|O_CREAT
|O_EXCL
,
502 (long) mail_parameters(NIL
,GET_MBXPROTECTION
,NIL
))) >=0)
504 if (!ret
) { /* error? */
505 sprintf (tmp
,"Can't create mailbox node %.80s: %.80s",path
,strerror (errno
));
508 umask (mask
); /* restore mask */
509 return ret
; /* return status */
512 /* Dummy delete mailbox
513 * Accepts: mail stream
514 * mailbox name to delete
515 * Returns: T on success, NIL on failure
518 long dummy_delete (MAILSTREAM
*stream
,char *mailbox
)
521 char *s
,tmp
[MAILTMPLEN
];
522 if (!(s
= dummy_file (tmp
,mailbox
))) {
523 sprintf (tmp
,"Can't delete - invalid name: %.80s",s
);
526 /* no trailing / (workaround BSD kernel bug) */
527 if ((s
= strrchr (tmp
,'/')) && !s
[1]) *s
= '\0';
528 if (stat (tmp
,&sbuf
) || ((sbuf
.st_mode
& S_IFMT
) == S_IFDIR
) ?
529 rmdir (tmp
) : unlink (tmp
)) {
530 sprintf (tmp
,"Can't delete mailbox %.80s: %.80s",mailbox
,strerror (errno
));
534 return T
; /* return success */
537 /* Mail rename mailbox
538 * Accepts: mail stream
541 * Returns: T on success, NIL on failure
544 long dummy_rename (MAILSTREAM
*stream
,char *old
,char *newname
)
547 char c
,*s
,tmp
[MAILTMPLEN
],mbx
[MAILTMPLEN
],oldname
[MAILTMPLEN
];
548 /* no trailing / allowed */
549 if (!dummy_file (oldname
,old
) || !(s
= dummy_file (mbx
,newname
)) ||
550 stat (oldname
,&sbuf
) || ((s
= strrchr (s
,'/')) && !s
[1] &&
551 ((sbuf
.st_mode
& S_IFMT
) != S_IFDIR
))) {
552 sprintf (mbx
,"Can't rename %.80s to %.80s: invalid name",old
,newname
);
556 if (s
) { /* found a directory delimiter? */
557 if (!s
[1]) *s
= '\0'; /* ignore trailing delimiter */
558 else { /* found superior to destination name? */
559 c
= *++s
; /* remember first character of inferior */
560 *s
= '\0'; /* tie off to get just superior */
561 /* name doesn't exist, create it */
562 if ((stat (mbx
,&sbuf
) || ((sbuf
.st_mode
& S_IFMT
) != S_IFDIR
)) &&
563 !dummy_create (stream
,mbx
)) return NIL
;
564 *s
= c
; /* restore full name */
567 /* rename of non-ex INBOX creates dest */
568 if (!compare_cstring (old
,"INBOX") && stat (oldname
,&sbuf
))
569 return dummy_create (NIL
,mbx
);
570 if (rename (oldname
,mbx
)) {
571 sprintf (tmp
,"Can't rename mailbox %.80s to %.80s: %.80s",old
,newname
,
576 return T
; /* return success */
580 * Accepts: stream to open
581 * Returns: stream on success, NIL on failure
584 MAILSTREAM
*dummy_open (MAILSTREAM
*stream
)
587 char err
[MAILTMPLEN
],tmp
[MAILTMPLEN
];
589 /* OP_PROTOTYPE call */
590 if (!stream
) return &dummyproto
;
591 err
[0] = '\0'; /* no error message yet */
592 /* can we open the file? */
593 if (!dummy_file (tmp
,stream
->mailbox
))
594 sprintf (err
,"Can't open this name: %.80s",stream
->mailbox
);
595 else if ((fd
= open (tmp
,O_RDONLY
,NIL
)) < 0) {
596 /* no, error unless INBOX */
597 if (compare_cstring (stream
->mailbox
,"INBOX"))
598 sprintf (err
,"%.80s: %.80s",strerror (errno
),stream
->mailbox
);
600 else { /* file had better be empty then */
601 fstat (fd
,&sbuf
); /* sniff at its size */
603 if ((sbuf
.st_mode
& S_IFMT
) != S_IFREG
)
604 sprintf (err
,"Can't open %.80s: not a selectable mailbox",
606 else if (sbuf
.st_size
) /* bogus format if non-empty */
607 sprintf (err
,"Can't open %.80s (file %.80s): not in valid mailbox format",
608 stream
->mailbox
,tmp
);
610 if (err
[0]) { /* if an error happened */
611 MM_LOG (err
,stream
->silent
? WARN
: ERROR
);
614 else if (!stream
->silent
) { /* only if silence not requested */
615 mail_exists (stream
,0); /* say there are 0 messages */
616 mail_recent (stream
,0); /* and certainly no recent ones! */
617 stream
->uid_validity
= time (0);
619 stream
->inbox
= T
; /* note that it's an INBOX */
620 return stream
; /* return success */
625 * Accepts: MAIL stream
629 void dummy_close (MAILSTREAM
*stream
,long options
)
631 /* return silently */
634 /* Dummy ping mailbox
635 * Accepts: MAIL stream
636 * Returns: T if stream alive, else NIL
639 long dummy_ping (MAILSTREAM
*stream
)
642 if (time (0) >= /* time to do another test? */
643 ((time_t) (stream
->gensym
+
644 (long) mail_parameters (NIL
,GET_SNARFINTERVAL
,NIL
)))) {
645 /* has mailbox format changed? */
646 if ((test
= mail_open (NIL
,stream
->mailbox
,OP_PROTOTYPE
)) &&
647 (test
->dtb
!= stream
->dtb
) &&
648 (test
= mail_open (NIL
,stream
->mailbox
,NIL
))) {
649 /* preserve some resources */
650 test
->original_mailbox
= stream
->original_mailbox
;
651 stream
->original_mailbox
= NIL
;
652 test
->sparep
= stream
->sparep
;
653 stream
->sparep
= NIL
;
654 test
->sequence
= stream
->sequence
;
655 mail_close ((MAILSTREAM
*) /* flush resources used by dummy stream */
656 memcpy (fs_get (sizeof (MAILSTREAM
)),stream
,
657 sizeof (MAILSTREAM
)));
658 /* swap the streams */
659 memcpy (stream
,test
,sizeof (MAILSTREAM
));
660 fs_give ((void **) &test
);/* flush test now that copied */
661 /* make sure application knows */
662 mail_exists (stream
,stream
->recent
= stream
->nmsgs
);
664 /* still hasn't changed */
665 else stream
->gensym
= time (0);
671 /* Dummy check mailbox
672 * Accepts: MAIL stream
673 * No-op for readonly files, since read/writer can expunge it from under us!
676 void dummy_check (MAILSTREAM
*stream
)
678 dummy_ping (stream
); /* invoke ping */
682 /* Dummy expunge mailbox
683 * Accepts: MAIL stream
684 * sequence to expunge if non-NIL
689 long dummy_expunge (MAILSTREAM
*stream
,char *sequence
,long options
)
694 /* Dummy copy message(s)
695 * Accepts: MAIL stream
697 * destination mailbox
699 * Returns: T if copy successful, else NIL
702 long dummy_copy (MAILSTREAM
*stream
,char *sequence
,char *mailbox
,long options
)
704 if ((options
& CP_UID
) ? mail_uid_sequence (stream
,sequence
) :
705 mail_sequence (stream
,sequence
)) fatal ("Impossible dummy_copy");
710 /* Dummy append message string
711 * Accepts: mail stream
712 * destination mailbox
713 * append callback function
715 * Returns: T on success, NIL on failure
718 long dummy_append (MAILSTREAM
*stream
,char *mailbox
,append_t af
,void *data
)
723 char tmp
[MAILTMPLEN
];
724 MAILSTREAM
*ts
= default_proto (T
);
725 /* append to INBOX? */
726 if (!compare_cstring (mailbox
,"INBOX")) {
727 /* yes, if no empty proto try creating */
728 if (!ts
&& !(*(ts
= default_proto (NIL
))->dtb
->create
) (ts
,"INBOX"))
731 else if (dummy_file (tmp
,mailbox
) && ((fd
= open (tmp
,O_RDONLY
,NIL
)) < 0)) {
732 if ((e
= errno
) == ENOENT
) /* failed, was it no such file? */
733 MM_NOTIFY (stream
,"[TRYCREATE] Must create mailbox before append",NIL
);
734 sprintf (tmp
,"%.80s: %.80s",strerror (e
),mailbox
);
735 MM_LOG (tmp
,ERROR
); /* pass up error */
736 return NIL
; /* always fails */
738 else if (fd
>= 0) { /* found file? */
739 fstat (fd
,&sbuf
); /* get its size */
740 close (fd
); /* toss out the fd */
741 if (sbuf
.st_size
) ts
= NIL
; /* non-empty file? */
743 if (ts
) return (*ts
->dtb
->append
) (stream
,mailbox
,af
,data
);
744 sprintf (tmp
,"Indeterminate mailbox format: %.80s",mailbox
);
749 /* Dummy mail generate file string
750 * Accepts: temporary buffer to write into
751 * mailbox name string
752 * Returns: local file string or NIL if failure
755 char *dummy_file (char *dst
,char *name
)
757 char *s
= mailboxfile (dst
,name
);
758 /* return our standard inbox */
759 return (s
&& !*s
) ? strcpy (dst
,sysinbox ()) : s
;
763 /* Dummy canonicalize name
764 * Accepts: buffer to write name
767 * Returns: T if success, NIL if failure
770 long dummy_canonicalize (char *tmp
,char *ref
,char *pat
)
774 if (ref
) { /* preliminary reference check */
775 if (*ref
== '{') return NIL
;/* remote reference not allowed */
776 else if (!*ref
) ref
= NIL
; /* treat empty reference as no reference */
779 case '#': /* namespace name */
780 if (mailboxfile (tmp
,pat
)) strcpy (tmp
,pat
);
781 else return NIL
; /* unknown namespace */
783 case '{': /* remote names not allowed */
785 case '/': /* rooted name */
786 case '~': /* home directory name */
787 if (!ref
|| (*ref
!= '#')) {/* non-namespace reference? */
788 strcpy (tmp
,pat
); /* yes, ignore */
792 default: /* apply reference for all other names */
793 if (!ref
) strcpy (tmp
,pat
); /* just copy if no namespace */
794 else if ((*ref
!= '#') || mailboxfile (tmp
,ref
)) {
795 /* wants root of name? */
796 if (*pat
== '/') strcpy (strchr (strcpy (tmp
,ref
),'/'),pat
);
797 /* otherwise just append */
798 else sprintf (tmp
,"%s%s",ref
,pat
);
800 else return NIL
; /* unknown namespace */
802 /* count wildcards */
803 for (i
= 0, s
= tmp
; *s
; *s
++) if ((*s
== '*') || (*s
== '%')) ++i
;
804 if (i
> MAXWILDCARDS
) { /* ridiculous wildcarding? */
805 MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR
);