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