git.fiddlerwoaroof.com
Browse code

saving progress and ignoring build artifacts

fiddlerwoaroof authored on 23/05/2016 23:34:08
Showing 5 changed files
... ...
@@ -1,2 +1,3 @@
1 1
 .*.sw[a-z]
2 2
 
3
+simple_list
... ...
@@ -1 +1 @@
1
-This is the stub README.txt for the "simple_list" project.
1
+A simple mailing list platform
2 2
deleted file mode 100755
3 3
Binary files a/simple_list and /dev/null differ
... ...
@@ -10,7 +10,8 @@
10 10
                #:anaphora
11 11
                #:serapeum
12 12
                #:ubiquitous
13
-               #:fwoar.lisputils)
13
+               #:fwoar.lisputils
14
+               #:manardb)
14 15
   :serial t
15 16
   :components ((:file "package")
16 17
                (:file "simple_list")))
... ...
@@ -5,6 +5,33 @@
5 5
 ;;; "simple_list" goes here. Hacks and glory await!
6 6
 
7 7
 (defvar *imap-socket*)
8
+(defvar *mailing-list*)
9
+
10
+(defclass <mailing-list> ()
11
+  ((name :initform nil :initarg :name :accessor ml-name)
12
+   (emails :initform nil :initarg :name :accessor ml-emails)))
13
+
14
+(manardb:defmmclass <persistent-mailing-list> ()
15
+  ((name :initform nil :initarg :name :accessor ml-name)
16
+   (emails :initform nil :initarg :emails :accessor ml-emails)) )
17
+
18
+(defmethod print-object ((obj <persistent-mailing-list>) s)
19
+  (print-unreadable-object (obj s :type t :identity t)
20
+    (format s "~a with ~d emails" (ml-name obj) (length (ml-emails obj)))))
21
+
22
+(defun make-mailing-list (name &optional persistent)
23
+  (aprog1 (make-instance '<persistent-mailing-list>)
24
+    (setf (slot-value it 'name)
25
+          (make-keyword
26
+            (ctypecase name
27
+              (string (string-to-list-name name))
28
+              (symbol name))))))
29
+
30
+(defmacro aprogn (&body body)
31
+  `(let*
32
+     ,(loop for el in body
33
+            collect `(it ,el))
34
+     it))
8 35
 
9 36
 (defmacro define-special-variable-wrapper (name var &body value-form)
10 37
   (alexandria:with-gensyms (body)
... ...
@@ -13,48 +40,55 @@
13 40
          `(let ((,var ,@value-form))
14 41
             ,@,body)))))
15 42
 
16
-(defmacro aprogn (&body body)
17
-  `(let*
18
-     ,(loop for el in body
19
-            collect `(it ,el))
20
-     it))
43
+(eval-when (:compile-toplevel :load-toplevel :execute)
44
+  (defun get-connection-spec ()
45
+    (clonsigna:make-imap :host (ubiquitous:value 'imap 'host)
46
+                         :port (ubiquitous:value 'imap 'port)
47
+                         :ssl-p (ubiquitous:value 'imap 'ssl-p))))
21 48
 
22
-(defun get-connection-spec ()
23
-  (clonsigna:make-imap :host (ubiquitous:value 'imap 'host)
24
-                       :port (ubiquitous:value 'imap 'port)
25
-                       :ssl-p (ubiquitous:value 'imap 'ssl-p)))
26 49
 
27 50
 (define-special-variable-wrapper with-connection *imap-socket* (get-connection-spec))
51
+(define-special-variable-wrapper with-mailing-lists *mailing-list*
52
+  ((manardb:retrieve-all-instances '<persistent-mailing-list>))
53
+  )
28 54
 
29
-(defun connect-toplevel ()
30
-  (setf *imap-socket* (get-connection-spec)))
55
+(defmacro insert-imap-socket (&body body)
56
+  (list* 'progn
57
+         (loop for cmd in body
58
+               collect (list* (car cmd) '*imap-socket* (cdr cmd)))))
31 59
 
32 60
 (defun connect-and-authorize (auth-info)
33 61
   (clonsigna:cmd-connect *imap-socket*)
34 62
   (clonsigna:cmd-login *imap-socket* (car auth-info) (cdr auth-info)))
35 63
 
64
+(defmacro with-authorized-connection ((auth-info)  &body body)
65
+  `(with-connection ()
66
+     (connect-and-authorize ,auth-info)
67
+     ,@body))
68
+
69
+(defun connect-toplevel ()
70
+  (setf *imap-socket* (get-connection-spec)))
36 71
 
37 72
 (defun get-mailbox (name)
38 73
   (cmd-select *imap-socket* name)
39 74
   (mapcar #'princ-to-string
40
-          (alexandria:flatten
41
-            (parse-thread (cmd-thread *imap-socket* :uid-p t)))))
75
+          (parse-search (cmd-search *imap-socket* :criteria "(recent unseen)" :uid-p t))))
42 76
 
43
-(defun get-inbox ()
44
-  (get-mailbox "inbox"))
77
+(defun get-inbox () (get-mailbox "inbox"))
45 78
 
46 79
 (defun get-headers (uid)
47 80
   (parse-fetch-fields (cmd-fetch-fields *imap-socket* uid)))
48 81
 
49 82
 (defun get-bodystructures (uid)
50
-  (let ((headers (get-headers x)))
83
+  (let ((headers (get-headers uid)))
51 84
     (mapcar (lambda (x)
52 85
               (make-bodystructure
53 86
                 (parse-bodystructure
54
-                  (getf x :bodystructure)))))))
87
+                  (getf x :bodystructure))))
88
+            headers)))
55 89
 
