~ubuntu-dev/ubuntu/lucid/dovecot/lucid-201002101901

« back to all changes in this revision

Viewing changes to managesieve/src/managesieve-login/client.c

  • Committer: Chuck Short
  • Date: 2010-01-21 20:21:25 UTC
  • mfrom: (4.1.11 squeeze)
  • Revision ID: zulcss@ubuntu.com-20100121202125-pme73o491kfwj5nc
* Merge from debian testing, remaining changes:
  + Add new binary pkg dovecot-postfix that integrates postfix and dovecot
    automatically: (LP: #164837)
  + debian/control:
    - add new binary with short description
    - set Architecture all for dovecot-postfix (LP: #329878)
  + debian/dovecot-postfix.postinst:
    - create initial certificate symlinks to snakeoil.
    - set up postfix with postconf to:
      - use Maildir/ as the default mailbox.
      - use dovecot as the sasl authentication server.
      - use dovecot LDA (deliver).
      - use tls for smtp{d} services.
    - fix certificates paths in postfix' main.cf
    - add reject_unauth_destination to postfix' recipient restrictions
    - add reject_unknown_sender_domain to postfix' sender restriction
    - rename configuration name on remove, delete on purge
    - restart dovecot after linking certificates
    - handle use case when postfix is unconfigurated
  + debian/dovecot-postfix.dirs: create backup directory for postfix's config
    configuration
  + restart postfix and dovecot.
  + debian/dovecot-postfix.postrm:
    - remove all dovecot related configuration from postfix.
    - restart postfix and dovecot.
  + debian/dovecot-common.init:
    - check if /etc/dovecot/dovecot-postfix.conf exists and use it
      as the configuration file if so.
  + debian/patches/warning-ubuntu-postfix.dpatch
    - add warning about dovecot-postfix.conf in dovecot default
      configuration file
  + debian/patches/dovecot-postfix.conf.diff:
    - Ubuntu server custom changes to the default dovecot configuration for
      better interfation with postfix.
    - enable sieve plugin.
    - Ubuntu server custom changes to the default dovecot configuration for
      better integration with postfix:
      - enable imap, pop3, imaps, pop3s and managesieve by default.
      - enable dovecot LDA (deliver).
      - enable SASL auth socket in postfix private directory
   + debian/rules:
     - copy, patch and install dovecot-postfix.conf in /etc/dovecot/.
     - build architecure independent packages too
   + Use Snakeoil SSL certificates by default.
     - debian/control: Depend on ssl-cert.
     - debian/patches/ssl-cert-snakeoil.dpatch: Change default SSL cert
       paths to snakeoil.
     - debian/dovecot-common.postinst: Relax grep for SSL_* a bit.
   + Add autopkgtest to debian/tests/*.
   + Fast TearDown: Update the lsb init header to not stop in level 6.
   + Add ufw integration:
     - Created debian/dovecot-common.ufw.profile.
     - debian/rules: install profile.
     - debian/control: suggest ufw.
   + debian/{control,rules}: enable PIE hardening.
   + dovecot-imapd, dovecot-pop3: Replaces dovecot-common (<< 1:1.1). (LP: #254721)
   + debian/control: Update Vcs-* headers.
   + Add SMTP-AUTH support for Outlook (login auth mechanism)
* New upstream release.
* debian/patches/gold-fix.patch: Removed. Fixed upstream.
* Moved libexec to lib corrections in dovecot-managesieve.patch and
  dovecot-managesieve-dist.patch to dovecot-example.patch
* debian/patches/dovecot-mboxlocking.patch: Regenerated to avoid FTBFS
  when quilt isn't installed.
* debian/patches/quota-mountpoint.patch: Removed. Not needed anymore.
* debian/patches/dovecot-quota.patch: Removed. Quotas aren't properly
  enabled unless mail_plugins = quota imap_quota.
* debian/patches/gold-fix.patch: Fixed configure script to build even
  with binutils-gold or --no-add-needed linker flag (Closes: #554306)
* debian/dovecot-common.init: fixed LSB headers. Thanks to Pascal Volk.
  (Closes: #558040)
* debian/changelog: added CVE references to previous changelog entry.
* debian/rules: checked up the build system. It's not fragile anymore.
  (Closes: 493803)
* debian/dovecot-common.postinst: Now invoking dpkg-reconfigure
  on dovecot-common is enough to generate new certificates
  if the previous ones were removed. (Closes: #545582)
* debian/rules: No longer install convert-tool in /usr/bin.
  It isn't an user utility and it should stay in /usr/lib/dovecot
  like all other similar tool.
* New upstream release. (Closes: #557601)
* [SECURITY] Fixes local information disclosure and denial of service.
  (see: http://www.dovecot.org/list/dovecot-news/2009-November/000143.html
  and CVE-2009-3897)
* Added myself to uploaders.
* Switched to the new source format "3.0 (quilt)":
  - removed dpatch from build-depends
  - removed debian/README.source because now we use only standard
    dpkg features
  - regenerated all patches
* Prepared to switch to multi-origin source:
  - recreated dovecot-libsieve.patch and dovecot-managesieve-dist.patch
    starting from the upstream tarball
  - removed all autotools related build-depends and build-conflict
  - renamed dovecot-libsieve and dovecot-managesieve directories
    to libsieve and managesieve.
* debian/rules: Moved the configuration of libsieve and managesieve from
  the build phase to the configuration phase
* Added dovecot-dbg package  with debugging symbols.  Thanks Stephan Bosch.
  (Closes: #554710)
* Fixed some stray libexec'isms in the default configuration.
* New upstream release.
* debian/dovecot-common.init:
  - use $CONF when starting the daemon. (Closes: #549944)
  - always output start/stop messages. (Closes: #523810)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
 
2
 */
 
3
 
 
4
#include "common.h"
 
5
#include "buffer.h"
 
6
#include "ioloop.h"
 
7
#include "istream.h"
 
8
#include "ostream.h"
 
9
#include "process-title.h"
 
10
#include "safe-memset.h"
 
11
#include "str.h"
 
12
#include "strfuncs.h"
 
13
#include "strescape.h"
 
14
 
 
15
#include "sieve.h"
 
16
 
 
17
#include "managesieve-parser.h"
 
18
#include "managesieve-quote.h"
 
19
 
 
20
#include "client.h"
 
21
#include "client-authenticate.h"
 
22
#include "commands.h"
 
23
#include "auth-client.h"
 
24
#include "ssl-proxy.h"
 
25
 
 
26
#include "managesieve-capability.h"
 
27
#include "managesieve-proxy.h"
 
28
 
 
29
#include <stdlib.h>
 
30
 
 
31
/* max. size of output buffer. if it gets full, the client is disconnected.
 
32
   SASL authentication gives the largest output. */
 
33
#define MAX_OUTBUF_SIZE 4096
 
34
 
 
35
/* Disconnect client when it sends too many bad commands */
 
36
#define CLIENT_MAX_BAD_COMMANDS 10
 
37
 
 
38
/* When max. number of simultaneous connections is reached, few of the
 
39
   oldest connections are disconnected. Since we have to go through all of the
 
40
   clients, it's faster if we disconnect multiple clients. */
 
41
#define CLIENT_DESTROY_OLDEST_COUNT 16
 
42
 
 
43
/* If we've been waiting auth server to respond for over this many milliseconds,
 
44
   send a "waiting" message. */
 
45
#define AUTH_WAITING_TIMEOUT_MSECS (30*1000)
 
46
 
 
47
#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS < AUTH_REQUEST_TIMEOUT*1000
 
48
#  error client idle timeout must be larger than authentication timeout
 
49
#endif
 
50
 
 
51
const char *login_protocol = "MANAGESIEVE";
 
52
const char *capability_string = NULL;
 
53
 
 
54
const char *managesieve_implementation_string;
 
55
 
 
56
static void client_set_title(struct managesieve_client *client)
 
57
{
 
58
        const char *addr;
 
59
 
 
60
        if (!verbose_proctitle || !process_per_connection)
 
61
                return;
 
62
 
 
63
        addr = net_ip2addr(&client->common.ip);
 
64
        if (addr == NULL)
 
65
                addr = "??";
 
66
 
 
67
        process_title_set(t_strdup_printf(client->common.tls ?
 
68
                                          "[%s TLS]" : "[%s]", addr));
 
69
}
 
70
 
 
71
static void client_open_streams(struct managesieve_client *client, int fd)
 
72
{
 
73
        client->common.input =
 
74
                i_stream_create_fd(fd, LOGIN_MAX_INBUF_SIZE, FALSE);
 
75
        client->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE);
 
76
        client->parser = managesieve_parser_create(client->common.input, client->output,
 
77
                                            MAX_MANAGESIEVE_LINE);
 
78
}
 
79
 
 
80
/* Skip incoming data until newline is found,
 
81
   returns TRUE if newline was found. */
 
82
bool client_skip_line(struct managesieve_client *client)
 
83
{
 
84
        const unsigned char *data;
 
85
        size_t i, data_size;
 
86
 
 
87
        data = i_stream_get_data(client->common.input, &data_size);
 
88
 
 
89
        for (i = 0; i < data_size; i++) {
 
90
                if (data[i] == '\n') {
 
91
                        i_stream_skip(client->common.input, i+1);
 
92
                        return TRUE;
 
93
                }
 
94
        }
 
95
 
 
96
        return FALSE;
 
97
}
 
98
 
 
99
static void client_send_capabilities(struct managesieve_client *client)
 
100
{
 
101
        const char *saslcap, *sievecap, *notifycap;
 
102
 
 
103
        T_BEGIN {
 
104
                /* Get capabilities */
 
105
                sievecap = managesieve_capability_sieve;
 
106
                saslcap = client_authenticate_get_capabilities(client->common.secured);
 
107
                notifycap = managesieve_capability_notify;
 
108
 
 
109
                /* Default capabilities */
 
110
                client_send_line(client, t_strconcat("\"IMPLEMENTATION\" \"", 
 
111
                        managesieve_implementation_string, "\"", NULL));
 
112
                client_send_line(client, t_strconcat("\"SIEVE\" \"",
 
113
                        ( sievecap == NULL ? "" : sievecap ), "\"", NULL));
 
114
                client_send_line(client, t_strconcat("\"SASL\" \"", 
 
115
                        saslcap, "\"", NULL));
 
116
 
 
117
                /* STARTTLS */
 
118
                if (ssl_initialized && !client->common.tls)
 
119
                        client_send_line(client, "\"STARTTLS\"" );
 
120
 
 
121
                /* Notify methods */
 
122
                if ( notifycap != NULL ) {
 
123
                        client_send_line(client, t_strconcat("\"NOTIFY\" \"", 
 
124
                                notifycap, "\"", NULL));
 
125
                }
 
126
 
 
127
                /* Protocol version */
 
128
                client_send_line(client, "\"VERSION\" \"1.0\"");
 
129
        } T_END;
 
130
}
 
131
 
 
132
static int cmd_capability(struct managesieve_client *client)
 
133
{
 
134
        o_stream_cork(client->output);
 
135
 
 
136
        client_send_capabilities(client);
 
137
        client_send_ok(client, "Capability completed.");
 
138
 
 
139
        o_stream_uncork(client->output);
 
140
 
 
141
        return 1;
 
142
}
 
143
 
 
144
static void client_start_tls(struct managesieve_client *client)
 
145
{
 
146
        int fd_ssl;
 
147
 
 
148
        client_ref(client);
 
149
        connection_queue_add(1);
 
150
        if (!client_unref(client) || client->destroyed)
 
151
                return;
 
152
 
 
153
        fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
 
154
                               &client->common.proxy);
 
155
        if (fd_ssl == -1) {
 
156
                client_send_bye(client, "TLS initialization failed.");
 
157
                client_destroy(client, "Disconnected: TLS initialization failed.");
 
158
                return;
 
159
        }
 
160
 
 
161
        client->common.tls = TRUE;
 
162
        client->common.secured = TRUE;
 
163
        client_set_title(client);
 
164
 
 
165
        client->common.fd = fd_ssl;
 
166
        i_stream_unref(&client->common.input);
 
167
        o_stream_unref(&client->output);
 
168
        managesieve_parser_destroy(&client->parser);
 
169
 
 
170
        /* CRLF is lost from buffer when streams are reopened. */
 
171
        client->skip_line = FALSE;
 
172
 
 
173
        client_open_streams(client, fd_ssl);
 
174
        client->io = io_add(client->common.fd, IO_READ, client_input, client);
 
175
}
 
