void expire_grp() { int pos, fd; char fgrp[80], fold[80], fnew[80]; SLOT grp; pos = 0; sprintf(fgrp, "2nd/%s", FN_GRP); sprintf(fnew, "2nd/%s.new", FN_GRP); fd = open(fnew, O_CREAT | O_TRUNC, 0600); // touch a new file if(fd > 0) close(fd); while(rec_get(fgrp, &grp, sizeof(SLOT), pos) != -1) { if(grp.prop & PROP_G_CANCEL) { sprintf(fold, "2nd/%s", grp.fn); f_rm(fold); } else { rec_add(fnew, &grp, sizeof(SLOT)); } pos++; } sprintf(fold, "2nd/%s.old", FN_GRP); f_cp(fgrp, fold, O_TRUNC); f_mv(fnew, fgrp); return; }
void u_exit(char *mode) { extern void auto_backup(); /* 編輯器自動備份 */ userec xuser; int diff = (time(0) - login_start_time) / 60; rec_get(fn_passwd, &xuser, sizeof(xuser), usernum); auto_backup(); setflags(PAGER_FLAG, currutmp->pager != 1); setflags(CLOAK_FLAG, currutmp->invisible); xuser.pager = currutmp->pager; /* 記錄pager狀態, add by wisely */ xuser.invisible = currutmp->invisible; /* 紀錄隱形狀態 by wildcat */ xuser.totaltime += time(0) - update_time; xuser.numposts = cuser.numposts; xuser.feeling[4] = '\0'; if (!HAS_PERM(PERM_DENYPOST) && !currutmp->invisible) { char buf [256]; time_t now; time(&now); sprintf(buf, "<<下站通知>> -- 我走囉! - %s", Etime(&now)); do_aloha(buf); } purge_utmp(currutmp); if (!diff && cuser.numlogins > 1 && strcmp(cuser.userid, STR_GUEST)) xuser.numlogins = --cuser.numlogins; /* Leeym 上站停留時間限制式 */ substitute_record(fn_passwd, &xuser, sizeof(userec), usernum); log_usies(mode, NULL); }
void expire_item() { int pos, pos1, fd; char fgrp[80], fitem[80], fname[80], fold[80], fnew[80]; time_t expire; SLOT grp, item; expire = time(0) - EXP * 86400; pos = pos1 = 0; sprintf(fgrp, "2nd/%s", FN_GRP); while(rec_get(fgrp, &grp, sizeof(SLOT), pos) != -1) { sprintf(fitem, "2nd/%s/%s", grp.fn, FN_ITEM); sprintf(fnew, "2nd/%s/%s.new", grp.fn, FN_ITEM); fd = open(fnew, O_CREAT | O_TRUNC, 0600); // touch a new file if(fd > 0) close(fd); fd = 0; while(rec_get(fitem, &item, sizeof(SLOT), pos1) != -1) { if((item.prop & PROP_I_CANCEL) || (item.chrono < expire)) { sprintf(fname, "2nd/%s/%c/%s", grp.fn, item.fn[7], item.fn); f_rm(fname); } else { fd++; rec_add(fnew, &item, sizeof(SLOT)); } pos1++; } sprintf(fold, "2nd/%s/%s.old", grp.fn, FN_ITEM); f_cp(fitem, fold, O_TRUNC); f_mv(fnew, fitem); grp.reply = fd; rec_put(fgrp, &grp, sizeof(SLOT), pos); pos++; } return; }
DELIVERED_HDR_INFO *delivered_hdr_init(VSTREAM *fp, off_t offset, int flags) { char *cp; DELIVERED_HDR_INFO *info; const HEADER_OPTS *hdr; /* * Sanity check. */ info = (DELIVERED_HDR_INFO *) mymalloc(sizeof(*info)); info->flags = flags; info->buf = vstring_alloc(10); info->table = htable_create(0); if (vstream_fseek(fp, offset, SEEK_SET) < 0) msg_fatal("seek queue file %s: %m", VSTREAM_PATH(fp)); /* * XXX Assume that mail_copy() produces delivered-to headers that fit in * a REC_TYPE_NORM record. Lowercase the delivered-to addresses for * consistency. * * XXX Don't get bogged down by gazillions of delivered-to headers. */ #define DELIVERED_HDR_LIMIT 1000 while (rec_get(fp, info->buf, 0) == REC_TYPE_NORM && info->table->used < DELIVERED_HDR_LIMIT) { if (is_header(STR(info->buf))) { if ((hdr = header_opts_find(STR(info->buf))) != 0 && hdr->type == HDR_DELIVERED_TO) { cp = STR(info->buf) + strlen(hdr->name) + 1; while (ISSPACE(*cp)) cp++; if (info->flags & FOLD_ADDR_ALL) fold_addr(cp, info->flags); if (msg_verbose) msg_info("delivered_hdr_init: %s", cp); htable_enter(info->table, cp, (char *) 0); } } else if (ISSPACE(STR(info->buf)[0])) { continue; } else { break; } } return (info); }
static void cleanup_service(VSTREAM *src, char *unused_service, char **argv) { VSTRING *buf = vstring_alloc(100); CLEANUP_STATE *state; int flags; int type = 0; int status; /* * Sanity check. This service takes no command-line arguments. */ if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); /* * Open a queue file and initialize state. */ state = cleanup_open(src); /* * Send the queue id to the client. Read client processing options. If we * can't read the client processing options we can pretty much forget * about the whole operation. */ attr_print(src, ATTR_FLAG_NONE, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, state->queue_id, ATTR_TYPE_END); if (attr_scan(src, ATTR_FLAG_STRICT, ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &flags, ATTR_TYPE_END) != 1) { state->errs |= CLEANUP_STAT_BAD; flags = 0; } cleanup_control(state, flags); /* * XXX Rely on the front-end programs to enforce record size limits. * * First, copy the envelope records to the queue file. Then, copy the * message content (headers and body). Finally, attach any information * extracted from message headers. */ while (CLEANUP_OUT_OK(state)) { if ((type = rec_get_raw(src, buf, 0, REC_FLAG_NONE)) < 0) { state->errs |= CLEANUP_STAT_BAD; break; } if (REC_GET_HIDDEN_TYPE(type)) { msg_warn("%s: record type %d not allowed - discarding this message", state->queue_id, type); state->errs |= CLEANUP_STAT_BAD; break; } CLEANUP_RECORD(state, type, vstring_str(buf), VSTRING_LEN(buf)); if (type == REC_TYPE_END) break; } /* * Keep reading in case of problems, until the sender is ready to receive * our status report. */ if (CLEANUP_OUT_OK(state) == 0 && type > 0) { while (type != REC_TYPE_END && (type = rec_get(src, buf, 0)) > 0) /* void */ ; } /* * Log something to make timeout errors easier to debug. */ if (vstream_ftimeout(src)) msg_warn("%s: read timeout on %s", state->queue_id, VSTREAM_PATH(src)); /* * Finish this message, and report the result status to the client. */ status = cleanup_flush(state); /* in case state is modified */ attr_print(src, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, ATTR_TYPE_STR, MAIL_ATTR_WHY, (state->flags & CLEANUP_FLAG_SMTP_REPLY) && state->smtp_reply ? state->smtp_reply : state->reason ? state->reason : "", ATTR_TYPE_END); cleanup_free(state); /* * Cleanup. */ vstring_free(buf); }
int main() { char c; int usernum = 0, avgmoney = 0; long long int totalmoney = 0; FILE *fp; ECONOMY economy; long long int moneydiff = 0; double avgmoneydiff, days; time_t now_time, interval; chdir(BBSHOME); /* 第一次統計 */ if (rec_get(FN_ECONOMY, &economy, sizeof(ECONOMY), 0)) memset(&economy, 0, sizeof(ECONOMY)); for (c = 'a'; c <= 'z'; c++) { char buf[64]; struct dirent *de; DIR *dirp; sprintf(buf, BBSHOME "/usr/%c", c); chdir(buf); if (!(dirp = opendir("."))) continue; while (de = readdir(dirp)) { ACCT acct; int fd; char *fname; fname = de->d_name; if (*fname <= ' ' || *fname == '.') continue; sprintf(buf, "%s/.ACCT", fname); if ((fd = open(buf, O_RDONLY)) < 0) continue; read(fd, &acct, sizeof(ACCT)); close(fd); totalmoney += acct.money; usernum++; } closedir(dirp); } chdir(BBSHOME); avgmoney = totalmoney / usernum; now_time = time(0); interval = now_time - economy.last_time; days = (double) interval / (double) 86400; moneydiff = totalmoney - economy.totalmoney; avgmoneydiff = ((double) moneydiff / (double) usernum) / days; if (fp = fopen(OUTFILE_ECONOMY, "w")) { fprintf(fp, "\n\n\t\t\t\t\t本站經濟概況\n"); fprintf(fp, "\n\t\t╭─┤\033[1;32;41m %s \033[m├─╮\n", Now()); fprintf(fp, "\t\t│本站目前總人口:%14d 人 │\n", usernum); fprintf(fp, "\t\t│\033[1m本站人民總銀幣:%14lld 元\033[m │\n", totalmoney); fprintf(fp, "\t\t│\033[1m平均每人有銀幣:%14d 元\033[m │\n", avgmoney); fprintf(fp, "\t\t│\033[1m銀幣日生產毛額:%14lld 元\033[m │\n", moneydiff); fprintf(fp, "\t\t│\033[1m平均銀幣日產額:%14.3f 元\033[m │\n", avgmoneydiff); fprintf(fp, "\t\t╰─────────────────╯\n"); fclose(fp); } economy.totalmoney = totalmoney; economy.last_time = now_time; rec_put(FN_ECONOMY, &economy, sizeof(ECONOMY), 0, NULL); }
int more_web(char *fpath, int promptend) { char *ch; char genbuf[41]; if (ch = strstr(fpath, "mailto:")) { if (!HAS_PERM(PERM_LOGINOK)) { outmsg("[41m ±zªºÅv¤£¨¬µLªk¨Ï¥Îinternet mail... [m"); return 0; } if (!not_addr(&ch[7]) && getdata(b_lines - 1, 0, "[±H«H]¥DÃD¡G", genbuf, 40, DOECHO, 0)) { do_send(&ch[7], genbuf); } else { outmsg("[41m ¦¬«H¤Hemail ©Î ¼ÐÃD ¦³»~... [m"); } return 0; } #if 0 if (ch = strstr(fpath, "gopher://")) { ITEM item; strcpy(item.X.G.server, &ch[9]); strcpy(item.X.G.path, "1/"); item.X.G.port = 70; gem(fpath, &item, 0); return 0; } #endif /* wildcat : ¤ä´©ª½±µ¶i¤J¬ÝªO */ if (ch = strstr(fpath, "board://")) { char bname[20], bpath[60], oldch[STRLEN]; struct stat st; int mode0 = currutmp->mode; int stat0 = currstat; int pos; boardheader *bhdr, *getbcache(); strcpy(oldch, ch); strcpy(bname, strtok(oldch + 8, "#")); setbpath(bpath, bname); if ((*bname == '\0') || (stat(bpath, &st) == -1)) { pressanykey(err_bid); return RC_FULL; } if (bhdr = getbcache(bname)) { if (Ben_Perm(bhdr) != 1) { pressanykey("§A¨S¦³¶i¤J¸ÓªOªºÅv"); return 0; } } else { pressanykey("§A¨S¦³¶i¤J¸ÓªOªºÅv"); return 0; } /* shakalaca.000123: ¤ä´©¬Ý¬Y¤@½g */ if (ch = strstr(fpath, "#")) { fileheader fhdr; pos = atoi(ch + 1); setbdir(bpath, bname); rec_get(bpath, &fhdr, sizeof(fileheader), pos); setbfile(bpath, bname, fhdr.filename); more(bpath, 0); } else { /* shakalaca.000124: ¸Ñ¨M "¥¼Åª" °ÝÃD.. */ brc_initial(bname); Read(); } currutmp->mode = mode0; currstat = stat0; return 0; } }
int cleanup_bounce(CLEANUP_STATE *state) { const char *myname = "cleanup_bounce"; VSTRING *buf = vstring_alloc(100); const CLEANUP_STAT_DETAIL *detail; DSN_SPLIT dp; const char *dsn_status; const char *dsn_text; char *rcpt = 0; RECIPIENT recipient; DSN dsn; char *attr_name; char *attr_value; char *dsn_orcpt = 0; int dsn_notify = 0; char *orig_rcpt = 0; char *start; int rec_type; int junk; long curr_offset; const char *encoding; const char *dsn_envid; int dsn_ret; int bounce_err; /* * Parse the failure reason if one was given, otherwise use a generic * mapping from cleanup-internal error code to (DSN + text). */ if (state->reason) { dsn_split(&dp, "5.0.0", state->reason); dsn_status = DSN_STATUS(dp.dsn); dsn_text = dp.text; } else { detail = cleanup_stat_detail(state->errs); dsn_status = detail->dsn; dsn_text = detail->text; } /* * Create a bounce logfile with one entry for each final recipient. * Degrade gracefully in case of no recipients or no queue file. * * Victor Duchovni observes that the number of recipients in the queue file * can potentially be very large due to virtual alias expansion. This can * expand the recipient count by virtual_alias_expansion_limit (default: * 1000) times. * * After a queue file write error, purge any unwritten data (so that * vstream_fseek() won't fail while trying to flush it) and reset the * stream error flags to avoid false alarms. */ if (vstream_ferror(state->dst) || vstream_fflush(state->dst)) { (void) vstream_fpurge(state->dst, VSTREAM_PURGE_BOTH); vstream_clearerr(state->dst); } if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0) msg_fatal("%s: seek %s: %m", myname, cleanup_path); while ((state->errs & CLEANUP_STAT_WRITE) == 0) { if ((curr_offset = vstream_ftell(state->dst)) < 0) msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path); if ((rec_type = rec_get(state->dst, buf, 0)) <= 0 || rec_type == REC_TYPE_END) break; start = STR(buf); if (rec_type == REC_TYPE_ATTR) { if (split_nameval(STR(buf), &attr_name, &attr_value) != 0 || *attr_value == 0) continue; /* Map attribute names to pseudo record type. */ if ((junk = rec_attr_map(attr_name)) != 0) { start = attr_value; rec_type = junk; } } switch (rec_type) { case REC_TYPE_DSN_ORCPT: /* RCPT TO ORCPT parameter */ if (dsn_orcpt != 0) /* can't happen */ myfree(dsn_orcpt); dsn_orcpt = mystrdup(start); break; case REC_TYPE_DSN_NOTIFY: /* RCPT TO NOTIFY parameter */ if (alldig(start) && (junk = atoi(start)) > 0 && DSN_NOTIFY_OK(junk)) dsn_notify = junk; else dsn_notify = 0; break; case REC_TYPE_ORCP: /* unmodified RCPT TO address */ if (orig_rcpt != 0) /* can't happen */ myfree(orig_rcpt); orig_rcpt = mystrdup(start); break; case REC_TYPE_RCPT: /* rewritten RCPT TO address */ rcpt = start; RECIPIENT_ASSIGN(&recipient, curr_offset, dsn_orcpt ? dsn_orcpt : "", dsn_notify, orig_rcpt ? orig_rcpt : rcpt, rcpt); (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text); cleanup_bounce_append(state, &recipient, &dsn); /* FALLTHROUGH */ case REC_TYPE_DRCP: /* canceled recipient */ case REC_TYPE_DONE: /* can't happen */ if (orig_rcpt != 0) { myfree(orig_rcpt); orig_rcpt = 0; } if (dsn_orcpt != 0) { myfree(dsn_orcpt); dsn_orcpt = 0; } dsn_notify = 0; break; } } if (orig_rcpt != 0) /* can't happen */ myfree(orig_rcpt); if (dsn_orcpt != 0) /* can't happen */ myfree(dsn_orcpt); /* * No recipients. Yes, this can happen. */ if ((state->errs & CLEANUP_STAT_WRITE) == 0 && rcpt == 0) { RECIPIENT_ASSIGN(&recipient, 0, "", 0, "", "unknown"); (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text); cleanup_bounce_append(state, &recipient, &dsn); } vstring_free(buf); /* * Flush the bounce logfile to the sender. See also qmgr_active.c. */ if ((state->errs & CLEANUP_STAT_WRITE) == 0) { if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) == 0) encoding = MAIL_ATTR_ENC_NONE; dsn_envid = state->dsn_envid ? state->dsn_envid : ""; /* Do not send unfiltered (body) content. */ dsn_ret = (state->errs & (CLEANUP_STAT_CONT | CLEANUP_STAT_SIZE)) ? DSN_RET_HDRS : state->dsn_ret; if (state->verp_delims == 0 || var_verp_bounce_off) { bounce_err = bounce_flush(BOUNCE_FLAG_CLEAN, state->queue_name, state->queue_id, encoding, state->sender, dsn_envid, dsn_ret); } else { bounce_err = bounce_flush_verp(BOUNCE_FLAG_CLEAN, state->queue_name, state->queue_id, encoding, state->sender, dsn_envid, dsn_ret, state->verp_delims); } if (bounce_err != 0) { msg_warn("%s: bounce message failure", state->queue_id); state->errs |= CLEANUP_STAT_WRITE; } } /* * Schedule this message (and trace logfile) for deletion when all is * well. When all is not well these files would be deleted too, but the * client would get a different completion status so we have to carefully * maintain the bits anyway. */ if ((state->errs &= CLEANUP_STAT_WRITE) == 0) state->flags |= CLEANUP_FLAG_DISCARD; return (state->errs); }
static void qmgr_message_oldstyle_scan(QMGR_MESSAGE *message) { VSTRING *buf; long orig_offset, extra_offset; int rec_type; char *start; /* * Initialize. No early returns or we have a memory leak. */ buf = vstring_alloc(100); if ((orig_offset = vstream_ftell(message->fp)) < 0) msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp)); /* * Rewind to the very beginning to make sure we see all records. */ if (vstream_fseek(message->fp, 0, SEEK_SET) < 0) msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); /* * Scan through the old style queue file. Count the total number of * recipients and find the data/extra sections offsets. Note that the new * queue files require that data_size equals extra_offset - data_offset, * so we set data_size to this as well and ignore the size record itself * completely. */ message->rcpt_unread = 0; for (;;) { rec_type = rec_get(message->fp, buf, 0); if (rec_type <= 0) /* Report missing end record later. */ break; start = vstring_str(buf); if (msg_verbose > 1) msg_info("old-style scan record %c %s", rec_type, start); if (rec_type == REC_TYPE_END) break; if (rec_type == REC_TYPE_DONE || rec_type == REC_TYPE_RCPT || rec_type == REC_TYPE_DRCP) { message->rcpt_unread++; continue; } if (rec_type == REC_TYPE_MESG) { if (message->data_offset == 0) { if ((message->data_offset = vstream_ftell(message->fp)) < 0) msg_fatal("vstream_ftell %s: %m", VSTREAM_PATH(message->fp)); if ((extra_offset = atol(start)) <= message->data_offset) msg_fatal("bad extra offset %s file %s", start, VSTREAM_PATH(message->fp)); if (vstream_fseek(message->fp, extra_offset, SEEK_SET) < 0) msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); message->data_size = extra_offset - message->data_offset; } continue; } } /* * Clean up. */ if (vstream_fseek(message->fp, orig_offset, SEEK_SET) < 0) msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp)); vstring_free(buf); /* * Sanity checks. Verify that all required information was found, * including the queue file end marker. */ if (message->data_offset == 0 || rec_type != REC_TYPE_END) msg_fatal("%s: envelope records out of order", message->queue_id); }
static int smtpd_proxy_replay_send(SMTPD_STATE *state) { const char *myname = "smtpd_proxy_replay_send"; static VSTRING *replay_buf = 0; SMTPD_PROXY *proxy = state->proxy; int rec_type; int expect = SMTPD_PROX_WANT_BAD; /* * Sanity check. */ if (smtpd_proxy_replay_stream == 0) msg_panic("%s: no before-queue filter speed-adjust log", myname); /* * Errors first. */ if (vstream_ferror(smtpd_proxy_replay_stream) || vstream_feof(smtpd_proxy_replay_stream) || rec_put(smtpd_proxy_replay_stream, REC_TYPE_END, "", 0) != REC_TYPE_END || vstream_fflush(smtpd_proxy_replay_stream)) /* NOT: fsync(vstream_fileno(smtpd_proxy_replay_stream)) */ return (smtpd_proxy_replay_rdwr_error(state)); /* * Delayed connection to the before-queue filter. */ if (smtpd_proxy_connect(state) < 0) return (-1); /* * Replay the speed-match log. We do sanity check record content, but we * don't implement a protocol state engine here, since we are reading * from a file that we just wrote ourselves. * * This is different than the MailChannels patented solution that * multiplexes a large number of slowed-down inbound connections over a * small number of fast connections to a local MTA. * * - MailChannels receives mail directly from the Internet. It uses one * connection to the local MTA to reject invalid recipients before * receiving the entire email message at reduced bit rates, and then uses * a different connection to quickly deliver the message to the local * MTA. * * - Postfix receives mail directly from the Internet. The Postfix SMTP * server rejects invalid recipients before receiving the entire message * over the Internet, and then delivers the message quickly to a local * SMTP-based content filter. */ if (replay_buf == 0) replay_buf = vstring_alloc(100); if (vstream_fseek(smtpd_proxy_replay_stream, (off_t) 0, SEEK_SET) < 0) return (smtpd_proxy_replay_rdwr_error(state)); for (;;) { switch (rec_type = rec_get(smtpd_proxy_replay_stream, replay_buf, REC_FLAG_NONE)) { /* * Message content. */ case REC_TYPE_NORM: case REC_TYPE_CONT: if (smtpd_proxy_rec_put(proxy->service_stream, rec_type, STR(replay_buf), LEN(replay_buf)) < 0) return (-1); break; /* * Expected server reply type. */ case REC_TYPE_RCPT: if (!alldig(STR(replay_buf)) || (expect = atoi(STR(replay_buf))) == SMTPD_PROX_WANT_BAD) msg_panic("%s: malformed server reply type: %s", myname, STR(replay_buf)); break; /* * Client command, or void. Bail out on the first negative proxy * response. This is OK, because the filter must use the same * reply code for all recipients of a multi-recipient message. */ case REC_TYPE_FROM: if (expect == SMTPD_PROX_WANT_BAD) msg_panic("%s: missing server reply type", myname); if (smtpd_proxy_cmd(state, expect, *STR(replay_buf) ? "%s" : SMTPD_PROXY_CONN_FMT, STR(replay_buf)) < 0) return (-1); expect = SMTPD_PROX_WANT_BAD; break; /* * Explicit end marker, instead of implicit EOF. */ case REC_TYPE_END: return (0); /* * Errors. */ case REC_TYPE_ERROR: return (smtpd_proxy_replay_rdwr_error(state)); default: msg_panic("%s: unexpected record type; %d", myname, rec_type); } } }
int mail_copy(const char *sender, const char *orig_rcpt, const char *delivered, VSTREAM *src, VSTREAM *dst, int flags, const char *eol, DSN_BUF *why) { const char *myname = "mail_copy"; VSTRING *buf; char *bp; off_t orig_length; int read_error; int write_error; int corrupt_error = 0; time_t now; int type; int prev_type; struct stat st; off_t size_limit; /* * Workaround 20090114. This will hopefully get someone's attention. The * problem with file_size_limit < message_size_limit is that mail will be * delivered again and again until someone removes it from the queue by * hand, because Postfix cannot mark a recipient record as "completed". */ if (fstat(vstream_fileno(src), &st) < 0) msg_fatal("fstat: %m"); if ((size_limit = get_file_limit()) < st.st_size) msg_panic("file size limit %lu < message size %lu. This " "causes large messages to be delivered repeatedly " "after they were submitted with \"sendmail -t\" " "or after recipients were added with the Milter " "SMFIR_ADDRCPT request", (unsigned long) size_limit, (unsigned long) st.st_size); /* * Initialize. */ #ifndef NO_TRUNCATE if ((flags & MAIL_COPY_TOFILE) != 0) if ((orig_length = vstream_fseek(dst, (off_t) 0, SEEK_END)) < 0) msg_fatal("seek file %s: %m", VSTREAM_PATH(dst)); #endif buf = vstring_alloc(100); /* * Prepend a bunch of headers to the message. */ if (flags & (MAIL_COPY_FROM | MAIL_COPY_RETURN_PATH)) { if (sender == 0) msg_panic("%s: null sender", myname); quote_822_local(buf, sender); if (flags & MAIL_COPY_FROM) { time(&now); vstream_fprintf(dst, "From %s %.24s%s", *sender == 0 ? MAIL_ADDR_MAIL_DAEMON : vstring_str(buf), asctime(localtime(&now)), eol); } if (flags & MAIL_COPY_RETURN_PATH) { vstream_fprintf(dst, "Return-Path: <%s>%s", *sender ? vstring_str(buf) : "", eol); } } if (flags & MAIL_COPY_ORIG_RCPT) { if (orig_rcpt == 0) msg_panic("%s: null orig_rcpt", myname); /* * An empty original recipient record almost certainly means that * original recipient processing was disabled. */ if (*orig_rcpt) { quote_822_local(buf, orig_rcpt); vstream_fprintf(dst, "X-Original-To: %s%s", vstring_str(buf), eol); } } if (flags & MAIL_COPY_DELIVERED) { if (delivered == 0) msg_panic("%s: null delivered", myname); quote_822_local(buf, delivered); vstream_fprintf(dst, "Delivered-To: %s%s", vstring_str(buf), eol); } /* * Copy the message. Escape lines that could be confused with the ugly * From_ line. Make sure that there is a blank line at the end of the * message so that the next ugly From_ can be found by mail reading * software. * * XXX Rely on the front-end services to enforce record size limits. */ #define VSTREAM_FWRITE_BUF(s,b) \ vstream_fwrite((s),vstring_str(b),VSTRING_LEN(b)) prev_type = REC_TYPE_NORM; while ((type = rec_get(src, buf, 0)) > 0) { if (type != REC_TYPE_NORM && type != REC_TYPE_CONT) break; bp = vstring_str(buf); if (prev_type == REC_TYPE_NORM) { if ((flags & MAIL_COPY_QUOTE) && *bp == 'F' && !strncmp(bp, "From ", 5)) VSTREAM_PUTC('>', dst); if ((flags & MAIL_COPY_DOT) && *bp == '.') VSTREAM_PUTC('.', dst); } if (VSTRING_LEN(buf) && VSTREAM_FWRITE_BUF(dst, buf) != VSTRING_LEN(buf)) break; if (type == REC_TYPE_NORM && vstream_fputs(eol, dst) == VSTREAM_EOF) break; prev_type = type; } if (vstream_ferror(dst) == 0) { if (var_fault_inj_code == 1) type = 0; if (type != REC_TYPE_XTRA) { /* XXX Where is the queue ID? */ msg_warn("bad record type: %d in message content", type); corrupt_error = mark_corrupt(src); } if (prev_type != REC_TYPE_NORM) vstream_fputs(eol, dst); if (flags & MAIL_COPY_BLANK) vstream_fputs(eol, dst); } vstring_free(buf); /* * Make sure we read and wrote all. Truncate the file to its original * length when the delivery failed. POSIX does not require ftruncate(), * so we may have a portability problem. Note that fclose() may fail even * while fflush and fsync() succeed. Think of remote file systems such as * AFS that copy the file back to the server upon close. Oh well, no * point optimizing the error case. XXX On systems that use flock() * locking, we must truncate the file file before closing it (and losing * the exclusive lock). */ read_error = vstream_ferror(src); write_error = vstream_fflush(dst); #ifdef HAS_FSYNC if ((flags & MAIL_COPY_TOFILE) != 0) write_error |= fsync(vstream_fileno(dst)); #endif if (var_fault_inj_code == 2) { read_error = 1; errno = ENOENT; } if (var_fault_inj_code == 3) { write_error = 1; errno = ENOENT; } #ifndef NO_TRUNCATE if ((flags & MAIL_COPY_TOFILE) != 0) if (corrupt_error || read_error || write_error) /* Complain about ignored "undo" errors? So sue me. */ (void) ftruncate(vstream_fileno(dst), orig_length); #endif write_error |= vstream_fclose(dst); /* * Return the optional verbose error description. */ #define TRY_AGAIN_ERROR(errno) \ (errno == EAGAIN || errno == ESTALE) if (why && read_error) dsb_unix(why, TRY_AGAIN_ERROR(errno) ? "4.3.0" : "5.3.0", sys_exits_detail(EX_IOERR)->text, "error reading message: %m"); if (why && write_error) dsb_unix(why, mbox_dsn(errno, "5.3.0"), sys_exits_detail(EX_IOERR)->text, "error writing message: %m"); /* * Use flag+errno description when the optional verbose description is * not desired. */ return ((corrupt_error ? MAIL_COPY_STAT_CORRUPT : 0) | (read_error ? MAIL_COPY_STAT_READ : 0) | (write_error ? MAIL_COPY_STAT_WRITE : 0)); }
static int copy_segment(VSTREAM *qfile, VSTREAM *cleanup, PICKUP_INFO *info, VSTRING *buf, char *expected) { int type; int check_first = (*expected == REC_TYPE_CONTENT[0]); int time_seen = 0; char *attr_name; char *attr_value; char *saved_attr; int skip_attr; /* * Limit the input record size. All front-end programs should protect the * mail system against unreasonable inputs. This also requires that we * limit the size of envelope records written by the local posting agent. * * Records with named attributes are filtered by postdrop(1). * * We must allow PTR records here because of "postsuper -r". */ for (;;) { if ((type = rec_get(qfile, buf, var_line_limit)) < 0 || strchr(expected, type) == 0) return (file_read_error(info, type)); if (msg_verbose) msg_info("%s: read %c %s", info->id, type, vstring_str(buf)); if (type == *expected) break; if (type == REC_TYPE_FROM) { if (info->sender == 0) info->sender = mystrdup(vstring_str(buf)); /* Compatibility with Postfix < 2.3. */ if (time_seen == 0) rec_fprintf(cleanup, REC_TYPE_TIME, "%ld", (long) info->st.st_mtime); } if (type == REC_TYPE_TIME) time_seen = 1; /* * XXX Workaround: REC_TYPE_FILT (used in envelopes) == REC_TYPE_CONT * (used in message content). * * As documented in postsuper(1), ignore content filter record. */ if (*expected != REC_TYPE_CONTENT[0]) { if (type == REC_TYPE_FILT) /* Discard FILTER record after "postsuper -r". */ continue; if (type == REC_TYPE_RDR) /* Discard REDIRECT record after "postsuper -r". */ continue; } if (*expected == REC_TYPE_EXTRACT[0]) { if (type == REC_TYPE_RRTO) /* Discard return-receipt record after "postsuper -r". */ continue; if (type == REC_TYPE_ERTO) /* Discard errors-to record after "postsuper -r". */ continue; if (type == REC_TYPE_ATTR) { saved_attr = mystrdup(vstring_str(buf)); skip_attr = (split_nameval(saved_attr, &attr_name, &attr_value) == 0 && rec_attr_map(attr_name) == 0); myfree(saved_attr); /* Discard other/header/body action after "postsuper -r". */ if (skip_attr) continue; } } /* * XXX Force an empty record when the queue file content begins with * whitespace, so that it won't be considered as being part of our * own Received: header. What an ugly Kluge. */ if (check_first && (type == REC_TYPE_NORM || type == REC_TYPE_CONT)) { check_first = 0; if (VSTRING_LEN(buf) > 0 && IS_SPACE_TAB(vstring_str(buf)[0])) rec_put(cleanup, REC_TYPE_NORM, "", 0); } if ((REC_PUT_BUF(cleanup, type, buf)) < 0) return (cleanup_service_error(info, CLEANUP_STAT_WRITE)); } return (0); }
int main() { UCACHE *ushm; COUNTER counter; int num; char *fname, date[20]; FILE *fp; time_t now; struct tm *ptime; chdir(BBSHOME); if (!(fp = fopen(OUTFILE_COUNTER, "a+"))) return -1; fname = FN_RUN_COUNTER; memset(&counter, 0, sizeof(COUNTER)); rec_get(fname, &counter, sizeof(COUNTER), 0); counter.uptime = time(&now); ptime = localtime(&now); sprintf(date, "【%02d/%02d/%02d %02d:%02d】", ptime->tm_year % 100, ptime->tm_mon + 1, ptime->tm_mday, ptime->tm_hour, ptime->tm_min); /* 註冊人數 */ num = rec_num(FN_SCHEMA, sizeof(SCHEMA)); if (break_record(num, counter.total_acct)) { fprintf(fp, "★ %s \033[31m本站註冊人數\033[m狂賀超過 \033[1;31m%d\033[m 人\n", date, num); counter.total_acct = num; } /* 線上人數 */ ushm = shm_new(UTMPSHM_KEY, sizeof(UCACHE)); num = ushm->count; if (break_record(num, counter.online_usr)) { fprintf(fp, "◎ %s \033[32m同時線上人數\033[m首次達到 \033[1;32m%d\033[m 人\n", date, num); counter.online_usr = num; } counter.online_usr_every_hour[ptime->tm_hour] = num; /* 本小時的線上人數,就拿這次 sample 值 */ /* 本日上站人次 */ if (ptime->tm_hour == 23) /* 每天計算一次本日上站人次 */ { int i; /* itoc.註解: 這個值是不正確的,因為只是拿每個小時 sample 的和, 而且還假設每個人平均在站上時間是 60 分鐘 */ num = 0; for (i = 0; i < 24; i++) num += counter.online_usr_every_hour[i]; if (break_record(num, counter.today_usr)) { fprintf(fp, "◆ %s \033[33m單日上站人次\033[m正式突破 \033[1;33m%d\033[m 人\n", date, num); counter.today_usr = num; } } /* 看板個數 */ num = rec_num(FN_BRD, sizeof(BRD)); if (break_record(num, counter.total_brd)) { fprintf(fp, "☆ %s \033[34m本站看板個數\033[m宣佈高達 \033[1;34m%d\033[m 個\n", date, num); counter.total_brd = num; } rec_put(fname, &counter, sizeof(COUNTER), 0, NULL); fclose(fp); return 0; }
static int smtpd_proxy_replay_send(SMTPD_STATE *state) { const char *myname = "smtpd_proxy_replay_send"; static VSTRING *replay_buf = 0; SMTPD_PROXY *proxy = state->proxy; int rec_type; int expect = SMTPD_PROX_WANT_BAD; /* * Sanity check. */ if (smtpd_proxy_replay_stream == 0) msg_panic("%s: no before-queue filter speed-adjust log", myname); /* * Errors first. */ if (vstream_ferror(smtpd_proxy_replay_stream) || vstream_feof(smtpd_proxy_replay_stream) || rec_put(smtpd_proxy_replay_stream, REC_TYPE_END, "", 0) != REC_TYPE_END || vstream_fflush(smtpd_proxy_replay_stream)) /* NOT: fsync(vstream_fileno(smtpd_proxy_replay_stream)) */ return (smtpd_proxy_replay_rdwr_error(state)); /* * Delayed connection to the before-queue filter. */ if (smtpd_proxy_connect(state) < 0) return (-1); /* * Replay the speed-match log. We do sanity check record content, but we * don't implement a protocol state engine here, since we are reading * from a file that we just wrote ourselves. */ if (replay_buf == 0) replay_buf = vstring_alloc(100); if (vstream_fseek(smtpd_proxy_replay_stream, (off_t) 0, SEEK_SET) < 0) return (smtpd_proxy_replay_rdwr_error(state)); for (;;) { switch (rec_type = rec_get(smtpd_proxy_replay_stream, replay_buf, REC_FLAG_NONE)) { /* * Message content. */ case REC_TYPE_NORM: case REC_TYPE_CONT: if (smtpd_proxy_rec_put(proxy->service_stream, rec_type, STR(replay_buf), LEN(replay_buf)) < 0) return (-1); break; /* * Expected server reply type. */ case REC_TYPE_RCPT: if (!alldig(STR(replay_buf)) || (expect = atoi(STR(replay_buf))) == SMTPD_PROX_WANT_BAD) msg_panic("%s: malformed server reply type: %s", myname, STR(replay_buf)); break; /* * Client command, or void. Bail out on the first negative proxy * response. This is OK, because the filter must use the same * reply code for all recipients of a multi-recipient message. */ case REC_TYPE_FROM: if (expect == SMTPD_PROX_WANT_BAD) msg_panic("%s: missing server reply type", myname); if (smtpd_proxy_cmd(state, expect, *STR(replay_buf) ? "%s" : SMTPD_PROXY_CONN_FMT, STR(replay_buf)) < 0) return (-1); expect = SMTPD_PROX_WANT_BAD; break; /* * Explicit end marker, instead of implicit EOF. */ case REC_TYPE_END: return (0); /* * Errors. */ case REC_TYPE_ERROR: return (smtpd_proxy_replay_rdwr_error(state)); default: msg_panic("%s: unexpected record type; %d", myname, rec_type); } } }
static int forward_send(FORWARD_INFO *info, DELIVER_REQUEST *request, DELIVER_ATTR attr, char *delivered) { const char *myname = "forward_send"; VSTRING *buffer = vstring_alloc(100); VSTRING *folded; int status; int rec_type = 0; /* * Start the message content segment. Prepend our Delivered-To: header to * the message data. Stop at the first error. XXX Rely on the front-end * services to enforce record size limits. */ rec_fputs(info->cleanup, REC_TYPE_MESG, ""); vstring_strcpy(buffer, delivered); rec_fprintf(info->cleanup, REC_TYPE_NORM, "Received: by %s (%s)", var_myhostname, var_mail_name); rec_fprintf(info->cleanup, REC_TYPE_NORM, "\tid %s; %s", info->queue_id, mail_date(info->posting_time.tv_sec)); if (local_deliver_hdr_mask & DELIVER_HDR_FWD) { folded = vstring_alloc(100); rec_fprintf(info->cleanup, REC_TYPE_NORM, "Delivered-To: %s", casefold(folded, (STR(buffer)))); vstring_free(folded); } if ((status = vstream_ferror(info->cleanup)) == 0) if (vstream_fseek(attr.fp, attr.offset, SEEK_SET) < 0) msg_fatal("%s: seek queue file %s: %m:", myname, VSTREAM_PATH(attr.fp)); while (status == 0 && (rec_type = rec_get(attr.fp, buffer, 0)) > 0) { if (rec_type != REC_TYPE_CONT && rec_type != REC_TYPE_NORM) break; status = (REC_PUT_BUF(info->cleanup, rec_type, buffer) != rec_type); } if (status == 0 && rec_type != REC_TYPE_XTRA) { msg_warn("%s: bad record type: %d in message content", info->queue_id, rec_type); status |= mark_corrupt(attr.fp); } /* * Send the end-of-data marker only when there were no errors. */ if (status == 0) { rec_fputs(info->cleanup, REC_TYPE_XTRA, ""); rec_fputs(info->cleanup, REC_TYPE_END, ""); } /* * Retrieve the cleanup service completion status only if there are no * problems. */ if (status == 0) if (vstream_fflush(info->cleanup) || attr_scan(info->cleanup, ATTR_FLAG_MISSING, RECV_ATTR_INT(MAIL_ATTR_STATUS, &status), ATTR_TYPE_END) != 1) status = 1; /* * Log successful forwarding. * * XXX DSN alias and .forward expansion already report SUCCESS, so don't do * it again here. */ if (status == 0) { attr.rcpt.dsn_notify = (attr.rcpt.dsn_notify == DSN_NOTIFY_SUCCESS ? DSN_NOTIFY_NEVER : attr.rcpt.dsn_notify & ~DSN_NOTIFY_SUCCESS); dsb_update(attr.why, "2.0.0", "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY, "forwarded as %s", info->queue_id); status = sent(BOUNCE_FLAGS(request), SENT_ATTR(attr)); } /* * Cleanup. */ vstring_free(buffer); return (status); }