~ubuntu-branches/ubuntu/precise/evolution-data-server/precise

« back to all changes in this revision

Viewing changes to .pc/999git_EDS_3_1_3_1_to_f94a069.patch/camel/providers/imap/camel-imap-command.c

  • Committer: Bazaar Package Importer
  • Author(s): Mathieu Trudel-Lapierre
  • Date: 2011-07-14 15:00:38 UTC
  • Revision ID: james.westby@ubuntu.com-20110714150038-aha2wqajqp2i8s5v
Tags: 3.1.3.1-0ubuntu3
debian/patches/999git_EDS_3_1_3_1_to_f94a069.patch: apply changes from git
branch up to commit f94a069.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/* camel-imap-command.c: IMAP command sending/parsing routines */
 
3
 
 
4
/*
 
5
 *  Authors:
 
6
 *    Dan Winship <danw@ximian.com>
 
7
 *    Jeffrey Stedfast <fejj@ximian.com>
 
8
 *
 
9
 *  Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 
10
 *
 
11
 * This program is free software; you can redistribute it and/or
 
12
 * modify it under the terms of version 2 of the GNU Lesser General Public
 
13
 * License as published by the Free Software Foundation.
 
14
 *
 
15
 * This program is distributed in the hope that it will be useful,
 
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
18
 * General Public License for more details.
 
19
 *
 
20
 * You should have received a copy of the GNU Lesser General Public
 
21
 * License along with this program; if not, write to the
 
22
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
23
 * Boston, MA 02110-1301, USA.
 
24
 *
 
25
 */
 
26
 
 
27
#ifdef HAVE_CONFIG_H
 
28
#include <config.h>
 
29
#endif
 
30
 
 
31
#include <errno.h>
 
32
#include <stdarg.h>
 
33
#include <stdio.h>
 
34
#include <string.h>
 
35
 
 
36
#include <glib/gi18n-lib.h>
 
37
 
 
38
#include "camel-imap-command.h"
 
39
#include "camel-imap-folder.h"
 
40
#include "camel-imap-store-summary.h"
 
41
#include "camel-imap-store.h"
 
42
#include "camel-imap-utils.h"
 
43
 
 
44
extern gint camel_verbose_debug;
 
45
 
 
46
static gboolean imap_command_start (CamelImapStore *store, CamelFolder *folder,
 
47
                                    const gchar *cmd, GCancellable *cancellable,
 
48
                                    GError **error);
 
49
static CamelImapResponse *imap_read_response (CamelImapStore *store,
 
50
                                              GCancellable *cancellable,
 
51
                                              GError **error);
 
52
static gchar *imap_read_untagged (CamelImapStore *store, gchar *line,
 
53
                                 GCancellable *cancellable, GError **error);
 
54
static gchar *imap_command_strdup_vprintf (CamelImapStore *store,
 
55
                                          const gchar *fmt, va_list ap);
 
56
static gchar *imap_command_strdup_printf (CamelImapStore *store,
 
57
                                         const gchar *fmt, ...);
 
58
 
 
59
/**
 
60
 * camel_imap_command:
 
61
 * @store: the IMAP store
 
62
 * @folder: The folder to perform the operation in (or %NULL if not
 
63
 * relevant).
 
64
 * @cancellable: optional #GCancellable object, or %NULL
 
65
 * @error: return location for a #GError, or %NULL
 
66
 * @fmt: a sort of printf-style format string, followed by arguments
 
67
 *
 
68
 * This function calls camel_imap_command_start() to send the
 
69
 * command, then reads the complete response to it using
 
70
 * camel_imap_command_response() and returns a CamelImapResponse
 
71
 * structure.
 
72
 *
 
73
 * As a special case, if @fmt is %NULL, it will just select @folder
 
74
 * and return the response from doing so.
 
75
 *
 
76
 * See camel_imap_command_start() for details on @fmt.
 
77
 *
 
78
 * On success, the store's connect_lock will be locked. It will be freed
 
79
 * when you call camel_imap_response_free. (The lock is recursive, so
 
80
 * callers can grab and release it themselves if they need to run
 
81
 * multiple commands atomically.)
 
82
 *
 
83
 * Returns: %NULL if an error occurred (in which case @ex will
 
84
 * be set). Otherwise, a CamelImapResponse describing the server's
 
85
 * response, which the caller must free with camel_imap_response_free().
 
86
 **/
 
87
CamelImapResponse *
 
88
camel_imap_command (CamelImapStore *store,
 
89
                    CamelFolder *folder,
 
90
                    GCancellable *cancellable,
 
91
                    GError **error,
 
92
                    const gchar *fmt, ...)
 