176
 
 
177
static int client_output_starttls(void *context)
 
178
{
 
179
        struct managesieve_client *client = context;
 
180
        int ret;
 
181
 
 
182
        if ((ret = o_stream_flush(client->output)) < 0) {
 
183
                client_destroy(client, "Disconnected");
 
184
                return 1;
 
185
        }
 
186
 
 
187
        if (ret > 0) {
 
188
                o_stream_unset_flush_callback(client->output);
 
189
                client_start_tls(client);
 
190
        }
 
191
        return 1;
 
192
}
 
193
 
 
194
static int cmd_starttls(struct managesieve_client *client)
 
195
{
 
196
        if (client->common.tls) {
 
197
                client_send_no(client, "TLS is already active.");
 
198
                return 1;
 
199
        }
 
200
 
 
201
        if (!ssl_initialized) {
 
202
                client_send_no(client, "TLS support isn't enabled.");
 
203
                return 1;
 
204
        }
 
205
 
 
206
        /* remove input handler, SSL proxy gives us a new fd. we also have to
 
207
           remove it in case we have to wait for buffer to be flushed */
 
208
        if (client->io != NULL)
 
209
                io_remove(&client->io);
 
210
 
 
211
        client_send_ok(client, "Begin TLS negotiation now.");
 
212
 
 
213
        /* uncork the old fd */
 
214
        o_stream_uncork(client->output);
 
215
 
 
216
        if (o_stream_flush(client->output) <= 0) {
 
217
                /* the buffer has to be flushed */
 
218
                o_stream_set_flush_pending(client->output, TRUE);
 
219
                o_stream_set_flush_callback(client->output,
 
220
                                            client_output_starttls, client);
 
221
        } else {
 
222
                client_start_tls(client);
 
223
        }
 
224
 
 
225
        /* Cork the stream to send the capability data as a single tcp frame
 
226
         *   Some naive clients break if we don't.
 
227
         */
 
228
        o_stream_cork(client->output);
 
229
 
 
230
        client_send_capabilities(client);
 
231
        client_send_ok(client, "TLS negotiation successful.");
 
232
 
 
233
        o_stream_uncork(client->output);
 
234
 
 
235
        return 1;
 
236
}
 
