static int generate_dh_parameters(int bitsize, buffer_t *output, const char **error_r) { DH *dh; unsigned char *p; int len, len2; dh = DH_generate_parameters(bitsize, DH_GENERATOR, NULL, NULL); if (dh == NULL) { *error_r = t_strdup_printf( "DH_generate_parameters(bits=%d, gen=%d) failed: %s", bitsize, DH_GENERATOR, openssl_iostream_error()); return -1; } len = i2d_DHparams(dh, NULL); if (len < 0) { *error_r = t_strdup_printf("i2d_DHparams() failed: %s", openssl_iostream_error()); DH_free(dh); return -1; } buffer_append(output, &bitsize, sizeof(bitsize)); buffer_append(output, &len, sizeof(len)); p = buffer_append_space_unsafe(output, len); len2 = i2d_DHparams(dh, &p); i_assert(len == len2); DH_free(dh); return 0; }
static int mail_transaction_log_file_read_more(struct mail_transaction_log_file *file) { void *data; size_t size; uint32_t read_offset; ssize_t ret; read_offset = file->buffer_offset + file->buffer->used; do { data = buffer_append_space_unsafe(file->buffer, LOG_PREFETCH); ret = pread(file->fd, data, LOG_PREFETCH, read_offset); if (ret > 0) read_offset += ret; size = read_offset - file->buffer_offset; buffer_set_used_size(file->buffer, size); } while (ret > 0 || (ret < 0 && errno == EINTR)); file->last_size = read_offset; if (ret < 0) { if (errno == ESTALE) { /* log file was deleted in NFS server, fail silently */ return 0; } log_file_set_syscall_error(file, "pread()"); return -1; } return 1; }
static void test_buffer_set_used_size(void) { buffer_t *buf; test_begin("buffer_set_used_size"); buf = t_buffer_create(8); memset(buffer_append_space_unsafe(buf, 7), 'a', 7); buffer_set_used_size(buf, 4); test_assert(memcmp(buffer_get_space_unsafe(buf, 0, 7), "aaaa\0\0\0", 7) == 0); memset(buffer_get_space_unsafe(buf, 4, 7), 'b', 7); buffer_set_used_size(buf, 10); test_assert(memcmp(buffer_append_space_unsafe(buf, 1), "\0", 1) == 0); buffer_set_used_size(buf, 11); test_assert(memcmp(buffer_get_space_unsafe(buf, 0, 11), "aaaabbbbbb\0", 11) == 0); test_end(); }
void binary_to_hex_append(string_t *dest, const unsigned char *data, size_t size) { unsigned char *buf; buf = buffer_append_space_unsafe(dest, size * 2); binary_to_hex_case(buf, data, size, FALSE); }
static int dump_record(int fd, buffer_t *buf) { struct fts_expunge_log_record rec; off_t offset; void *data; const uint32_t *expunges, *uids; ssize_t ret; size_t data_size; unsigned int i, uids_count; offset = lseek(fd, 0, SEEK_CUR); ret = read(fd, &rec, sizeof(rec)); if (ret == 0) return 0; if (ret != sizeof(rec)) i_fatal("rec read() %d != %d", (int)ret, (int)sizeof(rec)); if (rec.record_size < sizeof(rec) + sizeof(uint32_t) || rec.record_size > INT_MAX) { i_fatal("Invalid record_size=%u at offset %"PRIuUOFF_T, rec.record_size, offset); } data_size = rec.record_size - sizeof(rec); buffer_set_used_size(buf, 0); data = buffer_append_space_unsafe(buf, data_size); ret = read(fd, data, data_size); if (ret != (ssize_t)data_size) i_fatal("rec read() %d != %d", (int)ret, (int)data_size); printf("#%"PRIuUOFF_T":\n", offset); printf(" checksum = %8x\n", rec.checksum); printf(" size .... = %u\n", rec.record_size); printf(" mailbox . = %s\n", guid_128_to_string(rec.guid)); expunges = CONST_PTR_OFFSET(data, data_size - sizeof(uint32_t)); printf(" expunges = %u\n", *expunges); printf(" uids .... = "); uids = data; uids_count = (rec.record_size - sizeof(rec) - sizeof(uint32_t)) / sizeof(uint32_t); for (i = 0; i < uids_count; i += 2) { if (i != 0) printf(","); if (uids[i] == uids[i+1]) printf("%u", uids[i]); else printf("%u-%u", uids[i], uids[i+1]); } printf("\n"); return 1; }
static void log_write_ext_hdr_init_data(struct mail_index *index, buffer_t *buf) { const struct mail_index_registered_ext *rext; struct mail_transaction_header *hdr; struct mail_transaction_ext_intro *intro; struct mail_transaction_ext_hdr_update *ext_hdr; unsigned int hdr_offset; rext = array_idx(&index->extensions, index->ext_hdr_init_id); /* introduce the extension */ hdr_offset = buf->used; hdr = buffer_append_space_unsafe(buf, sizeof(*hdr)); hdr->type = MAIL_TRANSACTION_EXT_INTRO; intro = buffer_append_space_unsafe(buf, sizeof(*intro)); intro->ext_id = (uint32_t)-1; intro->hdr_size = rext->hdr_size; intro->record_size = rext->record_size; intro->record_align = rext->record_align; intro->name_size = strlen(rext->name); buffer_append(buf, rext->name, intro->name_size); if (buf->used % 4 != 0) buffer_append_zero(buf, 4 - buf->used % 4); hdr = buffer_get_space_unsafe(buf, hdr_offset, sizeof(*hdr)); hdr->size = mail_index_uint32_to_offset(buf->used - hdr_offset); /* add the extension header data */ hdr_offset = buf->used; hdr = buffer_append_space_unsafe(buf, sizeof(*hdr)); hdr->type = MAIL_TRANSACTION_EXT_HDR_UPDATE; ext_hdr = buffer_append_space_unsafe(buf, sizeof(*ext_hdr)); ext_hdr->size = rext->hdr_size; buffer_append(buf, index->ext_hdr_init_data, rext->hdr_size); hdr = buffer_get_space_unsafe(buf, hdr_offset, sizeof(*hdr)); hdr->size = mail_index_uint32_to_offset(buf->used - hdr_offset); }
static ssize_t mail_transaction_log_file_read_header(struct mail_transaction_log_file *file) { void *dest; size_t pos, dest_size; ssize_t ret; i_assert(file->buffer == NULL && file->mmap_base == NULL); memset(&file->hdr, 0, sizeof(file->hdr)); if (file->last_size < mmap_get_page_size() && file->last_size > 0) { /* just read the entire transaction log to memory. note that if some of the data hasn't been fully committed yet (hdr.size=0), the buffer must be truncated later */ file->buffer = buffer_create_dynamic(default_pool, 4096); file->buffer_offset = 0; dest_size = file->last_size; dest = buffer_append_space_unsafe(file->buffer, dest_size); } else { /* read only the header */ dest = &file->hdr; dest_size = sizeof(file->hdr); } /* it's not necessarily an error to read less than wanted header size, since older versions of the log format used smaller headers. */ pos = 0; do { ret = pread(file->fd, PTR_OFFSET(dest, pos), dest_size - pos, pos); if (ret > 0) pos += ret; } while (ret > 0 && pos < dest_size); if (file->buffer != NULL) { buffer_set_used_size(file->buffer, pos); memcpy(&file->hdr, file->buffer->data, I_MIN(pos, sizeof(file->hdr))); } return ret < 0 ? -1 : (ssize_t)pos; }
static int ssl_refresh_parameters(struct master_service *service) { #define BUF_APPEND_SIZE 1024 const char *path; buffer_t *buf; void *data; ssize_t ret; int fd; if (ioloop_time == 0 || service->ssl_params_last_refresh > ioloop_time - SSL_PARAMS_CHECK_INTERVAL) return 0; service->ssl_params_last_refresh = ioloop_time; path = t_strdup_printf("%s/"SSL_PARAMETERS_PATH, service->set->base_dir); fd = net_connect_unix(path); if (fd == -1) { i_error("connect(%s) failed: %m", path); return -1; } net_set_nonblock(fd, FALSE); buf = buffer_create_dynamic(default_pool, BUF_APPEND_SIZE*2); for (;;) { data = buffer_append_space_unsafe(buf, BUF_APPEND_SIZE); ret = read(fd, data, BUF_APPEND_SIZE); buffer_set_used_size(buf, buf->used - BUF_APPEND_SIZE + (ret < 0 ? 0 : ret)); if (ret <= 0) break; } if (ret < 0) i_error("read(%s) failed: %m", path); else if (ssl_iostream_context_import_params(service->ssl_ctx, buf) < 0) { i_error("Corrupted SSL parameters file in state_dir: " "ssl-parameters.dat - disabling SSL %u", (int)buf->used); ret = -1; } i_close_fd(&fd); buffer_free(&buf); return ret < 0 ? -1 : 0; }
static int i_stream_seekable_write_failed(struct seekable_istream *sstream) { struct istream_private *stream = &sstream->istream; void *data; i_assert(sstream->membuf == NULL); sstream->membuf = buffer_create_dynamic(default_pool, sstream->write_peak); data = buffer_append_space_unsafe(sstream->membuf, sstream->write_peak); if (pread_full(sstream->fd, data, sstream->write_peak, 0) < 0) { i_error("istream-seekable: read(%s) failed: %m", sstream->temp_path); buffer_free(&sstream->membuf); return -1; } i_stream_destroy(&sstream->fd_input); i_close_fd(&sstream->fd); stream->max_buffer_size = (size_t)-1; i_free_and_null(sstream->temp_path); return 0; }
static void keywords_ext_register(struct mail_index_sync_map_ctx *ctx, uint32_t ext_map_idx, uint32_t reset_id, uint32_t hdr_size, uint32_t keywords_count) { buffer_t ext_intro_buf; struct mail_transaction_ext_intro *u; unsigned char ext_intro_data[sizeof(*u) + sizeof(MAIL_INDEX_EXT_KEYWORDS)-1]; i_assert(keywords_count > 0); buffer_create_from_data(&ext_intro_buf, ext_intro_data, sizeof(ext_intro_data)); u = buffer_append_space_unsafe(&ext_intro_buf, sizeof(*u)); u->ext_id = ext_map_idx; u->reset_id = reset_id; u->hdr_size = hdr_size; u->record_size = (keywords_count + CHAR_BIT - 1) / CHAR_BIT; if ((u->record_size % 4) != 0) { /* since we aren't properly aligned anyway, reserve one extra byte for future */ u->record_size++; } u->record_align = 1; if (ext_map_idx == (uint32_t)-1) { u->name_size = strlen(MAIL_INDEX_EXT_KEYWORDS); buffer_append(&ext_intro_buf, MAIL_INDEX_EXT_KEYWORDS, u->name_size); } ctx->internal_update = TRUE; if (mail_index_sync_ext_intro(ctx, u) < 0) i_panic("Keyword extension growing failed"); ctx->internal_update = FALSE; }
static void mbox_save_x_delivery_id(struct mbox_save_context *ctx) { unsigned char md5_result[MD5_RESULTLEN]; buffer_t *buf; string_t *str; void *randbuf; buf = buffer_create_dynamic(pool_datastack_create(), 256); buffer_append(buf, &ioloop_time, sizeof(ioloop_time)); buffer_append(buf, &ioloop_timeval.tv_usec, sizeof(ioloop_timeval.tv_usec)); randbuf = buffer_append_space_unsafe(buf, MBOX_DELIVERY_ID_RAND_BYTES); random_fill_weak(randbuf, MBOX_DELIVERY_ID_RAND_BYTES); md5_get_digest(buf->data, buf->used, md5_result); str = t_str_new(128); str_append(str, "X-Delivery-ID: "); base64_encode(md5_result, sizeof(md5_result), str); str_append_c(str, '\n'); ctx->x_delivery_id_header = i_strdup(str_c(str)); }
static void keywords_header_add(struct mail_index_sync_map_ctx *ctx, const char *keyword_name, unsigned int *keyword_idx_r) { struct mail_index_map *map; const struct mail_index_ext *ext = NULL; struct mail_index_keyword_header *kw_hdr; struct mail_index_keyword_header_rec kw_rec; uint32_t ext_map_idx; buffer_t *buf = NULL; size_t keyword_len, rec_offset, name_offset, name_offset_root; unsigned int keywords_count; /* if we crash in the middle of writing the header, the keywords are more or less corrupted. avoid that by making sure the header is updated atomically. */ map = mail_index_sync_get_atomic_map(ctx); if (!mail_index_map_lookup_ext(map, MAIL_INDEX_EXT_KEYWORDS, &ext_map_idx)) ext_map_idx = (uint32_t)-1; else { /* update existing header */ ext = array_idx(&map->extensions, ext_map_idx); buf = keywords_get_header_buf(map, ext, 1, &keywords_count, &rec_offset, &name_offset_root, &name_offset); } if (buf == NULL) { /* create new / replace broken header */ const unsigned int initial_keywords_count = 1; buf = buffer_create_dynamic(pool_datastack_create(), 512); kw_hdr = buffer_append_space_unsafe(buf, sizeof(*kw_hdr)); kw_hdr->keywords_count = initial_keywords_count; keywords_count = kw_hdr->keywords_count; rec_offset = buf->used; name_offset_root = rec_offset + initial_keywords_count * sizeof(kw_rec); name_offset = 0; } /* add the keyword */ memset(&kw_rec, 0, sizeof(kw_rec)); kw_rec.name_offset = name_offset; keyword_len = strlen(keyword_name) + 1; buffer_write(buf, rec_offset, &kw_rec, sizeof(kw_rec)); buffer_write(buf, name_offset_root, keyword_name, keyword_len); rec_offset += sizeof(kw_rec); kw_rec.name_offset += keyword_len; name_offset_root += keyword_len; if ((buf->used % 4) != 0) buffer_append_zero(buf, 4 - (buf->used % 4)); if (ext == NULL || buf->used > ext->hdr_size || (uint32_t)ext->record_size * CHAR_BIT < keywords_count) { /* if we need to grow the buffer, add some padding */ buffer_append_zero(buf, 128); keywords_ext_register(ctx, ext_map_idx, ext == NULL ? 0 : ext->reset_id, buf->used, keywords_count); /* map may have changed */ map = ctx->view->map; if (!mail_index_map_lookup_ext(map, MAIL_INDEX_EXT_KEYWORDS, &ext_map_idx)) i_unreached(); ext = array_idx(&map->extensions, ext_map_idx); i_assert(ext->hdr_size == buf->used); } buffer_copy(map->hdr_copy_buf, ext->hdr_offset, buf, 0, buf->used); map->hdr_base = map->hdr_copy_buf->data; i_assert(map->hdr_copy_buf->used == map->hdr.header_size); if (mail_index_map_parse_keywords(map) < 0) i_panic("Keyword update corrupted keywords header"); *keyword_idx_r = keywords_count - 1; i_assert(*keyword_idx_r / CHAR_BIT < ext->record_size); }
static bool client_exec_script(struct master_service_connection *conn) { ARRAY_TYPE(const_string) envs; const char *const *args; string_t *input; void *buf; size_t prev_size, scanpos; bool header_complete = FALSE, noreply = FALSE; ssize_t ret; int status; pid_t pid; net_set_nonblock(conn->fd, FALSE); input = t_buffer_create(IO_BLOCK_SIZE); /* Input contains: VERSION .. <lf> [alarm=<secs> <lf>] "noreply" | "-" (or anything really) <lf> arg 1 <lf> arg 2 <lf> ... <lf> DATA This is quite a horrible protocol. If alarm is specified, it MUST be before "noreply". If "noreply" isn't given, something other string (typically "-") must be given which is eaten away. */ alarm(SCRIPT_READ_TIMEOUT_SECS); scanpos = 1; while (!header_complete) { const unsigned char *pos, *end; prev_size = input->used; buf = buffer_append_space_unsafe(input, IO_BLOCK_SIZE); /* peek in socket input buffer */ ret = recv(conn->fd, buf, IO_BLOCK_SIZE, MSG_PEEK); if (ret <= 0) { buffer_set_used_size(input, prev_size); if (strchr(str_c(input), '\n') != NULL) script_verify_version(t_strcut(str_c(input), '\n')); if (ret < 0) i_fatal("recv(MSG_PEEK) failed: %m"); i_fatal("recv(MSG_PEEK) failed: disconnected"); } /* scan for final \n\n */ pos = CONST_PTR_OFFSET(input->data, scanpos); end = CONST_PTR_OFFSET(input->data, prev_size + ret); for (; pos < end; pos++) { if (pos[-1] == '\n' && pos[0] == '\n') { header_complete = TRUE; pos++; break; } } scanpos = pos - (const unsigned char *)input->data; /* read data for real (up to and including \n\n) */ ret = recv(conn->fd, buf, scanpos-prev_size, 0); if (prev_size+ret != scanpos) { if (ret < 0) i_fatal("recv() failed: %m"); if (ret == 0) i_fatal("recv() failed: disconnected"); i_fatal("recv() failed: size of definitive recv() differs from peek"); } buffer_set_used_size(input, scanpos); } alarm(0); /* drop the last two LFs */ buffer_set_used_size(input, scanpos-2); args = t_strsplit(str_c(input), "\n"); script_verify_version(*args); args++; t_array_init(&envs, 16); if (*args != NULL) { const char *p; if (str_begins(*args, "alarm=")) { unsigned int seconds; if (str_to_uint(*args + 6, &seconds) < 0) i_fatal("invalid alarm option"); alarm(seconds); args++; } while (str_begins(*args, "env_")) { const char *envname, *env; env = t_str_tabunescape(*args+4); p = strchr(env, '='); if (p == NULL) i_fatal("invalid environment variable"); envname = t_strdup_until(*args+4, p); if (str_array_find(accepted_envs, envname)) array_append(&envs, &env, 1); args++; } if (strcmp(*args, "noreply") == 0) { noreply = TRUE; } if (**args == '\0') i_fatal("empty options"); args++; } array_append_zero(&envs); if (noreply) { /* no need to fork and check exit status */ exec_child(conn, args, array_idx(&envs, 0)); i_unreached(); } if ((pid = fork()) == (pid_t)-1) { i_error("fork() failed: %m"); return FALSE; } if (pid == 0) { /* child */ exec_child(conn, args, array_idx(&envs, 0)); i_unreached(); } /* parent */ /* check script exit status */ if (waitpid(pid, &status, 0) < 0) { i_error("waitpid() failed: %m"); return FALSE; } else if (WIFEXITED(status)) { ret = WEXITSTATUS(status); if (ret != 0) { i_error("Script terminated abnormally, exit status %d", (int)ret); return FALSE; } } else if (WIFSIGNALED(status)) { i_error("Script terminated abnormally, signal %d", WTERMSIG(status)); return FALSE; } else if (WIFSTOPPED(status)) { i_fatal("Script stopped, signal %d", WSTOPSIG(status)); return FALSE; } else { i_fatal("Script terminated abnormally, return status %d", status); return FALSE; } return TRUE; }
static int o_stream_encrypt_keydata_create_v2(struct encrypt_ostream *stream, const char *malg) { const struct hash_method *hash = hash_method_lookup(malg); const char *error; size_t tagsize; const unsigned char *ptr; size_t kl; unsigned int val; buffer_t *keydata, *res; if (hash == NULL) { io_stream_set_error(&stream->ostream.iostream, "Encryption init error: Hash algorithm '%s' not supported", malg); return -1; } /* key data length for internal use */ if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { tagsize = IOSTREAM_TAG_SIZE; } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { tagsize = IOSTREAM_TAG_SIZE; } else { /* do not include MAC */ tagsize = 0; } /* generate keydata length of random data for key/iv/mac */ kl = dcrypt_ctx_sym_get_key_length(stream->ctx_sym) + dcrypt_ctx_sym_get_iv_length(stream->ctx_sym) + tagsize; keydata = buffer_create_dynamic(pool_datastack_create(), kl); random_fill(buffer_append_space_unsafe(keydata, kl), kl); buffer_set_used_size(keydata, kl); ptr = keydata->data; res = buffer_create_dynamic(default_pool, 256); /* store number of public key(s) */ buffer_append(res, "\1", 1); /* one key for now */ /* we can do multiple keys at this point, but do it only once now */ if (o_stream_encrypt_key_for_pubkey_v2(stream, malg, ptr, kl, stream->pub, res) != 0) { buffer_free(&res); return -1; } /* create hash of the key data */ unsigned char hctx[hash->context_size]; unsigned char hres[hash->digest_size]; hash->init(hctx); hash->loop(hctx, ptr, kl); hash->result(hctx, hres); for(int i = 1; i < 2049; i++) { uint32_t i_msb = htonl(i); hash->init(hctx); hash->loop(hctx, hres, sizeof(hres)); hash->loop(hctx, &i_msb, sizeof(i_msb)); hash->result(hctx, hres); } /* store key data hash */ val = htonl(sizeof(hres)); buffer_append(res, &val, 4); buffer_append(res, hres, sizeof(hres)); /* pick up key data that goes into stream */ stream->key_data_len = res->used; stream->key_data = buffer_free_without_data(&res); /* prime contexts */ dcrypt_ctx_sym_set_key(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_key_length(stream->ctx_sym)); ptr += dcrypt_ctx_sym_get_key_length(stream->ctx_sym); dcrypt_ctx_sym_set_iv(stream->ctx_sym, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); ptr += dcrypt_ctx_sym_get_iv_length(stream->ctx_sym); if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) { dcrypt_ctx_hmac_set_key(stream->ctx_mac, ptr, tagsize); dcrypt_ctx_hmac_init(stream->ctx_mac, &error); } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) { dcrypt_ctx_sym_set_aad(stream->ctx_sym, ptr, tagsize); } /* clear out private key data */ safe_memset(buffer_get_modifiable_data(keydata, 0), 0, keydata->used); if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { io_stream_set_error(&stream->ostream.iostream, "Encryption init error: %s", error); return -1; } return 0; }
static void log_append_ext_intro(struct mail_index_export_context *ctx, uint32_t ext_id, uint32_t reset_id) { struct mail_index_transaction *t = ctx->trans; const struct mail_index_registered_ext *rext; struct mail_transaction_ext_intro *intro, *resizes; buffer_t *buf; uint32_t idx; unsigned int count; i_assert(ext_id != (uint32_t)-1); if (t->reset || !mail_index_map_get_ext_idx(t->view->index->map, ext_id, &idx)) { /* new extension */ idx = (uint32_t)-1; } rext = array_idx(&t->view->index->extensions, ext_id); if (!array_is_created(&t->ext_resizes)) { resizes = NULL; count = 0; } else { resizes = array_get_modifiable(&t->ext_resizes, &count); } buf = buffer_create_dynamic(pool_datastack_create(), 128); if (ext_id < count && resizes[ext_id].name_size != 0) { /* we're resizing the extension. use the resize struct. */ intro = &resizes[ext_id]; i_assert(intro->ext_id == idx); intro->name_size = idx != (uint32_t)-1 ? 0 : strlen(rext->name); buffer_append(buf, intro, sizeof(*intro)); } else { /* generate a new intro structure */ intro = buffer_append_space_unsafe(buf, sizeof(*intro)); intro->ext_id = idx; intro->hdr_size = rext->hdr_size; intro->record_size = rext->record_size; intro->record_align = rext->record_align; intro->flags = MAIL_TRANSACTION_EXT_INTRO_FLAG_NO_SHRINK; intro->name_size = idx != (uint32_t)-1 ? 0 : strlen(rext->name); } if (reset_id != 0) { /* we're going to reset this extension in this transaction */ intro->reset_id = reset_id; } else if (idx != (uint32_t)-1) { /* use the existing reset_id */ const struct mail_index_ext *map_ext = array_idx(&t->view->index->map->extensions, idx); intro->reset_id = map_ext->reset_id; } else { /* new extension, reset_id defaults to 0 */ } buffer_append(buf, rext->name, intro->name_size); if ((buf->used % 4) != 0) buffer_append_zero(buf, 4 - (buf->used % 4)); if (ctx->append_ctx->new_highest_modseq == 0 && strcmp(rext->name, MAIL_INDEX_MODSEQ_EXT_NAME) == 0) { /* modseq tracking started */ ctx->append_ctx->new_highest_modseq = 1; } log_append_buffer(ctx, buf, MAIL_TRANSACTION_EXT_INTRO); }