Add support for tab-completion when selecting by rule
[alpine.git] / alpine / rpdump.c
blob680ab0d441fe9c68e6e568fde8ffb9844aeba210
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "headers.h"
16 #include "radio.h" /* OE_PASSWD */
17 #include "../pith/util.h" /* IS_REMOTE() */
18 #include "../pith/remote.h" /* REMOTE_ABOOK_SUBTYPE... */
21 typedef enum {Pinerc, Abook, Sig, Smime, NotSet} RemoteType;
24 int parse_args(int, char **, int *, char **, char **);
25 RemoteType check_for_header_msg(MAILSTREAM *);
26 char *ptype(RemoteType);
27 char *spechdr(RemoteType);
28 char *err_desc(int);
29 int opt_enter(char *, int, char *, int *);
30 char *last_cmpnt(char *);
31 int wantto(char *, int, int);
34 char *ustr = "usage: %s [-f] -l Local_file -r Remote_folder\n";
35 int noshow_error = 0;
37 /* look for my_timer_period in pico directory for an explanation */
38 int my_timer_period = ((IDLE_TIMEOUT + 1)*1000);
41 #ifdef _WINDOWS
43 #undef main
45 app_main (argc, argv)
46 int argc;
47 char argv[];
51 #endif /* _WINDOWS */
55 * rpdump [-f] -l Local_file -r Remote_folder
57 * -f (skip check for special header)
59 * Note: We're not worrying about memory leaks.
61 int
62 main(argc, argv)
63 int argc;
64 char *argv[];
66 MAILSTREAM *stream = NULL;
67 FILE *fp;
68 int usage = 0;
69 char buf[10000];
70 char *local = NULL, *remote = NULL;
71 int force = 0;
72 BODY *body = NULL;
73 char *data, *p;
74 RemoteType rtype;
75 unsigned long i;
76 struct stat sbuf;
78 #include "../c-client/linkage.c"
80 if(parse_args(argc, argv, &force, &local, &remote)){
81 fprintf(stderr, ustr, argv[0]);
82 exit(-1);
85 if(!local || !*local){
86 fprintf(stderr, "No local file specified\n");
87 usage++;
90 if(!remote || !*remote){
91 fprintf(stderr, "No remote folder specified\n");
92 usage++;
95 if(usage){
96 fprintf(stderr, ustr, argv[0]);
97 exit(-1);
100 if(!IS_REMOTE(remote)){
101 fprintf(stderr,
102 "Remote folder name \"%s\" %s\n", remote,
103 (*remote != '{') ? "must begin with \"{\"" : "not valid");
104 exit(-1);
107 if(IS_REMOTE(local)){
108 fprintf(stderr, "Argument to -l (%s) must be a local filename", local);
109 exit(-1);
112 #ifdef _WINDOWS
113 if(stat(local, &sbuf))
114 #else
115 if(lstat(local, &sbuf))
116 #endif
118 if(errno == ENOENT){ /* File did not exist */
119 int fd;
121 /* create it */
122 if(((fd = open(local, O_CREAT|O_EXCL|O_WRONLY,0600)) < 0)
123 || (close(fd) != 0)){
124 fprintf(stderr, "%s: %s\n", local, err_desc(errno));
125 exit(-1);
128 /* now it exists! */
130 else{ /* unknown error */
131 fprintf(stderr, "%s: %s\n", local, err_desc(errno));
132 exit(-1);
135 else{ /* file exists */
137 /* is it a regular file? */
138 #ifdef S_ISREG
139 if(!S_ISREG(sbuf.st_mode))
140 #else
141 if(!(S_IFREG & sbuf.st_mode))
142 #endif
144 fprintf(stderr, "Only allowed to write to regular local files. Try another filename.\n");
145 exit(-1);
148 if(access(local, WRITE_ACCESS) == 0){
150 snprintf(buf, sizeof(buf), "Local file \"%s\" exists, overwrite it",
151 (p = last_cmpnt(local)) ? p : local);
152 if(wantto(buf, 'n', 'n') != 'y'){
153 fprintf(stderr, "Dump cancelled\n");
154 exit(-1);
157 else{
158 fprintf(stderr, "Local file \"%s\" is not writable\n", local);
159 exit(-1);
164 * Try opening the local file.
166 if((fp = fopen(local, "w")) == NULL){
167 fprintf(stderr, "Can't open \"%s\": %s\n", local, err_desc(errno));
168 mail_close(stream);
169 exit(-1);
173 * Try opening the remote folder.
175 stream = mail_open(NULL, remote, OP_READONLY);
176 if(!stream || stream->halfopen){
177 fprintf(stderr, "Remote folder \"%s\" is not readable\n", remote);
178 if(stream)
179 mail_close(stream);
181 exit(-1);
184 if(stream->nmsgs >= 2){
186 * There is a first message already. Check to see if it is one of
187 * our special header messages.
189 rtype = check_for_header_msg(stream);
190 if(!force && rtype == NotSet){
191 fprintf(stderr, "Folder \"%s\"\ndoes not appear to be an Alpine remote data folder.\nUse -f to force.\n", remote);
192 mail_close(stream);
193 exit(-1);
196 else if(stream->nmsgs == 1){
197 fprintf(stderr, "No data in remote folder to copy (only 1 message)\n");
198 mail_close(stream);
199 exit(-1);
201 else{
202 fprintf(stderr, "No data in remote folder to copy\n");
203 mail_close(stream);
204 exit(-1);
207 if(!mail_fetchstructure(stream, stream->nmsgs, &body)){
208 fprintf(stderr, "Can't access remote IMAP data\n");
209 mail_close(stream);
210 exit(-1);
213 if(!body ||
214 body->type != REMOTE_DATA_TYPE ||
215 !body->subtype ||
216 (!force && strucmp(body->subtype, spechdr(rtype)))){
217 fprintf(stderr, "Remote IMAP folder has wrong contents\n");
218 mail_close(stream);
219 exit(-1);
222 if(!mail_fetchenvelope(stream, stream->nmsgs)){
223 fprintf(stderr, "Can't access envelope in remote data\n");
224 mail_close(stream);
225 exit(-1);
228 data = mail_fetch_body(stream, stream->nmsgs, "1", &i, FT_PEEK);
230 p = data;
231 for(p = data; p < data+i; p++){
233 /* convert to unix newlines */
234 if(*p == '\r' && *(p+1) == '\n')
235 p++;
237 if(putc(*p, fp) == EOF){
238 fprintf(stderr,
239 "Error writing \"%s\": %s\n", local, err_desc(errno));
240 fclose(fp);
241 mail_close(stream);
242 exit(-1);
246 mail_close(stream);
247 fclose(fp);
249 fprintf(stderr,
250 "Remote folder is of type \"%s\", contents saved to \"%s\"\n",
251 ptype(rtype) ? ptype(rtype) : "unknown", local);
252 exit(0);
256 RemoteType
257 check_for_header_msg(stream)
258 MAILSTREAM *stream;
260 STRINGLIST *sl;
261 int ret = NotSet;
262 char *h, *try;
263 size_t len;
264 char *pinerc, *abook, *sig, *smime;
266 pinerc = spechdr(Pinerc);
267 abook = spechdr(Abook);
268 sig = spechdr(Sig);
269 smime = spechdr(Smime);
271 len = MAX(MAX(strlen(pinerc), strlen(abook)), MAX(strlen(sig), strlen(smime)));
273 sl = mail_newstringlist();
274 sl->text.data = (unsigned char *)fs_get((len+1) * sizeof(unsigned char));
275 try = pinerc;
276 strncpy((char *) sl->text.data, try, len);
277 sl->text.data[len] = '\0';
278 sl->text.size = strlen((char *) sl->text.data);
280 if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
282 if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
283 ret = Pinerc;
286 if(ret == NotSet){
287 try = abook;
288 strncpy((char *)sl->text.data, try, len);
289 sl->text.data[len] = '\0';
290 sl->text.size = strlen((char *) sl->text.data);
291 if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
293 if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
294 ret = Abook;
298 if(ret == NotSet){
299 try = sig;
300 strncpy((char *) sl->text.data, try, len);
301 sl->text.data[len] = '\0';
302 sl->text.size = strlen((char *) sl->text.data);
303 if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
305 if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
306 ret = Sig;
310 if(sl)
311 mail_free_stringlist(&sl);
313 if(pinerc)
314 fs_give((void **)&pinerc);
315 if(abook)
316 fs_give((void **)&abook);
317 if(sig)
318 fs_give((void **)&sig);
319 if(smime)
320 fs_give((void **)&smime);
322 return(ret);
326 char *
327 ptype(rtype)
328 RemoteType rtype;
330 char *ret = NULL;
332 switch(rtype){
333 case Pinerc:
334 ret = cpystr("pinerc");
335 break;
336 case Abook:
337 ret = cpystr("abook");
338 break;
339 case Sig:
340 ret = cpystr("sig");
341 break;
342 case Smime:
343 ret = cpystr("smime");
344 break;
345 default:
346 break;
349 return(ret);
353 char *
354 spechdr(rtype)
355 RemoteType rtype;
357 char *ret = NULL;
359 switch(rtype){
360 case Pinerc:
361 ret = cpystr(REMOTE_PINERC_SUBTYPE);
362 break;
363 case Abook:
364 ret = cpystr(REMOTE_ABOOK_SUBTYPE);
365 break;
366 case Sig:
367 ret = cpystr(REMOTE_SIG_SUBTYPE);
368 break;
369 case Smime:
370 ret = cpystr(REMOTE_SMIME_SUBTYPE);
371 break;
372 default:
373 break;
376 return(ret);
381 parse_args(argc, argv, force, local, remote)
382 int argc;
383 char **argv;
384 int *force;
385 char **local, **remote;
387 int ac;
388 char **av;
389 int c;
390 char *str;
391 int usage = 0;
393 ac = argc;
394 av = argv;
396 /* while more arguments with leading - */
397 Loop: while(--ac > 0 && **++av == '-'){
398 /* while more chars in this argument */
399 while(*++*av){
400 switch(c = **av){
401 case 'h':
402 usage++;
403 break;
404 case 'f':
405 (*force)++;
406 break;
408 case 'r': case 'l': /* string args */
409 if(*++*av)
410 str = *av;
411 else if(--ac)
412 str = *++av;
413 else{
414 fprintf(stderr, "missing argument for flag \"%c\"\n", c);
415 ++usage;
416 goto Loop;
419 switch(c){
420 case 'l':
421 if(str)
422 *local = str;
424 break;
425 case 'r':
426 if(str)
427 *remote = str;
429 break;
432 goto Loop;
434 default:
435 fprintf(stderr, "unknown flag \"%c\"\n", c);
436 ++usage;
437 break;
442 if(ac != 0)
443 usage++;
445 return(usage);
449 char *
450 err_desc(err)
451 int err;
453 return((char *) strerror(err));
457 void mm_exists(stream, number)
458 MAILSTREAM *stream;
459 unsigned long number;
464 void mm_expunged(stream, number)
465 MAILSTREAM *stream;
466 unsigned long number;
471 void mm_flags(stream, number)
472 MAILSTREAM *stream;
473 unsigned long number;
478 void mm_list(stream, delim, name, attrib)
479 MAILSTREAM *stream;
480 int delim;
481 char *name;
482 long attrib;
487 void mm_lsub(stream, delimiter, name, attributes)
488 MAILSTREAM *stream;
489 int delimiter;
490 char *name;
491 long attributes;
496 void mm_notify(stream, string, errflg)
497 MAILSTREAM *stream;
498 char *string;
499 long errflg;
501 mm_log(string, errflg);
505 void mm_log(string, errflg)
506 char *string;
507 long errflg;
509 if(noshow_error)
510 return;
512 switch(errflg){
513 case BYE:
514 case NIL:
515 break;
517 case PARSE:
518 fprintf(stderr, "PARSE: %s\n", string);
519 break;
521 case WARN:
522 fprintf(stderr, "WARN: %s\n", string);
523 break;
525 case ERROR:
526 fprintf(stderr, "ERROR: %s\n", string);
527 break;
529 default:
530 fprintf(stderr, "%s\n", string);
531 break;
535 void mm_login_method(mb, user, pwd, trial, method)
536 NETMBX *mb;
537 char *user;
538 void *pwd;
539 long trial;
540 char *method;
542 mm_login(mb, user, (char **) pwd, trial);
545 void mm_login(mb, user, pwd, trial)
546 NETMBX *mb;
547 char *user;
548 char **pwd;
549 long trial;
551 char prompt[100], *last, tmp[MAILTMPLEN];
552 int i, j, goal, ugoal, len, rc, flags = 0;
553 #define NETMAXPASSWD 100
555 user[NETMAXUSER-1] = '\0';
557 if(trial == 0L){
558 if(mb->user && *mb->user){
559 strncpy(user, mb->user, NETMAXUSER);
560 user[NETMAXUSER-1] = '\0';
564 if(!*mb->user){
565 /* Dress up long hostnames */
566 snprintf(prompt, sizeof(prompt), "%sHOST: ",
567 (mb->sslflag||mb->tlsflag) ? "+ " : "");
568 len = strlen(prompt);
569 /* leave space for "HOST", "ENTER NAME", and 15 chars for input name */
570 goal = 80 - (len + 20 + MIN(15, 80/5));
571 last = " ENTER LOGIN NAME: ";
572 if(goal < 9){
573 last = " LOGIN: ";
574 if((goal += 13) < 9){
575 last += 1;
576 goal = 0;
580 if(goal){
581 for(i = len, j = 0;
582 i < sizeof(prompt) && (prompt[i] = mb->host[j]); i++, j++)
583 if(i == goal && mb->host[goal+1] && i < sizeof(prompt)){
584 strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
585 prompt[sizeof(prompt)-1] = '\0';
586 break;
589 else
590 i = 0;
592 strncpy(&prompt[i], last, sizeof(prompt)-i);
593 prompt[sizeof(prompt)-1] = '\0';
595 while(1) {
596 rc = opt_enter(user, NETMAXUSER, prompt, &flags);
597 if(rc != 4)
598 break;
601 if(rc == 1 || !user[0]) {
602 user[0] = '\0';
603 *pwd = NULL;
606 else
607 strncpy(user, mb->user, NETMAXUSER);
609 user[NETMAXUSER-1] = '\0';
610 // pwd[NETMAXPASSWD-1] = '\0';
612 if(!user[0])
613 return;
616 /* Dress up long host/user names */
617 /* leave space for "HOST", "USER" "ENTER PWD", 12 for user 6 for pwd */
618 snprintf(prompt, sizeof(prompt), "%sHOST: ", (mb->sslflag||mb->tlsflag) ? "+ " : "");
619 len = strlen(prompt);
620 goal = strlen(mb->host);
621 ugoal = strlen(user);
622 if((i = 80 - (len + 8 + 18 + 6)) < 14){
623 goal = 0; /* no host! */
624 if((i = 80 - (6 + 18 + 6)) <= 6){
625 ugoal = 0; /* no user! */
626 if((i = 80 - (18 + 6)) <= 0)
627 i = 0;
629 else{
630 ugoal = i; /* whatever's left */
631 i = 0;
634 else
635 while(goal + ugoal > i)
636 if(goal > ugoal)
637 goal--;
638 else
639 ugoal--;
641 if(goal){
642 snprintf(prompt, sizeof(prompt), "%sHOST: ",
643 (mb->sslflag||mb->tlsflag) ? "+ " : "");
644 for(i = len, j = 0;
645 i < sizeof(prompt) && (prompt[i] = mb->host[j]); i++, j++)
646 if(i == goal && mb->host[goal+1] && i < sizeof(prompt)){
647 strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
648 prompt[sizeof(prompt)-1] = '\0';
649 break;
652 else
653 i = 0;
655 if(ugoal){
656 strncpy(&prompt[i], &" USER: "[i ? 0 : 2], sizeof(prompt)-i);
657 prompt[sizeof(prompt)-1] = '\0';
658 for(i += strlen(&prompt[i]), j = 0;
659 i < sizeof(prompt) && (prompt[i] = user[j]); i++, j++)
660 if(j == ugoal && user[ugoal+1] && i < sizeof(prompt)){
661 strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
662 prompt[sizeof(prompt)-1] = '\0';
663 break;
667 strncpy(&prompt[i], &" ENTER PASSWORD: "[i ? 0 : 8], sizeof(prompt)-i);
668 prompt[sizeof(prompt)-1] = '\0';
670 tmp[0] = '\0';
671 while(1) {
672 flags = OE_PASSWD;
673 rc = opt_enter(tmp, NETMAXPASSWD, prompt, &flags);
674 if(rc != 4)
675 break;
678 if(tmp[0]) *pwd = cpystr(tmp);
679 if(rc == 1 || !tmp[0]) {
680 user[0] = '\0';
681 return;
686 void mm_critical(stream)
687 MAILSTREAM *stream;
692 void mm_nocritical(stream)
693 MAILSTREAM *stream;
698 long mm_diskerror(stream, errcode, serious)
699 MAILSTREAM *stream;
700 long errcode;
701 long serious;
703 return T;
707 void mm_fatal(string)
708 char *string;
710 fprintf(stderr, "%s\n", string);
714 void mm_searched(stream, msgno)
715 MAILSTREAM *stream;
716 unsigned long msgno;
721 void mm_status(stream, mailbox, status)
722 MAILSTREAM *stream;
723 char *mailbox;
724 MAILSTATUS *status;
728 void mm_dlog(string)
729 char *string;
731 fprintf(stderr, "%s\n", string);
736 opt_enter(string, field_len, prompt, flags)
737 char *string, *prompt;
738 int field_len;
739 int *flags;
741 char *pw;
742 int return_v = -10;
744 while(return_v == -10){
746 if(flags && *flags & OE_PASSWD){
747 if((pw = getpass(prompt)) != NULL){
748 if(strlen(pw) < field_len){
749 strncpy(string, pw, field_len);
750 string[field_len-1] = '\0';
751 return_v = 0;
753 else{
754 fputs("Password too long\n", stderr);
755 return_v = -1;
758 else
759 return_v = 1; /* cancel? */
761 else{
762 char *p;
764 fputs(prompt, stdout);
765 if(!fgets(string, field_len, stdin))
766 return_v = 1; /* cancel? */
767 string[field_len-1] = '\0';
768 if((p = strpbrk(string, "\r\n")) != NULL)
769 *p = '\0';
771 return_v = 0;
775 return(return_v);
778 char *
779 last_cmpnt(filename)
780 char *filename;
782 register char *p = NULL, *q = filename;
784 if(!q)
785 return(q);
787 while((q = strchr(q, '/')) != NULL)
788 if(*++q)
789 p = q;
791 return(p);
795 wantto(question, dflt, on_ctrl_C)
796 char *question;
797 int dflt, on_ctrl_C;
799 int ret = 0;
800 char rep[1000], *p;
802 while(!ret){
803 fprintf(stdout, "%s? [%c]:", question, dflt);
804 if(!fgets(rep, sizeof(rep), stdin))
805 *rep = '\0';
806 if((p = strpbrk(rep, "\r\n")) != NULL)
807 *p = '\0';
808 switch(*rep){
809 case 'Y':
810 case 'y':
811 ret = (int)'y';
812 break;
813 case 'N':
814 case 'n':
815 ret = (int)'n';
816 break;
817 case '\0':
818 ret = dflt;
819 break;
820 default:
821 break;
825 return ret;