Exemplo n.º 1
0
static void dict_mysql_quote(DICT *dict, const char *name, VSTRING *result)
{
    DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
    int     len = strlen(name);
    int     buflen;

    /*
     * We won't get integer overflows in 2*len + 1, because Postfix input
     * keys have reasonable size limits, better safe than sorry.
     */
    if (len > (INT_MAX - VSTRING_LEN(result) - 1) / 2)
	msg_panic("dict_mysql_quote: integer overflow in %lu+2*%d+1",
		  (unsigned long) VSTRING_LEN(result), len);
    buflen = 2 * len + 1;
    VSTRING_SPACE(result, buflen);

#if defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
    if (dict_mysql->active_host)
	mysql_real_escape_string(dict_mysql->active_host->db,
				 vstring_end(result), name, len);
    else
#endif
	mysql_escape_string(vstring_end(result), name, len);

    VSTRING_SKIP(result);
}
Exemplo n.º 2
0
VSTRING *vstring_memcat(VSTRING *vp, const char *src, ssize_t len)
{
    VSTRING_SPACE(vp, len);
    memcpy(vstring_end(vp), src, len);
    len += VSTRING_LEN(vp);
    VSTRING_AT_OFFSET(vp, len);
    return (vp);
}
Exemplo n.º 3
0
char   *vstring_memchr(VSTRING *vp, int ch)
{
    unsigned char *cp;

    for (cp = (unsigned char *) vstring_str(vp); cp < (unsigned char *) vstring_end(vp); cp++)
	if (*cp == ch)
	    return ((char *) cp);
    return (0);
}
Exemplo n.º 4
0
int     main(int unused_argc, char **unused_argv)
{
	VSTRING *vp = vstring_alloc(100);
	TOK822 *list;
	VSTRING *buf = vstring_alloc(100);

#define TEST_TOKEN_LIMIT 20

	while (readlline(buf, VSTREAM_IN, (int *) 0)) {
		while (VSTRING_LEN(buf) > 0 && vstring_end(buf)[-1] == '\n') {
			vstring_end(buf)[-1] = 0;
			vstring_truncate(buf, VSTRING_LEN(buf) - 1);
		}
		if (!isatty(vstream_fileno(VSTREAM_IN)))
			vstream_printf(">>>%s<<<\n\n", vstring_str(buf));
		list = tok822_parse_limit(vstring_str(buf), TEST_TOKEN_LIMIT);
		vstream_printf("Parse tree:\n");
		tok822_print(list, 0);
		vstream_printf("\n");

		vstream_printf("Internalized:\n%s\n\n",
				vstring_str(tok822_internalize(vp, list, TOK822_STR_DEFL)));
		vstream_fflush(VSTREAM_OUT);
		vstream_printf("Externalized, no newlines inserted:\n%s\n\n",
				vstring_str(tok822_externalize(vp, list,
						TOK822_STR_DEFL | TOK822_STR_TRNC)));
		vstream_fflush(VSTREAM_OUT);
		vstream_printf("Externalized, newlines inserted:\n%s\n\n",
				vstring_str(tok822_externalize(vp, list,
						TOK822_STR_DEFL | TOK822_STR_LINE | TOK822_STR_TRNC)));
		vstream_fflush(VSTREAM_OUT);
		tok822_free_tree(list);
	}
	vstring_free(vp);
	vstring_free(buf);
	return (0);
}
Exemplo n.º 5
0
int     memcache_get(VSTREAM *stream, VSTRING *vp, ssize_t bound)
{
    int     last_char;
    int     next_char;

    last_char = (bound == 0 ? vstring_get(vp, stream) :
		 vstring_get_bound(vp, stream, bound));

    switch (last_char) {

	/*
	 * Do some repair in the rare case that we stopped reading in the
	 * middle of the CRLF record terminator.
	 */
    case '\r':
	if ((next_char = VSTREAM_GETC(stream)) == '\n') {
	    VSTRING_ADDCH(vp, '\n');
	    /* FALLTRHOUGH */
	} else {
	    if (next_char != VSTREAM_EOF)
		vstream_ungetc(stream, next_char);

	    /*
	     * Input too long, or EOF
	     */
    default:
	    if (msg_verbose)
		msg_info("%s got %s", VSTREAM_PATH(stream),
			 LEN(vp) < bound ? "EOF" : "input too long");
	    return (-1);
	}

	/*
	 * Strip off the record terminator: either CRLF or just bare LF.
	 */
    case '\n':
	vstring_truncate(vp, VSTRING_LEN(vp) - 1);
	if (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r')
	    vstring_truncate(vp, VSTRING_LEN(vp) - 1);
	VSTRING_TERMINATE(vp);
	if (msg_verbose)
	    msg_info("%s got: %s", VSTREAM_PATH(stream), STR(vp));
	return (0);
    }
}
Exemplo n.º 6
0
static void qmqpd_copy_sender(QMQPD_STATE *state)
{
    char   *end_prefix;
    char   *end_origin;
    int     verp_requested;
    static char verp_delims[] = "-=";

    /*
     * If the sender address looks like prefix@origin-@[], then request
     * variable envelope return path delivery, with an envelope sender
     * address of prefi@origin, and with VERP delimiters of x and =. This
     * way, the recipients will see envelope sender addresses that look like:
     * prefixuser=domain@origin.
     */
    state->where = "receiving sender address";
    netstring_get(state->client, state->buf, var_line_limit);
    VSTRING_TERMINATE(state->buf);
    verp_requested =
	((end_origin = vstring_end(state->buf) - 4) > STR(state->buf)
	 && strcmp(end_origin, "-@[]") == 0
	 && (end_prefix = strchr(STR(state->buf), '@')) != 0	/* XXX */
	 && --end_prefix < end_origin - 2	/* non-null origin */
	 && end_prefix > STR(state->buf));	/* non-null prefix */
    if (verp_requested) {
	verp_delims[0] = end_prefix[0];
	if (verp_delims_verify(verp_delims) != 0) {
	    state->err |= CLEANUP_STAT_CONT;	/* XXX */
	    vstring_sprintf(state->why_rejected, "Invalid VERP delimiters: \"%s\". Need two characters from \"%s\"",
			    verp_delims, var_verp_filter);
	}
	memmove(end_prefix, end_prefix + 1, end_origin - end_prefix - 1);
	vstring_truncate(state->buf, end_origin - STR(state->buf) - 1);
    }
    if (state->err == CLEANUP_STAT_OK
	&& REC_PUT_BUF(state->cleanup, REC_TYPE_FROM, state->buf) < 0)
	state->err = CLEANUP_STAT_WRITE;
    if (verp_requested)
	if (state->err == CLEANUP_STAT_OK
	    && rec_put(state->cleanup, REC_TYPE_VERP, verp_delims, 2) < 0)
	    state->err = CLEANUP_STAT_WRITE;
    state->sender = mystrndup(STR(state->buf), LEN(state->buf));
}
Exemplo n.º 7
0
static void enqueue(const int flags, const char *encoding,
		         const char *dsn_envid, int dsn_ret, int dsn_notify,
		            const char *rewrite_context, const char *sender,
		            const char *full_name, char **recipients)
{
    VSTRING *buf;
    VSTREAM *dst;
    char   *saved_sender;
    char  **cpp;
    int     type;
    char   *start;
    int     skip_from_;
    TOK822 *tree;
    TOK822 *tp;
    int     rcpt_count = 0;
    enum {
	STRIP_CR_DUNNO, STRIP_CR_DO, STRIP_CR_DONT, STRIP_CR_ERROR
    }       strip_cr;
    MAIL_STREAM *handle;
    VSTRING *postdrop_command;
    uid_t   uid = getuid();
    int     status;
    int     naddr;
    int     prev_type;
    MIME_STATE *mime_state = 0;
    SM_STATE state;
    int     mime_errs;
    const char *errstr;
    int     addr_count;
    int     level;
    static NAME_CODE sm_fix_eol_table[] = {
	SM_FIX_EOL_ALWAYS, STRIP_CR_DO,
	SM_FIX_EOL_STRICT, STRIP_CR_DUNNO,
	SM_FIX_EOL_NEVER, STRIP_CR_DONT,
	0, STRIP_CR_ERROR,
    };

    /*
     * Access control is enforced in the postdrop command. The code here
     * merely produces a more user-friendly interface.
     */
    if ((errstr = check_user_acl_byuid(VAR_SUBMIT_ACL,
				       var_submit_acl, uid)) != 0)
	msg_fatal_status(EX_NOPERM,
	  "User %s(%ld) is not allowed to submit mail", errstr, (long) uid);

    /*
     * Initialize.
     */
    buf = vstring_alloc(100);

    /*
     * Stop run-away process accidents by limiting the queue file size. This
     * is not a defense against DOS attack.
     */
    if (var_message_limit > 0 && get_file_limit() > var_message_limit)
	set_file_limit((off_t) var_message_limit);

    /*
     * The sender name is provided by the user. In principle, the mail pickup
     * service could deduce the sender name from queue file ownership, but:
     * pickup would not be able to run chrooted, and it may not be desirable
     * to use login names at all.
     */
    if (sender != 0) {
	VSTRING_RESET(buf);
	VSTRING_TERMINATE(buf);
	tree = tok822_parse(sender);
	for (naddr = 0, tp = tree; tp != 0; tp = tp->next)
	    if (tp->type == TOK822_ADDR && naddr++ == 0)
		tok822_internalize(buf, tp->head, TOK822_STR_DEFL);
	tok822_free_tree(tree);
	saved_sender = mystrdup(STR(buf));
	if (naddr > 1)
	    msg_warn("-f option specified malformed sender: %s", sender);
    } else {
	if ((sender = username()) == 0)
	    msg_fatal_status(EX_OSERR, "no login name found for user ID %lu",
			     (unsigned long) uid);
	saved_sender = mystrdup(sender);
    }

    /*
     * Let the postdrop command open the queue file for us, and sanity check
     * the content. XXX Make postdrop a manifest constant.
     */
    errno = 0;
    postdrop_command = vstring_alloc(1000);
    vstring_sprintf(postdrop_command, "%s/postdrop -r", var_command_dir);
    for (level = 0; level < msg_verbose; level++)
	vstring_strcat(postdrop_command, " -v");
    if ((handle = mail_stream_command(STR(postdrop_command))) == 0)
	msg_fatal_status(EX_UNAVAILABLE, "%s(%ld): unable to execute %s: %m",
			 saved_sender, (long) uid, STR(postdrop_command));
    vstring_free(postdrop_command);
    dst = handle->stream;

    /*
     * First, write envelope information to the output stream.
     * 
     * For sendmail compatibility, parse each command-line recipient as if it
     * were an RFC 822 message header; some MUAs specify comma-separated
     * recipient lists; and some MUAs even specify "word word <address>".
     * 
     * Sort-uniq-ing the recipient list is done after address canonicalization,
     * before recipients are written to queue file. That's cleaner than
     * having the queue manager nuke duplicate recipient status records.
     * 
     * XXX Should limit the size of envelope records.
     * 
     * With "sendmail -N", instead of a per-message NOTIFY record we store one
     * per recipient so that we can simplify the implementation somewhat.
     */
    if (dsn_envid)
	rec_fprintf(dst, REC_TYPE_ATTR, "%s=%s",
		    MAIL_ATTR_DSN_ENVID, dsn_envid);
    if (dsn_ret)
	rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d",
		    MAIL_ATTR_DSN_RET, dsn_ret);
    rec_fprintf(dst, REC_TYPE_ATTR, "%s=%s",
		MAIL_ATTR_RWR_CONTEXT, rewrite_context);
    if (full_name || (full_name = fullname()) != 0)
	rec_fputs(dst, REC_TYPE_FULL, full_name);
    rec_fputs(dst, REC_TYPE_FROM, saved_sender);
    if (verp_delims && *saved_sender == 0)
	msg_fatal_status(EX_USAGE,
		      "%s(%ld): -V option requires non-null sender address",
			 saved_sender, (long) uid);
    if (encoding)
	rec_fprintf(dst, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_ENCODING, encoding);
    if (DEL_REQ_TRACE_FLAGS(flags))
	rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d", MAIL_ATTR_TRACE_FLAGS,
		    DEL_REQ_TRACE_FLAGS(flags));
    if (verp_delims)
	rec_fputs(dst, REC_TYPE_VERP, verp_delims);
    if (recipients) {
	for (cpp = recipients; *cpp != 0; cpp++) {
	    tree = tok822_parse(*cpp);
	    for (addr_count = 0, tp = tree; tp != 0; tp = tp->next) {
		if (tp->type == TOK822_ADDR) {
		    tok822_internalize(buf, tp->head, TOK822_STR_DEFL);
		    if (dsn_notify)
			rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d",
				    MAIL_ATTR_DSN_NOTIFY, dsn_notify);
		    if (REC_PUT_BUF(dst, REC_TYPE_RCPT, buf) < 0)
			msg_fatal_status(EX_TEMPFAIL,
				    "%s(%ld): error writing queue file: %m",
					 saved_sender, (long) uid);
		    ++rcpt_count;
		    ++addr_count;
		}
	    }
	    tok822_free_tree(tree);
	    if (addr_count == 0) {
		if (rec_put(dst, REC_TYPE_RCPT, "", 0) < 0)
		    msg_fatal_status(EX_TEMPFAIL,
				     "%s(%ld): error writing queue file: %m",
				     saved_sender, (long) uid);
		++rcpt_count;
	    }
	}
    }

    /*
     * Append the message contents to the queue file. Write chunks of at most
     * 1kbyte. Internally, we use different record types for data ending in
     * LF and for data that doesn't, so we can actually be binary transparent
     * for local mail. Unfortunately, SMTP has no record continuation
     * convention, so there is no guarantee that arbitrary data will be
     * delivered intact via SMTP. Strip leading From_ lines. For the benefit
     * of UUCP environments, also get rid of leading >>>From_ lines.
     */
    rec_fputs(dst, REC_TYPE_MESG, "");
    if (DEL_REQ_TRACE_ONLY(flags) != 0) {
	if (flags & SM_FLAG_XRCPT)
	    msg_fatal_status(EX_USAGE, "%s(%ld): -t option cannot be used with -bv",
			     saved_sender, (long) uid);
	if (*saved_sender)
	    rec_fprintf(dst, REC_TYPE_NORM, "From: %s", saved_sender);
	rec_fprintf(dst, REC_TYPE_NORM, "Subject: probe");
	if (recipients) {
	    rec_fprintf(dst, REC_TYPE_CONT, "To:");
	    for (cpp = recipients; *cpp != 0; cpp++) {
		rec_fprintf(dst, REC_TYPE_NORM, "	%s%s",
			    *cpp, cpp[1] ? "," : "");
	    }
	}
    } else {

	/*
	 * Initialize the MIME processor and set up the callback context.
	 */
	if (flags & SM_FLAG_XRCPT) {
	    state.dst = dst;
	    state.recipients = argv_alloc(2);
	    state.resent_recip = argv_alloc(2);
	    state.resent = 0;
	    state.saved_sender = saved_sender;
	    state.uid = uid;
	    state.temp = vstring_alloc(10);
	    mime_state = mime_state_alloc(MIME_OPT_DISABLE_MIME
					  | MIME_OPT_REPORT_TRUNC_HEADER,
					  output_header,
					  (MIME_STATE_ANY_END) 0,
					  output_text,
					  (MIME_STATE_ANY_END) 0,
					  (MIME_STATE_ERR_PRINT) 0,
					  (void *) &state);
	}

	/*
	 * Process header/body lines.
	 */
	skip_from_ = 1;
	strip_cr = name_code(sm_fix_eol_table, NAME_CODE_FLAG_STRICT_CASE,
			     var_sm_fix_eol);
	if (strip_cr == STRIP_CR_ERROR)
	    msg_fatal_status(EX_USAGE,
		    "invalid %s value: %s", VAR_SM_FIX_EOL, var_sm_fix_eol);
	for (prev_type = 0; (type = rec_streamlf_get(VSTREAM_IN, buf, var_line_limit))
	     != REC_TYPE_EOF; prev_type = type) {
	    if (strip_cr == STRIP_CR_DUNNO && type == REC_TYPE_NORM) {
		if (VSTRING_LEN(buf) > 0 && vstring_end(buf)[-1] == '\r')
		    strip_cr = STRIP_CR_DO;
		else
		    strip_cr = STRIP_CR_DONT;
	    }
	    if (skip_from_) {
		if (type == REC_TYPE_NORM) {
		    start = STR(buf);
		    if (strncmp(start + strspn(start, ">"), "From ", 5) == 0)
			continue;
		}
		skip_from_ = 0;
	    }
	    if (strip_cr == STRIP_CR_DO && type == REC_TYPE_NORM)
		while (VSTRING_LEN(buf) > 0 && vstring_end(buf)[-1] == '\r')
		    vstring_truncate(buf, VSTRING_LEN(buf) - 1);
	    if ((flags & SM_FLAG_AEOF) && prev_type != REC_TYPE_CONT
		&& VSTRING_LEN(buf) == 1 && *STR(buf) == '.')
		break;
	    if (mime_state) {
		mime_errs = mime_state_update(mime_state, type, STR(buf),
					      VSTRING_LEN(buf));
		if (mime_errs)
		    msg_fatal_status(EX_DATAERR,
				"%s(%ld): unable to extract recipients: %s",
				     saved_sender, (long) uid,
				     mime_state_error(mime_errs));
	    } else {
		if (REC_PUT_BUF(dst, type, buf) < 0)
		    msg_fatal_status(EX_TEMPFAIL,
				     "%s(%ld): error writing queue file: %m",
				     saved_sender, (long) uid);
	    }
	}
    }

    /*
     * Finish MIME processing. We need a final mime_state_update() call in
     * order to flush text that is still buffered. That can happen when the
     * last line did not end in newline.
     */
    if (mime_state) {
	mime_errs = mime_state_update(mime_state, REC_TYPE_EOF, "", 0);
	if (mime_errs)
	    msg_fatal_status(EX_DATAERR,
			     "%s(%ld): unable to extract recipients: %s",
			     saved_sender, (long) uid,
			     mime_state_error(mime_errs));
	mime_state = mime_state_free(mime_state);
    }

    /*
     * Append recipient addresses that were extracted from message headers.
     */
    rec_fputs(dst, REC_TYPE_XTRA, "");
    if (flags & SM_FLAG_XRCPT) {
	for (cpp = state.resent ? state.resent_recip->argv :
	     state.recipients->argv; *cpp; cpp++) {
	    if (dsn_notify)
		rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d",
			    MAIL_ATTR_DSN_NOTIFY, dsn_notify);
	    if (rec_put(dst, REC_TYPE_RCPT, *cpp, strlen(*cpp)) < 0)
		msg_fatal_status(EX_TEMPFAIL,
				 "%s(%ld): error writing queue file: %m",
				 saved_sender, (long) uid);
	    ++rcpt_count;
	}
	argv_free(state.recipients);
	argv_free(state.resent_recip);
	vstring_free(state.temp);
    }
    if (rcpt_count == 0)
	msg_fatal_status(EX_USAGE, (flags & SM_FLAG_XRCPT) ?
		 "%s(%ld): No recipient addresses found in message header" :
			 "%s(%ld): Recipient addresses must be specified on"
			 " the command line or via the -t option",
			 saved_sender, (long) uid);

    /*
     * Identify the end of the queue file.
     */
    rec_fputs(dst, REC_TYPE_END, "");

    /*
     * Make sure that the message makes it to the file system. Once we have
     * terminated with successful exit status we cannot lose the message due
     * to "frivolous reasons". If all goes well, prevent the run-time error
     * handler from removing the file.
     */
    if (vstream_ferror(VSTREAM_IN))
	msg_fatal_status(EX_DATAERR, "%s(%ld): error reading input: %m",
			 saved_sender, (long) uid);
    if ((status = mail_stream_finish(handle, (VSTRING *) 0)) != 0)
	msg_fatal_status((status & CLEANUP_STAT_BAD) ? EX_SOFTWARE :
			 (status & CLEANUP_STAT_WRITE) ? EX_TEMPFAIL :
			 EX_UNAVAILABLE, "%s(%ld): %s", saved_sender,
			 (long) uid, cleanup_strerror(status));

    /*
     * Don't leave them in the dark.
     */
    if (DEL_REQ_TRACE_FLAGS(flags)) {
	vstream_printf("Mail Delivery Status Report will be mailed to <%s>.\n",
		       saved_sender);
	vstream_fflush(VSTREAM_OUT);
    }

    /*
     * Cleanup. Not really necessary as we're about to exit, but good for
     * debugging purposes.
     */
    vstring_free(buf);
    myfree(saved_sender);
}
Exemplo n.º 8
0
static void dict_pgsql_quote(DICT *dict, const char *name, VSTRING *result)
{
    DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
    HOST   *active_host = dict_pgsql->active_host;
    char   *myname = "dict_pgsql_quote";
    size_t  len = strlen(name);
    size_t  buflen = 2 * len + 1;
    int     err = 1;

    if (active_host == 0)
	msg_panic("%s: bogus dict_pgsql->active_host", myname);

    /*
     * We won't get arithmetic overflows in 2*len + 1, because Postfix input
     * keys have reasonable size limits, better safe than sorry.
     */
    if (buflen <= len)
	msg_panic("%s: arithmetic overflow in 2*%lu+1",
		  myname, (unsigned long) len);

    /*
     * XXX Workaround: stop further processing when PQescapeStringConn()
     * (below) fails. A more proper fix requires invasive changes, not
     * suitable for a stable release.
     */
    if (active_host->stat == STATFAIL)
	return;

    /*
     * Escape the input string, using PQescapeStringConn(), because the older
     * PQescapeString() is not safe anymore, as stated by the documentation.
     * 
     * From current libpq (8.1.4) documentation:
     * 
     * PQescapeStringConn writes an escaped version of the from string to the to
     * buffer, escaping special characters so that they cannot cause any
     * harm, and adding a terminating zero byte.
     * 
     * ...
     * 
     * The parameter from points to the first character of the string that is to
     * be escaped, and the length parameter gives the number of bytes in this
     * string. A terminating zero byte is not required, and should not be
     * counted in length.
     * 
     * ...
     * 
     * (The parameter) to shall point to a buffer that is able to hold at least
     * one more byte than twice the value of length, otherwise the behavior
     * is undefined.
     * 
     * ...
     * 
     * If the error parameter is not NULL, then *error is set to zero on
     * success, nonzero on error ... The output string is still generated on
     * error, but it can be expected that the server will reject it as
     * malformed. On error, a suitable message is stored in the conn object,
     * whether or not error is NULL.
     */
    VSTRING_SPACE(result, buflen);
    PQescapeStringConn(active_host->db, vstring_end(result), name, len, &err);
    if (err == 0) {
	VSTRING_SKIP(result);
    } else {

	/*
	 * PQescapeStringConn() failed. According to the docs, we still have
	 * a valid, null-terminated output string, but we need not rely on
	 * this behavior.
	 */
	msg_warn("dict pgsql: (host %s) cannot escape input string: %s",
		 active_host->hostname, PQerrorMessage(active_host->db));
	active_host->stat = STATFAIL;
	VSTRING_TERMINATE(result);
    }
}
Exemplo n.º 9
0
const char *mail_date(time_t when)
{
    static VSTRING *vp;
    struct tm *lt;
    struct tm gmt;
    int     gmtoff;

    /*
     * As if strftime() isn't expensive enough, we're dynamically adjusting
     * the size for the result, so we won't be surprised by long names etc.
     */
    if (vp == 0)
	vp = vstring_alloc(100);
    else
	VSTRING_RESET(vp);

    /*
     * POSIX does not require that struct tm has a tm_gmtoff field, so we
     * must compute the time offset from UTC by hand.
     * 
     * Starting with the difference in hours/minutes between 24-hour clocks,
     * adjust for differences in years, in yeardays, and in (leap) seconds.
     * 
     * Assume 0..23 hours in a day, 0..59 minutes in an hour. The library spec
     * has changed: we can no longer assume that there are 0..59 seconds in a
     * minute.
     */
    gmt = *gmtime(&when);
    lt = localtime(&when);
    gmtoff = (lt->tm_hour - gmt.tm_hour) * HOUR_MIN + lt->tm_min - gmt.tm_min;
    if (lt->tm_year < gmt.tm_year)
	gmtoff -= DAY_MIN;
    else if (lt->tm_year > gmt.tm_year)
	gmtoff += DAY_MIN;
    else if (lt->tm_yday < gmt.tm_yday)
	gmtoff -= DAY_MIN;
    else if (lt->tm_yday > gmt.tm_yday)
	gmtoff += DAY_MIN;
    if (lt->tm_sec <= gmt.tm_sec - MIN_SEC)
	gmtoff -= 1;
    else if (lt->tm_sec >= gmt.tm_sec + MIN_SEC)
	gmtoff += 1;

    /*
     * First, format the date and wall-clock time. XXX The %e format (day of
     * month, leading zero replaced by blank) isn't in my POSIX book, but
     * many vendors seem to support it.
     */
#ifdef MISSING_STRFTIME_E
#define STRFTIME_FMT "%a, %d %b %Y %H:%M:%S "
#else
#define STRFTIME_FMT "%a, %e %b %Y %H:%M:%S "
#endif

    while (strftime(vstring_end(vp), vstring_avail(vp), STRFTIME_FMT, lt) == 0)
	VSTRING_SPACE(vp, 100);
    VSTRING_SKIP(vp);

    /*
     * Then, add the UTC offset.
     */
    if (gmtoff < -DAY_MIN || gmtoff > DAY_MIN)
	msg_panic("UTC time offset %d is larger than one day", gmtoff);
    vstring_sprintf_append(vp, "%+03d%02d", (int) (gmtoff / HOUR_MIN),
			   (int) (abs(gmtoff) % HOUR_MIN));

    /*
     * Finally, add the time zone name.
     */
    while (strftime(vstring_end(vp), vstring_avail(vp), " (%Z)", lt) == 0)
	VSTRING_SPACE(vp, vstring_avail(vp) + 100);
    VSTRING_SKIP(vp);

    return (vstring_str(vp));
}
Exemplo n.º 10
0
int     smtp_get(VSTRING *vp, VSTREAM *stream, int bound)
{
    int     last_char;
    int     next_char;

    /*
     * It's painful to do I/O with records that may span multiple buffers.
     * Allow for partial long lines (we will read the remainder later) and
     * allow for lines ending in bare LF. The idea is to be liberal in what
     * we accept, strict in what we send.
     * 
     * XXX 2821: Section 4.1.1.4 says that an SMTP server must not recognize
     * bare LF as record terminator.
     */
    smtp_timeout_reset(stream);
    last_char = (bound == 0 ? vstring_get(vp, stream) :
		 vstring_get_bound(vp, stream, bound));

    switch (last_char) {

	/*
	 * Do some repair in the rare case that we stopped reading in the
	 * middle of the CRLF record terminator.
	 */
    case '\r':
	if ((next_char = VSTREAM_GETC(stream)) == '\n') {
	    VSTRING_ADDCH(vp, '\n');
	    last_char = '\n';
	    /* FALLTRHOUGH */
	} else {
	    if (next_char != VSTREAM_EOF)
		vstream_ungetc(stream, next_char);
	    break;
	}

	/*
	 * Strip off the record terminator: either CRLF or just bare LF.
	 * 
	 * XXX RFC 2821 disallows sending bare CR everywhere. We remove bare CR
	 * if received before CRLF, and leave it alone otherwise.
	 */
    case '\n':
	vstring_truncate(vp, VSTRING_LEN(vp) - 1);
	while (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r')
	    vstring_truncate(vp, VSTRING_LEN(vp) - 1);
	VSTRING_TERMINATE(vp);

	/*
	 * Partial line: just read the remainder later. If we ran into EOF,
	 * the next test will deal with it.
	 */
    default:
	break;
    }
    smtp_timeout_detect(stream);

    /*
     * EOF is bad, whether or not it happens in the middle of a record. Don't
     * allow data that was truncated because of EOF.
     */
    if (vstream_feof(stream) || vstream_ferror(stream)) {
	if (msg_verbose)
	    msg_info("smtp_get: EOF");
	vstream_longjmp(stream, SMTP_ERR_EOF);
    }
    return (last_char);
}