~ubuntu-branches/ubuntu/wily/dovecot/wily

« back to all changes in this revision

Viewing changes to src/lib-imap-urlauth/imap-urlauth-connection.c

  • Committer: Package Import Robot
  • Author(s): Jaldhar H. Vyas
  • Date: 2013-09-09 00:57:32 UTC
  • mfrom: (1.13.11)
  • mto: (4.8.5 experimental) (1.16.1)
  • mto: This revision was merged to the branch mainline in revision 97.
  • Revision ID: package-import@ubuntu.com-20130909005732-dn1eell8srqbhh0e
Tags: upstream-2.2.5
ImportĀ upstreamĀ versionĀ 2.2.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "lib.h"
 
4
#include "llist.h"
 
5
#include "str.h"
 
6
#include "str-sanitize.h"
 
7
#include "strescape.h"
 
8
#include "ioloop.h"
 
9
#include "safe-mkstemp.h"
 
10
#include "hostpid.h"
 
11
#include "net.h"
 
12
#include "istream.h"
 
13
#include "ostream.h"
 
14
#include "write-full.h"
 
15
#include "array.h"
 
16
#include "aqueue.h"
 
17
#include "mail-user.h"
 
18
#include "imap-urlauth-fetch.h"
 
19
 
 
20
#include "imap-urlauth-connection.h"
 
21
 
 
22
enum imap_urlauth_state {
 
23
        IMAP_URLAUTH_STATE_DISCONNECTED = 0,
 
24
        IMAP_URLAUTH_STATE_AUTHENTICATING,
 
25
        IMAP_URLAUTH_STATE_AUTHENTICATED,
 
26
        IMAP_URLAUTH_STATE_SELECTING_TARGET,
 
27
        IMAP_URLAUTH_STATE_UNSELECTING_TARGET,
 
28
        IMAP_URLAUTH_STATE_READY,
 
29
        IMAP_URLAUTH_STATE_REQUEST_PENDING,
 
30
        IMAP_URLAUTH_STATE_REQUEST_WAIT,
 
31
};
 
32
 
 
33
struct imap_urlauth_request {
 
34
        struct imap_urlauth_target *target;
 
35
        struct imap_urlauth_request *prev, *next;
 
36
        
 
37
        char *url;
 
38
        enum imap_urlauth_fetch_flags flags;
 
39
 
 
40
        char *bodypartstruct;
 
41
 
 
42
        imap_urlauth_request_callback_t *callback;
 
43
        void *context;
 
44
 
 
45
        unsigned int binary_has_nuls;
 
46
};
 
47
 
 
48
struct imap_urlauth_target {
 
49
        struct imap_urlauth_target *prev, *next;
 
50
 
 
51
        char *userid;
 
52
 
 
53
        struct imap_urlauth_request *requests_head, *requests_tail;
 
54
};
 
55
 
 
56
struct imap_urlauth_connection {
 
57
        int refcount;
 
58
 
 
59
        char *path, *session_id;
 
60
        struct mail_user *user;
 
61
 
 
62
        int fd;
 
63
        struct istream *input;
 
64
        struct ostream *output;
 
65
        struct io *io;
 
66
 
 
67
        struct timeout *to_reconnect, *to_idle, *to_response;
 
68
        time_t last_reconnect;
 
69
        unsigned int reconnect_attempts;
 
70
        unsigned int idle_timeout_msecs;
 
71
 
 
72
        char *literal_temp_path;
 
73
        int literal_fd;
 
74
        buffer_t *literal_buf;
 
75
        uoff_t literal_size, literal_bytes_left;
 
76
 
 
77
        enum imap_urlauth_state state;
 
78
 
 
79
        /* userid => target struct */
 
80
        struct imap_urlauth_target *targets_head, *targets_tail;
 
81
 
 
82
        unsigned int reading_literal:1;
 
83
};
 
84
 
 
85
#define IMAP_URLAUTH_RECONNECT_MIN_SECS 2
 
86
#define IMAP_URLAUTH_RECONNECT_MAX_ATTEMPTS 3
 
87
 
 
88
#define IMAP_URLAUTH_RESPONSE_TIMEOUT_MSECS 2*60*1000
 
89
 
 
90
#define IMAP_URLAUTH_HANDSHAKE "VERSION\timap-urlauth\t1\t0\n"
 
91
 
 
92
#define IMAP_URLAUTH_MAX_INLINE_LITERAL_SIZE (1024*32)
 
93
 
 
94
static void imap_urlauth_connection_disconnect
 
95
        (struct imap_urlauth_connection *conn, const char *reason);
 
96
static void imap_urlauth_connection_abort
 
97
        (struct imap_urlauth_connection *conn, const char *reason);
 
98
static void imap_urlauth_connection_reconnect
 
99
        (struct imap_urlauth_connection *conn);
 
100
static void imap_urlauth_connection_idle_disconnect
 
101
        (struct imap_urlauth_connection *conn);
 
102
static void imap_urlauth_connection_timeout_abort
 
103
        (struct imap_urlauth_connection *conn);
 
104
static void imap_urlauth_connection_fail
 
105
        (struct imap_urlauth_connection *conn);
 
106
 
 
107
struct imap_urlauth_connection *
 
108
imap_urlauth_connection_init(const char *path, struct mail_user *user,
 
109
                             const char *session_id,
 
110
                             unsigned int idle_timeout_msecs)
 
111
{
 
112
        struct imap_urlauth_connection *conn;
 
113
 
 
114
        conn = i_new(struct imap_urlauth_connection, 1);
 
115
        conn->refcount = 1;
 
116
        conn->path = i_strdup(path);
 
117
        if (session_id != NULL)
 
118
                conn->session_id = i_strdup(session_id);
 
119
        conn->user = user;
 
120
        conn->fd = -1;
 
121
        conn->literal_fd = -1;
 
122
        conn->idle_timeout_msecs = idle_timeout_msecs;
 
123
        return conn;
 
124
}
 
