diff options
Diffstat (limited to 'src/tracker-indexer/modules/evolution.c')
-rw-r--r-- | src/tracker-indexer/modules/evolution.c | 1626 |
1 files changed, 1626 insertions, 0 deletions
diff --git a/src/tracker-indexer/modules/evolution.c b/src/tracker-indexer/modules/evolution.c new file mode 100644 index 000000000..997ac92c3 --- /dev/null +++ b/src/tracker-indexer/modules/evolution.c @@ -0,0 +1,1626 @@ +/* Copyright (C) 2006, Mr Jamie McCracken (jamiemcc@gnome.org) + * Copyright (C) 2008, Nokia + + * This library 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 library 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 library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <glib.h> +#include <glib/gstdio.h> +#include <gmime/gmime.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <gconf/gconf-client.h> +#include <tracker-indexer/tracker-module.h> +#include <tracker-indexer/tracker-metadata.h> +#include <tracker-indexer/tracker-metadata-utils.h> +#include <libtracker-common/tracker-utils.h> +#include <libtracker-common/tracker-file-utils.h> +#include <libtracker-common/tracker-type-utils.h> + +#define METADATA_FILE_PATH "File:Path" +#define METADATA_FILE_NAME "File:Name" +#define METADATA_EMAIL_RECIPIENT "Email:Recipient" +#define METADATA_EMAIL_DATE "Email:Date" +#define METADATA_EMAIL_SENDER "Email:Sender" +#define METADATA_EMAIL_SUBJECT "Email:Subject" +#define METADATA_EMAIL_SENT_TO "Email:SentTo" +#define METADATA_EMAIL_CC "Email:CC" + +typedef union EvolutionFileData EvolutionFileData; +typedef struct EvolutionLocalData EvolutionLocalData; +typedef struct EvolutionImapData EvolutionImapData; +typedef struct EvolutionAccountContext EvolutionAccountContext; +typedef enum MailStorageType MailStorageType; + +enum MailStorageType { + MAIL_STORAGE_NONE, + MAIL_STORAGE_LOCAL, + MAIL_STORAGE_IMAP +}; + +struct EvolutionLocalData { + MailStorageType type; + GMimeStream *stream; + GMimeParser *parser; + GMimeMessage *message; + + GList *mime_parts; + GList *current_mime_part; +}; + +struct EvolutionImapData { + MailStorageType type; + gint fd; + FILE *summary; + guint n_messages; + guint cur_message; + gchar *cur_message_uid; + + GList *mime_parts; + GList *current_mime_part; +}; + +union EvolutionFileData { + MailStorageType type; + EvolutionLocalData mbox; + EvolutionImapData imap; +}; + +enum SummaryDataType { + SUMMARY_TYPE_INT32, + SUMMARY_TYPE_UINT32, + SUMMARY_TYPE_STRING, + SUMMARY_TYPE_TOKEN, + SUMMARY_TYPE_TIME_T +}; + +struct EvolutionAccountContext { + gchar *account; + gchar *uid; +}; + +enum EvolutionFlags { + EVOLUTION_MESSAGE_ANSWERED = 1 << 0, + EVOLUTION_MESSAGE_DELETED = 1 << 1, + EVOLUTION_MESSAGE_DRAFT = 1 << 2, + EVOLUTION_MESSAGE_FLAGGED = 1 << 3, + EVOLUTION_MESSAGE_SEEN = 1 << 4, + EVOLUTION_MESSAGE_ATTACHMENTS = 1 << 5, + EVOLUTION_MESSAGE_ANSWERED_ALL = 1 << 6, + EVOLUTION_MESSAGE_JUNK = 1 << 7, + EVOLUTION_MESSAGE_SECURE = 1 << 8 +}; + + +static gchar *local_dir = NULL; +static gchar *imap_dir = NULL; +static GHashTable *accounts = NULL; + + +void get_imap_accounts (void); + + +static gboolean +read_summary (FILE *summary, + ...) +{ + va_list args; + gint value_type; + + if (!summary) { + return FALSE; + } + + va_start (args, summary); + + while ((value_type = va_arg (args, gint)) != -1) { + switch (value_type) { + case SUMMARY_TYPE_TIME_T: { + time_t value, *dest; + + if (fread (&value, sizeof (time_t), 1, summary) != 1) { + return FALSE; + } + + dest = va_arg (args, time_t*); + + if (dest) { + *dest = g_ntohl (value); + } + break; + } + case SUMMARY_TYPE_INT32: { + gint32 value, *dest; + + if (fread (&value, sizeof (gint32), 1, summary) != 1) { + return FALSE; + } + + dest = va_arg (args, gint32*); + + if (dest) { + *dest = g_ntohl (value); + } + break; + } + case SUMMARY_TYPE_UINT32: { + guint32 *dest, value = 0; + gint c; + + while (((c = fgetc (summary)) & 0x80) == 0 && c != EOF) { + value |= c; + value <<= 7; + } + + if (c == EOF) { + return FALSE; + } else { + value |= (c & 0x7f); + } + + dest = va_arg (args, guint32*); + + if (dest) { + *dest = value; + } + break; + } + case SUMMARY_TYPE_STRING: + case SUMMARY_TYPE_TOKEN: { + guint32 len; + gchar *str, **dest; + + /* read string length */ + read_summary (summary, SUMMARY_TYPE_UINT32, &len, -1); + dest = va_arg (args, gchar **); + + if (dest) { + *dest = NULL; + } + + if (value_type == SUMMARY_TYPE_TOKEN) { + if (len < 32) { + continue; + } else { + len -= 31; + } + } + + if (len <= 1) { + continue; + } + + str = g_try_malloc0 (len); + if (!str) { + return FALSE; + } + + if (fread (str, len - 1, 1, summary) != 1) { + g_free (str); + return FALSE; + } + + if (dest) { + *dest = str; + } else { + g_free (str); + } + + break; + } + default: + break; + } + } + + va_end (args); + + return TRUE; +} + +void +tracker_module_init (void) +{ + g_mime_init (0); + get_imap_accounts (); + + local_dir = g_build_filename (g_get_home_dir (), ".evolution", "mail", "local", G_DIR_SEPARATOR_S, NULL); + imap_dir = g_build_filename (g_get_home_dir (), ".evolution", "mail", "imap", G_DIR_SEPARATOR_S, NULL); +} + +void +tracker_module_shutdown (void) +{ + g_mime_shutdown (); + + g_hash_table_destroy (accounts); + g_free (local_dir); + g_free (imap_dir); +} + +G_CONST_RETURN gchar * +tracker_module_get_name (void) +{ + /* Return module name here */ + return "EvolutionEmails"; +} + +static void +account_start_element_handler (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attr_names, + const gchar **attr_values, + gpointer user_data, + GError **error) +{ + EvolutionAccountContext *account_context; + gint i = 0; + + if (strcmp (element_name, "account") != 0) { + return; + } + + account_context = (EvolutionAccountContext *) user_data; + + while (attr_names[i]) { + if (strcmp (attr_names[i], "uid") == 0) { + account_context->uid = g_strdup (attr_values[i]); + return; + } + + i++; + } +} + +static gchar * +get_account_name_from_imap_uri (const gchar *imap_uri) +{ + const gchar *start, *at, *semic; + gchar *user_name, *at_host_name, *account_name; + + /* Assume url schema is: + * imap://foo@imap.free.fr/;etc + * or + * imap://foo;auth=DIGEST-MD5@imap.bar.com/;etc + * + * We try to get "foo@imap.free.fr". + */ + + if (!g_str_has_prefix (imap_uri, "imap://")) { + return NULL; + } + + user_name = at_host_name = account_name = NULL; + + /* check for embedded @ and then look for first colon after that */ + start = imap_uri + 7; + at = strchr (start, '@'); + semic = strchr (start, ';'); + + if ( strlen (imap_uri) < 7 || at == NULL ) { + return g_strdup ("Unknown"); + } + + if (semic < at) { + /* we have a ";auth=FOO@host" schema + Set semic to the next semicolon, which ends the hostname. */ + user_name = g_strndup (start, semic - start); + /* look for ';' at the end of the domain name */ + semic = strchr (at, ';'); + } else { + user_name = g_strndup (start, at - start); + } + + at_host_name = g_strndup (at, (semic - 1) - at); + + account_name = g_strconcat (user_name, at_host_name, NULL); + + g_free (user_name); + g_free (at_host_name); + + return account_name; +} + +static void +account_text_handler (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + EvolutionAccountContext *account_context; + const GSList *uri_element, *source_element; + gchar *url; + + uri_element = g_markup_parse_context_get_element_stack (context); + source_element = uri_element->next; + + if (strcmp ((gchar *) uri_element->data, "url") != 0 || + !source_element || + strcmp ((gchar *) source_element->data, "source") != 0) { + return; + } + + account_context = (EvolutionAccountContext *) user_data; + + url = g_strndup (text, text_len); + account_context->account = get_account_name_from_imap_uri (url); + g_free (url); +} + +void +get_imap_accounts (void) +{ + GConfClient *client; + GMarkupParser parser = { 0 }; + GMarkupParseContext *parse_context; + GSList *list, *l; + EvolutionAccountContext account_context = { 0 }; + + client = gconf_client_get_default (); + + list = gconf_client_get_list (client, + "/apps/evolution/mail/accounts", + GCONF_VALUE_STRING, + NULL); + + parser.start_element = account_start_element_handler; + parser.text = account_text_handler; + parse_context = g_markup_parse_context_new (&parser, 0, &account_context, NULL); + + accounts = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + + for (l = list; l; l = l->next) { + g_markup_parse_context_parse (parse_context, (const gchar *) l->data, -1, NULL); + + if (account_context.account && + account_context.uid) { + g_hash_table_insert (accounts, + account_context.account, + account_context.uid); + } else { + g_free (account_context.account); + g_free (account_context.uid); + } + } + + g_markup_parse_context_free (parse_context); + + g_slist_foreach (list, (GFunc) g_free, NULL); + g_slist_free (list); +} + +static MailStorageType +get_mail_storage_type_from_path (const gchar *path) +{ + MailStorageType type = MAIL_STORAGE_NONE; + gchar *basename; + + basename = g_path_get_basename (path); + + if (g_str_has_prefix (path, local_dir) && + strchr (basename, '.') == NULL) { + type = MAIL_STORAGE_LOCAL; + } else if (g_str_has_prefix (path, imap_dir) && + strcmp (basename, "summary") == 0) { + type = MAIL_STORAGE_IMAP; + } + + /* Exclude non wanted folders */ + if (strcasestr (path, "junk") || + strcasestr (path, "spam") || + strcasestr (path, "trash") || + strcasestr (path, "drafts") || + strcasestr (path, "sent") || + strcasestr (path, "outbox")) { + type = MAIL_STORAGE_NONE; + } + + g_free (basename); + + return type; +} + +static GMimeStream * +email_get_stream (const gchar *path, + gint flags, + off_t start) +{ + GMimeStream *stream; + gint fd; + + fd = g_open (path, flags, S_IRUSR | S_IWUSR); + + if (fd == -1) { + return NULL; + } + + stream = g_mime_stream_fs_new_with_bounds (fd, start, -1); + + if (!stream) { + close (fd); + } + + return stream; +} + +static gint +read_summary_header (FILE *summary) +{ + gint32 version, n_messages; + + read_summary (summary, + SUMMARY_TYPE_INT32, &version, + SUMMARY_TYPE_INT32, NULL, /* flags */ + SUMMARY_TYPE_INT32, NULL, /* nextuid */ + SUMMARY_TYPE_TIME_T, NULL, /* time */ + SUMMARY_TYPE_INT32, &n_messages, + -1); + + if ((version < 0x100 && version >= 13)) { + read_summary (summary, + SUMMARY_TYPE_INT32, NULL, /* unread count*/ + SUMMARY_TYPE_INT32, NULL, /* deleted count*/ + SUMMARY_TYPE_INT32, NULL, /* junk count */ + -1); + } + + if (version != 0x30c) { + read_summary (summary, + SUMMARY_TYPE_INT32, NULL, + SUMMARY_TYPE_INT32, NULL, + -1); + } + + return n_messages; +} + +gpointer +tracker_module_file_get_data (const gchar *path) +{ + EvolutionFileData *data = NULL; + MailStorageType type; + + type = get_mail_storage_type_from_path (path); + + if (type == MAIL_STORAGE_NONE) { + return NULL; + } + + data = g_slice_new0 (EvolutionFileData); + data->type = type; + + if (type == MAIL_STORAGE_IMAP) { + EvolutionImapData *imap_data; + + imap_data = (EvolutionImapData *) data; + + imap_data->fd = tracker_file_open (path, TRUE); + + if (imap_data->fd == -1) { + return NULL; + } + + imap_data->summary = fdopen (imap_data->fd, "r"); + imap_data->n_messages = read_summary_header (imap_data->summary); + imap_data->cur_message = 1; + + if (imap_data->n_messages > 0) { + /* save current message uid */ + read_summary (imap_data->summary, + SUMMARY_TYPE_STRING, &imap_data->cur_message_uid, /* message uid */ + -1); + } + } else { + EvolutionLocalData *local_data; + + local_data = (EvolutionLocalData *) data; + +#if defined(__linux__) + local_data->stream = email_get_stream (path, O_RDONLY | O_NOATIME, 0); +#else + local_data->stream = email_get_stream (path, O_RDONLY, 0); +#endif + + if (local_data->stream) { + local_data->parser = g_mime_parser_new_with_stream (local_data->stream); + g_mime_parser_set_scan_from (local_data->parser, TRUE); + + /* Initialize to the first message */ + local_data->message = g_mime_parser_construct_message (local_data->parser); + } + } + + return data; +} + +static void +free_imap_data (EvolutionImapData *data) +{ + fclose (data->summary); + close (data->fd); +} + +static void +free_local_data (EvolutionLocalData *data) +{ + if (data->mime_parts) { + g_list_foreach (data->mime_parts, (GFunc) g_object_unref, NULL); + g_list_free (data->mime_parts); + } + + if (data->message) { + g_object_unref (data->message); + } + + if (data->parser) { + g_object_unref (data->parser); + } + + if (data->stream) { + g_mime_stream_close (data->stream); + g_object_unref (data->stream); + } +} + +void +tracker_module_file_free_data (gpointer file_data) +{ + EvolutionFileData *data; + + data = (EvolutionFileData *) file_data; + + if (data->type == MAIL_STORAGE_LOCAL) { + free_local_data ((EvolutionLocalData *) data); + } else if (data->type == MAIL_STORAGE_IMAP) { + free_imap_data ((EvolutionImapData *) data); + } + + g_slice_free (EvolutionFileData, data); +} + +static gint +get_mbox_message_id (GMimeMessage *message) +{ + const gchar *header, *pos; + gchar *number; + gint id; + + header = g_mime_message_get_header (message, "X-Evolution"); + pos = strchr (header, '-'); + + number = g_strndup (header, pos - header); + id = strtoul (number, NULL, 16); + + g_free (number); + + return id; +} + +static guint +get_mbox_message_flags (GMimeMessage *message) +{ + const gchar *header, *pos; + + header = g_mime_message_get_header (message, "X-Evolution"); + pos = strchr (header, '-'); + + return (guint) strtoul (pos + 1, NULL, 16); +} + +static void +get_mbox_uri (TrackerFile *file, + GMimeMessage *message, + gchar **dirname, + gchar **basename) +{ + gchar *dir, *name; + + dir = tracker_string_replace (file->path, local_dir, NULL); + dir = tracker_string_remove (dir, ".sbd"); + + name = g_strdup_printf ("%s;uid=%d", dir, get_mbox_message_id (message)); + + *dirname = g_strdup ("email://local@local"); + *basename = name; + + g_free (dir); +} + +static void +get_mbox_attachment_uri (TrackerFile *file, + GMimeMessage *message, + GMimePart *part, + gchar **dirname, + gchar **basename) +{ + gchar *dir; + + dir = tracker_string_replace (file->path, local_dir, NULL); + dir = tracker_string_remove (dir, ".sbd"); + + *dirname = g_strdup_printf ("email://local@local/%s;uid=%d", + dir, get_mbox_message_id (message)); + *basename = g_strdup (g_mime_part_get_filename (part)); + + g_free (dir); +} + +static GList * +get_mbox_recipient_list (GMimeMessage *message, + const gchar *type) +{ + GList *list = NULL; + const InternetAddressList *addresses; + + addresses = g_mime_message_get_recipients (message, type); + + while (addresses) { + InternetAddress *address; + gchar *str; + + address = addresses->address; + + if (address->name && address->value.addr) { + str = g_strdup_printf ("%s %s", address->name, address->value.addr); + } else if (address->value.addr) { + str = g_strdup (address->value.addr); + } else if (address->name) { + str = g_strdup (address->name); + } else { + str = NULL; + } + + if (str) { + list = g_list_prepend (list, str); + } + + addresses = addresses->next; + } + + return g_list_reverse (list); +} + +static TrackerMetadata * +get_metadata_for_data_wrapper (GMimeDataWrapper *wrapper) +{ + TrackerMetadata *metadata; + GMimeStream *stream; + gchar *path; + gint fd; + + path = g_build_filename (g_get_tmp_dir (), "tracker-evolution-module-XXXXXX", NULL); + fd = g_mkstemp (path); + metadata = NULL; + + stream = g_mime_stream_fs_new (fd); + + if (g_mime_data_wrapper_write_to_stream (wrapper, stream) != -1) { + g_mime_stream_flush (stream); + + metadata = tracker_metadata_utils_get_data (path); + g_unlink (path); + } + + g_mime_stream_close (stream); + g_object_unref (stream); + g_free (path); + + return metadata; +} + +static TrackerMetadata * +get_metadata_for_mbox_attachment (TrackerFile *file, + GMimeMessage *message, + GMimePart *part) +{ + TrackerMetadata *metadata; + GMimeDataWrapper *content; + + content = g_mime_part_get_content_object (part); + + if (!content) { + return NULL; + } + + metadata = get_metadata_for_data_wrapper (content); + + if (metadata) { + gchar *dirname, *basename; + + get_mbox_attachment_uri (file, message, part, + &dirname, &basename); + + tracker_metadata_insert (metadata, METADATA_FILE_PATH, dirname); + tracker_metadata_insert (metadata, METADATA_FILE_NAME, basename); + } + + g_object_unref (content); + + return metadata; +} + +static TrackerMetadata * +get_metadata_for_mbox (TrackerFile *file) +{ + EvolutionLocalData *data; + GMimeMessage *message; + TrackerMetadata *metadata; + gchar *dirname, *basename; + time_t date; + GList *list; + guint flags; + + data = file->data; + message = data->message; + + if (!message) { + return NULL; + } + + flags = get_mbox_message_flags (message); + + if (flags & EVOLUTION_MESSAGE_JUNK || + flags & EVOLUTION_MESSAGE_DELETED) { + return NULL; + } + + if (data->current_mime_part) { + /* We're processing an attachment */ + return get_metadata_for_mbox_attachment (file, message, data->current_mime_part->data); + } + + metadata = tracker_metadata_new (); + + get_mbox_uri (file, message, &dirname, &basename); + tracker_metadata_insert (metadata, METADATA_FILE_PATH, dirname); + tracker_metadata_insert (metadata, METADATA_FILE_NAME, basename); + + g_mime_message_get_date (message, &date, NULL); + tracker_metadata_insert (metadata, METADATA_EMAIL_DATE, + tracker_guint_to_string (date)); + + tracker_metadata_insert (metadata, METADATA_EMAIL_SENDER, + g_strdup (g_mime_message_get_sender (message))); + tracker_metadata_insert (metadata, METADATA_EMAIL_SUBJECT, + g_strdup (g_mime_message_get_subject (message))); + + list = get_mbox_recipient_list (message, GMIME_RECIPIENT_TYPE_TO); + tracker_metadata_insert_multiple_values (metadata, METADATA_EMAIL_SENT_TO, list); + + list = get_mbox_recipient_list (message, GMIME_RECIPIENT_TYPE_CC); + tracker_metadata_insert_multiple_values (metadata, METADATA_EMAIL_CC, list); + + return metadata; +} + +static void +skip_content_info (FILE *summary) +{ + guint32 count, i; + + if (fgetc (summary)) { + read_summary (summary, + SUMMARY_TYPE_TOKEN, NULL, + SUMMARY_TYPE_TOKEN, NULL, + SUMMARY_TYPE_UINT32, &count, + -1); + + if (count <= 500) { + for (i = 0; i < count; i++) { + read_summary (summary, + SUMMARY_TYPE_TOKEN, NULL, + SUMMARY_TYPE_TOKEN, NULL, + -1); + } + } + + read_summary (summary, + SUMMARY_TYPE_TOKEN, NULL, + SUMMARY_TYPE_TOKEN, NULL, + SUMMARY_TYPE_TOKEN, NULL, + SUMMARY_TYPE_UINT32, NULL, + -1); + } + + read_summary (summary, + SUMMARY_TYPE_UINT32, &count, + -1); + + for (i = 0; i < count; i++) { + skip_content_info (summary); + } +} + +static gboolean +get_imap_attachment_info (const gchar *mime_file, + gchar **name, + GMimePartEncodingType *encoding) +{ + GMimeContentType *mime; + gchar *tmp, *mime_content; + const gchar *pos_content_type, *pos_encoding, *pos_end_encoding; + + if (name) { + *name = NULL; + } + + if (encoding) { + *encoding = GMIME_PART_ENCODING_DEFAULT; + } + + if (!g_file_get_contents (mime_file, &tmp, NULL, NULL)) { + return FALSE; + } + + /* all text content in lower case for comparisons */ + mime_content = g_ascii_strdown (tmp, -1); + g_free (tmp); + + pos_content_type = strstr (mime_content, "content-type:"); + + if (pos_content_type) { + pos_encoding = strstr (pos_content_type, "content-transfer-encoding:"); + } + + if (!pos_content_type || !pos_encoding) { + g_free (mime_content); + return FALSE; + } + + pos_content_type += strlen ("content-type:"); + pos_encoding += strlen ("content-transfer-encoding:"); + + /* ignore spaces, tab or line returns */ + while (*pos_content_type != '\0' && (*pos_content_type == ' ' || *pos_content_type == '\t' || *pos_content_type == '\n')) { + pos_content_type++; + } + + while (*pos_encoding != '\0' && *pos_encoding == ' ') { + pos_encoding++; + } + + if (*pos_content_type == '\0' || + *pos_encoding == '\0') { + g_free (mime_content); + return FALSE; + } + + mime = g_mime_content_type_new_from_string (pos_content_type); + + if (mime) { + if (name) { + *name = g_strdup (g_mime_content_type_get_parameter (mime, "name")); + } + + g_mime_content_type_destroy (mime); + } + + if (name && !*name) { + g_free (mime_content); + return FALSE; + } + + /* Find end of encoding */ + pos_end_encoding = pos_encoding; + + while (*pos_end_encoding != '\0' && + *pos_end_encoding != ' ' && + *pos_end_encoding != '\n' && + *pos_end_encoding != '\t') { + pos_end_encoding++; + } + + if (encoding && pos_encoding != pos_end_encoding) { + gchar *encoding_str = g_strndup (pos_encoding, pos_end_encoding - pos_encoding); + + if (strcmp (encoding_str, "7bit") == 0) { + *encoding = GMIME_PART_ENCODING_7BIT; + } else if (strcmp (encoding_str, "8bit") == 0) { + *encoding = GMIME_PART_ENCODING_7BIT; + } else if (strcmp (encoding_str, "binary") == 0) { + *encoding = GMIME_PART_ENCODING_BINARY; + } else if (strcmp (encoding_str, "base64") == 0) { + *encoding = GMIME_PART_ENCODING_BASE64; + } else if (strcmp (encoding_str, "quoted-printable") == 0) { + *encoding = GMIME_PART_ENCODING_QUOTEDPRINTABLE; + } else if (strcmp (encoding_str, "x-uuencode") == 0) { + *encoding = GMIME_PART_ENCODING_UUENCODE; + } + + g_free (encoding_str); + } + + g_free (mime_content); + + return TRUE; +} + +static void +get_imap_uri (TrackerFile *file, + const gchar *uid, + gchar **uri_base, + gchar **basename) +{ + GList *keys, *k; + gchar *path, *dir, *subdirs; + + path = file->path; + keys = g_hash_table_get_keys (accounts); + *uri_base = *basename = NULL; + + for (k = keys; k; k = k->next) { + if (strstr (path, k->data)) { + *uri_base = g_strdup_printf ("email://%s", (gchar *) g_hash_table_lookup (accounts, k->data)); + + dir = g_build_filename (imap_dir, k->data, NULL); + + /* now remove all relevant info to create the email:// basename */ + subdirs = g_strdup (path); + subdirs = tracker_string_remove (subdirs, dir); + subdirs = tracker_string_remove (subdirs, "/folders"); + subdirs = tracker_string_remove (subdirs, "/subfolders"); + subdirs = tracker_string_remove (subdirs, "/summary"); + + *basename = g_strdup_printf ("%s;uid=%s", subdirs, uid); + + g_free (subdirs); + g_free (dir); + + break; + } + } + + g_list_free (keys); + + return; +} + +static void +get_imap_attachment_uri (TrackerFile *file, + gchar **dirname, + gchar **basename) +{ + EvolutionImapData *data; + gchar *message_dirname, *message_basename, *name; + + data = file->data; + + if (!get_imap_attachment_info (data->current_mime_part->data, &name, NULL)) { + return; + } + + get_imap_uri (file, data->cur_message_uid, &message_dirname, &message_basename); + *dirname = g_strdup_printf ("%s/%s", message_dirname, message_basename); + *basename = name; + + g_free (message_dirname); + g_free (message_basename); +} + +static GList * +get_imap_recipient_list (const gchar *str) +{ + GList *list = NULL; + gchar **arr; + gint i; + + if (!str) { + return NULL; + } + + arr = g_strsplit (str, ",", -1); + + for (i = 0; arr[i]; i++) { + g_strstrip (arr[i]); + list = g_list_prepend (list, g_strdup (arr[i])); + } + + g_strfreev (arr); + + return g_list_reverse (list); +} + +static gchar * +get_imap_message_path (TrackerFile *file, + const gchar *uid) +{ + gchar *prefix, *message_path; + + prefix = g_strndup (file->path, strlen (file->path) - strlen ("summary")); + message_path = g_strconcat (prefix, uid, ".", NULL); + g_free (prefix); + + return message_path; +} + +static TrackerMetadata * +get_metadata_for_imap_attachment (TrackerFile *file, + const gchar *mime_file) +{ + TrackerMetadata *metadata; + GMimeStream *stream; + GMimeDataWrapper *wrapper; + GMimePartEncodingType encoding; + gchar *path, *name; + + if (!get_imap_attachment_info (mime_file, &name, &encoding)) { + return NULL; + } + + path = g_strdup (mime_file); + path = tracker_string_remove (path, ".MIME"); + +#if defined(__linux__) + stream = email_get_stream (path, O_RDONLY | O_NOATIME, 0); +#else + stream = email_get_stream (path, O_RDONLY, 0); +#endif + + if (!stream) { + g_free (name); + g_free (path); + return NULL; + } + + wrapper = g_mime_data_wrapper_new_with_stream (stream, encoding); + metadata = get_metadata_for_data_wrapper (wrapper); + + if (metadata) { + EvolutionImapData *data; + gchar *dirname, *basename; + + data = file->data; + + get_imap_uri (file, + data->cur_message_uid, + &dirname, &basename); + + tracker_metadata_insert (metadata, METADATA_FILE_NAME, g_strdup (name)); + tracker_metadata_insert (metadata, METADATA_FILE_PATH, + g_strdup_printf ("%s/%s", dirname, basename)); + + g_free (dirname); + g_free (basename); + } + + g_object_unref (wrapper); + g_object_unref (stream); + g_free (name); + g_free (path); + + return metadata; +} + +static TrackerMetadata * +get_metadata_for_imap (TrackerFile *file) +{ + EvolutionImapData *data; + TrackerMetadata *metadata = NULL; + gchar *dirname, *basename; + gchar *subject, *from, *to, *cc; + gint32 i, count, flags; + time_t date; + GList *list; + gboolean deleted; + + data = file->data; + + if (data->cur_message > data->n_messages) { + return NULL; + } + + if (data->current_mime_part) { + return get_metadata_for_imap_attachment (file, data->current_mime_part->data); + } + + if (!read_summary (data->summary, + SUMMARY_TYPE_UINT32, &flags, /* flags */ + -1)) { + return NULL; + } + + deleted = ((flags & EVOLUTION_MESSAGE_JUNK) != 0 || + (flags & EVOLUTION_MESSAGE_DELETED) != 0); + + subject = NULL; + from = NULL; + to = NULL; + cc = NULL; + + if (!read_summary (data->summary, + SUMMARY_TYPE_UINT32, NULL, /* size */ + SUMMARY_TYPE_TIME_T, NULL, /* date sent */ + SUMMARY_TYPE_TIME_T, &date, /* date received */ + SUMMARY_TYPE_STRING, &subject, /* subject */ + SUMMARY_TYPE_STRING, &from, /* from */ + SUMMARY_TYPE_STRING, &to, /* to */ + SUMMARY_TYPE_STRING, &cc, /* cc */ + SUMMARY_TYPE_STRING, NULL, /* mlist */ + -1)) { + g_free (subject); + g_free (from); + g_free (to); + g_free (cc); + return NULL; + } + + if (!deleted) { + metadata = tracker_metadata_new (); + get_imap_uri (file, data->cur_message_uid, &dirname, &basename); + + tracker_metadata_insert (metadata, METADATA_FILE_PATH, dirname); + tracker_metadata_insert (metadata, METADATA_FILE_NAME, basename); + + tracker_metadata_insert (metadata, METADATA_EMAIL_DATE, + tracker_guint_to_string (date)); + + tracker_metadata_insert (metadata, METADATA_EMAIL_SENDER, from); + tracker_metadata_insert (metadata, METADATA_EMAIL_SUBJECT, subject); + + list = get_imap_recipient_list (to); + tracker_metadata_insert_multiple_values (metadata, METADATA_EMAIL_SENT_TO, list); + + list = get_imap_recipient_list (cc); + tracker_metadata_insert_multiple_values (metadata, METADATA_EMAIL_CC, list); + } + + g_free (to); + g_free (cc); + + if (!read_summary (data->summary, + SUMMARY_TYPE_INT32, NULL, + SUMMARY_TYPE_INT32, NULL, + SUMMARY_TYPE_UINT32, &count, + -1)) { + goto corruption; + } + + /* references */ + for (i = 0; i < count; i++) { + if (read_summary (data->summary, + SUMMARY_TYPE_INT32, NULL, + SUMMARY_TYPE_INT32, NULL, + -1)) { + continue; + } + + goto corruption; + } + + if (!read_summary (data->summary, SUMMARY_TYPE_UINT32, &count, -1)) { + goto corruption; + } + + /* user flags */ + for (i = 0; i < count; i++) { + if (read_summary (data->summary, SUMMARY_TYPE_STRING, NULL, -1)) { + continue; + } + + goto corruption; + } + + if (!read_summary (data->summary, SUMMARY_TYPE_UINT32, &count, -1)) { + goto corruption; + } + + /* user tags */ + for (i = 0; i < count; i++) { + if (read_summary (data->summary, + SUMMARY_TYPE_STRING, NULL, + SUMMARY_TYPE_STRING, NULL, + -1)) { + continue; + } + + goto corruption; + } + + /* server flags */ + if (!read_summary (data->summary, + SUMMARY_TYPE_UINT32, NULL, + -1)) { + goto corruption; + } + + skip_content_info (data->summary); + + return metadata; + +corruption: + /* assume corruption */ + if (metadata) { + tracker_metadata_free (metadata); + } + + return NULL; +} + +void +tracker_module_file_get_uri (TrackerFile *file, + gchar **dirname, + gchar **basename) +{ + EvolutionFileData *file_data; + + file_data = file->data; + + if (!file_data) { + /* It isn't any of the files the + * module is interested for */ + return; + } + + switch (file_data->type) { + case MAIL_STORAGE_LOCAL: { + EvolutionLocalData *data = file->data; + + if (!data->message) { + return; + } + + if (data->current_mime_part) { + /* We're currently on an attachment */ + get_mbox_attachment_uri (file, data->message, + data->current_mime_part->data, + dirname, basename); + } else { + get_mbox_uri (file, data->message, dirname, basename); + } + + break; + } + case MAIL_STORAGE_IMAP: { + EvolutionImapData *data = file->data; + + if (data->current_mime_part) { + /* We're currently on an attachment */ + get_imap_attachment_uri (file, dirname, basename); + } else { + get_imap_uri (file, data->cur_message_uid, dirname, basename); + } + + break; + } + default: + break; + } +} + +gchar * +tracker_module_file_get_service_type (TrackerFile *file) +{ + EvolutionFileData *data; + + data = file->data; + + if (!data) { + /* It isn't any of the files the module handles */ + return NULL; + } + + if (data->type == MAIL_STORAGE_LOCAL) { + EvolutionLocalData *local_data = file->data; + + if (local_data->current_mime_part) { + return g_strdup ("EvolutionAttachments"); + } + } else if (data->type == MAIL_STORAGE_IMAP) { + EvolutionImapData *imap_data = file->data; + + if (imap_data->current_mime_part) { + return g_strdup ("EvolutionAttachments"); + } + } + + return g_strdup ("EvolutionEmails"); +} + +TrackerMetadata * +tracker_module_file_get_metadata (TrackerFile *file) +{ + EvolutionFileData *data; + + data = file->data; + + if (!data) { + /* It isn't any of the files the + * module is interested for */ + return NULL; + } + + switch (data->type) { + case MAIL_STORAGE_LOCAL: + return get_metadata_for_mbox (file); + case MAIL_STORAGE_IMAP: + return get_metadata_for_imap (file); + default: + break; + } + + return NULL; +} + +static gchar * +get_message_encoding (GMimeMessage *message) +{ + const gchar *content_type, *start_encoding, *end_encoding; + + content_type = g_mime_message_get_header (message, "Content-Type"); + + if (!content_type) { + return NULL; + } + + start_encoding = strstr (content_type, "charset="); + + if (!start_encoding) { + return NULL; + } + + start_encoding += strlen ("charset="); + + if (start_encoding[0] == '"') { + /* encoding is quoted */ + start_encoding++; + end_encoding = strstr (start_encoding, "\""); + } else { + end_encoding = strstr (start_encoding, ";"); + } + + if (end_encoding) { + return g_strndup (start_encoding, end_encoding - start_encoding); + } else { + return g_strdup (start_encoding); + } +} + +static gchar * +get_text_for_mbox (TrackerFile *file) +{ + EvolutionLocalData *data; + gboolean is_html; + gchar *text, *encoding, *utf8_text; + + data = file->data; + + if (data->current_mime_part) { + /* FIXME: Extract text from attachments */ + return NULL; + } + + text = g_mime_message_get_body (data->message, TRUE, &is_html); + + if (!text) { + return NULL; + } + + encoding = get_message_encoding (data->message); + + if (!encoding) { + /* FIXME: could still puke on non-utf8 + * messages without proper content type + */ + return text; + } + + utf8_text = g_convert (text, -1, "utf8", encoding, NULL, NULL, NULL); + + g_free (encoding); + g_free (text); + + return utf8_text; +} + +static gchar * +get_text_for_imap (TrackerFile *file) +{ + EvolutionImapData *data; + gchar *message_path; + gchar *body = NULL; + + data = file->data; + + if (data->current_mime_part) { + /* FIXME: Extract text from attachments */ + return NULL; + } + + message_path = get_imap_message_path (file, data->cur_message_uid); + g_file_get_contents (message_path, &body, NULL, NULL); + g_free (message_path); + + return body; +} + +gchar * +tracker_module_file_get_text (TrackerFile *file) +{ + EvolutionFileData *data; + gchar *text = NULL; + + data = file->data; + + if (!data) { + /* It isn't any of the files the + * module is interested in */ + return NULL; + } + + if (data->type == MAIL_STORAGE_LOCAL) { + text = get_text_for_mbox (file); + } else if (data->type == MAIL_STORAGE_IMAP) { + text = get_text_for_imap (file); + } + + return text; +} + +static GList * +extract_imap_mime_parts (TrackerFile *file) +{ + EvolutionImapData *data; + gboolean has_attachment = TRUE; + gint n_attachment = 0; + gchar *message_path; + GList *mime_parts = NULL; + + data = file->data; + message_path = get_imap_message_path (file, data->cur_message_uid); + + while (has_attachment) { + gchar *mime_file; + + n_attachment++; + mime_file = g_strdup_printf ("%s%d.MIME", message_path, n_attachment); + + if (g_file_test (mime_file, G_FILE_TEST_EXISTS)) { + mime_parts = g_list_prepend (mime_parts, mime_file); + } else { + g_free (mime_file); + has_attachment = FALSE; + } + } + + g_free (message_path); + + return g_list_reverse (mime_parts);; +} + +static void +extract_mbox_mime_parts (GMimeObject *object, + gpointer user_data) +{ + GList **list = (GList **) user_data; + GMimePart *part; + const gchar *disposition, *filename; + + if (GMIME_IS_MESSAGE_PART (object)) { + GMimeMessage *message; + + message = g_mime_message_part_get_message (GMIME_MESSAGE_PART (object)); + + if (message) { + g_mime_message_foreach_part (message, extract_mbox_mime_parts, user_data); + g_object_unref (message); + } + + return; + } else if (GMIME_IS_MULTIPART (object)) { + g_mime_multipart_foreach (GMIME_MULTIPART (object), extract_mbox_mime_parts, user_data); + return; + } + + part = GMIME_PART (object); + disposition = g_mime_part_get_content_disposition (part); + + if (!disposition || + (strcmp (disposition, GMIME_DISPOSITION_ATTACHMENT) != 0 && + strcmp (disposition, GMIME_DISPOSITION_INLINE) != 0)) { + return; + } + + filename = g_mime_part_get_filename (GMIME_PART (object)); + + if (!filename || + strcmp (filename, "signature.asc") == 0 || + strcmp (filename, "signature.pgp") == 0) { + return; + } + + *list = g_list_prepend (*list, g_object_ref (object)); +} + +gboolean +tracker_module_file_iter_contents (TrackerFile *file) +{ + EvolutionFileData *data; + + data = file->data; + + if (data->type == MAIL_STORAGE_IMAP) { + EvolutionImapData *imap_data = file->data; + + /* Iterate through mime parts, if any */ + if (!imap_data->mime_parts) { + imap_data->mime_parts = extract_imap_mime_parts (file); + imap_data->current_mime_part = imap_data->mime_parts; + } else { + imap_data->current_mime_part = imap_data->current_mime_part->next; + } + + if (imap_data->current_mime_part) { + return TRUE; + } + + g_list_foreach (imap_data->mime_parts, (GFunc) g_free, NULL); + g_list_free (imap_data->mime_parts); + imap_data->mime_parts = NULL; + + g_free (imap_data->cur_message_uid); + imap_data->cur_message_uid = NULL; + + /* save current message uid */ + read_summary (imap_data->summary, + SUMMARY_TYPE_STRING, &imap_data->cur_message_uid, /* message uid */ + -1); + + imap_data->cur_message++; + + return (imap_data->cur_message < imap_data->n_messages); + } else if (data->type == MAIL_STORAGE_LOCAL) { + EvolutionLocalData *local_data = file->data; + + if (!local_data->parser) { + return FALSE; + } + + if (local_data->message) { + /* Iterate through mime parts, if any */ + if (!local_data->mime_parts) { + g_mime_message_foreach_part (local_data->message, + extract_mbox_mime_parts, + &local_data->mime_parts); + local_data->current_mime_part = local_data->mime_parts; + } else { + local_data->current_mime_part = local_data->current_mime_part->next; + } + + if (local_data->current_mime_part) { + return TRUE; + } + + /* all possible mime parts have been already iterated, move on */ + g_object_unref (local_data->message); + + g_list_foreach (local_data->mime_parts, (GFunc) g_object_unref, NULL); + g_list_free (local_data->mime_parts); + local_data->mime_parts = NULL; + } + + local_data->message = g_mime_parser_construct_message (local_data->parser); + + return (local_data->message != NULL); + } + + return FALSE; +} |