aboutsummaryrefslogtreecommitdiffstats
path: root/lib/guile/mail-tools.scm
blob: 1186306d763bba00e17a04d8c208ef99af083032 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
(define-module (mail-tools)
  #:use-module (ffi notmuch)
  #:use-module (ice-9 rdelim)
  #:use-module (ice-9 regex)
  #:use-module (srfi srfi-1)
  #:use-module (system ffi-help-rt)
  #:use-module (system foreign)
  #:export (rename-higher
            get-uidvalidity
            get-folder-uidvalidity
            new-path
            tags-with-new
            query-with-new
            nm-open-database
            nm-query-db
            nm-result-messages
            nm-header
            nm-count-messages
            nm-apply-tags
            nm-iter
            with-nm-database
            with-nm-query))

(define (rename-higher name limit)
  (let ((mat (string-match ",U=[0-9]+" name)))
    (if (and mat (< limit (string->number (substring (match:substring mat) 3))))
        (string-append (match:prefix mat) (match:suffix mat))
        name)))

(define (get-uidvalidity port)
  (read-line port)
  (string->number (read-line port)))

(define (get-folder-uidvalidity folder)
  (call-with-input-file (string-append folder "/.uidvalidity") get-uidvalidity))

(define (new-path path destination)
  (let ((filename (basename path))
        (submaildir (basename (dirname path))))
    (string-join (list destination submaildir filename)
                 file-name-separator-string)))

(define (tags-with-new rule new)
  (string-append (car rule) (if new " -new" "")))

(define (query-with-new rule new)
  (cond
   ((and new (string=? (cadr rule) "*")) "tag:new")
   (new (simple-format #f "(~a) and ~a" (cadr rule) "tag:new"))
   (else (cadr rule))))

;; NOTMUCH interface
(define (nm-open-database path mode)
  (let ((ffi-db (make-notmuch_database_t*)))
    (notmuch_database_open (string->pointer path) mode (pointer-to ffi-db))
    ffi-db))

(define (nm-query-db db str)
  (let ((query (notmuch_query_create db (string->pointer str))))
    (for-each (lambda (tag)
                (notmuch_query_add_tag_exclude query (string->pointer tag)))
              (list "deleted" "spam"))
    query))

(define (nm-result-messages query)
  (let ((messages (make-notmuch_messages_t*)))
    (notmuch_query_search_messages query (pointer-to messages))
    messages))

(define (nm-header message label)
  (pointer->string (notmuch_message_get_header message (string->pointer label))))

(define (nm-count-messages query)
  (let ((counter (make-int32)))
    (notmuch_query_count_messages query (pointer-to counter))
    (fh-object-ref counter)))

(eval-when (expand load eval)
  (define (stx->str stx) (symbol->string (syntax->datum stx)))
  (define (singular stx) (string-drop-right (stx->str stx) 1))
  (define (nm-symbols tmpl-id . args)
    (datum->syntax
     tmpl-id
     (string->symbol
      (apply string-append
             (map (lambda (ss) (if (string? ss) ss (stx->str ss))) args))))))

(define-syntax nm-iter
  (lambda (x)
    (syntax-case x ()
      ((_ type query proc)
       (with-syntax ((valid? (nm-symbols #'type "notmuch_" #'type "_valid"))
                     (destroy (nm-symbols #'type "notmuch_" #'type "_destroy"))
                     (get (nm-symbols #'type "notmuch_" #'type "_get"))
                     (next (nm-symbols #'type "notmuch_" #'type "_move_to_next"))
                     (item-destroy (nm-symbols #'type "notmuch_" (singular #'type) "_destroy")))
         #'(let ((obj query))
             (let loop ((item (get obj))
                        (acc '()))
               (if (= 0 (valid? obj))
                   (begin
                     (destroy obj)
                     acc)
                   (let ((result (proc item)))
                     (when (defined? (quote item-destroy))
                       (item-destroy item))
                     (next obj)
                     (loop (get obj) (cons result acc)))))))))))

(define (nm-apply-tags message tags)
  (let loop ((rest (string-tokenize tags)))
    (unless (null-list? rest)
      (let ((tag (string->pointer (substring (car rest) 1))))
        (if (string-prefix? "-" (car rest))
            (notmuch_message_remove_tag message tag)
            (notmuch_message_add_tag message tag)))
      (loop (cdr rest)))))

(define-syntax-rule (with-nm-database (db path mode) body body* ...)
  (let ((db (nm-open-database path mode)))
    body body* ...
    (notmuch_database_destroy db)))

(define-syntax-rule (with-nm-query (db qobj query) body body* ...)
  (let ((qobj (nm-query-db db query)))
    body body* ...
    (notmuch_query_destroy qobj)))