125
 
 
126
void imap_urlauth_connection_deinit(struct imap_urlauth_connection **_conn)
 
127
{
 
128
        struct imap_urlauth_connection *conn = *_conn;
 
129
 
 
130
        *_conn = NULL;
 
131
 
 
132
        imap_urlauth_connection_abort(conn, NULL);
 
133
 
 
134
        i_free(conn->path);
 
135
        if (conn->session_id != NULL)
 
136
                i_free(conn->session_id);
 
137
 
 
138
        i_assert(conn->to_idle == NULL);
 
139
        i_assert(conn->to_reconnect == NULL);
 
140
        i_assert(conn->to_response == NULL);
 
141
 
 
142
        i_free(conn);
 
143
}
 
144
 
 
145
static void
 
146
imap_urlauth_stop_response_timeout(struct imap_urlauth_connection *conn)
 
147
{
 
148
        if (conn->to_response != NULL)
 
149
                timeout_remove(&conn->to_response);
 
150
}
 
151
 
 
152
static void
 
153
imap_urlauth_start_response_timeout(struct imap_urlauth_connection *conn)
 
154
{
 
155
        imap_urlauth_stop_response_timeout(conn);
 
156
        conn->to_response = timeout_add(IMAP_URLAUTH_RESPONSE_TIMEOUT_MSECS,
 
157
                imap_urlauth_connection_timeout_abort, conn);
 
158
}
 
159
 
 
160
static struct imap_urlauth_target *
 
161
imap_urlauth_connection_get_target(struct imap_urlauth_connection *conn,
 
162
                                   const char *target_user)
 
163
{
 
164
        struct imap_urlauth_target *target = conn->targets_head;
 
165
 
 
166
        while (target != NULL) {
 
167
                if (strcmp(target->userid, target_user) == 0)
 
168
                        return target;
 
169
                target = target->next;
 
170
        }
 
171
 
 
172
        target = i_new(struct imap_urlauth_target, 1);
 
173
        target->userid = i_strdup(target_user);
 
174
        DLLIST2_APPEND(&conn->targets_head, &conn->targets_tail, target);
 
175
        return target;
 
176
}
 
177
 
 
178
static void
 
179
imap_urlauth_target_free(struct imap_urlauth_connection *conn,
 
180
                         struct imap_urlauth_target *target)
 
181
{
 
182
        DLLIST2_REMOVE(&conn->targets_head, &conn->targets_tail, target);
 
183
        i_free(target->userid);
 
184
        i_free(target);
 
185
}
 
186
 
 
187
static void
 
188
imap_urlauth_connection_select_target(struct imap_urlauth_connection *conn)
 
189
{
 
190
        struct imap_urlauth_target *target = conn->targets_head;
 
191
        const char *cmd;
 
192
 
 
193
        if (target == NULL || conn->state != IMAP_URLAUTH_STATE_AUTHENTICATED)
 
194
                return;
 
195
 
 
196
        if (conn->user->mail_debug)
 
197
                i_debug("imap-urlauth: Selecting target user `%s'", target->userid);
 
198
 
 
199
        conn->state = IMAP_URLAUTH_STATE_SELECTING_TARGET;
 
200
        cmd = t_strdup_printf("USER\t%s\n", str_tabescape(target->userid));
 
201
        if (o_stream_send_str(conn->output, cmd) < 0) {
 
202
                i_warning("Error sending USER request to imap-urlauth server: %m");
 
203
                imap_urlauth_connection_fail(conn);
 
204
        }
 
205
 
 
206
        imap_urlauth_start_response_timeout(conn);
 
207
}
 
208
 
 
209
static void
 
210
imap_urlauth_connection_send_request(struct imap_urlauth_connection *conn)
 
