git.fiddlerwoaroof.com
Browse code

imap 1.0

jkf authored on 27/09/1999 20:21:57
Showing 5 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,11 @@
1
+1999-09-27  John Foderaro  <jkf@tiger.franz.com>
2
+version 1.0
3
+	* start ChangeLog.
4
+	* imap.cl - the code for the imap and pop interface
5
+	* imap.html - the documentation
6
+	* t-imap.cl - the test suite
7
+	* rfc1939.html - pop spec
8
+	* rfc2060.txt  - imap spec
9
+	
10
+	
11
+
... ...
@@ -1,8 +1,8 @@
1 1
 ;; imap protocol
2 2
 ;; (with hooks for pop too)
3 3
 
4
-(defpackage :mailbox
5
-  (:nicknames :mb)
4
+(defpackage :post-office
5
+  (:nicknames :po)
6 6
   (:use :lisp :excl)
7 7
   (:export 
8 8
    #:address-name
... ...
@@ -11,7 +11,7 @@
11 11
    #:address-host
12 12
    
13 13
    #:alter-flags
14
-   #:close-imap-connection
14
+   #:close-connection
15 15
    #:close-mailbox
16 16
    #:copy-to-mailbox
17 17
    #:create-mailbox
... ...
@@ -32,6 +32,8 @@
32 32
    #:expunge-mailbox
33 33
    #:fetch-field
34 34
    #:fetch-letter
35
+   #:fetch-parts
36
+   #:*imap-version-number*
35 37
    #:mailbox-flags      ; accessor
36 38
    #:mailbox-permanent-flags ; acc
37 39
    #:mailbox-list
... ...
@@ -43,6 +45,7 @@
43 45
    #:mailbox-separator  ; accessor
44 46
    #:mailbox-uidvalidity
45 47
    #:make-imap-connection
48
+   #:make-pop-connection
46 49
    #:noop
47 50
    #:rename-mailbox
48 51
    #:search-mailbox
... ...
@@ -50,41 +53,26 @@
50 53
    )
51 54
   )
52 55
 
53
-(in-package :mailbox)
54
-
55
-; kinds of things that come back from the server
56
-; <tag> OK random text 
57
-; <tag> OK [atom] random text 
58
-; <tag> OK [atom value] random text
59
-; * <number> atom random text
60
-; * LIST (atom ...) string string
61
-; * STATUS mboxname (atom value .... ...)
62
-; * CAPABILITY atom ...
63
-
64
-; our parsing will return
65
-; tag
66
-;    a string or :untagged or :continue
67
-; command 
68
-;    the string like "OK" which describes what this response is saying
69
-; args
70
-;    list of arguments.
71
-;       
56
+(in-package :post-office)
57
+
58
+
59
+(defparameter *imap-version-number* '(:major 1 :minor 0)) ; major.minor
72 60
 
73 61
 (defvar *debug-imap* nil)
74 62
 
75 63
 
76
-(defclass mailbox ()
64
+(defclass post-office ()
77 65
   ((socket :initarg :socket
78
-	   :accessor mailbox-socket)
66
+	   :accessor post-office-socket)
79 67
    
80 68
    (host :initarg :host
81
-	 :accessor  mailbox-host
69
+	 :accessor  post-office-host
82 70
 	 :initform nil)
83 71
    (user  :initarg :user
84
-	  :accessor mailbox-user
72
+	  :accessor post-office-user
85 73
 	  :initform nil)
86 74
    
87
-   (state :accessor mailbox-state
75
+   (state :accessor post-office-state
88 76
 	  :initarg :state
89 77
 	  :initform :unconnected)
90 78
    
... ...
@@ -96,7 +84,7 @@
96 84
     :accessor timeout) 
97 85
   ))
98 86
 
99
-(defclass imap-mailbox (mailbox)
87
+(defclass imap-mailbox (post-office)
100 88
   ((mailbox-name   ; currently selected mailbox
101 89
     :accessor mailbox-name
102 90
     :initform nil)
... ...
@@ -136,13 +124,15 @@
136 124
     :accessor first-unseen
137 125
     :initform 0)
138 126
    
139
-   ;;; end list of values for the currently selected maibox
127
+   ;;; end list of values for the currently selected mailbox
140 128
    )
141 129
   )
142 130
 
143 131
 
144
-(defclass pop-mailbox (mailbox)
145
-  ())
132
+(defclass pop-mailbox (post-office)
133
+  ((message-count  ; how many in the mailbox
134
+    :accessor mailbox-message-count
135
+    :initform 0)))
146 136
 
147 137
 
148 138
 
... ...
@@ -222,9 +212,9 @@
222 212
     imap))
223 213
 
224 214
 
