精華區beta SetupBBS 關於我們 聯絡資訊
/*-------------------------------------------------------*/ /* util/mailpost.c ( NTHU CS MapleBBS Ver 2.36 ) */ /*-------------------------------------------------------*/ /* target : (1) general user E-mail post 到看板 */ /* (2) BM E-mail post 到精華區 */ /* (3) 自動審核身份認證信函之回信 */ /* create : 95/03/29 */ /* update : 95/12/15 */ /*-------------------------------------------------------*/ #include <stdio.h> #include <ctype.h> #include <sys/stat.h> #include <sys/file.h> #include <fcntl.h> #include <time.h> #include "bbs.h" char *fn_passwd = ".PASSWDS"; char *crypt(); #define _BBS_UTIL_C_ #include "cache.c" #include "record.c" userec record; char myfrom[128], mysub[128], myname[128], mypasswd[128], myboard[128], mytitle[128], mydigest[128]; #define JUNK 0 #define NET_SAVE 1 #define LOCAL_SAVE 2 int mymode = JUNK; #define LOG_FILE "etc/mailog" /* ----------------------------------------------------- */ /* buffered I/O for stdin */ /* ----------------------------------------------------- */ #define POOL_SIZE 8192 #define LINE_SIZE 512 char pool[POOL_SIZE]; char mybuf[LINE_SIZE]; int pool_size = POOL_SIZE; int pool_ptr = POOL_SIZE; int readline(buf) char *buf; { register char ch; register int len, bytes; len = bytes = 0; do { if (pool_ptr >= pool_size) { if (pool_size = fread(pool, 1, POOL_SIZE, stdin)) pool_ptr = 0; else break; } ch = pool[pool_ptr++]; bytes++; if (ch == '\r') continue; buf[len++] = ch; } while (ch != '\n' && len < (LINE_SIZE - 1)); buf[len] = '\0'; return bytes; } /* ----------------------------------------------------- */ /* record etc/mailog for management */ /* ----------------------------------------------------- */ void mailog(mode, msg) char *mode, *msg; { FILE *fp; if (fp = fopen(LOG_FILE, "a")) { time_t now; struct tm *p; time(&now); p = localtime(&now); fprintf(fp, "%02d/%02d/%02d %02d:%02d:%02d <%s> %s\n", p->tm_year, p->tm_mon + 1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec, mode, msg); fclose(fp); } } void str_lower(t, s) char *t, *s; { register uschar ch; do { ch = *s++; *t++ = (ch >= 'A' && ch <= 'Z') ? ch | 32 : ch; } while (ch); } int valid_ident(ident) char *ident; { static char *invalid[] = {"unknown@", "root@", "gopher@", "bbs@", "@bbs", "guest@", "@ppp", "@slip", NULL}; char buf[128]; int i; str_lower(buf, ident); for (i = 0; invalid[i]; i++) if (strstr(buf, invalid[i])) return 0; return 1; } int get_userec(usrnum, record) int usrnum; userec *record; { int fh; if ((fh = open(fn_passwd, O_RDWR)) == -1) { printf(":Err: unable to open %s file.\n", fn_passwd); } else { lseek(fh, (usrnum - 1) * sizeof(userec), SEEK_SET); read(fh, record, sizeof(userec)); } return fh; } /* ------------------------------------------------------- */ /* 記錄驗證資料:user 有可能正在線上,所以寫入檔案以保周全 */ /* ------------------------------------------------------- */ void justify_user() { FILE *fp; char buf[80]; sprintf(buf, "home/%s/email", record.userid); if (fp = fopen(buf, "w")) { fprintf(fp, "%s\n", myfrom); fclose(fp); } strncpy(record.justify, myfrom, 43); record.userlevel |= PERM_LOGINOK; } void verify_user(magic) char *magic; { char *ptr, *next, ch; ushort checksum, usrnum; int fh; if (ptr = (char *) strchr(magic, '(')) { *ptr++ = '\0'; if (next = (char *) strchr(ptr, ':')) { *next++ = '\0'; usrnum = atoi(ptr) - MAGIC_KEY; if (ptr = (char *) strchr(next, ')')) { *ptr++ = '\0'; checksum = atoi(next); if ((fh = get_userec(usrnum, &record)) == -1) return; if (!ci_strcmp(magic, record.userid)) { ptr = myfrom; /* record.email; */ while (ch = *ptr++) { if (ch <= ' ') break; if (ch >= 'A' && ch <= 'Z') ch |= 0x20; usrnum = (usrnum << 1) ^ ch; } if ((usrnum == checksum) && valid_ident(myfrom)) { justify_user(); lseek(fh, -(sizeof record), SEEK_CUR); write(fh, &record, sizeof record); sprintf(mybuf, "[%s]%s", record.userid, myfrom); mailog("verify", mybuf); } } close(fh); } } } } int post_article() { fileheader header; char index[64], fpath[80]; FILE *fidx; struct stat st; sprintf(fpath, "boards/%s", mymode ? myboard : "junk"); sprintf(index, "%s/.DIR", fpath); if (mymode == JUNK) { pool_ptr = 0; if (!readline(mybuf)) exit(0); if (!*myname) strcpy(myname, "系統備忘錄"); if (!*mytitle) strcpy(mytitle, *mysub ? mysub : "[原信照登]"); } else { char msgbuf[256]; sprintf(msgbuf, "[%s]%s => %s", record.userid, myfrom, myboard); mailog("mailpost", msgbuf); } #ifdef VERBOSE if (stat(fpath, &st) == -1) { if (mkdir(fpath, 0755) == -1) { printf("board dir create error\n"); return -1; } } else if (!(st.st_mode & S_IFDIR)) { printf("board dir error\n"); return -1; } printf("dir: %s\n", fpath); #endif stampfile(fpath, &header); fidx = fopen(fpath, "w"); printf("post to %s\n", fpath); if (mymode) { time_t now = time(NULL); fprintf(fidx, "作者: %s (%s) %s: %s\n標題: %s\n時間: %s\n", myname, record.username, (mymode == LOCAL_SAVE ? "站內" : "看板"), myboard, mytitle, ctime(&now)); if (mymode == LOCAL_SAVE) { header.savemode = 'L'; /* local article */ header.filemode = FILE_LOCAL; } else header.savemode = 'S'; /* 須轉信 */ } do { fputs(mybuf, fidx); } while (readline(mybuf)); fclose(fidx); strncpy(header.owner, myname, IDLEN); strncpy(header.title, mytitle, TTLEN); append_record(index, &header, sizeof(header)); if ((mymode == NET_SAVE) && (fidx = fopen("innd/out.bntp", "a"))) { fprintf(fidx, "%s\t%s\t%s\t%s\t%s\n", myboard, header.filename, myname, record.username, mytitle); fclose(fidx); } return 0; } int digest_article() { char *str_mandex = "/.Names"; char index[256], mpath[80], *ptr, ch; FILE *fh; time_t now; struct tm *ptime; sprintf(mybuf, "[%s]%s => %s", record.userid, myfrom, myboard); mailog("digest", mybuf); sprintf(mpath, "man/%s", myboard); sprintf(index, "%s%s", mpath, str_mandex); if (!(fh = fopen(index, "r"))) { printf(":Err: Unable to digest in %s.\n", myboard); return -1; } ch = 1; while (fgets(index, 256, fh)) { if (!strncmp(index, "# Title=", 8)) { ch = 0; break; } } fclose(fh); if (ch) return -1; if (ptr = (char *) strstr(index, myname)) { ch = ptr[-1]; if ((ch == '[') || (ch == '/')) { ch = ptr[strlen(myname)]; if ((ch == ']') || (ch == '/')) { /* truncate long lines */ mydigest[79] = mytitle[62] = '\0'; if (ptr = (char *) strchr(mydigest, ' ')) *ptr = '\0'; sprintf(index, "%s/%s", mpath, mydigest); if (fh = fopen(index, "a+")) { printf("digest: %s\n", index); do { fputs(mybuf, fh); } while (readline(mybuf)); fclose(fh); now = time(NULL); ptime = localtime(&now); ptr = (char *) strrchr(index, '/'); strcpy(mydigest, ptr + 1); strcpy(ptr, str_mandex); fh = fopen(index, "a+"); fprintf(fh, "Name=◇ %s\nDate=%02d/%02d/%02d\nPath=%s\n\n", mytitle, ptime->tm_mon + 1, ptime->tm_mday, ptime->tm_year, mydigest); fclose(fh); return 0; } } } } return -1; } int mailpost() { int uid, fh, dirty; char *ip, *ptr; char *token, *key; /* parse header */ readline(mybuf); if (strncasecmp(mybuf, "From", 4)) return post_article(); /* junk */ dirty = *myfrom = *mysub = *myname = *mypasswd = *myboard = *mytitle = *mydigest = 0; while (!*myname || !*mypasswd || !*myboard || !*mytitle) { if (mybuf[0] == '#') { key = mybuf + 1; /* skip trailing space */ if (ptr = strchr(key, '\n')) { do { *ptr = '\0'; fh = *(--ptr); } while (fh == ' ' || fh == '\t'); } /* split token & skip leading space */ if (token = strchr(key, ':')) { *token = '\0'; do { fh = *(++token); } while (fh == ' ' || fh == '\t'); } if (!ci_strcmp(key, "name")) { strcpy(myname, token); } else if (!ci_strcmp(key, "passwd") || !ci_strcmp(key, "password") || !ci_strcmp(key, "passward")) { strcpy(mypasswd, token); } else if (!ci_strcmp(key, "board")) { strcpy(myboard, token); } else if (!ci_strcmp(key, "title") || !ci_strcmp(key, "subject")) { strcpy(mytitle, token); } else if (!ci_strcmp(key, "digest")) { strcpy(mydigest, token); } else if (!ci_strcmp(key, "local")) { mymode = LOCAL_SAVE; } } else if (!strncasecmp(mybuf, "From", 4)) { str_lower(myfrom, mybuf + 4); if (strstr(myfrom, "mailer-daemon")) return post_article(); if ((ip = strchr(mybuf, '<')) && (ptr = strrchr(ip, '>'))) { *ptr = '\0'; if (ip[-1] == ' ') ip[-1] = '\0'; ptr = (char *) strchr(mybuf, ' '); while (*++ptr == ' '); sprintf(myfrom, "%s (%s)", ip + 1, ptr); } else { strtok(mybuf, " "); strcpy(myfrom, (char *) strtok(NULL, " ")); } } else if (!strncmp(mybuf, "Subject: ", 9)) { /* audit justify mail */ if (ptr = strstr(mybuf, "[MapleBBS]To ")) { verify_user(ptr + 13); return -1; } if (ptr = strstr(mybuf, "[MapleBridge]To ")) { verify_user(ptr + 16); return -1; } if (ptr = strchr(token = mybuf + 9, '\n')) *ptr = '\0'; strcpy(mysub, token); } if ((++dirty > 50) || !readline(mybuf)) { mymode = JUNK; return post_article(); /* junk */ } } dirty = 0; /* check if the userid is in our bbs now */ if (!(uid = searchuser(myname))) { printf("BBS user <%s> not existed\n", myname); return -1; } if ((fh = get_userec(uid, &record)) == -1) { return -1; } /* check password */ key = crypt(mypasswd, record.passwd); if (strcmp(key, record.passwd)) { printf(":Err: user [%s] password incorrect!\n", myname); close(fh); return -1; } if (valid_ident(myfrom)) { /* ------------------------------ */ /* 順便記錄 user's E-mail address */ /* ------------------------------ */ justify_user(); dirty = YEA; } /* check permission */ if (!*mydigest) { if (mymode != LOCAL_SAVE) mymode = NET_SAVE; if (!strstr(myboard, "test")) { record.numposts++; dirty = YEA; } } while (mybuf[0] && mybuf[0] != '\n') readline(mybuf); while (mybuf[0] == '\n') readline(mybuf); if (dirty && mybuf[0]) { lseek(fh, -sizeof(struct userec), SEEK_CUR); write(fh, &record, sizeof(struct userec)); } close(fh); if (mybuf[0]) return (*mydigest) ? digest_article() : post_article(); mymode = JUNK; return post_article(); } void main(void) { chdir(BBSHOME); if (mailpost()) { /* eat mail queue */ while (fread(pool, 1, POOL_SIZE, stdin)); } exit(0); }