* elmo.el: Moved obsolete variable definitions from
[elisp/wanderlust.git] / elmo / elmo-pop3.el
1 ;;; elmo-pop3.el -- POP3 Interface for ELMO.
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4 ;; Copyright (C) 1999,2000      Kenichi OKADA <okada@opaopa.org>
5
6 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
7 ;;      Kenichi OKADA <okada@opaopa.org>
8 ;; Keywords: mail, net news
9
10 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
11
12 ;; This program is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
16 ;;
17 ;; This program is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ;; GNU General Public License for more details.
21 ;;
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
26 ;;
27
28 ;;; Commentary:
29 ;; 
30
31 ;;; Code:
32 ;; 
33
34 (require 'elmo-msgdb)
35 (require 'elmo-net)
36
37 (eval-when-compile
38   (require 'elmo-util))
39
40 (eval-and-compile
41   (autoload 'md5 "md5"))
42
43 ;; POP3
44 (defcustom elmo-pop3-default-user (or (getenv "USER")
45                                       (getenv "LOGNAME")
46                                       (user-login-name))
47   "*Default username for POP3."
48   :type 'string
49   :group 'elmo)
50
51 (defcustom elmo-pop3-default-server  "localhost"
52   "*Default POP3 server."
53   :type 'string
54   :group 'elmo)
55
56 (defcustom elmo-pop3-default-authenticate-type 'user
57   "*Default Authentication type for POP3."
58   :type 'symbol
59   :group 'elmo)
60
61 (defcustom elmo-pop3-default-port 110
62   "*Default POP3 port."
63   :type 'integer
64   :group 'elmo)
65
66 (defcustom elmo-pop3-default-stream-type nil
67   "*Default stream type for POP3.
68 Any symbol value of `elmo-network-stream-type-alist' or
69 `elmo-pop3-stream-type-alist'."
70   :type 'symbol
71   :group 'elmo)
72
73 (defcustom elmo-pop3-default-use-uidl t
74   "If non-nil, use UIDL on POP3."
75   :type 'boolean
76   :group 'elmo)
77
78 (defvar elmo-pop3-stream-type-alist nil
79   "*Stream bindings for POP3.
80 This is taken precedence over `elmo-network-stream-type-alist'.")
81
82 (defvar elmo-pop3-use-uidl-internal t
83   "(Internal switch for using UIDL on POP3).")
84
85 (defvar elmo-pop3-use-cache t
86   "Use cache in pop3 folder.")
87
88 (defvar elmo-pop3-send-command-synchronously nil
89   "If non-nil, commands are send synchronously.
90 If server doesn't accept asynchronous commands, this variable should be
91 set as non-nil.")
92
93 (defvar elmo-pop3-exists-exactly t)
94 (defvar sasl-mechanism-alist)
95
96 ;;; ELMO POP3 folder
97 (eval-and-compile
98   (luna-define-class elmo-pop3-folder (elmo-net-folder)
99                      (use-uidl location-alist))
100   (luna-define-internal-accessors 'elmo-pop3-folder))
101
102 (luna-define-method elmo-folder-initialize :around ((folder
103                                                      elmo-pop3-folder)
104                                                     name)
105   (let ((elmo-network-stream-type-alist
106          (if elmo-pop3-stream-type-alist
107              (append elmo-pop3-stream-type-alist
108                      elmo-network-stream-type-alist)
109            elmo-network-stream-type-alist)))
110     (setq name (luna-call-next-method))
111     ;; Setup slots for elmo-net-folder
112     (when (string-match "^\\([^:/!]*\\)\\(/[^/:@!]+\\)?\\(:[^/:@!]+\\)?" name)
113       (elmo-net-folder-set-user-internal folder
114                                          (if (match-beginning 1)
115                                              (elmo-match-string 1 name)))
116       (if (eq (length (elmo-net-folder-user-internal folder)) 0)
117           (elmo-net-folder-set-user-internal folder
118                                              elmo-pop3-default-user))
119       (elmo-net-folder-set-auth-internal
120        folder
121        (if (match-beginning 2)
122            (intern (elmo-match-substring 2 name 1))
123          elmo-pop3-default-authenticate-type))
124       (elmo-pop3-folder-set-use-uidl-internal
125        folder
126        (if (match-beginning 3)
127            (string= (elmo-match-substring 3 name 1) "uidl")
128          elmo-pop3-default-use-uidl)))
129     (unless (elmo-net-folder-server-internal folder)
130       (elmo-net-folder-set-server-internal folder 
131                                            elmo-pop3-default-server))
132     (unless (elmo-net-folder-port-internal folder)
133       (elmo-net-folder-set-port-internal folder
134                                          elmo-pop3-default-port))
135     (unless (elmo-net-folder-stream-type-internal folder)
136       (elmo-net-folder-set-stream-type-internal
137        folder
138        elmo-pop3-default-stream-type))
139     folder))
140
141 ;;; POP3 session
142 (luna-define-class elmo-pop3-session (elmo-network-session) ())
143
144 ;; buffer-local
145 (defvar elmo-pop3-read-point nil)
146 (defvar elmo-pop3-number-uidl-hash nil) ; number -> uidl
147 (defvar elmo-pop3-uidl-number-hash nil) ; uidl -> number
148 (defvar elmo-pop3-size-hash nil) ; number -> size
149 (defvar elmo-pop3-uidl-done nil)
150 (defvar elmo-pop3-list-done nil)
151
152 (defvar elmo-pop3-local-variables '(elmo-pop3-read-point
153                                     elmo-pop3-uidl-number-hash
154                                     elmo-pop3-number-uidl-hash
155                                     elmo-pop3-uidl-done
156                                     elmo-pop3-size-hash
157                                     elmo-pop3-list-done))
158
159 (luna-define-method elmo-network-close-session ((session elmo-pop3-session))
160   (when (elmo-network-session-process-internal session)
161     (when (memq (process-status
162                  (elmo-network-session-process-internal session))
163                 '(open run))
164       (let ((buffer (process-buffer 
165                      (elmo-network-session-process-internal session))))
166         (elmo-pop3-send-command (elmo-network-session-process-internal session)
167                                 "quit")
168         ;; process is dead.
169         (or (elmo-pop3-read-response
170              (elmo-network-session-process-internal session)
171              t buffer)
172             (error "POP error: QUIT failed"))))
173     (kill-buffer (process-buffer
174                   (elmo-network-session-process-internal session)))
175     (delete-process (elmo-network-session-process-internal session))))
176
177 (defun elmo-pop3-get-session (folder &optional if-exists)
178   (let ((elmo-pop3-use-uidl-internal (if elmo-inhibit-number-mapping
179                                          nil
180                                        (elmo-pop3-folder-use-uidl-internal
181                                         folder))))
182     (elmo-network-get-session 'elmo-pop3-session "POP3" folder if-exists)))
183
184 (defun elmo-pop3-send-command (process command &optional no-erase)
185   (with-current-buffer (process-buffer process)
186     (unless no-erase
187       (erase-buffer))
188     (goto-char (point-min))
189     (setq elmo-pop3-read-point (point))
190     (process-send-string process command)
191     (process-send-string process "\r\n")))
192
193 (defun elmo-pop3-read-response (process &optional not-command buffer)
194   ;; buffer is in case for process is dead.
195   (with-current-buffer (or buffer (process-buffer process))
196     (let ((case-fold-search nil)
197           (response-string nil)
198           (response-continue t)
199           (return-value nil)
200           match-end)
201       (while response-continue
202         (goto-char elmo-pop3-read-point)
203         (while (not (re-search-forward "\r?\n" nil t))
204           (accept-process-output process)
205           (goto-char elmo-pop3-read-point))
206         (setq match-end (point))
207         (setq response-string
208               (buffer-substring elmo-pop3-read-point (- match-end 2)))
209         (goto-char elmo-pop3-read-point)
210         (if (looking-at "\\+.*$")
211             (progn
212               (setq response-continue nil)
213               (setq elmo-pop3-read-point match-end)
214               (setq return-value
215                     (if return-value
216                         (concat return-value "\n" response-string)
217                       response-string)))
218           (if (looking-at "\\-.*$")
219               (progn
220                 (setq response-continue nil)
221                 (setq elmo-pop3-read-point match-end)
222                 (setq return-value nil))
223             (setq elmo-pop3-read-point match-end)
224             (if not-command
225                 (setq response-continue nil))
226             (setq return-value
227                   (if return-value
228                       (concat return-value "\n" response-string)
229                     response-string)))
230           (setq elmo-pop3-read-point match-end)))
231       return-value)))
232
233 (defun elmo-pop3-process-filter (process output)
234   (save-excursion
235     (set-buffer (process-buffer process))
236     (goto-char (point-max))
237     (insert output)))
238
239 (defun elmo-pop3-auth-user (session)
240   (let ((process (elmo-network-session-process-internal session)))
241     ;; try USER/PASS
242     (elmo-pop3-send-command
243      process
244      (format "user %s" (elmo-network-session-user-internal session)))
245     (or (elmo-pop3-read-response process t)
246         (signal 'elmo-authenticate-error
247                 '(elmo-pop-auth-user)))
248     (elmo-pop3-send-command  process
249                              (format
250                               "pass %s"
251                               (elmo-get-passwd
252                                (elmo-network-session-password-key session))))
253     (or (elmo-pop3-read-response process t)
254         (signal 'elmo-authenticate-error
255                 '(elmo-pop-auth-user)))))
256
257 (defun elmo-pop3-auth-apop (session)
258   (if (string-match "^\+OK .*\\(<[^\>]+>\\)"
259                     (elmo-network-session-greeting-internal session))
260       ;; good, APOP ready server
261       (progn
262         (elmo-pop3-send-command
263          (elmo-network-session-process-internal session)
264          (format "apop %s %s"
265                  (elmo-network-session-user-internal session)
266                  (md5
267                   (concat (match-string
268                            1
269                            (elmo-network-session-greeting-internal session))
270                           (elmo-get-passwd
271                            (elmo-network-session-password-key session))))))
272         (or (elmo-pop3-read-response
273              (elmo-network-session-process-internal session)
274              t)
275             (signal 'elmo-authenticate-error
276                     '(elmo-pop3-auth-apop))))
277     (signal 'elmo-open-error '(elmo-pop3-auth-apop))))
278     
279 (luna-define-method elmo-network-initialize-session-buffer :after
280   ((session elmo-pop3-session) buffer)
281   (with-current-buffer buffer
282     (mapcar 'make-variable-buffer-local elmo-pop3-local-variables)))
283
284 (luna-define-method elmo-network-initialize-session ((session
285                                                       elmo-pop3-session))
286   (let ((process (elmo-network-session-process-internal session))
287         response mechanism)
288     (with-current-buffer (process-buffer process)
289       (set-process-filter process 'elmo-pop3-process-filter)
290       (setq elmo-pop3-read-point (point-min))
291       ;; Skip garbage output from process before greeting.
292       (while (and (memq (process-status process) '(open run))
293                   (goto-char (point-max))
294                   (forward-line -1)
295                   (not (looking-at "+OK")))
296         (accept-process-output process 1))
297       (setq elmo-pop3-read-point (point))
298       (or (elmo-network-session-set-greeting-internal
299            session
300            (elmo-pop3-read-response process t))
301           (signal 'elmo-open-error
302                   '(elmo-network-intialize-session)))
303       (when (eq (elmo-network-stream-type-symbol
304                  (elmo-network-session-stream-type-internal session))
305                 'starttls)
306         (elmo-pop3-send-command process "stls")
307         (if (string-match "^\+OK"
308                           (elmo-pop3-read-response process))
309             (starttls-negotiate process)
310           (signal 'elmo-open-error
311                   '(elmo-pop3-starttls-error)))))))
312
313 (luna-define-method elmo-network-authenticate-session ((session
314                                                         elmo-pop3-session))
315   (with-current-buffer (process-buffer 
316                         (elmo-network-session-process-internal session))
317     (let* ((process (elmo-network-session-process-internal session))
318            (auth (elmo-network-session-auth-internal session))
319            (auth (mapcar '(lambda (mechanism) (upcase (symbol-name mechanism)))
320                          (if (listp auth) auth (list auth))))
321            sasl-mechanisms
322            client name step response mechanism
323            sasl-read-passphrase)
324       (or (and (string= "USER" (car auth))
325                (elmo-pop3-auth-user session))
326           (and (string= "APOP" (car auth))
327                (elmo-pop3-auth-apop session))
328           (progn
329             (require 'sasl)
330             (setq sasl-mechanisms (mapcar 'car sasl-mechanism-alist))
331             (setq mechanism (sasl-find-mechanism auth))
332             (unless mechanism
333               (signal 'elmo-authenticate-error '(elmo-pop3-auth-no-mechanisms)))
334             (setq client
335                   (sasl-make-client
336                    mechanism
337                    (elmo-network-session-user-internal session)
338                    "pop"
339                    (elmo-network-session-server-internal session)))
340 ;;;         (if elmo-pop3-auth-user-realm
341 ;;;             (sasl-client-set-property client 'realm elmo-pop3-auth-user-realm))
342             (setq name (sasl-mechanism-name mechanism))
343             (elmo-network-session-set-auth-internal session
344                                                     (intern (downcase name)))
345             (setq sasl-read-passphrase
346                   (function
347                    (lambda (prompt)
348                      (elmo-get-passwd
349                       (elmo-network-session-password-key session)))))
350             (setq step (sasl-next-step client nil))
351             (elmo-pop3-send-command
352              process
353              (concat "AUTH " name
354                      (and (sasl-step-data step)
355                           (concat
356                            " "
357                            (elmo-base64-encode-string
358                             (sasl-step-data step) 'no-line-break))))) ;)
359             (catch 'done
360               (while t
361                 (unless (setq response (elmo-pop3-read-response process t))
362                   ;; response is NO or BAD.
363                   (signal 'elmo-authenticate-error
364                           (list (intern
365                                  (concat "elmo-pop3-auth-"
366                                          (downcase name))))))
367                 (if (string-match "^\+OK" response)
368                     (if (sasl-next-step client step)
369                         ;; Bogus server?
370                         (signal 'elmo-authenticate-error
371                                 (list (intern
372                                        (concat "elmo-pop3-auth-"
373                                                (downcase name)))))
374                       ;; The authentication process is finished.
375                       (throw 'done nil)))
376                 (sasl-step-set-data
377                  step
378                  (elmo-base64-decode-string 
379                   (cadr (split-string response " "))))
380                 (setq step (sasl-next-step client step))
381                 (elmo-pop3-send-command
382                  process
383                  (if (sasl-step-data step)
384                      (elmo-base64-encode-string (sasl-step-data step)
385                                                 'no-line-break)
386                    "")))))))))
387
388 (luna-define-method elmo-network-setup-session ((session
389                                                  elmo-pop3-session))
390   (let ((process (elmo-network-session-process-internal session))
391         count response)
392     (with-current-buffer (process-buffer process)
393       (setq elmo-pop3-size-hash (elmo-make-hash 31))
394       ;; To get obarray of uidl and size
395       (elmo-pop3-send-command process "list")
396       (if (null (elmo-pop3-read-response process))
397           (error "POP LIST command failed"))
398       (if (null (setq response
399                       (elmo-pop3-read-contents
400                        (current-buffer) process)))
401           (error "POP LIST command failed"))
402       ;; POP server always returns a sequence of serial numbers.
403       (setq count (elmo-pop3-parse-list-response response))
404       ;; UIDL
405       (when elmo-pop3-use-uidl-internal
406         (setq elmo-pop3-uidl-number-hash (elmo-make-hash (* count 2)))
407         (setq elmo-pop3-number-uidl-hash (elmo-make-hash (* count 2)))
408         ;; UIDL
409         (elmo-pop3-send-command process "uidl")
410         (unless (elmo-pop3-read-response process)
411           (error "POP UIDL failed"))
412         (unless (setq response (elmo-pop3-read-contents
413                                 (current-buffer) process))
414           (error "POP UIDL failed"))
415         (elmo-pop3-parse-uidl-response response)))))
416
417 (defun elmo-pop3-read-contents (buffer process)
418   (save-excursion
419     (set-buffer buffer)
420     (let ((case-fold-search nil)
421           match-end)
422       (goto-char elmo-pop3-read-point)
423       (while (not (re-search-forward "^\\.\r\n" nil t))
424         (accept-process-output process)
425         (goto-char elmo-pop3-read-point))
426       (setq match-end (point))
427       (elmo-delete-cr
428        (buffer-substring elmo-pop3-read-point
429                          (- match-end 3))))))
430
431 (luna-define-method elmo-folder-expand-msgdb-path ((folder elmo-pop3-folder))
432   (convert-standard-filename
433    (expand-file-name
434     (elmo-safe-filename (elmo-net-folder-user-internal folder))
435     (expand-file-name (elmo-net-folder-server-internal folder)
436                       (expand-file-name
437                        "pop"
438                        elmo-msgdb-dir)))))
439
440 (luna-define-method elmo-folder-exists-p ((folder elmo-pop3-folder))
441   (if (and elmo-pop3-exists-exactly
442            (elmo-folder-plugged-p folder))
443       (save-excursion
444         (let (elmo-auto-change-plugged  ; don't change plug status.
445               (elmo-inhibit-number-mapping t) ; No need to use uidl.
446               session)
447           (prog1
448               (setq session (elmo-pop3-get-session folder))
449             (if session
450                 (elmo-network-close-session session)))))
451     t))
452
453 (defun elmo-pop3-parse-uidl-response (string)
454   (let ((buffer (current-buffer))
455         number list size)
456     (with-temp-buffer
457       (let (number uid list)
458         (insert string)
459         (goto-char (point-min))
460         (while (re-search-forward "^\\([0-9]+\\)[\t ]\\([^ \n]+\\)$" nil t)
461           (setq number  (elmo-match-buffer 1))
462           (setq uid (elmo-match-buffer 2))
463           (with-current-buffer buffer
464             (elmo-set-hash-val uid number elmo-pop3-uidl-number-hash)
465             (elmo-set-hash-val (concat "#" number) uid
466                                elmo-pop3-number-uidl-hash))
467           (setq list (cons uid list)))
468         (with-current-buffer buffer (setq elmo-pop3-uidl-done t))
469         (nreverse list)))))
470
471 (defun elmo-pop3-parse-list-response (string)
472   (let ((buffer (current-buffer))
473         (count 0)
474         alist)
475     (with-temp-buffer
476       (insert string)
477       (goto-char (point-min))
478       (while (re-search-forward "^\\([0-9]+\\)[\t ]\\([0-9]+\\)$" nil t)
479         (setq alist
480               (cons
481                (cons (elmo-match-buffer 1)
482                      (elmo-match-buffer 2))
483                alist))
484         (setq count (1+ count)))
485       (with-current-buffer buffer
486         (setq elmo-pop3-size-hash (elmo-make-hash (* (length alist) 2)))
487         (while alist
488           (elmo-set-hash-val (concat "#" (car (car alist)))
489                              (cdr (car alist))
490                              elmo-pop3-size-hash)
491           (setq alist (cdr alist)))
492         (setq elmo-pop3-list-done t))
493       count)))
494
495 (defun elmo-pop3-list-location (folder)
496   (with-current-buffer (process-buffer
497                         (elmo-network-session-process-internal
498                          (elmo-pop3-get-session folder)))
499     (let (list)
500       (if elmo-pop3-uidl-done
501           (progn
502             (mapatoms
503              (lambda (atom)
504                (setq list (cons (symbol-name atom) list)))
505              elmo-pop3-uidl-number-hash)
506             (nreverse list))
507         (error "POP3: Error in UIDL")))))
508
509 (defun elmo-pop3-list-folder-by-location (folder locations)
510   (let* ((location-alist (elmo-pop3-folder-location-alist-internal folder))
511          (locations-in-db (mapcar 'cdr location-alist))
512          result new-locs new-alist deleted-locs i)
513     (setq new-locs
514           (elmo-delete-if (function
515                            (lambda (x) (member x locations-in-db)))
516                           locations))
517     (setq deleted-locs
518           (elmo-delete-if (function
519                            (lambda (x) (member x locations)))
520                           locations-in-db))
521     (setq i (or (elmo-max-of-list (mapcar 'car location-alist)) 0))
522     (mapcar
523      (function
524       (lambda (x)
525         (setq location-alist
526               (delq (rassoc x location-alist) location-alist))))
527      deleted-locs)
528     (while new-locs
529       (setq i (1+ i))
530       (setq new-alist (cons (cons i (car new-locs)) new-alist))
531       (setq new-locs (cdr new-locs)))
532     (setq result (nconc location-alist new-alist))
533     (setq result (sort result (lambda (x y) (< (car x)(car y)))))
534     (elmo-pop3-folder-set-location-alist-internal folder result)
535     (mapcar 'car result)))
536
537 (defun elmo-pop3-list-by-uidl-subr (folder &optional nonsort)
538   (let ((flist (elmo-pop3-list-folder-by-location
539                 folder
540                 (elmo-pop3-list-location folder))))
541     (if nonsort
542         (cons (elmo-max-of-list flist) (length flist))
543       (sort flist '<))))
544
545 (defun elmo-pop3-list-by-list (folder)
546   (with-current-buffer (process-buffer
547                         (elmo-network-session-process-internal
548                          (elmo-pop3-get-session folder)))
549     (let (list)
550       (if elmo-pop3-list-done
551           (progn
552             (mapatoms (lambda (atom)
553                         (setq list (cons (string-to-int
554                                           (substring (symbol-name atom) 1))
555                                          list)))
556                       elmo-pop3-size-hash)
557             (sort list '<))
558         (error "POP3: Error in list")))))
559
560 (defsubst elmo-pop3-folder-list-messages (folder)
561   (if (and (not elmo-inhibit-number-mapping)
562            (elmo-pop3-folder-use-uidl-internal folder))
563       (elmo-pop3-list-by-uidl-subr folder)
564     (elmo-pop3-list-by-list folder)))
565
566 (luna-define-method elmo-folder-list-messages-internal
567   ((folder elmo-pop3-folder) &optional nohide)
568   (elmo-pop3-folder-list-messages folder))
569
570 (luna-define-method elmo-folder-status ((folder elmo-pop3-folder))
571   (elmo-folder-check folder)
572   (if (elmo-pop3-folder-use-uidl-internal folder)
573       (elmo-pop3-list-by-uidl-subr folder 'nonsort)
574     (let* ((process
575             (elmo-network-session-process-internal
576              (elmo-pop3-get-session folder)))
577            (total 0)
578            response)
579       (with-current-buffer (process-buffer process)
580         (elmo-pop3-send-command process "STAT")
581         (setq response (elmo-pop3-read-response process))
582         ;; response: "^\+OK 2 7570$"
583         (if (not (string-match "^\+OK[ \t]*\\([0-9]*\\)" response))
584             (error "POP STAT command failed")
585           (setq total
586                 (string-to-int
587                  (substring response (match-beginning 1)(match-end 1 ))))
588           (cons total total))))))
589
590 (defvar elmo-pop3-header-fetch-chop-length 200)
591
592 (defsubst elmo-pop3-next-result-arrived-p ()
593   (cond
594    ((eq (following-char) ?+)
595     (if (re-search-forward "\n\\.\r?\n" nil t)
596         t
597       nil))
598    ((looking-at "-")
599     (if (search-forward "\n" nil t)
600         t
601       nil))
602    (t
603     nil)))
604      
605 (defun elmo-pop3-retrieve-headers (buffer tobuffer process articles)
606   (save-excursion
607     (set-buffer buffer)
608     (erase-buffer)
609     (let ((number (length articles))
610           (count 0)
611           (received 0)
612           (last-point (point-min)))
613       ;; Send HEAD commands.
614       (while articles
615         (elmo-pop3-send-command process (format
616                                          "top %s 0" (car articles))
617                                 'no-erase)
618 ;;;     (accept-process-output process 1)
619         (setq articles (cdr articles))
620         (setq count (1+ count))
621         ;; Every 200 requests we have to read the stream in
622         ;; order to avoid deadlocks.
623         (when (or elmo-pop3-send-command-synchronously
624                   (null articles)       ;All requests have been sent.
625                   (zerop (% count elmo-pop3-header-fetch-chop-length)))
626           (unless elmo-pop3-send-command-synchronously
627             (accept-process-output process 1))
628           (discard-input)
629           (while (progn
630                    (set-buffer buffer)
631                    (goto-char last-point)
632                    ;; Count replies.
633                    (while (elmo-pop3-next-result-arrived-p)
634                      (setq last-point (point))
635                      (setq received (1+ received)))
636                    (< received count))
637             (when (> number elmo-display-progress-threshold)
638               (if (or (zerop (% received 5)) (= received number))
639                   (elmo-display-progress
640                    'elmo-pop3-retrieve-headers "Getting headers..."
641                    (/ (* received 100) number))))
642             (accept-process-output process 1)
643 ;;;         (accept-process-output process)
644             (discard-input))))
645       ;; Remove all "\r"'s.
646       (goto-char (point-min))
647       (while (search-forward "\r\n" nil t)
648         (replace-match "\n"))
649       (copy-to-buffer tobuffer (point-min) (point-max)))))
650
651 (luna-define-method elmo-folder-msgdb-create ((folder elmo-pop3-folder)
652                                               numlist new-mark
653                                               already-mark seen-mark
654                                               important-mark seen-list)
655   (let ((process (elmo-network-session-process-internal
656                   (elmo-pop3-get-session folder))))
657     (with-current-buffer (process-buffer process)
658       (elmo-pop3-sort-msgdb-by-original-number
659        folder
660        (elmo-pop3-msgdb-create-by-header
661         process
662         numlist
663         new-mark already-mark
664         seen-mark seen-list
665         (if (elmo-pop3-folder-use-uidl-internal folder)
666             (elmo-pop3-folder-location-alist-internal folder)))))))
667
668 (defun elmo-pop3-sort-overview-by-original-number (overview loc-alist)
669   (if loc-alist
670       (sort overview
671             (lambda (ent1 ent2)
672               (< (elmo-pop3-uidl-to-number
673                   (cdr (assq (elmo-msgdb-overview-entity-get-number ent1)
674                              loc-alist)))
675                  (elmo-pop3-uidl-to-number
676                   (cdr (assq (elmo-msgdb-overview-entity-get-number ent2)
677                              loc-alist))))))
678     overview))
679
680 (defun elmo-pop3-sort-msgdb-by-original-number (folder msgdb)
681   (message "Sorting...")
682   (let ((overview (elmo-msgdb-get-overview msgdb)))
683     (current-buffer)
684     (setq overview (elmo-pop3-sort-overview-by-original-number
685                     overview
686                     (elmo-pop3-folder-location-alist-internal folder)))
687     (message "Sorting...done")
688     (list overview (nth 1 msgdb)(nth 2 msgdb)(nth 3 msgdb))))
689
690 (defun elmo-pop3-uidl-to-number (uidl)
691   (string-to-number (elmo-get-hash-val uidl
692                                        elmo-pop3-uidl-number-hash)))
693
694 (defun elmo-pop3-number-to-uidl (number)
695   (elmo-get-hash-val (format "#%d" number)
696                      elmo-pop3-number-uidl-hash))
697
698 (defun elmo-pop3-number-to-size (number)
699   (elmo-get-hash-val (format "#%d" number)
700                      elmo-pop3-size-hash))
701
702 (defun elmo-pop3-msgdb-create-by-header (process numlist
703                                                  new-mark already-mark
704                                                  seen-mark
705                                                  seen-list
706                                                  loc-alist)
707   (let ((tmp-buffer (get-buffer-create " *ELMO Overview TMP*")))
708     (with-current-buffer (process-buffer process)
709       (if loc-alist ; use uidl.
710           (setq numlist
711                 (delq
712                  nil
713                  (mapcar
714                   (lambda (number)
715                     (elmo-pop3-uidl-to-number (cdr (assq number loc-alist))))
716                   numlist))))
717       (elmo-pop3-retrieve-headers (process-buffer process)
718                                   tmp-buffer process numlist)
719       (prog1
720           (elmo-pop3-msgdb-create-message
721            tmp-buffer
722            process
723            (length numlist)
724            numlist
725            new-mark already-mark seen-mark seen-list loc-alist)
726         (kill-buffer tmp-buffer)))))
727
728 (defun elmo-pop3-msgdb-create-message (buffer
729                                        process
730                                        num
731                                        numlist new-mark already-mark
732                                        seen-mark
733                                        seen-list
734                                        loc-alist)
735   (save-excursion
736     (let (beg overview number-alist mark-alist
737               entity i number message-id gmark seen size)
738       (set-buffer buffer)
739       (elmo-set-buffer-multibyte default-enable-multibyte-characters)
740       (goto-char (point-min))
741       (setq i 0)
742       (message "Creating msgdb...")
743       (while (not (eobp))
744         (setq beg (save-excursion (forward-line 1) (point)))
745         (elmo-pop3-next-result-arrived-p)
746         (save-excursion
747           (forward-line -1)
748           (save-restriction
749             (narrow-to-region beg (point))
750             (setq entity
751                   (elmo-msgdb-create-overview-from-buffer
752                    (car numlist)))
753             (setq numlist (cdr numlist))
754             (when entity
755               (setq overview
756                     (elmo-msgdb-append-element
757                      overview entity))
758               (with-current-buffer (process-buffer process)
759                 (elmo-msgdb-overview-entity-set-size
760                  entity
761                  (string-to-number
762                   (elmo-pop3-number-to-size
763                    (elmo-msgdb-overview-entity-get-number entity))))
764                 (if (setq number
765                           (car
766                            (rassoc
767                             (elmo-pop3-number-to-uidl
768                              (elmo-msgdb-overview-entity-get-number entity))
769                             loc-alist)))
770                     (elmo-msgdb-overview-entity-set-number entity number)))
771               (setq number-alist
772                     (elmo-msgdb-number-add
773                      number-alist
774                      (elmo-msgdb-overview-entity-get-number entity)
775                      (car entity)))
776               (setq message-id (car entity))
777               (setq seen (member message-id seen-list))
778               (if (setq gmark (or (elmo-msgdb-global-mark-get message-id)
779                                   (if (elmo-file-cache-status
780                                        (elmo-file-cache-get message-id))
781                                       (if seen
782                                           nil
783                                         already-mark)
784                                     (if seen
785                                         (if elmo-pop3-use-cache
786                                             seen-mark)
787                                       new-mark))))
788                   (setq mark-alist
789                         (elmo-msgdb-mark-append
790                          mark-alist
791                          (elmo-msgdb-overview-entity-get-number entity)
792                          gmark))))))
793         (when (> num elmo-display-progress-threshold)
794           (setq i (1+ i))
795           (if (or (zerop (% i 5)) (= i num))
796               (elmo-display-progress
797                'elmo-pop3-msgdb-create-message "Creating msgdb..."
798                (/ (* i 100) num)))))
799       (list overview number-alist mark-alist))))
800
801 (defun elmo-pop3-read-body (process outbuf)
802   (with-current-buffer (process-buffer process)
803     (let ((start elmo-pop3-read-point)
804           end)
805       (goto-char start)
806       (while (not (re-search-forward "^\\.\r?\n" nil t))
807         (accept-process-output process)
808         (goto-char start))
809       (setq end (point))
810       (with-current-buffer outbuf
811         (erase-buffer)
812         (insert-buffer-substring (process-buffer process) start (- end 3))))))
813
814 (luna-define-method elmo-folder-open-internal ((folder elmo-pop3-folder))
815   (if (elmo-pop3-folder-use-uidl-internal folder)
816       (elmo-pop3-folder-set-location-alist-internal
817        folder (elmo-msgdb-location-load (elmo-folder-msgdb-path folder)))))
818
819 (luna-define-method elmo-folder-commit :after ((folder elmo-pop3-folder))
820   (when (elmo-folder-persistent-p folder)
821     (elmo-msgdb-location-save (elmo-folder-msgdb-path folder)
822                               (elmo-pop3-folder-location-alist-internal
823                                folder))))
824
825 (luna-define-method elmo-folder-close-internal ((folder elmo-pop3-folder))
826   (elmo-pop3-folder-set-location-alist-internal folder nil)
827   (elmo-folder-check folder))
828
829 (luna-define-method elmo-message-fetch-plugged ((folder elmo-pop3-folder)
830                                                 number strategy
831                                                 &optional section
832                                                 outbuf unseen)
833   (let* ((loc-alist (elmo-pop3-folder-location-alist-internal folder))
834          (process (elmo-network-session-process-internal
835                    (elmo-pop3-get-session folder)))
836          response errmsg msg)
837     (with-current-buffer (process-buffer process)
838       (if loc-alist
839           (setq number (elmo-pop3-uidl-to-number
840                         (cdr (assq number loc-alist)))))
841       (when number
842         (elmo-pop3-send-command process
843                                 (format "retr %s" number))
844         (when (null (setq response (elmo-pop3-read-response
845                                     process t)))
846           (error "Fetching message failed"))
847         (setq response (elmo-pop3-read-body process outbuf))
848         (set-buffer outbuf)
849         (goto-char (point-min))
850         (while (re-search-forward "^\\." nil t)
851           (replace-match "")
852           (forward-line))
853         response))))
854
855 (defun elmo-pop3-delete-msg (process number loc-alist)
856   (with-current-buffer (process-buffer process)
857     (let (response errmsg msg)
858       (if loc-alist
859           (setq number (elmo-pop3-uidl-to-number
860                         (cdr (assq number loc-alist)))))
861       (if number
862           (progn
863             (elmo-pop3-send-command process
864                                     (format "dele %s" number))
865             (when (null (setq response (elmo-pop3-read-response
866                                         process t)))
867               (error "Deleting message failed")))
868         (error "Deleting message failed")))))
869
870 (luna-define-method elmo-folder-delete-messages ((folder elmo-pop3-folder)
871                                                       msgs)
872   (let ((loc-alist (elmo-pop3-folder-location-alist-internal folder))
873         (process (elmo-network-session-process-internal
874                   (elmo-pop3-get-session folder))))
875     (mapcar '(lambda (msg) (elmo-pop3-delete-msg
876                             process msg loc-alist))
877             msgs)))
878
879 (luna-define-method elmo-message-use-cache-p ((folder elmo-pop3-folder) number)
880   elmo-pop3-use-cache)
881
882 (luna-define-method elmo-folder-persistent-p ((folder elmo-pop3-folder))
883   (and (elmo-folder-persistent-internal folder)
884        (elmo-pop3-folder-use-uidl-internal folder)))
885
886
887 (luna-define-method elmo-folder-check ((folder elmo-pop3-folder))
888   (if (elmo-folder-plugged-p folder)
889       (let ((session (elmo-pop3-get-session folder 'if-exists)))
890         (and session
891              (elmo-network-close-session session)))))
892
893 (require 'product)
894 (product-provide (provide 'elmo-pop3) (require 'elmo-version))
895
896 ;;; elmo-pop3.el ends here