cce304ce29fc9199faa8b3616945e3e21bd74278
[notmuch] / message.cc
1 /* message.cc - Results of message-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 struct _notmuch_message {
27     Xapian::Document doc;
28 };
29
30 struct _notmuch_tags {
31     Xapian::TermIterator iterator;
32     Xapian::TermIterator iterator_end;
33 };
34
35 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
36
37 /* These prefix values are specifically chosen to be compatible
38  * with sup, (http://sup.rubyforge.org), written by
39  * William Morgan <wmorgan-sup@masanjin.net>, and released
40  * under the GNU GPL v2.
41  */
42
43 typedef struct {
44     const char *name;
45     const char *prefix;
46 } prefix_t;
47
48 prefix_t NORMAL_PREFIX[] = {
49     { "subject", "S" },
50     { "body", "B" },
51     { "from_name", "FN" },
52     { "to_name", "TN" },
53     { "name", "N" },
54     { "attachment", "A" }
55 };
56
57 prefix_t BOOLEAN_PREFIX[] = {
58     { "type", "K" },
59     { "from_email", "FE" },
60     { "to_email", "TE" },
61     { "email", "E" },
62     { "date", "D" },
63     { "label", "L" },
64     { "source_id", "I" },
65     { "attachment_extension", "O" },
66     { "msgid", "Q" },
67     { "thread", "H" },
68     { "ref", "R" }
69 };
70
71 const char *
72 _find_prefix (const char *name)
73 {
74     unsigned int i;
75
76     for (i = 0; i < ARRAY_SIZE (NORMAL_PREFIX); i++)
77         if (strcmp (name, NORMAL_PREFIX[i].name) == 0)
78             return NORMAL_PREFIX[i].prefix;
79
80     for (i = 0; i < ARRAY_SIZE (BOOLEAN_PREFIX); i++)
81         if (strcmp (name, BOOLEAN_PREFIX[i].name) == 0)
82             return BOOLEAN_PREFIX[i].prefix;
83
84     return "";
85 }
86
87 /* We end up having to call the destructor explicitly because we had
88  * to use "placement new" in order to initialize C++ objects within a
89  * block that we allocated with talloc. So C++ is making talloc
90  * slightly less simple to use, (we wouldn't need
91  * talloc_set_destructor at all otherwise).
92  */
93 static int
94 _notmuch_message_destructor (notmuch_message_t *message)
95 {
96     message->doc.~Document ();
97
98     return 0;
99 }
100
101 notmuch_message_t *
102 _notmuch_message_create (notmuch_results_t *owner,
103                          notmuch_database_t *notmuch,
104                          Xapian::docid doc_id)
105 {
106     notmuch_message_t *message;
107
108     message = talloc (owner, notmuch_message_t);
109     if (unlikely (message == NULL))
110         return NULL;
111
112     new (&message->doc) Xapian::Document;
113
114     talloc_set_destructor (message, _notmuch_message_destructor);
115
116     message->doc = notmuch->xapian_db->get_document (doc_id);
117
118     return message;
119 }
120
121 const char *
122 notmuch_message_get_message_id (notmuch_message_t *message)
123 {
124     Xapian::TermIterator i;
125
126     i = message->doc.termlist_begin ();
127     i.skip_to ("Q");
128     if (i != message->doc.termlist_end ())
129         return talloc_strdup (message, (*i).c_str () + 1);
130     else
131         return NULL;
132 }
133
134 /* We end up having to call the destructors explicitly because we had
135  * to use "placement new" in order to initialize C++ objects within a
136  * block that we allocated with talloc. So C++ is making talloc
137  * slightly less simple to use, (we wouldn't need
138  * talloc_set_destructor at all otherwise).
139  */
140 static int
141 _notmuch_tags_destructor (notmuch_tags_t *tags)
142 {
143     tags->iterator.~TermIterator ();
144     tags->iterator_end.~TermIterator ();
145
146     return 0;
147 }
148
149 notmuch_tags_t *
150 notmuch_message_get_tags (notmuch_message_t *message)
151 {
152     notmuch_tags_t *tags;
153
154     tags = talloc (message, notmuch_tags_t);
155     if (unlikely (tags == NULL))
156         return NULL;
157
158     new (&tags->iterator) Xapian::TermIterator;
159     new (&tags->iterator_end) Xapian::TermIterator;
160
161     talloc_set_destructor (tags, _notmuch_tags_destructor);
162
163     tags->iterator = message->doc.termlist_begin ();
164     tags->iterator.skip_to ("L");
165     tags->iterator_end = message->doc.termlist_end ();
166
167     return tags;
168 }
169
170 void
171 notmuch_message_destroy (notmuch_message_t *message)
172 {
173     talloc_free (message);
174 }
175
176 notmuch_bool_t
177 notmuch_tags_has_more (notmuch_tags_t *tags)
178 {
179     std::string s;
180
181     if (tags->iterator == tags->iterator_end)
182         return FALSE;
183
184     s = *tags->iterator;
185     if (s.size () && s[0] == 'L')
186         return TRUE;
187     else
188         return FALSE;
189 }
190
191 const char *
192 notmuch_tags_get (notmuch_tags_t *tags)
193 {
194     return talloc_strdup (tags, (*tags->iterator).c_str () + 1);
195 }
196
197 void
198 notmuch_tags_advance (notmuch_tags_t *tags)
199 {
200     tags->iterator++;
201 }
202
203 void
204 notmuch_tags_destroy (notmuch_tags_t *tags)
205 {
206     talloc_free (tags);
207 }