93
{
 
94
        va_list ap;
 
95
        gchar *cmd;
 
96
 
 
97
        camel_service_lock (CAMEL_SERVICE (store), CAMEL_SERVICE_REC_CONNECT_LOCK);
 
98
 
 
99
        if (fmt) {
 
100
                va_start (ap, fmt);
 
101
                cmd = imap_command_strdup_vprintf (store, fmt, ap);
 
102
                va_end (ap);
 
103
        } else {
 
104
                const gchar *full_name;
 
105
 
 
106
                g_object_ref (folder);
 
107
                if (store->current_folder)
 
108
                        g_object_unref (store->current_folder);
 
109
                store->current_folder = folder;
 
110
 
 
111
                full_name = camel_folder_get_full_name (folder);
 
112
                cmd = imap_command_strdup_printf (store, "SELECT %F", full_name);
 
113
        }
 
114
 
 
115
        if (!imap_command_start (store, folder, cmd, cancellable, error)) {
 
116
                g_free (cmd);
 
117
                camel_service_unlock (CAMEL_SERVICE (store), CAMEL_SERVICE_REC_CONNECT_LOCK);
 
118
                return NULL;
 
119
        }
 
120
        g_free (cmd);
 
121
 
 
122
        return imap_read_response (store, cancellable, error);
 
123
}
 
124
 
 
125
/**
 
126
 * camel_imap_command_start:
 
127
 * @store: the IMAP store
 
128
 * @folder: The folder to perform the operation in (or %NULL if not
 
129
 * relevant).
 
130
 * @cancellable: optional #GCancellable object, or %NULL
 
131
 * @error: return location for a #GError, or %NULL
 
132
 * @fmt: a sort of printf-style format string, followed by arguments
 
133
 *
 
134
 * This function makes sure that @folder (if non-%NULL) is the
 
135
 * currently-selected folder on @store and then sends the IMAP command
 
136
 * specified by @fmt and the following arguments.
 
137
 *
 
138
 * @fmt can include the following %-escapes ONLY:
 
139
 *      %s, %d, %%: as with printf
 
140
 *      %S: an IMAP "string" (quoted string or literal)
 
141
 *      %F: an IMAP folder name
 
142
 *      %G: an IMAP folder name, with namespace already prepended
 
143
 *
 
144
 * %S strings will be passed as literals if the server supports LITERAL+
 
145
 * and quoted strings otherwise. (%S does not support strings that
 
146
 * contain newlines.)
 
147
 *
 
148
 * %F will have the imap store's namespace prepended; %F and %G will then
 
149
 * be converted to UTF-7 and processed like %S.
 
150
 *
 
151
 * On success, the store's connect_lock will be locked. It will be
 
152
 * freed when %CAMEL_IMAP_RESPONSE_TAGGED or %CAMEL_IMAP_RESPONSE_ERROR
 
153
 * is returned from camel_imap_command_response(). (The lock is
 
154
 * recursive, so callers can grab and release it themselves if they
 
155
 * need to run multiple commands atomically.)
 
156
 *
 
157
 * Returns: %TRUE if the command was sent successfully, %FALSE if
 
158
 * an error occurred (in which case @ex will be set).
 
159
 **/
 
160
gboolean
 
161
camel_imap_command_start (CamelImapStore *store,
 
162
                          CamelFolder *folder,
 
163
                          GCancellable *cancellable,
 
164
                          GError **error,
 
165
                          const gchar *fmt, ...)
 
166
{
 
167
        va_list ap;
 
168
        gchar *cmd;
 
169
        gboolean ok;
 
170
 
 
171
        va_start (ap, fmt);
 
172
        cmd = imap_command_strdup_vprintf (store, fmt, ap);
 
173
        va_end (ap);
 
174
 
 
175
        camel_service_lock (CAMEL_SERVICE (store), CAMEL_SERVICE_REC_CONNECT_LOCK);
 
176
        ok = imap_command_start (store, folder, cmd, cancellable, error);
 
177
        g_free (cmd);
 
178
 
 
179
        if (!ok)
 
180
                camel_service_unlock (CAMEL_SERVICE (store), CAMEL_SERVICE_REC_CONNECT_LOCK);
 
181
        return ok;
 
182
}
 
183
 
 
184
static gboolean
 
185
imap_command_start (CamelImapStore *store,
 
186
                    CamelFolder *folder,
 
187
                    const gchar *cmd,
 
188
                    GCancellable *cancellable,
 
189
                    GError **error)
 
