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