static const char *tok822_comment(TOK822 *tp, const char *str) { int level = 1; int ch; /* * XXX We cheat by storing comments in their external form. Otherwise it * would be a royal pain to preserve \ before (. That would require a * recursive parser; the easy to implement stack-based recursion would be * too expensive. */ VSTRING_ADDCH(tp->vstr, '('); while ((ch = *(unsigned char *) str) != 0) { VSTRING_ADDCH(tp->vstr, ch); str++; if (ch == '(') { /* comments can nest! */ level++; } else if (ch == ')') { if (--level == 0) break; } else if (ch == '\\') { if ((ch = *(unsigned char *) str) == 0) break; VSTRING_ADDCH(tp->vstr, ch); str++; } } VSTRING_TERMINATE(tp->vstr); return (str); }
VSTRING *format_tv(VSTRING *buf, int sec, int usec, int sig_dig, int max_dig) { static int pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000}; int n; int rem; int wid; int ures; /* * Sanity check. */ if (max_dig < 0 || max_dig > 6) msg_panic("format_tv: bad maximum decimal count %d", max_dig); if (sec < 0 || usec < 0 || usec > MILLION) msg_panic("format_tv: bad time %ds %dus", sec, usec); if (sig_dig < 1 || sig_dig > 6) msg_panic("format_tv: bad significant decimal count %d", sig_dig); ures = MILLION / pow10[max_dig]; wid = pow10[sig_dig]; /* * Adjust the resolution to suppress irrelevant digits. */ if (ures < MILLION) { if (sec > 0) { for (n = 1; sec >= n && n <= wid / 10; n *= 10) /* void */ ; ures = (MILLION / wid) * n; } else { while (usec >= wid * ures) ures *= 10; } } /* * Round up the number if necessary. Leave thrash below the resolution. */ if (ures > 1) { usec += ures / 2; if (usec >= MILLION) { sec += 1; usec -= MILLION; } } /* * Format the number. Truncate trailing null and thrash below resolution. */ vstring_sprintf_append(buf, "%d", sec); if (usec >= ures) { VSTRING_ADDCH(buf, '.'); for (rem = usec, n = MILLION / 10; rem >= ures && n > 0; n /= 10) { VSTRING_ADDCH(buf, "0123456789"[rem / n]); rem %= n; } } VSTRING_TERMINATE(buf); return (buf); }
static VSTRING *flush_site_to_path(VSTRING *path, const char *site) { const char *ptr; int ch; /* * Allocate buffer on the fly; caller still needs to clean up. */ if (path == 0) path = vstring_alloc(10); /* * Mask characters that could upset the name-to-queue-file mapping code. */ for (ptr = site; (ch = *(unsigned const char *) ptr) != 0; ptr++) if (ISALNUM(ch)) VSTRING_ADDCH(path, ch); else VSTRING_ADDCH(path, '_'); VSTRING_TERMINATE(path); if (msg_verbose) msg_info("site %s to path %s", site, STR(path)); return (path); }
static VSTRING *flush_site_to_path(VSTRING *path, const char *site) { const char *ptr; int ch; /* * Convert the name to ASCII, so that we don't to end up with non-ASCII * names in the file system. The IDNA library functions fold case. */ #ifndef NO_EAI if ((site = midna_domain_to_ascii(site)) == 0) return (0); #endif /* * Allocate buffer on the fly; caller still needs to clean up. */ if (path == 0) path = vstring_alloc(10); /* * Mask characters that could upset the name-to-queue-file mapping code. */ for (ptr = site; (ch = *(unsigned const char *) ptr) != 0; ptr++) if (ISALNUM(ch)) VSTRING_ADDCH(path, tolower(ch)); else VSTRING_ADDCH(path, '_'); VSTRING_TERMINATE(path); if (msg_verbose) msg_info("site %s to path %s", site, STR(path)); return (path); }
char *safe_ultostr(VSTRING *buf, unsigned long ulval, int base, int padlen, int padchar) { const char *myname = "safe_ultostr"; char *start; char *last; int i; /* * Sanity check. */ if (base < SAFE_MIN_BASE || base > SAFE_MAX_BASE) msg_panic("%s: bad base: %d", myname, base); /* * First accumulate the result, backwards. */ VSTRING_RESET(buf); while (ulval != 0) { VSTRING_ADDCH(buf, safe_chars[ulval % base]); ulval /= base; } while (VSTRING_LEN(buf) < padlen) VSTRING_ADDCH(buf, padchar); VSTRING_TERMINATE(buf); /* * Then, reverse the result. */ start = STR(buf); last = END(buf) - 1; for (i = 0; i < VSTRING_LEN(buf) / 2; i++) SWAP(int, start[i], last[-i]); return (STR(buf)); }
static void tok822_copy_quoted(VSTRING *vp, char *str, char *quote_set) { int ch; while ((ch = *(unsigned char *) str++) != 0) { if (strchr(quote_set, ch)) VSTRING_ADDCH(vp, '\\'); VSTRING_ADDCH(vp, ch); } }
static void mime_state_downgrade(MIME_STATE *state, int rec_type, const char *text, int len) { static char hexchars[] = "0123456789ABCDEF"; const unsigned char *cp; int ch; #define QP_ENCODE(buffer, ch) { \ VSTRING_ADDCH(buffer, '='); \ VSTRING_ADDCH(buffer, hexchars[(ch >> 4) & 0xff]); \ VSTRING_ADDCH(buffer, hexchars[ch & 0xf]); \ } /* * Insert a soft line break when the output reaches a critical length * before we reach a hard line break. */ for (cp = CU_CHAR_PTR(text); cp < CU_CHAR_PTR(text + len); cp++) { /* Critical length before hard line break. */ if (LEN(state->output_buffer) > 72) { VSTRING_ADDCH(state->output_buffer, '='); VSTRING_TERMINATE(state->output_buffer); BODY_OUT(state, REC_TYPE_NORM, STR(state->output_buffer), LEN(state->output_buffer)); VSTRING_RESET(state->output_buffer); } /* Append the next character. */ ch = *cp; if ((ch < 32 && ch != '\t') || ch == '=' || ch > 126) { QP_ENCODE(state->output_buffer, ch); } else { VSTRING_ADDCH(state->output_buffer, ch); } } /* * Flush output after a hard line break (i.e. the end of a REC_TYPE_NORM * record). Fix trailing whitespace as per the RFC: in the worst case, * the output length will grow from 73 characters to 75 characters. */ if (rec_type == REC_TYPE_NORM) { if (LEN(state->output_buffer) > 0 && ((ch = END(state->output_buffer)[-1]) == ' ' || ch == '\t')) { vstring_truncate(state->output_buffer, LEN(state->output_buffer) - 1); QP_ENCODE(state->output_buffer, ch); } VSTRING_TERMINATE(state->output_buffer); BODY_OUT(state, REC_TYPE_NORM, STR(state->output_buffer), LEN(state->output_buffer)); VSTRING_RESET(state->output_buffer); } }
static void encode_utf8(VSTRING *buffer, int codepoint) { const char myname[] = "encode_utf8"; VSTRING_RESET(buffer); if (codepoint < 0x80) { VSTRING_ADDCH(buffer, codepoint); } else if (codepoint < 0x800) { VSTRING_ADDCH(buffer, 0xc0 | (codepoint >> 6)); VSTRING_ADDCH(buffer, 0x80 | (codepoint & 0x3f)); } else if (codepoint < 0x10000) {
char *dir_forest(VSTRING *buf, const char *path, int depth) { const char *myname = "dir_forest"; static VSTRING *private_buf = 0; int n; const char *cp; int ch; /* * Sanity checks. */ if (*path == 0) msg_panic("%s: empty path", myname); if (depth < 1) msg_panic("%s: depth %d", myname, depth); /* * Your buffer or mine? */ if (buf == 0) { if (private_buf == 0) private_buf = vstring_alloc(1); buf = private_buf; } /* * Generate one or more subdirectory levels, depending on the pathname * contents. When the pathname is short, use underscores instead. * Disallow non-printable characters or characters that are special to * the file system. */ VSTRING_RESET(buf); for (cp = path, n = 0; n < depth; n++) { if ((ch = *cp) == 0) { ch = '_'; } else { if (!ISPRINT(ch) || ch == '.' || ch == '/') msg_panic("%s: invalid pathname: %s", myname, path); cp++; } VSTRING_ADDCH(buf, ch); VSTRING_ADDCH(buf, '/'); } VSTRING_TERMINATE(buf); if (msg_verbose > 1) msg_info("%s: %s -> %s", myname, path, vstring_str(buf)); return (vstring_str(buf)); }
static char *xsasl_dovecot_server_mech_filter(ARGV *mechanism_argv, XSASL_DCSRV_MECH *mechanism_list, unsigned int conf_props) { const char *myname = "xsasl_dovecot_server_mech_filter"; unsigned int pos_conf_props = (conf_props & SEC_PROPS_POS_MASK); unsigned int neg_conf_props = (conf_props & SEC_PROPS_NEG_MASK); VSTRING *mechanisms_str = vstring_alloc(10); XSASL_DCSRV_MECH *mp; /* * Match Postfix properties against Dovecot server properties. */ for (mp = mechanism_list; mp != 0; mp = mp->next) { if ((mp->sec_props & pos_conf_props) == pos_conf_props && (mp->sec_props & neg_conf_props) == 0) { if (VSTRING_LEN(mechanisms_str) > 0) VSTRING_ADDCH(mechanisms_str, ' '); vstring_strcat(mechanisms_str, mp->mech_name); argv_add(mechanism_argv, mp->mech_name, (char *) 0); if (msg_verbose) msg_info("%s: keep mechanism: %s", myname, mp->mech_name); } else { if (msg_verbose) msg_info("%s: skip mechanism: %s", myname, mp->mech_name); } } return (vstring_export(mechanisms_str)); }
VSTRING *off_cvt_number(VSTRING *buf, off_t offset) { static char digs[] = "0123456789"; char *start; char *last; int i; /* * Sanity checks */ if (offset < 0) msg_panic("off_cvt_number: negative offset -%s", STR(off_cvt_number(buf, -offset))); /* * First accumulate the result, backwards. */ VSTRING_RESET(buf); while (offset != 0) { VSTRING_ADDCH(buf, digs[offset % 10]); offset /= 10; } VSTRING_TERMINATE(buf); /* * Then, reverse the result. */ start = STR(buf); last = END(buf) - 1; for (i = 0; i < VSTRING_LEN(buf) / 2; i++) SWAP(int, start[i], last[-i]); return (buf); }
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); }
static void qmqpd_reply(QMQPD_STATE *state, int log_message, int status_code, const char *fmt,...) { va_list ap; /* * Optionally change hard errors into retryable ones. Send the reply and * optionally log it. Always insert a delay before reporting a problem. * This slows down software run-away conditions. */ if (status_code == QMQPD_STAT_HARD && var_soft_bounce) status_code = QMQPD_STAT_RETRY; VSTRING_RESET(state->buf); VSTRING_ADDCH(state->buf, status_code); va_start(ap, fmt); vstring_vsprintf_append(state->buf, fmt, ap); va_end(ap); NETSTRING_PUT_BUF(state->client, state->buf); if (log_message) (status_code == QMQPD_STAT_OK ? msg_info : msg_warn) ("%s: %s: %s", state->queue_id, state->namaddr, STR(state->buf) + 1); if (status_code != QMQPD_STAT_OK) sleep(var_qmqpd_err_sleep); netstring_fflush(state->client); }
VSTRING *xtext_unquote_append(VSTRING *unquoted, const char *quoted) { const char *cp; int ch; for (cp = quoted; (ch = *cp) != 0; cp++) { if (ch == '+') { if (ISDIGIT(cp[1])) ch = (cp[1] - '0') << 4; else if (cp[1] >= 'a' && cp[1] <= 'f') ch = (cp[1] - 'a' + 10) << 4; else if (cp[1] >= 'A' && cp[1] <= 'F') ch = (cp[1] - 'A' + 10) << 4; else return (0); if (ISDIGIT(cp[2])) ch |= (cp[2] - '0'); else if (cp[2] >= 'a' && cp[2] <= 'f') ch |= (cp[2] - 'a' + 10); else if (cp[2] >= 'A' && cp[2] <= 'F') ch |= (cp[2] - 'A' + 10); else return (0); cp += 2; } VSTRING_ADDCH(unquoted, ch); } VSTRING_TERMINATE(unquoted); return (unquoted); }
VSTRING *unquote_822_local(VSTRING *dst, const char *mbox) { const char *start; /* first byte of localpart */ const char *end; /* first byte after localpart */ const char *colon; const char *cp; if (mbox[0] == '@' && (colon = strchr(mbox, ':')) != 0) { start = colon + 1; vstring_strncpy(dst, mbox, start - mbox); } else { start = mbox; VSTRING_RESET(dst); } if ((end = strrchr(start, '@')) == 0) end = start + strlen(start); for (cp = start; cp < end; cp++) { if (*cp == '"') continue; if (*cp == '\\') { if (cp[1] == 0) continue; cp++; } VSTRING_ADDCH(dst, *cp); } if (*end) vstring_strcat(dst, end); else VSTRING_TERMINATE(dst); return (dst); }
VSTRING *hex_decode(VSTRING *result, const char *in, ssize_t len) { const unsigned char *cp; ssize_t count; unsigned int hex; unsigned int bin; VSTRING_RESET(result); for (cp = UCHAR_PTR(in), count = len; count > 0; cp += 2, count -= 2) { if (count < 2) return (0); hex = cp[0]; if (hex >= '0' && hex <= '9') bin = (hex - '0') << 4; else if (hex >= 'A' && hex <= 'F') bin = (hex - 'A' + 10) << 4; else if (hex >= 'a' && hex <= 'f') bin = (hex - 'a' + 10) << 4; else return (0); hex = cp[1]; if (hex >= '0' && hex <= '9') bin |= (hex - '0'); else if (hex >= 'A' && hex <= 'F') bin |= (hex - 'A' + 10); else if (hex >= 'a' && hex <= 'f') bin |= (hex - 'a' + 10); else return (0); VSTRING_ADDCH(result, bin); } VSTRING_TERMINATE(result); return (result); }
static const char *dict_union_lookup(DICT *dict, const char *query) { static const char myname[] = "dict_union_lookup"; DICT_UNION *dict_union = (DICT_UNION *) dict; DICT *map; char **cpp; char *dict_type_name; const char *result = 0; /* * After Roel van Meer, postfix-users mailing list, Sept 2014. */ VSTRING_RESET(dict_union->re_buf); for (cpp = dict_union->map_union->argv; (dict_type_name = *cpp) != 0; cpp++) { if ((map = dict_handle(dict_type_name)) == 0) msg_panic("%s: dictionary \"%s\" not found", myname, dict_type_name); if ((result = dict_get(map, query)) == 0) continue; if (VSTRING_LEN(dict_union->re_buf) > 0) VSTRING_ADDCH(dict_union->re_buf, ','); vstring_strcat(dict_union->re_buf, result); } DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, VSTRING_LEN(dict_union->re_buf) > 0 ? STR(dict_union->re_buf) : 0); }
VSTRING *hex_unquote(VSTRING *raw, const char *hex) { const char *cp; int ch; VSTRING_RESET(raw); for (cp = hex; (ch = *cp) != 0; cp++) { if (ch == '%') { if (ISDIGIT(cp[1])) ch = (cp[1] - '0') << 4; else if (cp[1] >= 'a' && cp[1] <= 'f') ch = (cp[1] - 'a' + 10) << 4; else if (cp[1] >= 'A' && cp[1] <= 'F') ch = (cp[1] - 'A' + 10) << 4; else return (0); if (ISDIGIT(cp[2])) ch |= (cp[2] - '0'); else if (cp[2] >= 'a' && cp[2] <= 'f') ch |= (cp[2] - 'a' + 10); else if (cp[2] >= 'A' && cp[2] <= 'F') ch |= (cp[2] - 'A' + 10); else return (0); cp += 2; } VSTRING_ADDCH(raw, ch); } VSTRING_TERMINATE(raw); return (raw); }
const char *fullname(void) { static VSTRING *result; char *cp; int ch; uid_t uid; struct passwd *pwd; if (result == 0) result = vstring_alloc(10); /* * Try the environment. */ if ((cp = safe_getenv("NAME")) != 0) return (vstring_str(vstring_strcpy(result, cp))); /* * Try the password file database. */ uid = getuid(); if ((pwd = getpwuid(uid)) == 0) return (0); /* * Replace all `&' characters by the login name of this user, first * letter capitalized. Although the full name comes from the protected * password file, the actual data is specified by the user so we should * not trust its sanity. */ VSTRING_RESET(result); for (cp = pwd->pw_gecos; (ch = *(unsigned char *) cp) != 0; cp++) { if (ch == ',' || ch == ';' || ch == '%') break; if (ch == '&') { if (pwd->pw_name[0]) { VSTRING_ADDCH(result, TOUPPER(pwd->pw_name[0])); vstring_strcat(result, pwd->pw_name + 1); } } else { VSTRING_ADDCH(result, ch); } } VSTRING_TERMINATE(result); return (vstring_str(result)); }
static char *pop3_next_token(char *cp, POP3D_TOKEN *arg) { int c; VSTRING_RESET(arg->vstrval); arg->tokval = POP3D_TOK_OTHER; #define STR(x) vstring_str(x) #define LEN(x) VSTRING_LEN(x) #define STREQ(x,y,l) (strncasecmp((x), (y), (l)) == 0) for (;;) { if ((c = *cp) == 0) /* end of input */ break; cp++; if (ISSPACE(c)) { /* whitespace, skip */ while (*cp && ISSPACE(*cp)) cp++; if (LEN(arg->vstrval) > 0) /* end of token */ break; } else if (c == '<') { /* <stuff> */ cp = pop3_quoted(cp, arg, c, '>'); } else if (c == '"') { /* "stuff" */ cp = pop3_quoted(cp, arg, c, c); } else if (c == ':') { /* this is gross, but... */ VSTRING_ADDCH(arg->vstrval, c); if (STREQ(STR(arg->vstrval), "to:", LEN(arg->vstrval)) || STREQ(STR(arg->vstrval), "from:", LEN(arg->vstrval))) break; } else { /* other */ if (c == '\\') { VSTRING_ADDCH(arg->vstrval, c); if ((c = *cp) == 0) break; cp++; } VSTRING_ADDCH(arg->vstrval, c); } } if (LEN(arg->vstrval) <= 0) /* no token found */ return (0); VSTRING_TERMINATE(arg->vstrval); arg->strval = vstring_str(arg->vstrval); return (cp); }
VSTRING *vstring_strncat(VSTRING *vp, const char *src, ssize_t len) { while (len-- > 0 && *src) { VSTRING_ADDCH(vp, *src); src++; } VSTRING_TERMINATE(vp); return (vp); }
VSTRING *vstring_strcat(VSTRING *vp, const char *src) { while (*src) { VSTRING_ADDCH(vp, *src); src++; } VSTRING_TERMINATE(vp); return (vp); }
static char *pop3_quoted(char *cp, POP3D_TOKEN *arg, int start, int last) { static VSTRING *stack; int wanted; int c; /* * Parser stack. `ch' is always the most-recently entered character. */ #define ENTER_CHAR(buf, ch) VSTRING_ADDCH(buf, ch); #define LEAVE_CHAR(buf, ch) { \ vstring_truncate(buf, VSTRING_LEN(buf) - 1); \ ch = vstring_end(buf)[-1]; \ } if (stack == 0) stack = vstring_alloc(1); VSTRING_RESET(stack); ENTER_CHAR(stack, wanted = last); VSTRING_ADDCH(arg->vstrval, start); for (;;) { if ((c = *cp) == 0) break; cp++; VSTRING_ADDCH(arg->vstrval, c); if (c == '\\') { /* parse escape sequence */ if ((c = *cp) == 0) break; cp++; VSTRING_ADDCH(arg->vstrval, c); } else if (c == wanted) { /* closing quote etc. */ if (VSTRING_LEN(stack) == 1) return (cp); LEAVE_CHAR(stack, wanted); } else if (c == '"') { ENTER_CHAR(stack, wanted = '"'); /* highest precedence */ } else if (c == '<' && wanted == '>') { ENTER_CHAR(stack, wanted = '>'); /* lowest precedence */ } } arg->tokval = POP3D_TOK_ERROR; /* missing end */ return (cp); }
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)); }
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 void strip_address(VSTRING *vp, int start, TOK822 *addr) { VSTRING *tmp; /* * Emit plain <address>. Discard any comments or phrases. */ msg_warn("stripping too many comments from address: %.100s...", printable(vstring_str(vp) + start, '?')); vstring_truncate(vp, start); VSTRING_ADDCH(vp, '<'); if (addr) { tmp = vstring_alloc(100); tok822_internalize(tmp, addr, TOK822_STR_TERM); quote_822_local_flags(vp, vstring_str(tmp), QUOTE_FLAG_8BITCLEAN | QUOTE_FLAG_APPEND); vstring_free(tmp); } VSTRING_ADDCH(vp, '>'); }
static VSTRING *make_822_quoted_string(VSTRING *dst, const char *local_part, const char *end, int flags) { const char *cp; int ch; /* * Put quotes around the result, and prepend a backslash to characters * that need quoting when they occur in a quoted-string. */ VSTRING_ADDCH(dst, '"'); for (cp = local_part; cp < end && (ch = *(unsigned char *) cp) != 0; cp++) { if ((ch > 127 && !(flags & QUOTE_FLAG_8BITCLEAN)) || ch == '"' || ch == '\\' || ch == '\r') VSTRING_ADDCH(dst, '\\'); VSTRING_ADDCH(dst, ch); } VSTRING_ADDCH(dst, '"'); return (dst); }
static void smtp_key_append_str(VSTRING *buffer, const char *str, const char *delim_na) { if (str == 0 || str[0] == 0) { smtp_key_append_na(buffer, delim_na); } else if (str[strcspn(str, delim_na)] != 0) { base64_encode_opt(buffer, str, strlen(str), BASE64_FLAG_APPEND); VSTRING_ADDCH(buffer, delim_na[0]); } else { vstring_sprintf_append(buffer, "%s%c", str, delim_na[0]); } }
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_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)); }