client.c revision 02c335c23bf5fa225a467c19f2c063fb0dc7b8c3
8c294c1cd4d721818a59684cf7f2b36123f79163Stephen Gallagher/* Copyright (c) 2011-2016 Dovecot authors, see the included COPYING file */
8c294c1cd4d721818a59684cf7f2b36123f79163Stephen Gallagher
8c294c1cd4d721818a59684cf7f2b36123f79163Stephen Gallagher#include "lib.h"
8c294c1cd4d721818a59684cf7f2b36123f79163Stephen Gallagher#include "llist.h"
8c294c1cd4d721818a59684cf7f2b36123f79163Stephen Gallagher#include "ioloop.h"
c252d148fa8ab50aaaa8bbae7beb4d208025171dNikolai Kondrashov#include "istream.h"
9542512d7be40f2000298c86d3d2b728f4f0f65aStephen Gallagher#include "ostream.h"
9542512d7be40f2000298c86d3d2b728f4f0f65aStephen Gallagher#include "strescape.h"
9542512d7be40f2000298c86d3d2b728f4f0f65aStephen Gallagher#include "master-service.h"
c6e39e15178675d0779e0ae855245774a09b4eb5Nikolai Kondrashov#include "mail-command.h"
c6e39e15178675d0779e0ae855245774a09b4eb5Nikolai Kondrashov#include "mail-session.h"
c6e39e15178675d0779e0ae855245774a09b4eb5Nikolai Kondrashov#include "mail-user.h"
c6e39e15178675d0779e0ae855245774a09b4eb5Nikolai Kondrashov#include "mail-domain.h"
c6e39e15178675d0779e0ae855245774a09b4eb5Nikolai Kondrashov#include "mail-ip.h"
29c5542feb4c45865ea61be97e0e84a1d1f04918Jakub Hrozek#include "client-export.h"
29c5542feb4c45865ea61be97e0e84a1d1f04918Jakub Hrozek#include "client.h"
fd5a4eacd56700ffb08a73121aeacdc806cb0132Sumit Bose
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#include <unistd.h>
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#define CLIENT_MAX_SIMULTANEOUS_ITER_COUNT 1000
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#define MAX_INBUF_SIZE 1024
8b1f525acd20f36c836e827de3c251088961c5d9Stephen Gallagher#define OUTBUF_THROTTLE_SIZE (1024*64)
84ae5edab16ad6be5e3be956cb6fa031c1428eb5Stephen Gallagher
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovstatic struct client *clients;
428db8a58c0c149d5efccc6d788f70916c1d34d7Jakub Hrozek
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherbool client_is_busy(struct client *client)
df4cc3a83c5d6700b6a09ff96cb4a6b1949b1aa9Stephen Gallagher{
df4cc3a83c5d6700b6a09ff96cb4a6b1949b1aa9Stephen Gallagher client->iter_count++;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (client->iter_count % CLIENT_MAX_SIMULTANEOUS_ITER_COUNT == 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (o_stream_get_buffer_used_size(client->output) < OUTBUF_THROTTLE_SIZE)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return FALSE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (o_stream_flush(client->output) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return TRUE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return o_stream_get_buffer_used_size(client->output) >= OUTBUF_THROTTLE_SIZE;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
59744cff6edb106ae799b2321cb8731edadf409aStephen Gallagherstatic int
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherclient_handle_request(struct client *client, const char *const *args,
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char **error_r)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *cmd = args[0];
8a5e793a0576250da80371e53aa3e7eba15cdb63Sumit Bose
8a5e793a0576250da80371e53aa3e7eba15cdb63Sumit Bose if (cmd == NULL) {
8a5e793a0576250da80371e53aa3e7eba15cdb63Sumit Bose *error_r = "Missing command";
90fd1bbd6035cdab46faa3a695a2fb2be6508b17Sumit Bose return -1;
90fd1bbd6035cdab46faa3a695a2fb2be6508b17Sumit Bose }
90fd1bbd6035cdab46faa3a695a2fb2be6508b17Sumit Bose args++;
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzke
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzke if (strcmp(cmd, "EXPORT") == 0)
af4ffe1001adcc0a96897e426d26444f07af9aa1Benjamin Franzke return client_export(client, args, error_r);
f3c85d900c4663854cc7bbae7d9f77867ed1f69bSumit Bose
f3c85d900c4663854cc7bbae7d9f77867ed1f69bSumit Bose *error_r = "Unknown command";
f3c85d900c4663854cc7bbae7d9f77867ed1f69bSumit Bose return -1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher}
2a552e43581c74f51205c7141ec9f6e9542509f8Stephen Gallagher
2a552e43581c74f51205c7141ec9f6e9542509f8Stephen Gallagherstatic const char *const*
41291f19dbc5bf14f20729959b852fa605fcc02dJakub Hrozekclient_read_next_line(struct client *client)
8214510f125879c3b1d247f2ce981ee20b5375d1Jakub Hrozek{
1a59af8245f183f22d87d067a90197d8e2ea958dJakub Hrozek const char *line;
a5bb518446d5ce565d7ba819590a009cabb0b0b4Jakub Hrozek char **args;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher unsigned int i;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
d921c1eba437662437847279f251a0a5d8f70127Maxim line = i_stream_next_line(client->input);
2cbdd12983eb85eddb90f64cfafb24eae5b448f4Jakub Hrozek if (line == NULL)
b9c8ce2bdd4045782c243605a1b999098bedcffcNoam Meltzer return NULL;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher args = p_strsplit(pool_datastack_create(), line, "\t");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher for (i = 0; args[i] != NULL; i++)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher args[i] = str_tabunescape(args[i]);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return (void *)args;
eb2e21b764d03544d8161e9956d7f70b07b75f77Simo Sorce}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
2a5790216f57e9bdfb2930d52860bb5300366536Jakub Hrozekstatic void client_input(struct client *client)
5377441d7a846461c2d9a7a870cea711360a529aNikolai Kondrashov{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher const char *const *args, *error;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher int ret;
32381402a4a9afc003782c9e2301fc59c9bda2a9Yassir Elley
dbfc407eef1d9ba2469687c3ffbe7fd8bb111d94Jakub Hrozek if (client->to_pending != NULL)
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher timeout_remove(&client->to_pending);
4b6a0d0b3d42e5fdb457f47d9adfa5e66b160256Stephen Gallagher
e124844907ed6973915e4d56f5442ecd07535a12Jakub Hrozek switch (i_stream_read(client->input)) {
5484044ea7bb632b915f706685fce509f6eacc48Jakub Hrozek case -2:
59744cff6edb106ae799b2321cb8731edadf409aStephen Gallagher i_error("BUG: Stats client sent too much data");
6dcbfe52d5e64205c0d922f3e89add066b42c496Jakub Hrozek client_destroy(&client);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return;
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher case -1:
b32159300fea63222d8dd9200ed634087704ea74Stephen Gallagher client_destroy(&client);
87d3b47abba6a40fcf809c85a2b138bc1013d9c5Jakub Hrozek return;
bc13c352ba9c2877f1e9bc62e55ad60fc000a55dJakub Hrozek }
bc13c352ba9c2877f1e9bc62e55ad60fc000a55dJakub Hrozek
bc13c352ba9c2877f1e9bc62e55ad60fc000a55dJakub Hrozek o_stream_cork(client->output);
bc13c352ba9c2877f1e9bc62e55ad60fc000a55dJakub Hrozek while ((args = client_read_next_line(client)) != NULL) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = client_handle_request(client, args, &error);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ret < 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_error("Stats client input error: %s", error);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client_destroy(&client);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
054b5d4bb98973698f74d66b14ccd14394b53f10Lukas Slebodnik if (ret == 0) {
054b5d4bb98973698f74d66b14ccd14394b53f10Lukas Slebodnik o_stream_set_flush_pending(client->output, TRUE);
62bda5f75bda6b77aea30d708c74efaf725d9367Lukas Slebodnik io_remove(&client->io);
a3d176d116ceccd6a7547c128fab5df5cdd2c2b6Michal Zidek break;
a3d176d116ceccd6a7547c128fab5df5cdd2c2b6Michal Zidek }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client->cmd_more = NULL;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
4f6931e854c698dcb1c09f99eb330ce2fb97e7c6Lukas Slebodnik o_stream_uncork(client->output);
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher}
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagherstatic int client_output(struct client *client)
558998ce664055a75595371118f818084d8f2b23Jan Cholasta{
558998ce664055a75595371118f818084d8f2b23Jan Cholasta int ret = 1;
9a3e40dc49c1e38bf58e45be5adff37615f3910bJan Cholasta
9a3e40dc49c1e38bf58e45be5adff37615f3910bJan Cholasta o_stream_cork(client->output);
558998ce664055a75595371118f818084d8f2b23Jan Cholasta if (o_stream_flush(client->output) < 0) {
558998ce664055a75595371118f818084d8f2b23Jan Cholasta client_destroy(&client);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher return 1;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher }
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (client->cmd_more != NULL)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher ret = client->cmd_more(client);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher o_stream_uncork(client->output);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (ret > 0) {
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client->cmd_more = NULL;
c737e1444fb186e349e59bfa9dac4995b720b4b1Jan Zeleny if (client->io == NULL)
f1828234a850dd28465425248a83a993f262918fPavel Březina client_enable_io(client);
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay }
b69cb1787209e85cc246eb9a944242689bfe0c46Pavel Březina return ret;
b69cb1787209e85cc246eb9a944242689bfe0c46Pavel Březina}
b69cb1787209e85cc246eb9a944242689bfe0c46Pavel Březina
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallaghervoid client_enable_io(struct client *client)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_assert(client->io == NULL);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client->io = io_add(client->fd, IO_READ, client_input, client);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (client->to_pending == NULL)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client->to_pending = timeout_add(0, client_input, client);
1746e8b8399da2a7a8da4aace186f66055ccfec1Jakub Hrozek}
1746e8b8399da2a7a8da4aace186f66055ccfec1Jakub Hrozek
1746e8b8399da2a7a8da4aace186f66055ccfec1Jakub Hrozekstruct client *client_create(int fd)
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina{
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina struct client *client;
2827b0d03f7b6bafa504d22a5d7ca39cbda048b3Pavel Březina
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek client = i_new(struct client, 1);
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek client->fd = fd;
9e80079370ff3b943832adc3c5ef430e64be0a0cJakub Hrozek client->io = io_add(fd, IO_READ, client_input, client);
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta client->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta client->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
e7311aec8d691e5427317442387af1bc8fff3742Jan Cholasta o_stream_set_no_error_handling(client->output, TRUE);
cb4d5b588e704114b7090678752d33512baa718eJakub Hrozek o_stream_set_flush_callback(client->output, client_output, client);
cb4d5b588e704114b7090678752d33512baa718eJakub Hrozek client->cmd_pool = pool_alloconly_create("cmd pool", 1024);
cb4d5b588e704114b7090678752d33512baa718eJakub Hrozek
19d3aba12c70528708be9440aca66038a291f29eYassir Elley DLLIST_PREPEND(&clients, client);
19d3aba12c70528708be9440aca66038a291f29eYassir Elley return client;
19d3aba12c70528708be9440aca66038a291f29eYassir Elley}
f3a25949de81f80c136bb073e4a8f504b080c20cJakub Hrozek
f3a25949de81f80c136bb073e4a8f504b080c20cJakub Hrozekstatic void client_unref_iters(struct client *client)
f3a25949de81f80c136bb073e4a8f504b080c20cJakub Hrozek{
45726939a48e605b0166521f94300ae04981a3a7Sumit Bose if (client->mail_cmd_iter != NULL)
45726939a48e605b0166521f94300ae04981a3a7Sumit Bose mail_command_unref(&client->mail_cmd_iter);
3be9e26dcd169d44ae105f1b8a0674464c700b77Sumit Bose if (client->mail_session_iter != NULL)
5484044ea7bb632b915f706685fce509f6eacc48Jakub Hrozek mail_session_unref(&client->mail_session_iter);
5484044ea7bb632b915f706685fce509f6eacc48Jakub Hrozek if (client->mail_user_iter != NULL)
3be9e26dcd169d44ae105f1b8a0674464c700b77Sumit Bose mail_user_unref(&client->mail_user_iter);
3be9e26dcd169d44ae105f1b8a0674464c700b77Sumit Bose if (client->mail_domain_iter != NULL)
45726939a48e605b0166521f94300ae04981a3a7Sumit Bose mail_domain_unref(&client->mail_domain_iter);
5484044ea7bb632b915f706685fce509f6eacc48Jakub Hrozek if (client->mail_ip_iter != NULL)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher mail_ip_unref(&client->mail_ip_iter);
b9e5bd09a5ff7009537a18914dbebcf10498f592Sumit Bose}
b9e5bd09a5ff7009537a18914dbebcf10498f592Sumit Bose
b9e5bd09a5ff7009537a18914dbebcf10498f592Sumit Bosevoid client_destroy(struct client **_client)
b9e5bd09a5ff7009537a18914dbebcf10498f592Sumit Bose{
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher struct client *client = *_client;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
36ccdecd053a9ad88dce86b8c84770dc2aa11d21Simo Sorce *_client = NULL;
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher DLLIST_REMOVE(&clients, client);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (client->io != NULL)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher io_remove(&client->io);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_stream_destroy(&client->input);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher o_stream_destroy(&client->output);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher if (close(client->fd) < 0)
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher i_error("close(client) failed: %m");
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher client_unref_iters(client);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher pool_unref(&client->cmd_pool);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher i_free(client);
551aa6c36797ed720487f5974dcadabf19e6ff9fStephen Gallagher
69aaef8719c5cf33ed1c4090fa313ba281bf8a02Jakub Hrozek master_service_client_connection_destroyed(master_service);
4dd615c01357b8715711aad6820ba9595d3ad377Stephen Gallagher}
fe60346714a73ac3987f786731389320633dd245Pavel Březina
a6098862048d4bb469130b9ff21be3020d6f2c54Sumit Bosevoid clients_destroy_all(void)
2d257ccf620ce1b611f89cec8f0a94c88c2f2881Sumit Bose{
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter while (clients != NULL) {
4bd20c075f0f187db0181dc53d00ab6cd47fdb4dJakub Hrozek struct client *client = clients;
e5e8252ec48bfdd4e7529debc705c8e090264b9aSumit Bose
71e7918be3ca5d38794a16a17f6b4f19a24d51fcPavel Březina client_destroy(&client);
8359bf07a2e6c0181251ce8d5d9160dc57546c55Stephen Gallagher }
71e7918be3ca5d38794a16a17f6b4f19a24d51fcPavel Březina}
71e7918be3ca5d38794a16a17f6b4f19a24d51fcPavel Březina