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