2004-03-27 16:07:20 +00:00
|
|
|
/*
|
|
|
|
* mbsync - mailbox synchronizer
|
|
|
|
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
2013-04-20 14:57:16 +00:00
|
|
|
* Copyright (C) 2002-2006,2008,2010-2013 Oswald Buddenhagen <ossi@users.sf.net>
|
2004-03-27 16:07:20 +00:00
|
|
|
* Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2011-04-10 17:34:36 +00:00
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2004-03-27 16:07:20 +00:00
|
|
|
*
|
|
|
|
* As a special exception, mbsync may be linked with the OpenSSL library,
|
|
|
|
* despite that library's more restrictive license.
|
|
|
|
*/
|
|
|
|
|
2013-12-08 19:46:40 +00:00
|
|
|
#include "driver.h"
|
|
|
|
|
|
|
|
#include "socket.h"
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
2004-09-08 16:14:12 +00:00
|
|
|
#include <stddef.h>
|
2006-01-25 06:35:19 +00:00
|
|
|
#include <limits.h>
|
2004-03-27 16:07:20 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
2013-07-28 13:55:13 +00:00
|
|
|
#include <time.h>
|
2013-07-27 08:37:15 +00:00
|
|
|
#include <sys/wait.h>
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2014-07-27 16:41:22 +00:00
|
|
|
#ifdef HAVE_LIBSASL
|
|
|
|
# include <sasl/sasl.h>
|
|
|
|
# include <sasl/saslutil.h>
|
|
|
|
#endif
|
|
|
|
|
2019-11-27 16:13:44 +00:00
|
|
|
#ifdef HAVE_MACOS_KEYCHAIN
|
|
|
|
# include <Security/Security.h>
|
|
|
|
#endif
|
|
|
|
|
2014-07-12 18:35:55 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
|
|
|
enum { SSL_None, SSL_STARTTLS, SSL_IMAPS };
|
|
|
|
#endif
|
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
typedef struct imap_server_conf {
|
|
|
|
struct imap_server_conf *next;
|
|
|
|
char *name;
|
2011-01-23 12:43:00 +00:00
|
|
|
server_conf_t sconf;
|
2004-03-27 16:07:20 +00:00
|
|
|
char *user;
|
2019-11-26 11:17:33 +00:00
|
|
|
char *user_cmd;
|
2004-03-27 16:07:20 +00:00
|
|
|
char *pass;
|
2013-07-27 08:37:15 +00:00
|
|
|
char *pass_cmd;
|
2011-03-27 14:58:23 +00:00
|
|
|
int max_in_progress;
|
2020-07-08 15:27:37 +00:00
|
|
|
uint cap_mask;
|
2014-07-12 19:02:25 +00:00
|
|
|
string_list_t *auth_mechs;
|
2011-03-27 10:06:41 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
2014-07-12 18:35:55 +00:00
|
|
|
char ssl_type;
|
2019-11-27 16:13:44 +00:00
|
|
|
#endif
|
|
|
|
#ifdef HAVE_MACOS_KEYCHAIN
|
|
|
|
char use_keychain;
|
2004-03-27 16:07:20 +00:00
|
|
|
#endif
|
2015-04-26 18:54:05 +00:00
|
|
|
char failed;
|
2004-03-27 16:07:20 +00:00
|
|
|
} imap_server_conf_t;
|
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
typedef union imap_store_conf {
|
2004-03-27 16:07:20 +00:00
|
|
|
store_conf_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
STORE_CONF
|
|
|
|
imap_server_conf_t *server;
|
2021-03-19 17:21:34 +00:00
|
|
|
char *path; // Note: this may be modified after the delimiter is determined.
|
2020-12-17 14:53:40 +00:00
|
|
|
char delimiter;
|
|
|
|
char use_namespace;
|
|
|
|
char use_lsub;
|
|
|
|
};
|
2004-03-27 16:07:20 +00:00
|
|
|
} imap_store_conf_t;
|
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
typedef union imap_message {
|
2004-03-27 16:07:20 +00:00
|
|
|
message_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
MESSAGE(union imap_message)
|
|
|
|
// uint seq; will be needed when expunges are tracked
|
|
|
|
};
|
2004-03-27 16:07:20 +00:00
|
|
|
} imap_message_t;
|
|
|
|
|
|
|
|
#define NIL (void*)0x1
|
|
|
|
#define LIST (void*)0x2
|
|
|
|
|
|
|
|
typedef struct _list {
|
|
|
|
struct _list *next, *child;
|
|
|
|
char *val;
|
2020-07-08 15:27:37 +00:00
|
|
|
uint len;
|
2004-03-27 16:07:20 +00:00
|
|
|
} list_t;
|
|
|
|
|
2011-04-03 16:47:37 +00:00
|
|
|
#define MAX_LIST_DEPTH 5
|
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
typedef union imap_store imap_store_t;
|
2013-07-27 12:31:13 +00:00
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
typedef struct {
|
2011-04-03 16:47:37 +00:00
|
|
|
list_t *head, **stack[MAX_LIST_DEPTH];
|
2017-04-02 13:42:18 +00:00
|
|
|
int (*callback)( imap_store_t *ctx, list_t *list, char *cmd );
|
2011-04-03 16:47:37 +00:00
|
|
|
int level, need_bytes;
|
|
|
|
} parse_list_state_t;
|
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
typedef struct imap_cmd imap_cmd_t;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
union imap_store {
|
2006-03-20 15:01:48 +00:00
|
|
|
store_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
STORE(union imap_store)
|
|
|
|
const char *label; // foreign
|
|
|
|
const char *name;
|
2021-03-19 17:21:34 +00:00
|
|
|
char *prefix;
|
2020-12-17 14:53:40 +00:00
|
|
|
uint ref_count;
|
|
|
|
uint opts;
|
|
|
|
enum { SST_BAD, SST_HALF, SST_GOOD } state;
|
|
|
|
// The trash folder's existence is not confirmed yet
|
|
|
|
enum { TrashUnknown, TrashChecking, TrashKnown } trashnc;
|
|
|
|
// What kind of BODY-less FETCH response we're expecting
|
|
|
|
enum { FetchNone, FetchMsgs, FetchUidNext } fetch_sts;
|
|
|
|
uint got_namespace:1;
|
|
|
|
uint has_forwarded:1;
|
2022-05-20 07:54:50 +00:00
|
|
|
uint capability_hack:1;
|
2020-12-17 14:53:40 +00:00
|
|
|
char delimiter[2]; // Hierarchy delimiter
|
|
|
|
char *ns_prefix, ns_delimiter; // NAMESPACE info
|
|
|
|
string_list_t *boxes; // _list results
|
|
|
|
char listed; // was _list already run with these flags?
|
|
|
|
// note that the message counts do _not_ reflect stats from msgs,
|
|
|
|
// but mailbox totals.
|
|
|
|
int total_msgs, recent_msgs;
|
|
|
|
uint uidvalidity, uidnext;
|
|
|
|
imap_message_t **msgapp, *msgs; // FETCH results
|
|
|
|
uint caps; // CAPABILITY results
|
|
|
|
string_list_t *auth_mechs;
|
|
|
|
parse_list_state_t parse_list_sts;
|
|
|
|
// Command queue
|
|
|
|
imap_cmd_t *pending, **pending_append;
|
|
|
|
imap_cmd_t *in_progress, **in_progress_append;
|
|
|
|
imap_cmd_t *wait_check, **wait_check_append;
|
|
|
|
int nexttag, num_in_progress, num_wait_check;
|
|
|
|
uint buffer_mem; // Memory currently occupied by buffers in the queue
|
|
|
|
|
|
|
|
// Used during sequential operations like connect
|
|
|
|
enum { GreetingPending = 0, GreetingBad, GreetingOk, GreetingPreauth } greeting;
|
|
|
|
int expectBYE; // LOGOUT is in progress
|
|
|
|
int expectEOF; // received LOGOUT's OK or unsolicited BYE
|
|
|
|
int canceling; // imap_cancel() is in progress
|
|
|
|
union {
|
|
|
|
void (*imap_open)( int sts, void *aux );
|
|
|
|
void (*imap_cancel)( void *aux );
|
|
|
|
} callbacks;
|
|
|
|
void *callback_aux;
|
2014-07-27 16:41:22 +00:00
|
|
|
#ifdef HAVE_LIBSASL
|
2020-12-17 14:53:40 +00:00
|
|
|
sasl_conn_t *sasl;
|
|
|
|
int sasl_cont;
|
2014-07-27 16:41:22 +00:00
|
|
|
#endif
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
void (*bad_callback)( void *aux );
|
|
|
|
void *bad_callback_aux;
|
2017-03-21 18:27:04 +00:00
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
conn_t conn; // This is BIG, so put it last
|
|
|
|
};
|
2017-04-02 13:42:18 +00:00
|
|
|
};
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
#define IMAP_CMD \
|
|
|
|
struct imap_cmd *next; \
|
|
|
|
char *cmd; \
|
|
|
|
int tag; \
|
|
|
|
\
|
|
|
|
struct { \
|
|
|
|
/* Will be called on each continuation request until it resets this pointer. \
|
|
|
|
* Needs to invoke bad_callback and return -1 on error, otherwise return 0. */ \
|
|
|
|
int (*cont)( imap_store_t *ctx, imap_cmd_t *cmd, const char *prompt ); \
|
|
|
|
void (*done)( imap_store_t *ctx, imap_cmd_t *cmd, int response ); \
|
|
|
|
char *data; \
|
|
|
|
uint data_len; \
|
|
|
|
uint uid; /* to identify fetch responses */ \
|
|
|
|
char high_prio; /* if command is queued, put it at the front of the queue. */ \
|
|
|
|
char wait_check; /* Don't report success until subsequent CHECK success. */ \
|
|
|
|
char to_trash; /* we are storing to trash, not current. */ \
|
|
|
|
char create; /* create the mailbox if we get an error which suggests so. */ \
|
|
|
|
char failok; /* Don't complain about NO response. */ \
|
2008-08-31 20:14:59 +00:00
|
|
|
} param;
|
2020-12-17 14:53:40 +00:00
|
|
|
|
|
|
|
struct imap_cmd {
|
|
|
|
IMAP_CMD
|
2004-03-27 16:07:20 +00:00
|
|
|
};
|
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
#define IMAP_CMD_SIMPLE \
|
|
|
|
IMAP_CMD \
|
|
|
|
void (*callback)( int sts, void *aux ); \
|
2011-04-03 16:15:36 +00:00
|
|
|
void *callback_aux;
|
2020-12-17 14:53:40 +00:00
|
|
|
|
|
|
|
typedef union {
|
|
|
|
imap_cmd_t gen;
|
|
|
|
struct {
|
|
|
|
IMAP_CMD_SIMPLE
|
|
|
|
};
|
2017-04-02 13:42:18 +00:00
|
|
|
} imap_cmd_simple_t;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
typedef union {
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_simple_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
IMAP_CMD_SIMPLE
|
|
|
|
msg_data_t *msg_data;
|
|
|
|
};
|
2017-04-02 13:42:18 +00:00
|
|
|
} imap_cmd_fetch_msg_t;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
typedef union {
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
IMAP_CMD
|
|
|
|
void (*callback)( int sts, uint uid, void *aux );
|
|
|
|
void *callback_aux;
|
|
|
|
};
|
2017-04-02 13:42:18 +00:00
|
|
|
} imap_cmd_out_uid_t;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
typedef union {
|
2017-03-26 16:44:43 +00:00
|
|
|
imap_cmd_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
IMAP_CMD
|
|
|
|
void (*callback)( int sts, message_t *msgs, void *aux );
|
|
|
|
void *callback_aux;
|
|
|
|
imap_message_t **out_msgs;
|
|
|
|
uint uid;
|
|
|
|
};
|
2017-04-02 13:42:18 +00:00
|
|
|
} imap_cmd_find_new_t;
|
2015-01-11 13:29:19 +00:00
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
#define IMAP_CMD_REFCOUNTED_STATE \
|
|
|
|
uint ref_count; \
|
2011-04-03 16:15:36 +00:00
|
|
|
int ret_val;
|
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
typedef struct {
|
2020-12-17 14:53:40 +00:00
|
|
|
IMAP_CMD_REFCOUNTED_STATE
|
|
|
|
} imap_cmd_refcounted_state_t;
|
|
|
|
|
|
|
|
typedef union {
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
IMAP_CMD
|
|
|
|
imap_cmd_refcounted_state_t *state;
|
|
|
|
};
|
2017-04-02 13:42:18 +00:00
|
|
|
} imap_cmd_refcounted_t;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2006-03-20 15:01:48 +00:00
|
|
|
#define CAP(cap) (ctx->caps & (1 << (cap)))
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
enum CAPABILITY {
|
|
|
|
NOLOGIN = 0,
|
2014-07-27 16:41:22 +00:00
|
|
|
#ifdef HAVE_LIBSASL
|
|
|
|
SASLIR,
|
|
|
|
#endif
|
2011-03-27 10:06:41 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
2004-03-27 16:07:20 +00:00
|
|
|
STARTTLS,
|
|
|
|
#endif
|
2011-03-13 14:03:59 +00:00
|
|
|
UIDPLUS,
|
|
|
|
LITERALPLUS,
|
2013-12-08 14:11:00 +00:00
|
|
|
MOVE,
|
2014-12-13 11:09:15 +00:00
|
|
|
NAMESPACE,
|
|
|
|
COMPRESS_DEFLATE
|
2004-03-27 16:07:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const char *cap_list[] = {
|
|
|
|
"LOGINDISABLED",
|
2014-07-27 16:41:22 +00:00
|
|
|
#ifdef HAVE_LIBSASL
|
|
|
|
"SASL-IR",
|
|
|
|
#endif
|
2011-03-27 10:06:41 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
2004-03-27 16:07:20 +00:00
|
|
|
"STARTTLS",
|
|
|
|
#endif
|
2011-03-13 14:03:59 +00:00
|
|
|
"UIDPLUS",
|
|
|
|
"LITERAL+",
|
2013-12-08 14:11:00 +00:00
|
|
|
"MOVE",
|
2014-12-13 11:09:15 +00:00
|
|
|
"NAMESPACE",
|
|
|
|
"COMPRESS=DEFLATE"
|
2004-03-27 16:07:20 +00:00
|
|
|
};
|
|
|
|
|
2012-07-15 10:55:04 +00:00
|
|
|
#define RESP_OK 0
|
|
|
|
#define RESP_NO 1
|
|
|
|
#define RESP_CANCEL 2
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2011-04-03 16:15:36 +00:00
|
|
|
static INLINE void imap_ref( imap_store_t *ctx ) { ++ctx->ref_count; }
|
|
|
|
static int imap_deref( imap_store_t *ctx );
|
|
|
|
|
2012-07-15 10:55:04 +00:00
|
|
|
static void imap_invoke_bad_callback( imap_store_t *ctx );
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2018-06-21 14:52:00 +00:00
|
|
|
/* Keep the mailbox driver flag definitions in sync: */
|
|
|
|
/* grep for MAILBOX_DRIVER_FLAG */
|
|
|
|
/* The order is according to alphabetical maildir flag sort */
|
2004-03-27 16:07:20 +00:00
|
|
|
static const char *Flags[] = {
|
2018-06-21 14:52:01 +00:00
|
|
|
"\\Draft", /* 'D' */
|
|
|
|
"\\Flagged", /* 'F' */
|
|
|
|
"$Forwarded", /* 'P' */
|
|
|
|
"\\Answered", /* 'R' */
|
|
|
|
"\\Seen", /* 'S' */
|
|
|
|
"\\Deleted", /* 'T' */
|
2004-03-27 16:07:20 +00:00
|
|
|
};
|
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
static imap_cmd_t *
|
2020-07-08 15:27:37 +00:00
|
|
|
new_imap_cmd( uint size )
|
2008-08-31 20:14:59 +00:00
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t *cmd = nfmalloc( size );
|
2008-08-31 20:14:59 +00:00
|
|
|
memset( &cmd->param, 0, sizeof(cmd->param) );
|
|
|
|
return cmd;
|
|
|
|
}
|
|
|
|
|
2011-04-03 16:15:36 +00:00
|
|
|
#define INIT_IMAP_CMD(type, cmdp, cb, aux) \
|
2017-04-02 13:42:18 +00:00
|
|
|
cmdp = (type *)new_imap_cmd( sizeof(*cmdp) ); \
|
2011-04-03 16:15:36 +00:00
|
|
|
cmdp->callback = cb; \
|
|
|
|
cmdp->callback_aux = aux;
|
|
|
|
|
|
|
|
#define INIT_IMAP_CMD_X(type, cmdp, cb, aux) \
|
2017-04-02 13:42:18 +00:00
|
|
|
cmdp = (type *)new_imap_cmd( sizeof(*cmdp) ); \
|
2020-12-17 14:53:40 +00:00
|
|
|
cmdp->callback = cb; \
|
|
|
|
cmdp->callback_aux = aux;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2011-03-27 10:34:25 +00:00
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
done_imap_cmd( imap_store_t *ctx, imap_cmd_t *cmd, int response )
|
2011-03-27 10:34:25 +00:00
|
|
|
{
|
2020-07-28 14:14:00 +00:00
|
|
|
if (cmd->param.wait_check)
|
|
|
|
ctx->num_wait_check--;
|
2011-03-27 10:34:25 +00:00
|
|
|
cmd->param.done( ctx, cmd, response );
|
2015-02-15 17:13:05 +00:00
|
|
|
if (cmd->param.data) {
|
|
|
|
free( cmd->param.data );
|
|
|
|
ctx->buffer_mem -= cmd->param.data_len;
|
|
|
|
}
|
2011-03-27 10:34:25 +00:00
|
|
|
free( cmd->cmd );
|
|
|
|
free( cmd );
|
|
|
|
}
|
|
|
|
|
2015-05-09 17:17:41 +00:00
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
send_imap_cmd( imap_store_t *ctx, imap_cmd_t *cmd )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2021-06-11 15:56:39 +00:00
|
|
|
int litplus, iovcnt = 3;
|
|
|
|
uint tbufl, lbufl;
|
|
|
|
conn_iovec_t iov[5];
|
|
|
|
char tagbuf[16];
|
|
|
|
char lenbuf[16];
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2008-08-31 20:14:59 +00:00
|
|
|
cmd->tag = ++ctx->nexttag;
|
2021-06-11 15:56:39 +00:00
|
|
|
tbufl = nfsnprintf( tagbuf, sizeof(tagbuf), "%d ", cmd->tag );
|
2010-11-15 09:38:50 +00:00
|
|
|
if (!cmd->param.data) {
|
2021-06-11 15:56:39 +00:00
|
|
|
memcpy( lenbuf, "\r\n", 3 );
|
|
|
|
lbufl = 2;
|
2010-11-15 09:38:50 +00:00
|
|
|
litplus = 0;
|
2014-12-13 17:29:14 +00:00
|
|
|
} else if ((cmd->param.to_trash && ctx->trashnc == TrashUnknown) || !CAP(LITERALPLUS) || cmd->param.data_len >= 100*1024) {
|
2021-06-11 15:56:39 +00:00
|
|
|
lbufl = nfsnprintf( lenbuf, sizeof(lenbuf), "{%u}\r\n", cmd->param.data_len );
|
2010-11-15 09:38:50 +00:00
|
|
|
litplus = 0;
|
|
|
|
} else {
|
2021-06-11 15:56:39 +00:00
|
|
|
lbufl = nfsnprintf( lenbuf, sizeof(lenbuf), "{%u+}\r\n", cmd->param.data_len );
|
2010-11-15 09:38:50 +00:00
|
|
|
litplus = 1;
|
|
|
|
}
|
2015-03-23 07:42:51 +00:00
|
|
|
if (DFlags & DEBUG_NET) {
|
2006-03-20 15:01:48 +00:00
|
|
|
if (ctx->num_in_progress)
|
|
|
|
printf( "(%d in progress) ", ctx->num_in_progress );
|
2015-03-23 17:05:57 +00:00
|
|
|
if (starts_with( cmd->cmd, -1, "LOGIN", 5 ))
|
2021-06-11 15:56:39 +00:00
|
|
|
printf( "%s>>> %sLOGIN <user> <pass>\r\n", ctx->label, tagbuf );
|
2015-03-23 17:05:57 +00:00
|
|
|
else if (starts_with( cmd->cmd, -1, "AUTHENTICATE PLAIN", 18 ))
|
2021-06-11 15:56:39 +00:00
|
|
|
printf( "%s>>> %sAUTHENTICATE PLAIN <authdata>\r\n", ctx->label, tagbuf );
|
2015-03-23 17:05:57 +00:00
|
|
|
else
|
2021-06-11 15:56:39 +00:00
|
|
|
printf( "%s>>> %s%s%s", ctx->label, tagbuf, cmd->cmd, lenbuf );
|
2012-09-22 15:48:09 +00:00
|
|
|
fflush( stdout );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2021-06-11 15:56:39 +00:00
|
|
|
iov[0].buf = tagbuf;
|
|
|
|
iov[0].len = tbufl;
|
2014-10-26 20:10:25 +00:00
|
|
|
iov[0].takeOwn = KeepOwn;
|
2021-06-11 15:56:39 +00:00
|
|
|
iov[1].buf = cmd->cmd;
|
|
|
|
iov[1].len = strlen( cmd->cmd );
|
|
|
|
iov[1].takeOwn = KeepOwn;
|
|
|
|
iov[2].buf = lenbuf;
|
|
|
|
iov[2].len = lbufl;
|
|
|
|
iov[2].takeOwn = KeepOwn;
|
2010-11-15 09:38:50 +00:00
|
|
|
if (litplus) {
|
2017-01-28 17:26:12 +00:00
|
|
|
if (DFlags & DEBUG_NET_ALL) {
|
|
|
|
printf( "%s>>>>>>>>>\n", ctx->label );
|
|
|
|
fwrite( cmd->param.data, cmd->param.data_len, 1, stdout );
|
|
|
|
printf( "%s>>>>>>>>>\n", ctx->label );
|
|
|
|
fflush( stdout );
|
|
|
|
}
|
2021-06-11 15:56:39 +00:00
|
|
|
iov[3].buf = cmd->param.data;
|
|
|
|
iov[3].len = cmd->param.data_len;
|
|
|
|
iov[3].takeOwn = GiveOwn;
|
2019-07-28 18:50:31 +00:00
|
|
|
cmd->param.data = NULL;
|
2015-02-15 17:13:05 +00:00
|
|
|
ctx->buffer_mem -= cmd->param.data_len;
|
2021-06-11 15:56:39 +00:00
|
|
|
iov[4].buf = "\r\n";
|
|
|
|
iov[4].len = 2;
|
|
|
|
iov[4].takeOwn = KeepOwn;
|
|
|
|
iovcnt = 5;
|
2010-11-15 09:38:50 +00:00
|
|
|
}
|
2015-05-09 17:17:41 +00:00
|
|
|
socket_write( &ctx->conn, iov, iovcnt );
|
2012-08-25 16:26:23 +00:00
|
|
|
if (cmd->param.to_trash && ctx->trashnc == TrashUnknown)
|
|
|
|
ctx->trashnc = TrashChecking;
|
2019-07-28 18:50:31 +00:00
|
|
|
cmd->next = NULL;
|
2006-03-20 15:01:48 +00:00
|
|
|
*ctx->in_progress_append = cmd;
|
|
|
|
ctx->in_progress_append = &cmd->next;
|
|
|
|
ctx->num_in_progress++;
|
2019-11-16 16:14:57 +00:00
|
|
|
socket_expect_activity( &ctx->conn, 1 );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2012-08-25 16:26:23 +00:00
|
|
|
static int
|
2017-04-02 13:42:18 +00:00
|
|
|
cmd_sendable( imap_store_t *ctx, imap_cmd_t *cmd )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2017-03-04 11:47:33 +00:00
|
|
|
if (ctx->conn.write_buf) {
|
|
|
|
/* Don't build up a long queue in the socket, so we can
|
|
|
|
* control when the commands are actually sent.
|
|
|
|
* This allows reliable cancelation of pending commands,
|
|
|
|
* injecting commands in front of other pending commands,
|
|
|
|
* and keeping num_in_progress accurate. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (ctx->in_progress) {
|
|
|
|
/* If the last command in flight ... */
|
|
|
|
imap_cmd_t *cmdp = (imap_cmd_t *)((char *)ctx->in_progress_append -
|
|
|
|
offsetof(imap_cmd_t, next));
|
|
|
|
if (cmdp->param.cont || cmdp->param.data) {
|
|
|
|
/* ... is expected to trigger a continuation request, we need to
|
|
|
|
* wait for that round-trip before sending the next command. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cmd->param.to_trash && ctx->trashnc == TrashChecking) {
|
|
|
|
/* Don't build a queue of MOVE/COPY/APPEND commands that may all fail. */
|
|
|
|
return 0;
|
|
|
|
}
|
2020-12-17 14:53:40 +00:00
|
|
|
if (ctx->num_in_progress >= ctx->conf->server->max_in_progress) {
|
2017-03-04 11:47:33 +00:00
|
|
|
/* Too many commands in flight. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
2012-08-25 16:26:23 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2015-05-09 17:18:40 +00:00
|
|
|
static void
|
2012-08-25 16:26:23 +00:00
|
|
|
flush_imap_cmds( imap_store_t *ctx )
|
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t *cmd;
|
2012-08-25 16:26:23 +00:00
|
|
|
|
2015-05-09 15:44:36 +00:00
|
|
|
if ((cmd = ctx->pending) && cmd_sendable( ctx, cmd )) {
|
2012-08-25 16:26:23 +00:00
|
|
|
if (!(ctx->pending = cmd->next))
|
|
|
|
ctx->pending_append = &ctx->pending;
|
2015-05-09 17:17:41 +00:00
|
|
|
send_imap_cmd( ctx, cmd );
|
2012-08-25 16:26:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-28 14:14:00 +00:00
|
|
|
static void
|
|
|
|
finalize_checked_imap_cmds( imap_store_t *ctx, int resp )
|
|
|
|
{
|
|
|
|
imap_cmd_t *cmd;
|
|
|
|
|
|
|
|
while ((cmd = ctx->wait_check)) {
|
|
|
|
if (!(ctx->wait_check = cmd->next))
|
|
|
|
ctx->wait_check_append = &ctx->wait_check;
|
|
|
|
done_imap_cmd( ctx, cmd, resp );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-25 16:26:23 +00:00
|
|
|
static void
|
|
|
|
cancel_pending_imap_cmds( imap_store_t *ctx )
|
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t *cmd;
|
2012-08-25 16:26:23 +00:00
|
|
|
|
|
|
|
while ((cmd = ctx->pending)) {
|
|
|
|
if (!(ctx->pending = cmd->next))
|
|
|
|
ctx->pending_append = &ctx->pending;
|
|
|
|
done_imap_cmd( ctx, cmd, RESP_CANCEL );
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2011-03-19 21:12:55 +00:00
|
|
|
static void
|
2015-05-09 15:44:36 +00:00
|
|
|
cancel_sent_imap_cmds( imap_store_t *ctx )
|
2011-03-19 21:12:55 +00:00
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t *cmd;
|
2011-03-19 21:12:55 +00:00
|
|
|
|
2019-11-16 16:14:57 +00:00
|
|
|
socket_expect_activity( &ctx->conn, 0 );
|
2011-03-19 21:12:55 +00:00
|
|
|
while ((cmd = ctx->in_progress)) {
|
|
|
|
ctx->in_progress = cmd->next;
|
|
|
|
/* don't update num_in_progress and in_progress_append - store is dead */
|
2011-03-27 10:34:25 +00:00
|
|
|
done_imap_cmd( ctx, cmd, RESP_CANCEL );
|
2011-03-19 21:12:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-09 17:17:41 +00:00
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
submit_imap_cmd( imap_store_t *ctx, imap_cmd_t *cmd )
|
2012-08-25 16:26:23 +00:00
|
|
|
{
|
|
|
|
assert( ctx );
|
2017-03-21 18:27:04 +00:00
|
|
|
assert( ctx->bad_callback );
|
2012-08-25 16:26:23 +00:00
|
|
|
assert( cmd );
|
|
|
|
assert( cmd->param.done );
|
|
|
|
|
2020-07-28 14:14:00 +00:00
|
|
|
if (cmd->param.wait_check)
|
|
|
|
ctx->num_wait_check++;
|
2015-05-09 15:44:36 +00:00
|
|
|
if ((ctx->pending && !cmd->param.high_prio) || !cmd_sendable( ctx, cmd )) {
|
2012-08-25 16:26:23 +00:00
|
|
|
if (ctx->pending && cmd->param.high_prio) {
|
|
|
|
cmd->next = ctx->pending;
|
|
|
|
ctx->pending = cmd;
|
|
|
|
} else {
|
2019-07-28 18:50:31 +00:00
|
|
|
cmd->next = NULL;
|
2012-08-25 16:26:23 +00:00
|
|
|
*ctx->pending_append = cmd;
|
|
|
|
ctx->pending_append = &cmd->next;
|
|
|
|
}
|
2015-05-09 17:17:41 +00:00
|
|
|
} else {
|
|
|
|
send_imap_cmd( ctx, cmd );
|
2012-08-25 16:26:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-25 18:55:32 +00:00
|
|
|
/* Minimal printf() replacement that supports an %\s format sequence to print backslash-escaped
|
|
|
|
* string literals. Note that this does not automatically add quotes around the printed string,
|
|
|
|
* so it is possible to concatenate multiple segments. */
|
|
|
|
static char *
|
|
|
|
imap_vprintf( const char *fmt, va_list ap )
|
|
|
|
{
|
2016-12-29 13:34:30 +00:00
|
|
|
const char *s;
|
2013-09-25 18:55:32 +00:00
|
|
|
char *d, *ed;
|
|
|
|
char c;
|
2021-06-11 15:56:39 +00:00
|
|
|
#define MAX_SEGS 16
|
|
|
|
#define add_seg(s, l) \
|
|
|
|
do { \
|
|
|
|
if (nsegs == MAX_SEGS) \
|
|
|
|
oob(); \
|
|
|
|
segs[nsegs] = s; \
|
|
|
|
segls[nsegs++] = l; \
|
|
|
|
totlen += l; \
|
|
|
|
} while (0)
|
|
|
|
int nsegs = 0;
|
|
|
|
uint totlen = 0;
|
|
|
|
const char *segs[MAX_SEGS];
|
|
|
|
uint segls[MAX_SEGS];
|
|
|
|
char buf[1000];
|
2013-09-25 18:55:32 +00:00
|
|
|
|
|
|
|
d = buf;
|
|
|
|
ed = d + sizeof(buf);
|
|
|
|
s = fmt;
|
|
|
|
for (;;) {
|
|
|
|
c = *fmt;
|
|
|
|
if (!c || c == '%') {
|
2020-07-08 15:27:37 +00:00
|
|
|
uint l = fmt - s;
|
2021-06-11 15:56:39 +00:00
|
|
|
if (l)
|
|
|
|
add_seg( s, l );
|
2016-11-06 16:22:04 +00:00
|
|
|
if (!c)
|
2021-06-11 15:56:39 +00:00
|
|
|
break;
|
2020-07-08 15:27:37 +00:00
|
|
|
uint maxlen = UINT_MAX;
|
2013-09-25 18:55:32 +00:00
|
|
|
c = *++fmt;
|
|
|
|
if (c == '\\') {
|
|
|
|
c = *++fmt;
|
|
|
|
if (c != 's') {
|
|
|
|
fputs( "Fatal: unsupported escaped format specifier. Please report a bug.\n", stderr );
|
|
|
|
abort();
|
|
|
|
}
|
2021-06-11 15:56:39 +00:00
|
|
|
char *bd = d;
|
2013-09-25 18:55:32 +00:00
|
|
|
s = va_arg( ap, const char * );
|
|
|
|
while ((c = *s++)) {
|
|
|
|
if (d + 2 > ed)
|
|
|
|
oob();
|
|
|
|
if (c == '\\' || c == '"')
|
|
|
|
*d++ = '\\';
|
|
|
|
*d++ = c;
|
|
|
|
}
|
2021-06-11 15:56:39 +00:00
|
|
|
l = d - bd;
|
|
|
|
if (l)
|
|
|
|
add_seg( bd, l );
|
2013-09-25 18:55:32 +00:00
|
|
|
} else { /* \\ cannot be combined with anything else. */
|
|
|
|
if (c == '.') {
|
|
|
|
c = *++fmt;
|
|
|
|
if (c != '*') {
|
|
|
|
fputs( "Fatal: unsupported string length specification. Please report a bug.\n", stderr );
|
|
|
|
abort();
|
|
|
|
}
|
2020-07-08 15:27:37 +00:00
|
|
|
maxlen = va_arg( ap, uint );
|
2013-09-25 18:55:32 +00:00
|
|
|
c = *++fmt;
|
|
|
|
}
|
|
|
|
if (c == 'c') {
|
|
|
|
if (d + 1 > ed)
|
|
|
|
oob();
|
2021-06-11 15:56:39 +00:00
|
|
|
add_seg( d, 1 );
|
2013-09-25 18:55:32 +00:00
|
|
|
*d++ = (char)va_arg( ap , int );
|
|
|
|
} else if (c == 's') {
|
|
|
|
s = va_arg( ap, const char * );
|
2016-12-29 13:34:30 +00:00
|
|
|
l = strnlen( s, maxlen );
|
2021-06-11 15:56:39 +00:00
|
|
|
if (l)
|
|
|
|
add_seg( s, l );
|
2013-09-25 18:55:32 +00:00
|
|
|
} else if (c == 'd') {
|
2021-06-11 15:56:39 +00:00
|
|
|
l = nfsnprintf( d, ed - d, "%d", va_arg( ap, int ) );
|
|
|
|
add_seg( d, l );
|
|
|
|
d += l;
|
2017-03-21 19:05:29 +00:00
|
|
|
} else if (c == 'u') {
|
2021-06-11 15:56:39 +00:00
|
|
|
l = nfsnprintf( d, ed - d, "%u", va_arg( ap, uint ) );
|
|
|
|
add_seg( d, l );
|
|
|
|
d += l;
|
2013-09-25 18:55:32 +00:00
|
|
|
} else {
|
|
|
|
fputs( "Fatal: unsupported format specifier. Please report a bug.\n", stderr );
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s = ++fmt;
|
|
|
|
} else {
|
|
|
|
fmt++;
|
|
|
|
}
|
|
|
|
}
|
2021-06-11 15:56:39 +00:00
|
|
|
char *out = d = nfmalloc( totlen + 1 );
|
|
|
|
for (int i = 0; i < nsegs; i++) {
|
|
|
|
memcpy( d, segs[i], segls[i] );
|
|
|
|
d += segls[i];
|
|
|
|
}
|
|
|
|
*d = 0;
|
|
|
|
return out;
|
2013-09-25 18:55:32 +00:00
|
|
|
}
|
|
|
|
|
2015-05-09 17:17:41 +00:00
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_exec( imap_store_t *ctx, imap_cmd_t *cmdp,
|
|
|
|
void (*done)( imap_store_t *ctx, imap_cmd_t *cmd, int response ),
|
2011-04-03 16:15:36 +00:00
|
|
|
const char *fmt, ... )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
2011-04-03 16:15:36 +00:00
|
|
|
if (!cmdp)
|
|
|
|
cmdp = new_imap_cmd( sizeof(*cmdp) );
|
|
|
|
cmdp->param.done = done;
|
2004-03-27 16:07:20 +00:00
|
|
|
va_start( ap, fmt );
|
2013-09-25 18:55:32 +00:00
|
|
|
cmdp->cmd = imap_vprintf( fmt, ap );
|
2004-03-27 16:07:20 +00:00
|
|
|
va_end( ap );
|
2015-05-09 17:17:41 +00:00
|
|
|
submit_imap_cmd( ctx, cmdp );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2011-04-03 16:15:36 +00:00
|
|
|
static void
|
|
|
|
transform_box_response( int *response )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2011-04-03 16:15:36 +00:00
|
|
|
switch (*response) {
|
|
|
|
case RESP_CANCEL: *response = DRV_CANCELED; break;
|
|
|
|
case RESP_NO: *response = DRV_BOX_BAD; break;
|
|
|
|
default: *response = DRV_OK; break;
|
|
|
|
}
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2011-04-03 16:15:36 +00:00
|
|
|
static void
|
|
|
|
imap_done_simple_box( imap_store_t *ctx ATTR_UNUSED,
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t *cmd, int response )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_simple_t *cmdp = (imap_cmd_simple_t *)cmd;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
|
|
|
transform_box_response( &response );
|
|
|
|
cmdp->callback( response, cmdp->callback_aux );
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2011-04-03 16:15:36 +00:00
|
|
|
static void
|
|
|
|
transform_msg_response( int *response )
|
|
|
|
{
|
|
|
|
switch (*response) {
|
|
|
|
case RESP_CANCEL: *response = DRV_CANCELED; break;
|
|
|
|
case RESP_NO: *response = DRV_MSG_BAD; break;
|
|
|
|
default: *response = DRV_OK; break;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-03 16:15:36 +00:00
|
|
|
static void
|
|
|
|
imap_done_simple_msg( imap_store_t *ctx ATTR_UNUSED,
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t *cmd, int response )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_simple_t *cmdp = (imap_cmd_simple_t *)cmd;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2011-04-03 16:15:36 +00:00
|
|
|
transform_msg_response( &response );
|
|
|
|
cmdp->callback( response, cmdp->callback_aux );
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
static imap_cmd_refcounted_state_t *
|
2020-07-08 15:27:37 +00:00
|
|
|
imap_refcounted_new_state( uint sz )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_cmd_refcounted_state_t *sts = nfmalloc( sz );
|
2011-04-03 16:15:36 +00:00
|
|
|
sts->ref_count = 1; /* so forced sync does not cause an early exit */
|
|
|
|
sts->ret_val = DRV_OK;
|
|
|
|
return sts;
|
|
|
|
}
|
|
|
|
|
2017-03-24 15:56:43 +00:00
|
|
|
#define INIT_REFCOUNTED_STATE(type, sts, cb, aux) \
|
|
|
|
type *sts = (type *)imap_refcounted_new_state( sizeof(type) ); \
|
|
|
|
sts->callback = cb; \
|
|
|
|
sts->callback_aux = aux;
|
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
static imap_cmd_t *
|
|
|
|
imap_refcounted_new_cmd( imap_cmd_refcounted_state_t *sts )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_refcounted_t *cmd = (imap_cmd_refcounted_t *)new_imap_cmd( sizeof(*cmd) );
|
2011-04-03 16:15:36 +00:00
|
|
|
cmd->state = sts;
|
|
|
|
sts->ref_count++;
|
|
|
|
return &cmd->gen;
|
|
|
|
}
|
|
|
|
|
2017-03-24 15:56:43 +00:00
|
|
|
#define DONE_REFCOUNTED_STATE(sts) \
|
2020-12-17 14:53:40 +00:00
|
|
|
if (!--sts->ref_count) { \
|
|
|
|
sts->callback( sts->ret_val, sts->callback_aux ); \
|
2017-03-24 15:56:43 +00:00
|
|
|
free( sts ); \
|
|
|
|
}
|
|
|
|
|
2018-11-24 13:15:53 +00:00
|
|
|
#define DONE_REFCOUNTED_STATE_ARGS(sts, finalize, ...) \
|
2020-12-17 14:53:40 +00:00
|
|
|
if (!--sts->ref_count) { \
|
2018-11-24 13:15:53 +00:00
|
|
|
finalize \
|
2020-12-17 14:53:40 +00:00
|
|
|
sts->callback( sts->ret_val, __VA_ARGS__, sts->callback_aux ); \
|
2017-03-24 15:56:43 +00:00
|
|
|
free( sts ); \
|
2011-03-20 17:23:09 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2011-06-13 10:13:31 +00:00
|
|
|
static void
|
2017-03-24 15:56:43 +00:00
|
|
|
transform_refcounted_box_response( imap_cmd_refcounted_state_t *sts, int response )
|
2011-06-13 10:13:31 +00:00
|
|
|
{
|
|
|
|
switch (response) {
|
|
|
|
case RESP_CANCEL:
|
|
|
|
sts->ret_val = DRV_CANCELED;
|
|
|
|
break;
|
|
|
|
case RESP_NO:
|
|
|
|
if (sts->ret_val == DRV_OK) /* Don't override cancelation. */
|
|
|
|
sts->ret_val = DRV_BOX_BAD;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-24 16:00:00 +00:00
|
|
|
static void
|
|
|
|
transform_refcounted_msg_response( imap_cmd_refcounted_state_t *sts, int response )
|
|
|
|
{
|
|
|
|
switch (response) {
|
|
|
|
case RESP_CANCEL:
|
|
|
|
sts->ret_val = DRV_CANCELED;
|
|
|
|
break;
|
|
|
|
case RESP_NO:
|
|
|
|
if (sts->ret_val == DRV_OK) /* Don't override cancelation. */
|
|
|
|
sts->ret_val = DRV_MSG_BAD;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-25 18:55:32 +00:00
|
|
|
static const char *
|
|
|
|
imap_strchr( const char *s, char tc )
|
|
|
|
{
|
|
|
|
for (;; s++) {
|
|
|
|
char c = *s;
|
|
|
|
if (c == '\\')
|
|
|
|
c = *++s;
|
|
|
|
if (!c)
|
2019-07-28 18:50:31 +00:00
|
|
|
return NULL;
|
2013-09-25 18:55:32 +00:00
|
|
|
if (c == tc)
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-15 09:24:57 +00:00
|
|
|
static char *
|
2013-09-25 16:53:18 +00:00
|
|
|
next_arg( char **ps )
|
2012-09-15 09:24:57 +00:00
|
|
|
{
|
2013-09-25 18:55:32 +00:00
|
|
|
char *ret, *s, *d;
|
|
|
|
char c;
|
2012-09-15 09:24:57 +00:00
|
|
|
|
2013-09-25 16:53:18 +00:00
|
|
|
assert( ps );
|
|
|
|
s = *ps;
|
|
|
|
if (!s)
|
2019-07-28 18:50:31 +00:00
|
|
|
return NULL;
|
2014-12-07 12:19:30 +00:00
|
|
|
while (isspace( (uchar)*s ))
|
2013-09-25 16:53:18 +00:00
|
|
|
s++;
|
|
|
|
if (!*s) {
|
2019-07-28 18:50:31 +00:00
|
|
|
*ps = NULL;
|
|
|
|
return NULL;
|
2012-09-15 09:24:57 +00:00
|
|
|
}
|
2013-09-25 16:53:18 +00:00
|
|
|
if (*s == '"') {
|
2013-09-25 18:55:32 +00:00
|
|
|
s++;
|
|
|
|
ret = d = s;
|
|
|
|
while ((c = *s++) != '"') {
|
|
|
|
if (c == '\\')
|
|
|
|
c = *s++;
|
|
|
|
if (!c) {
|
2019-07-28 18:50:31 +00:00
|
|
|
*ps = NULL;
|
|
|
|
return NULL;
|
2013-09-25 18:55:32 +00:00
|
|
|
}
|
|
|
|
*d++ = c;
|
|
|
|
}
|
|
|
|
*d = 0;
|
2012-09-15 09:24:57 +00:00
|
|
|
} else {
|
2013-09-25 16:53:18 +00:00
|
|
|
ret = s;
|
2013-09-25 18:55:32 +00:00
|
|
|
while ((c = *s)) {
|
2014-12-07 12:19:30 +00:00
|
|
|
if (isspace( (uchar)c )) {
|
2013-09-25 18:55:32 +00:00
|
|
|
*s++ = 0;
|
|
|
|
break;
|
|
|
|
}
|
2013-09-25 16:53:18 +00:00
|
|
|
s++;
|
2013-09-25 18:55:32 +00:00
|
|
|
}
|
2012-09-15 09:24:57 +00:00
|
|
|
}
|
2013-09-25 18:55:32 +00:00
|
|
|
if (!*s)
|
2019-07-28 18:50:31 +00:00
|
|
|
s = NULL;
|
2013-09-25 16:53:18 +00:00
|
|
|
|
|
|
|
*ps = s;
|
2012-09-15 09:24:57 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-12-04 10:23:47 +00:00
|
|
|
static int
|
|
|
|
is_opt_atom( list_t *list )
|
|
|
|
{
|
|
|
|
return list && list->val && list->val != LIST;
|
|
|
|
}
|
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
static int
|
|
|
|
is_atom( list_t *list )
|
|
|
|
{
|
|
|
|
return list && list->val && list->val != NIL && list->val != LIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
is_list( list_t *list )
|
|
|
|
{
|
|
|
|
return list && list->val == LIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_list( list_t *list )
|
|
|
|
{
|
|
|
|
list_t *tmp;
|
|
|
|
|
|
|
|
for (; list; list = tmp) {
|
|
|
|
tmp = list->next;
|
|
|
|
if (is_list( list ))
|
|
|
|
free_list( list->child );
|
|
|
|
else if (is_atom( list ))
|
|
|
|
free( list->val );
|
|
|
|
free( list );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-03 16:47:37 +00:00
|
|
|
enum {
|
|
|
|
LIST_OK,
|
|
|
|
LIST_PARTIAL,
|
|
|
|
LIST_BAD
|
|
|
|
};
|
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
static int
|
2011-04-03 16:47:37 +00:00
|
|
|
parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2011-04-03 16:47:37 +00:00
|
|
|
list_t *cur, **curp;
|
2013-09-25 18:55:32 +00:00
|
|
|
char *s = *sp, *d, *p;
|
2015-02-15 11:15:46 +00:00
|
|
|
int n, bytes;
|
2013-09-25 18:55:32 +00:00
|
|
|
char c;
|
2011-04-03 16:47:37 +00:00
|
|
|
|
|
|
|
assert( sts );
|
|
|
|
assert( sts->level > 0 );
|
|
|
|
curp = sts->stack[--sts->level];
|
|
|
|
bytes = sts->need_bytes;
|
|
|
|
if (bytes >= 0) {
|
|
|
|
sts->need_bytes = -1;
|
|
|
|
if (!bytes)
|
|
|
|
goto getline;
|
|
|
|
cur = (list_t *)((char *)curp - offsetof(list_t, next));
|
|
|
|
s = cur->val + cur->len - bytes;
|
|
|
|
goto getbytes;
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2012-10-16 07:27:58 +00:00
|
|
|
if (!s)
|
|
|
|
return LIST_BAD;
|
2004-03-27 16:07:20 +00:00
|
|
|
for (;;) {
|
2014-12-07 12:19:30 +00:00
|
|
|
while (isspace( (uchar)*s ))
|
2004-03-27 16:07:20 +00:00
|
|
|
s++;
|
2011-04-03 16:47:37 +00:00
|
|
|
if (sts->level && *s == ')') {
|
2004-03-27 16:07:20 +00:00
|
|
|
s++;
|
2011-04-03 16:47:37 +00:00
|
|
|
curp = sts->stack[--sts->level];
|
|
|
|
goto next;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
*curp = cur = nfmalloc( sizeof(*cur) );
|
2019-07-28 18:50:31 +00:00
|
|
|
cur->val = NULL; /* for clean bail */
|
2011-04-03 16:47:37 +00:00
|
|
|
curp = &cur->next;
|
2019-07-28 18:50:31 +00:00
|
|
|
*curp = NULL; /* ditto */
|
2004-03-27 16:07:20 +00:00
|
|
|
if (*s == '(') {
|
|
|
|
/* sublist */
|
2011-04-03 16:47:37 +00:00
|
|
|
if (sts->level == MAX_LIST_DEPTH)
|
|
|
|
goto bail;
|
2004-03-27 16:07:20 +00:00
|
|
|
s++;
|
|
|
|
cur->val = LIST;
|
2011-04-03 16:47:37 +00:00
|
|
|
sts->stack[sts->level++] = curp;
|
|
|
|
curp = &cur->child;
|
2019-07-28 18:50:31 +00:00
|
|
|
*curp = NULL; /* for clean bail */
|
2011-04-03 16:47:37 +00:00
|
|
|
goto next2;
|
2006-03-20 15:01:48 +00:00
|
|
|
} else if (ctx && *s == '{') {
|
2004-03-27 16:07:20 +00:00
|
|
|
/* literal */
|
2020-07-08 15:27:37 +00:00
|
|
|
bytes = (int)(cur->len = strtoul( s + 1, &s, 10 ));
|
2011-04-03 16:47:37 +00:00
|
|
|
if (*s != '}' || *++s)
|
2004-03-27 16:07:20 +00:00
|
|
|
goto bail;
|
CVE-2021-3657: reject excessively large IMAP literals
we didn't limit the 32-bit size of literals so far, which, given that we
use int-sized lengths & offsets, permitted all kinds of buffer
overflows. malicious/compromised servers may have been able to exploit
this. actual email senders would be constrained by size limits for
delivered mails, and to cause more than a crash they'd have to predict
the exact size of the final message.
we now limit to 2GB, which, given that we use unsigned ints since
e2d3b4d55 (v1.4.0), gives the handlers downstream plenty of headroom.
an alternative would have been using 64-bit offsets, but this seems like
major overkill, even if IMAP4rev2 recently mandated it (we talk only
IMAP4rev1, so we can ignore it).
2021-11-24 18:21:48 +00:00
|
|
|
if ((uint)bytes >= INT_MAX) {
|
|
|
|
error( "IMAP error: excessively large literal from %s "
|
|
|
|
"- THIS MIGHT BE AN ATTEMPT TO HACK YOU!\n", ctx->conn.name );
|
|
|
|
goto bail;
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2014-06-28 09:04:41 +00:00
|
|
|
s = cur->val = nfmalloc( cur->len + 1 );
|
|
|
|
s[cur->len] = 0;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2011-04-03 16:47:37 +00:00
|
|
|
getbytes:
|
2020-07-08 15:27:37 +00:00
|
|
|
n = socket_read( &ctx->conn, s, (uint)bytes );
|
2015-02-15 11:15:46 +00:00
|
|
|
if (n < 0) {
|
|
|
|
badeof:
|
|
|
|
error( "IMAP error: unexpected EOF from %s\n", ctx->conn.name );
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
bytes -= n;
|
2011-04-03 16:47:37 +00:00
|
|
|
if (bytes > 0)
|
|
|
|
goto postpone;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2015-03-23 07:42:51 +00:00
|
|
|
if (DFlags & DEBUG_NET_ALL) {
|
2013-12-08 15:37:20 +00:00
|
|
|
printf( "%s=========\n", ctx->label );
|
2010-02-06 09:40:36 +00:00
|
|
|
fwrite( cur->val, cur->len, 1, stdout );
|
2013-12-08 15:37:20 +00:00
|
|
|
printf( "%s=========\n", ctx->label );
|
2012-09-22 15:48:09 +00:00
|
|
|
fflush( stdout );
|
2010-02-06 09:40:36 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2011-04-03 16:47:37 +00:00
|
|
|
getline:
|
|
|
|
if (!(s = socket_read_line( &ctx->conn )))
|
|
|
|
goto postpone;
|
2015-02-15 11:15:46 +00:00
|
|
|
if (s == (void *)~0)
|
|
|
|
goto badeof;
|
2015-03-23 07:42:51 +00:00
|
|
|
if (DFlags & DEBUG_NET) {
|
2013-12-08 15:37:20 +00:00
|
|
|
printf( "%s%s\n", ctx->label, s );
|
2013-12-08 15:32:30 +00:00
|
|
|
fflush( stdout );
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
} else if (*s == '"') {
|
|
|
|
/* quoted string */
|
|
|
|
s++;
|
2013-09-25 18:55:32 +00:00
|
|
|
p = d = s;
|
|
|
|
while ((c = *s++) != '"') {
|
|
|
|
if (c == '\\')
|
|
|
|
c = *s++;
|
|
|
|
if (!c)
|
2004-03-27 16:07:20 +00:00
|
|
|
goto bail;
|
2013-09-25 18:55:32 +00:00
|
|
|
*d++ = c;
|
|
|
|
}
|
2020-07-08 15:27:37 +00:00
|
|
|
cur->len = (uint)(d - p);
|
2016-11-06 16:22:04 +00:00
|
|
|
cur->val = nfstrndup( p, cur->len );
|
2004-03-27 16:07:20 +00:00
|
|
|
} else {
|
|
|
|
/* atom */
|
|
|
|
p = s;
|
2014-12-07 12:19:30 +00:00
|
|
|
for (; *s && !isspace( (uchar)*s ); s++)
|
2011-04-03 16:47:37 +00:00
|
|
|
if (sts->level && *s == ')')
|
2004-03-27 16:07:20 +00:00
|
|
|
break;
|
2020-07-08 15:27:37 +00:00
|
|
|
cur->len = (uint)(s - p);
|
|
|
|
if (equals( p, (int)cur->len, "NIL", 3 ))
|
2004-03-27 16:07:20 +00:00
|
|
|
cur->val = NIL;
|
2016-11-06 16:22:04 +00:00
|
|
|
else
|
|
|
|
cur->val = nfstrndup( p, cur->len );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2011-04-03 16:47:37 +00:00
|
|
|
next:
|
|
|
|
if (!sts->level)
|
2004-03-27 16:07:20 +00:00
|
|
|
break;
|
2011-04-03 16:47:37 +00:00
|
|
|
next2:
|
2004-03-27 16:07:20 +00:00
|
|
|
if (!*s)
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
*sp = s;
|
2011-04-03 16:47:37 +00:00
|
|
|
return LIST_OK;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2011-04-03 16:47:37 +00:00
|
|
|
postpone:
|
|
|
|
if (sts->level < MAX_LIST_DEPTH) {
|
|
|
|
sts->stack[sts->level++] = curp;
|
|
|
|
sts->need_bytes = bytes;
|
|
|
|
return LIST_PARTIAL;
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
bail:
|
2011-04-03 16:47:37 +00:00
|
|
|
free_list( sts->head );
|
2021-02-14 22:06:24 +00:00
|
|
|
sts->level = 0;
|
2011-04-03 16:47:37 +00:00
|
|
|
return LIST_BAD;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2011-04-03 16:47:37 +00:00
|
|
|
static void
|
|
|
|
parse_list_init( parse_list_state_t *sts )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2011-04-03 16:47:37 +00:00
|
|
|
sts->need_bytes = -1;
|
|
|
|
sts->level = 1;
|
2019-07-28 18:50:31 +00:00
|
|
|
sts->head = NULL;
|
2011-04-03 16:47:37 +00:00
|
|
|
sts->stack[0] = &sts->head;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2013-07-27 12:31:13 +00:00
|
|
|
static int
|
|
|
|
parse_list_continue( imap_store_t *ctx, char *s )
|
|
|
|
{
|
|
|
|
list_t *list;
|
|
|
|
int resp;
|
|
|
|
if ((resp = parse_imap_list( ctx, &s, &ctx->parse_list_sts )) != LIST_PARTIAL) {
|
2019-07-28 18:50:31 +00:00
|
|
|
list = (resp == LIST_BAD) ? NULL : ctx->parse_list_sts.head;
|
|
|
|
ctx->parse_list_sts.head = NULL;
|
2013-07-27 12:31:13 +00:00
|
|
|
resp = ctx->parse_list_sts.callback( ctx, list, s );
|
2019-11-11 12:51:14 +00:00
|
|
|
free_list( list );
|
2013-07-27 12:31:13 +00:00
|
|
|
}
|
|
|
|
return resp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parse_list( imap_store_t *ctx, char *s, int (*cb)( imap_store_t *ctx, list_t *list, char *s ) )
|
|
|
|
{
|
|
|
|
parse_list_init( &ctx->parse_list_sts );
|
|
|
|
ctx->parse_list_sts.callback = cb;
|
|
|
|
return parse_list_continue( ctx, s );
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_namespace_rsp_p2( imap_store_t *, list_t *, char * );
|
|
|
|
static int parse_namespace_rsp_p3( imap_store_t *, list_t *, char * );
|
|
|
|
|
|
|
|
static int
|
2019-11-11 12:41:32 +00:00
|
|
|
parse_namespace_rsp( imap_store_t *ctx, list_t *list, char *s )
|
2013-07-27 12:31:13 +00:00
|
|
|
{
|
2019-11-11 12:41:32 +00:00
|
|
|
// We use only the 1st personal namespace. Making this configurable
|
|
|
|
// would not add value over just specifying Path.
|
|
|
|
|
|
|
|
if (!list) {
|
|
|
|
bad:
|
|
|
|
error( "IMAP error: malformed NAMESPACE response\n" );
|
|
|
|
return LIST_BAD;
|
|
|
|
}
|
|
|
|
if (list->val != NIL) {
|
2016-12-04 10:14:34 +00:00
|
|
|
if (list->val != LIST)
|
|
|
|
goto bad;
|
2019-11-11 12:41:32 +00:00
|
|
|
list_t *nsp_1st = list->child;
|
|
|
|
if (nsp_1st->val != LIST)
|
2016-12-04 10:14:34 +00:00
|
|
|
goto bad;
|
2019-11-11 12:41:32 +00:00
|
|
|
list_t *nsp_1st_ns = nsp_1st->child;
|
|
|
|
if (!is_atom( nsp_1st_ns ))
|
2016-12-04 10:14:34 +00:00
|
|
|
goto bad;
|
2019-11-11 12:41:32 +00:00
|
|
|
ctx->ns_prefix = nsp_1st_ns->val;
|
|
|
|
nsp_1st_ns->val = NULL;
|
|
|
|
list_t *nsp_1st_dl = nsp_1st_ns->next;
|
|
|
|
if (!is_opt_atom( nsp_1st_dl ))
|
|
|
|
goto bad;
|
|
|
|
if (is_atom( nsp_1st_dl ))
|
|
|
|
ctx->ns_delimiter = nsp_1st_dl->val[0];
|
|
|
|
// Namespace response extensions may follow here; we don't care.
|
2016-12-04 10:14:34 +00:00
|
|
|
}
|
2013-07-27 12:31:13 +00:00
|
|
|
|
|
|
|
return parse_list( ctx, s, parse_namespace_rsp_p2 );
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2019-11-11 12:51:14 +00:00
|
|
|
parse_namespace_rsp_p2( imap_store_t *ctx, list_t *list ATTR_UNUSED, char *s )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2013-07-27 12:31:13 +00:00
|
|
|
return parse_list( ctx, s, parse_namespace_rsp_p3 );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2019-11-11 12:51:14 +00:00
|
|
|
parse_namespace_rsp_p3( imap_store_t *ctx ATTR_UNUSED, list_t *list ATTR_UNUSED, char *s ATTR_UNUSED )
|
2013-07-27 12:31:13 +00:00
|
|
|
{
|
|
|
|
return LIST_OK;
|
|
|
|
}
|
|
|
|
|
2013-11-09 13:35:07 +00:00
|
|
|
static time_t
|
|
|
|
parse_date( const char *str )
|
|
|
|
{
|
|
|
|
char *end;
|
|
|
|
time_t date;
|
|
|
|
int hours, mins;
|
|
|
|
struct tm datetime;
|
|
|
|
|
2013-11-30 14:07:05 +00:00
|
|
|
memset( &datetime, 0, sizeof(datetime) );
|
2019-02-20 18:19:58 +00:00
|
|
|
if (!(end = strptime( str, "%e-%b-%Y %H:%M:%S ", &datetime )))
|
2013-11-09 13:35:07 +00:00
|
|
|
return -1;
|
2014-01-02 19:50:42 +00:00
|
|
|
if ((date = timegm( &datetime )) == -1)
|
2013-11-09 13:35:07 +00:00
|
|
|
return -1;
|
|
|
|
if (sscanf( end, "%3d%2d", &hours, &mins ) != 2)
|
|
|
|
return -1;
|
2014-01-02 19:50:42 +00:00
|
|
|
return date - (hours * 60 + mins) * 60;
|
2013-11-09 13:35:07 +00:00
|
|
|
}
|
|
|
|
|
2019-11-11 15:32:32 +00:00
|
|
|
static int
|
|
|
|
parse_fetched_flags( list_t *list, uchar *flags, uchar *status )
|
|
|
|
{
|
|
|
|
for (; list; list = list->next) {
|
|
|
|
if (!is_atom( list )) {
|
|
|
|
error( "IMAP error: unable to parse FLAGS list\n" );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (list->val[0] != '\\' && list->val[0] != '$')
|
|
|
|
continue;
|
|
|
|
if (!strcmp( "\\Recent", list->val )) {
|
|
|
|
*status |= M_RECENT;
|
|
|
|
goto flagok;
|
|
|
|
}
|
|
|
|
for (uint i = 0; i < as(Flags); i++) {
|
|
|
|
if (!strcmp( Flags[i], list->val )) {
|
|
|
|
*flags |= 1 << i;
|
|
|
|
goto flagok;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (list->val[0] == '$')
|
|
|
|
goto flagok; // Ignore unknown user-defined flags (keywords)
|
|
|
|
if (list->val[1] == 'X' && list->val[2] == '-')
|
|
|
|
goto flagok; // Ignore system flag extensions
|
|
|
|
warn( "IMAP warning: unknown system flag %s\n", list->val );
|
|
|
|
flagok: ;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-11-11 15:18:40 +00:00
|
|
|
static void
|
2020-08-05 15:43:19 +00:00
|
|
|
parse_fetched_header( char *val, uint uid, char **tuid, char **msgid, uint *msgid_len )
|
2019-11-11 15:18:40 +00:00
|
|
|
{
|
|
|
|
char *end;
|
|
|
|
int off, in_msgid = 0;
|
|
|
|
for (; (end = strchr( val, '\n' )); val = end + 1) {
|
|
|
|
int len = (int)(end - val);
|
|
|
|
if (len && end[-1] == '\r')
|
|
|
|
len--;
|
|
|
|
if (!len)
|
|
|
|
break;
|
|
|
|
if (starts_with_upper( val, len, "X-TUID: ", 8 )) {
|
|
|
|
if (len < 8 + TUIDL) {
|
|
|
|
warn( "IMAP warning: malformed X-TUID header (UID %u)\n", uid );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*tuid = val + 8;
|
|
|
|
in_msgid = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (starts_with_upper( val, len, "MESSAGE-ID:", 11 )) {
|
|
|
|
off = 11;
|
|
|
|
} else if (in_msgid) {
|
|
|
|
if (!isspace( val[0] )) {
|
|
|
|
in_msgid = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
off = 1;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
while (off < len && isspace( val[off] ))
|
|
|
|
off++;
|
|
|
|
if (off == len) {
|
|
|
|
in_msgid = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2020-08-05 15:43:19 +00:00
|
|
|
*msgid = val + off;
|
|
|
|
*msgid_len = (uint)(len - off);
|
2019-11-11 15:18:40 +00:00
|
|
|
in_msgid = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-27 12:31:13 +00:00
|
|
|
static int
|
|
|
|
parse_fetch_rsp( imap_store_t *ctx, list_t *list, char *s ATTR_UNUSED )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2019-11-11 15:32:32 +00:00
|
|
|
list_t *body = NULL, *tmp;
|
2019-11-11 13:29:42 +00:00
|
|
|
char *tuid = NULL, *msgid = NULL, *ep;
|
2004-03-27 16:07:20 +00:00
|
|
|
imap_message_t *cur;
|
|
|
|
msg_data_t *msgdata;
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t *cmdp;
|
2020-07-08 15:27:37 +00:00
|
|
|
uchar mask = 0, status = 0;
|
2020-08-05 16:06:08 +00:00
|
|
|
uint uid = 0, size = 0, msgid_len = 0;
|
2013-07-28 13:55:13 +00:00
|
|
|
time_t date = 0;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
if (!is_list( list )) {
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "IMAP error: bogus FETCH response\n" );
|
2013-07-27 12:31:13 +00:00
|
|
|
return LIST_BAD;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (tmp = list->child; tmp; tmp = tmp->next) {
|
2019-11-22 16:49:04 +00:00
|
|
|
if (!is_atom( tmp )) {
|
|
|
|
error( "IMAP error: bogus item name in FETCH response\n" );
|
2020-08-05 15:43:19 +00:00
|
|
|
return LIST_BAD;
|
2019-11-22 16:49:04 +00:00
|
|
|
}
|
2019-11-22 16:50:01 +00:00
|
|
|
const char *name = tmp->val;
|
|
|
|
tmp = tmp->next;
|
|
|
|
if (!strcmp( "UID", name )) {
|
2019-11-22 15:54:31 +00:00
|
|
|
if (!is_atom( tmp ) || (uid = strtoul( tmp->val, &ep, 10 ), *ep)) {
|
|
|
|
error( "IMAP error: unable to parse UID\n" );
|
2020-08-05 15:43:19 +00:00
|
|
|
return LIST_BAD;
|
2019-11-22 15:54:31 +00:00
|
|
|
}
|
2019-11-22 16:50:01 +00:00
|
|
|
} else if (!strcmp( "FLAGS", name )) {
|
2019-11-22 15:54:31 +00:00
|
|
|
if (!is_list( tmp )) {
|
|
|
|
error( "IMAP error: unable to parse FLAGS\n" );
|
2020-08-05 15:43:19 +00:00
|
|
|
return LIST_BAD;
|
2019-11-22 15:54:31 +00:00
|
|
|
}
|
|
|
|
if (!parse_fetched_flags( tmp->child, &mask, &status ))
|
2020-08-05 15:43:19 +00:00
|
|
|
return LIST_BAD;
|
2020-08-05 16:06:08 +00:00
|
|
|
status |= M_FLAGS;
|
2019-11-22 16:50:01 +00:00
|
|
|
} else if (!strcmp( "INTERNALDATE", name )) {
|
2019-11-22 15:54:31 +00:00
|
|
|
if (!is_atom( tmp )) {
|
|
|
|
error( "IMAP error: unable to parse INTERNALDATE\n" );
|
2020-08-05 15:43:19 +00:00
|
|
|
return LIST_BAD;
|
2019-11-22 15:54:31 +00:00
|
|
|
}
|
|
|
|
if ((date = parse_date( tmp->val )) == -1) {
|
|
|
|
error( "IMAP error: unable to parse INTERNALDATE format\n" );
|
2020-08-05 15:43:19 +00:00
|
|
|
return LIST_BAD;
|
2019-11-22 15:54:31 +00:00
|
|
|
}
|
2020-08-05 16:06:08 +00:00
|
|
|
status |= M_DATE;
|
2019-11-22 16:50:01 +00:00
|
|
|
} else if (!strcmp( "RFC822.SIZE", name )) {
|
2019-11-22 15:54:31 +00:00
|
|
|
if (!is_atom( tmp ) || (size = strtoul( tmp->val, &ep, 10 ), *ep)) {
|
|
|
|
error( "IMAP error: unable to parse RFC822.SIZE\n" );
|
2020-08-05 15:43:19 +00:00
|
|
|
return LIST_BAD;
|
2019-11-22 15:54:31 +00:00
|
|
|
}
|
2020-08-05 16:06:08 +00:00
|
|
|
status |= M_SIZE;
|
2019-12-29 13:37:53 +00:00
|
|
|
} else if (!strcmp( "BODY[]", name ) || !strcmp( "BODY[HEADER]", name )) {
|
2019-11-22 15:54:31 +00:00
|
|
|
if (!is_atom( tmp )) {
|
|
|
|
error( "IMAP error: unable to parse BODY[]\n" );
|
2020-08-05 15:43:19 +00:00
|
|
|
return LIST_BAD;
|
2019-11-22 15:54:31 +00:00
|
|
|
}
|
|
|
|
body = tmp;
|
2020-08-05 16:06:08 +00:00
|
|
|
status |= M_BODY;
|
2019-11-22 16:50:01 +00:00
|
|
|
} else if (!strcmp( "BODY[HEADER.FIELDS", name )) {
|
2019-11-22 15:54:31 +00:00
|
|
|
if (!is_list( tmp )) {
|
|
|
|
bfail:
|
|
|
|
error( "IMAP error: unable to parse BODY[HEADER.FIELDS ...]\n" );
|
2020-08-05 15:43:19 +00:00
|
|
|
return LIST_BAD;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2019-11-22 15:54:31 +00:00
|
|
|
tmp = tmp->next;
|
|
|
|
if (!is_atom( tmp ) || strcmp( tmp->val, "]" ))
|
|
|
|
goto bfail;
|
|
|
|
tmp = tmp->next;
|
|
|
|
if (!is_atom( tmp ))
|
|
|
|
goto bfail;
|
2020-08-05 15:43:19 +00:00
|
|
|
parse_fetched_header( tmp->val, uid, &tuid, &msgid, &msgid_len );
|
2020-08-05 16:06:08 +00:00
|
|
|
status |= M_HEADER;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-28 18:19:41 +00:00
|
|
|
if (!uid) {
|
|
|
|
// Ignore async flag updates for now.
|
2020-08-05 16:06:08 +00:00
|
|
|
status &= ~(M_FLAGS | M_RECENT);
|
|
|
|
} else if (status & M_BODY) {
|
2006-03-20 15:01:48 +00:00
|
|
|
for (cmdp = ctx->in_progress; cmdp; cmdp = cmdp->next)
|
2008-08-31 20:14:59 +00:00
|
|
|
if (cmdp->param.uid == uid)
|
2004-03-27 16:07:20 +00:00
|
|
|
goto gotuid;
|
2021-02-14 20:25:26 +00:00
|
|
|
error( "IMAP error: unexpected FETCH response with BODY (UID %u)\n", uid );
|
|
|
|
return LIST_BAD;
|
2004-03-27 16:07:20 +00:00
|
|
|
gotuid:
|
2017-04-02 13:42:18 +00:00
|
|
|
msgdata = ((imap_cmd_fetch_msg_t *)cmdp)->msg_data;
|
2019-11-11 13:29:42 +00:00
|
|
|
msgdata->data = body->val;
|
|
|
|
body->val = NULL; // Don't free together with list.
|
|
|
|
msgdata->len = body->len;
|
2013-07-28 13:55:13 +00:00
|
|
|
msgdata->date = date;
|
2004-03-27 16:07:20 +00:00
|
|
|
if (status & M_FLAGS)
|
|
|
|
msgdata->flags = mask;
|
2020-08-05 16:06:08 +00:00
|
|
|
status &= ~(M_FLAGS | M_RECENT | M_BODY | M_DATE);
|
2020-08-05 17:48:58 +00:00
|
|
|
} else if (ctx->fetch_sts == FetchUidNext) {
|
|
|
|
// Workaround for server not sending UIDNEXT and/or APPENDUID.
|
|
|
|
ctx->uidnext = uid + 1;
|
|
|
|
} else if (ctx->fetch_sts == FetchMsgs) {
|
2004-03-27 16:07:20 +00:00
|
|
|
cur = nfcalloc( sizeof(*cur) );
|
2020-12-17 14:53:40 +00:00
|
|
|
*ctx->msgapp = cur;
|
|
|
|
ctx->msgapp = &cur->next;
|
|
|
|
cur->uid = uid;
|
|
|
|
cur->flags = mask;
|
|
|
|
cur->status = status;
|
|
|
|
cur->size = size;
|
2020-08-05 15:43:19 +00:00
|
|
|
if (msgid)
|
2020-12-17 14:53:40 +00:00
|
|
|
cur->msgid = nfstrndup( msgid, msgid_len );
|
2011-04-10 11:06:07 +00:00
|
|
|
if (tuid)
|
2020-12-17 14:53:40 +00:00
|
|
|
memcpy( cur->tuid, tuid, TUIDL );
|
2020-08-05 16:06:08 +00:00
|
|
|
status &= ~(M_FLAGS | M_RECENT | M_SIZE | M_HEADER);
|
2020-08-05 17:48:58 +00:00
|
|
|
} else {
|
2021-02-14 20:25:26 +00:00
|
|
|
// These may come in as a result of STORE FLAGS despite .SILENT.
|
|
|
|
status &= ~(M_FLAGS | M_RECENT);
|
2020-08-05 16:06:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (status) {
|
|
|
|
error( "IMAP error: received extraneous data in FETCH response\n" );
|
|
|
|
return LIST_BAD;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2013-07-27 12:31:13 +00:00
|
|
|
return LIST_OK;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2006-03-20 15:01:48 +00:00
|
|
|
parse_capability( imap_store_t *ctx, char *cmd )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
|
|
|
char *arg;
|
2014-12-07 12:19:30 +00:00
|
|
|
uint i;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2014-07-12 19:02:25 +00:00
|
|
|
free_string_list( ctx->auth_mechs );
|
2019-07-28 18:50:31 +00:00
|
|
|
ctx->auth_mechs = NULL;
|
2006-03-20 15:01:48 +00:00
|
|
|
ctx->caps = 0x80000000;
|
2014-07-12 19:02:25 +00:00
|
|
|
while ((arg = next_arg( &cmd ))) {
|
2014-12-29 01:16:28 +00:00
|
|
|
if (starts_with( arg, -1, "AUTH=", 5 )) {
|
2014-07-12 19:02:25 +00:00
|
|
|
add_string_list( &ctx->auth_mechs, arg + 5 );
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < as(cap_list); i++)
|
|
|
|
if (!strcmp( cap_list[i], arg ))
|
|
|
|
ctx->caps |= 1 << i;
|
|
|
|
}
|
|
|
|
}
|
2020-12-17 14:53:40 +00:00
|
|
|
ctx->caps &= ~ctx->conf->server->cap_mask;
|
2014-07-12 19:02:25 +00:00
|
|
|
if (!CAP(NOLOGIN))
|
|
|
|
add_string_list( &ctx->auth_mechs, "LOGIN" );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-04-02 13:42:18 +00:00
|
|
|
parse_response_code( imap_store_t *ctx, imap_cmd_t *cmd, char *s )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2008-08-23 07:54:00 +00:00
|
|
|
char *arg, *earg, *p;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2012-10-16 07:27:58 +00:00
|
|
|
if (!s || *s != '[')
|
2004-03-27 16:07:20 +00:00
|
|
|
return RESP_OK; /* no response code */
|
|
|
|
s++;
|
2021-02-14 22:37:39 +00:00
|
|
|
if (!(arg = next_arg( &s ))) {
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "IMAP error: malformed response code\n" );
|
2012-07-15 10:55:04 +00:00
|
|
|
return RESP_CANCEL;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
if (!strcmp( "UIDVALIDITY", arg )) {
|
2008-08-23 07:54:00 +00:00
|
|
|
if (!(arg = next_arg( &s )) ||
|
2021-02-14 22:37:39 +00:00
|
|
|
(ctx->uidvalidity = strtoul( arg, &earg, 10 ), *earg != ']'))
|
2008-08-23 07:54:00 +00:00
|
|
|
{
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "IMAP error: malformed UIDVALIDITY status\n" );
|
2012-07-15 10:55:04 +00:00
|
|
|
return RESP_CANCEL;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
} else if (!strcmp( "UIDNEXT", arg )) {
|
2017-03-21 19:05:29 +00:00
|
|
|
if (!(arg = next_arg( &s )) ||
|
2021-02-14 22:37:39 +00:00
|
|
|
(ctx->uidnext = strtoul( arg, &earg, 10 ), *earg != ']'))
|
2017-03-21 19:05:29 +00:00
|
|
|
{
|
2019-11-23 12:30:12 +00:00
|
|
|
error( "IMAP error: malformed UIDNEXT status\n" );
|
2012-07-15 10:55:04 +00:00
|
|
|
return RESP_CANCEL;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
} else if (!strcmp( "CAPABILITY", arg )) {
|
2021-04-14 14:52:31 +00:00
|
|
|
if (!s || !(p = strchr( s, ']' ))) {
|
2021-02-14 22:37:39 +00:00
|
|
|
error( "IMAP error: malformed CAPABILITY status\n" );
|
|
|
|
return RESP_CANCEL;
|
|
|
|
}
|
|
|
|
*p = 0;
|
2006-03-20 15:01:48 +00:00
|
|
|
parse_capability( ctx, s );
|
2022-05-20 07:54:50 +00:00
|
|
|
if (strstr( p + 1, "mac.com IMAP4 service (Oracle Communications Messaging Server" ))
|
|
|
|
ctx->capability_hack = 1;
|
2021-02-14 22:37:39 +00:00
|
|
|
} else if (!strcmp( "ALERT]", arg )) {
|
2004-03-27 16:07:20 +00:00
|
|
|
/* RFC2060 says that these messages MUST be displayed
|
|
|
|
* to the user
|
|
|
|
*/
|
2021-02-14 22:37:39 +00:00
|
|
|
if (!s) {
|
|
|
|
error( "IMAP error: malformed ALERT status\n" );
|
|
|
|
return RESP_CANCEL;
|
|
|
|
}
|
|
|
|
for (; isspace( (uchar)*s ); s++);
|
|
|
|
error( "*** IMAP ALERT *** %s\n", s );
|
2021-04-14 14:58:27 +00:00
|
|
|
} else if (!strcmp( "APPENDUID", arg )) {
|
|
|
|
// The checks ensure that:
|
|
|
|
// - cmd => this is the final tagged response of a command, at which
|
|
|
|
// point cmd was already removed from ctx->in_progress, so param.uid
|
|
|
|
// is available for reuse.
|
|
|
|
// - !param.uid => the command isn't actually a FETCH. This doesn't
|
|
|
|
// really matter, as the field is safe to overwrite given the
|
|
|
|
// previous condition; it just has no effect for non-APPENDs.
|
|
|
|
if (!cmd || cmd->param.uid) {
|
|
|
|
error( "IMAP error: unexpected APPENDUID status\n" );
|
|
|
|
return RESP_CANCEL;
|
|
|
|
}
|
2008-08-23 07:54:00 +00:00
|
|
|
if (!(arg = next_arg( &s )) ||
|
2017-03-21 19:05:29 +00:00
|
|
|
(ctx->uidvalidity = strtoul( arg, &earg, 10 ), *earg) ||
|
2011-04-03 16:15:36 +00:00
|
|
|
!(arg = next_arg( &s )) ||
|
2021-06-03 09:04:56 +00:00
|
|
|
(cmd->param.uid = strtoul( arg, &earg, 10 ), *earg != ']'))
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "IMAP error: malformed APPENDUID status\n" );
|
2012-07-15 10:55:04 +00:00
|
|
|
return RESP_CANCEL;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2020-01-08 17:22:48 +00:00
|
|
|
} else if (!strcmp( "PERMANENTFLAGS", arg )) {
|
|
|
|
parse_list_init( &ctx->parse_list_sts );
|
2021-02-14 22:37:39 +00:00
|
|
|
if (parse_imap_list( NULL, &s, &ctx->parse_list_sts ) != LIST_OK || *s != ']') {
|
2020-01-08 17:22:48 +00:00
|
|
|
error( "IMAP error: malformed PERMANENTFLAGS status\n" );
|
|
|
|
return RESP_CANCEL;
|
|
|
|
}
|
|
|
|
int ret = RESP_OK;
|
|
|
|
for (list_t *tmp = ctx->parse_list_sts.head->child; tmp; tmp = tmp->next) {
|
|
|
|
if (!is_atom( tmp )) {
|
|
|
|
error( "IMAP error: malformed PERMANENTFLAGS status item\n" );
|
|
|
|
ret = RESP_CANCEL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!strcmp( tmp->val, "\\*" ) || !strcmp( tmp->val, "$Forwarded" )) {
|
|
|
|
ctx->has_forwarded = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free_list( ctx->parse_list_sts.head );
|
|
|
|
ctx->parse_list_sts.head = NULL;
|
|
|
|
return ret;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
return RESP_OK;
|
|
|
|
}
|
|
|
|
|
2019-05-28 15:27:09 +00:00
|
|
|
static int parse_list_rsp_p1( imap_store_t *, list_t *, char * );
|
2013-07-27 13:32:49 +00:00
|
|
|
static int parse_list_rsp_p2( imap_store_t *, list_t *, char * );
|
|
|
|
|
2012-10-16 07:27:58 +00:00
|
|
|
static int
|
2013-07-27 12:31:13 +00:00
|
|
|
parse_list_rsp( imap_store_t *ctx, list_t *list, char *cmd )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2013-07-27 12:31:13 +00:00
|
|
|
list_t *lp;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2013-07-27 13:35:42 +00:00
|
|
|
if (!is_list( list )) {
|
2014-04-12 12:58:18 +00:00
|
|
|
error( "IMAP error: malformed LIST response\n" );
|
2013-07-27 12:31:13 +00:00
|
|
|
return LIST_BAD;
|
|
|
|
}
|
2013-07-27 13:35:42 +00:00
|
|
|
for (lp = list->child; lp; lp = lp->next)
|
2019-11-11 12:51:14 +00:00
|
|
|
if (is_atom( lp ) && !strcasecmp( lp->val, "\\NoSelect" ))
|
2013-07-27 13:35:42 +00:00
|
|
|
return LIST_OK;
|
2019-05-28 15:27:09 +00:00
|
|
|
return parse_list( ctx, cmd, parse_list_rsp_p1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parse_list_rsp_p1( imap_store_t *ctx, list_t *list, char *cmd ATTR_UNUSED )
|
|
|
|
{
|
|
|
|
if (!is_opt_atom( list )) {
|
|
|
|
error( "IMAP error: malformed LIST response\n" );
|
|
|
|
return LIST_BAD;
|
|
|
|
}
|
|
|
|
if (!ctx->delimiter[0] && is_atom( list ))
|
|
|
|
ctx->delimiter[0] = list->val[0];
|
2013-07-27 13:32:49 +00:00
|
|
|
return parse_list( ctx, cmd, parse_list_rsp_p2 );
|
|
|
|
}
|
|
|
|
|
2019-09-09 16:47:32 +00:00
|
|
|
// Use this to check whether a full path refers to the actual IMAP INBOX.
|
2013-08-03 13:10:57 +00:00
|
|
|
static int
|
2014-10-04 15:07:17 +00:00
|
|
|
is_inbox( imap_store_t *ctx, const char *arg, int argl )
|
2019-09-09 16:47:32 +00:00
|
|
|
{
|
|
|
|
if (!starts_with_upper( arg, argl, "INBOX", 5 ))
|
|
|
|
return 0;
|
|
|
|
if (arg[5] && arg[5] != ctx->delimiter[0])
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use this to check whether a path fragment collides with the canonical INBOX.
|
|
|
|
static int
|
|
|
|
is_INBOX( imap_store_t *ctx, const char *arg, int argl )
|
2013-08-03 13:10:57 +00:00
|
|
|
{
|
2014-10-04 15:07:17 +00:00
|
|
|
if (!starts_with( arg, argl, "INBOX", 5 ))
|
2013-08-03 13:10:57 +00:00
|
|
|
return 0;
|
2015-05-23 08:47:48 +00:00
|
|
|
if (arg[5] && arg[5] != ctx->delimiter[0])
|
|
|
|
return 0;
|
2013-08-03 13:10:57 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-03-19 17:21:34 +00:00
|
|
|
static void
|
|
|
|
normalize_INBOX( imap_store_t *ctx, char *arg, int argl )
|
|
|
|
{
|
|
|
|
if (is_inbox( ctx, arg, argl ))
|
|
|
|
memcpy( arg, "INBOX", 5 );
|
|
|
|
}
|
|
|
|
|
2013-07-27 13:32:49 +00:00
|
|
|
static int
|
|
|
|
parse_list_rsp_p2( imap_store_t *ctx, list_t *list, char *cmd ATTR_UNUSED )
|
|
|
|
{
|
2013-08-03 13:10:57 +00:00
|
|
|
string_list_t *narg;
|
2021-02-14 19:42:37 +00:00
|
|
|
char *arg, c;
|
2020-07-08 15:27:37 +00:00
|
|
|
int argl;
|
|
|
|
uint l;
|
2013-07-27 13:32:49 +00:00
|
|
|
|
|
|
|
if (!is_atom( list )) {
|
|
|
|
error( "IMAP error: malformed LIST response\n" );
|
|
|
|
return LIST_BAD;
|
|
|
|
}
|
|
|
|
arg = list->val;
|
2020-07-08 15:27:37 +00:00
|
|
|
argl = (int)list->len;
|
2021-11-24 17:24:00 +00:00
|
|
|
if (argl > 1000) {
|
|
|
|
warn( "IMAP warning: ignoring unreasonably long mailbox name '%.100s[...]'\n", arg );
|
|
|
|
return LIST_OK;
|
|
|
|
}
|
2021-03-19 17:21:34 +00:00
|
|
|
// The server might be weird and have a non-uppercase INBOX. It
|
|
|
|
// may legitimately do so, but we need the canonical spelling.
|
|
|
|
normalize_INBOX( ctx, arg, argl );
|
2021-02-03 13:25:44 +00:00
|
|
|
if ((l = strlen( ctx->prefix ))) {
|
|
|
|
if (!starts_with( arg, argl, ctx->prefix, l )) {
|
2021-03-19 17:21:34 +00:00
|
|
|
if (!is_INBOX( ctx, arg, argl ))
|
|
|
|
return LIST_OK;
|
|
|
|
// INBOX and its subfolders bypass the namespace.
|
|
|
|
} else {
|
|
|
|
arg += l;
|
|
|
|
argl -= l;
|
|
|
|
// A folder named "INBOX" would be indistinguishable from the
|
|
|
|
// actual INBOX after prefix stripping, so drop it. This applies
|
|
|
|
// only to the fully uppercased spelling, as our canonical box
|
|
|
|
// names are case-sensitive (unlike IMAP's INBOX).
|
|
|
|
if (is_INBOX( ctx, arg, argl )) {
|
|
|
|
if (!arg[5]) // No need to complain about subfolders as well.
|
|
|
|
warn( "IMAP warning: ignoring INBOX in %s\n", ctx->prefix );
|
|
|
|
return LIST_OK;
|
2021-02-03 13:25:44 +00:00
|
|
|
}
|
2012-08-11 16:34:46 +00:00
|
|
|
}
|
2011-06-02 17:21:19 +00:00
|
|
|
}
|
2014-10-04 15:07:17 +00:00
|
|
|
if (argl >= 5 && !memcmp( arg + argl - 5, ".lock", 5 )) /* workaround broken servers */
|
2019-11-11 12:51:14 +00:00
|
|
|
return LIST_OK;
|
2013-08-03 13:10:57 +00:00
|
|
|
if (map_name( arg, (char **)&narg, offsetof(string_list_t, string), ctx->delimiter, "/") < 0) {
|
2012-08-11 16:34:46 +00:00
|
|
|
warn( "IMAP warning: ignoring mailbox %s (reserved character '/' in name)\n", arg );
|
2019-11-11 12:51:14 +00:00
|
|
|
return LIST_OK;
|
2012-08-11 16:34:46 +00:00
|
|
|
}
|
2021-02-14 19:42:37 +00:00
|
|
|
// Validate the normalized name. Technically speaking, we could tolerate
|
|
|
|
// '//' and '/./', and '/../' being forbidden is a limitation of the Maildir
|
|
|
|
// driver, but there isn't really a legitimate reason for these being present.
|
|
|
|
for (const char *p = narg->string, *sp = p;;) {
|
|
|
|
if (!(c = *p) || c == '/') {
|
|
|
|
uint pcl = (uint)(p - sp);
|
|
|
|
if (!pcl) {
|
|
|
|
error( "IMAP warning: ignoring mailbox '%s' due to empty name component\n", narg->string );
|
|
|
|
free( narg );
|
2021-02-21 20:26:54 +00:00
|
|
|
return LIST_OK;
|
2021-02-14 19:42:37 +00:00
|
|
|
}
|
|
|
|
if (pcl == 1 && sp[0] == '.') {
|
|
|
|
error( "IMAP warning: ignoring mailbox '%s' due to '.' component\n", narg->string );
|
|
|
|
free( narg );
|
2021-02-21 20:26:54 +00:00
|
|
|
return LIST_OK;
|
2021-02-14 19:42:37 +00:00
|
|
|
}
|
|
|
|
if (pcl == 2 && sp[0] == '.' && sp[1] == '.') {
|
|
|
|
error( "IMAP error: LIST'd mailbox name '%s' contains '..' component - THIS MIGHT BE AN ATTEMPT TO HACK YOU!\n", narg->string );
|
|
|
|
free( narg );
|
2021-02-21 20:26:54 +00:00
|
|
|
return LIST_BAD;
|
2021-02-14 19:42:37 +00:00
|
|
|
}
|
|
|
|
if (!c)
|
|
|
|
break;
|
|
|
|
sp = ++p;
|
|
|
|
} else {
|
|
|
|
++p;
|
|
|
|
}
|
|
|
|
}
|
2017-03-24 16:44:11 +00:00
|
|
|
narg->next = ctx->boxes;
|
|
|
|
ctx->boxes = narg;
|
2013-07-27 12:31:13 +00:00
|
|
|
return LIST_OK;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2012-08-11 16:34:46 +00:00
|
|
|
static int
|
2013-08-03 13:10:57 +00:00
|
|
|
prepare_name( char **buf, const imap_store_t *ctx, const char *prefix, const char *name )
|
2012-08-11 16:34:46 +00:00
|
|
|
{
|
2020-07-08 15:27:37 +00:00
|
|
|
uint pl = strlen( prefix );
|
2012-08-11 16:34:46 +00:00
|
|
|
|
2013-08-03 13:10:57 +00:00
|
|
|
switch (map_name( name, buf, pl, "/", ctx->delimiter )) {
|
2012-08-11 16:34:46 +00:00
|
|
|
case -1:
|
2013-08-03 13:10:57 +00:00
|
|
|
error( "IMAP error: mailbox name %s contains server's hierarchy delimiter\n", name );
|
2012-08-11 16:34:46 +00:00
|
|
|
return -1;
|
|
|
|
case -2:
|
|
|
|
error( "IMAP error: server's hierarchy delimiter not known\n" );
|
|
|
|
return -1;
|
|
|
|
default:
|
2013-08-03 13:10:57 +00:00
|
|
|
memcpy( *buf, prefix, pl );
|
2012-08-11 16:34:46 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2013-08-03 13:10:57 +00:00
|
|
|
prepare_box( char **buf, const imap_store_t *ctx )
|
2012-08-11 16:34:46 +00:00
|
|
|
{
|
2014-10-04 16:26:10 +00:00
|
|
|
const char *name = ctx->name;
|
2011-06-02 17:41:03 +00:00
|
|
|
const char *pfx = ctx->prefix;
|
|
|
|
|
|
|
|
if (starts_with_upper( name, -1, "INBOX", 5 ) && (!name[5] || name[5] == '/')) {
|
|
|
|
if (!memcmp( name, "INBOX", 5 )) {
|
|
|
|
pfx = "";
|
|
|
|
} else if (!*pfx) {
|
|
|
|
error( "IMAP error: cannot use unqualified '%s'. Did you mean INBOX?", name );
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return prepare_name( buf, ctx, pfx, name );
|
2012-08-11 16:34:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2013-08-03 13:10:57 +00:00
|
|
|
prepare_trash( char **buf, const imap_store_t *ctx )
|
2012-08-11 16:34:46 +00:00
|
|
|
{
|
2020-12-17 14:53:40 +00:00
|
|
|
return prepare_name( buf, ctx, ctx->prefix, ctx->conf->trash );
|
2012-08-11 16:34:46 +00:00
|
|
|
}
|
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
typedef union {
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
IMAP_CMD
|
|
|
|
imap_cmd_t *orig_cmd;
|
|
|
|
};
|
2017-04-02 13:42:18 +00:00
|
|
|
} imap_cmd_trycreate_t;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2011-03-13 11:06:49 +00:00
|
|
|
static void imap_open_store_greeted( imap_store_t * );
|
2017-04-02 13:42:18 +00:00
|
|
|
static void get_cmd_result_p2( imap_store_t *, imap_cmd_t *, int );
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2012-08-25 16:26:23 +00:00
|
|
|
static void
|
|
|
|
imap_socket_read( void *aux )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2012-08-25 16:26:23 +00:00
|
|
|
imap_store_t *ctx = (imap_store_t *)aux;
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t *cmdp, **pcmdp;
|
2004-03-27 16:07:20 +00:00
|
|
|
char *cmd, *arg, *arg1, *p;
|
2014-12-13 17:01:52 +00:00
|
|
|
int resp, resp2, tag;
|
2014-10-26 20:10:25 +00:00
|
|
|
conn_iovec_t iov[2];
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
for (;;) {
|
2013-07-27 12:31:13 +00:00
|
|
|
if (ctx->parse_list_sts.level) {
|
2019-07-28 18:50:31 +00:00
|
|
|
resp = parse_list_continue( ctx, NULL );
|
2013-07-27 12:31:13 +00:00
|
|
|
listret:
|
|
|
|
if (resp == LIST_PARTIAL)
|
|
|
|
return;
|
|
|
|
if (resp == LIST_BAD)
|
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
2012-08-25 16:26:23 +00:00
|
|
|
if (!(cmd = socket_read_line( &ctx->conn )))
|
|
|
|
return;
|
2015-02-15 11:15:46 +00:00
|
|
|
if (cmd == (void *)~0) {
|
|
|
|
if (!ctx->expectEOF)
|
|
|
|
error( "IMAP error: unexpected EOF from %s\n", ctx->conn.name );
|
|
|
|
/* A clean shutdown sequence ends with bad_callback as well (see imap_cleanup()). */
|
|
|
|
break;
|
|
|
|
}
|
2015-03-23 07:42:51 +00:00
|
|
|
if (DFlags & DEBUG_NET) {
|
2013-12-08 15:37:20 +00:00
|
|
|
printf( "%s%s\n", ctx->label, cmd );
|
2013-12-08 15:32:30 +00:00
|
|
|
fflush( stdout );
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
arg = next_arg( &cmd );
|
2012-10-16 07:27:58 +00:00
|
|
|
if (!arg) {
|
|
|
|
error( "IMAP error: empty response\n" );
|
|
|
|
break;
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
if (*arg == '*') {
|
|
|
|
arg = next_arg( &cmd );
|
|
|
|
if (!arg) {
|
2011-04-10 13:32:25 +00:00
|
|
|
error( "IMAP error: malformed untagged response\n" );
|
2012-07-15 10:55:04 +00:00
|
|
|
break;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2014-12-13 17:01:52 +00:00
|
|
|
if (ctx->greeting == GreetingPending && !strcmp( "PREAUTH", arg )) {
|
2019-07-28 18:50:31 +00:00
|
|
|
parse_response_code( ctx, NULL, cmd );
|
2014-12-13 17:01:52 +00:00
|
|
|
ctx->greeting = GreetingPreauth;
|
|
|
|
dogreet:
|
|
|
|
imap_ref( ctx );
|
|
|
|
imap_open_store_greeted( ctx );
|
|
|
|
if (imap_deref( ctx ))
|
|
|
|
return;
|
2011-03-13 11:06:49 +00:00
|
|
|
} else if (!strcmp( "OK", arg )) {
|
2019-07-28 18:50:31 +00:00
|
|
|
parse_response_code( ctx, NULL, cmd );
|
2014-12-13 17:01:52 +00:00
|
|
|
if (ctx->greeting == GreetingPending) {
|
|
|
|
ctx->greeting = GreetingOk;
|
|
|
|
goto dogreet;
|
|
|
|
}
|
2014-12-13 16:57:43 +00:00
|
|
|
} else if (!strcmp( "BYE", arg )) {
|
2015-02-15 11:15:46 +00:00
|
|
|
if (!ctx->expectBYE) {
|
2014-12-13 17:01:52 +00:00
|
|
|
ctx->greeting = GreetingBad;
|
|
|
|
error( "IMAP error: unexpected BYE response: %s\n", cmd );
|
2015-02-15 11:15:46 +00:00
|
|
|
/* We just wait for the server to close the connection now. */
|
|
|
|
ctx->expectEOF = 1;
|
2022-06-06 09:55:37 +00:00
|
|
|
socket_expect_eof( &ctx->conn );
|
2015-02-15 11:15:46 +00:00
|
|
|
} else {
|
|
|
|
/* We still need to wait for the LOGOUT's tagged OK. */
|
2014-12-13 17:01:52 +00:00
|
|
|
}
|
|
|
|
} else if (ctx->greeting == GreetingPending) {
|
|
|
|
error( "IMAP error: bogus greeting response %s\n", arg );
|
|
|
|
break;
|
2014-12-13 16:57:43 +00:00
|
|
|
} else if (!strcmp( "NO", arg )) {
|
|
|
|
warn( "Warning from IMAP server: %s\n", cmd );
|
|
|
|
} else if (!strcmp( "BAD", arg )) {
|
|
|
|
error( "Error from IMAP server: %s\n", cmd );
|
2012-10-16 07:27:58 +00:00
|
|
|
} else if (!strcmp( "CAPABILITY", arg )) {
|
2006-03-20 15:01:48 +00:00
|
|
|
parse_capability( ctx, cmd );
|
2019-11-26 14:49:19 +00:00
|
|
|
} else if (!strcmp( "LIST", arg ) || !strcmp( "LSUB", arg )) {
|
2013-07-27 12:31:13 +00:00
|
|
|
resp = parse_list( ctx, cmd, parse_list_rsp );
|
|
|
|
goto listret;
|
2014-12-13 17:01:52 +00:00
|
|
|
} else if (!strcmp( "NAMESPACE", arg )) {
|
|
|
|
resp = parse_list( ctx, cmd, parse_namespace_rsp );
|
|
|
|
goto listret;
|
2012-10-16 07:27:58 +00:00
|
|
|
} else if ((arg1 = next_arg( &cmd ))) {
|
2004-03-27 16:07:20 +00:00
|
|
|
if (!strcmp( "EXISTS", arg1 ))
|
2017-03-24 17:09:40 +00:00
|
|
|
ctx->total_msgs = atoi( arg );
|
2020-08-05 18:58:53 +00:00
|
|
|
else if (!strcmp( "EXPUNGE", arg1 ))
|
|
|
|
ctx->total_msgs--;
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcmp( "RECENT", arg1 ))
|
2017-03-24 17:09:40 +00:00
|
|
|
ctx->recent_msgs = atoi( arg );
|
2004-03-27 16:07:20 +00:00
|
|
|
else if(!strcmp ( "FETCH", arg1 )) {
|
2013-07-27 12:31:13 +00:00
|
|
|
resp = parse_list( ctx, cmd, parse_fetch_rsp );
|
|
|
|
goto listret;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
} else {
|
2012-07-15 10:55:04 +00:00
|
|
|
error( "IMAP error: unrecognized untagged response '%s'\n", arg );
|
|
|
|
break; /* this may mean anything, so prefer not to spam the log */
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2012-08-25 16:26:23 +00:00
|
|
|
continue;
|
2006-03-20 15:01:48 +00:00
|
|
|
} else if (!ctx->in_progress) {
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" );
|
2012-07-15 10:55:04 +00:00
|
|
|
break; /* this may mean anything, so prefer not to spam the log */
|
2004-03-27 16:07:20 +00:00
|
|
|
} else if (*arg == '+') {
|
2019-11-16 16:14:57 +00:00
|
|
|
socket_expect_activity( &ctx->conn, 0 );
|
2015-03-21 11:18:56 +00:00
|
|
|
/* There can be any number of commands in flight, but only the last
|
|
|
|
* one can require a continuation, as it enforces a round-trip. */
|
2017-04-02 13:42:18 +00:00
|
|
|
cmdp = (imap_cmd_t *)((char *)ctx->in_progress_append -
|
|
|
|
offsetof(imap_cmd_t, next));
|
2008-08-31 20:14:59 +00:00
|
|
|
if (cmdp->param.data) {
|
2010-11-15 09:38:50 +00:00
|
|
|
if (cmdp->param.to_trash)
|
2012-08-25 16:26:23 +00:00
|
|
|
ctx->trashnc = TrashKnown; /* Can't get NO [TRYCREATE] any more. */
|
2017-01-28 17:26:12 +00:00
|
|
|
if (DFlags & DEBUG_NET_ALL) {
|
|
|
|
printf( "%s>>>>>>>>>\n", ctx->label );
|
|
|
|
fwrite( cmdp->param.data, cmdp->param.data_len, 1, stdout );
|
|
|
|
printf( "%s>>>>>>>>>\n", ctx->label );
|
|
|
|
fflush( stdout );
|
|
|
|
}
|
2014-10-26 20:10:25 +00:00
|
|
|
iov[0].buf = cmdp->param.data;
|
|
|
|
iov[0].len = cmdp->param.data_len;
|
|
|
|
iov[0].takeOwn = GiveOwn;
|
2019-07-28 18:50:31 +00:00
|
|
|
cmdp->param.data = NULL;
|
2015-02-15 17:13:05 +00:00
|
|
|
ctx->buffer_mem -= cmdp->param.data_len;
|
2014-10-26 20:10:25 +00:00
|
|
|
iov[1].buf = "\r\n";
|
|
|
|
iov[1].len = 2;
|
|
|
|
iov[1].takeOwn = KeepOwn;
|
2015-05-09 17:17:41 +00:00
|
|
|
socket_write( &ctx->conn, iov, 2 );
|
2008-08-31 20:14:59 +00:00
|
|
|
} else if (cmdp->param.cont) {
|
|
|
|
if (cmdp->param.cont( ctx, cmdp, cmd ))
|
2012-08-25 16:26:23 +00:00
|
|
|
return;
|
2004-03-27 16:07:20 +00:00
|
|
|
} else {
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "IMAP error: unexpected command continuation request\n" );
|
2012-07-15 10:55:04 +00:00
|
|
|
break;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2019-11-16 16:14:57 +00:00
|
|
|
socket_expect_activity( &ctx->conn, 1 );
|
2004-03-27 16:07:20 +00:00
|
|
|
} else {
|
|
|
|
tag = atoi( arg );
|
2006-03-20 15:01:48 +00:00
|
|
|
for (pcmdp = &ctx->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
|
2004-03-27 16:07:20 +00:00
|
|
|
if (cmdp->tag == tag)
|
|
|
|
goto gottag;
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "IMAP error: unexpected tag %s\n", arg );
|
2012-07-15 10:55:04 +00:00
|
|
|
break;
|
2004-03-27 16:07:20 +00:00
|
|
|
gottag:
|
|
|
|
if (!(*pcmdp = cmdp->next))
|
2006-03-20 15:01:48 +00:00
|
|
|
ctx->in_progress_append = pcmdp;
|
2015-04-06 14:49:33 +00:00
|
|
|
if (!--ctx->num_in_progress)
|
2019-11-16 16:14:57 +00:00
|
|
|
socket_expect_activity( &ctx->conn, 0 );
|
2004-03-27 16:07:20 +00:00
|
|
|
arg = next_arg( &cmd );
|
2012-10-16 07:27:58 +00:00
|
|
|
if (!arg) {
|
|
|
|
error( "IMAP error: malformed tagged response\n" );
|
|
|
|
break;
|
|
|
|
}
|
2010-11-15 09:38:50 +00:00
|
|
|
if (!strcmp( "OK", arg )) {
|
|
|
|
if (cmdp->param.to_trash)
|
2012-08-25 16:26:23 +00:00
|
|
|
ctx->trashnc = TrashKnown; /* Can't get NO [TRYCREATE] any more. */
|
2011-03-20 15:27:51 +00:00
|
|
|
resp = RESP_OK;
|
2010-11-15 09:38:50 +00:00
|
|
|
} else {
|
2004-03-27 16:07:20 +00:00
|
|
|
if (!strcmp( "NO", arg )) {
|
2014-12-29 00:42:17 +00:00
|
|
|
if (cmdp->param.create && cmd && starts_with( cmd, -1, "[TRYCREATE]", 11 )) { /* APPEND or UID COPY */
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_trycreate_t *cmd2 =
|
|
|
|
(imap_cmd_trycreate_t *)new_imap_cmd( sizeof(*cmd2) );
|
2011-04-03 16:15:36 +00:00
|
|
|
cmd2->orig_cmd = cmdp;
|
2020-12-17 14:53:40 +00:00
|
|
|
cmd2->param.high_prio = 1;
|
2004-03-27 16:07:20 +00:00
|
|
|
p = strchr( cmdp->cmd, '"' );
|
2015-05-09 17:17:41 +00:00
|
|
|
imap_exec( ctx, &cmd2->gen, get_cmd_result_p2,
|
|
|
|
"CREATE %.*s", imap_strchr( p + 1, '"' ) - p + 1, p );
|
2004-03-27 16:07:20 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
resp = RESP_NO;
|
2014-12-29 00:42:17 +00:00
|
|
|
if (cmdp->param.failok)
|
|
|
|
goto doresp;
|
2004-03-27 16:07:20 +00:00
|
|
|
} else /*if (!strcmp( "BAD", arg ))*/
|
2012-07-15 10:55:04 +00:00
|
|
|
resp = RESP_CANCEL;
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "IMAP command '%s' returned an error: %s %s\n",
|
2015-05-09 15:06:24 +00:00
|
|
|
starts_with( cmdp->cmd, -1, "LOGIN", 5 ) ?
|
|
|
|
"LOGIN <user> <pass>" :
|
|
|
|
starts_with( cmdp->cmd, -1, "AUTHENTICATE PLAIN", 18 ) ?
|
|
|
|
"AUTHENTICATE PLAIN <authdata>" :
|
|
|
|
cmdp->cmd,
|
2006-03-19 11:29:12 +00:00
|
|
|
arg, cmd ? cmd : "" );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2014-12-29 00:42:17 +00:00
|
|
|
doresp:
|
2008-08-31 20:14:59 +00:00
|
|
|
if ((resp2 = parse_response_code( ctx, cmdp, cmd )) > resp)
|
2004-03-27 16:07:20 +00:00
|
|
|
resp = resp2;
|
2011-04-03 16:15:36 +00:00
|
|
|
imap_ref( ctx );
|
2012-07-15 10:55:04 +00:00
|
|
|
if (resp == RESP_CANCEL)
|
|
|
|
imap_invoke_bad_callback( ctx );
|
2020-07-28 14:14:00 +00:00
|
|
|
if (resp == RESP_OK && cmdp->param.wait_check) {
|
|
|
|
cmdp->next = NULL;
|
|
|
|
*ctx->wait_check_append = cmdp;
|
|
|
|
ctx->wait_check_append = &cmdp->next;
|
|
|
|
} else {
|
|
|
|
done_imap_cmd( ctx, cmdp, resp );
|
|
|
|
}
|
2011-04-03 16:15:36 +00:00
|
|
|
if (imap_deref( ctx ))
|
2012-08-25 16:26:23 +00:00
|
|
|
return;
|
|
|
|
if (ctx->canceling && !ctx->in_progress) {
|
|
|
|
ctx->canceling = 0;
|
|
|
|
ctx->callbacks.imap_cancel( ctx->callback_aux );
|
|
|
|
return;
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2015-05-09 17:17:41 +00:00
|
|
|
flush_imap_cmds( ctx );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2012-07-15 10:55:04 +00:00
|
|
|
imap_invoke_bad_callback( ctx );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2011-04-03 16:15:36 +00:00
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
get_cmd_result_p2( imap_store_t *ctx, imap_cmd_t *cmd, int response )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_trycreate_t *cmdp = (imap_cmd_trycreate_t *)cmd;
|
|
|
|
imap_cmd_t *ocmd = cmdp->orig_cmd;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
|
|
|
if (response != RESP_OK) {
|
2011-03-27 10:34:25 +00:00
|
|
|
done_imap_cmd( ctx, ocmd, response );
|
2011-04-03 16:15:36 +00:00
|
|
|
} else {
|
2020-07-28 14:14:00 +00:00
|
|
|
assert( !ocmd->param.wait_check );
|
2017-03-24 17:43:39 +00:00
|
|
|
ctx->uidnext = 1;
|
2012-08-25 16:26:23 +00:00
|
|
|
if (ocmd->param.to_trash)
|
|
|
|
ctx->trashnc = TrashKnown;
|
2011-04-03 16:15:36 +00:00
|
|
|
ocmd->param.create = 0;
|
2012-08-25 16:26:23 +00:00
|
|
|
ocmd->param.high_prio = 1;
|
|
|
|
submit_imap_cmd( ctx, ocmd );
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-03 16:21:46 +00:00
|
|
|
/******************* imap_cancel_store *******************/
|
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
static void
|
2006-03-20 19:38:20 +00:00
|
|
|
imap_cancel_store( store_t *gctx )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2006-03-20 15:01:48 +00:00
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2014-07-27 16:41:22 +00:00
|
|
|
#ifdef HAVE_LIBSASL
|
|
|
|
sasl_dispose( &ctx->sasl );
|
|
|
|
#endif
|
2011-01-23 13:06:03 +00:00
|
|
|
socket_close( &ctx->conn );
|
2020-07-28 14:14:00 +00:00
|
|
|
finalize_checked_imap_cmds( ctx, RESP_CANCEL );
|
2015-05-09 15:44:36 +00:00
|
|
|
cancel_sent_imap_cmds( ctx );
|
2012-08-25 16:26:23 +00:00
|
|
|
cancel_pending_imap_cmds( ctx );
|
2019-11-11 12:41:32 +00:00
|
|
|
free( ctx->ns_prefix );
|
2014-07-12 19:02:25 +00:00
|
|
|
free_string_list( ctx->auth_mechs );
|
2020-12-17 14:53:40 +00:00
|
|
|
free_generic_messages( &ctx->msgs->gen );
|
2020-08-04 08:10:47 +00:00
|
|
|
free_string_list( ctx->boxes );
|
2011-04-03 16:15:36 +00:00
|
|
|
imap_deref( ctx );
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
imap_deref( imap_store_t *ctx )
|
|
|
|
{
|
|
|
|
if (!--ctx->ref_count) {
|
|
|
|
free( ctx );
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2017-03-21 18:27:04 +00:00
|
|
|
static void
|
|
|
|
imap_set_bad_callback( store_t *gctx, void (*cb)( void *aux ), void *aux )
|
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
|
|
|
|
ctx->bad_callback = cb;
|
|
|
|
ctx->bad_callback_aux = aux;
|
|
|
|
}
|
|
|
|
|
2012-07-15 10:55:04 +00:00
|
|
|
static void
|
|
|
|
imap_invoke_bad_callback( imap_store_t *ctx )
|
|
|
|
{
|
2017-03-21 18:27:04 +00:00
|
|
|
ctx->bad_callback( ctx->bad_callback_aux );
|
2012-07-15 10:55:04 +00:00
|
|
|
}
|
|
|
|
|
2015-05-24 09:37:15 +00:00
|
|
|
/******************* imap_free_store *******************/
|
2011-04-03 16:21:46 +00:00
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
static imap_store_t *unowned;
|
2006-03-20 19:38:20 +00:00
|
|
|
|
2012-07-15 10:55:04 +00:00
|
|
|
static void
|
|
|
|
imap_cancel_unowned( void *gctx )
|
|
|
|
{
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_store_t *store, **storep;
|
2012-07-15 10:55:04 +00:00
|
|
|
|
|
|
|
for (storep = &unowned; (store = *storep); storep = &store->next)
|
|
|
|
if (store == gctx) {
|
|
|
|
*storep = store->next;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
imap_cancel_store( gctx );
|
|
|
|
}
|
|
|
|
|
2006-03-20 19:38:20 +00:00
|
|
|
static void
|
2015-05-24 09:37:15 +00:00
|
|
|
imap_free_store( store_t *gctx )
|
2006-03-20 19:38:20 +00:00
|
|
|
{
|
2017-03-24 17:09:40 +00:00
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
|
2020-07-28 14:14:00 +00:00
|
|
|
assert( !ctx->pending && !ctx->in_progress && !ctx->wait_check );
|
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
free_generic_messages( &ctx->msgs->gen );
|
2019-07-28 18:50:31 +00:00
|
|
|
ctx->msgs = NULL;
|
2017-03-21 18:27:04 +00:00
|
|
|
imap_set_bad_callback( gctx, imap_cancel_unowned, gctx );
|
2020-12-17 14:53:40 +00:00
|
|
|
ctx->next = unowned;
|
|
|
|
unowned = ctx;
|
2006-03-20 19:38:20 +00:00
|
|
|
}
|
|
|
|
|
2011-04-03 16:21:46 +00:00
|
|
|
/******************* imap_cleanup *******************/
|
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
static void imap_cleanup_p2( imap_store_t *, imap_cmd_t *, int );
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2006-03-20 19:38:20 +00:00
|
|
|
static void
|
|
|
|
imap_cleanup( void )
|
|
|
|
{
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_store_t *ctx, *nctx;
|
2006-03-20 19:38:20 +00:00
|
|
|
|
|
|
|
for (ctx = unowned; ctx; ctx = nctx) {
|
|
|
|
nctx = ctx->next;
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_set_bad_callback( &ctx->gen, (void (*)(void *))imap_cancel_store, ctx );
|
2015-09-06 20:56:38 +00:00
|
|
|
if (((imap_store_t *)ctx)->state != SST_BAD) {
|
|
|
|
((imap_store_t *)ctx)->expectBYE = 1;
|
2019-07-28 18:50:31 +00:00
|
|
|
imap_exec( (imap_store_t *)ctx, NULL, imap_cleanup_p2, "LOGOUT" );
|
2015-09-06 20:56:38 +00:00
|
|
|
} else {
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_cancel_store( &ctx->gen );
|
2015-09-06 20:56:38 +00:00
|
|
|
}
|
2006-03-20 19:38:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-03 16:15:36 +00:00
|
|
|
static void
|
|
|
|
imap_cleanup_p2( imap_store_t *ctx,
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t *cmd ATTR_UNUSED, int response )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2022-06-06 09:55:37 +00:00
|
|
|
if (response == RESP_NO) {
|
2011-04-03 16:15:36 +00:00
|
|
|
imap_cancel_store( &ctx->gen );
|
2022-06-06 09:55:37 +00:00
|
|
|
} else if (response == RESP_OK) {
|
2015-02-15 11:15:46 +00:00
|
|
|
ctx->expectEOF = 1;
|
2022-06-06 09:55:37 +00:00
|
|
|
socket_expect_eof( &ctx->conn );
|
|
|
|
}
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
|
|
|
|
2011-04-03 16:21:46 +00:00
|
|
|
/******************* imap_open_store *******************/
|
|
|
|
|
2012-08-25 16:26:23 +00:00
|
|
|
static void imap_open_store_connected( int, void * );
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
|
|
static void imap_open_store_tlsstarted1( int, void * );
|
|
|
|
#endif
|
2017-04-02 13:42:18 +00:00
|
|
|
static void imap_open_store_p2( imap_store_t *, imap_cmd_t *, int );
|
2011-04-03 16:15:36 +00:00
|
|
|
static void imap_open_store_authenticate( imap_store_t * );
|
|
|
|
#ifdef HAVE_LIBSSL
|
2017-04-02 13:42:18 +00:00
|
|
|
static void imap_open_store_authenticate_p2( imap_store_t *, imap_cmd_t *, int );
|
2012-08-25 16:26:23 +00:00
|
|
|
static void imap_open_store_tlsstarted2( int, void * );
|
2017-04-02 13:42:18 +00:00
|
|
|
static void imap_open_store_authenticate_p3( imap_store_t *, imap_cmd_t *, int );
|
2011-04-03 16:15:36 +00:00
|
|
|
#endif
|
|
|
|
static void imap_open_store_authenticate2( imap_store_t * );
|
2017-04-02 13:42:18 +00:00
|
|
|
static void imap_open_store_authenticate2_p2( imap_store_t *, imap_cmd_t *, int );
|
2021-12-09 10:42:40 +00:00
|
|
|
static void imap_open_store_authenticate2_p3( imap_store_t *, imap_cmd_t *, int );
|
2015-03-07 16:46:41 +00:00
|
|
|
static void imap_open_store_compress( imap_store_t * );
|
2014-12-13 11:09:15 +00:00
|
|
|
#ifdef HAVE_LIBZ
|
2017-04-02 13:42:18 +00:00
|
|
|
static void imap_open_store_compress_p2( imap_store_t *, imap_cmd_t *, int );
|
2014-12-13 11:09:15 +00:00
|
|
|
#endif
|
2015-05-17 15:07:54 +00:00
|
|
|
static void imap_open_store_namespace( imap_store_t * );
|
2017-04-02 13:42:18 +00:00
|
|
|
static void imap_open_store_namespace_p2( imap_store_t *, imap_cmd_t *, int );
|
2015-05-17 15:07:54 +00:00
|
|
|
static void imap_open_store_namespace2( imap_store_t * );
|
2011-04-03 16:15:36 +00:00
|
|
|
static void imap_open_store_finalize( imap_store_t * );
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
|
|
static void imap_open_store_ssl_bail( imap_store_t * );
|
|
|
|
#endif
|
2015-01-03 23:39:06 +00:00
|
|
|
static void imap_open_store_bail( imap_store_t *, int );
|
|
|
|
|
2015-05-24 09:37:15 +00:00
|
|
|
static store_t *
|
|
|
|
imap_alloc_store( store_conf_t *conf, const char *label )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
|
|
|
imap_store_conf_t *cfg = (imap_store_conf_t *)conf;
|
|
|
|
imap_server_conf_t *srvc = cfg->server;
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_store_t *ctx, **ctxp;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2015-05-24 09:37:15 +00:00
|
|
|
/* First try to recycle a whole store. */
|
2020-12-17 14:53:40 +00:00
|
|
|
for (ctxp = &unowned; (ctx = *ctxp); ctxp = &ctx->next)
|
|
|
|
if (ctx->state == SST_GOOD && ctx->conf == cfg) {
|
|
|
|
*ctxp = ctx->next;
|
2020-08-04 07:08:17 +00:00
|
|
|
goto gotstore;
|
2013-12-08 14:49:03 +00:00
|
|
|
}
|
2015-05-24 09:37:15 +00:00
|
|
|
|
|
|
|
/* Then try to recycle a server connection. */
|
2020-12-17 14:53:40 +00:00
|
|
|
for (ctxp = &unowned; (ctx = *ctxp); ctxp = &ctx->next)
|
|
|
|
if (ctx->state != SST_BAD && ctx->conf->server == srvc) {
|
|
|
|
*ctxp = ctx->next;
|
2020-08-04 08:10:47 +00:00
|
|
|
free_string_list( ctx->boxes );
|
|
|
|
ctx->boxes = NULL;
|
|
|
|
ctx->listed = 0;
|
2006-03-20 19:38:20 +00:00
|
|
|
/* One could ping the server here, but given that the idle timeout
|
|
|
|
* is at least 30 minutes, this sounds pretty pointless. */
|
2015-05-24 09:37:15 +00:00
|
|
|
ctx->state = SST_HALF;
|
|
|
|
goto gotsrv;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2015-05-24 09:37:15 +00:00
|
|
|
/* Finally, schedule opening a new server connection. */
|
2006-03-20 15:01:48 +00:00
|
|
|
ctx = nfcalloc( sizeof(*ctx) );
|
2020-12-17 14:53:40 +00:00
|
|
|
ctx->driver = &imap_driver;
|
2020-08-04 07:06:41 +00:00
|
|
|
ctx->ref_count = 1;
|
2015-05-24 09:37:15 +00:00
|
|
|
socket_init( &ctx->conn, &srvc->sconf,
|
|
|
|
(void (*)( void * ))imap_invoke_bad_callback,
|
|
|
|
imap_socket_read, (void (*)(void *))flush_imap_cmds, ctx );
|
|
|
|
ctx->in_progress_append = &ctx->in_progress;
|
|
|
|
ctx->pending_append = &ctx->pending;
|
2020-07-28 14:14:00 +00:00
|
|
|
ctx->wait_check_append = &ctx->wait_check;
|
2015-05-24 09:37:15 +00:00
|
|
|
|
|
|
|
gotsrv:
|
2020-12-17 14:53:40 +00:00
|
|
|
ctx->conf = cfg;
|
2020-08-04 07:08:17 +00:00
|
|
|
gotstore:
|
2013-12-08 15:37:20 +00:00
|
|
|
ctx->label = label;
|
2015-05-24 09:37:15 +00:00
|
|
|
return &ctx->gen;
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2015-05-24 09:37:15 +00:00
|
|
|
static void
|
|
|
|
imap_connect_store( store_t *gctx,
|
|
|
|
void (*cb)( int sts, void *aux ), void *aux )
|
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
|
|
|
|
if (ctx->state == SST_GOOD) {
|
|
|
|
cb( DRV_OK, aux );
|
|
|
|
} else {
|
|
|
|
ctx->callbacks.imap_open = cb;
|
|
|
|
ctx->callback_aux = aux;
|
|
|
|
if (ctx->state == SST_HALF)
|
|
|
|
imap_open_store_namespace( ctx );
|
|
|
|
else
|
|
|
|
socket_connect( &ctx->conn, imap_open_store_connected );
|
|
|
|
}
|
2012-08-25 16:26:23 +00:00
|
|
|
}
|
2011-03-27 14:50:32 +00:00
|
|
|
|
2012-08-25 16:26:23 +00:00
|
|
|
static void
|
|
|
|
imap_open_store_connected( int ok, void *aux )
|
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)aux;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2012-08-25 16:26:23 +00:00
|
|
|
if (!ok)
|
2015-01-03 23:39:06 +00:00
|
|
|
imap_open_store_bail( ctx, FAIL_WAIT );
|
2011-03-27 10:06:41 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
2020-12-17 14:53:40 +00:00
|
|
|
else if (ctx->conf->server->ssl_type == SSL_IMAPS)
|
2012-08-25 16:26:23 +00:00
|
|
|
socket_start_tls( &ctx->conn, imap_open_store_tlsstarted1 );
|
2006-05-28 15:43:58 +00:00
|
|
|
#endif
|
2015-04-06 14:49:33 +00:00
|
|
|
else
|
2019-11-16 16:14:57 +00:00
|
|
|
socket_expect_activity( &ctx->conn, 1 );
|
2012-08-25 16:26:23 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2012-08-25 16:26:23 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
|
|
|
static void
|
|
|
|
imap_open_store_tlsstarted1( int ok, void *aux )
|
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)aux;
|
|
|
|
|
|
|
|
if (!ok)
|
|
|
|
imap_open_store_ssl_bail( ctx );
|
2015-04-06 14:49:33 +00:00
|
|
|
else
|
2019-11-16 16:14:57 +00:00
|
|
|
socket_expect_activity( &ctx->conn, 1 );
|
2011-03-13 11:06:49 +00:00
|
|
|
}
|
2012-08-25 16:26:23 +00:00
|
|
|
#endif
|
2011-03-13 11:06:49 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
imap_open_store_greeted( imap_store_t *ctx )
|
|
|
|
{
|
2019-11-16 16:14:57 +00:00
|
|
|
socket_expect_activity( &ctx->conn, 0 );
|
2011-04-03 16:15:36 +00:00
|
|
|
if (!ctx->caps)
|
2019-07-28 18:50:31 +00:00
|
|
|
imap_exec( ctx, NULL, imap_open_store_p2, "CAPABILITY" );
|
2011-04-03 16:15:36 +00:00
|
|
|
else
|
|
|
|
imap_open_store_authenticate( ctx );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_open_store_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2013-03-23 09:34:51 +00:00
|
|
|
if (response == RESP_NO)
|
2015-01-03 23:39:06 +00:00
|
|
|
imap_open_store_bail( ctx, FAIL_FINAL );
|
2013-03-23 09:34:51 +00:00
|
|
|
else if (response == RESP_OK)
|
2011-04-03 16:15:36 +00:00
|
|
|
imap_open_store_authenticate( ctx );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_open_store_authenticate( imap_store_t *ctx )
|
|
|
|
{
|
2011-03-27 10:06:41 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_server_conf_t *srvc = ctx->conf->server;
|
don't ignore RequireSSL for PREAUTHenticated connections
such connections don't support STARTTLS. that is reasonable, as whatever
makes the connection preauthenticated (typically a Tunnel used to launch
imapd via a shell login) must already rely on the connection's security.
consequently, we would not try to use STARTTLS with such connections.
unfortunately, we'd also skip the RequireSSL check as a side effect.
this means that a rogue server (via a MITM attack) could simply offer a
preauthenticated connection to make us not use SSL, and thus bypass
server authentication. as a result, we could send potentially sensitive
data to the attacker:
- with Patterns used, we would send a LIST command which reveals the
remote Path setting. this isn't very useful to an attacker. also, IMAP
Accounts usually rely on the server-provided NAMESPACE to start with.
- with Create enabled for the remote Store, we would upload messages
from newly appeared local folders. this isn't a very likely situation,
unless the attacker manages to convince the victim to move/copy
interesting mails to a new folder right before the attack.
- with Expunge enabled for the local Store, previously synchronized
folders would be wiped. however, this would require the attacker to
know the correct UIDVALIDITY of each remote folder, which would
require incredible luck or convincing the victim to disclose them.
the first mismatch would likely tip off the victim.
in practice, someone with the level of technical and social engineering
skills required for this attack would very likely find more attractive
attack vectors. therefore, i don't consider this a particularly serious
issue.
configurations with UseIMAPS enabled or using a secure Tunnel were not
affected to start with.
a side effect of this fix is that most users of Tunnel will now need to
explicitly set RequireSSL to false.
an alternative approach would be defaulting all SSL-related settings to
off when Tunnel is used. this would be too invasive for a patch release,
but i'll consider it for 1.2.
see also CVE-2014-2567 for the Trojita MUA.
2014-07-05 20:52:40 +00:00
|
|
|
#endif
|
2011-04-03 16:15:36 +00:00
|
|
|
|
don't ignore RequireSSL for PREAUTHenticated connections
such connections don't support STARTTLS. that is reasonable, as whatever
makes the connection preauthenticated (typically a Tunnel used to launch
imapd via a shell login) must already rely on the connection's security.
consequently, we would not try to use STARTTLS with such connections.
unfortunately, we'd also skip the RequireSSL check as a side effect.
this means that a rogue server (via a MITM attack) could simply offer a
preauthenticated connection to make us not use SSL, and thus bypass
server authentication. as a result, we could send potentially sensitive
data to the attacker:
- with Patterns used, we would send a LIST command which reveals the
remote Path setting. this isn't very useful to an attacker. also, IMAP
Accounts usually rely on the server-provided NAMESPACE to start with.
- with Create enabled for the remote Store, we would upload messages
from newly appeared local folders. this isn't a very likely situation,
unless the attacker manages to convince the victim to move/copy
interesting mails to a new folder right before the attack.
- with Expunge enabled for the local Store, previously synchronized
folders would be wiped. however, this would require the attacker to
know the correct UIDVALIDITY of each remote folder, which would
require incredible luck or convincing the victim to disclose them.
the first mismatch would likely tip off the victim.
in practice, someone with the level of technical and social engineering
skills required for this attack would very likely find more attractive
attack vectors. therefore, i don't consider this a particularly serious
issue.
configurations with UseIMAPS enabled or using a secure Tunnel were not
affected to start with.
a side effect of this fix is that most users of Tunnel will now need to
explicitly set RequireSSL to false.
an alternative approach would be defaulting all SSL-related settings to
off when Tunnel is used. this would be too invasive for a patch release,
but i'll consider it for 1.2.
see also CVE-2014-2567 for the Trojita MUA.
2014-07-05 20:52:40 +00:00
|
|
|
if (ctx->greeting != GreetingPreauth) {
|
|
|
|
#ifdef HAVE_LIBSSL
|
2014-07-12 18:35:55 +00:00
|
|
|
if (srvc->ssl_type == SSL_STARTTLS) {
|
2004-03-27 16:07:20 +00:00
|
|
|
if (CAP(STARTTLS)) {
|
2019-07-28 18:50:31 +00:00
|
|
|
imap_exec( ctx, NULL, imap_open_store_authenticate_p2, "STARTTLS" );
|
2011-04-03 16:15:36 +00:00
|
|
|
return;
|
2004-03-27 16:07:20 +00:00
|
|
|
} else {
|
2014-07-12 18:35:55 +00:00
|
|
|
error( "IMAP error: SSL support not available\n" );
|
2015-01-03 23:39:06 +00:00
|
|
|
imap_open_store_bail( ctx, FAIL_FINAL );
|
2014-07-12 18:35:55 +00:00
|
|
|
return;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2011-04-03 16:15:36 +00:00
|
|
|
imap_open_store_authenticate2( ctx );
|
|
|
|
} else {
|
don't ignore RequireSSL for PREAUTHenticated connections
such connections don't support STARTTLS. that is reasonable, as whatever
makes the connection preauthenticated (typically a Tunnel used to launch
imapd via a shell login) must already rely on the connection's security.
consequently, we would not try to use STARTTLS with such connections.
unfortunately, we'd also skip the RequireSSL check as a side effect.
this means that a rogue server (via a MITM attack) could simply offer a
preauthenticated connection to make us not use SSL, and thus bypass
server authentication. as a result, we could send potentially sensitive
data to the attacker:
- with Patterns used, we would send a LIST command which reveals the
remote Path setting. this isn't very useful to an attacker. also, IMAP
Accounts usually rely on the server-provided NAMESPACE to start with.
- with Create enabled for the remote Store, we would upload messages
from newly appeared local folders. this isn't a very likely situation,
unless the attacker manages to convince the victim to move/copy
interesting mails to a new folder right before the attack.
- with Expunge enabled for the local Store, previously synchronized
folders would be wiped. however, this would require the attacker to
know the correct UIDVALIDITY of each remote folder, which would
require incredible luck or convincing the victim to disclose them.
the first mismatch would likely tip off the victim.
in practice, someone with the level of technical and social engineering
skills required for this attack would very likely find more attractive
attack vectors. therefore, i don't consider this a particularly serious
issue.
configurations with UseIMAPS enabled or using a secure Tunnel were not
affected to start with.
a side effect of this fix is that most users of Tunnel will now need to
explicitly set RequireSSL to false.
an alternative approach would be defaulting all SSL-related settings to
off when Tunnel is used. this would be too invasive for a patch release,
but i'll consider it for 1.2.
see also CVE-2014-2567 for the Trojita MUA.
2014-07-05 20:52:40 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
2014-07-12 18:35:55 +00:00
|
|
|
if (srvc->ssl_type == SSL_STARTTLS) {
|
don't ignore RequireSSL for PREAUTHenticated connections
such connections don't support STARTTLS. that is reasonable, as whatever
makes the connection preauthenticated (typically a Tunnel used to launch
imapd via a shell login) must already rely on the connection's security.
consequently, we would not try to use STARTTLS with such connections.
unfortunately, we'd also skip the RequireSSL check as a side effect.
this means that a rogue server (via a MITM attack) could simply offer a
preauthenticated connection to make us not use SSL, and thus bypass
server authentication. as a result, we could send potentially sensitive
data to the attacker:
- with Patterns used, we would send a LIST command which reveals the
remote Path setting. this isn't very useful to an attacker. also, IMAP
Accounts usually rely on the server-provided NAMESPACE to start with.
- with Create enabled for the remote Store, we would upload messages
from newly appeared local folders. this isn't a very likely situation,
unless the attacker manages to convince the victim to move/copy
interesting mails to a new folder right before the attack.
- with Expunge enabled for the local Store, previously synchronized
folders would be wiped. however, this would require the attacker to
know the correct UIDVALIDITY of each remote folder, which would
require incredible luck or convincing the victim to disclose them.
the first mismatch would likely tip off the victim.
in practice, someone with the level of technical and social engineering
skills required for this attack would very likely find more attractive
attack vectors. therefore, i don't consider this a particularly serious
issue.
configurations with UseIMAPS enabled or using a secure Tunnel were not
affected to start with.
a side effect of this fix is that most users of Tunnel will now need to
explicitly set RequireSSL to false.
an alternative approach would be defaulting all SSL-related settings to
off when Tunnel is used. this would be too invasive for a patch release,
but i'll consider it for 1.2.
see also CVE-2014-2567 for the Trojita MUA.
2014-07-05 20:52:40 +00:00
|
|
|
error( "IMAP error: SSL support not available\n" );
|
2015-01-03 23:39:06 +00:00
|
|
|
imap_open_store_bail( ctx, FAIL_FINAL );
|
don't ignore RequireSSL for PREAUTHenticated connections
such connections don't support STARTTLS. that is reasonable, as whatever
makes the connection preauthenticated (typically a Tunnel used to launch
imapd via a shell login) must already rely on the connection's security.
consequently, we would not try to use STARTTLS with such connections.
unfortunately, we'd also skip the RequireSSL check as a side effect.
this means that a rogue server (via a MITM attack) could simply offer a
preauthenticated connection to make us not use SSL, and thus bypass
server authentication. as a result, we could send potentially sensitive
data to the attacker:
- with Patterns used, we would send a LIST command which reveals the
remote Path setting. this isn't very useful to an attacker. also, IMAP
Accounts usually rely on the server-provided NAMESPACE to start with.
- with Create enabled for the remote Store, we would upload messages
from newly appeared local folders. this isn't a very likely situation,
unless the attacker manages to convince the victim to move/copy
interesting mails to a new folder right before the attack.
- with Expunge enabled for the local Store, previously synchronized
folders would be wiped. however, this would require the attacker to
know the correct UIDVALIDITY of each remote folder, which would
require incredible luck or convincing the victim to disclose them.
the first mismatch would likely tip off the victim.
in practice, someone with the level of technical and social engineering
skills required for this attack would very likely find more attractive
attack vectors. therefore, i don't consider this a particularly serious
issue.
configurations with UseIMAPS enabled or using a secure Tunnel were not
affected to start with.
a side effect of this fix is that most users of Tunnel will now need to
explicitly set RequireSSL to false.
an alternative approach would be defaulting all SSL-related settings to
off when Tunnel is used. this would be too invasive for a patch release,
but i'll consider it for 1.2.
see also CVE-2014-2567 for the Trojita MUA.
2014-07-05 20:52:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
2015-05-17 15:07:54 +00:00
|
|
|
imap_open_store_compress( ctx );
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2011-04-03 16:15:36 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_open_store_authenticate_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2013-03-23 09:34:51 +00:00
|
|
|
if (response == RESP_NO)
|
2015-01-03 23:39:06 +00:00
|
|
|
imap_open_store_bail( ctx, FAIL_FINAL );
|
2013-03-23 09:34:51 +00:00
|
|
|
else if (response == RESP_OK)
|
2012-08-25 16:26:23 +00:00
|
|
|
socket_start_tls( &ctx->conn, imap_open_store_tlsstarted2 );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_open_store_tlsstarted2( int ok, void *aux )
|
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)aux;
|
|
|
|
|
|
|
|
if (!ok)
|
2011-04-03 16:15:36 +00:00
|
|
|
imap_open_store_ssl_bail( ctx );
|
|
|
|
else
|
2019-07-28 18:50:31 +00:00
|
|
|
imap_exec( ctx, NULL, imap_open_store_authenticate_p3, "CAPABILITY" );
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_open_store_authenticate_p3( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2013-03-23 09:34:51 +00:00
|
|
|
if (response == RESP_NO)
|
2015-01-03 23:39:06 +00:00
|
|
|
imap_open_store_bail( ctx, FAIL_FINAL );
|
2013-03-23 09:34:51 +00:00
|
|
|
else if (response == RESP_OK)
|
2011-04-03 16:15:36 +00:00
|
|
|
imap_open_store_authenticate2( ctx );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-11-26 11:17:33 +00:00
|
|
|
static char *
|
|
|
|
cred_from_cmd( const char *cred, const char *cmd, const char *srv_name )
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
int ret;
|
|
|
|
char buffer[8192]; // Hopefully more than enough room for XOAUTH2, etc. tokens
|
|
|
|
|
|
|
|
if (*cmd == '+') {
|
|
|
|
flushn();
|
|
|
|
cmd++;
|
|
|
|
}
|
|
|
|
if (!(fp = popen( cmd, "r" ))) {
|
|
|
|
pipeerr:
|
|
|
|
sys_error( "Skipping account %s, %s failed", srv_name, cred );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!fgets( buffer, sizeof(buffer), fp ))
|
|
|
|
buffer[0] = 0;
|
|
|
|
if ((ret = pclose( fp )) < 0)
|
|
|
|
goto pipeerr;
|
|
|
|
if (ret) {
|
|
|
|
if (WIFSIGNALED( ret ))
|
|
|
|
error( "Skipping account %s, %s crashed\n", srv_name, cred );
|
|
|
|
else
|
|
|
|
error( "Skipping account %s, %s exited with status %d\n", srv_name, cred, WEXITSTATUS( ret ) );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!buffer[0]) {
|
|
|
|
error( "Skipping account %s, %s produced no output\n", srv_name, cred );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
buffer[strcspn( buffer, "\n" )] = 0; /* Strip trailing newline */
|
|
|
|
return nfstrdup( buffer );
|
|
|
|
}
|
|
|
|
|
2014-07-27 17:29:07 +00:00
|
|
|
static const char *
|
|
|
|
ensure_user( imap_server_conf_t *srvc )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
|
|
|
if (!srvc->user) {
|
2019-11-26 11:17:33 +00:00
|
|
|
if (srvc->user_cmd) {
|
|
|
|
srvc->user = cred_from_cmd( "UserCmd", srvc->user_cmd, srvc->name );
|
|
|
|
} else {
|
|
|
|
error( "Skipping account %s, no user\n", srvc->name );
|
|
|
|
}
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
2014-07-27 17:29:07 +00:00
|
|
|
return srvc->user;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
ensure_password( imap_server_conf_t *srvc )
|
|
|
|
{
|
2019-11-26 11:17:33 +00:00
|
|
|
if (!srvc->pass) {
|
|
|
|
if (srvc->pass_cmd) {
|
|
|
|
srvc->pass = cred_from_cmd( "PassCmd", srvc->pass_cmd, srvc->name );
|
2019-11-27 16:13:44 +00:00
|
|
|
#ifdef HAVE_MACOS_KEYCHAIN
|
|
|
|
} else if (srvc->use_keychain) {
|
|
|
|
void *password_data;
|
|
|
|
UInt32 password_length;
|
|
|
|
OSStatus ret = SecKeychainFindInternetPassword(
|
|
|
|
NULL, // keychainOrArray
|
|
|
|
strlen( srvc->sconf.host ), srvc->sconf.host,
|
|
|
|
0, NULL, // securityDomain
|
|
|
|
strlen( srvc->user ), srvc->user,
|
|
|
|
0, NULL, // path
|
|
|
|
0, // port - we could use it, but it seems pointless
|
|
|
|
kSecProtocolTypeIMAP,
|
|
|
|
kSecAuthenticationTypeDefault,
|
|
|
|
&password_length, &password_data,
|
|
|
|
NULL ); // itemRef
|
|
|
|
if (ret != errSecSuccess) {
|
|
|
|
CFStringRef errmsg = SecCopyErrorMessageString( ret, NULL );
|
|
|
|
error( "Looking up Keychain failed: %s\n",
|
|
|
|
CFStringGetCStringPtr( errmsg, kCFStringEncodingUTF8 ) );
|
|
|
|
CFRelease( errmsg );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
srvc->pass = nfstrndup( password_data, password_length );
|
|
|
|
SecKeychainItemFreeContent( NULL, password_data );
|
|
|
|
#endif /* HAVE_MACOS_KEYCHAIN */
|
2019-11-26 11:17:33 +00:00
|
|
|
} else {
|
2015-03-30 12:52:02 +00:00
|
|
|
flushn();
|
2019-11-26 11:17:33 +00:00
|
|
|
char prompt[80];
|
|
|
|
sprintf( prompt, "Password (%s): ", srvc->name );
|
|
|
|
char *pass = getpass( prompt );
|
|
|
|
if (!pass) {
|
|
|
|
perror( "getpass" );
|
|
|
|
exit( 1 );
|
|
|
|
}
|
|
|
|
if (!*pass) {
|
|
|
|
error( "Skipping account %s, no password\n", srvc->name );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* getpass() returns a pointer to a static buffer. Make a copy for long term storage. */
|
|
|
|
srvc->pass = nfstrdup( pass );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
2014-07-27 17:29:07 +00:00
|
|
|
return srvc->pass;
|
|
|
|
}
|
|
|
|
|
2014-07-27 16:41:22 +00:00
|
|
|
#ifdef HAVE_LIBSASL
|
|
|
|
|
|
|
|
static sasl_callback_t sasl_callbacks[] = {
|
2015-04-09 08:05:45 +00:00
|
|
|
{ SASL_CB_USER, NULL, NULL },
|
2015-03-30 10:59:40 +00:00
|
|
|
{ SASL_CB_AUTHNAME, NULL, NULL },
|
2014-07-27 16:41:22 +00:00
|
|
|
{ SASL_CB_PASS, NULL, NULL },
|
|
|
|
{ SASL_CB_LIST_END, NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
process_sasl_interact( sasl_interact_t *interact, imap_server_conf_t *srvc )
|
|
|
|
{
|
|
|
|
const char *val;
|
|
|
|
|
|
|
|
for (;; ++interact) {
|
|
|
|
switch (interact->id) {
|
|
|
|
case SASL_CB_LIST_END:
|
|
|
|
return 0;
|
2021-01-05 18:44:13 +00:00
|
|
|
case SASL_CB_USER: // aka authorization id - who to act as
|
|
|
|
case SASL_CB_AUTHNAME: // who is really logging in
|
2014-07-27 16:41:22 +00:00
|
|
|
val = ensure_user( srvc );
|
|
|
|
break;
|
|
|
|
case SASL_CB_PASS:
|
|
|
|
val = ensure_password( srvc );
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error( "Error: Unknown SASL interaction ID\n" );
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!val)
|
|
|
|
return -1;
|
|
|
|
interact->result = val;
|
|
|
|
interact->len = strlen( val );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2014-12-07 12:19:30 +00:00
|
|
|
process_sasl_step( imap_store_t *ctx, int rc, const char *in, uint in_len,
|
|
|
|
sasl_interact_t *interact, const char **out, uint *out_len )
|
2014-07-27 16:41:22 +00:00
|
|
|
{
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_server_conf_t *srvc = ctx->conf->server;
|
2014-07-27 16:41:22 +00:00
|
|
|
|
|
|
|
while (rc == SASL_INTERACT) {
|
|
|
|
if (process_sasl_interact( interact, srvc ) < 0)
|
|
|
|
return -1;
|
|
|
|
rc = sasl_client_step( ctx->sasl, in, in_len, &interact, out, out_len );
|
|
|
|
}
|
|
|
|
if (rc == SASL_CONTINUE) {
|
|
|
|
ctx->sasl_cont = 1;
|
|
|
|
} else if (rc == SASL_OK) {
|
|
|
|
ctx->sasl_cont = 0;
|
|
|
|
} else {
|
2021-01-05 18:45:51 +00:00
|
|
|
error( "Error performing SASL authentication step: %s\n", sasl_errdetail( ctx->sasl ) );
|
2014-07-27 16:41:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2014-12-07 12:19:30 +00:00
|
|
|
decode_sasl_data( const char *prompt, char **in, uint *in_len )
|
2014-07-27 16:41:22 +00:00
|
|
|
{
|
|
|
|
if (prompt) {
|
|
|
|
int rc;
|
2014-12-07 12:19:30 +00:00
|
|
|
uint prompt_len = strlen( prompt );
|
2014-07-27 16:41:22 +00:00
|
|
|
/* We're decoding, the output will be shorter than prompt_len. */
|
|
|
|
*in = nfmalloc( prompt_len );
|
|
|
|
rc = sasl_decode64( prompt, prompt_len, *in, prompt_len, in_len );
|
|
|
|
if (rc != SASL_OK) {
|
|
|
|
free( *in );
|
2021-01-05 18:45:51 +00:00
|
|
|
error( "Error decoding SASL prompt: %s\n", sasl_errstring( rc, NULL, NULL ) );
|
2014-07-27 16:41:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
*in = NULL;
|
|
|
|
*in_len = 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2014-12-07 12:19:30 +00:00
|
|
|
encode_sasl_data( const char *out, uint out_len, char **enc, uint *enc_len )
|
2014-07-27 16:41:22 +00:00
|
|
|
{
|
|
|
|
int rc;
|
2014-12-07 12:19:30 +00:00
|
|
|
uint enc_len_max = ((out_len + 2) / 3) * 4 + 1;
|
2014-07-27 16:41:22 +00:00
|
|
|
*enc = nfmalloc( enc_len_max );
|
|
|
|
rc = sasl_encode64( out, out_len, *enc, enc_len_max, enc_len );
|
|
|
|
if (rc != SASL_OK) {
|
|
|
|
free( *enc );
|
2021-01-05 18:45:51 +00:00
|
|
|
error( "Error encoding SASL response: %s\n", sasl_errstring( rc, NULL, NULL ) );
|
2014-07-27 16:41:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-04-02 13:42:18 +00:00
|
|
|
do_sasl_auth( imap_store_t *ctx, imap_cmd_t *cmdp ATTR_UNUSED, const char *prompt )
|
2014-07-27 16:41:22 +00:00
|
|
|
{
|
2014-10-26 20:10:25 +00:00
|
|
|
int rc, ret, iovcnt = 0;
|
2014-12-07 12:19:30 +00:00
|
|
|
uint in_len, out_len, enc_len;
|
2014-07-27 16:41:22 +00:00
|
|
|
const char *out;
|
|
|
|
char *in, *enc;
|
|
|
|
sasl_interact_t *interact = NULL;
|
2014-10-26 20:10:25 +00:00
|
|
|
conn_iovec_t iov[2];
|
2014-07-27 16:41:22 +00:00
|
|
|
|
|
|
|
if (!ctx->sasl_cont) {
|
|
|
|
error( "Error: IMAP wants more steps despite successful SASL authentication.\n" );
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
if (decode_sasl_data( prompt, &in, &in_len ) < 0)
|
|
|
|
goto bail;
|
|
|
|
rc = sasl_client_step( ctx->sasl, in, in_len, &interact, &out, &out_len );
|
|
|
|
ret = process_sasl_step( ctx, rc, in, in_len, interact, &out, &out_len );
|
|
|
|
free( in );
|
|
|
|
if (ret < 0)
|
|
|
|
goto bail;
|
|
|
|
|
|
|
|
if (out) {
|
|
|
|
if (encode_sasl_data( out, out_len, &enc, &enc_len ) < 0)
|
|
|
|
goto bail;
|
|
|
|
|
2014-10-26 20:10:25 +00:00
|
|
|
iov[0].buf = enc;
|
|
|
|
iov[0].len = enc_len;
|
|
|
|
iov[0].takeOwn = GiveOwn;
|
|
|
|
iovcnt = 1;
|
|
|
|
|
2015-03-23 07:42:51 +00:00
|
|
|
if (DFlags & DEBUG_NET) {
|
2014-07-27 16:41:22 +00:00
|
|
|
printf( "%s>+> %s\n", ctx->label, enc );
|
|
|
|
fflush( stdout );
|
|
|
|
}
|
|
|
|
} else {
|
2015-03-23 07:42:51 +00:00
|
|
|
if (DFlags & DEBUG_NET) {
|
2014-07-27 16:41:22 +00:00
|
|
|
printf( "%s>+>\n", ctx->label );
|
|
|
|
fflush( stdout );
|
|
|
|
}
|
|
|
|
}
|
2014-10-26 20:10:25 +00:00
|
|
|
iov[iovcnt].buf = "\r\n";
|
|
|
|
iov[iovcnt].len = 2;
|
|
|
|
iov[iovcnt].takeOwn = KeepOwn;
|
|
|
|
iovcnt++;
|
2015-05-09 17:17:41 +00:00
|
|
|
socket_write( &ctx->conn, iov, iovcnt );
|
|
|
|
return 0;
|
2014-07-27 16:41:22 +00:00
|
|
|
|
|
|
|
bail:
|
2015-01-03 23:39:06 +00:00
|
|
|
imap_open_store_bail( ctx, FAIL_FINAL );
|
2014-07-27 16:41:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
done_sasl_auth( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response )
|
2014-07-27 16:41:22 +00:00
|
|
|
{
|
|
|
|
if (response == RESP_OK && ctx->sasl_cont) {
|
|
|
|
sasl_interact_t *interact = NULL;
|
|
|
|
const char *out;
|
2014-12-07 12:19:30 +00:00
|
|
|
uint out_len;
|
2014-07-27 16:41:22 +00:00
|
|
|
int rc = sasl_client_step( ctx->sasl, NULL, 0, &interact, &out, &out_len );
|
|
|
|
if (process_sasl_step( ctx, rc, NULL, 0, interact, &out, &out_len ) < 0)
|
|
|
|
warn( "Warning: SASL reported failure despite successful IMAP authentication. Ignoring...\n" );
|
2019-02-05 10:23:44 +00:00
|
|
|
else if (out_len > 0)
|
2014-07-27 16:41:22 +00:00
|
|
|
warn( "Warning: SASL wants more steps despite successful IMAP authentication. Ignoring...\n" );
|
|
|
|
}
|
|
|
|
|
|
|
|
imap_open_store_authenticate2_p2( ctx, NULL, response );
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2014-07-27 17:29:07 +00:00
|
|
|
static void
|
|
|
|
imap_open_store_authenticate2( imap_store_t *ctx )
|
|
|
|
{
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_server_conf_t *srvc = ctx->conf->server;
|
2014-07-27 17:29:07 +00:00
|
|
|
string_list_t *mech, *cmech;
|
|
|
|
int auth_login = 0;
|
2016-12-03 18:18:12 +00:00
|
|
|
int skipped_login = 0;
|
2014-07-27 16:41:22 +00:00
|
|
|
#ifdef HAVE_LIBSASL
|
2016-12-03 19:58:23 +00:00
|
|
|
const char *saslavail;
|
2014-07-27 16:41:22 +00:00
|
|
|
char saslmechs[1024], *saslend = saslmechs;
|
2021-01-03 18:39:08 +00:00
|
|
|
int want_external = 0;
|
2014-07-27 16:41:22 +00:00
|
|
|
#endif
|
2014-07-27 17:29:07 +00:00
|
|
|
|
2019-11-26 11:17:33 +00:00
|
|
|
// Ensure that there are no leftovers from previous runs. This is needed in case
|
|
|
|
// the credentials have a timing dependency or otherwise lose validity after use.
|
|
|
|
if (srvc->user_cmd) {
|
|
|
|
free( srvc->user );
|
|
|
|
srvc->user = NULL;
|
|
|
|
}
|
|
|
|
if (srvc->pass_cmd) {
|
|
|
|
free( srvc->pass );
|
|
|
|
srvc->pass = NULL;
|
|
|
|
}
|
|
|
|
|
2014-07-27 17:29:07 +00:00
|
|
|
info( "Logging in...\n" );
|
2014-07-12 19:02:25 +00:00
|
|
|
for (mech = srvc->auth_mechs; mech; mech = mech->next) {
|
|
|
|
int any = !strcmp( mech->string, "*" );
|
|
|
|
for (cmech = ctx->auth_mechs; cmech; cmech = cmech->next) {
|
|
|
|
if (any || !strcasecmp( mech->string, cmech->string )) {
|
|
|
|
if (!strcasecmp( cmech->string, "LOGIN" )) {
|
|
|
|
#ifdef HAVE_LIBSSL
|
|
|
|
if (ctx->conn.ssl || !any)
|
2016-12-03 19:00:38 +00:00
|
|
|
#else
|
|
|
|
if (!any)
|
2014-07-12 19:02:25 +00:00
|
|
|
#endif
|
|
|
|
auth_login = 1;
|
2016-12-03 18:18:12 +00:00
|
|
|
else
|
|
|
|
skipped_login = 1;
|
2014-07-27 16:41:22 +00:00
|
|
|
#ifdef HAVE_LIBSASL
|
2016-12-03 13:32:51 +00:00
|
|
|
} else {
|
2020-07-08 15:27:37 +00:00
|
|
|
uint len = strlen( cmech->string );
|
2014-07-27 16:41:22 +00:00
|
|
|
if (saslend + len + 2 > saslmechs + sizeof(saslmechs))
|
|
|
|
oob();
|
|
|
|
*saslend++ = ' ';
|
|
|
|
memcpy( saslend, cmech->string, len + 1 );
|
|
|
|
saslend += len;
|
2021-01-03 18:39:08 +00:00
|
|
|
|
|
|
|
if (!strcasecmp( cmech->string, "EXTERNAL" ))
|
|
|
|
want_external = 1;
|
2014-07-27 16:41:22 +00:00
|
|
|
#endif
|
2014-07-12 19:02:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-07-27 16:41:22 +00:00
|
|
|
#ifdef HAVE_LIBSASL
|
|
|
|
if (saslend != saslmechs) {
|
|
|
|
int rc;
|
2014-12-07 12:19:30 +00:00
|
|
|
uint out_len = 0;
|
2014-07-27 16:41:22 +00:00
|
|
|
char *enc = NULL;
|
|
|
|
const char *gotmech = NULL, *out = NULL;
|
|
|
|
sasl_interact_t *interact = NULL;
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_t *cmd;
|
2014-07-27 16:41:22 +00:00
|
|
|
static int sasl_inited;
|
|
|
|
|
|
|
|
if (!sasl_inited) {
|
|
|
|
rc = sasl_client_init( sasl_callbacks );
|
|
|
|
if (rc != SASL_OK) {
|
|
|
|
saslbail:
|
2021-01-05 18:45:51 +00:00
|
|
|
error( "Error initializing SASL client: %s\n", sasl_errstring( rc, NULL, NULL ) );
|
2014-07-27 16:41:22 +00:00
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
sasl_inited = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = sasl_client_new( "imap", srvc->sconf.host, NULL, NULL, NULL, 0, &ctx->sasl );
|
|
|
|
if (rc != SASL_OK) {
|
2016-12-03 19:58:16 +00:00
|
|
|
if (rc == SASL_NOMECH)
|
|
|
|
goto notsasl;
|
2014-07-27 16:41:22 +00:00
|
|
|
if (!ctx->sasl)
|
|
|
|
goto saslbail;
|
2021-01-05 18:45:51 +00:00
|
|
|
error( "Error initializing SASL context: %s\n", sasl_errdetail( ctx->sasl ) );
|
2014-07-27 16:41:22 +00:00
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
2021-01-03 18:39:08 +00:00
|
|
|
// The built-in EXTERNAL mechanism wants the authentication id to be set
|
|
|
|
// even before instantiation; consequently it won't prompt for it, either.
|
|
|
|
// While this clearly makes sense on the server side, it arguably does not
|
|
|
|
// on the client side. Ah, well ...
|
|
|
|
if (want_external && ensure_user( srvc )) {
|
|
|
|
rc = sasl_setprop( ctx->sasl, SASL_AUTH_EXTERNAL, srvc->user );
|
|
|
|
if (rc != SASL_OK ) {
|
|
|
|
error( "Error setting SASL authentication id: %s\n", sasl_errdetail( ctx->sasl ) );
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-27 16:41:22 +00:00
|
|
|
rc = sasl_client_start( ctx->sasl, saslmechs + 1, &interact, CAP(SASLIR) ? &out : NULL, &out_len, &gotmech );
|
2016-12-03 19:58:16 +00:00
|
|
|
if (rc == SASL_NOMECH)
|
|
|
|
goto notsasl;
|
2014-07-27 16:41:22 +00:00
|
|
|
if (gotmech)
|
|
|
|
info( "Authenticating with SASL mechanism %s...\n", gotmech );
|
|
|
|
/* Technically, we are supposed to loop over sasl_client_start(),
|
|
|
|
* but it just calls sasl_client_step() anyway. */
|
|
|
|
if (process_sasl_step( ctx, rc, NULL, 0, interact, CAP(SASLIR) ? &out : NULL, &out_len ) < 0)
|
|
|
|
goto bail;
|
|
|
|
if (out) {
|
|
|
|
if (!out_len)
|
|
|
|
enc = nfstrdup( "=" ); /* A zero-length initial response is encoded as padding. */
|
|
|
|
else if (encode_sasl_data( out, out_len, &enc, NULL ) < 0)
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = new_imap_cmd( sizeof(*cmd) );
|
|
|
|
cmd->param.cont = do_sasl_auth;
|
2021-12-09 10:42:40 +00:00
|
|
|
ctx->caps = 0;
|
2014-07-27 16:41:22 +00:00
|
|
|
imap_exec( ctx, cmd, done_sasl_auth, enc ? "AUTHENTICATE %s %s" : "AUTHENTICATE %s", gotmech, enc );
|
|
|
|
free( enc );
|
|
|
|
return;
|
2016-12-03 19:58:16 +00:00
|
|
|
notsasl:
|
2019-03-10 10:30:05 +00:00
|
|
|
if (!ctx->sasl || sasl_listmech( ctx->sasl, NULL, "", " ", "", &saslavail, NULL, NULL ) != SASL_OK)
|
2021-01-05 18:44:13 +00:00
|
|
|
saslavail = "(none)";
|
2016-12-03 19:58:23 +00:00
|
|
|
if (!auth_login) {
|
|
|
|
error( "IMAP error: selected SASL mechanism(s) not available;\n"
|
|
|
|
" selected:%s\n available: %s\n", saslmechs, saslavail );
|
2016-12-03 18:18:12 +00:00
|
|
|
goto skipnote;
|
2016-12-03 19:58:23 +00:00
|
|
|
}
|
|
|
|
info( "NOT using available SASL mechanism(s): %s\n", saslavail );
|
2016-12-03 19:58:16 +00:00
|
|
|
sasl_dispose( &ctx->sasl );
|
2014-07-27 16:41:22 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
#endif
|
2014-07-12 19:02:25 +00:00
|
|
|
if (auth_login) {
|
2014-07-27 17:29:07 +00:00
|
|
|
if (!ensure_user( srvc ) || !ensure_password( srvc ))
|
|
|
|
goto bail;
|
2011-03-27 10:06:41 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
2014-07-12 19:02:25 +00:00
|
|
|
if (!ctx->conn.ssl)
|
2004-03-27 16:07:20 +00:00
|
|
|
#endif
|
2014-07-12 19:02:25 +00:00
|
|
|
warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
|
2021-12-09 10:42:40 +00:00
|
|
|
ctx->caps = 0;
|
2019-07-28 18:50:31 +00:00
|
|
|
imap_exec( ctx, NULL, imap_open_store_authenticate2_p2,
|
2014-07-12 19:02:25 +00:00
|
|
|
"LOGIN \"%\\s\" \"%\\s\"", srvc->user, srvc->pass );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
error( "IMAP error: server supports no acceptable authentication mechanism\n" );
|
2016-12-03 18:18:12 +00:00
|
|
|
#ifdef HAVE_LIBSASL
|
|
|
|
skipnote:
|
|
|
|
#endif
|
|
|
|
if (skipped_login)
|
|
|
|
error( "Note: not using LOGIN because connection is not encrypted;\n"
|
|
|
|
" use 'AuthMechs LOGIN' explicitly to force it.\n" );
|
2011-04-03 16:15:36 +00:00
|
|
|
|
|
|
|
bail:
|
2015-01-03 23:39:06 +00:00
|
|
|
imap_open_store_bail( ctx, FAIL_FINAL );
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_open_store_authenticate2_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response )
|
2021-12-09 10:42:40 +00:00
|
|
|
{
|
|
|
|
if (response == RESP_NO) {
|
|
|
|
imap_open_store_bail( ctx, FAIL_FINAL );
|
|
|
|
} else if (response == RESP_OK) {
|
2022-05-20 07:54:50 +00:00
|
|
|
// iCloud (imap.mail.me.com) apparently runs the real server behind a
|
|
|
|
// proxy that injects XAPPLEPUSHSERVICE into (and deletes STARTTLS from)
|
|
|
|
// the server's outgoing data stream following occurrences of CAPABILITY.
|
|
|
|
// This process is rather indiscriminate, so it will mess up IMAP
|
|
|
|
// literals if it is not deactivated in time by issuing a (redundant)
|
|
|
|
// CAPABILITY command after logging in.
|
|
|
|
if (!ctx->caps || ctx->capability_hack)
|
2021-12-09 10:42:40 +00:00
|
|
|
imap_exec( ctx, NULL, imap_open_store_authenticate2_p3, "CAPABILITY" );
|
|
|
|
else
|
|
|
|
imap_open_store_compress( ctx );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_open_store_authenticate2_p3( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2013-03-23 09:34:51 +00:00
|
|
|
if (response == RESP_NO)
|
2015-01-03 23:39:06 +00:00
|
|
|
imap_open_store_bail( ctx, FAIL_FINAL );
|
2013-03-23 09:34:51 +00:00
|
|
|
else if (response == RESP_OK)
|
2015-05-17 15:07:54 +00:00
|
|
|
imap_open_store_compress( ctx );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_open_store_compress( imap_store_t *ctx )
|
|
|
|
{
|
|
|
|
#ifdef HAVE_LIBZ
|
|
|
|
if (CAP(COMPRESS_DEFLATE)) {
|
2019-07-28 18:50:31 +00:00
|
|
|
imap_exec( ctx, NULL, imap_open_store_compress_p2, "COMPRESS DEFLATE" );
|
2015-05-17 15:07:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
imap_open_store_namespace( ctx );
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBZ
|
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_open_store_compress_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response )
|
2015-05-17 15:07:54 +00:00
|
|
|
{
|
|
|
|
if (response == RESP_NO) {
|
|
|
|
/* We already reported an error, but it's not fatal to us. */
|
|
|
|
imap_open_store_namespace( ctx );
|
|
|
|
} else if (response == RESP_OK) {
|
|
|
|
socket_start_deflate( &ctx->conn );
|
2011-04-03 16:15:36 +00:00
|
|
|
imap_open_store_namespace( ctx );
|
2015-05-17 15:07:54 +00:00
|
|
|
}
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
2015-05-17 15:07:54 +00:00
|
|
|
#endif
|
2011-04-03 16:15:36 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
imap_open_store_namespace( imap_store_t *ctx )
|
|
|
|
{
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_store_conf_t *cfg = ctx->conf;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2015-05-24 09:37:15 +00:00
|
|
|
ctx->state = SST_HALF;
|
2020-12-17 14:53:40 +00:00
|
|
|
ctx->prefix = cfg->path;
|
2017-10-15 14:46:49 +00:00
|
|
|
ctx->delimiter[0] = cfg->delimiter;
|
2014-10-04 11:12:50 +00:00
|
|
|
if (((!ctx->prefix && cfg->use_namespace) || !cfg->delimiter) && CAP(NAMESPACE)) {
|
2004-03-27 16:07:20 +00:00
|
|
|
/* get NAMESPACE info */
|
2011-04-03 16:15:36 +00:00
|
|
|
if (!ctx->got_namespace)
|
2019-07-28 18:50:31 +00:00
|
|
|
imap_exec( ctx, NULL, imap_open_store_namespace_p2, "NAMESPACE" );
|
2011-04-03 16:15:36 +00:00
|
|
|
else
|
|
|
|
imap_open_store_namespace2( ctx );
|
|
|
|
return;
|
|
|
|
}
|
2015-05-17 15:07:54 +00:00
|
|
|
imap_open_store_finalize( ctx );
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_open_store_namespace_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2013-03-23 09:34:51 +00:00
|
|
|
if (response == RESP_NO) {
|
2015-01-03 23:39:06 +00:00
|
|
|
imap_open_store_bail( ctx, FAIL_FINAL );
|
2013-03-23 09:34:51 +00:00
|
|
|
} else if (response == RESP_OK) {
|
2011-04-03 16:15:36 +00:00
|
|
|
ctx->got_namespace = 1;
|
|
|
|
imap_open_store_namespace2( ctx );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_open_store_namespace2( imap_store_t *ctx )
|
|
|
|
{
|
2020-12-17 14:53:40 +00:00
|
|
|
if (!ctx->prefix && ctx->conf->use_namespace)
|
2019-11-11 12:41:32 +00:00
|
|
|
ctx->prefix = ctx->ns_prefix;
|
|
|
|
if (!ctx->delimiter[0])
|
|
|
|
ctx->delimiter[0] = ctx->ns_delimiter;
|
2016-12-04 10:14:34 +00:00
|
|
|
imap_open_store_finalize( ctx );
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_open_store_finalize( imap_store_t *ctx )
|
|
|
|
{
|
2015-05-24 09:37:15 +00:00
|
|
|
ctx->state = SST_GOOD;
|
2014-10-04 11:12:50 +00:00
|
|
|
if (!ctx->prefix)
|
|
|
|
ctx->prefix = "";
|
2021-03-19 17:21:34 +00:00
|
|
|
else
|
|
|
|
normalize_INBOX( ctx, ctx->prefix, -1 );
|
2012-08-25 16:26:23 +00:00
|
|
|
ctx->trashnc = TrashUnknown;
|
2015-05-24 09:37:15 +00:00
|
|
|
ctx->callbacks.imap_open( DRV_OK, ctx->callback_aux );
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2011-03-27 10:06:41 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
2011-04-03 16:15:36 +00:00
|
|
|
static void
|
|
|
|
imap_open_store_ssl_bail( imap_store_t *ctx )
|
|
|
|
{
|
2010-10-03 09:53:18 +00:00
|
|
|
/* This avoids that we try to send LOGOUT to an unusable socket. */
|
2011-01-23 13:06:03 +00:00
|
|
|
socket_close( &ctx->conn );
|
2015-01-03 23:39:06 +00:00
|
|
|
imap_open_store_bail( ctx, FAIL_FINAL );
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
2010-10-03 09:53:18 +00:00
|
|
|
#endif
|
2011-04-03 16:15:36 +00:00
|
|
|
|
|
|
|
static void
|
2015-01-03 23:39:06 +00:00
|
|
|
imap_open_store_bail( imap_store_t *ctx, int failed )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2020-12-17 14:53:40 +00:00
|
|
|
ctx->conf->server->failed = (char)failed;
|
2015-05-24 09:37:15 +00:00
|
|
|
ctx->callbacks.imap_open( DRV_STORE_BAD, ctx->callback_aux );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2014-12-27 22:39:55 +00:00
|
|
|
/******************* imap_open_box *******************/
|
2011-04-03 16:21:46 +00:00
|
|
|
|
2014-12-27 22:39:55 +00:00
|
|
|
static int
|
|
|
|
imap_select_box( store_t *gctx, const char *name )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
|
2020-07-28 14:14:00 +00:00
|
|
|
assert( !ctx->pending && !ctx->in_progress && !ctx->wait_check );
|
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
free_generic_messages( &ctx->msgs->gen );
|
2019-07-28 18:50:31 +00:00
|
|
|
ctx->msgs = NULL;
|
2017-03-24 17:09:40 +00:00
|
|
|
ctx->msgapp = &ctx->msgs;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2014-10-04 16:26:10 +00:00
|
|
|
ctx->name = name;
|
2014-12-27 22:39:55 +00:00
|
|
|
return DRV_OK;
|
|
|
|
}
|
|
|
|
|
2017-03-24 13:06:19 +00:00
|
|
|
static const char *
|
|
|
|
imap_get_box_path( store_t *gctx ATTR_UNUSED )
|
|
|
|
{
|
2019-07-28 18:50:31 +00:00
|
|
|
return NULL;
|
2017-03-24 13:06:19 +00:00
|
|
|
}
|
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
typedef union {
|
2017-03-24 18:24:30 +00:00
|
|
|
imap_cmd_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
IMAP_CMD
|
|
|
|
void (*callback)( int sts, uint uidvalidity, void *aux );
|
|
|
|
void *callback_aux;
|
|
|
|
};
|
2017-03-24 18:24:30 +00:00
|
|
|
} imap_cmd_open_box_t;
|
|
|
|
|
2017-01-28 18:19:41 +00:00
|
|
|
static void imap_open_box_p2( imap_store_t *, imap_cmd_t *, int );
|
|
|
|
static void imap_open_box_p3( imap_store_t *, imap_cmd_t *, int );
|
2017-03-24 18:24:30 +00:00
|
|
|
static void imap_open_box_p4( imap_store_t *, imap_cmd_open_box_t *, int );
|
2017-01-28 18:19:41 +00:00
|
|
|
|
2014-12-27 22:39:55 +00:00
|
|
|
static void
|
2014-12-29 00:42:17 +00:00
|
|
|
imap_open_box( store_t *gctx,
|
2020-07-08 11:45:06 +00:00
|
|
|
void (*cb)( int sts, uint uidvalidity, void *aux ), void *aux )
|
2014-12-27 22:39:55 +00:00
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
2017-03-24 18:24:30 +00:00
|
|
|
imap_cmd_open_box_t *cmd;
|
2014-12-27 22:39:55 +00:00
|
|
|
char *buf;
|
|
|
|
|
2013-08-03 13:10:57 +00:00
|
|
|
if (prepare_box( &buf, ctx ) < 0) {
|
2017-03-24 18:24:30 +00:00
|
|
|
cb( DRV_BOX_BAD, UIDVAL_BAD, aux );
|
2012-08-11 16:34:46 +00:00
|
|
|
return;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2017-03-24 18:24:30 +00:00
|
|
|
ctx->uidvalidity = UIDVAL_BAD;
|
2017-03-24 17:43:39 +00:00
|
|
|
ctx->uidnext = 0;
|
2010-10-03 12:31:30 +00:00
|
|
|
|
2017-03-24 18:24:30 +00:00
|
|
|
INIT_IMAP_CMD(imap_cmd_open_box_t, cmd, cb, aux)
|
2020-12-17 14:53:40 +00:00
|
|
|
cmd->param.failok = 1;
|
2017-01-28 18:19:41 +00:00
|
|
|
imap_exec( ctx, &cmd->gen, imap_open_box_p2,
|
2013-09-25 18:55:32 +00:00
|
|
|
"SELECT \"%\\s\"", buf );
|
2013-08-03 13:10:57 +00:00
|
|
|
free( buf );
|
2011-07-23 14:06:32 +00:00
|
|
|
}
|
|
|
|
|
2017-01-28 18:19:41 +00:00
|
|
|
static void
|
|
|
|
imap_open_box_p2( imap_store_t *ctx, imap_cmd_t *gcmd, int response )
|
|
|
|
{
|
2017-03-24 18:24:30 +00:00
|
|
|
imap_cmd_open_box_t *cmdp = (imap_cmd_open_box_t *)gcmd;
|
|
|
|
imap_cmd_open_box_t *cmd;
|
2017-01-28 18:19:41 +00:00
|
|
|
|
2017-03-24 17:43:39 +00:00
|
|
|
if (response != RESP_OK || ctx->uidnext) {
|
2017-03-24 18:24:30 +00:00
|
|
|
imap_open_box_p4( ctx, cmdp, response );
|
2017-01-28 18:19:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-05 17:48:58 +00:00
|
|
|
assert( ctx->fetch_sts == FetchNone );
|
|
|
|
ctx->fetch_sts = FetchUidNext;
|
2017-03-24 18:24:30 +00:00
|
|
|
INIT_IMAP_CMD(imap_cmd_open_box_t, cmd, cmdp->callback, cmdp->callback_aux)
|
2017-01-28 18:19:41 +00:00
|
|
|
imap_exec( ctx, &cmd->gen, imap_open_box_p3,
|
2017-11-18 08:57:05 +00:00
|
|
|
"UID FETCH * (UID)" );
|
2017-01-28 18:19:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_open_box_p3( imap_store_t *ctx, imap_cmd_t *gcmd, int response )
|
|
|
|
{
|
2017-03-24 18:24:30 +00:00
|
|
|
imap_cmd_open_box_t *cmdp = (imap_cmd_open_box_t *)gcmd;
|
|
|
|
|
2020-08-05 17:48:58 +00:00
|
|
|
ctx->fetch_sts = FetchNone;
|
2019-11-23 12:51:20 +00:00
|
|
|
if (!ctx->uidnext) {
|
|
|
|
if (ctx->total_msgs) {
|
|
|
|
error( "IMAP error: querying server for highest UID failed\n" );
|
|
|
|
imap_open_box_p4( ctx, cmdp, RESP_NO );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// This is ok, the box is simply empty.
|
2017-03-24 17:43:39 +00:00
|
|
|
ctx->uidnext = 1;
|
2019-11-23 12:51:20 +00:00
|
|
|
}
|
2017-01-28 18:19:41 +00:00
|
|
|
|
2017-03-24 18:24:30 +00:00
|
|
|
imap_open_box_p4( ctx, cmdp, response );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_open_box_p4( imap_store_t *ctx, imap_cmd_open_box_t *cmdp, int response )
|
|
|
|
{
|
|
|
|
transform_box_response( &response );
|
|
|
|
cmdp->callback( response, ctx->uidvalidity, cmdp->callback_aux );
|
2017-01-28 18:19:41 +00:00
|
|
|
}
|
|
|
|
|
2020-07-08 11:45:06 +00:00
|
|
|
static uint
|
2017-03-24 17:43:39 +00:00
|
|
|
imap_get_uidnext( store_t *gctx )
|
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
|
|
|
|
return ctx->uidnext;
|
|
|
|
}
|
|
|
|
|
2020-01-08 17:22:48 +00:00
|
|
|
static xint
|
|
|
|
imap_get_supported_flags( store_t *gctx )
|
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
|
|
|
|
return ctx->has_forwarded ? 255 : (255 & ~F_FORWARDED);
|
|
|
|
}
|
|
|
|
|
2014-12-29 00:42:17 +00:00
|
|
|
/******************* imap_create_box *******************/
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_create_box( store_t *gctx,
|
|
|
|
void (*cb)( int sts, void *aux ), void *aux )
|
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_simple_t *cmd;
|
2014-12-29 00:42:17 +00:00
|
|
|
char *buf;
|
|
|
|
|
|
|
|
if (prepare_box( &buf, ctx ) < 0) {
|
|
|
|
cb( DRV_BOX_BAD, aux );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
INIT_IMAP_CMD(imap_cmd_simple_t, cmd, cb, aux)
|
2014-12-29 00:42:17 +00:00
|
|
|
imap_exec( ctx, &cmd->gen, imap_done_simple_box,
|
|
|
|
"CREATE \"%\\s\"", buf );
|
|
|
|
free( buf );
|
|
|
|
}
|
|
|
|
|
2014-12-29 01:08:48 +00:00
|
|
|
/******************* imap_delete_box *******************/
|
|
|
|
|
|
|
|
static int
|
|
|
|
imap_confirm_box_empty( store_t *gctx )
|
|
|
|
{
|
2017-03-24 17:09:40 +00:00
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
|
|
|
|
return ctx->total_msgs ? DRV_BOX_BAD : DRV_OK;
|
2014-12-29 01:08:48 +00:00
|
|
|
}
|
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
static void imap_delete_box_p2( imap_store_t *, imap_cmd_t *, int );
|
2014-12-29 01:08:48 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
imap_delete_box( store_t *gctx,
|
|
|
|
void (*cb)( int sts, void *aux ), void *aux )
|
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_simple_t *cmd;
|
2014-12-29 01:08:48 +00:00
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
INIT_IMAP_CMD(imap_cmd_simple_t, cmd, cb, aux)
|
2014-12-29 01:08:48 +00:00
|
|
|
imap_exec( ctx, &cmd->gen, imap_delete_box_p2, "CLOSE" );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_delete_box_p2( imap_store_t *ctx, imap_cmd_t *gcmd, int response )
|
2014-12-29 01:08:48 +00:00
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_simple_t *cmdp = (imap_cmd_simple_t *)gcmd;
|
|
|
|
imap_cmd_simple_t *cmd;
|
2014-12-29 01:08:48 +00:00
|
|
|
char *buf;
|
|
|
|
|
|
|
|
if (response != RESP_OK) {
|
|
|
|
imap_done_simple_box( ctx, &cmdp->gen, response );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prepare_box( &buf, ctx ) < 0) {
|
|
|
|
imap_done_simple_box( ctx, &cmdp->gen, RESP_NO );
|
|
|
|
return;
|
|
|
|
}
|
2017-04-02 13:42:18 +00:00
|
|
|
INIT_IMAP_CMD(imap_cmd_simple_t, cmd, cmdp->callback, cmdp->callback_aux)
|
2014-12-29 01:08:48 +00:00
|
|
|
imap_exec( ctx, &cmd->gen, imap_done_simple_box,
|
|
|
|
"DELETE \"%\\s\"", buf );
|
|
|
|
free( buf );
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
imap_finish_delete_box( store_t *gctx ATTR_UNUSED )
|
|
|
|
{
|
|
|
|
return DRV_OK;
|
|
|
|
}
|
|
|
|
|
2014-12-27 21:13:24 +00:00
|
|
|
/******************* imap_load_box *******************/
|
2011-04-03 16:21:46 +00:00
|
|
|
|
2020-07-08 15:27:37 +00:00
|
|
|
static uint
|
|
|
|
imap_prepare_load_box( store_t *gctx, uint opts )
|
2014-12-14 11:36:05 +00:00
|
|
|
{
|
2017-03-24 13:18:41 +00:00
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
|
|
|
|
ctx->opts = opts;
|
|
|
|
return opts;
|
2014-12-14 11:36:05 +00:00
|
|
|
}
|
|
|
|
|
2016-12-18 19:50:20 +00:00
|
|
|
enum { WantSize = 1, WantTuids = 2, WantMsgids = 4 };
|
2017-04-02 13:42:18 +00:00
|
|
|
typedef struct {
|
2020-07-08 15:27:37 +00:00
|
|
|
uint first, last;
|
|
|
|
int flags;
|
2016-12-18 20:24:16 +00:00
|
|
|
} imap_range_t;
|
|
|
|
|
2021-12-29 20:55:53 +00:00
|
|
|
#define MAX_RANGES 4
|
|
|
|
|
2016-12-18 20:24:16 +00:00
|
|
|
static void
|
2020-07-08 15:27:37 +00:00
|
|
|
imap_set_range( imap_range_t *ranges, uint *nranges, int low_flags, int high_flags, uint maxlow )
|
2016-12-18 20:24:16 +00:00
|
|
|
{
|
|
|
|
if (low_flags != high_flags) {
|
2020-07-08 15:27:37 +00:00
|
|
|
for (uint r = 0; r < *nranges; r++) {
|
2016-12-18 20:24:16 +00:00
|
|
|
if (ranges[r].first > maxlow)
|
|
|
|
break; /* Range starts above split point; so do all subsequent ranges. */
|
|
|
|
if (ranges[r].last < maxlow)
|
|
|
|
continue; /* Range ends below split point; try next one. */
|
|
|
|
if (ranges[r].last != maxlow) {
|
|
|
|
/* Range does not end exactly at split point; need to split. */
|
2021-12-29 20:55:53 +00:00
|
|
|
if (*nranges == MAX_RANGES)
|
|
|
|
oob();
|
2016-12-18 20:24:16 +00:00
|
|
|
memmove( &ranges[r + 1], &ranges[r], ((*nranges)++ - r) * sizeof(*ranges) );
|
|
|
|
ranges[r].last = maxlow;
|
|
|
|
ranges[r + 1].first = maxlow + 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-07-08 15:27:37 +00:00
|
|
|
for (uint r = 0; r < *nranges; r++)
|
2016-12-18 20:24:16 +00:00
|
|
|
ranges[r].flags |= (ranges[r].last <= maxlow) ? low_flags : high_flags;
|
|
|
|
}
|
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
typedef union {
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_cmd_refcounted_state_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
IMAP_CMD_REFCOUNTED_STATE
|
|
|
|
void (*callback)( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux );
|
|
|
|
void *callback_aux;
|
|
|
|
};
|
2017-03-24 15:56:43 +00:00
|
|
|
} imap_load_box_state_t;
|
|
|
|
|
|
|
|
static void imap_submit_load( imap_store_t *, const char *, int, imap_load_box_state_t * );
|
2017-03-24 17:09:40 +00:00
|
|
|
static void imap_submit_load_p3( imap_store_t *ctx, imap_load_box_state_t * );
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2011-07-23 14:06:32 +00:00
|
|
|
static void
|
2019-12-29 10:52:26 +00:00
|
|
|
imap_load_box( store_t *gctx, uint minuid, uint maxuid, uint finduid, uint pairuid, uint newuid, uint_array_t excs,
|
2017-03-24 17:09:40 +00:00
|
|
|
void (*cb)( int sts, message_t *msgs, int total_msgs, int recent_msgs, void *aux ), void *aux )
|
2011-07-23 14:06:32 +00:00
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
char buf[1000];
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2017-03-24 17:09:40 +00:00
|
|
|
if (!ctx->total_msgs) {
|
2016-11-04 20:48:58 +00:00
|
|
|
free( excs.data );
|
2019-07-28 18:50:31 +00:00
|
|
|
cb( DRV_OK, NULL, 0, 0, aux );
|
2011-04-03 16:15:36 +00:00
|
|
|
} else {
|
2020-08-05 17:48:58 +00:00
|
|
|
assert( ctx->fetch_sts == FetchNone );
|
|
|
|
ctx->fetch_sts = FetchMsgs;
|
|
|
|
|
2017-03-24 15:56:43 +00:00
|
|
|
INIT_REFCOUNTED_STATE(imap_load_box_state_t, sts, cb, aux)
|
2020-07-08 15:27:37 +00:00
|
|
|
for (uint i = 0; i < excs.size; ) {
|
|
|
|
for (int bl = 0; i < excs.size && bl < 960; i++) {
|
2004-03-27 16:07:20 +00:00
|
|
|
if (bl)
|
|
|
|
buf[bl++] = ',';
|
2017-03-21 19:05:29 +00:00
|
|
|
bl += sprintf( buf + bl, "%u", excs.data[i] );
|
2020-07-08 15:27:37 +00:00
|
|
|
uint j = i;
|
2016-11-04 20:48:58 +00:00
|
|
|
for (; i + 1 < excs.size && excs.data[i + 1] == excs.data[i] + 1; i++) {}
|
2004-03-27 16:07:20 +00:00
|
|
|
if (i != j)
|
2017-03-21 19:05:29 +00:00
|
|
|
bl += sprintf( buf + bl, ":%u", excs.data[i] );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2017-03-24 13:18:41 +00:00
|
|
|
imap_submit_load( ctx, buf, shifted_bit( ctx->opts, OPEN_OLD_IDS, WantMsgids ), sts );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2017-03-21 19:05:29 +00:00
|
|
|
if (maxuid == UINT_MAX)
|
2017-03-24 17:43:39 +00:00
|
|
|
maxuid = ctx->uidnext - 1;
|
2011-04-03 16:15:36 +00:00
|
|
|
if (maxuid >= minuid) {
|
2021-12-29 20:55:53 +00:00
|
|
|
imap_range_t ranges[MAX_RANGES];
|
2016-12-18 20:24:16 +00:00
|
|
|
ranges[0].first = minuid;
|
|
|
|
ranges[0].last = maxuid;
|
2016-12-18 20:22:52 +00:00
|
|
|
ranges[0].flags = 0;
|
2020-07-08 15:27:37 +00:00
|
|
|
uint nranges = 1;
|
2019-12-29 13:37:53 +00:00
|
|
|
if (ctx->opts & OPEN_NEW_SIZE)
|
|
|
|
imap_set_range( ranges, &nranges, 0, WantSize, newuid );
|
2017-03-24 13:18:41 +00:00
|
|
|
if (ctx->opts & OPEN_FIND)
|
2019-12-29 10:52:26 +00:00
|
|
|
imap_set_range( ranges, &nranges, 0, WantTuids, finduid - 1 );
|
2017-03-24 13:18:41 +00:00
|
|
|
if (ctx->opts & OPEN_OLD_IDS)
|
2019-12-29 10:52:26 +00:00
|
|
|
imap_set_range( ranges, &nranges, WantMsgids, 0, pairuid );
|
2020-07-08 15:27:37 +00:00
|
|
|
for (uint r = 0; r < nranges; r++) {
|
2017-03-21 19:05:29 +00:00
|
|
|
sprintf( buf, "%u:%u", ranges[r].first, ranges[r].last );
|
2016-12-18 20:24:16 +00:00
|
|
|
imap_submit_load( ctx, buf, ranges[r].flags, sts );
|
2011-04-10 11:06:07 +00:00
|
|
|
}
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
2016-11-04 20:48:58 +00:00
|
|
|
free( excs.data );
|
2017-03-24 17:09:40 +00:00
|
|
|
imap_submit_load_p3( ctx, sts );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
|
|
|
|
2018-11-24 13:15:53 +00:00
|
|
|
static int
|
|
|
|
imap_sort_msgs_comp( const void *a_, const void *b_ )
|
|
|
|
{
|
|
|
|
const message_t *a = *(const message_t * const *)a_;
|
|
|
|
const message_t *b = *(const message_t * const *)b_;
|
|
|
|
|
|
|
|
if (a->uid < b->uid)
|
|
|
|
return -1;
|
|
|
|
if (a->uid > b->uid)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_sort_msgs( imap_store_t *ctx )
|
|
|
|
{
|
2020-12-17 14:53:40 +00:00
|
|
|
uint count = count_generic_messages( &ctx->msgs->gen );
|
2018-11-24 13:15:53 +00:00
|
|
|
if (count <= 1)
|
|
|
|
return;
|
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_message_t **t = nfmalloc( sizeof(*t) * count );
|
2018-11-24 13:15:53 +00:00
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_message_t *m = ctx->msgs;
|
2020-07-08 15:27:37 +00:00
|
|
|
for (uint i = 0; i < count; i++) {
|
2018-11-24 13:15:53 +00:00
|
|
|
t[i] = m;
|
|
|
|
m = m->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort( t, count, sizeof(*t), imap_sort_msgs_comp );
|
|
|
|
|
|
|
|
ctx->msgs = t[0];
|
|
|
|
|
2020-07-08 15:27:37 +00:00
|
|
|
uint j;
|
2018-11-24 13:15:53 +00:00
|
|
|
for (j = 0; j < count - 1; j++)
|
|
|
|
t[j]->next = t[j + 1];
|
|
|
|
ctx->msgapp = &t[j]->next;
|
|
|
|
*ctx->msgapp = NULL;
|
|
|
|
|
|
|
|
free( t );
|
|
|
|
}
|
|
|
|
|
2017-03-24 15:56:43 +00:00
|
|
|
static void imap_submit_load_p2( imap_store_t *, imap_cmd_t *, int );
|
|
|
|
|
2015-05-09 17:17:41 +00:00
|
|
|
static void
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_submit_load( imap_store_t *ctx, const char *buf, int flags, imap_load_box_state_t *sts )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_exec( ctx, imap_refcounted_new_cmd( &sts->gen ), imap_submit_load_p2,
|
2016-12-18 19:50:20 +00:00
|
|
|
"UID FETCH %s (UID%s%s%s%s%s%s%s)", buf,
|
2017-03-24 13:18:41 +00:00
|
|
|
(ctx->opts & OPEN_FLAGS) ? " FLAGS" : "",
|
2016-12-18 20:24:16 +00:00
|
|
|
(flags & WantSize) ? " RFC822.SIZE" : "",
|
2016-12-18 19:50:20 +00:00
|
|
|
(flags & (WantTuids | WantMsgids)) ? " BODY.PEEK[HEADER.FIELDS (" : "",
|
|
|
|
(flags & WantTuids) ? "X-TUID" : "",
|
|
|
|
!(~flags & (WantTuids | WantMsgids)) ? " " : "",
|
|
|
|
(flags & WantMsgids) ? "MESSAGE-ID" : "",
|
|
|
|
(flags & (WantTuids | WantMsgids)) ? ")]" : "");
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
|
2017-03-24 15:56:43 +00:00
|
|
|
static void
|
2017-03-24 17:09:40 +00:00
|
|
|
imap_submit_load_p2( imap_store_t *ctx, imap_cmd_t *cmd, int response )
|
2017-03-24 15:56:43 +00:00
|
|
|
{
|
|
|
|
imap_load_box_state_t *sts = (imap_load_box_state_t *)((imap_cmd_refcounted_t *)cmd)->state;
|
|
|
|
|
|
|
|
transform_refcounted_box_response( &sts->gen, response );
|
2017-03-24 17:09:40 +00:00
|
|
|
imap_submit_load_p3( ctx, sts );
|
2017-03-24 15:56:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-03-24 17:09:40 +00:00
|
|
|
imap_submit_load_p3( imap_store_t *ctx, imap_load_box_state_t *sts )
|
2017-03-24 15:56:43 +00:00
|
|
|
{
|
2018-11-24 13:15:53 +00:00
|
|
|
DONE_REFCOUNTED_STATE_ARGS(sts, {
|
2020-08-05 17:48:58 +00:00
|
|
|
ctx->fetch_sts = FetchNone;
|
2020-12-17 14:53:40 +00:00
|
|
|
if (sts->ret_val == DRV_OK)
|
2018-11-24 13:15:53 +00:00
|
|
|
imap_sort_msgs( ctx );
|
2020-12-17 14:53:40 +00:00
|
|
|
}, &ctx->msgs->gen, ctx->total_msgs, ctx->recent_msgs)
|
2017-03-24 15:56:43 +00:00
|
|
|
}
|
|
|
|
|
2011-04-03 16:21:46 +00:00
|
|
|
/******************* imap_fetch_msg *******************/
|
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
static void imap_fetch_msg_p2( imap_store_t *, imap_cmd_t *, int );
|
2013-09-25 15:13:00 +00:00
|
|
|
|
2012-07-29 21:14:48 +00:00
|
|
|
static void
|
2019-12-29 13:37:53 +00:00
|
|
|
imap_fetch_msg( store_t *ctx, message_t *msg, msg_data_t *data, int minimal,
|
2012-07-29 21:14:48 +00:00
|
|
|
void (*cb)( int sts, void *aux ), void *aux )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_fetch_msg_t *cmd;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
INIT_IMAP_CMD_X(imap_cmd_fetch_msg_t, cmd, cb, aux)
|
2020-12-17 14:53:40 +00:00
|
|
|
cmd->param.uid = msg->uid;
|
2011-04-03 16:15:36 +00:00
|
|
|
cmd->msg_data = data;
|
2019-07-28 18:50:31 +00:00
|
|
|
data->data = NULL;
|
2013-09-25 15:13:00 +00:00
|
|
|
imap_exec( (imap_store_t *)ctx, &cmd->gen.gen, imap_fetch_msg_p2,
|
2019-12-29 13:37:53 +00:00
|
|
|
"UID FETCH %u (%s%sBODY.PEEK[%s])", msg->uid,
|
2013-07-28 13:55:13 +00:00
|
|
|
!(msg->status & M_FLAGS) ? "FLAGS " : "",
|
2019-12-29 13:37:53 +00:00
|
|
|
(data->date== -1) ? "INTERNALDATE " : "",
|
|
|
|
minimal ? "HEADER" : "" );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2013-09-25 15:13:00 +00:00
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_fetch_msg_p2( imap_store_t *ctx, imap_cmd_t *gcmd, int response )
|
2013-09-25 15:13:00 +00:00
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_fetch_msg_t *cmd = (imap_cmd_fetch_msg_t *)gcmd;
|
2013-09-25 15:13:00 +00:00
|
|
|
|
|
|
|
if (response == RESP_OK && !cmd->msg_data->data) {
|
|
|
|
/* The FETCH succeeded, but there is no message with this UID. */
|
|
|
|
response = RESP_NO;
|
|
|
|
}
|
|
|
|
imap_done_simple_msg( ctx, gcmd, response );
|
|
|
|
}
|
|
|
|
|
2014-12-27 21:13:24 +00:00
|
|
|
/******************* imap_set_msg_flags *******************/
|
2011-04-03 16:21:46 +00:00
|
|
|
|
2020-07-08 15:27:37 +00:00
|
|
|
static uint
|
2004-03-27 16:07:20 +00:00
|
|
|
imap_make_flags( int flags, char *buf )
|
|
|
|
{
|
|
|
|
const char *s;
|
2014-12-07 12:19:30 +00:00
|
|
|
uint i, d;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
for (i = d = 0; i < as(Flags); i++)
|
|
|
|
if (flags & (1 << i)) {
|
|
|
|
buf[d++] = ' ';
|
|
|
|
for (s = Flags[i]; *s; s++)
|
|
|
|
buf[d++] = *s;
|
|
|
|
}
|
|
|
|
buf[0] = '(';
|
|
|
|
buf[d++] = ')';
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
typedef union {
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_cmd_refcounted_state_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
IMAP_CMD_REFCOUNTED_STATE
|
|
|
|
void (*callback)( int sts, void *aux );
|
|
|
|
void *callback_aux;
|
|
|
|
};
|
2017-03-24 15:56:43 +00:00
|
|
|
} imap_set_msg_flags_state_t;
|
|
|
|
|
|
|
|
static void imap_set_flags_p2( imap_store_t *, imap_cmd_t *, int );
|
|
|
|
static void imap_set_flags_p3( imap_set_msg_flags_state_t * );
|
|
|
|
|
2015-05-09 17:17:41 +00:00
|
|
|
static void
|
2017-03-21 19:05:29 +00:00
|
|
|
imap_flags_helper( imap_store_t *ctx, uint uid, char what, int flags,
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_set_msg_flags_state_t *sts )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
buf[imap_make_flags( flags, buf )] = 0;
|
2020-07-28 14:14:00 +00:00
|
|
|
imap_cmd_t *cmd = imap_refcounted_new_cmd( &sts->gen );
|
|
|
|
cmd->param.wait_check = 1;
|
|
|
|
imap_exec( ctx, cmd, imap_set_flags_p2,
|
2017-03-21 19:05:29 +00:00
|
|
|
"UID STORE %u %cFLAGS.SILENT %s", uid, what, buf );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2012-07-29 21:14:48 +00:00
|
|
|
static void
|
2017-03-21 19:05:29 +00:00
|
|
|
imap_set_msg_flags( store_t *gctx, message_t *msg, uint uid, int add, int del,
|
2014-12-27 21:13:24 +00:00
|
|
|
void (*cb)( int sts, void *aux ), void *aux )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
|
|
|
|
if (msg) {
|
|
|
|
uid = msg->uid;
|
|
|
|
add &= ~msg->flags;
|
|
|
|
del &= msg->flags;
|
|
|
|
msg->flags |= add;
|
|
|
|
msg->flags &= ~del;
|
|
|
|
}
|
2011-04-03 16:15:36 +00:00
|
|
|
if (add || del) {
|
2017-03-24 15:56:43 +00:00
|
|
|
INIT_REFCOUNTED_STATE(imap_set_msg_flags_state_t, sts, cb, aux)
|
2015-05-09 17:17:41 +00:00
|
|
|
if (add)
|
|
|
|
imap_flags_helper( ctx, uid, '+', add, sts );
|
|
|
|
if (del)
|
|
|
|
imap_flags_helper( ctx, uid, '-', del, sts );
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_set_flags_p3( sts );
|
2011-04-03 16:15:36 +00:00
|
|
|
} else {
|
|
|
|
cb( DRV_OK, aux );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_set_flags_p2( imap_store_t *ctx ATTR_UNUSED, imap_cmd_t *cmd, int response )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_set_msg_flags_state_t *sts = (imap_set_msg_flags_state_t *)((imap_cmd_refcounted_t *)cmd)->state;
|
2017-03-24 16:00:00 +00:00
|
|
|
|
|
|
|
transform_refcounted_msg_response( &sts->gen, response);
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_set_flags_p3( sts );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_set_flags_p3( imap_set_msg_flags_state_t *sts )
|
|
|
|
{
|
|
|
|
DONE_REFCOUNTED_STATE(sts)
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2014-12-27 21:13:24 +00:00
|
|
|
/******************* imap_close_box *******************/
|
2011-04-03 16:21:46 +00:00
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
typedef union {
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_cmd_refcounted_state_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
IMAP_CMD_REFCOUNTED_STATE
|
|
|
|
void (*callback)( int sts, void *aux );
|
|
|
|
void *callback_aux;
|
|
|
|
};
|
2017-03-24 15:56:43 +00:00
|
|
|
} imap_expunge_state_t;
|
|
|
|
|
|
|
|
static void imap_close_box_p2( imap_store_t *, imap_cmd_t *, int );
|
|
|
|
static void imap_close_box_p3( imap_expunge_state_t * );
|
|
|
|
|
2012-07-29 21:14:48 +00:00
|
|
|
static void
|
2014-12-27 21:13:24 +00:00
|
|
|
imap_close_box( store_t *gctx,
|
|
|
|
void (*cb)( int sts, void *aux ), void *aux )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2012-08-25 18:30:04 +00:00
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2020-07-28 14:14:00 +00:00
|
|
|
assert( !ctx->num_wait_check );
|
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
if (ctx->conf->trash && CAP(UIDPLUS)) {
|
2017-03-24 15:56:43 +00:00
|
|
|
INIT_REFCOUNTED_STATE(imap_expunge_state_t, sts, cb, aux)
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_message_t *msg, *fmsg, *nmsg;
|
2012-08-25 18:30:04 +00:00
|
|
|
int bl;
|
|
|
|
char buf[1000];
|
|
|
|
|
2017-03-24 17:09:40 +00:00
|
|
|
for (msg = ctx->msgs; ; ) {
|
2012-08-25 18:30:04 +00:00
|
|
|
for (bl = 0; msg && bl < 960; msg = msg->next) {
|
|
|
|
if (!(msg->flags & F_DELETED))
|
|
|
|
continue;
|
|
|
|
if (bl)
|
|
|
|
buf[bl++] = ',';
|
2017-03-21 19:05:29 +00:00
|
|
|
bl += sprintf( buf + bl, "%u", msg->uid );
|
2012-08-25 18:30:04 +00:00
|
|
|
fmsg = msg;
|
|
|
|
for (; (nmsg = msg->next) && (nmsg->flags & F_DELETED); msg = nmsg) {}
|
|
|
|
if (msg != fmsg)
|
2017-03-21 19:05:29 +00:00
|
|
|
bl += sprintf( buf + bl, ":%u", msg->uid );
|
2012-08-25 18:30:04 +00:00
|
|
|
}
|
|
|
|
if (!bl)
|
|
|
|
break;
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_exec( ctx, imap_refcounted_new_cmd( &sts->gen ), imap_close_box_p2,
|
2015-05-09 17:17:41 +00:00
|
|
|
"UID EXPUNGE %s", buf );
|
2012-08-25 18:30:04 +00:00
|
|
|
}
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_close_box_p3( sts );
|
2012-08-25 18:30:04 +00:00
|
|
|
} else {
|
|
|
|
/* This is inherently racy: it may cause messages which other clients
|
|
|
|
* marked as deleted to be expunged without being trashed. */
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_simple_t *cmd;
|
|
|
|
INIT_IMAP_CMD(imap_cmd_simple_t, cmd, cb, aux)
|
2012-08-25 18:30:04 +00:00
|
|
|
imap_exec( ctx, &cmd->gen, imap_done_simple_box, "CLOSE" );
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2017-03-24 15:56:43 +00:00
|
|
|
static void
|
|
|
|
imap_close_box_p2( imap_store_t *ctx ATTR_UNUSED, imap_cmd_t *cmd, int response )
|
|
|
|
{
|
|
|
|
imap_expunge_state_t *sts = (imap_expunge_state_t *)((imap_cmd_refcounted_t *)cmd)->state;
|
|
|
|
|
|
|
|
transform_refcounted_box_response( &sts->gen, response );
|
|
|
|
imap_close_box_p3( sts );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_close_box_p3( imap_expunge_state_t *sts )
|
|
|
|
{
|
|
|
|
DONE_REFCOUNTED_STATE(sts)
|
|
|
|
}
|
|
|
|
|
2011-04-03 16:21:46 +00:00
|
|
|
/******************* imap_trash_msg *******************/
|
|
|
|
|
2012-07-29 21:14:48 +00:00
|
|
|
static void
|
2006-03-21 20:03:21 +00:00
|
|
|
imap_trash_msg( store_t *gctx, message_t *msg,
|
2012-07-29 21:14:48 +00:00
|
|
|
void (*cb)( int sts, void *aux ), void *aux )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_simple_t *cmd;
|
2013-08-03 13:10:57 +00:00
|
|
|
char *buf;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
INIT_IMAP_CMD(imap_cmd_simple_t, cmd, cb, aux)
|
2020-12-17 14:53:40 +00:00
|
|
|
cmd->param.create = 1;
|
|
|
|
cmd->param.to_trash = 1;
|
2013-08-03 13:10:57 +00:00
|
|
|
if (prepare_trash( &buf, ctx ) < 0) {
|
2012-08-11 16:34:46 +00:00
|
|
|
cb( DRV_BOX_BAD, aux );
|
2021-12-26 18:14:01 +00:00
|
|
|
free( cmd );
|
2012-08-11 16:34:46 +00:00
|
|
|
return;
|
|
|
|
}
|
2011-04-03 16:15:36 +00:00
|
|
|
imap_exec( ctx, &cmd->gen, imap_done_simple_msg,
|
2017-03-21 19:05:29 +00:00
|
|
|
CAP(MOVE) ? "UID MOVE %u \"%\\s\"" : "UID COPY %u \"%\\s\"", msg->uid, buf );
|
2013-08-03 13:10:57 +00:00
|
|
|
free( buf );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2011-04-03 16:21:46 +00:00
|
|
|
/******************* imap_store_msg *******************/
|
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
static void imap_store_msg_p2( imap_store_t *, imap_cmd_t *, int );
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2012-07-29 21:14:48 +00:00
|
|
|
static void
|
2006-03-21 20:03:21 +00:00
|
|
|
imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
|
2017-03-21 19:05:29 +00:00
|
|
|
void (*cb)( int sts, uint uid, void *aux ), void *aux )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_out_uid_t *cmd;
|
2013-08-03 13:10:57 +00:00
|
|
|
char *buf;
|
2020-07-08 15:27:37 +00:00
|
|
|
uint d;
|
2013-08-03 13:10:57 +00:00
|
|
|
char flagstr[128], datestr[64];
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
d = 0;
|
|
|
|
if (data->flags) {
|
|
|
|
d = imap_make_flags( data->flags, flagstr );
|
|
|
|
flagstr[d++] = ' ';
|
|
|
|
}
|
|
|
|
flagstr[d] = 0;
|
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
INIT_IMAP_CMD(imap_cmd_out_uid_t, cmd, cb, aux)
|
2015-02-15 17:13:05 +00:00
|
|
|
ctx->buffer_mem += data->len;
|
2020-12-17 14:53:40 +00:00
|
|
|
cmd->param.data_len = data->len;
|
|
|
|
cmd->param.data = data->data;
|
2006-03-21 20:03:21 +00:00
|
|
|
|
|
|
|
if (to_trash) {
|
2020-12-17 14:53:40 +00:00
|
|
|
cmd->param.create = 1;
|
|
|
|
cmd->param.to_trash = 1;
|
2013-08-03 13:10:57 +00:00
|
|
|
if (prepare_trash( &buf, ctx ) < 0) {
|
2020-07-08 11:45:06 +00:00
|
|
|
cb( DRV_BOX_BAD, 0, aux );
|
2021-12-26 18:14:01 +00:00
|
|
|
free( cmd );
|
2012-08-11 16:34:46 +00:00
|
|
|
return;
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
} else {
|
2013-08-03 13:10:57 +00:00
|
|
|
if (prepare_box( &buf, ctx ) < 0) {
|
2020-07-08 11:45:06 +00:00
|
|
|
cb( DRV_BOX_BAD, 0, aux );
|
2021-12-26 18:14:01 +00:00
|
|
|
free( cmd );
|
2012-08-11 16:34:46 +00:00
|
|
|
return;
|
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2013-07-28 13:55:13 +00:00
|
|
|
if (data->date) {
|
|
|
|
/* configure ensures that %z actually works. */
|
2019-07-28 18:42:04 +00:00
|
|
|
DIAG_PUSH
|
|
|
|
DIAG_DISABLE("-Wformat")
|
|
|
|
strftime( datestr, sizeof(datestr), "%d-%b-%Y %H:%M:%S %z", localtime( &data->date ) );
|
|
|
|
DIAG_POP
|
2013-07-28 13:55:13 +00:00
|
|
|
imap_exec( ctx, &cmd->gen, imap_store_msg_p2,
|
2013-09-25 18:55:32 +00:00
|
|
|
"APPEND \"%\\s\" %s\"%\\s\" ", buf, flagstr, datestr );
|
2013-07-28 13:55:13 +00:00
|
|
|
} else {
|
|
|
|
imap_exec( ctx, &cmd->gen, imap_store_msg_p2,
|
2013-09-25 18:55:32 +00:00
|
|
|
"APPEND \"%\\s\" %s", buf, flagstr );
|
2013-07-28 13:55:13 +00:00
|
|
|
}
|
2013-08-03 13:10:57 +00:00
|
|
|
free( buf );
|
2011-04-03 16:15:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_store_msg_p2( imap_store_t *ctx ATTR_UNUSED, imap_cmd_t *cmd, int response )
|
2011-04-03 16:15:36 +00:00
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_out_uid_t *cmdp = (imap_cmd_out_uid_t *)cmd;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
|
|
|
transform_msg_response( &response );
|
2021-06-03 09:04:56 +00:00
|
|
|
cmdp->callback( response, cmdp->param.uid, cmdp->callback_aux );
|
2006-02-03 21:33:43 +00:00
|
|
|
}
|
|
|
|
|
2011-04-10 11:06:07 +00:00
|
|
|
/******************* imap_find_new_msgs *******************/
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2017-04-02 13:42:18 +00:00
|
|
|
static void imap_find_new_msgs_p2( imap_store_t *, imap_cmd_t *, int );
|
2017-01-28 18:19:41 +00:00
|
|
|
static void imap_find_new_msgs_p3( imap_store_t *, imap_cmd_t *, int );
|
2017-03-26 16:44:43 +00:00
|
|
|
static void imap_find_new_msgs_p4( imap_store_t *, imap_cmd_t *, int );
|
2013-07-27 16:46:57 +00:00
|
|
|
|
2012-07-29 21:14:48 +00:00
|
|
|
static void
|
2017-03-21 19:05:29 +00:00
|
|
|
imap_find_new_msgs( store_t *gctx, uint newuid,
|
2017-03-26 16:44:43 +00:00
|
|
|
void (*cb)( int sts, message_t *msgs, void *aux ), void *aux )
|
2006-02-03 21:33:43 +00:00
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_find_new_t *cmd;
|
2011-04-03 16:15:36 +00:00
|
|
|
|
2017-03-26 16:44:43 +00:00
|
|
|
INIT_IMAP_CMD(imap_cmd_find_new_t, cmd, cb, aux)
|
|
|
|
cmd->out_msgs = ctx->msgapp;
|
2015-01-11 13:29:19 +00:00
|
|
|
cmd->uid = newuid;
|
2017-03-30 17:39:14 +00:00
|
|
|
// Some servers fail to enumerate recently STOREd messages without syncing first.
|
2017-03-26 16:44:43 +00:00
|
|
|
imap_exec( (imap_store_t *)ctx, &cmd->gen, imap_find_new_msgs_p2, "CHECK" );
|
2013-07-27 16:46:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_find_new_msgs_p2( imap_store_t *ctx, imap_cmd_t *gcmd, int response )
|
2013-07-27 16:46:57 +00:00
|
|
|
{
|
2017-04-02 13:42:18 +00:00
|
|
|
imap_cmd_find_new_t *cmdp = (imap_cmd_find_new_t *)gcmd;
|
2017-01-28 18:19:41 +00:00
|
|
|
imap_cmd_find_new_t *cmd;
|
2013-07-27 16:46:57 +00:00
|
|
|
|
|
|
|
if (response != RESP_OK) {
|
|
|
|
imap_done_simple_box( ctx, gcmd, response );
|
|
|
|
return;
|
|
|
|
}
|
2017-01-28 18:19:41 +00:00
|
|
|
|
2019-11-17 18:45:00 +00:00
|
|
|
// We appended messages, so we need to re-query UIDNEXT.
|
2017-03-24 17:43:39 +00:00
|
|
|
ctx->uidnext = 0;
|
2020-08-05 17:48:58 +00:00
|
|
|
assert( ctx->fetch_sts == FetchNone );
|
|
|
|
ctx->fetch_sts = FetchUidNext;
|
2017-01-28 18:19:41 +00:00
|
|
|
|
2017-03-26 16:44:43 +00:00
|
|
|
INIT_IMAP_CMD(imap_cmd_find_new_t, cmd, cmdp->callback, cmdp->callback_aux)
|
|
|
|
cmd->out_msgs = cmdp->out_msgs;
|
2017-01-28 18:19:41 +00:00
|
|
|
cmd->uid = cmdp->uid;
|
2017-03-26 16:44:43 +00:00
|
|
|
imap_exec( ctx, &cmd->gen, imap_find_new_msgs_p3,
|
2018-07-01 09:05:21 +00:00
|
|
|
"UID FETCH * (UID)" );
|
2017-01-28 18:19:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_find_new_msgs_p3( imap_store_t *ctx, imap_cmd_t *gcmd, int response )
|
|
|
|
{
|
|
|
|
imap_cmd_find_new_t *cmdp = (imap_cmd_find_new_t *)gcmd;
|
2017-03-26 16:44:43 +00:00
|
|
|
imap_cmd_find_new_t *cmd;
|
2017-01-28 18:19:41 +00:00
|
|
|
|
2019-11-23 12:51:20 +00:00
|
|
|
if (response != RESP_OK) {
|
2017-03-26 16:44:43 +00:00
|
|
|
imap_find_new_msgs_p4( ctx, gcmd, response );
|
2017-01-28 18:19:41 +00:00
|
|
|
return;
|
|
|
|
}
|
2020-08-05 18:58:53 +00:00
|
|
|
if (ctx->uidnext <= cmdp->uid) {
|
|
|
|
if (!ctx->uidnext && ctx->total_msgs) {
|
|
|
|
error( "IMAP error: re-querying server for highest UID failed\n" );
|
|
|
|
imap_find_new_msgs_p4( ctx, gcmd, RESP_NO );
|
|
|
|
} else {
|
|
|
|
// The messages evaporated, or the server just didn't register them -
|
|
|
|
// we'll catch that later (via lost TUIDs).
|
|
|
|
// This case is why we do the extra roundtrip instead of simply passing
|
|
|
|
// '*' as the end of the range below - IMAP ranges are unordered, so we
|
|
|
|
// would potentially re-fetch an already loaded message.
|
|
|
|
imap_find_new_msgs_p4( ctx, gcmd, RESP_OK );
|
|
|
|
}
|
2019-11-23 12:51:20 +00:00
|
|
|
return;
|
|
|
|
}
|
2021-12-24 01:20:38 +00:00
|
|
|
ctx->fetch_sts = FetchMsgs;
|
2017-03-26 16:44:43 +00:00
|
|
|
INIT_IMAP_CMD(imap_cmd_find_new_t, cmd, cmdp->callback, cmdp->callback_aux)
|
|
|
|
cmd->out_msgs = cmdp->out_msgs;
|
|
|
|
imap_exec( (imap_store_t *)ctx, &cmd->gen, imap_find_new_msgs_p4,
|
2017-03-21 19:05:29 +00:00
|
|
|
"UID FETCH %u:%u (UID BODY.PEEK[HEADER.FIELDS (X-TUID)])", cmdp->uid, ctx->uidnext - 1 );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2017-03-26 16:44:43 +00:00
|
|
|
static void
|
|
|
|
imap_find_new_msgs_p4( imap_store_t *ctx ATTR_UNUSED, imap_cmd_t *gcmd, int response )
|
|
|
|
{
|
|
|
|
imap_cmd_find_new_t *cmdp = (imap_cmd_find_new_t *)gcmd;
|
|
|
|
|
2021-12-24 01:20:38 +00:00
|
|
|
ctx->fetch_sts = FetchNone;
|
2017-03-26 16:44:43 +00:00
|
|
|
transform_box_response( &response );
|
2020-12-17 14:53:40 +00:00
|
|
|
cmdp->callback( response, &(*cmdp->out_msgs)->gen, cmdp->callback_aux );
|
2017-03-26 16:44:43 +00:00
|
|
|
}
|
|
|
|
|
2014-12-27 21:13:24 +00:00
|
|
|
/******************* imap_list_store *******************/
|
2011-04-03 16:21:46 +00:00
|
|
|
|
2020-12-17 14:53:40 +00:00
|
|
|
typedef union {
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_cmd_refcounted_state_t gen;
|
2020-12-17 14:53:40 +00:00
|
|
|
struct {
|
|
|
|
IMAP_CMD_REFCOUNTED_STATE
|
|
|
|
void (*callback)( int sts, string_list_t *, void *aux );
|
|
|
|
void *callback_aux;
|
|
|
|
};
|
2017-03-24 15:56:43 +00:00
|
|
|
} imap_list_store_state_t;
|
|
|
|
|
|
|
|
static void imap_list_store_p2( imap_store_t *, imap_cmd_t *, int );
|
2017-03-24 16:44:11 +00:00
|
|
|
static void imap_list_store_p3( imap_store_t *, imap_list_store_state_t * );
|
2017-03-24 15:56:43 +00:00
|
|
|
|
2006-03-21 20:03:21 +00:00
|
|
|
static void
|
2014-12-27 21:13:24 +00:00
|
|
|
imap_list_store( store_t *gctx, int flags,
|
2017-03-24 16:44:11 +00:00
|
|
|
void (*cb)( int sts, string_list_t *boxes, void *aux ), void *aux )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
2020-12-17 14:53:40 +00:00
|
|
|
imap_store_conf_t *cfg = ctx->conf;
|
2017-03-24 15:56:43 +00:00
|
|
|
INIT_REFCOUNTED_STATE(imap_list_store_state_t, sts, cb, aux)
|
2012-08-11 16:34:46 +00:00
|
|
|
|
2017-03-30 17:39:14 +00:00
|
|
|
// ctx->prefix may be empty, "INBOX.", or something else.
|
2017-03-21 17:46:30 +00:00
|
|
|
// 'flags' may be LIST_INBOX, LIST_PATH (or LIST_PATH_MAYBE), or both. 'listed'
|
|
|
|
// already containing a particular value effectively removes it from 'flags'.
|
|
|
|
// This matrix determines what to query, and what comes out as a side effect.
|
|
|
|
// The lowercase letters indicate unnecessary results; the queries are done
|
|
|
|
// this way to have non-overlapping result sets, so subsequent calls create
|
|
|
|
// no duplicates:
|
2017-03-30 17:39:14 +00:00
|
|
|
//
|
|
|
|
// qry \ pfx | empty | inbox | other
|
|
|
|
// ----------+-------+-------+-------
|
2017-03-21 17:46:30 +00:00
|
|
|
// inbox | p [I] | I [p] | I
|
|
|
|
// both | P [I] | I [P] | I + P
|
|
|
|
// path | P [i] | i [P] | P
|
2017-03-30 17:39:14 +00:00
|
|
|
//
|
2017-03-21 17:46:30 +00:00
|
|
|
int pfx_is_empty = !*ctx->prefix;
|
|
|
|
int pfx_is_inbox = !pfx_is_empty && is_inbox( ctx, ctx->prefix, -1 );
|
2017-03-24 16:44:11 +00:00
|
|
|
if (((flags & (LIST_PATH | LIST_PATH_MAYBE)) || pfx_is_empty) && !pfx_is_inbox && !(ctx->listed & LIST_PATH)) {
|
|
|
|
ctx->listed |= LIST_PATH;
|
2017-03-21 17:46:30 +00:00
|
|
|
if (pfx_is_empty)
|
2017-03-24 16:44:11 +00:00
|
|
|
ctx->listed |= LIST_INBOX;
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_exec( ctx, imap_refcounted_new_cmd( &sts->gen ), imap_list_store_p2,
|
2019-11-26 14:49:19 +00:00
|
|
|
"%s \"\" \"%\\s*\"", cfg->use_lsub ? "LSUB" : "LIST", ctx->prefix );
|
2017-03-21 17:46:30 +00:00
|
|
|
}
|
2017-03-24 16:44:11 +00:00
|
|
|
if (((flags & LIST_INBOX) || pfx_is_inbox) && !pfx_is_empty && !(ctx->listed & LIST_INBOX)) {
|
|
|
|
ctx->listed |= LIST_INBOX;
|
2017-03-21 17:46:30 +00:00
|
|
|
if (pfx_is_inbox)
|
2017-03-24 16:44:11 +00:00
|
|
|
ctx->listed |= LIST_PATH;
|
2017-03-24 15:56:43 +00:00
|
|
|
imap_exec( ctx, imap_refcounted_new_cmd( &sts->gen ), imap_list_store_p2,
|
2019-11-26 14:49:19 +00:00
|
|
|
"%s \"\" INBOX*", cfg->use_lsub ? "LSUB" : "LIST" );
|
2017-03-21 17:46:30 +00:00
|
|
|
}
|
2017-03-24 16:44:11 +00:00
|
|
|
imap_list_store_p3( ctx, sts );
|
2017-03-24 15:56:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-03-24 16:44:11 +00:00
|
|
|
imap_list_store_p2( imap_store_t *ctx, imap_cmd_t *cmd, int response )
|
2017-03-24 15:56:43 +00:00
|
|
|
{
|
|
|
|
imap_list_store_state_t *sts = (imap_list_store_state_t *)((imap_cmd_refcounted_t *)cmd)->state;
|
|
|
|
|
|
|
|
transform_refcounted_box_response( &sts->gen, response );
|
2017-03-24 16:44:11 +00:00
|
|
|
imap_list_store_p3( ctx, sts );
|
2017-03-24 15:56:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-03-24 16:44:11 +00:00
|
|
|
imap_list_store_p3( imap_store_t *ctx, imap_list_store_state_t *sts )
|
2017-03-24 15:56:43 +00:00
|
|
|
{
|
2018-11-24 13:15:53 +00:00
|
|
|
DONE_REFCOUNTED_STATE_ARGS(sts, , ctx->boxes)
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2014-12-27 21:13:24 +00:00
|
|
|
/******************* imap_cancel_cmds *******************/
|
2011-04-03 16:21:46 +00:00
|
|
|
|
2006-03-21 20:03:21 +00:00
|
|
|
static void
|
2014-12-27 21:13:24 +00:00
|
|
|
imap_cancel_cmds( store_t *gctx,
|
|
|
|
void (*cb)( void *aux ), void *aux )
|
2006-03-21 20:03:21 +00:00
|
|
|
{
|
2012-08-25 16:26:23 +00:00
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
|
2020-07-28 14:14:00 +00:00
|
|
|
finalize_checked_imap_cmds( ctx, RESP_CANCEL );
|
2012-08-25 16:26:23 +00:00
|
|
|
cancel_pending_imap_cmds( ctx );
|
|
|
|
if (ctx->in_progress) {
|
|
|
|
ctx->canceling = 1;
|
|
|
|
ctx->callbacks.imap_cancel = cb;
|
|
|
|
ctx->callback_aux = aux;
|
|
|
|
} else {
|
|
|
|
cb( aux );
|
|
|
|
}
|
2006-03-21 20:03:21 +00:00
|
|
|
}
|
|
|
|
|
2014-12-27 21:13:24 +00:00
|
|
|
/******************* imap_commit_cmds *******************/
|
2011-04-03 16:21:46 +00:00
|
|
|
|
2020-07-28 14:14:00 +00:00
|
|
|
static void imap_commit_cmds_p2( imap_store_t *, imap_cmd_t *, int );
|
|
|
|
|
2006-03-21 20:03:21 +00:00
|
|
|
static void
|
2014-12-27 21:13:24 +00:00
|
|
|
imap_commit_cmds( store_t *gctx )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
2020-07-28 14:14:00 +00:00
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
|
|
|
|
if (ctx->num_wait_check)
|
|
|
|
imap_exec( ctx, NULL, imap_commit_cmds_p2, "CHECK" );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imap_commit_cmds_p2( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response )
|
|
|
|
{
|
|
|
|
finalize_checked_imap_cmds( ctx, response );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
|
2017-03-19 12:46:03 +00:00
|
|
|
/******************* imap_get_memory_usage *******************/
|
2015-02-15 17:13:05 +00:00
|
|
|
|
2020-07-08 15:27:37 +00:00
|
|
|
static uint
|
2017-03-19 12:46:03 +00:00
|
|
|
imap_get_memory_usage( store_t *gctx )
|
2015-02-15 17:13:05 +00:00
|
|
|
{
|
|
|
|
imap_store_t *ctx = (imap_store_t *)gctx;
|
|
|
|
|
|
|
|
return ctx->buffer_mem + ctx->conn.buffer_mem;
|
|
|
|
}
|
|
|
|
|
2017-03-19 12:46:03 +00:00
|
|
|
/******************* imap_get_fail_state *******************/
|
2015-04-26 18:54:05 +00:00
|
|
|
|
|
|
|
static int
|
2017-03-19 12:46:03 +00:00
|
|
|
imap_get_fail_state( store_conf_t *gconf )
|
2015-04-26 18:54:05 +00:00
|
|
|
{
|
|
|
|
return ((imap_store_conf_t *)gconf)->server->failed;
|
|
|
|
}
|
|
|
|
|
2011-04-03 16:21:46 +00:00
|
|
|
/******************* imap_parse_store *******************/
|
|
|
|
|
2019-07-28 18:54:01 +00:00
|
|
|
static imap_server_conf_t *servers, **serverapp = &servers;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
static int
|
2012-09-15 09:46:42 +00:00
|
|
|
imap_parse_store( conffile_t *cfg, store_conf_t **storep )
|
2004-03-27 16:07:20 +00:00
|
|
|
{
|
|
|
|
imap_store_conf_t *store;
|
|
|
|
imap_server_conf_t *server, *srv, sserver;
|
2014-07-12 18:35:55 +00:00
|
|
|
const char *type, *name, *arg;
|
2015-05-08 08:20:09 +00:00
|
|
|
unsigned u;
|
2006-03-19 10:44:53 +00:00
|
|
|
int acc_opt = 0;
|
2014-07-12 18:35:55 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
|
|
|
/* Legacy SSL options */
|
|
|
|
int require_ssl = -1, use_imaps = -1;
|
2019-11-26 15:18:58 +00:00
|
|
|
int use_tlsv1 = -1, use_tlsv11 = -1, use_tlsv12 = -1, use_tlsv13 = -1;
|
2014-07-12 18:35:55 +00:00
|
|
|
#endif
|
2015-05-09 15:12:31 +00:00
|
|
|
/* Legacy SASL option */
|
|
|
|
int require_cram = -1;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
if (!strcasecmp( "IMAPAccount", cfg->cmd )) {
|
|
|
|
server = nfcalloc( sizeof(*server) );
|
2020-10-05 11:15:28 +00:00
|
|
|
name = server->name = nfstrdup( cfg->val );
|
2004-03-27 16:07:20 +00:00
|
|
|
*serverapp = server;
|
|
|
|
serverapp = &server->next;
|
2019-07-28 18:50:31 +00:00
|
|
|
store = NULL;
|
|
|
|
*storep = NULL;
|
2020-10-05 11:15:28 +00:00
|
|
|
type = "IMAP account";
|
2004-03-27 16:07:20 +00:00
|
|
|
} else if (!strcasecmp( "IMAPStore", cfg->cmd )) {
|
|
|
|
store = nfcalloc( sizeof(*store) );
|
2020-12-17 14:53:40 +00:00
|
|
|
store->driver = &imap_driver;
|
|
|
|
name = store->name = nfstrdup( cfg->val );
|
2004-03-27 16:07:20 +00:00
|
|
|
store->use_namespace = 1;
|
2006-03-19 10:44:53 +00:00
|
|
|
*storep = &store->gen;
|
2004-03-27 16:07:20 +00:00
|
|
|
memset( &sserver, 0, sizeof(sserver) );
|
|
|
|
server = &sserver;
|
2020-10-05 11:15:28 +00:00
|
|
|
type = "IMAP store";
|
2004-03-27 16:07:20 +00:00
|
|
|
} else
|
|
|
|
return 0;
|
|
|
|
|
2015-04-06 14:49:33 +00:00
|
|
|
server->sconf.timeout = 20;
|
2011-03-27 10:06:41 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
2014-07-12 18:35:55 +00:00
|
|
|
server->ssl_type = -1;
|
|
|
|
server->sconf.ssl_versions = -1;
|
2014-07-27 13:42:33 +00:00
|
|
|
server->sconf.system_certs = 1;
|
2004-03-27 16:07:20 +00:00
|
|
|
#endif
|
2012-08-25 16:26:23 +00:00
|
|
|
server->max_in_progress = INT_MAX;
|
2004-03-27 16:07:20 +00:00
|
|
|
|
|
|
|
while (getcline( cfg ) && cfg->cmd) {
|
2006-03-19 10:44:53 +00:00
|
|
|
if (!strcasecmp( "Host", cfg->cmd )) {
|
2006-05-28 15:43:58 +00:00
|
|
|
/* The imap[s]: syntax is just a backwards compat hack. */
|
2014-07-12 18:35:55 +00:00
|
|
|
arg = cfg->val;
|
2011-03-27 10:06:41 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
2014-07-12 18:35:55 +00:00
|
|
|
if (starts_with( arg, -1, "imaps:", 6 )) {
|
|
|
|
arg += 6;
|
|
|
|
server->ssl_type = SSL_IMAPS;
|
|
|
|
if (server->sconf.ssl_versions == -1)
|
2019-11-26 15:18:58 +00:00
|
|
|
server->sconf.ssl_versions = TLSv1 | TLSv1_1 | TLSv1_2 | TLSv1_3;
|
2004-03-27 16:07:20 +00:00
|
|
|
} else
|
|
|
|
#endif
|
2014-07-12 18:35:55 +00:00
|
|
|
if (starts_with( arg, -1, "imap:", 5 ))
|
|
|
|
arg += 5;
|
2014-12-29 01:16:28 +00:00
|
|
|
if (starts_with( arg, -1, "//", 2 ))
|
2014-07-12 18:35:55 +00:00
|
|
|
arg += 2;
|
|
|
|
if (arg != cfg->val)
|
|
|
|
warn( "%s:%d: Notice: URL notation is deprecated; use a plain host name and possibly 'SSLType IMAPS' instead\n", cfg->file, cfg->line );
|
|
|
|
server->sconf.host = nfstrdup( arg );
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
else if (!strcasecmp( "User", cfg->cmd ))
|
|
|
|
server->user = nfstrdup( cfg->val );
|
2019-11-26 11:17:33 +00:00
|
|
|
else if (!strcasecmp( "UserCmd", cfg->cmd ))
|
|
|
|
server->user_cmd = nfstrdup( cfg->val );
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "Pass", cfg->cmd ))
|
|
|
|
server->pass = nfstrdup( cfg->val );
|
2013-07-27 08:37:15 +00:00
|
|
|
else if (!strcasecmp( "PassCmd", cfg->cmd ))
|
|
|
|
server->pass_cmd = nfstrdup( cfg->val );
|
2019-11-27 16:13:44 +00:00
|
|
|
#ifdef HAVE_MACOS_KEYCHAIN
|
|
|
|
else if (!strcasecmp( "UseKeychain", cfg->cmd ))
|
|
|
|
server->use_keychain = parse_bool( cfg );
|
|
|
|
#endif
|
2018-07-01 11:22:17 +00:00
|
|
|
else if (!strcasecmp( "Port", cfg->cmd )) {
|
|
|
|
int port = parse_int( cfg );
|
|
|
|
if ((unsigned)port > 0xffff) {
|
|
|
|
error( "%s:%d: Invalid port number\n", cfg->file, cfg->line );
|
|
|
|
cfg->err = 1;
|
|
|
|
} else {
|
|
|
|
server->sconf.port = (ushort)port;
|
|
|
|
}
|
|
|
|
} else if (!strcasecmp( "Timeout", cfg->cmd ))
|
2015-04-06 14:49:33 +00:00
|
|
|
server->sconf.timeout = parse_int( cfg );
|
2011-03-27 14:58:23 +00:00
|
|
|
else if (!strcasecmp( "PipelineDepth", cfg->cmd )) {
|
|
|
|
if ((server->max_in_progress = parse_int( cfg )) < 1) {
|
|
|
|
error( "%s:%d: PipelineDepth must be at least 1\n", cfg->file, cfg->line );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfg->err = 1;
|
2011-03-27 14:58:23 +00:00
|
|
|
}
|
2015-05-08 08:20:09 +00:00
|
|
|
} else if (!strcasecmp( "DisableExtension", cfg->cmd ) ||
|
|
|
|
!strcasecmp( "DisableExtensions", cfg->cmd )) {
|
|
|
|
arg = cfg->val;
|
|
|
|
do {
|
|
|
|
for (u = 0; u < as(cap_list); u++) {
|
|
|
|
if (!strcasecmp( cap_list[u], arg )) {
|
|
|
|
server->cap_mask |= 1 << u;
|
|
|
|
goto gotcap;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
error( "%s:%d: Unrecognized IMAP extension '%s'\n", cfg->file, cfg->line, arg );
|
|
|
|
cfg->err = 1;
|
|
|
|
gotcap: ;
|
2019-07-28 18:50:31 +00:00
|
|
|
} while ((arg = get_arg( cfg, ARG_OPTIONAL, NULL )));
|
2011-03-27 14:58:23 +00:00
|
|
|
}
|
2011-03-27 10:06:41 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
2006-03-19 10:44:53 +00:00
|
|
|
else if (!strcasecmp( "CertificateFile", cfg->cmd )) {
|
2011-01-23 12:43:00 +00:00
|
|
|
server->sconf.cert_file = expand_strdup( cfg->val );
|
|
|
|
if (access( server->sconf.cert_file, R_OK )) {
|
2011-04-10 13:32:25 +00:00
|
|
|
sys_error( "%s:%d: CertificateFile '%s'",
|
|
|
|
cfg->file, cfg->line, server->sconf.cert_file );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfg->err = 1;
|
2006-03-19 10:44:53 +00:00
|
|
|
}
|
2014-07-27 13:42:33 +00:00
|
|
|
} else if (!strcasecmp( "SystemCertificates", cfg->cmd )) {
|
|
|
|
server->sconf.system_certs = parse_bool( cfg );
|
2015-08-08 17:45:53 +00:00
|
|
|
} else if (!strcasecmp( "ClientCertificate", cfg->cmd )) {
|
|
|
|
server->sconf.client_certfile = expand_strdup( cfg->val );
|
|
|
|
if (access( server->sconf.client_certfile, R_OK )) {
|
|
|
|
sys_error( "%s:%d: ClientCertificate '%s'",
|
|
|
|
cfg->file, cfg->line, server->sconf.client_certfile );
|
|
|
|
cfg->err = 1;
|
|
|
|
}
|
|
|
|
} else if (!strcasecmp( "ClientKey", cfg->cmd )) {
|
|
|
|
server->sconf.client_keyfile = expand_strdup( cfg->val );
|
|
|
|
if (access( server->sconf.client_keyfile, R_OK )) {
|
|
|
|
sys_error( "%s:%d: ClientKey '%s'",
|
|
|
|
cfg->file, cfg->line, server->sconf.client_keyfile );
|
|
|
|
cfg->err = 1;
|
|
|
|
}
|
2019-11-09 18:47:55 +00:00
|
|
|
} else if (!strcasecmp( "CipherString", cfg->cmd )) {
|
|
|
|
server->sconf.cipher_string = nfstrdup( cfg->val );
|
2014-07-12 18:35:55 +00:00
|
|
|
} else if (!strcasecmp( "SSLType", cfg->cmd )) {
|
|
|
|
if (!strcasecmp( "None", cfg->val )) {
|
|
|
|
server->ssl_type = SSL_None;
|
|
|
|
} else if (!strcasecmp( "STARTTLS", cfg->val )) {
|
|
|
|
server->ssl_type = SSL_STARTTLS;
|
|
|
|
} else if (!strcasecmp( "IMAPS", cfg->val )) {
|
|
|
|
server->ssl_type = SSL_IMAPS;
|
|
|
|
} else {
|
|
|
|
error( "%s:%d: Invalid SSL type\n", cfg->file, cfg->line );
|
|
|
|
cfg->err = 1;
|
|
|
|
}
|
|
|
|
} else if (!strcasecmp( "SSLVersion", cfg->cmd ) ||
|
|
|
|
!strcasecmp( "SSLVersions", cfg->cmd )) {
|
|
|
|
server->sconf.ssl_versions = 0;
|
|
|
|
arg = cfg->val;
|
|
|
|
do {
|
|
|
|
if (!strcasecmp( "SSLv2", arg )) {
|
2017-08-11 07:20:28 +00:00
|
|
|
warn( "Warning: SSLVersion SSLv2 is no longer supported\n" );
|
2014-07-12 18:35:55 +00:00
|
|
|
} else if (!strcasecmp( "SSLv3", arg )) {
|
2019-11-26 15:18:58 +00:00
|
|
|
warn( "Warning: SSLVersion SSLv3 is no longer supported\n" );
|
2014-07-12 18:35:55 +00:00
|
|
|
} else if (!strcasecmp( "TLSv1", arg )) {
|
|
|
|
server->sconf.ssl_versions |= TLSv1;
|
|
|
|
} else if (!strcasecmp( "TLSv1.1", arg )) {
|
|
|
|
server->sconf.ssl_versions |= TLSv1_1;
|
|
|
|
} else if (!strcasecmp( "TLSv1.2", arg )) {
|
|
|
|
server->sconf.ssl_versions |= TLSv1_2;
|
2019-11-26 15:05:46 +00:00
|
|
|
} else if (!strcasecmp( "TLSv1.3", arg )) {
|
|
|
|
server->sconf.ssl_versions |= TLSv1_3;
|
2014-07-12 18:35:55 +00:00
|
|
|
} else {
|
|
|
|
error( "%s:%d: Unrecognized SSL version\n", cfg->file, cfg->line );
|
|
|
|
cfg->err = 1;
|
|
|
|
}
|
2019-07-28 18:50:31 +00:00
|
|
|
} while ((arg = get_arg( cfg, ARG_OPTIONAL, NULL )));
|
2006-03-19 10:44:53 +00:00
|
|
|
} else if (!strcasecmp( "RequireSSL", cfg->cmd ))
|
2014-07-12 18:35:55 +00:00
|
|
|
require_ssl = parse_bool( cfg );
|
2006-05-28 15:43:58 +00:00
|
|
|
else if (!strcasecmp( "UseIMAPS", cfg->cmd ))
|
2014-07-12 18:35:55 +00:00
|
|
|
use_imaps = parse_bool( cfg );
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "UseSSLv2", cfg->cmd ))
|
2017-08-11 07:20:28 +00:00
|
|
|
warn( "Warning: UseSSLv2 is no longer supported\n" );
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "UseSSLv3", cfg->cmd ))
|
2019-11-26 15:18:58 +00:00
|
|
|
warn( "Warning: UseSSLv3 is no longer supported\n" );
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "UseTLSv1", cfg->cmd ))
|
2014-07-12 18:35:55 +00:00
|
|
|
use_tlsv1 = parse_bool( cfg );
|
2013-02-03 16:47:05 +00:00
|
|
|
else if (!strcasecmp( "UseTLSv1.1", cfg->cmd ))
|
2014-07-12 18:35:55 +00:00
|
|
|
use_tlsv11 = parse_bool( cfg );
|
2013-02-03 16:47:05 +00:00
|
|
|
else if (!strcasecmp( "UseTLSv1.2", cfg->cmd ))
|
2014-07-12 18:35:55 +00:00
|
|
|
use_tlsv12 = parse_bool( cfg );
|
2019-11-26 15:05:46 +00:00
|
|
|
else if (!strcasecmp( "UseTLSv1.3", cfg->cmd ))
|
|
|
|
use_tlsv13 = parse_bool( cfg );
|
2015-05-09 15:12:31 +00:00
|
|
|
#endif
|
2014-07-12 19:02:25 +00:00
|
|
|
else if (!strcasecmp( "AuthMech", cfg->cmd ) ||
|
|
|
|
!strcasecmp( "AuthMechs", cfg->cmd )) {
|
|
|
|
arg = cfg->val;
|
|
|
|
do
|
|
|
|
add_string_list( &server->auth_mechs, arg );
|
2019-07-28 18:50:31 +00:00
|
|
|
while ((arg = get_arg( cfg, ARG_OPTIONAL, NULL )));
|
2014-07-12 19:02:25 +00:00
|
|
|
} else if (!strcasecmp( "RequireCRAM", cfg->cmd ))
|
|
|
|
require_cram = parse_bool( cfg );
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "Tunnel", cfg->cmd ))
|
2011-01-23 12:43:00 +00:00
|
|
|
server->sconf.tunnel = nfstrdup( cfg->val );
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (store) {
|
2006-03-19 10:44:53 +00:00
|
|
|
if (!strcasecmp( "Account", cfg->cmd )) {
|
|
|
|
for (srv = servers; srv; srv = srv->next)
|
|
|
|
if (srv->name && !strcmp( srv->name, cfg->val ))
|
|
|
|
goto gotsrv;
|
2006-03-19 11:29:12 +00:00
|
|
|
error( "%s:%d: unknown IMAP account '%s'\n", cfg->file, cfg->line, cfg->val );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfg->err = 1;
|
2006-03-19 10:44:53 +00:00
|
|
|
continue;
|
|
|
|
gotsrv:
|
|
|
|
store->server = srv;
|
|
|
|
} else if (!strcasecmp( "UseNamespace", cfg->cmd ))
|
2004-03-27 16:07:20 +00:00
|
|
|
store->use_namespace = parse_bool( cfg );
|
2019-11-26 14:49:19 +00:00
|
|
|
else if (!strcasecmp( "SubscribedOnly", cfg->cmd ))
|
|
|
|
store->use_lsub = parse_bool( cfg );
|
2004-03-27 16:07:20 +00:00
|
|
|
else if (!strcasecmp( "Path", cfg->cmd ))
|
2020-12-17 14:53:40 +00:00
|
|
|
store->path = nfstrdup( cfg->val );
|
2015-05-23 08:47:48 +00:00
|
|
|
else if (!strcasecmp( "PathDelimiter", cfg->cmd )) {
|
|
|
|
if (strlen( cfg->val ) != 1) {
|
|
|
|
error( "%s:%d: Path delimiter must be exactly one character long\n", cfg->file, cfg->line );
|
|
|
|
cfg->err = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
store->delimiter = cfg->val[0];
|
|
|
|
} else
|
2020-10-05 11:05:38 +00:00
|
|
|
parse_generic_store( &store->gen, cfg, "IMAPStore" );
|
2006-03-19 10:44:53 +00:00
|
|
|
continue;
|
2004-03-27 16:07:20 +00:00
|
|
|
} else {
|
2020-10-05 11:05:38 +00:00
|
|
|
error( "%s:%d: keyword '%s' is not recognized in IMAPAccount sections\n",
|
|
|
|
cfg->file, cfg->line, cfg->cmd );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfg->err = 1;
|
2006-03-19 10:44:53 +00:00
|
|
|
continue;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2006-03-19 10:44:53 +00:00
|
|
|
acc_opt = 1;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
if (!store || !store->server) {
|
2011-01-23 12:43:00 +00:00
|
|
|
if (!server->sconf.tunnel && !server->sconf.host) {
|
2014-07-05 21:03:42 +00:00
|
|
|
error( "%s '%s' has neither Tunnel nor Host\n", type, name );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfg->err = 1;
|
2004-03-27 16:07:20 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2019-11-26 11:17:33 +00:00
|
|
|
if (server->user && server->user_cmd) {
|
|
|
|
error( "%s '%s' has both User and UserCmd\n", type, name );
|
|
|
|
cfg->err = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
2013-07-27 08:37:15 +00:00
|
|
|
if (server->pass && server->pass_cmd) {
|
2014-07-05 21:02:30 +00:00
|
|
|
error( "%s '%s' has both Pass and PassCmd\n", type, name );
|
2013-07-27 08:37:15 +00:00
|
|
|
cfg->err = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
2019-11-27 16:13:44 +00:00
|
|
|
#ifdef HAVE_MACOS_KEYCHAIN
|
|
|
|
if (server->use_keychain && (server->pass || server->pass_cmd)) {
|
|
|
|
error( "%s '%s' has UseKeychain enabled despite specifying Pass/PassCmd\n", type, name );
|
|
|
|
cfg->err = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
#endif
|
2014-07-05 21:10:09 +00:00
|
|
|
#ifdef HAVE_LIBSSL
|
2019-11-26 15:18:58 +00:00
|
|
|
if ((use_tlsv1 & use_tlsv11 & use_tlsv12 & use_tlsv13) != -1 || use_imaps >= 0 || require_ssl >= 0) {
|
2014-07-12 18:35:55 +00:00
|
|
|
if (server->ssl_type >= 0 || server->sconf.ssl_versions >= 0) {
|
2017-09-23 10:32:48 +00:00
|
|
|
error( "%s '%s': The deprecated UseSSL*, UseTLS*, UseIMAPS, and RequireSSL options are mutually exclusive with SSLType and SSLVersions.\n", type, name );
|
2014-07-12 18:35:55 +00:00
|
|
|
cfg->err = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
warn( "Notice: %s '%s': UseSSL*, UseTLS*, UseIMAPS, and RequireSSL are deprecated. Use SSLType and SSLVersions instead.\n", type, name );
|
|
|
|
server->sconf.ssl_versions =
|
|
|
|
(use_tlsv1 == 0 ? 0 : TLSv1) |
|
|
|
|
(use_tlsv11 != 1 ? 0 : TLSv1_1) |
|
2019-11-26 15:05:46 +00:00
|
|
|
(use_tlsv12 != 1 ? 0 : TLSv1_2) |
|
|
|
|
(use_tlsv13 != 1 ? 0 : TLSv1_3);
|
2014-07-12 18:35:55 +00:00
|
|
|
if (use_imaps == 1) {
|
|
|
|
server->ssl_type = SSL_IMAPS;
|
|
|
|
} else if (require_ssl) {
|
|
|
|
server->ssl_type = SSL_STARTTLS;
|
|
|
|
} else if (!server->sconf.ssl_versions) {
|
|
|
|
server->ssl_type = SSL_None;
|
|
|
|
} else {
|
|
|
|
warn( "Notice: %s '%s': 'RequireSSL no' is being ignored\n", type, name );
|
|
|
|
server->ssl_type = SSL_STARTTLS;
|
|
|
|
}
|
|
|
|
if (server->ssl_type != SSL_None && !server->sconf.ssl_versions) {
|
|
|
|
error( "%s '%s' requires SSL but no SSL versions enabled\n", type, name );
|
|
|
|
cfg->err = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (server->sconf.ssl_versions < 0)
|
2019-11-26 15:05:46 +00:00
|
|
|
server->sconf.ssl_versions = TLSv1 | TLSv1_1 | TLSv1_2 | TLSv1_3;
|
2014-07-12 18:35:55 +00:00
|
|
|
if (server->ssl_type < 0)
|
|
|
|
server->ssl_type = server->sconf.tunnel ? SSL_None : SSL_STARTTLS;
|
2014-07-05 21:11:45 +00:00
|
|
|
}
|
2014-07-05 21:10:09 +00:00
|
|
|
#endif
|
2014-07-12 19:02:25 +00:00
|
|
|
if (require_cram >= 0) {
|
|
|
|
if (server->auth_mechs) {
|
2017-09-23 10:32:48 +00:00
|
|
|
error( "%s '%s': The deprecated RequireCRAM option is mutually exclusive with AuthMech.\n", type, name );
|
2014-07-12 19:02:25 +00:00
|
|
|
cfg->err = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
warn( "Notice: %s '%s': RequireCRAM is deprecated. Use AuthMech instead.\n", type, name );
|
|
|
|
if (require_cram)
|
|
|
|
add_string_list(&server->auth_mechs, "CRAM-MD5");
|
|
|
|
}
|
|
|
|
if (!server->auth_mechs)
|
|
|
|
add_string_list( &server->auth_mechs, "*" );
|
2014-07-06 08:06:40 +00:00
|
|
|
if (!server->sconf.port)
|
|
|
|
server->sconf.port =
|
|
|
|
#ifdef HAVE_LIBSSL
|
2014-07-12 18:35:55 +00:00
|
|
|
server->ssl_type == SSL_IMAPS ? 993 :
|
2014-07-06 08:06:40 +00:00
|
|
|
#endif
|
|
|
|
143;
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
2006-03-19 10:44:53 +00:00
|
|
|
if (store) {
|
|
|
|
if (!store->server) {
|
|
|
|
store->server = nfmalloc( sizeof(sserver) );
|
|
|
|
memcpy( store->server, &sserver, sizeof(sserver) );
|
2020-12-17 14:53:40 +00:00
|
|
|
store->server->name = store->name;
|
2006-03-19 10:44:53 +00:00
|
|
|
} else if (acc_opt) {
|
2014-07-05 21:02:30 +00:00
|
|
|
error( "%s '%s' has both Account and account-specific options\n", type, name );
|
2012-09-15 09:46:42 +00:00
|
|
|
cfg->err = 1;
|
2006-03-19 10:44:53 +00:00
|
|
|
}
|
2004-03-27 16:07:20 +00:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-07-08 15:27:37 +00:00
|
|
|
static uint
|
2017-03-19 12:50:41 +00:00
|
|
|
imap_get_caps( store_t *gctx ATTR_UNUSED )
|
|
|
|
{
|
2020-12-14 22:16:01 +00:00
|
|
|
return DRV_CRLF | DRV_VERBOSE | DRV_ASYNC;
|
2017-03-19 12:50:41 +00:00
|
|
|
}
|
|
|
|
|
2004-03-27 16:07:20 +00:00
|
|
|
struct driver imap_driver = {
|
2017-03-19 12:50:41 +00:00
|
|
|
imap_get_caps,
|
2004-03-27 16:07:20 +00:00
|
|
|
imap_parse_store,
|
2006-03-20 19:38:20 +00:00
|
|
|
imap_cleanup,
|
2015-05-24 09:37:15 +00:00
|
|
|
imap_alloc_store,
|
2017-03-21 18:27:04 +00:00
|
|
|
imap_set_bad_callback,
|
2015-05-24 09:37:15 +00:00
|
|
|
imap_connect_store,
|
|
|
|
imap_free_store,
|
2006-03-20 19:38:20 +00:00
|
|
|
imap_cancel_store,
|
2014-12-27 21:13:24 +00:00
|
|
|
imap_list_store,
|
|
|
|
imap_select_box,
|
2017-03-24 13:06:19 +00:00
|
|
|
imap_get_box_path,
|
2014-12-29 00:42:17 +00:00
|
|
|
imap_create_box,
|
2014-12-27 22:39:55 +00:00
|
|
|
imap_open_box,
|
2017-03-24 17:43:39 +00:00
|
|
|
imap_get_uidnext,
|
2020-01-08 17:22:48 +00:00
|
|
|
imap_get_supported_flags,
|
2014-12-29 01:08:48 +00:00
|
|
|
imap_confirm_box_empty,
|
|
|
|
imap_delete_box,
|
|
|
|
imap_finish_delete_box,
|
2014-12-27 21:13:24 +00:00
|
|
|
imap_prepare_load_box,
|
|
|
|
imap_load_box,
|
2004-03-27 16:07:20 +00:00
|
|
|
imap_fetch_msg,
|
|
|
|
imap_store_msg,
|
2011-04-10 11:06:07 +00:00
|
|
|
imap_find_new_msgs,
|
2014-12-27 21:13:24 +00:00
|
|
|
imap_set_msg_flags,
|
2004-03-27 16:07:20 +00:00
|
|
|
imap_trash_msg,
|
2014-12-27 21:13:24 +00:00
|
|
|
imap_close_box,
|
|
|
|
imap_cancel_cmds,
|
|
|
|
imap_commit_cmds,
|
2017-03-19 12:46:03 +00:00
|
|
|
imap_get_memory_usage,
|
|
|
|
imap_get_fail_state,
|
2004-03-27 16:07:20 +00:00
|
|
|
};
|