From deeaf41f33cc0931dacff0f47d8c943ccaad0a3b Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 2 Mar 2011 00:13:24 +0200 Subject: [PATCH] * WIP: contact listing / searching. this is the first part, adding a cache for all the contacts in mails (~/.mu/cache/contacts), which is a .ini-type file. --- TODO | 3 ++ src/Makefile.am | 2 + src/mu-cmd-index.c | 4 +- src/mu-index.c | 4 +- src/mu-index.h | 4 +- src/mu-runtime.c | 111 +++++++++++++++++++++++++------------- src/mu-runtime.h | 10 ++++ src/mu-store.cc | 41 +++++++++----- src/mu-store.h | 6 +-- src/tests/test-mu-cmd.c | 2 +- src/tests/test-mu-store.c | 8 +-- 11 files changed, 134 insertions(+), 61 deletions(-) diff --git a/TODO b/TODO index 0292691b..31b3c7b5 100644 --- a/TODO +++ b/TODO @@ -14,6 +14,9 @@ - [ ] better 'usage' info +** release 0.9.4 + - [ ] add 'mu cfind' to find contacts + * Releases already done ** release 0.9.3 [100%] diff --git a/src/Makefile.am b/src/Makefile.am index 81dced40..fa190ca3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -55,6 +55,8 @@ libmu_la_SOURCES= \ mu-cmd.h \ mu-config.c \ mu-config.h \ + mu-contacts.h \ + mu-contacts.c \ mu-index.c \ mu-index.h \ mu-log.c \ diff --git a/src/mu-cmd-index.c b/src/mu-cmd-index.c index 99c2dff3..dfd05b71 100644 --- a/src/mu-cmd-index.c +++ b/src/mu-cmd-index.c @@ -351,7 +351,9 @@ cmd_index_or_cleanup (MuConfig *opts) return MU_EXITCODE_ERROR; err = NULL; - if (!(midx = mu_index_new (mu_runtime_xapian_dir(), &err))) + if (!(midx = mu_index_new (mu_runtime_xapian_dir(), + mu_runtime_contacts_cache_file(), + &err))) return handle_index_error_and_free (err); mu_index_set_max_msg_size (midx, opts->max_msg_size); diff --git a/src/mu-index.c b/src/mu-index.c index 312c7e81..ab8013dc 100644 --- a/src/mu-index.c +++ b/src/mu-index.c @@ -45,7 +45,7 @@ struct _MuIndex { }; MuIndex* -mu_index_new (const char *xpath, GError **err) +mu_index_new (const char *xpath, const char* contacts_cache, GError **err) { MuIndex *index; @@ -53,7 +53,7 @@ mu_index_new (const char *xpath, GError **err) index = g_new0 (MuIndex, 1); - index->_store = mu_store_new (xpath, err); + index->_store = mu_store_new (xpath, contacts_cache, err); if (!index->_store) { g_warning ("%s: failed to open xapian store (%s)", __FUNCTION__, xpath); diff --git a/src/mu-index.h b/src/mu-index.h index c0c090ef..a7085f32 100644 --- a/src/mu-index.h +++ b/src/mu-index.h @@ -46,12 +46,14 @@ typedef struct _MuIndexStats MuIndexStats; * * @param xpath path to the 'homedir'; the xapian directory will be * this homedir/xapian + * @param contacts_cache file to store the cache of contacts, or NULL * @param err to receive error or NULL; there are only errors when this * function returns NULL. Possible errors: see mu-error.h * * @return a new MuIndex instance, or NULL in case of error */ -MuIndex* mu_index_new (const char* muhome, GError **err) +MuIndex* mu_index_new (const char* muhome, const char* contacts_cache, + GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; diff --git a/src/mu-runtime.c b/src/mu-runtime.c index bc9f8390..376bee29 100644 --- a/src/mu-runtime.c +++ b/src/mu-runtime.c @@ -32,14 +32,23 @@ #include "mu-log.h" #include "mu-util.h" -#define MU_XAPIAN_DIRNAME "xapian" -#define MU_BOOKMARKS_FILENAME "bookmarks" +enum { + MU_RUNTIME_STR_MU_HOMEPATH, + MU_RUNTIME_STR_XAPIAN_PATH, + MU_RUNTIME_STR_BOOKMARKS_PATH, + MU_RUNTIME_STR_CACHE_PATH, + MU_RUNTIME_STR_CONTACTS_PATH, + MU_RUNTIME_STR_NUM +}; + +#define MU_XAPIAN_DIRNAME "xapian" +#define MU_BOOKMARKS_FILENAME "bookmarks" +#define MU_CACHE_DIRNAME "cache" +#define MU_CONTACTS_FILENAME "contacts" struct _MuRuntimeData { - gchar *_muhome; - gchar *_xapian_dir; - gchar *_bookmarks_file; - MuConfig *_config; + gchar *_str[MU_RUNTIME_STR_NUM]; + MuConfig *_config; }; typedef struct _MuRuntimeData MuRuntimeData; @@ -50,7 +59,7 @@ static MuRuntimeData *_data = NULL; static void runtime_free (void); static gboolean -mu_dir_is_readable_and_writable (const char* muhome) +mu_dir_is_readable_and_writable (const char *muhome) { if (mu_util_create_dir_maybe (muhome, 0700)) return TRUE; @@ -61,6 +70,39 @@ mu_dir_is_readable_and_writable (const char* muhome) return FALSE; } +static gboolean +init_paths (const char* muhome, MuRuntimeData *data) +{ + data->_str [MU_RUNTIME_STR_XAPIAN_PATH] = + g_strdup_printf ("%s%c%s", muhome, + G_DIR_SEPARATOR, + MU_XAPIAN_DIRNAME); + + data->_str [MU_RUNTIME_STR_BOOKMARKS_PATH] = + g_strdup_printf ("%s%c%s", muhome, + G_DIR_SEPARATOR, + MU_BOOKMARKS_FILENAME); + + data->_str [MU_RUNTIME_STR_CACHE_PATH] = + g_strdup_printf ("%s%c%s", muhome, + G_DIR_SEPARATOR, + MU_CACHE_DIRNAME); + + if (!mu_util_create_dir_maybe + (_data->_str[MU_RUNTIME_STR_CACHE_PATH], 0700)) { + g_warning ("failed to create cache dir"); + return FALSE; + } + + data->_str [MU_RUNTIME_STR_CONTACTS_PATH] = + g_strdup_printf ("%s%c%s", + data->_str[MU_RUNTIME_STR_CACHE_PATH], + G_DIR_SEPARATOR, + MU_CONTACTS_FILENAME); + return TRUE; +} + + gboolean mu_runtime_init (const char* muhome_arg) { @@ -87,8 +129,9 @@ mu_runtime_init (const char* muhome_arg) } _data = g_new0 (MuRuntimeData, 1); - _data->_muhome = muhome; - + _data->_str[MU_RUNTIME_STR_MU_HOMEPATH] = muhome; + init_paths (muhome, _data); + mu_msg_gmime_init (); return _initialized = TRUE; @@ -131,8 +174,10 @@ mu_runtime_init_from_cmdline (int *pargc, char ***pargv) return FALSE; } - _data->_muhome = g_strdup (_data->_config->muhome); - + _data->_str[MU_RUNTIME_STR_MU_HOMEPATH] = + g_strdup (_data->_config->muhome); + init_paths (_data->_str[MU_RUNTIME_STR_MU_HOMEPATH], _data); + mu_msg_gmime_init (); return _initialized = TRUE; @@ -142,9 +187,10 @@ mu_runtime_init_from_cmdline (int *pargc, char ***pargv) static void runtime_free (void) { - g_free (_data->_xapian_dir); - g_free (_data->_muhome); - g_free (_data->_bookmarks_file); + int i; + + for (i = 0; i != MU_RUNTIME_STR_NUM; ++i) + g_free (_data->_str[i]); mu_config_destroy (_data->_config); @@ -158,8 +204,7 @@ mu_runtime_uninit (void) { g_return_if_fail (_initialized); - mu_msg_gmime_uninit (); - + mu_msg_gmime_uninit (); runtime_free (); _initialized = FALSE; @@ -170,43 +215,37 @@ const char* mu_runtime_mu_home_dir (void) { g_return_val_if_fail (_initialized, NULL); - - return _data->_muhome; + return _data->_str[MU_RUNTIME_STR_MU_HOMEPATH]; } + const char* mu_runtime_xapian_dir (void) { g_return_val_if_fail (_initialized, NULL); - - if (!_data->_xapian_dir) - _data->_xapian_dir = g_strdup_printf ("%s%c%s", - _data->_muhome, - G_DIR_SEPARATOR, - MU_XAPIAN_DIRNAME); - return _data->_xapian_dir; + return _data->_str[MU_RUNTIME_STR_XAPIAN_PATH]; } const char* mu_runtime_bookmarks_file (void) { g_return_val_if_fail (_initialized, NULL); - - if (!_data->_bookmarks_file) - _data->_bookmarks_file = - g_strdup_printf ("%s%c%s", - _data->_muhome, - G_DIR_SEPARATOR, - MU_BOOKMARKS_FILENAME); - - return _data->_bookmarks_file; + return _data->_str[MU_RUNTIME_STR_BOOKMARKS_PATH]; } +const char* +mu_runtime_contacts_cache_file (void) +{ + g_return_val_if_fail (_initialized, NULL); + return _data->_str[MU_RUNTIME_STR_CONTACTS_PATH]; +} + + + MuConfig* mu_runtime_config (void) { - g_return_val_if_fail (_initialized, NULL); - + g_return_val_if_fail (_initialized, NULL); return _data->_config; } diff --git a/src/mu-runtime.h b/src/mu-runtime.h index 9f5f011f..e298d348 100644 --- a/src/mu-runtime.h +++ b/src/mu-runtime.h @@ -83,6 +83,16 @@ const char* mu_runtime_xapian_dir (void); */ const char* mu_runtime_bookmarks_file (void); +/** + * get the mu contacts cache file name (typically, + * ~/.mu/contacts.cache); this can only be called after + * mu_runtime_init and before mu_runtime_uninit + * + * @return the contacts cache file name as a string which should be not be + * modified, or NULL in case of error. + */ +const char* mu_runtime_contacts_cache_file (void); + /** * get the mu configuration options (ie., the parsed command line diff --git a/src/mu-store.cc b/src/mu-store.cc index ad555846..58be103e 100644 --- a/src/mu-store.cc +++ b/src/mu-store.cc @@ -31,7 +31,7 @@ #include "mu-util.h" #include "mu-str.h" #include "mu-msg-flags.h" - +#include "mu-contacts.h" /* by default, use transactions of 30000 messages */ #define MU_STORE_DEFAULT_TRX_SIZE 30000 @@ -42,6 +42,9 @@ struct _MuStore { Xapian::WritableDatabase *_db; + /* contacts object to cache all the contact information */ + MuContacts *_contacts; + char *_version; /* transaction handling */ @@ -119,12 +122,12 @@ check_version (MuStore *store) } MuStore* -mu_store_new (const char* xpath, GError **err) +mu_store_new (const char* xpath, const char *contacts_cache, + GError **err) { MuStore *store (0); g_return_val_if_fail (xpath, NULL); - try { store = g_new0(MuStore,1); @@ -136,6 +139,12 @@ mu_store_new (const char* xpath, GError **err) return NULL; } + if (contacts_cache) { + store->_contacts = mu_contacts_new (contacts_cache); + if (!store->_contacts) /* don't bail-out for this */ + g_warning ("failed to init contacts cache"); + } + /* keep count of processed docs */ store->_in_transaction = false; store->_processed = 0; @@ -169,6 +178,8 @@ mu_store_destroy (MuStore *store) MU_WRITE_LOG ("closing xapian database with %d documents", (int)store->_db->get_doccount()); + mu_contacts_destroy (store->_contacts); + g_free (store->_version); delete store->_db; g_free (store); @@ -403,6 +414,7 @@ add_terms_values_body (Xapian::Document& doc, MuMsg *msg, struct _MsgDoc { Xapian::Document *_doc; MuMsg *_msg; + MuStore *_store; }; typedef struct _MsgDoc MsgDoc; @@ -438,10 +450,9 @@ add_terms_values (MuMsgFieldId mfid, MsgDoc* msgdoc) static void -each_contact_info (MuMsgContact *contact, MsgDoc *data) +each_contact_info (MuMsgContact *contact, MsgDoc *msgdoc) { const std::string *pfxp; - static const std::string to_pfx (1, mu_msg_field_xapian_prefix(MU_MSG_FIELD_ID_TO)); static const std::string from_pfx (1, @@ -454,13 +465,12 @@ each_contact_info (MuMsgContact *contact, MsgDoc *data) case MU_MSG_CONTACT_TYPE_TO: pfxp = &to_pfx; break; case MU_MSG_CONTACT_TYPE_FROM: pfxp = &from_pfx; break; case MU_MSG_CONTACT_TYPE_CC: pfxp = &cc_pfx; break; - default: return; - /* other types (like bcc) are ignored */ + default: return; /* other types (like bcc) are ignored */ } if (contact->name && strlen(contact->name) > 0) { Xapian::TermGenerator termgen; - termgen.set_document (*data->_doc); + termgen.set_document (*msgdoc->_doc); char *norm = mu_str_normalize (contact->name, TRUE); termgen.index_text_without_positions (norm, 1, *pfxp); g_free (norm); @@ -468,11 +478,16 @@ each_contact_info (MuMsgContact *contact, MsgDoc *data) /* don't normalize e-mail address, but do lowercase it */ if (contact->address && contact->address[0] != '\0') { - char *escaped = - mu_str_ascii_xapian_escape (contact->address); - data->_doc->add_term + char *escaped = mu_str_ascii_xapian_escape (contact->address); + msgdoc->_doc->add_term (std::string (*pfxp + escaped, 0, MU_STORE_MAX_TERM_LENGTH)); g_free (escaped); + + /* store it also in our contacts cache */ + if (msgdoc->_store->_contacts) + mu_contacts_add (msgdoc->_store->_contacts, + contact->name, contact->address, + mu_msg_get_date(msgdoc->_msg)); } } @@ -502,9 +517,9 @@ mu_store_store (MuStore *store, MuMsg *msg) try { Xapian::Document newdoc; Xapian::docid id; - MsgDoc msgdoc = { &newdoc, msg }; + MsgDoc msgdoc = { &newdoc, msg, store }; const std::string uid(get_message_uid(msg)); - + begin_trx_if (store, !store->_in_transaction); /* we must add a unique term, so we can replace * matching documents */ diff --git a/src/mu-store.h b/src/mu-store.h index 53069b30..20f6ca1b 100644 --- a/src/mu-store.h +++ b/src/mu-store.h @@ -35,14 +35,14 @@ typedef struct _MuStore MuStore; * create a new Xapian store, a place to store documents * * @param path the path to the database - * @param batchsize size of batch before committing + * @param ccachepath path where to cache the contacts information, or NULL * @param err to receive error info or NULL. err->code can be found in * mu-error.h * * @return a new MuStore object, or NULL in case of error */ -MuStore* mu_store_new (const char *path, GError **err) - G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; +MuStore* mu_store_new (const char *xpath, const char *ccachepath, + GError **err) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** diff --git a/src/tests/test-mu-cmd.c b/src/tests/test-mu-cmd.c index b7b45a46..44de2467 100644 --- a/src/tests/test-mu-cmd.c +++ b/src/tests/test-mu-cmd.c @@ -110,7 +110,7 @@ test_mu_index (void) xpath = g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, "xapian"); - store = mu_store_new (xpath, NULL); + store = mu_store_new (xpath, NULL, NULL); g_assert (store); g_assert_cmpuint (mu_store_count (store), ==, 5); diff --git a/src/tests/test-mu-store.c b/src/tests/test-mu-store.c index 4329dae9..836d8f43 100644 --- a/src/tests/test-mu-store.c +++ b/src/tests/test-mu-store.c @@ -42,7 +42,7 @@ test_mu_store_new_destroy (void) g_assert (tmpdir); err = NULL; - store = mu_store_new (tmpdir, &err); + store = mu_store_new (tmpdir, NULL, &err); g_assert (store); g_assert (err == NULL); @@ -67,7 +67,7 @@ test_mu_store_version (void) g_assert (tmpdir); err = NULL; - store = mu_store_new (tmpdir, &err); + store = mu_store_new (tmpdir, NULL, &err); g_assert (store); g_assert (err == NULL); @@ -90,7 +90,7 @@ test_mu_store_store_and_count (void) tmpdir = test_mu_common_get_random_tmpdir(); g_assert (tmpdir); - store = mu_store_new (tmpdir, NULL); + store = mu_store_new (tmpdir, NULL, NULL); g_assert (store); g_assert_cmpuint (0,==,mu_store_count (store)); @@ -138,7 +138,7 @@ test_mu_store_store_remove_and_count (void) tmpdir = test_mu_common_get_random_tmpdir(); g_assert (tmpdir); - store = mu_store_new (tmpdir, NULL); + store = mu_store_new (tmpdir, NULL, NULL); g_assert (store); g_assert_cmpuint (0,==,mu_store_count (store));