211
{
 
212
        struct imap_urlauth_request *urlreq;
 
213
        string_t *cmd;
 
214
 
 
215
        if (conn->targets_head == NULL ||
 
216
            (conn->targets_head->requests_head == NULL &&
 
217
             conn->targets_head->next == NULL &&
 
218
             conn->state == IMAP_URLAUTH_STATE_READY)) {
 
219
                if (conn->user->mail_debug)
 
220
                        i_debug("imap-urlauth: No more requests pending; scheduling disconnect");
 
221
                if (conn->to_idle != NULL)
 
222
                        timeout_remove(&conn->to_idle);
 
223
                if (conn->idle_timeout_msecs > 0) {
 
224
                        conn->to_idle = timeout_add(conn->idle_timeout_msecs,
 
225
                                imap_urlauth_connection_idle_disconnect, conn);
 
226
                }
 
227
                return;
 
228
        }
 
229
 
 
230
        if (conn->state == IMAP_URLAUTH_STATE_AUTHENTICATED) {
 
231
                imap_urlauth_connection_select_target(conn);
 
232
                return;
 
233
        }
 
234
 
 
235
        if (conn->state != IMAP_URLAUTH_STATE_READY)
 
236
                return;
 
237
 
 
238
        urlreq = conn->targets_head->requests_head;
 
239
        if (urlreq == NULL) {
 
240
                if (conn->targets_head->next == NULL)
 
241
                        return;
 
242
                
 
243
                conn->state = IMAP_URLAUTH_STATE_UNSELECTING_TARGET;
 
244
                imap_urlauth_target_free(conn, conn->targets_head);
 
245
 
 
246
                if (o_stream_send_str(conn->output, "END\n") < 0) {
 
247
                        i_warning("Error sending END request to imap-urlauth server: %m");
 
248
                        imap_urlauth_connection_fail(conn);
 
249
                }
 
250
                imap_urlauth_start_response_timeout(conn);
 
251
                return;
 
252
        }       
 
253
 
 
254
        if (conn->user->mail_debug)
 
255
                i_debug("imap-urlauth: Fetching URL `%s'", urlreq->url);
 
256
 
 
257
        cmd = t_str_new(128);
 
258
        str_append(cmd, "URL\t");
 
259
        str_append_tabescaped(cmd, urlreq->url);
 
260
        if ((urlreq->flags & IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE) != 0)
 
261
                str_append(cmd, "\tbpstruct");
 
262
        if ((urlreq->flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0)
 
263
                str_append(cmd, "\tbinary");
 
264
        else if ((urlreq->flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0)
 
265
                str_append(cmd, "\tbody");
 
266
        str_append_c(cmd, '\n');
 
267
 
 
268
        conn->state = IMAP_URLAUTH_STATE_REQUEST_PENDING;
 
269
        if (o_stream_send(conn->output, str_data(cmd), str_len(cmd)) < 0) {
 
270
                i_warning("Error sending URL request to imap-urlauth server: %m");
 
271
                imap_urlauth_connection_fail(conn);
 
272
        }
 
273
 
 
274
        imap_urlauth_start_response_timeout(conn);
 
275
}
 
276
 
 
277
struct imap_urlauth_request *
 
278
imap_urlauth_request_new(struct imap_urlauth_connection *conn,
 
279
                         const char *target_user, const char *url,
 
280
                         enum imap_urlauth_fetch_flags flags,
 
281
                         imap_urlauth_request_callback_t *callback,
 
282
                         void *context)
 
283
{
 
284
        struct imap_urlauth_request *urlreq;
 
285
        struct imap_urlauth_target *target;
 
286
 
 
287
        target = imap_urlauth_connection_get_target(conn, target_user);
 
288
        
 
289
        urlreq = i_new(struct imap_urlauth_request, 1);
 
290
        urlreq->url = i_strdup(url);
 
291
        urlreq->flags = flags;
 
292
        urlreq->target = target;
 
293
        urlreq->callback = callback;
 
294
        urlreq->context = context;
 
295
 
 
296
        DLLIST2_APPEND(&target->requests_head, &target->requests_tail, urlreq);
 
297
 
 
298
        if (conn->to_idle != NULL)
 
299
                timeout_remove(&conn->to_idle);
 
300
        
 
301
        if (conn->user->mail_debug) {
 
302
                i_debug("imap-urlauth: Added request for URL `%s' from user `%s'",
 
303
                        url, target_user);
 
304
        }
 
305
 
 
306
        imap_urlauth_connection_send_request(conn);
 
307
        return urlreq;
 
308
}
 
309
 
 
310
static void imap_urlauth_request_free(struct imap_urlauth_request *urlreq)
 
311
{
 
312
        struct imap_urlauth_target *target = urlreq->target;
 
313
 
 
314
        DLLIST2_REMOVE(&target->requests_head, &target->requests_tail, urlreq);
 
315
        i_free(urlreq->url);
 
316
        i_free(urlreq->bodypartstruct);
 
317
        i_free(urlreq);
 
318
}
 
319
 
 
320
static void imap_urlauth_request_drop(struct imap_urlauth_connection *conn,
 
321
                                struct imap_urlauth_request *urlreq)
 
322
{
 
323
        if ((conn->state == IMAP_URLAUTH_STATE_REQUEST_PENDING ||
 
324
                        conn->state == IMAP_URLAUTH_STATE_REQUEST_WAIT) &&
 
325
            conn->targets_head != NULL &&
 
326
            conn->targets_head->requests_head == urlreq) {
 
327
                /* cannot just drop pending request without breaking
 
328
                   protocol state */
 
329
                return;
 
330
        }
 
331
        imap_urlauth_request_free(urlreq);
 
332
 
 
333
}
 
334
 
 
335
void imap_urlauth_request_abort(struct imap_urlauth_connection *conn,
 
336
                                struct imap_urlauth_request *urlreq)
 
337
{
 
338
        imap_urlauth_request_callback_t *callback;
 
339
 
 
340
        callback = urlreq->callback;
 
341
        urlreq->callback = NULL;
 
342
        if (callback != NULL) {
 
343
                T_BEGIN {
 
344
                        callback(NULL, urlreq->context);
 
345
                } T_END;
 
346
        }
 
347
 
 
348
        imap_urlauth_request_drop(conn, urlreq);
 
349
}
 
350
 
 
351
static void
 
352
imap_urlauth_request_fail(struct imap_urlauth_connection *conn,
 
353
                          struct imap_urlauth_request *urlreq,
 
354
                          const char *error)
 
355
{
 
356
        struct imap_urlauth_fetch_reply reply;
 
357
        imap_urlauth_request_callback_t *callback;
 
358
        int ret = 1;
 
359
 
 
360
        callback = urlreq->callback;
 
361
        urlreq->callback = NULL;
 
362
        if (callback != NULL) {
 
363
                memset(&reply, 0, sizeof(reply));
 
364
                reply.url = urlreq->url;
 
365
                reply.flags = urlreq->flags;
 
366
                reply.succeeded = FALSE;
 
367
                reply.error = error;
 
368
 
 
369
                T_BEGIN {
 
370
                        ret = callback(&reply, urlreq->context);
 
371
                } T_END;
 
372
        }
 
373
 
 
374
        imap_urlauth_request_drop(conn, urlreq);
 
375
 
 
376
        if (ret < 0) {
 
377
                /* Drop any related requests upon error */
 
378
                imap_urlauth_request_abort_by_context(conn, urlreq->context);
 
379
        }
 
380
 
 
381
        if (ret != 0)
 
382
                imap_urlauth_connection_continue(conn);
 
383
}
 
384
 
 
385
static void
 
386
imap_urlauth_target_abort(struct imap_urlauth_connection *conn,
 
387
                          struct imap_urlauth_target *target)
 
388
{
 
389
        struct imap_urlauth_request *urlreq, *next;
 
390
 
 
391
        urlreq = target->requests_head;
 
392
        while (urlreq != NULL) {
 
393
                next = urlreq->next;
 
394
                imap_urlauth_request_abort(conn, urlreq);
 
395
                urlreq = next;
 
396
        }
 
397
 
 
398
        imap_urlauth_target_free(conn, target);
 
399
}
 
400
 
 
401
static void
 
402
imap_urlauth_target_fail(struct imap_urlauth_connection *conn,
 
403
                         struct imap_urlauth_target *target, const char *error)
 
404
{
 
405
        struct imap_urlauth_request *urlreq, *next;
 
406
 
 
407
        urlreq = target->requests_head;
 
408
        while (urlreq != NULL) {
 
409
                next = urlreq->next;
 
410
                imap_urlauth_request_fail(conn, urlreq, error);
 
411
                urlreq = next;
 
412
        }
 
413
 
 
414
        imap_urlauth_target_free(conn, target);
 
415
}
 
416
 
 
417
static void
 
418
imap_urlauth_target_abort_by_context(struct imap_urlauth_connection *conn,
 
419
                                     struct imap_urlauth_target *target,
 
420
                                     void *context)
 
421
{
 
422
        struct imap_urlauth_request *urlreq, *next;
 
423
 
 
424
        /* abort all matching requests */
 
425
        urlreq = target->requests_head;
 
426
        while (urlreq != NULL) {
 
427
                next = urlreq->next;
 
428
                if (urlreq->context == context)
 
429
                        imap_urlauth_request_abort(conn, urlreq);
 
430
                urlreq = next;
 
431
        }
 
432
 
 
433
        if (target->requests_head == NULL)
 
434
                imap_urlauth_target_free(conn, target);
 
435
}
 
436
 
 
437
static void
 
438
imap_urlauth_connection_abort(struct imap_urlauth_connection *conn,
 
439
                              const char *reason)
 
440
{
 
441
        struct imap_urlauth_target *target, *next;
 
442
        
 
443
        if (reason == NULL)
 
444
                reason = "Aborting due to error";
 
445
        imap_urlauth_connection_disconnect(conn, reason);
 
446
 
 
447
        /* abort all requests */
 
448
        target = conn->targets_head;
 
449
        while (target != NULL) {
 
450
                next = target->next;
 
451
                imap_urlauth_target_abort(conn, target);
 
452
                target = next;
 
453
        }
 
454
}
 
455
 
 
456
void imap_urlauth_request_abort_by_context(struct imap_urlauth_connection *conn,
 
457
                                           void *context)
 
458
{
 
459
        struct imap_urlauth_target *target, *next;
 
460
 
 
461
        /* abort all matching requests */
 
462
        target = conn->targets_head;
 
463
        while (target != NULL) {
 
464
                next = target->next;
 
465
                imap_urlauth_target_abort_by_context(conn, target, context);
 
466
                target = next;
 
467
        }
 
468
}
 
469
 
 
470
static void imap_urlauth_connection_fail(struct imap_urlauth_connection *conn)
 
471
{
 
472
        if (conn->reconnect_attempts > IMAP_URLAUTH_RECONNECT_MAX_ATTEMPTS) {
 
473
                imap_urlauth_connection_abort(conn,
 
474
                        "Connection failed and connection attempts exhausted");
 
475
        } else {
 
476
                imap_urlauth_connection_reconnect(conn);
 
477
        }
 
478
}
 
479
 
 
480
static int
 
481
imap_urlauth_connection_create_temp_fd(struct imap_urlauth_connection *conn,
 
482
                                       const char **path_r)
 
483
{
 
484
        string_t *path;
 
485
        int fd;
 
486
 
 
487
        path = t_str_new(128);
 
488
        mail_user_set_get_temp_prefix(path, conn->user->set);
 
489
        fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
 
490
        if (fd == -1) {
 
491
                i_error("safe_mkstemp(%s) failed: %m", str_c(path));
 
492
                return -1;
 
493
        }
 
494
 
 
495
        /* we just want the fd, unlink it */
 
496
        if (unlink(str_c(path)) < 0) {
 
497
                /* shouldn't happen.. */
 
498
                i_error("unlink(%s) failed: %m", str_c(path));
 
499
                i_close_fd(&fd);
 
500
                return -1;
 
501
        }
 
502
 
 
503
        *path_r = str_c(path);
 
504
        return fd;
 
505
}
 
506
 
 
507
static int
 
508
imap_urlauth_connection_read_literal_init(struct imap_urlauth_connection *conn,
 
509
                                          uoff_t size)
 
510
{
 
511
        const char *path;
 
512
 
 
513
        i_assert(conn->literal_fd == -1 && conn->literal_buf == NULL);
 
514
 
 
515
        if (size <= IMAP_URLAUTH_MAX_INLINE_LITERAL_SIZE) {
 
516
                /* read the literal directly */
 
517
                if (size > 0) {
 
518
                        conn->literal_buf =
 
519
                                buffer_create_dynamic(default_pool, size);
 
520
                }
 
521
        } else {
 
522
                /* read it into a file */
 
523
                conn->literal_fd =
 
524
                        imap_urlauth_connection_create_temp_fd(conn, &path);
 
525
                if (conn->literal_fd == -1)
 
526
                        return -1;
 
527
                conn->literal_temp_path = i_strdup(path);
 
528
        }
 
529
 
 
530
        conn->literal_size = size;
 
531
        conn->literal_bytes_left = size;
 
532
        conn->reading_literal = TRUE;
 
533
        return 1;
 
534
}
 
535
 
 
536
void imap_urlauth_connection_continue(struct imap_urlauth_connection *conn)
 
537
{
 
538
        i_assert(conn->targets_head != NULL);
 
539
        i_assert(conn->targets_head->requests_head != NULL);
 
540
 
 
541
        if (conn->state != IMAP_URLAUTH_STATE_REQUEST_WAIT)
 
542
                return;
 
543
 
 
544
        conn->state = IMAP_URLAUTH_STATE_READY;
 
545
        imap_urlauth_request_free(conn->targets_head->requests_head);
 
546
 
 
547
        imap_urlauth_connection_send_request(conn);
 
548
}
 
549
 
 
550
static int
 
551
imap_urlauth_connection_read_literal_data(struct imap_urlauth_connection *conn)
 
552
{
 
553
        const unsigned char *data;
 
554
        size_t size;
 
555
 
 
556
        /* read data */
 
557
        data = i_stream_get_data(conn->input, &size);
 
558
        if (size > conn->literal_bytes_left)
 
559
                size = conn->literal_bytes_left;
 
560
 
 
561
        /* write to buffer or file */
 
562
        if (size > 0) {
 
563
                if (conn->literal_fd >= 0) {
 
564
                        if (write_full(conn->literal_fd, data, size) < 0) {
 
565
                                i_error("imap-urlauth: write(%s) failed: %m",
 
566
                                        conn->literal_temp_path);
 
567
                                return -1;
 
568
                        }
 
569
                } else {
 
570
                        i_assert(conn->literal_buf != NULL);
 
571
                        buffer_append(conn->literal_buf, data, size);
 
572
                }
 
573
                i_stream_skip(conn->input, size);
 
574
                conn->literal_bytes_left -= size;
 
575
        }
 
576
 
 
577
        /* exit if not finished */
 
578
        if (conn->literal_bytes_left > 0)
 
579
                return 0;
 
580
 
 
581
        /* read LF guard */
 
582
        data = i_stream_get_data(conn->input, &size);
 
583
        if (size < 1)
 
584
                return 0;
 
585
 
 
586
        /* check LF guard */
 
587
        if (data[0] != '\n') {
 
588
                i_error("imap-urlauth: no LF at end of literal (found 0x%x)",
 
589
                        data[0]);
 
590
                return -1;
 
591
        }
 
592
        i_stream_skip(conn->input, 1);
 
593
        return 1;
 
594
}
 
595
 
 
596
static void literal_stream_destroy(buffer_t *buffer)
 
597
{
 
598
        buffer_free(&buffer);
 
599
}
 
600
 
 
601
static int
 
602
imap_urlauth_fetch_reply_set_literal_stream(struct imap_urlauth_connection *conn,
 
603
                                            struct imap_urlauth_fetch_reply *reply)
 
604
{
 
605
        const unsigned char *data;
 
606
        size_t size;
 
607
        uoff_t fd_size;
 
608
 
 
609
        if (conn->literal_fd != -1) {
 
610
                reply->input = i_stream_create_fd(conn->literal_fd,
 
611
                                                  (size_t)-1, TRUE);
 
612
                if (i_stream_get_size(reply->input, TRUE, &fd_size) < 1 ||
 
613
                    fd_size != conn->literal_size) {
 
614
                        i_stream_unref(&reply->input);
 
615
                        i_error("imap-urlauth: Failed to obtain proper size from literal stream");
 
616
                        imap_urlauth_connection_abort(conn,
 
617
                                "Failed during literal transfer");
 
618
                        return -1;
 
619
                }
 
620
        } else {
 
621
                data = buffer_get_data(conn->literal_buf, &size);
 
622
                i_assert(size == conn->literal_size);
 
623
                reply->input = i_stream_create_from_data(data, size);
 
624
                i_stream_add_destroy_callback(reply->input,
 
625
                                              literal_stream_destroy,
 
626
                                              conn->literal_buf);
 
627
        }
 
628
        reply->size = conn->literal_size;
 
629
        return 0;
 
630
}
 
631
 
 
632
static int
 
633
imap_urlauth_connection_read_literal(struct imap_urlauth_connection *conn)
 
634
{
 
635
        struct imap_urlauth_request *urlreq = conn->targets_head->requests_head;
 
636
        struct imap_urlauth_fetch_reply reply;
 
637
        imap_urlauth_request_callback_t *callback;
 
638
        int ret;
 
639
 
 
640
        i_assert(conn->reading_literal);
 
641
        i_assert(urlreq != NULL);
 
642
 
 
643
        if (conn->literal_size > 0) {
 
644
                ret = imap_urlauth_connection_read_literal_data(conn);
 
645
                if (ret <= 0)
 
646
                        return ret;
 
647
        }
 
648
        i_assert(conn->literal_bytes_left == 0);
 
649
 
 
650
        /* reply */
 
651
        memset(&reply, 0, sizeof(reply));
 
652
        reply.url = urlreq->url;
 
653
        reply.flags = urlreq->flags;
 
654
        reply.bodypartstruct = urlreq->bodypartstruct;
 
655
        reply.binary_has_nuls = urlreq->binary_has_nuls;
 
656
 
 
657
        if (conn->literal_size > 0) {
 
658
                if (imap_urlauth_fetch_reply_set_literal_stream(conn, &reply) < 0)
 
659
                        return -1;
 
660
        }
 
661
        reply.succeeded = TRUE;
 
662
 
 
663
        ret = 1;
 
664
        callback = urlreq->callback;
 
665
        urlreq->callback = NULL;
 
666
        if (callback != NULL) T_BEGIN {
 
667
                ret = callback(&reply, urlreq->context);
 
668
        } T_END;
 
669
 
 
670
        if (reply.input != NULL)
 
671
                i_stream_unref(&reply.input);
 
672
 
 
673
        if (ret < 0) {
 
674
                /* Drop any related requests upon error */
 
675
                imap_urlauth_request_abort_by_context(conn, urlreq->context);
 
676
        }
 
677
 
 
678
        conn->state = IMAP_URLAUTH_STATE_REQUEST_WAIT;
 
679
        if (ret != 0)
 
680
                imap_urlauth_connection_continue(conn);
 
681
 
 
682
        /* finished */
 
683
        i_free_and_null(conn->literal_temp_path);
 
684
        conn->literal_fd = -1;
 
685
        conn->literal_buf = NULL;
 
686
        conn->reading_literal = FALSE;
 
687
        return 1;
 
688
}
 
689
 
 
690
static int imap_urlauth_input_pending(struct imap_urlauth_connection *conn)
 
691
{
 
692
        struct imap_urlauth_request *urlreq;
 
693
        const char *response, *const *args, *bpstruct = NULL;
 
694
        uoff_t literal_size;
 
695
 
 
696
        i_assert(conn->targets_head != NULL);
 
697
        i_assert(conn->targets_head->requests_head != NULL);
 
698
        urlreq = conn->targets_head->requests_head;
 
699
 
 
700
        if (conn->reading_literal) {
 
701
                /* Read pending literal; may callback */
 
702
                return imap_urlauth_connection_read_literal(conn);
 
703
        }
 
704
 
 
705
        /* "OK"[<metadata-items>]"\t"<literal-size>"\n" or
 
706
           "NO"["\terror="<error>]"\n" */
 
707
        if ((response = i_stream_next_line(conn->input)) == NULL)
 
708
                return 0;
 
709
        imap_urlauth_stop_response_timeout(conn);
 
710
 
 
711
        args = t_strsplit_tabescaped(response);
 
712
        if (args[0] == NULL) {
 
713
                i_error("imap-urlauth: Empty URL response: %s",
 
714
                        str_sanitize(response, 80));
 
715
                return -1;
 
716
        }
 
717
 
 
718
        if (strcmp(args[0], "OK") != 0 || args[1] == NULL) {
 
719
                if (strcmp(args[0], "NO") == 0) {
 
720
                        const char *param = args[1], *error = NULL;
 
721
 
 
722
                        if (param != NULL &&
 
723
                            strncasecmp(param, "error=", 6) == 0 &&
 
724
                            param[6] != '\0') {
 
725
                                error = param+6;
 
726
                        }
 
727
                        conn->state = IMAP_URLAUTH_STATE_REQUEST_WAIT;
 
728
                        imap_urlauth_request_fail(conn,
 
729
                                conn->targets_head->requests_head, error);
 
730
                        return 1;
 
731
                }
 
732
                i_error("imap-urlauth: Unexpected URL response: %s",
 
733
                        str_sanitize(response, 80));
 
734
                return -1;
 
735
        }
 
736
 
 
737
        /* read metadata */
 
738
        args++;
 
739
        for (; args[1] != NULL; args++) {
 
740
                const char *param = args[0];
 
741
 
 
742
                if (strcasecmp(param, "hasnuls") == 0) {
 
743
                        urlreq->binary_has_nuls = TRUE;
 
744
                } else if (strncasecmp(param, "bpstruct=", 9) == 0 &&
 
745
                           param[9] != '\0') {
 
746
                        bpstruct = param+9;
 
747
                }
 
748
        }
 
749
 
 
750
        /* read literal size */
 
751
        if (str_to_uoff(args[0], &literal_size) < 0) {
 
752
                i_error("imap-urlauth: "
 
753
                        "Overflowing unsigned integer value for literal size: %s",
 
754
                        args[1]);
 
755
                return -1;
 
756
        }
 
757
 
 
758
        /* read literal */
 
759
        if (imap_urlauth_connection_read_literal_init(conn, literal_size) < 0)
 
760
                return -1;
 
761
 
 
762
        urlreq->bodypartstruct = i_strdup(bpstruct);
 
763
        return imap_urlauth_connection_read_literal(conn);
 
764
}
 
765
 
 
766
static int imap_urlauth_input_next(struct imap_urlauth_connection *conn)
 
767
{
 
768
        const char *response;
 
769
        int ret;
 
770
 
 
771
        switch (conn->state) {
 
772
        case IMAP_URLAUTH_STATE_AUTHENTICATING:
 
773
        case IMAP_URLAUTH_STATE_UNSELECTING_TARGET:
 
774
                if ((response = i_stream_next_line(conn->input)) == NULL)
 
775
                        return 0;
 
776
                imap_urlauth_stop_response_timeout(conn);
 
777
 
 
778
                if (strcasecmp(response, "OK") != 0) {
 
779
                        if (conn->state == IMAP_URLAUTH_STATE_AUTHENTICATING)
 
780
                                i_error("imap-urlauth: Failed to authenticate to service: "
 
781
                                        "Got unexpected response: %s", str_sanitize(response, 80));
 
782
                        else
 
783
                                i_error("imap-urlauth: Failed to unselect target user: "
 
784
                                        "Got unexpected response: %s", str_sanitize(response, 80));
 
785
                        imap_urlauth_connection_abort(conn, NULL);
 
786
                        return -1;
 
787
                }
 
788
 
 
789
                if (conn->user->mail_debug) {
 
790
                        if (conn->state == IMAP_URLAUTH_STATE_AUTHENTICATING)
 
791
                                i_debug("imap-urlauth: Successfully authenticated to service");
 
792
                        else
 
793
                                i_debug("imap-urlauth: Successfully unselected target user");
 
794
                }
 
795
 
 
796
                conn->state = IMAP_URLAUTH_STATE_AUTHENTICATED;
 
797
                imap_urlauth_connection_select_target(conn);
 
798
                return 0;
 
799
        case IMAP_URLAUTH_STATE_SELECTING_TARGET:
 
800
                if ((response = i_stream_next_line(conn->input)) == NULL)
 
801
                        return 0;
 
802
                imap_urlauth_stop_response_timeout(conn);
 
803
 
 
804
                i_assert(conn->targets_head != NULL);
 
805
 
 
806
                if (strcasecmp(response, "NO") == 0) {
 
807
                        if (conn->user->mail_debug) {
 
808
                                i_debug("imap-urlauth: Failed to select target user %s",
 
809
                                        conn->targets_head->userid);
 
810
                        }
 
811
                        imap_urlauth_target_fail(conn, conn->targets_head, NULL);
 
812
 
 
813
                        conn->state = IMAP_URLAUTH_STATE_AUTHENTICATED;
 
814
                        imap_urlauth_connection_select_target(conn);
 
815
                        return 0;
 
816
                }
 
817
                if (strcasecmp(response, "OK") != 0) {
 
818
                        i_error("imap-urlauth: Failed to select target user %s: "
 
819
                                "Got unexpected response: %s", conn->targets_head->userid,
 
820
                                str_sanitize(response, 80));
 
821
                        imap_urlauth_connection_abort(conn, NULL);
 
822
                        return -1;
 
823
                }
 
824
 
 
825
                if (conn->user->mail_debug) {
 
826
                        i_debug("imap-urlauth: Successfully selected target user %s",
 
827
                                conn->targets_head->userid);
 
828
                }
 
829
                conn->state = IMAP_URLAUTH_STATE_READY;
 
830
                imap_urlauth_connection_send_request(conn);
 
831
                return 0;
 
832
        case IMAP_URLAUTH_STATE_AUTHENTICATED:
 
833
        case IMAP_URLAUTH_STATE_READY:
 
834
        case IMAP_URLAUTH_STATE_REQUEST_WAIT:
 
835
                if ((response = i_stream_next_line(conn->input)) == NULL)
 
836
                        return 0;
 
837
 
 
838
                i_error("imap-urlauth: Received input while no requests were pending");
 
839
                imap_urlauth_connection_abort(conn, NULL);
 
840
                return -1;
 
841
        case IMAP_URLAUTH_STATE_REQUEST_PENDING:
 
842
                if ((ret = imap_urlauth_input_pending(conn)) < 0)
 
843
                        imap_urlauth_connection_fail(conn);
 
844
                return ret;
 
845
        case IMAP_URLAUTH_STATE_DISCONNECTED:
 
846
                break;
 
847
        }
 
848
        i_unreached();
 
849
}
 
850
 
 
851
static void imap_urlauth_input(struct imap_urlauth_connection *conn)
 
852
{
 
853
        int ret;
 
854
 
 
855
        i_assert(conn->state != IMAP_URLAUTH_STATE_DISCONNECTED);
 
856
 
 
857
        if (conn->input->closed) {
 
858
                /* disconnected */
 
859
                i_error("imap-urlauth: Service disconnected unexpectedly");
 
860
                imap_urlauth_connection_fail(conn);
 
861
                return;
 
862
        }
 
863
 
 
864
        switch (i_stream_read(conn->input)) {
 
865
        case -1:
 
866
                /* disconnected */
 
867
                i_error("imap-urlauth: Service disconnected unexpectedly");
 
868
                imap_urlauth_connection_fail(conn);
 
869
                return;
 
870
        case -2:
 
871
                /* input buffer full */
 
872
                i_error("imap-urlauth: Service sent too large input");
 
873
                imap_urlauth_connection_abort(conn, NULL);
 
874
                return;
 
875
        }
 
876
 
 
877
        while (!conn->input->closed) {
 
878
                if ((ret = imap_urlauth_input_next(conn)) <= 0)
 
879
                        break;
 
880
        }
 
881
}
 
882
 
 
883
static int
 
884
imap_urlauth_connection_do_connect(struct imap_urlauth_connection *conn)
 
885
{
 
886
        string_t *str;
 
887
        int fd;
 
888
 
 
889
        if (conn->state != IMAP_URLAUTH_STATE_DISCONNECTED) {
 
890
                imap_urlauth_connection_send_request(conn);
 
891
                return 1;
 
892
        }
 
893
 
 
894
        if (conn->user->auth_token == NULL) {
 
895
                i_error("imap-urlauth: cannot authenticate because no auth token "
 
896
                        "is available for this session (standalone IMAP?).");
 
897
                imap_urlauth_connection_abort(conn, NULL);
 
898
                return -1;
 
899
        }
 
900
 
 
901
        if (conn->user->mail_debug)
 
902
                i_debug("imap-urlauth: Connecting to service at %s", conn->path);
 
903
 
 
904
        i_assert(conn->fd == -1);
 
905
        fd = net_connect_unix(conn->path);
 
906
        if (fd == -1) {
 
907
                i_error("imap-urlauth: net_connect_unix(%s) failed: %m",
 
908
                        conn->path);
 
909
                imap_urlauth_connection_abort(conn, NULL);
 
910
                return -1;
 
911
        }
 
912
 
 
913
        if (conn->to_reconnect != NULL)
 
914
                timeout_remove(&conn->to_reconnect);
 
915
 
 
916
        conn->fd = fd;
 
917
        conn->input = i_stream_create_fd(fd, (size_t)-1, FALSE);
 
918
        conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
 
919
        conn->io = io_add(fd, IO_READ, imap_urlauth_input, conn);
 
920
        conn->state = IMAP_URLAUTH_STATE_AUTHENTICATING;
 
921
 
 
922
        str = t_str_new(128);
 
923
        str_printfa(str, IMAP_URLAUTH_HANDSHAKE"AUTH\t%s\t", my_pid);
 
924
        str_append_tabescaped(str, conn->user->username);
 
925
        str_append_c(str, '\t');
 
926
        if (conn->session_id != NULL)
 
927
                str_append_tabescaped(str, conn->session_id);
 
928
        str_append_c(str, '\t');
 
929
        str_append_tabescaped(str, conn->user->auth_token);
 
930
        str_append_c(str, '\n');
 
931
        if (o_stream_send(conn->output, str_data(str), str_len(str)) < 0) {
 
932
                i_warning("Error sending handshake to imap-urlauth server: %m");
 
933
                imap_urlauth_connection_abort(conn, NULL);
 
934
                return -1;
 
935
        }
 
936
 
 
937
        imap_urlauth_start_response_timeout(conn);
 
938
        return 0;
 
939
}
 
940
 
 
941
int imap_urlauth_connection_connect(struct imap_urlauth_connection *conn)
 
942
{
 
943
        conn->reconnect_attempts = 0;
 
944
 
 
945
        if (conn->to_reconnect == NULL)
 
946
                return imap_urlauth_connection_do_connect(conn);
 
947
        return 0;
 
948
}
 
949
 
 
950
static void imap_urlauth_connection_disconnect
 
951
(struct imap_urlauth_connection *conn, const char *reason)
 
952
{
 
953
        conn->state = IMAP_URLAUTH_STATE_DISCONNECTED;
 
954
 
 
955
        if (conn->fd != -1) {
 
956
                if (conn->user->mail_debug) {
 
957
                        if (reason == NULL)
 
958
                                i_debug("imap-urlauth: Disconnecting from service");
 
959
                        else
 
960
                                i_debug("imap-urlauth: Disconnected: %s", reason);
 
961
                }
 
962
 
 
963
                io_remove(&conn->io);
 
964
                i_stream_destroy(&conn->input);
 
965
                o_stream_destroy(&conn->output);
 
966
                net_disconnect(conn->fd);
 
967
                conn->fd = -1;
 
968
        }
 
969
        conn->reading_literal = FALSE;
 
970
 
 
971
        if (conn->literal_fd != -1) {
 
972
                if (close(conn->literal_fd) < 0)
 
973
                        i_error("imap-urlauth: close(%s) failed: %m", conn->literal_temp_path);
 
974
 
 
975
                i_free_and_null(conn->literal_temp_path);
 
976
                conn->literal_fd = -1;
 
977
        }
 
978
 
 
979
        if (conn->literal_buf != NULL)
 
980
                buffer_free(&conn->literal_buf);
 
981
        if (conn->to_reconnect != NULL)
 
982
                timeout_remove(&conn->to_reconnect);
 
983
        if (conn->to_idle != NULL)
 
984
                timeout_remove(&conn->to_idle);
 
985
        imap_urlauth_stop_response_timeout(conn);
 
986
}
 
987
 
 
988
static void
 
989
imap_urlauth_connection_do_reconnect(struct imap_urlauth_connection *conn)
 
990
{
 
991
        if (conn->reconnect_attempts >= IMAP_URLAUTH_RECONNECT_MAX_ATTEMPTS) {
 
992
                imap_urlauth_connection_abort(conn,
 
993
                        "Connection failed and connection attempts exhausted");
 
994
                return;
 
995
        }
 
996
 
 
997
        if (ioloop_time - conn->last_reconnect < IMAP_URLAUTH_RECONNECT_MIN_SECS) {
 
998
                if (conn->user->mail_debug)
 
999
                        i_debug("imap-urlauth: Scheduling reconnect");
 
1000
                if (conn->to_reconnect != NULL)
 
1001
                        timeout_remove(&conn->to_reconnect);
 
1002
                conn->to_reconnect =
 
1003
                        timeout_add(IMAP_URLAUTH_RECONNECT_MIN_SECS*1000,
 
1004
                                imap_urlauth_connection_do_reconnect, conn);
 
1005
        } else {
 
1006
                conn->reconnect_attempts++;
 
1007
                conn->last_reconnect = ioloop_time;
 
1008
                (void)imap_urlauth_connection_do_connect(conn);
 
1009
        }
 
1010
}
 
1011
 
 
1012
static void
 
1013
imap_urlauth_connection_reconnect(struct imap_urlauth_connection *conn)
 
1014
{
 
1015
        imap_urlauth_connection_disconnect(conn, NULL);
 
1016
 
 
1017
        /* don't reconnect if there are no requests */
 
1018
        if (conn->targets_head == NULL)
 
1019
                return;
 
1020
 
 
1021
        imap_urlauth_connection_do_reconnect(conn);
 
1022
}
 
1023
 
 
1024
static void
 
1025
imap_urlauth_connection_idle_disconnect(struct imap_urlauth_connection *conn)
 
1026
{
 
1027
        imap_urlauth_connection_disconnect(conn, "Idle timeout");
 
1028
}
 
1029
 
 
1030
static void
 
1031
imap_urlauth_connection_timeout_abort(struct imap_urlauth_connection *conn)
 
1032
{
 
1033
        imap_urlauth_connection_abort(conn, "Service is not responding");
 
1034
}
 
1035
 
 
1036
bool imap_urlauth_connection_is_connected(struct imap_urlauth_connection *conn)
 
1037
{
 
1038
        return conn->fd != -1 && conn->state != IMAP_URLAUTH_STATE_DISCONNECTED;
 
1039
}