* New version 2.21.999
[alpine.git] / alpine / rpdump.c
blob2c677d93ecf21c60a70641618bf05dbbb7985f02
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: rpdump.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2018 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "headers.h"
20 #include "radio.h" /* OE_PASSWD */
21 #include "../pith/util.h" /* IS_REMOTE() */
22 #include "../pith/remote.h" /* REMOTE_ABOOK_SUBTYPE... */
25 typedef enum {Pinerc, Abook, Sig, Smime, NotSet} RemoteType;
28 int parse_args(int, char **, int *, char **, char **);
29 RemoteType check_for_header_msg(MAILSTREAM *);
30 char *ptype(RemoteType);
31 char *spechdr(RemoteType);
32 char *err_desc(int);
33 int opt_enter(char *, int, char *, int *);
34 char *last_cmpnt(char *);
35 int wantto(char *, int, int);
38 char *ustr = "usage: %s [-f] -l Local_file -r Remote_folder\n";
39 int noshow_error = 0;
41 /* look for my_timer_period in pico directory for an explanation */
42 int my_timer_period = ((IDLE_TIMEOUT + 1)*1000);
45 #ifdef _WINDOWS
47 #undef main
49 app_main (argc, argv)
50 int argc;
51 char argv[];
55 #endif /* _WINDOWS */
59 * rpdump [-f] -l Local_file -r Remote_folder
61 * -f (skip check for special header)
63 * Note: We're not worrying about memory leaks.
65 int
66 main(argc, argv)
67 int argc;
68 char *argv[];
70 MAILSTREAM *stream = NULL;
71 FILE *fp;
72 int usage = 0;
73 char buf[10000];
74 char *local = NULL, *remote = NULL;
75 int force = 0;
76 BODY *body = NULL;
77 char *data, *p;
78 RemoteType rtype;
79 unsigned long i;
80 struct stat sbuf;
82 #include "../c-client/linkage.c"
84 if(parse_args(argc, argv, &force, &local, &remote)){
85 fprintf(stderr, ustr, argv[0]);
86 exit(-1);
89 if(!local || !*local){
90 fprintf(stderr, "No local file specified\n");
91 usage++;
94 if(!remote || !*remote){
95 fprintf(stderr, "No remote folder specified\n");
96 usage++;
99 if(usage){
100 fprintf(stderr, ustr, argv[0]);
101 exit(-1);
104 if(!IS_REMOTE(remote)){
105 fprintf(stderr,
106 "Remote folder name \"%s\" %s\n", remote,
107 (*remote != '{') ? "must begin with \"{\"" : "not valid");
108 exit(-1);
111 if(IS_REMOTE(local)){
112 fprintf(stderr, "Argument to -l (%s) must be a local filename", local);
113 exit(-1);
116 #ifdef _WINDOWS
117 if(stat(local, &sbuf))
118 #else
119 if(lstat(local, &sbuf))
120 #endif
122 if(errno == ENOENT){ /* File did not exist */
123 int fd;
125 /* create it */
126 if(((fd = open(local, O_CREAT|O_EXCL|O_WRONLY,0600)) < 0)
127 || (close(fd) != 0)){
128 fprintf(stderr, "%s: %s\n", local, err_desc(errno));
129 exit(-1);
132 /* now it exists! */
134 else{ /* unknown error */
135 fprintf(stderr, "%s: %s\n", local, err_desc(errno));
136 exit(-1);
139 else{ /* file exists */
141 /* is it a regular file? */
142 #ifdef S_ISREG
143 if(!S_ISREG(sbuf.st_mode))
144 #else
145 if(!(S_IFREG & sbuf.st_mode))
146 #endif
148 fprintf(stderr, "Only allowed to write to regular local files. Try another filename.\n");
149 exit(-1);
152 if(access(local, WRITE_ACCESS) == 0){
154 snprintf(buf, sizeof(buf), "Local file \"%s\" exists, overwrite it",
155 (p = last_cmpnt(local)) ? p : local);
156 if(wantto(buf, 'n', 'n') != 'y'){
157 fprintf(stderr, "Dump cancelled\n");
158 exit(-1);
161 else{
162 fprintf(stderr, "Local file \"%s\" is not writable\n", local);
163 exit(-1);
168 * Try opening the local file.
170 if((fp = fopen(local, "w")) == NULL){
171 fprintf(stderr, "Can't open \"%s\": %s\n", local, err_desc(errno));
172 mail_close(stream);
173 exit(-1);
177 * Try opening the remote folder.
179 stream = mail_open(NULL, remote, OP_READONLY);
180 if(!stream || stream->halfopen){
181 fprintf(stderr, "Remote folder \"%s\" is not readable\n", remote);
182 if(stream)
183 mail_close(stream);
185 exit(-1);
188 if(stream->nmsgs >= 2){
190 * There is a first message already. Check to see if it is one of
191 * our special header messages.
193 rtype = check_for_header_msg(stream);
194 if(!force && rtype == NotSet){
195 fprintf(stderr, "Folder \"%s\"\ndoes not appear to be an Alpine remote data folder.\nUse -f to force.\n", remote);
196 mail_close(stream);
197 exit(-1);
200 else if(stream->nmsgs == 1){
201 fprintf(stderr, "No data in remote folder to copy (only 1 message)\n");
202 mail_close(stream);
203 exit(-1);
205 else{
206 fprintf(stderr, "No data in remote folder to copy\n");
207 mail_close(stream);
208 exit(-1);
211 if(!mail_fetchstructure(stream, stream->nmsgs, &body)){
212 fprintf(stderr, "Can't access remote IMAP data\n");
213 mail_close(stream);
214 exit(-1);
217 if(!body ||
218 body->type != REMOTE_DATA_TYPE ||
219 !body->subtype ||
220 (!force && strucmp(body->subtype, spechdr(rtype)))){
221 fprintf(stderr, "Remote IMAP folder has wrong contents\n");
222 mail_close(stream);
223 exit(-1);
226 if(!mail_fetchenvelope(stream, stream->nmsgs)){
227 fprintf(stderr, "Can't access envelope in remote data\n");
228 mail_close(stream);
229 exit(-1);
232 data = mail_fetch_body(stream, stream->nmsgs, "1", &i, FT_PEEK);
234 p = data;
235 for(p = data; p < data+i; p++){
237 /* convert to unix newlines */
238 if(*p == '\r' && *(p+1) == '\n')
239 p++;
241 if(putc(*p, fp) == EOF){
242 fprintf(stderr,
243 "Error writing \"%s\": %s\n", local, err_desc(errno));
244 fclose(fp);
245 mail_close(stream);
246 exit(-1);
250 mail_close(stream);
251 fclose(fp);
253 fprintf(stderr,
254 "Remote folder is of type \"%s\", contents saved to \"%s\"\n",
255 ptype(rtype) ? ptype(rtype) : "unknown", local);
256 exit(0);
260 RemoteType
261 check_for_header_msg(stream)
262 MAILSTREAM *stream;
264 STRINGLIST *sl;
265 int ret = NotSet;
266 char *h, *try;
267 size_t len;
268 char *pinerc, *abook, *sig, *smime;
270 pinerc = spechdr(Pinerc);
271 abook = spechdr(Abook);
272 sig = spechdr(Sig);
273 smime = spechdr(Smime);
275 len = MAX(MAX(strlen(pinerc), strlen(abook)), MAX(strlen(sig), strlen(smime)));
277 sl = mail_newstringlist();
278 sl->text.data = (unsigned char *)fs_get((len+1) * sizeof(unsigned char));
279 try = pinerc;
280 strncpy((char *) sl->text.data, try, len);
281 sl->text.data[len] = '\0';
282 sl->text.size = strlen((char *) sl->text.data);
284 if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
286 if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
287 ret = Pinerc;
290 if(ret == NotSet){
291 try = abook;
292 strncpy((char *)sl->text.data, try, len);
293 sl->text.data[len] = '\0';
294 sl->text.size = strlen((char *) sl->text.data);
295 if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
297 if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
298 ret = Abook;
302 if(ret == NotSet){
303 try = sig;
304 strncpy((char *) sl->text.data, try, len);
305 sl->text.data[len] = '\0';
306 sl->text.size = strlen((char *) sl->text.data);
307 if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
309 if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
310 ret = Sig;
314 if(sl)
315 mail_free_stringlist(&sl);
317 if(pinerc)
318 fs_give((void **)&pinerc);
319 if(abook)
320 fs_give((void **)&abook);
321 if(sig)
322 fs_give((void **)&sig);
323 if(smime)
324 fs_give((void **)&smime);
326 return(ret);
330 char *
331 ptype(rtype)
332 RemoteType rtype;
334 char *ret = NULL;
336 switch(rtype){
337 case Pinerc:
338 ret = cpystr("pinerc");
339 break;
340 case Abook:
341 ret = cpystr("abook");
342 break;
343 case Sig:
344 ret = cpystr("sig");
345 break;
346 case Smime:
347 ret = cpystr("smime");
348 break;
349 default:
350 break;
353 return(ret);
357 char *
358 spechdr(rtype)
359 RemoteType rtype;
361 char *ret = NULL;
363 switch(rtype){
364 case Pinerc:
365 ret = cpystr(REMOTE_PINERC_SUBTYPE);
366 break;
367 case Abook:
368 ret = cpystr(REMOTE_ABOOK_SUBTYPE);
369 break;
370 case Sig:
371 ret = cpystr(REMOTE_SIG_SUBTYPE);
372 break;
373 case Smime:
374 ret = cpystr(REMOTE_SMIME_SUBTYPE);
375 break;
376 default:
377 break;
380 return(ret);
385 parse_args(argc, argv, force, local, remote)
386 int argc;
387 char **argv;
388 int *force;
389 char **local, **remote;
391 int ac;
392 char **av;
393 int c;
394 char *str;
395 int usage = 0;
397 ac = argc;
398 av = argv;
400 /* while more arguments with leading - */
401 Loop: while(--ac > 0 && **++av == '-'){
402 /* while more chars in this argument */
403 while(*++*av){
404 switch(c = **av){
405 case 'h':
406 usage++;
407 break;
408 case 'f':
409 (*force)++;
410 break;
412 case 'r': case 'l': /* string args */
413 if(*++*av)
414 str = *av;
415 else if(--ac)
416 str = *++av;
417 else{
418 fprintf(stderr, "missing argument for flag \"%c\"\n", c);
419 ++usage;
420 goto Loop;
423 switch(c){
424 case 'l':
425 if(str)
426 *local = str;
428 break;
429 case 'r':
430 if(str)
431 *remote = str;
433 break;
436 goto Loop;
438 default:
439 fprintf(stderr, "unknown flag \"%c\"\n", c);
440 ++usage;
441 break;
446 if(ac != 0)
447 usage++;
449 return(usage);
453 char *
454 err_desc(err)
455 int err;
457 return((char *) strerror(err));
461 void mm_exists(stream, number)
462 MAILSTREAM *stream;
463 unsigned long number;
468 void mm_expunged(stream, number)
469 MAILSTREAM *stream;
470 unsigned long number;
475 void mm_flags(stream, number)
476 MAILSTREAM *stream;
477 unsigned long number;
482 void mm_list(stream, delim, name, attrib)
483 MAILSTREAM *stream;
484 int delim;
485 char *name;
486 long attrib;
491 void mm_lsub(stream, delimiter, name, attributes)
492 MAILSTREAM *stream;
493 int delimiter;
494 char *name;
495 long attributes;
500 void mm_notify(stream, string, errflg)
501 MAILSTREAM *stream;
502 char *string;
503 long errflg;
505 mm_log(string, errflg);
509 void mm_log(string, errflg)
510 char *string;
511 long errflg;
513 if(noshow_error)
514 return;
516 switch(errflg){
517 case BYE:
518 case NIL:
519 break;
521 case PARSE:
522 fprintf(stderr, "PARSE: %s\n", string);
523 break;
525 case WARN:
526 fprintf(stderr, "WARN: %s\n", string);
527 break;
529 case ERROR:
530 fprintf(stderr, "ERROR: %s\n", string);
531 break;
533 default:
534 fprintf(stderr, "%s\n", string);
535 break;
540 void mm_login(mb, user, pwd, trial)
541 NETMBX *mb;
542 char *user;
543 char *pwd;
544 long trial;
546 char prompt[100], *last;
547 int i, j, goal, ugoal, len, rc, flags = 0;
548 #define NETMAXPASSWD 100
550 user[NETMAXUSER-1] = '\0';
552 if(trial == 0L){
553 if(mb->user && *mb->user){
554 strncpy(user, mb->user, NETMAXUSER);
555 user[NETMAXUSER-1] = '\0';
559 if(!*mb->user){
560 /* Dress up long hostnames */
561 snprintf(prompt, sizeof(prompt), "%sHOST: ",
562 (mb->sslflag||mb->tlsflag) ? "+ " : "");
563 len = strlen(prompt);
564 /* leave space for "HOST", "ENTER NAME", and 15 chars for input name */
565 goal = 80 - (len + 20 + MIN(15, 80/5));
566 last = " ENTER LOGIN NAME: ";
567 if(goal < 9){
568 last = " LOGIN: ";
569 if((goal += 13) < 9){
570 last += 1;
571 goal = 0;
575 if(goal){
576 for(i = len, j = 0;
577 i < sizeof(prompt) && (prompt[i] = mb->host[j]); i++, j++)
578 if(i == goal && mb->host[goal+1] && i < sizeof(prompt)){
579 strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
580 prompt[sizeof(prompt)-1] = '\0';
581 break;
584 else
585 i = 0;
587 strncpy(&prompt[i], last, sizeof(prompt)-i);
588 prompt[sizeof(prompt)-1] = '\0';
590 while(1) {
591 rc = opt_enter(user, NETMAXUSER, prompt, &flags);
592 if(rc != 4)
593 break;
596 if(rc == 1 || !user[0]) {
597 user[0] = '\0';
598 pwd[0] = '\0';
601 else
602 strncpy(user, mb->user, NETMAXUSER);
604 user[NETMAXUSER-1] = '\0';
605 pwd[NETMAXPASSWD-1] = '\0';
607 if(!user[0])
608 return;
611 /* Dress up long host/user names */
612 /* leave space for "HOST", "USER" "ENTER PWD", 12 for user 6 for pwd */
613 snprintf(prompt, sizeof(prompt), "%sHOST: ", (mb->sslflag||mb->tlsflag) ? "+ " : "");
614 len = strlen(prompt);
615 goal = strlen(mb->host);
616 ugoal = strlen(user);
617 if((i = 80 - (len + 8 + 18 + 6)) < 14){
618 goal = 0; /* no host! */
619 if((i = 80 - (6 + 18 + 6)) <= 6){
620 ugoal = 0; /* no user! */
621 if((i = 80 - (18 + 6)) <= 0)
622 i = 0;
624 else{
625 ugoal = i; /* whatever's left */
626 i = 0;
629 else
630 while(goal + ugoal > i)
631 if(goal > ugoal)
632 goal--;
633 else
634 ugoal--;
636 if(goal){
637 snprintf(prompt, sizeof(prompt), "%sHOST: ",
638 (mb->sslflag||mb->tlsflag) ? "+ " : "");
639 for(i = len, j = 0;
640 i < sizeof(prompt) && (prompt[i] = mb->host[j]); i++, j++)
641 if(i == goal && mb->host[goal+1] && i < sizeof(prompt)){
642 strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
643 prompt[sizeof(prompt)-1] = '\0';
644 break;
647 else
648 i = 0;
650 if(ugoal){
651 strncpy(&prompt[i], &" USER: "[i ? 0 : 2], sizeof(prompt)-i);
652 prompt[sizeof(prompt)-1] = '\0';
653 for(i += strlen(&prompt[i]), j = 0;
654 i < sizeof(prompt) && (prompt[i] = user[j]); i++, j++)
655 if(j == ugoal && user[ugoal+1] && i < sizeof(prompt)){
656 strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
657 prompt[sizeof(prompt)-1] = '\0';
658 break;
662 strncpy(&prompt[i], &" ENTER PASSWORD: "[i ? 0 : 8], sizeof(prompt)-i);
663 prompt[sizeof(prompt)-1] = '\0';
665 *pwd = '\0';
666 while(1) {
667 flags = OE_PASSWD;
668 rc = opt_enter(pwd, NETMAXPASSWD, prompt, &flags);
669 if(rc != 4)
670 break;
673 if(rc == 1 || !pwd[0]) {
674 user[0] = pwd[0] = '\0';
675 return;
680 void mm_critical(stream)
681 MAILSTREAM *stream;
686 void mm_nocritical(stream)
687 MAILSTREAM *stream;
692 long mm_diskerror(stream, errcode, serious)
693 MAILSTREAM *stream;
694 long errcode;
695 long serious;
697 return T;
701 void mm_fatal(string)
702 char *string;
704 fprintf(stderr, "%s\n", string);
708 void mm_searched(stream, msgno)
709 MAILSTREAM *stream;
710 unsigned long msgno;
715 void mm_status(stream, mailbox, status)
716 MAILSTREAM *stream;
717 char *mailbox;
718 MAILSTATUS *status;
722 void mm_dlog(string)
723 char *string;
725 fprintf(stderr, "%s\n", string);
730 opt_enter(string, field_len, prompt, flags)
731 char *string, *prompt;
732 int field_len;
733 int *flags;
735 char *pw;
736 int return_v = -10;
738 while(return_v == -10){
740 if(flags && *flags & OE_PASSWD){
741 if((pw = getpass(prompt)) != NULL){
742 if(strlen(pw) < field_len){
743 strncpy(string, pw, field_len);
744 string[field_len-1] = '\0';
745 return_v = 0;
747 else{
748 fputs("Password too long\n", stderr);
749 return_v = -1;
752 else
753 return_v = 1; /* cancel? */
755 else{
756 char *p;
758 fputs(prompt, stdout);
759 fgets(string, field_len, stdin);
760 string[field_len-1] = '\0';
761 if((p = strpbrk(string, "\r\n")) != NULL)
762 *p = '\0';
764 return_v = 0;
768 return(return_v);
771 char *
772 last_cmpnt(filename)
773 char *filename;
775 register char *p = NULL, *q = filename;
777 if(!q)
778 return(q);
780 while((q = strchr(q, '/')) != NULL)
781 if(*++q)
782 p = q;
784 return(p);
788 wantto(question, dflt, on_ctrl_C)
789 char *question;
790 int dflt, on_ctrl_C;
792 int ret = 0;
793 char rep[1000], *p;
795 while(!ret){
796 fprintf(stdout, "%s? [%c]:", question, dflt);
797 fgets(rep, sizeof(rep), stdin);
798 if((p = strpbrk(rep, "\r\n")) != NULL)
799 *p = '\0';
800 switch(*rep){
801 case 'Y':
802 case 'y':
803 ret = (int)'y';
804 break;
805 case 'N':
806 case 'n':
807 ret = (int)'n';
808 break;
809 case '\0':
810 ret = dflt;
811 break;
812 default:
813 break;
817 return ret;