4890f7f7 |
;;;; simple_list.lisp
(in-package #:simple_list)
;;; "simple_list" goes here. Hacks and glory await!
|
6307d400 |
(defvar *imap-socket*)
|
0efcfbd7 |
(defvar *mailing-list*)
(defclass <mailing-list> ()
((name :initform nil :initarg :name :accessor ml-name)
(emails :initform nil :initarg :name :accessor ml-emails)))
(manardb:defmmclass <persistent-mailing-list> ()
((name :initform nil :initarg :name :accessor ml-name)
(emails :initform nil :initarg :emails :accessor ml-emails)) )
(defmethod print-object ((obj <persistent-mailing-list>) s)
(print-unreadable-object (obj s :type t :identity t)
(format s "~a with ~d emails" (ml-name obj) (length (ml-emails obj)))))
(defun make-mailing-list (name &optional persistent)
(aprog1 (make-instance '<persistent-mailing-list>)
(setf (slot-value it 'name)
(make-keyword
(ctypecase name
(string (string-to-list-name name))
(symbol name))))))
(defmacro aprogn (&body body)
`(let*
,(loop for el in body
collect `(it ,el))
it))
|
6307d400 |
(defmacro define-special-variable-wrapper (name var &body value-form)
(alexandria:with-gensyms (body)
`(defmacro ,name (() &body ,body)
(let ((var ',var) (value-form ',value-form))
`(let ((,var ,@value-form))
,@,body)))))
|
0efcfbd7 |
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun get-connection-spec ()
(clonsigna:make-imap :host (ubiquitous:value 'imap 'host)
:port (ubiquitous:value 'imap 'port)
:ssl-p (ubiquitous:value 'imap 'ssl-p))))
|
6307d400 |
(define-special-variable-wrapper with-connection *imap-socket* (get-connection-spec))
|
0efcfbd7 |
(define-special-variable-wrapper with-mailing-lists *mailing-list*
((manardb:retrieve-all-instances '<persistent-mailing-list>))
)
|
6307d400 |
|
0efcfbd7 |
(defmacro insert-imap-socket (&body body)
(list* 'progn
(loop for cmd in body
collect (list* (car cmd) '*imap-socket* (cdr cmd)))))
|
6307d400 |
(defun connect-and-authorize (auth-info)
(clonsigna:cmd-connect *imap-socket*)
(clonsigna:cmd-login *imap-socket* (car auth-info) (cdr auth-info)))
|
0efcfbd7 |
(defmacro with-authorized-connection ((auth-info) &body body)
`(with-connection ()
(connect-and-authorize ,auth-info)
,@body))
(defun connect-toplevel ()
(setf *imap-socket* (get-connection-spec)))
|
6307d400 |
(defun get-mailbox (name)
(cmd-select *imap-socket* name)
(mapcar #'princ-to-string
|
0efcfbd7 |
(parse-search (cmd-search *imap-socket* :criteria "(recent unseen)" :uid-p t))))
|
6307d400 |
|
0efcfbd7 |
(defun get-inbox () (get-mailbox "inbox"))
|
6307d400 |
(defun get-headers (uid)
(parse-fetch-fields (cmd-fetch-fields *imap-socket* uid)))
(defun get-bodystructures (uid)
|
0efcfbd7 |
(let ((headers (get-headers uid)))
|
6307d400 |
(mapcar (lambda (x)
(make-bodystructure
(parse-bodystructure
|
0efcfbd7 |
(getf x :bodystructure))))
headers)))
|
6307d400 |
(defun get-raw-message (uid)
|
0efcfbd7 |
(let* ((message (split-sequence #\Newline (car (cmd-fetch-body *imap-socket* uid :uid-p t))))
|
6307d400 |
(message-without-imap (slice message 0 -1)))
(string-join message-without-imap #\Newline)))
(defun get-message (uid)
(let* ((bodystructures (get-bodystructures uid))
(bodies (flatten (mapcar #'bodystructure-body-list bodystructures)))
(sections (flatten (mapcar #'structure-element-section bodies)))
|
0efcfbd7 |
(charsets (flatten (mapcar (fw.lu:alambda
|
6307d400 |
(cadr (structure-element-body-parameters it)))
bodies))))
(parse-fetch-body
(car (cmd-fetch-body *imap-socket* uid :section sections :uid-p t))
:charset (car charsets))))
(defun lookup-list (list-name &optional (lookup-table *mailing-list*))
(cdr (assoc list-name lookup-table)))
(defun email-to-list (email)
(aprogn
(car (split-sequence #\@ email :count 1))
(car (split-sequence #\- it :count 1 :from-end t))))
(defun peek-headers (uid)
(cmd-fetch *imap-socket* uid
:criteria (format nil "(~{~a ~}BODY.PEEK[header.fields (~{~a ~})])"
'(uid flags bodystructure)
'(date from to cc bcc subject message-id in-reply-to references))
:uid-p t))
(defun send-to-list (list-name from subject message host auth-info)
(destructuring-bind (recipients . other-headers) (lookup-list list-name)
(destructuring-bind (user . password) auth-info
(apply #'cl-smtp:send-email
(list* host
from
recipients
subject
message
:ssl t
:port 587
:authentication `(,user ,password)
other-headers)))))
(defun get-message-envelope-info (uid)
(let* ((envelope (car (parse-fetch-fields (peek-headers uid))))
(headers (getf envelope :headers))
)
(values (getf headers :to) ; To
(getf headers :from) ; From
(getf headers :subject) ; From
(getf envelope :flags) ; Message Flags
(getf envelope :bodystructure) ; Message bodystructure
headers ; Message headers
envelope))) ; Envelope info
|
0efcfbd7 |
(defun string-to-list-name (name-string)
(aprogn name-string
(string-upcase it)
(make-keyword it)))
|
6307d400 |
(defun process-imap-message (uid host auth-info)
(multiple-value-bind (to from subject flags) (get-message-envelope-info uid)
(unless (member 'seen flags)
|
0efcfbd7 |
(let* ((list-name (string-to-list-name (email-to-list to)))
|
6307d400 |
(body (get-message uid)))
(send-to-list list-name from subject body host auth-info)))))
(defun process-imap-mailbox (name host auth-info)
(let ((uids (get-mailbox name)))
(mapcar (lambda (x) (process-imap-message x host auth-info)) uids)))
(defun process-inbox (host auth-info)
(process-imap-mailbox "inbox" host auth-info))
|
0efcfbd7 |
(defun get-auth-info ()
(cons (ubiquitous:value 'auth 'user)
(ubiquitous:value 'auth 'password)))
(define-condition simple-list-error () ()
(:documentation "The base condition for exceptions thrown by this utility"))
(define-condition no-such-list (simple-list-error)
((list-name :initarg :name :accessor ml-name))
(:report (lambda (c s) (format s "No list named ~a" (ml-name c)))))
(defgeneric add-email (mailing-list email)
(:method :around (mailing-list email)
(let ((result (call-next-method)))
(typecase mailing-list
(symbol result)
(t mailing-list))))
(:method ((mailing-list <persistent-mailing-list>) email)
(with-slots (emails) mailing-list
(unless (member email emails :test #'equalp)
(push email emails))))
(:method ((list-name symbol) email)
(let ((lists (remove-if-not
(fw.lu:alambda (eq list-name (ml-name it)))
(manardb:retrieve-all-instances '<persistent-mailing-list>))))
(if-let ((mailing-list (car lists)))
(add-email mailing-list email)
(error 'no-such-list :name list-name)))))
(defun add-email-to-list (list-name email)
(restart-case
(add-email list-name email)
(make-list () (add-email (make-mailing-list list-name) email))))
(defun main (&optional argv)
|
6307d400 |
(ubiquitous:restore 'simple_list)
|
0efcfbd7 |
(let ((auth-info (get-auth-info))
(host-name (ubiquitous:value 'imap 'host)))
(with-mailing-lists ()
(format t "Connecting to server ~a" host-name)
(with-authorized-connection (auth-info)
(setf +debug+ t)
(process-inbox host-name auth-info)))))
|