237
 
 
238
static int cmd_logout(struct managesieve_client *client)
 
239
{
 
240
        client_send_ok(client, "Logout completed.");
 
241
        client_destroy(client, "Aborted login");
 
242
        return 1;
 
243
}
 
244
 
 
245
static int client_command_execute(struct managesieve_client *client, const char *cmd,
 
246
                                  struct managesieve_arg *args)
 
247
{
 
248
        cmd = t_str_ucase(cmd);
 
249
        if (strcmp(cmd, "AUTHENTICATE") == 0)
 
250
                return cmd_authenticate(client, args);
 
251
        if (strcmp(cmd, "CAPABILITY") == 0)
 
252
                return cmd_capability(client);
 
253
        if (strcmp(cmd, "STARTTLS") == 0)
 
254
                return cmd_starttls(client);
 
255
        if (strcmp(cmd, "LOGOUT") == 0)
 
256
                return cmd_logout(client);
 
257
        if (strcmp(cmd, "NOOP") == 0)
 
258
                return cmd_noop(client);
 
259
 
 
260
        return -1;
 
261
}
 
262
 
 
263
static bool client_handle_input(struct managesieve_client *client)
 
264
{
 
265
        struct managesieve_arg *args;
 
266
        const char *msg;
 
267
        int ret;
 
268
        bool fatal;
 
269
 
 
270
        i_assert(!client->common.authenticating);
 
271
 
 
272
        if (client->cmd_finished) {
 
273
                /* clear the previous command from memory. don't do this
 
274
                   immediately after handling command since we need the
 
275
                   cmd_tag to stay some time after authentication commands. */
 
276
                client->cmd_name = NULL;
 
277
                managesieve_parser_reset(client->parser);
 
278
 
 
279
                /* remove \r\n */
 
280
                if (client->skip_line) {
 
281
                        if (!client_skip_line(client))
 
282
                                return FALSE;
 
283
                        client->skip_line = FALSE;
 
284
                }
 
285
 
 
286
                client->cmd_finished = FALSE;
 
287
        }
 
288
 
 
289
        if (client->cmd_name == NULL) {
 
290
                client->cmd_name = managesieve_parser_read_word(client->parser);
 
291
                if (client->cmd_name == NULL)
 
292
                        return FALSE; /* need more data */
 
293
        }
 
294
 
 
295
        switch (managesieve_parser_read_args(client->parser, 0, 0, &args)) {
 
296
        case -1:
 
297
                /* error */
 
298
                msg = managesieve_parser_get_error(client->parser, &fatal);
 
299
                if (fatal) {
 
300
                        client_send_bye(client, msg);
 
301
                        client_destroy(client, t_strconcat("Disconnected: ",
 
302
                                msg, NULL));
 
303
                        return FALSE;
 
304
                }
 
305
 
 
306
                client_send_no(client, msg);
 
307
                client->cmd_finished = TRUE;
 
308
                client->skip_line = TRUE;
 
309
                return TRUE;
 
310
        case -2:
 
311
                /* not enough data */
 
312
                return FALSE;
 
313
        }
 
314
        /* we read the entire line - skip over the CRLF */
 
315
        if (!client_skip_line(client))
 
316
                i_unreached();
 
317
 
 
318
        ret = client_command_execute(client, client->cmd_name, args);
 
319
 
 
320
        client->cmd_finished = TRUE;
 
321
        if (ret < 0) {
 
322
                if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
 
323
                        client_send_bye(client, 
 
324
                                "Too many invalid MANAGESIEVE commands.");
 
325
                        client_destroy(client, "Disconnected: "
 
326
                                "Too many invalid commands.");
 
327
                        return FALSE;
 
328
                }  
 
329
                client_send_no(client,
 
330
                        "Error in MANAGESIEVE command received by server.");
 
331
        }
 
