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
11 X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]
\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
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
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
60 Signed-off-by: Justus Winter <4winter@informatik.uni-hamburg.de>
\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
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
89 -// 3rd-party imports
\r
91 -import "github.com/kless/goconfig/config"
\r
93 -type mail_addr_freq struct {
\r
98 -type frequencies map[string]uint
\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
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
120 -type maddresses []*mail_addr_freq
\r
122 -func (self *maddresses) Len() int {
\r
123 - return len(*self)
\r
126 -func (self *maddresses) Less(i,j int) bool {
\r
129 - v := sort_by_freq(m1, m2)
\r
136 -func (self *maddresses) Swap(i,j int) {
\r
137 - (*self)[i], (*self)[j] = (*self)[j], (*self)[i]
\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
144 - freqs_sz := len(freqs)
\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
157 -func addresses_by_frequency(msgs *notmuch.Messages, name string, pass uint, addr_to_realname *map[string]*frequencies) *frequencies {
\r
159 - freqs := make(frequencies)
\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
172 - headers := []string{"from"}
\r
174 - headers = append(headers, "to", "cc", "bcc")
\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
192 - freqs[match] = occ+1
\r
199 -func search_address_passes(queries [3]*notmuch.Query, name string) []string {
\r
201 - addr_freq := make(map[string]*mail_addr_freq)
\r
202 - addr_to_realname := make(map[string]*frequencies)
\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
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
215 - freq = &mail_addr_freq{addr:addr, count:[3]uint{0,0,0}}
\r
217 - freq.count[pass] = count
\r
218 - addr_freq[addr] = freq
\r
224 - addrs := make(maddresses, len(addr_freq))
\r
227 - for _, freq := range addr_freq {
\r
228 - addrs[iaddr] = freq
\r
232 - sort.Sort(&addrs)
\r
234 - for _,addr := range addrs {
\r
235 - freqs,ok := addr_to_realname[addr.addr]
\r
237 - val = append(val, frequent_fullname(*freqs))
\r
239 - val = append(val, addr.addr)
\r
242 - //println("val:",val)
\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
257 -func new_address_matcher() *address_matcher {
\r
258 - var cfg *config.Config
\r
261 - // honor NOTMUCH_CONFIG
\r
262 - home := os.Getenv("NOTMUCH_CONFIG")
\r
264 - home = os.Getenv("HOME")
\r
267 - if cfg,err = config.ReadDefault(path.Join(home, ".notmuch-config")); err != nil {
\r
268 - log.Fatalf("error loading config file:",err)
\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
275 - addrbook_tag = "addressbook"
\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
285 -func (self *address_matcher) run(name string) {
\r
286 - queries := [3]*notmuch.Query{}
\r
288 - // open the database
\r
289 - self.db = notmuch.OpenDatabase(self.user_db_path,
\r
290 - notmuch.DATABASE_MODE_READ_ONLY)
\r
292 - // pass 1: look at all from: addresses with the address book tag
\r
293 - query := "tag:" + self.user_addrbook_tag
\r
295 - query = query + " and from:" + name + "*"
\r
297 - queries[0] = self.db.CreateQuery(query)
\r
299 - // pass 2: look at all to: addresses sent from our primary mail
\r
302 - query = "to:"+name+"*"
\r
304 - if self.user_primary_email != "" {
\r
305 - query = query + " from:" + self.user_primary_email
\r
307 - queries[1] = self.db.CreateQuery(query)
\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
313 - query = "from:"+name+"*"
\r
315 - queries[2] = self.db.CreateQuery(query)
\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
329 - //fmt.Println("args:",os.Args)
\r
330 - app := new_address_matcher()
\r
332 - if len(os.Args) > 1 {
\r
333 - name = os.Args[1]
\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
344 -// Wrapper around the notmuch library
\r
349 -#include <stdlib.h>
\r
350 -#include <string.h>
\r
352 -#include "notmuch.h"
\r
357 -// Status codes used for the return values of most functions
\r
358 -type Status C.notmuch_status_t
\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
371 - STATUS_LAST_STATUS
\r
374 -func (self Status) String() string {
\r
377 - // p is read-only
\r
378 - p = C.notmuch_status_to_string(C.notmuch_status_t(self))
\r
380 - s := C.GoString(p)
\r
386 -/* Various opaque data types. For each notmuch_<foo>_t see the various
\r
387 - * notmuch_<foo> functions below. */
\r
389 -type Database struct {
\r
390 - db *C.notmuch_database_t
\r
393 -type Query struct {
\r
394 - query *C.notmuch_query_t
\r
397 -type Threads struct {
\r
398 - threads *C.notmuch_threads_t
\r
401 -type Thread struct {
\r
402 - thread *C.notmuch_thread_t
\r
405 -type Messages struct {
\r
406 - messages *C.notmuch_messages_t
\r
409 -type Message struct {
\r
410 - message *C.notmuch_message_t
\r
413 -type Tags struct {
\r
414 - tags *C.notmuch_tags_t
\r
417 -type Directory struct {
\r
418 - dir *C.notmuch_directory_t
\r
421 -type Filenames struct {
\r
422 - fnames *C.notmuch_filenames_t
\r
425 -type DatabaseMode C.notmuch_database_mode_t
\r
427 - DATABASE_MODE_READ_ONLY DatabaseMode = 0
\r
428 - DATABASE_MODE_READ_WRITE
\r
431 -// Create a new, empty notmuch database located at 'path'
\r
432 -func NewDatabase(path string) *Database {
\r
434 - var c_path *C.char = C.CString(path)
\r
435 - defer C.free(unsafe.Pointer(c_path))
\r
437 - if c_path == nil {
\r
441 - self := &Database{db:nil}
\r
442 - self.db = C.notmuch_database_create(c_path)
\r
443 - if self.db == nil {
\r
449 -/* Open an existing notmuch database located at 'path'.
\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
457 - * An existing notmuch database can be identified by the presence of a
\r
458 - * directory named ".notmuch" below 'path'.
\r
460 - * The caller should call notmuch_database_destroy when finished with
\r
463 - * In case of any failure, this function returns NULL, (after printing
\r
464 - * an error message on stderr).
\r
466 -func OpenDatabase(path string, mode DatabaseMode) *Database {
\r
468 - var c_path *C.char = C.CString(path)
\r
469 - defer C.free(unsafe.Pointer(c_path))
\r
471 - if c_path == nil {
\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
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
489 -/* Return the database path of the given database.
\r
491 -func (self *Database) GetPath() string {
\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
497 - s := C.GoString(p)
\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
508 -/* Does this database need to be upgraded before writing to it?
\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
522 -// TODO: notmuch_database_upgrade
\r
525 -/* Retrieve a directory object from the database for 'path'.
\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
531 - * Can return NULL if a Xapian exception occurs.
\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
537 - if c_path == nil {
\r
541 - c_dir := C.notmuch_database_get_directory(self.db, c_path)
\r
542 - if c_dir == nil {
\r
545 - return &Directory{dir:c_dir}
\r
548 -/* Add a new message to the given notmuch database.
\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
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
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
568 - * NOTMUCH_STATUS_SUCCESS: Message successfully added to database.
\r
570 - * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,
\r
571 - * message not added.
\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
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
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
585 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
586 - * mode so no message can be added.
\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
593 - if c_fname == nil {
\r
594 - return nil, STATUS_OUT_OF_MEMORY
\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
600 - return &Message{message:c_msg}, st
\r
603 -/* Remove a message from the given notmuch database.
\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
614 - * NOTMUCH_STATUS_SUCCESS: The last filename was removed and the
\r
615 - * message was removed from the database.
\r
617 - * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,
\r
618 - * message not removed.
\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
624 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
625 - * mode so no message can be removed.
\r
627 -func (self *Database) RemoveMessage(fname string) Status {
\r
629 - var c_fname *C.char = C.CString(fname)
\r
630 - defer C.free(unsafe.Pointer(c_fname))
\r
632 - if c_fname == nil {
\r
633 - return STATUS_OUT_OF_MEMORY
\r
636 - st := C.notmuch_database_remove_message(self.db, c_fname)
\r
637 - return Status(st)
\r
640 -/* Find a message with the given message_id.
\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
646 - * This function returns NULL in the following situations:
\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
652 -func (self *Database) FindMessage(message_id string) (*Message, Status) {
\r
654 - var c_msg_id *C.char = C.CString(message_id)
\r
655 - defer C.free(unsafe.Pointer(c_msg_id))
\r
657 - if c_msg_id == nil {
\r
658 - return nil, STATUS_OUT_OF_MEMORY
\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
669 -/* Return a list of all tags found in the database.
\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
674 - * On error this function returns NULL.
\r
676 -func (self *Database) GetAllTags() *Tags {
\r
677 - tags := C.notmuch_database_get_all_tags(self.db)
\r
681 - return &Tags{tags:tags}
\r
684 -/* Create a new query for 'database'.
\r
686 - * Here, 'database' should be an open database, (see
\r
687 - * notmuch_database_open and notmuch_database_create).
\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
693 - * http://xapian.org/docs/queryparser.html
\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
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
703 - * User should call notmuch_query_destroy when finished with this
\r
706 - * Will return NULL if insufficient memory is available.
\r
708 -func (self *Database) CreateQuery(query string) *Query {
\r
710 - var c_query *C.char = C.CString(query)
\r
711 - defer C.free(unsafe.Pointer(c_query))
\r
713 - if c_query == nil {
\r
717 - q := C.notmuch_query_create(self.db, c_query)
\r
721 - return &Query{query:q}
\r
724 -/* Sort values for notmuch_query_set_sort */
\r
725 -type Sort C.notmuch_sort_t
\r
727 - SORT_OLDEST_FIRST Sort = 0
\r
728 - SORT_NEWEST_FIRST
\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
740 - s := C.GoString(q)
\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
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
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
762 - * Typical usage might be:
\r
764 - * notmuch_query_t *query;
\r
765 - * notmuch_threads_t *threads;
\r
766 - * notmuch_thread_t *thread;
\r
768 - * query = notmuch_query_create (database, query_string);
\r
770 - * for (threads = notmuch_query_search_threads (query);
\r
771 - * notmuch_threads_valid (threads);
\r
772 - * notmuch_threads_move_to_next (threads))
\r
774 - * thread = notmuch_threads_get (threads);
\r
776 - * notmuch_thread_destroy (thread);
\r
779 - * notmuch_query_destroy (query);
\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
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
793 - * If a Xapian exception occurs this function will return NULL.
\r
795 -func (self *Query) SearchThreads() *Threads {
\r
796 - threads := C.notmuch_query_search_threads(self.query)
\r
797 - if threads == nil {
\r
800 - return &Threads{threads:threads}
\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
808 - * Typical usage might be:
\r
810 - * notmuch_query_t *query;
\r
811 - * notmuch_messages_t *messages;
\r
812 - * notmuch_message_t *message;
\r
814 - * query = notmuch_query_create (database, query_string);
\r
816 - * for (messages = notmuch_query_search_messages (query);
\r
817 - * notmuch_messages_valid (messages);
\r
818 - * notmuch_messages_move_to_next (messages))
\r
820 - * message = notmuch_messages_get (messages);
\r
822 - * notmuch_message_destroy (message);
\r
825 - * notmuch_query_destroy (query);
\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
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
839 - * If a Xapian exception occurs this function will return NULL.
\r
841 -func (self *Query) SearchMessages() *Messages {
\r
842 - msgs := C.notmuch_query_search_messages(self.query)
\r
846 - return &Messages{messages:msgs}
\r
849 -/* Destroy a notmuch_query_t along with any associated resources.
\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
857 -func (self *Query) Destroy() {
\r
858 - if self.query != nil {
\r
859 - C.notmuch_query_destroy(self.query)
\r
863 -/* Return an estimate of the number of messages matching a search
\r
865 - * This function performs a search and returns Xapian's best
\r
866 - * guess as to number of matching messages.
\r
868 - * If a Xapian exception occurs, this function may return 0 (after
\r
869 - * printing a message).
\r
871 -func (self *Query) CountMessages() uint {
\r
872 - return uint(C.notmuch_query_count_messages(self.query))
\r
875 -// TODO: wrap threads and thread
\r
877 -/* Is the given 'threads' iterator pointing at a valid thread.
\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
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
886 -func (self *Threads) Valid() bool {
\r
887 - if self.threads == nil {
\r
890 - valid := C.notmuch_threads_valid(self.threads)
\r
897 -/* Destroy a notmuch_threads_t object.
\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
903 -func (self *Threads) Destroy() {
\r
904 - if self.threads != nil {
\r
905 - C.notmuch_threads_destroy(self.threads)
\r
909 -/* Is the given 'messages' iterator pointing at a valid message.
\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
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
918 -func (self *Messages) Valid() bool {
\r
919 - if self.messages == nil {
\r
922 - valid := C.notmuch_messages_valid(self.messages)
\r
929 -/* Get the current message from 'messages' as a notmuch_message_t.
\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
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
937 - * If an out-of-memory situation occurs, this function will return
\r
940 -func (self *Messages) Get() *Message {
\r
941 - if self.messages == nil {
\r
944 - msg := C.notmuch_messages_get(self.messages)
\r
948 - return &Message{message:msg}
\r
951 -/* Move the 'messages' iterator to the next message.
\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
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
961 -func (self *Messages) MoveToNext() {
\r
962 - if self.messages == nil {
\r
965 - C.notmuch_messages_move_to_next(self.messages)
\r
968 -/* Destroy a notmuch_messages_t object.
\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
974 -func (self *Messages) Destroy() {
\r
975 - if self.messages != nil {
\r
976 - C.notmuch_messages_destroy(self.messages)
\r
980 -/* Return a list of tags from all messages.
\r
982 - * The resulting list is guaranteed not to contain duplicated tags.
\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
990 - * The function returns NULL on error.
\r
992 -func (self *Messages) CollectTags() *Tags {
\r
993 - if self.messages == nil {
\r
996 - tags := C.notmuch_messages_collect_tags(self.messages)
\r
1000 - return &Tags{tags:tags}
\r
1003 -/* Get the message ID of 'message'.
\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
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
1014 -func (self *Message) GetMessageId() string {
\r
1016 - if self.message == nil {
\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
1025 - return C.GoString(id)
\r
1028 -/* Get the thread ID of 'message'.
\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
1036 - * This function will not return NULL since Notmuch ensures that every
\r
1037 - * message belongs to a single thread.
\r
1039 -func (self *Message) GetThreadId() string {
\r
1041 - if self.message == nil {
\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
1052 - return C.GoString(id)
\r
1055 -/* Get a notmuch_messages_t iterator for all of the replies to
\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
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
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
1072 -func (self *Message) GetReplies() *Messages {
\r
1073 - if self.message == nil {
\r
1076 - msgs := C.notmuch_message_get_replies(self.message)
\r
1077 - if msgs == nil {
\r
1080 - return &Messages{messages:msgs}
\r
1083 -/* Get a filename for the email corresponding to 'message'.
\r
1085 - * The returned filename is an absolute filename, (the initial
\r
1086 - * component will match notmuch_database_get_path() ).
\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
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
1097 -func (self *Message) GetFileName() string {
\r
1098 - if self.message == nil {
\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
1105 - if fname == nil {
\r
1109 - return C.GoString(fname)
\r
1112 -type Flag C.notmuch_message_flag_t
\r
1114 - MESSAGE_FLAG_MATCH Flag = 0
\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
1122 - v := C.notmuch_message_get_flag(self.message, C.notmuch_message_flag_t(flag))
\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
1134 - var v C.notmuch_bool_t = 0
\r
1138 - C.notmuch_message_set_flag(self.message, C.notmuch_message_flag_t(flag), v)
\r
1141 -// TODO: wrap notmuch_message_get_date
\r
1143 -/* Get the value of the specified header from 'message'.
\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
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
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
1155 -func (self *Message) GetHeader(header string) string {
\r
1156 - if self.message == nil {
\r
1160 - var c_header *C.char = C.CString(header)
\r
1161 - defer C.free(unsafe.Pointer(c_header))
\r
1163 - /* we dont own value */
\r
1164 - value := C.notmuch_message_get_header(self.message, c_header)
\r
1165 - if value == nil {
\r
1169 - return C.GoString(value)
\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
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
1179 - * Typical usage might be:
\r
1181 - * notmuch_message_t *message;
\r
1182 - * notmuch_tags_t *tags;
\r
1183 - * const char *tag;
\r
1185 - * message = notmuch_database_find_message (database, message_id);
\r
1187 - * for (tags = notmuch_message_get_tags (message);
\r
1188 - * notmuch_tags_valid (tags);
\r
1189 - * notmuch_result_move_to_next (tags))
\r
1191 - * tag = notmuch_tags_get (tags);
\r
1195 - * notmuch_message_destroy (message);
\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
1202 -func (self *Message) GetTags() *Tags {
\r
1203 - if self.message == nil {
\r
1206 - tags := C.notmuch_message_get_tags(self.message)
\r
1207 - if tags == nil {
\r
1210 - return &Tags{tags:tags}
\r
1213 -/* The longest possible tag value. */
\r
1214 -const TAG_MAX = 200
\r
1216 -/* Add a tag to the given message.
\r
1220 - * NOTMUCH_STATUS_SUCCESS: Tag successfully added to message
\r
1222 - * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL
\r
1224 - * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
\r
1225 - * (exceeds NOTMUCH_TAG_MAX)
\r
1227 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
1228 - * mode so message cannot be modified.
\r
1230 -func (self *Message) AddTag(tag string) Status {
\r
1231 - if self.message == nil {
\r
1232 - return STATUS_NULL_POINTER
\r
1234 - c_tag := C.CString(tag)
\r
1235 - defer C.free(unsafe.Pointer(c_tag))
\r
1237 - return Status(C.notmuch_message_add_tag(self.message, c_tag))
\r
1240 -/* Remove a tag from the given message.
\r
1244 - * NOTMUCH_STATUS_SUCCESS: Tag successfully removed from message
\r
1246 - * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL
\r
1248 - * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
\r
1249 - * (exceeds NOTMUCH_TAG_MAX)
\r
1251 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
1252 - * mode so message cannot be modified.
\r
1254 -func (self *Message) RemoveTag(tag string) Status {
\r
1255 - if self.message == nil {
\r
1256 - return STATUS_NULL_POINTER
\r
1258 - c_tag := C.CString(tag)
\r
1259 - defer C.free(unsafe.Pointer(c_tag))
\r
1261 - return Status(C.notmuch_message_remove_tag(self.message, c_tag))
\r
1264 -/* Remove all tags from the given message.
\r
1266 - * See notmuch_message_freeze for an example showing how to safely
\r
1267 - * replace tag values.
\r
1269 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
1270 - * mode so message cannot be modified.
\r
1272 -func (self *Message) RemoveAllTags() Status {
\r
1273 - if self.message == nil {
\r
1274 - return STATUS_NULL_POINTER
\r
1276 - return Status(C.notmuch_message_remove_all_tags(self.message))
\r
1279 -/* Freeze the current state of 'message' within the database.
\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
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
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
1294 - * notmuch_message_freeze (message);
\r
1296 - * notmuch_message_remove_all_tags (message);
\r
1298 - * for (i = 0; i < NUM_TAGS; i++)
\r
1299 - * notmuch_message_add_tag (message, tags[i]);
\r
1301 - * notmuch_message_thaw (message);
\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
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
1314 - * NOTMUCH_STATUS_SUCCESS: Message successfully frozen.
\r
1316 - * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
1317 - * mode so message cannot be modified.
\r
1319 -func (self *Message) Freeze() Status {
\r
1320 - if self.message == nil {
\r
1321 - return STATUS_NULL_POINTER
\r
1323 - return Status(C.notmuch_message_freeze(self.message))
\r
1326 -/* Thaw the current 'message', synchronizing any changes that may have
\r
1327 - * occurred while 'message' was frozen into the notmuch database.
\r
1329 - * See notmuch_message_freeze for an example of how to use this
\r
1330 - * function to safely provide tag changes.
\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
1338 - * NOTMUCH_STATUS_SUCCESS: Message successfully thawed, (or at least
\r
1339 - * its frozen count has successfully been reduced by 1).
\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
1346 -func (self *Message) Thaw() Status {
\r
1347 - if self.message == nil {
\r
1348 - return STATUS_NULL_POINTER
\r
1351 - return Status(C.notmuch_message_thaw(self.message))
\r
1354 -/* Destroy a notmuch_message_t object.
\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
1362 -func (self *Message) Destroy() {
\r
1363 - if self.message == nil {
\r
1366 - C.notmuch_message_destroy(self.message)
\r
1369 -/* Is the given 'tags' iterator pointing at a valid tag.
\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
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
1378 -func (self *Tags) Valid() bool {
\r
1379 - if self.tags == nil {
\r
1382 - v := C.notmuch_tags_valid(self.tags)
\r
1389 -/* Get the current tag from 'tags' as a string.
\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
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
1397 -func (self *Tags) Get() string {
\r
1398 - if self.tags == nil {
\r
1401 - s := C.notmuch_tags_get(self.tags)
\r
1402 - // we dont own 's'
\r
1404 - return C.GoString(s)
\r
1406 -func (self *Tags) String() string {
\r
1407 - return self.Get()
\r
1410 -/* Move the 'tags' iterator to the next tag.
\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
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
1420 -func (self *Tags) MoveToNext() {
\r
1421 - if self.tags == nil {
\r
1424 - C.notmuch_tags_move_to_next(self.tags)
\r
1427 -/* Destroy a notmuch_tags_t object.
\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
1433 -func (self *Tags) Destroy() {
\r
1434 - if self.tags == nil {
\r
1437 - C.notmuch_tags_destroy(self.tags)
\r
1440 -// TODO: wrap notmuch_directory_<fct>
\r
1442 -/* Destroy a notmuch_directory_t object. */
\r
1443 -func (self *Directory) Destroy() {
\r
1444 - if self.dir == nil {
\r
1447 - C.notmuch_directory_destroy(self.dir)
\r
1450 -// TODO: wrap notmuch_filenames_<fct>
\r
1452 -/* Destroy a notmuch_filenames_t object.
\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
1458 - * It is acceptable to pass NULL for 'filenames', in which case this
\r
1459 - * function will do nothing.
\r
1461 -func (self *Filenames) Destroy() {
\r
1462 - if self.fnames == nil {
\r
1465 - C.notmuch_filenames_destroy(self.fnames)
\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
1472 +++ b/bindings/go/src/notmuch-addrlookup/addrlookup.go
\r
1476 +// stdlib imports
\r
1485 +// 3rd-party imports
\r
1487 +import "github.com/kless/goconfig/config"
\r
1489 +type mail_addr_freq struct {
\r
1494 +type frequencies map[string]uint
\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
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
1516 +type maddresses []*mail_addr_freq
\r
1518 +func (self *maddresses) Len() int {
\r
1519 + return len(*self)
\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
1532 +func (self *maddresses) Swap(i,j int) {
\r
1533 + (*self)[i], (*self)[j] = (*self)[j], (*self)[i]
\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
1540 + freqs_sz := len(freqs)
\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
1553 +func addresses_by_frequency(msgs *notmuch.Messages, name string, pass uint, addr_to_realname *map[string]*frequencies) *frequencies {
\r
1555 + freqs := make(frequencies)
\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
1568 + headers := []string{"from"}
\r
1570 + headers = append(headers, "to", "cc", "bcc")
\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
1585 + freqs[match] = 0
\r
1588 + freqs[match] = occ+1
\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
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
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
1611 + freq = &mail_addr_freq{addr:addr, count:[3]uint{0,0,0}}
\r
1613 + freq.count[pass] = count
\r
1614 + addr_freq[addr] = freq
\r
1620 + addrs := make(maddresses, len(addr_freq))
\r
1623 + for _, freq := range addr_freq {
\r
1624 + addrs[iaddr] = freq
\r
1628 + sort.Sort(&addrs)
\r
1630 + for _,addr := range addrs {
\r
1631 + freqs,ok := addr_to_realname[addr.addr]
\r
1633 + val = append(val, frequent_fullname(*freqs))
\r
1635 + val = append(val, addr.addr)
\r
1638 + //println("val:",val)
\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
1653 +func new_address_matcher() *address_matcher {
\r
1654 + var cfg *config.Config
\r
1655 + var err os.Error
\r
1657 + // honor NOTMUCH_CONFIG
\r
1658 + home := os.Getenv("NOTMUCH_CONFIG")
\r
1660 + home = os.Getenv("HOME")
\r
1663 + if cfg,err = config.ReadDefault(path.Join(home, ".notmuch-config")); err != nil {
\r
1664 + log.Fatalf("error loading config file:",err)
\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
1671 + addrbook_tag = "addressbook"
\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
1681 +func (self *address_matcher) run(name string) {
\r
1682 + queries := [3]*notmuch.Query{}
\r
1684 + // open the database
\r
1685 + self.db = notmuch.OpenDatabase(self.user_db_path,
\r
1686 + notmuch.DATABASE_MODE_READ_ONLY)
\r
1688 + // pass 1: look at all from: addresses with the address book tag
\r
1689 + query := "tag:" + self.user_addrbook_tag
\r
1691 + query = query + " and from:" + name + "*"
\r
1693 + queries[0] = self.db.CreateQuery(query)
\r
1695 + // pass 2: look at all to: addresses sent from our primary mail
\r
1698 + query = "to:"+name+"*"
\r
1700 + if self.user_primary_email != "" {
\r
1701 + query = query + " from:" + self.user_primary_email
\r
1703 + queries[1] = self.db.CreateQuery(query)
\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
1709 + query = "from:"+name+"*"
\r
1711 + queries[2] = self.db.CreateQuery(query)
\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
1725 + //fmt.Println("args:",os.Args)
\r
1726 + app := new_address_matcher()
\r
1728 + if len(os.Args) > 1 {
\r
1729 + name = os.Args[1]
\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
1738 +++ b/bindings/go/src/notmuch/notmuch.go
\r
1739 @@ -0,0 +1,1124 @@
\r
1740 +// Wrapper around the notmuch library
\r
1745 +#include <stdlib.h>
\r
1746 +#include <string.h>
\r
1747 +#include <time.h>
\r
1748 +#include "notmuch.h"
\r
1753 +// Status codes used for the return values of most functions
\r
1754 +type Status C.notmuch_status_t
\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
1767 + STATUS_LAST_STATUS
\r
1770 +func (self Status) String() string {
\r
1773 + // p is read-only
\r
1774 + p = C.notmuch_status_to_string(C.notmuch_status_t(self))
\r
1776 + s := C.GoString(p)
\r
1782 +/* Various opaque data types. For each notmuch_<foo>_t see the various
\r
1783 + * notmuch_<foo> functions below. */
\r
1785 +type Database struct {
\r
1786 + db *C.notmuch_database_t
\r
1789 +type Query struct {
\r
1790 + query *C.notmuch_query_t
\r
1793 +type Threads struct {
\r
1794 + threads *C.notmuch_threads_t
\r
1797 +type Thread struct {
\r
1798 + thread *C.notmuch_thread_t
\r
1801 +type Messages struct {
\r
1802 + messages *C.notmuch_messages_t
\r
1805 +type Message struct {
\r
1806 + message *C.notmuch_message_t
\r
1809 +type Tags struct {
\r
1810 + tags *C.notmuch_tags_t
\r
1813 +type Directory struct {
\r
1814 + dir *C.notmuch_directory_t
\r
1817 +type Filenames struct {
\r
1818 + fnames *C.notmuch_filenames_t
\r
1821 +type DatabaseMode C.notmuch_database_mode_t
\r
1823 + DATABASE_MODE_READ_ONLY DatabaseMode = 0
\r
1824 + DATABASE_MODE_READ_WRITE
\r
1827 +// Create a new, empty notmuch database located at 'path'
\r
1828 +func NewDatabase(path string) *Database {
\r
1830 + var c_path *C.char = C.CString(path)
\r
1831 + defer C.free(unsafe.Pointer(c_path))
\r
1833 + if c_path == nil {
\r
1837 + self := &Database{db:nil}
\r
1838 + self.db = C.notmuch_database_create(c_path)
\r
1839 + if self.db == nil {
\r
1845 +/* Open an existing notmuch database located at 'path'.
\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
1853 + * An existing notmuch database can be identified by the presence of a
\r
1854 + * directory named ".notmuch" below 'path'.
\r
1856 + * The caller should call notmuch_database_destroy when finished with
\r
1857 + * this database.
\r
1859 + * In case of any failure, this function returns NULL, (after printing
\r
1860 + * an error message on stderr).
\r
1862 +func OpenDatabase(path string, mode DatabaseMode) *Database {
\r
1864 + var c_path *C.char = C.CString(path)
\r
1865 + defer C.free(unsafe.Pointer(c_path))
\r
1867 + if c_path == nil {
\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
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
1885 +/* Return the database path of the given database.
\r
1887 +func (self *Database) GetPath() string {
\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
1893 + s := C.GoString(p)
\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
1904 +/* Does this database need to be upgraded before writing to it?
\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
1918 +// TODO: notmuch_database_upgrade
\r
1921 +/* Retrieve a directory object from the database for 'path'.
\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
1927 + * Can return NULL if a Xapian exception occurs.
\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
1933 + if c_path == nil {
\r
1937 + c_dir := C.notmuch_database_get_directory(self.db, c_path)
\r
1938 + if c_dir == nil {
\r
1941 + return &Directory{dir:c_dir}
\r
1944 +/* Add a new message to the given notmuch database.
\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
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
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
1964 + * NOTMUCH_STATUS_SUCCESS: Message successfully added to database.
\r
1966 + * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,
\r
1967 + * message not added.
\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
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
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
1981 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
1982 + * mode so no message can be added.
\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
1989 + if c_fname == nil {
\r
1990 + return nil, STATUS_OUT_OF_MEMORY
\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
1996 + return &Message{message:c_msg}, st
\r
1999 +/* Remove a message from the given notmuch database.
\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
2010 + * NOTMUCH_STATUS_SUCCESS: The last filename was removed and the
\r
2011 + * message was removed from the database.
\r
2013 + * NOTMUCH_STATUS_XAPIAN_EXCEPTION: A Xapian exception occurred,
\r
2014 + * message not removed.
\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
2020 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
2021 + * mode so no message can be removed.
\r
2023 +func (self *Database) RemoveMessage(fname string) Status {
\r
2025 + var c_fname *C.char = C.CString(fname)
\r
2026 + defer C.free(unsafe.Pointer(c_fname))
\r
2028 + if c_fname == nil {
\r
2029 + return STATUS_OUT_OF_MEMORY
\r
2032 + st := C.notmuch_database_remove_message(self.db, c_fname)
\r
2033 + return Status(st)
\r
2036 +/* Find a message with the given message_id.
\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
2042 + * This function returns NULL in the following situations:
\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
2048 +func (self *Database) FindMessage(message_id string) (*Message, Status) {
\r
2050 + var c_msg_id *C.char = C.CString(message_id)
\r
2051 + defer C.free(unsafe.Pointer(c_msg_id))
\r
2053 + if c_msg_id == nil {
\r
2054 + return nil, STATUS_OUT_OF_MEMORY
\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
2065 +/* Return a list of all tags found in the database.
\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
2070 + * On error this function returns NULL.
\r
2072 +func (self *Database) GetAllTags() *Tags {
\r
2073 + tags := C.notmuch_database_get_all_tags(self.db)
\r
2074 + if tags == nil {
\r
2077 + return &Tags{tags:tags}
\r
2080 +/* Create a new query for 'database'.
\r
2082 + * Here, 'database' should be an open database, (see
\r
2083 + * notmuch_database_open and notmuch_database_create).
\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
2089 + * http://xapian.org/docs/queryparser.html
\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
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
2099 + * User should call notmuch_query_destroy when finished with this
\r
2102 + * Will return NULL if insufficient memory is available.
\r
2104 +func (self *Database) CreateQuery(query string) *Query {
\r
2106 + var c_query *C.char = C.CString(query)
\r
2107 + defer C.free(unsafe.Pointer(c_query))
\r
2109 + if c_query == nil {
\r
2113 + q := C.notmuch_query_create(self.db, c_query)
\r
2117 + return &Query{query:q}
\r
2120 +/* Sort values for notmuch_query_set_sort */
\r
2121 +type Sort C.notmuch_sort_t
\r
2123 + SORT_OLDEST_FIRST Sort = 0
\r
2124 + SORT_NEWEST_FIRST
\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
2136 + s := C.GoString(q)
\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
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
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
2158 + * Typical usage might be:
\r
2160 + * notmuch_query_t *query;
\r
2161 + * notmuch_threads_t *threads;
\r
2162 + * notmuch_thread_t *thread;
\r
2164 + * query = notmuch_query_create (database, query_string);
\r
2166 + * for (threads = notmuch_query_search_threads (query);
\r
2167 + * notmuch_threads_valid (threads);
\r
2168 + * notmuch_threads_move_to_next (threads))
\r
2170 + * thread = notmuch_threads_get (threads);
\r
2172 + * notmuch_thread_destroy (thread);
\r
2175 + * notmuch_query_destroy (query);
\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
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
2189 + * If a Xapian exception occurs this function will return NULL.
\r
2191 +func (self *Query) SearchThreads() *Threads {
\r
2192 + threads := C.notmuch_query_search_threads(self.query)
\r
2193 + if threads == nil {
\r
2196 + return &Threads{threads:threads}
\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
2204 + * Typical usage might be:
\r
2206 + * notmuch_query_t *query;
\r
2207 + * notmuch_messages_t *messages;
\r
2208 + * notmuch_message_t *message;
\r
2210 + * query = notmuch_query_create (database, query_string);
\r
2212 + * for (messages = notmuch_query_search_messages (query);
\r
2213 + * notmuch_messages_valid (messages);
\r
2214 + * notmuch_messages_move_to_next (messages))
\r
2216 + * message = notmuch_messages_get (messages);
\r
2218 + * notmuch_message_destroy (message);
\r
2221 + * notmuch_query_destroy (query);
\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
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
2235 + * If a Xapian exception occurs this function will return NULL.
\r
2237 +func (self *Query) SearchMessages() *Messages {
\r
2238 + msgs := C.notmuch_query_search_messages(self.query)
\r
2239 + if msgs == nil {
\r
2242 + return &Messages{messages:msgs}
\r
2245 +/* Destroy a notmuch_query_t along with any associated resources.
\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
2253 +func (self *Query) Destroy() {
\r
2254 + if self.query != nil {
\r
2255 + C.notmuch_query_destroy(self.query)
\r
2259 +/* Return an estimate of the number of messages matching a search
\r
2261 + * This function performs a search and returns Xapian's best
\r
2262 + * guess as to number of matching messages.
\r
2264 + * If a Xapian exception occurs, this function may return 0 (after
\r
2265 + * printing a message).
\r
2267 +func (self *Query) CountMessages() uint {
\r
2268 + return uint(C.notmuch_query_count_messages(self.query))
\r
2271 +// TODO: wrap threads and thread
\r
2273 +/* Is the given 'threads' iterator pointing at a valid thread.
\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
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
2282 +func (self *Threads) Valid() bool {
\r
2283 + if self.threads == nil {
\r
2286 + valid := C.notmuch_threads_valid(self.threads)
\r
2293 +/* Destroy a notmuch_threads_t object.
\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
2299 +func (self *Threads) Destroy() {
\r
2300 + if self.threads != nil {
\r
2301 + C.notmuch_threads_destroy(self.threads)
\r
2305 +/* Is the given 'messages' iterator pointing at a valid message.
\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
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
2314 +func (self *Messages) Valid() bool {
\r
2315 + if self.messages == nil {
\r
2318 + valid := C.notmuch_messages_valid(self.messages)
\r
2325 +/* Get the current message from 'messages' as a notmuch_message_t.
\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
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
2333 + * If an out-of-memory situation occurs, this function will return
\r
2336 +func (self *Messages) Get() *Message {
\r
2337 + if self.messages == nil {
\r
2340 + msg := C.notmuch_messages_get(self.messages)
\r
2344 + return &Message{message:msg}
\r
2347 +/* Move the 'messages' iterator to the next message.
\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
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
2357 +func (self *Messages) MoveToNext() {
\r
2358 + if self.messages == nil {
\r
2361 + C.notmuch_messages_move_to_next(self.messages)
\r
2364 +/* Destroy a notmuch_messages_t object.
\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
2370 +func (self *Messages) Destroy() {
\r
2371 + if self.messages != nil {
\r
2372 + C.notmuch_messages_destroy(self.messages)
\r
2376 +/* Return a list of tags from all messages.
\r
2378 + * The resulting list is guaranteed not to contain duplicated tags.
\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
2386 + * The function returns NULL on error.
\r
2388 +func (self *Messages) CollectTags() *Tags {
\r
2389 + if self.messages == nil {
\r
2392 + tags := C.notmuch_messages_collect_tags(self.messages)
\r
2393 + if tags == nil {
\r
2396 + return &Tags{tags:tags}
\r
2399 +/* Get the message ID of 'message'.
\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
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
2410 +func (self *Message) GetMessageId() string {
\r
2412 + if self.message == nil {
\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
2421 + return C.GoString(id)
\r
2424 +/* Get the thread ID of 'message'.
\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
2432 + * This function will not return NULL since Notmuch ensures that every
\r
2433 + * message belongs to a single thread.
\r
2435 +func (self *Message) GetThreadId() string {
\r
2437 + if self.message == nil {
\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
2448 + return C.GoString(id)
\r
2451 +/* Get a notmuch_messages_t iterator for all of the replies to
\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
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
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
2468 +func (self *Message) GetReplies() *Messages {
\r
2469 + if self.message == nil {
\r
2472 + msgs := C.notmuch_message_get_replies(self.message)
\r
2473 + if msgs == nil {
\r
2476 + return &Messages{messages:msgs}
\r
2479 +/* Get a filename for the email corresponding to 'message'.
\r
2481 + * The returned filename is an absolute filename, (the initial
\r
2482 + * component will match notmuch_database_get_path() ).
\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
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
2493 +func (self *Message) GetFileName() string {
\r
2494 + if self.message == nil {
\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
2501 + if fname == nil {
\r
2505 + return C.GoString(fname)
\r
2508 +type Flag C.notmuch_message_flag_t
\r
2510 + MESSAGE_FLAG_MATCH Flag = 0
\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
2518 + v := C.notmuch_message_get_flag(self.message, C.notmuch_message_flag_t(flag))
\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
2530 + var v C.notmuch_bool_t = 0
\r
2534 + C.notmuch_message_set_flag(self.message, C.notmuch_message_flag_t(flag), v)
\r
2537 +// TODO: wrap notmuch_message_get_date
\r
2539 +/* Get the value of the specified header from 'message'.
\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
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
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
2551 +func (self *Message) GetHeader(header string) string {
\r
2552 + if self.message == nil {
\r
2556 + var c_header *C.char = C.CString(header)
\r
2557 + defer C.free(unsafe.Pointer(c_header))
\r
2559 + /* we dont own value */
\r
2560 + value := C.notmuch_message_get_header(self.message, c_header)
\r
2561 + if value == nil {
\r
2565 + return C.GoString(value)
\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
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
2575 + * Typical usage might be:
\r
2577 + * notmuch_message_t *message;
\r
2578 + * notmuch_tags_t *tags;
\r
2579 + * const char *tag;
\r
2581 + * message = notmuch_database_find_message (database, message_id);
\r
2583 + * for (tags = notmuch_message_get_tags (message);
\r
2584 + * notmuch_tags_valid (tags);
\r
2585 + * notmuch_result_move_to_next (tags))
\r
2587 + * tag = notmuch_tags_get (tags);
\r
2591 + * notmuch_message_destroy (message);
\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
2598 +func (self *Message) GetTags() *Tags {
\r
2599 + if self.message == nil {
\r
2602 + tags := C.notmuch_message_get_tags(self.message)
\r
2603 + if tags == nil {
\r
2606 + return &Tags{tags:tags}
\r
2609 +/* The longest possible tag value. */
\r
2610 +const TAG_MAX = 200
\r
2612 +/* Add a tag to the given message.
\r
2616 + * NOTMUCH_STATUS_SUCCESS: Tag successfully added to message
\r
2618 + * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL
\r
2620 + * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
\r
2621 + * (exceeds NOTMUCH_TAG_MAX)
\r
2623 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
2624 + * mode so message cannot be modified.
\r
2626 +func (self *Message) AddTag(tag string) Status {
\r
2627 + if self.message == nil {
\r
2628 + return STATUS_NULL_POINTER
\r
2630 + c_tag := C.CString(tag)
\r
2631 + defer C.free(unsafe.Pointer(c_tag))
\r
2633 + return Status(C.notmuch_message_add_tag(self.message, c_tag))
\r
2636 +/* Remove a tag from the given message.
\r
2640 + * NOTMUCH_STATUS_SUCCESS: Tag successfully removed from message
\r
2642 + * NOTMUCH_STATUS_NULL_POINTER: The 'tag' argument is NULL
\r
2644 + * NOTMUCH_STATUS_TAG_TOO_LONG: The length of 'tag' is too long
\r
2645 + * (exceeds NOTMUCH_TAG_MAX)
\r
2647 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
2648 + * mode so message cannot be modified.
\r
2650 +func (self *Message) RemoveTag(tag string) Status {
\r
2651 + if self.message == nil {
\r
2652 + return STATUS_NULL_POINTER
\r
2654 + c_tag := C.CString(tag)
\r
2655 + defer C.free(unsafe.Pointer(c_tag))
\r
2657 + return Status(C.notmuch_message_remove_tag(self.message, c_tag))
\r
2660 +/* Remove all tags from the given message.
\r
2662 + * See notmuch_message_freeze for an example showing how to safely
\r
2663 + * replace tag values.
\r
2665 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
2666 + * mode so message cannot be modified.
\r
2668 +func (self *Message) RemoveAllTags() Status {
\r
2669 + if self.message == nil {
\r
2670 + return STATUS_NULL_POINTER
\r
2672 + return Status(C.notmuch_message_remove_all_tags(self.message))
\r
2675 +/* Freeze the current state of 'message' within the database.
\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
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
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
2690 + * notmuch_message_freeze (message);
\r
2692 + * notmuch_message_remove_all_tags (message);
\r
2694 + * for (i = 0; i < NUM_TAGS; i++)
\r
2695 + * notmuch_message_add_tag (message, tags[i]);
\r
2697 + * notmuch_message_thaw (message);
\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
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
2710 + * NOTMUCH_STATUS_SUCCESS: Message successfully frozen.
\r
2712 + * NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in read-only
\r
2713 + * mode so message cannot be modified.
\r
2715 +func (self *Message) Freeze() Status {
\r
2716 + if self.message == nil {
\r
2717 + return STATUS_NULL_POINTER
\r
2719 + return Status(C.notmuch_message_freeze(self.message))
\r
2722 +/* Thaw the current 'message', synchronizing any changes that may have
\r
2723 + * occurred while 'message' was frozen into the notmuch database.
\r
2725 + * See notmuch_message_freeze for an example of how to use this
\r
2726 + * function to safely provide tag changes.
\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
2734 + * NOTMUCH_STATUS_SUCCESS: Message successfully thawed, (or at least
\r
2735 + * its frozen count has successfully been reduced by 1).
\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
2742 +func (self *Message) Thaw() Status {
\r
2743 + if self.message == nil {
\r
2744 + return STATUS_NULL_POINTER
\r
2747 + return Status(C.notmuch_message_thaw(self.message))
\r
2750 +/* Destroy a notmuch_message_t object.
\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
2758 +func (self *Message) Destroy() {
\r
2759 + if self.message == nil {
\r
2762 + C.notmuch_message_destroy(self.message)
\r
2765 +/* Is the given 'tags' iterator pointing at a valid tag.
\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
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
2774 +func (self *Tags) Valid() bool {
\r
2775 + if self.tags == nil {
\r
2778 + v := C.notmuch_tags_valid(self.tags)
\r
2785 +/* Get the current tag from 'tags' as a string.
\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
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
2793 +func (self *Tags) Get() string {
\r
2794 + if self.tags == nil {
\r
2797 + s := C.notmuch_tags_get(self.tags)
\r
2798 + // we dont own 's'
\r
2800 + return C.GoString(s)
\r
2802 +func (self *Tags) String() string {
\r
2803 + return self.Get()
\r
2806 +/* Move the 'tags' iterator to the next tag.
\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
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
2816 +func (self *Tags) MoveToNext() {
\r
2817 + if self.tags == nil {
\r
2820 + C.notmuch_tags_move_to_next(self.tags)
\r
2823 +/* Destroy a notmuch_tags_t object.
\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
2829 +func (self *Tags) Destroy() {
\r
2830 + if self.tags == nil {
\r
2833 + C.notmuch_tags_destroy(self.tags)
\r
2836 +// TODO: wrap notmuch_directory_<fct>
\r
2838 +/* Destroy a notmuch_directory_t object. */
\r
2839 +func (self *Directory) Destroy() {
\r
2840 + if self.dir == nil {
\r
2843 + C.notmuch_directory_destroy(self.dir)
\r
2846 +// TODO: wrap notmuch_filenames_<fct>
\r
2848 +/* Destroy a notmuch_filenames_t object.
\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
2854 + * It is acceptable to pass NULL for 'filenames', in which case this
\r
2855 + * function will do nothing.
\r
2857 +func (self *Filenames) Destroy() {
\r
2858 + if self.fnames == nil {
\r
2861 + C.notmuch_filenames_destroy(self.fnames)
\r