diff options
author | Jeffrey Stedfast <fejj@helixcode.com> | 2000-05-26 01:42:53 +0800 |
---|---|---|
committer | Jeffrey Stedfast <fejj@src.gnome.org> | 2000-05-26 01:42:53 +0800 |
commit | a979df3778682adb6f90ef3477e62fc077a8e9b8 (patch) | |
tree | 91ac1af4e87cc5f0a728d93d1b5ca920056ef75e /camel/providers/imap | |
parent | da3c570d5e91ceef20d9846946a5bebec5b5fa50 (diff) | |
download | gsoc2013-evolution-a979df3778682adb6f90ef3477e62fc077a8e9b8.tar gsoc2013-evolution-a979df3778682adb6f90ef3477e62fc077a8e9b8.tar.gz gsoc2013-evolution-a979df3778682adb6f90ef3477e62fc077a8e9b8.tar.bz2 gsoc2013-evolution-a979df3778682adb6f90ef3477e62fc077a8e9b8.tar.lz gsoc2013-evolution-a979df3778682adb6f90ef3477e62fc077a8e9b8.tar.xz gsoc2013-evolution-a979df3778682adb6f90ef3477e62fc077a8e9b8.tar.zst gsoc2013-evolution-a979df3778682adb6f90ef3477e62fc077a8e9b8.zip |
Took out code that had been there to reconnect to the server if it was not
2000-05-25 Jeffrey Stedfast <fejj@helixcode.com>
* providers/smtp/camel-smtp-transport.c (_send_to): Took out code
that had been there to reconnect to the server if it was not
already connected - Mailer code was fixed so that this should not
be needed.
* providers/imap/camel-imap-store.[c,h]: Initial code.
svn path=/trunk/; revision=3202
Diffstat (limited to 'camel/providers/imap')
-rw-r--r-- | camel/providers/imap/camel-imap-store.c | 482 | ||||
-rw-r--r-- | camel/providers/imap/camel-imap-store.h | 17 | ||||
-rw-r--r-- | camel/providers/imap/imap.c | 835 | ||||
-rw-r--r-- | camel/providers/imap/imap.h | 86 |
4 files changed, 1419 insertions, 1 deletions
diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c new file mode 100644 index 0000000000..22f81c189d --- /dev/null +++ b/camel/providers/imap/camel-imap-store.c @@ -0,0 +1,482 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-imap-store.c : class for an imap store */ + +/* + * Authors: Jeffrey Stedfast <fejj@helixcode.com> + * Ross Golder <ross@golder.org> + * + * Copyright (C) 2000 Helix Code, Inc. + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + + +#include <config.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + + +#include "camel-imap-store.h" +#include "camel-imap-folder.h" +#include "camel-exception.h" +#include "camel-session.h" +#include "camel-stream.h" +#include "camel-stream-buffer.h" +#include "camel-stream-fs.h" +#include "camel-url.h" + +/* Specified in RFC 2060 */ +#define IMAP_PORT 143 + +static CamelServiceClass *service_class = NULL; + +static void finalize (GtkObject *object); + +static gboolean imap_connect (CamelService *service, CamelException *ex); +static gboolean imap_disconnect (CamelService *service, CamelException *ex); +static GList *query_auth_types (CamelService *service, CamelException *ex); +static void free_auth_types (CamelService *service, GList *authtypes); + +static CamelFolder *get_folder (CamelStore *store, const char *folder_name, + CamelException *ex); +static char *get_folder_name (CamelStore *store, const char *folder_name, + CamelException *ex); + +static void +camel_imap_store_class_init (CamelImapStoreClass *camel_imap_store_class) +{ + /* virtual method overload */ + GtkObjectClass *object_class = + GTK_OBJECT_CLASS (camel_imap_store_class); + CamelServiceClass *camel_service_class = + CAMEL_SERVICE_CLASS (camel_imap_store_class); + CamelStoreClass *camel_store_class = + CAMEL_STORE_CLASS (camel_imap_store_class); + + service_class = gtk_type_class (camel_service_get_type ()); + + /* virtual method overload */ + object_class->finalize = finalize; + + camel_service_class->connect = imap_connect; + camel_service_class->disconnect = imap_disconnect; + camel_service_class->query_auth_types = query_auth_types; + camel_service_class->free_auth_types = free_auth_types; + + camel_store_class->get_folder = get_folder; + camel_store_class->get_folder_name = get_folder_name; +} + + +static void +camel_imap_store_init (gpointer object, gpointer klass) +{ + CamelService *service = CAMEL_SERVICE (object); + CamelStore *store = CAMEL_STORE (object); + + service->url_flags = ( CAMEL_SERVICE_URL_NEED_USER | + CAMEL_SERVICE_URL_NEED_HOST ); + + store->folders = g_hash_table_new (g_str_hash, g_str_equal); +} + + +GtkType +camel_imap_store_get_type (void) +{ + static GtkType camel_imap_store_type = 0; + + if (!camel_imap_store_type) { + GtkTypeInfo camel_imap_store_info = + { + "CamelImapStore", + sizeof (CamelImapStore), + sizeof (CamelImapStoreClass), + (GtkClassInitFunc) camel_imap_store_class_init, + (GtkObjectInitFunc) camel_imap_store_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + camel_imap_store_type = gtk_type_unique (CAMEL_STORE_TYPE, &camel_imap_store_info); + } + + return camel_imap_store_type; +} + +static void +finalize (GtkObject *object) +{ + CamelException ex; + + camel_exception_init (&ex); + imap_disconnect (CAMEL_SERVICE (object), &ex); + camel_exception_clear (&ex); +} + +static CamelServiceAuthType password_authtype = { + "Password", + + "This option will connect to the IMAP server using a " + "plaintext password.", + + "", + TRUE +}; + +static gboolean +try_connect (CamelService *service, CamelException *ex) +{ + struct hostent *h; + struct sockaddr_in sin; + gint fd; + + h = camel_service_gethost (service, ex); + if (!h) + return FALSE; + + sin.sin_family = h->h_addrtype; + sin.sin_port = htons (service->url->port ? service->url->port : IMAP_PORT); + memcpy (&sin.sin_addr, h->h_addr, sizeof (sin.sin_addr)); + + fd = socket (h->h_addrtype, SOCK_STREAM, 0); + if (fd == -1 || connect (fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + "Could not connect to %s (port %s): %s", + service->url->host, service->url->port, + strerror(errno)); + if (fd > -1) + close (fd); + + return FALSE; + } + + close (fd); + return TRUE; +} + +static GList * +query_auth_types (CamelService *service, CamelException *ex) +{ + GList *ret = NULL; + gboolean passwd = TRUE; + + + if (service->url) { + passwd = try_connect (service, ex); + if (camel_exception_get_id (ex) != CAMEL_EXCEPTION_NONE) + return NULL; + } + + if (passwd) + ret = g_list_append (ret, &password_authtype); + + if (!ret) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + "Could not connect to IMAP server on " + "%s.", service->url->host); + } + + return ret; +} + +static void +free_auth_types (CamelService *service, GList *authtypes) +{ + g_list_free (authtypes); +} + +static gboolean +imap_connect (CamelService *service, CamelException *ex) +{ + struct hostent *h; + struct sockaddr_in sin; + gint fd, status; + gchar *buf, *msg; + CamelImapStore *store = CAMEL_IMAP_STORE (service); + + + h = camel_service_gethost (service, ex); + if (!h) + return FALSE; + + if (!service->url->authmech && !service->url->passwd) { + gchar *prompt = g_strdup_printf ("Please enter the IMAP password for %s@%s", + service->url->user, h->h_name); + service->url->passwd = + camel_session_query_authenticator (camel_service_get_session (service), + prompt, TRUE, + service, "password", + ex); + g_free (prompt); + if (!service->url->passwd) + return FALSE; + } + + sin.sin_family = h->h_addrtype; + if (service->url->port) + sin.sin_port = htons(service->url->port); + else + sin.sin_port = htons(IMAP_PORT); + + memcpy (&sin.sin_addr, h->h_addr, sizeof (sin.sin_addr)); + + fd = socket (h->h_addrtype, SOCK_STREAM, 0); + if (fd == -1 || connect (fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + "Could not connect to %s (port %s): %s", + service->url->host, service->url->port, + strerror(errno)); + if (fd > -1) + close (fd); + + return FALSE; + } + + + store->ostream = camel_stream_fs_new_with_fd (fd); + store->istream = camel_stream_buffer_new (store->ostream, + CAMEL_STREAM_BUFFER_READ); + store->command = 0; + + /* Read the greeting, if any. */ + buf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (store->istream)); + if (!buf) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + "Could not read greeting from IMAP " + "server: %s", + camel_exception_get_description (ex)); + gtk_object_unref (GTK_OBJECT (store->ostream)); + gtk_object_unref (GTK_OBJECT (store->istream)); + return FALSE; + } + g_free (buf); + + status = camel_imap_command (store, &msg, "LOGIN \"%s\" \"%s\"", + service->url->user, + service->url->passwd); + + if (status != CAMEL_IMAP_OK) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + "Unable to authenticate to IMAP " + "server. Error sending password:" + " %s", msg ? msg : "(Unknown)"); + g_free (msg); + gtk_object_unref (GTK_OBJECT (store->ostream)); + gtk_object_unref (GTK_OBJECT (store->istream)); + return FALSE; + } + + service_class->connect (service, ex); + return TRUE; +} + +static gboolean +imap_disconnect (CamelService *service, CamelException *ex) +{ + CamelImapStore *store = CAMEL_IMAP_STORE (service); + + if (!service->connected) + return TRUE; + + if (!service_class->disconnect (service, ex)) + return FALSE; + + gtk_object_unref (GTK_OBJECT (store->ostream)); + gtk_object_unref (GTK_OBJECT (store->istream)); + store->ostream = NULL; + store->istream = NULL; + + return TRUE; +} + +const gchar * +camel_imap_store_get_toplevel_dir (CamelImapStore *store) +{ + CamelURL *url = CAMEL_SERVICE (store)->url; + + g_assert(url != NULL); + return url->path; +} + + +static CamelFolder +*get_folder (CamelStore *store, const char *folder_name, CamelException *ex) +{ + CamelImapFolder *new_imap_folder; + CamelFolder *new_folder; + + new_imap_folder = gtk_type_new (CAMEL_IMAP_FOLDER_TYPE); + new_folder = CAMEL_FOLDER (new_imap_folder); + + /* XXX We shouldn't be passing NULL here, but it's equivalent to + * what was there before, and there's no + * CamelImapFolder::get_subfolder yet anyway... + */ + CF_CLASS (new_folder)->init (new_folder, store, NULL, + folder_name, '/', ex); + + return new_folder; +} + +static gchar +*get_folder_name (CamelStore *store, const char *folder_name, + CamelException *ex) +{ + return g_strdup (folder_name); +} + +/** + * camel_imap_command: Send a command to a IMAP server. + * @store: the IMAP store + * @ret: a pointer to return the full server response in + * @fmt: a printf-style format string, followed by arguments + * + * This command sends the command specified by @fmt and the following + * arguments to the connected IMAP store specified by @store. It then + * reads the server's response and parses out the status code. If + * the caller passed a non-NULL pointer for @ret, camel_imap_command + * will set it to point to an buffer containing the rest of the + * response from the IMAP server. (If @ret was passed but there was + * no extended response, @ret will be set to NULL.) The caller must + * free this buffer when it is done with it. + * + * Return value: one of CAMEL_IMAP_OK (command executed successfully), + * CAMEL_IMAP_ERR (command encounted an error), or CAMEL_IMAP_FAIL + * (a protocol-level error occurred, and Camel is uncertain of the + * result of the command.) + **/ +int +camel_imap_command (CamelImapStore *store, char **ret, char *fmt, ...) +{ + gchar *cmdbuf, *respbuf; + gchar *cmdid; + va_list ap; + gint status; + + /* create the command */ + cmdid = g_strdup_printf("A%.5d", store->command++); + va_start (ap, fmt); + cmdbuf = g_strdup_vprintf (fmt, ap); + va_end (ap); + + fprintf(stderr, "sending : %s %s\r\n", cmdid, cmdbuf); + + if (camel_stream_printf (store->ostream, "%s %s\r\n", cmdid, cmdbuf) == -1) { + g_free(cmdbuf); + g_free(cmdid); + if (*ret) + *ret = g_strdup(strerror(errno)); + return CAMEL_IMAP_FAIL; + } + g_free(cmdbuf); + g_free(cmdid); + + /* Read the response */ + respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (store->istream)); + if (respbuf == NULL) { + if (*ret) + *ret = g_strdup(strerror(errno)); + return CAMEL_IMAP_FAIL; + } + + fprintf(stderr, "received: %s\n", respbuf); + + if (!strncmp (respbuf + 11, "OK", 2)) + status = CAMEL_IMAP_OK; + else if (!strncmp (respbuf + 11, "BAD", 3)) + status = CAMEL_IMAP_ERR; + else + status = CAMEL_IMAP_FAIL; + + if (ret) { + if (status != CAMEL_IMAP_FAIL) { + *ret = strchr (respbuf, ' '); + if (*ret) + *ret = g_strdup (*ret + 1); + } else + *ret = NULL; + } + g_free (respbuf); + + return status; +} + +/** + * camel_imap_command_get_additional_data: get "additional data" from + * an IMAP command. + * @store: the IMAP store + * + * This command gets the additional data returned by "multi-line" IMAP + * commands, such as LIST, RETR, TOP, and UIDL. This command _must_ + * be called after a successful (CAMEL_IMAP_OK) call to + * camel_imap_command for a command that has a multi-line response. + * The returned data is un-byte-stuffed, and has lines termined by + * newlines rather than CR/LF pairs. + * + * Return value: the data, which the caller must free. + **/ +char * +camel_imap_command_get_additional_data (CamelImapStore *store, + CamelException *ex) +{ + CamelStreamBuffer *stream = CAMEL_STREAM_BUFFER (store->istream); + GPtrArray *data; + char *buf; + int i, status = CAMEL_IMAP_OK; + + data = g_ptr_array_new (); + while (1) { + buf = camel_stream_buffer_read_line (stream); + if (!buf) { + status = CAMEL_IMAP_FAIL; + break; + } + + if (!strcmp (buf, ".")) + break; + if (*buf == '.') + memmove (buf, buf + 1, strlen (buf)); + g_ptr_array_add (data, buf); + } + + if (status == CAMEL_IMAP_OK) { + /* Append an empty string to the end of the array + * so when we g_strjoinv it, we get a "\n" after + * the last real line. + */ + g_ptr_array_add (data, ""); + g_ptr_array_add (data, NULL); + buf = g_strjoinv ("\n", (char **)data->pdata); + } else + buf = NULL; + + for (i = 0; i < data->len - 2; i++) + g_free (data->pdata[i]); + g_ptr_array_free (data, TRUE); + + return buf; +} + diff --git a/camel/providers/imap/camel-imap-store.h b/camel/providers/imap/camel-imap-store.h index bf1ffcd26a..970461582b 100644 --- a/camel/providers/imap/camel-imap-store.h +++ b/camel/providers/imap/camel-imap-store.h @@ -3,6 +3,7 @@ /* * Authors: Jeffrey Stedfast <fejj@helixcode.com> + * Ross Golder <ross@golder.org> * * Copyright (C) 2000 Helix Code, Inc. * @@ -43,7 +44,10 @@ extern "C" { typedef struct { CamelStore parent_object; - + + CamelStream *istream, *ostream; + guint32 command; + } CamelImapStore; @@ -55,6 +59,17 @@ typedef struct { /* public methods */ +void camel_imap_store_open (CamelImapStore *store, CamelException *ex); +void camel_imap_store_close (CamelImapStore *store, gboolean expunge, + CamelException *ex); + +/* support functions */ + +enum { CAMEL_IMAP_OK, CAMEL_IMAP_ERR, CAMEL_IMAP_FAIL }; + +gint camel_imap_command (CamelImapStore *store, char **ret, char *fmt, ...); +gchar *camel_imap_command_get_additional_data (CamelImapStore *store, + CamelException *ex); /* Standard Gtk function */ GtkType camel_imap_store_get_type (void); diff --git a/camel/providers/imap/imap.c b/camel/providers/imap/imap.c new file mode 100644 index 0000000000..3a4109ed47 --- /dev/null +++ b/camel/providers/imap/imap.c @@ -0,0 +1,835 @@ +/* Spruce + * Copyright (C) 1999-2000 Jeffrey Stedfast + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "imap.h" + +#define IMAP_LOGGING + +/* this is used in the tag before each command */ +static guint32 imap_commands = 0; + +extern gint timeout; +extern GList *mime_parts; + + +gint imap_ok (gint tag, gchar *line) +{ + /* returns 1 if <tag> OK was found */ + gchar find[64]; + gint ret; + + g_snprintf(find, sizeof(find)-1, "A%.5d OK", tag); + + ret = find_string (line, find); + + if (ret < 0) + return 0; + + return 1; +} + +gint imap_login_cram_md5 (gint socket, gchar *username, gchar *password) +{ + /* Log in to server using CRAM-MD5 keyed hash. */ + gchar buffer[512]; + gchar *retstr; + gint pos; + + if (username == NULL || password == NULL) + return ERROR; + + memset(buffer, 0, sizeof(buffer)); + if (recvline(socket, buffer, sizeof(buffer)-1) < 0) + return ERROR; /* Fetch the OK line from the server */ + + if (find_string(buffer, "OK") == -1) + return ERROR; + + g_snprintf(buffer, sizeof(buffer)-1, "A%.5d AUTHENTICATE CRAM-MD5\r\n", imap_commands); + + if (send(socket, buffer, strlen(buffer), 0) < 0) + return ERROR; + + memset(buffer, 0, sizeof(buffer)); + if (recvline(socket, buffer, sizeof(buffer)-1) < 0) + return ERROR; + + pos = find_string(buffer, "\r\n"); + if (pos != -1) + buffer[pos] = '\0'; + retstr = cram_md5(username, password, buffer); + + if (retstr[strlen(retstr)-1] == '\n') + retstr[strlen(retstr)-1] = '\0'; + + g_snprintf(buffer, sizeof(buffer)-1, "%s\r\n", retstr); + g_free(retstr); + + if (send (socket, buffer, strlen(buffer), 0) < 0) + return ERROR; + + if (recvline(socket, buffer, sizeof(buffer)-1) < 0) + return ERROR; + + if (!imap_ok(imap_commands, buffer)) + return ERROR; + + imap_commands++; + + return SUCCESS; +} + +gint imap_login (gint socket, gchar *username, gchar *password) +{ + /* this logs us in to the server */ + gchar buffer[512]; + gchar temp[64]; + + if (username == NULL || password == NULL) + return ERROR; + + g_snprintf(buffer, sizeof(buffer)-1, "A%.5d LOGIN \"%s\" \"%s\"\r\n", imap_commands, username, password); +#ifdef IMAP_LOGGING + fprintf(stderr, "%s", buffer); +#endif + + if (send (socket, buffer, strlen(buffer), 0) < 0) + { + return ERROR; + } + + g_snprintf(temp, sizeof(temp)-1, "A%.5d", imap_commands); + + memset(buffer, 0, sizeof(buffer)); + recvline_timeo(socket, buffer, sizeof(buffer)-1, timeout); + while (!strstr(buffer, temp)) + { + memset(buffer, 0, sizeof(buffer)); + recvline_timeo(socket, buffer, sizeof(buffer)-1, timeout); + } + + if (!imap_ok(imap_commands, buffer)) + return ERROR; + + imap_commands++; + + return SUCCESS; +} + +GList *imap_list (gint socket, gchar *namespace) +{ + /* this gets the names of all the mailboxes */ + gchar buffer[512]; + gchar flags[256]; + gchar temp[64], *ptr = NULL, *flagptr = NULL; + gchar slashdot = '\0'; + GList *list = NULL; + gint ret, size = 0, flaglen = 0; + + if (namespace && *namespace) + { + if (*namespace && namespace[strlen(namespace)-1] != '/' && namespace[strlen(namespace)-1] != '.') + slashdot = '/'; + g_snprintf(buffer, sizeof(buffer)-1, "A%.5d LIST \"\" %s%c*\r\n", imap_commands, namespace, slashdot); + } + else + g_snprintf(buffer, sizeof(buffer)-1, "A%.5d LIST \"\" INBOX.*\r\n", imap_commands); +#ifdef IMAP_LOGGING + fprintf(stderr, "%s", buffer); +#endif + + if (send(socket, buffer, strlen(buffer), 0) < 0) + { + return NULL; + } + + do + { + memset(buffer, 0, sizeof(buffer)); + ret = recvline(socket, buffer, sizeof(buffer)-1); + if (ret > 0) + { +#ifdef IMAP_LOGGING + fprintf(stderr, "received: %s", buffer); +#endif + if (buffer[0] == '*') + { + strip(buffer, '\r'); + strip(buffer, '\n'); + + /* skip ahead to the flag section */ + ptr = strstr(buffer, "("); + + /* find the end of the flags section */ + flagptr = ptr + 1; + ptr = strstr(ptr, ")") + 1; + + /* eventually we will need to parse this */ + memset(flags, 0, sizeof(flags)); + flaglen = (gint)(ptr - flagptr) - 1; + size = sizeof(flags); + strncpy(flags, flagptr, flaglen > size ? size : flaglen); + if (!strstrcase(flags, "\\NoSelect")) /* is this a selectable mailbox? */ + { + /* skip the reference name */ + ptr += imap_get_string (ptr, temp, sizeof(temp)-1, ""); + + /* the rest of the return string is fair play... */ + g_strstrip(ptr); /* trim off any extra white space */ + unquote(ptr); /* unquote the mailbox name if it is quoted */ + if (slashdot) + strcut(ptr, 0, strlen(namespace)+1); /* cut out the namespace and the '/' */ + else + strcut(ptr, 0, strlen(namespace)); /* cut out the namespace */ + + + list = g_list_append (list, g_strdup(ptr)); + } + } + else + break; + } + } while (ret > 0); + + imap_commands++; + + return list; +} + +gint imap_select_mailbox (gint socket, gchar *mailbox, gchar *namespace) +{ + /* selects a mailbox, returns the number of messages in that mailbox + * or -1 on error */ + gchar *cmdbuf, buffer[512], temp[64], *index, mesgs[16]; + gchar slashdot = '\0'; + gint ret, i; + + if (mailbox == NULL) + return ERROR; + + if (namespace && strcmp(mailbox, "INBOX")) + { + if (*namespace && namespace[strlen(namespace)-1] != '/' && namespace[strlen(namespace)-1] != '.') + slashdot = '/'; + + cmdbuf = g_strdup_printf("A%.5d SELECT %s%c%s\r\n", imap_commands, namespace, slashdot, mailbox); + } + else + cmdbuf = g_strdup_printf("A%.5d SELECT %s\r\n", imap_commands, mailbox); +#ifdef IMAP_LOGGING + fprintf(stderr, "%s", cmdbuf); +#endif + + if (send(socket, cmdbuf, strlen(cmdbuf), 0) < 0) + { + g_free(cmdbuf); + return -1; + } + g_free(cmdbuf); + + g_snprintf(temp, sizeof(temp)-1, "A%.5d", imap_commands); + + memset(buffer, 0, sizeof(buffer)); + ret = recvline(socket, buffer, sizeof(buffer)-1); + while (ret > 0) + { +#ifdef IMAP_LOGGING + fprintf(stderr, "received: %s", buffer); +#endif + if (strstr(buffer, temp)) + break; + if (buffer[0] == '*') + { + if (strstr(buffer, "EXISTS")) + { + index = buffer; + while (*index != ' ') + index++; + index++; + + i = 0; + memset(mesgs, 0, sizeof(mesgs)); + while (*index != ' ' && i < sizeof(mesgs)-1) + { + mesgs[i] = *index; + index++; + i++; + } + } + } + memset(buffer, 0, sizeof(buffer)); + ret = recvline(socket, buffer, sizeof(buffer)-1); + } + + if (!imap_ok(imap_commands, buffer)) + return -1; + + imap_commands++; + + return atoi(mesgs); +} + +gint imap_logout (gint socket) +{ + /* logs out */ + gchar buffer[256]; + + g_snprintf(buffer, sizeof(buffer)-1, "A%.5d LOGOUT\r\n", imap_commands); +#ifdef IMAP_LOGGING + fprintf(stderr, "%s", buffer); +#endif + + if (send(socket, buffer, strlen(buffer), 0) < 0) + { + return ERROR; + } + + return SUCCESS; +} + +gint imap_mailbox_create (gint socket, gchar *mailbox) +{ + /* creates a new mailbox */ + gchar buffer[256]; + + if (mailbox == NULL) + return ERROR; + + g_snprintf(buffer, sizeof(buffer)-1, "A%.5d CREATE %s\r\n", imap_commands, mailbox); +#ifdef IMAP_LOGGING + fprintf(stderr, "%s", buffer); +#endif + + if (send(socket, buffer, strlen(buffer), 0) < 0) + { + return ERROR; + } + + memset(buffer, 0, sizeof(buffer)); + if (recvline(socket, buffer, sizeof(buffer)-1) < 0 || !imap_ok(imap_commands, buffer)) + { + return ERROR; + } + + imap_commands++; + + return SUCCESS; +} + +gint imap_mailbox_delete (gint socket, gchar *mailbox) +{ + /* deletes a mailbox */ + gchar buffer[256]; + + if (mailbox == NULL) + return ERROR; + + g_snprintf(buffer, sizeof(buffer)-1, "A%.5d DELETE %s\r\n", imap_commands, mailbox); +#ifdef IMAP_LOGGING + fprintf(stderr, "%s", buffer); +#endif + + if (send(socket, buffer, strlen(buffer), 0) < 0) + { + return ERROR; + } + + memset(buffer, 0, sizeof(buffer)); + if (recvline(socket, buffer, sizeof(buffer)-1) < 0 || + !imap_ok(imap_commands, buffer)) + { + return ERROR; + } + + imap_commands++; + + return SUCCESS; +} + +/* fetches the specified part of a message, which can be alot of + * if you use peek the \Seen flag is not set */ +gchar *imap_fetch (gint socket, gint mesgnum, gchar *part, gint *seen) +{ + /* fetches the specified part of the mesg. */ + gchar *mesg = NULL; + gchar buffer[512], *index; + gchar flags[128], size[16], temp[64]; + gint i, n, msgsize = 1000; + + if (mesgnum < 0) + return (gchar *)NULL; + + g_snprintf(buffer, sizeof(buffer)-1, "A%.5d FETCH %d (FLAGS %s)\r\n", imap_commands, mesgnum, part); +#ifdef IMAP_LOGGING + fprintf(stderr, "%s", buffer); +#endif + + if (send(socket, buffer, strlen(buffer), 0) < 0) + { + return (gchar *)NULL; + } + + + memset(buffer, 0, sizeof(buffer)); + n = recvline(socket, buffer, sizeof(buffer)-1); + + if (buffer[0] != '*' && imap_ok(imap_commands, buffer)) + { + memset(buffer, 0, sizeof(buffer)); + n = recvline(socket, buffer, sizeof(buffer)-1); + } + + if (buffer[0] == '*') + /*if (imap_ok(imap_commands, buffer))*/ + { + index = strstrcase(buffer, "FLAGS"); + if (index == NULL) /* hmm */ + { + fprintf(stderr, _("IMAP server replied using unknown tokens.\n")); + return (gchar *)NULL; + } + else + { +#ifdef IMAP_LOGGING + fprintf(stderr, "received: %s", buffer); +#endif + /* skip to the FLAGS token */ + for ( ; *index && *index != '('; index++); + index++; + + i = 0; + memset(flags, 0, sizeof(flags)); + while (*index != ')' && i < sizeof(flags)-1) + { + flags[i] = *index; + index++; + i++; + } + flags[i] = '\0'; + + /* skip to the next significant token */ + for (index++; *index && *index != '{'; index++); + index++; + + i = 0; + memset(size, 0, sizeof(size)); + while (*index != '}' && i < sizeof(size)-1) + { + size[i] = *index; + index++; + i++; + } + size[i] = '\0'; + msgsize = atoi(size); + } + } + else + { + g_snprintf(temp, sizeof(temp)-1, "A%.5d", imap_commands); + if (strstr(buffer, temp)) /* this means there's no such message */ + { + fprintf(stderr, _("IMAP responded with \"no such message\".\n")); + return (gchar *)NULL; + } + } + + + mesg = g_malloc0(msgsize + 50); /* just to be safe */ + n = recvline(socket, buffer, sizeof(buffer)-1); + + while (!(n <= 0) && !imap_ok(imap_commands, buffer)) + { + strip(buffer, '\r'); /* strip all the \r's */ + strcat(mesg, buffer); + memset(buffer, 0, sizeof(buffer)); + n = recvline(socket, buffer, sizeof(buffer)-1); + } + + if (mesg) + mesg[strlen(mesg)-3] = '\0'; /* strip the ending ) */ + + if (seen != NULL) + { + if (strstrcase(flags, "\\Seen")) + *seen = 1; + else + *seen = 0; + } + + imap_commands++; + + return (gchar*)mesg; +} + +gboolean imap_delete(const ImapAccount_t *imap, GList *sorted) +{ + GList *p = sorted; + gchar buffer[256]; + gchar temp[16]; + gint ret; + + do + { + gint id = GPOINTER_TO_INT(p->data); + g_snprintf(buffer, sizeof(buffer)-1, "A%.5d STORE %d +FLAGS (\\Deleted)\r\n", imap_commands, id); +#ifdef IMAP_LOGGING + fprintf(stderr, "%s", buffer); +#endif + if (send(imap->socket, buffer, strlen(buffer), 0) < 0) + { + return FALSE; + } + g_snprintf(temp, sizeof(temp)-1, "A%.5d", imap_commands); + + memset(buffer, 0, sizeof(buffer)); + ret = recvline(imap->socket, buffer, sizeof(buffer)-1); + while (ret > 0) + { + if (find_string(buffer, temp) >= 0) + break; + + memset(buffer, 0, sizeof(buffer)); + ret = recvline(imap->socket, buffer, sizeof(buffer)-1); + } + + if (!imap_ok(imap_commands, buffer)) + { + return FALSE; + } + imap_commands++; + } while ((p = g_list_next(p))); + + g_snprintf(buffer, 255, "A%.5d EXPUNGE\r\n", imap_commands); +#ifdef IMAP_LOGGING + fprintf(stderr, "%s", buffer); +#endif + if (send(imap->socket, buffer, strlen(buffer), 0) < 0) + { + return FALSE; + } + + g_snprintf (temp, 15, "A%.5d", imap_commands); + + memset(buffer, 0, sizeof(buffer)); + ret = recvline(imap->socket, buffer, sizeof(buffer)-1); + while (ret > 0) + { + if (find_string(buffer, temp) >= 0) + break; + + memset(buffer, 0, sizeof(buffer)); + ret = recvline(imap->socket, buffer, sizeof(buffer)-1); + } + + if (!imap_ok(imap_commands, buffer)) + { + return FALSE; + } + + imap_commands++; + + return TRUE; +} + +gint imap_connect (Server *server) +{ + /* connects to the server and returns the socket or -1 on error */ + gchar buffer[512]; + gint sock; + + if (!Resolve(server)) + return -1; + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock < 0) + return -1; + + server->sin.sin_family = AF_INET; + server->sin.sin_port = htons(server->port); + +#ifdef IMAP_LOGGING + fprintf(stderr, _("Connecting to IMAP server (%s)..."), server->ip); +#endif + if (connect_timeo(sock, (struct sockaddr*)&server->sin, sizeof(server->sin), timeout) < 0) + { + fprintf(stderr, _("failed.\n")); + close(sock); + return -1; + } + fprintf(stderr, _("success.\n")); + + { + /* read the connect responce */ + memset(buffer, 0, sizeof(buffer)); + recvline_timeo(sock, buffer, sizeof(buffer)-1, timeout); + } + + return sock; +} + +gint imap_add_part(gchar *c) +{ + gchar name[64], value[64]; + gchar temp[64]; + gchar *start = c; + struct mime_part *part; + + part = g_malloc0(sizeof(struct mime_part)); + + c += imap_get_string (c, part->type, sizeof(part->type)-1, "text"); + c += imap_get_string (c, part->subtype, sizeof(part->subtype)-1, "plain"); + + /* seek to the beginning of the parameter... */ + for ( ; *c && *c == ' '; c++); + + if (*c) + { + gchar *p = part->parameter; + if (*c == '(') + { + c++; + while (*c && *c != ')') + { + c += imap_get_string (c, name, sizeof(name)-1, ""); + c += imap_get_string (c, value, sizeof(value)-1, ""); + /* don't buffer overrun */ + g_snprintf(p, sizeof(part->parameter)-1, "%s=\"%s\"; ", name, value); + p += strlen(p); + + while (*c && *c == ' ') /* skip any spaces */ + c++; + } + } + else + { + c += imap_get_string (c, name, sizeof(name)-1, ""); + strcpy(value, name); + *p++ = '\0'; + } + + c++; /* skip over the ')' belonging to the parameter values */ + if (*c) + { + /* ignore id and description */ + c += imap_get_string (c, temp, sizeof(temp)-1, ""); + c += imap_get_string (c, temp, sizeof(temp)-1, ""); + + /* encoding */ + c += imap_get_string (c, part->encoding, sizeof(part->encoding)-1, ""); + + /* size */ + c += imap_get_number (c, &part->len); + + /* skip the optional info */ + c += imap_skip_section(c); + + part->pos = 0; /* isn't useful in imap */ +#ifdef IMAP_LOGGING + fprintf(stderr, "type = %s/%s\n", part->type, part->subtype); + fprintf(stderr, "encoding = %s\n", part->encoding); + fprintf(stderr, "param = %s\n", part->parameter); +#endif + mime_parts = g_list_append (mime_parts, part); + + return (c - start); + } + } + return -1; +} + +gint imap_parts (gint socket, gint mesg_num) +{ + GList *tmp; + gchar *buffer = NULL, *c; + gint res = 1, cnt; + + tmp = mime_parts; + while (tmp != NULL) + { + g_free(tmp->data); + tmp = tmp->next; + } + + if (mime_parts != NULL) + { + g_list_free(mime_parts); + mime_parts = NULL; + } + + buffer = g_malloc0(sizeof(gchar)*2048); + + g_snprintf(buffer, 2047, "A%.5d FETCH %d (BODYSTRUCTURE)\r\n", imap_commands, mesg_num); +#ifdef IMAP_LOGGING + fprintf(stderr, "%s", buffer); +#endif + + if (send(socket, buffer, strlen(buffer), 0) < 0) + { + g_free(buffer); + return 0; + } + + /* get the structure of the body */ + memset (buffer, 0, sizeof(gchar)*2048); + recvline (socket, buffer, sizeof(gchar)*2048); +#ifdef IMAP_LOGGING + fprintf(stderr, "received: %s", buffer); +#endif + + c = buffer; + /* skip to the BODYSTRUCTURE */ + c = strstr(c, "BODYSTRUCTURE"); + if (c == NULL) + return 0; + + c += strlen("BODYSTRUCTURE"); + if (*c) + { + /* looks good so far, skip to the parts */ + for ( ; *c && *c != '('; c++); + + if (*c && *(c+1) == '(') + { + c++; +#ifdef IMAP_LOGGING + fprintf(stderr, "message is multipart\n"); +#endif + /* multipart */ + while (*c == '(') + { + cnt = imap_skip_section(c); + if (cnt > 1) + { + c[cnt-1] = '\0'; + cnt = imap_add_part(c); + if (cnt == -1) + { + res = 0; + break; + } + c += cnt; + } + else + { + res = 0; + break; + } + /* skip to the next mime part */ + for ( ; *c && *c == ' '; c++); + } + } + else + if (*c) + { + /* one part */ + cnt = imap_add_part(c); + res = res != -1; + } + /* just forget the rest, who cares?? */ + } + + g_free(buffer); + + return res; +} + +gint imap_get_string (gchar *index, gchar *dest, gint destlen, gchar *def) +{ + /* gets a string ("data" or NIL) , if NIL it copies def instead */ + gint i; + gchar *start = index; + + while (*index && *index == ' ') /* skip white space */ + index++; + + if (strncmp(index, "NIL", 3)) + { + /* progress to the first quote (we should already be there but just in case) */ + while (*index && *index != '"') + index++; + + index++; + + i = 0; + while (*index && *index != '"') + { + if (i < destlen-1) + { + dest[i] = *index; + i++; + } + index++; + } + dest[i] = '\0'; + } + else + { + /* if there were no data we just copy def */ + index += 3; + strncpy (dest, def, destlen); + } + + return index - start + 1; +} + +gint imap_get_number (gchar *index, gint *dest) +{ + /* gets a number */ + gchar number[32]; + gchar *start = index; + gint i; + + /* skip white space **/ + while (*index == ' ') + index++; + + i = 0; + while (*index != ' ' && i < sizeof(number)-1) + { + number[i] = *index; + index++; + i++; + } + number[i] = '\0'; + + *dest = atoi(number); + + return index - start; +} + +gint imap_skip_section(gchar *index) +{ + gint depth = 1; + gchar *start = index; + + while (depth != 0 && *index) + { + if (*index == '(') + depth++; + else if ( *index == ')' ) + depth--; + index++; + } + + return index - start; +} + diff --git a/camel/providers/imap/imap.h b/camel/providers/imap/imap.h new file mode 100644 index 0000000000..3884651c42 --- /dev/null +++ b/camel/providers/imap/imap.h @@ -0,0 +1,86 @@ +/* Spruce + * Copyright (C) 1999-2000 Jeffrey Stedfast + * + * 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 + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef __IMAP_H__ +#define __IMAP_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include <gtk/gtk.h> +#undef MIN +#undef MAX +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include "parse.h" +#include "server.h" +#include "mime.h" +#include "cram-md5.h" + +struct imap_account +{ + Server server; + gchar *username; + gchar *password; + gint socket; +}; + +gint imap_ok (gint tag, gchar *line); + +gint imap_login_cram_md5(gint socket, gchar *username, gchar *password); + +gint imap_login (gint socket, gchar *username, gchar *password); + +GList *imap_list (gint socket, gchar *namespace); + +gint imap_select_mailbox (gint socket, gchar *mailbox, gchar *namespace); + +gint imap_logout (gint socket); + +gint imap_mailbox_create (gint socket, gchar *mailbox); + +gint imap_mailbox_delete (gint socket, gchar *mailbox); + +gchar *imap_fetch (gint socket, gint mesgnum, gchar *part, gint *seen); + +gboolean imap_delete (const ImapAccount_t *imap, GList *sorted); + +gint imap_connect(Server *server); + +gint imap_add_part(gchar *c); + +gint imap_parts (gint socket, gint mesg_num); + +gint imap_get_string (gchar *index, gchar *dest, gint destlen, gchar *def); + +gint imap_get_number (gchar *index, gint *dest); + +gint imap_skip_section(gchar *index); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + |