client-connection-http.c revision 664503c588de5db6ca138f90b3c7fa56352adcad
/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "compat.h"
#include "lib-signals.h"
#include "base64.h"
#include "ioloop.h"
#include "str.h"
#include "str-sanitize.h"
#include "istream.h"
#include "ostream.h"
#include "strescape.h"
#include "settings-parser.h"
#include "iostream-ssl.h"
#include "iostream-temp.h"
#include "istream-seekable.h"
#include "master-service.h"
#include "master-service-ssl.h"
#include "master-service-settings.h"
#include "mail-storage-service.h"
#include "http-server.h"
#include "http-request.h"
#include "http-response.h"
#include "http-url.h"
#include "doveadm-util.h"
#include "doveadm-server.h"
#include "doveadm-mail.h"
#include "doveadm-print.h"
#include "doveadm-settings.h"
#include "client-connection-private.h"
#include "json-parser.h"
#include <unistd.h>
#include <ctype.h>
enum client_request_parse_state {
};
struct client_request_http {
struct client_connection_http *conn;
struct http_server_request *http_request;
struct json_parser *json_parser;
const struct doveadm_cmd_ver2 *cmd;
struct doveadm_cmd_param *cmd_param;
int method_err;
char *method_id;
bool first_row;
bool value_is_array;
};
struct client_connection_http {
struct client_connection conn;
struct http_server_connection *http_conn;
struct client_request_http *request;
};
struct doveadm_http_server_mount {
const char *verb;
const char *path;
bool auth;
};
static struct http_server *doveadm_http_server;
/*
* API
*/
static void doveadm_http_server_options_handler(struct client_request_http *);
static void doveadm_http_server_print_mounts(struct client_request_http *);
static void doveadm_http_server_send_api_v1(struct client_request_http *);
static void doveadm_http_server_read_request_v1(struct client_request_http *);
static struct doveadm_http_server_mount doveadm_http_server_mounts[] = {
{
.verb = "OPTIONS",
},{
.verb = "GET",
.path = "/",
},{
.verb = "GET",
},{
.verb = "POST",
}
};
{
str_truncate(escaped,0);
str_truncate(escaped,0);
}
}
{
}
}
static void
{
struct doveadm_cmd_context cctx;
const char *user;
/* final preflight check */
if (req->method_err != 0) {
} else {
}
return;
}
// create iostream
/* then call it */
ioloop = io_loop_create();
doveadm_exit_code = 0;
else
if (o_stream_nfinish(doveadm_print_ostream) < 0) {
i_info("Error writing output in command %s: %s",
}
else
if (doveadm_exit_code != 0) {
} else {
}
i_stream_unref(&is);
}
{
const unsigned char *data;
return 0;
if (v_input->stream_errno != 0) {
i_error("read(%s) failed: %s",
return -1;
}
return 1;
}
/**
* this is to ensure we can handle arrays and other special parameter types
*/
static int doveadm_http_server_json_parse_next(struct client_request_http *req, enum json_type *type, const char **value)
{
int ret;
const char *tmp;
switch (req->parse_state) {
if (ret != 1)
return ret;
break;
/* reading through parameters in an array */
if (*type == JSON_TYPE_ARRAY_END)
break;
if (*type != JSON_TYPE_STRING)
return -2;
}
if (ret <= 0)
return ret;
break;
if (ret != 1)
return ret;
req->cmd_param->value.v_istream = i_stream_create_seekable_path(is, IO_BLOCK_SIZE, "/tmp/doveadm.");
i_stream_unref(&is[0]);
}
if (ret != 1)
return ret;
if (*type == JSON_TYPE_ARRAY) {
/* start of array */
}
if (*type != JSON_TYPE_STRING) {
/* FIXME: should handle other than string too */
return -2;
}
} else {
case CMD_PARAM_BOOL:
break;
case CMD_PARAM_INT64:
}
break;
case CMD_PARAM_IP:
}
break;
case CMD_PARAM_STR:
break;
default:
break;
}
}
break;
default:
break;
}
}
static bool
{
const struct doveadm_cmd_ver2 *ccmd;
struct doveadm_cmd_param *par;
bool found;
switch (req->parse_state) {
if (type != JSON_TYPE_ARRAY)
return FALSE;
return TRUE;
case CLIENT_REQUEST_PARSE_CMD:
if (type == JSON_TYPE_ARRAY_END) {
return TRUE;
}
if (type != JSON_TYPE_ARRAY)
return FALSE;
req->method_err = 0;
return TRUE;
if (type != JSON_TYPE_STRING)
return FALSE;
/* see if we can find it */
break;
}
}
if (!found) {
} else {
struct doveadm_cmd_param *param;
int pargc;
/* initialize pargv */
}
}
return TRUE;
if (type == JSON_TYPE_OBJECT_END) {
return TRUE;
}
if (type != JSON_TYPE_OBJECT)
return FALSE;
return TRUE;
if (type == JSON_TYPE_OBJECT_END) {
return TRUE;
}
// can happen...
return FALSE;
/* go hunting */
/* it's already set, cannot have same key twice in json */
return FALSE;
break;
}
}
/* skip parameters if error has already occurred */
} else {
// FIXME: should be returned as error to client, not logged
i_info("Parameter %s already set",
return FALSE;
}
}
return TRUE;
if (type != JSON_TYPE_STRING)
return FALSE;
return TRUE;
/* should be end of array */
if (type != JSON_TYPE_ARRAY_END)
return FALSE;
return TRUE;
// FIXME: should be returned as error to client, not logged
i_info("Got unexpected elements in JSON data");
return TRUE;
default:
break;
}
i_unreached();
}
static void
{
int ret;
}
break;
}
return;
/* this will happen if the parser above runs into unexpected element, but JSON is OK */
// FIXME: should be returned as error to client, not logged
i_info("unexpected element");
return;
}
i_info("read(client) failed: %s",
return;
}
// istream JSON parsing failures do not count as errors
// FIXME: should be returned as error to client, not logged
return;
}
}
{
size_t i, k;
i++;
} else {
}
}
str_truncate(value, k);
}
static void
{
const struct doveadm_cmd_ver2 *cmd;
const struct doveadm_cmd_param *par;
unsigned int i, k;
bool sent;
for (i = 0; i < array_count(&doveadm_cmds_ver2); i++) {
if (i > 0)
str_truncate(tmp, 0);
continue;
if (sent)
else
case CMD_PARAM_BOOL:
break;
case CMD_PARAM_INT64:
break;
case CMD_PARAM_ARRAY:
break;
case CMD_PARAM_IP:
case CMD_PARAM_ISTREAM:
case CMD_PARAM_STR:
}
}
if (k > 0)
str_truncate(tmp, 0);
}
}
static void
{
struct http_server_response *http_resp;
"Access-Control-Allow-Origin", "*");
"Access-Control-Allow-Methods", "GET, POST, OPTIONS");
"Access-Control-Allow-Request-Headers",
"Content-Type, X-API-Key, Authorization");
"Access-Control-Allow-Headers",
"Content-Type, WWW-Authenticate");
}
static void
{
unsigned int i;
for (i = 0; i < N_ELEMENTS(doveadm_http_server_mounts); i++) {
if (i > 0)
else
else
}
}
/*
* Request
*/
{
struct http_server_response *http_resp;
i_info("error writing output: %s",
return;
}
}
"application/json; charset=utf-8");
}
}
static void
{
const struct http_request *http_req =
struct http_server_response *http_resp =
int status;
i_info("doveadm: %s %s %s \"%s %s "
}
const char *error ATTR_UNUSED;
// we've already failed, ignore error
}
}
static bool
const struct http_auth_credentials *creds)
{
char *value;
i_error("Invalid authentication attempt to HTTP API: "
"Basic authentication scheme not enabled");
return FALSE;
}
return TRUE;
i_error("Invalid authentication attempt to HTTP API "
"(using Basic authentication scheme)");
return FALSE;
}
static bool
const struct http_auth_credentials *creds)
{
i_error("Invalid authentication attempt to HTTP API: "
"X-Dovecot-API authentication scheme not enabled");
return FALSE;
}
return TRUE;
i_error("Invalid authentication attempt to HTTP API "
"(using X-Dovecot-API authentication scheme)");
return FALSE;
}
static bool
const struct http_auth_credentials *creds)
{
/* see if the mech is supported */
i_error("Unsupported authentication scheme to HTTP API: %s",
return FALSE;
}
static bool
{
struct http_auth_credentials creds;
/* no authentication specified */
i_error("No authentication defined in configuration. Add API key or password");
return FALSE;
}
if (!auth) {
struct http_server_response *http_resp;
"WWW-Authenticate", "X-Dovecot-API"
);
}
"WWW-Authenticate", "Basic Realm=\"doveadm\""
);
}
}
return auth;
}
static void
{
struct client_request_http *req;
const struct http_request *http_req =
unsigned int i;
/* no pipelining possible due to synchronous handling of requests */
for (i = 0; i < N_ELEMENTS(doveadm_http_server_mounts); i++) {
ep = &doveadm_http_server_mounts[i];
break;
}
}
}
return;
}
return;
/* handle request */
} else {
}
}
/*
* Connection
*/
static const struct http_server_callbacks doveadm_http_callbacks = {
};
static void
{
struct client_connection_http *conn =
(struct client_connection_http *)_conn;
"Server shutting down");
}
}
struct client_connection *
{
struct client_connection_http *conn;
return NULL;
}
}
static void
const char *reason ATTR_UNUSED)
{
struct client_connection_http *conn =
(struct client_connection_http *)context;
/* already destroying client directly */
return;
}
/* HTTP connection is destroyed already now */
/* destroy the connection itself */
}
/*
* Server
*/
void doveadm_http_server_init(void)
{
struct http_server_settings http_set = {
};
}
void doveadm_http_server_deinit(void)
{
}