332
 
 
333
        return ret != 0;
 
334
}
 
335
 
 
336
bool client_read(struct managesieve_client *client)
 
337
{
 
338
        switch (i_stream_read(client->common.input)) {
 
339
        case -2:
 
340
                /* buffer full */
 
341
                client_send_bye(client, "Input buffer full, aborting");
 
342
                client_destroy(client, "Disconnected: Input buffer full");
 
343
                return FALSE;
 
344
        case -1:
 
345
                /* disconnected */
 
346
                client_destroy(client, "Disconnected");
 
347
                return FALSE;
 
348
        case 0:
 
349
                /* nothing new read */
 
350
                return TRUE;
 
351
        default:
 
352
                /* something was read */
 
353
                timeout_reset(client->to_idle_disconnect);
 
354
                return TRUE;
 
355
        }
 
356
}
 
357
 
 
358
void client_input(struct managesieve_client *client)
 
359
{
 
360
        if (!client_read(client))
 
361
                return;
 
362
 
 
363
        client_ref(client);
 
364
 
 
365
        if (!auth_client_is_connected(auth_client)) {
 
366
                /* we're not yet connected to auth process -
 
367
                   don't allow any commands */
 
368
                /* FIXME: Can't do this with managesieve. Any other ways?
 
369
                client_send_ok(client, AUTH_WAITING_MSG);
 
370
                */
 
371
                if (client->to_auth_waiting != NULL)
 
372
                        timeout_remove(&client->to_auth_waiting);
 
373
                
 
374
                client->input_blocked = TRUE;
 
375
        } else {
 
376
                o_stream_cork(client->output);
 
377
                while (client_handle_input(client)) ;
 
378
                o_stream_uncork(client->output);
 
379
        }
 
380
 
 
381
        client_unref(client);
 
382
}
 