190
{
 
191
        gssize nwritten;
 
192
 
 
193
        if (!store->ostream) {
 
194
                g_set_error (
 
195
                        error, CAMEL_STORE_ERROR,
 
196
                        CAMEL_STORE_ERROR_INVALID,
 
197
                        _("No output stream"));
 
198
                return FALSE;
 
199
        }
 
200
 
 
201
        if (!store->istream) {
 
202
                g_set_error (
 
203
                        error, CAMEL_STORE_ERROR,
 
204
                        CAMEL_STORE_ERROR_INVALID,
 
205
                        _("No input stream"));
 
206
                return FALSE;
 
207
        }
 
208
 
 
209
        /* Check for current folder */
 
210
        if (folder && folder != store->current_folder) {
 
211
                CamelImapResponse *response;
 
212
                GError *local_error = NULL;
 
213
 
 
214
                response = camel_imap_command (
 
215
                        store, folder, cancellable, error, NULL);
 
216
                if (!response)
 
217
                        return FALSE;
 
218
 
 
219
                /* FIXME Pass a GCancellable */
 
220
                camel_imap_folder_selected (
 
221
                        folder, response, NULL, &local_error);
 
222
                camel_imap_response_free (store, response);
 
223
 
 
224
                if (local_error != NULL) {
 
225
                        g_propagate_error (error, local_error);
 
226
                        return FALSE;
 
227
                }
 
228
        }
 
229
 
 
230
        /* Send the command */
 
231
        if (camel_verbose_debug) {
 
232
                const gchar *mask;
 
233
 
 
234
                if (!strncmp ("LOGIN \"", cmd, 7))
 
235
                        mask = "LOGIN \"xxx\" xxx";
 
236
                else if (!strncmp ("LOGIN {", cmd, 7))
 
237
                        mask = "LOGIN {N+}\r\nxxx {N+}\r\nxxx";
 
238
                else if (!strncmp ("LOGIN ", cmd, 6))
 
239
                        mask = "LOGIN xxx xxx";
 
240
                else
 
241
                        mask = cmd;
 
242
 
 
243
                fprintf (stderr, "sending : %c%.5u %s\r\n", store->tag_prefix, store->command, mask);
 
244
        }
 
245
 
 
246
        nwritten = camel_stream_printf (
 
247
                store->ostream, "%c%.5u %s\r\n",
 
248
                store->tag_prefix, store->command++, cmd);
 
249
 
 
250
        if (nwritten == -1) {
 
251
                if (errno == EINTR)
 
252
                        g_set_error (
 
253
                                error, G_IO_ERROR,
 
254
                                G_IO_ERROR_CANCELLED,
 
255
                                _("Operation cancelled"));
 
256
                else
 
257
                        g_set_error (
 
258
                                error, G_IO_ERROR,
 
259
                                g_io_error_from_errno (errno),
 
260
                                "%s", g_strerror (errno));
 
261
 
 
262
                camel_service_disconnect_sync (
 
263
                        CAMEL_SERVICE (store), FALSE, NULL);
 
264
 
 
265
                return FALSE;
 
266
        }
 
267
 
 
268
        return TRUE;
 
269
}
 
270
 
 
271
/**
 
272
 * camel_imap_command_continuation:
 
273
 * @store: the IMAP store
 
274
 * @cmd: buffer containing the response/request data
 
275
 * @cmdlen: command length
 
276
 * @error: return location for a #GError, or %NULL
 
277
 *
 
278
 * This method is for sending continuing responses to the IMAP server
 
279
 * after camel_imap_command() or camel_imap_command_response() returns
 
280
 * a continuation response.
 
281
 *
 
282
 * This function assumes you have an exclusive lock on the imap stream.
 
283
 *
 
284
 * Returns: as for camel_imap_command(). On failure, the store's
 
285
 * connect_lock will be released.
 
286
 **/
 
287
CamelImapResponse *
 
288
camel_imap_command_continuation (CamelImapStore *store,
 
289
                                 const gchar *cmd,
 
290
                                 gsize cmdlen,
 
291
                                 GCancellable *cancellable,
 
292
                                 GError **error)
 
293
{
 
294
        if (!camel_imap_store_connected (store, error))
 
295
                return NULL;
 
296
 
 
297
        if (!store->ostream) {
 
298
                g_set_error (
 
299
                        error, CAMEL_STORE_ERROR,
 
300
                        CAMEL_STORE_ERROR_INVALID,
 
301
                        _("No output stream"));
 
302
                return NULL;
 
303
        }
 
304
 
 
305
        if (!store->istream) {
 
306
                g_set_error (
 
307
                        error, CAMEL_STORE_ERROR,
 
308
                        CAMEL_STORE_ERROR_INVALID,
 
309
                        _("No input stream"));
 
310
                return NULL;
 
311
        }
 
312
 
 
313
        if (camel_stream_write (store->ostream, cmd, cmdlen, cancellable, error) == -1 ||
 
314
            camel_stream_write (store->ostream, "\r\n", 2, cancellable, error) == -1) {
 
315
                camel_service_disconnect_sync (
 
316
                        CAMEL_SERVICE (store), FALSE, NULL);
 
317
                camel_service_unlock (
 
318
                        CAMEL_SERVICE (store),
 
319
                        CAMEL_SERVICE_REC_CONNECT_LOCK);
 
320
                return NULL;
 
321
        }
 
322
 
 
323
        return imap_read_response (store, cancellable, error);
 
324
}
 
325
 
 
326
/**
 
327
 * camel_imap_command_response:
 
328
 * @store: the IMAP store
 
329
 * @response: a pointer to pass back the response data in
 
330
 * @cancellable: optional #GCancellable object, or %NULL
 
331
 * @error: return location for a #GError, or %NULL
 
332
 *
 
333
 * This reads a single tagged, untagged, or continuation response from
 
334
 * @store into *@response. The caller must free the string when it is
 
335
 * done with it.
 
336
 *
 
337
 * Returns: One of %CAMEL_IMAP_RESPONSE_CONTINUATION,
 
338
 * %CAMEL_IMAP_RESPONSE_UNTAGGED, %CAMEL_IMAP_RESPONSE_TAGGED, or
 
339
 * %CAMEL_IMAP_RESPONSE_ERROR. If either of the last two, @store's
 
340
 * command lock will be unlocked.
 
341
 **/
 
342
CamelImapResponseType
 
