1 /* ========================================================================
2 * Copyright 2009 Mark Crispin
3 * ========================================================================
7 * Program: Mail utility
11 * Date: 2 February 1994
12 * Last Edited: 14 May 2009
14 * Previous versions of this file were
16 * Copyright 1988-2008 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
29 extern int errno
; /* just in case */
31 #ifdef SYSCONFIG /* defined in env_unix.h */
37 char *version
= "16"; /* edit number */
38 int debugp
= NIL
; /* flag saying debug */
39 int verbosep
= NIL
; /* flag saying verbose */
40 int rwcopyp
= NIL
; /* flag saying readwrite copy (for POP) */
41 int kwcopyp
= NIL
; /* flag saying keyword copy */
42 int ignorep
= NIL
; /* flag saying ignore keywords */
43 int critical
= NIL
; /* flag saying in critical code */
44 int trycreate
= NIL
; /* [TRYCREATE] seen */
45 char *suffix
= NIL
; /* suffer merge mode suffix text */
46 int ddelim
= -1; /* destination delimiter */
51 char *usage2
= "usage: %s %s\n\n%s\n";
52 char *usage3
= "usage: %s %s %s\n\n%s\n";
53 char *usgchk
= "check [MAILBOX]";
54 char *usgcre
= "create MAILBOX";
55 char *usgdel
= "delete MAILBOX";
56 char *usgren
= "rename SOURCE DESTINATION";
57 char *usgcpymov
= "[-rw[copy]] [-kw[copy]] [-ig[nore]] SOURCE DESTINATION";
58 char *usgappdel
= "[-rw[copy]] [-kw[copy]] [-ig[nore]] SOURCE DESTINATION";
59 char *usgprn
= "prune mailbox SEARCH_CRITERIA";
60 char *usgxfr
= "transfer [-rw[copy]] [-kw[copy]] [-ig[nore]] [-m[erge] m] SOURCE DEST";
62 char *stdsw
= "Standard switches valid with any command:\n\t[-d[ebug]] [-v[erbose]] [-u[ser] userid] [--]";
64 char *stdsw
= "Standard switches valid with any command:\n\t[-d[ebug]] [-v[erbose]]";
74 /* Function prototypes */
76 void ms_init (STRING
*s
,void *data
,unsigned long size
);
77 char ms_next (STRING
*s
);
78 void ms_setpos (STRING
*s
,unsigned long i
);
79 int main (int argc
,char *argv
[]);
80 SEARCHPGM
*prune_criteria (char *criteria
);
81 int criteria_number (unsigned long *number
,char **r
);
82 int mbxcopy (MAILSTREAM
*source
,MAILSTREAM
*dest
,char *dst
,int create
,int del
,
84 long mm_append (MAILSTREAM
*stream
,void *data
,char **flags
,char **date
,
90 typedef struct append_package
{
91 MAILSTREAM
*stream
; /* source stream */
92 unsigned long msgno
; /* current message number */
93 unsigned long msgmax
; /* maximum message number */
94 char *flags
; /* current flags */
95 char *date
; /* message internal date */
96 STRING
*message
; /* stringstruct of message */
100 /* Message string driver for message stringstructs */
102 STRINGDRIVER mstring
= {
103 ms_init
, /* initialize string structure */
104 ms_next
, /* get next byte in string structure */
105 ms_setpos
/* set position in string structure */
108 /* Initialize file string structure for file stringstruct
109 * Accepts: string structure
110 * pointer to message data structure
114 void ms_init (STRING
*s
,void *data
,unsigned long size
)
116 APPENDPACKAGE
*md
= (APPENDPACKAGE
*) data
;
117 s
->data
= data
; /* note stream/msgno and header length */
118 mail_fetch_header (md
->stream
,md
->msgno
,NIL
,NIL
,&s
->data1
,
119 FT_PREFETCHTEXT
|FT_PEEK
);
121 s
->size
= size
; /* message size */
122 #else /* This kludge is necessary because of broken IMAP servers (sigh!) */
123 mail_fetch_text (md
->stream
,md
->msgno
,NIL
,&s
->size
,FT_PEEK
);
124 s
->size
+= s
->data1
; /* header + body size */
130 /* Get next character from file stringstruct
131 * Accepts: string structure
132 * Returns: character, string structure chunk refreshed
135 char ms_next (STRING
*s
)
137 char c
= *s
->curpos
++; /* get next byte */
138 SETPOS (s
,GETPOS (s
)); /* move to next chunk */
139 return c
; /* return the byte */
143 /* Set string pointer position for file stringstruct
144 * Accepts: string structure
148 void ms_setpos (STRING
*s
,unsigned long i
)
150 APPENDPACKAGE
*md
= (APPENDPACKAGE
*) s
->data
;
151 if (i
< s
->data1
) { /* want header? */
152 s
->chunk
= mail_fetch_header (md
->stream
,md
->msgno
,NIL
,NIL
,NIL
,FT_PEEK
);
153 s
->chunksize
= s
->data1
; /* header length */
154 s
->offset
= 0; /* offset is start of message */
156 else if (i
< s
->size
) { /* want body */
157 s
->chunk
= mail_fetch_text (md
->stream
,md
->msgno
,NIL
,NIL
,FT_PEEK
);
158 s
->chunksize
= s
->size
- s
->data1
;
159 s
->offset
= s
->data1
; /* offset is end of header */
161 else { /* off end of message */
162 s
->chunk
= NIL
; /* make sure that we crack on this then */
163 s
->chunksize
= 1; /* make sure SNX cracks the right way... */
166 /* initial position and size */
167 s
->curpos
= s
->chunk
+ (i
-= s
->offset
);
168 s
->cursize
= s
->chunksize
- i
;
173 int main (int argc
,char *argv
[])
175 MAILSTREAM
*source
= NIL
;
176 MAILSTREAM
*dest
= NIL
;
178 char c
,*s
,*dp
,*t
,*t1
,tmp
[MAILTMPLEN
],mbx
[MAILTMPLEN
];
179 unsigned long m
,len
,curlen
,start
,last
;
187 char *pgm
= argc
? argv
[0] : "mailutil";
189 for (i
= 1; i
< argc
; i
++) {
190 s
= argv
[i
]; /* pick up argument */
192 if (moreswitchp
&& (*s
== '-')) {
193 if (!strcmp (s
,"-debug") || !strcmp (s
,"-d")) debugp
= T
;
194 else if (!strcmp (s
,"-verbose") || !strcmp (s
,"-v")) verbosep
= T
;
195 else if (!strcmp (s
,"-rwcopy") || !strcmp (s
,"-rw")) rwcopyp
= T
;
196 else if (!strcmp (s
,"-kwcopy") || !strcmp (s
,"-kw")) kwcopyp
= T
;
197 else if (!strcmp (s
,"-ignore") || !strcmp (s
,"-ig")) ignorep
= T
;
198 else if ((!strcmp (s
,"-merge") || !strcmp (s
,"-m")) && (++i
< argc
)) {
199 if (!strcmp (s
= argv
[i
],"prompt")) merge
= mPROMPT
;
200 else if (!strcmp (s
,"append")) merge
= mAPPEND
;
201 else if (!strncmp (s
,"suffix=",7) && s
[7]) {
203 suffix
= cpystr (s
+7);
206 printf ("unknown merge option: %s\n",s
);
212 else if ((!strcmp (s
,"-user") || !strcmp (s
,"-u")) && (++i
< argc
)) {
213 struct passwd
*pw
= getpwnam (s
= argv
[i
]);
215 printf ("unknown user id: %s\n",argv
[i
]);
218 else if (setuid (pw
->pw_uid
)) {
219 perror ("unable to change user id");
222 /* become victim in environment */
223 env_init (argv
[1], pw
->pw_dir
);
224 /* cancel restrictions since root call */
225 mail_parameters (NIL
,SET_RESTRICTIONS
,NIL
);
228 /* -- means no more switches, so mailbox
229 name can start with "-" */
230 else if ((s
[1] == '-') && !s
[2]) moreswitchp
= NIL
;
232 printf ("unknown switch: %s\n",s
);
236 else if (!cmd
) cmd
= s
; /* first non-switch is command */
237 else if (!src
) src
= s
; /* second non-switch is source */
238 else if (!dst
) dst
= s
; /* third non-switch is destination */
240 printf ("unknown argument: %s\n",s
);
244 if (kwcopyp
&& ignorep
) {
245 puts ("-kwcopy and -ignore are mutually exclusive");
248 if (!cmd
) cmd
= ""; /* prevent SEGV */
250 if (!strcmp (cmd
,"check")) { /* check for new messages */
251 if (!src
) src
= "INBOX";
252 if (dst
|| merge
|| rwcopyp
|| kwcopyp
|| ignorep
)
253 printf (usage2
,pgm
,usgchk
,stdsw
);
254 else if (mail_status (source
= (*src
== '{') ?
255 mail_open (NIL
,src
,OP_HALFOPEN
|
256 (debugp
? OP_DEBUG
: NIL
)) : NIL
,
257 src
,SA_MESSAGES
| SA_RECENT
| SA_UNSEEN
))
260 else if (!strcmp (cmd
,"create")) {
261 if (!src
|| dst
|| merge
|| rwcopyp
|| kwcopyp
|| ignorep
)
262 printf (usage2
,pgm
,usgcre
,stdsw
);
263 else if (mail_create (source
= (*src
== '{') ?
264 mail_open (NIL
,src
,OP_HALFOPEN
|
265 (debugp
? OP_DEBUG
: NIL
)) : NIL
,src
))
268 else if (!strcmp (cmd
,"delete")) {
269 if (!src
|| dst
|| merge
|| rwcopyp
|| kwcopyp
|| ignorep
)
270 printf (usage2
,pgm
,usgdel
,stdsw
);
271 else if (mail_delete (source
= (*src
== '{') ?
272 mail_open (NIL
,src
,OP_HALFOPEN
|
273 (debugp
? OP_DEBUG
: NIL
)) : NIL
,src
))
276 else if (!strcmp (cmd
,"rename")) {
277 if (!src
|| !dst
|| merge
|| rwcopyp
|| kwcopyp
|| ignorep
)
278 printf (usage2
,pgm
,usgren
,stdsw
);
279 else if (mail_rename (source
= (*src
== '{') ?
280 mail_open (NIL
,src
,OP_HALFOPEN
|
281 (debugp
? OP_DEBUG
: NIL
)) : NIL
,src
,dst
))
285 else if ((i
= !strcmp (cmd
,"move")) || !strcmp (cmd
,"copy")) {
286 if (!src
|| !dst
|| merge
) printf (usage3
,pgm
,cmd
,usgcpymov
,stdsw
);
287 else if (source
= mail_open (NIL
,src
,((i
|| rwcopyp
) ? NIL
: OP_READONLY
) |
288 (debugp
? OP_DEBUG
: NIL
))) {
289 dest
= NIL
; /* open destination stream if network */
290 if ((*dst
!= '{') || (dest
= mail_open (NIL
,dst
,OP_HALFOPEN
|
291 (debugp
? OP_DEBUG
: NIL
)))) {
292 if (mbxcopy (source
,dest
,dst
,T
,i
,merge
)) retcode
= 0;
296 else if ((i
= !strcmp (cmd
,"appenddelete")) || !strcmp (cmd
,"append")) {
297 if (!src
|| !dst
|| merge
) printf (usage3
,pgm
,cmd
,usgappdel
,stdsw
);
298 else if (source
= mail_open (NIL
,src
,((i
|| rwcopyp
) ? NIL
: OP_READONLY
) |
299 (debugp
? OP_DEBUG
: NIL
))) {
300 dest
= NIL
; /* open destination stream if network */
301 if ((*dst
!= '{') || (dest
= mail_open (NIL
,dst
,OP_HALFOPEN
|
302 (debugp
? OP_DEBUG
: NIL
)))) {
303 if (mbxcopy (source
,dest
,dst
,NIL
,i
,merge
)) retcode
= 0;
308 else if (!strcmp (cmd
,"prune")) {
309 if (!src
|| !dst
|| merge
|| rwcopyp
|| kwcopyp
|| ignorep
||
310 !(criteria
= prune_criteria (dst
))) printf (usage2
,pgm
,usgprn
,stdsw
);
311 else if ((source
= mail_open (NIL
,src
,(debugp
? OP_DEBUG
: NIL
))) &&
312 mail_search_full (source
,NIL
,criteria
,SE_FREE
)) {
313 for (m
= 1, s
= t
= NIL
, len
= start
= last
= 0; m
<= source
->nmsgs
; m
++)
314 if (mail_elt (source
,m
)->searched
) {
315 if (s
) { /* continuing a range? */
316 if (m
== last
+ 1) last
= m
;
317 else { /* no, end of previous range? */
318 if (last
!= start
) sprintf (t
,":%lu,%lu",last
,m
);
319 /* no, just this message */
320 else sprintf (t
,",%lu",m
);
321 start
= last
= m
; /* either way, start new range */
322 /* running out of space? */
323 if ((len
- (curlen
= (t
+= strlen (t
)) - s
)) < 20) {
324 fs_resize ((void **) &s
,len
+= MAILTMPLEN
);
325 t
= s
+ curlen
; /* relocate current pointer */
329 else { /* first time, start new buffer */
330 s
= (char *) fs_get (len
= MAILTMPLEN
);
331 sprintf (s
,"%lu",start
= last
= m
);
332 t
= s
+ strlen (s
); /* end of buffer */
335 /* finish last range if necessary */
336 if (last
!= start
) sprintf (t
,":%lu",last
);
337 if (s
) { /* delete/expunge any matching messages */
338 mail_flag (source
,s
,"\\Deleted",ST_SET
);
339 m
= source
->nmsgs
; /* get number of messages before purge */
340 mail_expunge (source
);
341 printf ("%lu message(s) purged\n",m
- source
->nmsgs
);
342 fs_give ((void **) &s
); /* flush buffer */
344 else puts ("No matching messages, so nothing purged");
345 source
= mail_close (source
);
349 else if (!strcmp (cmd
,"transfer")) {
350 if (!src
|| !dst
) printf (usage2
,pgm
,usgxfr
,stdsw
);
351 else if ((*src
== '{') && /* open source mailbox */
352 !(source
= mail_open (NIL
,src
,OP_HALFOPEN
|
353 (debugp
? OP_DEBUG
: NIL
))));
354 else if ((*dst
== '{') && /* open destination server */
355 !(dest
= mail_open (NIL
,dst
,OP_HALFOPEN
|
356 (debugp
? OP_DEBUG
: NIL
))));
357 else if (!(f
= tmpfile ())) puts ("can't open temporary file");
359 if (verbosep
) puts ("Listing mailboxes...");
360 if (dest
) strcpy (strchr (strcpy (tmp
,dest
->mailbox
),'}') + 1,
361 dp
= strchr (dst
,'}') + 1);
366 mail_list (dest
,tmp
,"");
367 rewind (f
); /* list all mailboxes matching prefix */
368 if (ddelim
< 0) { /* if server failed to give delimiter */
369 puts ("warning: unable to get destination hierarchy delimiter!");
370 ddelim
= 0; /* default to none */
372 if (source
) strcpy (strchr (strcpy (tmp
,source
->mailbox
),'}') + 1,
373 strchr (src
,'}') + 1);
374 else strcpy (tmp
,src
);
375 mail_list (source
,tmp
,"*");
377 /* read back mailbox names */
378 for (retcode
= 0; !retcode
&& (fgets (tmp
,MAILTMPLEN
-1,f
)); ) {
379 if (t
= strchr (tmp
+1,'\n')) *t
= '\0';
380 for (t
= mbx
,t1
= dest
? dest
->mailbox
: "",c
= NIL
; (c
!= '}') && *t1
;
382 for (t1
= dp
; *t1
; *t
++ = *t1
++);
383 /* point to name without delim or netspec */
384 t1
= source
? (strchr (tmp
+1,'}') + 1) : tmp
+ 1;
385 /* src and mbx have different delimiters? */
386 if (ddelim
&& (ddelim
!= tmp
[0]))
387 while (c
= *t1
++) { /* swap delimiters then */
388 if (c
== ddelim
) c
= tmp
[0] ? tmp
[0] : 'x';
389 else if (c
== tmp
[0]) c
= ddelim
;
393 else while (*t1
) *t
++ = *t1
++;
396 printf ("Copying %s\n => %s\n",tmp
+1,mbx
);
399 if (source
= mail_open (source
,tmp
+1,(debugp
? OP_DEBUG
: NIL
) |
400 (rwcopyp
? NIL
: OP_READONLY
))) {
401 if (!mbxcopy (source
,dest
,mbx
,T
,NIL
,merge
)) retcode
= 1;
402 if (source
->dtb
->flags
& DR_LOCAL
) source
= mail_close (source
);
404 else printf ("can't open source mailbox %s\n",tmp
+1);
410 printf ("%s version %s.%s\n\n",pgm
,CCLIENTVERSION
,version
);
411 printf (usage2
,pgm
,"command [switches] arguments",stdsw
);
412 printf ("\nCommands:\n %s\n",usgchk
);
413 puts (" ;; report number of messages and new messages");
414 printf (" %s\n",usgcre
);
415 puts (" ;; create new mailbox");
416 printf (" %s\n",usgdel
);
417 puts (" ;; delete existing mailbox");
418 printf (" %s\n",usgren
);
419 puts (" ;; rename mailbox to a new name");
420 printf (" copy %s\n",usgcpymov
);
421 printf (" move %s\n",usgcpymov
);
422 puts (" ;; create new mailbox and copy/move messages");
423 printf (" append %s\n",usgappdel
);
424 printf (" appenddelete %s\n",usgappdel
);
425 puts (" ;; copy/move messages to existing mailbox");
426 printf (" %s\n",usgprn
);
427 puts (" ;; prune mailbox of messages matching criteria");
428 printf (" %s\n",usgxfr
);
429 puts (" ;; copy source hierarchy to destination");
430 puts (" ;; -merge modes are prompt, append, or suffix=xxxx");
433 if (source
) mail_close (source
);
434 if (dest
) mail_close (dest
);
436 return retcode
; /* stupid compilers */
439 /* Pruning criteria, somewhat extended from mail_criteria()
441 * Returns: search program if parse successful, else NIL
444 SEARCHPGM
*prune_criteria (char *criteria
)
446 SEARCHPGM
*pgm
= NIL
;
447 char *criterion
,*r
,tmp
[MAILTMPLEN
];
449 if (criteria
) { /* only if criteria defined */
450 /* make writeable copy of criteria */
451 criteria
= cpystr (criteria
);
452 /* for each criterion */
453 for (pgm
= mail_newsearchpgm (), criterion
= strtok_r (criteria
," ",&r
);
454 criterion
; (criterion
= strtok_r (NIL
," ",&r
))) {
455 f
= NIL
; /* init then scan the criterion */
456 switch (*ucase (criterion
)) {
457 case 'A': /* possible ALL, ANSWERED */
458 if (!strcmp (criterion
+1,"LL")) f
= T
;
459 else if (!strcmp (criterion
+1,"NSWERED")) f
= pgm
->answered
= T
;
461 case 'B': /* possible BCC, BEFORE, BODY */
462 if (!strcmp (criterion
+1,"CC"))
463 f
= mail_criteria_string (&pgm
->bcc
,&r
);
464 else if (!strcmp (criterion
+1,"EFORE"))
465 f
= mail_criteria_date (&pgm
->before
,&r
);
466 else if (!strcmp (criterion
+1,"ODY"))
467 f
= mail_criteria_string (&pgm
->body
,&r
);
469 case 'C': /* possible CC */
470 if (!strcmp (criterion
+1,"C")) f
= mail_criteria_string (&pgm
->cc
,&r
);
472 case 'D': /* possible DELETED, DRAFT */
473 if (!strcmp (criterion
+1,"ELETED")) f
= pgm
->deleted
= T
;
474 else if (!strcmp (criterion
+1,"RAFT")) f
= pgm
->draft
= T
;
476 case 'F': /* possible FLAGGED, FROM */
477 if (!strcmp (criterion
+1,"LAGGED")) f
= pgm
->flagged
= T
;
478 else if (!strcmp (criterion
+1,"ROM"))
479 f
= mail_criteria_string (&pgm
->from
,&r
);
481 case 'K': /* possible KEYWORD */
482 if (!strcmp (criterion
+1,"EYWORD"))
483 f
= mail_criteria_string (&pgm
->keyword
,&r
);
485 case 'L': /* possible LARGER */
486 if (!strcmp (criterion
+1,"ARGER"))
487 f
= criteria_number (&pgm
->larger
,&r
);
489 case 'N': /* possible NEW */
490 if (!strcmp (criterion
+1,"EW")) f
= pgm
->recent
= pgm
->unseen
= T
;
492 case 'O': /* possible OLD, ON */
493 if (!strcmp (criterion
+1,"LD")) f
= pgm
->old
= T
;
494 else if (!strcmp (criterion
+1,"N"))
495 f
= mail_criteria_date (&pgm
->on
,&r
);
497 case 'R': /* possible RECENT */
498 if (!strcmp (criterion
+1,"ECENT")) f
= pgm
->recent
= T
;
500 case 'S': /* possible SEEN, SENT*, SINCE, SMALLER,
502 if (!strcmp (criterion
+1,"EEN")) f
= pgm
->seen
= T
;
503 else if (!strncmp (criterion
+1,"ENT",3)) {
504 if (!strcmp (criterion
+4,"BEFORE"))
505 f
= mail_criteria_date (&pgm
->sentbefore
,&r
);
506 else if (!strcmp (criterion
+4,"ON"))
507 f
= mail_criteria_date (&pgm
->senton
,&r
);
508 else if (!strcmp (criterion
+4,"SINCE"))
509 f
= mail_criteria_date (&pgm
->sentsince
,&r
);
511 else if (!strcmp (criterion
+1,"INCE"))
512 f
= mail_criteria_date (&pgm
->since
,&r
);
513 else if (!strcmp (criterion
+1,"MALLER"))
514 f
= criteria_number (&pgm
->smaller
,&r
);
515 else if (!strcmp (criterion
+1,"UBJECT"))
516 f
= mail_criteria_string (&pgm
->subject
,&r
);
518 case 'T': /* possible TEXT, TO */
519 if (!strcmp (criterion
+1,"EXT"))
520 f
= mail_criteria_string (&pgm
->text
,&r
);
521 else if (!strcmp (criterion
+1,"O"))
522 f
= mail_criteria_string (&pgm
->to
,&r
);
524 case 'U': /* possible UN* */
525 if (criterion
[1] == 'N') {
526 if (!strcmp (criterion
+2,"ANSWERED")) f
= pgm
->unanswered
= T
;
527 else if (!strcmp (criterion
+2,"DELETED")) f
= pgm
->undeleted
= T
;
528 else if (!strcmp (criterion
+2,"DRAFT")) f
= pgm
->undraft
= T
;
529 else if (!strcmp (criterion
+2,"FLAGGED")) f
= pgm
->unflagged
= T
;
530 else if (!strcmp (criterion
+2,"KEYWORD"))
531 f
= mail_criteria_string (&pgm
->unkeyword
,&r
);
532 else if (!strcmp (criterion
+2,"SEEN")) f
= pgm
->unseen
= T
;
535 default: /* we will barf below */
539 if (!f
) { /* if can't identify criterion */
540 sprintf (tmp
,"Unknown search criterion: %.30s",criterion
);
542 mail_free_searchpgm (&pgm
);
546 /* no longer need copy of criteria */
547 fs_give ((void **) &criteria
);
554 * Accepts: pointer to integer to return
555 * pointer to strtok state
556 * Returns: T if successful, else NIL
559 int criteria_number (unsigned long *number
,char **r
)
563 /* parse the date and return fn if OK */
564 int ret
= (mail_criteria_string (&s
,r
) &&
565 (*number
= strtoul ((char *) s
->text
.data
,&t
,10)) && !*t
) ?
567 if (s
) mail_free_stringlist (&s
);
572 * Accepts: stream open on source
573 * halfopen stream for destination or NIL
574 * destination mailbox name
575 * non-zero to create destination mailbox
576 * non-zero to delete messages from source after copying
578 * Returns: T if success, NIL if error
581 int mbxcopy (MAILSTREAM
*source
,MAILSTREAM
*dest
,char *dst
,int create
,int del
,
584 char *s
,tmp
[MAILTMPLEN
];
589 trycreate
= NIL
; /* no TRYCREATE yet */
590 if (create
) while (!mail_create (dest
,dst
) && (mode
!= mAPPEND
)) {
592 case mPROMPT
: /* prompt user for new name */
594 while (!tmp
[0]) { /* read name */
595 fputs ("alternative name: ",stdout
);
597 fgets (tmp
,MAILTMPLEN
-1,stdin
);
598 if (s
= strchr (tmp
,'\n')) *s
= '\0';
600 if (ndst
) fs_give ((void **) &ndst
);
603 case mSUFFIX
: /* try again with new suffix */
604 if (ndst
) fs_give ((void **) &ndst
);
605 sprintf (ndst
= (char *) fs_get (strlen (dst
) + strlen (suffix
) + 1),
607 printf ("retry to create %s\n",ndst
);
608 mode
= mPROMPT
; /* switch to prompt mode if name fails */
610 case NIL
: /* not merging */
613 if (ndst
) dst
= ndst
; /* if alternative name given, use it */
619 char *dummymsg
= "Date: Thu, 18 May 2006 00:00 -0700\r\nFrom: dummy@example.com\r\nSubject: dummy\r\n\r\ndummy\r\n";
620 for (i
= 0,len
= 0; i
< NUSERFLAGS
; ++i
)
621 if (source
->user_flags
[i
]) len
+= strlen (source
->user_flags
[i
]) + 1;
622 if (len
) { /* easy if no user flags to copy... */
624 char *tail
= "\\Deleted)";
625 char *flags
= (char *) fs_get (1 + len
+ strlen (tail
) + 1);
626 s
= flags
; *s
++ = '(';
627 for (i
= 0; i
< NUSERFLAGS
; ++i
) if (t
= source
->user_flags
[i
]) {
628 while (*t
) *s
++ = *t
++;
631 strcpy (s
,tail
); /* terminate flags list */
632 if ((dst
[0] == '#') && ((dst
[1] == 'D') || (dst
[1] == 'd')) &&
633 ((dst
[2] == 'R') || (dst
[2] == 'r')) &&
634 ((dst
[3] == 'I') || (dst
[3] == 'i')) &&
635 ((dst
[4] == 'V') || (dst
[4] == 'v')) &&
636 ((dst
[5] == 'E') || (dst
[5] == 'e')) &&
637 ((dst
[6] == 'R') || (dst
[6] == 'r')) && (dst
[7] == '.') &&
638 (t
= strchr (dst
+8,'/'))) ++t
;
640 INIT (&st
,mail_string
,dummymsg
,strlen (dummymsg
));
641 if (!(mail_append (dest
,dst
,&st
) &&
642 (dest
= mail_open (dest
,t
,debugp
? OP_DEBUG
: NIL
)))) {
643 fs_give ((void **) &flags
);
646 mail_setflag (dest
,"*",flags
);
648 fs_give ((void **) &flags
);
652 if (source
->nmsgs
) { /* non-empty source */
653 if (verbosep
) printf ("%s [%lu message(s)] => %s\n",
654 source
->mailbox
,source
->nmsgs
,dst
);
655 ap
.stream
= source
; /* prepare append package */
657 ap
.msgmax
= source
->nmsgs
;
658 ap
.flags
= ap
.date
= NIL
;
660 /* make sure we have all messages */
661 sprintf (tmp
,"1:%lu",ap
.msgmax
);
662 mail_fetchfast (source
,tmp
);
663 if (mail_append_multiple (dest
,dst
,mm_append
,(void *) &ap
)) {
664 --ap
.msgno
; /* make sure user knows it won */
665 if (verbosep
) printf ("[Ok %lu messages(s)]\n",ap
.msgno
);
666 if (del
&& ap
.msgno
) { /* delete source messages */
667 sprintf (tmp
,"1:%lu",ap
.msgno
);
668 mail_flag (source
,tmp
,"\\Deleted",ST_SET
);
669 /* flush moved messages */
670 mail_expunge (source
);
674 else if ((mode
== mAPPEND
) && trycreate
)
675 ret
= mbxcopy (source
,dest
,dst
,create
,del
,mPROMPT
);
676 else if (verbosep
) puts ("[Failed]");
678 else { /* empty source */
679 if (verbosep
) printf ("%s [empty] => %s\n",source
->mailbox
,dst
);
682 if (ndst
) fs_give ((void **) &ndst
);
687 * Accepts: mail stream
689 * pointer to return flags
690 * pointer to return date
691 * pointer to return message stringstruct
692 * Returns: T on success
695 long mm_append (MAILSTREAM
*stream
,void *data
,char **flags
,char **date
,
698 char *t
,*t1
,tmp
[MAILTMPLEN
];
701 APPENDPACKAGE
*ap
= (APPENDPACKAGE
*) data
;
702 *flags
= *date
= NIL
; /* assume no flags or date */
703 if (ap
->flags
) fs_give ((void **) &ap
->flags
);
704 if (ap
->date
) fs_give ((void **) &ap
->date
);
705 mail_gc (ap
->stream
,GC_TEXTS
);
706 if (++ap
->msgno
<= ap
->msgmax
) {
707 /* initialize flag string */
708 memset (t
= tmp
,0,MAILTMPLEN
);
709 /* output system flags */
710 if ((elt
= mail_elt (ap
->stream
,ap
->msgno
))->seen
) strcat (t
," \\Seen");
711 if (elt
->deleted
) strcat (t
," \\Deleted");
712 if (elt
->flagged
) strcat (t
," \\Flagged");
713 if (elt
->answered
) strcat (t
," \\Answered");
714 if (elt
->draft
) strcat (t
," \\Draft");
715 /* any user flags? */
716 if (!ignorep
&& (u
= elt
->user_flags
)) do
717 if ((t1
= ap
->stream
->user_flags
[find_rightmost_bit (&u
)]) &&
718 (MAILTMPLEN
- ((t
+= strlen (t
)) - tmp
)) > (long) (2 + strlen (t1
))){
719 *t
++ = ' '; /* space delimiter */
720 strcpy (t
,t1
); /* copy the user flag */
722 while (u
); /* until no more user flags */
723 *flags
= ap
->flags
= cpystr (tmp
+ 1);
724 *date
= ap
->date
= cpystr (mail_date (tmp
,elt
));
725 *message
= ap
->message
; /* message stringstruct */
726 INIT (ap
->message
,mstring
,(void *) ap
,elt
->rfc822_size
);
728 else *message
= NIL
; /* all done */
732 /* Co-routines from MAIL library */
735 /* Message matches a search
736 * Accepts: MAIL stream
740 void mm_searched (MAILSTREAM
*stream
,unsigned long msgno
)
746 /* Message exists (i.e. there are that many messages in the mailbox)
747 * Accepts: MAIL stream
751 void mm_exists (MAILSTREAM
*stream
,unsigned long number
)
758 * Accepts: MAIL stream
762 void mm_expunged (MAILSTREAM
*stream
,unsigned long number
)
768 /* Message flags update seen
769 * Accepts: MAIL stream
773 void mm_flags (MAILSTREAM
*stream
,unsigned long number
)
779 * Accepts: MAIL stream
780 * hierarchy delimiter
785 void mm_list (MAILSTREAM
*stream
,int delimiter
,char *name
,long attributes
)
787 /* note destination delimiter */
788 if (ddelim
< 0) ddelim
= delimiter
;
789 /* if got a selectable name */
790 else if (!(attributes
& LATT_NOSELECT
) && *name
)
791 fprintf (f
,"%c%s\n",delimiter
,name
);
795 /* Subscribe mailbox found
796 * Accepts: MAIL stream
797 * hierarchy delimiter
802 void mm_lsub (MAILSTREAM
*stream
,int delimiter
,char *name
,long attributes
)
809 * Accepts: MAIL stream
814 void mm_status (MAILSTREAM
*stream
,char *mailbox
,MAILSTATUS
*status
)
816 if (status
->recent
|| status
->unseen
)
817 printf ("%lu new message(s) (%lu unseen),",status
->recent
,status
->unseen
);
818 else fputs ("No new messages,",stdout
);
819 printf (" %lu total in %s\n",status
->messages
,mailbox
);
822 /* Notification event
823 * Accepts: MAIL stream
828 void mm_notify (MAILSTREAM
*stream
,char *string
,long errflg
)
830 if (!errflg
&& (string
[0] == '[') &&
831 ((string
[1] == 'T') || (string
[1] == 't')) &&
832 ((string
[2] == 'R') || (string
[2] == 'r')) &&
833 ((string
[3] == 'Y') || (string
[3] == 'y')) &&
834 ((string
[4] == 'C') || (string
[4] == 'c')) &&
835 ((string
[5] == 'R') || (string
[5] == 'r')) &&
836 ((string
[6] == 'E') || (string
[6] == 'e')) &&
837 ((string
[7] == 'A') || (string
[7] == 'a')) &&
838 ((string
[8] == 'T') || (string
[8] == 't')) &&
839 ((string
[9] == 'E') || (string
[9] == 'e')) &&
842 mm_log (string
,errflg
); /* just do mm_log action */
846 /* Log an event for the user to see
847 * Accepts: string to log
851 void mm_log (char *string
,long errflg
)
855 case NIL
: /* no error */
856 if (verbosep
) fprintf (stderr
,"[%s]\n",string
);
858 case PARSE
: /* parsing problem */
859 case WARN
: /* warning */
860 fprintf (stderr
,"warning: %s\n",string
);
862 case ERROR
: /* error */
864 fprintf (stderr
,"%s\n",string
);
870 /* Log an event to debugging telemetry
871 * Accepts: string to log
874 void mm_dlog (char *string
)
876 fprintf (stderr
,"%s\n",string
);
879 /* Get user name and password for this host
880 * Accepts: parse of network mailbox name
881 * where to return user name
882 * where to return password
886 void mm_login (NETMBX
*mb
,char *username
,char *password
,long trial
)
888 char *s
,tmp
[MAILTMPLEN
];
889 sprintf (s
= tmp
,"{%s/%s",mb
->host
,mb
->service
);
890 if (*mb
->user
) sprintf (tmp
+strlen (tmp
),"/user=%s",
891 strcpy (username
,mb
->user
));
892 if (*mb
->authuser
) sprintf (tmp
+strlen (tmp
),"/authuser=%s",mb
->authuser
);
893 if (*mb
->user
) strcat (s
= tmp
,"} password:");
895 printf ("%s} username: ",tmp
);
896 fgets (username
,NETMAXUSER
-1,stdin
);
897 username
[NETMAXUSER
-1] = '\0';
898 if (s
= strchr (username
,'\n')) *s
= '\0';
901 if(strlen (s
= getpass (s
)) < MAILTMPLEN
) strcpy (password
,s
);
905 /* About to enter critical code
909 void mm_critical (MAILSTREAM
*stream
)
911 critical
= T
; /* note in critical code */
915 /* About to exit critical code
919 void mm_nocritical (MAILSTREAM
*stream
)
921 critical
= NIL
; /* note not in critical code */
928 * flag indicating that mailbox may be clobbered
929 * Returns: T if user wants to abort
932 long mm_diskerror (MAILSTREAM
*stream
,long errcode
,long serious
)
938 /* Log a fatal error event
939 * Accepts: string to log
942 void mm_fatal (char *string
)
944 fprintf (stderr
,"FATAL: %s\n",string
);