# include "config.h"
#endif
+#include "defs.h"
+
#include <glib.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <ctype.h>
+#include <time.h>
#include "intl.h"
#include "imap.h"
#include "socket.h"
+#include "ssl.h"
#include "recv.h"
#include "procmsg.h"
#include "procheader.h"
#include "inputdialog.h"
#define IMAP4_PORT 143
+#if USE_SSL
+#define IMAPS_PORT 993
+#endif
static GList *session_list = NULL;
guint32 last_uid);
static void imap_delete_all_cached_messages (FolderItem *item);
+#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);
+#endif
static gint imap_set_message_flags (IMAPSession *session,
guint32 first_uid,
static gint imap_get_uid (IMAPSession *session,
gint msgnum,
guint32 *uid);
-#if 0
-static gint imap_status_uidnext (IMAPSession *session,
+static gint imap_status (IMAPSession *session,
IMAPFolder *folder,
const gchar *path,
- guint32 *uid_next);
-#endif
+ gint *messages,
+ gint *recent,
+ gint *unseen,
+ guint32 *uid_validity);
static void imap_parse_namespace (IMAPSession *session,
IMAPFolder *folder);
const gchar *ref,
const gchar *mailbox,
GPtrArray *argbuf);
+static gint imap_cmd_do_select (SockInfo *sock,
+ const gchar *folder,
+ gboolean examine,
+ gint *exists,
+ gint *recent,
+ gint *unseen,
+ guint32 *uid_validity);
static gint imap_cmd_select (SockInfo *sock,
const gchar *folder,
gint *exists,
gint *recent,
gint *unseen,
guint32 *uid_validity);
-#if 0
-static gint imap_cmd_status (SockInfo *sock,
+static gint imap_cmd_examine (SockInfo *sock,
const gchar *folder,
- const gchar *status,
- gint *value);
-#endif
+ gint *exists,
+ gint *recent,
+ gint *unseen,
+ guint32 *uid_validity);
static gint imap_cmd_create (SockInfo *sock,
const gchar *folder);
static gint imap_cmd_delete (SockInfo *sock,
gchar ch,
gchar *dest,
gint len);
+static gchar *get_quoted (const gchar *src,
+ gchar ch,
+ gchar *dest,
+ gint len);
static gchar *search_array_contain_str (GPtrArray *array,
gchar *str);
static void imap_path_separator_subst (gchar *str,
static IMAPSession *imap_session_get(Folder *folder)
{
RemoteFolder *rfolder = REMOTE_FOLDER(folder);
+ gushort port;
g_return_val_if_fail(folder != NULL, NULL);
g_return_val_if_fail(folder->type == F_IMAP, NULL);
g_return_val_if_fail(folder->account != NULL, NULL);
+#if USE_SSL
+ port = folder->account->set_imapport ? folder->account->imapport
+ : folder->account->ssl_imap ? IMAPS_PORT : IMAP4_PORT;
+#else
+ port = folder->account->set_imapport ? folder->account->imapport
+ : IMAP4_PORT;
+#endif
+
if (!rfolder->session) {
rfolder->session =
- imap_session_new(folder->account->recv_server,
- IMAP4_PORT,
+#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);
- if (rfolder->session)
+#endif
+ if (rfolder->session) {
imap_parse_namespace(IMAP_SESSION(rfolder->session),
IMAP_FOLDER(folder));
+ rfolder->session->last_access_time = time(NULL);
+ }
+ statusbar_pop_all();
+ return IMAP_SESSION(rfolder->session);
+ }
+
+ if (time(NULL) - rfolder->session->last_access_time < SESSION_TIMEOUT) {
+ rfolder->session->last_access_time = time(NULL);
statusbar_pop_all();
return IMAP_SESSION(rfolder->session);
}
if (imap_cmd_noop(rfolder->session->sock) != IMAP_SUCCESS) {
log_warning(_("IMAP4 connection to %s:%d has been"
" disconnected. Reconnecting...\n"),
- folder->account->recv_server, IMAP4_PORT);
+ folder->account->recv_server, port);
session_destroy(rfolder->session);
rfolder->session =
- imap_session_new(folder->account->recv_server,
- IMAP4_PORT, folder->account->userid,
+#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_parse_namespace(IMAP_SESSION(rfolder->session),
IMAP_FOLDER(folder));
}
+ 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
{
gchar buf[IMAPBUFSIZE];
IMAPSession *session;
log_message(_("creating IMAP4 connection to %s:%d ...\n"),
server, port);
+#if USE_SSL
+ if ((imap_sock = imap_open(server, port, buf, use_ssl)) == NULL)
+#else
if ((imap_sock = imap_open(server, port, buf)) == NULL)
+#endif
return NULL;
if (imap_cmd_login(imap_sock, user, pass) != IMAP_SUCCESS) {
imap_cmd_logout(imap_sock);
}
session = g_new(IMAPSession, 1);
- SESSION(session)->type = SESSION_IMAP;
- SESSION(session)->server = g_strdup(server);
- SESSION(session)->sock = imap_sock;
- SESSION(session)->connected = TRUE;
- SESSION(session)->phase = SESSION_READY;
- SESSION(session)->data = NULL;
+ SESSION(session)->type = SESSION_IMAP;
+ SESSION(session)->server = g_strdup(server);
+ SESSION(session)->sock = imap_sock;
+ SESSION(session)->connected = TRUE;
+ SESSION(session)->phase = SESSION_READY;
+ SESSION(session)->last_access_time = time(NULL);
+ SESSION(session)->data = NULL;
session->mbox = NULL;
session_list = g_list_append(session_list, session);
g_return_val_if_fail(item != NULL, NULL);
path = folder_item_get_path(item);
+ if (!is_dir_exist(path))
+ make_dir_hier(path);
filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
g_free(path);
return IMAP_SUCCESS;
}
+#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); \
+ } \
+}
+
gint imap_remove_all_msg(Folder *folder, FolderItem *item)
{
gint exists, recent, unseen;
void imap_scan_folder(Folder *folder, FolderItem *item)
{
+ IMAPSession *session;
+ gint messages, recent, unseen;
+ guint32 uid_validity;
+ gint ok;
+
+ g_return_if_fail(folder != NULL);
+ g_return_if_fail(item != NULL);
+
+ session = imap_session_get(folder);
+ if (!session) return;
+
+ ok = imap_status(session, IMAP_FOLDER(folder), item->path,
+ &messages, &recent, &unseen, &uid_validity);
+ statusbar_pop_all();
+ if (ok != IMAP_SUCCESS) return;
+
+ item->new = recent;
+ item->unread = unseen;
+ item->total = messages;
+ /* item->mtime = uid_validity; */
}
void imap_scan_tree(Folder *folder)
IMAPFolder *imapfolder;
FolderItem *new_item;
GSList *item_list, *cur;
- gchar *real_path;
+ gchar *real_path, *wildcard_path, *wildcard_path_;
g_return_if_fail(item != NULL);
g_return_if_fail(item->folder != NULL);
if (item->path) {
real_path = imap_get_real_path(imapfolder, item->path);
- imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s%c%%",
- real_path,
+ Xstrconcat_a(wildcard_path, real_path,"/%", return IMAP_ERROR);
+ QUOTE_IF_REQUIRED(wildcard_path_, wildcard_path);
+ imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s",
+ wildcard_path_,
namespace && namespace->separator
? namespace->separator : '/');
+ g_free(wildcard_path);
} else {
real_path = g_strdup(namespace && namespace->name
? namespace->name : "");
- imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s%%",
- real_path);
+ Xstrconcat_a(wildcard_path, real_path, "%", return IMAP_ERROR);
+ QUOTE_IF_REQUIRED(wildcard_path_, wildcard_path);
+ imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s",
+ wildcard_path_);
+ g_free(wildcard_path);
}
strtailchomp(real_path, namespace && namespace->separator
}
}
folder_item_append(item, new_item);
+ if (new_item->no_select == FALSE)
+ imap_scan_folder(new_item->folder, new_item);
if (new_item->no_sub == FALSE)
imap_scan_tree_recursive(session, new_item, namespace);
}
gint ok;
IMAPSession *session;
gchar *path;
+ gint exists, recent, unseen;
+ guint32 uid_validity;
g_return_val_if_fail(folder != NULL, -1);
g_return_val_if_fail(item != NULL, -1);
if (!session) return -1;
path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
+
+ ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
+ &exists, &recent, &unseen, &uid_validity);
+ statusbar_pop_all();
+ if (ok != IMAP_SUCCESS) {
+ g_free(path);
+ return -1;
+ }
+
ok = imap_cmd_delete(SESSION(session)->sock, path);
statusbar_pop_all();
if (ok != IMAP_SUCCESS) {
debug_print(_("done.\n"));
}
+#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)
+#endif
{
SockInfo *sock;
return NULL;
}
+#if USE_SSL
+ if (use_ssl && !ssl_init_socket(sock)) {
+ sock_close(sock);
+ return NULL;
+ }
+#endif
+
imap_cmd_count = 0;
if (imap_cmd_noop(sock) != IMAP_SUCCESS) {
} else if (*cur_pos == '\"') {
gchar *p;
- p = strchr_cpy(cur_pos + 1, '\"', dest, dest_len);
+ p = get_quoted(cur_pos, '\"', dest, dest_len);
cur_pos = p ? p : cur_pos + 2;
} else if (*cur_pos == '{') {
gchar buf[32];
const gchar *p = flag_str;
MsgFlags flags;
- flags = 0;
- MSG_SET_FLAGS(flags, MSG_UNREAD|MSG_IMAP);
+ flags.perm_flags = MSG_UNREAD;
+ flags.tmp_flags = MSG_IMAP;
while ((p = strchr(p, '\\')) != NULL) {
p++;
if (g_strncasecmp(p, "Recent", 6) == 0) {
- MSG_SET_FLAGS(flags, MSG_NEW|MSG_UNREAD);
+ MSG_SET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
} else if (g_strncasecmp(p, "Seen", 4) == 0) {
- MSG_UNSET_FLAGS(flags, MSG_NEW|MSG_UNREAD);
+ MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
} else if (g_strncasecmp(p, "Deleted", 7) == 0) {
- MSG_SET_FLAGS(flags, MSG_DELETED);
+ MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
} else if (g_strncasecmp(p, "Flagged", 7) == 0) {
- MSG_SET_FLAGS(flags, MSG_MARKED);
+ MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
}
}
gchar *to = NULL;
gchar *inreplyto = NULL;
gchar *msgid = NULL;
- MsgFlags flags = 0;
- gint n_element = 0;
+ MsgFlags flags = {0, 0};
g_return_val_if_fail(line_str != NULL, NULL);
g_return_val_if_fail(line_str->str[0] == '*' &&
g_return_val_if_fail(*cur_pos == '(', NULL);
cur_pos++;
- while (n_element < 4) {
+ while (*cur_pos != '\0' && *cur_pos != ')') {
while (*cur_pos == ' ') cur_pos++;
if (!strncmp(cur_pos, "UID ", 4)) {
g_warning("invalid FETCH response: %s\n", cur_pos);
break;
}
-
- if (*cur_pos == '\0' || *cur_pos == ')') break;
- n_element++;
}
msginfo = g_new0(MsgInfo, 1);
return ok;
}
-#undef THROW
-
-#if 0
-static gint imap_status_uidnext(IMAPSession *session, IMAPFolder *folder,
- const gchar *path, guint32 *uid_next)
+static gint imap_status(IMAPSession *session, IMAPFolder *folder,
+ const gchar *path,
+ gint *messages, gint *recent, gint *unseen,
+ guint32 *uid_validity)
{
gchar *real_path;
+ gchar *real_path_;
gint ok;
+ GPtrArray *argbuf;
+ gchar *str;
+
+ *messages = *recent = *unseen = *uid_validity = 0;
+
+ argbuf = g_ptr_array_new();
real_path = imap_get_real_path(folder, path);
- ok = imap_cmd_status(SESSION(session)->sock, real_path,
- "UIDNEXT", uid_next);
- if (ok != IMAP_SUCCESS)
- log_warning(_("can't get the next uid of folder: %s\n"),
- real_path);
+ QUOTE_IF_REQUIRED(real_path_, real_path);
+ imap_cmd_gen_send(SESSION(session)->sock, "STATUS %s "
+ "(MESSAGES RECENT UNSEEN UIDVALIDITY)", real_path_);
+
+ ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
+ if (ok != IMAP_SUCCESS) THROW(ok);
+
+ str = search_array_contain_str(argbuf, "STATUS");
+ if (!str) THROW(IMAP_ERROR);
+
+ str = strchr(str, '(');
+ if (!str) THROW(IMAP_ERROR);
+ str++;
+ while (*str != '\0' && *str != ')') {
+ while (*str == ' ') str++;
+
+ if (!strncmp(str, "MESSAGES ", 9)) {
+ str += 9;
+ *messages = strtol(str, &str, 10);
+ } else if (!strncmp(str, "RECENT ", 7)) {
+ str += 7;
+ *recent = strtol(str, &str, 10);
+ } else if (!strncmp(str, "UNSEEN ", 7)) {
+ str += 7;
+ *unseen = strtol(str, &str, 10);
+ } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
+ str += 12;
+ *uid_validity = strtoul(str, &str, 10);
+ } else {
+ g_warning("invalid STATUS response: %s\n", str);
+ break;
+ }
+ }
+
+catch:
g_free(real_path);
+ ptr_array_free_strings(argbuf);
+ g_ptr_array_free(argbuf, TRUE);
return ok;
}
-#endif
+
+#undef THROW
/* low-level IMAP4rev1 commands */
static gint imap_cmd_login(SockInfo *sock,
const gchar *user, const gchar *pass)
{
+ gchar *user_, *pass_;
gint ok;
- if (strchr(user, ' ') != NULL)
- imap_cmd_gen_send(sock, "LOGIN \"%s\" %s", user, pass);
- else
- imap_cmd_gen_send(sock, "LOGIN %s %s", user, pass);
+ QUOTE_IF_REQUIRED(user_, user);
+ QUOTE_IF_REQUIRED(pass_, pass);
+ imap_cmd_gen_send(sock, "LOGIN %s %s", user_, pass_);
ok = imap_cmd_ok(sock, NULL);
if (ok != IMAP_SUCCESS)
static gint imap_cmd_list(SockInfo *sock, const gchar *ref,
const gchar *mailbox, GPtrArray *argbuf)
{
- gchar *new_ref;
- gchar *new_mailbox;
+ gchar *ref_, *mailbox_;
if (!ref) ref = "\"\"";
if (!mailbox) mailbox = "\"\"";
- if (*ref != '"' && strchr(ref, ' ') != NULL)
- new_ref = g_strdup_printf("\"%s\"", ref);
- else
- new_ref = g_strdup(ref);
- if (*mailbox != '"' && strchr(mailbox, ' ') != NULL)
- new_mailbox = g_strdup_printf("\"%s\"", mailbox);
- else
- new_mailbox = g_strdup(mailbox);
-
- imap_cmd_gen_send(sock, "LIST %s %s", new_ref, new_mailbox);
-
- g_free(new_ref);
- g_free(new_mailbox);
+ QUOTE_IF_REQUIRED(ref_, ref);
+ QUOTE_IF_REQUIRED(mailbox_, mailbox);
+ imap_cmd_gen_send(sock, "LIST %s %s", ref_, mailbox_);
return imap_cmd_ok(sock, argbuf);
}
#define THROW goto catch
-static gint imap_cmd_select(SockInfo *sock, const gchar *folder,
- gint *exists, gint *recent, gint *unseen,
- guint32 *uid_validity)
+static gint imap_cmd_do_select(SockInfo *sock, const gchar *folder,
+ gboolean examine,
+ gint *exists, gint *recent, gint *unseen,
+ guint32 *uid_validity)
{
gint ok;
gchar *resp_str;
GPtrArray *argbuf;
+ gchar *select_cmd;
+ gchar *folder_;
*exists = *recent = *unseen = *uid_validity = 0;
argbuf = g_ptr_array_new();
- if (strchr(folder, ' ') != NULL)
- imap_cmd_gen_send(sock, "SELECT \"%s\"", folder);
+ if (examine)
+ select_cmd = "EXAMINE";
else
- imap_cmd_gen_send(sock, "SELECT %s", folder);
+ select_cmd = "SELECT";
+
+ QUOTE_IF_REQUIRED(folder_, folder);
+ imap_cmd_gen_send(sock, "%s %s", select_cmd, folder_);
if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW;
return ok;
}
-#if 0
-static gint imap_cmd_status(SockInfo *sock, const gchar *folder,
- const gchar *status, gint *value)
+static gint imap_cmd_select(SockInfo *sock, const gchar *folder,
+ gint *exists, gint *recent, gint *unseen,
+ guint32 *uid_validity)
{
- gint ok;
- GPtrArray *argbuf;
- gchar *str;
-
- if (value) *value = 0;
- argbuf = g_ptr_array_new();
-
- if (strchr(folder, ' ') != NULL)
- imap_cmd_gen_send(sock, "STATUS \"%s\" (%s)", folder, status);
- else
- imap_cmd_gen_send(sock, "STATUS %s (%s)", folder, status);
-
- if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW;
-
- str = search_array_contain_str(argbuf, "STATUS");
- if (!str) THROW;
- str = strchr(str, '(');
- if (!str) THROW;
- str++;
- if (strncmp(str, status, strlen(status)) != 0) THROW;
- str += strlen(status);
- if (*str != ' ') THROW;
- str++;
- if (value) *value = atoi(str);
-
-catch:
- ptr_array_free_strings(argbuf);
- g_ptr_array_free(argbuf, TRUE);
+ return imap_cmd_do_select(sock, folder, FALSE,
+ exists, recent, unseen, uid_validity);
+}
- return ok;
+static gint imap_cmd_examine(SockInfo *sock, const gchar *folder,
+ gint *exists, gint *recent, gint *unseen,
+ guint32 *uid_validity)
+{
+ return imap_cmd_do_select(sock, folder, TRUE,
+ exists, recent, unseen, uid_validity);
}
-#endif
#undef THROW
static gint imap_cmd_create(SockInfo *sock, const gchar *folder)
{
- if (strchr(folder, ' ') != NULL)
- imap_cmd_gen_send(sock, "CREATE \"%s\"", folder);
- else
- imap_cmd_gen_send(sock, "CREATE %s", folder);
+ gchar *folder_;
+
+ QUOTE_IF_REQUIRED(folder_, folder);
+ imap_cmd_gen_send(sock, "CREATE %s", folder_);
return imap_cmd_ok(sock, NULL);
}
static gint imap_cmd_delete(SockInfo *sock, const gchar *folder)
{
- if (strchr(folder, ' ') != NULL)
- imap_cmd_gen_send(sock, "DELETE \"%s\"", folder);
- else
- imap_cmd_gen_send(sock, "DELETE %s", folder);
+ gchar *folder_;
+
+ QUOTE_IF_REQUIRED(folder_, folder);
+ imap_cmd_gen_send(sock, "DELETE %s", folder_);
return imap_cmd_ok(sock, NULL);
}
{
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;
}
static gint imap_cmd_copy(SockInfo *sock, guint32 uid, const gchar *destfolder)
{
gint ok;
+ gchar *destfolder_;
g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
- if (strchr(destfolder, ' ') != NULL)
- imap_cmd_gen_send(sock, "UID COPY %d \"%s\"", uid, destfolder);
- else
- imap_cmd_gen_send(sock, "UID COPY %d %s", uid, destfolder);
+ QUOTE_IF_REQUIRED(destfolder_, destfolder);
+ imap_cmd_gen_send(sock, "UID COPY %d %s", uid, destfolder_);
ok = imap_cmd_ok(sock, NULL);
if (ok != IMAP_SUCCESS) {
- log_warning(_("can't copy %d to %s\n"), uid, destfolder);
+ log_warning(_("can't copy %d to %s\n"), uid, destfolder_);
return -1;
}
return tmp + 1;
}
+static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
+{
+ const gchar *p = src;
+ gint n = 0;
+
+ g_return_val_if_fail(*p == ch, NULL);
+
+ *dest = '\0';
+ p++;
+
+ while (*p != '\0' && *p != ch) {
+ if (n < len - 1) {
+ if (*p == '\\' && *(p + 1) != '\0')
+ p++;
+ *dest++ = *p++;
+ } else
+ p++;
+ n++;
+ }
+
+ *dest = '\0';
+ return (gchar *)(*p == ch ? p + 1 : p);
+}
+
static gchar *search_array_contain_str(GPtrArray *array, gchar *str)
{
gint i;