343
camel_imap_command_response (CamelImapStore *store,
 
344
                             gchar **response,
 
345
                             GCancellable *cancellable,
 
346
                             GError **error)
 
347
{
 
348
        CamelImapResponseType type;
 
349
        gchar *respbuf;
 
350
 
 
351
        if (camel_imap_store_readline (store, &respbuf, cancellable, error) < 0) {
 
352
                camel_service_unlock (CAMEL_SERVICE (store), CAMEL_SERVICE_REC_CONNECT_LOCK);
 
353
                return CAMEL_IMAP_RESPONSE_ERROR;
 
354
        }
 
355
 
 
356
        switch (*respbuf) {
 
357
        case '*':
 
358
                if (!g_ascii_strncasecmp (respbuf, "* BYE", 5)) {
 
359
                        const gchar *err = NULL;
 
360
 
 
361
                        if (respbuf [5] && g_ascii_strncasecmp (respbuf + 6, "[ALERT] ", 8) == 0)
 
362
                                err = respbuf + 14;
 
363
 
 
364
                        if (!err || !*err)
 
365
                                err = g_strerror (104);
 
366
 
 
367
                        /* Connection was lost, no more data to fetch */
 
368
                        camel_service_disconnect_sync (
 
369
                                CAMEL_SERVICE (store), FALSE, NULL);
 
370
                        g_set_error (
 
371
                                error, CAMEL_SERVICE_ERROR,
 
372
                                CAMEL_SERVICE_ERROR_UNAVAILABLE,
 
373
                                _("Server unexpectedly disconnected: %s"), err);
 
374
                        store->connected = FALSE;
 
375
                        g_free (respbuf);
 
376
                        respbuf = NULL;
 
377
                        type = CAMEL_IMAP_RESPONSE_ERROR;
 
378
                        break;
 
379
                }
 
380
 
 
381
                /* Read the rest of the response. */
 
382
                type = CAMEL_IMAP_RESPONSE_UNTAGGED;
 
383
                respbuf = imap_read_untagged (
 
384
                        store, respbuf, cancellable, error);
 
385
                if (!respbuf)
 
386
                        type = CAMEL_IMAP_RESPONSE_ERROR;
 
387
                else if (!g_ascii_strncasecmp (respbuf, "* OK [ALERT]", 12)
 
388
                         || !g_ascii_strncasecmp (respbuf, "* NO [ALERT]", 12)
 
389
                         || !g_ascii_strncasecmp (respbuf, "* BAD [ALERT]", 13)) {
 
390
                        CamelService *service;
 
391
                        CamelSession *session;
 
392
                        CamelURL *url;
 
393
                        gchar *msg;
 
394
 
 
395
                        /* for imap ALERT codes, account user@host */
 
396
                        /* we might get a ']' from a BAD response since we +12,
 
397
                         * but who cares? */
 
398
                        service = CAMEL_SERVICE (store);
 
399
                        url = camel_service_get_camel_url (service);
 
400
                        session = camel_service_get_session (service);
 
401
 
 
402
                        msg = g_strdup_printf (
 
403
                                _("Alert from IMAP server %s@%s:\n%s"),
 
404
                                url->user, url->host, respbuf + 12);
 
405
                        camel_session_alert_user (
 
406
                                session, CAMEL_SESSION_ALERT_WARNING,
 
407
                                msg, FALSE);
 
408
                        g_free (msg);
 
409
                }
 
410
 
 
411
                break;
 
412
        case '+':
 
413
                type = CAMEL_IMAP_RESPONSE_CONTINUATION;
 
414
                break;
 
415
        default:
 
416
                type = CAMEL_IMAP_RESPONSE_TAGGED;
 
417
                break;
 
418
        }
 
419
        *response = respbuf;
 
420
 
 
421
        if (type == CAMEL_IMAP_RESPONSE_ERROR ||
 
422
            type == CAMEL_IMAP_RESPONSE_TAGGED)
 
423
                camel_service_unlock (CAMEL_SERVICE (store), CAMEL_SERVICE_REC_CONNECT_LOCK);
 
424
 
 
425
        return type;
 
426
}
 
427
 
 
428
static CamelImapResponse *
 
429
imap_read_response (CamelImapStore *store,
 
430
                    GCancellable *cancellable,
 
431
                    GError **error)
 
