git.fiddlerwoaroof.com
jkf authored on 28/10/2003 21:52:08
Showing 2 changed files
... ...
@@ -1,3 +1,9 @@
1
+2003-10-28  Ahmon Dancy  <dancy@franz.com>
2
+imap 1.12
3
+	* imap.cl - add functions to allow letter data to be read
4
+	    as a stream of data from the server instead of just
5
+	    returned as a big string
6
+
1 7
 2003-09-18    <jkf@main.verada.com>
2 8
 imap 1.11
3 9
 	* imap.cl: fix processing of imap flags into keywords so it
... ...
@@ -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.23 2003/09/18 18:12:29 jkf Exp $
22
+;; $Id: imap.cl,v 1.24 2003/10/28 21:52:08 jkf Exp $
23 23
 
24 24
 ;; Description:
25 25
 ;;
... ...
@@ -59,6 +59,9 @@
59 59
    #:expunge-mailbox
60 60
    #:fetch-field
61 61
    #:fetch-letter
62
+   #:fetch-letter-sequence
63
+   #:end-of-letter-p
64
+   #:with-fetch-letter-sequence
62 65
    #:fetch-parts
63 66
    #:*imap-version-number*
64 67
    #:make-envelope-from-text
... ...
@@ -74,6 +77,8 @@
74 77
    #:mailbox-uidvalidity
75 78
    #:make-imap-connection
76 79
    #:make-pop-connection
80
+   #:with-imap-connection
81
+   #:with-pop-connection
77 82
    #:noop
78 83
    #:parse-mail-header
79 84
    #:top-lines	; pop only
... ...
@@ -96,7 +101,7 @@
96 101
 
97 102
 (provide :imap)
98 103
 
99
-(defparameter *imap-version-number* '(:major 1 :minor 11)) ; major.minor
104
+(defparameter *imap-version-number* '(:major 1 :minor 12)) ; major.minor
100 105
 
101 106
 ;; todo
102 107
 ;;  have the list of tags selected done on a per connection basis to
... ...
@@ -181,7 +186,11 @@
181 186
 (defclass pop-mailbox (post-office)
182 187
   ((message-count  ; how many in the mailbox
183 188
     :accessor mailbox-message-count
184
-    :initform 0)))
189
+    :initform 0)
190
+   (fetch-letter-state 
191
+    :accessor state
192
+    :initform :invalid)))
193
+    
185 194
 
186 195
 
187 196
 
... ...
@@ -517,8 +526,77 @@
517 526
   )
518 527
 
519 528
 
