* Version number is increased to 2.5.6.
[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            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-sort-overview-by-original-number (overview loc-alist)
524   (sort overview
525         (lambda (ent1 ent2)
526           (< (elmo-pop3-uidl-to-number
527               (cdr (assq (elmo-msgdb-overview-entity-get-number ent1)
528                          loc-alist)))
529              (elmo-pop3-uidl-to-number
530               (cdr (assq (elmo-msgdb-overview-entity-get-number ent2)
531                          loc-alist)))))))
532
533 (defun elmo-pop3-sort-msgdb-by-original-number (msgdb)
534   (message "Sorting...")
535   (let ((overview (elmo-msgdb-get-overview msgdb)))
536     (setq overview (elmo-pop3-sort-overview-by-original-number
537                     overview
538                     (elmo-msgdb-get-location msgdb)))
539     (message "Sorting...done")
540     (list overview (nth 1 msgdb)(nth 2 msgdb)(nth 3 msgdb)(nth 4 msgdb))))
541
542 (defun elmo-pop3-msgdb-create-as-numlist (spec numlist new-mark
543                                                already-mark seen-mark
544                                                important-mark seen-list
545                                                &optional msgdb)
546   (when numlist
547     (let ((process (elmo-network-session-process-internal
548                     (elmo-pop3-get-session spec)))
549           loc-alist)
550       (if elmo-pop3-use-uidl
551           (setq loc-alist (if msgdb (elmo-msgdb-get-location msgdb)
552                             (elmo-msgdb-location-load
553                              (elmo-msgdb-expand-path spec)))))
554       (with-current-buffer (process-buffer process)
555         (elmo-pop3-sort-msgdb-by-original-number
556          (elmo-pop3-msgdb-create-by-header process numlist
557                                            new-mark already-mark
558                                            seen-mark seen-list
559                                            loc-alist))))))
560
561 (defun elmo-pop3-uidl-to-number (uidl)
562   (string-to-number (elmo-get-hash-val uidl
563                                        elmo-pop3-uidl-number-hash)))
564
565 (defun elmo-pop3-number-to-uidl (number)
566   (elmo-get-hash-val (format "#%d" number)
567                      elmo-pop3-number-uidl-hash))
568
569 (defun elmo-pop3-number-to-size (number)
570   (elmo-get-hash-val (format "#%d" number)
571                      elmo-pop3-size-hash))
572
573 (defun elmo-pop3-msgdb-create-by-header (process numlist
574                                                  new-mark already-mark
575                                                  seen-mark
576                                                  seen-list
577                                                  loc-alist)
578   (let ((tmp-buffer (get-buffer-create " *ELMO Overview TMP*")))
579     (with-current-buffer (process-buffer process)
580       (if loc-alist ; use uidl.
581           (setq numlist
582                 (delq
583                  nil
584                  (mapcar
585                   (lambda (number)
586                     (elmo-pop3-uidl-to-number (cdr (assq number loc-alist))))
587                   numlist))))
588       (elmo-pop3-retrieve-headers (process-buffer process)
589                                   tmp-buffer process numlist)
590       (prog1
591           (elmo-pop3-msgdb-create-message
592            tmp-buffer
593            process
594            (length numlist)
595            numlist
596            new-mark already-mark seen-mark seen-list loc-alist)
597         (kill-buffer tmp-buffer)))))
598
599 (defun elmo-pop3-msgdb-create-message (buffer
600                                        process
601                                        num
602                                        numlist new-mark already-mark
603                                        seen-mark
604                                        seen-list
605                                        loc-alist)
606   (save-excursion
607     (let (beg overview number-alist mark-alist
608               entity i number message-id gmark seen size)
609       (set-buffer buffer)
610       (elmo-set-buffer-multibyte default-enable-multibyte-characters)
611       (goto-char (point-min))
612       (setq i 0)
613       (message "Creating msgdb...")
614       (while (not (eobp))
615         (setq beg (save-excursion (forward-line 1) (point)))
616         (elmo-pop3-next-result-arrived-p)
617         (save-excursion
618           (forward-line -1)
619           (save-restriction
620             (narrow-to-region beg (point))
621             (setq entity
622                   (elmo-msgdb-create-overview-from-buffer
623                    (car numlist)))
624             (setq numlist (cdr numlist))
625             (when entity
626               (setq overview
627                     (elmo-msgdb-append-element
628                      overview entity))
629               (with-current-buffer (process-buffer process)
630                 (elmo-msgdb-overview-entity-set-size
631                  entity
632                  (string-to-number
633                   (elmo-pop3-number-to-size
634                    (elmo-msgdb-overview-entity-get-number entity))))
635                 (if (setq number
636                           (car
637                            (rassoc
638                             (elmo-pop3-number-to-uidl
639                              (elmo-msgdb-overview-entity-get-number entity))
640                             loc-alist)))
641                     (elmo-msgdb-overview-entity-set-number entity number)))
642               (setq number-alist
643                     (elmo-msgdb-number-add
644                      number-alist
645                      (elmo-msgdb-overview-entity-get-number entity)
646                      (car entity)))
647               (setq message-id (car entity))
648               (setq seen (member message-id seen-list))
649               (if (setq gmark (or (elmo-msgdb-global-mark-get message-id)
650                                   (if (elmo-cache-exists-p
651                                        message-id) ; XXX
652                                       (if seen
653                                           nil
654                                         already-mark)
655                                     (if seen
656                                         (if elmo-pop3-use-cache
657                                             seen-mark)
658                                       new-mark))))
659                   (setq mark-alist
660                         (elmo-msgdb-mark-append
661                          mark-alist
662                          (elmo-msgdb-overview-entity-get-number entity)
663                          gmark))))))
664         (when (> num elmo-display-progress-threshold)
665           (setq i (1+ i))
666           (if (or (zerop (% i 5)) (= i num))
667               (elmo-display-progress
668                'elmo-pop3-msgdb-create-message "Creating msgdb..."
669                (/ (* i 100) num)))))
670       (list overview number-alist mark-alist loc-alist))))
671
672 (defun elmo-pop3-read-body (process outbuf)
673   (with-current-buffer (process-buffer process)
674     (let ((start elmo-pop3-read-point)
675           end)
676       (goto-char start)
677       (while (not (re-search-forward "^\\.\r?\n" nil t))
678         (accept-process-output process)
679         (goto-char start))
680       (setq end (point))
681       (with-current-buffer outbuf
682         (erase-buffer)
683         (insert-buffer-substring (process-buffer process) start (- end 3))
684         (elmo-delete-cr-get-content-type)))))
685
686 (defun elmo-pop3-read-msg (spec number outbuf &optional msgdb)
687   (let* ((loc-alist (if elmo-pop3-use-uidl
688                         (if msgdb
689                             (elmo-msgdb-get-location msgdb)
690                           (elmo-msgdb-location-load
691                            (elmo-msgdb-expand-path spec)))))
692          (process (elmo-network-session-process-internal
693                    (elmo-pop3-get-session spec)))
694          response errmsg msg)
695     (with-current-buffer (process-buffer process)
696       (if loc-alist
697           (setq number (elmo-pop3-uidl-to-number
698                         (cdr (assq number loc-alist)))))
699       (when number
700         (elmo-pop3-send-command process
701                                 (format "retr %s" number))
702         (when (null (setq response (elmo-pop3-read-response
703                                     process t)))
704           (error "Fetching message failed"))
705         (setq response (elmo-pop3-read-body process outbuf))
706         (set-buffer outbuf)
707         (goto-char (point-min))
708         (while (re-search-forward "^\\." nil t)
709           (replace-match "")
710           (forward-line))
711         response))))
712
713 (defun elmo-pop3-delete-msg (process number loc-alist)
714   (with-current-buffer (process-buffer process)
715     (let (response errmsg msg)
716       (if loc-alist
717           (setq number (elmo-pop3-uidl-to-number
718                         (cdr (assq number loc-alist)))))
719       (if number
720           (progn
721             (elmo-pop3-send-command process
722                                     (format "dele %s" number))
723             (when (null (setq response (elmo-pop3-read-response
724                                         process t)))
725               (error "Deleting message failed")))
726         (error "Deleting message failed")))))
727
728 (defun elmo-pop3-delete-msgs (spec msgs &optional msgdb)
729   (let ((loc-alist (if elmo-pop3-use-uidl
730                        (if msgdb
731                            (elmo-msgdb-get-location msgdb)
732                          (elmo-msgdb-location-load
733                           (elmo-msgdb-expand-path spec)))))
734         (process (elmo-network-session-process-internal
735                   (elmo-pop3-get-session spec))))
736     (mapcar '(lambda (msg) (elmo-pop3-delete-msg
737                             process msg loc-alist))
738             msgs)))
739
740 (defun elmo-pop3-search (spec condition &optional numlist)
741   (error "Searching in pop3 folder is not implemented yet"))
742
743 (defun elmo-pop3-use-cache-p (spec number)
744   elmo-pop3-use-cache)
745
746 (defun elmo-pop3-local-file-p (spec number)
747   nil)
748
749 (defun elmo-pop3-port-label (spec)
750   (concat "pop3"
751           (if (elmo-pop3-spec-stream-type spec)
752               (concat "!" (symbol-name
753                            (elmo-network-stream-type-symbol
754                             (elmo-pop3-spec-stream-type spec)))))))
755
756 (defsubst elmo-pop3-portinfo (spec)
757   (list (elmo-pop3-spec-hostname spec)
758         (elmo-pop3-spec-port spec)))
759
760 (defun elmo-pop3-plugged-p (spec)
761   (apply 'elmo-plugged-p
762          (append (elmo-pop3-portinfo spec)
763                  (list nil (quote (elmo-pop3-port-label spec))))))
764
765 (defun elmo-pop3-set-plugged (spec plugged add)
766   (apply 'elmo-set-plugged plugged
767          (append (elmo-pop3-portinfo spec)
768                  (list nil nil (quote (elmo-pop3-port-label spec)) add))))
769
770 (defalias 'elmo-pop3-sync-number-alist
771   'elmo-generic-sync-number-alist)
772 (defalias 'elmo-pop3-list-folder-unread
773   'elmo-generic-list-folder-unread)
774 (defalias 'elmo-pop3-list-folder-important
775   'elmo-generic-list-folder-important)
776 (defalias 'elmo-pop3-folder-diff 'elmo-generic-folder-diff)
777
778 (defun elmo-pop3-commit (spec)
779   (if (elmo-pop3-plugged-p spec)
780       (let ((session (elmo-pop3-get-session spec 'if-exists)))
781         (and session
782              (elmo-network-close-session session)))))
783        
784
785 (require 'product)
786 (product-provide (provide 'elmo-pop3) (require 'elmo-version))
787
788 ;;; elmo-pop3.el ends here