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