1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: rpload.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2016 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 * ========================================================================
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 *, int *, char **, char **, RemoteType
*);
29 RemoteType
check_for_header_msg(MAILSTREAM
*);
30 char *ptype(RemoteType
);
31 char *spechdr(RemoteType
);
32 int add_initial_msg(MAILSTREAM
*, char *, char *);
33 int append_data(MAILSTREAM
*, char *, char *, FILE *);
34 void trim_data(MAILSTREAM
*, int);
35 void write_fake_headers(RFC822BUFFER
*, char *, char *, char *);
37 int opt_enter(char *, int, char *, int *);
41 char *ustr
= "usage: %s [-s trimSize] [-f] -t Type -l Local_file -r Remote_folder\n";
44 /* look for my_timer_period in pico directory for an explanation */
45 int my_timer_period
= ((IDLE_TIMEOUT
+ 1)*1000);
62 * rpload [-s trimSize] [-f] -t Type -l Local_file -r Remote_folder
64 * Type is one of abook
67 * sig (this is mostly obsolete, literal sigs
68 * should be used instead)
69 * -f means force the folder to be written even if it
70 * doesn't look like it is of the right format
72 * Note: We're not worrying about memory leaks.
79 MAILSTREAM
*stream
= NULL
;
81 int delete_existing
= 0, usage
= 0;
82 int force
= 0, trimsize
= 0;
83 char *local
= NULL
, *remote
= NULL
, *special_hdr
= NULL
;
84 RemoteType rt
, type
= NotSet
;
86 #include "../c-client/linkage.c"
88 if(parse_args(argc
, argv
, &force
, &trimsize
, &local
, &remote
, &type
)){
89 fprintf(stderr
, ustr
, argv
[0]);
93 if(!local
|| !*local
){
94 fprintf(stderr
, "No local file specified\n");
98 if(!remote
|| !*remote
){
99 fprintf(stderr
, "No remote folder specified\n");
104 fprintf(stderr
, "Type must be set to one of:\n");
105 for(rt
= Pinerc
; rt
!= NotSet
; rt
++)
106 fprintf(stderr
, " %s\n", ptype(rt
));
112 fprintf(stderr
, ustr
, argv
[0]);
116 if(!IS_REMOTE(remote
)){
118 "Remote folder name \"%s\" %s\n", remote
,
119 (*remote
!= '{') ? "must begin with \"{\"" : "not valid");
123 if(IS_REMOTE(local
)){
124 fprintf(stderr
, "Argument to -l (%s) must be a local filename", local
);
128 if(access(local
, ACCESS_EXISTS
) != 0){
129 fprintf(stderr
, "Local file \"%s\" does not exist\n", local
);
133 if(access(local
, READ_ACCESS
) != 0){
135 "Can't read local file \"%s\": %s\n",
136 local
, err_desc(errno
));
141 * Try opening the local file.
143 if((fp
= fopen(local
, "r")) == NULL
){
144 fprintf(stderr
, "Can't open \"%s\": %s\n", local
, err_desc(errno
));
149 * Try opening the remote folder. If it doesn't exist, create it.
152 /* failure would be normal here, so don't show it */
154 stream
= mail_open(NULL
, remote
, 0L);
155 if(!stream
|| stream
->halfopen
){
156 if(stream
&& stream
->halfopen
){
158 if(!mail_create(stream
, remote
))
161 stream
= mail_open(stream
, remote
, 0L);
162 if(!stream
|| stream
->halfopen
)
166 fprintf(stderr
, "Trouble opening remote folder \"%s\"\n", remote
);
174 fprintf(stderr
, "Remote folder \"%s\" is not writable\n", remote
);
178 if(stream
->nmsgs
> 0){
180 * There is a first message already. Check to see if it is one of
181 * our special header messages.
183 rt
= check_for_header_msg(stream
);
188 fprintf(stderr
, "Folder \"%s\"\ndoes not appear to be an Alpine remote \"%s\" folder.\nUse -f to force.\n", remote
, ptype(type
));
189 fprintf(stderr
, "-f will cause %ld messages to be deleted\n",
198 fprintf(stderr
, "Folder \"%s\" is type \"%s\"\nUse -f to force switch.\n", remote
, ptype(rt
));
199 fprintf(stderr
, "-f will cause %ld messages to be deleted\n",
210 snprintf(sequence
, sizeof(sequence
), "1:%ld", stream
->nmsgs
);
211 mail_flag(stream
, sequence
, "\\DELETED", ST_SET
);
212 mail_expunge(stream
);
216 special_hdr
= spechdr(type
);
219 * Add the explanatory header message if needed.
221 if(stream
->nmsgs
== 0){
222 if(add_initial_msg(stream
, remote
, special_hdr
) != 0){
229 * Add the actual data in a message.
231 if(append_data(stream
, remote
, special_hdr
, fp
) != 0){
237 * Trim the size of the remote folder.
240 trim_data(stream
, trimsize
);
248 check_for_header_msg(stream
)
255 char *pinerc
, *abook
, *sig
, *smime
;
257 pinerc
= spechdr(Pinerc
);
258 abook
= spechdr(Abook
);
260 smime
= spechdr(Smime
);
262 len
= MAX(MAX(strlen(pinerc
), strlen(abook
)), MAX(strlen(sig
), strlen(smime
)));
264 sl
= mail_newstringlist();
265 sl
->text
.data
= (unsigned char *)fs_get((len
+1) * sizeof(unsigned char));
267 strncpy((char *)sl
->text
.data
, try, len
);
268 sl
->text
.data
[len
] = '\0';
269 sl
->text
.size
= strlen((char *) sl
->text
.data
);
271 if(stream
&& (h
=mail_fetch_header(stream
,1L,NULL
,sl
,NULL
,FT_PEEK
))){
273 if(strlen(h
) >= sl
->text
.size
&& !struncmp(h
, try, sl
->text
.size
))
279 strncpy((char *)sl
->text
.data
, try, len
);
280 sl
->text
.data
[len
] = '\0';
281 sl
->text
.size
= strlen((char *) sl
->text
.data
);
282 if(stream
&& (h
=mail_fetch_header(stream
,1L,NULL
,sl
,NULL
,FT_PEEK
))){
284 if(strlen(h
) >= sl
->text
.size
&& !struncmp(h
, try, sl
->text
.size
))
291 strncpy((char *)sl
->text
.data
, try, len
);
292 sl
->text
.data
[len
] = '\0';
293 sl
->text
.size
= strlen((char *) sl
->text
.data
);
294 if(stream
&& (h
=mail_fetch_header(stream
,1L,NULL
,sl
,NULL
,FT_PEEK
))){
296 if(strlen(h
) >= sl
->text
.size
&& !struncmp(h
, try, sl
->text
.size
))
302 mail_free_stringlist(&sl
);
305 fs_give((void **)&pinerc
);
307 fs_give((void **)&abook
);
309 fs_give((void **)&sig
);
311 fs_give((void **)&smime
);
325 ret
= cpystr("pinerc");
328 ret
= cpystr("abook");
334 ret
= cpystr("smime");
352 ret
= cpystr(REMOTE_PINERC_SUBTYPE
);
355 ret
= cpystr(REMOTE_ABOOK_SUBTYPE
);
358 ret
= cpystr(REMOTE_SIG_SUBTYPE
);
361 ret
= cpystr(REMOTE_SMIME_SUBTYPE
);
372 parse_args(argc
, argv
, force
, trimsize
, local
, remote
, type
)
375 int *force
, *trimsize
;
376 char **local
, **remote
;
389 /* while more arguments with leading - */
390 Loop
: while(--ac
> 0 && **++av
== '-'){
391 /* while more chars in this argument */
401 case 't': case 'l': /* string args */
403 case 's': /* integer args */
409 fprintf(stderr
, "missing argument for flag \"%c\"\n", c
);
426 for(rt
= Pinerc
; rt
!= NotSet
; rt
++){
427 if(!strucmp(str
, ptype(rt
)))
434 if(!isdigit((unsigned char)str
[0])){
436 "non-numeric argument for flag \"%c\"\n", c
);
441 *trimsize
= atoi(str
);
443 fprintf(stderr
, "trimsize of %d is too small, have to leave at least one copy\n", *trimsize
);
446 else if(*trimsize
> 100){
448 "trimsize of %d is too large, 5 or 10 is sufficient\n",
459 fprintf(stderr
, "unknown flag \"%c\"\n", c
);
474 dummy_soutr(stream
, string
)
483 * Add an explanatory first message advising user that this is a
484 * special sort of folder.
487 add_initial_msg(stream
, mailbox
, special_hdr
)
496 rbuf
.f
= dummy_soutr
;
500 rbuf
.end
= buf
+sizeof(buf
)-1;
501 write_fake_headers(&rbuf
, "Header Message for Remote Data", "plain", special_hdr
);
504 buf
[sizeof(buf
)-1] = '\0';
506 if(!strucmp(special_hdr
, REMOTE_ABOOK_SUBTYPE
)){
507 strncat(buf
, "This folder contains a single Alpine addressbook.\015\012", sizeof(buf
)-strlen(buf
)-1);
508 strncat(buf
, "This message is just an explanatory message.\015\012", sizeof(buf
)-strlen(buf
)-1);
509 strncat(buf
, "The last message in the folder is the live addressbook data.\015\012", sizeof(buf
)-strlen(buf
)-1);
510 strncat(buf
, "The rest of the messages contain previous revisions of the addressbook data.\015\012", sizeof(buf
)-strlen(buf
)-1);
511 strncat(buf
, "To restore a previous revision just delete and expunge all of the messages\015\012", sizeof(buf
)-strlen(buf
)-1);
512 strncat(buf
, "which come after it.\015\012", sizeof(buf
)-strlen(buf
)-1);
514 else if(!strucmp(special_hdr
, REMOTE_PINERC_SUBTYPE
)){
515 strncat(buf
, "This folder contains an Alpine config file.\015\012", sizeof(buf
)-strlen(buf
)-1);
516 strncat(buf
, "This message is just an explanatory message.\015\012", sizeof(buf
)-strlen(buf
)-1);
517 strncat(buf
, "The last message in the folder is the live config data.\015\012", sizeof(buf
)-strlen(buf
)-1);
518 strncat(buf
, "The rest of the messages contain previous revisions of the data.\015\012", sizeof(buf
)-strlen(buf
)-1);
519 strncat(buf
, "To restore a previous revision just delete and expunge all of the messages\015\012", sizeof(buf
)-strlen(buf
)-1);
520 strncat(buf
, "which come after it.\015\012", sizeof(buf
)-strlen(buf
)-1);
522 else if(!strucmp(special_hdr
, REMOTE_SMIME_SUBTYPE
)){
523 strncat(buf
, "This folder contains Alpine S/MIME config information.\015\012", sizeof(buf
)-strlen(buf
)-1);
524 strncat(buf
, "This message is just an explanatory message.\015\012", sizeof(buf
)-strlen(buf
)-1);
525 strncat(buf
, "The last message in the folder is the live data.\015\012", sizeof(buf
)-strlen(buf
)-1);
526 strncat(buf
, "The rest of the messages contain previous revisions of the data.\015\012", sizeof(buf
)-strlen(buf
)-1);
527 strncat(buf
, "To restore a previous revision just delete and expunge all of the messages\015\012", sizeof(buf
)-strlen(buf
)-1);
528 strncat(buf
, "which come after it.\015\012", sizeof(buf
)-strlen(buf
)-1);
531 strncat(buf
, "This folder contains remote Alpine data.\015\012", sizeof(buf
)-strlen(buf
)-1);
532 strncat(buf
, "This message is just an explanatory message.\015\012", sizeof(buf
)-strlen(buf
)-1);
533 strncat(buf
, "The last message in the folder is the live data.\015\012", sizeof(buf
)-strlen(buf
)-1);
534 strncat(buf
, "The rest of the messages contain previous revisions of the data.\015\012", sizeof(buf
)-strlen(buf
)-1);
535 strncat(buf
, "To restore a previous revision just delete and expunge all of the messages\015\012", sizeof(buf
)-strlen(buf
)-1);
536 strncat(buf
, "which come after it.\015\012", sizeof(buf
)-strlen(buf
)-1);
539 INIT(&msg
, mail_string
, (void *)buf
, strlen(buf
));
540 if(!mail_append(stream
, mailbox
, &msg
))
548 * Add a message to the folder with the contents of the local data
552 append_data(stream
, mailbox
, special_hdr
, fp
)
559 char buf
[20000], *sto
, *p
;
565 if(fstat(fileno(fp
), &sbuf
) != 0){
566 fprintf(stderr
, "fstat of local file failed\n");
570 filelen
= (long) sbuf
.st_size
;
572 rbuf
.f
= dummy_soutr
;
576 rbuf
.end
= buf
+sizeof(buf
)-1;
577 write_fake_headers(&rbuf
, "Pine Remote Data Container", special_hdr
,
581 buf
[sizeof(buf
)-1] = '\0';
583 /* very conservative estimate of space needed */
584 len
= filelen
+ filelen
+ strlen(buf
) + 10;
585 sto
= fs_get((len
+1) * sizeof(char));
587 strncpy(sto
, buf
, len
);
589 p
= sto
+ strlen(sto
);
590 /* Write the contents */
591 while((c
= getc(fp
)) != EOF
){
593 * c-client expects CRLF-terminated lines. These lines
594 * can be either CRLF- or LF-terminated. We have to convert them
595 * when we copy into c-client.
597 if(c
== '\r' || c
== '\n'){
598 if(c
== '\r' && ((nextc
= getc(fp
)) != '\n') && nextc
!= EOF
)
608 else if(p
- sto
< len
)
618 INIT(&msg
, mail_string
, (void *)sto
, strlen(sto
));
619 if(!mail_append(stream
, mailbox
, &msg
)){
620 fprintf(stderr
, "Copy failed\n");
624 fs_give((void **)&sto
);
631 * Trim the number of saved copies of the remote data history in case
632 * this is the only way this folder is ever updated. We leave
633 * the first message there because it is supposed to be an explanatory
634 * message, but we don't actually check to see whether or not it is
635 * such a message or not.
638 trim_data(stream
, trimsize
)
642 if(stream
->nmsgs
> trimsize
+ 1){
646 snprintf(sequence
, sizeof(sequence
), "2:%ld", stream
->nmsgs
- trimsize
);
647 mail_flag(stream
, sequence
, "\\DELETED", ST_SET
);
648 mail_expunge(stream
);
654 write_fake_headers(where
, subject
, subtype
, special_hdr
)
663 char date
[200], vers
[10];
665 fake_env
= (ENVELOPE
*)fs_get(sizeof(ENVELOPE
));
666 memset(fake_env
, 0, sizeof(ENVELOPE
));
667 fake_body
= (BODY
*)fs_get(sizeof(BODY
));
668 memset(fake_body
, 0, sizeof(BODY
));
669 fake_from
= (ADDRESS
*)fs_get(sizeof(ADDRESS
));
670 memset(fake_from
, 0, sizeof(ADDRESS
));
673 fake_env
->subject
= cpystr(subject
);
674 fake_env
->date
= (unsigned char *) cpystr(date
);
675 fake_from
->personal
= cpystr("Pine Remote Data");
676 fake_from
->mailbox
= cpystr("nobody");
677 fake_from
->host
= cpystr("nowhere");
678 fake_env
->from
= fake_from
;
679 fake_body
->type
= REMOTE_DATA_TYPE
;
680 fake_body
->subtype
= cpystr(subtype
);
682 snprintf(vers
, sizeof(vers
), "%d", REMOTE_DATA_VERS_NUM
);
684 /* re-use subtype for special header name, too */
685 rfc822_output_header_line(where
, special_hdr
, 0L, vers
);
686 rfc822_output_header(where
, fake_env
, fake_body
, NULL
, 0L);
687 mail_free_envelope(&fake_env
);
688 mail_free_body(&fake_body
);
696 return((char *) strerror(err
));
700 void mm_exists(stream
, number
)
702 unsigned long number
;
707 void mm_expunged(stream
, number
)
709 unsigned long number
;
714 void mm_flags(stream
, number
)
716 unsigned long number
;
721 void mm_list(stream
, delim
, name
, attrib
)
730 void mm_lsub(stream
, delimiter
, name
, attributes
)
739 void mm_notify(stream
, string
, errflg
)
744 mm_log(string
, errflg
);
748 void mm_log(string
, errflg
)
761 fprintf(stderr
, "PARSE: %s\n", string
);
765 fprintf(stderr
, "WARN: %s\n", string
);
769 fprintf(stderr
, "ERROR: %s\n", string
);
773 fprintf(stderr
, "%s\n", string
);
779 void mm_login(mb
, user
, pwd
, trial
)
785 char prompt
[100], *last
;
786 int i
, j
, goal
, ugoal
, len
, rc
, flags
= 0;
787 #define NETMAXPASSWD 100
789 user
[NETMAXUSER
-1] = '\0';
792 if(mb
->user
&& *mb
->user
){
793 strncpy(user
, mb
->user
, NETMAXUSER
);
794 user
[NETMAXUSER
-1] = '\0';
799 /* Dress up long hostnames */
800 snprintf(prompt
, sizeof(prompt
), "%sHOST: ",
801 (mb
->sslflag
||mb
->tlsflag
) ? "+ " : "");
802 len
= strlen(prompt
);
803 /* leave space for "HOST", "ENTER NAME", and 15 chars for input name */
804 goal
= 80 - (len
+ 20 + MIN(15, 80/5));
805 last
= " ENTER LOGIN NAME: ";
808 if((goal
+= 13) < 9){
816 i
< sizeof(prompt
) && (prompt
[i
] = mb
->host
[j
]); i
++, j
++)
817 if(i
== goal
&& mb
->host
[goal
+1] && i
< sizeof(prompt
)){
818 strncpy(&prompt
[i
-3], "...", sizeof(prompt
)-(i
-3));
819 prompt
[sizeof(prompt
)-1] = '\0';
826 strncpy(&prompt
[i
], last
, sizeof(prompt
)-i
);
827 prompt
[sizeof(prompt
)-1] = '\0';
830 rc
= opt_enter(user
, NETMAXUSER
, prompt
, &flags
);
835 if(rc
== 1 || !user
[0]) {
841 strncpy(user
, mb
->user
, NETMAXUSER
);
843 user
[NETMAXUSER
-1] = '\0';
844 pwd
[NETMAXPASSWD
-1] = '\0';
850 /* Dress up long host/user names */
851 /* leave space for "HOST", "USER" "ENTER PWD", 12 for user 6 for pwd */
852 snprintf(prompt
, sizeof(prompt
), "%sHOST: ", (mb
->sslflag
||mb
->tlsflag
) ? "+ " : "");
853 len
= strlen(prompt
);
854 goal
= strlen(mb
->host
);
855 ugoal
= strlen(user
);
856 if((i
= 80 - (len
+ 8 + 18 + 6)) < 14){
857 goal
= 0; /* no host! */
858 if((i
= 80 - (6 + 18 + 6)) <= 6){
859 ugoal
= 0; /* no user! */
860 if((i
= 80 - (18 + 6)) <= 0)
864 ugoal
= i
; /* whatever's left */
869 while(goal
+ ugoal
> i
)
876 snprintf(prompt
, sizeof(prompt
), "%sHOST: ",
877 (mb
->sslflag
||mb
->tlsflag
) ? "+ " : "");
879 i
< sizeof(prompt
) && (prompt
[i
] = mb
->host
[j
]); i
++, j
++)
880 if(i
== goal
&& mb
->host
[goal
+1] && i
< sizeof(prompt
)){
881 strncpy(&prompt
[i
-3], "...", sizeof(prompt
)-(i
-3));
882 prompt
[sizeof(prompt
)-1] = '\0';
890 strncpy(&prompt
[i
], &" USER: "[i
? 0 : 2], sizeof(prompt
)-i
);
891 prompt
[sizeof(prompt
)-1] = '\0';
892 for(i
+= strlen(&prompt
[i
]), j
= 0;
893 i
< sizeof(prompt
) && (prompt
[i
] = user
[j
]); i
++, j
++)
894 if(j
== ugoal
&& user
[ugoal
+1] && i
< sizeof(prompt
)){
895 strncpy(&prompt
[i
-3], "...", sizeof(prompt
)-(i
-3));
896 prompt
[sizeof(prompt
)-1] = '\0';
901 strncpy(&prompt
[i
], &" ENTER PASSWORD: "[i
? 0 : 8], sizeof(prompt
)-i
);
902 prompt
[sizeof(prompt
)-1] = '\0';
907 rc
= opt_enter(pwd
, NETMAXPASSWD
, prompt
, &flags
);
912 if(rc
== 1 || !pwd
[0]) {
913 user
[0] = pwd
[0] = '\0';
919 void mm_critical(stream
)
925 void mm_nocritical(stream
)
931 long mm_diskerror(stream
, errcode
, serious
)
940 void mm_fatal(string
)
943 fprintf(stderr
, "%s\n", string
);
947 void mm_searched(stream
, msgno
)
954 void mm_status(stream
, mailbox
, status
)
964 fprintf(stderr
, "%s\n", string
);
969 opt_enter(string
, field_len
, prompt
, flags
)
970 char *string
, *prompt
;
977 while(return_v
== -10){
979 if(flags
&& *flags
& OE_PASSWD
){
980 if((pw
= getpass(prompt
)) != NULL
){
981 if(strlen(pw
) < field_len
){
982 strncpy(string
, pw
, field_len
);
983 string
[field_len
-1] = '\0';
987 fputs("Password too long\n", stderr
);
992 return_v
= 1; /* cancel? */
997 fputs(prompt
, stdout
);
998 fgets(string
, field_len
, stdin
);
999 string
[field_len
-1] = '\0';
1000 if((p
= strpbrk(string
, "\r\n")) != NULL
)