383
 
 
384
void client_destroy_oldest(void)
 
385
{
 
386
        struct client *client;
 
387
        struct managesieve_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
 
388
        unsigned int i, destroy_count;
 
389
 
 
390
        /* find the oldest clients and put them to destroy-buffer */
 
391
        memset(destroy_buf, 0, sizeof(destroy_buf));
 
392
 
 
393
        destroy_count = max_connections > CLIENT_DESTROY_OLDEST_COUNT*2 ?
 
394
                CLIENT_DESTROY_OLDEST_COUNT : I_MIN(max_connections/2, 1);
 
395
        for (client = clients; client != NULL; client = client->next) {
 
396
                struct managesieve_client *msieve_client = 
 
397
                        (struct managesieve_client *) client;
 
398
 
 
399
                for (i = 0; i < destroy_count; i++) {
 
400
                        if (destroy_buf[i] == NULL ||
 
401
                                destroy_buf[i]->created > msieve_client->created) {
 
402
                                /* @UNSAFE */
 
403
                                memmove(destroy_buf+i+1, destroy_buf+i,
 
404
                                        sizeof(destroy_buf) -
 
405
                                        (i+1) * sizeof(struct managesieve_client *));
 
406
                                destroy_buf[i] = msieve_client;
 
407
                                break;
 
408
                        }
 
409
                }
 
410
        }
 
411
 
 
412
        /* then kill them */
 
413
        for (i = 0; i < destroy_count; i++) {
 
414
                if (destroy_buf[i] == NULL)
 
415
                        break;
 
416
 
 
417
                client_destroy(destroy_buf[i], "Disconnected: Connection queue full");
 
418
        }
 
419
}
 
