(elmo-progress-start): Avoid updating `elmo-progress-counter' when a query
[elisp/wanderlust.git] / elmo / elmo-net.el
1 ;;; elmo-net.el --- Network module for ELMO.
2
3 ;; Copyright (C) 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 (eval-when-compile (require 'cl))
30
31 (require 'elmo-util)
32 (require 'elmo-dop)
33 (require 'elmo-vars)
34 (require 'elmo-cache)
35 (require 'elmo)
36
37 ;;; Code:
38 ;;
39
40 (defconst elmo-net-folder-name-syntax '((?@ [server ".+"])
41                                         (?: [port "^[0-9]+$"])
42                                         (?! stream-type)))
43
44 ;;; ELMO net folder
45 (eval-and-compile
46   (luna-define-class elmo-net-folder
47                      (elmo-folder)
48                      (user auth server port stream-type))
49   (luna-define-internal-accessors 'elmo-net-folder))
50
51 ;;; Session
52 (eval-and-compile
53   (autoload 'starttls-negotiate "starttls")
54   (autoload 'sasl-find-mechanism "sasl")
55   (autoload 'sasl-make-client "sasl")
56   (autoload 'sasl-mechanism-name "sasl")
57   (autoload 'sasl-next-step "sasl")
58   (autoload 'sasl-step-data "sasl")
59   (autoload 'sasl-step-set-data "sasl"))
60
61 (defvar sasl-mechanisms)
62
63 (defcustom elmo-network-session-idle-timeout nil
64   "Idle timeout of the network cache. Specified in seconds.
65 If elapsed time since last access is larger than this value,
66 cached session is not reused.
67 If nil, network cache is reused."
68   :type '(choice number (const nil))
69   :group 'elmo)
70
71 (defcustom elmo-network-session-retry-count nil
72   "Retry count for authentication when open network session.
73 If nil, just once. If t, until success."
74   :type '(choice (integer :tag "Times")
75                  (const :tag "Just once" nil)
76                  (const :tag "Until success" t))
77   :group 'elmo)
78
79 ;;; Code:
80 ;;
81 (eval-and-compile
82   (luna-define-class elmo-network-session () (name
83                                               server
84                                               port
85                                               user
86                                               auth
87                                               stream-type
88                                               process
89                                               greeting
90                                               last-accessed))
91   (luna-define-internal-accessors 'elmo-network-session))
92
93 (luna-define-generic elmo-network-initialize-session (session)
94   "Initialize SESSION (Called before authentication).")
95
96 (luna-define-generic elmo-network-initialize-session-buffer (session buffer)
97   "Initialize SESSION's BUFFER.")
98
99 (luna-define-generic elmo-network-authenticate-session (session)
100   "Authenticate SESSION.")
101
102 (luna-define-generic elmo-network-setup-session (session)
103   "Setup SESSION. (Called after authentication).")
104
105 (luna-define-generic elmo-network-close-session (session)
106   "Close SESSION.")
107
108 (luna-define-method
109   elmo-network-initialize-session-buffer ((session
110                                            elmo-network-session) buffer)
111   (with-current-buffer buffer
112     (set-buffer-multibyte nil)
113     (buffer-disable-undo (current-buffer))))
114
115 (luna-define-method elmo-network-close-session ((session elmo-network-session))
116   (when (elmo-network-session-process-internal session)
117 ;;; (memq (process-status (elmo-network-session-process-internal session))
118 ;;;       '(open run))
119     (kill-buffer (process-buffer
120                   (elmo-network-session-process-internal session)))
121     (delete-process (elmo-network-session-process-internal session))))
122
123 (defmacro elmo-network-stream-type-spec-string (stream-type)
124   (` (nth 0 (, stream-type))))
125
126 (defmacro elmo-network-stream-type-symbol (stream-type)
127   (` (nth 1 (, stream-type))))
128
129 (defmacro elmo-network-stream-type-feature (stream-type)
130   (` (nth 2 (, stream-type))))
131
132 (defmacro elmo-network-stream-type-function (stream-type)
133   (` (nth 3 (, stream-type))))
134
135 (defsubst elmo-network-session-password-key (session)
136   (format "%s:%s/%s@%s:%d"
137           (upcase
138            (nth 1 (split-string (symbol-name
139                                  (luna-class-name session)) "[4-]")))
140           (elmo-network-session-user-internal session)
141           (elmo-network-session-auth-internal session)
142           (elmo-network-session-server-internal session)
143           (elmo-network-session-port-internal session)))
144
145 (defvar elmo-network-session-cache nil)
146
147 (defsubst elmo-network-session-cache-key (name folder)
148   "Returns session cache key for NAME and FOLDER."
149   (format "%s:%s/%s@%s:%d%s"
150           name
151           (elmo-net-folder-user-internal folder)
152           (elmo-net-folder-auth-internal folder)
153           (elmo-net-folder-server-internal folder)
154           (elmo-net-folder-port-internal folder)
155           (or
156            (elmo-network-stream-type-spec-string
157             (elmo-net-folder-stream-type-internal folder)) "")))
158
159 (defun elmo-network-clear-session-cache ()
160   "Clear session cache."
161   (interactive)
162   (dolist (pair elmo-network-session-cache)
163     (elmo-network-close-session (cdr pair)))
164   (setq elmo-network-session-cache nil))
165
166 (defsubst elmo-network-session-buffer-name (session)
167   (format " *%s session for %s@%s:%d%s"
168           (elmo-network-session-name-internal session)
169           (elmo-network-session-user-internal session)
170           (elmo-network-session-server-internal session)
171           (elmo-network-session-port-internal session)
172           (or (elmo-network-stream-type-spec-string
173                (elmo-network-session-stream-type-internal session))
174               "")))
175
176 (defmacro elmo-network-session-buffer (session)
177   "Get buffer for SESSION."
178   (` (process-buffer (elmo-network-session-process-internal
179                       (, session)))))
180
181 (defun elmo-network-get-session (class name folder &optional if-exists)
182   "Get network session from session cache or a new network session.
183 CLASS is the class name of the session.
184 NAME is the name of the process.
185 FOLDER is the ELMO folder structure.
186 Returns a `elmo-network-session' instance.
187 If optional argument IF-EXISTS is non-nil, it does not return session
188 if there is no session cache.
189 if making session failed, returns nil."
190   (let (pair session key)
191     (if (not (elmo-plugged-p
192               (elmo-net-folder-server-internal folder)
193               (elmo-net-folder-port-internal folder)
194               (elmo-network-stream-type-symbol
195                (elmo-net-folder-stream-type-internal folder))))
196         (error "Unplugged"))
197     (setq pair (assoc (setq key (elmo-network-session-cache-key name folder))
198                       elmo-network-session-cache))
199     (when (and pair
200                (or (not (memq (process-status
201                                (elmo-network-session-process-internal
202                                 (cdr pair)))
203                               '(open run)))
204                    (and elmo-network-session-idle-timeout
205                         (elmo-network-session-last-accessed-internal
206                          (cdr pair))
207                         (elmo-time-expire
208                          (elmo-network-session-last-accessed-internal
209                           (cdr pair))
210                          elmo-network-session-idle-timeout))))
211       (setq elmo-network-session-cache
212             (delq pair elmo-network-session-cache))
213       (elmo-network-close-session (cdr pair))
214       (setq pair nil))
215     (if pair
216         (progn
217           (elmo-network-session-set-last-accessed-internal
218            (cdr pair) (current-time))
219           (cdr pair))                   ; connection cache exists.
220       (unless if-exists
221         (setq session
222               (elmo-network-open-session
223                class
224                name
225                (elmo-net-folder-server-internal folder)
226                (elmo-net-folder-port-internal folder)
227                (elmo-net-folder-user-internal folder)
228                (elmo-net-folder-auth-internal folder)
229                (elmo-net-folder-stream-type-internal folder)))
230         (setq elmo-network-session-cache
231               (cons (cons key session)
232                     elmo-network-session-cache))
233         session))))
234
235 (defun elmo-network-session-buffer-create (session)
236   (let ((buffer-name (elmo-network-session-buffer-name session))
237         buffer)
238     (when (get-buffer buffer-name)
239       (kill-buffer buffer-name))
240     (setq buffer (get-buffer-create buffer-name))
241     (elmo-network-initialize-session-buffer session buffer)
242     buffer))
243
244 (defun elmo-network-open-session (class name server port user auth
245                                         stream-type)
246   "Open an authenticated network session.
247 CLASS is the class name of the session.
248 NAME is the name of the process.
249 SERVER is the name of the server server.
250 PORT is the port number of the service.
251 USER is the user-id for the authenticate.
252 AUTH is the authenticate method name (symbol).
253 STREAM-TYPE is the stream type (See also `elmo-network-stream-type-alist').
254 Returns a process object.  if making session failed, returns nil."
255   (let ((session
256          (luna-make-entity class
257                            :name name
258                            :server server
259                            :port port
260                            :user user
261                            :auth auth
262                            :stream-type stream-type
263                            :process nil
264                            :greeting nil
265                            :last-accessed (current-time)))
266         (retry elmo-network-session-retry-count)
267         success)
268     (while (not success)
269       (condition-case error
270           (when (elmo-network-session-set-process-internal
271                  session
272                  (elmo-open-network-stream
273                   (elmo-network-session-name-internal session)
274                   (elmo-network-session-buffer-create session)
275                   server port stream-type))
276             (elmo-network-initialize-session session)
277             (elmo-network-authenticate-session session)
278             (elmo-network-setup-session session)
279             (setq success t))
280         (elmo-authenticate-error
281          (elmo-remove-passwd (elmo-network-session-password-key session))
282          (message "Authetication is failed")
283          (sit-for 1)
284          (elmo-network-close-session session)
285          (unless (if (numberp retry)
286                      (> (setq retry (1- retry)) 0)
287                    retry)
288            (signal (car error) (cdr error))))
289         (elmo-open-error
290          (elmo-set-plugged nil server port
291                            (elmo-network-stream-type-symbol stream-type)
292                            (current-time))
293          (message "Auto plugged off at %s:%d :%s" server port (cadr error))
294          (sit-for 1)
295          (elmo-network-close-session session)
296          (signal (car error) (cdr error)))
297         (error
298          (elmo-network-close-session session)
299          (signal (car error) (cdr error)))))
300     session))
301
302 (defun elmo-open-network-stream (name buffer server service stream-type)
303   (let ((auto-plugged (and elmo-auto-change-plugged
304                            (> elmo-auto-change-plugged 0)))
305         process)
306     (if (and stream-type
307              (elmo-network-stream-type-feature stream-type))
308         (require (elmo-network-stream-type-feature stream-type)))
309     (condition-case err
310         (let (process-connection-type)
311           (as-binary-process
312            (setq process
313                  (if stream-type
314                      (funcall (elmo-network-stream-type-function stream-type)
315                               name buffer server service)
316                    (open-network-stream name buffer server service)))))
317       (error
318        (when auto-plugged
319          (elmo-set-plugged nil server service
320                            (elmo-network-stream-type-symbol stream-type)
321                            (current-time))
322          (message "Auto plugged off at %s:%d" server service)
323          (sit-for 1))
324        (signal (car err) (cdr err))))
325     (when process
326       (process-kill-without-query process)
327       (when auto-plugged
328         (elmo-set-plugged t server service
329                           (elmo-network-stream-type-symbol stream-type)))
330       process)))
331
332 (defun elmo-get-network-stream-type (symbol)
333   "Return network stream type corresponding to SYMBOL.
334 Returned value is searched from `elmo-network-stream-type-alist'."
335   (let ((alist elmo-network-stream-type-alist)
336         spec)
337     (while alist
338       (when (eq (nth 1 (car alist)) symbol)
339         (setq spec (car alist))
340         (setq alist nil))
341       (setq alist (cdr alist)))
342     spec))
343
344 (defun elmo-net-folder-set-parameters (folder params &optional defaults)
345   (let ((port (cdr (assq 'port params)))
346         (stream-type (cdr (assq 'stream-type params))))
347     ;; server
348     (elmo-net-folder-set-server-internal
349      folder
350      (or (cdr (assq 'server params))
351          (plist-get defaults :server)))
352     ;; port
353     (elmo-net-folder-set-port-internal
354      folder
355      (or (and port (string-to-int port))
356          (plist-get defaults :port)))
357     ;; stream-type
358     (elmo-net-folder-set-stream-type-internal
359      folder
360      (or (and stream-type
361               (assoc (concat "!" stream-type) elmo-network-stream-type-alist))
362          (plist-get defaults :stream-type)))))
363
364 (luna-define-method elmo-folder-initialize ((folder elmo-net-folder) name)
365   ;; user and auth should be set in subclass.
366   (when (string-match "\\(@[^@:/!]+\\)?\\(:[0-9]+\\)?\\(!.*\\)?$" name)
367     (elmo-net-folder-set-parameters
368      folder
369      (car (elmo-parse-separated-tokens
370            (substring name (match-beginning 0))
371            elmo-net-folder-name-syntax))))
372   folder)
373
374 (luna-define-method elmo-net-port-info ((folder elmo-net-folder))
375   (list (elmo-net-folder-server-internal folder)
376         (elmo-net-folder-port-internal folder)
377         (elmo-network-stream-type-symbol
378          (elmo-net-folder-stream-type-internal folder))))
379
380 (defun elmo-net-port-label (folder)
381   (concat
382    (symbol-name (elmo-folder-type-internal folder))
383    (if (elmo-net-folder-stream-type-internal folder)
384        (concat "!" (symbol-name
385                     (elmo-network-stream-type-symbol
386                      (elmo-net-folder-stream-type-internal
387                       folder)))))))
388
389 (luna-define-method elmo-folder-plugged-p ((folder elmo-net-folder))
390   (apply 'elmo-plugged-p
391          (append (elmo-net-port-info folder)
392                  (list nil (quote (elmo-net-port-label folder))))))
393
394 (luna-define-method elmo-folder-set-plugged ((folder elmo-net-folder)
395                                              plugged &optional add)
396   (apply 'elmo-set-plugged plugged
397          (append (elmo-net-port-info folder)
398                  (list nil nil (quote (elmo-net-port-label folder)) add))))
399
400 (luna-define-method elmo-folder-create ((folder elmo-net-folder))
401   (if (elmo-folder-plugged-p folder)
402       (elmo-folder-send folder 'elmo-folder-create-plugged)
403     (elmo-folder-send folder 'elmo-folder-create-unplugged)))
404
405 (luna-define-method elmo-folder-create-unplugged ((folder elmo-net-folder))
406   (if elmo-enable-disconnected-operation
407       (elmo-folder-create-dop folder)
408     (error "Unplugged")))
409
410 (luna-define-method elmo-folder-exists-p ((folder elmo-net-folder))
411   (if (elmo-folder-plugged-p folder)
412       (elmo-folder-send folder 'elmo-folder-exists-p-plugged)
413     ;; If unplugged, guess by msgdb.
414     (file-directory-p (elmo-folder-msgdb-path folder))))
415
416 (luna-define-method elmo-folder-status ((folder elmo-net-folder))
417   (if (elmo-folder-plugged-p folder)
418       (elmo-folder-send folder 'elmo-folder-status-plugged)
419     (elmo-folder-send folder 'elmo-folder-status-unplugged)))
420
421 (luna-define-method elmo-folder-status-unplugged
422   ((folder elmo-net-folder))
423   (if elmo-enable-disconnected-operation
424       (elmo-folder-status-dop folder)
425     (error "Unplugged")))
426
427 (luna-define-method elmo-folder-next-message-number ((folder elmo-net-folder))
428   (if (elmo-folder-plugged-p folder)
429       (elmo-folder-send folder 'elmo-folder-next-message-number-plugged)
430     (elmo-folder-send folder 'elmo-folder-next-message-number-unplugged)))
431
432 (luna-define-method elmo-folder-next-message-number-unplugged
433   ((folder elmo-net-folder))
434   (if elmo-enable-disconnected-operation
435       (elmo-folder-next-message-number-dop folder)
436     (error "Unplugged")))
437
438 (luna-define-method elmo-folder-list-messages-internal
439   ((folder elmo-net-folder) &optional nohide)
440   (elmo-net-folder-list-messages-internal folder nohide))
441
442 (defun elmo-net-folder-list-messages-internal (folder nohide)
443   (if (elmo-folder-plugged-p folder)
444       (elmo-folder-send folder 'elmo-folder-list-messages-plugged nohide)
445     (elmo-folder-send folder 'elmo-folder-list-messages-unplugged)))
446
447 (luna-define-method elmo-folder-list-messages-plugged
448   ((folder elmo-net-folder))
449   nil)
450
451 ;; Should consider offline append and removal.
452 (luna-define-method elmo-folder-list-messages-unplugged ((folder
453                                                           elmo-net-folder))
454   (if elmo-enable-disconnected-operation
455       (let ((deleting (elmo-dop-list-deleting-messages folder)))
456         (nconc
457          ;; delete deleting messages
458          (elmo-delete-if
459           (lambda (number) (memq number deleting))
460           ;; current number-list.
461           (elmo-folder-list-messages folder nil 'in-msgdb))
462          ;; append appending messages
463          (mapcar (lambda (x) (* -1 x))
464                  (elmo-dop-spool-folder-list-messages folder))))
465     t))
466
467 (luna-define-method elmo-folder-list-flagged-internal ((folder elmo-net-folder)
468                                                        flag)
469   (if (and (elmo-folder-plugged-p folder)
470            (elmo-folder-use-flag-p folder))
471       (elmo-folder-send folder 'elmo-folder-list-flagged-plugged flag)
472     ;; Should consider offline append and removal?
473     t))
474
475 (luna-define-method elmo-folder-list-flagged-plugged ((folder elmo-net-folder)
476                                                       flag)
477   t)
478
479 (luna-define-method elmo-folder-delete-messages-internal ((folder
480                                                            elmo-net-folder)
481                                                           numbers)
482    (if (elmo-folder-plugged-p folder)
483        (elmo-folder-send folder 'elmo-folder-delete-messages-plugged numbers)
484      (elmo-folder-send folder 'elmo-folder-delete-messages-unplugged numbers)))
485
486 (luna-define-method elmo-folder-delete-messages-unplugged ((folder
487                                                             elmo-net-folder)
488                                                            numbers)
489   (elmo-folder-delete-messages-dop folder numbers))
490
491 (luna-define-method elmo-folder-msgdb-create ((folder elmo-net-folder)
492                                               numbers flag-table)
493   (if (elmo-folder-plugged-p folder)
494       (elmo-folder-send folder 'elmo-folder-msgdb-create-plugged
495                         numbers flag-table)
496     (elmo-folder-send folder 'elmo-folder-msgdb-create-unplugged
497                       numbers flag-table)))
498
499 (luna-define-method elmo-folder-msgdb-create-unplugged ((folder
500                                                          elmo-net-folder)
501                                                         numbers
502                                                         flag-table)
503   ;; XXXX should be appended to already existing msgdb.
504   (elmo-dop-msgdb
505    (elmo-folder-msgdb-create (elmo-dop-spool-folder folder)
506                              (mapcar 'abs numbers)
507                              flag-table)))
508
509 (luna-define-method elmo-folder-set-flag :before ((folder elmo-net-folder)
510                                                   numbers
511                                                   flag
512                                                   &optional is-local)
513   (when (and (not is-local)
514              (elmo-folder-use-flag-p folder))
515     (elmo-folder-send folder
516                       (if (elmo-folder-plugged-p folder)
517                           'elmo-folder-set-flag-plugged
518                         'elmo-folder-set-flag-unplugged)
519                       numbers
520                       flag)))
521
522 (luna-define-method elmo-folder-unset-flag :before ((folder elmo-net-folder)
523                                                     numbers
524                                                     flag
525                                                     &optional is-local)
526   (when (and (not is-local)
527              (elmo-folder-use-flag-p folder))
528     (elmo-folder-send folder
529                       (if (elmo-folder-plugged-p folder)
530                           'elmo-folder-unset-flag-plugged
531                         'elmo-folder-unset-flag-unplugged)
532                       numbers
533                       flag)))
534
535 (luna-define-method elmo-folder-set-flag-unplugged ((folder elmo-net-folder)
536                                                     numbers flag)
537   (elmo-folder-set-flag-dop folder numbers flag))
538
539 (luna-define-method elmo-folder-unset-flag-unplugged ((folder elmo-net-folder)
540                                                       numbers flag)
541   (elmo-folder-unset-flag-dop folder numbers flag))
542
543 (luna-define-method elmo-message-encache :around ((folder elmo-net-folder)
544                                                   number &optional read)
545   (if (elmo-folder-plugged-p folder)
546       (luna-call-next-method)
547     (if elmo-enable-disconnected-operation
548         (elmo-message-encache-dop folder number read)
549       (error "Unplugged"))))
550
551 (luna-define-generic elmo-message-fetch-plugged (folder number strategy
552                                                         &optional
553                                                         section
554                                                         outbuf
555                                                         unseen)
556   "")
557
558 (luna-define-generic elmo-message-fetch-unplugged (folder number strategy
559                                                           &optional
560                                                           section
561                                                           outbuf
562                                                           unseen)
563   "")
564
565 (luna-define-method elmo-message-fetch-internal ((folder elmo-net-folder)
566                                                  number strategy
567                                                  &optional section unseen)
568   (if (elmo-folder-plugged-p folder)
569       (elmo-message-fetch-plugged folder number
570                                   strategy section
571                                   (current-buffer) unseen)
572     (elmo-message-fetch-unplugged folder number
573                                   strategy section
574                                   (current-buffer) unseen)))
575
576 (luna-define-method elmo-message-fetch-unplugged
577   ((folder elmo-net-folder) number strategy  &optional section outbuf unseen)
578   (if (and elmo-enable-disconnected-operation
579            (< number 0))
580       (elmo-message-fetch-internal
581        (elmo-dop-spool-folder folder) (abs number) strategy
582        section unseen)
583     (error "Unplugged")))
584
585 (luna-define-method elmo-folder-check ((folder elmo-net-folder))
586   (if (elmo-folder-plugged-p folder)
587       (elmo-folder-send folder 'elmo-folder-check-plugged)))
588
589 (luna-define-method elmo-folder-close :after ((folder elmo-net-folder))
590   (if (elmo-folder-plugged-p folder)
591       (elmo-folder-send folder 'elmo-folder-check-plugged)))
592
593 (luna-define-method elmo-folder-diff :around ((folder elmo-net-folder))
594   (if (and (elmo-folder-use-flag-p folder)
595            (elmo-folder-plugged-p folder))
596       (elmo-folder-send folder 'elmo-folder-diff-plugged)
597     (luna-call-next-method)))
598
599 (luna-define-method elmo-folder-local-p ((folder elmo-net-folder))
600   nil)
601
602 (luna-define-method elmo-quit ((folder elmo-net-folder))
603   (elmo-network-clear-session-cache))
604
605 (require 'product)
606 (product-provide (provide 'elmo-net) (require 'elmo-version))
607
608 ;;; elmo-net.el ends here