int memcache_fread(VSTREAM *stream, VSTRING *buf, ssize_t todo) { /* * Sanity check. */ if (todo < 0) msg_panic("memcache_fread: negative todo %ld", (long) todo); /* * Do the I/O. */ if (vstream_fread_buf(stream, buf, todo) != todo || VSTREAM_GETC(stream) != '\r' || VSTREAM_GETC(stream) != '\n') { if (msg_verbose) msg_info("%s read: error", VSTREAM_PATH(stream)); return (-1); } else { VSTRING_TERMINATE(buf); if (msg_verbose) msg_info("%s read: %s", VSTREAM_PATH(stream), STR(buf)); return (0); } }
VSTRING *readlline(VSTRING *buf, VSTREAM *fp, int *lineno) { int ch; int next; int start; char *cp; VSTRING_RESET(buf); /* * Ignore comment lines, all whitespace lines, and empty lines. Terminate * at EOF or at the beginning of the next logical line. */ for (;;) { /* Read one line, possibly not newline terminated. */ start = LEN(buf); while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n') VSTRING_ADDCH(buf, ch); if (ch == '\n' && lineno != 0) *lineno += 1; /* Ignore comment line, all whitespace line, or empty line. */ for (cp = STR(buf) + start; cp < END(buf) && ISSPACE(*cp); cp++) /* void */ ; if (cp == END(buf) || *cp == '#') vstring_truncate(buf, start); /* Terminate at EOF or at the beginning of the next logical line. */ if (ch == VSTREAM_EOF) break; if (LEN(buf) > 0) { if ((next = VSTREAM_GETC(fp)) != VSTREAM_EOF) vstream_ungetc(fp, next); if (next != '#' && !ISSPACE(next)) break; } } VSTRING_TERMINATE(buf); /* * Invalid input: continuing text without preceding text. Allowing this * would complicate "postconf -e", which implements its own multi-line * parsing routine. Do not abort, just warn, so that critical programs * like postmap do not leave behind a truncated table. */ if (LEN(buf) > 0 && ISSPACE(*STR(buf))) { msg_warn("%s: logical line must not start with whitespace: \"%.30s%s\"", VSTREAM_PATH(fp), STR(buf), LEN(buf) > 30 ? "..." : ""); return (readlline(buf, fp, lineno)); } /* * Done. */ return (LEN(buf) > 0 ? buf : 0); }
static int attr_scan_plain_string(VSTREAM *fp, VSTRING *plain_buf, int terminator, const char *context) { #if 0 extern int var_line_limit; /* XXX */ int limit = var_line_limit * 4; #endif int ch; VSTRING_RESET(plain_buf); while ((ch = VSTREAM_GETC(fp)) != '\n' && (terminator == 0 || ch != terminator)) { if (ch == VSTREAM_EOF) { msg_warn("%s on %s while reading %s", vstream_ftimeout(fp) ? "timeout" : "premature end-of-input", VSTREAM_PATH(fp), context); return (-1); } VSTRING_ADDCH(plain_buf, ch); #if 0 if (LEN(plain_buf) > limit) { msg_warn("string length > %d characters from %s while reading %s", limit, VSTREAM_PATH(fp), context); return (-1); } #endif } VSTRING_TERMINATE(plain_buf); if (msg_verbose) msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)"); return (ch); }
ssize_t netstring_get_length(VSTREAM *stream) { const char *myname = "netstring_get_length"; ssize_t len = 0; int ch; for (;;) { switch (ch = VSTREAM_GETC(stream)) { case VSTREAM_EOF: netstring_except(stream, vstream_ftimeout(stream) ? NETSTRING_ERR_TIME : NETSTRING_ERR_EOF); case ':': if (msg_verbose > 1) msg_info("%s: read netstring length %ld", myname, (long) len); return (len); default: if (!ISDIGIT(ch)) netstring_except(stream, NETSTRING_ERR_FORMAT); len = len * 10 + ch - '0'; /* vstream_fread() would read zero bytes. Reject input anyway. */ if (len < 0) netstring_except(stream, NETSTRING_ERR_SIZE); break; } } }
static int deliver_request_final(VSTREAM *stream, DELIVER_REQUEST *request, int status) { DSN *hop_status; int err; /* XXX This DSN structure initialization bypasses integrity checks. */ static DSN dummy_dsn = {"", "", "", "", "", "", ""}; /* * Send the status and the optional reason. */ if ((hop_status = request->hop_status) == 0) hop_status = &dummy_dsn; if (msg_verbose) msg_info("deliver_request_final: send: \"%s\" %d", hop_status->reason, status); attr_print(stream, ATTR_FLAG_NONE, ATTR_TYPE_FUNC, dsn_print, (void *) hop_status, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, ATTR_TYPE_END); if ((err = vstream_fflush(stream)) != 0) if (msg_verbose) msg_warn("send final status: %m"); /* * With some UNIX systems, stream sockets lose data when you close them * immediately after writing to them. That is not how sockets are * supposed to behave! The workaround is to wait until the receiver * closes the connection. Calling VSTREAM_GETC() has the benefit of using * whatever timeout is specified in the ipc_timeout parameter. */ (void) VSTREAM_GETC(stream); return (err); }
static void read_data(int unused_event, void *context) { SINK_STATE *state = (SINK_STATE *) context; int fd = vstream_fileno(state->stream); int count; /* * Refill the VSTREAM buffer, if necessary. */ if (VSTREAM_GETC(state->stream) == VSTREAM_EOF) netstring_except(state->stream, vstream_ftimeout(state->stream) ? NETSTRING_ERR_TIME : NETSTRING_ERR_EOF); state->count--; /* * Flush the VSTREAM buffer. As documented, vstream_fseek() discards * unread input. */ if ((count = vstream_peek(state->stream)) > 0) { state->count -= count; if (state->count <= 0) { send_reply(state); return; } vstream_fpurge(state->stream, VSTREAM_PURGE_BOTH); } /* * Do not block while waiting for the arrival of more data. */ event_disable_readwrite(fd); event_enable_read(fd, read_data, context); }
int vstring_get_null(VSTRING *vp, VSTREAM *fp) { int c; VSTRING_RESET(vp); while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0) VSTRING_ADDCH(vp, c); VSTRING_TERMINATE(vp); return (c == 0 ? c : VSTRING_GET_RESULT(vp)); }
int vstring_get_null_bound(VSTRING *vp, VSTREAM *fp, ssize_t bound) { int c; if (bound <= 0) msg_panic("vstring_get_nonl_bound: invalid bound %ld", (long) bound); VSTRING_RESET(vp); while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0) VSTRING_ADDCH(vp, c); VSTRING_TERMINATE(vp); return (c == 0 ? c : VSTRING_GET_RESULT(vp)); }
int vstring_get(VSTRING *vp, VSTREAM *fp) { int c; VSTRING_RESET(vp); while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF) { VSTRING_ADDCH(vp, c); if (c == '\n') break; } VSTRING_TERMINATE(vp); return (VSTRING_GET_RESULT(vp)); }
int vstring_get_bound(VSTRING *vp, VSTREAM *fp, ssize_t bound) { int c; if (bound <= 0) msg_panic("vstring_get_bound: invalid bound %ld", (long) bound); VSTRING_RESET(vp); while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF) { VSTRING_ADDCH(vp, c); if (c == '\n') break; } VSTRING_TERMINATE(vp); return (VSTRING_GET_RESULT(vp)); }
static void pcf_gobble_cf_line(VSTRING *full_entry_buf, VSTRING *line_buf, VSTREAM *src, VSTREAM *dst, int *lineno) { int ch; vstring_strcpy(full_entry_buf, STR(line_buf)); for (;;) { if ((ch = VSTREAM_GETC(src)) != VSTREAM_EOF) vstream_ungetc(src, ch); if ((ch != '#' && !ISSPACE(ch)) || vstring_get(line_buf, src) == VSTREAM_EOF) break; lineno += 1; if (pcf_find_cf_info(line_buf, dst)) vstring_strcat(full_entry_buf, STR(line_buf)); } }
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); } }
static void qmqpd_copy_recipients(QMQPD_STATE *state) { int ch; /* * Remember the first recipient. We are done when we read the over-all * netstring terminator. * * XXX This approach violates abstractions, but it is a heck of a lot more * convenient than counting the over-all byte count down to zero, like * qmail does. */ state->where = "receiving recipient address"; while ((ch = VSTREAM_GETC(state->client)) != ',') { vstream_ungetc(state->client, ch); netstring_get(state->client, state->buf, var_line_limit); if (state->err == CLEANUP_STAT_OK && REC_PUT_BUF(state->cleanup, REC_TYPE_RCPT, state->buf) < 0) state->err = CLEANUP_STAT_WRITE; state->rcpt_count++; if (state->recipient == 0) state->recipient = mystrndup(STR(state->buf), LEN(state->buf)); } }
static void psc_endpt_haproxy_event(int event, char *context) { const char *myname = "psc_endpt_haproxy_event"; PSC_HAPROXY_STATE *state = (PSC_HAPROXY_STATE *) context; int status = 0; MAI_HOSTADDR_STR smtp_client_addr; MAI_SERVPORT_STR smtp_client_port; MAI_HOSTADDR_STR smtp_server_addr; MAI_SERVPORT_STR smtp_server_port; int last_char = 0; const char *err; VSTRING *escape_buf; char read_buf[HAPROXY_MAX_LEN]; ssize_t read_len; char *cp; /* * We must not read(2) past the <CR><LF> that terminates the haproxy * line. For efficiency reasons we read the entire haproxy line in one * read(2) call when we know that the line is unfragmented. In the rare * case that the line is fragmented, we fall back and read(2) it one * character at a time. */ switch (event) { case EVENT_TIME: msg_warn("haproxy read: time limit exceeded"); status = -1; break; case EVENT_READ: /* Determine the initial VSTREAM read(2) buffer size. */ if (VSTRING_LEN(state->buffer) == 0) { if ((read_len = recv(vstream_fileno(state->stream), read_buf, sizeof(read_buf) - 1, MSG_PEEK)) > 0 && ((cp = memchr(read_buf, '\n', read_len)) != 0)) { read_len = cp - read_buf + 1; } else { read_len = 1; } vstream_control(state->stream, VSTREAM_CTL_BUFSIZE, read_len, VSTREAM_CTL_END); } /* Drain the VSTREAM buffer, otherwise this pseudo-thread will hang. */ do { if ((last_char = VSTREAM_GETC(state->stream)) == VSTREAM_EOF) { if (vstream_ferror(state->stream)) msg_warn("haproxy read: %m"); else msg_warn("haproxy read: lost connection"); status = -1; break; } if (VSTRING_LEN(state->buffer) >= HAPROXY_MAX_LEN) { msg_warn("haproxy read: line too long"); status = -1; break; } VSTRING_ADDCH(state->buffer, last_char); } while (vstream_peek(state->stream) > 0); break; } /* * Parse the haproxy line. Note: the haproxy_srvr_parse() routine * performs address protocol checks, address and port syntax checks, and * converts IPv4-in-IPv6 address string syntax (:ffff::1.2.3.4) to IPv4 * syntax where permitted by the main.cf:inet_protocols setting. */ if (status == 0 && last_char == '\n') { VSTRING_TERMINATE(state->buffer); if ((err = haproxy_srvr_parse(vstring_str(state->buffer), &smtp_client_addr, &smtp_client_port, &smtp_server_addr, &smtp_server_port)) != 0) { escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2); escape(escape_buf, vstring_str(state->buffer), VSTRING_LEN(state->buffer)); msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf)); status = -1; vstring_free(escape_buf); } } /* * Are we done yet? */ if (status < 0 || last_char == '\n') { PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->stream), psc_endpt_haproxy_event, context); vstream_control(state->stream, VSTREAM_CTL_BUFSIZE, (ssize_t) VSTREAM_BUFSIZE, VSTREAM_CTL_END); state->notify(status, state->stream, &smtp_client_addr, &smtp_client_port, &smtp_server_addr, &smtp_server_port); /* Note: the stream may be closed at this point. */ vstring_free(state->buffer); myfree((char *) state); } }
static void postcat(VSTREAM *fp, VSTRING *buffer, int flags) { int prev_type = 0; int rec_type; struct timeval tv; time_t time; int ch; off_t offset; const char *error_text; char *attr_name; char *attr_value; int rec_flags = (msg_verbose ? REC_FLAG_NONE : REC_FLAG_DEFAULT); int state; /* state machine, input type */ int do_print; /* state machine, output control */ long data_offset; /* state machine, read optimization */ long data_size; /* state machine, read optimization */ #define TEXT_RECORD(rec_type) \ (rec_type == REC_TYPE_CONT || rec_type == REC_TYPE_NORM) /* * See if this is a plausible file. */ if ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) { if (!strchr(REC_TYPE_ENVELOPE, ch)) { msg_warn("%s: input is not a valid queue file", VSTREAM_PATH(fp)); return; } vstream_ungetc(fp, ch); } /* * Other preliminaries. */ if (flags & PC_FLAG_PRINT_ENV) vstream_printf("*** ENVELOPE RECORDS %s ***\n", VSTREAM_PATH(fp)); state = PC_STATE_ENV; do_print = (flags & PC_FLAG_PRINT_ENV); data_offset = data_size = -1; /* * Now look at the rest. */ for (;;) { if (flags & PC_FLAG_PRINT_OFFSET) offset = vstream_ftell(fp); rec_type = rec_get_raw(fp, buffer, 0, rec_flags); if (rec_type == REC_TYPE_ERROR) msg_fatal("record read error"); if (rec_type == REC_TYPE_EOF) break; /* * First inspect records that have side effects on the (envelope, * header, body) state machine or on the record reading order. * * XXX Comments marked "Optimization:" identify subtle code that will * likely need to be revised when the queue file organization is * changed. */ #define PRINT_MARKER(flags, fp, offset, type, text) do { \ if ((flags) & PC_FLAG_PRINT_OFFSET) \ vstream_printf("%9lu ", (unsigned long) (offset)); \ if (flags & PC_FLAG_PRINT_RTYPE_DEC) \ vstream_printf("%3d ", (type)); \ vstream_printf("*** %s %s ***\n", (text), VSTREAM_PATH(fp)); \ vstream_fflush(VSTREAM_OUT); \ } while (0) #define PRINT_RECORD(flags, offset, type, value) do { \ if ((flags) & PC_FLAG_PRINT_OFFSET) \ vstream_printf("%9lu ", (unsigned long) (offset)); \ if (flags & PC_FLAG_PRINT_RTYPE_DEC) \ vstream_printf("%3d ", (type)); \ vstream_printf("%s: %s\n", rec_type_name(rec_type), (value)); \ vstream_fflush(VSTREAM_OUT); \ } while (0) if (TEXT_RECORD(rec_type)) { /* This is wrong when the message starts with whitespace. */ if (state == PC_STATE_HEADER && (flags & (PC_MASK_PRINT_TEXT)) && prev_type != REC_TYPE_CONT && TEXT_RECORD(rec_type) && !(is_header(STR(buffer)) || IS_SPACE_TAB(STR(buffer)[0]))) { /* Update the state machine. */ state = PC_STATE_BODY; do_print = (flags & PC_FLAG_PRINT_BODY); /* Optimization: terminate if nothing left to print. */ if (do_print == 0 && (flags & PC_FLAG_PRINT_ENV) == 0) break; /* Optimization: skip to extracted segment marker. */ if (do_print == 0 && (flags & PC_FLAG_PRINT_ENV) && data_offset >= 0 && data_size >= 0 && vstream_fseek(fp, data_offset + data_size, SEEK_SET) < 0) msg_fatal("seek error: %m"); } /* Optional output happens further down below. */ } else if (rec_type == REC_TYPE_MESG) { /* Sanity check. */ if (state != PC_STATE_ENV) msg_warn("%s: out-of-order message content marker", VSTREAM_PATH(fp)); /* Optional output. */ if (flags & PC_FLAG_PRINT_ENV) PRINT_MARKER(flags, fp, offset, rec_type, "MESSAGE CONTENTS"); /* Optimization: skip to extracted segment marker. */ if ((flags & PC_MASK_PRINT_TEXT) == 0 && data_offset >= 0 && data_size >= 0 && vstream_fseek(fp, data_offset + data_size, SEEK_SET) < 0) msg_fatal("seek error: %m"); /* Update the state machine, even when skipping. */ state = PC_STATE_HEADER; do_print = (flags & PC_FLAG_PRINT_HEADER); continue; } else if (rec_type == REC_TYPE_XTRA) { /* Sanity check. */ if (state != PC_STATE_HEADER && state != PC_STATE_BODY) msg_warn("%s: out-of-order extracted segment marker", VSTREAM_PATH(fp)); /* Optional output (terminate preceding header/body line). */ if (do_print && prev_type == REC_TYPE_CONT) VSTREAM_PUTCHAR('\n'); if (flags & PC_FLAG_PRINT_ENV) PRINT_MARKER(flags, fp, offset, rec_type, "HEADER EXTRACTED"); /* Update the state machine. */ state = PC_STATE_ENV; do_print = (flags & PC_FLAG_PRINT_ENV); /* Optimization: terminate if nothing left to print. */ if (do_print == 0) break; continue; } else if (rec_type == REC_TYPE_END) { /* Sanity check. */ if (state != PC_STATE_ENV) msg_warn("%s: out-of-order message end marker", VSTREAM_PATH(fp)); /* Optional output. */ if (flags & PC_FLAG_PRINT_ENV) PRINT_MARKER(flags, fp, offset, rec_type, "MESSAGE FILE END"); /* Terminate the state machine. */ break; } else if (rec_type == REC_TYPE_PTR) { /* Optional output. */ /* This record type is exposed only with '-v'. */ if (do_print) PRINT_RECORD(flags, offset, rec_type, STR(buffer)); /* Skip to the pointer's target record. */ if (rec_goto(fp, STR(buffer)) == REC_TYPE_ERROR) msg_fatal("bad pointer record, or input is not seekable"); continue; } else if (rec_type == REC_TYPE_SIZE) { /* Optional output (here before we update the state machine). */ if (do_print) PRINT_RECORD(flags, offset, rec_type, STR(buffer)); /* Read the message size/offset for the state machine optimizer. */ if (data_size >= 0 || data_offset >= 0) { msg_warn("file contains multiple size records"); } else { if (sscanf(STR(buffer), "%ld %ld", &data_size, &data_offset) != 2 || data_offset <= 0 || data_size <= 0) msg_fatal("invalid size record: %.100s", STR(buffer)); /* Optimization: skip to the message header. */ if ((flags & PC_FLAG_PRINT_ENV) == 0) { if (vstream_fseek(fp, data_offset, SEEK_SET) < 0) msg_fatal("seek error: %m"); /* Update the state machine. */ state = PC_STATE_HEADER; do_print = (flags & PC_FLAG_PRINT_HEADER); } } continue; } /* * Don't inspect side-effect-free records that aren't printed. */ if (do_print == 0) continue; if (flags & PC_FLAG_PRINT_OFFSET) vstream_printf("%9lu ", (unsigned long) offset); if (flags & PC_FLAG_PRINT_RTYPE_DEC) vstream_printf("%3d ", rec_type); switch (rec_type) { case REC_TYPE_TIME: REC_TYPE_TIME_SCAN(STR(buffer), tv); time = tv.tv_sec; vstream_printf("%s: %s", rec_type_name(rec_type), asctime(localtime(&time))); break; case REC_TYPE_WARN: REC_TYPE_WARN_SCAN(STR(buffer), time); vstream_printf("%s: %s", rec_type_name(rec_type), asctime(localtime(&time))); break; case REC_TYPE_CONT: /* REC_TYPE_FILT collision */ if (state == PC_STATE_ENV) vstream_printf("%s: ", rec_type_name(rec_type)); else if (msg_verbose) vstream_printf("unterminated_text: "); vstream_fwrite(VSTREAM_OUT, STR(buffer), LEN(buffer)); if (state == PC_STATE_ENV || msg_verbose || (flags & PC_FLAG_PRINT_OFFSET) != 0) { rec_type = 0; VSTREAM_PUTCHAR('\n'); } break; case REC_TYPE_NORM: if (msg_verbose) vstream_printf("%s: ", rec_type_name(rec_type)); vstream_fwrite(VSTREAM_OUT, STR(buffer), LEN(buffer)); VSTREAM_PUTCHAR('\n'); break; case REC_TYPE_DTXT: /* This record type is exposed only with '-v'. */ vstream_printf("%s: ", rec_type_name(rec_type)); vstream_fwrite(VSTREAM_OUT, STR(buffer), LEN(buffer)); VSTREAM_PUTCHAR('\n'); break; case REC_TYPE_ATTR: error_text = split_nameval(STR(buffer), &attr_name, &attr_value); if (error_text != 0) { msg_warn("%s: malformed attribute: %s: %.100s", VSTREAM_PATH(fp), error_text, STR(buffer)); break; } if (strcmp(attr_name, MAIL_ATTR_CREATE_TIME) == 0) { time = atol(attr_value); vstream_printf("%s: %s", MAIL_ATTR_CREATE_TIME, asctime(localtime(&time))); } else { vstream_printf("%s: %s=%s\n", rec_type_name(rec_type), attr_name, attr_value); } break; default: vstream_printf("%s: %s\n", rec_type_name(rec_type), STR(buffer)); break; } prev_type = rec_type; /* * In case the next record is broken. */ vstream_fflush(VSTREAM_OUT); } }
int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap) { const char *myname = "attr_scan_plain"; static VSTRING *str_buf = 0; static VSTRING *name_buf = 0; int wanted_type = -1; char *wanted_name; unsigned int *number; unsigned long *long_number; VSTRING *string; HTABLE *hash_table; int ch; int conversions; ATTR_SCAN_SLAVE_FN scan_fn; void *scan_arg; /* * Sanity check. */ if (flags & ~ATTR_FLAG_ALL) msg_panic("%s: bad flags: 0x%x", myname, flags); /* * EOF check. */ if ((ch = VSTREAM_GETC(fp)) == VSTREAM_EOF) return (0); vstream_ungetc(fp, ch); /* * Initialize. */ if (str_buf == 0) { str_buf = vstring_alloc(10); name_buf = vstring_alloc(10); } /* * Iterate over all (type, name, value) triples. */ for (conversions = 0; /* void */ ; conversions++) { /* * Determine the next attribute type and attribute name on the * caller's wish list. * * If we're reading into a hash table, we already know that the * attribute value is string-valued, and we get the attribute name * from the input stream instead. This is secure only when the * resulting table is queried with known to be good attribute names. */ if (wanted_type != ATTR_TYPE_HASH && wanted_type != ATTR_TYPE_CLOSE) { wanted_type = va_arg(ap, int); if (wanted_type == ATTR_TYPE_END) { if ((flags & ATTR_FLAG_MORE) != 0) return (conversions); wanted_name = "(list terminator)"; } else if (wanted_type == ATTR_TYPE_HASH) { wanted_name = "(any attribute name or list terminator)"; hash_table = va_arg(ap, HTABLE *); } else if (wanted_type != ATTR_TYPE_FUNC) {
void netstring_get_terminator(VSTREAM *stream) { if (VSTREAM_GETC(stream) != ',') netstring_except(stream, NETSTRING_ERR_FORMAT); }
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); }