1 # pragma once 2 3 # include <mutex> 4 # include <condition_variable> 5 # include <atomic> 6 # include <functional> 7 8 # include <vector> 9 10 # include <time.h> 11 12 # include <glibmm.h> 13 # include <notmuch.h> 14 15 # include "astroid.hh" 16 # include "config.hh" 17 # include "proto.hh" 18 19 /* there was a bit of a round-dance of with the _st versions of these returning 20 * to the old name, but with different signature */ 21 # if (LIBNOTMUCH_MAJOR_VERSION < 5) 22 # define notmuch_query_search_threads(x,y) notmuch_query_search_threads_st(x,y) 23 # define notmuch_query_count_threads(x,y) notmuch_query_count_threads_st(x,y) 24 # define notmuch_query_search_messages(x,y) notmuch_query_search_messages_st(x,y) 25 # define notmuch_query_count_messages(x,y) notmuch_query_count_messages_st(x,y) 26 # endif 27 28 # ifndef HAVE_NOTMUCH_INDEX_FILE 29 # define notmuch_database_index_file(d,f,o,m) notmuch_database_add_message(d,f,m) 30 # endif 31 32 namespace Astroid { 33 class NotmuchItem : public Glib::Object { 34 public: 35 ustring thread_id; 36 ustring subject; 37 38 bool unread; 39 bool attachment; 40 bool flagged; 41 42 virtual bool refresh (Db *) = 0; 43 44 std::vector<ustring> tags; 45 bool has_tag (ustring); 46 47 virtual bool remove_tag (Db *, ustring) = 0; 48 virtual bool add_tag (Db *, ustring) = 0; 49 50 virtual void emit_updated (Db *) = 0; 51 52 virtual ustring str () = 0; 53 virtual bool matches (std::vector<ustring> &k) = 0; 54 virtual bool in_query (Db *, ustring) = 0; 55 }; 56 57 /* the notmuch message object should get by on the db only */ 58 class NotmuchMessage : public NotmuchItem { 59 public: 60 NotmuchMessage (notmuch_message_t *); 61 NotmuchMessage (refptr<Message>); 62 63 ustring mid; 64 ustring sender = ""; 65 time_t time; 66 ustring filename = ""; 67 68 void load (notmuch_message_t *); 69 bool refresh (Db *) override; 70 void refresh (notmuch_message_t *); 71 72 bool remove_tag (Db *, ustring) override; 73 bool add_tag (Db *, ustring) override; 74 void emit_updated (Db *) override; 75 76 ustring str () override; 77 bool matches (std::vector<ustring> &k) override; 78 bool in_query (Db *, ustring) override; 79 80 private: 81 std::vector<ustring> get_tags (notmuch_message_t *); 82 83 ustring index_str = ""; 84 }; 85 86 /* the notmuch thread object should get by on the db only */ 87 class NotmuchThread : public NotmuchItem { 88 public: 89 NotmuchThread (notmuch_thread_t *); 90 ~NotmuchThread (); 91 92 time_t newest_date; 93 time_t oldest_date; 94 int total_messages; 95 std::vector<std::tuple<ustring,bool>> authors; 96 97 void load (notmuch_thread_t *); 98 bool refresh (Db *) override; 99 100 bool remove_tag (Db *, ustring) override; 101 bool add_tag (Db *, ustring) override; 102 void emit_updated (Db *) override; 103 104 ustring str () override; 105 bool matches (std::vector<ustring> &k) override; 106 bool in_query (Db *, ustring) override; 107 108 std::vector<std::pair<int, refptr<NotmuchMessage>>> messages (Db *); 109 110 private: 111 int check_total_messages (notmuch_thread_t *); 112 std::vector<std::tuple<ustring,bool>> get_authors (notmuch_thread_t *); 113 std::vector<ustring> get_tags (notmuch_thread_t *); 114 115 ustring index_str = ""; 116 }; 117 118 class Db { 119 public: 120 enum DbMode { 121 DATABASE_READ_ONLY, 122 DATABASE_READ_WRITE, 123 }; 124 125 Db (DbMode = DATABASE_READ_ONLY); 126 ~Db (); 127 void close (); 128 129 void on_thread (ustring, std::function <void(notmuch_thread_t *)>); 130 void on_message (ustring, std::function <void(notmuch_message_t *)>); 131 132 bool thread_in_query (ustring, ustring); 133 bool message_in_query (ustring, ustring); 134 135 unsigned long get_revision (); 136 137 notmuch_database_t * nm_db; 138 139 static std::vector<ustring> tags; 140 141 void load_tags (); 142 143 static std::vector<ustring> sent_tags; 144 static std::vector<ustring> draft_tags; 145 static std::vector<ustring> excluded_tags; 146 147 ustring add_sent_message (ustring, std::vector<ustring>, ustring); 148 ustring add_draft_message (ustring); 149 ustring add_message_with_tags (ustring fname, std::vector<ustring> tags); 150 bool remove_message (ustring); 151 152 static ustring sanitize_tag (ustring); 153 static bool check_tag (ustring); 154 155 /* lock db: use if you need the db in external program and need 156 * a specific lock */ 157 static std::unique_lock<std::mutex> acquire_rw_lock (); 158 static void release_rw_lock (std::unique_lock<std::mutex> &); 159 160 static void acquire_ro_lock (); 161 static void release_ro_lock (); 162 163 static bool maildir_synchronize_flags; 164 static void init (); 165 static bfs::path path_db; 166 167 private: 168 /* 169 * + We can have as many read-only db's open as we want. 170 * 171 * + There can only be one read-write db open at the time. 172 * 173 * + It is not possible to have read-only db's open when there is a 174 * read-write db open. 175 * 176 * If you open one read-only db, and try to open a read-write db in the 177 * same thread without closing the read-only db there will be a deadlock. 178 * 179 */ 180 181 /* number of open read-only dbs, when 0 a read-write can be opened */ 182 static std::atomic<int> read_only_dbs_open; 183 184 /* synchronizes openings, read-write dbs will lock this for 185 * its entire lifespan. read-only, only locks it while incrementing 186 * the read_only_dbs_open */ 187 static std::mutex db_open; 188 189 /* notify when read_only_dbs change */ 190 static std::condition_variable dbs_open; 191 std::unique_lock<std::mutex> rw_lock; 192 193 DbMode mode; 194 195 bool open_db_write (bool); 196 bool open_db_read_only (bool); 197 bool closed = false; 198 199 const int db_open_timeout = 120; // seconds 200 const int db_open_delay = 100; // milliseconds 201 202 }; 203 204 /* exceptions */ 205 class database_error : public std::runtime_error { 206 public: 207 database_error (const char *); 208 209 }; 210 } 211 212