432
{
 
433
        CamelImapResponse *response;
 
434
        CamelImapResponseType type;
 
435
        gchar *respbuf, *p;
 
436
 
 
437
        /* Get another lock so that when we reach the tagged
 
438
         * response and camel_imap_command_response unlocks,
 
439
         * we're still locked. This lock is owned by response
 
440
         * and gets unlocked when response is freed.
 
441
         */
 
442
        camel_service_lock (CAMEL_SERVICE (store), CAMEL_SERVICE_REC_CONNECT_LOCK);
 
443
 
 
444
        response = g_new0 (CamelImapResponse, 1);
 
445
/*FIXME if (store->current_folder && camel_disco_store_status (CAMEL_DISCO_STORE (store)) != CAMEL_DISCO_STORE_RESYNCING) {
 
446
                response->folder = store->current_folder;
 
447
                g_object_ref (response->folder);
 
448
        } */
 
449
 
 
450
        response->untagged = g_ptr_array_new ();
 
451
        while ((type = camel_imap_command_response (
 
452
                store, &respbuf, cancellable, error))
 
453
                == CAMEL_IMAP_RESPONSE_UNTAGGED)
 
454
                g_ptr_array_add (response->untagged, respbuf);
 
455
 
 
456
        if (type == CAMEL_IMAP_RESPONSE_ERROR) {
 
457
                camel_imap_response_free_without_processing (store, response);
 
458
                return NULL;
 
459
        }
 
460
 
 
461
        response->status = respbuf;
 
462
 
 
463
        /* Check for OK or continuation response. */
 
464
        if (*respbuf == '+')
 
465
                return response;
 
466
        p = strchr (respbuf, ' ');
 
467
        if (p && !g_ascii_strncasecmp (p, " OK", 3))
 
468
                return response;
 
469
 
 
470
        /* We should never get BAD, or anything else but +, OK, or NO
 
471
         * for that matter.  Well, we could get BAD, treat as NO.
 
472
         */
 
473
        if (!p || (g_ascii_strncasecmp(p, " NO", 3) != 0 && g_ascii_strncasecmp(p, " BAD", 4)) ) {
 
474
                g_warning ("Unexpected response from IMAP server: %s",
 
475
                           respbuf);
 
476
                g_set_error (
 
477
                        error, CAMEL_SERVICE_ERROR,
 
478
                        CAMEL_SERVICE_ERROR_UNAVAILABLE,
 
479
                        _("Unexpected response from IMAP server: %s"),
 
480
                        respbuf);
 
481
                camel_imap_response_free_without_processing (store, response);
 
482
                return NULL;
 
483
        }
 
484
 
 
485
        p += 3;
 
486
        if (!*p++)
 
487
                p = NULL;
 
488
        g_set_error (
 
489
                error, CAMEL_SERVICE_ERROR,
 
490
                CAMEL_SERVICE_ERROR_INVALID,
 
491
                _("IMAP command failed: %s"),
 
492
                (p != NULL) ? p : _("Unknown error"));
 
493
        camel_imap_response_free_without_processing (store, response);
 
494
        return NULL;
 
495
}
 
496
 
 
497
/* Given a line that is the start of an untagged response, read and
 
498
 * return the complete response, which may include an arbitrary number
 
499
 * of literals.
 
500
 */
 
501
static gchar *
 
502
imap_read_untagged (CamelImapStore *store,
 
503
                    gchar *line,
 
504
                    GCancellable *cancellable,
 
505
                    GError **error)
 
