static void dsync_connected_callback(int exit_code, const char *error, void *context) { struct dsync_cmd_context *ctx = context; ctx->ctx.exit_code = exit_code; switch (exit_code) { case 0: server_connection_extract(ctx->tcp_conn, &ctx->input, &ctx->output, &ctx->ssl_iostream); break; case SERVER_EXIT_CODE_DISCONNECTED: ctx->error = p_strdup_printf(ctx->ctx.pool, "Disconnected from remote: %s", error); break; case EX_NOUSER: ctx->error = "Unknown user in remote"; break; default: ctx->error = p_strdup_printf(ctx->ctx.pool, "Failed to start dsync-server command: %u", exit_code); break; } io_loop_stop(current_ioloop); }
static void mail_crypt_mail_user_created(struct mail_user *user) { struct mail_user_vfuncs *v = user->vlast; struct mail_crypt_user *muser; const char *error = NULL; muser = p_new(user->pool, struct mail_crypt_user, 1); muser->module_ctx.super = *v; user->vlast = &muser->module_ctx.super; const char *curve = mail_user_plugin_getenv(user, "mail_crypt_curve"); buffer_t *tmp = t_str_new(64); if (curve == NULL) { if (user->mail_debug) { i_debug("mail_crypt_plugin: mail_crypt_curve setting " "missing - generating EC keys disabled"); } } else if (!dcrypt_name2oid(curve, tmp, &error)) { user->error = p_strdup_printf(user->pool, "mail_crypt_plugin: " "invalid mail_crypt_curve setting %s: %s - " "plugin disabled", curve, error); } else { muser->curve = p_strdup(user->pool, curve); } const char *version = mail_user_plugin_getenv(user, "mail_crypt_save_version"); if (version == NULL) { user->error = p_strdup_printf(user->pool, "mail_crypt_plugin: " "mail_crypt_save_version setting missing " "- plugin disabled"); } else if (version[0] == '0') { muser->save_version = 0; } else if (version[0] == '1') { muser->save_version = 1; } else if (version[0] == '2') { muser->save_version = 2; } else { user->error = p_strdup_printf(user->pool, "mail_crypt_plugin: Invalid " "mail_crypt_save_version %s: use 0, 1, or 2 ", version); } if (mail_crypt_global_keys_load(user, "mail_crypt_global", &muser->global_keys, FALSE, &error) < 0) { user->error = p_strdup_printf(user->pool, "mail_crypt_plugin: %s", error); } v->deinit = mail_crypt_mail_user_deinit; MODULE_CONTEXT_SET(user, mail_crypt_user_module, muser); }
static void mail_filter_parse_setting(struct mail_user *user, const char *name, const char **socket_path_r, const char **args_r) { const char *value, *p; value = mail_user_plugin_getenv(user, name); if (value == NULL) return; p = strchr(value, ' '); if (p == NULL) { *socket_path_r = p_strdup(user->pool, value); *args_r = ""; } else { *socket_path_r = p_strdup_until(user->pool, value, p); *args_r = p_strdup(user->pool, p + 1); } if (**socket_path_r != '/') { /* relative to base_dir */ *socket_path_r = p_strdup_printf(user->pool, "%s/%s", user->set->base_dir, *socket_path_r); } if (user->mail_debug) { i_debug("mail_filter: Filtering %s via socket %s", name, *socket_path_r); } }
static void mail_user_expand_plugins_envs(struct mail_user *user) { const char **envs, *home; string_t *str; unsigned int i, count; if (!array_is_created(&user->set->plugin_envs)) return; str = t_str_new(256); envs = array_get_modifiable(&user->set->plugin_envs, &count); i_assert((count % 2) == 0); for (i = 0; i < count; i += 2) { if (user->_home == NULL && var_has_key(envs[i+1], 'h', "home") && mail_user_get_home(user, &home) <= 0) { user->error = p_strdup_printf(user->pool, "userdb didn't return a home directory, " "but plugin setting %s used it (%%h): %s", envs[i], envs[i+1]); return; } str_truncate(str, 0); var_expand(str, envs[i+1], mail_user_var_expand_table(user)); envs[i+1] = p_strdup(user->pool, str_c(str)); } }
static void search_update_flag_changes(struct dsync_mailbox_exporter *exporter, struct mail *mail, struct dsync_mail_change *change) { const char *const *keywords; unsigned int i; char type; i_assert((change->add_flags & change->remove_flags) == 0); change->modseq = mail_get_modseq(mail); change->pvt_modseq = mail_get_pvt_modseq(mail); change->final_flags = mail_get_flags(mail); keywords = mail_get_keywords(mail); if (!array_is_created(&change->keyword_changes) && keywords[0] != NULL) { p_array_init(&change->keyword_changes, exporter->pool, str_array_length(keywords)); } for (i = 0; keywords[i] != NULL; i++) { /* add the final keyword if it's not already there as +keyword */ if (!final_keyword_check(change, keywords[i], &type)) { const char *keyword_change = p_strdup_printf(exporter->pool, "%c%s", type, keywords[i]); array_append(&change->keyword_changes, &keyword_change, 1); } } }
static void http_client_request_do_submit(struct http_client_request *req) { struct http_client *client = req->client; struct http_client_host *host; const struct http_url *proxy_url = client->set.proxy_url; const char *authority, *target; i_assert(req->state == HTTP_REQUEST_STATE_NEW); authority = http_url_create_authority(&req->origin_url); if (req->connect_tunnel) { /* connect requests require authority form for request target */ target = authority; } else { /* absolute target url */ target = t_strconcat (http_url_create_host(&req->origin_url), req->target, NULL); } /* determine what host to contact to submit this request */ if (proxy_url != NULL) { if (req->origin_url.have_ssl && !client->set.no_ssl_tunnel && !req->connect_tunnel) { req->host_url = &req->origin_url; /* tunnel to origin server */ req->ssl_tunnel = TRUE; } else { req->host_url = proxy_url; /* proxy server */ } } else { req->host_url = &req->origin_url; /* origin server */ } /* use submission date if no date is set explicitly */ if (req->date == (time_t)-1) req->date = ioloop_time; /* prepare value for Host header */ req->authority = p_strdup(req->pool, authority); /* debug label */ req->label = p_strdup_printf(req->pool, "[%s %s]", req->method, target); /* update request target */ if (req->connect_tunnel || proxy_url != NULL) req->target = p_strdup(req->pool, target); if (proxy_url == NULL) { /* if we don't have a proxy, CONNECT requests are handled by creating the requested connection directly */ req->connect_direct = req->connect_tunnel; if (req->connect_direct) req->urgent = TRUE; } host = http_client_host_get(req->client, req->host_url); req->state = HTTP_REQUEST_STATE_QUEUED; http_client_host_submit_request(host, req); }
const char * http_client_request_label(struct http_client_request *req) { if (req->label == NULL) { req->label = p_strdup_printf(req->pool, "[Req%u: %s %s%s]", req->id, req->method, http_url_create(&req->origin_url), req->target); } return req->label; }
void script_client_set_env (struct script_client *sclient, const char *name, const char *value) { const char *env; if ( !array_is_created(&sclient->envs) ) p_array_init(&sclient->envs, sclient->pool, 16); env = p_strdup_printf(sclient->pool, "%s=%s", name, value); array_append(&sclient->envs, &env, 1); }
void program_client_set_env (struct program_client *pclient, const char *name, const char *value) { const char *env; if ( !array_is_created(&pclient->envs) ) p_array_init(&pclient->envs, pclient->pool, 16); env = p_strdup_printf(pclient->pool, "%s=%s", name, value); array_append(&pclient->envs, &env, 1); }
static int dsync_mail_error(struct dsync_mailbox_exporter *exporter, struct mail *mail, const char *field) { const char *errstr; enum mail_error error; errstr = mailbox_get_last_error(exporter->box, &error); if (error == MAIL_ERROR_EXPUNGED) return 0; exporter->error = p_strdup_printf(exporter->pool, "Can't lookup %s for UID=%u: %s", field, mail->uid, errstr); return -1; }
static void mailbox_list_index_generate_name(struct mailbox_list_index *ilist, struct mailbox_list_index_node *node) { guid_128_t guid; char *name; guid_128_generate(guid); name = p_strdup_printf(ilist->mailbox_pool, "unknown-%s", guid_128_to_string(guid)); node->name = name; hash_table_insert(ilist->mailbox_names, POINTER_CAST(node->name_id), name); if (ilist->highest_name_id < node->name_id) ilist->highest_name_id = node->name_id; }
static void userdb_nss_load_module(struct nss_userdb_module *module, pool_t pool) { const char *name = module->nss_module.name; char *path; path = p_strdup_printf(pool, "/usr/lib/libnss_%s.so", name); module->nss_module.handle = dlopen(path, RTLD_GLOBAL | RTLD_NOW); if (module->nss_module.handle == NULL) i_fatal("dlopen(%s) failed: %s", path, dlerror()); module->nss_module.path = path; module->getpwnam_r = module_get_symbol(&module->nss_module, t_strdup_printf("_nss_%s_getpwnam_r", name)); if (module->getpwnam_r == NULL) i_fatal("userdb nss: Module %s missing getpwnam_r()", path); }
int mail_user_init(struct mail_user *user, const char **error_r) { const struct mail_storage_settings *mail_set; const char *home, *key, *value; bool need_home_dir; need_home_dir = user->_home == NULL && settings_vars_have_key(user->set_info, user->set, 'h', "home", &key, &value); if (need_home_dir && mail_user_get_home(user, &home) <= 0) { user->error = p_strdup_printf(user->pool, "userdb didn't return a home directory, " "but %s used it (%%h): %s", key, value); } /* expand settings after we can expand %h */ settings_var_expand_with_funcs(user->set_info, user->set, user->pool, mail_user_var_expand_table(user), mail_user_var_expand_func_table, user); user->settings_expanded = TRUE; mail_user_expand_plugins_envs(user); /* autocreated users for shared mailboxes need to be fully initialized if they don't exist, since they're going to be used anyway */ if (user->error == NULL || user->nonexistent) { mail_set = mail_user_set_get_storage_set(user); user->mail_debug = mail_set->mail_debug; user->initialized = TRUE; hook_mail_user_created(user); } if (user->error != NULL) { *error_r = t_strdup(user->error); return -1; } return 0; }
static int imapc_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; struct index_mail *imail = (struct index_mail *)_mail; uint64_t num; switch (field) { case MAIL_FETCH_GUID: if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GUID_FORCED) && mbox->guid_fetch_field_name == NULL) { /* GUIDs not supported by server */ break; } *value_r = ""; return imapc_mail_get_guid(_mail, value_r); case MAIL_FETCH_UIDL_BACKEND: if (!IMAPC_BOX_HAS_FEATURE(mbox, IMAPC_FEATURE_GMAIL_MIGRATION)) break; if (imapc_mail_get_guid(_mail, value_r) < 0) return -1; if (str_to_uint64(*value_r, &num) < 0) { mail_storage_set_critical(_mail->box->storage, "X-GM-MSGID not 64bit integer as expected for POP3 UIDL generation: %s", *value_r); return -1; } *value_r = p_strdup_printf(imail->mail.data_pool, "GmailId%llx", (unsigned long long)num); return 0; default: break; } return index_mail_get_special(_mail, field, value_r); }
static int maildir_mail_get_special(struct mail *_mail, enum mail_fetch_field field, const char **value_r) { struct index_mail *mail = (struct index_mail *)_mail; struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box; const char *path, *fname = NULL, *end, *guid, *uidl, *order; struct stat st; switch (field) { case MAIL_FETCH_GUID: /* use GUID from uidlist if it exists */ i_assert(!_mail->saving); if (mail->data.guid != NULL) { *value_r = mail->data.guid; return 0; } /* first make sure that we have a refreshed uidlist */ if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0) return -1; guid = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid, MAILDIR_UIDLIST_REC_EXT_GUID); if (guid != NULL) { if (*guid != '\0') { *value_r = mail->data.guid = p_strdup(mail->mail.data_pool, guid); return 0; } mail_storage_set_critical(_mail->box->storage, "Maildir %s: Corrupted dovecot-uidlist: " "UID %u had empty GUID, clearing it", mailbox_get_path(_mail->box), _mail->uid); maildir_uidlist_unset_ext(mbox->uidlist, _mail->uid, MAILDIR_UIDLIST_REC_EXT_GUID); } /* default to base filename: */ if (maildir_mail_get_special(_mail, MAIL_FETCH_STORAGE_ID, value_r) < 0) return -1; mail->data.guid = mail->data.filename; return 0; case MAIL_FETCH_STORAGE_ID: if (mail->data.filename != NULL) { *value_r = mail->data.filename; return 0; } if (fname != NULL) { /* we came here from MAIL_FETCH_GUID, avoid a second lookup */ } else if (!_mail->saving) { if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0) return -1; } else { path = maildir_save_file_get_path(_mail->transaction, _mail->seq); fname = strrchr(path, '/'); fname = fname != NULL ? fname + 1 : path; } end = strchr(fname, MAILDIR_INFO_SEP); mail->data.filename = end == NULL ? p_strdup(mail->mail.data_pool, fname) : p_strdup_until(mail->mail.data_pool, fname, end); *value_r = mail->data.filename; return 0; case MAIL_FETCH_UIDL_BACKEND: uidl = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid, MAILDIR_UIDLIST_REC_EXT_POP3_UIDL); if (uidl == NULL) { /* use the default */ *value_r = ""; } else if (*uidl == '\0') { /* special optimization case: use the base file name */ return maildir_mail_get_special(_mail, MAIL_FETCH_STORAGE_ID, value_r); } else { *value_r = p_strdup(mail->mail.data_pool, uidl); } return 0; case MAIL_FETCH_POP3_ORDER: order = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid, MAILDIR_UIDLIST_REC_EXT_POP3_ORDER); if (order == NULL) { *value_r = ""; } else { *value_r = p_strdup(mail->mail.data_pool, order); } return 0; case MAIL_FETCH_REFCOUNT: if (maildir_mail_stat(_mail, &st) < 0) return -1; *value_r = p_strdup_printf(mail->mail.data_pool, "%lu", (unsigned long)st.st_nlink); return 0; default: return index_mail_get_special(_mail, field, value_r); } }
bool imap_fetch_binary_init(struct imap_fetch_init_context *ctx) { struct imap_fetch_body_data *body; const struct imap_arg *list_args; unsigned int list_count; const char *str, *p, *error; i_assert(strncmp(ctx->name, "BINARY", 6) == 0); p = ctx->name + 6; body = p_new(ctx->pool, struct imap_fetch_body_data, 1); body->binary = TRUE; if (strncmp(p, ".SIZE", 5) == 0) { /* fetch decoded size of the section */ p += 5; body->binary_size = TRUE; } else if (strncmp(p, ".PEEK", 5) == 0) { p += 5; } else { ctx->fetch_ctx->flags_update_seen = TRUE; } if (*p != '[') { ctx->error = "Invalid BINARY[..] parameter: Missing '['"; return FALSE; } if (imap_arg_get_list_full(&ctx->args[0], &list_args, &list_count)) { /* BINARY[HEADER.FIELDS.. (headers list)] */ if (!imap_arg_get_atom(&ctx->args[1], &str) || str[0] != ']') { ctx->error = "Invalid BINARY[..] parameter: Missing ']'"; return FALSE; } if (body_header_fields_parse(ctx, body, p+1, list_args, list_count) < 0) return FALSE; p = str+1; ctx->args += 2; } else { /* no headers list */ body->section = p+1; p = strchr(body->section, ']'); if (p == NULL) { ctx->error = "Invalid BINARY[..] parameter: Missing ']'"; return FALSE; } body->section = p_strdup_until(ctx->pool, body->section, p); p++; } if (imap_msgpart_parse(body->section, &body->msgpart) < 0) { ctx->error = "Invalid BINARY[..] section"; return FALSE; } imap_msgpart_set_decode_to_binary(body->msgpart); ctx->fetch_ctx->fetch_data |= imap_msgpart_get_fetch_data(body->msgpart); if (!body->binary_size) { if (body_parse_partial(body, p, &error) < 0) { ctx->error = p_strdup_printf(ctx->pool, "Invalid BINARY[..] parameter: %s", error); return FALSE; } } /* update the section name for the imap_fetch_add_handler() */ ctx->name = p_strdup(ctx->pool, get_body_name(body)); if (body->binary_size) { imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT, "0", fetch_binary_size, body); } else { imap_fetch_add_handler(ctx, IMAP_FETCH_HANDLER_FLAG_WANT_DEINIT, "NIL", fetch_body_msgpart, body); } return TRUE; }
int cmd_rcpt(struct client *client, const char *args) { struct mail_recipient *rcpt; struct mail_storage_service_input input; const char *params, *address, *username, *detail, *prefix; const char *const *argv; const char *error = NULL; char delim = '\0'; int ret = 0; if (client->state.mail_from == NULL) { client_send_line(client, "503 5.5.1 MAIL needed first"); return 0; } if (strncasecmp(args, "TO:", 3) != 0 || parse_address(args + 3, &address, ¶ms) < 0) { client_send_line(client, "501 5.5.4 Invalid parameters"); return 0; } rcpt = p_new(client->state_pool, struct mail_recipient, 1); rcpt->client = client; address = lmtp_unescape_address(address); argv = t_strsplit(params, " "); for (; *argv != NULL; argv++) { if (strncasecmp(*argv, "ORCPT=", 6) == 0) { rcpt->params.dsn_orcpt = parse_xtext(client, *argv + 6); } else { client_send_line(client, "501 5.5.4 Unsupported options"); return 0; } } rcpt_address_parse(client, address, &username, &delim, &detail); client_state_set(client, "RCPT TO", address); if (client->lmtp_set->lmtp_proxy) { if (client_proxy_rcpt(client, address, username, detail, delim, &rcpt->params)) return 0; } /* Use a unique session_id for each mail delivery. This is especially important for stats process to not see duplicate sessions. */ if (array_count(&client->state.rcpt_to) == 0) rcpt->session_id = client->state.session_id; else { rcpt->session_id = p_strdup_printf(client->state_pool, "%s:%u", client->state.session_id, array_count(&client->state.rcpt_to)+1); } memset(&input, 0, sizeof(input)); input.module = input.service = "lmtp"; input.username = username; input.local_ip = client->local_ip; input.remote_ip = client->remote_ip; input.local_port = client->local_port; input.remote_port = client->remote_port; input.session_id = rcpt->session_id; ret = mail_storage_service_lookup(storage_service, &input, &rcpt->service_user, &error); if (ret < 0) { prefix = t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL_PREFIX, username); client_send_line(client, "%s%s", prefix, error); return 0; } if (ret == 0) { client_send_line(client, "550 5.1.1 <%s> User doesn't exist: %s", address, username); return 0; } if (client->proxy != NULL) { /* NOTE: if this restriction is ever removed, we'll also need to send different message bodies to local and proxy (with and without Return-Path: header) */ client_send_line(client, "451 4.3.0 <%s> " "Can't handle mixed proxy/non-proxy destinations", address); mail_storage_service_user_free(&rcpt->service_user); return 0; } lmtp_address_translate(client, &address); rcpt->address = p_strdup(client->state_pool, address); rcpt->detail = p_strdup(client->state_pool, detail); if ((ret = lmtp_rcpt_to_is_over_quota(client, rcpt)) != 0) { if (ret < 0) { client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, rcpt->address); } mail_storage_service_user_free(&rcpt->service_user); return 0; } array_append(&client->state.rcpt_to, &rcpt, 1); client_send_line(client, "250 2.1.5 OK"); if (client->lmtp_set->lmtp_user_concurrency_limit > 0) { const char *query = t_strconcat("LOOKUP\t", master_service_get_name(master_service), "/", str_tabescape(username), NULL); client->state.anvil_queries++; rcpt->anvil_query = anvil_client_query(anvil, query, rcpt_anvil_lookup_callback, rcpt); } return 0; }
static void settings_export(struct config_export_context *ctx, const struct setting_parser_info *info, bool parent_unique_deflist, const void *set, const void *change_set) { const struct setting_define *def; const void *value, *default_value, *change_value; void *const *children = NULL, *const *change_children = NULL; unsigned int i, count, count2, prefix_len; const char *str; char *key; bool dump, dump_default = FALSE; for (def = info->defines; def->key != NULL; def++) { value = CONST_PTR_OFFSET(set, def->offset); default_value = info->defaults == NULL ? NULL : CONST_PTR_OFFSET(info->defaults, def->offset); change_value = CONST_PTR_OFFSET(change_set, def->offset); switch (ctx->scope) { case CONFIG_DUMP_SCOPE_ALL: dump_default = TRUE; break; case CONFIG_DUMP_SCOPE_SET: dump_default = *((const char *)change_value) != 0; break; case CONFIG_DUMP_SCOPE_CHANGED: dump_default = FALSE; break; } if (!parent_unique_deflist || (ctx->flags & CONFIG_DUMP_FLAG_HIDE_LIST_DEFAULTS) == 0) { /* .. */ } else if (*((const char *)change_value) == 0 && def->offset != info->type_offset) { /* this is mainly for service {} blocks. if value hasn't changed, it's the default. even if info->defaults has a different value. */ default_value = value; } else { /* value is set explicitly, but we don't know the default here. assume it's not the default. */ dump_default = TRUE; } dump = FALSE; count = 0; str_truncate(ctx->value, 0); switch (def->type) { case SET_BOOL: case SET_SIZE: case SET_UINT: case SET_UINT_OCT: case SET_TIME: case SET_STR_VARS: case SET_STR: case SET_ENUM: if (!config_export_type(ctx->value, value, default_value, def->type, dump_default, &dump)) i_unreached(); break; case SET_DEFLIST: case SET_DEFLIST_UNIQUE: { const ARRAY_TYPE(void_array) *val = value; const ARRAY_TYPE(void_array) *change_val = change_value; if (!array_is_created(val)) break; children = array_get(val, &count); for (i = 0; i < count; i++) { if (i > 0) str_append_c(ctx->value, ' '); setting_export_section_name(ctx->value, def, children[i], i); } change_children = array_get(change_val, &count2); i_assert(count == count2); break; } case SET_STRLIST: { const ARRAY_TYPE(const_string) *val = value; const char *const *strings; if (!array_is_created(val)) break; key = p_strconcat(ctx->pool, str_c(ctx->prefix), def->key, NULL); if (hash_table_lookup(ctx->keys, key) != NULL) { /* already added all of these */ break; } hash_table_insert(ctx->keys, key, key); /* for doveconf -n to see this KEY_LIST */ ctx->callback(key, "", CONFIG_KEY_LIST, ctx->context); strings = array_get(val, &count); i_assert(count % 2 == 0); for (i = 0; i < count; i += 2) { str = p_strdup_printf(ctx->pool, "%s%s%c%s", str_c(ctx->prefix), def->key, SETTINGS_SEPARATOR, strings[i]); ctx->callback(str, strings[i+1], CONFIG_KEY_NORMAL, ctx->context); } count = 0; break; } case SET_ALIAS: break; } if (str_len(ctx->value) > 0 || dump) { key = p_strconcat(ctx->pool, str_c(ctx->prefix), def->key, NULL); if (hash_table_lookup(ctx->keys, key) == NULL) { enum config_key_type type; if (def->offset == info->type_offset && parent_unique_deflist) type = CONFIG_KEY_UNIQUE_KEY; else if (SETTING_TYPE_IS_DEFLIST(def->type)) type = CONFIG_KEY_LIST; else type = CONFIG_KEY_NORMAL; ctx->callback(key, str_c(ctx->value), type, ctx->context); hash_table_insert(ctx->keys, key, key); } } prefix_len = str_len(ctx->prefix); for (i = 0; i < count; i++) { str_append(ctx->prefix, def->key); str_append_c(ctx->prefix, SETTINGS_SEPARATOR); setting_export_section_name(ctx->prefix, def, children[i], i); str_append_c(ctx->prefix, SETTINGS_SEPARATOR); settings_export(ctx, def->list_info, def->type == SET_DEFLIST_UNIQUE, children[i], change_children[i]); str_truncate(ctx->prefix, prefix_len); } } }
void http_client_request_redirect(struct http_client_request *req, unsigned int status, const char *location) { struct http_url *url; const char *error, *target, *origin_url; i_assert(!req->payload_wait); /* parse URL */ if (http_url_parse(location, NULL, 0, pool_datastack_create(), &url, &error) < 0) { http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT, t_strdup_printf("Invalid redirect location: %s", error)); return; } if (++req->redirects > req->client->set.max_redirects) { if (req->client->set.max_redirects > 0) { http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT, t_strdup_printf("Redirected more than %d times", req->client->set.max_redirects)); } else { http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_INVALID_REDIRECT, "Redirect refused"); } return; } /* rewind payload stream */ if (req->payload_input != NULL && req->payload_size > 0 && status != 303) { if (req->payload_input->v_offset != req->payload_offset && !req->payload_input->seekable) { http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_ABORTED, "Redirect failed: Cannot resend payload; stream is not seekable"); return; } else { i_stream_seek(req->payload_input, req->payload_offset); } } /* drop payload output stream from previous attempt */ if (req->payload_output != NULL) o_stream_unref(&req->payload_output); target = http_url_create_target(url); http_url_copy(req->pool, &req->origin_url, url); req->target = p_strdup(req->pool, target); req->host = NULL; req->conn = NULL; origin_url = http_url_create(&req->origin_url); http_client_request_debug(req, "Redirecting to %s%s", origin_url, target); req->label = p_strdup_printf(req->pool, "[%s %s%s]", req->method, origin_url, req->target); /* RFC 7231, Section 6.4.4: -> A 303 `See Other' redirect status response is handled a bit differently. Basically, the response content is located elsewhere, but the original (POST) request is handled already. */ if (status == 303 && strcasecmp(req->method, "HEAD") != 0 && strcasecmp(req->method, "GET") != 0) { // FIXME: should we provide the means to skip this step? The original // request was already handled at this point. req->method = p_strdup(req->pool, "GET"); /* drop payload */ if (req->payload_input != NULL) i_stream_unref(&req->payload_input); req->payload_size = 0; req->payload_offset = 0; } /* resubmit */ req->state = HTTP_REQUEST_STATE_NEW; http_client_request_do_submit(req); }