420
 
 
421
static void client_send_greeting(struct managesieve_client *client)
 
422
{
 
423
        /* Cork the stream to send the capability data as a single tcp frame
 
424
         *   Some naive clients break if we don't.
 
425
         */
 
426
        o_stream_cork(client->output);
 
427
 
 
428
        /* Send initial capabilities */   
 
429
        client_send_capabilities(client);
 
430
        client_send_ok(client, greeting);
 
431
        client->greeting_sent = TRUE;
 
432
 
 
433
        o_stream_uncork(client->output);
 
434
}
 
435
 
 
436
static void client_idle_disconnect_timeout(struct managesieve_client *client)
 
437
{
 
438
        client_send_bye(client, "Disconnected for inactivity.");
 
439
        client_destroy(client, "Disconnected: Inactivity");
 
440
}
 
441
 
 
442
static void client_auth_waiting_timeout(struct managesieve_client *client)
 
443
{
 
444
        timeout_remove(&client->to_auth_waiting);
 
445
}
 
446
 
 
447
void client_set_auth_waiting(struct managesieve_client *client)
 
448
{
 
449
        i_assert(client->to_auth_waiting == NULL);
 
450
        client->to_auth_waiting =
 
451
                timeout_add(AUTH_WAITING_TIMEOUT_MSECS,
 
452
                        client_auth_waiting_timeout, client);
 
453
}
 
454
 
 
455
struct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
 
456
                             const struct ip_addr *ip)
 
457
{
 
458
        struct managesieve_client *client;
 
459
 
 
460
        i_assert(fd != -1);
 
461
 
 
462
        connection_queue_add(1);
 
463
 
 
464
        /* always use nonblocking I/O */
 
465
        net_set_nonblock(fd, TRUE);
 
466
 
 
467
        client = i_new(struct managesieve_client, 1);
 
468
        client->created = ioloop_time;
 
469
        client->refcount = 1;
 
470
 
 
471
        client->common.local_ip = *local_ip;
 
472
        client->common.ip = *ip;
 
473
        client->common.fd = fd;
 
474
        client->common.tls = ssl;
 
475
        client->common.trusted = client_is_trusted(&client->common);
 
476
        client->common.secured = ssl || client->common.trusted ||
 
477
                net_ip_compare(ip, local_ip);
 
478
 
 
479
        client_open_streams(client, fd);
 
480
        client->io = io_add(fd, IO_READ, client_input, client);
 
481
 
 
482
        client_link(&client->common);
 
483
 
 
484
        main_ref();
 
485
 
 
486
        if (auth_client_is_connected(auth_client))
 
487
                client_send_greeting(client);
 
488
        else
 
489
                client_set_auth_waiting(client);
 
490
        client_set_title(client);
 
491
 
 
492
        client->to_idle_disconnect =
 
493
                timeout_add(CLIENT_LOGIN_IDLE_TIMEOUT_MSECS,
 
494
                        client_idle_disconnect_timeout, client);
 
495
        return &client->common;
 
496
}
 
497
 
 
498
void client_destroy(struct managesieve_client *client, const char *reason)
 
