diff options
author | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2008-03-20 19:33:49 +0800 |
---|---|---|
committer | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2008-03-20 19:33:49 +0800 |
commit | 6c7b18b32d87c2a835f7e5c48faac4a8ad44668b (patch) | |
tree | e88e1b2b1007f4ddcd348a4a1bf1d13c515c4564 /console/record.c | |
parent | f59699c22c130373cda3cc4cb6fab5bae510bd5a (diff) | |
download | pttbbs-piaip.newlayout.tar pttbbs-piaip.newlayout.tar.gz pttbbs-piaip.newlayout.tar.bz2 pttbbs-piaip.newlayout.tar.lz pttbbs-piaip.newlayout.tar.xz pttbbs-piaip.newlayout.tar.zst pttbbs-piaip.newlayout.zip |
- (internal/exp) first draft of new layoutpiaip.newlayout
git-svn-id: http://opensvn.csie.org/pttbbs/branches/piaip.newlayout@4013 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'console/record.c')
-rw-r--r-- | console/record.c | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/console/record.c b/console/record.c new file mode 100644 index 00000000..d50df1d9 --- /dev/null +++ b/console/record.c @@ -0,0 +1,666 @@ +/* $Id$ */ + +#include "bbs.h" + +#undef HAVE_MMAP +#define BUFSIZE 512 + +#define safewrite write + +int +get_num_records(const char *fpath, int size) +{ + struct stat st; + if (stat(fpath, &st) == -1) + { + /* TODO: delete this entry, or mark as read */ + return 0; + } + return st.st_size / size; +} + +int +get_sum_records(const char *fpath, int size) +{ + struct stat st; + int ans = 0; + FILE *fp; + fileheader_t fhdr; + char buf[200], *p; + + // Ptt : should avoid big loop + if ((fp = fopen(fpath, "r"))==NULL) + return -1; + + strlcpy(buf, fpath, sizeof(buf)); + p = strrchr(buf, '/'); + assert(p); + p++; + + while (fread(&fhdr, size, 1, fp)==1) { + strlcpy(p, fhdr.filename, sizeof(buf) - (p - buf)); + if (stat(buf, &st) == 0 && S_ISREG(st.st_mode) && st.st_nlink == 1) + ans += st.st_size; + } + fclose(fp); + return ans / 1024; +} + +int +get_record_keep(const char *fpath, void *rptr, int size, int id, int *fd) +{ + /* 和 get_record() 一樣. 不過藉由 *fd, 可使同一個檔案不要一直重複開關 */ + if (id >= 1 && + (*fd > 0 || + ((*fd = open(fpath, O_RDONLY, 0)) > 0))){ // FIXME leak if *fd==0 + if (lseek(*fd, (off_t) (size * (id - 1)), SEEK_SET) != -1) { + if (read(*fd, rptr, size) == size) { + return 0; + } + } + } + return -1; +} + +int +get_record(const char *fpath, void *rptr, int size, int id) +{ + int fd = -1; + /* TODO merge with get_records() */ + + if (id >= 1 && (fd = open(fpath, O_RDONLY, 0)) != -1) { + if (lseek(fd, (off_t) (size * (id - 1)), SEEK_SET) != -1) { + if (read(fd, rptr, size) == size) { + close(fd); + return 0; + } + } + close(fd); + } + return -1; +} + +int +get_records(const char *fpath, void *rptr, int size, int id, int number) +{ + int fd; + + if (id < 1 || (fd = open(fpath, O_RDONLY, 0)) == -1) + return -1; + + if (lseek(fd, (off_t) (size * (id - 1)), SEEK_SET) == -1) { + close(fd); + return 0; + } + if ((id = read(fd, rptr, size * number)) == -1) { + close(fd); + return -1; + } + close(fd); + return id / size; +} + +int +substitute_record(const char *fpath, const void *rptr, int size, int id) +{ + int fd; + int offset=size * (id - 1); + if (id < 1 || (fd = open(fpath, O_WRONLY | O_CREAT, 0644)) == -1) + return -1; + + lseek(fd, (off_t) (offset), SEEK_SET); + PttLock(fd, offset, size, F_WRLCK); + write(fd, rptr, size); + PttLock(fd, offset, size, F_UNLCK); + close(fd); + + return 0; +} + +/* return index>0 if thisstamp==stamp[index], + * return -index<0 if stamp[index-1]<thisstamp<stamp[index+1], XXX thisstamp ?<>? stamp[index] + * or XXX filename[index]="" + * return 0 if error + */ +int +getindex_m(const char *direct, fileheader_t *fhdr, int end, int isloadmoney) +{ // Ptt: 從前面找很費力 太暴力 + int fd = -1, begin = 1, i, s, times, stamp; + fileheader_t fh; + + int n = get_num_records(direct, sizeof(fileheader_t)); + if( end > n || end<=0 ) + end = n; + stamp = atoi(fhdr->filename + 2); + for( i = (begin + end ) / 2, times = 0 ; + end >= begin && times < 20 ; /* 最多只找 20 次 */ + i = (begin + end ) / 2, ++times ){ + if( get_record_keep(direct, &fh, sizeof(fileheader_t), i, &fd)==-1 || + !fh.filename[0] ) + break; + s = atoi(fh.filename + 2); + if( s > stamp ) + end = i - 1; + else if( s == stamp ){ + close(fd); + if(isloadmoney) + fhdr->multi.money = fh.multi.money; + return i; + } + else + begin = i + 1; + } + + if( times < 20) // Not forever loop. It any because of deletion. + { + close(fd); + return -i; + } + if( fd != -1 ) + close(fd); + return 0; +} + +inline int +getindex(const char *direct, fileheader_t *fhdr, int end) +{ + return getindex_m(direct, fhdr, end, 0); +} + +int +substitute_ref_record(const char *direct, fileheader_t * fhdr, int ent) +{ + fileheader_t hdr; + char fname[PATHLEN]; + int num = 0; + + /* rocker.011018: 串接模式用reference增進效率 */ + if (!(fhdr->filemode & FILE_BOTTOM) && (fhdr->multi.refer.flag) && + (num = fhdr->multi.refer.ref)){ + setdirpath(fname, direct, FN_DIR); + get_record(fname, &hdr, sizeof(hdr), num); + if (strcmp(hdr.filename, fhdr->filename)) { + if((num = getindex_m(fname, fhdr, num, 1))>0) { + substitute_record(fname, fhdr, sizeof(*fhdr), num); + } + } + else if(num>0) { + fhdr->multi.money = hdr.multi.money; + substitute_record(fname, fhdr, sizeof(*fhdr), num); + } + fhdr->multi.refer.flag = 1; + fhdr->multi.refer.ref = num; // Ptt: update now! + } + substitute_record(direct, fhdr, sizeof(*fhdr), ent); + return num; +} + + +/* rocker.011022: 避免lock檔開啟時不正常斷線,造成永久lock */ +#ifndef _BBS_UTIL_C_ +static int +force_open(const char *fname) +{ + int fd; + time4_t expire; + + expire = now - 3600; /* lock 存在超過一個小時就是有問題! */ + + if (dasht(fname) < expire) + return -1; + unlink(fname); + fd = open(fname, O_WRONLY | O_TRUNC, 0644); + + return fd; +} +#endif + +/* new/old/lock file processing */ +typedef struct nol_t { + char newfn[256]; + char oldfn[256]; + char lockfn[256]; +} nol_t; + +#ifndef _BBS_UTIL_C_ +static void +nolfilename(nol_t * n, const char *fpath) +{ + snprintf(n->newfn, sizeof(n->newfn), "%s.new", fpath); + snprintf(n->oldfn, sizeof(n->oldfn), "%s.old", fpath); + snprintf(n->lockfn, sizeof(n->lockfn), "%s.lock", fpath); +} +#endif + +int +delete_records(const char *fpath, int size, int id, int num) +{ + char abuf[BUFSIZE]; + int fi, fo, locksize=0, readsize=0, offset = size * (id - 1), c, d=0; + struct stat st; + + + if ((fi=open(fpath, O_RDONLY, 0)) == -1) + return -1; + + if ((fo=open(fpath, O_WRONLY, 0)) == -1) + { + close(fi); + return -1; + } + + if(fstat(fi, &st)==-1) + { close(fo); close(fi); return -1;} + + locksize = st.st_size - offset; + readsize = locksize - size*num; + if (locksize < 0 ) + { close(fo); close(fi); return -1;} + + PttLock(fo, offset, locksize, F_WRLCK); + if(readsize>0) + { + lseek(fi, offset+size, SEEK_SET); + lseek(fo, offset, SEEK_SET); + while( d<readsize && (c = read(fi, abuf, BUFSIZE))>0) + { + write(fo, abuf, c); + d=d+c; + } + } + close(fi); + ftruncate(fo, st.st_size - size*num); + PttLock(fo, offset, locksize, F_UNLCK); + close(fo); + return 0; + +} + + +int delete_record(const char *fpath, int size, int id) +{ + return delete_records(fpath, size, id, 1); +} + + +#ifndef _BBS_UTIL_C_ +#ifdef SAFE_ARTICLE_DELETE +void safe_delete_range(const char *fpath, int id1, int id2) +{ + int fd, i; + fileheader_t fhdr; + char fullpath[STRLEN], *t; + strlcpy(fullpath, fpath, sizeof(fullpath)); + t = strrchr(fullpath, '/'); + assert(t); + t++; + if( (fd = open(fpath, O_RDONLY)) == -1 ) + return; + for( i = 1 ; (read(fd, &fhdr, sizeof(fileheader_t)) == + sizeof(fileheader_t)) ; ++i ){ + strcpy(t, fhdr.filename); + /* rocker.011018: add new tag delete */ + if (!((fhdr.filemode & FILE_MARKED) || /* 標記 */ + (fhdr.filemode & FILE_DIGEST) || /* 文摘 */ + (id1 && (i < id1 || i > id2)) || /* range */ + (!id1 && Tagger(atoi(t + 2), i, TAG_NIN)))) /* TagList */ + safe_article_delete(i, &fhdr, fpath); + } + close(fd); +} +#endif + +int +delete_range(const char *fpath, int id1, int id2) +{ + fileheader_t fhdr; + nol_t my; + char fullpath[STRLEN], *t; + int fdr, fdw, fd; + int count, dcount=0; + + nolfilename(&my, fpath); + + if ((fd = open(my.lockfn, O_RDWR | O_CREAT | O_APPEND, 0644)) == -1) + return -1; + + flock(fd, LOCK_EX); + + if ((fdr = open(fpath, O_RDONLY, 0)) == -1) { + flock(fd, LOCK_UN); + close(fd); + return -1; + } + if ( + ((fdw = open(my.newfn, O_WRONLY | O_CREAT | O_EXCL, 0644)) == -1) && + ((fdw = force_open(my.newfn)) == -1)) { + close(fdr); + flock(fd, LOCK_UN); + close(fd); + return -1; + } + count = 1; + strlcpy(fullpath, fpath, sizeof(fullpath)); + t = strrchr(fullpath, '/'); + assert(t); + t++; + + while (read(fdr, &fhdr, sizeof(fileheader_t)) == sizeof(fileheader_t)) { + strcpy(t, fhdr.filename); + + /* rocker.011018: add new tag delete */ + if ( + (fhdr.filemode & FILE_MARKED) || /* 標記 */ + ((fhdr.filemode & FILE_DIGEST) && (currstat != RMAIL) )|| + /* 文摘 , FILE_DIGEST is used as REPLIED in mail menu.*/ + (id1 && (count < id1 || count > id2)) || /* range */ + (!id1 && Tagger(atoi(t + 2), count, TAG_NIN))) { /* TagList */ + if ((safewrite(fdw, &fhdr, sizeof(fileheader_t)) == -1)) { + close(fdr); + close(fdw); + unlink(my.newfn); + flock(fd, LOCK_UN); + close(fd); + return -1; + } + } else { + //if (dashd(fullpath)) + unlink(fullpath); + dcount++; + } + ++count; + } + close(fdr); + close(fdw); + if (Rename(fpath, my.oldfn) == -1 || Rename(my.newfn, fpath) == -1) { + flock(fd, LOCK_UN); + close(fd); + return -1; + } + flock(fd, LOCK_UN); + close(fd); + return dcount; +} +#endif + + +#ifdef SAFE_ARTICLE_DELETE +int +safe_article_delete(int ent, const fileheader_t *fhdr, const char *direct) +{ + fileheader_t newfhdr; + memcpy(&newfhdr, fhdr, sizeof(fileheader_t)); + sprintf(newfhdr.title, "(本文已被刪除)"); + strcpy(newfhdr.filename, ".deleted"); + strcpy(newfhdr.owner, "-"); + substitute_record(direct, &newfhdr, sizeof(newfhdr), ent); + return 0; +} + +int +safe_article_delete_range(const char *direct, int from, int to) +{ + fileheader_t newfhdr; + int fd; + char fn[128], *ptr; + + strlcpy(fn, direct, sizeof(fn)); + if( (ptr = rindex(fn, '/')) == NULL ) + return 0; + + ++ptr; + if( (fd = open(direct, O_RDWR)) != -1 && + lseek(fd, sizeof(fileheader_t) * (from - 1), SEEK_SET) != -1 ){ + + for( ; from <= to ; ++from ){ + read(fd, &newfhdr, sizeof(fileheader_t)); + if( newfhdr.filemode & (FILE_MARKED | FILE_DIGEST) ) + continue; + if(newfhdr.filename[0]=='L') newfhdr.filename[0]='M'; + strlcpy(ptr, newfhdr.filename, sizeof(newfhdr.filename)); + unlink(fn); + + sprintf(newfhdr.title, "(本文已被刪除)"); + strcpy(newfhdr.filename, ".deleted"); + strcpy(newfhdr.owner, "-"); + // because off_t is unsigned, we could NOT seek backward. + lseek(fd, sizeof(fileheader_t) * (from - 1), SEEK_SET); + write(fd, &newfhdr, sizeof(fileheader_t)); + } + close(fd); + } + return 0; +} + + +#endif + +int +apply_record(const char *fpath, int (*fptr) (void *item, void *optarg), int size, void *arg){ + char abuf[BUFSIZE]; + int fp; + + if((fp=open(fpath, O_RDONLY, 0)) == -1) + return -1; + + assert(size<=sizeof(abuf)); + while (read(fp, abuf, size) == (size_t)size) + if ((*fptr) (abuf, arg) == QUIT) { + close(fp); + return QUIT; + } + close(fp); + return 0; +} + +/* mail / post 時,依據時間建立檔案,加上郵戳 */ +int +stampfile_u(char *fpath, fileheader_t * fh) + // Ptt: stampfile_u: won't clear fileheader + // stampfile: will clear fileheader +{ + register char *ip = fpath; + time4_t dtime = COMMON_TIME; + struct tm *ptime; +#ifdef _BBS_UTIL_C_ + int fp = 0; //Ptt: don't need to check + // for utils, the time may be the same between several runs, by scw +#endif + + if (access(fpath, X_OK | R_OK | W_OK)) + mkdir(fpath, 0755); + + while (*(++ip)); + *ip++ = '/'; +#ifdef _BBS_UTIL_C_ + do { +#endif + sprintf(ip, "M.%d.A.%3.3X", (int)(++dtime), (unsigned int)(random() & 0xFFF)); +#ifdef _BBS_UTIL_C_ + if (fp == -1 && errno != EEXIST) + return -1; + } while ((fp = open(fpath, O_CREAT | O_EXCL | O_WRONLY, 0644)) == -1); + close(fp); +#endif + strlcpy(fh->filename, ip, sizeof(fh->filename)); + ptime = localtime4(&dtime); + snprintf(fh->date, sizeof(fh->date), + "%2d/%02d", ptime->tm_mon + 1, ptime->tm_mday); + return 0; +} + +inline int +stampfile(char *fpath, fileheader_t * fh) +{ + memset(fh, 0, sizeof(fileheader_t)); + return stampfile_u(fpath, fh); +} + +void +stampdir(char *fpath, fileheader_t * fh) +{ + register char *ip = fpath; + time4_t dtime = COMMON_TIME; + struct tm *ptime; + + if (access(fpath, X_OK | R_OK | W_OK)) + mkdir(fpath, 0755); + + while (*(++ip)); + *ip++ = '/'; + do { + sprintf(ip, "D%X", (int)++dtime & 07777); + } while (mkdir(fpath, 0755) == -1); + memset(fh, 0, sizeof(fileheader_t)); + strlcpy(fh->filename, ip, sizeof(fh->filename)); + ptime = localtime4(&dtime); + snprintf(fh->date, sizeof(fh->date), + "%2d/%02d", ptime->tm_mon + 1, ptime->tm_mday); +} + +void +stamplink(char *fpath, fileheader_t * fh) +{ + register char *ip = fpath; + time4_t dtime = COMMON_TIME; + struct tm *ptime; + + if (access(fpath, X_OK | R_OK | W_OK)) + mkdir(fpath, 0755); + + while (*(++ip)); + *ip++ = '/'; + do { + sprintf(ip, "S%X", (int)++dtime); + } while (symlink("temp", fpath) == -1); + memset(fh, 0, sizeof(fileheader_t)); + strlcpy(fh->filename, ip, sizeof(fh->filename)); + ptime = localtime4(&dtime); + snprintf(fh->date, sizeof(fh->date), + "%2d/%02d", ptime->tm_mon + 1, ptime->tm_mday); +} + +int +append_record(const char *fpath, const fileheader_t * record, int size) +{ + int fd, fsize=0, index; + struct stat st; + + if ((fd = open(fpath, O_WRONLY | O_CREAT, 0644)) == -1) { + char buf[STRLEN]; + assert(errno != EISDIR); + sprintf(buf, "id(%s), open(%s)", cuser.userid, fpath); + perror(buf); + return -1; + } + flock(fd, LOCK_EX); + + if(fstat(fd, &st)!=-1) + fsize = st.st_size; + + index = fsize / size; + lseek(fd, index * size, SEEK_SET); // avoid offset + + safewrite(fd, record, size); + + flock(fd, LOCK_UN); + close(fd); + return index + 1; +} + +int +append_record_forward(char *fpath, fileheader_t * record, int size, const char *origid) +{ +#if !defined(_BBS_UTIL_C_) + if (get_num_records(fpath, sizeof(fileheader_t)) <= MAX_KEEPMAIL * 2) { + FILE *fp; + char buf[512]; + int n; + + for (n = strlen(fpath) - 1; fpath[n] != '/' && n > 0; n--); + if (n + sizeof(".forward") > sizeof(buf)) + return -1; + memcpy(buf, fpath, n+1); + strcpy(buf + n + 1, ".forward"); + if ((fp = fopen(buf, "r"))) { + + char address[64]; + int flIdiotSent2Self = 0; + int oidlen = origid ? strlen(origid) : 0; + + address[0] = 0; + fscanf(fp, "%63s", address); + fclose(fp); + /* some idiots just set forwarding to themselves. + * and even after we checked "sameid", some still + * set STUPID_ID.bbs@host <- "自以為聰明" + * damn it, we have a complex rule now. + */ + if(oidlen > 0) { + if (strncasecmp(address, origid, oidlen) == 0) + { + int addrlen = strlen(address); + if( addrlen == oidlen || + (addrlen > oidlen && + strcasecmp(address + oidlen, str_mail_address) == 0)) + flIdiotSent2Self = 1; + } + } + + if (buf[0] && buf[0] != ' ' && !flIdiotSent2Self) { + buf[n + 1] = 0; + strcat(buf, record->filename); + append_record(fpath, record, size); +#ifndef USE_BSMTP + bbs_sendmail(buf, record->title, address); +#else + bsmtp(buf, record->title, address); +#endif + return 0; + } + } + } +#endif + + append_record(fpath, record, size); + + return 0; +} + +#ifndef _BBS_UTIL_C_ +void +setaidfile(char *buf, const char *bn, aidu_t aidu) +{ + // try to load by AID + int bid = 0; + int n = 0, fd = 0; + char bfpath[PATHLEN] = ""; + boardheader_t *bh = NULL; + fileheader_t fh; + + buf[0] = 0; + bid = getbnum(bn); + + if (bid <= 0) return; + assert(0<=bid-1 && bid-1<MAX_BOARD); + bh = getbcache(bid); + if (!HasBoardPerm(bh)) return; + + setbfile(bfpath, bh->brdname, FN_DIR); + n = search_aidu(bfpath, aidu); + + if (n < 0) return; + fd = open(bfpath, O_RDONLY); + if (fd < 0) return; + + lseek(fd, n*sizeof(fileheader_t), SEEK_SET); + memset(&fh, 0, sizeof(fh)); + if (read(fd, &fh, sizeof(fh)) > 0) + { + setbfile(buf, bh->brdname, fh.filename); + if (!dashf(buf)) + buf[0] = 0; + } + close(fd); +} +#endif |