Exemple #1
0
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;
}
Exemple #2
0
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);
}
Exemple #3
0
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;
}
Exemple #4
0
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);
}
Exemple #5
0
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);
}
Exemple #6
0
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);
}
Exemple #7
0
int
more_web(char *fpath, int promptend)
{
  char *ch;
  char genbuf[41];

  if (ch = strstr(fpath, "mailto:"))
  {
    if (!HAS_PERM(PERM_LOGINOK))
    {
      outmsg(" ±zªºÅv­­¤£¨¬µLªk¨Ï¥Îinternet mail... ");
      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(" ¦¬«H¤Hemail ©Î ¼ÐÃD ¦³»~... ");
    }
    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;
  }
}
Exemple #8
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);
}
Exemple #9
0
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);
}
Exemple #10
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.
     * 
     * 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);
	}
    }
}
Exemple #11
0
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));
}
Exemple #12
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);
}
Exemple #13
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;
}
Exemple #14
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);
	}
    }
}
Exemple #15
0
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);
}