506
{
 
507
        gint fulllen, ldigits, nread, n, i, sexp = 0;
 
508
        guint length;
 
509
        GPtrArray *data;
 
510
        GString *str;
 
511
        gchar *end, *p, *s, *d;
 
512
 
 
513
        p = strrchr (line, '{');
 
514
        if (!p)
 
515
                return line;
 
516
 
 
517
        data = g_ptr_array_new ();
 
518
        fulllen = 0;
 
519
 
 
520
        while (1) {
 
521
                str = g_string_new (line);
 
522
                g_free (line);
 
523
                fulllen += str->len;
 
524
                g_ptr_array_add (data, str);
 
525
 
 
526
                if (!(p = strrchr (str->str, '{')) || p[1] == '-')
 
527
                        break;
 
528
 
 
529
                /* HACK ALERT: We scan the non-literal part of the string, looking for possible s expression braces.
 
530
                   This assumes we're getting s-expressions, which we should be.
 
531
                   This is so if we get a blank line after a literal, in an s-expression, we can keep going, since
 
532
                   we do no other parsing at this level.
 
533
                   TODO: handle quoted strings? */
 
534
                for (s=str->str; s<p; s++) {
 
535
                        if (*s == '(')
 
536
                                sexp++;
 
537
                        else if (*s == ')')
 
538
                                sexp--;
 
539
                }
 
540
 
 
541
                length = strtoul (p + 1, &end, 10);
 
542
                if (*end != '}' || *(end + 1) || end == p + 1 || length >= UINT_MAX - 2)
 
543
                        break;
 
544
                ldigits = end - (p + 1);
 
545
 
 
546
                /* Read the literal */
 
547
                str = g_string_sized_new (length + 2);
 
548
                str->str[0] = '\n';
 
549
                nread = 0;
 
550
 
 
551
                do {
 
552
                        n = camel_stream_read (
 
553
                                store->istream,
 
554
                                str->str + nread + 1,
 
555
                                length - nread,
 
556
                                cancellable, error);
 
557
                        if (n == -1) {
 
558
                                camel_service_disconnect_sync (
 
559
                                        CAMEL_SERVICE (store), FALSE, NULL);
 
560
                                g_string_free (str, TRUE);
 
561
                                goto lose;
 
562
                        }
 
563
 
 
564
                        nread += n;
 
565
                } while (n > 0 && nread < length);
 
566
 
 
567
                if (nread < length) {
 
568
                        g_set_error (
 
569
                                error, CAMEL_SERVICE_ERROR,
 
570
                                CAMEL_SERVICE_ERROR_UNAVAILABLE,
 
571
                                _("Server response ended too soon."));
 
572
                        camel_service_disconnect_sync (
 
573
                                CAMEL_SERVICE (store), FALSE, NULL);
 
574
                        g_string_free (str, TRUE);
 
575
                        goto lose;
 
576
                }
 
577
                str->str[length + 1] = '\0';
 
578
 
 
579
                if (camel_debug("imap")) {
 
580
                        printf("Literal: -->");
 
581
                        fwrite (str->str+1, 1, length, stdout);
 
582
                        printf("<--\n");
 
583
                }
 
584
 
 
585
                /* Fix up the literal, turning CRLFs into LF. Also, if
 
586
                 * we find any embedded NULs, strip them. This is
 
587
                 * dubious, but:
 
588
                 *   - The IMAP grammar says you can't have NULs here
 
589
                 *     anyway, so this will not affect our behavior
 
590
                 *     against any completely correct server.
 
591
                 *   - WU-imapd 12.264 (at least) will cheerily pass
 
592
                 *     NULs along if they are embedded in the message
 
593
                 */
 
594
 
 
595
                s = d = str->str + 1;
 
596
                end = str->str + 1 + length;
 
597
                while (s < end) {
 
598
                        while (s < end && *s == '\0') {
 
599
                                s++;
 
600
                                length--;
 
601
                        }
 
602
                        if (*s == '\r' && *(s + 1) == '\n') {
 
603
                                s++;
 
604
                                length--;
 
605
                        }
 
606
                        *d++ = *s++;
 
607
                }
 
608
                *d = '\0';
 
609
                str->len = length + 1;
 
610
 
 
611
                /* p points to the "{" in the line that starts the
 
612
                 * literal. The length of the CR-less response must be
 
613
                 * less than or equal to the length of the response
 
614
                 * with CRs, therefore overwriting the old value with
 
615
                 * the new value cannot cause an overrun. However, we
 
616
                 * don't want it to be shorter either, because then the
 
617
                 * GString's length would be off...
 
618
                 */
 
619
                sprintf (p, "{%0*u}", ldigits, length);
 
620
 
 
621
                fulllen += str->len;
 
622
                g_ptr_array_add (data, str);
 
623
 
 
624
                /* Read the next line. */
 
625
                do {
 
626
                        if (camel_imap_store_readline (store, &line, cancellable, error) < 0)
 
627
                                goto lose;
 
628
 
 
629
                        /* MAJOR HACK ALERT, gropuwise sometimes sends an extra blank line after literals, check that here
 
630
                           But only do it if we're inside an sexpression */
 
631
                        if (line[0] == 0 && sexp > 0)
 
632
                                g_warning("Server sent empty line after a literal, assuming in error");
 
633
                } while (line[0] == 0 && sexp > 0);
 
634
        }
 
635
 
 
636
        /* Now reassemble the data. */
 
637
        p = line = g_malloc (fulllen + 1);
 
638
        for (i = 0; i < data->len; i++) {
 
639
                str = data->pdata[i];
 
640
                memcpy (p, str->str, str->len);
 
641
                p += str->len;
 
642
                g_string_free (str, TRUE);
 
643
        }
 
644
        *p = '\0';
 
645
        g_ptr_array_free (data, TRUE);
 
646
        return line;
 
647
 
 
648
 lose:
 
649
        for (i = 0; i < data->len; i++)
 
650
                g_string_free (data->pdata[i], TRUE);
 
651
        g_ptr_array_free (data, TRUE);
 
652
        return NULL;
 
653
}
 
654
 
 
655
/**
 
656
 * camel_imap_response_free:
 
657
 * @store: the CamelImapStore the response is from
 
658
 * @response: a CamelImapResponse
 
659
 *
 
660
 * Frees all of the data in @response and processes any untagged
 
661
 * EXPUNGE and EXISTS responses in it. Releases @store's connect_lock.
 
662
 **/
 
663
void
 
664
camel_imap_response_free (CamelImapStore *store, CamelImapResponse *response)
 
665
{
 
666
        gint i, number, exists = 0;
 
667
        GArray *expunged = NULL;
 
668
        gchar *resp, *p;
 
669
 
 
670
        if (!response)
 
671
                return;
 
672
 
 
673
        for (i = 0; i < response->untagged->len; i++) {
 
674
                resp = response->untagged->pdata[i];
 
675
 
 
676
                if (response->folder) {
 
677
                        /* Check if it's something we need to handle. */
 
678
                        number = strtoul (resp + 2, &p, 10);
 
679
                        if (!g_ascii_strcasecmp (p, " EXISTS")) {
 
680
                                exists = number;
 
681
                        } else if (!g_ascii_strcasecmp (p, " EXPUNGE")
 
682
                                   || !g_ascii_strcasecmp(p, " XGWMOVE")) {
 
683
                                /* XGWMOVE response is the same as an EXPUNGE response */
 
684
                                if (!expunged) {
 
685
                                        expunged = g_array_new (FALSE, FALSE,
 
686
                                                                sizeof (gint));
 
687
                                }
 
688
                                g_array_append_val (expunged, number);
 
689
                        }
 
690
                }
 
691
                g_free (resp);
 
692
        }
 
693
 
 
694
        g_ptr_array_free (response->untagged, TRUE);
 
695
        g_free (response->status);
 
696
 
 
697
        if (response->folder) {
 
698
                if (exists > 0 || expunged) {
 
699
                        /* Update the summary */
 
700
                        /* FIXME Pass a GCancellable */
 
701
                        camel_imap_folder_changed (
 
702
                                response->folder, exists,
 
703
                                expunged, NULL, NULL);
 
704
                        if (expunged)
 
705
                                g_array_free (expunged, TRUE);
 
706
                }
 
707
 
 
708
                g_object_unref (response->folder);
 
709
        }
 
710
 
 
711
        g_free (response);
 
712
        camel_service_unlock (CAMEL_SERVICE (store), CAMEL_SERVICE_REC_CONNECT_LOCK);
 
713
}
 