529
+(defmethod begin-extended-results-sequence ((mb pop-mailbox))
530
+  (setf (state mb) 1))
531
+
532
+(defmethod get-extended-results-sequence ((mb pop-mailbox) buffer &key (start 0) (end (length buffer)))
533
+  (declare (optimize (speed 3) (safety 1)))
534
+  (let ((inpos start)
535
+	(outpos start)
536
+	(sock (post-office-socket mb))
537
+	ch
538
+	stop)
539
+    (macrolet ((add-to-buffer () 
540
+		 `(progn
541
+		    (setf (schar buffer outpos) ch)
542
+		    (incf outpos))))
543
+      (while (and (< inpos end) (/= (state mb) 4))
544
+	(setf stop (read-sequence buffer sock :start inpos :end end :partial-fill t))
545
+	(while (< inpos stop)
546
+	  (setf ch (schar buffer inpos))
547
+	  (if* (eq ch #\return)
548
+	     thenret			; ignore crs
549
+	     else (ecase (state mb)
550
+		    (1 (if* (eq ch #\.)	; at beginning of line
551
+			  then (setf (state mb) 2)
552
+			elseif (eq ch #\linefeed)
553
+			  then 
554
+			       (add-to-buffer) ; state stays at 1
555
+			  else 
556
+			       (setf (state mb) 3)
557
+			       (add-to-buffer)))
558
+		    (2			; seen first dot
559
+		     (if* (eq ch #\linefeed)
560
+			then		; end of results
561
+			     (setf (state mb) 4)
562
+			     (return) 
563
+			else 
564
+			     (setf (state mb) 3)
565
+			     (add-to-buffer))) ; normal reading
566
+		    (3			; middle of line
567
+		     (if* (eq ch #\linefeed)
568
+			then (setf (state mb) 1))
569
+		     (add-to-buffer))))
570
+	  (incf inpos))
571
+	(setf inpos outpos))
572
+      outpos)))
573
+
574
+(defmacro end-of-extended-results-p (mb)
575
+  `(= (state ,mb) 4))
576
+
577
+(defmethod end-extended-results-sequence ((mb pop-mailbox))
578
+  (declare (optimize (speed 3) (safety 1)))
579
+  (let ((buffer (make-string 4096)))
580
+    (until (end-of-extended-results-p mb)
581
+      (get-extended-results-sequence mb buffer)))
582
+  (setf (state mb) :invalid-state)
583
+  t)
584
+
585
+(defmacro with-extended-results-sequence ((mailbox) &body body)
586
+  (let ((mb (gensym)))
587
+    `(let ((,mb ,mailbox))
588
+       (begin-extended-results-sequence ,mb)
589
+       (unwind-protect
590
+	   (progn
591
+	     ,@body)
592
+	 ;; cleanup
593
+	 (end-extended-results-sequence ,mb)))))
594
+
595
+
596
+  
520 597
 
521 598
 (defun send-pop-command-get-results (pop command &optional extrap)
599
+  (declare (optimize (speed 3) (safety 1)))
522 600
   ;; send the given command to the pop server
523 601
   ;; if extrap is true and if the response is +ok, then data
524 602
   ;;  will follow the command (up to and excluding the first line consisting 
... ...
@@ -548,67 +626,36 @@
548 626
 	    ;; many but not all pop servers return the size of the data
549 627
 	    ;; after the +ok, so we use that to initially size the 
550 628
 	    ;; retreival buffer.
551
-	    (let ((buf (get-line-buffer (+ (if* (fixnump (car parsed))
552
-					      then (car parsed) 
553
-					      else 2048 ; reasonable size
554
-						   )
555
-					   50)))
556
-		  (pos 0)
557
-		  ; states
558
-		  ;  1 - after lf
559
-		  ;  2 - seen dot at beginning of line
560
-		  ;  3 - seen regular char on line
561
-		  (state 1)
562
-		  (sock (post-office-socket pop)))
563
-	      (flet ((add-to-buffer (ch)
564
-		       (if* (>= pos (length buf))
565
-			  then ; grow buffer
566
-			       (if* (>= (length buf) 
567
-					(1- array-total-size-limit))
568
-				  then ; can't grow it any further
569
-				       (po-error
570
-					:response-too-large
571
-					:format-control
572
-					"response from mail server is too large to hold in a lisp array"))
573
-			       (let ((new-buf (get-line-buffer
574
-					       (* (length buf) 2))))
575
-				 (init-line-buffer new-buf buf)
576
-				 (free-line-buffer buf)
577
-				 (setq buf new-buf)))
578
-		       (setf (schar buf pos) ch)
579
-		       (incf pos)))
580
-		(loop
581
-		  (let ((ch (read-char sock nil nil)))
582
-		    (if* (null ch)
583
-		       then (po-error :unexpected
584
-				      :format-control "premature end of file from server"))
585
-		    (if* (eq ch #\return)
586
-		       thenret ; ignore crs
587
-		       else (case state
588
-			      (1 (if* (eq ch #\.)
589
-				    then (setq state 2)
590
-				  elseif (eq ch #\linefeed)
591
-				    then (add-to-buffer ch)
592
-					 ; state stays at 1
593
-				    else (add-to-buffer ch)
594
-					 (setq state 3)))
595
-			      (2 ; seen first dot
596
-			       (if* (eq ch #\linefeed)
597
-				  then ; end of message
598
-				       (return)
599
-				  else (add-to-buffer ch)
600
-				       (setq state 3)))
601
-			      (3 ; normal reading
602
-			       (add-to-buffer ch)
603
-			       (if* (eq ch #\linefeed)
604
-				  then (setq state 1))))))))
629
+	    (let* ((buf (get-line-buffer (+ (if* (fixnump (car parsed))
630
+					       then (car parsed) 
631
+					       else 2048 ; reasonable size
632
+						    )
633
+					    50)))
634
+		   (buflen (length buf))
635
+		   (pos 0))
636
+	      (with-extended-results-sequence (pop)
637
+		(until (end-of-extended-results-p pop)
638
+		  (if* (>= pos buflen)
639
+		     then    ;; grow buffer
640
+			  (if* (>= buflen (1- array-total-size-limit))
641
+			     then	; can't grow it any further
642
+				  (po-error
643
+				   :response-too-large
644
+				   :format-control
645
+				   "response from mail server is too large to hold in a lisp array"))
646
+			  (let ((new-buf (get-line-buffer (* buflen 2))))
647
+			    (init-line-buffer new-buf buf)
648
+			    (free-line-buffer buf)
649
+			    (setq buf new-buf)
650
+			    (setq buflen (length buf))))
651
+		  (setf pos (get-extended-results-sequence pop buf :start pos :end buflen))))
605 652
 	      (prog1 (subseq buf 0 pos)
606 653
 		(free-line-buffer buf)))
607 654
        else parsed)))
608 655
   
609 656
 
610
-  
611
-  
657
+
658
+
612 659
 (defun convert-flags-plist (plist)
613 660
   ;; scan the plist looking for "flags" indicators and 
614 661
   ;; turn value into a list of symbols rather than strings
... ...
@@ -650,6 +697,31 @@
650 697
 				t ; extra stuff
651 698
 				))
652 699
 
700
+(defmethod begin-fetch-letter-sequence ((mb pop-mailbox) number &key uid)
701
+  (declare (ignore uid))
702
+  (send-pop-command-get-results mb (format nil "RETR ~d" number))
703
+  (begin-extended-results-sequence mb))
704
+
705
+
706
+(defmethod fetch-letter-sequence ((mb pop-mailbox) buffer &key (start 0) (end (length buffer)))
707
+  (get-extended-results-sequence mb buffer :start start :end end))
708
+
709
+(defmethod end-fetch-letter-sequence ((mb pop-mailbox))
710
+  (end-extended-results-sequence mb))
711
+
712
+(defmethod end-of-letter-p ((mb pop-mailbox))
713
+  (end-of-extended-results-p mb))
714
+
715
+(defmacro with-fetch-letter-sequence ((mailbox &rest args) &body body)
716
+  (let ((mb (gensym)))
717
+    `(let ((,mb ,mailbox))
718
+       (begin-fetch-letter-sequence ,mb ,@args)
719
+       (unwind-protect
720
+	   (progn
721
+	     ,@body)
722
+	 ;; cleanup
723
+	 (end-fetch-letter-sequence ,mb)))))
724
+	    
653 725
 (defmethod fetch-parts ((mb imap-mailbox) number parts &key uid)
654 726
   (let (res)
655 727
     (send-command-get-results 
... ...
@@ -1891,7 +1963,7 @@
1891 1963
 (defun get-block-of-data-from-server  (mb count &key save-returns)
1892 1964
   ;; read count bytes from the server returning it in a line buffer object
1893 1965
   ;; return as a second value the number of characters saved 
1894
-  ;; (we drop #\return's so that lines are sepisarated by a #\newline
1966
+  ;; (we drop #\return's so that lines are separated by a #\newline
1895 1967
   ;; like lisp likes).
1896 1968
   ;;
1897 1969
   (let ((buff (get-line-buffer count))
... ...
@@ -1936,7 +2008,6 @@
1936 2008
     (declare (fixnum i))
1937 2009
     (setf (schar new i) (schar old i))))
1938 2010
 
1939
-
1940 2011
   
1941 2012
 
1942 2013
   ;;;;;;;
... ...
@@ -1960,5 +2031,23 @@
1960 2031
 	    year)))
1961 2032
   
1962 2033
 			  
1963
-	  
1964
-		  
2034
+
2035
+
2036
+;; utility
2037
+
2038
+(defmacro with-imap-connection ((mb &rest options) &body body)
2039
+  `(let ((,mb (make-imap-connection ,@options)))
2040
+     (unwind-protect
2041
+	 (progn
2042
+	   ,@body)
2043
+       (close-connection ,mb))))
2044
+
2045
+
2046
+(defmacro with-pop-connection ((mb &rest options) &body body)
2047
+  `(let ((,mb (make-pop-connection ,@options)))
2048
+     (unwind-protect
2049
+	 (progn
2050
+	   ,@body)
2051
+       (close-connection ,mb))))
2052
+
2053
+