comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;; Simple POP (RFC 1460) client for VM
2 ;;; Copyright (C) 1993, 1994 Kyle E. Jones
3 ;;;
4 ;;; This program is free software; you can redistribute it and/or modify
5 ;;; it under the terms of the GNU General Public License as published by
6 ;;; the Free Software Foundation; either version 1, or (at your option)
7 ;;; any later version.
8 ;;;
9 ;;; This program is distributed in the hope that it will be useful,
10 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ;;; GNU General Public License for more details.
13 ;;;
14 ;;; You should have received a copy of the GNU General Public License
15 ;;; along with this program; if not, write to the Free Software
16 ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 (provide 'vm-pop)
19
20 ;; Nothing fancy here.
21 ;; Our goal is to drag the mail from the POP maildrop to the crash box.
22 ;; just as if we were using movemail on a spool file.
23 (defun vm-pop-move-mail (source destination)
24 (let ((process nil)
25 (folder-type vm-folder-type)
26 (saved-password t)
27 (handler (and (fboundp 'find-file-name-handler)
28 (condition-case ()
29 (find-file-name-handler source 'vm-pop-move-mail)
30 (wrong-number-of-arguments
31 (find-file-name-handler source)))))
32 (popdrop (vm-safe-popdrop-string source))
33 greeting timestamp n message-count
34 host port auth user pass source-list process-buffer)
35 (unwind-protect
36 (catch 'done
37 (if handler
38 (throw 'done
39 (funcall handler 'vm-pop-move-mail source destination)))
40 ;; parse the maildrop
41 (setq source-list (vm-parse source "\\([^:]+\\):?")
42 host (nth 0 source-list)
43 port (nth 1 source-list)
44 auth (nth 2 source-list)
45 user (nth 3 source-list)
46 pass (nth 4 source-list))
47 ;; carp if parts are missing
48 (if (null host)
49 (error "No host in POP maildrop specification, \"%s\""
50 source))
51 (if (null port)
52 (error "No port in POP maildrop specification, \"%s\""
53 source))
54 (if (string-match "^[0-9]+$" port)
55 (setq port (string-to-int port)))
56 (if (null auth)
57 (error
58 "No authentication method in POP maildrop specification, \"%s\""
59 source))
60 (if (null user)
61 (error "No user in POP maildrop specification, \"%s\""
62 source))
63 (if (null pass)
64 (error "No password in POP maildrop specification, \"%s\""
65 source))
66 (if (equal pass "*")
67 (progn
68 (setq pass (car (cdr (assoc source vm-pop-passwords))))
69 (if (null pass)
70 (setq pass
71 (vm-read-password
72 (format "POP password for %s: "
73 popdrop))
74 vm-pop-passwords (cons (list source pass)
75 vm-pop-passwords)
76 saved-password t))))
77 ;; get the trace buffer
78 (setq process-buffer
79 (get-buffer-create (format "trace of POP session to %s" host)))
80 ;; clear the trace buffer of old output
81 (save-excursion
82 (set-buffer process-buffer)
83 (erase-buffer))
84 ;; open the connection to the server
85 (setq process (open-network-stream "POP" process-buffer host port))
86 (and (null process) (throw 'done nil))
87 (set-process-filter process 'vm-pop-process-filter)
88 (save-excursion
89 (set-buffer process-buffer)
90 (make-local-variable 'vm-pop-read-point)
91 (setq vm-pop-read-point (point-min)
92 vm-folder-type (or folder-type vm-default-folder-type))
93 (and (null (setq greeting (vm-pop-read-response process t)))
94 (throw 'done nil))
95 ;; authentication
96 (cond ((equal auth "pass")
97 (vm-pop-send-command process (format "USER %s" user))
98 (and (null (vm-pop-read-response process))
99 (throw 'done nil))
100 (vm-pop-send-command process (format "PASS %s" pass))
101 (if (null (vm-pop-read-response process))
102 (progn
103 (if saved-password
104 (setq vm-pop-passwords
105 (delete (list source pass)
106 vm-pop-passwords)))
107 (throw 'done nil))))
108 ((equal auth "rpop")
109 (vm-pop-send-command process (format "USER %s" user))
110 (and (null (vm-pop-read-response process))
111 (throw 'done nil))
112 (vm-pop-send-command process (format "RPOP %s" pass))
113 (and (null (vm-pop-read-response process))
114 (throw 'done nil)))
115 ((equal auth "apop")
116 (setq timestamp (vm-parse greeting "[^<]+\\(<[^>]+>\\)")
117 timestamp (car timestamp))
118 (if (null timestamp)
119 (progn
120 (goto-char (point-max))
121 (insert "<<< ooops, no timestamp found in greeting! >>>\n")
122 (throw 'done nil)))
123 (vm-pop-send-command
124 process
125 (format "APOP %s %s"
126 user
127 (vm-pop-md5 (concat timestamp pass))))
128 (and (null (vm-pop-read-response process))
129 (throw 'done nil)))
130 (t (error "Don't know how to authenticate with %s" auth)))
131 ;; find out how many messages are in the box.
132 (vm-pop-send-command process "STAT")
133 (setq message-count (vm-pop-read-stat-response process))
134 ;; forget it if the command fails
135 ;; or if there are no messages present.
136 (if (or (null message-count)
137 (< message-count 1))
138 (throw 'done nil))
139 ;; loop through the maildrop retrieving and deleting
140 ;; messages as we go.
141 (setq n 1)
142 (while (<= n message-count)
143 (vm-unsaved-message "Retrieving message %d (of %d) from %s..."
144 n message-count popdrop)
145 (vm-pop-send-command process (format "RETR %d" n))
146 (and (null (vm-pop-read-response process))
147 (throw 'done (not (equal n 1))))
148 (and (null (vm-pop-retrieve-to-crashbox process destination))
149 (throw 'done (not (equal n 1))))
150 (vm-pop-send-command process (format "DELE %d" n))
151 ;; DELE can't fail but Emacs or this code might
152 ;; blow a gasket and spew filth down the
153 ;; connection, so...
154 (and (null (vm-pop-read-response process))
155 (throw 'done (not (equal n 1))))
156 (vm-increment n))
157 t ))
158 (if process
159 (save-excursion
160 (set-buffer (process-buffer process))
161 (vm-pop-send-command process "QUIT")
162 (vm-pop-read-response process)
163 (delete-process process))))))
164
165 (defun vm-pop-process-filter (process output)
166 (save-excursion
167 (set-buffer (process-buffer process))
168 (goto-char (point-max))
169 (insert output)))
170
171 (defun vm-pop-send-command (process command)
172 (goto-char (point-max))
173 (if (= (aref command 0) ?P)
174 (insert "PASS <omitted>\r\n")
175 (insert command "\r\n"))
176 (setq vm-pop-read-point (point))
177 (process-send-string process command)
178 (process-send-string process "\r\n"))
179
180 (defun vm-pop-read-response (process &optional return-response-string)
181 (let ((case-fold-search nil)
182 match-end)
183 (goto-char vm-pop-read-point)
184 (while (not (search-forward "\r\n" nil t))
185 (accept-process-output process)
186 (goto-char vm-pop-read-point))
187 (setq match-end (point))
188 (goto-char vm-pop-read-point)
189 (if (not (looking-at "+OK"))
190 (progn (setq vm-pop-read-point match-end) nil)
191 (setq vm-pop-read-point match-end)
192 (if return-response-string
193 (buffer-substring (point) match-end)
194 t ))))
195
196 (defun vm-pop-read-stat-response (process)
197 (let ((response (vm-pop-read-response process t)))
198 (string-to-int (nth 1 (vm-parse response "\\([^ ]+\\) *")))))
199
200 (defun vm-pop-retrieve-to-crashbox (process crash)
201 (let ((start vm-pop-read-point) end)
202 (goto-char start)
203 (while (not (re-search-forward "^\\.\r\n" nil t))
204 (accept-process-output process)
205 (goto-char start))
206 (setq vm-pop-read-point (point-marker))
207 (goto-char (match-beginning 0))
208 (setq end (point-marker))
209 (vm-pop-cleanup-region start end)
210 ;; Some POP servers strip leading and trailing message
211 ;; separators, some don't. Figure out what kind we're
212 ;; talking to and do the right thing.
213 (if (eq (vm-get-folder-type nil start end) 'unknown)
214 (progn
215 (vm-munge-message-separators vm-folder-type start end)
216 (goto-char start)
217 ;; avoid the consing and stat() call for all but babyl
218 ;; files, since this will probably slow things down.
219 ;; only babyl files have the folder header, and we
220 ;; should only insert it if the crash box is empty.
221 (if (and (eq vm-folder-type 'babyl)
222 (let ((attrs (file-attributes crash)))
223 (or (null attrs) (equal 0 (nth 7 attrs)))))
224 (let ((opoint (point)))
225 (vm-convert-folder-header nil vm-folder-type)
226 ;; if start is a marker, then it was moved
227 ;; forward by the insertion. restore it.
228 (setq start opoint)
229 (goto-char start)
230 (vm-skip-past-folder-header)))
231 (insert (vm-leading-message-separator))
232 ;; this will not find the trailing message separator but
233 ;; for the Content-Length stuff counting from eob is
234 ;; the same thing in this case.
235 (vm-convert-folder-type-headers nil vm-folder-type)
236 (goto-char end)
237 (insert-before-markers (vm-trailing-message-separator))))
238 (write-region start end crash t 0)
239 (delete-region start end)
240 t ))
241
242 (defun vm-pop-cleanup-region (start end)
243 (setq end (vm-marker end))
244 (save-excursion
245 (goto-char start)
246 ;; CRLF -> LF
247 (while (and (< (point) end) (search-forward "\r\n" end t))
248 (replace-match "\n" t t))
249 (goto-char start)
250 ;; chop leading dots
251 (while (and (< (point) end) (re-search-forward "^\\." end t))
252 (replace-match "" t t)
253 (forward-char)))
254 (set-marker end nil))
255
256 (defun vm-pop-md5 (string)
257 (let ((buffer nil))
258 (unwind-protect
259 (save-excursion
260 (setq buffer (generate-new-buffer "*vm-work*"))
261 (set-buffer buffer)
262 (insert string)
263 (call-process-region (point-min) (point-max)
264 "/bin/sh" t buffer nil
265 "-c" vm-pop-md5-program)
266 ;; MD5 digest is 32 chars long
267 ;; mddriver adds a newline to make neaten output for tty
268 ;; viewing, make sure we leave it behind.
269 (vm-buffer-substring-no-properties (point-min) (+ (point-min) 32)))
270 (and buffer (kill-buffer buffer)))))