1 ;;; pop3.el --- Post Office Protocol (RFC 1460) interface
3 ;; Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
4 ;; Copyright (C) 1997 Franklin Lee
6 ;; Author: Richard L. Pieri <ratinox@peorth.gweep.net>
7 ;; Author: Franklin Lee <flee@lehman.com>
8 ;; Author: Andy Piper <andy@xemacs.org>
10 ;; Maintainer: Andy Piper <andy@xemacs.org>
12 ;; Keywords: mail, pop3
15 ;; Sync'ed up with: official pop3.el version 1.3s.
16 ;; This version is a fork of the original pop3.el and the changes have
17 ;; not been merged back in to that version due to political difficulties.
19 ;; This file is part of XEmacs.
21 ;; XEmacs is free software; you can redistribute it and/or modify
22 ;; it under the terms of the GNU General Public License as published by
23 ;; the Free Software Foundation; either version 2, or (at your option)
26 ;; XEmacs is distributed in the hope that it will be useful,
27 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
28 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 ;; GNU General Public License for more details.
31 ;; You should have received a copy of the GNU General Public License
32 ;; along with XEmacs; see the file COPYING. If not, write to the
33 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
34 ;; Boston, MA 02111-1307, USA.
38 ;; Most of the standard and optional Post Office Protocol version 3
39 ;; (RFC 1460) commands are implemented.
41 ;; This program was inspired by Kyle E. Jones's vm-pop program.
43 ;; This version has been enhanced for speed, UIDL and regexp matching
44 ;; of headers by Andy Piper <andy@xemacs.org>. UIDL support has been
45 ;; mostly stolen from epop3mail. Please address problems with this
46 ;; version (2.xx) to me.
48 ;; Speed has been enhanced by trying to slurp a maildrop in one
49 ;; go. Also performance is vastly affected by how many POP3 commands
50 ;; are issued, thus I have tried to cut down on this. Note that now
51 ;; the single biggest time hog by a long way is
52 ;; accept-process-output. I don't know of a good way to fix this as
53 ;; this program is now close to the theoretical maximum as measured
54 ;; against movemail. Under cygwin (and probably windows also) I think
55 ;; socket I/O is just plain slow. YMMV.
59 ;; To use this with gnus add:
60 ;; (setq nnmail-movemail-program 'pop3-nnmail-movemail)
63 ;; You may also want to add the following
64 ;; (setq pop3-leave-mail-on-server t)
66 ;; If you (like me) use gnus to only pick up some of your mail you can
67 ;; add something like:
68 ;; (setq pop3-retr-regexp "..:.*someaddress")
69 ;; You probably want to leave mail on server in this instance as this
70 ;; will prevent matching every message in your maildrop every time you
71 ;; read mail. If you do this you may want to:
72 ;; (setq pop3-delete-retrieved-mail t)
73 ;; so that mail you have actually downloaded gets deleted from your
80 (defconst pop3-version "2.06-xemacs")
82 (defvar pop3-maildrop (or (user-login-name) (getenv "LOGNAME") (getenv "USER") nil)
84 (defvar pop3-mailhost (or (getenv "MAILHOST") nil)
89 (defvar pop3-password-required t
90 "*Non-nil if a password is required when connecting to POP server.")
92 (defvar pop3-password nil
93 "*Password to use when connecting to POP server.")
95 (defvar pop3-authentication-scheme 'pass
96 "*POP3 authentication scheme.
97 Defaults to 'pass, for the standard USER/PASS authentication. Other valid
100 (defvar pop3-timestamp nil
101 "Timestamp returned when initially connected to the POP server.
102 Used for APOP authentication.")
104 (defvar pop3-extended-response-beginning nil
105 "Start of the region containing the last pop3 extended response.
106 This does NOT include the initial response from `pop3-read-response'.")
108 (defvar pop3-extended-response-end nil
109 "End of the region containing the last pop3 extended response.")
111 (defvar pop3-leave-mail-on-server nil
112 "Non-nil if mail is to be left on the server and UIDL used for message retrieval.")
114 (defvar pop3-delete-retrieved-mail nil
115 "Non-nil if mail that has been retrieved is to be deleted from the server.
116 This is not the opposite to `pop3-leave-mail-on-server' since it is
117 possible to not download all mail, hence leaving mail on the server
118 and hence requiring the use of UIDL support, but still wanting to
119 delete the mail that has been downloaded. If
120 `pop3-leave-mail-on-server' is nil then this variable has no effect.")
122 (defvar pop3-retr-regexp nil
123 "If non-nil only retrieve messages matching this regexp.")
125 (defvar pop3-maximum-message-size nil
126 "If non-nil only download messages smaller than this.")
128 (defvar pop3-uidl-file-name "~/.uidls"
129 "File in which to store the UIDL of processed messages.")
131 (defvar pop3-cache-password nil
132 "Whether to cache the pop password or not.
133 If NIL ask for password each time mail is retrieved. Otherwise cache
136 (defvar pop3-read-point nil)
137 (defvar pop3-debug nil)
138 (defvar pop3-uidl-support 'dont-know
139 "Whether the server supports UIDL.
140 Nil means no, t means yes, not-nil-or-t means yet to be determined.")
142 (defvar pop3-utab nil
146 (defun pop3-nnmail-movemail (inbox crashbox)
147 "Function to move mail from INBOX on a pop3 server to file CRASHBOX."
149 (substring inbox (match-end (string-match "^po:" inbox)))))
150 (pop3-movemail crashbox)))
153 (defun pop3-movemail (&optional crashbox)
154 "Transfer contents of a maildrop to the specified CRASHBOX."
155 (or crashbox (setq crashbox (expand-file-name "~/.crashbox")))
156 (let* ((process (pop3-open-server pop3-mailhost pop3-port))
157 (crashbuf (get-buffer-create " *pop3-retr*"))
162 (password pop3-password)
163 (retrieved-messages '())
165 ;; for debugging only
166 (if pop3-debug (switch-to-buffer (process-buffer process)))
167 ;; query for password
168 (if (and pop3-password-required (not pop3-password))
170 (pop3-read-passwd (format "Password for %s: " pop3-maildrop))))
171 (cond ((equal 'apop pop3-authentication-scheme)
172 (pop3-apop process password pop3-maildrop))
173 ((equal 'pass pop3-authentication-scheme)
174 (pop3-user process pop3-maildrop)
175 (pop3-pass process password))
176 (t (error "Invalid POP3 authentication scheme.")))
177 ;; cache the password if required
178 (when pop3-cache-password
179 (setq pop3-password password))
180 ;; reset uidl support
181 (unless pop3-uidl-support
182 (setq pop3-uidl-support 'dont-know))
183 ;; get messages that are suitable for download
184 (message "Retrieving message list...")
185 (setq message-list (pop3-get-message-numbers process))
186 (setq message-count (length message-list))
187 (message (format "Retrieving message list...%d unread" message-count))
190 (while (<= n message-count)
191 (setq msgid (car (car message-list)))
192 (setq msglen (cdr (car message-list)))
193 (setq message-list (cdr message-list))
194 ;; only retrieve messages matching our regexp or in the uidl list
196 ;; don't download messages that are too large
197 (and pop3-maximum-message-size
198 (> msglen pop3-maximum-message-size))
199 (and (or (null pop3-leave-mail-on-server)
200 ;; we top messages that are longer than 10k
201 ;; since retrieving a large message only to
202 ;; ignore it is wasteful.
205 (not (string-match pop3-retr-regexp
206 (pop3-top process msgid)))))
207 (message (format "Ignoring message %d of %d from %s..."
208 n message-count pop3-mailhost))
209 (message (format "Retrieving message %d of %d from %s..."
210 n message-count pop3-mailhost))
211 (if (pop3-retr process msgid crashbuf)
212 (setq retrieved-messages (cons msgid retrieved-messages))
213 (message (format "Retrieving message %d of %d from %s...ignored"
214 n message-count pop3-mailhost))))
215 ;; deleted a whole bunch of stuff here that updates the crashbox
216 ;; incrementally. This is way slow and mostly uneccessary, gnus
217 ;; and others will simply slurp the whole mail buffer anyway so
220 ;; frob the crash buffer at the end
221 (set-buffer crashbuf)
222 (let ((coding-system-for-write 'binary))
223 (append-to-file (point-min) (point-max) crashbox))
224 ;; mark messages as read
225 (when pop3-leave-mail-on-server
227 ;; now delete the messages we have retrieved
228 (unless (and pop3-leave-mail-on-server (null pop3-delete-retrieved-mail))
231 (message (format "Deleting message %d of %d from %s..."
232 n message-count pop3-mailhost))
233 (pop3-dele process n)) retrieved-messages))
235 (kill-buffer crashbuf)
239 (defun pop3-open-server (mailhost port)
240 "Open TCP connection to MAILHOST.
241 Returns the process associated with the connection."
242 (let ((process-buffer
243 (get-buffer-create (format "trace of POP session to %s" mailhost)))
245 (coding-system-for-read 'binary)
246 (coding-system-for-write 'binary))
248 (set-buffer process-buffer)
251 (open-network-stream "POP" process-buffer mailhost port))
252 (setq pop3-read-point (point-min))
253 (let ((response (pop3-read-response process t)))
255 (substring response (or (string-match "<" response) 0)
256 (+ 1 (or (string-match ">" response) -1)))))
262 (defun pop3-process-filter (process output)
264 (set-buffer (process-buffer process))
265 (goto-char (point-max))
268 (defun pop3-send-command (process command)
269 (set-buffer (process-buffer process))
270 (goto-char (point-max))
271 ;; (if (= (aref command 0) ?P)
272 ;; (insert "PASS <omitted>\r\n")
273 ;; (insert command "\r\n"))
274 (setq pop3-read-point (point))
275 (goto-char (point-max))
276 (process-send-string process (concat command "\r\n"))
279 (defun pop3-read-response (process &optional return)
280 "Read the response from the server.
281 Return the response string if optional second argument is non-nil."
282 (let ((case-fold-search nil)
285 (set-buffer (process-buffer process))
286 (goto-char pop3-read-point)
287 (while (not (search-forward "\r\n" nil t))
288 (accept-process-output process)
289 (goto-char pop3-read-point))
290 (setq match-end (point))
291 (goto-char pop3-read-point)
292 (if (looking-at "-ERR")
293 (signal 'error (list (buffer-substring (point) (- match-end 2))))
294 (if (not (looking-at "+OK"))
295 (progn (setq pop3-read-point match-end) nil)
296 (setq pop3-read-point match-end)
298 (buffer-substring (point) match-end)
302 (defun pop3-string-to-list (string &optional regexp)
303 "Chop up a string into a list."
305 (regexp (or regexp " "))
306 (string (if (string-match "\r" string)
307 (substring string 0 (match-beginning 0))
309 (store-match-data nil)
311 (if (string-match regexp string)
312 (setq list (cons (substring string 0 (- (match-end 0) 1)) list)
313 string (substring string (match-end 0)))
314 (setq list (cons string list)
318 (defvar pop3-read-passwd nil)
319 (defun pop3-read-passwd (prompt)
320 (if (not pop3-read-passwd)
321 (if (load "passwd" t)
322 (setq pop3-read-passwd 'read-passwd)
323 (autoload 'ange-ftp-read-passwd "ange-ftp")
324 (setq pop3-read-passwd 'ange-ftp-read-passwd)))
325 (funcall pop3-read-passwd prompt))
327 (defun pop3-clean-region (start end)
328 "Convert CRLF line endings to LF line endings.
329 Also remove '.' from the beginning of lines.
330 Also escape 'From ' after '\n\n' with '>' (for mbox)."
331 (setq end (set-marker (make-marker) end))
334 (while (and (< (point) end) (search-forward "\r\n" end t))
335 (replace-match "\n" t t))
337 (while (re-search-forward "\n\n\\(From \\)" end t)
338 (replace-match "\n\n>\\1" t nil))
340 (while (and (< (point) end) (re-search-forward "^\\." end t))
341 (replace-match "" t t)
343 (set-marker end nil))
345 (defun pop3-munge-message-separator (start end)
346 "Check to see if a message separator exists. If not, generate one."
347 (if (not (fboundp 'message-make-date)) (autoload 'message-make-date "message"))
350 (narrow-to-region start end)
351 (goto-char (point-min))
352 (if (not (or (looking-at "From .?") ; Unix mail
353 (looking-at "\001\001\001\001\n") ; MMDF
354 (looking-at "BABYL OPTIONS:") ; Babyl
356 (let ((from (mail-strip-quoted-names (mail-fetch-field "From")))
357 (date (pop3-string-to-list (or (mail-fetch-field "Date")
358 (message-make-date))))
360 ;; sample date formats I have seen
361 ;; Date: Tue, 9 Jul 1996 09:04:21 -0400 (EDT)
362 ;; Date: 08 Jul 1996 23:22:24 -0400
364 ;; Tue Jul 9 09:04:21 1996
366 (cond ((string-match "[A-Z]" (nth 0 date))
367 (format "%s %s %s %s %s"
368 (nth 0 date) (nth 2 date) (nth 1 date)
369 (nth 4 date) (nth 3 date)))
371 ;; this really needs to be better but I don't feel
372 ;; like writing a date to day converter.
373 (format "Sun %s %s %s %s"
374 (nth 1 date) (nth 0 date)
375 (nth 3 date) (nth 2 date)))
377 (setq From_ (format "\nFrom %s %s\n" from date))
378 (while (string-match "," From_)
379 (setq From_ (concat (substring From_ 0 (match-beginning 0))
380 (substring From_ (match-end 0)))))
381 (goto-char (point-min))
383 (re-search-forward "\n\n")
384 (narrow-to-region (point) (point-max))
385 (let ((size (- (point-max) (point-min))))
386 (goto-char (point-min))
389 (insert (format "Content-Length: %s\n" size)))
392 ;; UIDL support, mostly copied from epop3
394 (defun pop3-get-message-numbers (process)
395 "Get the list of message numbers and lengths to retrieve via PROCESS."
396 ;; we use the LIST comand first anyway to get the message lengths.
397 ;; then if we're leaving mail on the server, see if the UIDL command
398 ;; is implemented. if so, we use it to get the message number list.
399 (let ((msg-list (pop3-list process))
400 (uidl (if pop3-leave-mail-on-server
401 (pop3-get-uidl process)))
404 (setq msg-list (cdr msg-list))
405 (if (eq pop3-uidl-support t)
406 ;; remove elements not in the uidl, this assumes the uidl is short
407 (mapcar '(lambda (elt)
408 (when (memq (car elt) uidl)
409 (push elt tmplist))) (reverse msg-list))
410 (setq tmplist msg-list))
413 (defun pop3-get-list (process)
414 "Use PROCESS to get a list of message numbers."
415 (let ((pairs (pop3-list process))
418 (mapcar '(lambda (elt)
419 (push (car elt) tmplist))
420 (cdr pairs)) tmplist)))
422 (defun pop3-get-uidl (process)
423 "Use PROCESS to get a list of unread message numbers."
424 (let ((pairs (pop3-uidl process)))
425 (if (or (null pairs) (null pop3-uidl-support))
426 (setq pop3-uidl-support nil)
427 (setq pop3-uidl-support t)
428 (pop3-init-uidl-tables)
429 (mapcar 'pop3-update-tables (cdr pairs))
430 (pop3-get-unread-message-numbers))))
432 (defun pop3-init-uidl-tables ()
433 "Create the hash table for uidl processing.
434 This is only sensible to do when 'pop3-leave-mail-on-server' is non-nil."
438 (when (file-readable-p pop3-uidl-file-name)
439 (insert-file-contents pop3-uidl-file-name))
440 (setq pop3-utab (make-hash-table :test 'equal))
441 (goto-char (point-min))
442 (while (looking-at "\\([^ \n\t]+\\)")
443 (setq uid (buffer-substring (match-beginning 1) (match-end 1)))
444 (puthash uid (cons nil t) pop3-utab)
445 (forward-line 1))))))
447 (defun pop3-get-unread-message-numbers ()
448 "Return a sorted list of unread msg numbers to retrieve."
449 (let ((pop3-tmplist '()))
450 (maphash '(lambda (key val)
452 (push (car val) pop3-tmplist))) pop3-utab)
453 (sort pop3-tmplist '<)))
455 (defun pop3-update-tables (pair)
456 "Update uidl hash-tables given a PAIR list (msgno uid)."
457 (let ((msgno (car pair))
459 (if (null (gethash uid pop3-utab))
460 (puthash uid (cons msgno nil) pop3-utab)
461 (puthash uid (cons msgno t) pop3-utab))))
463 ; (if (null (gethash msgno pop3-utab))
464 ; (puthash uid uid pop3-utab))))
466 (defun pop3-save-uidls ()
467 "Save the updated UIDLs to disk for use next time."
469 ;; write the uidl, msgid to the local uidl file EXCEPT the ones which
470 ;; don't have msgnos, since they've been deleted from the server
473 (when (and pop3-leave-mail-on-server
475 (hash-table-count pop3-utab))
478 ;; back this up because we'll write to it later.
479 (when (file-readable-p pop3-uidl-file-name)
480 (copy-file pop3-uidl-file-name
481 (concat pop3-uidl-file-name ".old")
483 (maphash '(lambda (key val)
485 (insert (format "%s\n" key))))
487 (write-file pop3-uidl-file-name)))))
491 ;; AUTHORIZATION STATE
493 (defun pop3-user (process user)
494 "Send USER information to POP3 server."
495 (pop3-send-command process (format "USER %s" user))
496 (let ((response (pop3-read-response process t)))
497 (if (not (and response (string-match "+OK" response)))
498 (error (format "USER %s not valid." user)))))
500 (defun pop3-pass (process password)
501 "Send authentication information to the server."
502 (pop3-send-command process (format "PASS %s" password))
503 (let ((response (pop3-read-response process t)))
504 (if (not (and response (string-match "+OK" response)))
505 (pop3-quit process))))
507 (defun pop3-apop (process password user)
508 "Send alternate authentication information to the server."
509 (if (not (fboundp 'md5)) (autoload 'md5 "md5"))
510 (let ((hash (md5 (concat pop3-timestamp password))))
511 (pop3-send-command process (format "APOP %s %s" user hash))
512 (let ((response (pop3-read-response process t)))
513 (if (not (and response (string-match "+OK" response)))
514 (pop3-quit process)))))
518 (defun pop3-stat (process)
519 "Return the number of messages in the maildrop and the maildrop's size."
520 (pop3-send-command process "STAT")
521 (let ((response (pop3-read-response process t)))
522 (list (string-to-int (nth 1 (pop3-string-to-list response)))
523 (string-to-int (nth 2 (pop3-string-to-list response))))
526 (defun pop3-retr (process msg crashbuf)
527 "Retrieve message-id MSG to buffer CRASHBUF."
528 (pop3-send-command process (format "RETR %s" msg))
529 (pop3-read-response process)
530 ; (accept-process-output process)
533 (pop3-get-extended-response process)
534 (pop3-munge-message-separator pop3-extended-response-beginning
535 pop3-extended-response-end)
536 ;; only get message if it matches
537 (if (or (null pop3-leave-mail-on-server)
538 (null pop3-retr-regexp)
539 (and (goto-char pop3-extended-response-beginning)
540 (re-search-forward pop3-retr-regexp pop3-extended-response-end t)))
541 (append-to-buffer crashbuf pop3-extended-response-beginning
542 pop3-extended-response-end)
544 (setq retrieved nil))
545 (delete-region pop3-extended-response-beginning
546 pop3-extended-response-end)
549 (defun pop3-dele (process msg)
550 "Mark message-id MSG as deleted."
551 (pop3-send-command process (format "DELE %s" msg))
552 (pop3-read-response process))
554 (defun pop3-noop (process msg)
556 (pop3-send-command process "NOOP")
557 (pop3-read-response process))
559 (defun pop3-last (process)
560 "Return highest accessed message-id number for the session."
561 (pop3-send-command process "LAST")
562 (let ((response (pop3-read-response process t)))
563 (string-to-int (nth 1 (pop3-string-to-list response)))
566 (defun pop3-rset (process)
567 "Remove all delete marks from current maildrop."
568 (pop3-send-command process "RSET")
569 (pop3-read-response process))
573 (defun pop3-quit (process)
574 "Close connection to POP3 server.
575 Tell server to remove all messages marked as deleted, unlock the maildrop,
576 and close the connection."
577 (pop3-send-command process "QUIT")
578 (pop3-read-response process t)
581 (set-buffer (process-buffer process))
582 (goto-char (point-max))
583 (delete-process process))))
585 (defun pop3-uidl (process &optional msgno)
586 "Return the results of a UIDL command in PROCESS for optional MSGNO.
587 If UIDL is unsupported on this mail server or if msgno is invalid, return nil.
588 Otherwise, return a list in the form
590 (N (1 UIDL-1) (2 UIDL-2) ... (N UIDL-N))
594 N is an integer for the number of UIDLs returned (could be 0)
598 (pop3-send-command process (format "UIDL %d" msgno))
599 (pop3-send-command process "UIDL"))
601 (let ((uidl-not-supported nil))
603 (pop3-read-response process t)
604 (error (setq uidl-not-supported t)))
606 (unless uidl-not-supported
611 (pop3-get-extended-response process)
612 (goto-char pop3-extended-response-beginning)
614 (while (looking-at "\\([^ \n\t]*\\) \\([^ \n\t]*\\)")
615 (setq msgno (string-to-int
616 (buffer-substring (match-beginning 1) (match-end 1))))
617 (setq uidl (buffer-substring (match-beginning 2) (match-end 2)))
618 (push (cons msgno uidl) retlist)
619 (beginning-of-line 2))
620 (cons (length retlist) (nreverse retlist)))))))
622 (defun pop3-list (process &optional msgno)
623 "Return the results of a LIST command for PROCESS and optional MSGNO.
624 If (optional) msgno is invalid, return nil. Otherwise, return a list
627 (N (1 LEN-1) (2 LEN-2) ... (N LEN-N))
631 N is an integer for the number of msg/len pairs (could be 0)
632 LEN-n is an integer."
633 (let ((bad-msgno nil))
636 (pop3-send-command process (format "LIST %d" msgno))
637 (pop3-send-command process "LIST"))
640 (pop3-read-response process t)
641 (error (setq bad-msgno t)))
648 (pop3-get-extended-response process)
649 (goto-char pop3-extended-response-beginning)
651 (while (looking-at "\\([^ \n\t]*\\) \\([^ \n\t]*\\)")
652 (setq msgno (string-to-int
653 (buffer-substring (match-beginning 1) (match-end 1))))
654 (setq len (string-to-int
655 (buffer-substring (match-beginning 2) (match-end 2))))
656 (push (cons msgno len) retlist)
657 (beginning-of-line 2))
658 (cons (length retlist) (nreverse retlist)))))))
660 (defun pop3-top (process msgno &optional lines)
661 "Return the top LINES of messages for PROCESS and MSGNO.
662 If msgno is invalid, return nil. Otherwise, return a string."
663 (let ((bad-msgno nil))
664 (pop3-send-command process (format "TOP %d %d" msgno (or lines 1)))
667 (pop3-read-response process t)
668 (error (setq bad-msgno t)))
672 (pop3-get-extended-response process)
673 (buffer-substring pop3-extended-response-beginning
674 pop3-extended-response-end)))))
678 (defun pop3-get-extended-response (process)
679 "Get the extended pop3 response in the PROCESS buffer."
680 (let ((start pop3-read-point) end)
681 (set-buffer (process-buffer process))
682 (while (not (re-search-forward "^\\.\r\n" nil t))
683 (accept-process-output process)
685 (setq pop3-extended-response-beginning start)
686 (setq pop3-read-point (point-marker))
687 (goto-char (match-beginning 0))
688 (setq end (point-marker)
689 pop3-extended-response-end (point-marker))
690 (pop3-clean-region start end)))
693 ;; Summary of POP3 (Post Office Protocol version 3) commands and responses
695 ;;; AUTHORIZATION STATE
697 ;; Initial TCP connection
699 ;; Restrictions: none
700 ;; Possible responses:
701 ;; +OK [POP3 server ready]
704 ;; Arguments: a server specific user-id (required)
705 ;; Restrictions: authorization state [after unsuccessful USER or PASS
706 ;; Possible responses:
707 ;; +OK [valid user-id]
708 ;; -ERR [invalid user-id]
711 ;; Arguments: a server/user-id specific password (required)
712 ;; Restrictions: authorization state, after successful USER
713 ;; Possible responses:
714 ;; +OK [maildrop locked and ready]
715 ;; -ERR [invalid password]
716 ;; -ERR [unable to lock maildrop]
718 ;;; TRANSACTION STATE
722 ;; Restrictions: transaction state
723 ;; Possible responses:
724 ;; +OK nn mm [# of messages, size of maildrop]
727 ;; Arguments: a message-id (optional)
728 ;; Restrictions: transaction state; msg must not be deleted
729 ;; Possible responses:
730 ;; +OK [scan listing follows]
731 ;; -ERR [no such message]
734 ;; Arguments: a message-id (required), number of lines (optional)
735 ;; Restrictions: transaction state; msg must not be deleted
736 ;; Possible responses:
737 ;; +OK [partial message listing follows]
738 ;; -ERR [no such message]
741 ;; Arguments: a message-id (optional)
742 ;; Restrictions: transaction state; msg must not be deleted
743 ;; Possible responses:
744 ;; +OK [uidl listing follows]
745 ;; -ERR [no such message]
748 ;; Arguments: a message-id (required)
749 ;; Restrictions: transaction state; msg must not be deleted
750 ;; Possible responses:
751 ;; +OK [message contents follow]
752 ;; -ERR [no such message]
755 ;; Arguments: a message-id (required)
756 ;; Restrictions: transaction state; msg must not be deleted
757 ;; Possible responses:
758 ;; +OK [message deleted]
759 ;; -ERR [no such message]
763 ;; Restrictions: transaction state
764 ;; Possible responses:
769 ;; Restrictions: transaction state
770 ;; Possible responses:
771 ;; +OK nn [highest numbered message accessed]
775 ;; Restrictions: transaction state
776 ;; Possible responses:
777 ;; +OK [all delete marks removed]
783 ;; Restrictions: none
784 ;; Possible responses:
785 ;; +OK [TCP connection closed]