#include <time.h>
#include "intl.h"
+#include "prefs_account.h"
#include "imap.h"
#include "socket.h"
#include "ssl.h"
#include "procheader.h"
#include "folder.h"
#include "statusbar.h"
-#include "prefs_account.h"
#include "codeconv.h"
#include "utils.h"
#include "inputdialog.h"
#define IMAPS_PORT 993
#endif
+#define QUOTE_IF_REQUIRED(out, str) \
+{ \
+ if (*str != '"' && strchr(str, ' ')) { \
+ gchar *__tmp; \
+ gint len; \
+ \
+ len = strlen(str) + 3; \
+ Xalloca(__tmp, len, return IMAP_ERROR); \
+ g_snprintf(__tmp, len, "\"%s\"", str); \
+ out = __tmp; \
+ } else { \
+ Xstrdup_a(out, str, return IMAP_ERROR); \
+ } \
+}
+
static GList *session_list = NULL;
static gint imap_cmd_count = 0;
static gchar *imap_query_password (const gchar *server,
const gchar *user);
-static void imap_scan_tree_recursive (IMAPSession *session,
+static gint imap_scan_tree_recursive (IMAPSession *session,
FolderItem *item,
IMAPNameSpace *namespace);
static GSList *imap_parse_list (IMAPSession *session,
#if USE_SSL
static SockInfo *imap_open (const gchar *server,
gushort port,
- gchar *buf,
gboolean use_ssl);
#else
static SockInfo *imap_open (const gchar *server,
- gushort port,
- gchar *buf);
+ gushort port);
#endif
+static SockInfo *imap_open_tunnel(const gchar *server,
+ const gchar *tunnelcmd);
+
+static SockInfo *imap_init_sock(SockInfo *sock);
+
static gint imap_set_message_flags (IMAPSession *session,
guint32 first_uid,
guint32 last_uid,
static MsgFlags imap_parse_flags (const gchar *flag_str);
static MsgInfo *imap_parse_envelope (SockInfo *sock,
GString *line_str);
+static gint imap_greeting (SockInfo *sock,
+ gboolean *is_preauth);
/* low-level IMAP4rev1 commands */
static gint imap_cmd_login (SockInfo *sock,
gint len);
static gchar *search_array_contain_str (GPtrArray *array,
gchar *str);
+static gchar *search_array_str (GPtrArray *array,
+ gchar *str);
+static gchar *search_array_starts (GPtrArray *array,
+ const gchar *str);
static void imap_path_separator_subst (gchar *str,
gchar separator);
if (!rfolder->session) {
rfolder->session =
-#if USE_SSL
- imap_session_new(folder->account->recv_server, port,
- folder->account->userid,
- folder->account->passwd,
- folder->account->ssl_imap);
-#else
- imap_session_new(folder->account->recv_server, port,
- folder->account->userid,
- folder->account->passwd);
-#endif
- if (rfolder->session)
+ imap_session_new(folder->account);
+ if (rfolder->session) {
imap_parse_namespace(IMAP_SESSION(rfolder->session),
IMAP_FOLDER(folder));
-
- rfolder->session->last_access_time = time(NULL);
+ rfolder->session->last_access_time = time(NULL);
+ }
statusbar_pop_all();
return IMAP_SESSION(rfolder->session);
}
+ /* I think the point of this code is to avoid sending a
+ * keepalive if we've used the session recently and therefore
+ * think it's still alive. Unfortunately, most of the code
+ * does not yet check for errors on the socket, and so if the
+ * connection drops we don't notice until the timeout expires.
+ * A better solution than sending a NOOP every time would be
+ * for every command to be prepared to retry until it is
+ * successfully sent. -- mbp */
if (time(NULL) - rfolder->session->last_access_time < SESSION_TIMEOUT) {
rfolder->session->last_access_time = time(NULL);
statusbar_pop_all();
folder->account->recv_server, port);
session_destroy(rfolder->session);
rfolder->session =
-#if USE_SSL
- imap_session_new(folder->account->recv_server, port,
- folder->account->userid,
- folder->account->passwd,
- folder->account->ssl_imap);
-#else
- imap_session_new(folder->account->recv_server, port,
- folder->account->userid,
- folder->account->passwd);
-#endif
+ imap_session_new(folder->account);
if (rfolder->session)
imap_parse_namespace(IMAP_SESSION(rfolder->session),
IMAP_FOLDER(folder));
}
- rfolder->session->last_access_time = time(NULL);
+ if (rfolder->session)
+ rfolder->session->last_access_time = time(NULL);
statusbar_pop_all();
return IMAP_SESSION(rfolder->session);
}
return pass;
}
-#if USE_SSL
-Session *imap_session_new(const gchar *server, gushort port,
- const gchar *user, const gchar *pass,
- gboolean use_ssl)
-#else
-Session *imap_session_new(const gchar *server, gushort port,
- const gchar *user, const gchar *pass)
-#endif
+Session *imap_session_new(const PrefsAccount *account)
{
- gchar buf[IMAPBUFSIZE];
IMAPSession *session;
SockInfo *imap_sock;
+ gushort port;
+ gchar *pass;
+ gboolean is_preauth;
- g_return_val_if_fail(server != NULL, NULL);
- g_return_val_if_fail(user != NULL, NULL);
+#ifdef USE_SSL
+ /* FIXME: IMAP over SSL only... */
+ gboolean use_ssl;
- if (!pass) {
- gchar *tmp_pass;
- tmp_pass = imap_query_password(server, user);
- if (!tmp_pass)
- return NULL;
- Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return NULL;});
- g_free(tmp_pass);
- }
+ port = account->set_imapport ? account->imapport
+ : account->ssl_imap ? IMAPS_PORT : IMAP4_PORT;
+ use_ssl = account->ssl_imap ? TRUE : FALSE;
+#else
+ port = account->set_imapport ? account->imapport
+ : IMAP4_PORT;
+#endif
- log_message(_("creating IMAP4 connection to %s:%d ...\n"),
- server, port);
+ if (account->set_tunnelcmd) {
+ log_message(_("creating tunneled IMAP4 connection\n"));
+ if ((imap_sock = imap_open_tunnel(account->recv_server,
+ account->tunnelcmd)) == NULL)
+ return NULL;
+ } else {
+ g_return_val_if_fail(account->recv_server != NULL, NULL);
+ log_message(_("creating IMAP4 connection to %s:%d ...\n"),
+ account->recv_server, port);
+
#if USE_SSL
- if ((imap_sock = imap_open(server, port, buf, use_ssl)) == NULL)
+ if ((imap_sock = imap_open(account->recv_server, port,
+ use_ssl)) == NULL)
#else
- if ((imap_sock = imap_open(server, port, buf)) == NULL)
+ if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
#endif
- return NULL;
- if (imap_cmd_login(imap_sock, user, pass) != IMAP_SUCCESS) {
- imap_cmd_logout(imap_sock);
- sock_close(imap_sock);
- return NULL;
+ return NULL;
+ }
+
+ /* Only need to log in if the connection was not PREAUTH */
+ imap_greeting(imap_sock, &is_preauth);
+ log_message("IMAP connection is %s-authenticated\n",
+ (is_preauth) ? "pre" : "un");
+ if (!is_preauth) {
+ g_return_val_if_fail(account->userid != NULL, NULL);
+
+ pass = account->passwd;
+ if (!pass) {
+ gchar *tmp_pass;
+ tmp_pass = imap_query_password(account->recv_server, account->userid);
+ if (!tmp_pass)
+ return NULL;
+ Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return NULL;});
+ g_free(tmp_pass);
+ }
+
+ if (imap_cmd_login(imap_sock, account->userid, pass) != IMAP_SUCCESS) {
+ imap_cmd_logout(imap_sock);
+ sock_close(imap_sock);
+ return NULL;
+ }
}
session = g_new(IMAPSession, 1);
SESSION(session)->type = SESSION_IMAP;
- SESSION(session)->server = g_strdup(server);
+ SESSION(session)->server = g_strdup(account->recv_server);
SESSION(session)->sock = imap_sock;
SESSION(session)->connected = TRUE;
SESSION(session)->phase = SESSION_READY;
imap_create_trash(folder);
}
-static void imap_scan_tree_recursive(IMAPSession *session,
- FolderItem *item,
+static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item,
IMAPNameSpace *namespace)
{
IMAPFolder *imapfolder;
FolderItem *new_item;
GSList *item_list, *cur;
gchar *real_path;
+ gchar *wildcard_path;
+ gchar separator = '/';
+ gchar wildcard[3];
- g_return_if_fail(item != NULL);
- g_return_if_fail(item->folder != NULL);
- g_return_if_fail(item->no_sub == FALSE);
+ g_return_val_if_fail(item != NULL, -1);
+ g_return_val_if_fail(item->folder != NULL, -1);
+ g_return_val_if_fail(item->no_sub == FALSE, -1);
imapfolder = IMAP_FOLDER(item->folder);
+ if (namespace && namespace->separator)
+ separator = namespace->separator;
+
if (item->folder->ui_func)
item->folder->ui_func(item->folder, item,
item->folder->ui_func_data);
if (item->path) {
+ wildcard[0] = separator;
+ wildcard[1] = '%';
+ wildcard[2] = '\0';
real_path = imap_get_real_path(imapfolder, item->path);
- imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s%c%%",
- real_path,
- namespace && namespace->separator
- ? namespace->separator : '/');
} else {
+ wildcard[0] = '%';
+ wildcard[1] = '\0';
real_path = g_strdup(namespace && namespace->name
? namespace->name : "");
- imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s%%",
- real_path);
}
- strtailchomp(real_path, namespace && namespace->separator
- ? namespace->separator : '/');
+ Xstrcat_a(wildcard_path, real_path, wildcard,
+ {g_free(real_path); return IMAP_ERROR;});
+ QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
+ imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s",
+ wildcard_path);
+
+ strtailchomp(real_path, separator);
item_list = imap_parse_list(session, real_path);
+ g_free(real_path);
+
for (cur = item_list; cur != NULL; cur = cur->next) {
new_item = cur->data;
if (!strcmp(new_item->path, "INBOX")) {
if (new_item->no_sub == FALSE)
imap_scan_tree_recursive(session, new_item, namespace);
}
+
+ return IMAP_SUCCESS;
}
static GSList *imap_parse_list(IMAPSession *session, const gchar *path)
debug_print(_("done.\n"));
}
+
+static SockInfo *imap_open_tunnel(const gchar *server,
+ const gchar *tunnelcmd)
+{
+ SockInfo *sock;
+
+ if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL)
+ return NULL;
+
+ return imap_init_sock(sock);
+}
+
+
#if USE_SSL
-static SockInfo *imap_open(const gchar *server, gushort port, gchar *buf,
+static SockInfo *imap_open(const gchar *server, gushort port,
gboolean use_ssl)
#else
-static SockInfo *imap_open(const gchar *server, gushort port, gchar *buf)
+static SockInfo *imap_open(const gchar *server, gushort port)
#endif
{
SockInfo *sock;
}
#endif
- imap_cmd_count = 0;
+ return imap_init_sock(sock);
+}
- if (imap_cmd_noop(sock) != IMAP_SUCCESS) {
- sock_close(sock);
- return NULL;
- }
+static SockInfo *imap_init_sock(SockInfo *sock)
+{
+ imap_cmd_count = 0;
return sock;
}
+
+
#define THROW goto catch
static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
} else if (g_strncasecmp(p, "Flagged", 7) == 0) {
MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
+ } else if (g_strncasecmp(p, "Answered", 8) == 0) {
+ MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
}
}
return msginfo;
}
+gint imap_msg_set_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
+{
+ Folder *folder;
+ IMAPSession *session;
+ IMAPFlags iflags = 0;
+ gint ok = IMAP_SUCCESS;
+
+ g_return_val_if_fail(msginfo != NULL, -1);
+ g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1);
+ g_return_val_if_fail(msginfo->folder != NULL, -1);
+ g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
+
+ folder = msginfo->folder->folder;
+ g_return_val_if_fail(folder->type == F_IMAP, -1);
+
+ session = imap_session_get(folder);
+ if (!session) return -1;
+
+ if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
+ if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
+ if (iflags) {
+ ok = imap_set_message_flags(session, msginfo->msgnum,
+ msginfo->msgnum, iflags, TRUE);
+ if (ok != IMAP_SUCCESS) return ok;
+ }
+
+ if (flags & MSG_UNREAD)
+ ok = imap_set_message_flags(session, msginfo->msgnum,
+ msginfo->msgnum, IMAP_FLAG_SEEN,
+ FALSE);
+ return ok;
+}
+
+gint imap_msg_unset_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
+{
+ Folder *folder;
+ IMAPSession *session;
+ IMAPFlags iflags = 0;
+ gint ok = IMAP_SUCCESS;
+
+ g_return_val_if_fail(msginfo != NULL, -1);
+ g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1);
+ g_return_val_if_fail(msginfo->folder != NULL, -1);
+ g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
+
+ folder = msginfo->folder->folder;
+ g_return_val_if_fail(folder->type == F_IMAP, -1);
+
+ session = imap_session_get(folder);
+ if (!session) return -1;
+
+ if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
+ if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
+ if (iflags) {
+ ok = imap_set_message_flags(session, msginfo->msgnum,
+ msginfo->msgnum, iflags, FALSE);
+ if (ok != IMAP_SUCCESS) return ok;
+ }
+
+ if (flags & MSG_UNREAD)
+ ok = imap_set_message_flags(session, msginfo->msgnum,
+ msginfo->msgnum, IMAP_FLAG_SEEN,
+ TRUE);
+ return ok;
+}
+
static gint imap_set_message_flags(IMAPSession *session,
guint32 first_uid,
guint32 last_uid,
return ok;
}
-#define QUOTE_IF_REQUIRED(out, str) \
-{ \
- if (*str != '"' && strchr(str, ' ')) { \
- gint len; \
- len = strlen(str) + 3; \
- Xalloca(out, len, return IMAP_ERROR); \
- g_snprintf(out, len, "\"%s\"", str); \
- } else { \
- Xstrdup_a(out, str, return IMAP_ERROR); \
- } \
-}
-
static gint imap_status(IMAPSession *session, IMAPFolder *folder,
const gchar *path,
gint *messages, gint *recent, gint *unseen,
ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
if (ok != IMAP_SUCCESS) THROW(ok);
- str = search_array_contain_str(argbuf, "STATUS");
+ str = search_array_str(argbuf, "STATUS");
if (!str) THROW(IMAP_ERROR);
str = strchr(str, '(');
return imap_cmd_ok(sock, NULL);
}
+/* Send a NOOP, and examine the server's response to see whether this
+ * connection is pre-authenticated or not. */
+static gint imap_greeting(SockInfo *sock, gboolean *is_preauth)
+{
+ GPtrArray *argbuf;
+ gint r;
+
+ imap_cmd_gen_send(sock, "NOOP");
+ argbuf = g_ptr_array_new(); /* will hold messages sent back */
+ r = imap_cmd_ok(sock, argbuf);
+ *is_preauth = search_array_starts(argbuf, "PREAUTH") != NULL;
+
+ return r;
+}
+
static gint imap_cmd_noop(SockInfo *sock)
{
imap_cmd_gen_send(sock, "NOOP");
imap_cmd_gen_send(sock, "NAMESPACE");
if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW(ok);
- str = search_array_contain_str(argbuf, "NAMESPACE");
+ str = search_array_str(argbuf, "NAMESPACE");
if (!str) THROW(IMAP_ERROR);
*ns_str = g_strdup(str);
{
gint ok;
gint size;
+ gchar *destfolder_;
g_return_val_if_fail(file != NULL, IMAP_ERROR);
size = get_file_size(file);
- imap_cmd_gen_send(sock, "APPEND %s {%d}", destfolder, size);
+ QUOTE_IF_REQUIRED(destfolder_, destfolder);
+ imap_cmd_gen_send(sock, "APPEND %s {%d}", destfolder_, size);
ok = imap_cmd_ok(sock, NULL);
if (ok != IMAP_SUCCESS) {
- log_warning(_("can't append %s to %s\n"), file, destfolder);
+ log_warning(_("can't append %s to %s\n"), file, destfolder_);
return -1;
}
return IMAP_SUCCESS;
}
+
static gint imap_cmd_ok(SockInfo *sock, GPtrArray *argbuf)
{
gint ok;
return (gchar *)(*p == ch ? p + 1 : p);
}
+static gchar *search_array_starts(GPtrArray *array, const gchar *str)
+{
+ gint i;
+ size_t len;
+
+ g_return_val_if_fail(str != NULL, NULL);
+ len = strlen(str);
+ for (i = 0; i < array->len; i++) {
+ gchar *tmp;
+ tmp = g_ptr_array_index(array, i);
+ if (strncmp(tmp, str, len) == 0)
+ return tmp;
+ }
+ return NULL;
+}
+
static gchar *search_array_contain_str(GPtrArray *array, gchar *str)
{
gint i;
return NULL;
}
+static gchar *search_array_str(GPtrArray *array, gchar *str)
+{
+ gint i;
+ gint len;
+
+ len = strlen(str);
+
+ for (i = 0; i < array->len; i++) {
+ gchar *tmp;
+
+ tmp = g_ptr_array_index(array, i);
+ if (!strncmp(tmp, str, len))
+ return tmp;
+ }
+
+ return NULL;
+}
+
static void imap_path_separator_subst(gchar *str, gchar separator)
{
if (separator && separator != '/')