/* ======================================================= * FreeDOS SMTP/POP3 client program vers. 0.12 (06-29-97) * * = Free DOS SMTP and POP3 protocols implementation = * = based on WATTCP library = * * by Yury Semenov (yury@petre.odessa.ua, Fidonet#2:467/2) * ======================================================= * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include "tcp.h" #define VERSION "FD-SMTP/POP3 v. 0.12 for DOS" #define LOGFILE "FDSMTPOPLOG" #define SMTP_PORT 25 #define POP3_PORT 110 #define SMTP_INTRO 220 #define SMTP_GOODBY 221 #define SMTP_LOCAL 250 #define SMTP_REMOTE1 251 #define SMTP_REMOTE2 252 #define SMTP_INVALID 550 #define SMTP_DATA 354 #define SMTP_OK SMTP_LOCAL #define POP_OK 1 #define emacs (qtype == 2) #define pm (qtype == 1) #define uupc (qtype == 0) int port = SMTP_PORT; int pport = POP3_PORT; int leave = 0, new = 0, kill = 0, direct = 1, qtype = 0, pop3 = 0, smtp = 1, verbose = 4; char *host = NULL, *pophost = NULL, *domain = NULL, *from0 = NULL, *username, *password, *queue = "*.out", *mail; char /* SMTP commands */ *helo = "HELO %s\n", *from = "MAIL FROM: %s\n", *to = "RCPT TO: %s\n", *datastart = "DATA\n", *dataend = "\n.\n", *vrfy = "VRFY %s\n", /* Common (SMTP and POP3) commands */ *quit = "QUIT\n", *rset = "RSET\n", /* POP3 commands */ *user = "USER %s\n", *pass = "PASS %s\n", *list = "LIST\n", *last = "LAST\n", *retr = "RETR %ld\n", *dele = "DELE %ld\n"; struct list { long msgnum, msgsize; }; long POP_RESP_VALUE, LAST_MSG; struct list *msglist = NULL; long maxmsg = 0; char *startmsg = "Starting %s at %s %s", *tcpmsg = "TCP connection to %s:%d %s %s", *swmes = "Switching to %s mode %s"; char *error[9] = { "Oops!", "Hostname is not defined", "Can't resolve SMTP/POP3 host address", "TCP error", "Outbound mail queue directory is not specified", "Inbound mail directory is not specified", "Sorry, now direct SMTP access is not implemented", "POP3 server address is not specified", "-Undefined-" }; time_t timer; FILE *log = NULL; popexit(n) int n; { char *errfrmt = "Aborted: %s, %s"; time(&timer); if(verbose >= 2) fprintf(stderr,errfrmt,error[n == -1 ? 0: n],ctime(&timer)); if(log) fprintf(log,errfrmt,error[n == -1 ? 0 : n],ctime(&timer)); if(log) fclose(log); exit(n); } int status; int smtp_resp(socket) tcp_Socket *socket; { char buffer[4], ch; int i, smtp_stat = 555; sock_tick(socket,&status); for (i = 0;;) { sock_wait_input(socket, sock_delay, NULL, &status); if(sock_read(socket, &ch, 1) <= 0) break; if(verbose >= 8) { if(isprint(ch) || isspace(ch))putc(ch,stderr); else putc('.',stderr); } if(ch == '\n') break; if(isspace(ch) && i < 4) buffer[i++] = ch; if(isdigit(ch) && i < 3) buffer[i++] = ch; } sscanf(buffer,"%d",&smtp_stat); sock_err: return smtp_stat; } int pop_resp(socket) tcp_Socket *socket; { char buffer[32], ch; int i, pop_stat = 0; POP_RESP_VALUE = 0; sock_tick(socket,&status); for (i = 0;;) { sock_wait_input(socket, sock_delay, NULL, &status); if(sock_read(socket, &ch, 1) <= 0) break; if(verbose >= 8) { if(isprint(ch) || isspace(ch))putc(ch,stderr); else putc('.',stderr); } if(ch == '\n') break; if(i < 31) buffer[i++] = ch; } buffer[i] = '\0'; pop_stat = !strncmp(buffer,"+OK ",4); sscanf(buffer,"+OK %ld",&POP_RESP_VALUE); sock_err: return pop_stat; } struct list *pop_list(socket) tcp_Socket *socket; { char buffer[32], ch; int i, EOL = 1; struct list *msglist1; if(msglist) free(msglist), maxmsg = 0; msglist1 = msglist = malloc(sizeof(struct list)); sock_tick(socket,&status); for (i = 0;;) { sock_wait_input(socket, sock_delay, NULL, &status); if(sock_read(socket, &ch, 1) <= 0) break; if(i < 31) buffer[i++] = ch; if(ch == '\r') continue; if(verbose >= 10) { if(isprint(ch) || isspace(ch))putc(ch,stderr); else putc('.',stderr); } if(ch == '\n') { if(EOL == 2) break; if(!EOL) EOL++; if(msglist1) { buffer[i] = '\0'; sscanf(buffer,"%ld %ld", &msglist[maxmsg].msgnum, &msglist[maxmsg].msgsize); i = 0; msglist1 = realloc(msglist,(maxmsg + 2)* sizeof(struct list)); if(msglist1) { msglist = msglist1; maxmsg++; msglist[maxmsg].msgnum = 0; msglist[maxmsg].msgsize = 0; } } } else if(EOL == 1 && ch == '.') EOL ++; else EOL = 0; } sock_err: return maxmsg? msglist : NULL; } void pop_msg(socket,msg,mbox) tcp_Socket *socket; FILE *msg; int mbox; { char ch, *magic = "From "; int i, j, EOL = 0; sock_tick(socket,&status); for (i = 0, j = 0;; i++) { sock_wait_input(socket, sock_delay, NULL, &status); if(sock_read(socket, &ch, 1) <= 0) break; if(ch == '\r') continue; if(EOL == 1 && ch == '.') {EOL++; continue;} if(verbose >= 10) { if(isprint(ch) || isspace(ch)) putc(ch,stderr); else putc('.',stderr); } if(!mbox || i != j) putc(ch,msg); else { if(ch == magic[i]) { if(!magic[++j]) {fprintf(msg,">%s",magic); j = 0;} } else { int k; for(k=0; k= 4) fprintf(stderr,"Close current link\n"); sock_close(socket); if(log) { timer = time(&timer); fprintf(log,"Actual TCP conncection closed %s",ctime(&timer)); } sock_wait_closed(socket, sock_delay, NULL, status); sock_err: ; } longword tcpopen(host,port,socket,status) char *host; int port, *status; tcp_Socket *socket; { longword hostaddr = 0; if(verbose >= 4) fprintf(stderr,"Trying to open %s:%d ",host,port); if((hostaddr = resolve(host)) == 0) { if(verbose >= 2) fprintf(stderr,"... can't resolve %s\n",host); return 0; } if(verbose >= 4) fprintf(stderr,"... resolved "); if(!tcp_open(socket, 0, hostaddr, port, NULL)) { if(verbose >= 2) fprintf(stderr,"... can't open tcp connection\n"); return 0; } if(verbose >= 4) fprintf(stderr,"... waiting "); sock_wait_established(socket, sock_delay, NULL, status); sock_tick(socket, status); if(verbose >= 4) fprintf(stderr,"... established!\n"); if(log) { timer = time(&timer); fprintf(log,tcpmsg,host,port,"established",ctime(&timer)); } return hostaddr; sock_err: if(verbose >= 2) fprintf(stderr,"... no answer\n"); tcpclose(socket,status); return 0; } procarg(arg) char *arg; { static int swstat = 0; char *cp; if(swstat) { switch(swstat) { case 1: /* host */ host = arg; if((cp = strchr(arg,':')) != NULL) { *cp++ = '\0'; sscanf(cp,"%d",&port); if((cp = strchr(cp,':')) != NULL) sscanf(++cp,"%d",&pport); } break; case 7: /* pop3 host */ pophost = arg; if((cp = strchr(arg,':')) != NULL) { *cp++ = '\0'; sscanf(cp,"%d",&pport); } break; case 2: /* from */ from0 = arg; break; case 3: /* domain */ domain = arg; break; case 4: /* queue */ queue = arg; break; case 5: /* mail */ mail = arg; break; case 6: /* user */ username = arg; password = strchr(arg,':'); if(password) *password++ = '\0'; break; case 8: sscanf(arg,"%d",&verbose); } swstat = 0; } else if(arg[0] == '-') switch(arg[1]) { case 'l': leave = 1; return; case 'h': swstat = 1; direct = 0; return; case 'f': swstat = 2; return; case 'd': swstat = 3; return; case 'q': swstat = 4; if(arg[2] == 'e') /*emacs*/ qtype = 2; if(arg[2] == 'p') /*pm*/ qtype = 1; if(arg[2] == 'u') /*uupc*/ qtype = 0; return; case 'm': swstat = 5; return; case 'p': swstat = 6; return; case 'H': swstat = 7; return; case 'v': swstat = 8; return; case 'n': new = 1; return; case 'k': kill = 1; return; default: swstat = 0; return; } /* protocol */ if(!strcmp(arg,"smtp")) smtp++; else if(!strcmp(arg,"pop3")) smtp--,pop3++; } #define CHK_SOCK if(status) goto sock_err; else sock_tick(&sock,&status); main(argc,argv) int argc; char **argv; { struct ffblk curdta; char host1[128] ="", *cnf_buff, *cnf_name = NULL, buffer[128], buffer1[128], hostname[128] = "", qpath[MAXPATH]; char *cp; int smtp_stat, wmark, ff, ss = 0, MODE = -1, i; longword hostaddr = 0; tcp_Socket sock; timer = time(&timer); if((cp = getenv(LOGFILE))!=NULL) log = fopen(cp,"a"); tcp_config(NULL); if(gethostname(hostname,64) && hostname[0]) { if(verbose >= 1) fprintf(stderr,startmsg,VERSION,hostname,ctime(&timer)); if(log)fprintf(log,startmsg,VERSION,hostname,ctime(&timer)); } else popexit(1); for(i=1; i wmark ) { if((mbox = fopen(qpath,"a")) == NULL) { if(errno == EACCES) { /* Not mbox but directory */ strcat(qpath,"/"); wmark = strlen(qpath); } } else if(log) fprintf(log,"Receiving mail from %s to mbox %s %s", host, qpath, ctime(&timer)); } if((hostaddr = tcpopen(pophost ? pophost : host,pport,&sock,&status)) == 0) popexit(2); if(pop_resp(&sock) != POP_OK) { char *fmt = "POP3 host %s refused our call %s"; timer = time(&timer); if(log) fprintf(log, fmt, host, ctime(&timer)); if(verbose >= 2) fprintf(log, fmt, host, ctime(&timer)); goto AbortSes; } if(username) { if(verbose >= 6) fprintf(stderr,user,username); CHK_SOCK; sock_printf(&sock,user,username); if(pop_resp(&sock) != POP_OK) { char *fmt = "Invalid user name %s %s"; timer = time(&timer); if(log) fprintf(log,fmt, username, ctime(&timer)); if(verbose >= 2) fprintf(stderr,fmt, username, ctime(&timer)); goto AbortSes; } } if(password) { strcpy(buffer1,password); cp = buffer1; while(*cp) *cp++ = '*'; if(verbose >= 6) fprintf(stderr,pass,buffer1); CHK_SOCK; sock_printf(&sock,pass,password); if(pop_resp(&sock) != POP_OK) { char *fmt = "Invalid password %s"; timer = time(&timer); if(log) fprintf(log,fmt, ctime(&timer)); if(verbose >= 2) fprintf(stderr,fmt, ctime(&timer)); goto AbortSes; } } if(verbose >= 6) fprintf(stderr,last); CHK_SOCK; sock_printf(&sock,last); if(pop_resp(&sock) != POP_OK) { char *fmt = "Can't get status of last mail session %s"; timer = time(&timer); if(log) fprintf(log,fmt, ctime(&timer)); if(verbose >= 2) fprintf(stderr,fmt, ctime(&timer)); LAST_MSG = 0; } else LAST_MSG = POP_RESP_VALUE; if(verbose >= 6) fprintf(stderr,list); CHK_SOCK; sock_printf(&sock,list); if(pop_resp(&sock) != POP_OK) { char *fmt = "Can't get list of waiting mail %s"; timer = time(&timer); if(log) fprintf(log, fmt, ctime(&timer)); if(verbose >= 2) fprintf(stderr, fmt, ctime(&timer)); goto AbortSes; } if(pop_list(&sock)) { long i; if(!mbox) { qpath[wmark] = '\0'; strcat(qpath,"lastmsg.num"); if((message = fopen(qpath,"r"))!=NULL) { fscanf(message,"%ld",&maxfile); fclose(message); } } for(i=0; i= 6) fprintf(stderr,dele,msglist[i].msgnum); CHK_SOCK; sock_printf(&sock,dele,msglist[i].msgnum); if(pop_resp(&sock) != POP_OK) { char *fmt = "Can't remove old message #%ld message %s"; timer = time(&timer); if(log) fprintf(log, fmt, msglist[i].msgnum, ctime(&timer)); if(verbose >= 2) fprintf(stderr, fmt, msglist[i].msgnum, ctime(&timer)); } continue; } if(new) continue; } if(!mbox) { do { qpath[wmark] = '\0'; sprintf(qpath+wmark,"%08ld.msg",maxfile++); fd = open(qpath, O_WRONLY | O_CREAT | O_EXCL, S_IREAD | S_IWRITE); } while(fd == -1 && errno == EEXIST); if(fd == -1) { char *fmt = "Can't create file %s to store incoming message, skip it %s"; SkipIt: timer = time(&timer); if(log) fprintf(log, fmt, qpath, ctime(&timer)); if(verbose >= 2) fprintf(stderr, fmt, qpath, ctime(&timer)); break; } if((message = fdopen(fd,"w"))==NULL) goto SkipIt; } else message = mbox; if(verbose >= 6) fprintf(stderr,retr,msglist[i].msgnum); CHK_SOCK; sock_printf(&sock,retr,msglist[i].msgnum); if(pop_resp(&sock) != POP_OK) { char *fmt = "Can't retrieve incoming message %s"; timer = time(&timer); if(log) fprintf(log, fmt, ctime(&timer)); if(verbose >= 2) fprintf(stderr, fmt, ctime(&timer)); fclose(message); if(!mbox) unlink(qpath); goto AbortSes; } timer = time(&timer); { char *fmt = "Receiving message #%ld into %s %s %s"; if(log) fprintf(log, fmt,msglist[i].msgnum, mbox ? "mail box": "file", qpath, ctime(&timer)); if(verbose >= 4) fprintf(stderr, fmt,msglist[i].msgnum, mbox ? "mail box": "file", qpath, ctime(&timer)); } if(mbox) fprintf(message,"From send-mail@%s %s",host,ctime(&timer)); /* My fantasy ^^^^^^^^^ */ pop_msg(&sock,message,(mbox != NULL)); if(!mbox) fclose(message); if(!leave) { if(verbose >= 6) fprintf(stderr,dele,msglist[i].msgnum); CHK_SOCK; sock_printf(&sock,dele,msglist[i].msgnum); if(pop_resp(&sock) != POP_OK) { char *fmt = "Can't remove on server received message %s"; timer = time(&timer); if(log) fprintf(log, fmt, ctime(&timer)); if(verbose >= 2) fprintf(stderr, fmt, ctime(&timer)); goto AbortSes; } } } } else { char *fmt = "No waiting mail %s"; timer = time(&timer); if(log) fprintf(log, fmt, ctime(&timer)); if(verbose >= 4) fprintf(stderr, fmt, ctime(&timer)); goto AbortSes; } qpath[wmark] = '\0'; strcat(qpath,"lastmsg.num"); if(!mbox && (message = fopen(qpath,"w")) != NULL) { fprintf(message,"%ld",maxfile); fclose(message); } if(mbox) fclose(mbox); if(verbose >= 6) fprintf(stderr,quit); CHK_SOCK; sock_printf(&sock,quit); if(pop_resp(&sock) != POP_OK) { timer = time(&timer); if(log) fprintf(log,"Server %s don't accept QUIT command, exiting anyway %s", host , ctime(&timer)); goto AbortSes; } timer = time(&timer); if(log) fprintf(log,"POP3 session with %s finished %s", host , ctime(&timer)); goto AbortSes; /* ^^^ POP3 HERE ^^^ */ } RetryScan: if((ff = findfirst(queue,&curdta,NULL))==NULL) { if((hostaddr = tcpopen(host,port,&sock,&status)) == 0) popexit(2); if(smtp_resp(&sock) != SMTP_INTRO) goto AbortSes; if(verbose >= 6) fprintf(stderr,helo,hostname); CHK_SOCK; sock_printf(&sock,helo,domain); if(smtp_resp(&sock) != SMTP_OK) goto AbortSes; do { FILE *qfile; char *cp; int STOP, recnum = 0, destnum = 0; qpath[wmark] = '\0'; strcat(qpath,curdta.ff_name); if(verbose >= 4) fprintf(stderr,"Processing queue file -->%s\n",qpath); if(log) { timer = time(&timer); fprintf(log,"Processing outbound queue file %s %s",qpath,ctime(&timer)); } host1[0] = '\0'; if((qfile = fopen(qpath,"r"))==NULL) continue; if(emacs) fscanf(qfile,"%128[^\n]\n",host1); /* skip host ? */ if(uupc) { if(!fscanf(qfile,"%128[^\n]\n",cp = buffer)) cp = NULL; } else { if(!fscanf(qfile,pm ? "$$ %128[^\n]\n" : "%128[^\n]\n",cp = buffer)) cp = NULL; /* PM specific procession -- really must be more accurate :-( */ if(pm && cp) /* strip () - comments */ { char *c = strchr(cp,'('); if(c) { *c = '\0'; while(cp < c && isspace(*--c)) *c = '\0'; } } } if(from0) cp = from0; if(!cp || !*cp) goto AbortSend; if(verbose >= 6) fprintf(stderr,from,cp); strncpy(buffer1,cp,128); CHK_SOCK; sock_printf(&sock,from,cp); if(smtp_resp(&sock) != SMTP_OK || feof(qfile)) { AbortSend: fclose(qfile); if(verbose >= 6) fprintf(stderr,rset); CHK_SOCK; sock_printf(&sock,rset); if(smtp_resp(&sock) != SMTP_OK); if(log) { timer = time(&timer); fprintf(log,"Message from %s is refused by %s %s",buffer1,host,ctime(&timer)); } continue; } STOP = 0; do { if(emacs) {if (feof(qfile)) STOP = 1;} if(!fscanf(qfile,pm ? "T %128[^\n]\n" : "%128[^\n]\n",cp = buffer)) buffer[0] = '\0'; if(uupc) {if (!strcmp(cp,"<>")) STOP = 1;} else if(pm) {if (!*cp) STOP = 1;} /* else if(emacs) {if (feof(qfile)) STOP = 1;}*/ if(!STOP) { destnum++; if(verbose >= 6) fprintf(stderr,to,cp); CHK_SOCK; sock_printf(&sock,to,cp); if((smtp_stat = smtp_resp(&sock)) != SMTP_LOCAL && smtp_stat != SMTP_REMOTE1 && smtp_stat != SMTP_REMOTE2 ) { if(log) { timer = time(&timer); fprintf(log,"Message from %s to %s was not accepted by host %s %s", buffer1, cp, host, ctime(&timer)); } continue; } recnum++; } } while(!STOP); if(emacs) { fclose(qfile); qpath[strlen(qpath)-3] = '\0'; strcat(qpath,"txt"); qfile = fopen(qpath,"r"); } if(!recnum) { if(log) fprintf(log,"No valid recipients. "); goto AbortSend; } if(verbose >= 6) fprintf(stderr,datastart,cp); CHK_SOCK; sock_printf(&sock,datastart,cp); if(smtp_resp(&sock) != SMTP_DATA) { if(log) { timer = time(&timer); fprintf(log,"Host %s can't accept message data from %s %s", host, buffer1, ctime(&timer)); } fclose(qfile); if(verbose >= 6) fprintf(stderr,rset); CHK_SOCK; sock_printf(&sock,rset); if(smtp_resp(&sock) != SMTP_OK) { if(log) fprintf(log,"Can't reset connection. "); goto AbortSend; } continue; } /* Here we ready to send data! */ while(fgets(buffer,128,qfile)) { if(buffer[0] == '.') { if(verbose >= 10) fprintf(stderr,".%s",buffer); CHK_SOCK; sock_printf(&sock,".%s",buffer); } else { if(verbose >= 10) fprintf(stderr,"%s",buffer); CHK_SOCK; sock_printf(&sock,"%s",buffer); } while(buffer[strlen(buffer) - 1] != '\n') { if(fgets(buffer,128,qfile)) { if(verbose >= 10) fprintf(stderr,"%s",buffer); CHK_SOCK; sock_printf(&sock,"%s",buffer); } else break; } } if(verbose >= 10) fprintf(stderr,"\n.\n"); CHK_SOCK; sock_printf(&sock,"\n.\n"); if(smtp_resp(&sock) != SMTP_OK) { if(log) { timer = time(&timer); fprintf(log,"Message data from %s is not accepted by %s %s", buffer1, host, ctime(&timer)); } continue; } fclose(qfile); timer = time(&timer); if(recnum == destnum) { char *dm = "Deleting %s\n"; if(verbose >= 4) fprintf(stderr,dm,qpath); if(log) fprintf(log,"Deleting sent message %s %s",qpath,ctime(&timer)); unlink(qpath); if(emacs) { qpath[strlen(qpath)-3] = '\0'; strcat(qpath,"wrk"); if(verbose >= 4) fprintf(stderr,dm,qpath); unlink(qpath); } } else { char *hm = "Set mode to hidden %s\n"; if(verbose >= 4) fprintf(stderr,hm,qpath); if(log) fprintf(log,"Warning: message %s was not delivered to all recepients, change queue file mode to hidden %s", qpath,ctime(&timer)); _chmod(qpath,1,FA_RDONLY | FA_HIDDEN); if(emacs) { qpath[strlen(qpath)-3] = '\0'; strcat(qpath,"wrk"); if(verbose >= 4) fprintf(stderr,hm,qpath); _chmod(qpath,1,FA_RDONLY | FA_HIDDEN); } } } while (findnext(&curdta)==NULL); if(verbose >= 6) fprintf(stderr,quit); CHK_SOCK; sock_printf(&sock,quit); if(smtp_resp(&sock) != SMTP_GOODBY); AbortSes: timer = time(&timer); if(log) { fprintf(log,"Mail session finished %s",ctime(&timer)); } if(hostaddr) tcpclose(&sock,&status); if(!MODE && pop3) goto SwProto; } if(ff && !ss && !MODE) { char c = qpath[strlen(queue) - 1]; if(strchr(queue,'*') || strchr(queue,'?')) goto ChkMode; strcpy(qpath,queue); if(c != '\\' && c != '/') strcat(qpath,"/"); wmark = strlen(qpath); strcat(queue = qpath, emacs ? "*.wrk" : "*.*"); ss = 1; goto RetryScan; } ChkMode: if(!MODE && pop3) goto SwProto; if(log) fclose(log); exit(0); sock_err: if(status == 1) popexit(3); else { char *fmt = "TCP timeout. Trying to close session %s"; timer = time(&timer); if(verbose >= 2) fprintf(stderr,fmt,ctime(&timer)); if(log) fprintf(log,fmt,ctime(&timer)); goto AbortSes; } }