notmuch.el: Add commands to add tag, remove tag, and archive (== remove inbox tag)
[notmuch] / 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 <glib.h> /* GHashTable */
27
28 struct _notmuch_thread {
29     notmuch_database_t *notmuch;
30     char *thread_id;
31     char *subject;
32     GHashTable *tags;
33
34     notmuch_bool_t has_message;
35     time_t oldest;
36     time_t newest;
37 };
38
39 static int
40 _notmuch_thread_destructor (notmuch_thread_t *thread)
41 {
42     g_hash_table_unref (thread->tags);
43
44     return 0;
45 }
46
47 /* Create a new notmuch_thread_t object for an existing document in
48  * the database.
49  *
50  * Here, 'talloc owner' is an optional talloc context to which the new
51  * thread will belong. This allows for the caller to not bother
52  * calling notmuch_thread_destroy on the thread, and know that all
53  * memory will be reclaimed with 'talloc_owner' is freed. The caller
54  * still can call notmuch_thread_destroy when finished with the
55  * thread if desired.
56  *
57  * The 'talloc_owner' argument can also be NULL, in which case the
58  * caller *is* responsible for calling notmuch_thread_destroy.
59  *
60  * This function returns NULL in the case of any error.
61  */
62 notmuch_thread_t *
63 _notmuch_thread_create (const void *talloc_owner,
64                         notmuch_database_t *notmuch,
65                         const char *thread_id)
66 {
67     notmuch_thread_t *thread;
68
69     thread = talloc (talloc_owner, notmuch_thread_t);
70     if (unlikely (thread == NULL))
71         return NULL;
72
73     talloc_set_destructor (thread, _notmuch_thread_destructor);
74
75     thread->notmuch = notmuch;
76     thread->thread_id = talloc_strdup (thread, thread_id);
77     thread->subject = NULL;
78     thread->tags = g_hash_table_new_full (g_str_hash, g_str_equal,
79                                           free, NULL);
80
81     thread->has_message = 0;
82     thread->oldest = 0;
83     thread->newest = 0;
84
85     return thread;
86 }
87
88 const char *
89 notmuch_thread_get_thread_id (notmuch_thread_t *thread)
90 {
91     return thread->thread_id;
92 }
93
94 void
95 _notmuch_thread_add_message (notmuch_thread_t *thread,
96                              notmuch_message_t *message)
97 {
98     notmuch_tags_t *tags;
99     const char *tag;
100     time_t date;
101
102     if (! thread->subject) {
103         const char *subject;
104         subject = notmuch_message_get_header (message, "subject");
105         thread->subject = talloc_strdup (thread, subject);
106     }
107
108     for (tags = notmuch_message_get_tags (message);
109          notmuch_tags_has_more (tags);
110          notmuch_tags_advance (tags))
111     {
112         tag = notmuch_tags_get (tags);
113         g_hash_table_insert (thread->tags, xstrdup (tag), NULL);
114     }
115
116     date = notmuch_message_get_date (message);
117
118     if (date < thread->oldest || ! thread->has_message)
119         thread->oldest = date;
120
121     if (date > thread->newest || ! thread->has_message)
122         thread->newest = date;
123
124     thread->has_message = 1;
125 }
126
127 const char *
128 notmuch_thread_get_subject (notmuch_thread_t *thread)
129 {
130     return thread->subject;
131 }
132
133 time_t
134 notmuch_thread_get_oldest_date (notmuch_thread_t *thread)
135 {
136     return thread->oldest;
137 }
138
139 time_t
140 notmuch_thread_get_newest_date (notmuch_thread_t *thread)
141 {
142     return thread->newest;
143 }
144
145 notmuch_tags_t *
146 notmuch_thread_get_tags (notmuch_thread_t *thread)
147 {
148     notmuch_tags_t *tags;
149     GList *keys, *l;
150
151     tags = _notmuch_tags_create (thread);
152     if (unlikely (tags == NULL))
153         return NULL;
154
155     keys = g_hash_table_get_keys (thread->tags);
156
157     for (l = keys; l; l = l->next)
158         _notmuch_tags_add_tag (tags, (char *) l->data);
159
160     g_list_free (keys);
161
162     _notmuch_tags_prepare_iterator (tags);
163
164     return tags;
165 }
166
167 void
168 notmuch_thread_destroy (notmuch_thread_t *thread)
169 {
170     talloc_free (thread);
171 }