* elmo-pop3.el (elmo-network-authenticate-session): Bind `sasl-mechanisms'
[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 (defvar elmo-pop3-use-uidl t
44   "*If non-nil, use UIDL.")
45
46 (defvar elmo-pop3-exists-exactly t)
47
48 (luna-define-class elmo-pop3-session (elmo-network-session) ())
49
50 ;; buffer-local
51 (defvar elmo-pop3-read-point nil)
52 (defvar elmo-pop3-number-uidl-hash nil) ; number -> uidl
53 (defvar elmo-pop3-uidl-number-hash nil) ; uidl -> number
54 (defvar elmo-pop3-size-hash nil) ; number -> size
55 (defvar elmo-pop3-uidl-done nil)
56 (defvar elmo-pop3-list-done nil)
57
58 (defvar elmo-pop3-local-variables '(elmo-pop3-read-point
59                                     elmo-pop3-uidl-number-hash
60                                     elmo-pop3-number-uidl-hash
61                                     elmo-pop3-uidl-done
62                                     elmo-pop3-size-hash
63                                     elmo-pop3-list-done))
64
65 (luna-define-method elmo-network-close-session ((session elmo-pop3-session))
66   (when (elmo-network-session-process-internal session)
67     (when (memq (process-status
68                  (elmo-network-session-process-internal session))
69                 '(open run))
70       (elmo-pop3-send-command (elmo-network-session-process-internal session)
71                               "quit")
72       (or (elmo-pop3-read-response
73            (elmo-network-session-process-internal session) t)
74           (error "POP error: QUIT failed")))
75     (kill-buffer (process-buffer
76                   (elmo-network-session-process-internal session)))
77     (delete-process (elmo-network-session-process-internal session))))
78
79 (defun elmo-pop3-get-session (spec &optional if-exists)
80   (elmo-network-get-session
81    'elmo-pop3-session
82    "POP3"
83    (elmo-pop3-spec-hostname spec)
84    (elmo-pop3-spec-port spec)
85    (elmo-pop3-spec-username spec)
86    (elmo-pop3-spec-auth spec)
87    (elmo-pop3-spec-stream-type spec)
88    if-exists))
89
90 (defun elmo-pop3-send-command (process command &optional no-erase)
91   (with-current-buffer (process-buffer process)
92     (unless no-erase
93       (erase-buffer))
94     (goto-char (point-min))
95     (setq elmo-pop3-read-point (point))
96     (process-send-string process command)
97     (process-send-string process "\r\n")))
98
99 (defun elmo-pop3-read-response (process &optional not-command)
100   (with-current-buffer (process-buffer process)
101     (let ((case-fold-search nil)
102           (response-string nil)
103           (response-continue t)
104           (return-value nil)
105           match-end)
106       (while response-continue
107         (goto-char elmo-pop3-read-point)
108         (while (not (re-search-forward "\r?\n" nil t))
109           (accept-process-output process)
110           (goto-char elmo-pop3-read-point))
111         (setq match-end (point))
112         (setq response-string
113               (buffer-substring elmo-pop3-read-point (- match-end 2)))
114         (goto-char elmo-pop3-read-point)
115         (if (looking-at "\\+.*$")
116             (progn
117               (setq response-continue nil)
118               (setq elmo-pop3-read-point match-end)
119               (setq return-value
120                     (if return-value
121                         (concat return-value "\n" response-string)
122                       response-string)))
123           (if (looking-at "\\-.*$")
124               (progn
125                 (setq response-continue nil)
126                 (setq elmo-pop3-read-point match-end)
127                 (setq return-value nil))
128             (setq elmo-pop3-read-point match-end)
129             (if not-command
130                 (setq response-continue nil))
131             (setq return-value
132                   (if return-value
133                       (concat return-value "\n" response-string)
134                     response-string)))
135           (setq elmo-pop3-read-point match-end)))
136       return-value)))
137
138 (defun elmo-pop3-process-filter (process output)
139   (save-excursion
140     (set-buffer (process-buffer process))
141     (goto-char (point-max))
142     (insert output)))
143
144 (defun elmo-pop3-auth-user (session)
145   (let ((process (elmo-network-session-process-internal session)))
146     ;; try USER/PASS
147     (elmo-pop3-send-command
148      process
149      (format "user %s" (elmo-network-session-user-internal session)))
150     (or (elmo-pop3-read-response process t)
151         (signal 'elmo-authenticate-error
152                 '(elmo-pop-auth-user)))
153     (elmo-pop3-send-command  process
154                              (format
155                               "pass %s"
156                               (elmo-get-passwd
157                                (elmo-network-session-password-key session))))
158     (or (elmo-pop3-read-response process t)
159         (signal 'elmo-authenticate-error
160                 '(elmo-pop-auth-user)))))
161
162 (defun elmo-pop3-auth-apop (session)
163   (if (string-match "^\+OK .*\\(<[^\>]+>\\)"
164                     (elmo-network-session-greeting-internal session))
165       ;; good, APOP ready server
166       (progn
167         (elmo-pop3-send-command
168          (elmo-network-session-process-internal session)
169          (format "apop %s %s"
170                  (elmo-network-session-user-internal session)
171                  (md5
172                   (concat (match-string
173                            1
174                            (elmo-network-session-greeting-internal session))
175                           (elmo-get-passwd
176                            (elmo-network-session-password-key session))))))
177         (or (elmo-pop3-read-response
178              (elmo-network-session-process-internal session)
179              t)
180             (signal 'elmo-authenticate-error
181                     '(elmo-pop3-auth-apop))))
182     (signal 'elmo-open-error '(elmo-pop3-auth-apop))))
183     
184 (luna-define-method elmo-network-initialize-session-buffer :after
185   ((session elmo-pop3-session) buffer)
186   (with-current-buffer buffer
187     (mapcar 'make-variable-buffer-local elmo-pop3-local-variables)))
188
189 (luna-define-method elmo-network-initialize-session ((session
190                                                       elmo-pop3-session))
191   (let ((process (elmo-network-session-process-internal session))
192         response mechanism)
193     (with-current-buffer (process-buffer process)
194       (set-process-filter process 'elmo-pop3-process-filter)
195       (setq elmo-pop3-read-point (point-min))
196       ;; Skip garbage output from process before greeting.
197       (while (and (memq (process-status process) '(open run))
198                   (goto-char (point-max))
199                   (forward-line -1)
200                   (not (looking-at "+OK")))
201         (accept-process-output process 1))
202       (setq elmo-pop3-read-point (point))
203       (or (elmo-network-session-set-greeting-internal
204            session
205            (elmo-pop3-read-response process t))
206           (signal 'elmo-open-error
207                   '(elmo-network-intialize-session)))
208       (when (eq (elmo-network-stream-type-symbol
209                  (elmo-network-session-stream-type-internal session))
210                 'starttls)
211         (elmo-pop3-send-command process "stls")
212         (if (string-match "^\+OK"
213                           (elmo-pop3-read-response process))
214             (starttls-negotiate process)
215           (signal 'elmo-open-error
216                   '(elmo-pop3-starttls-error)))))))
217
218 (luna-define-method elmo-network-authenticate-session ((session
219                                                         elmo-pop3-session))
220   (with-current-buffer (process-buffer 
221                         (elmo-network-session-process-internal session))
222     (let* ((process (elmo-network-session-process-internal session))
223            (auth (elmo-network-session-auth-internal session))
224            (auth (mapcar '(lambda (mechanism) (upcase (symbol-name mechanism)))
225                          (if (listp auth) auth (list auth))))
226            (sasl-mechanisms (mapcar 'car sasl-mechanism-alist))
227            client name step response mechanism
228            sasl-read-passphrase)
229       (or (and (string= "USER" (car auth))
230                (elmo-pop3-auth-user session))
231           (and (string= "APOP" (car auth))
232                (elmo-pop3-auth-apop session))
233           (progn
234             (setq mechanism (sasl-find-mechanism auth))
235             (unless mechanism
236               (signal 'elmo-authenticate-error '(elmo-pop3-auth-no-mechanisms)))
237             (setq client
238                   (sasl-make-client
239                    mechanism
240                    (elmo-network-session-user-internal session)
241                    "pop"
242                    (elmo-network-session-host-internal session)))
243 ;;;         (if elmo-pop3-auth-user-realm
244 ;;;             (sasl-client-set-property client 'realm elmo-pop3-auth-user-realm))
245             (setq name (sasl-mechanism-name mechanism))
246             (elmo-network-session-set-auth-internal session
247                                                     (intern (downcase name)))
248             (setq sasl-read-passphrase
249                   (function
250                    (lambda (prompt)
251                      (elmo-get-passwd
252                       (elmo-network-session-password-key session)))))
253             (setq step (sasl-next-step client nil))
254             (elmo-pop3-send-command
255              process
256              (concat "AUTH " name
257                      (and (sasl-step-data step)
258                           (concat
259                            " "
260                            (elmo-base64-encode-string
261                             (sasl-step-data step) 'no-line-break))))) ;)
262             (catch 'done
263               (while t
264                 (unless (setq response (elmo-pop3-read-response process t))
265                   (signal 'elmo-authenticate-error
266                           (list (intern
267                                  (concat "elmo-pop3-auth-"
268                                          (downcase name))))))
269                 (if (string-match "^\+OK" response)
270                     (if (sasl-next-step client step)
271                         (signal 'elmo-authenticate-error
272                                 (list (intern
273                                        (concat "elmo-pop3-auth-"
274                                                (downcase name)))))
275                       (throw 'done nil)))
276                 (sasl-step-set-data
277                  step
278                  (elmo-base64-decode-string 
279                   (cadr (split-string response " "))))
280                 (setq step (sasl-next-step client step))
281                 (elmo-pop3-send-command
282                  process
283                  (if (sasl-step-data step)
284                      (elmo-base64-encode-string (sasl-step-data step)
285                                                 'no-line-break)
286                    "")))))))))
287
288 (luna-define-method elmo-network-setup-session ((session
289                                                  elmo-pop3-session))
290   (let ((process (elmo-network-session-process-internal session))
291         count response)
292     (with-current-buffer (process-buffer process)
293       (setq elmo-pop3-size-hash (elmo-make-hash 31))
294       ;; To get obarray of uidl and size
295       (elmo-pop3-send-command process "list")
296       (if (null (elmo-pop3-read-response process))
297           (error "POP LIST command failed"))
298       (if (null (setq response
299                       (elmo-pop3-read-contents
300                        (current-buffer) process)))
301           (error "POP LIST command failed"))
302       ;; POP server always returns a sequence of serial numbers.
303       (setq count (elmo-pop3-parse-list-response response))
304       ;; UIDL
305       (when elmo-pop3-use-uidl
306         (setq elmo-pop3-uidl-number-hash (elmo-make-hash (* count 2)))
307         (setq elmo-pop3-number-uidl-hash (elmo-make-hash (* count 2)))
308         ;; UIDL
309         (elmo-pop3-send-command process "uidl")
310         (unless (elmo-pop3-read-response process)
311           (error "POP UIDL failed"))
312         (unless (setq response (elmo-pop3-read-contents
313                                 (current-buffer) process))
314           (error "POP UIDL failed"))
315         (elmo-pop3-parse-uidl-response response)))))
316
317 (defun elmo-pop3-read-contents (buffer process)
318   (save-excursion
319     (set-buffer buffer)
320     (let ((case-fold-search nil)
321           match-end)
322       (goto-char elmo-pop3-read-point)
323       (while (not (re-search-forward "^\\.\r\n" nil t))
324         (accept-process-output process)
325         (goto-char elmo-pop3-read-point))
326       (setq match-end (point))
327       (elmo-delete-cr
328        (buffer-substring elmo-pop3-read-point
329                          (- match-end 3))))))
330
331 ;; dummy functions
332 (defun elmo-pop3-list-folders (spec &optional hierarchy) nil)
333 (defun elmo-pop3-append-msg (spec string) nil nil)
334 (defun elmo-pop3-folder-creatable-p (spec) nil)
335 (defun elmo-pop3-create-folder (spec) nil)
336
337 (defun elmo-pop3-folder-exists-p (spec)
338   (if (and elmo-pop3-exists-exactly
339            (elmo-pop3-plugged-p spec))
340       (save-excursion
341         (let (elmo-auto-change-plugged ; don't change plug status.
342               elmo-pop3-use-uidl       ; No need to use uidl.
343               session)
344           (prog1
345               (setq session (elmo-pop3-get-session spec))
346             (if session
347                 (elmo-network-close-session session)))))
348     t))
349
350 (defun elmo-pop3-parse-uidl-response (string)
351   (let ((buffer (current-buffer))
352         number list size)
353     (with-temp-buffer
354       (let (number uid list)
355         (insert string)
356         (goto-char (point-min))
357         (while (re-search-forward "^\\([0-9]+\\)[\t ]\\([^ \n]+\\)$" nil t)
358           (setq number  (elmo-match-buffer 1))
359           (setq uid (elmo-match-buffer 2))
360           (with-current-buffer buffer
361             (elmo-set-hash-val uid number elmo-pop3-uidl-number-hash)
362             (elmo-set-hash-val (concat "#" number) uid
363                                elmo-pop3-number-uidl-hash))
364           (setq list (cons uid list)))
365         (with-current-buffer buffer (setq elmo-pop3-uidl-done t))
366         (nreverse list)))))
367
368 (defun elmo-pop3-parse-list-response (string)
369   (let ((buffer (current-buffer))
370         (count 0)
371         alist)
372     (with-temp-buffer
373       (insert string)
374       (goto-char (point-min))
375       (while (re-search-forward "^\\([0-9]+\\)[\t ]\\([0-9]+\\)$" nil t)
376         (setq alist
377               (cons
378                (cons (elmo-match-buffer 1)
379                      (elmo-match-buffer 2))
380                alist))
381         (setq count (1+ count)))
382       (with-current-buffer buffer
383         (setq elmo-pop3-size-hash (elmo-make-hash (* (length alist) 2)))
384         (while alist
385           (elmo-set-hash-val (concat "#" (car (car alist)))
386                              (cdr (car alist))
387                              elmo-pop3-size-hash)
388           (setq alist (cdr alist)))
389         (setq elmo-pop3-list-done t))
390       count)))
391
392 (defun elmo-pop3-list-location (spec)
393   (with-current-buffer (process-buffer
394                         (elmo-network-session-process-internal
395                          (elmo-pop3-get-session spec)))
396     (let (list)
397       (if elmo-pop3-uidl-done
398           (progn
399             (mapatoms
400              (lambda (atom)
401                (setq list (cons (symbol-name atom) list)))
402              elmo-pop3-uidl-number-hash)
403             (nreverse list))
404         (error "POP3: Error in UIDL")))))
405
406 (defun elmo-pop3-list-by-uidl-subr (spec &optional nonsort)
407   (let ((flist (elmo-list-folder-by-location
408                 spec
409                 (elmo-pop3-list-location spec))))
410     (if nonsort
411         (cons (elmo-max-of-list flist) (length flist))
412       (sort flist '<))))
413
414 (defun elmo-pop3-list-by-list (spec)
415   (with-current-buffer (process-buffer
416                         (elmo-network-session-process-internal
417                          (elmo-pop3-get-session spec)))
418     (let (list)
419       (if elmo-pop3-list-done
420           (progn
421             (mapatoms (lambda (atom)
422                         (setq list (cons (string-to-int
423                                           (substring (symbol-name atom) 1))
424                                          list)))
425                       elmo-pop3-size-hash)
426             (sort list '<))
427         (error "POP3: Error in list")))))
428
429 (defun elmo-pop3-list-folder (spec)
430   (let ((killed (and elmo-use-killed-list
431                      (elmo-msgdb-killed-list-load
432                       (elmo-msgdb-expand-path spec))))
433         numbers)
434     (elmo-pop3-commit spec)
435     (setq numbers (if elmo-pop3-use-uidl
436                       (progn
437                         (elmo-pop3-list-by-uidl-subr spec))
438                     (elmo-pop3-list-by-list spec)))
439     (elmo-living-messages numbers killed)))
440
441 (defun elmo-pop3-max-of-folder (spec)
442   (elmo-pop3-commit spec)
443   (if elmo-pop3-use-uidl
444       (elmo-pop3-list-by-uidl-subr spec 'nonsort)
445     (let* ((process
446             (elmo-network-session-process-internal
447              (elmo-pop3-get-session spec)))
448            (total 0)
449            response)
450       (with-current-buffer (process-buffer process)
451         (elmo-pop3-send-command process "STAT")
452         (setq response (elmo-pop3-read-response process))
453         ;; response: "^\+OK 2 7570$"
454         (if (not (string-match "^\+OK[ \t]*\\([0-9]*\\)" response))
455             (error "POP STAT command failed")
456           (setq total
457                 (string-to-int
458                  (substring response (match-beginning 1)(match-end 1 ))))
459           (cons total total))))))
460
461 (defvar elmo-pop3-header-fetch-chop-length 200)
462
463 (defsubst elmo-pop3-next-result-arrived-p ()
464   (cond
465    ((eq (following-char) ?+)
466     (if (re-search-forward "\n\\.\r?\n" nil t)
467         t
468       nil))
469    ((looking-at "-")
470     (if (search-forward "\n" nil t)
471         t
472       nil))
473    (t
474     nil)))
475      
476 (defun elmo-pop3-retrieve-headers (buffer tobuffer process articles)
477   (save-excursion
478     (set-buffer buffer)
479     (erase-buffer)
480     (let ((number (length articles))
481           (count 0)
482           (received 0)
483           (last-point (point-min)))
484       ;; Send HEAD commands.
485       (while articles
486         (elmo-pop3-send-command process (format
487                                          "top %s 0" (car articles))
488                                 'no-erase)
489 ;;;     (accept-process-output process 1)
490         (setq articles (cdr articles))
491         (setq count (1+ count))
492         ;; Every 200 requests we have to read the stream in
493         ;; order to avoid deadlocks.
494         (when (or elmo-pop3-send-command-synchronously
495                   (null articles)       ;All requests have been sent.
496                   (zerop (% count elmo-pop3-header-fetch-chop-length)))
497           (unless elmo-pop3-send-command-synchronously
498             (accept-process-output process 1))
499           (discard-input)
500           (while (progn
501                    (set-buffer buffer)
502                    (goto-char last-point)
503                    ;; Count replies.
504                    (while (elmo-pop3-next-result-arrived-p)
505                      (setq last-point (point))
506                      (setq received (1+ received)))
507                    (< received count))
508             (when (> number elmo-display-progress-threshold)
509               (if (or (zerop (% received 5)) (= received number))
510                   (elmo-display-progress
511                    'elmo-pop3-retrieve-headers "Getting headers..."
512                    (/ (* received 100) number))))
513             (accept-process-output process 1)
514 ;;;         (accept-process-output process)
515             (discard-input))))
516       ;; Remove all "\r"'s.
517       (goto-char (point-min))
518       (while (search-forward "\r\n" nil t)
519         (replace-match "\n"))
520       (copy-to-buffer tobuffer (point-min) (point-max)))))
521
522 (defalias 'elmo-pop3-msgdb-create 'elmo-pop3-msgdb-create-as-numlist)
523
524 (defun elmo-pop3-sort-overview-by-original-number (overview loc-alist)
525   (if loc-alist
526       (sort overview
527             (lambda (ent1 ent2)
528               (< (elmo-pop3-uidl-to-number
529                   (cdr (assq (elmo-msgdb-overview-entity-get-number ent1)
530                              loc-alist)))
531                  (elmo-pop3-uidl-to-number
532                   (cdr (assq (elmo-msgdb-overview-entity-get-number ent2)
533                              loc-alist))))))
534     overview))
535
536 (defun elmo-pop3-sort-msgdb-by-original-number (msgdb)
537   (message "Sorting...")
538   (let ((overview (elmo-msgdb-get-overview msgdb)))
539     (setq overview (elmo-pop3-sort-overview-by-original-number
540                     overview
541                     (elmo-msgdb-get-location msgdb)))
542     (message "Sorting...done")
543     (list overview (nth 1 msgdb)(nth 2 msgdb)(nth 3 msgdb)(nth 4 msgdb))))
544
545 (defun elmo-pop3-msgdb-create-as-numlist (spec numlist new-mark
546                                                already-mark seen-mark
547                                                important-mark seen-list
548                                                &optional msgdb)
549   (when numlist
550     (let ((process (elmo-network-session-process-internal
551                     (elmo-pop3-get-session spec)))
552           loc-alist)
553       (if elmo-pop3-use-uidl
554           (setq loc-alist (if msgdb (elmo-msgdb-get-location msgdb)
555                             (elmo-msgdb-location-load
556                              (elmo-msgdb-expand-path spec)))))
557       (with-current-buffer (process-buffer process)
558         (elmo-pop3-sort-msgdb-by-original-number
559          (elmo-pop3-msgdb-create-by-header process numlist
560                                            new-mark already-mark
561                                            seen-mark seen-list
562                                            loc-alist))))))
563
564 (defun elmo-pop3-uidl-to-number (uidl)
565   (string-to-number (elmo-get-hash-val uidl
566                                        elmo-pop3-uidl-number-hash)))
567
568 (defun elmo-pop3-number-to-uidl (number)
569   (elmo-get-hash-val (format "#%d" number)
570                      elmo-pop3-number-uidl-hash))
571
572 (defun elmo-pop3-number-to-size (number)
573   (elmo-get-hash-val (format "#%d" number)
574                      elmo-pop3-size-hash))
575
576 (defun elmo-pop3-msgdb-create-by-header (process numlist
577                                                  new-mark already-mark
578                                                  seen-mark
579                                                  seen-list
580                                                  loc-alist)
581   (let ((tmp-buffer (get-buffer-create " *ELMO Overview TMP*")))
582     (with-current-buffer (process-buffer process)
583       (if loc-alist ; use uidl.
584           (setq numlist
585                 (delq
586                  nil
587                  (mapcar
588                   (lambda (number)
589                     (elmo-pop3-uidl-to-number (cdr (assq number loc-alist))))
590                   numlist))))
591       (elmo-pop3-retrieve-headers (process-buffer process)
592                                   tmp-buffer process numlist)
593       (prog1
594           (elmo-pop3-msgdb-create-message
595            tmp-buffer
596            process
597            (length numlist)
598            numlist
599            new-mark already-mark seen-mark seen-list loc-alist)
600         (kill-buffer tmp-buffer)))))
601
602 (defun elmo-pop3-msgdb-create-message (buffer
603                                        process
604                                        num
605                                        numlist new-mark already-mark
606                                        seen-mark
607                                        seen-list
608                                        loc-alist)
609   (save-excursion
610     (let (beg overview number-alist mark-alist
611               entity i number message-id gmark seen size)
612       (set-buffer buffer)
613       (elmo-set-buffer-multibyte default-enable-multibyte-characters)
614       (goto-char (point-min))
615       (setq i 0)
616       (message "Creating msgdb...")
617       (while (not (eobp))
618         (setq beg (save-excursion (forward-line 1) (point)))
619         (elmo-pop3-next-result-arrived-p)
620         (save-excursion
621           (forward-line -1)
622           (save-restriction
623             (narrow-to-region beg (point))
624             (setq entity
625                   (elmo-msgdb-create-overview-from-buffer
626                    (car numlist)))
627             (setq numlist (cdr numlist))
628             (when entity
629               (setq overview
630                     (elmo-msgdb-append-element
631                      overview entity))
632               (with-current-buffer (process-buffer process)
633                 (elmo-msgdb-overview-entity-set-size
634                  entity
635                  (string-to-number
636                   (elmo-pop3-number-to-size
637                    (elmo-msgdb-overview-entity-get-number entity))))
638                 (if (setq number
639                           (car
640                            (rassoc
641                             (elmo-pop3-number-to-uidl
642                              (elmo-msgdb-overview-entity-get-number entity))
643                             loc-alist)))
644                     (elmo-msgdb-overview-entity-set-number entity number)))
645               (setq number-alist
646                     (elmo-msgdb-number-add
647                      number-alist
648                      (elmo-msgdb-overview-entity-get-number entity)
649                      (car entity)))
650               (setq message-id (car entity))
651               (setq seen (member message-id seen-list))
652               (if (setq gmark (or (elmo-msgdb-global-mark-get message-id)
653                                   (if (elmo-cache-exists-p
654                                        message-id) ; XXX
655                                       (if seen
656                                           nil
657                                         already-mark)
658                                     (if seen
659                                         (if elmo-pop3-use-cache
660                                             seen-mark)
661                                       new-mark))))
662                   (setq mark-alist
663                         (elmo-msgdb-mark-append
664                          mark-alist
665                          (elmo-msgdb-overview-entity-get-number entity)
666                          gmark))))))
667         (when (> num elmo-display-progress-threshold)
668           (setq i (1+ i))
669           (if (or (zerop (% i 5)) (= i num))
670               (elmo-display-progress
671                'elmo-pop3-msgdb-create-message "Creating msgdb..."
672                (/ (* i 100) num)))))
673       (list overview number-alist mark-alist loc-alist))))
674
675 (defun elmo-pop3-read-body (process outbuf)
676   (with-current-buffer (process-buffer process)
677     (let ((start elmo-pop3-read-point)
678           end)
679       (goto-char start)
680       (while (not (re-search-forward "^\\.\r?\n" nil t))
681         (accept-process-output process)
682         (goto-char start))
683       (setq end (point))
684       (with-current-buffer outbuf
685         (erase-buffer)
686         (insert-buffer-substring (process-buffer process) start (- end 3))
687         (elmo-delete-cr-get-content-type)))))
688
689 (defun elmo-pop3-read-msg (spec number outbuf &optional msgdb unread)
690   (let* ((loc-alist (if elmo-pop3-use-uidl
691                         (if msgdb
692                             (elmo-msgdb-get-location msgdb)
693                           (elmo-msgdb-location-load
694                            (elmo-msgdb-expand-path spec)))))
695          (process (elmo-network-session-process-internal
696                    (elmo-pop3-get-session spec)))
697          response errmsg msg)
698     (with-current-buffer (process-buffer process)
699       (if loc-alist
700           (setq number (elmo-pop3-uidl-to-number
701                         (cdr (assq number loc-alist)))))
702       (when number
703         (elmo-pop3-send-command process
704                                 (format "retr %s" number))
705         (when (null (setq response (elmo-pop3-read-response
706                                     process t)))
707           (error "Fetching message failed"))
708         (setq response (elmo-pop3-read-body process outbuf))
709         (set-buffer outbuf)
710         (goto-char (point-min))
711         (while (re-search-forward "^\\." nil t)
712           (replace-match "")
713           (forward-line))
714         response))))
715
716 (defun elmo-pop3-delete-msg (process number loc-alist)
717   (with-current-buffer (process-buffer process)
718     (let (response errmsg msg)
719       (if loc-alist
720           (setq number (elmo-pop3-uidl-to-number
721                         (cdr (assq number loc-alist)))))
722       (if number
723           (progn
724             (elmo-pop3-send-command process
725                                     (format "dele %s" number))
726             (when (null (setq response (elmo-pop3-read-response
727                                         process t)))
728               (error "Deleting message failed")))
729         (error "Deleting message failed")))))
730
731 (defun elmo-pop3-delete-msgs (spec msgs &optional msgdb)
732   (let ((loc-alist (if elmo-pop3-use-uidl
733                        (if msgdb
734                            (elmo-msgdb-get-location msgdb)
735                          (elmo-msgdb-location-load
736                           (elmo-msgdb-expand-path spec)))))
737         (process (elmo-network-session-process-internal
738                   (elmo-pop3-get-session spec))))
739     (mapcar '(lambda (msg) (elmo-pop3-delete-msg
740                             process msg loc-alist))
741             msgs)))
742
743 (defun elmo-pop3-search (spec condition &optional numlist)
744   (error "Searching in pop3 folder is not implemented yet"))
745
746 (defun elmo-pop3-use-cache-p (spec number)
747   elmo-pop3-use-cache)
748
749 (defun elmo-pop3-local-file-p (spec number)
750   nil)
751
752 (defun elmo-pop3-port-label (spec)
753   (concat "pop3"
754           (if (elmo-pop3-spec-stream-type spec)
755               (concat "!" (symbol-name
756                            (elmo-network-stream-type-symbol
757                             (elmo-pop3-spec-stream-type spec)))))))
758
759 (defsubst elmo-pop3-portinfo (spec)
760   (list (elmo-pop3-spec-hostname spec)
761         (elmo-pop3-spec-port spec)))
762
763 (defun elmo-pop3-plugged-p (spec)
764   (apply 'elmo-plugged-p
765          (append (elmo-pop3-portinfo spec)
766                  (list nil (quote (elmo-pop3-port-label spec))))))
767
768 (defun elmo-pop3-set-plugged (spec plugged add)
769   (apply 'elmo-set-plugged plugged
770          (append (elmo-pop3-portinfo spec)
771                  (list nil nil (quote (elmo-pop3-port-label spec)) add))))
772
773 (defalias 'elmo-pop3-sync-number-alist
774   'elmo-generic-sync-number-alist)
775 (defalias 'elmo-pop3-list-folder-unread
776   'elmo-generic-list-folder-unread)
777 (defalias 'elmo-pop3-list-folder-important
778   'elmo-generic-list-folder-important)
779 (defalias 'elmo-pop3-folder-diff 'elmo-generic-folder-diff)
780
781 (defun elmo-pop3-commit (spec)
782   (if (elmo-pop3-plugged-p spec)
783       (let ((session (elmo-pop3-get-session spec 'if-exists)))
784         (and session
785              (elmo-network-close-session session)))))
786        
787
788 (require 'product)
789 (product-provide (provide 'elmo-pop3) (require 'elmo-version))
790
791 ;;; elmo-pop3.el ends here