/* $Id$ */ #include "bbs.h" #define SPOOL BBSHOME "/out" #define INDEX SPOOL "/.DIR" #define NEWINDEX SPOOL "/.DIR.sending" #define FROM ".bbs@" MYHOSTNAME #define SMTPPORT 25 char *smtpname, *hiname; int smtpport, hiport; char disclaimer[1024]; /* qp_encode() modified from mutt-1.5.7/rfc2047.c q_encoder() */ const char MimeSpecials[] = "@.,;:<>[]\\\"()?/= \t"; char * qp_encode (char *s, size_t slen, const char *d, const char *tocode) { char hex[] = "0123456789ABCDEF"; char *s0 = s; memcpy (s, "=?", 2), s += 2; memcpy (s, tocode, strlen (tocode)), s += strlen (tocode); memcpy (s, "?Q?", 3), s += 3; assert(s-s0+3= 0x7f || c < 0x20 || c == '_' || strchr (MimeSpecials, c)) { *s++ = '='; *s++ = hex[(c & 0xf0) >> 4]; *s++ = hex[c & 0x0f]; } else *s++ = c; } memcpy (s, "?=", 2), s += 2; *s='\0'; return s0; } int waitReply(int sock) { char buf[256]; if(read(sock, buf, sizeof(buf)) <= 0) return -1; else return buf[0] - '0'; } int sendRequest(int sock, char *request) { return write(sock, request, strlen(request)) < 0 ? -1 : 0; } int connectMailServer(char *servername, int serverport) { int sock; struct sockaddr_in addr; if((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return -1; } memset(&addr, 0, sizeof(addr)); #ifdef __FreeBSD__ addr.sin_len = sizeof(addr); #endif addr.sin_family = AF_INET; addr.sin_port = htons(serverport); addr.sin_addr.s_addr = inet_addr(servername); if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { printf("servername: %s\n", servername); perror(servername); close(sock); return -1; } if(waitReply(sock) != 2) { close(sock); return -1; } if(sendRequest(sock, "helo " MYHOSTNAME "\n") || waitReply(sock) != 2) { close(sock); return -1; } else return sock; } void disconnectMailServer(int sock) { sendRequest(sock, "quit\n"); /* drop the reply :p */ close(sock); } void doSendBody(int sock, FILE *fp, char *from, char *to, char *subject) { int n; char buf[2048]; char subject_qp[STRLEN*3+100]; static int starttime = -1, msgid = 0; if( starttime == -1 ){ srandom(starttime = (int)time(NULL)); msgid = random(); } n = snprintf(buf, sizeof(buf), "From: %s <%s>\r\n" "To: %s\r\n" "Subject: %s\r\n" "X-Sender: outmail of pttbbs\r\n" "Mime-Version: 1.0\r\n" "Content-Type: text/plain; charset=\"big5\"\r\n" "Content-Transfer-Encoding: 8bit\r\n" "Message-Id: <%d.%x.outmail@" MYHOSTNAME ">\r\n" "X-Disclaimer: %s\r\n\r\n", from, from, to, qp_encode(subject_qp, sizeof(subject_qp), subject, "big5"), starttime, (msgid += (int)(random() >> 24)), disclaimer); write(sock, buf, n); while(fgets(buf, sizeof(buf), fp)) { if(buf[0] == '.' && buf[1] == '\n') strcpy(buf, "..\n"); write(sock, buf, strlen(buf)); } } void doSendMail(int sock, FILE *fp, char *from, char *to, char *subject) { char buf[256]; snprintf(buf, sizeof(buf), "mail from: %s\n", from); if(sendRequest(sock, buf) || waitReply(sock) != 2) return; snprintf(buf, sizeof(buf), "rcpt to: %s\n", to); if(sendRequest(sock, buf) || waitReply(sock) != 2) return; if(sendRequest(sock, "data\n") || waitReply(sock) != 3) return; doSendBody(sock, fp, from, to, subject); if(sendRequest(sock, "\n.\n") || waitReply(sock) != 2) return; } void sendMail() { int fd, smtpsock, hisock; MailQueue mq; if(access(NEWINDEX, R_OK | W_OK)) { if(link(INDEX, NEWINDEX) || unlink(INDEX)) /* nothing to do */ return; } smtpsock = connectMailServer(smtpname, smtpport); hisock = (hiname != NULL) ? connectMailServer(hiname, hiport) : -1; if( smtpsock < 0 && hisock >= 0 ){ smtpsock = hisock; hisock = -1; } if( smtpsock < 0 && hisock < 0 ){ fprintf(stderr, "connecting to relay server failure...\n"); return; } fd = open(NEWINDEX, O_RDONLY); flock(fd, LOCK_EX); while(read(fd, &mq, sizeof(mq)) > 0) { FILE *fp; char buf[256]; snprintf(buf, sizeof(buf), "%s%s", mq.sender, FROM); if((fp = fopen(mq.filepath, "r"))) { setproctitle("outmail: sending %s", mq.filepath); if( hisock >= 0 && !strstr(mq.rcpt, ".edu.tw") && !strstr(mq.rcpt, ".twbbs.org") && !strstr(mq.rcpt, "ptt.cc") && !strstr(mq.rcpt, "ptt2.cc") ){ printf("mailto: %s, relay server: %s:%d\n", mq.rcpt, hiname, hiport); doSendMail(hisock, fp, buf, mq.rcpt, mq.subject); } else{ printf("mailto: %s, relay server: %s:%d\n", mq.rcpt, smtpname, smtpport); doSendMail(smtpsock, fp, buf, mq.rcpt, mq.subject); } fclose(fp); unlink(mq.filepath); } else { perror(mq.filepath); } } flock(fd, LOCK_UN); close(fd); unlink(NEWINDEX); disconnectMailServer(smtpsock); if( hisock >= 0 ) disconnectMailServer(hisock); } void listQueue() { int fd; if((fd = open(INDEX, O_RDONLY)) >= 0) { int counter = 0; MailQueue mq; flock(fd, LOCK_EX); while(read(fd, &mq, sizeof(mq)) > 0) { printf("%s:%s -> %s:%s\n", mq.filepath, mq.username, mq.rcpt, mq.subject); counter++; } flock(fd, LOCK_UN); close(fd); printf("\nTotal: %d mails in queue\n", counter); } else { perror(INDEX); } } void wakeup(int s) { } void parseserver(char *sx, char **name, int *port) { char *save = strdup(sx); char *ptr; if( (ptr = strstr(save, ":")) == NULL ){ *name = strdup(save); *port = 25; } else{ *ptr = 0; *name = strdup(save); *port = atoi(ptr + 1); } free(save); } int main(int argc, char **argv, char **envp) { int ch; Signal(SIGHUP, wakeup); initsetproctitle(argc, argv, envp); if(chdir(BBSHOME)) return 1; while((ch = getopt(argc, argv, "qhs:o:")) != -1) { switch(ch) { case 's': parseserver(optarg, &smtpname, &smtpport); break; case 'o': parseserver(optarg, &hiname, &hiport); break; case 'q': listQueue(); return 0; default: printf("usage:\toutmail [-qh] -s host[:port] [-o host[:port]]\n" "\t-q\tlistqueue\n" "\t-h\thelp\n" "\t-s\tset default smtp server to host[:port]\n" "\t-o\tset non-Tanet smtp server to host[:port]\n"); return 0; } } if( smtpname == NULL ){ #ifdef RELAY_SERVER_IP smtpname = RELAY_SERVER_IP; #else smtpname = "127.0.0.1"; #endif smtpport = 25; } qp_encode(disclaimer, sizeof(disclaimer), "[" BBSNAME "]對本信內容恕不負責", "big5"); for(;;) { sendMail(); setproctitle("outmail: sleeping"); sleep(60); /* send mail every minute */ } return 0; }