Browse code
1.4
jkf authored on 21/04/2000 21:52:15
Showing 4 changed files
Showing 4 changed files
... | ... |
@@ -1,3 +1,11 @@ |
1 |
+2000-04-21 John Foderaro <jkf@tiger.franz.com> |
|
2 |
+versio 1.4 |
|
3 |
+ * imap.cl: added pop commands unique-id and top-lines |
|
4 |
+ plus make-envelope-from-text |
|
5 |
+ |
|
6 |
+ * imap.html - update document |
|
7 |
+ |
|
8 |
+ |
|
1 | 9 |
1999-11-29 John Foderaro <jkf@tiger.franz.com> |
2 | 10 |
version 1.3 |
3 | 11 |
* imap.cl - fixed bug where extra ^b's ended up in strings |
... | ... |
@@ -19,7 +19,7 @@ |
19 | 19 |
;; Commercial Software developed at private expense as specified in |
20 | 20 |
;; DOD FAR Supplement 52.227-7013 (c) (1) (ii), as applicable. |
21 | 21 |
;; |
22 |
-;; $Id: imap.cl,v 1.9 2000/04/21 15:03:37 jkf Exp $ |
|
22 |
+;; $Id: imap.cl,v 1.10 2000/04/21 21:52:15 jkf Exp $ |
|
23 | 23 |
|
24 | 24 |
;; Description: |
25 | 25 |
;; |
... | ... |
@@ -62,6 +62,7 @@ |
62 | 62 |
#:fetch-letter |
63 | 63 |
#:fetch-parts |
64 | 64 |
#:*imap-version-number* |
65 |
+ #:make-envelope-from-text |
|
65 | 66 |
#:mailbox-flags ; accessor |
66 | 67 |
#:mailbox-permanent-flags ; acc |
67 | 68 |
#:mailbox-list |
... | ... |
@@ -75,6 +76,8 @@ |
75 | 76 |
#:make-imap-connection |
76 | 77 |
#:make-pop-connection |
77 | 78 |
#:noop |
79 |
+ #:top-lines ; pop only |
|
80 |
+ #:unique-id ; pop only |
|
78 | 81 |
|
79 | 82 |
#:po-condition |
80 | 83 |
#:po-condition-indentifier |
... | ... |
@@ -84,6 +87,7 @@ |
84 | 87 |
#:rename-mailbox |
85 | 88 |
#:search-mailbox |
86 | 89 |
#:select-mailbox |
90 |
+ |
|
87 | 91 |
) |
88 | 92 |
) |
89 | 93 |
|
... | ... |
@@ -91,7 +95,7 @@ |
91 | 95 |
|
92 | 96 |
(provide :imap) |
93 | 97 |
|
94 |
-(defparameter *imap-version-number* '(:major 1 :minor 3)) ; major.minor |
|
98 |
+(defparameter *imap-version-number* '(:major 1 :minor 4)) ; major.minor |
|
95 | 99 |
|
96 | 100 |
;; todo |
97 | 101 |
;; have the list of tags selected done on a per connection basis to |
... | ... |
@@ -512,7 +516,17 @@ |
512 | 516 |
|
513 | 517 |
|
514 | 518 |
(defun send-pop-command-get-results (pop command &optional extrap) |
515 |
- ;; if extrap is true then we're expecting data to follow an +ok |
|
519 |
+ ;; send the given command to the pop server |
|
520 |
+ ;; if extrap is true and if the response is +ok, then data |
|
521 |
+ ;; will follow the command (up to and excluding the first line consisting |
|
522 |
+ ;; of just a period) |
|
523 |
+ ;; |
|
524 |
+ ;; if the pop server returns an error code we signal a lisp error. |
|
525 |
+ ;; otherwise |
|
526 |
+ ;; return |
|
527 |
+ ;; extrap is nil -- return the list of tokens on the line after +ok |
|
528 |
+ ;; extrap is true -- return the extra object (a big string) |
|
529 |
+ ;; |
|
516 | 530 |
(format (post-office-socket pop) "~a~a" command *crlf*) |
517 | 531 |
(force-output (post-office-socket pop)) |
518 | 532 |
|
... | ... |
@@ -527,9 +541,15 @@ |
527 | 541 |
:server-string line)) |
528 | 542 |
|
529 | 543 |
(if* extrap |
530 |
- then ; get the rest of the data |
|
531 |
- |
|
532 |
- (let ((buf (get-line-buffer (+ (car parsed) 50))) |
|
544 |
+ then ;; get the rest of the data |
|
545 |
+ ;; many but not all pop servers return the size of the data |
|
546 |
+ ;; after the +ok, so we use that to initially size the |
|
547 |
+ ;; retreival buffer. |
|
548 |
+ (let ((buf (get-line-buffer (+ (if* (fixnump (car parsed)) |
|
549 |
+ then (car parsed) |
|
550 |
+ else 2048 ; reasonable size |
|
551 |
+ ) |
|
552 |
+ 50))) |
|
533 | 553 |
(pos 0) |
534 | 554 |
; states |
535 | 555 |
; 1 - after lf |
... | ... |
@@ -539,12 +559,14 @@ |
539 | 559 |
(sock (post-office-socket pop))) |
540 | 560 |
(flet ((add-to-buffer (ch) |
541 | 561 |
(if* (>= pos (length buf)) |
542 |
- then (po-error :unexpected |
|
543 |
- :format-control |
|
544 |
- "missinfomation from pop" |
|
545 |
- :server-string line) |
|
546 |
- else (setf (schar buf pos) ch) |
|
547 |
- (incf pos)))) |
|
562 |
+ then ; grow buffer |
|
563 |
+ (let ((new-buf (get-line-buffer |
|
564 |
+ (* (length buf) 2)))) |
|
565 |
+ (init-line-buffer new-buf buf) |
|
566 |
+ (free-line-buffer buf) |
|
567 |
+ (setq buf new-buf))) |
|
568 |
+ (setf (schar buf pos) ch) |
|
569 |
+ (incf pos))) |
|
548 | 570 |
(loop |
549 | 571 |
(let ((ch (read-char sock nil nil))) |
550 | 572 |
(if* (null ch) |
... | ... |
@@ -744,6 +766,77 @@ |
744 | 766 |
) |
745 | 767 |
|
746 | 768 |
|
769 |
+(defmethod unique-id ((pb pop-mailbox) &optional message) |
|
770 |
+ ;; if message is given, return the unique id of that |
|
771 |
+ ;; message, |
|
772 |
+ ;; if message is not given then return a list of lists: |
|
773 |
+ ;; (message unique-id) |
|
774 |
+ ;; for all messages not marked as deleted |
|
775 |
+ ;; |
|
776 |
+ (if* message |
|
777 |
+ then (let ((res (send-pop-command-get-results pb |
|
778 |
+ (format nil |
|
779 |
+ "UIDL ~d" |
|
780 |
+ message)))) |
|
781 |
+ (cadr res)) |
|
782 |
+ else ; get all of them |
|
783 |
+ (let* ((res (send-pop-command-get-results pb "UIDL" t)) |
|
784 |
+ (end (length res)) |
|
785 |
+ kind |
|
786 |
+ mnum |
|
787 |
+ mid |
|
788 |
+ (next 0)) |
|
789 |
+ |
|
790 |
+ |
|
791 |
+ (let ((coll)) |
|
792 |
+ (loop |
|
793 |
+ (multiple-value-setq (kind mnum next) |
|
794 |
+ (get-next-token res next end)) |
|
795 |
+ |
|
796 |
+ (if* (eq :eof kind) then (return)) |
|
797 |
+ |
|
798 |
+ (if* (not (eq :number kind)) |
|
799 |
+ then ; hmm. bogus |
|
800 |
+ (po-error :unexpected |
|
801 |
+ :format-control "uidl returned illegal message number in ~s" |
|
802 |
+ :format-arguments (list res))) |
|
803 |
+ |
|
804 |
+ ; now get message id |
|
805 |
+ |
|
806 |
+ (multiple-value-setq (kind mid next) |
|
807 |
+ (get-next-token res next end)) |
|
808 |
+ |
|
809 |
+ (if* (eq :number kind) |
|
810 |
+ then ; looked like a number to the tokenizer, |
|
811 |
+ ; make it a string to be consistent |
|
812 |
+ (setq mid (format nil "~d" mid)) |
|
813 |
+ elseif (not (eq :string kind)) |
|
814 |
+ then ; didn't find the uid |
|
815 |
+ (po-error :unexpected |
|
816 |
+ :format-control "uidl returned illegal message id in ~s" |
|
817 |
+ :format-arguments (list res))) |
|
818 |
+ |
|
819 |
+ (push (list mnum mid) coll)) |
|
820 |
+ |
|
821 |
+ (nreverse coll))))) |
|
822 |
+ |
|
823 |
+(defmethod top-lines ((pb pop-mailbox) message lines) |
|
824 |
+ ;; return the header and the given number of top lines of the message |
|
825 |
+ |
|
826 |
+ (let ((res (send-pop-command-get-results pb |
|
827 |
+ (format nil |
|
828 |
+ "TOP ~d ~d" |
|
829 |
+ message |
|
830 |
+ lines) |
|
831 |
+ t ; extra |
|
832 |
+ ))) |
|
833 |
+ res)) |
|
834 |
+ |
|
835 |
+ |
|
836 |
+ |
|
837 |
+ |
|
838 |
+ |
|
839 |
+ |
|
747 | 840 |
(defun check-for-success (mb command count extra comment command-string ) |
748 | 841 |
(declare (ignore mb count extra)) |
749 | 842 |
(if* (not (eq command :ok)) |
... | ... |
@@ -1163,7 +1256,125 @@ |
1163 | 1256 |
|
1164 | 1257 |
|
1165 | 1258 |
|
1259 |
+ |
|
1260 |
+ |
|
1261 |
+(defun make-envelope-from-text (text) |
|
1262 |
+ ;; given at least the headers part of a message return |
|
1263 |
+ ;; an envelope structure containing the contents |
|
1264 |
+ ;; This is useful for parsing the headers of things returned by |
|
1265 |
+ ;; a pop server |
|
1266 |
+ ;; |
|
1267 |
+ (let ((next 0) |
|
1268 |
+ (end (length text)) |
|
1269 |
+ header |
|
1270 |
+ value |
|
1271 |
+ kind |
|
1272 |
+ headers) |
|
1273 |
+ (labels ((next-header-line () |
|
1274 |
+ ;; find the next header line return |
|
1275 |
+ ;; :eof - no more |
|
1276 |
+ ;; :start - beginning of header value, header and |
|
1277 |
+ ;; value set |
|
1278 |
+ ;; :continue - continuation of previous header line |
|
1279 |
+ |
|
1280 |
+ |
|
1281 |
+ (let ((state 1) |
|
1282 |
+ beginv ; charpos beginning value |
|
1283 |
+ beginh ; charpos beginning header |
|
1284 |
+ ch |
|
1285 |
+ ) |
|
1286 |
+ (tagbody again |
|
1166 | 1287 |
|
1288 |
+ (return-from next-header-line |
|
1289 |
+ |
|
1290 |
+ (loop ; for each character |
|
1291 |
+ |
|
1292 |
+ (if* (>= next end) |
|
1293 |
+ then (return :eof)) |
|
1294 |
+ |
|
1295 |
+ (setq ch (char text next)) |
|
1296 |
+ |
|
1297 |
+ (if* (eq ch #\return) |
|
1298 |
+ thenret ; ignore return, (handle following linefeed) |
|
1299 |
+ else (case state |
|
1300 |
+ (1 ; no characters seen |
|
1301 |
+ (if* (eq ch #\linefeed) |
|
1302 |
+ then (incf next) |
|
1303 |
+ (return :eof) |
|
1304 |
+ elseif (member ch |
|
1305 |
+ '(#\space |
|
1306 |
+ #\tab)) |
|
1307 |
+ then ; continuation |
|
1308 |
+ (setq state 2) |
|
1309 |
+ else (setq beginh next) |
|
1310 |
+ (setq state 3) |
|
1311 |
+ )) |
|
1312 |
+ (2 ; looking for first non blank in value |
|
1313 |
+ (if* (eq ch #\linefeed) |
|
1314 |
+ then ; empty continuation line, ignore |
|
1315 |
+ (go again) |
|
1316 |
+ elseif (not (member ch |
|
1317 |
+ (member ch |
|
1318 |
+ '(#\space |
|
1319 |
+ #\tab)))) |
|
1320 |
+ then ; begin value part |
|
1321 |
+ (setq beginv next) |
|
1322 |
+ (setq state 4))) |
|
1323 |
+ (3 ; reading the header |
|
1324 |
+ (if* (eq ch #\linefeed) |
|
1325 |
+ then ; bogus header line, ignore |
|
1326 |
+ (go again) |
|
1327 |
+ elseif (eq ch #\:) |
|
1328 |
+ then (setq header |
|
1329 |
+ (subseq text beginh next)) |
|
1330 |
+ (setq state 2))) |
|
1331 |
+ (4 ; looking for the end of the value |
|
1332 |
+ (if* (eq ch #\linefeed) |
|
1333 |
+ then (setq value |
|
1334 |
+ (subseq text beginv |
|
1335 |
+ (if* (eq #\return |
|
1336 |
+ (char text |
|
1337 |
+ (1- next))) |
|
1338 |
+ then (1- next) |
|
1339 |
+ else next))) |
|
1340 |
+ (incf next) |
|
1341 |
+ (return (if* header |
|
1342 |
+ then :start |
|
1343 |
+ else :continue))))) |
|
1344 |
+ (incf next)))))))) |
|
1345 |
+ |
|
1346 |
+ |
|
1347 |
+ |
|
1348 |
+ (loop ; for each header line |
|
1349 |
+ (setq header nil) |
|
1350 |
+ (if* (eq :eof (setq kind (next-header-line))) |
|
1351 |
+ then (return)) |
|
1352 |
+ (case kind |
|
1353 |
+ (:start (push (cons header value) headers)) |
|
1354 |
+ (:continue |
|
1355 |
+ (if* headers |
|
1356 |
+ then ; append to previous one |
|
1357 |
+ (setf (cdr (car headers)) |
|
1358 |
+ (concatenate 'string (cdr (car headers)) |
|
1359 |
+ " " |
|
1360 |
+ value)))))) |
|
1361 |
+ |
|
1362 |
+ (make-envelope |
|
1363 |
+ :date (cdr (assoc "date" headers :test #'equalp)) |
|
1364 |
+ :subject (cdr (assoc "subject" headers :test #'equalp)) |
|
1365 |
+ :from (cdr (assoc "from" headers :test #'equalp)) |
|
1366 |
+ :sender (cdr (assoc "sender" headers :test #'equalp)) |
|
1367 |
+ :reply-to (cdr (assoc "reply-to" headers :test #'equalp)) |
|
1368 |
+ :to (cdr (assoc "to" headers :test #'equalp)) |
|
1369 |
+ :cc (cdr (assoc "cc" headers :test #'equalp)) |
|
1370 |
+ :bcc (cdr (assoc "bcc" headers :test #'equalp)) |
|
1371 |
+ :in-reply-to (cdr (assoc "in-reply-to" headers :test #'equalp)) |
|
1372 |
+ :message-id (cdr (assoc "message-id" headers :test #'equalp)) |
|
1373 |
+ )))) |
|
1374 |
+ |
|
1375 |
+ |
|
1376 |
+ |
|
1377 |
+ |
|
1167 | 1378 |
|
1168 | 1379 |
|
1169 | 1380 |
|
... | ... |
@@ -1171,8 +1382,8 @@ |
1171 | 1382 |
|
1172 | 1383 |
|
1173 | 1384 |
(defmethod get-and-parse-from-imap-server ((mb imap-mailbox)) |
1174 |
- ;; read the next line and parse it.... see parse-imap-response |
|
1175 |
- ;; for the return value of this function. |
|
1385 |
+ ;; read the next line and parse it |
|
1386 |
+ ;; |
|
1176 | 1387 |
;; |
1177 | 1388 |
(multiple-value-bind (line count) |
1178 | 1389 |
(get-line-from-server mb) |
... | ... |
@@ -1188,7 +1399,12 @@ |
1188 | 1399 |
|
1189 | 1400 |
(defmethod get-and-parse-from-pop-server ((mb pop-mailbox)) |
1190 | 1401 |
;; read the next line from the pop server |
1191 |
- ;; return the result of parsing it |
|
1402 |
+ ;; |
|
1403 |
+ ;; return 3 values: |
|
1404 |
+ ;; :ok or :error |
|
1405 |
+ ;; a list of rest of the tokens on the line |
|
1406 |
+ ;; the whole line after the +ok or -err |
|
1407 |
+ |
|
1192 | 1408 |
(multiple-value-bind (line count) |
1193 | 1409 |
(get-line-from-server mb) |
1194 | 1410 |
|
... | ... |
@@ -1312,9 +1528,10 @@ |
1312 | 1528 |
|
1313 | 1529 |
|
1314 | 1530 |
(defun parse-pop-response (line end) |
1315 |
- ;; return values: |
|
1531 |
+ ;; return 3 values: |
|
1316 | 1532 |
;; :ok or :error |
1317 |
- ;; a list of rest of the tokens on the line |
|
1533 |
+ ;; a list of rest of the tokens on the line, the tokens |
|
1534 |
+ ;; being either strings or integers |
|
1318 | 1535 |
;; the whole line after the +ok or -err |
1319 | 1536 |
;; |
1320 | 1537 |
(let (res lineres result) |
... | ... |
@@ -1359,6 +1576,8 @@ |
1359 | 1576 |
|
1360 | 1577 |
(setf (aref arr #.(char-code #\space)) :space) |
1361 | 1578 |
(setf (aref arr #.(char-code #\tab)) :space) |
1579 |
+ (setf (aref arr #.(char-code #\return)) :space) |
|
1580 |
+ (setf (aref arr #.(char-code #\linefeed)) :space) |
|
1362 | 1581 |
|
1363 | 1582 |
(setf (aref arr #.(char-code #\[)) :lbracket) |
1364 | 1583 |
(setf (aref arr #.(char-code #\])) :rbracket) |
... | ... |
@@ -1663,8 +1882,17 @@ |
1663 | 1882 |
(mp:without-scheduling |
1664 | 1883 |
(push buff *line-buffers*))) |
1665 | 1884 |
|
1885 |
+(defun init-line-buffer (new old) |
|
1886 |
+ ;; copy old into new |
|
1887 |
+ (declare (optimize (speed 3))) |
|
1888 |
+ (dotimes (i (length old)) |
|
1889 |
+ (declare (fixnum i)) |
|
1890 |
+ (setf (schar new i) (schar old i)))) |
|
1891 |
+ |
|
1892 |
+ |
|
1893 |
+ |
|
1666 | 1894 |
|
1667 |
-;;;;;;; |
|
1895 |
+ ;;;;;;; |
|
1668 | 1896 |
|
1669 | 1897 |
; date functions |
1670 | 1898 |
|
... | ... |
@@ -1687,12 +1915,3 @@ |
1687 | 1915 |
|
1688 | 1916 |
|
1689 | 1917 |
|
1690 |
- |
|
1691 |
- |
|
1692 |
- |
|
1693 |
- |
|
1694 |
- |
|
1695 |
- |
|
1696 |
- |
|
1697 |
- |
|
1698 |
- |
... | ... |
@@ -900,6 +900,15 @@ error to attempt to fetch a letter marked for deletion.</p> |
900 | 900 |
|
901 | 901 |
<p> </p> |
902 | 902 |
|
903 |
+<p><font face="Courier New"><strong>(make-envelope-from-text text)</strong></font></p> |
|
904 |
+ |
|
905 |
+<p><strong>text</strong> is a string that is the first part of a mail message, including |
|
906 |
+at least all of the headers lines and the blank line following the headers. This |
|
907 |
+function parses the header lines and return an <strong>envelope</strong> structure |
|
908 |
+containing information from the header. </p> |
|
909 |
+ |
|
910 |
+<p> </p> |
|
911 |
+ |
|
903 | 912 |
<p><font face="Courier New"><strong>(noop mb)</strong></font></p> |
904 | 913 |
|
905 | 914 |
<p>This is the no-operation command. It is useful for letting the <strong>pop</strong> |
... | ... |
@@ -912,11 +921,28 @@ will contain the current count of messages in the mailbox.</p> |
912 | 921 |
|
913 | 922 |
<p> </p> |
914 | 923 |
|
924 |
+<p><font face="Courier New"><strong>(top-lines mb message line-count)</strong></font></p> |
|
925 |
+ |
|
926 |
+<p>Return a string that contains all the header lines and the first <strong>line-count</strong> |
|
927 |
+lines of the body of <strong>message</strong>. To just retrieve the headers a <strong>line-count</strong> |
|
928 |
+of zero can be given. See the function <strong>make-envelope-from-text</strong> for |
|
929 |
+a means of reading the information in the header.</p> |
|
930 |
+ |
|
931 |
+<p> </p> |
|
932 |
+ |
|
933 |
+<p><font face="Courier New"><strong>(unique-id mb &optional message)</strong></font></p> |
|
934 |
+ |
|
935 |
+<p>Return the unique indentifier for the given message, or for all non-deleted messages if |
|
936 |
+<strong>message</strong> is nil. The unique identifier is is a string that is |
|
937 |
+different for every message. If <strong> </strong>the <strong>message</strong> |
|
938 |
+argument is not given then this command returns a list of lists where each list |
|
939 |
+contains two items: the message number and the unique id.</p> |
|
940 |
+ |
|
915 | 941 |
<h1>Cond<a name="conditions"></a>itions</h1> |
916 | 942 |
|
917 | 943 |
<p>When an unexpected event occurs a condition is signaled. This applies to |
918 | 944 |
both the <strong>imap</strong> and <strong>pop</strong> interfaces. There are two |
919 |
-classes of conditions signaled by this package: |
|
945 |
+classes of conditions signaled by this package: |
|
920 | 946 |
|
921 | 947 |
<ul> |
922 | 948 |
<li><strong>po-condition</strong> - this class denotes conditions that need not and in fact |
... | ... |
@@ -926,7 +952,7 @@ classes of conditions signaled by this package: |
926 | 952 |
and processed otherwise the next command issued will see messages in response to the |
927 | 953 |
previous command. Therefore the user code should never do a non-local-transfer |
928 | 954 |
in response to a <strong>po-condition.</strong></li> |
929 |
- <li><strong>po-error - </strong> this class denotes conditions that will prevent execution |
|
955 |
+ <li><strong>po-error - </strong>this class denotes conditions that will prevent execution |
|
930 | 956 |
from continuing. If one of these errors is not caught, the interactive debugger will |
931 | 957 |
be entered.</li> |
932 | 958 |
</ul> |
... | ... |
@@ -177,8 +177,11 @@ |
177 | 177 |
; still from before |
178 | 178 |
(test-eql 4 (po:mailbox-message-count pb)) |
179 | 179 |
|
180 |
+ (test-eql 4 (length (po:unique-id pb))) |
|
181 |
+ |
|
180 | 182 |
(po:delete-letter pb '(:seq 2 3)) |
181 | 183 |
|
184 |
+ (test-eql 2 (length (po:unique-id pb))) |
|
182 | 185 |
|
183 | 186 |
(test-eql 4 (and :second (po:mailbox-message-count pb))) |
184 | 187 |
|
... | ... |
@@ -201,6 +204,9 @@ |
201 | 204 |
|
202 | 205 |
(po:fetch-letter pb 1) ; just make sure there's no error |
203 | 206 |
|
207 |
+ (po:top-lines pb 1 1) ; just make sure there's no error |
|
208 |
+ (po:make-envelope-from-text (po:top-lines pb 1 0)) |
|
209 |
+ |
|
204 | 210 |
(po:close-connection pb))) |
205 | 211 |
|
206 | 212 |
|