* elmo-net.el: Add autoload settings for `starttls-negotiate',
[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-pop-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            client name step response mechanism
227            sasl-read-passphrase)
228       (or (and (string= "USER" (car auth))
229                (elmo-pop3-auth-user session))
230           (and (string= "APOP" (car auth))
231                (elmo-pop3-auth-apop session))
232           (progn
233             (setq mechanism (sasl-find-mechanism auth))
234             (unless mechanism
235               (signal 'elmo-authenticate-error '(elmo-pop3-auth-no-mechanisms)))
236             (setq client
237                   (sasl-make-client
238                    mechanism
239                    (elmo-network-session-user-internal session)
240                    "pop"
241                    (elmo-network-session-host-internal session)))
242 ;;;         (if elmo-pop3-auth-user-realm
243 ;;;             (sasl-client-set-property client 'realm elmo-pop3-auth-user-realm))
244             (setq name (sasl-mechanism-name mechanism))
245             (elmo-network-session-set-auth-internal session
246                                                     (intern (downcase name)))
247             (setq sasl-read-passphrase
248                   (function
249                    (lambda (prompt)
250                      (elmo-get-passwd
251                       (elmo-network-session-password-key session)))))
252             (setq step (sasl-next-step client nil))
253             (elmo-pop3-send-command
254              process
255              (concat "AUTH " name
256                      (and (sasl-step-data step)
257                           (concat 
258                            " "
259                            (elmo-base64-encode-string
260                             (sasl-step-data step) 'no-line-break))))) ;)
261             (catch 'done
262               (while t
263                 (unless (setq response (elmo-pop3-read-response process t))
264                   (signal 'elmo-authenticate-error
265                           (list (intern
266                                  (concat "elmo-pop3-auth-"
267                                          (downcase name))))))
268                 (if (string-match "^\+OK" response)
269                     (if (sasl-next-step client step)
270                         (signal 'elmo-authenticate-error
271                                 (list (intern
272                                        (concat "elmo-pop3-auth-"
273                                                (downcase name)))))
274                       (throw 'done nil)))
275                 (sasl-step-set-data
276                  step
277                  (elmo-base64-decode-string 
278                   (cadr (split-string response " "))))
279                 (setq step (sasl-next-step client step))
280                 (elmo-pop3-send-command
281                  process
282                  (if (sasl-step-data step)
283                      (elmo-base64-encode-string (sasl-step-data step)
284                                                 'no-line-break)
285                    "")))))))))
286
287 (luna-define-method elmo-network-setup-session ((session
288                                                  elmo-pop3-session))
289   (let ((process (elmo-network-session-process-internal session))
290         count response)
291     (with-current-buffer (process-buffer process)
292       (setq elmo-pop3-size-hash (elmo-make-hash 31))
293       ;; To get obarray of uidl and size
294       (elmo-pop3-send-command process "list")
295       (if (null (elmo-pop3-read-response process))
296           (error "POP LIST command failed"))
297       (if (null (setq response
298                       (elmo-pop3-read-contents
299                        (current-buffer) process)))
300           (error "POP LIST command failed"))
301       ;; POP server always returns a sequence of serial numbers.
302       (setq count (elmo-pop3-parse-list-response response))
303       ;; UIDL
304       (when elmo-pop3-use-uidl
305         (setq elmo-pop3-uidl-number-hash (elmo-make-hash (* count 2)))
306         (setq elmo-pop3-number-uidl-hash (elmo-make-hash (* count 2)))
307         ;; UIDL
308         (elmo-pop3-send-command process "uidl")
309         (unless (elmo-pop3-read-response process)
310           (error "POP UIDL failed"))
311         (unless (setq response (elmo-pop3-read-contents
312                                 (current-buffer) process))
313           (error "POP UIDL failed"))
314         (elmo-pop3-parse-uidl-response response)))))
315
316 (defun elmo-pop3-read-contents (buffer process)
317   (save-excursion
318     (set-buffer buffer)
319     (let ((case-fold-search nil)
320           match-end)
321       (goto-char elmo-pop3-read-point)
322       (while (not (re-search-forward "^\\.\r\n" nil t))
323         (accept-process-output process)
324         (goto-char elmo-pop3-read-point))
325       (setq match-end (point))
326       (elmo-delete-cr
327        (buffer-substring elmo-pop3-read-point
328                          (- match-end 3))))))
329
330 ;; dummy functions
331 (defun elmo-pop3-list-folders (spec &optional hierarchy) nil)
332 (defun elmo-pop3-append-msg (spec string) nil nil)
333 (defun elmo-pop3-folder-creatable-p (spec) nil)
334 (defun elmo-pop3-create-folder (spec) nil)
335
336 (defun elmo-pop3-folder-exists-p (spec)
337   (if (and elmo-pop3-exists-exactly
338            (elmo-pop3-plugged-p spec))
339       (save-excursion
340         (let (elmo-auto-change-plugged ; don't change plug status.
341               elmo-pop3-use-uidl       ; No need to use uidl.
342               session)
343           (prog1
344               (setq session (elmo-pop3-get-session spec))
345             (if session
346                 (elmo-network-close-session session)))))
347     t))
348
349 (defun elmo-pop3-parse-uidl-response (string)
350   (let ((buffer (current-buffer))
351         number list size)
352     (with-temp-buffer
353       (let (number uid list)
354         (insert string)
355         (goto-char (point-min))
356         (while (re-search-forward "^\\([0-9]+\\)[\t ]\\([^ \n]+\\)$" nil t)
357           (setq number  (elmo-match-buffer 1))
358           (setq uid (elmo-match-buffer 2))
359           (with-current-buffer buffer
360             (elmo-set-hash-val uid number elmo-pop3-uidl-number-hash)
361             (elmo-set-hash-val (concat "#" number) uid
362                                elmo-pop3-number-uidl-hash))
363           (setq list (cons uid list)))
364         (with-current-buffer buffer (setq elmo-pop3-uidl-done t))
365         (nreverse list)))))
366
367 (defun elmo-pop3-parse-list-response (string)
368   (let ((buffer (current-buffer))
369         (count 0)
370         alist)
371     (with-temp-buffer
372       (insert string)
373       (goto-char (point-min))
374       (while (re-search-forward "^\\([0-9]+\\)[\t ]\\([0-9]+\\)$" nil t)
375         (setq alist
376               (cons
377                (cons (elmo-match-buffer 1)
378                      (elmo-match-buffer 2))
379                alist))
380         (setq count (1+ count)))
381       (with-current-buffer buffer
382         (setq elmo-pop3-size-hash (elmo-make-hash (* (length alist) 2)))
383         (while alist
384           (elmo-set-hash-val (concat "#" (car (car alist)))
385                              (cdr (car alist))
386                              elmo-pop3-size-hash)
387           (setq alist (cdr alist)))
388         (setq elmo-pop3-list-done t))
389       count)))
390
391 (defun elmo-pop3-list-location (spec)
392   (with-current-buffer (process-buffer
393                         (elmo-network-session-process-internal
394                          (elmo-pop3-get-session spec)))
395     (let (list)
396       (if elmo-pop3-uidl-done
397           (progn
398             (mapatoms
399              (lambda (atom)
400                (setq list (cons (symbol-name atom) list)))
401              elmo-pop3-uidl-number-hash)
402             (nreverse list))
403         (error "POP3: Error in UIDL")))))
404
405 (defun elmo-pop3-list-by-uidl-subr (spec &optional nonsort)
406   (let ((flist (elmo-list-folder-by-location
407                 spec
408                 (elmo-pop3-list-location spec))))
409     (if nonsort
410         (cons (elmo-max-of-list flist) (length flist))
411       (sort flist '<))))
412
413 (defun elmo-pop3-list-by-list (spec)
414   (with-current-buffer (process-buffer
415                         (elmo-network-session-process-internal
416                          (elmo-pop3-get-session spec)))
417     (let (list)
418       (if elmo-pop3-list-done
419           (progn
420             (mapatoms (lambda (atom)
421                         (setq list (cons (string-to-int
422                                           (substring (symbol-name atom) 1))
423                                          list)))
424                       elmo-pop3-size-hash)
425             (sort list '<))
426         (error "POP3: Error in list")))))
427
428 (defun elmo-pop3-list-folder (spec)
429   (let ((killed (and elmo-use-killed-list
430                      (elmo-msgdb-killed-list-load
431                       (elmo-msgdb-expand-path spec))))
432         numbers)
433     (elmo-pop3-commit spec)
434     (setq numbers (if elmo-pop3-use-uidl
435                       (progn
436                         (elmo-pop3-list-by-uidl-subr spec))
437                     (elmo-pop3-list-by-list spec)))
438     (elmo-living-messages numbers killed)))
439
440 (defun elmo-pop3-max-of-folder (spec)
441   (elmo-pop3-commit spec)
442   (if elmo-pop3-use-uidl
443       (elmo-pop3-list-by-uidl-subr spec 'nonsort)
444     (let* ((process
445             (elmo-network-session-process-internal
446              (elmo-pop3-get-session spec)))
447            (total 0)
448            response)
449       (with-current-buffer (process-buffer process)
450         (elmo-pop3-send-command process "STAT")
451         (setq response (elmo-pop3-read-response process))
452         ;; response: "^\+OK 2 7570$"
453         (if (not (string-match "^\+OK[ \t]*\\([0-9]*\\)" response))
454             (error "POP STAT command failed")
455           (setq total
456                 (string-to-int
457                  (substring response (match-beginning 1)(match-end 1 ))))
458           (cons total total))))))
459
460 (defvar elmo-pop3-header-fetch-chop-length 200)
461
462 (defsubst elmo-pop3-next-result-arrived-p ()
463   (cond
464    ((eq (following-char) ?+)
465     (if (re-search-forward "\n\\.\r?\n" nil t)
466         t
467       nil))
468    ((looking-at "-")
469     (if (search-forward "\n" nil t)
470         t
471       nil))
472    (t
473     nil)))
474      
475 (defun elmo-pop3-retrieve-headers (buffer tobuffer process articles)
476   (save-excursion
477     (set-buffer buffer)
478     (erase-buffer)
479     (let ((number (length articles))
480           (count 0)
481           (received 0)
482           (last-point (point-min)))
483       ;; Send HEAD commands.
484       (while articles
485         (elmo-pop3-send-command process (format
486                                          "top %s 0" (car articles))
487                                 'no-erase)
488 ;;;     (accept-process-output process 1)
489         (setq articles (cdr articles))
490         (setq count (1+ count))
491         ;; Every 200 requests we have to read the stream in
492         ;; order to avoid deadlocks.
493         (when (or elmo-pop3-send-command-synchronously
494                   (null articles)       ;All requests have been sent.
495                   (zerop (% count elmo-pop3-header-fetch-chop-length)))
496           (unless elmo-pop3-send-command-synchronously
497             (accept-process-output process 1))
498           (discard-input)
499           (while (progn
500                    (set-buffer buffer)
501                    (goto-char last-point)
502                    ;; Count replies.
503                    (while (elmo-pop3-next-result-arrived-p)
504                      (setq last-point (point))
505                      (setq received (1+ received)))
506                    (< received count))
507             (when (> number elmo-display-progress-threshold)
508               (if (or (zerop (% received 5)) (= received number))
509                   (elmo-display-progress
510                    'elmo-pop3-retrieve-headers "Getting headers..."
511                    (/ (* received 100) number))))
512             (accept-process-output process 1)
513 ;;;         (accept-process-output process)
514             (discard-input))))
515       ;; Remove all "\r"'s.
516       (goto-char (point-min))
517       (while (search-forward "\r\n" nil t)
518         (replace-match "\n"))
519       (copy-to-buffer tobuffer (point-min) (point-max)))))
520
521 (defalias 'elmo-pop3-msgdb-create 'elmo-pop3-msgdb-create-as-numlist)
522
523 (defun elmo-pop3-msgdb-create-as-numlist (spec numlist new-mark
524                                                already-mark seen-mark
525                                                important-mark seen-list
526                                                &optional msgdb)
527   (when numlist
528     (let ((process (elmo-network-session-process-internal
529                     (elmo-pop3-get-session spec)))
530           loc-alist)
531       (if elmo-pop3-use-uidl
532           (setq loc-alist (if msgdb (elmo-msgdb-get-location msgdb)
533                             (elmo-msgdb-location-load
534                              (elmo-msgdb-expand-path spec)))))
535       (elmo-pop3-msgdb-create-by-header process numlist
536                                         new-mark already-mark
537                                         seen-mark seen-list
538                                         loc-alist))))
539
540 (defun elmo-pop3-uidl-to-number (uidl)
541   (string-to-number (elmo-get-hash-val uidl
542                                        elmo-pop3-uidl-number-hash)))
543
544 (defun elmo-pop3-number-to-uidl (number)
545   (elmo-get-hash-val (format "#%d" number)
546                      elmo-pop3-number-uidl-hash))
547
548 (defun elmo-pop3-number-to-size (number)
549   (elmo-get-hash-val (format "#%d" number)
550                      elmo-pop3-size-hash))
551
552 (defun elmo-pop3-msgdb-create-by-header (process numlist
553                                                  new-mark already-mark
554                                                  seen-mark
555                                                  seen-list
556                                                  loc-alist)
557   (let ((tmp-buffer (get-buffer-create " *ELMO Overview TMP*")))
558     (with-current-buffer (process-buffer process)
559       (if loc-alist ; use uidl.
560           (setq numlist
561                 (delq
562                  nil
563                  (mapcar
564                   (lambda (number)
565                     (elmo-pop3-uidl-to-number (cdr (assq number loc-alist))))
566                   numlist))))
567       (elmo-pop3-retrieve-headers (process-buffer process)
568                                   tmp-buffer process numlist)
569       (prog1
570           (elmo-pop3-msgdb-create-message
571            tmp-buffer
572            process
573            (length numlist)
574            numlist
575            new-mark already-mark seen-mark seen-list loc-alist)
576         (kill-buffer tmp-buffer)))))
577
578 (defun elmo-pop3-msgdb-create-message (buffer
579                                        process
580                                        num
581                                        numlist new-mark already-mark
582                                        seen-mark
583                                        seen-list
584                                        loc-alist)
585   (save-excursion
586     (let (beg overview number-alist mark-alist
587               entity i number message-id gmark seen size)
588       (set-buffer buffer)
589       (elmo-set-buffer-multibyte default-enable-multibyte-characters)
590       (goto-char (point-min))
591       (setq i 0)
592       (message "Creating msgdb...")
593       (while (not (eobp))
594         (setq beg (save-excursion (forward-line 1) (point)))
595         (elmo-pop3-next-result-arrived-p)
596         (save-excursion
597           (forward-line -1)
598           (save-restriction
599             (narrow-to-region beg (point))
600             (setq entity
601                   (elmo-msgdb-create-overview-from-buffer
602                    (car numlist)))
603             (setq numlist (cdr numlist))
604             (when entity
605               (setq overview
606                     (elmo-msgdb-append-element
607                      overview entity))
608               (with-current-buffer (process-buffer process)
609                 (elmo-msgdb-overview-entity-set-size
610                  entity
611                  (string-to-number
612                   (elmo-pop3-number-to-size
613                    (elmo-msgdb-overview-entity-get-number entity))))
614                 (if (setq number
615                           (car
616                            (rassoc
617                             (elmo-pop3-number-to-uidl
618                              (elmo-msgdb-overview-entity-get-number entity))
619                             loc-alist)))
620                     (elmo-msgdb-overview-entity-set-number entity number)))
621               (setq number-alist
622                     (elmo-msgdb-number-add
623                      number-alist
624                      (elmo-msgdb-overview-entity-get-number entity)
625                      (car entity)))
626               (setq message-id (car entity))
627               (setq seen (member message-id seen-list))
628               (if (setq gmark (or (elmo-msgdb-global-mark-get message-id)
629                                   (if (elmo-cache-exists-p
630                                        message-id) ; XXX
631                                       (if seen
632                                           nil
633                                         already-mark)
634                                     (if seen
635                                         (if elmo-pop3-use-cache
636                                             seen-mark)
637                                       new-mark))))
638                   (setq mark-alist
639                         (elmo-msgdb-mark-append
640                          mark-alist
641                          (elmo-msgdb-overview-entity-get-number entity)
642                          gmark))))))
643         (when (> num elmo-display-progress-threshold)
644           (setq i (1+ i))
645           (if (or (zerop (% i 5)) (= i num))
646               (elmo-display-progress
647                'elmo-pop3-msgdb-create-message "Creating msgdb..."
648                (/ (* i 100) num)))))
649       (list overview number-alist mark-alist loc-alist))))
650
651 (defun elmo-pop3-read-body (process outbuf)
652   (with-current-buffer (process-buffer process)
653     (let ((start elmo-pop3-read-point)
654           end)
655       (goto-char start)
656       (while (not (re-search-forward "^\\.\r?\n" nil t))
657         (accept-process-output process)
658         (goto-char start))
659       (setq end (point))
660       (with-current-buffer outbuf
661         (erase-buffer)
662         (insert-buffer-substring (process-buffer process) start (- end 3))
663         (elmo-delete-cr-get-content-type)))))
664
665 (defun elmo-pop3-read-msg (spec number outbuf &optional msgdb)
666   (let* ((loc-alist (if elmo-pop3-use-uidl
667                         (if msgdb
668                             (elmo-msgdb-get-location msgdb)
669                           (elmo-msgdb-location-load
670                            (elmo-msgdb-expand-path spec)))))
671          (process (elmo-network-session-process-internal
672                    (elmo-pop3-get-session spec)))
673          response errmsg msg)
674     (with-current-buffer (process-buffer process)
675       (if loc-alist
676           (setq number (elmo-pop3-uidl-to-number
677                         (cdr (assq number loc-alist)))))
678       (when number
679         (elmo-pop3-send-command process
680                                 (format "retr %s" number))
681         (when (null (setq response (elmo-pop3-read-response
682                                     process t)))
683           (error "Fetching message failed"))
684         (setq response (elmo-pop3-read-body process outbuf))
685         (set-buffer outbuf)
686         (goto-char (point-min))
687         (while (re-search-forward "^\\." nil t)
688           (replace-match "")
689           (forward-line))
690         response))))
691
692 (defun elmo-pop3-delete-msg (process number loc-alist)
693   (with-current-buffer (process-buffer process)
694     (let (response errmsg msg)
695       (if loc-alist
696           (setq number (elmo-pop3-uidl-to-number
697                         (cdr (assq number loc-alist)))))
698       (if number
699           (progn
700             (elmo-pop3-send-command process
701                                     (format "dele %s" number))
702             (when (null (setq response (elmo-pop3-read-response
703                                         process t)))
704               (error "Deleting message failed")))
705         (error "Deleting message failed")))))
706
707 (defun elmo-pop3-delete-msgs (spec msgs &optional msgdb)
708   (let ((loc-alist (if elmo-pop3-use-uidl
709                        (if msgdb
710                            (elmo-msgdb-get-location msgdb)
711                          (elmo-msgdb-location-load
712                           (elmo-msgdb-expand-path spec)))))
713         (process (elmo-network-session-process-internal
714                   (elmo-pop3-get-session spec))))
715     (mapcar '(lambda (msg) (elmo-pop3-delete-msg
716                             process msg loc-alist))
717             msgs)))
718
719 (defun elmo-pop3-search (spec condition &optional numlist)
720   (error "Searching in pop3 folder is not implemented yet"))
721
722 (defun elmo-pop3-use-cache-p (spec number)
723   elmo-pop3-use-cache)
724
725 (defun elmo-pop3-local-file-p (spec number)
726   nil)
727
728 (defun elmo-pop3-port-label (spec)
729   (concat "pop3"
730           (if (elmo-pop3-spec-stream-type spec)
731               (concat "!" (symbol-name
732                            (elmo-network-stream-type-symbol
733                             (elmo-pop3-spec-stream-type spec)))))))
734
735 (defsubst elmo-pop3-portinfo (spec)
736   (list (elmo-pop3-spec-hostname spec)
737         (elmo-pop3-spec-port spec)))
738
739 (defun elmo-pop3-plugged-p (spec)
740   (apply 'elmo-plugged-p
741          (append (elmo-pop3-portinfo spec)
742                  (list nil (quote (elmo-pop3-port-label spec))))))
743
744 (defun elmo-pop3-set-plugged (spec plugged add)
745   (apply 'elmo-set-plugged plugged
746          (append (elmo-pop3-portinfo spec)
747                  (list nil nil (quote (elmo-pop3-port-label spec)) add))))
748
749 (defalias 'elmo-pop3-sync-number-alist
750   'elmo-generic-sync-number-alist)
751 (defalias 'elmo-pop3-list-folder-unread
752   'elmo-generic-list-folder-unread)
753 (defalias 'elmo-pop3-list-folder-important
754   'elmo-generic-list-folder-important)
755 (defalias 'elmo-pop3-folder-diff 'elmo-generic-folder-diff)
756
757 (defun elmo-pop3-commit (spec)
758   (if (elmo-pop3-plugged-p spec)
759       (let ((session (elmo-pop3-get-session spec 'if-exists)))
760         (and session
761              (elmo-network-close-session session)))))
762        
763
764 (require 'product)
765 (product-provide (provide 'elmo-pop3) (require 'elmo-version))
766
767 ;;; elmo-pop3.el ends here