#if !defined(lint) && !defined(DOS) static char rcsid[] = "$Id: rpdump.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $"; #endif /* * ======================================================================== * Copyright 2013-2021 Eduardo Chappa * Copyright 2006-2008 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * ======================================================================== */ #include "headers.h" #include "radio.h" /* OE_PASSWD */ #include "../pith/util.h" /* IS_REMOTE() */ #include "../pith/remote.h" /* REMOTE_ABOOK_SUBTYPE... */ typedef enum {Pinerc, Abook, Sig, Smime, NotSet} RemoteType; int parse_args(int, char **, int *, char **, char **); RemoteType check_for_header_msg(MAILSTREAM *); char *ptype(RemoteType); char *spechdr(RemoteType); char *err_desc(int); int opt_enter(char *, int, char *, int *); char *last_cmpnt(char *); int wantto(char *, int, int); char *ustr = "usage: %s [-f] -l Local_file -r Remote_folder\n"; int noshow_error = 0; /* look for my_timer_period in pico directory for an explanation */ int my_timer_period = ((IDLE_TIMEOUT + 1)*1000); #ifdef _WINDOWS #undef main app_main (argc, argv) int argc; char argv[]; { } #endif /* _WINDOWS */ /* * rpdump [-f] -l Local_file -r Remote_folder * * -f (skip check for special header) * * Note: We're not worrying about memory leaks. */ int main(argc, argv) int argc; char *argv[]; { MAILSTREAM *stream = NULL; FILE *fp; int usage = 0; char buf[10000]; char *local = NULL, *remote = NULL; int force = 0; BODY *body = NULL; char *data, *p; RemoteType rtype; unsigned long i; struct stat sbuf; #include "../c-client/linkage.c" if(parse_args(argc, argv, &force, &local, &remote)){ fprintf(stderr, ustr, argv[0]); exit(-1); } if(!local || !*local){ fprintf(stderr, "No local file specified\n"); usage++; } if(!remote || !*remote){ fprintf(stderr, "No remote folder specified\n"); usage++; } if(usage){ fprintf(stderr, ustr, argv[0]); exit(-1); } if(!IS_REMOTE(remote)){ fprintf(stderr, "Remote folder name \"%s\" %s\n", remote, (*remote != '{') ? "must begin with \"{\"" : "not valid"); exit(-1); } if(IS_REMOTE(local)){ fprintf(stderr, "Argument to -l (%s) must be a local filename", local); exit(-1); } #ifdef _WINDOWS if(stat(local, &sbuf)) #else if(lstat(local, &sbuf)) #endif { if(errno == ENOENT){ /* File did not exist */ int fd; /* create it */ if(((fd = open(local, O_CREAT|O_EXCL|O_WRONLY,0600)) < 0) || (close(fd) != 0)){ fprintf(stderr, "%s: %s\n", local, err_desc(errno)); exit(-1); } /* now it exists! */ } else{ /* unknown error */ fprintf(stderr, "%s: %s\n", local, err_desc(errno)); exit(-1); } } else{ /* file exists */ /* is it a regular file? */ #ifdef S_ISREG if(!S_ISREG(sbuf.st_mode)) #else if(!(S_IFREG & sbuf.st_mode)) #endif { fprintf(stderr, "Only allowed to write to regular local files. Try another filename.\n"); exit(-1); } if(access(local, WRITE_ACCESS) == 0){ snprintf(buf, sizeof(buf), "Local file \"%s\" exists, overwrite it", (p = last_cmpnt(local)) ? p : local); if(wantto(buf, 'n', 'n') != 'y'){ fprintf(stderr, "Dump cancelled\n"); exit(-1); } } else{ fprintf(stderr, "Local file \"%s\" is not writable\n", local); exit(-1); } } /* * Try opening the local file. */ if((fp = fopen(local, "w")) == NULL){ fprintf(stderr, "Can't open \"%s\": %s\n", local, err_desc(errno)); mail_close(stream); exit(-1); } /* * Try opening the remote folder. */ stream = mail_open(NULL, remote, OP_READONLY); if(!stream || stream->halfopen){ fprintf(stderr, "Remote folder \"%s\" is not readable\n", remote); if(stream) mail_close(stream); exit(-1); } if(stream->nmsgs >= 2){ /* * There is a first message already. Check to see if it is one of * our special header messages. */ rtype = check_for_header_msg(stream); if(!force && rtype == NotSet){ fprintf(stderr, "Folder \"%s\"\ndoes not appear to be an Alpine remote data folder.\nUse -f to force.\n", remote); mail_close(stream); exit(-1); } } else if(stream->nmsgs == 1){ fprintf(stderr, "No data in remote folder to copy (only 1 message)\n"); mail_close(stream); exit(-1); } else{ fprintf(stderr, "No data in remote folder to copy\n"); mail_close(stream); exit(-1); } if(!mail_fetchstructure(stream, stream->nmsgs, &body)){ fprintf(stderr, "Can't access remote IMAP data\n"); mail_close(stream); exit(-1); } if(!body || body->type != REMOTE_DATA_TYPE || !body->subtype || (!force && strucmp(body->subtype, spechdr(rtype)))){ fprintf(stderr, "Remote IMAP folder has wrong contents\n"); mail_close(stream); exit(-1); } if(!mail_fetchenvelope(stream, stream->nmsgs)){ fprintf(stderr, "Can't access envelope in remote data\n"); mail_close(stream); exit(-1); } data = mail_fetch_body(stream, stream->nmsgs, "1", &i, FT_PEEK); p = data; for(p = data; p < data+i; p++){ /* convert to unix newlines */ if(*p == '\r' && *(p+1) == '\n') p++; if(putc(*p, fp) == EOF){ fprintf(stderr, "Error writing \"%s\": %s\n", local, err_desc(errno)); fclose(fp); mail_close(stream); exit(-1); } } mail_close(stream); fclose(fp); fprintf(stderr, "Remote folder is of type \"%s\", contents saved to \"%s\"\n", ptype(rtype) ? ptype(rtype) : "unknown", local); exit(0); } RemoteType check_for_header_msg(stream) MAILSTREAM *stream; { STRINGLIST *sl; int ret = NotSet; char *h, *try; size_t len; char *pinerc, *abook, *sig, *smime; pinerc = spechdr(Pinerc); abook = spechdr(Abook); sig = spechdr(Sig); smime = spechdr(Smime); len = MAX(MAX(strlen(pinerc), strlen(abook)), MAX(strlen(sig), strlen(smime))); sl = mail_newstringlist(); sl->text.data = (unsigned char *)fs_get((len+1) * sizeof(unsigned char)); try = pinerc; strncpy((char *) sl->text.data, try, len); sl->text.data[len] = '\0'; sl->text.size = strlen((char *) sl->text.data); if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){ if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size)) ret = Pinerc; } if(ret == NotSet){ try = abook; strncpy((char *)sl->text.data, try, len); sl->text.data[len] = '\0'; sl->text.size = strlen((char *) sl->text.data); if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){ if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size)) ret = Abook; } } if(ret == NotSet){ try = sig; strncpy((char *) sl->text.data, try, len); sl->text.data[len] = '\0'; sl->text.size = strlen((char *) sl->text.data); if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){ if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size)) ret = Sig; } } if(sl) mail_free_stringlist(&sl); if(pinerc) fs_give((void **)&pinerc); if(abook) fs_give((void **)&abook); if(sig) fs_give((void **)&sig); if(smime) fs_give((void **)&smime); return(ret); } char * ptype(rtype) RemoteType rtype; { char *ret = NULL; switch(rtype){ case Pinerc: ret = cpystr("pinerc"); break; case Abook: ret = cpystr("abook"); break; case Sig: ret = cpystr("sig"); break; case Smime: ret = cpystr("smime"); break; default: break; } return(ret); } char * spechdr(rtype) RemoteType rtype; { char *ret = NULL; switch(rtype){ case Pinerc: ret = cpystr(REMOTE_PINERC_SUBTYPE); break; case Abook: ret = cpystr(REMOTE_ABOOK_SUBTYPE); break; case Sig: ret = cpystr(REMOTE_SIG_SUBTYPE); break; case Smime: ret = cpystr(REMOTE_SMIME_SUBTYPE); break; default: break; } return(ret); } int parse_args(argc, argv, force, local, remote) int argc; char **argv; int *force; char **local, **remote; { int ac; char **av; int c; char *str; int usage = 0; ac = argc; av = argv; /* while more arguments with leading - */ Loop: while(--ac > 0 && **++av == '-'){ /* while more chars in this argument */ while(*++*av){ switch(c = **av){ case 'h': usage++; break; case 'f': (*force)++; break; case 'r': case 'l': /* string args */ if(*++*av) str = *av; else if(--ac) str = *++av; else{ fprintf(stderr, "missing argument for flag \"%c\"\n", c); ++usage; goto Loop; } switch(c){ case 'l': if(str) *local = str; break; case 'r': if(str) *remote = str; break; } goto Loop; default: fprintf(stderr, "unknown flag \"%c\"\n", c); ++usage; break; } } } if(ac != 0) usage++; return(usage); } char * err_desc(err) int err; { return((char *) strerror(err)); } void mm_exists(stream, number) MAILSTREAM *stream; unsigned long number; { } void mm_expunged(stream, number) MAILSTREAM *stream; unsigned long number; { } void mm_flags(stream, number) MAILSTREAM *stream; unsigned long number; { } void mm_list(stream, delim, name, attrib) MAILSTREAM *stream; int delim; char *name; long attrib; { } void mm_lsub(stream, delimiter, name, attributes) MAILSTREAM *stream; int delimiter; char *name; long attributes; { } void mm_notify(stream, string, errflg) MAILSTREAM *stream; char *string; long errflg; { mm_log(string, errflg); } void mm_log(string, errflg) char *string; long errflg; { if(noshow_error) return; switch(errflg){ case BYE: case NIL: break; case PARSE: fprintf(stderr, "PARSE: %s\n", string); break; case WARN: fprintf(stderr, "WARN: %s\n", string); break; case ERROR: fprintf(stderr, "ERROR: %s\n", string); break; default: fprintf(stderr, "%s\n", string); break; } } void mm_login_method(mb, user, pwd, trial, method) NETMBX *mb; char *user; void *pwd; long trial; char *method; { mm_login(mb, user, (char **) pwd, trial); } void mm_login(mb, user, pwd, trial) NETMBX *mb; char *user; char **pwd; long trial; { char prompt[100], *last, tmp[MAILTMPLEN]; int i, j, goal, ugoal, len, rc, flags = 0; #define NETMAXPASSWD 100 user[NETMAXUSER-1] = '\0'; if(trial == 0L){ if(mb->user && *mb->user){ strncpy(user, mb->user, NETMAXUSER); user[NETMAXUSER-1] = '\0'; } } if(!*mb->user){ /* Dress up long hostnames */ snprintf(prompt, sizeof(prompt), "%sHOST: ", (mb->sslflag||mb->tlsflag) ? "+ " : ""); len = strlen(prompt); /* leave space for "HOST", "ENTER NAME", and 15 chars for input name */ goal = 80 - (len + 20 + MIN(15, 80/5)); last = " ENTER LOGIN NAME: "; if(goal < 9){ last = " LOGIN: "; if((goal += 13) < 9){ last += 1; goal = 0; } } if(goal){ for(i = len, j = 0; i < sizeof(prompt) && (prompt[i] = mb->host[j]); i++, j++) if(i == goal && mb->host[goal+1] && i < sizeof(prompt)){ strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3)); prompt[sizeof(prompt)-1] = '\0'; break; } } else i = 0; strncpy(&prompt[i], last, sizeof(prompt)-i); prompt[sizeof(prompt)-1] = '\0'; while(1) { rc = opt_enter(user, NETMAXUSER, prompt, &flags); if(rc != 4) break; } if(rc == 1 || !user[0]) { user[0] = '\0'; *pwd = NULL; } } else strncpy(user, mb->user, NETMAXUSER); user[NETMAXUSER-1] = '\0'; // pwd[NETMAXPASSWD-1] = '\0'; if(!user[0]) return; /* Dress up long host/user names */ /* leave space for "HOST", "USER" "ENTER PWD", 12 for user 6 for pwd */ snprintf(prompt, sizeof(prompt), "%sHOST: ", (mb->sslflag||mb->tlsflag) ? "+ " : ""); len = strlen(prompt); goal = strlen(mb->host); ugoal = strlen(user); if((i = 80 - (len + 8 + 18 + 6)) < 14){ goal = 0; /* no host! */ if((i = 80 - (6 + 18 + 6)) <= 6){ ugoal = 0; /* no user! */ if((i = 80 - (18 + 6)) <= 0) i = 0; } else{ ugoal = i; /* whatever's left */ i = 0; } } else while(goal + ugoal > i) if(goal > ugoal) goal--; else ugoal--; if(goal){ snprintf(prompt, sizeof(prompt), "%sHOST: ", (mb->sslflag||mb->tlsflag) ? "+ " : ""); for(i = len, j = 0; i < sizeof(prompt) && (prompt[i] = mb->host[j]); i++, j++) if(i == goal && mb->host[goal+1] && i < sizeof(prompt)){ strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3)); prompt[sizeof(prompt)-1] = '\0'; break; } } else i = 0; if(ugoal){ strncpy(&prompt[i], &" USER: "[i ? 0 : 2], sizeof(prompt)-i); prompt[sizeof(prompt)-1] = '\0'; for(i += strlen(&prompt[i]), j = 0; i < sizeof(prompt) && (prompt[i] = user[j]); i++, j++) if(j == ugoal && user[ugoal+1] && i < sizeof(prompt)){ strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3)); prompt[sizeof(prompt)-1] = '\0'; break; } } strncpy(&prompt[i], &" ENTER PASSWORD: "[i ? 0 : 8], sizeof(prompt)-i); prompt[sizeof(prompt)-1] = '\0'; tmp[0] = '\0'; while(1) { flags = OE_PASSWD; rc = opt_enter(tmp, NETMAXPASSWD, prompt, &flags); if(rc != 4) break; } if(tmp[0]) *pwd = cpystr(tmp); if(rc == 1 || !tmp[0]) { user[0] = '\0'; return; } } void mm_critical(stream) MAILSTREAM *stream; { } void mm_nocritical(stream) MAILSTREAM *stream; { } long mm_diskerror(stream, errcode, serious) MAILSTREAM *stream; long errcode; long serious; { return T; } void mm_fatal(string) char *string; { fprintf(stderr, "%s\n", string); } void mm_searched(stream, msgno) MAILSTREAM *stream; unsigned long msgno; { } void mm_status(stream, mailbox, status) MAILSTREAM *stream; char *mailbox; MAILSTATUS *status; { } void mm_dlog(string) char *string; { fprintf(stderr, "%s\n", string); } int opt_enter(string, field_len, prompt, flags) char *string, *prompt; int field_len; int *flags; { char *pw; int return_v = -10; while(return_v == -10){ if(flags && *flags & OE_PASSWD){ if((pw = getpass(prompt)) != NULL){ if(strlen(pw) < field_len){ strncpy(string, pw, field_len); string[field_len-1] = '\0'; return_v = 0; } else{ fputs("Password too long\n", stderr); return_v = -1; } } else return_v = 1; /* cancel? */ } else{ char *p; fputs(prompt, stdout); fgets(string, field_len, stdin); string[field_len-1] = '\0'; if((p = strpbrk(string, "\r\n")) != NULL) *p = '\0'; return_v = 0; } } return(return_v); } char * last_cmpnt(filename) char *filename; { register char *p = NULL, *q = filename; if(!q) return(q); while((q = strchr(q, '/')) != NULL) if(*++q) p = q; return(p); } int wantto(question, dflt, on_ctrl_C) char *question; int dflt, on_ctrl_C; { int ret = 0; char rep[1000], *p; while(!ret){ fprintf(stdout, "%s? [%c]:", question, dflt); fgets(rep, sizeof(rep), stdin); if((p = strpbrk(rep, "\r\n")) != NULL) *p = '\0'; switch(*rep){ case 'Y': case 'y': ret = (int)'y'; break; case 'N': case 'n': ret = (int)'n'; break; case '\0': ret = dflt; break; default: break; } } return ret; }