714
 
 
715
/**
 
716
 * camel_imap_response_free_without_processing:
 
717
 * @store: the CamelImapStore the response is from.
 
718
 * @response: a CamelImapResponse:
 
719
 *
 
720
 * Frees all of the data in @response without processing any untagged
 
721
 * responses. Releases @store's command lock.
 
722
 **/
 
723
void
 
724
camel_imap_response_free_without_processing (CamelImapStore *store,
 
725
                                             CamelImapResponse *response)
 
726
{
 
727
        if (!response)
 
728
                return;
 
729
 
 
730
        if (response->folder) {
 
731
                g_object_unref (response->folder);
 
732
                response->folder = NULL;
 
733
        }
 
734
        camel_imap_response_free (store, response);
 
735
}
 
736
 
 
737
/**
 
738
 * camel_imap_response_extract:
 
739
 * @store: the store the response came from
 
740
 * @response: the response data returned from camel_imap_command
 
741
 * @type: the response type to extract
 
742
 * @error: return location for a #GError, or %NULL
 
743
 *
 
744
 * This checks that @response contains a single untagged response of
 
745
 * type @type and returns just that response data. If @response
 
746
 * doesn't contain the right information, the function will set @ex
 
747
 * and return %NULL. Either way, @response will be freed and the
 
748
 * store's connect_lock released.
 
749
 *
 
750
 * Returns: the desired response string, which the caller must free.
 
751
 **/
 
752
gchar *
 
753
camel_imap_response_extract (CamelImapStore *store,
 
754
                             CamelImapResponse *response,
 
755
                             const gchar *type,
 
756
                             GError **error)
 
757
{
 
758
        gint len, i;
 
759
        gchar *resp;
 
760
 
 
761
        len = strlen (type);
 
762
 
 
763
        for (i = 0; i < response->untagged->len; i++) {
 
764
                resp = response->untagged->pdata[i];
 
765
                /* Skip "* ", and initial sequence number, if present */
 
766
                strtoul (resp + 2, &resp, 10);
 
767
                if (*resp == ' ')
 
768
                        resp = (gchar *) imap_next_word (resp);
 
769
 
 
770
                if (!g_ascii_strncasecmp (resp, type, len))
 
771
                        break;
 
772
        }
 
773
 
 
774
        if (i < response->untagged->len) {
 
775
                resp = response->untagged->pdata[i];
 
776
                g_ptr_array_remove_index (response->untagged, i);
 
777
        } else {
 
778
                resp = NULL;
 
779
                g_set_error (
 
780
                        error, CAMEL_SERVICE_ERROR,
 
781
                        CAMEL_SERVICE_ERROR_UNAVAILABLE,
 
782
                        _("IMAP server response did not "
 
783
                          "contain %s information"), type);
 
784
        }
 
785
 
 
786
        camel_imap_response_free (store, response);
 
787
        return resp;
 
788
}
 
789
 
 
790
/**
 
791
 * camel_imap_response_extract_continuation:
 
792
 * @store: the store the response came from
 
793
 * @response: the response data returned from camel_imap_command
 
794
 * @error: return location for a #GError, or %NULL
 
795
 *
 
796
 * This checks that @response contains a continuation response, and
 
797
 * returns just that data. If @response doesn't contain a continuation
 
798
 * response, the function will set @ex, release @store's connect_lock,
 
799
 * and return %NULL. Either way, @response will be freed.
 
800
 *
 
801
 * Returns: the desired response string, which the caller must free.
 
802
 **/
 
803
gchar *
 
804
camel_imap_response_extract_continuation (CamelImapStore *store,
 
805
                                          CamelImapResponse *response,
 
806
                                          GError **error)
 
807
{
 
808
        gchar *status;
 
809
 
 
810
        if (response->status && *response->status == '+') {
 
811
                status = response->status;
 
812
                response->status = NULL;
 
813
                camel_imap_response_free (store, response);
 
814
                return status;
 
815
        }
 
816
 
 
817
        g_set_error (
 
818
                error, CAMEL_SERVICE_ERROR,
 
819
                CAMEL_SERVICE_ERROR_UNAVAILABLE,
 
820
                _("Unexpected OK response from IMAP server: %s"),
 
821
                response->status);
 
822
 
 
823
        camel_imap_response_free (store, response);
 
824
        return NULL;
 
825
}
 
826
 
 
827
static gchar *
 
