fix capability
[elisp/wanderlust.git] / elmo / elmo-pop3.el
1 ;;; elmo-pop3.el -- POP3 Interface for ELMO.
2
3 ;; Copyright 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
7
8 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
9
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14 ;;
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19 ;;
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24 ;;
25
26 ;;; Commentary:
27 ;; 
28
29 ;;; Code:
30 ;; 
31
32 (require 'elmo-msgdb)
33 (require 'elmo-net)
34 (eval-when-compile
35   (require 'elmo-util)
36   (condition-case nil
37       (progn
38         (require 'starttls)
39         (require 'sasl))
40     (error))
41   (defun-maybe md5 (a))
42   (defun-maybe sasl-digest-md5-digest-response
43     (digest-challenge username passwd serv-type host &optional realm))
44   (defun-maybe sasl-scram-md5-client-msg-1
45     (authenticate-id &optional authorize-id))
46   (defun-maybe sasl-scram-md5-client-msg-2
47     (server-msg-1 client-msg-1 salted-pass))
48   (defun-maybe sasl-scram-md5-make-salted-pass
49     (server-msg-1 passphrase))
50   (defun-maybe sasl-cram-md5 (username passphrase challenge))
51   (defun-maybe sasl-scram-md5-authenticate-server
52     (server-msg-1 server-msg-2 client-msg-1 salted-pass))
53   (defun-maybe starttls-negotiate (a)))
54 (condition-case nil
55     (progn
56       (require 'sasl))
57   (error))
58
59 (defvar elmo-pop3-use-uidl t
60   "*If non-nil, use UIDL.")
61
62 (defvar elmo-pop3-exists-exactly t)
63
64 (defvar elmo-pop3-authenticator-alist
65   '((user        elmo-pop3-auth-user)
66     (apop        elmo-pop3-auth-apop)
67     (cram-md5    elmo-pop3-auth-cram-md5)
68     (scram-md5   elmo-pop3-auth-scram-md5)
69     (digest-md5  elmo-pop3-auth-digest-md5))
70   "Definition of authenticators.")
71
72 (eval-and-compile
73   (luna-define-class elmo-pop3-session (elmo-network-session) ()))
74
75 ;; buffer-local
76 (defvar elmo-pop3-read-point nil)
77 (defvar elmo-pop3-number-uidl-hash nil) ; number -> uidl
78 (defvar elmo-pop3-uidl-number-hash nil) ; uidl -> number
79 (defvar elmo-pop3-size-hash nil) ; number -> size
80 (defvar elmo-pop3-uidl-done nil)
81 (defvar elmo-pop3-list-done nil)
82
83 (defvar elmo-pop3-local-variables '(elmo-pop3-read-point
84                                     elmo-pop3-uidl-number-hash
85                                     elmo-pop3-number-uidl-hash
86                                     elmo-pop3-uidl-done
87                                     elmo-pop3-size-hash
88                                     elmo-pop3-list-done))
89
90 (luna-define-method elmo-network-close-session ((session elmo-pop3-session))
91   (unless (memq (process-status
92                  (elmo-network-session-process-internal session))
93                 '(closed exit))
94     (elmo-pop3-send-command (elmo-network-session-process-internal session)
95                             "quit")
96     (or (elmo-pop3-read-response
97          (elmo-network-session-process-internal session) t)
98         (error "POP error: QUIT failed")))
99   (kill-buffer (process-buffer
100                 (elmo-network-session-process-internal session)))
101   (delete-process (elmo-network-session-process-internal session)))
102
103 (defun elmo-pop3-get-session (spec &optional if-exists)
104   (elmo-network-get-session
105    'elmo-pop3-session
106    "POP3"
107    (elmo-pop3-spec-hostname spec)
108    (elmo-pop3-spec-port spec)
109    (elmo-pop3-spec-username spec)
110    (elmo-pop3-spec-auth spec)
111    (elmo-pop3-spec-stream-type spec)
112    if-exists))
113
114 (defun elmo-pop3-send-command (process command &optional no-erase)
115   (with-current-buffer (process-buffer process)
116     (unless no-erase
117       (erase-buffer))
118     (goto-char (point-min))
119     (setq elmo-pop3-read-point (point))
120     (process-send-string process command)
121     (process-send-string process "\r\n")))
122
123 (defun elmo-pop3-read-response (process &optional not-command)
124   (with-current-buffer (process-buffer process)
125     (let ((case-fold-search nil)
126           (response-string nil)
127           (response-continue t)
128           (return-value nil)
129           match-end)
130       (while response-continue
131         (goto-char elmo-pop3-read-point)
132         (while (not (re-search-forward "\r?\n" nil t))
133           (accept-process-output process)
134           (goto-char elmo-pop3-read-point))
135         (setq match-end (point))
136         (setq response-string
137               (buffer-substring elmo-pop3-read-point (- match-end 2)))
138         (goto-char elmo-pop3-read-point)
139         (if (looking-at "\\+.*$")
140             (progn
141               (setq response-continue nil)
142               (setq elmo-pop3-read-point match-end)
143               (setq return-value
144                     (if return-value
145                         (concat return-value "\n" response-string)
146                       response-string)))
147           (if (looking-at "\\-.*$")
148               (progn
149                 (setq response-continue nil)
150                 (setq elmo-pop3-read-point match-end)
151                 (setq return-value nil))
152             (setq elmo-pop3-read-point match-end)
153             (if not-command
154                 (setq response-continue nil))
155             (setq return-value
156                   (if return-value
157                       (concat return-value "\n" response-string)
158                     response-string)))
159           (setq elmo-pop3-read-point match-end)))
160       return-value)))
161
162 (defun elmo-pop3-process-filter (process output)
163   (save-excursion
164     (set-buffer (process-buffer process))
165     (goto-char (point-max))
166     (insert output)))
167
168 (defun elmo-pop3-auth-user (session)
169   (let ((process (elmo-network-session-process-internal session)))
170     ;; try USER/PASS
171     (elmo-pop3-send-command
172      process
173      (format "user %s" (elmo-network-session-user-internal session)))
174     (or (elmo-pop3-read-response process t)
175         (signal 'elmo-authenticate-error
176                 '(elmo-pop-auth-user)))
177     (elmo-pop3-send-command  process
178                              (format
179                               "pass %s"
180                               (elmo-get-passwd
181                                (elmo-network-session-password-key session))))
182     (or (elmo-pop3-read-response process t)
183         (signal 'elmo-authenticate-error
184                 '(elmo-pop-auth-user)))))
185
186 (defun elmo-pop3-auth-apop (session)
187   (if (string-match "^\+OK .*\\(<[^\>]+>\\)"
188                     (elmo-network-session-greeting-internal session))
189       ;; good, APOP ready server
190       (progn
191         (require 'md5)
192         (elmo-pop3-send-command
193          (elmo-network-session-process-internal session)
194          (format "apop %s %s"
195                  (elmo-network-session-user-internal session)
196                  (md5
197                   (concat (match-string
198                            1
199                            (elmo-network-session-greeting-internal session))
200                           (elmo-get-passwd
201                            (elmo-network-session-password-key session))))))
202         (or (elmo-pop3-read-response
203              (elmo-network-session-process-internal session)
204              t)
205             (signal 'elmo-authenticate-error
206                     '(elmo-pop3-auth-apop))))
207     (signal 'elmo-open-error '(elmo-pop-auth-user))))
208     
209 (defun elmo-pop3-auth-cram-md5 (session)
210   (let ((process (elmo-network-session-process-internal session))
211         response)
212     (elmo-pop3-send-command  process "auth cram-md5")
213     (or (setq response
214               (elmo-pop3-read-response process t))
215         (signal 'elmo-open-error '(elmo-pop-auth-cram-md5)))
216     (elmo-pop3-send-command
217      process
218      (elmo-base64-encode-string
219       (sasl-cram-md5 (elmo-network-session-user-internal session)
220                      (elmo-get-passwd
221                       (elmo-network-session-password-key session))
222                      (elmo-base64-decode-string
223                       (cadr (split-string response " "))))))
224     (or (elmo-pop3-read-response process t)
225         (signal 'elmo-authenticate-error
226                 '(elmo-pop-auth-cram-md5)))))
227
228 (defun elmo-pop3-auth-scram-md5 (session)
229   (let ((process (elmo-network-session-process-internal session))
230         server-msg-1 server-msg-2 client-msg-1 client-msg-2
231         salted-pass response)
232     (elmo-pop3-send-command
233      process
234      (format "auth scram-md5 %s"
235              (elmo-base64-encode-string
236               (setq client-msg-1
237                     (sasl-scram-md5-client-msg-1
238                      (elmo-network-session-user-internal session))))))
239     (or (elmo-pop3-read-response process t)
240         (signal 'elmo-open-error '(elmo-pop-auth-scram-md5)))
241     (setq server-msg-1
242           (elmo-base64-decode-string (cadr (split-string response " "))))
243     (elmo-pop3-send-command
244      process
245      (elmo-base64-encode-string
246       (sasl-scram-md5-client-msg-2
247        server-msg-1
248        client-msg-1
249        (setq salted-pass
250              (sasl-scram-md5-make-salted-pass
251               server-msg-1
252               (elmo-get-passwd
253                (elmo-network-session-password-key session)))))))
254     (or (setq response (elmo-pop3-read-response process t))
255         (signal 'elmo-authenticate-error
256                 '(elmo-pop-auth-scram-md5)))
257     (setq server-msg-2 (elmo-base64-decode-string
258                         (cadr (split-string response " "))))
259     (or (sasl-scram-md5-authenticate-server server-msg-1
260                                             server-msg-2
261                                             client-msg-1
262                                             salted-pass)
263         (signal 'elmo-authenticate-error
264                 '(elmo-pop-auth-scram-md5)))
265     (elmo-pop3-send-command process "")
266     (or (setq response (elmo-pop3-read-response process t))
267         (signal 'elmo-authenticate-error
268                 '(elmo-pop-auth-scram-md5)))))
269
270 (defun elmo-pop3-auth-digest-md5 (session)
271   (let ((process (elmo-network-session-process-internal session))
272         response)
273     (elmo-pop3-send-command process "auth digest-md5")
274     (or (setq response
275               (elmo-pop3-read-response process t))
276         (signal 'elmo-open-error
277                 '(elmo-pop-auth-digest-md5)))
278     (elmo-pop3-send-command
279      process
280      (elmo-base64-encode-string
281       (sasl-digest-md5-digest-response
282        (elmo-base64-decode-string
283         (cadr (split-string response " ")))
284        (elmo-network-session-user-internal session)
285        (elmo-get-passwd
286         (elmo-network-session-password-key session))
287        "pop"
288        (elmo-network-session-host-internal session))
289       'no-line-break))
290     (or (elmo-pop3-read-response process t)
291         (signal 'elmo-authenticate-error
292                 '(elmo-pop-auth-digest-md5)))
293     (elmo-pop3-send-command process "")
294     (or (elmo-pop3-read-response process t)
295         (signal 'elmo-open-error
296                 '(elmo-pop-auth-digest-md5)))))
297
298 (luna-define-method elmo-network-initialize-session-buffer :after
299   ((session elmo-pop3-session) buffer)
300   (with-current-buffer buffer
301     (mapcar 'make-variable-buffer-local elmo-pop3-local-variables)))
302
303 (luna-define-method elmo-network-initialize-session ((session
304                                                       elmo-pop3-session))
305   (let ((process (elmo-network-session-process-internal session))
306         response mechanism)
307     (with-current-buffer (process-buffer process)
308       (set-process-filter process 'elmo-pop3-process-filter)
309       (setq elmo-pop3-read-point (point-min))
310       (or (elmo-network-session-set-greeting-internal
311            session
312            (elmo-pop3-read-response process t))
313           (signal 'elmo-open-error
314                   '(elmo-network-intialize-session)))
315       (when (eq (elmo-network-stream-type-symbol
316                  (elmo-network-session-stream-type-internal session))
317                 'starttls)
318         (elmo-pop3-send-command process "stls")
319         (if (string-match "^\+OK"
320                           (elmo-pop3-read-response process))
321             (starttls-negotiate process)
322           (signal 'elmo-open-error
323                   '(elmo-pop3-starttls-error)))))))
324
325 (luna-define-method elmo-network-authenticate-session ((session
326                                                         elmo-pop3-session))
327   (let (authenticator)
328     ;; defaults to 'user.
329     (unless (elmo-network-session-auth-internal session)
330       (elmo-network-session-set-auth-internal session 'user))
331     (setq authenticator
332           (nth 1 (assq (elmo-network-session-auth-internal session)
333                        elmo-pop3-authenticator-alist)))
334     (unless authenticator (error "There's no authenticator for %s"
335                                  (elmo-network-session-auth-internal session)))
336     (funcall authenticator session)))
337
338 (luna-define-method elmo-network-setup-session ((session
339                                                  elmo-pop3-session))
340   (let ((process (elmo-network-session-process-internal session))
341         response)
342     (with-current-buffer (process-buffer process)
343       (setq elmo-pop3-size-hash (make-vector 31 0))
344       ;; To get obarray of uidl and size
345       (elmo-pop3-send-command process "list")
346       (if (null (elmo-pop3-read-response process))
347           (error "POP List folder failed"))
348       (if (null (setq response
349                       (elmo-pop3-read-contents
350                        (current-buffer) process)))
351           (error "POP List folder failed"))
352       ;; POP server always returns a sequence of serial numbers.
353       (elmo-pop3-parse-list-response response)
354       ;; UIDL
355       (when elmo-pop3-use-uidl
356         (setq elmo-pop3-uidl-number-hash (make-vector 31 0))
357         (setq elmo-pop3-number-uidl-hash (make-vector 31 0))
358         ;; UIDL
359         (elmo-pop3-send-command process "uidl")
360         (unless (elmo-pop3-read-response process)
361           (error "UIDL failed"))
362         (unless (setq response (elmo-pop3-read-contents
363                                 (current-buffer) process))
364           (error "UIDL failed"))
365         (elmo-pop3-parse-uidl-response response)))))
366
367 (defun elmo-pop3-read-contents (buffer process)
368   (save-excursion
369     (set-buffer buffer)
370     (let ((case-fold-search nil)
371           match-end)
372       (goto-char elmo-pop3-read-point)
373       (while (not (re-search-forward "^\\.\r\n" nil t))
374         (accept-process-output process)
375         (goto-char elmo-pop3-read-point))
376       (setq match-end (point))
377       (elmo-delete-cr
378        (buffer-substring elmo-pop3-read-point
379                          (- match-end 3))))))
380
381 ;; dummy functions
382 (defun elmo-pop3-list-folders (spec &optional hierarchy) nil)
383 (defun elmo-pop3-append-msg (spec string) nil nil)
384 (defun elmo-pop3-folder-creatable-p (spec) nil)
385 (defun elmo-pop3-create-folder (spec) nil)
386
387 (defun elmo-pop3-folder-exists-p (spec)
388   (if (and elmo-pop3-exists-exactly
389            (elmo-pop3-plugged-p spec))
390       (save-excursion
391         (let (elmo-auto-change-plugged ; don't change plug status.
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