Mercurial > hg > xemacs-beta
diff lisp/vm/vm-pop.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 859a2309aef8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/vm/vm-pop.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,270 @@ +;;; Simple POP (RFC 1460) client for VM +;;; Copyright (C) 1993, 1994 Kyle E. Jones +;;; +;;; This program is free software; you can redistribute it and/or modify +;;; it under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 1, or (at your option) +;;; any later version. +;;; +;;; This program is distributed in the hope that it will be useful, +;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with this program; if not, write to the Free Software +;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +(provide 'vm-pop) + +;; Nothing fancy here. +;; Our goal is to drag the mail from the POP maildrop to the crash box. +;; just as if we were using movemail on a spool file. +(defun vm-pop-move-mail (source destination) + (let ((process nil) + (folder-type vm-folder-type) + (saved-password t) + (handler (and (fboundp 'find-file-name-handler) + (condition-case () + (find-file-name-handler source 'vm-pop-move-mail) + (wrong-number-of-arguments + (find-file-name-handler source))))) + (popdrop (vm-safe-popdrop-string source)) + greeting timestamp n message-count + host port auth user pass source-list process-buffer) + (unwind-protect + (catch 'done + (if handler + (throw 'done + (funcall handler 'vm-pop-move-mail source destination))) + ;; parse the maildrop + (setq source-list (vm-parse source "\\([^:]+\\):?") + host (nth 0 source-list) + port (nth 1 source-list) + auth (nth 2 source-list) + user (nth 3 source-list) + pass (nth 4 source-list)) + ;; carp if parts are missing + (if (null host) + (error "No host in POP maildrop specification, \"%s\"" + source)) + (if (null port) + (error "No port in POP maildrop specification, \"%s\"" + source)) + (if (string-match "^[0-9]+$" port) + (setq port (string-to-int port))) + (if (null auth) + (error + "No authentication method in POP maildrop specification, \"%s\"" + source)) + (if (null user) + (error "No user in POP maildrop specification, \"%s\"" + source)) + (if (null pass) + (error "No password in POP maildrop specification, \"%s\"" + source)) + (if (equal pass "*") + (progn + (setq pass (car (cdr (assoc source vm-pop-passwords)))) + (if (null pass) + (setq pass + (vm-read-password + (format "POP password for %s: " + popdrop)) + vm-pop-passwords (cons (list source pass) + vm-pop-passwords) + saved-password t)))) + ;; get the trace buffer + (setq process-buffer + (get-buffer-create (format "trace of POP session to %s" host))) + ;; clear the trace buffer of old output + (save-excursion + (set-buffer process-buffer) + (erase-buffer)) + ;; open the connection to the server + (setq process (open-network-stream "POP" process-buffer host port)) + (and (null process) (throw 'done nil)) + (set-process-filter process 'vm-pop-process-filter) + (save-excursion + (set-buffer process-buffer) + (make-local-variable 'vm-pop-read-point) + (setq vm-pop-read-point (point-min) + vm-folder-type (or folder-type vm-default-folder-type)) + (and (null (setq greeting (vm-pop-read-response process t))) + (throw 'done nil)) + ;; authentication + (cond ((equal auth "pass") + (vm-pop-send-command process (format "USER %s" user)) + (and (null (vm-pop-read-response process)) + (throw 'done nil)) + (vm-pop-send-command process (format "PASS %s" pass)) + (if (null (vm-pop-read-response process)) + (progn + (if saved-password + (setq vm-pop-passwords + (delete (list source pass) + vm-pop-passwords))) + (throw 'done nil)))) + ((equal auth "rpop") + (vm-pop-send-command process (format "USER %s" user)) + (and (null (vm-pop-read-response process)) + (throw 'done nil)) + (vm-pop-send-command process (format "RPOP %s" pass)) + (and (null (vm-pop-read-response process)) + (throw 'done nil))) + ((equal auth "apop") + (setq timestamp (vm-parse greeting "[^<]+\\(<[^>]+>\\)") + timestamp (car timestamp)) + (if (null timestamp) + (progn + (goto-char (point-max)) + (insert "<<< ooops, no timestamp found in greeting! >>>\n") + (throw 'done nil))) + (vm-pop-send-command + process + (format "APOP %s %s" + user + (vm-pop-md5 (concat timestamp pass)))) + (and (null (vm-pop-read-response process)) + (throw 'done nil))) + (t (error "Don't know how to authenticate with %s" auth))) + ;; find out how many messages are in the box. + (vm-pop-send-command process "STAT") + (setq message-count (vm-pop-read-stat-response process)) + ;; forget it if the command fails + ;; or if there are no messages present. + (if (or (null message-count) + (< message-count 1)) + (throw 'done nil)) + ;; loop through the maildrop retrieving and deleting + ;; messages as we go. + (setq n 1) + (while (<= n message-count) + (vm-unsaved-message "Retrieving message %d (of %d) from %s..." + n message-count popdrop) + (vm-pop-send-command process (format "RETR %d" n)) + (and (null (vm-pop-read-response process)) + (throw 'done (not (equal n 1)))) + (and (null (vm-pop-retrieve-to-crashbox process destination)) + (throw 'done (not (equal n 1)))) + (vm-pop-send-command process (format "DELE %d" n)) + ;; DELE can't fail but Emacs or this code might + ;; blow a gasket and spew filth down the + ;; connection, so... + (and (null (vm-pop-read-response process)) + (throw 'done (not (equal n 1)))) + (vm-increment n)) + t )) + (if process + (save-excursion + (set-buffer (process-buffer process)) + (vm-pop-send-command process "QUIT") + (vm-pop-read-response process) + (delete-process process)))))) + +(defun vm-pop-process-filter (process output) + (save-excursion + (set-buffer (process-buffer process)) + (goto-char (point-max)) + (insert output))) + +(defun vm-pop-send-command (process command) + (goto-char (point-max)) + (if (= (aref command 0) ?P) + (insert "PASS <omitted>\r\n") + (insert command "\r\n")) + (setq vm-pop-read-point (point)) + (process-send-string process command) + (process-send-string process "\r\n")) + +(defun vm-pop-read-response (process &optional return-response-string) + (let ((case-fold-search nil) + match-end) + (goto-char vm-pop-read-point) + (while (not (search-forward "\r\n" nil t)) + (accept-process-output process) + (goto-char vm-pop-read-point)) + (setq match-end (point)) + (goto-char vm-pop-read-point) + (if (not (looking-at "+OK")) + (progn (setq vm-pop-read-point match-end) nil) + (setq vm-pop-read-point match-end) + (if return-response-string + (buffer-substring (point) match-end) + t )))) + +(defun vm-pop-read-stat-response (process) + (let ((response (vm-pop-read-response process t))) + (string-to-int (nth 1 (vm-parse response "\\([^ ]+\\) *"))))) + +(defun vm-pop-retrieve-to-crashbox (process crash) + (let ((start vm-pop-read-point) end) + (goto-char start) + (while (not (re-search-forward "^\\.\r\n" nil t)) + (accept-process-output process) + (goto-char start)) + (setq vm-pop-read-point (point-marker)) + (goto-char (match-beginning 0)) + (setq end (point-marker)) + (vm-pop-cleanup-region start end) + ;; Some POP servers strip leading and trailing message + ;; separators, some don't. Figure out what kind we're + ;; talking to and do the right thing. + (if (eq (vm-get-folder-type nil start end) 'unknown) + (progn + (vm-munge-message-separators vm-folder-type start end) + (goto-char start) + ;; avoid the consing and stat() call for all but babyl + ;; files, since this will probably slow things down. + ;; only babyl files have the folder header, and we + ;; should only insert it if the crash box is empty. + (if (and (eq vm-folder-type 'babyl) + (let ((attrs (file-attributes crash))) + (or (null attrs) (equal 0 (nth 7 attrs))))) + (let ((opoint (point))) + (vm-convert-folder-header nil vm-folder-type) + ;; if start is a marker, then it was moved + ;; forward by the insertion. restore it. + (setq start opoint) + (goto-char start) + (vm-skip-past-folder-header))) + (insert (vm-leading-message-separator)) + ;; this will not find the trailing message separator but + ;; for the Content-Length stuff counting from eob is + ;; the same thing in this case. + (vm-convert-folder-type-headers nil vm-folder-type) + (goto-char end) + (insert-before-markers (vm-trailing-message-separator)))) + (write-region start end crash t 0) + (delete-region start end) + t )) + +(defun vm-pop-cleanup-region (start end) + (setq end (vm-marker end)) + (save-excursion + (goto-char start) + ;; CRLF -> LF + (while (and (< (point) end) (search-forward "\r\n" end t)) + (replace-match "\n" t t)) + (goto-char start) + ;; chop leading dots + (while (and (< (point) end) (re-search-forward "^\\." end t)) + (replace-match "" t t) + (forward-char))) + (set-marker end nil)) + +(defun vm-pop-md5 (string) + (let ((buffer nil)) + (unwind-protect + (save-excursion + (setq buffer (generate-new-buffer "*vm-work*")) + (set-buffer buffer) + (insert string) + (call-process-region (point-min) (point-max) + "/bin/sh" t buffer nil + "-c" vm-pop-md5-program) + ;; MD5 digest is 32 chars long + ;; mddriver adds a newline to make neaten output for tty + ;; viewing, make sure we leave it behind. + (vm-buffer-substring-no-properties (point-min) (+ (point-min) 32))) + (and buffer (kill-buffer buffer)))))