828
imap_command_strdup_vprintf (CamelImapStore *store, const gchar *fmt,
 
829
                             va_list ap)
 
830
{
 
831
        GPtrArray *args;
 
832
        const gchar *p, *start;
 
833
        gchar *out, *outptr, *string;
 
834
        gint num, len, i, arglen;
 
835
 
 
836
        args = g_ptr_array_new ();
 
837
 
 
838
        /* Determine the length of the data */
 
839
        len = strlen (fmt);
 
840
        p = start = fmt;
 
841
        while (*p) {
 
842
                p = strchr (start, '%');
 
843
                if (!p)
 
844
                        break;
 
845
 
 
846
                switch (*++p) {
 
847
                case 'd':
 
848
                        num = va_arg (ap, gint);
 
849
                        g_ptr_array_add (args, GINT_TO_POINTER (num));
 
850
                        start = p + 1;
 
851
                        len += 10;
 
852
                        break;
 
853
                case 's':
 
854
                        string = va_arg (ap, gchar *);
 
855
                        g_ptr_array_add (args, string);
 
856
                        start = p + 1;
 
857
                        len += strlen (string);
 
858
                        break;
 
859
                case 'S':
 
860
                case 'F':
 
861
                case 'G':
 
862
                        string = va_arg (ap, gchar *);
 
863
                        /* NB: string is freed during output for %F and %G */
 
864
                        if (*p == 'F') {
 
865
                                gchar *s = camel_imap_store_summary_full_from_path (store->summary, string);
 
866
                                if (s) {
 
867
                                        string = camel_utf8_utf7 (s);
 
868
                                        g_free (s);
 
869
                                } else {
 
870
                                        string = camel_utf8_utf7 (string);
 
871
                                }
 
872
                        } else if (*p == 'G') {
 
873
                                string = camel_utf8_utf7 (string);
 
874
                        }
 
875
 
 
876
                        arglen = strlen (string);
 
877
                        g_ptr_array_add (args, string);
 
878
                        if (imap_is_atom (string)) {
 
879
                                len += arglen;
 
880
                        } else {
 
881
                                if (store->capabilities & IMAP_CAPABILITY_LITERALPLUS)
 
882
                                        len += arglen + 15;
 
883
                                else
 
884
                                        len += arglen * 2;
 
885
                        }
 
886
                        start = p + 1;
 
887
                        break;
 
888
                case '%':
 
889
                        start = p;
 
890
                        break;
 
891
                default:
 
892
                        g_warning ("camel-imap-command is not printf. I don't "
 
893
                                   "know what '%%%c' means.", *p);
 
894
                        start = *p ? p + 1 : p;
 
895
                        break;
 
896
                }
 
897
        }
 
898
 
 
899
        /* Now write out the string */
 
900
        outptr = out = g_malloc (len + 1);
 
901
        p = start = fmt;
 
902
        i = 0;
 
903
        while (*p) {
 
904
                p = strchr (start, '%');
 
905
                if (!p) {
 
906
                        strcpy (outptr, start);
 
907
                        break;
 
908
                } else {
 
909
                        strncpy (outptr, start, p - start);
 
910
                        outptr += p - start;
 
911
                }
 
912
 
 
913
                switch (*++p) {
 
914
                case 'd':
 
915
                        num = GPOINTER_TO_INT (args->pdata[i++]);
 
916
                        outptr += sprintf (outptr, "%d", num);
 
917
                        break;
 
918
 
 
919
                case 's':
 
920
                        string = args->pdata[i++];
 
921
                        outptr += sprintf (outptr, "%s", string);
 
922
                        break;
 
923
                case 'S':
 
924
                case 'F':
 
925
                case 'G':
 
926
                        string = args->pdata[i++];
 
927
                        if (imap_is_atom (string)) {
 
928
                                outptr += sprintf (outptr, "%s", string);
 
929
                        } else {
 
930
                                len = strlen (string);
 
931
                                if (len && store->capabilities & IMAP_CAPABILITY_LITERALPLUS) {
 
932
                                        outptr += sprintf (outptr, "{%d+}\r\n%s", len, string);
 
933
                                } else {
 
934
                                        gchar *quoted = imap_quote_string (string);
 
935
 
 
936
                                        outptr += sprintf (outptr, "%s", quoted);
 
937
                                        g_free (quoted);
 
938
                                }
 
939
                        }
 
940
 
 
941
                        if (*p == 'F' || *p == 'G')
 
942
                                g_free (string);
 
943
                        break;
 
944
                default:
 
945
                        *outptr++ = '%';
 
946
                        *outptr++ = *p;
 
947
                }
 
948
 
 
949
                start = *p ? p + 1 : p;
 
950
        }
 
951
 
 
952
        g_ptr_array_free (args, TRUE);
 
953
 
 
954
        return out;
 
955
}
 
956
 
 
957
static gchar *
 
958
imap_command_strdup_printf (CamelImapStore *store, const gchar *fmt, ...)
 
959
{
 
960
        va_list ap;
 
961
        gchar *result;
 
962
 
 
963
        va_start (ap, fmt);
 
964
        result = imap_command_strdup_vprintf (store, fmt, ap);
 
965
        va_end (ap);
 
966
 
 
967
        return result;
 
968
}