499
{
 
500
        if (client->destroyed)
 
501
                return;
 
502
        client->destroyed = TRUE;
 
503
 
 
504
        if (!client->login_success && reason != NULL) {
 
505
                reason = t_strconcat(reason, " ",
 
506
                        client_get_extra_disconnect_reason(&client->common),
 
507
                        NULL);
 
508
        }
 
509
        if (reason != NULL)
 
510
                client_syslog(&client->common, reason);
 
511
 
 
512
        client_unlink(&client->common);
 
513
 
 
514
        if (client->common.input != NULL)
 
515
                i_stream_close(client->common.input);
 
516
        if (client->output != NULL)
 
517
                o_stream_close(client->output);
 
518
 
 
519
        if (client->common.master_tag != 0) {
 
520
                i_assert(client->common.auth_request == NULL);
 
521
                i_assert(client->common.authenticating);
 
522
                master_request_abort(&client->common);
 
523
        } else if (client->common.auth_request != NULL) {
 
524
                i_assert(client->common.authenticating);
 
525
                sasl_server_auth_abort(&client->common);
 
526
        } else {
 
527
                i_assert(!client->common.authenticating);
 
528
        }
 
529
 
 
530
        if (client->io != NULL)
 
531
                io_remove(&client->io);
 
532
        if (client->to_idle_disconnect != NULL)
 
533
                timeout_remove(&client->to_idle_disconnect);
 
534
        if (client->to_auth_waiting != NULL)
 
535
                timeout_remove(&client->to_auth_waiting);
 
536
        if (client->to_authfail_delay != NULL)
 
537
                timeout_remove(&client->to_authfail_delay);
 
538
 
 
539
        if (client->common.fd != -1) {
 
540
                net_disconnect(client->common.fd);
 
541
                client->common.fd = -1;
 
542
        }
 
543
 
 
544
        if (client->proxy_password != NULL) {
 
545
                safe_memset(client->proxy_password, 0, strlen(client->proxy_password));
 
546
                i_free(client->proxy_password);
 
547
                client->proxy_password = NULL;
 
548
        }
 
549
 
 
550
        i_free_and_null(client->proxy_user);
 
551
        i_free_and_null(client->proxy_master_user);
 
552
 
 
553
        if (client->proxy != NULL)
 
554
                login_proxy_free(&client->proxy);
 
555
 
 
556
        if (client->common.proxy != NULL) {
 
557
                ssl_proxy_free(client->common.proxy);
 
558
                client->common.proxy = NULL;
 
559
        }
 
560
 
 
561
        client_unref(client);
 
562
 
 
563
        main_listen_start();
 
564
        main_unref();
 
565
}
 
566
 
 
567
void client_destroy_success(struct managesieve_client *client, const char *reason)
 
568
{   
 
569
    client->login_success = TRUE;
 
570
    client_destroy(client, reason);
 
571
}
 
572
 
 
573
bool client_read_args(struct managesieve_client *client, unsigned int count,
 
574
        unsigned int flags, struct managesieve_arg **args)
 
575
{
 
576
        int ret;
 
577
        bool fatal;
 
578
        const char *msg;
 
579
 
 
580
        i_assert(count <= INT_MAX);
 
581
 
 
582
        ret = managesieve_parser_read_args(client->parser, count, flags, args);
 
583
        if (ret >= (int)count) {
 
584
                /* all parameters read successfully */
 
585
                return TRUE;
 
586
        } else if (ret == -2) {
 
587
                /* need more data */
 
588
                return FALSE;
 
589
        } else {
 
590
                /* error, or missing arguments */
 
591
                if ( ret < 0 ) {
 
592
                        msg = managesieve_parser_get_error(client->parser, &fatal);
 
593
                        client_send_no(client, msg);
 
594
                } else {
 
595
                        client_send_no(client, "Missing arguments");
 
596
                }
 
597
                return FALSE;
 
598
        }
 
599
}
 
600
 
 
601
void client_destroy_internal_failure(struct managesieve_client *client)
 
602
{
 
603
        client_send_byeresp(client, "TRYLATER", "Internal login failure. "
 
604
                "Refer to server log for more information.");
 
605
        client_destroy(client, "Internal login failure");
 
606
}
 
607
 
 
608
void client_ref(struct managesieve_client *client)
 
609
{
 
610
        client->refcount++;
 
611
}
 
612
 
 
613
bool client_unref(struct managesieve_client *client)
 