56 90
 (defun get-raw-message (uid)
57
-  (let* ((message (split-sequence #\Newline (car (cmd-fetch-body uid))))
91
+  (let* ((message (split-sequence #\Newline (car (cmd-fetch-body *imap-socket* uid :uid-p t))))
58 92
          (message-without-imap (slice message 0 -1)))
59 93
     (string-join message-without-imap #\Newline)))
60 94
 
... ...
@@ -62,14 +96,13 @@
62 96
   (let* ((bodystructures (get-bodystructures uid))
63 97
          (bodies (flatten (mapcar #'bodystructure-body-list bodystructures)))
64 98
          (sections (flatten (mapcar #'structure-element-section bodies)))
65
-         (charsets (flatten (mapcar (fw.lu::alambda
99
+         (charsets (flatten (mapcar (fw.lu:alambda
66 100
                                       (cadr (structure-element-body-parameters it)))
67 101
                                     bodies))))
68 102
     (parse-fetch-body
69 103
       (car (cmd-fetch-body *imap-socket* uid :section sections :uid-p t))
70 104
       :charset (car charsets))))
71 105
 
72
-(defvar *mailing-list*)
73 106
 
74 107
 (defun lookup-list (list-name &optional (lookup-table *mailing-list*))
75 108
   (cdr (assoc list-name lookup-table)))
... ...
@@ -112,12 +145,15 @@
112 145
             headers                          ; Message headers
113 146
             envelope)))                      ; Envelope info
114 147
 
148
+(defun string-to-list-name (name-string)
149
+  (aprogn name-string
150
+          (string-upcase it)
151
+          (make-keyword it)))
152
+
115 153
 (defun process-imap-message (uid host auth-info)
116 154
   (multiple-value-bind (to from subject flags) (get-message-envelope-info uid)
117 155
     (unless (member 'seen flags)
118
-      (let* ((list-name (aprogn (email-to-list to)
119
-                                (string-upcase it)
120
-                                (make-keyword it)))
156
+      (let* ((list-name (string-to-list-name (email-to-list to)))
121 157
              (body (get-message uid)))
122 158
         (send-to-list list-name from subject body host auth-info)))))
123 159
 
... ...
@@ -128,15 +164,48 @@
128 164
 (defun process-inbox (host auth-info)
129 165
   (process-imap-mailbox "inbox" host auth-info))
130 166
 
131
-(define-special-variable-wrapper with-mailing-lists *mailing-list* (ubiquitous:value 'mailing-lists))
132
-
133
-(defun main ()
167
+(defun get-auth-info ()
168
+  (cons (ubiquitous:value 'auth 'user)
169
+        (ubiquitous:value 'auth 'password)))
170
+
171
+(define-condition simple-list-error () ()
172
+  (:documentation "The base condition for exceptions thrown by this utility"))
173
+
174
+(define-condition no-such-list (simple-list-error)
175
+  ((list-name :initarg :name :accessor ml-name))
176
+  (:report (lambda (c s) (format s "No list named ~a" (ml-name c)))))
177
+
178
+(defgeneric add-email (mailing-list email)
179
+  (:method :around (mailing-list email)
180
+   (let ((result (call-next-method)))
181
+     (typecase mailing-list
182
+       (symbol result)
183
+       (t mailing-list))))
184
+
185
+  (:method ((mailing-list <persistent-mailing-list>) email)
186
+   (with-slots (emails) mailing-list
187
+     (unless (member email emails :test #'equalp)
188
+       (push email emails))))
189
+
190
+  (:method ((list-name symbol) email)
191
+   (let ((lists (remove-if-not
192
+                  (fw.lu:alambda (eq list-name (ml-name it)))
193
+                  (manardb:retrieve-all-instances '<persistent-mailing-list>))))
194
+     (if-let ((mailing-list (car lists)))
195
+       (add-email mailing-list email)
196
+       (error 'no-such-list :name list-name)))))
197
+
198
+(defun add-email-to-list (list-name email)
199
+  (restart-case
200
+    (add-email list-name email)
201
+    (make-list () (add-email (make-mailing-list list-name) email))))
202
+
203
+(defun main (&optional argv)
134 204
   (ubiquitous:restore 'simple_list)
135
-
136
-  (let ((auth-info (cons (ubiquitous:value 'auth 'user)
137
-                         (ubiquitous:value 'auth 'password))))  
138
-    (with-connection ()
139
-      (with-mailing-lists ()
140
-        (connect-and-authorize auth-info)
141
-        (get-inbox)
142
-        (process-inbox (ubiquitous:value 'imap 'host) auth-info)))))
205
+  (let ((auth-info (get-auth-info))
206
+        (host-name (ubiquitous:value 'imap 'host)))  
207
+    (with-mailing-lists ()
208
+      (format t "Connecting to server ~a" host-name)
209
+      (with-authorized-connection (auth-info)
210
+        (setf +debug+ t)
211
+        (process-inbox host-name auth-info)))))