225
-(defmethod close-imap-connection ((mb imap-mailbox))
215
+(defmethod close-connection ((mb imap-mailbox))
226 216
   
227
-  (let ((sock (mailbox-socket mb)))
217
+  (let ((sock (post-office-socket mb)))
228 218
     (if* sock
229 219
        then (ignore-errors
230 220
 	     (send-command-get-results 
... ...
@@ -238,20 +228,62 @@
238 228
 	      #'(lambda (mb command count extra)
239 229
 		  (check-for-success mb command count extra
240 230
 				     "logout")))))
241
-    (setf (mailbox-socket mb) nil)
231
+    (setf (post-office-socket mb) nil)
232
+    (if* sock then (ignore-errors (close sock)))
233
+    t))
234
+
235
+
236
+(defmethod close-connection ((pb pop-mailbox))
237
+  (let ((sock (post-office-socket pb)))
238
+    (if* sock
239
+       then (ignore-errors
240
+	     (send-pop-command-get-results 
241
+	      pb
242
+	      "QUIT")))
243
+    (setf (post-office-socket pb) nil)
242 244
     (if* sock then (ignore-errors (close sock)))
243 245
     t))
244 246
 
245 247
 
248
+
249
+(defun make-pop-connection (host &key (port 110)
250
+				      user
251
+				      password
252
+				      (timeout 30))
253
+  (let* ((sock (socket:make-socket :remote-host host
254
+				   :remote-port port))
255
+	 (pop (make-instance 'pop-mailbox
256
+		:socket sock
257
+		:host   host
258
+		:timeout timeout
259
+		:state :unauthorized)))
260
+    
261
+    (multiple-value-bind (result)
262
+	(get-and-parse-from-pop-server pop)
263
+      (if* (not (eq :ok result))
264
+	 then  (error "unexpected line from server after connect")))
265
+      
266
+    ; now login
267
+    (send-pop-command-get-results pop (format nil "user ~a" user))
268
+    (send-pop-command-get-results pop (format nil "pass ~a" password))
269
+
270
+    (let ((res (send-pop-command-get-results pop "stat")))
271
+      (setf (mailbox-message-count pop) (car res)))
272
+    
273
+    			    
274
+				    
275
+    pop))
276
+			    
277
+
246 278
 (defmethod send-command-get-results ((mb imap-mailbox) 
247 279
 				     command untagged-handler tagged-handler)
248 280
   ;; send a command and retrieve results until we get the tagged
249 281
   ;; response for the command we sent
250 282
   ;;
251 283
   (let ((tag (get-next-tag)))
252
-    (format (mailbox-socket mb)
284
+    (format (post-office-socket mb)
253 285
 	    "~a ~a~a" tag command *crlf*)
254
-    (force-output (mailbox-socket mb))
286
+    (force-output (post-office-socket mb))
255 287
     
256 288
     (if* *debug-imap*
257 289
        then (format t
... ...
@@ -284,7 +316,7 @@
284 316
     (:recent (setf (mailbox-recent-messages mb) count))
285 317
     (:flags  (setf (mailbox-flags mb) (mapcar #'kwd-intern extra)))
286 318
     (:bye ; occurs when connection times out or mailbox lock is stolen
287
-     (ignore-errors (close (mailbox-socket mb)))
319
+     (ignore-errors (close (post-office-socket mb)))
288 320
      (error "connection to the imap server was closed by the server"))
289 321
     (:no ; used when grabbing a lock from another process
290 322
      (warn "grabbing mailbox lock from another process"))
... ...
@@ -304,6 +336,69 @@
304 336
 	     
305 337
   )
306 338
 
339
+
340
+
341
+(defun send-pop-command-get-results (pop command &optional extrap)
342
+  ;; if extrap is true then we're expecting data to follow an +ok
343
+  (format (post-office-socket pop) "~a~a" command *crlf*)
344
+  (force-output (post-office-socket pop))
345
+  
346
+  (if* *debug-imap*
347
+     then (format t "~a~a" command *crlf*)
348
+	  (force-output t))
349
+
350
+  (multiple-value-bind (result parsed line)
351
+      (get-and-parse-from-pop-server pop)
352
+    (if* (not (eq result :ok))
353
+       then (error "error from pop server: ~a" line))
354
+
355
+    (if* extrap
356
+       then ; get the rest of the data
357
+	    
358
+	    (let ((buf (get-line-buffer (+ (car parsed) 50)))
359
+		  (pos 0)
360
+		  ; states
361
+		  ;  1 - after lf
362
+		  ;  2 - seen dot at beginning of line
363
+		  ;  3 - seen regular char on line
364
+		  (state 1)
365
+		  (sock (post-office-socket pop)))
366
+	      (flet ((add-to-buffer (ch)
367
+		       (if* (>= pos (length buf))
368
+			  then (error "missinfomation from pop")
369
+			  else (setf (schar buf pos) ch)
370
+			       (incf pos))))
371
+		(loop
372
+		  (let ((ch (read-char sock nil nil)))
373
+		    (if* (null ch)
374
+		       then (error "premature end of file from server"))
375
+		    (if* (eq ch #\return)
376
+		       thenret ; ignore crs
377
+		       else (case state
378
+			      (1 (if* (eq ch #\.)
379
+				    then (setq state 2)
380
+				  elseif (eq ch #\linefeed)
381
+				    then (add-to-buffer ch)
382
+					 ; state stays at 1
383
+				    else (add-to-buffer ch)
384
+					 (setq state 3)))
385
+			      (2 ; seen first dot
386
+			       (if* (eq ch #\linefeed)
387
+				  then ; end of message
388
+				       (return)
389
+				  else (add-to-buffer ch)
390
+				       (setq state 3)))
391
+			      (3 ; normal reading
392
+			       (add-to-buffer ch)
393
+			       (if* (eq ch #\linefeed)
394
+				  then (setq state 1))))))))
395
+	      (prog1 (subseq buf 0 pos)
396
+		(free-line-buffer buf)))
397
+       else parsed)))
398
+  
399
+
400
+  
401
+  
307 402
 (defun convert-flags-plist (plist)
308 403
   ;; scan the plist looking for "flags" indicators and 
309 404
   ;; turn value into a list of symbols rather than strings
... ...
@@ -313,8 +408,8 @@
313 408
        then (setf (cadr xx) (mapcar #'kwd-intern (cadr xx))))))
314 409
 
315 410
 
316
-(defun select-mailbox (mb name)
317
-  ;; select the given maibox
411
+(defmethod select-mailbox ((mb imap-mailbox) name)
412
+  ;; select the given mailbox
318 413
   (send-command-get-results mb
319 414
 			    (format nil "select ~a" name)
320 415
 			    #'handle-untagged-response
... ...
@@ -327,8 +422,21 @@
327 422
   )
328 423
 
329 424
 
425
+(defmethod fetch-letter ((mb imap-mailbox) number &key uid)
426
+  ;; return the whole letter
427
+  (fetch-field number "body[]"
428
+	       (fetch-parts mb number "body[]" :uid uid)
429
+	       :uid uid))
330 430
 
331
-(defun fetch-letter (mb number parts &key uid)
431
+
432
+(defmethod fetch-letter ((pb pop-mailbox) number &key uid)
433
+  (declare (ignore uid))
434
+  (send-pop-command-get-results pb 
435
+				(format nil "RETR ~d" number) 
436
+				t ; extra stuff
437
+				))
438
+
439
+(defmethod fetch-parts ((mb imap-mailbox) number parts &key uid)
332 440
   (let (res)
333 441
     (send-command-get-results 
334 442
      mb
... ...
@@ -394,15 +502,42 @@
394 502
 					
395 503
 
396 504
 
397
-(defun delete-letter (mb messages &key (expunge t) uid)
505
+(defmethod delete-letter ((mb imap-mailbox) messages &key (expunge t) uid)
398 506
   ;; delete all the mesasges and do the expunge to make 
399 507
   ;; it permanent if expunge is true
400 508
   (alter-flags mb messages :add-flags :\\deleted :uid uid)
401 509
   (if* expunge then (expunge-mailbox mb)))
510
+
511
+(defmethod delete-letter ((pb pop-mailbox) messages  &key (expunge nil) uid)
512
+  ;; delete all the messages.   We can't expunge without quitting so
513
+  ;; we don't expunge
514
+  (declare (ignore expunge uid))
515
+  
516
+  (if* (or (numberp messages) 
517
+	   (and (consp messages) (eq :seq (car messages))))
518
+     then (setq messages (list messages)))
519
+  
520
+  (if* (not (consp messages))
521
+     then (error "expect a mesage number or list of messages, not ~s"
522
+		 messages))
523
+  
524
+  (dolist (message messages)
525
+    (if* (numberp message)
526
+       then (send-pop-command-get-results pb
527
+					  (format nil "DELE ~d" message))
528
+     elseif (and (consp message) (eq :seq (car message)))
529
+       then (do ((start (cadr message) (1+ start))
530
+		 (end (caddr message)))
531
+		((> start end))
532
+	      (send-pop-command-get-results pb
533
+					    (format nil "DELE ~d" start)))
534
+       else (error "bad message number ~s" message))))
535
+	    
536
+	    
402 537
 			    
403 538
 					
404 539
 
405
-(defun noop (mb)
540
+(defmethod noop ((mb imap-mailbox))
406 541
   ;; just poke the server... keeping it awake and checking for
407 542
   ;; new letters
408 543
   (send-command-get-results mb
... ...
@@ -414,6 +549,13 @@
414 549
 				 "noop"))))
415 550
 
416 551
 
552
+(defmethod noop ((pb pop-mailbox))
553
+  ;; send the stat command instead so we can update the message count
554
+  (let ((res (send-pop-command-get-results pb "stat")))
555
+      (setf (mailbox-message-count pb) (car res)))
556
+  )
557
+
558
+
417 559
 (defun check-for-success (mb command count extra command-string)
418 560
   (declare (ignore mb count extra))
419 561
   (if* (not (eq command :ok))
... ...
@@ -423,7 +565,7 @@
423 565
 			    
424 566
 
425 567
 
426
-(defun mailbox-list (mb &key (reference "") (pattern ""))
568
+(defmethod mailbox-list ((mb imap-mailbox) &key (reference "") (pattern ""))
427 569
   ;; return a list of mailbox names with respect to a given
428 570
   (let (res)
429 571
     (send-command-get-results mb
... ...
@@ -447,7 +589,7 @@
447 589
     ))
448 590
 
449 591
 
450
-(defun create-mailbox (mb mailbox-name)
592
+(defmethod create-mailbox ((mb imap-mailbox) mailbox-name)
451 593
   ;; create a mailbox name of the given name.
452 594
   ;; use mailbox-separator if you want to create a hierarchy
453 595
   (send-command-get-results mb
... ...
@@ -459,7 +601,7 @@
459 601
   t)
460 602
 
461 603
 
462
-(defun delete-mailbox (mb mailbox-name)
604
+(defmethod delete-mailbox ((mb imap-mailbox) mailbox-name)
463 605
   ;; create a mailbox name of the given name.
464 606
   ;; use mailbox-separator if you want to create a hierarchy
465 607
   (send-command-get-results mb
... ...
@@ -469,7 +611,7 @@
469 611
 				  (check-for-success 
470 612
 				   mb command count extra "delete"))))
471 613
 
472
-(defun rename-mailbox (mb old-mailbox-name new-mailbox-name)
614
+(defmethod rename-mailbox ((mb imap-mailbox) old-mailbox-name new-mailbox-name)
473 615
   ;; create a mailbox name of the given name.
474 616
   ;; use mailbox-separator if you want to create a hierarchy
475 617
   (send-command-get-results mb
... ...
@@ -483,7 +625,9 @@
483 625
 
484 626
 
485 627
 
486
-(defun alter-flags (mb messages &key (flags nil flags-p) add-flags remove-flags
628
+(defmethod alter-flags ((mb imap-mailbox)
629
+			messages &key (flags nil flags-p) 
630
+				      add-flags remove-flags
487 631
 				      silent uid)
488 632
   ;;
489 633
   ;; change the flags using the store command
... ...
@@ -598,7 +742,7 @@
598 742
 
599 743
 ;; search command
600 744
 
601
-(defun search-mailbox (mb search-expression &key uid)
745
+(defmethod search-mailbox ((mb imap-mailbox) search-expression &key uid)
602 746
   (let (res)
603 747
     (send-command-get-results mb
604 748
 			      (format nil "~asearch ~a" 
... ...
@@ -816,6 +960,22 @@
816 960
     ))
817 961
 
818 962
 
963
+
964
+(defmethod get-and-parse-from-pop-server ((mb pop-mailbox))
965
+  ;; read the next line from the pop server
966
+  ;; return the result of parsing it
967
+  (multiple-value-bind (line count)
968
+      (get-line-from-server mb)
969
+    
970
+    (if* *debug-imap* 
971
+       then (format t "from server: " count)
972
+	    (dotimes (i count)(write-char (schar line i)))
973
+	    (terpri))
974
+    
975
+    (parse-pop-response line count)))
976
+
977
+  
978
+  
819 979
 ;; Parse and return the data from each line
820 980
 ;; values returned
821 981
 ;;  tag -- either a string or the symbol :untagged
... ...
@@ -911,7 +1071,42 @@
911 1071
        (values kind nil next))
912 1072
       (t (error "bad sexpression")))))
913 1073
 
1074
+
1075
+(defun parse-pop-response (line end)
1076
+  ;; return values:
1077
+  ;;   :ok or :error 
1078
+  ;;   a list of rest of the tokens on the line
1079
+  ;;   the whole line after the +ok or -err
1080
+  ;;
1081
+  (let (res lineres result)
1082
+    (multiple-value-bind (kind value next)
1083
+	(get-next-token line 0 end)
1084
+    
1085
+      (case kind
1086
+	(:string (setq result (if* (equal "+OK" value) 
1087
+				 then :ok
1088
+				 else :error)))
1089
+	(t (error "bad response from server: ~s" (subseq line 0 end))))
914 1090
     
1091
+      (setq lineres (subseq line next end))
1092
+
1093
+      (loop
1094
+	(multiple-value-setq (kind value next)
1095
+	  (get-next-token line next end))
1096
+	
1097
+	(case kind
1098
+	  (:eof (return))
1099
+	  ((:string :number) (push value res))))
1100
+      
1101
+      (values result (nreverse res) lineres))))
1102
+    
1103
+	
1104
+    
1105
+    
1106
+    
1107
+    
1108
+      
1109
+      
915 1110
 			 
916 1111
     
917 1112
 (defparameter *char-to-kind*
... ...
@@ -1074,7 +1269,7 @@
1074 1269
   (let* ((buff (get-line-buffer 0))
1075 1270
 	 (len  (length buff))
1076 1271
 	 (i 0)
1077
-	 (p (mailbox-socket mailbox))
1272
+	 (p (post-office-socket mailbox))
1078 1273
 	 (ch nil)
1079 1274
 	 (whole-count) 
1080 1275
 	 )
... ...
@@ -1181,7 +1376,7 @@
1181 1376
   ;; like lisp likes).
1182 1377
   ;;
1183 1378
   (let ((buff (get-line-buffer count))
1184
-	(p (mailbox-socket mb))
1379
+	(p (post-office-socket mb))
1185 1380
 	(ind 0))
1186 1381
     (mp:with-timeout ((timeout mb)
1187 1382
 		      (error "imap server timed out"))
... ...
@@ -1,13 +1,13 @@
1 1
 <html>
2 2
 
3 3
 <head>
4
-<title>Allegro CL imap interface</title>
4
+<title>Allegro CL imap and pop interface</title>
5 5
 <meta name="GENERATOR" content="Microsoft FrontPage 3.0">
6 6
 </head>
7 7
 
8 8
 <body>
9 9
 
10
-<h1 align="center">Allegro CL imap interface</h1>
10
+<h1 align="center">Allegro CL imap and pop interface</h1>
11 11
 
12 12
 <p align="left">copyright (c) 1999 Franz Inc.</p>
13 13
 
... ...
@@ -15,11 +15,14 @@
15 15
 
16 16
 <p align="left"><strong>imap</strong> is a client-server protocol for processing
17 17
 electronic mail boxes.&nbsp; <strong>imap </strong>is the successor to the <strong>pop</strong>
18
-protocol.&nbsp;&nbsp; It is not an upward compatible successor.</p>
18
+protocol.&nbsp;&nbsp; It is <strong>not</strong> an upward compatible successor.
19
+&nbsp;&nbsp;&nbsp;&nbsp; The main focus of this document is the <strong>imap</strong>
20
+protocol.&nbsp;&nbsp;&nbsp; Only one small section describes the functions in the <strong>pop</strong>
21
+interface.</p>
19 22
 
20 23
 <p align="left">This document and interface is based on the Imap4rev1 protocol described
21 24
 in rfc2060.&nbsp;&nbsp; Where this document is describing the actions of the imap commands
22
-it should be considered a secondary source of information about those command and rfc2060
25
+it should be considered a secondary source of information about those commands and rfc2060
23 26
 should be considered the primary source.</p>
24 27
 
25 28
 <p align="left">The advantages of <strong>imap</strong> over <strong>pop</strong> are:</p>
... ...
@@ -46,6 +49,13 @@ should be considered the primary source.</p>
46 49
 
47 50
 <p align="left">&nbsp;</p>
48 51
 
52
+<h1 align="left">Package</h1>
53
+
54
+<p align="left">The functions in this interface are defined in the <strong>post-office</strong>
55
+package which has a nickname <strong>po</strong>.</p>
56
+
57
+<p align="left">&nbsp;</p>
58
+
49 59
 <h1 align="left">Mailboxes</h1>
50 60
 
51 61
 <p align="left">Mailboxes are repositories for messages.&nbsp;&nbsp; Mailboxes are named
... ...
@@ -144,7 +154,7 @@ select a mailbox using <strong>select-mailbox</strong> shortly after connecting.
144 154
 
145 155
 <p align="left">&nbsp;</p>
146 156
 
147
-<p align="left"><strong><font face="Courier New">(close-imap-connection mailbox)</font></strong></p>
157
+<p align="left"><strong><font face="Courier New">(close-connection mailbox)</font></strong></p>
148 158
 
149 159
 <p align="left">This sends a <strong>logout</strong> command to the <strong>imap</strong>
150 160
 server and then closes the socket that's communicating with the <strong>imap</strong>
... ...
@@ -324,20 +334,29 @@ string just as used in the call to <strong>fetch-letter</strong>.</p>
324 334
 
325 335
 <p align="left">&nbsp;</p>
326 336
 
327
-<p align="left"><font face="Courier New"><strong>(fetch-letter mailbox messages parts
337
+<p align="left"><strong><font face="Courier New">(fetch-letter mailbox message &amp;key
338
+uid)</font></strong></p>
339
+
340
+<p align="left">Return the complete message, headers and body, as one big string. &nbsp;
341
+This is a combination of <strong>fetch-field</strong> and <strong>fetch-parts</strong>
342
+where the part specification is &quot;body[]&quot;.</p>
343
+
344
+<p align="left">&nbsp;</p>
345
+
346
+<p align="left"><font face="Courier New"><strong>(fetch-parts mailbox messages parts
328 347
 &amp;key uid)</strong></font></p>
329 348
 
330 349
 <p align="left">retrieves the specified <strong>parts</strong> of the specified <strong>messages.
331 350
 &nbsp;&nbsp; </strong>If <strong>uid</strong> is true then the <strong>messages</strong>
332 351
 are considered to be unique ids rather than message sequence numbers.
333 352
 &nbsp;&nbsp;&nbsp;&nbsp; The description of what can be specified for <strong>parts </strong>is
334
-quite complex and has been moved to the section below &quot;Fetching a Letter&quot;.</p>
353
+quite complex and is described in the section below &quot;Fetching a Letter&quot;.</p>
335 354
 
336 355
 <p align="left">The return value from this function is a structure that can be examined
337 356
 with <strong>fetch-field</strong>.</p>
338 357
 
339 358
 <p align="left">When the result returned includes an envelope value the following
340
-functions can be used to extract&nbsp; the parts of the envelope:</p>
359
+functions can be used to extract&nbsp; the components of the envelope:</p>
341 360
 
342 361
 <ul>
343 362
   <li><p align="left"><font face="Courier New"><strong>envelope-date</strong></font></p>
... ...
@@ -443,7 +462,7 @@ next message.</p>
443 462
 
444 463
 <h1 align="left">Fetching a Letter</h1>
445 464
 
446
-<p align="left">When using <strong>fetch-letter</strong> to access letters, you must
465
+<p align="left">When using <strong>fetch-parts</strong> to access letters, you must
447 466
 specify the parts of the messages in which you're interested.&nbsp;&nbsp; There are a wide
448 467
 variety of specifiers, some redundant and overlapping, described in the imap specification
449 468
 in rfe2060.&nbsp; We'll describe the most common ones here.&nbsp;&nbsp; The specification
... ...
@@ -454,7 +473,8 @@ the string, e.g. &quot;(flags envelope)&quot;.&nbsp;&nbsp; </p>
454 473
 
455 474
 <ul>
456 475
   <li><p align="left"><strong>body[]</strong> - this returns the full message: headers and
457
-    body.</p>
476
+    body.&nbsp;&nbsp; You can use <strong>fetch-letter</strong> if you only want this part and
477
+    you want to avoid having to call <strong>fetch-field</strong>.</p>
458 478
   </li>
459 479
   <li><p align="left"><strong>body[text]</strong> - this returns just the the text of the body
460 480
     of the message, not the header.</p>
... ...
@@ -474,7 +494,7 @@ the string, e.g. &quot;(flags envelope)&quot;.&nbsp;&nbsp; </p>
474 494
 
475 495
 <p align="left">&nbsp;</p>
476 496
 
477
-<p align="left">The result of a <strong>fetch-letter</strong> is a data structure
497
+<p align="left">The result of a <strong>fetch-parts</strong> is a data structure
478 498
 containing all of the requested information.&nbsp;&nbsp; The <strong>fetch-field</strong>
479 499
 function is then used to extract the particular information for the particular message.</p>
480 500
 
... ...
@@ -606,7 +626,7 @@ these forms:</p>
606 626
 <p align="left"><strong>Connect to the imap server on the machine holding the email:</strong></p>
607 627
 <div align="left">
608 628
 
609
-<pre>user(2): (setq mb (mb:make-imap-connection &quot;mailmachine.franz.com&quot; 
629
+<pre>user(2): (setq mb (po:make-imap-connection &quot;mailmachine.franz.com&quot; 
610 630
                             :user &quot;myacct&quot; 
611 631
                             :password &quot;mypasswd&quot;))
612 632
 #&lt;mailbox::imap-mailbox @ #x2064ca4a&gt;</pre>
... ...
@@ -618,7 +638,7 @@ these forms:</p>
618 638
 <div align="left">
619 639
 
620 640
 <pre>
621
-user(3): (mb:select-mailbox mb &quot;inbox&quot;)
641
+user(3): (po:select-mailbox mb &quot;inbox&quot;)
622 642
 t</pre>
623 643
 </div>
624 644
 
... ...
@@ -628,16 +648,17 @@ t</pre>
628 648
 <div align="left">
629 649
 
630 650
 <pre>
631
-user(4): (mb:mailbox-message-count mb)
651
+user(4): (po:mailbox-message-count mb)
632 652
 7</pre>
633 653
 </div>
634 654
 
635 655
 <p align="left"><strong>There are seven messages at the moment.&nbsp;&nbsp; Fetch the
636
-whole 4th message</strong></p>
656
+whole 4th message.&nbsp; We could call (po:fetch-letter mb 4) here instead and then not
657
+have to call fetch-field later.</strong></p>
637 658
 <div align="left">
638 659
 
639 660
 <pre>
640
-user(5): (setq body (mb:fetch-letter mb 4 &quot;body[]&quot;))
661
+user(5): (setq body (po:fetch-parts mb 4 &quot;body[]&quot;))
641 662
 ((4
642 663
 (&quot;BODY[]&quot; &quot;Return-Path: &lt;jkfmail@tiger.franz.com&gt;
643 664
 Received: from tiger.franz.com (jkf@tiger [192.132.95.103])
... ...
@@ -657,7 +678,7 @@ information we want we use fetch-field:</strong></p>
657 678
 <div align="left">
658 679
 
659 680
 <pre>
660
-user(6): (mb:fetch-field 4 &quot;body[]&quot; body)
681
+user(6): (po:fetch-field 4 &quot;body[]&quot; body)
661 682
 &quot;Return-Path: &lt;jkfmail@tiger.franz.com&gt;
662 683
 Received: from tiger.franz.com (jkf@tiger [192.132.95.103])
663 684
 &nbsp;&nbsp;&nbsp; by tiger.franz.com (8.8.7/8.8.7) with SMTP id LAA20261
... ...
@@ -676,9 +697,9 @@ that message.</strong></p>
676 697
 <div align="left">
677 698
 
678 699
 <pre>
679
-user(7): (mb:search-mailbox mb '(:text &quot;blitzfig&quot;))
700
+user(7): (po:search-mailbox mb '(:text &quot;blitzfig&quot;))
680 701
 (7)
681
-user(8): (mb:fetch-field 7 &quot;body[]&quot; (mb:fetch-letter mb 7 &quot;body[]&quot;))
702
+user(8): (po:fetch-field 7 &quot;body[]&quot; (po:fetch-letter mb 7 &quot;body[]&quot;))
682 703
 &quot;Return-Path: &lt;jkf@verada.com&gt;
683 704
 Received: from main.verada.com (main.verada.com [208.164.216.3])
684 705
 &nbsp;&nbsp;&nbsp; by tiger.franz.com (8.8.7/8.8.7) with ESMTP id NAA20541
... ...
@@ -697,16 +718,16 @@ ok?
697 718
 &quot;</pre>
698 719
 </div>
699 720
 
700
-<p align="left"><strong>We've been using message sequence numbers up to now.
701
-&nbsp;&nbsp; The are the simplest to use but if you're concerned with keeping track of
702
-messages when deletions are being done then using unique id's is useful.&nbsp;&nbsp; Here
703
-we do the above search example using uids:</strong></p>
721
+<p align="left"><strong>We've been using message sequence numbers up to now. &nbsp;&nbsp;
722
+The are the simplest to use but if you're concerned with keeping track of messages when
723
+deletions are being done then using unique id's is useful.&nbsp;&nbsp; Here we do the
724
+above search example using uids:</strong></p>
704 725
 <div align="left">
705 726
 
706 727
 <pre>
707
-user(9): (mb:search-mailbox mb '(:text &quot;blitzfig&quot;) :uid t)
728
+user(9): (po:search-mailbox mb '(:text &quot;blitzfig&quot;) :uid t)
708 729
 (68)
709
-user(10): (mb:fetch-field 68 &quot;body[]&quot; (mb:fetch-letter mb 68 &quot;body[]&quot; :uid t) :uid t)
730
+user(10): (po:fetch-field 68 &quot;body[]&quot; (po:fetch-letter mb 68 &quot;body[]&quot; :uid t) :uid t)
710 731
 &quot;Return-Path: &lt;jkf@verada.com&gt;
711 732
 Received: from main.verada.com (main.verada.com [208.164.216.3])
712 733
 &nbsp;&nbsp;&nbsp; by tiger.franz.com (8.8.7/8.8.7) with ESMTP id NAA20541
... ...
@@ -730,9 +751,9 @@ we have only six messages in the mailbox.</strong></p>
730 751
 <div align="left">
731 752
 
732 753
 <pre>
733
-user(11): (mb:delete-letter mb 68 :uid t)
754
+user(11): (po:delete-letter mb 68 :uid t)
734 755
 (7)
735
-user(12): (mb:mailbox-message-count mb)
756
+user(12): (po:mailbox-message-count mb)
736 757
 6</pre>
737 758
 </div>
738 759
 
... ...
@@ -744,9 +765,9 @@ do we issue the noop command, which does nothing on the server.</strong></p>
744 765
 <div align="left">
745 766
 
746 767
 <pre>
747
-user(13): (mb:noop mb)
768
+user(13): (po:noop mb)
748 769
 nil
749
-user(14): (mb:mailbox-message-count mb)
770
+user(14): (po:mailbox-message-count mb)
750 771
 7</pre>
751 772
 </div>
752 773
 
... ...
@@ -757,13 +778,13 @@ to specify a sequence of messages.</strong></p>
757 778
 <div align="left">
758 779
 
759 780
 <pre>
760
-user(15): (mb:create-mailbox mb &quot;tempbox&quot;)
781
+user(15): (po:create-mailbox mb &quot;tempbox&quot;)
761 782
 t
762
-user(18): (let ((count (mb:mailbox-message-count mb)))
763
-(mb:copy-to-mailbox mb `(:seq 1 ,count) &quot;tempbox&quot;)
764
-(mb:delete-letter mb `(:seq 1 ,count)))
783
+user(18): (let ((count (po:mailbox-message-count mb)))
784
+(po:copy-to-mailbox mb `(:seq 1 ,count) &quot;tempbox&quot;)
785
+(po:delete-letter mb `(:seq 1 ,count)))
765 786
 (1 1 1 1 1 1 1)
766
-user(19): (mb:mailbox-message-count mb)
787
+user(19): (po:mailbox-message-count mb)
767 788
 0</pre>
768 789
 </div>
769 790
 
... ...
@@ -773,9 +794,9 @@ messages are there.</strong></p>
773 794
 <div align="left">
774 795
 
775 796
 <pre>
776
-user(22): (mb:select-mailbox mb &quot;tempbox&quot;)
797
+user(22): (po:select-mailbox mb &quot;tempbox&quot;)
777 798
 t
778
-user(23): (mb:mailbox-message-count mb)
799
+user(23): (po:mailbox-message-count mb)
779 800
 7</pre>
780 801
 </div>
781 802
 
... ...
@@ -788,13 +809,82 @@ the lisp side in order to free up the resources still in use for the now dead co
788 809
 <div align="left">
789 810
 
790 811
 <pre>
791
-user(24): (mb:close-imap-connection mb)
812
+user(24): (po:close-connection mb)
792 813
 t
793 814
 </pre>
794 815
 </div>
795 816
 
796 817
 <p align="left">&nbsp;</p>
797 818
 
819
+<h1>The Pop interface</h1>
820
+
821
+<p>The <strong>pop</strong> protocol is a very simple means for retreiving messages from a
822
+single mailbox.&nbsp;&nbsp;&nbsp;&nbsp; The functions in the interface are:</p>
823
+
824
+<p>&nbsp;</p>
825
+
826
+<p align="left"><font face="Courier New">(<strong>make-pop-connection host &amp;key user
827
+password port timeout)</strong></font></p>
828
+
829
+<p align="left">This creates a connection to the <strong>pop</strong> server on machine <strong>host</strong>
830
+and logs in as <strong>user </strong>with password <strong>password.&nbsp;&nbsp; </strong>The
831
+<strong>port</strong> argument defaults to 110, which is the port on which the <strong>pop</strong>
832
+server normally listens.&nbsp;&nbsp;&nbsp; The <strong>timeout</strong> argument defaults
833
+to 30 (seconds) and this value is used to limit the amount of time this pop interface code
834
+will wait for a response from the server before giving up.&nbsp;&nbsp;&nbsp; In certain
835
+circumstances the server may get so busy that you see timeout errors signaled in this
836
+code.&nbsp; In that case you should specify a larger timeout when connecting. </p>
837
+
838
+<p>The value returned by this function is a <strong>mailbox</strong> object.&nbsp; You can
839
+call <strong>mailbox-message-count</strong> on the <strong>mailbox</strong> object to
840
+determine how many letters are currently stored in the mailbox.</p>
841
+
842
+<p>&nbsp;</p>
843
+
844
+<p><font face="Courier New"><strong>(close-connection mb)</strong></font></p>
845
+
846
+<p>Disconnect from the pop server.&nbsp; All messages marked for deletion will be deleted.</p>
847
+
848
+<p>&nbsp;</p>
849
+
850
+<p><strong><font face="Courier New">(delete-letter mb messages)</font></strong></p>
851
+
852
+<p>Mark the specified <strong>messages</strong> for deletion.&nbsp; <strong>mb </strong>is
853
+the mailbox object returned by <strong>make-pop-connection</strong>.&nbsp; The messages
854
+are only&nbsp; marked for deletion.&nbsp; They are not removed until a <strong>close-connection</strong>
855
+is done.&nbsp; If the connection to the <strong>pop</strong> server is broken before a <strong>close-connection</strong>
856
+is done, the messages will <strong>not</strong> be deleted and they will no longer be
857
+marked for deletion either.</p>
858
+
859
+<p><strong>messages</strong> can either be a message number, a list of the form <strong>(:seq
860
+N M)</strong> meaning messages <strong>N </strong>through <strong>M </strong>or it can be
861
+a list of message numbers and/or <strong>:seq </strong>specifiers.&nbsp;&nbsp; The
862
+messages in a mailbox are numbered starting with one.&nbsp; Marking a message for deletion
863
+does not affect the numbering of other messages in the mailbox.</p>
864
+
865
+<p>&nbsp;</p>
866
+
867
+<p><font face="Courier New"><strong>(fetch-letter mb message)</strong></font></p>
868
+
869
+<p>Fetch from the pop server connection <strong>mb</strong> the letter numbered <strong>message</strong>.
870
+&nbsp;&nbsp; The letters in a mailbox are numbered starting with one.&nbsp; The entire
871
+message, including the headers,&nbsp; is returned as a string.&nbsp;&nbsp;&nbsp; It is an
872
+error to attempt to fetch a letter marked for deletion.</p>
873
+
874
+<p>&nbsp;</p>
875
+
876
+<p><font face="Courier New"><strong>(noop mb)</strong></font></p>
877
+
878
+<p>This is the no-operation command.&nbsp; It is useful for letting the <strong>pop</strong>
879
+server know that this connection should be kept alive (<strong>pop </strong>servers tend
880
+to disconnect after a few minutes of inactivity).&nbsp;&nbsp; In order to make <strong>noop</strong>
881
+have behavior similar to that of the <strong>imap</strong> version of <strong>noop</strong>,
882
+we don't send a 'noop' command to the pop server, instead we send a 'stat' command.
883
+&nbsp;&nbsp; This means that after this command is completed the <strong>mailbox-message-count</strong>
884
+will contain the current count of messages in the mailbox.</p>
885
+
886
+<p>&nbsp;</p>
887
+
798 888
 <p>&nbsp;</p>
799 889
 </body>
800 890
 </html>
... ...
@@ -2,10 +2,15 @@
2 2
 (load (compile-file-if-needed "../smtp/smtp"))
3 3
 
4 4
 (defun test ()
5
-  (setq *xx* (mb::make-imap-connection "tiger.franz.com"
5
+  (setq *xx* (po::make-imap-connection "tiger.franz.com"
6 6
 				   :user "jkfmail"
7 7
 				   :password "jkf.imap"
8 8
 				   ))
9
-  (mb::select-mailbox *xx* "inbox"))
9
+  (po::select-mailbox *xx* "inbox"))
10 10
 
11 11
 				   
12
+(defun testp ()
13
+  (setq *xx* (po::make-pop-connection "tiger.franz.com"
14
+				   :user "jkfmail"
15
+				   :password "jkf.imap"
16
+				   )))
... ...
@@ -19,7 +19,7 @@
19 19
 (defun test-connect ()
20 20
   ;; test connecting and disconnecting from the server
21 21
   
22
-  (let ((mb (mb:make-imap-connection *test-machine*
22
+  (let ((mb (po:make-imap-connection *test-machine*
23 23
 				     :user *test-account*
24 24
 				     :password *test-password*)))
25 25
     (unwind-protect
... ...
@@ -28,19 +28,19 @@
28 28
 	  (test-t (not (null mb)))  ; make sure we got a mailbox object
29 29
     
30 30
 	  ; check that we've stored resonable values in the mb object
31
-	  (test-equal "/" (mb:mailbox-separator mb)) 
31
+	  (test-equal "/" (po:mailbox-separator mb)) 
32 32
     
33
-	  (test-t (mb::select-mailbox mb "inbox"))
33
+	  (test-t (po::select-mailbox mb "inbox"))
34 34
     
35
-	  (test-t (> (mb:mailbox-uidvalidity mb) 0))
36
-	  (test-t (not (null (mb:mailbox-flags mb)))))
35
+	  (test-t (> (po:mailbox-uidvalidity mb) 0))
36
+	  (test-t (not (null (po:mailbox-flags mb)))))
37 37
     
38
-      (test-t (mb:close-imap-connection mb)))))
38
+      (test-t (po:close-connection mb)))))
39 39
 
40 40
 
41 41
 (defun test-sends ()
42 42
   ;; test sending and reading mail
43
-  (let ((mb (mb:make-imap-connection *test-machine*
43
+  (let ((mb (po:make-imap-connection *test-machine*
44 44
 				     :user *test-account*
45 45
 				     :password *test-password*)))
46 46
     (unwind-protect
... ...
@@ -48,18 +48,18 @@
48 48
 	  (test-t (not (null mb)))  ; make sure we got a mailbox object
49 49
 
50 50
 	  ;; go through the mailboxes and delete all letters
51
-	  (dolist (mblist (mb:mailbox-list mb :pattern "*"))
52
-	    (if* (not (member :\\noselect (mb:mailbox-list-flags mblist)))
53
-	       then (mb:select-mailbox mb (mb:mailbox-list-name mblist))
54
-		    (let ((count (mb:mailbox-message-count mb)))
51
+	  (dolist (mblist (po:mailbox-list mb :pattern "*"))
52
+	    (if* (not (member :\\noselect (po:mailbox-list-flags mblist)))
53
+	       then (po:select-mailbox mb (po:mailbox-list-name mblist))
54
+		    (let ((count (po:mailbox-message-count mb)))
55 55
 		      ; remove all old mail
56 56
 		      (if* (> count 0)
57
-			 then (mb:alter-flags mb `(:seq 1 ,count) :add-flags :\\deleted)
58
-			      (mb:expunge-mailbox mb)
59
-			      (test-eql 0 (mb:mailbox-message-count mb)))
57
+			 then (po:alter-flags mb `(:seq 1 ,count) :add-flags :\\deleted)
58
+			      (po:expunge-mailbox mb)
59
+			      (test-eql 0 (po:mailbox-message-count mb)))
60 60
 		      ; remove mailbox (except inbox)
61
-		      (if* (not (equalp "inbox" (mb:mailbox-list-name mblist)))
62
-			 then (mb:delete-mailbox mb (mb:mailbox-list-name mblist)))
61
+		      (if* (not (equalp "inbox" (po:mailbox-list-name mblist)))
62
+			 then (po:delete-mailbox mb (po:mailbox-list-name mblist)))
63 63
       
64 64
 		      )))
65 65
       
... ...
@@ -72,15 +72,15 @@
72 72
 			    (format nil "message number ~d" (1+ i))))
73 73
     
74 74
 	  ; test to see if imap figures out that the letters are there
75
-	  (mb:select-mailbox mb "inbox")
75
+	  (po:select-mailbox mb "inbox")
76 76
 
77 77
 	  ; wait a bit for the mail to be delivered
78 78
 	  (dotimes (i 5) 
79
-	    (if* (not (eql 5 (mb:mailbox-message-count mb)))
79
+	    (if* (not (eql 5 (po:mailbox-message-count mb)))
80 80
 	       then (sleep 1)
81
-		    (mb: noop mb)))
81
+		    (po: noop mb)))
82 82
 	      
83
-	  (test-eql 5 (mb:mailbox-message-count mb))
83
+	  (test-eql 5 (po:mailbox-message-count mb))
84 84
     
85 85
 	  ; test the search facility
86 86
 	  ; look for the message number we put in each message.
... ...
@@ -88,23 +88,23 @@
88 88
 	  (dotimes (i 5)
89 89
 	    (let ((mn (1+ i)))
90 90
 	      (test-equal (list mn)
91
-			  (mb:search-mailbox mb 
91
+			  (po:search-mailbox mb 
92 92
 					     `(:body ,(format nil "~d" mn))))))
93 93
 	  
94 94
 	  ; test getting data from mail message
95
-	  (let ((fetch-info (mb:fetch-letter mb 
95
+	  (let ((fetch-info (po:fetch-parts mb 
96 96
 					   1
97 97
 					   "(envelope body[1])")))
98
-	    (let ((envelope (mb:fetch-field 1 "envelope" fetch-info))
99
-		  (body (mb:fetch-field 1 "body[1]" fetch-info)))
100
-	      (test-equal "jkfmail" (mb:address-mailbox
101
-				     (car (mb:envelope-from envelope))))
102
-	      (test-nil (mb:address-mailbox
103
-			 (car (mb:envelope-to envelope))))
98
+	    (let ((envelope (po:fetch-field 1 "envelope" fetch-info))
99
+		  (body (po:fetch-field 1 "body[1]" fetch-info)))
100
+	      (test-equal "jkfmail" (po:address-mailbox
101
+				     (car (po:envelope-from envelope))))
102
+	      (test-nil (po:address-mailbox
103
+			 (car (po:envelope-to envelope))))
104 104
 	      
105 105
 	      (test-equal (format nil "message number 1~c" #\newline)
106 106
 			  body))))
107
-      (test-t (mb:close-imap-connection mb)))))
107
+      (test-t (po:close-connection mb)))))
108 108
     
109 109
     
110 110
 
... ...
@@ -113,58 +113,96 @@
113 113
   ;;
114 114
   ;; assume we have 5 messages in inbox at this time
115 115
   ;;
116
-  (let ((mb (mb:make-imap-connection *test-machine*
116
+  (let ((mb (po:make-imap-connection *test-machine*
117 117
 				     :user *test-account*
118 118
 				     :password *test-password*)))
119 119
     (unwind-protect
120 120
 	(progn
121
-	  (mb:select-mailbox mb "inbox")
121
+	  (po:select-mailbox mb "inbox")
122 122
 	  
123
-	  (let ((flags (mb:fetch-field 3 
123
+	  (let ((flags (po:fetch-field 3 
124 124
 				       "flags"
125
-				       (mb:fetch-letter 
125
+				       (po:fetch-parts 
126 126
 					mb 3 "flags"))))
127 127
 	    (test-nil flags))
128 128
 				       
129 129
 	  ;; add flags
130
-	  (let ((info (mb:alter-flags mb 3 :add-flags :\\deleted)))
130
+	  (let ((info (po:alter-flags mb 3 :add-flags :\\deleted)))
131 131
 	    (test-equal '(:\\deleted)
132
-			(mb:fetch-field 3 "flags" info)))
132
+			(po:fetch-field 3 "flags" info)))
133 133
 
134 134
 	  ; good bye message
135
-	  (test-equal '(3) (mb:expunge-mailbox mb))
135
+	  (test-equal '(3) (po:expunge-mailbox mb))
136 136
 	  
137
-	  (mb:alter-flags mb 4 :add-flags ':\\bbbb)
137
+	  (po:alter-flags mb 4 :add-flags ':\\bbbb)
138 138
 	  (test-equal '(:\\bbbb)
139
-		      (mb:fetch-field 4 "flags"
140
-				      (mb:fetch-letter mb 4
139
+		      (po:fetch-field 4 "flags"
140
+				      (po:fetch-parts mb 4
141 141
 						       "flags")))
142 142
 	  
143 143
 	  
144 144
 	  )
145
-      (test-t (mb:close-imap-connection mb)))))
145
+      (test-t (po:close-connection mb)))))
146 146
 
147 147
 (defun test-mailboxes ()
148 148
   ;; should be 4 messages now in inbox
149 149
   ;; let's create 4 mailboxes, one for each letter
150
-  (let ((mb (mb:make-imap-connection *test-machine*
150
+  (let ((mb (po:make-imap-connection *test-machine*
151 151
 				     :user *test-account*
152 152
 				     :password *test-password*)))
153 153
     (unwind-protect
154 154
 	(progn 
155
-	  (mb:select-mailbox mb "inbox")
155
+	  (po:select-mailbox mb "inbox")
156 156
 	  (dotimes (i 4)
157 157
 	    (let ((mbname (format nil "temp/mb~d" i)))
158
-	      (test-t (mb:create-mailbox mb mbname))
159
-	      (mb:copy-to-mailbox mb (1+ i) mbname)))
158
+	      (test-t (po:create-mailbox mb mbname))
159
+	      (po:copy-to-mailbox mb (1+ i) mbname)))
160 160
 	  
161 161
 	  ; now check that each new mailbox has one message
162 162
 	  (dotimes (i 4)
163 163
 	    (let ((mbname (format nil "temp/mb~d" i)))
164
-	      (mb:select-mailbox mb mbname)
165
-	      (test-eql 1 (mb:mailbox-message-count mb)))))
166
-      (test-t (mb:close-imap-connection mb)))))
164
+	      (po:select-mailbox mb mbname)
165
+	      (test-eql 1 (po:mailbox-message-count mb)))))
166
+      (test-t (po:close-connection mb)))))
167
+
168
+
169
+(defun test-pop ()
170
+  ;; test out the pop interface to the mailbox.
167 171
   
172
+  (let ((pb (po:make-pop-connection *test-machine*
173
+				    :user *test-account*
174
+				    :password *test-password*)))
175
+    ; still from before
176
+    (test-eql 4 (po:mailbox-message-count pb))
177
+    
178
+    (po:delete-letter pb '(:seq 2 3))
179
+    
180
+    
181
+    (test-eql 4 (and :second (po:mailbox-message-count pb)))
182
+    
183
+    (po:noop pb)
184
+    
185
+    (test-eql 2 (and :third (po:mailbox-message-count pb)))
186
+    
187
+    (po:fetch-letter mb 1)
188
+    (test-err (po:fetch-letter mb 2))
189
+    (test-err (po:fetch-letter mb 3))
190
+    (po:fetch-letter mb 4)
191
+    
192
+    (po:close-connection pb)
193
+    
194
+    (setq pb (po:make-pop-connection *test-machine*
195
+				    :user *test-account*
196
+				    :password *test-password*))
197
+    
198
+    (test-eql 2 (and :fourth (po:mailbox-message-count pb)))
199
+    
200
+    (po:fetch-letter pb 1) ; just make sure there's no error
201
+    
202
+    (po:close-connection pb)))
203
+
204
+	  
205
+    
168 206
 (defun test-imap ()
169 207
   (test-connect)
170 208
   
... ...
@@ -173,6 +211,9 @@
173 211
   (test-flags)
174 212
  
175 213
   (test-mailboxes)
214
+
215
+  (test-pop)
216
+  
176 217
   
177 218
   )
178 219