Re: [PATCH v4 13/16] add indexopts to notmuch python bindings.
[notmuch-archives.git] / 14 / 633d5e44dbd3d1a0a7edcf9cff568723de7686
1 Return-Path: <teythoon@jade-hamburg.de>\r
2 X-Original-To: notmuch@notmuchmail.org\r
3 Delivered-To: notmuch@notmuchmail.org\r
4 Received: from localhost (localhost [127.0.0.1])\r
5         by olra.theworths.org (Postfix) with ESMTP id 591BA431FDA\r
6         for <notmuch@notmuchmail.org>; Mon, 30 Apr 2012 12:56:41 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: 0\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
12         autolearn=disabled\r
13 Received: from olra.theworths.org ([127.0.0.1])\r
14         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
15         with ESMTP id Z8lQCSxjR+TL for <notmuch@notmuchmail.org>;\r
16         Mon, 30 Apr 2012 12:56:35 -0700 (PDT)\r
17 Received: from mail.cryptobitch.de (cryptobitch.de [88.198.7.68])\r
18         (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits))\r
19         (No client certificate requested)\r
20         by olra.theworths.org (Postfix) with ESMTPS id BE109431FAE\r
21         for <notmuch@notmuchmail.org>; Mon, 30 Apr 2012 12:56:34 -0700 (PDT)\r
22 Received: from mail.jade-hamburg.de (mail.jade-hamburg.de [85.183.11.228])\r
23         (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits))\r
24         (No client certificate requested)\r
25         by mail.cryptobitch.de (Postfix) with ESMTPSA id 112ED584BA3\r
26         for <notmuch@notmuchmail.org>; Mon, 30 Apr 2012 21:56:33 +0200 (CEST)\r
27 Received: by mail.jade-hamburg.de (Postfix, from userid 401)\r
28         id 62DD1DF2A8; Mon, 30 Apr 2012 21:56:32 +0200 (CEST)\r
29 Received: from thinkbox.jade-hamburg.de (unknown [10.1.1.153])\r
30         (using TLSv1 with cipher AES256-SHA (256/256 bits))\r
31         (No client certificate requested) (Authenticated sender: teythoon)\r
32         by mail.jade-hamburg.de (Postfix) with ESMTPSA id 6A147DF2A1;\r
33         Mon, 30 Apr 2012 21:55:52 +0200 (CEST)\r
34 Received: from teythoon by thinkbox.jade-hamburg.de with local (Exim 4.77)\r
35         (envelope-from <teythoon@thinkbox.jade-hamburg.de>)\r
36         id 1SOwhL-0003yk-Hz; Mon, 30 Apr 2012 21:55:51 +0200\r
37 From: Justus Winter <4winter@informatik.uni-hamburg.de>\r
38 To: notmuch@notmuchmail.org\r
39 Subject: [PATCH 1/5] go: reorganize the go bindings\r
40 Date: Mon, 30 Apr 2012 21:55:40 +0200\r
41 Message-Id:\r
42  <1335815744-15258-1-git-send-email-4winter@informatik.uni-hamburg.de>\r
43 X-Mailer: git-send-email 1.7.10\r
44 In-Reply-To: <20120430195420.2528.48612@thinkbox.jade-hamburg.de>\r
45 References: <20120430195420.2528.48612@thinkbox.jade-hamburg.de>\r
46 X-BeenThere: notmuch@notmuchmail.org\r
47 X-Mailman-Version: 2.1.13\r
48 Precedence: list\r
49 List-Id: "Use and development of the notmuch mail system."\r
50         <notmuch.notmuchmail.org>\r
51 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
52         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
53 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
54 List-Post: <mailto:notmuch@notmuchmail.org>\r
55 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
56 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
57         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
58 X-List-Received-Date: Mon, 30 Apr 2012 19:56:41 -0000\r
59 \r
60 Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de>\r
61 ---\r
62  bindings/go/cmds/notmuch-addrlookup.go           |  259 -----\r
63  bindings/go/pkg/notmuch.go                       | 1124 ----------------------\r
64  bindings/go/src/notmuch-addrlookup/addrlookup.go |  259 +++++\r
65  bindings/go/src/notmuch/notmuch.go               | 1124 ++++++++++++++++++++++\r
66  4 files changed, 1383 insertions(+), 1383 deletions(-)\r
67  delete mode 100644 bindings/go/cmds/notmuch-addrlookup.go\r
68  delete mode 100644 bindings/go/pkg/notmuch.go\r
69  create mode 100644 bindings/go/src/notmuch-addrlookup/addrlookup.go\r
70  create mode 100644 bindings/go/src/notmuch/notmuch.go\r
71 \r
72 diff --git a/bindings/go/cmds/notmuch-addrlookup.go b/bindings/go/cmds/notmuch-addrlookup.go\r
73 deleted file mode 100644\r
74 index 16958e5..0000000\r
75 --- a/bindings/go/cmds/notmuch-addrlookup.go\r
76 +++ /dev/null\r
77 @@ -1,259 +0,0 @@\r
78 -package main\r
79 -\r
80 -// stdlib imports\r
81 -import "os"\r
82 -import "path"\r
83 -import "log"\r
84 -import "fmt"\r
85 -import "regexp"\r
86 -import "strings"\r
87 -import "sort"\r
88 -\r
89 -// 3rd-party imports\r
90 -import "notmuch"\r
91 -import "github.com/kless/goconfig/config"\r
92 -\r
93 -type mail_addr_freq struct {\r
94 -       addr  string\r
95 -       count [3]uint\r
96 -}\r
97 -\r
98 -type frequencies map[string]uint\r
99 -\r
100 -/* Used to sort the email addresses from most to least used */\r
101 -func sort_by_freq(m1, m2 *mail_addr_freq) int {\r
102 -       if (m1.count[0] == m2.count[0] &&\r
103 -               m1.count[1] == m2.count[1] &&\r
104 -               m1.count[2] == m2.count[2]) {\r
105 -               return 0\r
106 -       }\r
107 -\r
108 -       if (m1.count[0] >  m2.count[0] ||\r
109 -               m1.count[0] == m2.count[0] &&\r
110 -               m1.count[1] >  m2.count[1] ||\r
111 -               m1.count[0] == m2.count[0] &&\r
112 -               m1.count[1] == m2.count[1] &&\r
113 -               m1.count[2] >  m2.count[2]) {\r
114 -               return -1\r
115 -       }\r
116 -\r
117 -       return 1\r
118 -}\r
119 -\r
120 -type maddresses []*mail_addr_freq\r
121 -\r
122 -func (self *maddresses) Len() int {\r
123 -       return len(*self)\r
124 -}\r
125 -\r
126 -func (self *maddresses) Less(i,j int) bool {\r
127 -       m1 := (*self)[i]\r
128 -       m2 := (*self)[j]\r
129 -       v  := sort_by_freq(m1, m2)\r
130 -       if v<=0 {\r
131 -               return true\r
132 -       }\r
133 -       return false\r
134 -}\r
135 -\r
136 -func (self *maddresses) Swap(i,j int) {\r
137 -       (*self)[i], (*self)[j] = (*self)[j], (*self)[i]\r
138 -}\r
139 -\r
140 -// find most frequent real name for each mail address\r
141 -func frequent_fullname(freqs frequencies) string {\r
142 -       var maxfreq uint = 0\r
143 -       fullname := ""\r
144 -       freqs_sz := len(freqs)\r
145 -\r
146 -       for mail,freq := range freqs {\r
147 -               if (freq > maxfreq && mail != "") || freqs_sz == 1 {\r
148 -                       // only use the entry if it has a real name\r
149 -                       // or if this is the only entry\r
150 -                       maxfreq = freq\r
151 -                       fullname = mail\r
152 -               }\r
153 -       }\r
154 -       return fullname\r
155 -}\r
156 -\r
157 -func addresses_by_frequency(msgs *notmuch.Messages, name string, pass uint, addr_to_realname *map[string]*frequencies) *frequencies {\r
158 -\r
159 -       freqs := make(frequencies)\r
160 -\r
161 -       pattern := `\s*(("(\.|[^"])*"|[^,])*<?(?mail\b\w+([-+.]\w+)*\@\w+[-\.\w]*\.([-\.\w]+)*\w\b)>?)`\r
162 -       // pattern := "\\s*((\\\"(\\\\.|[^\\\\\"])*\\\"|[^,])*" +\r
163 -       //      "<?(?P<mail>\\b\\w+([-+.]\\w+)*\\@\\w+[-\\.\\w]*\\.([-\\.\\w]+)*\\w\\b)>?)"\r
164 -       pattern = `.*` + strings.ToLower(name) + `.*`\r
165 -       var re *regexp.Regexp = nil\r
166 -       var err os.Error = nil\r
167 -       if re,err = regexp.Compile(pattern); err != nil {\r
168 -               log.Printf("error: %v\n", err)\r
169 -               return &freqs\r
170 -       }\r
171 -       \r
172 -       headers := []string{"from"}\r
173 -       if pass == 1 {\r
174 -               headers = append(headers, "to", "cc", "bcc")\r
175 -       }\r
176 -\r
177 -       for ;msgs.Valid();msgs.MoveToNext() {\r
178 -               msg := msgs.Get()\r
179 -               //println("==> msg [", msg.GetMessageId(), "]")\r
180 -               for _,header := range headers {\r
181 -                       froms := strings.ToLower(msg.GetHeader(header))\r
182 -                       //println("  froms: ["+froms+"]")\r
183 -                       for _,from := range strings.Split(froms, ",", -1) {\r
184 -                               from = strings.Trim(from, " ")\r
185 -                               match := re.FindString(from)\r
186 -                               //println("  -> match: ["+match+"]")\r
187 -                               occ,ok := freqs[match]\r
188 -                               if !ok {\r
189 -                                       freqs[match] = 0\r
190 -                                       occ = 0\r
191 -                               }\r
192 -                               freqs[match] = occ+1\r
193 -                       }\r
194 -               }\r
195 -       }\r
196 -       return &freqs\r
197 -}\r
198 -\r
199 -func search_address_passes(queries [3]*notmuch.Query, name string) []string {\r
200 -       var val []string\r
201 -       addr_freq := make(map[string]*mail_addr_freq)\r
202 -       addr_to_realname := make(map[string]*frequencies)\r
203 -\r
204 -       var pass uint = 0 // 0-based\r
205 -       for _,query := range queries {\r
206 -               if query == nil {\r
207 -                       //println("**warning: idx [",idx,"] contains a nil query")\r
208 -                       continue\r
209 -               }\r
210 -               msgs := query.SearchMessages()\r
211 -               ht := addresses_by_frequency(msgs, name, pass, &addr_to_realname)\r
212 -               for addr, count := range *ht {\r
213 -                       freq,ok := addr_freq[addr]\r
214 -                       if !ok {\r
215 -                               freq = &mail_addr_freq{addr:addr, count:[3]uint{0,0,0}}\r
216 -                       }\r
217 -                       freq.count[pass] = count\r
218 -                       addr_freq[addr] = freq\r
219 -               }\r
220 -               msgs.Destroy()\r
221 -               pass += 1\r
222 -       }\r
223 -\r
224 -       addrs := make(maddresses, len(addr_freq))\r
225 -       {\r
226 -               iaddr := 0\r
227 -               for _, freq := range addr_freq {\r
228 -                       addrs[iaddr] = freq\r
229 -                       iaddr += 1\r
230 -               }\r
231 -       }\r
232 -       sort.Sort(&addrs)\r
233 -\r
234 -       for _,addr := range addrs {\r
235 -               freqs,ok := addr_to_realname[addr.addr]\r
236 -               if ok {\r
237 -                       val = append(val, frequent_fullname(*freqs))\r
238 -               } else {\r
239 -                       val = append(val, addr.addr)\r
240 -               }\r
241 -       }\r
242 -       //println("val:",val)\r
243 -       return val\r
244 -}\r
245 -\r
246 -type address_matcher struct {\r
247 -       // the notmuch database\r
248 -       db *notmuch.Database\r
249 -       // full path of the notmuch database\r
250 -       user_db_path string\r
251 -       // user primary email\r
252 -       user_primary_email string\r
253 -       // user tag to mark from addresses as in the address book\r
254 -       user_addrbook_tag string\r
255 -}\r
256 -\r
257 -func new_address_matcher() *address_matcher {\r
258 -       var cfg *config.Config\r
259 -       var err os.Error\r
260 -\r
261 -       // honor NOTMUCH_CONFIG\r
262 -       home := os.Getenv("NOTMUCH_CONFIG")\r
263 -       if home == "" {\r
264 -               home = os.Getenv("HOME")\r
265 -       }\r
266 -\r
267 -       if cfg,err = config.ReadDefault(path.Join(home, ".notmuch-config")); err != nil {\r
268 -               log.Fatalf("error loading config file:",err)\r
269 -       }\r
270 -\r
271 -       db_path,_ := cfg.String("database", "path")\r
272 -       primary_email,_ := cfg.String("user", "primary_email")\r
273 -       addrbook_tag,err := cfg.String("user", "addrbook_tag")\r
274 -       if err != nil {\r
275 -               addrbook_tag = "addressbook"\r
276 -       }\r
277 -\r
278 -       self := &address_matcher{db:nil, \r
279 -                                user_db_path:db_path,\r
280 -                                user_primary_email:primary_email,\r
281 -                                user_addrbook_tag:addrbook_tag}\r
282 -       return self\r
283 -}\r
284 -\r
285 -func (self *address_matcher) run(name string) {\r
286 -       queries := [3]*notmuch.Query{}\r
287 -       \r
288 -       // open the database\r
289 -       self.db = notmuch.OpenDatabase(self.user_db_path, \r
290 -               notmuch.DATABASE_MODE_READ_ONLY)\r
291 -\r
292 -       // pass 1: look at all from: addresses with the address book tag\r
293 -       query := "tag:" + self.user_addrbook_tag\r
294 -       if name != "" {\r
295 -               query = query + " and from:" + name + "*"\r
296 -       }\r
297 -       queries[0] = self.db.CreateQuery(query)\r
298 -\r
299 -       // pass 2: look at all to: addresses sent from our primary mail\r
300 -       query = ""\r
301 -       if name != "" {\r
302 -               query = "to:"+name+"*"\r
303 -       }\r
304 -       if self.user_primary_email != "" {\r
305 -               query = query + " from:" + self.user_primary_email\r
306 -       }\r
307 -       queries[1] = self.db.CreateQuery(query)\r
308 -\r
309 -       // if that leads only to a few hits, we check every from too\r
310 -       if queries[0].CountMessages() + queries[1].CountMessages() < 10 {\r
311 -               query = ""\r
312 -               if name != "" {\r
313 -                       query = "from:"+name+"*"\r
314 -               }\r
315 -               queries[2] = self.db.CreateQuery(query)\r
316 -       }\r
317 -       \r
318 -       // actually retrieve and sort addresses\r
319 -       results := search_address_passes(queries, name)\r
320 -       for _,v := range results {\r
321 -               if v != "" && v != "\n" {\r
322 -                       fmt.Println(v)\r
323 -               }\r
324 -       }\r
325 -       return\r
326 -}\r
327 -\r
328 -func main() {\r
329 -       //fmt.Println("args:",os.Args)\r
330 -       app := new_address_matcher()\r
331 -       name := ""\r
332 -       if len(os.Args) > 1 {\r
333 -               name = os.Args[1]\r
334 -       }\r
335 -       app.run(name)\r
336 -}\r
337 \ No newline at end of file\r
338 diff --git a/bindings/go/pkg/notmuch.go b/bindings/go/pkg/notmuch.go\r
339 deleted file mode 100644\r
340 index de9de23..0000000\r
341 --- a/bindings/go/pkg/notmuch.go\r
342 +++ /dev/null\r
343 @@ -1,1124 +0,0 @@\r
344 -// Wrapper around the notmuch library\r
345 -\r
346 -package notmuch\r
347 -\r
348 -/*\r
349 -#include <stdlib.h>\r
350 -#include <string.h>\r
351 -#include <time.h>\r
352 -#include "notmuch.h"\r
353 -*/\r
354 -import "C"\r
355 -import "unsafe"\r
356 -\r
357 -// Status codes used for the return values of most functions\r
358 -type Status C.notmuch_status_t\r
359 -const (\r
360 -       STATUS_SUCCESS Status = 0\r
361 -       STATUS_OUT_OF_MEMORY\r
362 -    STATUS_READ_ONLY_DATABASE\r
363 -    STATUS_XAPIAN_EXCEPTION\r
364 -    STATUS_FILE_ERROR\r
365 -    STATUS_FILE_NOT_EMAIL\r
366 -    STATUS_DUPLICATE_MESSAGE_ID\r
367 -    STATUS_NULL_POINTER\r
368 -    STATUS_TAG_TOO_LONG\r
369 -    STATUS_UNBALANCED_FREEZE_THAW\r
370 -\r
371 -    STATUS_LAST_STATUS\r
372 -)\r
373 -\r
374 -func (self Status) String() string {\r
375 -       var p *C.char\r
376 -       \r
377 -       // p is read-only\r
378 -       p = C.notmuch_status_to_string(C.notmuch_status_t(self))\r
379 -       if p != nil {\r
380 -               s := C.GoString(p)\r
381 -               return s\r
382 -       }\r
383 -       return ""\r
384 -}\r
385 -\r
386 -/* Various opaque data types. For each notmuch_<foo>_t see the various\r
387 - * notmuch_<foo> functions below. */\r
388 -\r
389 -type Database struct {\r
390 -       db *C.notmuch_database_t\r
391 -}\r
392 -\r
393 -type Query struct {\r
394 -       query *C.notmuch_query_t\r
395 -}\r
396 -\r
397 -type Threads struct {\r
398 -       threads *C.notmuch_threads_t\r
399 -}\r
400 -\r
401 -type Thread struct {\r
402 -       thread *C.notmuch_thread_t\r
403 -}\r
404 -\r
405 -type Messages struct {\r
406 -       messages *C.notmuch_messages_t\r
407 -}\r
408 -\r
409 -type Message struct {\r
410 -       message *C.notmuch_message_t\r
411 -}\r
412 -\r
413 -type Tags struct {\r
414 -       tags *C.notmuch_tags_t\r
415 -}\r
416 -\r
417 -type Directory struct {\r
418 -       dir *C.notmuch_directory_t\r
419 -}\r
420 -\r
421 -type Filenames struct {\r
422 -       fnames *C.notmuch_filenames_t\r
423 -}\r
424 -\r
425 -type DatabaseMode C.notmuch_database_mode_t\r
426 -const (\r
427 -    DATABASE_MODE_READ_ONLY DatabaseMode = 0\r
428 -    DATABASE_MODE_READ_WRITE\r
429 -)\r
430 -\r
431 -// Create a new, empty notmuch database located at 'path'\r
432 -func NewDatabase(path string) *Database {\r
433 -\r
434 -       var c_path *C.char = C.CString(path)\r
435 -       defer C.free(unsafe.Pointer(c_path))\r
436 -\r
437 -       if c_path == nil {\r
438 -               return nil\r
439 -       }\r
440 -\r
441 -       self := &Database{db:nil}\r
442 -       self.db = C.notmuch_database_create(c_path)\r
443 -       if self.db == nil {\r
444 -               return nil\r
445 -       }\r
446 -       return self\r
447 -}\r
448 -\r
449 -/* Open an existing notmuch database located at 'path'.\r
450 - *\r
451 - * The database should have been created at some time in the past,\r
452 - * (not necessarily by this process), by calling\r
453 - * notmuch_database_create with 'path'. By default the database should be\r
454 - * opened for reading only. In order to write to the database you need to\r
455 - * pass the NOTMUCH_DATABASE_MODE_READ_WRITE mode.\r
456 - *\r
457 - * An existing notmuch database can be identified by the presence of a\r
458 - * directory named ".notmuch" below 'path'.\r
459 - *\r
460 - * The caller should call notmuch_database_destroy when finished with\r
461 - * this database.\r
462 - *\r
463 - * In case of any failure, this function returns NULL, (after printing\r
464 - * an error message on stderr).\r
465 - */\r
466 -func OpenDatabase(path string, mode DatabaseMode) *Database {\r
467 -\r
468 -       var c_path *C.char = C.CString(path)\r
469 -       defer C.free(unsafe.Pointer(c_path))\r
470 -\r
471 -       if c_path == nil {\r
472 -               return nil\r
473 -       }\r
474 -\r
475 -       self := &Database{db:nil}\r
476 -       self.db = C.notmuch_database_open(c_path, C.notmuch_database_mode_t(mode))\r
477 -       if self.db == nil {\r
478 -               return nil\r
479 -       }\r
480 -       return self\r
481 -}\r
482 -\r
483 -/* Close the given notmuch database, freeing all associated\r
484 - * resources. See notmuch_database_open. */\r
485 -func (self *Database) Close() {\r
486 -       C.notmuch_database_destroy(self.db)\r
487 -}\r
488 -\r
489 -/* Return the database path of the given database.\r
490 - */\r
491 -func (self *Database) GetPath() string {\r
492 -       \r
493 - /* The return value is a string owned by notmuch so should not be\r
494 -  * modified nor freed by the caller. */\r
495 -       var p *C.char = C.notmuch_database_get_path(self.db)\r
496 -       if p != nil {\r
497 -               s := C.GoString(p)\r
498 -               return s\r
499 -       }\r
500 -       return ""\r
501 -}\r
502 -\r
503 -/* Return the database format version of the given database. */\r
504 -func (self *Database) GetVersion() uint {\r
505 -       return uint(C.notmuch_database_get_version(self.db))\r
506 -}\r
507 -\r
508 -/* Does this database need to be upgraded before writing to it?\r
509 - *\r
510 - * If this function returns TRUE then no functions that modify the\r
511 - * database (notmuch_database_add_message, notmuch_message_add_tag,\r
512 - * notmuch_directory_set_mtime, etc.) will work unless the function\r
513 - * notmuch_database_upgrade is called successfully first. */\r
514 -func (self *Database) NeedsUpgrade() bool {\r
515 -       do_upgrade := C.notmuch_database_needs_upgrade(self.db)\r
516 -       if do_upgrade == 0 {\r
517 -               return false\r
518 -       }\r
519 -       return true\r
520 -}\r
521 -\r
522 -// TODO: notmuch_database_upgrade\r
523 -\r
524 -\r
525 -/* Retrieve a directory object from the database for 'path'.\r
526 - *\r
527 - * Here, 'path' should be a path relative to the path of 'database'\r
528 - * (see notmuch_database_get_path), or else should be an absolute path\r
529 - * with initial components that match the path of 'database'.\r
530 - *\r
531 - * Can return NULL if a Xapian exception occurs.\r
532 - */\r
533 -func (self *Database) GetDirectory(path string) *Directory {\r
534 -       var c_path *C.char = C.CString(path)\r
535 -       defer C.free(unsafe.Pointer(c_path))\r
536 -\r
537 -       if c_path == nil {\r
538 -               return nil\r
539 -       }\r
540 -\r
541 -       c_dir := C.notmuch_database_get_directory(self.db, c_path)\r
542 -       if c_dir == nil {\r
543 -               return nil\r
544 -       }\r
545 -       return &Directory{dir:c_dir}\r
546 -}\r
547 -\r
548 -/* Add a new message to the given notmuch database.\r
549 - *\r
550 - * Here,'filename' should be a path relative to the path of\r
551 - * 'database' (see notmuch_database_get_path), or else should be an\r
552 - * absolute filename with initial components that match the path of\r
553 - * 'database'.\r
554 - *\r
555 - * The file should be a single mail message (not a multi-message mbox)\r
556 - * that is expected to remain at its current location, (since the\r
557 - * notmuch database will reference the filename, and will not copy the\r
558 - * entire contents of the file.\r
559 - *\r
560 - * If 'message' is not NULL, then, on successful return '*message'\r
561 - * will be initialized to a message object that can be used for things\r
562 - * such as adding tags to the just-added message. The user should call\r
563 - * notmuch_message_destroy when done with the message. On any failure\r
564 - * '*message' will be set to NULL.\r
565 - *\r
566 - * Return value:\r
567 - *\r
568 - * NOTMUCH_STATUS_SUCCESS: Message successfully added to database.\r
569 - *\r
570 - * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,\r
571 - *     message not added.\r
572 - *\r
573 - * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: Message has the same message\r
574 - *     ID as another message already in the database. The new\r
575 - *     filename was successfully added to the message in the database\r
576 - *     (if not already present).\r
577 - *\r
578 - * NOTMUCH_STATUS_FILE_ERROR: an error occurred trying to open the\r
579 - *     file, (such as permission denied, or file not found,\r
580 - *     etc.). Nothing added to the database.\r
581 - *\r
582 - * NOTMUCH_STATUS_FILE_NOT_EMAIL: the contents of filename don't look\r
583 - *     like an email message. Nothing added to the database.\r
584 - *\r
585 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
586 - *     mode so no message can be added.\r
587 - */\r
588 -func \r
589 -(self *Database) AddMessage(fname string) (*Message, Status) {\r
590 -       var c_fname *C.char = C.CString(fname)\r
591 -       defer C.free(unsafe.Pointer(c_fname))\r
592 -\r
593 -       if c_fname == nil {\r
594 -               return nil, STATUS_OUT_OF_MEMORY\r
595 -       }\r
596 -\r
597 -       var c_msg *C.notmuch_message_t = new(C.notmuch_message_t)\r
598 -       st := Status(C.notmuch_database_add_message(self.db, c_fname, &c_msg))\r
599 -\r
600 -       return &Message{message:c_msg}, st\r
601 -}\r
602 -\r
603 -/* Remove a message from the given notmuch database.\r
604 - *\r
605 - * Note that only this particular filename association is removed from\r
606 - * the database. If the same message (as determined by the message ID)\r
607 - * is still available via other filenames, then the message will\r
608 - * persist in the database for those filenames. When the last filename\r
609 - * is removed for a particular message, the database content for that\r
610 - * message will be entirely removed.\r
611 - *\r
612 - * Return value:\r
613 - *\r
614 - * NOTMUCH_STATUS_SUCCESS: The last filename was removed and the\r
615 - *     message was removed from the database.\r
616 - *\r
617 - * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,\r
618 - *     message not removed.\r
619 - *\r
620 - * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: This filename was removed but\r
621 - *     the message persists in the database with at least one other\r
622 - *     filename.\r
623 - *\r
624 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
625 - *     mode so no message can be removed.\r
626 - */\r
627 -func (self *Database) RemoveMessage(fname string) Status {\r
628 -       \r
629 -       var c_fname *C.char = C.CString(fname)\r
630 -       defer C.free(unsafe.Pointer(c_fname))\r
631 -\r
632 -       if c_fname == nil {\r
633 -               return STATUS_OUT_OF_MEMORY\r
634 -       }\r
635 -\r
636 -       st := C.notmuch_database_remove_message(self.db, c_fname)\r
637 -       return Status(st)\r
638 -}\r
639 -\r
640 -/* Find a message with the given message_id.\r
641 - *\r
642 - * If the database contains a message with the given message_id, then\r
643 - * a new notmuch_message_t object is returned. The caller should call\r
644 - * notmuch_message_destroy when done with the message.\r
645 - *\r
646 - * This function returns NULL in the following situations:\r
647 - *\r
648 - *     * No message is found with the given message_id\r
649 - *     * An out-of-memory situation occurs\r
650 - *     * A Xapian exception occurs\r
651 - */\r
652 -func (self *Database) FindMessage(message_id string) (*Message, Status) {\r
653 -       \r
654 -       var c_msg_id *C.char = C.CString(message_id)\r
655 -       defer C.free(unsafe.Pointer(c_msg_id))\r
656 -\r
657 -       if c_msg_id == nil {\r
658 -               return nil, STATUS_OUT_OF_MEMORY\r
659 -       }\r
660 -\r
661 -       msg := &Message{message:nil}\r
662 -       st := Status(C.notmuch_database_find_message(self.db, c_msg_id, &msg.message))\r
663 -       if st != STATUS_SUCCESS {\r
664 -               return nil, st\r
665 -       }\r
666 -       return msg, st\r
667 -}\r
668 -\r
669 -/* Return a list of all tags found in the database.\r
670 - *\r
671 - * This function creates a list of all tags found in the database. The\r
672 - * resulting list contains all tags from all messages found in the database.\r
673 - *\r
674 - * On error this function returns NULL.\r
675 - */\r
676 -func (self *Database) GetAllTags() *Tags {\r
677 -       tags := C.notmuch_database_get_all_tags(self.db)\r
678 -       if tags == nil {\r
679 -               return nil\r
680 -       }\r
681 -       return &Tags{tags:tags}\r
682 -}\r
683 -\r
684 -/* Create a new query for 'database'.\r
685 - *\r
686 - * Here, 'database' should be an open database, (see\r
687 - * notmuch_database_open and notmuch_database_create).\r
688 - *\r
689 - * For the query string, we'll document the syntax here more\r
690 - * completely in the future, but it's likely to be a specialized\r
691 - * version of the general Xapian query syntax:\r
692 - *\r
693 - * http://xapian.org/docs/queryparser.html\r
694 - *\r
695 - * As a special case, passing either a length-zero string, (that is ""),\r
696 - * or a string consisting of a single asterisk (that is "*"), will\r
697 - * result in a query that returns all messages in the database.\r
698 - *\r
699 - * See notmuch_query_set_sort for controlling the order of results.\r
700 - * See notmuch_query_search_messages and notmuch_query_search_threads\r
701 - * to actually execute the query.\r
702 - *\r
703 - * User should call notmuch_query_destroy when finished with this\r
704 - * query.\r
705 - *\r
706 - * Will return NULL if insufficient memory is available.\r
707 - */\r
708 -func (self *Database) CreateQuery(query string) *Query {\r
709 -       \r
710 -       var c_query *C.char = C.CString(query)\r
711 -       defer C.free(unsafe.Pointer(c_query))\r
712 -\r
713 -       if c_query == nil {\r
714 -               return nil\r
715 -       }\r
716 -\r
717 -       q := C.notmuch_query_create(self.db, c_query)\r
718 -       if q == nil {\r
719 -               return nil\r
720 -       }\r
721 -       return &Query{query:q}\r
722 -}\r
723 -\r
724 -/* Sort values for notmuch_query_set_sort */\r
725 -type Sort C.notmuch_sort_t\r
726 -const (\r
727 -       SORT_OLDEST_FIRST Sort = 0\r
728 -       SORT_NEWEST_FIRST\r
729 -       SORT_MESSAGE_ID\r
730 -       SORT_UNSORTED\r
731 -)\r
732 -\r
733 -/* Return the query_string of this query. See notmuch_query_create. */\r
734 -func (self *Query) String() string {\r
735 -       // FIXME: do we own 'q' or not ?\r
736 -       q := C.notmuch_query_get_query_string(self.query)\r
737 -       //defer C.free(unsafe.Pointer(q))\r
738 -       \r
739 -       if q != nil {\r
740 -               s := C.GoString(q)\r
741 -               return s\r
742 -       }\r
743 -\r
744 -       return ""\r
745 -}\r
746 -\r
747 -/* Specify the sorting desired for this query. */\r
748 -func (self *Query) SetSort(sort Sort) {\r
749 -       C.notmuch_query_set_sort(self.query, C.notmuch_sort_t(sort))\r
750 -}\r
751 -\r
752 -/* Return the sort specified for this query. See notmuch_query_set_sort. */\r
753 -func (self *Query) GetSort() Sort {\r
754 -       return Sort(C.notmuch_query_get_sort(self.query))\r
755 -}\r
756 -\r
757 -/* Execute a query for threads, returning a notmuch_threads_t object\r
758 - * which can be used to iterate over the results. The returned threads\r
759 - * object is owned by the query and as such, will only be valid until\r
760 - * notmuch_query_destroy.\r
761 - *\r
762 - * Typical usage might be:\r
763 - *\r
764 - *     notmuch_query_t *query;\r
765 - *     notmuch_threads_t *threads;\r
766 - *     notmuch_thread_t *thread;\r
767 - *\r
768 - *     query = notmuch_query_create (database, query_string);\r
769 - *\r
770 - *     for (threads = notmuch_query_search_threads (query);\r
771 - *          notmuch_threads_valid (threads);\r
772 - *          notmuch_threads_move_to_next (threads))\r
773 - *     {\r
774 - *         thread = notmuch_threads_get (threads);\r
775 - *         ....\r
776 - *         notmuch_thread_destroy (thread);\r
777 - *     }\r
778 - *\r
779 - *     notmuch_query_destroy (query);\r
780 - *\r
781 - * Note: If you are finished with a thread before its containing\r
782 - * query, you can call notmuch_thread_destroy to clean up some memory\r
783 - * sooner (as in the above example). Otherwise, if your thread objects\r
784 - * are long-lived, then you don't need to call notmuch_thread_destroy\r
785 - * and all the memory will still be reclaimed when the query is\r
786 - * destroyed.\r
787 - *\r
788 - * Note that there's no explicit destructor needed for the\r
789 - * notmuch_threads_t object. (For consistency, we do provide a\r
790 - * notmuch_threads_destroy function, but there's no good reason\r
791 - * to call it if the query is about to be destroyed).\r
792 - *\r
793 - * If a Xapian exception occurs this function will return NULL.\r
794 - */\r
795 -func (self *Query) SearchThreads() *Threads {\r
796 -       threads := C.notmuch_query_search_threads(self.query)\r
797 -       if threads == nil {\r
798 -               return nil\r
799 -       }\r
800 -       return &Threads{threads:threads}\r
801 -}\r
802 -\r
803 -/* Execute a query for messages, returning a notmuch_messages_t object\r
804 - * which can be used to iterate over the results. The returned\r
805 - * messages object is owned by the query and as such, will only be\r
806 - * valid until notmuch_query_destroy.\r
807 - *\r
808 - * Typical usage might be:\r
809 - *\r
810 - *     notmuch_query_t *query;\r
811 - *     notmuch_messages_t *messages;\r
812 - *     notmuch_message_t *message;\r
813 - *\r
814 - *     query = notmuch_query_create (database, query_string);\r
815 - *\r
816 - *     for (messages = notmuch_query_search_messages (query);\r
817 - *          notmuch_messages_valid (messages);\r
818 - *          notmuch_messages_move_to_next (messages))\r
819 - *     {\r
820 - *         message = notmuch_messages_get (messages);\r
821 - *         ....\r
822 - *         notmuch_message_destroy (message);\r
823 - *     }\r
824 - *\r
825 - *     notmuch_query_destroy (query);\r
826 - *\r
827 - * Note: If you are finished with a message before its containing\r
828 - * query, you can call notmuch_message_destroy to clean up some memory\r
829 - * sooner (as in the above example). Otherwise, if your message\r
830 - * objects are long-lived, then you don't need to call\r
831 - * notmuch_message_destroy and all the memory will still be reclaimed\r
832 - * when the query is destroyed.\r
833 - *\r
834 - * Note that there's no explicit destructor needed for the\r
835 - * notmuch_messages_t object. (For consistency, we do provide a\r
836 - * notmuch_messages_destroy function, but there's no good\r
837 - * reason to call it if the query is about to be destroyed).\r
838 - *\r
839 - * If a Xapian exception occurs this function will return NULL.\r
840 - */\r
841 -func (self *Query) SearchMessages() *Messages {\r
842 -       msgs := C.notmuch_query_search_messages(self.query)\r
843 -       if msgs == nil {\r
844 -               return nil\r
845 -       }\r
846 -       return &Messages{messages:msgs}\r
847 -}\r
848 -\r
849 -/* Destroy a notmuch_query_t along with any associated resources.\r
850 - *\r
851 - * This will in turn destroy any notmuch_threads_t and\r
852 - * notmuch_messages_t objects generated by this query, (and in\r
853 - * turn any notmuch_thread_t and notmuch_message_t objects generated\r
854 - * from those results, etc.), if such objects haven't already been\r
855 - * destroyed.\r
856 - */\r
857 -func (self *Query) Destroy() {\r
858 -       if self.query != nil {\r
859 -               C.notmuch_query_destroy(self.query)\r
860 -       }\r
861 -}\r
862 -\r
863 -/* Return an estimate of the number of messages matching a search\r
864 - *\r
865 - * This function performs a search and returns Xapian's best\r
866 - * guess as to number of matching messages.\r
867 - *\r
868 - * If a Xapian exception occurs, this function may return 0 (after\r
869 - * printing a message).\r
870 - */\r
871 -func (self *Query) CountMessages() uint {\r
872 -       return uint(C.notmuch_query_count_messages(self.query))\r
873 -}\r
874 -\r
875 -// TODO: wrap threads and thread\r
876 -\r
877 -/* Is the given 'threads' iterator pointing at a valid thread.\r
878 - *\r
879 - * When this function returns TRUE, notmuch_threads_get will return a\r
880 - * valid object. Whereas when this function returns FALSE,\r
881 - * notmuch_threads_get will return NULL.\r
882 - *\r
883 - * See the documentation of notmuch_query_search_threads for example\r
884 - * code showing how to iterate over a notmuch_threads_t object.\r
885 - */\r
886 -func (self *Threads) Valid() bool {\r
887 -       if self.threads == nil {\r
888 -               return false\r
889 -       }\r
890 -       valid := C.notmuch_threads_valid(self.threads)\r
891 -       if valid == 0 {\r
892 -               return false\r
893 -       }\r
894 -       return true\r
895 -}\r
896 -\r
897 -/* Destroy a notmuch_threads_t object.\r
898 - *\r
899 - * It's not strictly necessary to call this function. All memory from\r
900 - * the notmuch_threads_t object will be reclaimed when the\r
901 - * containg query object is destroyed.\r
902 - */\r
903 -func (self *Threads) Destroy() {\r
904 -       if self.threads != nil {\r
905 -               C.notmuch_threads_destroy(self.threads)\r
906 -       }\r
907 -}\r
908 -\r
909 -/* Is the given 'messages' iterator pointing at a valid message.\r
910 - *\r
911 - * When this function returns TRUE, notmuch_messages_get will return a\r
912 - * valid object. Whereas when this function returns FALSE,\r
913 - * notmuch_messages_get will return NULL.\r
914 - *\r
915 - * See the documentation of notmuch_query_search_messages for example\r
916 - * code showing how to iterate over a notmuch_messages_t object.\r
917 - */\r
918 -func (self *Messages) Valid() bool {\r
919 -       if self.messages == nil {\r
920 -               return false\r
921 -       }\r
922 -       valid := C.notmuch_messages_valid(self.messages)\r
923 -       if valid == 0 {\r
924 -               return false\r
925 -       }\r
926 -       return true\r
927 -}\r
928 -\r
929 -/* Get the current message from 'messages' as a notmuch_message_t.\r
930 - *\r
931 - * Note: The returned message belongs to 'messages' and has a lifetime\r
932 - * identical to it (and the query to which it belongs).\r
933 - *\r
934 - * See the documentation of notmuch_query_search_messages for example\r
935 - * code showing how to iterate over a notmuch_messages_t object.\r
936 - *\r
937 - * If an out-of-memory situation occurs, this function will return\r
938 - * NULL.\r
939 - */\r
940 -func (self *Messages) Get() *Message {\r
941 -       if self.messages == nil {\r
942 -               return nil\r
943 -       }\r
944 -       msg := C.notmuch_messages_get(self.messages)\r
945 -       if msg == nil {\r
946 -               return nil\r
947 -       }\r
948 -       return &Message{message:msg}\r
949 -}\r
950 -\r
951 -/* Move the 'messages' iterator to the next message.\r
952 - *\r
953 - * If 'messages' is already pointing at the last message then the\r
954 - * iterator will be moved to a point just beyond that last message,\r
955 - * (where notmuch_messages_valid will return FALSE and\r
956 - * notmuch_messages_get will return NULL).\r
957 - *\r
958 - * See the documentation of notmuch_query_search_messages for example\r
959 - * code showing how to iterate over a notmuch_messages_t object.\r
960 - */\r
961 -func (self *Messages) MoveToNext() {\r
962 -       if self.messages == nil {\r
963 -               return\r
964 -       }\r
965 -       C.notmuch_messages_move_to_next(self.messages)\r
966 -}\r
967 -\r
968 -/* Destroy a notmuch_messages_t object.\r
969 - *\r
970 - * It's not strictly necessary to call this function. All memory from\r
971 - * the notmuch_messages_t object will be reclaimed when the containing\r
972 - * query object is destroyed.\r
973 - */\r
974 -func (self *Messages) Destroy() {\r
975 -       if self.messages != nil {\r
976 -               C.notmuch_messages_destroy(self.messages)\r
977 -       }\r
978 -}\r
979 -\r
980 -/* Return a list of tags from all messages.\r
981 - *\r
982 - * The resulting list is guaranteed not to contain duplicated tags.\r
983 - *\r
984 - * WARNING: You can no longer iterate over messages after calling this\r
985 - * function, because the iterator will point at the end of the list.\r
986 - * We do not have a function to reset the iterator yet and the only\r
987 - * way how you can iterate over the list again is to recreate the\r
988 - * message list.\r
989 - *\r
990 - * The function returns NULL on error.\r
991 - */\r
992 -func (self *Messages) CollectTags() *Tags {\r
993 -       if self.messages == nil {\r
994 -               return nil\r
995 -       }\r
996 -       tags := C.notmuch_messages_collect_tags(self.messages)\r
997 -       if tags == nil {\r
998 -               return nil\r
999 -       }\r
1000 -       return &Tags{tags:tags}\r
1001 -}\r
1002 -\r
1003 -/* Get the message ID of 'message'.\r
1004 - *\r
1005 - * The returned string belongs to 'message' and as such, should not be\r
1006 - * modified by the caller and will only be valid for as long as the\r
1007 - * message is valid, (which is until the query from which it derived\r
1008 - * is destroyed).\r
1009 - *\r
1010 - * This function will not return NULL since Notmuch ensures that every\r
1011 - * message has a unique message ID, (Notmuch will generate an ID for a\r
1012 - * message if the original file does not contain one).\r
1013 - */\r
1014 -func (self *Message) GetMessageId() string {\r
1015 -\r
1016 -       if self.message == nil {\r
1017 -               return ""\r
1018 -       }\r
1019 -       id := C.notmuch_message_get_message_id(self.message)\r
1020 -       // we dont own id\r
1021 -       // defer C.free(unsafe.Pointer(id))\r
1022 -       if id == nil {\r
1023 -               return ""\r
1024 -       }\r
1025 -       return C.GoString(id)\r
1026 -}\r
1027 -\r
1028 -/* Get the thread ID of 'message'.\r
1029 - *\r
1030 - * The returned string belongs to 'message' and as such, should not be\r
1031 - * modified by the caller and will only be valid for as long as the\r
1032 - * message is valid, (for example, until the user calls\r
1033 - * notmuch_message_destroy on 'message' or until a query from which it\r
1034 - * derived is destroyed).\r
1035 - *\r
1036 - * This function will not return NULL since Notmuch ensures that every\r
1037 - * message belongs to a single thread.\r
1038 - */\r
1039 -func (self *Message) GetThreadId() string {\r
1040 -       \r
1041 -       if self.message == nil {\r
1042 -               return ""\r
1043 -       }\r
1044 -       id := C.notmuch_message_get_thread_id(self.message)\r
1045 -       // we dont own id\r
1046 -       // defer C.free(unsafe.Pointer(id))\r
1047 -       \r
1048 -       if id == nil {\r
1049 -               return ""\r
1050 -       }\r
1051 -\r
1052 -       return C.GoString(id)\r
1053 -}\r
1054 -\r
1055 -/* Get a notmuch_messages_t iterator for all of the replies to\r
1056 - * 'message'.\r
1057 - *\r
1058 - * Note: This call only makes sense if 'message' was ultimately\r
1059 - * obtained from a notmuch_thread_t object, (such as by coming\r
1060 - * directly from the result of calling notmuch_thread_get_\r
1061 - * toplevel_messages or by any number of subsequent\r
1062 - * calls to notmuch_message_get_replies).\r
1063 - *\r
1064 - * If 'message' was obtained through some non-thread means, (such as\r
1065 - * by a call to notmuch_query_search_messages), then this function\r
1066 - * will return NULL.\r
1067 - *\r
1068 - * If there are no replies to 'message', this function will return\r
1069 - * NULL. (Note that notmuch_messages_valid will accept that NULL\r
1070 - * value as legitimate, and simply return FALSE for it.)\r
1071 - */\r
1072 -func (self *Message) GetReplies() *Messages {\r
1073 -       if self.message == nil {\r
1074 -               return nil\r
1075 -       }\r
1076 -       msgs := C.notmuch_message_get_replies(self.message)\r
1077 -       if msgs == nil {\r
1078 -               return nil\r
1079 -       }\r
1080 -       return &Messages{messages:msgs}\r
1081 -}\r
1082 -\r
1083 -/* Get a filename for the email corresponding to 'message'.\r
1084 - *\r
1085 - * The returned filename is an absolute filename, (the initial\r
1086 - * component will match notmuch_database_get_path() ).\r
1087 - *\r
1088 - * The returned string belongs to the message so should not be\r
1089 - * modified or freed by the caller (nor should it be referenced after\r
1090 - * the message is destroyed).\r
1091 - *\r
1092 - * Note: If this message corresponds to multiple files in the mail\r
1093 - * store, (that is, multiple files contain identical message IDs),\r
1094 - * this function will arbitrarily return a single one of those\r
1095 - * filenames.\r
1096 - */\r
1097 -func (self *Message) GetFileName() string {\r
1098 -       if self.message == nil {\r
1099 -               return ""\r
1100 -       }\r
1101 -       fname := C.notmuch_message_get_filename(self.message)\r
1102 -       // we dont own fname\r
1103 -       // defer C.free(unsafe.Pointer(fname))\r
1104 -       \r
1105 -       if fname == nil {\r
1106 -               return ""\r
1107 -       }\r
1108 -\r
1109 -       return C.GoString(fname)\r
1110 -}\r
1111 -\r
1112 -type Flag C.notmuch_message_flag_t\r
1113 -const (\r
1114 -       MESSAGE_FLAG_MATCH Flag = 0\r
1115 -)\r
1116 -\r
1117 -/* Get a value of a flag for the email corresponding to 'message'. */\r
1118 -func (self *Message) GetFlag(flag Flag) bool {\r
1119 -       if self.message == nil {\r
1120 -               return false\r
1121 -       }\r
1122 -       v := C.notmuch_message_get_flag(self.message, C.notmuch_message_flag_t(flag))\r
1123 -       if v == 0 {\r
1124 -               return false\r
1125 -       }\r
1126 -       return true\r
1127 -}\r
1128 -\r
1129 -/* Set a value of a flag for the email corresponding to 'message'. */\r
1130 -func (self *Message) SetFlag(flag Flag, value bool) {\r
1131 -       if self.message == nil {\r
1132 -               return\r
1133 -       }\r
1134 -       var v C.notmuch_bool_t = 0\r
1135 -       if value {\r
1136 -               v = 1\r
1137 -       }\r
1138 -       C.notmuch_message_set_flag(self.message, C.notmuch_message_flag_t(flag), v)\r
1139 -}\r
1140 -\r
1141 -// TODO: wrap notmuch_message_get_date\r
1142 -\r
1143 -/* Get the value of the specified header from 'message'.\r
1144 - *\r
1145 - * The value will be read from the actual message file, not from the\r
1146 - * notmuch database. The header name is case insensitive.\r
1147 - *\r
1148 - * The returned string belongs to the message so should not be\r
1149 - * modified or freed by the caller (nor should it be referenced after\r
1150 - * the message is destroyed).\r
1151 - *\r
1152 - * Returns an empty string ("") if the message does not contain a\r
1153 - * header line matching 'header'. Returns NULL if any error occurs.\r
1154 - */\r
1155 -func (self *Message) GetHeader(header string) string {\r
1156 -       if self.message == nil {\r
1157 -               return ""\r
1158 -       }\r
1159 -       \r
1160 -       var c_header *C.char = C.CString(header)\r
1161 -       defer C.free(unsafe.Pointer(c_header))\r
1162 -       \r
1163 -       /* we dont own value */\r
1164 -       value := C.notmuch_message_get_header(self.message, c_header)\r
1165 -       if value == nil {\r
1166 -               return ""\r
1167 -       }\r
1168 -       \r
1169 -       return C.GoString(value)\r
1170 -}\r
1171 -\r
1172 -/* Get the tags for 'message', returning a notmuch_tags_t object which\r
1173 - * can be used to iterate over all tags.\r
1174 - *\r
1175 - * The tags object is owned by the message and as such, will only be\r
1176 - * valid for as long as the message is valid, (which is until the\r
1177 - * query from which it derived is destroyed).\r
1178 - *\r
1179 - * Typical usage might be:\r
1180 - *\r
1181 - *     notmuch_message_t *message;\r
1182 - *     notmuch_tags_t *tags;\r
1183 - *     const char *tag;\r
1184 - *\r
1185 - *     message = notmuch_database_find_message (database, message_id);\r
1186 - *\r
1187 - *     for (tags = notmuch_message_get_tags (message);\r
1188 - *          notmuch_tags_valid (tags);\r
1189 - *          notmuch_result_move_to_next (tags))\r
1190 - *     {\r
1191 - *         tag = notmuch_tags_get (tags);\r
1192 - *         ....\r
1193 - *     }\r
1194 - *\r
1195 - *     notmuch_message_destroy (message);\r
1196 - *\r
1197 - * Note that there's no explicit destructor needed for the\r
1198 - * notmuch_tags_t object. (For consistency, we do provide a\r
1199 - * notmuch_tags_destroy function, but there's no good reason to call\r
1200 - * it if the message is about to be destroyed).\r
1201 - */\r
1202 -func (self *Message) GetTags() *Tags {\r
1203 -       if self.message == nil {\r
1204 -               return nil\r
1205 -       }\r
1206 -       tags := C.notmuch_message_get_tags(self.message)\r
1207 -       if tags == nil {\r
1208 -               return nil\r
1209 -       }\r
1210 -       return &Tags{tags:tags}\r
1211 -}\r
1212 -\r
1213 -/* The longest possible tag value. */\r
1214 -const TAG_MAX = 200\r
1215 -\r
1216 -/* Add a tag to the given message.\r
1217 - *\r
1218 - * Return value:\r
1219 - *\r
1220 - * NOTMUCH_STATUS_SUCCESS: Tag successfully added to message\r
1221 - *\r
1222 - * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL\r
1223 - *\r
1224 - * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long\r
1225 - *     (exceeds NOTMUCH_TAG_MAX)\r
1226 - *\r
1227 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
1228 - *     mode so message cannot be modified.\r
1229 - */\r
1230 -func (self *Message) AddTag(tag string) Status {\r
1231 -       if self.message == nil {\r
1232 -               return STATUS_NULL_POINTER\r
1233 -       }\r
1234 -       c_tag := C.CString(tag)\r
1235 -       defer C.free(unsafe.Pointer(c_tag))\r
1236 -\r
1237 -       return Status(C.notmuch_message_add_tag(self.message, c_tag))\r
1238 -}\r
1239 -\r
1240 -/* Remove a tag from the given message.\r
1241 - *\r
1242 - * Return value:\r
1243 - *\r
1244 - * NOTMUCH_STATUS_SUCCESS: Tag successfully removed from message\r
1245 - *\r
1246 - * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL\r
1247 - *\r
1248 - * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long\r
1249 - *     (exceeds NOTMUCH_TAG_MAX)\r
1250 - *\r
1251 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
1252 - *     mode so message cannot be modified.\r
1253 - */\r
1254 -func (self *Message) RemoveTag(tag string) Status {\r
1255 -       if self.message == nil {\r
1256 -               return STATUS_NULL_POINTER\r
1257 -       }\r
1258 -       c_tag := C.CString(tag)\r
1259 -       defer C.free(unsafe.Pointer(c_tag))\r
1260 -\r
1261 -       return Status(C.notmuch_message_remove_tag(self.message, c_tag))\r
1262 -}\r
1263 -\r
1264 -/* Remove all tags from the given message.\r
1265 - *\r
1266 - * See notmuch_message_freeze for an example showing how to safely\r
1267 - * replace tag values.\r
1268 - *\r
1269 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
1270 - *     mode so message cannot be modified.\r
1271 - */\r
1272 -func (self *Message) RemoveAllTags() Status {\r
1273 -       if self.message == nil {\r
1274 -               return STATUS_NULL_POINTER\r
1275 -       }\r
1276 -       return Status(C.notmuch_message_remove_all_tags(self.message))\r
1277 -}\r
1278 -\r
1279 -/* Freeze the current state of 'message' within the database.\r
1280 - *\r
1281 - * This means that changes to the message state, (via\r
1282 - * notmuch_message_add_tag, notmuch_message_remove_tag, and\r
1283 - * notmuch_message_remove_all_tags), will not be committed to the\r
1284 - * database until the message is thawed with notmuch_message_thaw.\r
1285 - *\r
1286 - * Multiple calls to freeze/thaw are valid and these calls will\r
1287 - * "stack". That is there must be as many calls to thaw as to freeze\r
1288 - * before a message is actually thawed.\r
1289 - *\r
1290 - * The ability to do freeze/thaw allows for safe transactions to\r
1291 - * change tag values. For example, explicitly setting a message to\r
1292 - * have a given set of tags might look like this:\r
1293 - *\r
1294 - *    notmuch_message_freeze (message);\r
1295 - *\r
1296 - *    notmuch_message_remove_all_tags (message);\r
1297 - *\r
1298 - *    for (i = 0; i < NUM_TAGS; i++)\r
1299 - *        notmuch_message_add_tag (message, tags[i]);\r
1300 - *\r
1301 - *    notmuch_message_thaw (message);\r
1302 - *\r
1303 - * With freeze/thaw used like this, the message in the database is\r
1304 - * guaranteed to have either the full set of original tag values, or\r
1305 - * the full set of new tag values, but nothing in between.\r
1306 - *\r
1307 - * Imagine the example above without freeze/thaw and the operation\r
1308 - * somehow getting interrupted. This could result in the message being\r
1309 - * left with no tags if the interruption happened after\r
1310 - * notmuch_message_remove_all_tags but before notmuch_message_add_tag.\r
1311 - *\r
1312 - * Return value:\r
1313 - *\r
1314 - * NOTMUCH_STATUS_SUCCESS: Message successfully frozen.\r
1315 - *\r
1316 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
1317 - *     mode so message cannot be modified.\r
1318 - */\r
1319 -func (self *Message) Freeze() Status {\r
1320 -       if self.message == nil {\r
1321 -               return STATUS_NULL_POINTER\r
1322 -       }\r
1323 -       return Status(C.notmuch_message_freeze(self.message))\r
1324 -}\r
1325 -\r
1326 -/* Thaw the current 'message', synchronizing any changes that may have\r
1327 - * occurred while 'message' was frozen into the notmuch database.\r
1328 - *\r
1329 - * See notmuch_message_freeze for an example of how to use this\r
1330 - * function to safely provide tag changes.\r
1331 - *\r
1332 - * Multiple calls to freeze/thaw are valid and these calls with\r
1333 - * "stack". That is there must be as many calls to thaw as to freeze\r
1334 - * before a message is actually thawed.\r
1335 - *\r
1336 - * Return value:\r
1337 - *\r
1338 - * NOTMUCH_STATUS_SUCCESS: Message successfully thawed, (or at least\r
1339 - *     its frozen count has successfully been reduced by 1).\r
1340 - *\r
1341 - * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: An attempt was made to thaw\r
1342 - *     an unfrozen message. That is, there have been an unbalanced\r
1343 - *     number of calls to notmuch_message_freeze and\r
1344 - *     notmuch_message_thaw.\r
1345 - */\r
1346 -func (self *Message) Thaw() Status {\r
1347 -       if self.message == nil {\r
1348 -               return STATUS_NULL_POINTER\r
1349 -       }\r
1350 -\r
1351 -       return Status(C.notmuch_message_thaw(self.message))\r
1352 -}\r
1353 -\r
1354 -/* Destroy a notmuch_message_t object.\r
1355 - *\r
1356 - * It can be useful to call this function in the case of a single\r
1357 - * query object with many messages in the result, (such as iterating\r
1358 - * over the entire database). Otherwise, it's fine to never call this\r
1359 - * function and there will still be no memory leaks. (The memory from\r
1360 - * the messages get reclaimed when the containing query is destroyed.)\r
1361 - */\r
1362 -func (self *Message) Destroy() {\r
1363 -       if self.message == nil {\r
1364 -               return\r
1365 -       }\r
1366 -       C.notmuch_message_destroy(self.message)\r
1367 -}\r
1368 -\r
1369 -/* Is the given 'tags' iterator pointing at a valid tag.\r
1370 - *\r
1371 - * When this function returns TRUE, notmuch_tags_get will return a\r
1372 - * valid string. Whereas when this function returns FALSE,\r
1373 - * notmuch_tags_get will return NULL.\r
1374 - *\r
1375 - * See the documentation of notmuch_message_get_tags for example code\r
1376 - * showing how to iterate over a notmuch_tags_t object.\r
1377 - */\r
1378 -func (self *Tags) Valid() bool {\r
1379 -       if self.tags == nil {\r
1380 -               return false\r
1381 -       }\r
1382 -       v := C.notmuch_tags_valid(self.tags)\r
1383 -       if v == 0 {\r
1384 -               return false\r
1385 -       }\r
1386 -       return true\r
1387 -}\r
1388 -\r
1389 -/* Get the current tag from 'tags' as a string.\r
1390 - *\r
1391 - * Note: The returned string belongs to 'tags' and has a lifetime\r
1392 - * identical to it (and the query to which it ultimately belongs).\r
1393 - *\r
1394 - * See the documentation of notmuch_message_get_tags for example code\r
1395 - * showing how to iterate over a notmuch_tags_t object.\r
1396 - */\r
1397 -func (self *Tags) Get() string {\r
1398 -       if self.tags == nil {\r
1399 -               return ""\r
1400 -       }\r
1401 -       s := C.notmuch_tags_get(self.tags)\r
1402 -       // we dont own 's'\r
1403 -\r
1404 -       return C.GoString(s)\r
1405 -}\r
1406 -func (self *Tags) String() string {\r
1407 -       return self.Get()\r
1408 -}\r
1409 -\r
1410 -/* Move the 'tags' iterator to the next tag.\r
1411 - *\r
1412 - * If 'tags' is already pointing at the last tag then the iterator\r
1413 - * will be moved to a point just beyond that last tag, (where\r
1414 - * notmuch_tags_valid will return FALSE and notmuch_tags_get will\r
1415 - * return NULL).\r
1416 - *\r
1417 - * See the documentation of notmuch_message_get_tags for example code\r
1418 - * showing how to iterate over a notmuch_tags_t object.\r
1419 - */\r
1420 -func (self *Tags) MoveToNext() {\r
1421 -       if self.tags == nil {\r
1422 -               return\r
1423 -       }\r
1424 -       C.notmuch_tags_move_to_next(self.tags)\r
1425 -}\r
1426 -\r
1427 -/* Destroy a notmuch_tags_t object.\r
1428 - *\r
1429 - * It's not strictly necessary to call this function. All memory from\r
1430 - * the notmuch_tags_t object will be reclaimed when the containing\r
1431 - * message or query objects are destroyed.\r
1432 - */\r
1433 -func (self *Tags) Destroy() {\r
1434 -       if self.tags == nil {\r
1435 -               return\r
1436 -       }\r
1437 -       C.notmuch_tags_destroy(self.tags)\r
1438 -}\r
1439 -\r
1440 -// TODO: wrap notmuch_directory_<fct>\r
1441 -\r
1442 -/* Destroy a notmuch_directory_t object. */\r
1443 -func (self *Directory) Destroy() {\r
1444 -       if self.dir == nil {\r
1445 -               return\r
1446 -       }\r
1447 -       C.notmuch_directory_destroy(self.dir)\r
1448 -}\r
1449 -\r
1450 -// TODO: wrap notmuch_filenames_<fct>\r
1451 -\r
1452 -/* Destroy a notmuch_filenames_t object.\r
1453 - *\r
1454 - * It's not strictly necessary to call this function. All memory from\r
1455 - * the notmuch_filenames_t object will be reclaimed when the\r
1456 - * containing directory object is destroyed.\r
1457 - *\r
1458 - * It is acceptable to pass NULL for 'filenames', in which case this\r
1459 - * function will do nothing.\r
1460 - */\r
1461 -func (self *Filenames) Destroy() {\r
1462 -       if self.fnames == nil {\r
1463 -               return\r
1464 -       }\r
1465 -       C.notmuch_filenames_destroy(self.fnames)\r
1466 -}\r
1467 -/* EOF */\r
1468 diff --git a/bindings/go/src/notmuch-addrlookup/addrlookup.go b/bindings/go/src/notmuch-addrlookup/addrlookup.go\r
1469 new file mode 100644\r
1470 index 0000000..16958e5\r
1471 --- /dev/null\r
1472 +++ b/bindings/go/src/notmuch-addrlookup/addrlookup.go\r
1473 @@ -0,0 +1,259 @@\r
1474 +package main\r
1475 +\r
1476 +// stdlib imports\r
1477 +import "os"\r
1478 +import "path"\r
1479 +import "log"\r
1480 +import "fmt"\r
1481 +import "regexp"\r
1482 +import "strings"\r
1483 +import "sort"\r
1484 +\r
1485 +// 3rd-party imports\r
1486 +import "notmuch"\r
1487 +import "github.com/kless/goconfig/config"\r
1488 +\r
1489 +type mail_addr_freq struct {\r
1490 +       addr  string\r
1491 +       count [3]uint\r
1492 +}\r
1493 +\r
1494 +type frequencies map[string]uint\r
1495 +\r
1496 +/* Used to sort the email addresses from most to least used */\r
1497 +func sort_by_freq(m1, m2 *mail_addr_freq) int {\r
1498 +       if (m1.count[0] == m2.count[0] &&\r
1499 +               m1.count[1] == m2.count[1] &&\r
1500 +               m1.count[2] == m2.count[2]) {\r
1501 +               return 0\r
1502 +       }\r
1503 +\r
1504 +       if (m1.count[0] >  m2.count[0] ||\r
1505 +               m1.count[0] == m2.count[0] &&\r
1506 +               m1.count[1] >  m2.count[1] ||\r
1507 +               m1.count[0] == m2.count[0] &&\r
1508 +               m1.count[1] == m2.count[1] &&\r
1509 +               m1.count[2] >  m2.count[2]) {\r
1510 +               return -1\r
1511 +       }\r
1512 +\r
1513 +       return 1\r
1514 +}\r
1515 +\r
1516 +type maddresses []*mail_addr_freq\r
1517 +\r
1518 +func (self *maddresses) Len() int {\r
1519 +       return len(*self)\r
1520 +}\r
1521 +\r
1522 +func (self *maddresses) Less(i,j int) bool {\r
1523 +       m1 := (*self)[i]\r
1524 +       m2 := (*self)[j]\r
1525 +       v  := sort_by_freq(m1, m2)\r
1526 +       if v<=0 {\r
1527 +               return true\r
1528 +       }\r
1529 +       return false\r
1530 +}\r
1531 +\r
1532 +func (self *maddresses) Swap(i,j int) {\r
1533 +       (*self)[i], (*self)[j] = (*self)[j], (*self)[i]\r
1534 +}\r
1535 +\r
1536 +// find most frequent real name for each mail address\r
1537 +func frequent_fullname(freqs frequencies) string {\r
1538 +       var maxfreq uint = 0\r
1539 +       fullname := ""\r
1540 +       freqs_sz := len(freqs)\r
1541 +\r
1542 +       for mail,freq := range freqs {\r
1543 +               if (freq > maxfreq && mail != "") || freqs_sz == 1 {\r
1544 +                       // only use the entry if it has a real name\r
1545 +                       // or if this is the only entry\r
1546 +                       maxfreq = freq\r
1547 +                       fullname = mail\r
1548 +               }\r
1549 +       }\r
1550 +       return fullname\r
1551 +}\r
1552 +\r
1553 +func addresses_by_frequency(msgs *notmuch.Messages, name string, pass uint, addr_to_realname *map[string]*frequencies) *frequencies {\r
1554 +\r
1555 +       freqs := make(frequencies)\r
1556 +\r
1557 +       pattern := `\s*(("(\.|[^"])*"|[^,])*<?(?mail\b\w+([-+.]\w+)*\@\w+[-\.\w]*\.([-\.\w]+)*\w\b)>?)`\r
1558 +       // pattern := "\\s*((\\\"(\\\\.|[^\\\\\"])*\\\"|[^,])*" +\r
1559 +       //      "<?(?P<mail>\\b\\w+([-+.]\\w+)*\\@\\w+[-\\.\\w]*\\.([-\\.\\w]+)*\\w\\b)>?)"\r
1560 +       pattern = `.*` + strings.ToLower(name) + `.*`\r
1561 +       var re *regexp.Regexp = nil\r
1562 +       var err os.Error = nil\r
1563 +       if re,err = regexp.Compile(pattern); err != nil {\r
1564 +               log.Printf("error: %v\n", err)\r
1565 +               return &freqs\r
1566 +       }\r
1567 +       \r
1568 +       headers := []string{"from"}\r
1569 +       if pass == 1 {\r
1570 +               headers = append(headers, "to", "cc", "bcc")\r
1571 +       }\r
1572 +\r
1573 +       for ;msgs.Valid();msgs.MoveToNext() {\r
1574 +               msg := msgs.Get()\r
1575 +               //println("==> msg [", msg.GetMessageId(), "]")\r
1576 +               for _,header := range headers {\r
1577 +                       froms := strings.ToLower(msg.GetHeader(header))\r
1578 +                       //println("  froms: ["+froms+"]")\r
1579 +                       for _,from := range strings.Split(froms, ",", -1) {\r
1580 +                               from = strings.Trim(from, " ")\r
1581 +                               match := re.FindString(from)\r
1582 +                               //println("  -> match: ["+match+"]")\r
1583 +                               occ,ok := freqs[match]\r
1584 +                               if !ok {\r
1585 +                                       freqs[match] = 0\r
1586 +                                       occ = 0\r
1587 +                               }\r
1588 +                               freqs[match] = occ+1\r
1589 +                       }\r
1590 +               }\r
1591 +       }\r
1592 +       return &freqs\r
1593 +}\r
1594 +\r
1595 +func search_address_passes(queries [3]*notmuch.Query, name string) []string {\r
1596 +       var val []string\r
1597 +       addr_freq := make(map[string]*mail_addr_freq)\r
1598 +       addr_to_realname := make(map[string]*frequencies)\r
1599 +\r
1600 +       var pass uint = 0 // 0-based\r
1601 +       for _,query := range queries {\r
1602 +               if query == nil {\r
1603 +                       //println("**warning: idx [",idx,"] contains a nil query")\r
1604 +                       continue\r
1605 +               }\r
1606 +               msgs := query.SearchMessages()\r
1607 +               ht := addresses_by_frequency(msgs, name, pass, &addr_to_realname)\r
1608 +               for addr, count := range *ht {\r
1609 +                       freq,ok := addr_freq[addr]\r
1610 +                       if !ok {\r
1611 +                               freq = &mail_addr_freq{addr:addr, count:[3]uint{0,0,0}}\r
1612 +                       }\r
1613 +                       freq.count[pass] = count\r
1614 +                       addr_freq[addr] = freq\r
1615 +               }\r
1616 +               msgs.Destroy()\r
1617 +               pass += 1\r
1618 +       }\r
1619 +\r
1620 +       addrs := make(maddresses, len(addr_freq))\r
1621 +       {\r
1622 +               iaddr := 0\r
1623 +               for _, freq := range addr_freq {\r
1624 +                       addrs[iaddr] = freq\r
1625 +                       iaddr += 1\r
1626 +               }\r
1627 +       }\r
1628 +       sort.Sort(&addrs)\r
1629 +\r
1630 +       for _,addr := range addrs {\r
1631 +               freqs,ok := addr_to_realname[addr.addr]\r
1632 +               if ok {\r
1633 +                       val = append(val, frequent_fullname(*freqs))\r
1634 +               } else {\r
1635 +                       val = append(val, addr.addr)\r
1636 +               }\r
1637 +       }\r
1638 +       //println("val:",val)\r
1639 +       return val\r
1640 +}\r
1641 +\r
1642 +type address_matcher struct {\r
1643 +       // the notmuch database\r
1644 +       db *notmuch.Database\r
1645 +       // full path of the notmuch database\r
1646 +       user_db_path string\r
1647 +       // user primary email\r
1648 +       user_primary_email string\r
1649 +       // user tag to mark from addresses as in the address book\r
1650 +       user_addrbook_tag string\r
1651 +}\r
1652 +\r
1653 +func new_address_matcher() *address_matcher {\r
1654 +       var cfg *config.Config\r
1655 +       var err os.Error\r
1656 +\r
1657 +       // honor NOTMUCH_CONFIG\r
1658 +       home := os.Getenv("NOTMUCH_CONFIG")\r
1659 +       if home == "" {\r
1660 +               home = os.Getenv("HOME")\r
1661 +       }\r
1662 +\r
1663 +       if cfg,err = config.ReadDefault(path.Join(home, ".notmuch-config")); err != nil {\r
1664 +               log.Fatalf("error loading config file:",err)\r
1665 +       }\r
1666 +\r
1667 +       db_path,_ := cfg.String("database", "path")\r
1668 +       primary_email,_ := cfg.String("user", "primary_email")\r
1669 +       addrbook_tag,err := cfg.String("user", "addrbook_tag")\r
1670 +       if err != nil {\r
1671 +               addrbook_tag = "addressbook"\r
1672 +       }\r
1673 +\r
1674 +       self := &address_matcher{db:nil, \r
1675 +                                user_db_path:db_path,\r
1676 +                                user_primary_email:primary_email,\r
1677 +                                user_addrbook_tag:addrbook_tag}\r
1678 +       return self\r
1679 +}\r
1680 +\r
1681 +func (self *address_matcher) run(name string) {\r
1682 +       queries := [3]*notmuch.Query{}\r
1683 +       \r
1684 +       // open the database\r
1685 +       self.db = notmuch.OpenDatabase(self.user_db_path, \r
1686 +               notmuch.DATABASE_MODE_READ_ONLY)\r
1687 +\r
1688 +       // pass 1: look at all from: addresses with the address book tag\r
1689 +       query := "tag:" + self.user_addrbook_tag\r
1690 +       if name != "" {\r
1691 +               query = query + " and from:" + name + "*"\r
1692 +       }\r
1693 +       queries[0] = self.db.CreateQuery(query)\r
1694 +\r
1695 +       // pass 2: look at all to: addresses sent from our primary mail\r
1696 +       query = ""\r
1697 +       if name != "" {\r
1698 +               query = "to:"+name+"*"\r
1699 +       }\r
1700 +       if self.user_primary_email != "" {\r
1701 +               query = query + " from:" + self.user_primary_email\r
1702 +       }\r
1703 +       queries[1] = self.db.CreateQuery(query)\r
1704 +\r
1705 +       // if that leads only to a few hits, we check every from too\r
1706 +       if queries[0].CountMessages() + queries[1].CountMessages() < 10 {\r
1707 +               query = ""\r
1708 +               if name != "" {\r
1709 +                       query = "from:"+name+"*"\r
1710 +               }\r
1711 +               queries[2] = self.db.CreateQuery(query)\r
1712 +       }\r
1713 +       \r
1714 +       // actually retrieve and sort addresses\r
1715 +       results := search_address_passes(queries, name)\r
1716 +       for _,v := range results {\r
1717 +               if v != "" && v != "\n" {\r
1718 +                       fmt.Println(v)\r
1719 +               }\r
1720 +       }\r
1721 +       return\r
1722 +}\r
1723 +\r
1724 +func main() {\r
1725 +       //fmt.Println("args:",os.Args)\r
1726 +       app := new_address_matcher()\r
1727 +       name := ""\r
1728 +       if len(os.Args) > 1 {\r
1729 +               name = os.Args[1]\r
1730 +       }\r
1731 +       app.run(name)\r
1732 +}\r
1733 \ No newline at end of file\r
1734 diff --git a/bindings/go/src/notmuch/notmuch.go b/bindings/go/src/notmuch/notmuch.go\r
1735 new file mode 100644\r
1736 index 0000000..de9de23\r
1737 --- /dev/null\r
1738 +++ b/bindings/go/src/notmuch/notmuch.go\r
1739 @@ -0,0 +1,1124 @@\r
1740 +// Wrapper around the notmuch library\r
1741 +\r
1742 +package notmuch\r
1743 +\r
1744 +/*\r
1745 +#include <stdlib.h>\r
1746 +#include <string.h>\r
1747 +#include <time.h>\r
1748 +#include "notmuch.h"\r
1749 +*/\r
1750 +import "C"\r
1751 +import "unsafe"\r
1752 +\r
1753 +// Status codes used for the return values of most functions\r
1754 +type Status C.notmuch_status_t\r
1755 +const (\r
1756 +       STATUS_SUCCESS Status = 0\r
1757 +       STATUS_OUT_OF_MEMORY\r
1758 +    STATUS_READ_ONLY_DATABASE\r
1759 +    STATUS_XAPIAN_EXCEPTION\r
1760 +    STATUS_FILE_ERROR\r
1761 +    STATUS_FILE_NOT_EMAIL\r
1762 +    STATUS_DUPLICATE_MESSAGE_ID\r
1763 +    STATUS_NULL_POINTER\r
1764 +    STATUS_TAG_TOO_LONG\r
1765 +    STATUS_UNBALANCED_FREEZE_THAW\r
1766 +\r
1767 +    STATUS_LAST_STATUS\r
1768 +)\r
1769 +\r
1770 +func (self Status) String() string {\r
1771 +       var p *C.char\r
1772 +       \r
1773 +       // p is read-only\r
1774 +       p = C.notmuch_status_to_string(C.notmuch_status_t(self))\r
1775 +       if p != nil {\r
1776 +               s := C.GoString(p)\r
1777 +               return s\r
1778 +       }\r
1779 +       return ""\r
1780 +}\r
1781 +\r
1782 +/* Various opaque data types. For each notmuch_<foo>_t see the various\r
1783 + * notmuch_<foo> functions below. */\r
1784 +\r
1785 +type Database struct {\r
1786 +       db *C.notmuch_database_t\r
1787 +}\r
1788 +\r
1789 +type Query struct {\r
1790 +       query *C.notmuch_query_t\r
1791 +}\r
1792 +\r
1793 +type Threads struct {\r
1794 +       threads *C.notmuch_threads_t\r
1795 +}\r
1796 +\r
1797 +type Thread struct {\r
1798 +       thread *C.notmuch_thread_t\r
1799 +}\r
1800 +\r
1801 +type Messages struct {\r
1802 +       messages *C.notmuch_messages_t\r
1803 +}\r
1804 +\r
1805 +type Message struct {\r
1806 +       message *C.notmuch_message_t\r
1807 +}\r
1808 +\r
1809 +type Tags struct {\r
1810 +       tags *C.notmuch_tags_t\r
1811 +}\r
1812 +\r
1813 +type Directory struct {\r
1814 +       dir *C.notmuch_directory_t\r
1815 +}\r
1816 +\r
1817 +type Filenames struct {\r
1818 +       fnames *C.notmuch_filenames_t\r
1819 +}\r
1820 +\r
1821 +type DatabaseMode C.notmuch_database_mode_t\r
1822 +const (\r
1823 +    DATABASE_MODE_READ_ONLY DatabaseMode = 0\r
1824 +    DATABASE_MODE_READ_WRITE\r
1825 +)\r
1826 +\r
1827 +// Create a new, empty notmuch database located at 'path'\r
1828 +func NewDatabase(path string) *Database {\r
1829 +\r
1830 +       var c_path *C.char = C.CString(path)\r
1831 +       defer C.free(unsafe.Pointer(c_path))\r
1832 +\r
1833 +       if c_path == nil {\r
1834 +               return nil\r
1835 +       }\r
1836 +\r
1837 +       self := &Database{db:nil}\r
1838 +       self.db = C.notmuch_database_create(c_path)\r
1839 +       if self.db == nil {\r
1840 +               return nil\r
1841 +       }\r
1842 +       return self\r
1843 +}\r
1844 +\r
1845 +/* Open an existing notmuch database located at 'path'.\r
1846 + *\r
1847 + * The database should have been created at some time in the past,\r
1848 + * (not necessarily by this process), by calling\r
1849 + * notmuch_database_create with 'path'. By default the database should be\r
1850 + * opened for reading only. In order to write to the database you need to\r
1851 + * pass the NOTMUCH_DATABASE_MODE_READ_WRITE mode.\r
1852 + *\r
1853 + * An existing notmuch database can be identified by the presence of a\r
1854 + * directory named ".notmuch" below 'path'.\r
1855 + *\r
1856 + * The caller should call notmuch_database_destroy when finished with\r
1857 + * this database.\r
1858 + *\r
1859 + * In case of any failure, this function returns NULL, (after printing\r
1860 + * an error message on stderr).\r
1861 + */\r
1862 +func OpenDatabase(path string, mode DatabaseMode) *Database {\r
1863 +\r
1864 +       var c_path *C.char = C.CString(path)\r
1865 +       defer C.free(unsafe.Pointer(c_path))\r
1866 +\r
1867 +       if c_path == nil {\r
1868 +               return nil\r
1869 +       }\r
1870 +\r
1871 +       self := &Database{db:nil}\r
1872 +       self.db = C.notmuch_database_open(c_path, C.notmuch_database_mode_t(mode))\r
1873 +       if self.db == nil {\r
1874 +               return nil\r
1875 +       }\r
1876 +       return self\r
1877 +}\r
1878 +\r
1879 +/* Close the given notmuch database, freeing all associated\r
1880 + * resources. See notmuch_database_open. */\r
1881 +func (self *Database) Close() {\r
1882 +       C.notmuch_database_destroy(self.db)\r
1883 +}\r
1884 +\r
1885 +/* Return the database path of the given database.\r
1886 + */\r
1887 +func (self *Database) GetPath() string {\r
1888 +       \r
1889 + /* The return value is a string owned by notmuch so should not be\r
1890 +  * modified nor freed by the caller. */\r
1891 +       var p *C.char = C.notmuch_database_get_path(self.db)\r
1892 +       if p != nil {\r
1893 +               s := C.GoString(p)\r
1894 +               return s\r
1895 +       }\r
1896 +       return ""\r
1897 +}\r
1898 +\r
1899 +/* Return the database format version of the given database. */\r
1900 +func (self *Database) GetVersion() uint {\r
1901 +       return uint(C.notmuch_database_get_version(self.db))\r
1902 +}\r
1903 +\r
1904 +/* Does this database need to be upgraded before writing to it?\r
1905 + *\r
1906 + * If this function returns TRUE then no functions that modify the\r
1907 + * database (notmuch_database_add_message, notmuch_message_add_tag,\r
1908 + * notmuch_directory_set_mtime, etc.) will work unless the function\r
1909 + * notmuch_database_upgrade is called successfully first. */\r
1910 +func (self *Database) NeedsUpgrade() bool {\r
1911 +       do_upgrade := C.notmuch_database_needs_upgrade(self.db)\r
1912 +       if do_upgrade == 0 {\r
1913 +               return false\r
1914 +       }\r
1915 +       return true\r
1916 +}\r
1917 +\r
1918 +// TODO: notmuch_database_upgrade\r
1919 +\r
1920 +\r
1921 +/* Retrieve a directory object from the database for 'path'.\r
1922 + *\r
1923 + * Here, 'path' should be a path relative to the path of 'database'\r
1924 + * (see notmuch_database_get_path), or else should be an absolute path\r
1925 + * with initial components that match the path of 'database'.\r
1926 + *\r
1927 + * Can return NULL if a Xapian exception occurs.\r
1928 + */\r
1929 +func (self *Database) GetDirectory(path string) *Directory {\r
1930 +       var c_path *C.char = C.CString(path)\r
1931 +       defer C.free(unsafe.Pointer(c_path))\r
1932 +\r
1933 +       if c_path == nil {\r
1934 +               return nil\r
1935 +       }\r
1936 +\r
1937 +       c_dir := C.notmuch_database_get_directory(self.db, c_path)\r
1938 +       if c_dir == nil {\r
1939 +               return nil\r
1940 +       }\r
1941 +       return &Directory{dir:c_dir}\r
1942 +}\r
1943 +\r
1944 +/* Add a new message to the given notmuch database.\r
1945 + *\r
1946 + * Here,'filename' should be a path relative to the path of\r
1947 + * 'database' (see notmuch_database_get_path), or else should be an\r
1948 + * absolute filename with initial components that match the path of\r
1949 + * 'database'.\r
1950 + *\r
1951 + * The file should be a single mail message (not a multi-message mbox)\r
1952 + * that is expected to remain at its current location, (since the\r
1953 + * notmuch database will reference the filename, and will not copy the\r
1954 + * entire contents of the file.\r
1955 + *\r
1956 + * If 'message' is not NULL, then, on successful return '*message'\r
1957 + * will be initialized to a message object that can be used for things\r
1958 + * such as adding tags to the just-added message. The user should call\r
1959 + * notmuch_message_destroy when done with the message. On any failure\r
1960 + * '*message' will be set to NULL.\r
1961 + *\r
1962 + * Return value:\r
1963 + *\r
1964 + * NOTMUCH_STATUS_SUCCESS: Message successfully added to database.\r
1965 + *\r
1966 + * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,\r
1967 + *     message not added.\r
1968 + *\r
1969 + * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: Message has the same message\r
1970 + *     ID as another message already in the database. The new\r
1971 + *     filename was successfully added to the message in the database\r
1972 + *     (if not already present).\r
1973 + *\r
1974 + * NOTMUCH_STATUS_FILE_ERROR: an error occurred trying to open the\r
1975 + *     file, (such as permission denied, or file not found,\r
1976 + *     etc.). Nothing added to the database.\r
1977 + *\r
1978 + * NOTMUCH_STATUS_FILE_NOT_EMAIL: the contents of filename don't look\r
1979 + *     like an email message. Nothing added to the database.\r
1980 + *\r
1981 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
1982 + *     mode so no message can be added.\r
1983 + */\r
1984 +func \r
1985 +(self *Database) AddMessage(fname string) (*Message, Status) {\r
1986 +       var c_fname *C.char = C.CString(fname)\r
1987 +       defer C.free(unsafe.Pointer(c_fname))\r
1988 +\r
1989 +       if c_fname == nil {\r
1990 +               return nil, STATUS_OUT_OF_MEMORY\r
1991 +       }\r
1992 +\r
1993 +       var c_msg *C.notmuch_message_t = new(C.notmuch_message_t)\r
1994 +       st := Status(C.notmuch_database_add_message(self.db, c_fname, &c_msg))\r
1995 +\r
1996 +       return &Message{message:c_msg}, st\r
1997 +}\r
1998 +\r
1999 +/* Remove a message from the given notmuch database.\r
2000 + *\r
2001 + * Note that only this particular filename association is removed from\r
2002 + * the database. If the same message (as determined by the message ID)\r
2003 + * is still available via other filenames, then the message will\r
2004 + * persist in the database for those filenames. When the last filename\r
2005 + * is removed for a particular message, the database content for that\r
2006 + * message will be entirely removed.\r
2007 + *\r
2008 + * Return value:\r
2009 + *\r
2010 + * NOTMUCH_STATUS_SUCCESS: The last filename was removed and the\r
2011 + *     message was removed from the database.\r
2012 + *\r
2013 + * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,\r
2014 + *     message not removed.\r
2015 + *\r
2016 + * NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID: This filename was removed but\r
2017 + *     the message persists in the database with at least one other\r
2018 + *     filename.\r
2019 + *\r
2020 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
2021 + *     mode so no message can be removed.\r
2022 + */\r
2023 +func (self *Database) RemoveMessage(fname string) Status {\r
2024 +       \r
2025 +       var c_fname *C.char = C.CString(fname)\r
2026 +       defer C.free(unsafe.Pointer(c_fname))\r
2027 +\r
2028 +       if c_fname == nil {\r
2029 +               return STATUS_OUT_OF_MEMORY\r
2030 +       }\r
2031 +\r
2032 +       st := C.notmuch_database_remove_message(self.db, c_fname)\r
2033 +       return Status(st)\r
2034 +}\r
2035 +\r
2036 +/* Find a message with the given message_id.\r
2037 + *\r
2038 + * If the database contains a message with the given message_id, then\r
2039 + * a new notmuch_message_t object is returned. The caller should call\r
2040 + * notmuch_message_destroy when done with the message.\r
2041 + *\r
2042 + * This function returns NULL in the following situations:\r
2043 + *\r
2044 + *     * No message is found with the given message_id\r
2045 + *     * An out-of-memory situation occurs\r
2046 + *     * A Xapian exception occurs\r
2047 + */\r
2048 +func (self *Database) FindMessage(message_id string) (*Message, Status) {\r
2049 +       \r
2050 +       var c_msg_id *C.char = C.CString(message_id)\r
2051 +       defer C.free(unsafe.Pointer(c_msg_id))\r
2052 +\r
2053 +       if c_msg_id == nil {\r
2054 +               return nil, STATUS_OUT_OF_MEMORY\r
2055 +       }\r
2056 +\r
2057 +       msg := &Message{message:nil}\r
2058 +       st := Status(C.notmuch_database_find_message(self.db, c_msg_id, &msg.message))\r
2059 +       if st != STATUS_SUCCESS {\r
2060 +               return nil, st\r
2061 +       }\r
2062 +       return msg, st\r
2063 +}\r
2064 +\r
2065 +/* Return a list of all tags found in the database.\r
2066 + *\r
2067 + * This function creates a list of all tags found in the database. The\r
2068 + * resulting list contains all tags from all messages found in the database.\r
2069 + *\r
2070 + * On error this function returns NULL.\r
2071 + */\r
2072 +func (self *Database) GetAllTags() *Tags {\r
2073 +       tags := C.notmuch_database_get_all_tags(self.db)\r
2074 +       if tags == nil {\r
2075 +               return nil\r
2076 +       }\r
2077 +       return &Tags{tags:tags}\r
2078 +}\r
2079 +\r
2080 +/* Create a new query for 'database'.\r
2081 + *\r
2082 + * Here, 'database' should be an open database, (see\r
2083 + * notmuch_database_open and notmuch_database_create).\r
2084 + *\r
2085 + * For the query string, we'll document the syntax here more\r
2086 + * completely in the future, but it's likely to be a specialized\r
2087 + * version of the general Xapian query syntax:\r
2088 + *\r
2089 + * http://xapian.org/docs/queryparser.html\r
2090 + *\r
2091 + * As a special case, passing either a length-zero string, (that is ""),\r
2092 + * or a string consisting of a single asterisk (that is "*"), will\r
2093 + * result in a query that returns all messages in the database.\r
2094 + *\r
2095 + * See notmuch_query_set_sort for controlling the order of results.\r
2096 + * See notmuch_query_search_messages and notmuch_query_search_threads\r
2097 + * to actually execute the query.\r
2098 + *\r
2099 + * User should call notmuch_query_destroy when finished with this\r
2100 + * query.\r
2101 + *\r
2102 + * Will return NULL if insufficient memory is available.\r
2103 + */\r
2104 +func (self *Database) CreateQuery(query string) *Query {\r
2105 +       \r
2106 +       var c_query *C.char = C.CString(query)\r
2107 +       defer C.free(unsafe.Pointer(c_query))\r
2108 +\r
2109 +       if c_query == nil {\r
2110 +               return nil\r
2111 +       }\r
2112 +\r
2113 +       q := C.notmuch_query_create(self.db, c_query)\r
2114 +       if q == nil {\r
2115 +               return nil\r
2116 +       }\r
2117 +       return &Query{query:q}\r
2118 +}\r
2119 +\r
2120 +/* Sort values for notmuch_query_set_sort */\r
2121 +type Sort C.notmuch_sort_t\r
2122 +const (\r
2123 +       SORT_OLDEST_FIRST Sort = 0\r
2124 +       SORT_NEWEST_FIRST\r
2125 +       SORT_MESSAGE_ID\r
2126 +       SORT_UNSORTED\r
2127 +)\r
2128 +\r
2129 +/* Return the query_string of this query. See notmuch_query_create. */\r
2130 +func (self *Query) String() string {\r
2131 +       // FIXME: do we own 'q' or not ?\r
2132 +       q := C.notmuch_query_get_query_string(self.query)\r
2133 +       //defer C.free(unsafe.Pointer(q))\r
2134 +       \r
2135 +       if q != nil {\r
2136 +               s := C.GoString(q)\r
2137 +               return s\r
2138 +       }\r
2139 +\r
2140 +       return ""\r
2141 +}\r
2142 +\r
2143 +/* Specify the sorting desired for this query. */\r
2144 +func (self *Query) SetSort(sort Sort) {\r
2145 +       C.notmuch_query_set_sort(self.query, C.notmuch_sort_t(sort))\r
2146 +}\r
2147 +\r
2148 +/* Return the sort specified for this query. See notmuch_query_set_sort. */\r
2149 +func (self *Query) GetSort() Sort {\r
2150 +       return Sort(C.notmuch_query_get_sort(self.query))\r
2151 +}\r
2152 +\r
2153 +/* Execute a query for threads, returning a notmuch_threads_t object\r
2154 + * which can be used to iterate over the results. The returned threads\r
2155 + * object is owned by the query and as such, will only be valid until\r
2156 + * notmuch_query_destroy.\r
2157 + *\r
2158 + * Typical usage might be:\r
2159 + *\r
2160 + *     notmuch_query_t *query;\r
2161 + *     notmuch_threads_t *threads;\r
2162 + *     notmuch_thread_t *thread;\r
2163 + *\r
2164 + *     query = notmuch_query_create (database, query_string);\r
2165 + *\r
2166 + *     for (threads = notmuch_query_search_threads (query);\r
2167 + *          notmuch_threads_valid (threads);\r
2168 + *          notmuch_threads_move_to_next (threads))\r
2169 + *     {\r
2170 + *         thread = notmuch_threads_get (threads);\r
2171 + *         ....\r
2172 + *         notmuch_thread_destroy (thread);\r
2173 + *     }\r
2174 + *\r
2175 + *     notmuch_query_destroy (query);\r
2176 + *\r
2177 + * Note: If you are finished with a thread before its containing\r
2178 + * query, you can call notmuch_thread_destroy to clean up some memory\r
2179 + * sooner (as in the above example). Otherwise, if your thread objects\r
2180 + * are long-lived, then you don't need to call notmuch_thread_destroy\r
2181 + * and all the memory will still be reclaimed when the query is\r
2182 + * destroyed.\r
2183 + *\r
2184 + * Note that there's no explicit destructor needed for the\r
2185 + * notmuch_threads_t object. (For consistency, we do provide a\r
2186 + * notmuch_threads_destroy function, but there's no good reason\r
2187 + * to call it if the query is about to be destroyed).\r
2188 + *\r
2189 + * If a Xapian exception occurs this function will return NULL.\r
2190 + */\r
2191 +func (self *Query) SearchThreads() *Threads {\r
2192 +       threads := C.notmuch_query_search_threads(self.query)\r
2193 +       if threads == nil {\r
2194 +               return nil\r
2195 +       }\r
2196 +       return &Threads{threads:threads}\r
2197 +}\r
2198 +\r
2199 +/* Execute a query for messages, returning a notmuch_messages_t object\r
2200 + * which can be used to iterate over the results. The returned\r
2201 + * messages object is owned by the query and as such, will only be\r
2202 + * valid until notmuch_query_destroy.\r
2203 + *\r
2204 + * Typical usage might be:\r
2205 + *\r
2206 + *     notmuch_query_t *query;\r
2207 + *     notmuch_messages_t *messages;\r
2208 + *     notmuch_message_t *message;\r
2209 + *\r
2210 + *     query = notmuch_query_create (database, query_string);\r
2211 + *\r
2212 + *     for (messages = notmuch_query_search_messages (query);\r
2213 + *          notmuch_messages_valid (messages);\r
2214 + *          notmuch_messages_move_to_next (messages))\r
2215 + *     {\r
2216 + *         message = notmuch_messages_get (messages);\r
2217 + *         ....\r
2218 + *         notmuch_message_destroy (message);\r
2219 + *     }\r
2220 + *\r
2221 + *     notmuch_query_destroy (query);\r
2222 + *\r
2223 + * Note: If you are finished with a message before its containing\r
2224 + * query, you can call notmuch_message_destroy to clean up some memory\r
2225 + * sooner (as in the above example). Otherwise, if your message\r
2226 + * objects are long-lived, then you don't need to call\r
2227 + * notmuch_message_destroy and all the memory will still be reclaimed\r
2228 + * when the query is destroyed.\r
2229 + *\r
2230 + * Note that there's no explicit destructor needed for the\r
2231 + * notmuch_messages_t object. (For consistency, we do provide a\r
2232 + * notmuch_messages_destroy function, but there's no good\r
2233 + * reason to call it if the query is about to be destroyed).\r
2234 + *\r
2235 + * If a Xapian exception occurs this function will return NULL.\r
2236 + */\r
2237 +func (self *Query) SearchMessages() *Messages {\r
2238 +       msgs := C.notmuch_query_search_messages(self.query)\r
2239 +       if msgs == nil {\r
2240 +               return nil\r
2241 +       }\r
2242 +       return &Messages{messages:msgs}\r
2243 +}\r
2244 +\r
2245 +/* Destroy a notmuch_query_t along with any associated resources.\r
2246 + *\r
2247 + * This will in turn destroy any notmuch_threads_t and\r
2248 + * notmuch_messages_t objects generated by this query, (and in\r
2249 + * turn any notmuch_thread_t and notmuch_message_t objects generated\r
2250 + * from those results, etc.), if such objects haven't already been\r
2251 + * destroyed.\r
2252 + */\r
2253 +func (self *Query) Destroy() {\r
2254 +       if self.query != nil {\r
2255 +               C.notmuch_query_destroy(self.query)\r
2256 +       }\r
2257 +}\r
2258 +\r
2259 +/* Return an estimate of the number of messages matching a search\r
2260 + *\r
2261 + * This function performs a search and returns Xapian's best\r
2262 + * guess as to number of matching messages.\r
2263 + *\r
2264 + * If a Xapian exception occurs, this function may return 0 (after\r
2265 + * printing a message).\r
2266 + */\r
2267 +func (self *Query) CountMessages() uint {\r
2268 +       return uint(C.notmuch_query_count_messages(self.query))\r
2269 +}\r
2270 +\r
2271 +// TODO: wrap threads and thread\r
2272 +\r
2273 +/* Is the given 'threads' iterator pointing at a valid thread.\r
2274 + *\r
2275 + * When this function returns TRUE, notmuch_threads_get will return a\r
2276 + * valid object. Whereas when this function returns FALSE,\r
2277 + * notmuch_threads_get will return NULL.\r
2278 + *\r
2279 + * See the documentation of notmuch_query_search_threads for example\r
2280 + * code showing how to iterate over a notmuch_threads_t object.\r
2281 + */\r
2282 +func (self *Threads) Valid() bool {\r
2283 +       if self.threads == nil {\r
2284 +               return false\r
2285 +       }\r
2286 +       valid := C.notmuch_threads_valid(self.threads)\r
2287 +       if valid == 0 {\r
2288 +               return false\r
2289 +       }\r
2290 +       return true\r
2291 +}\r
2292 +\r
2293 +/* Destroy a notmuch_threads_t object.\r
2294 + *\r
2295 + * It's not strictly necessary to call this function. All memory from\r
2296 + * the notmuch_threads_t object will be reclaimed when the\r
2297 + * containg query object is destroyed.\r
2298 + */\r
2299 +func (self *Threads) Destroy() {\r
2300 +       if self.threads != nil {\r
2301 +               C.notmuch_threads_destroy(self.threads)\r
2302 +       }\r
2303 +}\r
2304 +\r
2305 +/* Is the given 'messages' iterator pointing at a valid message.\r
2306 + *\r
2307 + * When this function returns TRUE, notmuch_messages_get will return a\r
2308 + * valid object. Whereas when this function returns FALSE,\r
2309 + * notmuch_messages_get will return NULL.\r
2310 + *\r
2311 + * See the documentation of notmuch_query_search_messages for example\r
2312 + * code showing how to iterate over a notmuch_messages_t object.\r
2313 + */\r
2314 +func (self *Messages) Valid() bool {\r
2315 +       if self.messages == nil {\r
2316 +               return false\r
2317 +       }\r
2318 +       valid := C.notmuch_messages_valid(self.messages)\r
2319 +       if valid == 0 {\r
2320 +               return false\r
2321 +       }\r
2322 +       return true\r
2323 +}\r
2324 +\r
2325 +/* Get the current message from 'messages' as a notmuch_message_t.\r
2326 + *\r
2327 + * Note: The returned message belongs to 'messages' and has a lifetime\r
2328 + * identical to it (and the query to which it belongs).\r
2329 + *\r
2330 + * See the documentation of notmuch_query_search_messages for example\r
2331 + * code showing how to iterate over a notmuch_messages_t object.\r
2332 + *\r
2333 + * If an out-of-memory situation occurs, this function will return\r
2334 + * NULL.\r
2335 + */\r
2336 +func (self *Messages) Get() *Message {\r
2337 +       if self.messages == nil {\r
2338 +               return nil\r
2339 +       }\r
2340 +       msg := C.notmuch_messages_get(self.messages)\r
2341 +       if msg == nil {\r
2342 +               return nil\r
2343 +       }\r
2344 +       return &Message{message:msg}\r
2345 +}\r
2346 +\r
2347 +/* Move the 'messages' iterator to the next message.\r
2348 + *\r
2349 + * If 'messages' is already pointing at the last message then the\r
2350 + * iterator will be moved to a point just beyond that last message,\r
2351 + * (where notmuch_messages_valid will return FALSE and\r
2352 + * notmuch_messages_get will return NULL).\r
2353 + *\r
2354 + * See the documentation of notmuch_query_search_messages for example\r
2355 + * code showing how to iterate over a notmuch_messages_t object.\r
2356 + */\r
2357 +func (self *Messages) MoveToNext() {\r
2358 +       if self.messages == nil {\r
2359 +               return\r
2360 +       }\r
2361 +       C.notmuch_messages_move_to_next(self.messages)\r
2362 +}\r
2363 +\r
2364 +/* Destroy a notmuch_messages_t object.\r
2365 + *\r
2366 + * It's not strictly necessary to call this function. All memory from\r
2367 + * the notmuch_messages_t object will be reclaimed when the containing\r
2368 + * query object is destroyed.\r
2369 + */\r
2370 +func (self *Messages) Destroy() {\r
2371 +       if self.messages != nil {\r
2372 +               C.notmuch_messages_destroy(self.messages)\r
2373 +       }\r
2374 +}\r
2375 +\r
2376 +/* Return a list of tags from all messages.\r
2377 + *\r
2378 + * The resulting list is guaranteed not to contain duplicated tags.\r
2379 + *\r
2380 + * WARNING: You can no longer iterate over messages after calling this\r
2381 + * function, because the iterator will point at the end of the list.\r
2382 + * We do not have a function to reset the iterator yet and the only\r
2383 + * way how you can iterate over the list again is to recreate the\r
2384 + * message list.\r
2385 + *\r
2386 + * The function returns NULL on error.\r
2387 + */\r
2388 +func (self *Messages) CollectTags() *Tags {\r
2389 +       if self.messages == nil {\r
2390 +               return nil\r
2391 +       }\r
2392 +       tags := C.notmuch_messages_collect_tags(self.messages)\r
2393 +       if tags == nil {\r
2394 +               return nil\r
2395 +       }\r
2396 +       return &Tags{tags:tags}\r
2397 +}\r
2398 +\r
2399 +/* Get the message ID of 'message'.\r
2400 + *\r
2401 + * The returned string belongs to 'message' and as such, should not be\r
2402 + * modified by the caller and will only be valid for as long as the\r
2403 + * message is valid, (which is until the query from which it derived\r
2404 + * is destroyed).\r
2405 + *\r
2406 + * This function will not return NULL since Notmuch ensures that every\r
2407 + * message has a unique message ID, (Notmuch will generate an ID for a\r
2408 + * message if the original file does not contain one).\r
2409 + */\r
2410 +func (self *Message) GetMessageId() string {\r
2411 +\r
2412 +       if self.message == nil {\r
2413 +               return ""\r
2414 +       }\r
2415 +       id := C.notmuch_message_get_message_id(self.message)\r
2416 +       // we dont own id\r
2417 +       // defer C.free(unsafe.Pointer(id))\r
2418 +       if id == nil {\r
2419 +               return ""\r
2420 +       }\r
2421 +       return C.GoString(id)\r
2422 +}\r
2423 +\r
2424 +/* Get the thread ID of 'message'.\r
2425 + *\r
2426 + * The returned string belongs to 'message' and as such, should not be\r
2427 + * modified by the caller and will only be valid for as long as the\r
2428 + * message is valid, (for example, until the user calls\r
2429 + * notmuch_message_destroy on 'message' or until a query from which it\r
2430 + * derived is destroyed).\r
2431 + *\r
2432 + * This function will not return NULL since Notmuch ensures that every\r
2433 + * message belongs to a single thread.\r
2434 + */\r
2435 +func (self *Message) GetThreadId() string {\r
2436 +       \r
2437 +       if self.message == nil {\r
2438 +               return ""\r
2439 +       }\r
2440 +       id := C.notmuch_message_get_thread_id(self.message)\r
2441 +       // we dont own id\r
2442 +       // defer C.free(unsafe.Pointer(id))\r
2443 +       \r
2444 +       if id == nil {\r
2445 +               return ""\r
2446 +       }\r
2447 +\r
2448 +       return C.GoString(id)\r
2449 +}\r
2450 +\r
2451 +/* Get a notmuch_messages_t iterator for all of the replies to\r
2452 + * 'message'.\r
2453 + *\r
2454 + * Note: This call only makes sense if 'message' was ultimately\r
2455 + * obtained from a notmuch_thread_t object, (such as by coming\r
2456 + * directly from the result of calling notmuch_thread_get_\r
2457 + * toplevel_messages or by any number of subsequent\r
2458 + * calls to notmuch_message_get_replies).\r
2459 + *\r
2460 + * If 'message' was obtained through some non-thread means, (such as\r
2461 + * by a call to notmuch_query_search_messages), then this function\r
2462 + * will return NULL.\r
2463 + *\r
2464 + * If there are no replies to 'message', this function will return\r
2465 + * NULL. (Note that notmuch_messages_valid will accept that NULL\r
2466 + * value as legitimate, and simply return FALSE for it.)\r
2467 + */\r
2468 +func (self *Message) GetReplies() *Messages {\r
2469 +       if self.message == nil {\r
2470 +               return nil\r
2471 +       }\r
2472 +       msgs := C.notmuch_message_get_replies(self.message)\r
2473 +       if msgs == nil {\r
2474 +               return nil\r
2475 +       }\r
2476 +       return &Messages{messages:msgs}\r
2477 +}\r
2478 +\r
2479 +/* Get a filename for the email corresponding to 'message'.\r
2480 + *\r
2481 + * The returned filename is an absolute filename, (the initial\r
2482 + * component will match notmuch_database_get_path() ).\r
2483 + *\r
2484 + * The returned string belongs to the message so should not be\r
2485 + * modified or freed by the caller (nor should it be referenced after\r
2486 + * the message is destroyed).\r
2487 + *\r
2488 + * Note: If this message corresponds to multiple files in the mail\r
2489 + * store, (that is, multiple files contain identical message IDs),\r
2490 + * this function will arbitrarily return a single one of those\r
2491 + * filenames.\r
2492 + */\r
2493 +func (self *Message) GetFileName() string {\r
2494 +       if self.message == nil {\r
2495 +               return ""\r
2496 +       }\r
2497 +       fname := C.notmuch_message_get_filename(self.message)\r
2498 +       // we dont own fname\r
2499 +       // defer C.free(unsafe.Pointer(fname))\r
2500 +       \r
2501 +       if fname == nil {\r
2502 +               return ""\r
2503 +       }\r
2504 +\r
2505 +       return C.GoString(fname)\r
2506 +}\r
2507 +\r
2508 +type Flag C.notmuch_message_flag_t\r
2509 +const (\r
2510 +       MESSAGE_FLAG_MATCH Flag = 0\r
2511 +)\r
2512 +\r
2513 +/* Get a value of a flag for the email corresponding to 'message'. */\r
2514 +func (self *Message) GetFlag(flag Flag) bool {\r
2515 +       if self.message == nil {\r
2516 +               return false\r
2517 +       }\r
2518 +       v := C.notmuch_message_get_flag(self.message, C.notmuch_message_flag_t(flag))\r
2519 +       if v == 0 {\r
2520 +               return false\r
2521 +       }\r
2522 +       return true\r
2523 +}\r
2524 +\r
2525 +/* Set a value of a flag for the email corresponding to 'message'. */\r
2526 +func (self *Message) SetFlag(flag Flag, value bool) {\r
2527 +       if self.message == nil {\r
2528 +               return\r
2529 +       }\r
2530 +       var v C.notmuch_bool_t = 0\r
2531 +       if value {\r
2532 +               v = 1\r
2533 +       }\r
2534 +       C.notmuch_message_set_flag(self.message, C.notmuch_message_flag_t(flag), v)\r
2535 +}\r
2536 +\r
2537 +// TODO: wrap notmuch_message_get_date\r
2538 +\r
2539 +/* Get the value of the specified header from 'message'.\r
2540 + *\r
2541 + * The value will be read from the actual message file, not from the\r
2542 + * notmuch database. The header name is case insensitive.\r
2543 + *\r
2544 + * The returned string belongs to the message so should not be\r
2545 + * modified or freed by the caller (nor should it be referenced after\r
2546 + * the message is destroyed).\r
2547 + *\r
2548 + * Returns an empty string ("") if the message does not contain a\r
2549 + * header line matching 'header'. Returns NULL if any error occurs.\r
2550 + */\r
2551 +func (self *Message) GetHeader(header string) string {\r
2552 +       if self.message == nil {\r
2553 +               return ""\r
2554 +       }\r
2555 +       \r
2556 +       var c_header *C.char = C.CString(header)\r
2557 +       defer C.free(unsafe.Pointer(c_header))\r
2558 +       \r
2559 +       /* we dont own value */\r
2560 +       value := C.notmuch_message_get_header(self.message, c_header)\r
2561 +       if value == nil {\r
2562 +               return ""\r
2563 +       }\r
2564 +       \r
2565 +       return C.GoString(value)\r
2566 +}\r
2567 +\r
2568 +/* Get the tags for 'message', returning a notmuch_tags_t object which\r
2569 + * can be used to iterate over all tags.\r
2570 + *\r
2571 + * The tags object is owned by the message and as such, will only be\r
2572 + * valid for as long as the message is valid, (which is until the\r
2573 + * query from which it derived is destroyed).\r
2574 + *\r
2575 + * Typical usage might be:\r
2576 + *\r
2577 + *     notmuch_message_t *message;\r
2578 + *     notmuch_tags_t *tags;\r
2579 + *     const char *tag;\r
2580 + *\r
2581 + *     message = notmuch_database_find_message (database, message_id);\r
2582 + *\r
2583 + *     for (tags = notmuch_message_get_tags (message);\r
2584 + *          notmuch_tags_valid (tags);\r
2585 + *          notmuch_result_move_to_next (tags))\r
2586 + *     {\r
2587 + *         tag = notmuch_tags_get (tags);\r
2588 + *         ....\r
2589 + *     }\r
2590 + *\r
2591 + *     notmuch_message_destroy (message);\r
2592 + *\r
2593 + * Note that there's no explicit destructor needed for the\r
2594 + * notmuch_tags_t object. (For consistency, we do provide a\r
2595 + * notmuch_tags_destroy function, but there's no good reason to call\r
2596 + * it if the message is about to be destroyed).\r
2597 + */\r
2598 +func (self *Message) GetTags() *Tags {\r
2599 +       if self.message == nil {\r
2600 +               return nil\r
2601 +       }\r
2602 +       tags := C.notmuch_message_get_tags(self.message)\r
2603 +       if tags == nil {\r
2604 +               return nil\r
2605 +       }\r
2606 +       return &Tags{tags:tags}\r
2607 +}\r
2608 +\r
2609 +/* The longest possible tag value. */\r
2610 +const TAG_MAX = 200\r
2611 +\r
2612 +/* Add a tag to the given message.\r
2613 + *\r
2614 + * Return value:\r
2615 + *\r
2616 + * NOTMUCH_STATUS_SUCCESS: Tag successfully added to message\r
2617 + *\r
2618 + * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL\r
2619 + *\r
2620 + * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long\r
2621 + *     (exceeds NOTMUCH_TAG_MAX)\r
2622 + *\r
2623 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
2624 + *     mode so message cannot be modified.\r
2625 + */\r
2626 +func (self *Message) AddTag(tag string) Status {\r
2627 +       if self.message == nil {\r
2628 +               return STATUS_NULL_POINTER\r
2629 +       }\r
2630 +       c_tag := C.CString(tag)\r
2631 +       defer C.free(unsafe.Pointer(c_tag))\r
2632 +\r
2633 +       return Status(C.notmuch_message_add_tag(self.message, c_tag))\r
2634 +}\r
2635 +\r
2636 +/* Remove a tag from the given message.\r
2637 + *\r
2638 + * Return value:\r
2639 + *\r
2640 + * NOTMUCH_STATUS_SUCCESS: Tag successfully removed from message\r
2641 + *\r
2642 + * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL\r
2643 + *\r
2644 + * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long\r
2645 + *     (exceeds NOTMUCH_TAG_MAX)\r
2646 + *\r
2647 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
2648 + *     mode so message cannot be modified.\r
2649 + */\r
2650 +func (self *Message) RemoveTag(tag string) Status {\r
2651 +       if self.message == nil {\r
2652 +               return STATUS_NULL_POINTER\r
2653 +       }\r
2654 +       c_tag := C.CString(tag)\r
2655 +       defer C.free(unsafe.Pointer(c_tag))\r
2656 +\r
2657 +       return Status(C.notmuch_message_remove_tag(self.message, c_tag))\r
2658 +}\r
2659 +\r
2660 +/* Remove all tags from the given message.\r
2661 + *\r
2662 + * See notmuch_message_freeze for an example showing how to safely\r
2663 + * replace tag values.\r
2664 + *\r
2665 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
2666 + *     mode so message cannot be modified.\r
2667 + */\r
2668 +func (self *Message) RemoveAllTags() Status {\r
2669 +       if self.message == nil {\r
2670 +               return STATUS_NULL_POINTER\r
2671 +       }\r
2672 +       return Status(C.notmuch_message_remove_all_tags(self.message))\r
2673 +}\r
2674 +\r
2675 +/* Freeze the current state of 'message' within the database.\r
2676 + *\r
2677 + * This means that changes to the message state, (via\r
2678 + * notmuch_message_add_tag, notmuch_message_remove_tag, and\r
2679 + * notmuch_message_remove_all_tags), will not be committed to the\r
2680 + * database until the message is thawed with notmuch_message_thaw.\r
2681 + *\r
2682 + * Multiple calls to freeze/thaw are valid and these calls will\r
2683 + * "stack". That is there must be as many calls to thaw as to freeze\r
2684 + * before a message is actually thawed.\r
2685 + *\r
2686 + * The ability to do freeze/thaw allows for safe transactions to\r
2687 + * change tag values. For example, explicitly setting a message to\r
2688 + * have a given set of tags might look like this:\r
2689 + *\r
2690 + *    notmuch_message_freeze (message);\r
2691 + *\r
2692 + *    notmuch_message_remove_all_tags (message);\r
2693 + *\r
2694 + *    for (i = 0; i < NUM_TAGS; i++)\r
2695 + *        notmuch_message_add_tag (message, tags[i]);\r
2696 + *\r
2697 + *    notmuch_message_thaw (message);\r
2698 + *\r
2699 + * With freeze/thaw used like this, the message in the database is\r
2700 + * guaranteed to have either the full set of original tag values, or\r
2701 + * the full set of new tag values, but nothing in between.\r
2702 + *\r
2703 + * Imagine the example above without freeze/thaw and the operation\r
2704 + * somehow getting interrupted. This could result in the message being\r
2705 + * left with no tags if the interruption happened after\r
2706 + * notmuch_message_remove_all_tags but before notmuch_message_add_tag.\r
2707 + *\r
2708 + * Return value:\r
2709 + *\r
2710 + * NOTMUCH_STATUS_SUCCESS: Message successfully frozen.\r
2711 + *\r
2712 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only\r
2713 + *     mode so message cannot be modified.\r
2714 + */\r
2715 +func (self *Message) Freeze() Status {\r
2716 +       if self.message == nil {\r
2717 +               return STATUS_NULL_POINTER\r
2718 +       }\r
2719 +       return Status(C.notmuch_message_freeze(self.message))\r
2720 +}\r
2721 +\r
2722 +/* Thaw the current 'message', synchronizing any changes that may have\r
2723 + * occurred while 'message' was frozen into the notmuch database.\r
2724 + *\r
2725 + * See notmuch_message_freeze for an example of how to use this\r
2726 + * function to safely provide tag changes.\r
2727 + *\r
2728 + * Multiple calls to freeze/thaw are valid and these calls with\r
2729 + * "stack". That is there must be as many calls to thaw as to freeze\r
2730 + * before a message is actually thawed.\r
2731 + *\r
2732 + * Return value:\r
2733 + *\r
2734 + * NOTMUCH_STATUS_SUCCESS: Message successfully thawed, (or at least\r
2735 + *     its frozen count has successfully been reduced by 1).\r
2736 + *\r
2737 + * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW: An attempt was made to thaw\r
2738 + *     an unfrozen message. That is, there have been an unbalanced\r
2739 + *     number of calls to notmuch_message_freeze and\r
2740 + *     notmuch_message_thaw.\r
2741 + */\r
2742 +func (self *Message) Thaw() Status {\r
2743 +       if self.message == nil {\r
2744 +               return STATUS_NULL_POINTER\r
2745 +       }\r
2746 +\r
2747 +       return Status(C.notmuch_message_thaw(self.message))\r
2748 +}\r
2749 +\r
2750 +/* Destroy a notmuch_message_t object.\r
2751 + *\r
2752 + * It can be useful to call this function in the case of a single\r
2753 + * query object with many messages in the result, (such as iterating\r
2754 + * over the entire database). Otherwise, it's fine to never call this\r
2755 + * function and there will still be no memory leaks. (The memory from\r
2756 + * the messages get reclaimed when the containing query is destroyed.)\r
2757 + */\r
2758 +func (self *Message) Destroy() {\r
2759 +       if self.message == nil {\r
2760 +               return\r
2761 +       }\r
2762 +       C.notmuch_message_destroy(self.message)\r
2763 +}\r
2764 +\r
2765 +/* Is the given 'tags' iterator pointing at a valid tag.\r
2766 + *\r
2767 + * When this function returns TRUE, notmuch_tags_get will return a\r
2768 + * valid string. Whereas when this function returns FALSE,\r
2769 + * notmuch_tags_get will return NULL.\r
2770 + *\r
2771 + * See the documentation of notmuch_message_get_tags for example code\r
2772 + * showing how to iterate over a notmuch_tags_t object.\r
2773 + */\r
2774 +func (self *Tags) Valid() bool {\r
2775 +       if self.tags == nil {\r
2776 +               return false\r
2777 +       }\r
2778 +       v := C.notmuch_tags_valid(self.tags)\r
2779 +       if v == 0 {\r
2780 +               return false\r
2781 +       }\r
2782 +       return true\r
2783 +}\r
2784 +\r
2785 +/* Get the current tag from 'tags' as a string.\r
2786 + *\r
2787 + * Note: The returned string belongs to 'tags' and has a lifetime\r
2788 + * identical to it (and the query to which it ultimately belongs).\r
2789 + *\r
2790 + * See the documentation of notmuch_message_get_tags for example code\r
2791 + * showing how to iterate over a notmuch_tags_t object.\r
2792 + */\r
2793 +func (self *Tags) Get() string {\r
2794 +       if self.tags == nil {\r
2795 +               return ""\r
2796 +       }\r
2797 +       s := C.notmuch_tags_get(self.tags)\r
2798 +       // we dont own 's'\r
2799 +\r
2800 +       return C.GoString(s)\r
2801 +}\r
2802 +func (self *Tags) String() string {\r
2803 +       return self.Get()\r
2804 +}\r
2805 +\r
2806 +/* Move the 'tags' iterator to the next tag.\r
2807 + *\r
2808 + * If 'tags' is already pointing at the last tag then the iterator\r
2809 + * will be moved to a point just beyond that last tag, (where\r
2810 + * notmuch_tags_valid will return FALSE and notmuch_tags_get will\r
2811 + * return NULL).\r
2812 + *\r
2813 + * See the documentation of notmuch_message_get_tags for example code\r
2814 + * showing how to iterate over a notmuch_tags_t object.\r
2815 + */\r
2816 +func (self *Tags) MoveToNext() {\r
2817 +       if self.tags == nil {\r
2818 +               return\r
2819 +       }\r
2820 +       C.notmuch_tags_move_to_next(self.tags)\r
2821 +}\r
2822 +\r
2823 +/* Destroy a notmuch_tags_t object.\r
2824 + *\r
2825 + * It's not strictly necessary to call this function. All memory from\r
2826 + * the notmuch_tags_t object will be reclaimed when the containing\r
2827 + * message or query objects are destroyed.\r
2828 + */\r
2829 +func (self *Tags) Destroy() {\r
2830 +       if self.tags == nil {\r
2831 +               return\r
2832 +       }\r
2833 +       C.notmuch_tags_destroy(self.tags)\r
2834 +}\r
2835 +\r
2836 +// TODO: wrap notmuch_directory_<fct>\r
2837 +\r
2838 +/* Destroy a notmuch_directory_t object. */\r
2839 +func (self *Directory) Destroy() {\r
2840 +       if self.dir == nil {\r
2841 +               return\r
2842 +       }\r
2843 +       C.notmuch_directory_destroy(self.dir)\r
2844 +}\r
2845 +\r
2846 +// TODO: wrap notmuch_filenames_<fct>\r
2847 +\r
2848 +/* Destroy a notmuch_filenames_t object.\r
2849 + *\r
2850 + * It's not strictly necessary to call this function. All memory from\r
2851 + * the notmuch_filenames_t object will be reclaimed when the\r
2852 + * containing directory object is destroyed.\r
2853 + *\r
2854 + * It is acceptable to pass NULL for 'filenames', in which case this\r
2855 + * function will do nothing.\r
2856 + */\r
2857 +func (self *Filenames) Destroy() {\r
2858 +       if self.fnames == nil {\r
2859 +               return\r
2860 +       }\r
2861 +       C.notmuch_filenames_destroy(self.fnames)\r
2862 +}\r
2863 +/* EOF */\r
2864 -- \r
2865 1.7.10\r
2866 \r