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