Index: python/deskbar-handler/tracker-module.py =================================================================== --- python/deskbar-handler/tracker-module.py (revision 1249) +++ python/deskbar-handler/tracker-module.py (working copy) @@ -112,6 +112,7 @@ 'action': { # more actions for different MUAs 'key': 'mua', # see TrackerLiveSearchAction.action for a demo 'Evolution/Email': 'evolution %(uri)s', + 'Modest/Email': 'modest-open %(uri)s', 'Thunderbird/Email': 'thunderbird -viewtracker %(uri)s', 'KMail/Email': 'kmail --view %(uri)s', }, Index: python/deskbar-handler/tracker-handler.py =================================================================== --- python/deskbar-handler/tracker-handler.py (revision 1249) +++ python/deskbar-handler/tracker-handler.py (working copy) @@ -49,6 +49,7 @@ 'action': { # more actions for different MUAs 'key': 'mua', # see TrackerLiveSearchMatch.action for a demo 'Evolution/Email': 'evolution %(uri)s', + 'Modest/Email': 'modest-open %(uri)s', 'Thunderbird/Email': 'thunderbird -viewtracker %(uri)s', 'KMail/Email': 'kmail --view %(uri)s', }, Index: src/trackerd/tracker-db-email.c =================================================================== --- src/trackerd/tracker-db-email.c (revision 1249) +++ src/trackerd/tracker-db-email.c (working copy) @@ -393,6 +393,8 @@ { if (app == MAIL_APP_EVOLUTION) { return g_strdup ("EvolutionEmails"); + } else if (app == MAIL_APP_MODEST) { + return g_strdup ("ModestEmails"); } else if (app == MAIL_APP_KMAIL) { return g_strdup ("KMailEmails"); } else if (app == MAIL_APP_THUNDERBIRD) { @@ -410,6 +412,8 @@ { if (app == MAIL_APP_EVOLUTION) { return g_strdup ("Evolution/Email"); + } else if (app == MAIL_APP_MODEST) { + return g_strdup ("Modest/Email"); } else if (app == MAIL_APP_KMAIL) { return g_strdup ("KMail/Email"); } else if (app == MAIL_APP_THUNDERBIRD) { @@ -427,6 +431,8 @@ { if (app == MAIL_APP_EVOLUTION) { return g_strdup ("EvolutionAttachments"); + } else if (app == MAIL_APP_MODEST) { + return g_strdup ("ModestAttachments"); } else if (app == MAIL_APP_KMAIL) { return g_strdup ("KMailAttachments"); } else if (app == MAIL_APP_THUNDERBIRD) { Index: src/trackerd/tracker-dbus-search.c =================================================================== --- src/trackerd/tracker-dbus-search.c (revision 1249) +++ src/trackerd/tracker-dbus-search.c (working copy) @@ -103,7 +103,8 @@ service_array[1] = tracker_get_id_for_service ("EvolutionEmails"); service_array[2] = tracker_get_id_for_service ("KMailEmails"); service_array[3] = tracker_get_id_for_service ("ThunderbirdEmails"); - service_count = 4; + service_array[4] = tracker_get_id_for_service ("ModestEmails"); + service_count = 5; } else if (strcmp (service, "Conversations") == 0) { service_array[1] = tracker_get_id_for_service ("GaimConversations"); Index: src/trackerd/tracker-email-modest.c =================================================================== --- src/trackerd/tracker-email-modest.c (revision 0) +++ src/trackerd/tracker-email-modest.c (revision 0) @@ -0,0 +1,1991 @@ +/* Tracker + * routines for emails with Modest + * Copyright (C) 2008, Philip Van Hoof (pvanhoof gnome org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef TEST + +/* #define static */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tracker-email-modest.h" +#include "tracker-email-utils.h" +#include "tracker-db-email.h" +#include "tracker-cache.h" +#include "tracker-dbus.h" +#include "tracker-watch.h" + + +#define MODEST_MAIL_DIR_S ".modest/cache/mail" +#define MODEST_LOCAL_MAIL_DIR_S ".modest/local_folders" + +typedef enum { + MODEST_MAIL_PROTOCOL_UNKNOWN, + MODEST_MAIL_PROTOCOL_MBOX, + MODEST_MAIL_PROTOCOL_IMAP, + MODEST_MAIL_PROTOCOL_MH, + MODEST_MAIL_PROTOCOL_MAILDIR +} ModestMailProtocol; + +typedef struct { + gchar *name; /* laurent aguerreche free fr */ + gchar *uid; /* unique ID generated by Modest, looks like "1134161347 7985 16 foo bar" */ + gboolean enabled; /* enabled account or not */ + ModestMailProtocol protocol; /* protocol used: MBox, IMAP, MH, Maildir */ + gchar *source_url; /* mbox:///var/mail/laurent (used to find where root directory for email files is) */ +} ModestAccount; + +typedef struct { + gchar *mail_dir; /* something like "/home/laurent.modest/mail" */ + GSList *imap_dirs; /* list of IMAP directories */ + GSList *pop_dirs; /* list of POP directories */ + GSList *maildir_dirs; /* list of maildir directories */ +} ModestConfig; + + +enum { + MODEST_MESSAGE_ANSWERED = 1 << 0, + MODEST_MESSAGE_DELETED = 1 << 1, + MODEST_MESSAGE_DRAFT = 1 << 2, + MODEST_MESSAGE_FLAGGED = 1 << 3, + MODEST_MESSAGE_SEEN = 1 << 4, + MODEST_MESSAGE_ATTACHMENTS = 1 << 5, + MODEST_MESSAGE_ANSWERED_ALL = 1 << 6, + MODEST_MESSAGE_JUNK = 1 << 7, + MODEST_MESSAGE_SECURE = 1 << 8 +}; + +typedef struct { + gchar *path; /* path the summary file */ + FILE *f; /* opened file descriptor for the file */ +#if 0 + ModestAccount *associated_account; /* pointer to the associated Evo account of this file (so we will not free + this pointer...) */ +#endif +} SummaryFile; + +typedef struct { + gint32 version; + gboolean legacy; + gint32 flags; + gint32 nextuid; + time_t time; + gint32 saved_count; + gint32 unread_count; + gint32 deleted_count; + gint32 junk_count; + gchar *uri_prefix; +} SummaryFileHeader; + +/* Some infos are only accessible throw a deep code path but we need to retreive them. */ +typedef struct { + gchar *mail_uid; +} ModestAdHocInfos; + + +extern Tracker *tracker; + +static ModestConfig *modest_config = NULL; + + +static gboolean load_modest_config (ModestConfig **conf); +static void free_modest_config (ModestConfig *conf); + + +static gboolean is_in_dir_pop (const gchar *dir); +static gboolean is_in_dir_imap (const gchar *dir); +static gboolean is_in_dir_maildir (const gchar *dir); + +typedef gboolean (* LoadSummaryFileMetaHeaderFct) (SummaryFile *summary, SummaryFileHeader *header); +typedef gboolean (* LoadMailMessageFct) (SummaryFile *summary, MailMessage **mail_msg); +typedef gboolean (* SkipMailMessageFct) (SummaryFile *summary); +typedef gboolean (* SaveOnDiskMailMessageFct) (DBConnection *db_con, MailMessage *msg); + +static void index_mail_messages_by_summary_file (DBConnection *db_con, MailType mail_type, + const gchar *summary_file_path, + LoadSummaryFileMetaHeaderFct load_meta_header, + LoadMailMessageFct load_mail, + SkipMailMessageFct skip_mail, + SaveOnDiskMailMessageFct save_ondisk_mail); + +static gboolean open_summary_file (const gchar *path, SummaryFile **summary); +static void free_summary_file (SummaryFile *summary); + +static gboolean load_summary_file_header (SummaryFile *summary, SummaryFileHeader **header); +static void free_summary_file_header (SummaryFileHeader *header); +static gboolean load_summary_file_meta_header_for_pop (SummaryFile *summary, SummaryFileHeader *header); +static gboolean load_summary_file_meta_header_for_maildir (SummaryFile *summary, SummaryFileHeader *header); +static gboolean load_summary_file_meta_header_for_imap (SummaryFile *summary, SummaryFileHeader *header); + +static gboolean load_mail_message_for_imap (SummaryFile *summary, MailMessage **mail_msg); +static gboolean load_mail_message_for_pop (SummaryFile *summary, MailMessage **mail_msg); +static gboolean load_mail_message_for_maildir (SummaryFile *summary, MailMessage **mail_msg); +static gboolean do_load_mail_message_for_imap (SummaryFile *summary, MailMessage **mail_msg, gboolean do_skipping_of_content_info); +static gboolean do_load_mail_message_for_pop (SummaryFile *summary, MailMessage **mail_msg, gboolean do_skipping_of_content_info); +static gboolean do_load_mail_message_for_maildir (SummaryFile *summary, MailMessage **mail_msg, gboolean do_skipping_of_content_info); + +static gboolean load_mail_message (SummaryFile *summary, MailMessage *mail_msg); + +static gboolean skip_mail_message_for_imap (SummaryFile *summary); +static gboolean skip_mail_message_for_pop (SummaryFile *summary); +static gboolean skip_mail_message_for_maildir (SummaryFile *summary); + +static gboolean do_skip_mail_message_for_maildir (SummaryFile *summary, gboolean do_skipping_of_content_info); +static gboolean do_skip_mail_message_for_pop (SummaryFile *summary, gboolean do_skipping_of_content_info); +static gboolean do_skip_mail_message_for_imap (SummaryFile *summary, gboolean do_skipping_of_content_info); + +static gboolean skip_mail_message (SummaryFile *summary); + +static gboolean skip_loading_content_info (SummaryFile *summary); +static gboolean do_skip_loading_content_info (SummaryFile *summary); + +static gboolean save_ondisk_email_message_for_imap (DBConnection *db_con, MailMessage *mail_msg); +static gboolean save_ondisk_email_message_for_pop (DBConnection *db_con, MailMessage *mail_msg); +static gboolean save_ondisk_email_message_for_maildir (DBConnection *db_con, MailMessage *mail_msg); +static gboolean do_save_ondisk_email_message_generic (DBConnection *db_con, MailMessage *mail_msg); +static gboolean do_save_ondisk_email_message (DBConnection *db_con, MailMessage *mail_msg); +static gboolean index_mail_parts (DBConnection *db_con, MailMessage *mail_msg, const gchar *mail_part_radix); + +static GSList * add_persons_from_internet_address_list_string_parsing (GSList *list, const gchar *s); + +static inline gboolean decode_gint32 (FILE *f, gint32 *n); +static inline gboolean skip_gint32_decoding (FILE *f); +static inline gboolean decode_guint32 (FILE *f, guint32 *n); +static inline gboolean skip_guint32_decoding (FILE *f); +static inline gboolean decode_time_t (FILE *f, time_t *t); +static inline gboolean skip_time_t_decoding (FILE *f); +static inline gboolean decode_off_t (FILE *f, off_t *t); +static inline gboolean skip_off_t_decoding (FILE *f); +static inline gboolean decode_string (FILE *f, gchar **str); +static inline gboolean skip_string_decoding (FILE *f); +static inline gboolean skip_token_decoding (FILE *f); + +static void check_summary_file (DBConnection *db_con, const gchar *filename, MailStore *store); + +#endif + +static gboolean +modest_module_is_running (void) +{ + return modest_config != NULL; +} + + +/******************************************************************************************** + Public functions +*********************************************************************************************/ + +gboolean +tracker_email_init (void) +{ + ModestConfig *conf; + + if (modest_config) { + return TRUE; + } + + conf = NULL; + + if (load_modest_config (&conf)) { + modest_config = conf; + } + + return modest_module_is_running (); +} + + + +gboolean +tracker_email_finalize (void) +{ + if (!modest_config) { + return TRUE; + } + + free_modest_config (modest_config); + modest_config = NULL; + + return !modest_module_is_running (); +} + +static void +free_modest_config (ModestConfig *conf) +{ + if (!conf) { + return; + } + + if (conf->mail_dir) { + g_free (conf->mail_dir); + } + + #define FREE_MY_LIST(list, free_fct) \ + g_slist_foreach (list, (GFunc) free_fct, NULL); \ + g_slist_free (list); + + FREE_MY_LIST (conf->imap_dirs, g_free); + FREE_MY_LIST (conf->pop_dirs, g_free); + FREE_MY_LIST (conf->maildir_dirs, g_free); + + #undef FREE_MY_LIST + + g_slice_free (ModestConfig, conf); +} + +void +tracker_email_watch_emails (DBConnection *db_con) +{ + gchar ***res, **row; + gint j; + + /* if initial indexing has not finished reset mtime on all email stuff so they are rechecked */ + if (tracker_db_get_option_int (db_con->common, "InitialIndex") == 1) { + char *sql = g_strdup_printf ("update Services set mtime = 0 where path like '%s/.modest/%s'", g_get_home_dir (), "%"); + + tracker_exec_sql (db_con, sql); + g_free (sql); + } + + /* check all registered mbox/paths for deletions */ + res = tracker_db_email_get_mboxes (db_con); + + for (j = 0; (row = tracker_db_get_row (res, j)); j++) { + + if (row[2] && row[3]) { + MailStore *store = tracker_db_email_get_mbox_details (db_con, row[3]); + + if (store) { + check_summary_file (db_con, row[2], store); + tracker_db_email_free_mail_store (store); + } + } + } + + if (res) { + tracker_db_free_result (res); + } + + g_slist_foreach (modest_config->imap_dirs, (GFunc) email_watch_directory, "ModestEmails"); + g_slist_foreach (modest_config->pop_dirs, (GFunc) email_watch_directory, "ModestEmails"); + g_slist_foreach (modest_config->maildir_dirs, (GFunc) email_watch_directory, "ModestEmails"); +} + +static gboolean +modest_file_is_interesting (FileInfo *info) +{ + g_return_val_if_fail (info, FALSE); + g_return_val_if_fail (info->uri, FALSE); + g_return_val_if_fail (modest_config, FALSE); + g_return_val_if_fail (modest_config->mail_dir, FALSE); + + /* maildir/pop/imap all have summary files (*.ev-summary.mmap or "summary.mmap") */ + if ((strcmp (info->uri, "summary.mmap") == 0) || g_str_has_suffix (info->uri, "summary.mmap")) { + return TRUE; + } else { + return FALSE; + } + + return FALSE; +} + + +gboolean +tracker_email_index_file (DBConnection *db_con, FileInfo *info) +{ + gchar *file_name; + + g_return_val_if_fail (db_con, FALSE); + g_return_val_if_fail (info, FALSE); + + if (!modest_file_is_interesting (info)) + return FALSE; + + file_name = g_path_get_basename (info->uri); + + tracker_debug ("indexing email summary %s", info->uri); + + if (is_in_dir_imap (info->uri)) { + if (strcmp (file_name, "summary.mmap") == 0) { + index_mail_messages_by_summary_file (db_con, MAIL_TYPE_IMAP, info->uri, + load_summary_file_meta_header_for_imap, + load_mail_message_for_imap, + skip_mail_message_for_imap, + save_ondisk_email_message_for_imap); + } + } + + if (is_in_dir_pop (info->uri)) { + if (strcmp (file_name, "summary.mmap") == 0) { + index_mail_messages_by_summary_file (db_con, MAIL_TYPE_POP, info->uri, + load_summary_file_meta_header_for_pop, + load_mail_message_for_pop, + skip_mail_message_for_pop, + save_ondisk_email_message_for_pop); + } + } + + if (is_in_dir_maildir (info->uri)) { + if (strcmp (file_name, "summary.mmap") || g_str_has_suffix (info->uri, "summary.mmap")) { + index_mail_messages_by_summary_file (db_con, MAIL_TYPE_MAILDIR, info->uri, + load_summary_file_meta_header_for_maildir, + load_mail_message_for_maildir, + skip_mail_message_for_maildir, + save_ondisk_email_message_for_maildir); + } + } + + g_free (file_name); + + return TRUE; +} + +const gchar * +tracker_email_get_name (void) +{ + return "ModestEmails"; +} + + +/******************************************************************************************** + Private functions +*********************************************************************************************/ + + +static void +check_summary_file (DBConnection *db_con, const gchar *filename, MailStore *store) +{ + SummaryFile *summary = NULL; + + g_return_if_fail (store); + + if (open_summary_file (filename, &summary)) { + SummaryFileHeader *header; + gchar *path; + + header = NULL; + + tracker_log ("Scanning summary file %s for junk", filename); + + if (!load_summary_file_header (summary, &header)) { + free_summary_file (summary); + tracker_error ("ERROR: failed to open summary file %s", filename); + return; + } + + if (store->type == MAIL_TYPE_IMAP) { + if (!load_summary_file_meta_header_for_imap (summary, header)) { + free_summary_file_header (header); + free_summary_file (summary); + tracker_error ("ERROR: failed to open summary header file %s", filename); + return; + } + + } else if (store->type == MAIL_TYPE_POP) { + if (!load_summary_file_meta_header_for_pop (summary, header)) { + free_summary_file_header (header); + free_summary_file (summary); + tracker_error ("ERROR: failed to open summary header file %s", filename); + return; + } + } else if (store->type == MAIL_TYPE_MAILDIR) { + if (!load_summary_file_meta_header_for_pop (summary, header)) { + free_summary_file_header (header); + free_summary_file (summary); + tracker_error ("ERROR: failed to open summary header file %s", filename); + return; + } + + } else { + tracker_error ("ERROR: summary file not supported"); + free_summary_file_header (header); + free_summary_file (summary); + return; + + } + + path = tracker_db_email_get_mbox_path (db_con, filename); + tracker_db_email_set_message_counts (db_con, path, store->mail_count, header->junk_count, header->deleted_count); + g_free (path); + + free_summary_file_header (header); + free_summary_file (summary); + } +} + + + + +static GSList* +moredir (char *name, char *lastname, GSList *list) { + DIR *dir = opendir (name); + struct dirent *d; + + if (dir) { + while ( (d = readdir(dir)) ) { + struct stat st; + if (stat(name, &st) == 0 && S_ISDIR(st.st_mode)) { + char *tmp = g_build_filename(name, d->d_name, NULL); + list = moredir (tmp, name, list); + g_free (name); + } + + if ((strcmp (d->d_name, "summary.mmap") == 0) || g_str_has_suffix (d->d_name, "summary.mmap")) { + tracker_log ("Adding mail_dir: %s\n", lastname); + list = g_slist_prepend (list, g_strdup (lastname)); + + /* Question from Philip Van Hoof: + * I think we can return here, if a maildir's root + * has two summary files, for two subdirs, only + * monitoring the root once should suffice, I think + * + * Example FS structure: + * + * MyMailDirRoot <- Added to maildir_dirs twice (two summary files) + * +- MyFolder1 + * | +- cur <- Actual settled E-mails + * | +- new <- Recently created E-mails + * | `- tmp <- Being created E-mails + * | + * +- MyFolder2 <- Added to maildir_dirs once (one summary file) + * | +- MyFolder3.ev-summary.mmap <- to monitor + * | +- MyFolder3 + * | | +- cur + * | | +- new + * | | `- tmp + * | | + * | +- cur + * | +- new + * | `- tmp + * +- MyFolder1.ev-summary.mmap <- to monitor + * `- MyFolder2.ev-summary.mmap <- to monitor + */ + } + } + closedir(dir); + } + return list; +} + +static gboolean +load_modest_config (ModestConfig **conf) +{ + char *dir_imap, *dir_pop, *dir_maildir; + ModestConfig *m_conf; + + if (*conf) { + free_modest_config (*conf); + } + + *conf = g_slice_new0 (ModestConfig); + m_conf = *conf; + + m_conf->mail_dir = g_build_filename (g_get_home_dir (), MODEST_MAIL_DIR_S, NULL); + + + dir_imap = g_build_filename (m_conf->mail_dir, "imap", NULL); + dir_pop = g_build_filename (m_conf->mail_dir, "pop", NULL); + dir_maildir = g_strdup (MODEST_LOCAL_MAIL_DIR_S); + + tracker_log ("Checking for Modest email accounts..."); + + m_conf->imap_dirs = moredir (dir_imap, m_conf->mail_dir, m_conf->imap_dirs); + m_conf->pop_dirs = moredir (dir_pop, m_conf->mail_dir, m_conf->pop_dirs); + + /* TODO: Future support + * m_conf->maildir_dirs must be updated whenever an MMC card gets inserted */ + + m_conf->maildir_dirs = moredir (dir_maildir, dir_maildir, m_conf->maildir_dirs); + + g_free (dir_imap); + g_free (dir_pop); + g_free (dir_maildir); + + return TRUE; +} + + + +static gboolean +is_in_dir_imap (const gchar *path) +{ + g_return_val_if_fail (path, FALSE); + return strstr (path, G_DIR_SEPARATOR_S "imap") != NULL; +} + + +static gboolean +is_in_dir_maildir (const gchar *path) +{ + g_return_val_if_fail (path, FALSE); + return strstr (path, G_DIR_SEPARATOR_S "maildir") != NULL; +} + + +static gboolean +is_in_dir_pop (const gchar *path) +{ + g_return_val_if_fail (path, FALSE); + return strstr (path, G_DIR_SEPARATOR_S "pop" G_DIR_SEPARATOR_S) != NULL; +} + + +static void +index_mail_messages_by_summary_file (DBConnection *db_con, + MailType mail_type, + const gchar *summary_file_path, + LoadSummaryFileMetaHeaderFct load_meta_header, + LoadMailMessageFct load_mail, + SkipMailMessageFct skip_mail, + SaveOnDiskMailMessageFct save_ondisk_mail) +{ + SummaryFile *summary = NULL; + + if (!tracker->is_running) + return; + + if (open_summary_file (summary_file_path, &summary)) { + SummaryFileHeader *header; + gint32 mail_count, junk_count, delete_count; + gchar *dir; + + header = NULL; + + if (!load_summary_file_header (summary, &header)) { + free_summary_file (summary); + return; + } + + if (!(*load_meta_header) (summary, header)) { + free_summary_file_header (header); + free_summary_file (summary); + return; + } + + dir = g_path_get_dirname (summary->path); + + /* check summary file is registered */ + if (tracker_db_email_get_mbox_id (db_con, dir) == -1) { + const gchar *pos_folders = strstr (dir, G_DIR_SEPARATOR_S "folders" G_DIR_SEPARATOR_S); + + if (pos_folders) { + size_t len_pos_folders = strlen (pos_folders); + + if (len_pos_folders > 9) { /* strlen ("/folders/") == 9 */ + gchar *uri_dir, *clean_uri_dir, *uri_prefix; + + pos_folders += 9; + + uri_dir = NULL; + + /* we find relative summary file path from directory INBOX (or Sent, etc.). + It can be INBOX/, INBOX/sent-mail ,etc. */ + if (pos_folders) { + uri_dir = g_strdup (pos_folders); + } + + clean_uri_dir = NULL; + + if (uri_dir) { + gchar *tmp_str = tracker_string_replace (uri_dir, "subfolders/", NULL); + clean_uri_dir = tracker_string_replace (tmp_str, "folders/", NULL); + g_free (tmp_str); + } + +// TODO: +// if (clean_uri_dir) { +// uri_prefix = g_strdup_printf ("email://%s/%s;uid=", summary->associated_account->uid, clean_uri_dir); +// g_free (clean_uri_dir); +// } else { +// uri_prefix = g_strdup_printf ("email://%s;uid=", summary->associated_account->uid); +// } + + g_free (uri_dir); + + tracker_db_email_register_mbox (db_con, MAIL_APP_MODEST, mail_type, dir, summary_file_path, uri_prefix); + + g_free (uri_prefix); + } + } + } + + + MailStore *store = tracker_db_email_get_mbox_details (db_con, dir); + + if (!store) { + tracker_error ("ERROR: could not retrieve store for file %s", dir); + free_summary_file (summary); + free_summary_file_header (header); + + g_free (dir); + return; + } + + tracker_debug ("Number of existing messages in %s are %d, %d junk, %d deleted and header totals are %d, %d, %d", dir, + store->mail_count, store->junk_count, store->delete_count, header->saved_count, header->junk_count, header->deleted_count); + + tracker->mbox_count++; + tracker_dbus_send_index_progress_signal ("Emails", dir); + + if (header->saved_count > store->mail_count) { + /* assume new emails received */ + + gint i; + + /* skip already indexed emails */ + for (i = 0; i < store->mail_count; i++) { + if (!(*skip_mail) (summary)) { + tracker_error ("ERROR: skipping email no. %d in summary file", i+1); + tracker_db_email_free_mail_store (store); + free_summary_file (summary); + free_summary_file_header (header); + tracker->mbox_processed++; + g_free (dir); + return; + } + } + + mail_count = 0; + junk_count = 0; + delete_count = 0; + + /* now we will read the new emails */ + for (i = store->mail_count; i < header->saved_count; i++) { + MailMessage *mail_msg = NULL; + + mail_count++; + tracker_debug ("processing email no. %d / %" G_GINT32_FORMAT, store->mail_count + 1, header->saved_count); + + if (!(*load_mail) (summary, &mail_msg)) { + tracker_error ("ERROR: loading email no. %d in summary file", mail_count); + tracker_db_email_free_mail_store (store); + free_summary_file (summary); + free_summary_file_header (header); + tracker->mbox_processed++; + g_free (dir); + return; + } + + gchar *sum_file_dir = g_path_get_dirname (summary->path); + mail_msg->path = g_strdup_printf ("%s%s%d.", sum_file_dir, G_DIR_SEPARATOR_S, mail_msg->id); + g_free (sum_file_dir); + + gchar *str_id = tracker_int_to_str (mail_msg->id); + mail_msg->uri = g_strconcat (store->uri_prefix, str_id, NULL); + g_free (str_id); + + mail_msg->store = store; + + if (!(*save_ondisk_mail) (db_con, mail_msg)) { + tracker_log ("WARNING: Message, or message parts, could not be found locally - if you are using IMAP make sure you have selected the \"copy folder content locally for offline operation\" option in Modest"); + /* we do not have all infos but we still save them */ + if (!tracker_db_email_save_email (db_con, mail_msg)) { + tracker_log ("Failed to save email"); + } + } + + if (mail_msg->junk) { + junk_count++; + } + + if (mail_msg->deleted) { + delete_count++; + } + + email_free_mail_file (mail_msg->parent_mail_file); + email_free_mail_message (mail_msg); + + if (!tracker_cache_process_events (db_con->data, TRUE)) { + tracker->status = STATUS_SHUTDOWN; + tracker->shutdown = TRUE; + tracker_dbus_send_index_status_change_signal (); + return; + } + + if (tracker_db_regulate_transactions (db_con->data, 500)) { + + if (tracker->index_count % 1000 == 0) { + tracker_db_end_index_transaction (db_con->data); + tracker_db_refresh_all (db_con->data); + tracker_db_start_index_transaction (db_con->data); + } + + tracker_dbus_send_index_progress_signal ("Emails", dir); + + } + + + } + + tracker_log ("No. of new emails indexed in summary file %s is %d, %d junk, %d deleted", dir, mail_count, junk_count, delete_count); + + tracker_db_email_set_message_counts (db_con, dir, store->mail_count, store->junk_count, store->delete_count); + + } else { + /* schedule check for junk */ + tracker_db_email_flag_mbox_junk (db_con, dir); + } + + tracker->mbox_processed++; + tracker_dbus_send_index_progress_signal ("Emails", dir); + + tracker_db_email_free_mail_store (store); + free_summary_file (summary); + free_summary_file_header (header); + + g_free (dir); + } +} + + + + +static gboolean +open_summary_file (const gchar *path, SummaryFile **summary) +{ + gint fd; + FILE *f; + + g_return_val_if_fail (path, FALSE); + + if (!tracker_file_is_indexable (path)) { + return FALSE; + } + + if (*summary) { + free_summary_file (*summary); + *summary = NULL; + } + + fd = tracker_file_open (path, TRUE); + + if (fd == -1) { + return FALSE; + } + + f = fdopen (fd, "r"); + if (!f) { + tracker_file_close (fd, TRUE); + return FALSE; + } + + *summary = g_slice_new0 (SummaryFile); + (*summary)->f = f; + (*summary)->path = g_strdup (path); + + return TRUE; + +#if 0 + if (is_in_dir_local ((*summary)->path)) { + (*summary)->associated_account = NULL; + return TRUE; + } + + /* find associated Evo account */ + { + const GSList *account; + + for (account = modest_config->accounts; account; account = account->next) { + ModestAccount *evo_acc = account->data; + + switch (evo_acc->protocol) { + case MODEST_MAIL_PROTOCOL_MBOX: + + if (is_in_dir_local ((*summary)->path)) { + /* is it possible to have more than one MBox account? */ + (*summary)->associated_account = evo_acc; + goto loop_exit; + } + break; + + case MODEST_MAIL_PROTOCOL_IMAP: { + gchar *account_name = get_account_name_in_imap_path ((*summary)->path); + + if (!evo_acc->source_url || !account_name) { + continue; + } + + tracker_debug ("Account name for summary file is %s and path is %s", account_name, (*summary)->path); + + gchar *source_account_name = get_account_name_from_imap_uri (evo_acc->source_url); + + if (source_account_name) { + if (!strcmp (source_account_name, account_name)) { + (*summary)->associated_account = evo_acc; + g_free (source_account_name); + g_free (account_name); + goto loop_exit; + } + g_free (source_account_name); + } + g_free (account_name); + break; + } + + case MODEST_MAIL_PROTOCOL_MH: + case MODEST_MAIL_PROTOCOL_MAILDIR: { + gchar *sum_file_dir = g_path_get_dirname ((*summary)->path); + if (g_str_has_prefix (evo_acc->source_url, sum_file_dir)) { + (*summary)->associated_account = evo_acc; + g_free (sum_file_dir); + goto loop_exit; + } + g_free (sum_file_dir); + break; + } + + default: + tracker_error ("ERROR: no matching account found for email"); + continue; + break; + } + } + } + + loop_exit: + + if (!(*summary)->associated_account) { + free_summary_file (*summary); + *summary = NULL; + + return FALSE; + } else { + return TRUE; + } +#endif + +} + + +static void +free_summary_file (SummaryFile *summary) +{ + if (!summary) { + return; + } + + fclose (summary->f); + + g_free (summary->path); + g_slice_free (SummaryFile, summary); +} + + +static gboolean +load_summary_file_header (SummaryFile *summary, SummaryFileHeader **header) +{ + SummaryFileHeader *h; + FILE *f; + + g_return_val_if_fail (summary, FALSE); + + if (*header) { + free_summary_file_header (*header); + } + + *header = g_slice_new0 (SummaryFileHeader); + + h = *header; + + h->uri_prefix = NULL; + + f = summary->f; + + + if (!decode_gint32 (f, &h->version)) { + goto error; + } + + tracker_debug ("summary.version = %d", h->version); + + if (h->version > 0xff && (h->version & 0xff) < 12) { + tracker_error ("ERROR: summary file header version too low"); + goto error; + } + + h->legacy = !(h->version < 0x100 && h->version >= 13); + + if (h->legacy) { + tracker_debug ("WARNING: summary file is a legacy version"); + } + + + if (!decode_gint32 (f, &h->flags) || + !decode_gint32 (f, &h->nextuid) || + !decode_time_t (f, &h->time) || + !decode_gint32 (f, &h->saved_count)) { + goto error; + } + + tracker_debug ("summary.flags = %d", h->flags); + tracker_debug ("summary.nextuid = %d", h->nextuid); + tracker_debug ("summary.time = %d", h->time); + tracker_debug ("summary.count = %" G_GINT32_FORMAT, h->saved_count); + + if (!h->legacy) { + if (!decode_gint32 (f, &h->unread_count) || + !decode_gint32 (f, &h->deleted_count) || + !decode_gint32 (f, &h->junk_count)) { + goto error; + } + } + + tracker_debug ("summary.Unread = %d", h->unread_count); + tracker_debug ("summary.deleted = %d", h->deleted_count); + tracker_debug ("summary.junk = %d", h->junk_count); + + return TRUE; + + error: + free_summary_file_header (*header); + *header = NULL; + + return FALSE; +} + + +static void +free_summary_file_header (SummaryFileHeader *header) +{ + if (!header) { + return; + } + + if (header->uri_prefix) { + g_free (header->uri_prefix); + } + + g_slice_free (SummaryFileHeader, header); +} + + +static gboolean +load_summary_file_meta_header_generic (SummaryFile *summary, SummaryFileHeader *header) +{ + FILE *f; + + g_return_val_if_fail (summary, FALSE); + g_return_val_if_fail (header, FALSE); + + f = summary->f; + + /* check for legacy version */ + if (header->version != 0x30c) { + gint32 version; + + if (!decode_gint32 (f, &version)) { + return FALSE; + } + + if (version < 0) { + tracker_error ("ERROR: summary file version too low"); + return FALSE; + } + + /* Right now we only support summary versions 1 through 3 */ + if (version > 3) { + tracker_error ("ERROR: reported summary version (%" G_GINT32_FORMAT ") is too new", version); + return FALSE; + } + + if (version == 2) { + if (!skip_gint32_decoding (f)) { + return FALSE; + } + } + + } + + return TRUE; +} + +static gboolean +load_summary_file_meta_header_for_imap (SummaryFile *summary, SummaryFileHeader *header) +{ + FILE *f; + guint32 dummy0; + + g_return_val_if_fail (summary, FALSE); + g_return_val_if_fail (header, FALSE); + + f = summary->f; + + /* check for legacy version */ + if (header->version != 0x30c) { + gint32 version, dummy1; + + if (!decode_gint32 (f, &version)) { + return FALSE; + } + + if (version < 0) { + tracker_error ("ERROR: summary file version too low"); + return FALSE; + } + + /* Right now we only support summary versions 1 through 3 */ + if (version > 3) { + tracker_error ("ERROR: reported summary version (%" G_GINT32_FORMAT ") is too new", version); + return FALSE; + } + + if (version == 2) { + if (!skip_gint32_decoding (f)) { + return FALSE; + } + } + + /* validity */ + if (!decode_gint32 (f, &dummy1)) { + return FALSE; + } + } else { + /* validity */ + if (!decode_guint32 (f, &dummy0)) { + return FALSE; + } + } + + return TRUE; +} + + +static gboolean +load_summary_file_meta_header_for_pop (SummaryFile *summary, SummaryFileHeader *header) +{ + return load_summary_file_meta_header_generic (summary, header); +} + +static gboolean +load_summary_file_meta_header_for_maildir (SummaryFile *summary, SummaryFileHeader *header) +{ + return load_summary_file_meta_header_generic (summary, header); +} + + +static gboolean +load_mail_message_for_imap (SummaryFile *summary, MailMessage **mail_msg) +{ + return do_load_mail_message_for_imap (summary, mail_msg, TRUE); +} + +static gboolean +load_mail_message_for_maildir (SummaryFile *summary, MailMessage **mail_msg) +{ + return do_load_mail_message_for_maildir (summary, mail_msg, TRUE); +} + +static gboolean +load_mail_message_for_pop (SummaryFile *summary, MailMessage **mail_msg) +{ + return do_load_mail_message_for_pop (summary, mail_msg, TRUE); +} + + +static gboolean +do_load_mail_message_for_imap (SummaryFile *summary, MailMessage **mail_msg, gboolean do_skipping_of_content_info) +{ + guint32 server_flags; + + g_return_val_if_fail (summary, FALSE); + + if (*mail_msg) { + email_free_mail_message (*mail_msg); + } + + *mail_msg = email_allocate_mail_message (); + + if (!load_mail_message (summary, *mail_msg)) { + goto error; + } + + // This is the same in tny + if (!decode_guint32 (summary->f, &server_flags)) { + goto error; + } + + if (do_skipping_of_content_info) { + if (!skip_loading_content_info (summary)) { + goto error; + } + } + + return TRUE; + + error: + email_free_mail_message (*mail_msg); + *mail_msg = NULL; + return FALSE; +} + + + +static gboolean +do_load_mail_message_generic (SummaryFile *summary, MailMessage **mail_msg, gboolean do_skipping_of_content_info) +{ + g_return_val_if_fail (summary, FALSE); + + if (*mail_msg) { + email_free_mail_message (*mail_msg); + } + + *mail_msg = email_allocate_mail_message (); + + if (!load_mail_message (summary, *mail_msg)) { + goto error; + } + + if (do_skipping_of_content_info) { + if (!skip_loading_content_info (summary)) { + goto error; + } + } + + return TRUE; + + error: + email_free_mail_message (*mail_msg); + *mail_msg = NULL; + return FALSE; +} + + + +static gboolean +do_load_mail_message_for_pop (SummaryFile *summary, MailMessage **mail_msg, gboolean do_skipping_of_content_info) +{ + + return do_load_mail_message_generic (summary, mail_msg, do_skipping_of_content_info); +} + + + +static gboolean +do_load_mail_message_for_maildir (SummaryFile *summary, MailMessage **mail_msg, gboolean do_skipping_of_content_info) +{ + return do_load_mail_message_generic (summary, mail_msg, do_skipping_of_content_info); +} + +static gboolean +load_mail_message (SummaryFile *summary, MailMessage *mail_msg) +{ + FILE *f; + guint32 flags, size, count; + time_t date_sent, date_received; + gchar *uid, *to, *cc, *mlist; + + g_return_val_if_fail (summary, FALSE); + g_return_val_if_fail (mail_msg, FALSE); + + f = summary->f; + + if (!decode_string (f, &uid) || + !decode_guint32 (f, &size) || // Size and flags are reversed in tny + !decode_guint32 (f, &flags) || // Size and flags are reversed in tny + !decode_time_t (f, &date_sent) || + !decode_time_t (f, &date_received) || + !decode_string (f, &mail_msg->subject) || + !decode_string (f, &mail_msg->from) || + !decode_string (f, &to) || + !decode_string (f, &cc) || + !decode_string (f, &mlist)) // mlist will be an empty string in tny + { + + return FALSE; + } + + g_free (mlist); + + + mail_msg->id = strtoul (uid, NULL, 10); + + g_free (uid); + + if ((flags & MODEST_MESSAGE_DELETED) == MODEST_MESSAGE_DELETED) { + mail_msg->deleted = TRUE; + } + + if ((flags & MODEST_MESSAGE_JUNK) == MODEST_MESSAGE_JUNK) { + mail_msg->junk = TRUE; + } + + mail_msg->date = (long) date_received; + + mail_msg->to = add_persons_from_internet_address_list_string_parsing (NULL, to); + mail_msg->cc = add_persons_from_internet_address_list_string_parsing (NULL, cc); + + g_free (to); + g_free (cc); + + // Same in tny + skip_gint32_decoding (f); /* mi->message_id.id.part.hi */ + skip_gint32_decoding (f); /* mi->message_id.id.part.lo */ + + // Tny always has zero references currently, so count will be 0 + /* references */ + if (decode_guint32 (f, &count) && count <= 500) { + guint32 i; + for (i = 0; i < count; i++) { + skip_gint32_decoding (f); /* mi->references->references[i].id.part.hi */ + skip_gint32_decoding (f); /* mi->references->references[i].id.part.lo */ + } + } else { + return FALSE; + } + + // Tny always has zero flags currently, so count will be 0 + /* user flags */ + if (decode_guint32 (f, &count) && count <= 500) { + guint32 i; + for (i = 0; i < count; i++) { + skip_string_decoding (f); /* a flag */ + } + } else { + return FALSE; + } + + // Tny always has zero tags currently, so count will be 0 + /* user tags */ + if (decode_guint32 (f, &count) && count <= 500) { + guint32 i; + for (i = 0; i < count; i++) { + skip_string_decoding (f); /* tag name */ + skip_string_decoding (f); /* tag value */ + } + } else { + return FALSE; + } + + return TRUE; +} + + +static gboolean +skip_mail_message_for_imap (SummaryFile *summary) +{ + return do_skip_mail_message_for_imap (summary, TRUE); +} + + +static gboolean +skip_mail_message_for_pop (SummaryFile *summary) +{ + return do_skip_mail_message_for_pop (summary, TRUE); +} + +static gboolean +skip_mail_message_for_maildir (SummaryFile *summary) +{ + return do_skip_mail_message_for_maildir (summary, TRUE); +} + + +static gboolean +do_skip_mail_message_for_imap (SummaryFile *summary, gboolean do_skipping_of_content_info) +{ + g_return_val_if_fail (summary, FALSE); + + if (!skip_mail_message (summary)) { + return FALSE; + } + + if (!skip_guint32_decoding (summary->f)) { + return FALSE; + } + + if (do_skipping_of_content_info) { + if (!skip_loading_content_info (summary)) { + return FALSE; + } + } + + return TRUE; +} + + + +static gboolean +do_skip_mail_message_for_pop (SummaryFile *summary, gboolean do_skipping_of_content_info) +{ + g_return_val_if_fail (summary, FALSE); + + if (!skip_mail_message (summary)) { + return FALSE; + } + + if (!skip_guint32_decoding (summary->f)) { + return FALSE; + } + + if (do_skipping_of_content_info) { + if (!skip_loading_content_info (summary)) { + return FALSE; + } + } + + return TRUE; +} + + + +static gboolean +do_skip_mail_message_for_maildir (SummaryFile *summary, gboolean do_skipping_of_content_info) +{ + g_return_val_if_fail (summary, FALSE); + + if (!skip_mail_message (summary)) { + return FALSE; + } + + if (!skip_guint32_decoding (summary->f)) { + return FALSE; + } + + if (do_skipping_of_content_info) { + if (!skip_loading_content_info (summary)) { + return FALSE; + } + } + + return TRUE; +} + + + + +static gboolean +skip_mail_message (SummaryFile *summary) +{ + FILE *f; + guint32 count; + time_t tt; + guint n; + + g_return_val_if_fail (summary, FALSE); + + f = summary->f; + +/* if (!skip_over_mail (f)) { + return FALSE; + } +*/ + + // uid + if (!skip_string_decoding (f)) + return FALSE; + + // size and flags are switched in tny, but doesn't matter here + if (!decode_guint32 (f, &n)) + return FALSE; + + // size and flags are switched in tny, but doesn't matter here + if (!decode_guint32 (f, &n)) + return FALSE; + +// sent + if (!decode_time_t (f, &tt)) + return FALSE; + +// recv + if (!decode_time_t (f, &tt)) + return FALSE; + +// subj + if (!skip_string_decoding (f)) + return FALSE; + +// from + if (!skip_string_decoding (f)) + return FALSE; + +// to + if (!skip_string_decoding (f)) + return FALSE; + +// cc + if (!skip_string_decoding (f)) + return FALSE; + +// mlist? + if (!skip_string_decoding (f)) + return FALSE; + + + skip_gint32_decoding (f); /* mi->message_id.id.part.hi */ + skip_gint32_decoding (f); /* mi->message_id.id.part.lo */ + + /* references , always 0 items in tny */ + if (decode_guint32 (f, &count) && count <= 500) { + guint32 i; + for (i = 0; i < count; i++) { + skip_gint32_decoding (f); /* mi->references->references[i].id.part.hi */ + skip_gint32_decoding (f); /* mi->references->references[i].id.part.lo */ + } + } else { + return FALSE; + } + + /* user flags , always 0 items in tny */ + if (decode_guint32 (f, &count) && count <= 500) { + guint32 i; + for (i = 0; i < count; i++) { + skip_string_decoding (f); /* a flag */ + } + } else { + return FALSE; + } + + /* user tags , always 0 items in tny */ + if (decode_guint32 (f, &count) && count <= 500) { + guint32 i; + for (i = 0; i < count; i++) { + skip_string_decoding (f); /* tag name */ + skip_string_decoding (f); /* tag value */ + } + } else { + return FALSE; + } + + return TRUE; +} + + +static inline gboolean +skip_over_mail (FILE *f) +{ + return (skip_string_decoding (f) && /* uid */ + skip_guint32_decoding (f) && /* size switched with flags in tny */ + skip_guint32_decoding (f) && /* flags switched with size in tny */ + skip_time_t_decoding (f) && /* date_sent */ + skip_time_t_decoding (f) && /* date_received */ + skip_string_decoding (f) && /* subject */ + skip_string_decoding (f) && /* from */ + skip_string_decoding (f) && /* to */ + skip_string_decoding (f) && /* cc */ + skip_string_decoding (f)); /* mlist? */ +} + + +static gboolean +skip_loading_content_info (SummaryFile *summary) +{ + guint32 count, i; + + g_return_val_if_fail (summary, FALSE); + + if (!do_skip_loading_content_info (summary)) { + return FALSE; + } + + if (!decode_guint32 (summary->f, &count)) { + return FALSE; + } + + if (count > 500) { + return FALSE; + } + + for (i = 0; i < count; i++) { + if (!skip_loading_content_info (summary)) { + return FALSE; + } + } + + return TRUE; +} + + +static gboolean +do_skip_loading_content_info (SummaryFile *summary) +{ + FILE *f; + guint32 count; + guint32 doit = 0; + + g_return_val_if_fail (summary, FALSE); + + f = summary->f; + +// this is not a byte but an int in tinymail +// if (!fgetc (f)) { /* one byte to read... */ +// return TRUE; +// } + +// int replacement for tny + + if (!decode_guint32 (f, &doit)) + return FALSE; + + if (!doit) + return TRUE; +// -- + + skip_token_decoding (f); /* type */ + skip_token_decoding (f); /* subtype */ + + if (decode_guint32 (f, &count) && count <= 500) { + guint32 i; + for (i = 0; i < count; i++) { + skip_token_decoding (f); /* name */ + skip_token_decoding (f); /* value */ + } + } else { + return FALSE; + } + + if (!skip_token_decoding (f) || /* id */ + !skip_token_decoding (f) || /* description */ + !skip_token_decoding (f) || /* encoding */ + !decode_guint32 (f, &count)) { /* size */ + return FALSE; + } + + return TRUE; +} + + +static gboolean +save_ondisk_email_message_for_imap (DBConnection *db_con, MailMessage *mail_msg) +{ + return do_save_ondisk_email_message_generic (db_con, mail_msg); +} + + +static gboolean +save_ondisk_email_message_for_pop (DBConnection *db_con, MailMessage *mail_msg) +{ + return do_save_ondisk_email_message_generic (db_con, mail_msg); +} + +static gboolean +save_ondisk_email_message_for_maildir (DBConnection *db_con, MailMessage *mail_msg) +{ + return do_save_ondisk_email_message_generic (db_con, mail_msg); +} + +static gboolean +do_save_ondisk_email_message_generic (DBConnection *db_con, MailMessage *mail_msg) +{ + g_return_val_if_fail (db_con, FALSE); + g_return_val_if_fail (mail_msg, FALSE); + + tracker_log ("Trying to index mail \"%s\"", mail_msg->uri); + + if (!do_save_ondisk_email_message (db_con, mail_msg)) { + /* Mail not found... So two cases: + * 1/ mail naming uses this schema: + * - id.HEADER + * - id.1.MIME + * - id.1 <- contains a mail part + * - id.2.MIME + * - id.2 + * etc. + * But we may also have something like + * id.1.1.MIME, etc. + * 2/ mail do not exist on disk. + */ + gchar *header_file; + gboolean ret; + + ret = FALSE; + + tracker_log ("... Trying to index mail parts"); + + header_file = g_strconcat (mail_msg->path, "HEADER", NULL); + + if (tracker_file_is_indexable (header_file)) { + /* email is on disk */ + tracker_log ("... Indexing mail parts of email \"%s\"", mail_msg->uri); + ret = index_mail_parts (db_con, mail_msg, mail_msg->path); + tracker_log ("... Treatment of mail parts of \"%s\" finished", mail_msg->uri); + + if (ret) { + tracker_db_email_save_email (db_con, mail_msg); + } + } else { + tracker_log ("...Indexing of mail parts failed"); + } + + g_free (header_file); + + return ret; + + } else { + tracker_log ("Simple index of mail \"%s\" finished", mail_msg->uri); + return TRUE; + } +} + + +static gboolean +do_save_ondisk_email_message (DBConnection *db_con, MailMessage *mail_msg) +{ + g_return_val_if_fail (db_con, FALSE); + g_return_val_if_fail (mail_msg, FALSE); + g_return_val_if_fail (mail_msg->path, FALSE); + + if (tracker_file_is_indexable (mail_msg->path)) { + /* we have downloaded the mail message on disk so we can fully index it. */ + MailMessage *mail_msg_on_disk = NULL; + + mail_msg_on_disk = email_parse_mail_message_by_path (MAIL_APP_MODEST, + mail_msg->path, NULL, NULL); + + if (mail_msg_on_disk && mail_msg_on_disk->parent_mail_file) { + + mail_msg_on_disk->parent_mail_file->next_email_offset = 0; + mail_msg_on_disk->uri = g_strdup (mail_msg->uri); + mail_msg_on_disk->store = mail_msg->store; + + tracker_db_email_save_email (db_con, mail_msg_on_disk); + + email_free_mail_file (mail_msg_on_disk->parent_mail_file); + email_free_mail_message (mail_msg_on_disk); + + return TRUE; + } + } + + return FALSE; +} + + +static gboolean +index_mail_parts (DBConnection *db_con, MailMessage *mail_msg, const gchar *mail_part_radix) +{ + GQueue *mail_parts; + gint i, num_parts; + + typedef struct { + gchar *mail_file; + gchar *mime_file; + MimeInfos *mime_infos; + } MailPart; + + #define free_mail_part(x) \ + g_free (x->mail_file); \ + g_free (x->mime_file); \ + email_free_mime_infos (x->mime_infos); \ + g_slice_free (MailPart, x); + + g_return_val_if_fail (db_con, FALSE); + g_return_val_if_fail (mail_msg, FALSE); + g_return_val_if_fail (mail_part_radix, FALSE); + + mail_parts = g_queue_new (); + + /* makes first names of mail parts to index */ + for (i = 1; ; i++) { + gchar *str_i; + MailPart *m; + + str_i = tracker_int_to_str (i); + m = g_slice_new0 (MailPart); + m->mail_file = g_strconcat (mail_part_radix, str_i, NULL); + m->mime_file = g_strconcat (m->mail_file, ".MIME", NULL); + g_free (str_i); + + if (tracker_file_is_indexable (m->mime_file)) { + m->mime_infos = email_get_mime_infos_from_mime_file (m->mime_file); + if (! m->mime_infos) { + goto break_multipart_loop; + } + g_queue_push_tail (mail_parts, m); + } else { +break_multipart_loop: + g_free (m->mail_file); + g_free (m->mime_file); + g_slice_free (MailPart, m); + break; + } + } + + mail_msg->body = NULL; + + num_parts = 0; + + /* find mail parts in queue */ + while (!g_queue_is_empty (mail_parts)) { + MailPart *m; + + num_parts++; + + m = g_queue_pop_head (mail_parts); + + if (strcmp (m->mime_infos->type, "multipart") == 0) { + gint j; + + for (j = 1; ; j++) { + gchar *str_j; + MailPart *inner_m; + + str_j = tracker_int_to_str (j); + inner_m = g_slice_new0 (MailPart); + inner_m->mail_file = g_strconcat (m->mail_file, ".", str_j, NULL); + inner_m->mime_file = g_strconcat (inner_m->mail_file, ".MIME", NULL); + g_free (str_j); + + if (tracker_file_is_indexable (inner_m->mime_file)) { + inner_m->mime_infos = email_get_mime_infos_from_mime_file (inner_m->mime_file); + g_queue_push_tail (mail_parts, inner_m); + + } else { + g_free (inner_m->mail_file); + g_free (inner_m->mime_file); + g_slice_free (MailPart, inner_m); + break; + } + } + + } else{ + if (tracker_file_is_indexable (m->mail_file)) { + tracker_log ("Indexing message part \"%s\"", m->mail_file); + + if (num_parts == 1 && strcmp (m->mime_infos->type, "text") == 0) { + /* it is the first mail part so it is assume to be body mail */ + tracker_log ("Message part \"%s\" will be indexed as mail body...", m->mail_file); + g_file_get_contents (m->mail_file, &mail_msg->body, NULL, NULL); + + } else { + if (m->mime_infos) { + MailAttachment *ma; + gchar *tmp_filename; + + ma = email_allocate_mail_attachment (); + + ma->attachment_name = (m->mime_infos->name ? + g_strdup (m->mime_infos->name) : + g_path_get_basename (m->mail_file) /* this attachment has not a name... */ + ); + tmp_filename = (m->mime_infos->name ? + g_path_get_basename (m->mime_infos->name) : /* we only take end of name to avoid to give + access to anywhere on disk if the name + contains characters from a path... */ + g_strdup (ma->attachment_name) + ); + ma->mime = g_strconcat (m->mime_infos->type, "/", m->mime_infos->subtype, NULL); + ma->tmp_decoded_file = email_make_tmp_name_for_mail_attachment (tmp_filename); + g_free (tmp_filename); + + if (email_decode_mail_attachment_to_file (m->mail_file, ma->tmp_decoded_file, m->mime_infos->encoding)) { + email_add_saved_mail_attachment_to_mail_message (mail_msg, ma); + + } else { + tracker_error ("ERROR: failed to decode mail part \"%s\"", m->mail_file); + email_free_mail_attachment (ma); + } + } + } + } + } + + free_mail_part (m); + } + + #undef free_mail_part + + g_queue_free (mail_parts); + + return num_parts != 0; +} + + +static GSList * +add_persons_from_internet_address_list_string_parsing (GSList *list, const gchar *s) +{ + InternetAddressList *addrs_list, *tmp; + + g_return_val_if_fail (s, NULL); + + addrs_list = internet_address_parse_string (s); + + for (tmp = addrs_list; tmp; tmp = tmp->next) { + MailPerson *mp; + + mp = email_allocate_mail_person (); + + mp->addr = g_strdup (tmp->address->value.addr); + if(tmp->address->name) + mp->name = g_strdup (tmp->address->name); + else + mp->name = g_strdup (tmp->address->value.addr); + + list = g_slist_prepend (list, mp); + } + + internet_address_list_destroy (addrs_list); + + return list; +} + + + +/*** + Functions to decode summary (or ev-summary) files. +***/ + + +static inline gboolean +decode_string (FILE *in, gchar **str) +{ + guint32 len; + register gchar *ret; + + if (!decode_guint32 (in, &len)) { + *str = NULL; + return FALSE; + } + + /* I think I need to remove this for tinymail vs. evolution, + * because Tinymail's format cares about a last \0 character */ + + /* len--; */ + + if (len > 65536) { + *str = NULL; + return FALSE; + } + + ret = g_malloc (len+1); + if (len > 0 && fread (ret, len, 1, in) != 1) { + g_free (ret); + *str = NULL; + return FALSE; + } + + ret[len] = 0; + *str = ret; + return TRUE; +} + + +static inline gboolean +decode_gint32 (FILE *f, gint32 *dest) +{ + guint32 save; + + if (!f) return FALSE; + + if (fread (&save, sizeof (save), 1, f) == 1) { + *dest = g_ntohl (save); + return TRUE; + } else { + return FALSE; + } +} + +/* This is completely different in Tinymail (we just reuse the impl for signed) */ +static inline gboolean +decode_guint32 (FILE *f, guint32 *n) +{ + return decode_gint32 (f, (gint32*)n); +} + +/* These are all aliases for signed ints in tinymail's format */ + +#define CFU_DECODE_T(type) \ +static inline gboolean \ +decode_##type (FILE *in, type *dest) \ +{ \ + return decode_gint32 (in, (gint32*) dest); \ +} + +CFU_DECODE_T(time_t) +CFU_DECODE_T(off_t) +CFU_DECODE_T(size_t) + +static inline gboolean +skip_gint32_decoding (FILE *f) +{ + return fseek (f, 4, SEEK_CUR) > 0; +} + + +/* Same, we just reuse the impl. for signed numbers */ +static inline gboolean +skip_guint32_decoding (FILE *f) +{ + return skip_gint32_decoding (f); +} + +/* Same, we just reuse the impl. for signed numbers */ +static inline gboolean +skip_time_t_decoding (FILE *f) +{ + return skip_gint32_decoding (f); +} + +/* Same, we just reuse the impl. for signed numbers */ +static inline gboolean +skip_off_t_decoding (FILE *f) +{ + return skip_gint32_decoding (f); +} + + +static inline gboolean +skip_string_decoding (FILE *f) +{ + guint32 len; + + if (!decode_guint32 (f, &len)) { + return FALSE; + } + + /* Same as decode_string, Tinymail cares about the last \0 character + * whereas this character is not present in Evolution's format */ + + if (fseek (f, len /*- 1*/, SEEK_CUR) != 0) { + tracker_error ("ERROR: seek failed for string with length %d with error code %d", len - 1, errno); + return FALSE; + } + + return TRUE; +} + + +static inline gboolean +skip_token_decoding (FILE *f) +{ + guint32 len; + + if (!decode_guint32 (f, &len)) { + return FALSE; + } + + if (len < 32) { + return TRUE; + } + + len -= 32; + return fseek (f, len, SEEK_CUR) == 0; +} Index: src/trackerd/tracker-email-modest.h =================================================================== --- src/trackerd/tracker-email-modest.h (revision 0) +++ src/trackerd/tracker-email-modest.h (revision 0) @@ -0,0 +1 @@ + Index: src/trackerd/tracker-email-utils.h =================================================================== --- src/trackerd/tracker-email-utils.h (revision 1249) +++ src/trackerd/tracker-email-utils.h (working copy) @@ -31,6 +31,7 @@ MAIL_APP_KMAIL, MAIL_APP_THUNDERBIRD, MAIL_APP_THUNDERBIRD_FEED, + MAIL_APP_MODEST, MAIL_APP_UNKNOWN } MailApplication; @@ -41,7 +42,8 @@ MAIL_TYPE_IMAP, MAIL_TYPE_IMAP4, MAIL_TYPE_MAILDIR, - MAIL_TYPE_MH + MAIL_TYPE_MH, + MAIL_TYPE_POP, } MailType; Index: src/trackerd/Makefile.am =================================================================== --- src/trackerd/Makefile.am (revision 1249) +++ src/trackerd/Makefile.am (working copy) @@ -118,3 +118,25 @@ $(top_builddir)/src/libstemmer/libstemmer-private.la \ $(top_builddir)/src/xdgmime/libxdgmime.la \ $(trackerd_win_libs) + +mail_modules_LTLIBRARIES = \ + libemail-evolution.la \ + libemail-thunderbird.la \ + libemail-kmail.la \ + libemail-modest.la + +libemail_evolution_la_SOURCES = tracker-email-evolution.c +libemail_evolution_la_LDFLAGS = $(module_flags) +libemail_evolution_la_LIBADD = $(GLIB2_LIBS) + +libemail_modest_la_SOURCES = tracker-email-modest.c +libemail_modest_la_LDFLAGS = $(module_flags) +libemail_modest_la_LIBADD = $(GLIB2_LIBS) + +libemail_thunderbird_la_SOURCES = tracker-email-thunderbird.c +libemail_thunderbird_la_LDFLAGS = $(module_flags) +libemail_thunderbird_la_LIBADD = $(GLIB2_LIBS) + +libemail_kmail_la_SOURCES = tracker-email-kmail.c +libemail_kmail_la_LDFLAGS = $(module_flags) +libemail_kmail_la_LIBADD = $(GLIB2_LIBS) Index: src/libtracker-common/tracker-configuration.c =================================================================== --- src/libtracker-common/tracker-configuration.c (revision 1249) +++ src/libtracker-common/tracker-configuration.c (working copy) @@ -140,6 +140,8 @@ "[Emails]\n", "# Index email messages from Evolution\n", "IndexEvolutionEmails = true\n", + "# Index email messages from Modest\n", + "IndexModestEmails = false\n", "# Index email messages from Thunderbird\n", "IndexThunderbirdEmails = true\n\n", "[Performance]\n", Index: src/tracker-search-tool/tracker-search-tool-callbacks.c =================================================================== --- src/tracker-search-tool/tracker-search-tool-callbacks.c (revision 1249) +++ src/tracker-search-tool/tracker-search-tool-callbacks.c (working copy) @@ -603,6 +603,8 @@ if (gsearch->type == SERVICE_EMAILS) { if (strstr (mime, "Evolution")) { exec = g_strdup_printf ("evolution \"%s\"", uri); + } else if (strstr (mime, "Modest")) { + exec = g_strdup_printf ("modest-open \"%s\"", uri); } else if (strstr (mime, "KMail")) { exec = g_strdup_printf ("kmail --view \"%s\"", uri); } else if (strstr (mime, "Thunderbird")) { Index: src/tracker-preferences/tracker-preferences.glade =================================================================== --- src/tracker-preferences/tracker-preferences.glade (revision 1249) +++ src/tracker-preferences/tracker-preferences.glade (working copy) @@ -878,6 +878,19 @@ + + True + Enable _Modest email indexing + True + 0 + True + True + + + False + + + True False Index: src/tracker-preferences/tracker-preferences.c =================================================================== --- src/tracker-preferences/tracker-preferences.c (revision 1249) +++ src/tracker-preferences/tracker-preferences.c (working copy) @@ -468,6 +468,13 @@ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value); widget = glade_xml_get_widget (priv->gxml, + "chkEnableModestIndexing"); + value = tracker_configuration_get_boolean ("/Emails/IndexModestEmails", + NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), value); + + + widget = glade_xml_get_widget (priv->gxml, "chkEnableThunderbirdIndexing"); value = tracker_configuration_get_boolean ("/Emails/IndexThunderbirdEmails", NULL); @@ -750,6 +757,16 @@ tracker_configuration_set_boolean ("/Emails/IndexEvolutionEmails", bvalue); } + + widget = glade_xml_get_widget (priv->gxml, "chkEnableModestIndexing"); + bvalue = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); + bvalue_old = tracker_configuration_get_boolean ("/Emails/IndexModestEmails", + NULL); + if (bvalue != bvalue_old) { + set_bool_option (priv, "EnableModest", bvalue); + tracker_configuration_set_boolean ("/Emails/IndexModestEmails", bvalue); + } + widget = glade_xml_get_widget (priv->gxml, "chkEnableThunderbirdIndexing"); bvalue = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); bvalue_old = tracker_configuration_get_boolean ("/Emails/IndexThunderbirdEmails", Index: docs/tracker.cfg.5 =================================================================== --- docs/tracker.cfg.5 (revision 1249) +++ docs/tracker.cfg.5 (working copy) @@ -163,6 +163,9 @@ IndexEvolutionEmails=BOOLEAN Enable or disable indexing for Evolution emails. .TP +IndexModestEmails=BOOLEAN +Enable or disable indexing for Modest emails. +.TP IndexThunderbirdEmails=BOOLEAN Enable or disable indexing for Thunderbird emails. Index: data/services/default.service =================================================================== --- data/services/default.service (revision 1249) +++ data/services/default.service (working copy) @@ -170,6 +170,18 @@ TileMetadata=Email:Sender;Email:Subject;Email:Date;Email:SentTo;Email:CC;Email:Attachments ContentMetadata=Email:Body +[ModestEmails] +DisplayName=Modest Emails +Description=Modest based emails +Parent=Emails +ViewerExec=modest-open "%1" +KeyMetadata1=Email:Subject +KeyMetadata2=Email:Sender +KeyMetadata3=Email:Date +TabularMetadata=Email:Sender;Email:Subject;Email:Date; +TileMetadata=Email:Sender;Email:Subject;Email:Date;Email:SentTo;Email:CC;Email:Attachments +ContentMetadata=Email:Body + [ThunderbirdEmails] DisplayName=Thunderbird Emails Description=Thunderbird based emails @@ -206,6 +218,12 @@ Parent=EmailAttachments Icon=stock_attach +[ModestAttachments] +DisplayName=Modest Email Attachments +Description=All files that are attached to an Modest Email +Parent=EmailAttachments +Icon=stock_attach + [KMailAttachments] DisplayName=KMail Email Attachments Description=All files that are attached to an KMail Email