614
{
 
615
        i_assert(client->refcount > 0);
 
616
        if (--client->refcount > 0)
 
617
                return TRUE;
 
618
 
 
619
        i_assert(client->destroyed);
 
620
 
 
621
        managesieve_parser_destroy(&client->parser);
 
622
 
 
623
        if (client->common.input != NULL)
 
624
                i_stream_unref(&client->common.input);
 
625
        if (client->output != NULL)
 
626
                o_stream_unref(&client->output);
 
627
 
 
628
        i_free(client->common.virtual_user);
 
629
        i_free(client->common.auth_mech_name);
 
630
        i_free(client);
 
631
 
 
632
        return FALSE;
 
633
}
 
634
 
 
635
void client_send_line(struct managesieve_client *client, const char *line)
 
636
{
 
637
        struct const_iovec iov[2];
 
638
        ssize_t ret;
 
639
 
 
640
        iov[0].iov_base = line;
 
641
        iov[0].iov_len = strlen(line);
 
642
        iov[1].iov_base = "\r\n";
 
643
        iov[1].iov_len = 2;
 
644
 
 
645
        ret = o_stream_sendv(client->output, iov, 2);
 
646
        if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) {
 
647
                /* either disconnection or buffer full. in either case we
 
648
                   want this connection destroyed. however destroying it here
 
649
                   might break things if client is still tried to be accessed
 
650
                   without being referenced.. */
 
651
                i_stream_close(client->common.input);
 
652
        }
 
653
}
 
654
 
 
655
void _client_send_response(struct managesieve_client *client, 
 
656
        const char *oknobye, const char *resp_code, const char *msg)
 
657
{
 
658
        string_t *str;
 
659
 
 
660
        str = t_str_new(128);
 
661
        str_append(str, oknobye);
 
662
 
 
663
        if ( resp_code != NULL )
 
664
        {
 
665
                str_append(str, " (");
 
666
                str_append(str, resp_code);
 
667
                str_append_c(str, ')');
 
668
        }
 
669
 
 
670
        if ( msg != NULL )      
 
671
        {
 
672
                str_append_c(str, ' ');
 
673
                managesieve_quote_append_string(str, msg, TRUE);
 
674
        }
 
675
 
 
676
        client_send_line(client, str_c(str));
 
677
}
 
678
 
 
679
void clients_notify_auth_connected(void)
 
680
{
 
681
        struct client *client;
 
682
 
 
683
        for (client = clients; client != NULL; client = client->next) {
 
684
                struct managesieve_client *msieve_client = 
 
685
                        (struct managesieve_client *)client;
 
686
 
 
687
                if (msieve_client->to_auth_waiting != NULL)
 
688
                        timeout_remove(&msieve_client->to_auth_waiting);
 
689
                if (!msieve_client->greeting_sent)
 
690
                        client_send_greeting(msieve_client);
 
691
                if (msieve_client->input_blocked) {
 
692
                        msieve_client->input_blocked = FALSE;
 
693
                        client_input(msieve_client);
 
694
                }
 
695
        }
 
696
}
 
697
 
 
698
void clients_destroy_all(void)
 
699
{
 
700
        struct client *client, *next;
 
701
 
 
702
        for (client = clients; client != NULL; client = next) {
 
703
                struct managesieve_client *msieve_client = 
 
704
                        (struct managesieve_client *) client;
 
705
 
 
706
                next = client->next;
 
707
                client_destroy(msieve_client, "Disconnected: Shutting down");
 
708
        }
 
709
}
 
710
 
 
711
void clients_init(void)
 
712
{
 
713
        const char *str;
 
714
 
 
715
        /* Specific MANAGESIEVE settings */
 
716
        str = getenv("MANAGESIEVE_IMPLEMENTATION_STRING");
 
717
        managesieve_implementation_string = str != NULL ?
 
718
        str : DEFAULT_MANAGESIEVE_IMPLEMENTATION_STRING;
 
719
 
 
720
        /* Parse CAPABILITY_STRING */   
 
721
        managesieve_capabilities_init(capability_string);
 
722
}
 
723
 
 
724
void clients_deinit(void)
 
725
{
 
726
        clients_destroy_all();
 
727
 
 
728
        /* Free allocated capability strings */
 
729
        managesieve_capabilities_deinit();
 
730
}
 
731