imap for emacs - wip
Fetching latest commit…
Cannot retrieve the latest commit at this time.
|Type||Name||Latest commit message||Commit time|
|Failed to load latest commit information.|
;;; -*- mode: emacs-lisp; -*- ;;; WHAT IS THIS? ; eimap is an experiment to approach IMAP processing from a new angle. ; ; the protocol parser and generator are both formally generated from ; grammars. ; ; instead of using a request-reply approach (which does not work well ; with IMAP), eimap uses a streaming data model: ; ; data from the server is automatically parsed into usable lisp data ; structures and directly handed to a handler for the incoming data ; (not shown in the sample code below). ; ; if the application requires other data that has not been streamed in ; yet, it can request the server to send this data, using the ; `eimap-request' method (see example below). ;;; USING THE APPLICATION ; (add-to-list 'load-path default-directory) ; ; (require 'eimap) ; ; (eimap ; "0x2c.org" :user "2" ; ;; default is IMAP+STARTTLS. ; ;; pass :port "imaps" for SSL connection to port 993 ; ) ; ;;; USING THE BACKEND LIBRARY (add-to-list 'load-path default-directory) (require 'eimap) ;; this table will dispatch all the incoming messages ;; (eimap-declare-dispatch-table eimap-README) ;; upcalls are passed the `upcall-data' supplied to `eimap-open' and a ;; plist from the parsed message. `eimap-parse.el' contains the ;; formal grammar and self-documents the fields of each message. ;; ;; the upcall is called with the eimap connection buffer active ;; (eimap-define-method eimap-README connection-state (upcall-data state-data) ;; connection-state is issued when the state changes. Possible ;; `:state's are `connecting', `connected', `authenticating', ;; `authenticated', and `closed'. (when (eq 'authenticated (plist-get state-data :state)) ;; `eimap-request' has to be called with the eimap connection ;; buffer active. use `eimap-request*' to pass the connection ;; object explicitly. ;; ;; `eimap-request' takes a request element; see ;; `eimap-generate.el' for the self-documenting formal grammar. ;; ;; it also takes a `:cbdata' callback data argument that will be ;; passed to the `:barrier' and `:done' callbacks. There is no ;; data callback, because replies generally can not be associated ;; with the originating requests. The presence of a `:barrier' ;; also drains the request stream before the request is issued. (eimap-request '(:method SELECT :mailbox "INBOX") :cbdata upcall-data :done (lambda (respdata cbdata) ;; now that the SELECT is done, we can ;; sequence the FETCH. Maybe there should ;; be a way to add a tail barrier? (eimap-request '(:method FETCH ;; `:to' can be a ;; number or `*'. :ids (:from 1 :to *) :attr (FLAGS UID))))))) ;; Depending on the server, there will be more or less unsolicited ;; data. EXISTS is a mandatory response to SELECT. (eimap-define-method eimap-README EXISTS (upcall-data imap-data) (message "This mailbox has %d messages" (plist-get imap-data :exists))) ;; The server may also send status responses. All status responses ;; (`resp-text-code') are converted to their own upcalls. (eimap-define-method eimap-README UIDVALIDITY (upcall-data imap-data) ;; in reality, we'd have to empty caches if the UIDVALIDITY changed. (message "This mailbox UID validity is %d" (plist-get imap-data :uidvalidity))) ;; this upcall will be issued for every FETCH response ;; ;; let's assume we're building something like offlineIMAP, just to ;; illustrate how async requests would work with overlapping requests. ;; ;; for a real application, you'd collect uids first and issue several ;; per request to reduce the protocol overhead. (eimap-define-method eimap-README FETCH (upcall-data imap-data) ;; usually, you'd update local state with this information and then ;; issue new requests based on local state. (when (plist-get imap-data :flags) (message "flags for uid %d (msgid %d) are %s" (plist-get imap-data :uid) (plist-get imap-data :msgid) (pp-to-string (plist-get imap-data :flags)))) ;; now, say we decide that this message is new and its body needs ;; to be fetched. for ease of demonstration, we'll just fetch the ;; bodystructure instead. (if (plist-get imap-data :bodystructure) ;; jolly good, we already got the data. (message "message uid %d has structure %s" (plist-get imap-data :uid) (pp-to-string (plist-get imap-data :bodystructure))) ;; no bodystructure present, let's fetch it. ;; this just simulates us picking some messages (when (eq 1 (% (plist-get imap-data :msgid) 30)) (eimap-request `(:method FETCH :uid t :ids ,(plist-get imap-data :uid) :attr (UID BODYSTRUCTURE)))))) (eimap-open "0x2c.org" :user "2" ;; `:upcall' is where it's at. All incoming data (and connection ;; state change) are communicated via `:upcall'. If you want to use ;; the automatic dispatch, just use `eimap-create-dispatch' to do the ;; work for you. ;; ;; Otherwise, pass a function that takes (upcall-data method data) as ;; arguments. `upcall-data' is what you pass in `:upcall-data'; ;; `method' is the IMAP response `:method' (see `eimap-parse.el'), ;; and `data' is the corresponding response data. For example, ;; `:upcall' might be called with: ;; ;; ("o hi" 'FETCH (:msgid 3 :flags ("\\seen" "\\answered") :uid 667)) :upcall (eimap-create-dispatch eimap-README) :upcall-data "o hi") ;; check out (switch-to-buffer-other-window "*Messages*") ;; that's where all the parsed data arrived