From 4c6d5647dbfbb49c6dea93ffa94c4a3f9fdbfa5b Mon Sep 17 00:00:00 2001 From: Lars Magne Ingebrigtsen Date: Sat, 4 Sep 2010 23:28:50 +0200 Subject: [PATCH] Use streaming pop3 retrieval. --- lisp/ChangeLog | 9 ++++ lisp/mail-source.el | 6 ++- lisp/pop3.el | 114 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 108 insertions(+), 21 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 4873a7c4c..b78320100 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,5 +1,14 @@ 2010-09-04 Lars Magne Ingebrigtsen + * mail-source.el (mail-source-fetch-pop): Use streaming pop3 + retrieval. + + * pop3.el (pop3-process-filter): Removed unused function. + (pop3-streaming-movemail, pop3-send-streaming-command) + (pop3-wait-for-messages, pop3-write-to-file) + (pop3-number-of-responses): New functions for streaming pop3 + retrieval. + * gnus-start.el (gnus-get-unread-articles): Protect against groups that come from no known methods. (gnus-make-hashtable-from-newsrc-alist): Remove duplicates from .newsrc diff --git a/lisp/mail-source.el b/lisp/mail-source.el index 08b7a5ebb..70f354ed6 100644 --- a/lisp/mail-source.el +++ b/lisp/mail-source.el @@ -825,9 +825,11 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile) (if (eq authentication 'apop) 'apop 'pass)) (pop3-stream-type stream)) (if (or debug-on-quit debug-on-error) - (save-excursion (pop3-movemail mail-source-crash-box)) + (save-excursion (pop3-streaming-movemail + mail-source-crash-box)) (condition-case err - (save-excursion (pop3-movemail mail-source-crash-box)) + (save-excursion (pop3-streaming-movemail + mail-source-crash-box)) (error ;; We nix out the password in case the error ;; was because of a wrong password being given. diff --git a/lisp/pop3.el b/lisp/pop3.el index 8b9ff6627..72c88da73 100644 --- a/lisp/pop3.el +++ b/lisp/pop3.el @@ -128,15 +128,89 @@ Shorter values mean quicker response, but are more CPU intensive.") (truncate pop3-read-timeout)) 1000)))))) -(defun pop3-movemail (&optional crashbox) - "Transfer contents of a maildrop to the specified CRASHBOX." - (or crashbox (setq crashbox (expand-file-name "~/.crashbox"))) +(defun pop3-streaming-movemail (file) + "Transfer contents of a maildrop to the specified FILE. +Use streaming commands." (let* ((process (pop3-open-server pop3-mailhost pop3-port)) - (crashbuf (get-buffer-create " *pop3-retr*")) - (n 1) - message-count - message-sizes - (pop3-password pop3-password)) + message-count message-total-size) + (pop3-logon) + (with-current-buffer (process-buffer process) + (let ((size (pop3-stat process))) + (setq message-count (car size) + message-total-size (cadr size))) + (when (plusp message-count) + (pop3-send-streaming-command + process "RETR" message-count message-total-size) + (pop3-write-to-file file) + (pop3-send-streaming-command + process "DELE" message-count nil) + (pop3-quit process))))) + +(defun pop3-send-streaming-command (process command count total-size) + (erase-buffer) + (let ((i 1)) + (while (>= (1+ count) i) + (process-send-string process (format "%s %d\r\n" command i)) + ;; Only do 100 messages at a time to avoid pipe stalls. + (when (zerop (% i 100)) + (pop3-wait-for-messages process i total-size)) + (incf i))) + (pop3-wait-for-messages process count total-size)) + +(defun pop3-wait-for-messages (process count total-size) + (while (< (pop3-number-of-responses total-size) count) + (when total-size + (message "pop3 retrieved %dKB (%d%%)" + (truncate (/ (buffer-size) 1000)) + (truncate (* (/ (* (buffer-size) 1.0) + total-size) 100)))) + (nnheader-accept-process-output process))) + +(defun pop3-write-to-file (file) + (let ((pop-buffer (current-buffer)) + (start (point-min)) + beg end + temp-buffer) + (with-temp-buffer + (setq temp-buffer (current-buffer)) + (with-current-buffer pop-buffer + (goto-char (point-min)) + (while (re-search-forward "^\\+OK" nil t) + (forward-line 1) + (setq beg (point)) + (when (re-search-forward "^\\.\r?\n" nil t) + (setq start (point)) + (forward-line -1) + (setq end (point))) + (with-current-buffer temp-buffer + (goto-char (point-max)) + (let ((hstart (point))) + (insert-buffer-substring pop-buffer beg end) + (pop3-clean-region hstart (point)) + (goto-char (point-max)) + (pop3-munge-message-separator hstart (point)) + (goto-char (point-max)))))) + (let ((coding-system-for-write 'binary)) + (goto-char (point-min)) + ;; Check whether something inserted a newline at the start and + ;; delete it. + (when (eolp) + (delete-char 1)) + (write-region (point-min) (point-max) file))))) + +(defun pop3-number-of-responses (endp) + (let ((responses 0)) + (save-excursion + (goto-char (point-min)) + (while (or (and (re-search-forward "^\\+OK " nil t) + (or (not endp) + (re-search-forward "^\\.\r?\n" nil t))) + (re-search-forward "^-ERR " nil t)) + (incf responses))) + responses)) + +(defun pop3-logon () + (let ((pop3-password pop3-password)) ;; for debugging only (if pop3-debug (switch-to-buffer (process-buffer process))) ;; query for password @@ -148,7 +222,17 @@ Shorter values mean quicker response, but are more CPU intensive.") ((equal 'pass pop3-authentication-scheme) (pop3-user process pop3-maildrop) (pop3-pass process)) - (t (error "Invalid POP3 authentication scheme"))) + (t (error "Invalid POP3 authentication scheme"))))) + +(defun pop3-movemail (&optional crashbox) + "Transfer contents of a maildrop to the specified CRASHBOX." + (or crashbox (setq crashbox (expand-file-name "~/.crashbox"))) + (let* ((process (pop3-open-server pop3-mailhost pop3-port)) + (crashbuf (get-buffer-create " *pop3-retr*")) + (n 1) + message-count + message-sizes) + (pop3-logon) (setq message-count (car (pop3-stat process))) (when (and pop3-display-message-size-flag (> message-count 0)) @@ -277,16 +361,11 @@ Returns the process associated with the connection." (setq pop3-timestamp (substring response (or (string-match "<" response) 0) (+ 1 (or (string-match ">" response) -1))))) + (set-process-query-on-exit-flag process nil) process))) ;; Support functions -(defun pop3-process-filter (process output) - (save-excursion - (set-buffer (process-buffer process)) - (goto-char (point-max)) - (insert output))) - (defun pop3-send-command (process command) (set-buffer (process-buffer process)) (goto-char (point-max)) @@ -403,10 +482,7 @@ If NOW, use that time instead." nil (goto-char (point-max)) (insert "\n")) - (narrow-to-region (point) (point-max)) - (let ((size (- (point-max) (point-min)))) - (goto-char (point-min)) - (widen) + (let ((size (- (point-max) (point)))) (forward-line -1) (insert (format "Content-Length: %s\n" size))) ))))) -- 2.25.1