_thread_add_messages: Remove unused variable.
[notmuch] / lib / thread.cc
1 /* thread.cc - Results of thread-based searches from a notmuch database
2  *
3  * Copyright © 2009 Carl Worth
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see http://www.gnu.org/licenses/ .
17  *
18  * Author: Carl Worth <cworth@cworth.org>
19  */
20
21 #include "notmuch-private.h"
22 #include "database-private.h"
23
24 #include <xapian.h>
25
26 #include <gmime/gmime.h>
27 #include <glib.h> /* GHashTable */
28
29 struct _notmuch_thread {
30     notmuch_database_t *notmuch;
31     char *thread_id;
32     char *subject;
33     GHashTable *authors_hash;
34     char *authors;
35     GHashTable *tags;
36
37     int total_messages;
38     int matched_messages;
39     time_t oldest;
40     time_t newest;
41 };
42
43 static int
44 _notmuch_thread_destructor (notmuch_thread_t *thread)
45 {
46     g_hash_table_unref (thread->authors_hash);
47     g_hash_table_unref (thread->tags);
48
49     return 0;
50 }
51
52 static void
53 _thread_add_author (notmuch_thread_t *thread,
54                     const char *author)
55 {
56     if (author == NULL)
57         return;
58
59     if (g_hash_table_lookup_extended (thread->authors_hash,
60                                       author, NULL, NULL))
61         return;
62
63     g_hash_table_insert (thread->authors_hash, xstrdup (author), NULL);
64
65     if (thread->authors)
66         thread->authors = talloc_asprintf (thread, "%s, %s",
67                                            thread->authors,
68                                            author);
69     else
70         thread->authors = talloc_strdup (thread, author);
71 }
72
73 static void
74 _thread_add_message (notmuch_thread_t *thread,
75                      notmuch_message_t *message)
76 {
77     notmuch_tags_t *tags;
78     const char *tag;
79     InternetAddressList *list;
80     InternetAddress *address;
81     const char *from, *author;
82
83     from = notmuch_message_get_header (message, "from");
84     list = internet_address_list_parse_string (from);
85     if (list) {
86         address = internet_address_list_get_address (list, 0);
87         if (address) {
88             author = internet_address_get_name (address);
89             if (author == NULL) {
90                 InternetAddressMailbox *mailbox;
91                 mailbox = INTERNET_ADDRESS_MAILBOX (address);
92                 author = internet_address_mailbox_get_addr (mailbox);
93             }
94             _thread_add_author (thread, author);
95         }
96         g_object_unref (G_OBJECT (list));
97     }
98
99     if (! thread->subject) {
100         const char *subject;
101         subject = notmuch_message_get_header (message, "subject");
102         thread->subject = talloc_strdup (thread, subject);
103     }
104
105     for (tags = notmuch_message_get_tags (message);
106          notmuch_tags_has_more (tags);
107          notmuch_tags_advance (tags))
108     {
109         tag = notmuch_tags_get (tags);
110         g_hash_table_insert (thread->tags, xstrdup (tag), NULL);
111     }
112
113     thread->total_messages++;
114 }
115
116 static void
117 _thread_add_matched_message (notmuch_thread_t *thread,
118                              notmuch_message_t *message)
119 {
120     time_t date;
121
122     date = notmuch_message_get_date (message);
123
124     if (date < thread->oldest || ! thread->matched_messages)
125         thread->oldest = date;
126
127     if (date > thread->newest || ! thread->matched_messages)
128         thread->newest = date;
129
130     thread->matched_messages++;
131 }
132
133 /* Create a new notmuch_thread_t object for the given thread ID,
134  * treating any messages matching 'query_string' as "matched".
135  *
136  * Creating the thread will trigger two database searches. The first
137  * is for all messages belonging to the thread, (to get the first
138  * subject line, the total count of messages, and all authors). The
139  * second search is for all messages that are in the thread and that
140  * also match the given query_string. This is to allow for a separate
141  * count of matched messages, and to allow a viewer to diplay these
142  * messages differently.
143  *
144  * Here, 'ctx' is talloc context for the resulting thread object.
145  *
146  * This function returns NULL in the case of any error.
147  */
148 notmuch_thread_t *
149 _notmuch_thread_create (void *ctx,
150                         notmuch_database_t *notmuch,
151                         const char *thread_id,
152                         const char *query_string)
153 {
154     notmuch_thread_t *thread;
155     const char *thread_id_query_string, *matched_query_string;
156     notmuch_query_t *thread_id_query, *matched_query;
157     notmuch_messages_t *messages;
158
159     thread_id_query_string = talloc_asprintf (ctx, "thread:%s", thread_id);
160     if (unlikely (query_string == NULL))
161         return NULL;
162
163     matched_query_string = talloc_asprintf (ctx, "%s AND (%s)",
164                                             thread_id_query_string,
165                                             query_string);
166     if (unlikely (matched_query_string == NULL))
167         return NULL;
168
169     thread_id_query = notmuch_query_create (notmuch, thread_id_query_string);
170     if (unlikely (thread_id_query == NULL))
171         return NULL;
172
173     matched_query = notmuch_query_create (notmuch, matched_query_string);
174     if (unlikely (thread_id_query == NULL))
175         return NULL;
176
177     thread = talloc (ctx, notmuch_thread_t);
178     if (unlikely (thread == NULL))
179         return NULL;
180
181     talloc_set_destructor (thread, _notmuch_thread_destructor);
182
183     thread->notmuch = notmuch;
184     thread->thread_id = talloc_strdup (thread, thread_id);
185     thread->subject = NULL;
186     thread->authors_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
187                                                   free, NULL);
188     thread->authors = NULL;
189     thread->tags = g_hash_table_new_full (g_str_hash, g_str_equal,
190                                           free, NULL);
191
192     thread->total_messages = 0;
193     thread->matched_messages = 0;
194     thread->oldest = 0;
195     thread->newest = 0;
196
197     notmuch_query_set_sort (thread_id_query, NOTMUCH_SORT_DATE);
198
199     for (messages = notmuch_query_search_messages (thread_id_query, 0, -1);
200          notmuch_messages_has_more (messages);
201          notmuch_messages_advance (messages))
202     {
203         _thread_add_message (thread, notmuch_messages_get (messages));
204     }
205
206     notmuch_query_destroy (thread_id_query);
207
208     for (messages = notmuch_query_search_messages (matched_query, 0, -1);
209          notmuch_messages_has_more (messages);
210          notmuch_messages_advance (messages))
211     {
212         _thread_add_matched_message (thread, notmuch_messages_get (messages));
213     }
214
215     notmuch_query_destroy (matched_query);
216
217     return thread;
218 }
219
220 const char *
221 notmuch_thread_get_thread_id (notmuch_thread_t *thread)
222 {
223     return thread->thread_id;
224 }
225
226 int
227 notmuch_thread_get_total_messages (notmuch_thread_t *thread)
228 {
229     return thread->total_messages;
230 }
231
232 int
233 notmuch_thread_get_matched_messages (notmuch_thread_t *thread)
234 {
235     return thread->matched_messages;
236 }
237
238 const char *
239 notmuch_thread_get_authors (notmuch_thread_t *thread)
240 {
241     return thread->authors;
242 }
243
244 const char *
245 notmuch_thread_get_subject (notmuch_thread_t *thread)
246 {
247     return thread->subject;
248 }
249
250 time_t
251 notmuch_thread_get_oldest_date (notmuch_thread_t *thread)
252 {
253     return thread->oldest;
254 }
255
256 time_t
257 notmuch_thread_get_newest_date (notmuch_thread_t *thread)
258 {
259     return thread->newest;
260 }
261
262 notmuch_tags_t *
263 notmuch_thread_get_tags (notmuch_thread_t *thread)
264 {
265     notmuch_tags_t *tags;
266     GList *keys, *l;
267
268     tags = _notmuch_tags_create (thread);
269     if (unlikely (tags == NULL))
270         return NULL;
271
272     keys = g_hash_table_get_keys (thread->tags);
273
274     for (l = keys; l; l = l->next)
275         _notmuch_tags_add_tag (tags, (char *) l->data);
276
277     g_list_free (keys);
278
279     _notmuch_tags_prepare_iterator (tags);
280
281     return tags;
282 }
283
284 void
285 notmuch_thread_destroy (notmuch_thread_t *thread)
286 {
287     talloc_free (thread);
288 }