1 ;;; elmo-nntp.el -- NNTP Interface for ELMO.
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4 ;; Copyright (C) 1998,1999,2000 Masahiro MURATA <muse@ba2.so-net.ne.jp>
5 ;; Copyright (C) 1999,2000 Kenichi OKADA <okada@opaopa.org>
7 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
8 ;; Masahiro MURATA <muse@ba2.so-net.ne.jp>
9 ;; Kenichi OKADA <okada@opaopa.org>
10 ;; Keywords: mail, net news
12 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
14 ;; This program is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 2, or (at your option)
19 ;; This program is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING. If not, write to the
26 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27 ;; Boston, MA 02111-1307, USA.
45 (defcustom elmo-nntp-default-server "localhost"
46 "*Default NNTP server."
50 (defcustom elmo-nntp-default-user nil
51 "*Default User of NNTP. nil means no user authentication."
55 (defcustom elmo-nntp-default-port 119
56 "*Default Port number of NNTP."
60 (defcustom elmo-nntp-default-stream-type nil
61 "*Default stream type for NNTP.
62 Any symbol value of `elmo-network-stream-type-alist' or
63 `elmo-nntp-stream-type-alist'."
67 (elmo-define-obsolete-variable 'elmo-default-nntp-server
68 'elmo-nntp-default-server)
69 (elmo-define-obsolete-variable 'elmo-default-nntp-user
70 'elmo-nntp-default-user)
71 (elmo-define-obsolete-variable 'elmo-default-nntp-port
72 'elmo-nntp-default-port)
74 (defvar elmo-nntp-stream-type-alist nil
75 "*Stream bindings for NNTP.
76 This is taken precedence over `elmo-network-stream-type-alist'.")
78 (defvar elmo-nntp-overview-fetch-chop-length 200
79 "*Number of overviews to fetch in one request in nntp.")
81 (defvar elmo-nntp-use-cache t
82 "Use cache in nntp folder.")
84 (defvar elmo-nntp-max-number-precedes-list-active nil
85 "Non-nil means max number of msgdb is set as the max number of `list active'.
86 (Needed for inn 2.3 or later?).")
90 (luna-define-class elmo-nntp-folder (elmo-net-folder)
91 (group temp-crosses reads))
92 (luna-define-internal-accessors 'elmo-nntp-folder))
94 (luna-define-method elmo-folder-initialize :around ((folder
97 (let ((elmo-network-stream-type-alist
98 (if elmo-nntp-stream-type-alist
99 (setq elmo-network-stream-type-alist
100 (append elmo-nntp-stream-type-alist
101 elmo-network-stream-type-alist))
102 elmo-network-stream-type-alist)))
103 (setq name (luna-call-next-method))
105 "^\\([^:@!]*\\)\\(:[^/!]+\\)?\\(/[^/:@!]+\\)?"
107 (elmo-nntp-folder-set-group-internal
109 (if (match-beginning 1)
110 (elmo-match-string 1 name)))
111 ;; Setup slots for elmo-net-folder
112 (elmo-net-folder-set-user-internal folder
113 (if (match-beginning 2)
114 (elmo-match-substring 2 folder 1)
115 elmo-nntp-default-user))
116 (unless (elmo-net-folder-server-internal folder)
117 (elmo-net-folder-set-server-internal folder
118 elmo-nntp-default-server))
119 (unless (elmo-net-folder-port-internal folder)
120 (elmo-net-folder-set-port-internal folder
121 elmo-nntp-default-port))
122 (unless (elmo-net-folder-stream-type-internal folder)
123 (elmo-net-folder-set-stream-type-internal
125 elmo-nntp-default-stream-type))
128 (luna-define-method elmo-folder-expand-msgdb-path ((folder elmo-nntp-folder))
129 (convert-standard-filename
131 (elmo-nntp-folder-group-internal folder)
132 (expand-file-name (or (elmo-net-folder-server-internal folder) "nowhere")
133 (expand-file-name "nntp"
138 (luna-define-class elmo-nntp-session (elmo-network-session)
140 (luna-define-internal-accessors 'elmo-nntp-session))
143 ;; internal variables
146 (defvar elmo-nntp-connection-cache nil
147 "Cache of NNTP connection.")
148 ;; buffer local variable
150 (defvar elmo-nntp-list-folders-use-cache 600
151 "*Time to cache of list folders, as the number of seconds.
152 Don't cache if nil.")
154 (defvar elmo-nntp-list-folders-cache nil)
156 (defvar elmo-nntp-groups-async nil)
157 (defvar elmo-nntp-header-fetch-chop-length 200)
159 (defvar elmo-nntp-read-point 0)
161 (defvar elmo-nntp-send-mode-reader t)
163 (defvar elmo-nntp-opened-hook nil)
165 (defvar elmo-nntp-get-folders-securely nil)
167 (defvar elmo-nntp-default-use-xover t)
169 (defvar elmo-nntp-default-use-listgroup t)
171 (defvar elmo-nntp-default-use-list-active t)
173 (defvar elmo-nntp-default-use-xhdr t)
175 (defvar elmo-nntp-server-command-alist nil)
178 (defconst elmo-nntp-server-command-index '((xover . 0)
182 (defmacro elmo-nntp-get-server-command (session)
183 (` (assoc (cons (elmo-network-session-server-internal (, session))
184 (elmo-network-session-port-internal (, session)))
185 elmo-nntp-server-command-alist)))
187 (defmacro elmo-nntp-set-server-command (session com value)
189 (unless (setq entry (cdr (elmo-nntp-get-server-command
191 (setq elmo-nntp-server-command-alist
192 (nconc elmo-nntp-server-command-alist
195 (elmo-network-session-server-internal (, session))
196 (elmo-network-session-port-internal (, session)))
199 elmo-nntp-default-use-xover
200 elmo-nntp-default-use-listgroup
201 elmo-nntp-default-use-list-active
202 elmo-nntp-default-use-xhdr)))))))
204 (cdr (assq (, com) elmo-nntp-server-command-index))
207 (defmacro elmo-nntp-xover-p (session)
208 (` (let ((entry (elmo-nntp-get-server-command (, session))))
211 (cdr (assq 'xover elmo-nntp-server-command-index)))
212 elmo-nntp-default-use-xover))))
214 (defmacro elmo-nntp-set-xover (session value)
215 (` (elmo-nntp-set-server-command (, session) 'xover (, value))))
217 (defmacro elmo-nntp-listgroup-p (session)
218 (` (let ((entry (elmo-nntp-get-server-command (, session))))
221 (cdr (assq 'listgroup elmo-nntp-server-command-index)))
222 elmo-nntp-default-use-listgroup))))
224 (defmacro elmo-nntp-set-listgroup (session value)
225 (` (elmo-nntp-set-server-command (, session) 'listgroup (, value))))
227 (defmacro elmo-nntp-list-active-p (session)
228 (` (let ((entry (elmo-nntp-get-server-command (, session))))
231 (cdr (assq 'list-active elmo-nntp-server-command-index)))
232 elmo-nntp-default-use-list-active))))
234 (defmacro elmo-nntp-set-list-active (session value)
235 (` (elmo-nntp-set-server-command (, session) 'list-active (, value))))
237 (defmacro elmo-nntp-xhdr-p (session)
238 (` (let ((entry (elmo-nntp-get-server-command (, session))))
241 (cdr (assq 'xhdr elmo-nntp-server-command-index)))
242 elmo-nntp-default-use-xhdr))))
244 (defmacro elmo-nntp-set-xhdr (session value)
245 (` (elmo-nntp-set-server-command (, session) 'xhdr (, value))))
247 (defsubst elmo-nntp-max-number-precedes-list-active-p ()
248 elmo-nntp-max-number-precedes-list-active)
250 (defsubst elmo-nntp-folder-postfix (user server port type)
252 (and user (concat ":" user))
254 (null (string= server elmo-nntp-default-server)))
257 (null (eq port elmo-nntp-default-port)))
258 (concat ":" (if (numberp port)
259 (int-to-string port) port)))
260 (unless (eq (elmo-network-stream-type-symbol type)
261 elmo-nntp-default-stream-type)
262 (elmo-network-stream-type-spec-string type))))
264 (defun elmo-nntp-get-session (folder &optional if-exists)
265 (elmo-network-get-session
271 (luna-define-method elmo-network-initialize-session ((session
273 (let ((process (elmo-network-session-process-internal session)))
274 (set-process-filter (elmo-network-session-process-internal session)
275 'elmo-nntp-process-filter)
276 (with-current-buffer (elmo-network-session-buffer session)
277 (setq elmo-nntp-read-point (point-min))
278 ;; Skip garbage output from process before greeting.
279 (while (and (memq (process-status process) '(open run))
280 (goto-char (point-max))
282 (not (looking-at "20[01]")))
283 (accept-process-output process 1))
284 (setq elmo-nntp-read-point (point))
285 (or (elmo-nntp-read-response session t)
286 (error "Cannot open network"))
287 (when (eq (elmo-network-stream-type-symbol
288 (elmo-network-session-stream-type-internal session))
290 (elmo-nntp-send-command session "starttls")
291 (or (elmo-nntp-read-response session)
292 (error "Cannot open starttls session"))
293 (starttls-negotiate process)))))
295 (luna-define-method elmo-network-authenticate-session ((session
297 (with-current-buffer (elmo-network-session-buffer session)
298 (when (elmo-network-session-user-internal session)
299 (elmo-nntp-send-command session
300 (format "authinfo user %s"
301 (elmo-network-session-user-internal
303 (or (elmo-nntp-read-response session)
304 (signal 'elmo-authenticate-error '(authinfo)))
305 (elmo-nntp-send-command
307 (format "authinfo pass %s"
308 (elmo-get-passwd (elmo-network-session-password-key session))))
309 (or (elmo-nntp-read-response session)
310 (signal 'elmo-authenticate-error '(authinfo))))))
312 (luna-define-method elmo-network-setup-session ((session
314 (if elmo-nntp-send-mode-reader
315 (elmo-nntp-send-mode-reader session))
316 (run-hooks 'elmo-nntp-opened-hook))
318 (defun elmo-nntp-process-filter (process output)
320 (set-buffer (process-buffer process))
321 (goto-char (point-max))
324 (defun elmo-nntp-send-mode-reader (session)
325 (elmo-nntp-send-command session "mode reader")
326 (if (null (elmo-nntp-read-response session t))
327 (error "Mode reader failed")))
329 (defun elmo-nntp-send-command (session command &optional noerase)
330 (with-current-buffer (elmo-network-session-buffer session)
333 (goto-char (point-min)))
334 (setq elmo-nntp-read-point (point))
335 (process-send-string (elmo-network-session-process-internal
337 (process-send-string (elmo-network-session-process-internal
340 (defun elmo-nntp-read-response (session &optional not-command)
341 (with-current-buffer (elmo-network-session-buffer session)
342 (let ((process (elmo-network-session-process-internal session))
343 (case-fold-search nil)
344 (response-string nil)
345 (response-continue t)
347 (while response-continue
348 (goto-char elmo-nntp-read-point)
349 (while (not (search-forward "\r\n" nil t))
350 (accept-process-output process)
351 (goto-char elmo-nntp-read-point))
352 (setq match-end (point))
353 (setq response-string
354 (buffer-substring elmo-nntp-read-point (- match-end 2)))
355 (goto-char elmo-nntp-read-point)
356 (if (looking-at "[23][0-9]+ .*$")
357 (progn (setq response-continue nil)
358 (setq elmo-nntp-read-point match-end)
361 (concat response "\n" response-string)
363 (if (looking-at "[^23][0-9]+ .*$")
364 (progn (setq response-continue nil)
365 (setq elmo-nntp-read-point match-end)
367 (setq elmo-nntp-read-point match-end)
369 (setq response-continue nil))
372 (concat response "\n" response-string)
374 (setq elmo-nntp-read-point match-end)))
377 (defun elmo-nntp-read-raw-response (session)
378 (with-current-buffer (elmo-network-session-buffer session)
379 (goto-char elmo-nntp-read-point)
380 (while (not (search-forward "\r\n" nil t))
381 (accept-process-output (elmo-network-session-process-internal
383 (goto-char elmo-nntp-read-point))
384 (buffer-substring elmo-nntp-read-point (- (point) 2))))
386 (defun elmo-nntp-read-contents (session)
387 (with-current-buffer (elmo-network-session-buffer session)
388 (goto-char elmo-nntp-read-point)
389 (while (not (re-search-forward "^\\.\r\n" nil t))
390 (accept-process-output (elmo-network-session-process-internal
392 (goto-char elmo-nntp-read-point))
394 (buffer-substring elmo-nntp-read-point
397 (defun elmo-nntp-read-body (session outbuf)
398 (with-current-buffer (elmo-network-session-buffer session)
399 (goto-char elmo-nntp-read-point)
400 (while (not (re-search-forward "^\\.\r\n" nil t))
401 (accept-process-output (elmo-network-session-process-internal session))
402 (goto-char elmo-nntp-read-point))
403 (let ((start elmo-nntp-read-point)
405 (with-current-buffer outbuf
407 (insert-buffer-substring (elmo-network-session-buffer session)
410 (defun elmo-nntp-select-group (session group &optional force)
413 (not (string= (elmo-nntp-session-current-group-internal session)
417 (elmo-nntp-send-command session (format "group %s" group))
418 (setq response (elmo-nntp-read-response session)))
419 (elmo-nntp-session-set-current-group-internal session
420 (and response group))
423 (defun elmo-nntp-list-folders-get-cache (folder buf)
424 (when (and elmo-nntp-list-folders-use-cache
425 elmo-nntp-list-folders-cache
426 (string-match (concat "^"
429 (nth 1 elmo-nntp-list-folders-cache)
432 (let* ((cache-time (car elmo-nntp-list-folders-cache)))
433 (unless (elmo-time-expire cache-time
434 elmo-nntp-list-folders-use-cache)
438 (insert (nth 2 elmo-nntp-list-folders-cache))
439 (goto-char (point-min))
440 (or (string= folder "")
442 (keep-lines (concat "^" (regexp-quote folder) "\\."))))
446 (defsubst elmo-nntp-catchup-msgdb (msgdb max-number)
447 (let (msgdb-max number-alist)
448 (setq number-alist (elmo-msgdb-get-number-alist msgdb))
449 (setq msgdb-max (car (nth (max (- (length number-alist) 1) 0)
451 (if (or (not msgdb-max)
452 (and msgdb-max max-number
453 (< msgdb-max max-number)))
454 (elmo-msgdb-set-number-alist
456 (nconc number-alist (list (cons max-number nil)))))))
458 (luna-define-method elmo-folder-list-subfolders ((folder elmo-nntp-folder)
460 (elmo-nntp-folder-list-subfolders folder one-level))
462 (defun elmo-nntp-folder-list-subfolders (folder one-level)
463 (let ((session (elmo-nntp-get-session folder))
464 response ret-val top-ng append-serv use-list-active start)
466 (if (and (elmo-nntp-folder-group-internal folder)
467 (elmo-nntp-select-group
469 (elmo-nntp-folder-group-internal folder)))
470 ;; add top newsgroups
471 (setq ret-val (list (elmo-nntp-folder-group-internal folder))))
472 (unless (setq response (elmo-nntp-list-folders-get-cache
473 (elmo-nntp-folder-group-internal folder)
475 (when (setq use-list-active (elmo-nntp-list-active-p session))
476 (elmo-nntp-send-command
479 (if (and (elmo-nntp-folder-group-internal folder)
480 (null (string= (elmo-nntp-folder-group-internal
484 (elmo-nntp-folder-group-internal folder)
486 (if (elmo-nntp-read-response session t)
487 (if (null (setq response (elmo-nntp-read-contents session)))
488 (error "NNTP List folders failed")
489 (when elmo-nntp-list-folders-use-cache
490 (setq elmo-nntp-list-folders-cache
492 (elmo-nntp-folder-group-internal folder)
496 (elmo-nntp-set-list-active session nil)
497 (setq use-list-active nil)))
498 (when (null use-list-active)
499 (elmo-nntp-send-command session "list")
500 (if (null (and (elmo-nntp-read-response session t)
501 (setq response (elmo-nntp-read-contents session))))
502 (error "NNTP List folders failed"))
503 (when elmo-nntp-list-folders-use-cache
504 (setq elmo-nntp-list-folders-cache
505 (list (current-time) nil response)))
508 (while (string-match (concat "^"
511 (elmo-nntp-folder-group-internal
515 (insert (match-string 0 response) "\n")
516 (setq start (match-end 0)))))
517 (goto-char (point-min))
518 (let ((len (count-lines (point-min) (point-max)))
523 (format "^\\(%s[^. ]+\\)\\([. ]\\).*\n"
525 (elmo-nntp-folder-group-internal folder)
527 (elmo-nntp-folder-group-internal
529 (concat (elmo-nntp-folder-group-internal
532 (while (looking-at regexp)
533 (setq top-ng (elmo-match-buffer 1))
534 (if (string= (elmo-match-buffer 2) " ")
535 (if (not (or (member top-ng ret-val)
536 (assoc top-ng ret-val)))
537 (setq ret-val (nconc ret-val (list top-ng))))
538 (if (member top-ng ret-val)
539 (setq ret-val (delete top-ng ret-val)))
540 (if (not (assoc top-ng ret-val))
541 (setq ret-val (nconc ret-val (list (list top-ng))))))
542 (when (> len elmo-display-progress-threshold)
544 (if (or (zerop (% i 10)) (= i len))
545 (elmo-display-progress
546 'elmo-nntp-list-folders "Parsing active..."
549 (while (re-search-forward "\\([^ ]+\\) .*\n" nil t)
550 (setq ret-val (nconc ret-val
551 (list (elmo-match-buffer 1))))
552 (when (> len elmo-display-progress-threshold)
554 (if (or (zerop (% i 10)) (= i len))
555 (elmo-display-progress
556 'elmo-nntp-list-folders "Parsing active..."
557 (/ (* i 100) len))))))
558 (when (> len elmo-display-progress-threshold)
559 (elmo-display-progress
560 'elmo-nntp-list-folders "Parsing active..." 100))))
561 (unless (string= (elmo-net-folder-server-internal folder)
562 elmo-nntp-default-server)
563 (setq append-serv (concat "@" (elmo-net-folder-server-internal
565 (unless (eq (elmo-net-folder-port-internal folder) elmo-nntp-default-port)
566 (setq append-serv (concat append-serv
568 (elmo-net-folder-port-internal folder)))))
569 (unless (eq (elmo-network-stream-type-symbol
570 (elmo-net-folder-stream-type-internal folder))
571 elmo-nntp-default-stream-type)
574 (elmo-network-stream-type-spec-string
575 (elmo-net-folder-stream-type-internal folder)))))
576 (mapcar '(lambda (fld)
578 (list (concat "-" (car fld)
579 (and (elmo-net-folder-user-internal folder)
582 (elmo-net-folder-user-internal folder)))
584 (concat append-serv))))
586 (and (elmo-net-folder-user-internal folder)
587 (concat ":" (elmo-net-folder-user-internal
590 (concat append-serv)))))
593 (defun elmo-nntp-make-msglist (beg-str end-str)
595 (let ((beg-num (string-to-int beg-str))
596 (end-num (string-to-int end-str))
600 (while (<= i end-num)
601 (insert (format "%s " i))
604 (goto-char (point-min))
605 (read (current-buffer)))))
607 (luna-define-method elmo-folder-list-messages-internal ((folder
610 (let ((session (elmo-nntp-get-session folder))
611 (group (elmo-nntp-folder-group-internal folder))
612 response numbers use-listgroup)
614 (when (setq use-listgroup (elmo-nntp-listgroup-p session))
615 (elmo-nntp-send-command session
616 (format "listgroup %s" group))
617 (if (not (elmo-nntp-read-response session t))
619 (elmo-nntp-set-listgroup session nil)
620 (setq use-listgroup nil))
621 (if (null (setq response (elmo-nntp-read-contents session)))
622 (error "Fetching listgroup failed"))
623 (setq numbers (elmo-string-to-list response))
624 (elmo-nntp-session-set-current-group-internal session
626 (unless use-listgroup
627 (elmo-nntp-send-command session (format "group %s" group))
628 (if (null (setq response (elmo-nntp-read-response session)))
629 (error "Select group failed"))
632 "211 \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\) [^.].+$"
634 (> (string-to-int (elmo-match-string 1 response)) 0))
635 (setq numbers (elmo-nntp-make-msglist
636 (elmo-match-string 2 response)
637 (elmo-match-string 3 response)))))
640 (luna-define-method elmo-folder-status ((folder elmo-nntp-folder))
641 (elmo-nntp-folder-status folder))
643 (defun elmo-nntp-folder-status (folder)
644 (let ((killed-list (elmo-msgdb-killed-list-load
645 (elmo-folder-msgdb-path folder)))
647 (if elmo-nntp-groups-async
650 (concat (elmo-nntp-folder-group-internal folder)
651 (elmo-nntp-folder-postfix
652 (elmo-net-folder-user-internal folder)
653 (elmo-net-folder-server-internal folder)
654 (elmo-net-folder-port-internal folder)
655 (elmo-net-folder-stream-type-internal folder)))
656 elmo-newsgroups-hashtb))
658 (setq end-num (nth 2 entry))
659 (when(and killed-list
660 (elmo-number-set-member end-num killed-list))
663 (cons end-num (car entry)))
664 (error "No such newsgroup \"%s\""
665 (elmo-nntp-folder-group-internal folder)))
666 (let ((session (elmo-nntp-get-session folder))
669 (error "Connection failed"))
671 (elmo-nntp-send-command session
674 (elmo-nntp-folder-group-internal folder)))
675 (setq response (elmo-nntp-read-response session))
678 "211 \\([0-9]+\\) \\([0-9]+\\) \\([0-9]+\\) [^.].+$"
681 (setq end-num (string-to-int
682 (elmo-match-string 3 response)))
683 (setq e-num (string-to-int
684 (elmo-match-string 1 response)))
685 (when (and killed-list
686 (elmo-number-set-member end-num killed-list))
689 (cons end-num e-num))
691 (error "Selecting newsgroup \"%s\" failed"
692 (elmo-nntp-folder-group-internal folder))
695 (defconst elmo-nntp-overview-index
706 (defun elmo-nntp-create-msgdb-from-overview-string (str
713 (let (ov-list gmark message-id seen
714 ov-entity overview number-alist mark-alist num
715 extras extra ext field field-index)
716 (setq ov-list (elmo-nntp-parse-overview-string str))
718 (setq ov-entity (car ov-list))
720 ;;; (if (or (> (setq num (string-to-int (aref ov-entity 0)))
724 ;;; (setq num (int-to-string num))
725 (setq num (string-to-int (aref ov-entity 0)))
726 (when (or (null numlist)
728 (setq extras elmo-msgdb-extra-fields
731 (setq ext (downcase (car extras)))
732 (when (setq field-index (cdr (assoc ext elmo-nntp-overview-index)))
733 (setq field (aref ov-entity field-index))
734 (when (eq field-index 8) ;; xref
735 (setq field (elmo-msgdb-remove-field-string field)))
736 (setq extra (cons (cons ext field) extra)))
737 (setq extras (cdr extras)))
739 (elmo-msgdb-append-element
741 (cons (aref ov-entity 4)
743 (elmo-msgdb-get-last-message-id
746 (elmo-mime-string (elmo-delete-char
752 (elmo-mime-string (or (aref ov-entity 1)
754 (aref ov-entity 3) ;date
758 (aref ov-entity 6)) ; size
759 extra ; extra-field-list
762 (elmo-msgdb-number-add number-alist num
764 (setq message-id (aref ov-entity 4))
765 (setq seen (member message-id seen-list))
766 (if (setq gmark (or (elmo-msgdb-global-mark-get message-id)
767 (if (elmo-file-cache-status
768 (elmo-file-cache-get message-id))
773 (if elmo-nntp-use-cache
777 (elmo-msgdb-mark-append mark-alist
779 (setq ov-list (cdr ov-list)))
780 (list overview number-alist mark-alist)))
782 (luna-define-method elmo-folder-msgdb-create ((folder elmo-nntp-folder)
783 numbers new-mark already-mark
784 seen-mark important-mark
786 (elmo-nntp-folder-msgdb-create folder numbers new-mark already-mark
787 seen-mark important-mark
790 (defun elmo-nntp-folder-msgdb-create (folder numbers new-mark already-mark
791 seen-mark important-mark
793 (let ((filter numbers)
794 (session (elmo-nntp-get-session folder))
795 beg-num end-num cur length
796 ret-val ov-str use-xover dir)
797 (elmo-nntp-select-group session (elmo-nntp-folder-group-internal
799 (when (setq use-xover (elmo-nntp-xover-p session))
800 (setq beg-num (car numbers)
802 end-num (nth (1- (length numbers)) numbers)
803 length (+ (- end-num beg-num) 1))
804 (message "Getting overview...")
805 (while (<= cur end-num)
806 (elmo-nntp-send-command
813 elmo-nntp-overview-fetch-chop-length))))
814 (with-current-buffer (elmo-network-session-buffer session)
819 (elmo-nntp-create-msgdb-from-overview-string
828 (if (null (elmo-nntp-read-response session t))
830 (setq cur end-num);; exit while loop
831 (elmo-nntp-set-xover session nil)
832 (setq use-xover nil))
833 (if (null (setq ov-str (elmo-nntp-read-contents session)))
834 (error "Fetching overview failed")))
835 (setq cur (+ elmo-nntp-overview-fetch-chop-length cur 1))
836 (when (> length elmo-display-progress-threshold)
837 (elmo-display-progress
838 'elmo-nntp-msgdb-create "Getting overview..."
839 (/ (* (+ (- (min cur end-num)
840 beg-num) 1) 100) length))))
841 (when (> length elmo-display-progress-threshold)
842 (elmo-display-progress
843 'elmo-nntp-msgdb-create "Getting overview..." 100)))
845 (setq ret-val (elmo-nntp-msgdb-create-by-header
847 new-mark already-mark seen-mark seen-list))
848 (with-current-buffer (elmo-network-session-buffer session)
853 (elmo-nntp-create-msgdb-from-overview-string
861 (elmo-folder-set-killed-list-internal
864 (elmo-folder-killed-list-internal folder)
868 (elmo-msgdb-get-number-alist
870 ;; If there are canceled messages, overviews are not obtained
871 ;; to max-number(inn 2.3?).
872 (when (and (elmo-nntp-max-number-precedes-list-active-p)
873 (elmo-nntp-list-active-p session))
874 (elmo-nntp-send-command session
875 (format "list active %s"
876 (elmo-nntp-folder-group-internal
878 (if (null (elmo-nntp-read-response session))
880 (elmo-nntp-set-list-active session nil)
881 (error "NNTP list command failed")))
882 (elmo-nntp-catchup-msgdb
884 (nth 1 (read (concat "(" (elmo-nntp-read-contents
888 (luna-define-method elmo-folder-update-number ((folder elmo-nntp-folder))
889 (if (elmo-nntp-max-number-precedes-list-active-p)
890 (let ((session (elmo-nntp-get-session folder))
891 (number-alist (elmo-msgdb-get-number-alist
892 (elmo-folder-msgdb-internal folder))))
893 (if (elmo-nntp-list-active-p session)
894 (let (msgdb-max max-number)
895 ;; If there are canceled messages, overviews are not obtained
896 ;; to max-number(inn 2.3?).
897 (elmo-nntp-select-group session
898 (elmo-nntp-folder-group-internal folder))
899 (elmo-nntp-send-command session
900 (format "list active %s"
901 (elmo-nntp-folder-group-internal
903 (if (null (elmo-nntp-read-response session))
904 (error "NNTP list command failed"))
906 (nth 1 (read (concat "(" (elmo-nntp-read-contents
909 (car (nth (max (- (length number-alist) 1) 0)
911 (if (or (and number-alist (not msgdb-max))
912 (and msgdb-max max-number
913 (< msgdb-max max-number)))
914 (elmo-msgdb-set-number-alist
915 (elmo-folder-msgdb-internal folder)
917 (list (cons max-number nil))))))))))
919 (defun elmo-nntp-msgdb-create-by-header (session numbers
920 new-mark already-mark
923 (elmo-nntp-retrieve-headers session (current-buffer) numbers)
924 (elmo-nntp-msgdb-create-message
925 (length numbers) new-mark already-mark seen-mark seen-list)))
927 (defun elmo-nntp-parse-xhdr-response (string)
931 (goto-char (point-min))
933 (if (looking-at "^\\([0-9]+\\) \\(.*\\)$")
934 (setq response (cons (cons (string-to-int (elmo-match-buffer 1))
935 (elmo-match-buffer 2))
938 (nreverse response)))
940 (defun elmo-nntp-parse-overview-string (string)
942 (let ((tmp-buffer (get-buffer-create " *ELMO Overview TMP*"))
943 ret-list ret-val beg)
944 (set-buffer tmp-buffer)
946 (elmo-set-buffer-multibyte nil)
948 (goto-char (point-min))
952 (setq ret-list (save-match-data
953 (apply 'vector (split-string
954 (buffer-substring beg (point))
959 (setq ret-val (nconc ret-val (list ret-list))))
960 ;;; (kill-buffer tmp-buffer)
963 (defun elmo-nntp-get-newsgroup-by-msgid (msgid server user port type)
964 "Get nntp header string."
966 (let ((session (elmo-nntp-get-session
967 (list 'nntp nil user server port type))))
968 (elmo-nntp-send-command session
969 (format "head %s" msgid))
970 (if (elmo-nntp-read-response session)
971 (elmo-nntp-read-contents session))
972 (with-current-buffer (elmo-network-session-buffer session)
973 (std11-field-body "Newsgroups")))))
975 (luna-define-method elmo-message-fetch ((folder elmo-nntp-folder)
980 (if (elmo-folder-plugged-p folder)
981 (let ((cache-file (elmo-file-cache-expand-path
982 (elmo-fetch-strategy-cache-path strategy)
984 (if (and (elmo-fetch-strategy-use-cache strategy)
985 (file-exists-p cache-file))
987 (with-current-buffer outbuf
988 (insert-file-contents-as-binary cache-file)
989 (elmo-nntp-setup-crosspost-buffer folder number)
991 (elmo-nntp-folder-update-crosspost-message-alist
992 folder (list number)))
995 (insert-file-contents-as-binary cache-file)
996 (elmo-nntp-setup-crosspost-buffer folder number)
998 (elmo-nntp-folder-update-crosspost-message-alist
999 folder (list number)))
1002 (with-current-buffer outbuf
1003 (elmo-folder-send folder 'elmo-message-fetch-plugged
1004 number strategy section
1005 (current-buffer) unread)
1006 (elmo-delete-cr-buffer)
1007 (when (and (> (buffer-size) 0)
1008 (elmo-fetch-strategy-save-cache strategy))
1009 (elmo-file-cache-save
1010 (elmo-fetch-strategy-cache-path strategy)
1014 (elmo-folder-send folder 'elmo-message-fetch-plugged
1015 number strategy section
1016 (current-buffer) unread)
1017 (elmo-delete-cr-buffer)
1018 (when (and (> (buffer-size) 0)
1019 (elmo-fetch-strategy-save-cache strategy))
1020 (elmo-file-cache-save
1021 (elmo-fetch-strategy-cache-path strategy)
1024 (elmo-folder-send folder 'elmo-message-fetch-unplugged
1025 number strategy section outbuf unread)))
1027 (luna-define-method elmo-message-fetch-plugged ((folder elmo-nntp-folder)
1029 &optional section outbuf
1031 (elmo-nntp-message-fetch folder number strategy section outbuf unread))
1033 (luna-define-method elmo-message-fetch-unplugged ((folder elmo-nntp-folder)
1035 &optional section outbuf
1037 (if (elmo-fetch-strategy-use-cache strategy)
1039 (with-current-buffer outbuf
1040 (insert-file-contents-as-binary
1041 (elmo-file-cache-expand-path
1042 (elmo-fetch-strategy-cache-path strategy)
1044 (elmo-nntp-setup-crosspost-buffer folder number)
1046 (elmo-nntp-folder-update-crosspost-message-alist
1047 folder (list number)))
1050 (insert-file-contents-as-binary
1051 (elmo-file-cache-expand-path
1052 (elmo-fetch-strategy-cache-path strategy)
1054 (elmo-nntp-setup-crosspost-buffer folder number)
1056 (elmo-nntp-folder-update-crosspost-message-alist
1057 folder (list number)))
1059 (error "Unplugged")))
1061 (defun elmo-nntp-message-fetch (folder number strategy section outbuf unread)
1062 (let ((session (elmo-nntp-get-session folder))
1064 (with-current-buffer (elmo-network-session-buffer session)
1065 (elmo-nntp-select-group session (elmo-nntp-folder-group-internal folder))
1066 (elmo-nntp-send-command session (format "article %s" number))
1067 (if (null (elmo-nntp-read-response session t))
1069 (with-current-buffer outbuf (erase-buffer))
1070 (message "Fetching message failed")
1072 (prog1 (elmo-nntp-read-body session outbuf)
1073 (with-current-buffer outbuf
1074 (goto-char (point-min))
1075 (while (re-search-forward "^\\." nil t)
1078 (elmo-nntp-setup-crosspost-buffer folder number)
1080 (elmo-nntp-folder-update-crosspost-message-alist
1081 folder (list number)))))))))
1083 (defun elmo-nntp-post (hostname content-buf)
1084 (let ((session (elmo-nntp-get-session
1087 :user elmo-nntp-default-user
1089 :port elmo-nntp-default-port
1090 :stream-type elmo-nntp-default-stream-type)))
1091 response has-message-id)
1093 (set-buffer content-buf)
1094 (goto-char (point-min))
1095 (if (search-forward mail-header-separator nil t)
1096 (delete-region (match-beginning 0)(match-end 0)))
1097 (setq has-message-id (std11-field-body "message-id"))
1098 (elmo-nntp-send-command session "post")
1099 (if (string-match "^340" (setq response
1100 (elmo-nntp-read-raw-response session)))
1101 (if (string-match "recommended ID \\(<[^@]+@[^>]+>\\)" response)
1102 (unless has-message-id
1103 (goto-char (point-min))
1104 (insert (concat "Message-ID: "
1105 (elmo-match-string 1 response)
1107 (error "POST failed"))
1108 (run-hooks 'elmo-nntp-post-pre-hook)
1109 (elmo-nntp-send-buffer session content-buf)
1110 (elmo-nntp-send-command session ".")
1111 ;;; (elmo-nntp-read-response buffer process t)
1112 (if (not (string-match
1113 "^2" (setq response (elmo-nntp-read-raw-response
1115 (error (concat "NNTP error: " response))))))
1117 (defsubst elmo-nntp-send-data-line (session line)
1118 "Send LINE to SESSION."
1119 ;; Escape "." at start of a line
1120 (if (eq (string-to-char line) ?.)
1121 (process-send-string (elmo-network-session-process-internal
1123 (process-send-string (elmo-network-session-process-internal
1125 (process-send-string (elmo-network-session-process-internal
1128 (defun elmo-nntp-send-buffer (session databuf)
1129 "Send data content of DATABUF to SESSION."
1130 (let ((data-continue t)
1132 (with-current-buffer databuf
1133 (goto-char (point-min))
1134 (while data-continue
1138 (setq line (buffer-substring bol (point)))
1139 (unless (eq (forward-line 1) 0) (setq data-continue nil))
1140 (elmo-nntp-send-data-line session line)))))
1142 (luna-define-method elmo-folder-delete-messages ((folder elmo-nntp-folder)
1144 (elmo-nntp-folder-delete-messages folder numbers))
1146 (defun elmo-nntp-folder-delete-messages (folder numbers)
1147 (let ((killed-list (elmo-folder-killed-list-internal folder)))
1148 (dolist (number numbers)
1150 (elmo-msgdb-set-as-killed killed-list number)))
1151 (elmo-folder-set-killed-list-internal folder killed-list))
1154 (luna-define-method elmo-folder-exists-p ((folder elmo-nntp-folder))
1155 (let ((session (elmo-nntp-get-session folder)))
1156 (if (elmo-folder-plugged-p folder)
1158 (elmo-nntp-send-command
1161 (elmo-nntp-folder-group-internal folder)))
1162 (elmo-nntp-read-response session))
1165 (defun elmo-nntp-retrieve-field (spec field from-msgs)
1166 "Retrieve FIELD values from FROM-MSGS.
1167 Returns a list of cons cells like (NUMBER . VALUE)"
1168 (let ((session (elmo-nntp-get-session spec)))
1169 (if (elmo-nntp-xhdr-p session)
1171 (elmo-nntp-select-group session (elmo-nntp-folder-group-internal spec))
1172 (elmo-nntp-send-command session
1173 (format "xhdr %s %s"
1181 (- (length from-msgs) 1) 0)
1184 (if (elmo-nntp-read-response session t)
1185 (elmo-nntp-parse-xhdr-response
1186 (elmo-nntp-read-contents session))
1187 (elmo-nntp-set-xhdr session nil)
1188 (error "NNTP XHDR command failed"))))))
1190 (defun elmo-nntp-search-primitive (spec condition &optional from-msgs)
1191 (let ((search-key (elmo-filter-key condition)))
1193 ((string= "last" search-key)
1194 (let ((numbers (or from-msgs (elmo-folder-list-messages spec))))
1195 (nthcdr (max (- (length numbers)
1196 (string-to-int (elmo-filter-value condition)))
1199 ((string= "first" search-key)
1200 (let* ((numbers (or from-msgs (elmo-folder-list-messages spec)))
1201 (rest (nthcdr (string-to-int (elmo-filter-value condition) )
1203 (mapcar '(lambda (x) (delete x numbers)) rest)
1205 ((or (string= "since" search-key)
1206 (string= "before" search-key))
1207 (let* ((key-date (elmo-date-get-datevec (elmo-filter-value condition)))
1208 (key-datestr (elmo-date-make-sortable-string key-date))
1209 (since (string= "since" search-key))
1211 (if (eq (elmo-filter-type condition) 'unmatch)
1212 (setq since (not since)))
1218 (string< key-datestr
1219 (elmo-date-make-sortable-string
1222 (current-time-zone) nil)))
1223 (not (string< key-datestr
1224 (elmo-date-make-sortable-string
1227 (current-time-zone) nil)))))
1229 (elmo-nntp-retrieve-field spec "date" from-msgs))))
1231 (elmo-list-filter from-msgs result)
1234 (let ((val (elmo-filter-value condition))
1235 (negative (eq (elmo-filter-type condition) 'unmatch))
1236 (case-fold-search t)
1242 (if (string-match val
1243 (eword-decode-string
1244 (decode-mime-charset-string
1245 (cdr pair) elmo-mime-charset)))
1246 (unless negative (car pair))
1247 (if negative (car pair))))
1248 (elmo-nntp-retrieve-field spec search-key
1251 (elmo-list-filter from-msgs result)
1254 (luna-define-method elmo-folder-search ((folder elmo-nntp-folder)
1255 condition &optional from-msgs)
1258 ((vectorp condition)
1259 (setq result (elmo-nntp-search-primitive
1260 folder condition from-msgs)))
1261 ((eq (car condition) 'and)
1262 (setq result (elmo-folder-search folder (nth 1 condition) from-msgs)
1263 result (elmo-list-filter result
1265 folder (nth 2 condition)
1267 ((eq (car condition) 'or)
1268 (setq result (elmo-folder-search folder (nth 1 condition) from-msgs)
1269 result (elmo-uniq-list
1271 (elmo-folder-search folder (nth 2 condition)
1273 result (sort result '<))))))
1275 (defun elmo-nntp-get-folders-info-prepare (folder session-keys)
1277 (let ((session (elmo-nntp-get-session folder))
1279 (with-current-buffer (elmo-network-session-buffer session)
1280 (unless (setq key (assoc session session-keys))
1282 (setq key (cons session
1284 (elmo-net-folder-server-internal folder)
1285 (elmo-net-folder-user-internal folder)
1286 (elmo-net-folder-port-internal folder)
1287 (elmo-net-folder-stream-type-internal
1289 (setq session-keys (nconc session-keys (list key))))
1290 (elmo-nntp-send-command session
1292 (elmo-nntp-folder-group-internal
1295 (if elmo-nntp-get-folders-securely
1296 (accept-process-output
1297 (elmo-network-session-process-internal session)
1299 (setq count (aref (cdr key) 0))
1300 (aset (cdr key) 0 (1+ count))))
1302 (when elmo-auto-change-plugged
1307 (defun elmo-nntp-get-folders-info (session-keys)
1308 (let ((sessions session-keys)
1309 (cur (get-buffer-create " *ELMO NNTP Temp*")))
1311 (let* ((session (caar sessions))
1312 (key (cdar sessions))
1313 (count (aref key 0))
1314 (server (aref key 1))
1318 (hashtb (or elmo-newsgroups-hashtb
1319 (setq elmo-newsgroups-hashtb
1320 (elmo-make-hash count)))))
1322 (elmo-nntp-groups-read-response session cur count)
1324 (goto-char (point-min))
1325 (let ((case-replace nil)
1326 (postfix (elmo-nntp-folder-postfix user server port type)))
1327 (if (not (string= postfix ""))
1329 (replace-regexp "^\\(211 [0-9]+ [0-9]+ [0-9]+ [^ \n]+\\).*$"
1331 (elmo-replace-in-string
1333 "\\\\" "\\\\\\\\\\\\\\\\"))))))
1334 (let (len min max group)
1337 (when (= (following-char) ?2)
1339 (setq len (read cur)
1342 (set (setq group (let ((obarray hashtb)) (read cur)))
1343 (list len min max)))
1344 (error (and group (symbolp group) (set group nil))))
1346 (setq sessions (cdr sessions))))
1349 ;; original is 'nntp-retrieve-groups [Gnus]
1350 (defun elmo-nntp-groups-read-response (session outbuf count)
1352 (last-point (point-min)))
1353 (with-current-buffer (elmo-network-session-buffer session)
1354 (accept-process-output
1355 (elmo-network-session-process-internal session) 1)
1357 ;; Wait for all replies.
1358 (message "Getting folders info...")
1360 (goto-char last-point)
1362 (while (re-search-forward "^[0-9]" nil t)
1365 (setq last-point (point))
1367 (accept-process-output (elmo-network-session-process-internal session)
1370 (when (> count elmo-display-progress-threshold)
1371 (if (or (zerop (% received 10)) (= received count))
1372 (elmo-display-progress
1373 'elmo-nntp-groups-read-response "Getting folders info..."
1374 (/ (* received 100) count)))))
1375 (when (> count elmo-display-progress-threshold)
1376 (elmo-display-progress
1377 'elmo-nntp-groups-read-response "Getting folders info..." 100))
1378 ;; Wait for the reply from the final command.
1379 (goto-char (point-max))
1380 (re-search-backward "^[0-9]" nil t)
1381 (when (looking-at "^[23]")
1383 (goto-char (point-max))
1384 (not (re-search-backward "\r?\n" (- (point) 3) t)))
1385 (accept-process-output
1386 (elmo-network-session-process-internal session) 1)
1388 ;; Now all replies are received. We remove CRs.
1389 (goto-char (point-min))
1390 (while (search-forward "\r" nil t)
1391 (replace-match "" t t))
1392 (copy-to-buffer outbuf (point-min) (point-max)))))
1394 ;; from nntp.el [Gnus]
1396 (defsubst elmo-nntp-next-result-arrived-p ()
1398 ((eq (following-char) ?2)
1399 (if (re-search-forward "\n\\.\r?\n" nil t)
1402 ((looking-at "[34]")
1403 (if (search-forward "\n" nil t)
1409 (defun elmo-nntp-retrieve-headers (session outbuf articles)
1410 "Retrieve the headers of ARTICLES."
1411 (with-current-buffer (elmo-network-session-buffer session)
1413 (let ((number (length articles))
1416 (last-point (point-min))
1418 ;; Send HEAD commands.
1419 (while (setq article (pop articles))
1420 (elmo-nntp-send-command session
1421 (format "head %s" article)
1423 (setq count (1+ count))
1424 ;; Every 200 requests we have to read the stream in
1425 ;; order to avoid deadlocks.
1426 (when (or (null articles) ;All requests have been sent.
1427 (zerop (% count elmo-nntp-header-fetch-chop-length)))
1428 (accept-process-output
1429 (elmo-network-session-process-internal session) 1)
1432 (goto-char last-point)
1434 (while (elmo-nntp-next-result-arrived-p)
1435 (setq last-point (point))
1436 (setq received (1+ received)))
1438 (when (> number elmo-display-progress-threshold)
1439 (if (or (zerop (% received 20)) (= received number))
1440 (elmo-display-progress
1441 'elmo-nntp-retrieve-headers "Getting headers..."
1442 (/ (* received 100) number))))
1443 (accept-process-output
1444 (elmo-network-session-process-internal session) 1)
1446 (when (> number elmo-display-progress-threshold)
1447 (elmo-display-progress
1448 'elmo-nntp-retrieve-headers "Getting headers..." 100))
1449 (message "Getting headers...done")
1450 ;; Remove all "\r"'s.
1451 (goto-char (point-min))
1452 (while (search-forward "\r\n" nil t)
1453 (replace-match "\n"))
1454 (copy-to-buffer outbuf (point-min) (point-max)))))
1458 (defun elmo-nntp-msgdb-create-message (len new-mark
1459 already-mark seen-mark seen-list)
1461 (let (beg overview number-alist mark-alist
1462 entity i num gmark seen message-id)
1463 (elmo-set-buffer-multibyte nil)
1464 (goto-char (point-min))
1466 (message "Creating msgdb...")
1468 (setq beg (save-excursion (forward-line 1) (point)))
1470 (and (looking-at "^2[0-9]*[ ]+\\([0-9]+\\)")
1472 (elmo-match-buffer 1))))
1473 (elmo-nntp-next-result-arrived-p)
1478 (narrow-to-region beg (point))
1480 (elmo-msgdb-create-overview-from-buffer num))
1483 (elmo-msgdb-append-element
1486 (elmo-msgdb-number-add
1488 (elmo-msgdb-overview-entity-get-number entity)
1490 (setq message-id (car entity))
1491 (setq seen (member message-id seen-list))
1493 (or (elmo-msgdb-global-mark-get message-id)
1494 (if (elmo-file-cache-status
1495 (elmo-file-cache-get message-id))
1500 (if elmo-nntp-use-cache
1504 (elmo-msgdb-mark-append
1508 (when (> len elmo-display-progress-threshold)
1510 (if (or (zerop (% i 20)) (= i len))
1511 (elmo-display-progress
1512 'elmo-nntp-msgdb-create-message "Creating msgdb..."
1513 (/ (* i 100) len)))))
1514 (when (> len elmo-display-progress-threshold)
1515 (elmo-display-progress
1516 'elmo-nntp-msgdb-create-message "Creating msgdb..." 100))
1517 (list overview number-alist mark-alist))))
1519 (luna-define-method elmo-message-use-cache-p ((folder elmo-nntp-folder) number)
1520 elmo-nntp-use-cache)
1522 (luna-define-method elmo-folder-creatable-p ((folder elmo-nntp-folder))
1525 (luna-define-method elmo-folder-writable-p ((folder elmo-nntp-folder))
1528 (defun elmo-nntp-parse-newsgroups (string &optional subscribe-only)
1529 (let ((nglist (elmo-parse string "[ \t\f\r\n,]*\\([^ \t\f\r\n,]+\\)"))
1531 (if (not subscribe-only)
1534 (if (intern-soft ng elmo-newsgroups-hashtb)
1535 (setq ngs (cons ng ngs))))
1538 ;;; Crosspost processing.
1540 ;; 1. setup crosspost alist.
1541 ;; 1.1. When message is fetched and is crossposted message,
1542 ;; it is remembered in `temp-crosses' slot.
1543 ;; temp-crosses slot is a list of cons cell:
1544 ;; (NUMBER . (MESSAGE-ID (LIST-OF-NEWSGROUPS) 'ng))
1545 ;; 1.2. In elmo-folder-close, `temp-crosses' slot is cleared,
1546 ;; 1.3. In elmo-folder-mark-as-read, move crosspost entry
1547 ;; from `temp-crosses' slot to `elmo-crosspost-message-alist'.
1549 ;; 2. process crosspost alist.
1550 ;; 2.1. At elmo-folder-process-crosspost, setup `reads' slot from
1551 ;; `elmo-crosspost-message-alist'.
1552 ;; 2.2. remove crosspost entry for current newsgroup from
1553 ;; `elmo-crosspost-message-alist'.
1554 ;; 2.3. elmo-folder-list-unreads return unread message list according to
1556 ;; (There's a problem that if `elmo-folder-list-unreads'
1557 ;; never executed, crosspost information is thrown away.)
1558 ;; 2.4. In elmo-folder-close, `read' slot is cleared,
1560 (defun elmo-nntp-setup-crosspost-buffer (folder number)
1561 ;; 1.1. When message is fetched and is crossposted message,
1562 ;; it is remembered in `temp-crosses' slot.
1563 ;; temp-crosses slot is a list of cons cell:
1564 ;; (NUMBER . (MESSAGE-ID (LIST-OF-NEWSGROUPS) 'ng))
1565 (let (newsgroups crosspost-newsgroups message-id)
1567 (std11-narrow-to-header)
1568 (setq newsgroups (std11-fetch-field "newsgroups")
1569 message-id (std11-msg-id-string
1570 (car (std11-parse-msg-id-string
1571 (std11-fetch-field "message-id"))))))
1573 (when (setq crosspost-newsgroups
1575 (elmo-nntp-folder-group-internal folder)
1576 (elmo-nntp-parse-newsgroups newsgroups t)))
1577 (unless (assq number
1578 (elmo-nntp-folder-temp-crosses-internal folder))
1579 (elmo-nntp-folder-set-temp-crosses-internal
1581 (cons (cons number (list message-id crosspost-newsgroups 'ng))
1582 (elmo-nntp-folder-temp-crosses-internal folder))))))))
1584 (luna-define-method elmo-folder-close-internal ((folder elmo-nntp-folder))
1585 ;; 1.2. In elmo-folder-close, `temp-crosses' slot is cleared,
1586 (elmo-nntp-folder-set-temp-crosses-internal folder nil)
1587 (elmo-nntp-folder-set-reads-internal folder nil)
1590 (defun elmo-nntp-folder-update-crosspost-message-alist (folder numbers)
1591 ;; 1.3. In elmo-folder-mark-as-read, move crosspost entry
1592 ;; from `temp-crosses' slot to `elmo-crosspost-message-alist'.
1594 (dolist (number numbers)
1595 (when (setq elem (assq number
1596 (elmo-nntp-folder-temp-crosses-internal folder)))
1597 (unless (assoc (cdr (cdr elem)) elmo-crosspost-message-alist)
1598 (setq elmo-crosspost-message-alist
1599 (cons (cdr elem) elmo-crosspost-message-alist)))
1600 (elmo-nntp-folder-set-temp-crosses-internal
1602 (delq elem (elmo-nntp-folder-temp-crosses-internal folder)))))))
1604 (luna-define-method elmo-folder-mark-as-read ((folder elmo-nntp-folder)
1606 (elmo-nntp-folder-update-crosspost-message-alist folder numbers)
1609 (luna-define-method elmo-folder-process-crosspost ((folder elmo-nntp-folder)
1612 (elmo-nntp-folder-process-crosspost folder number-alist))
1614 (defun elmo-nntp-folder-process-crosspost (folder number-alist)
1615 ;; 2.1. At elmo-folder-process-crosspost, setup `reads' slot from
1616 ;; `elmo-crosspost-message-alist'.
1617 ;; 2.2. remove crosspost entry for current newsgroup from
1618 ;; `elmo-crosspost-message-alist'.
1619 (let (cross-deletes reads entity ngs)
1620 (dolist (cross elmo-crosspost-message-alist)
1622 (when (setq entity (rassoc (nth 0 cross) number-alist))
1623 (setq reads (cons (car entity) reads)))
1624 (when (setq entity (elmo-msgdb-overview-get-entity
1626 (elmo-folder-msgdb-internal folder)))
1627 (setq reads (cons (elmo-msgdb-overview-entity-get-number entity)
1630 (if (setq ngs (delete (elmo-nntp-folder-group-internal folder)
1632 (setcar (cdr cross) ngs)
1633 (setq cross-deletes (cons cross cross-deletes)))
1634 (setq elmo-crosspost-message-alist-modified t)))
1635 (dolist (dele cross-deletes)
1636 (setq elmo-crosspost-message-alist (delq
1638 elmo-crosspost-message-alist)))
1639 (elmo-nntp-folder-set-reads-internal folder reads)))
1641 (luna-define-method elmo-folder-list-unreads-internal
1642 ((folder elmo-nntp-folder) unread-marks mark-alist)
1643 ;; 2.3. elmo-folder-list-unreads return unread message list according to
1645 (let ((mark-alist (or mark-alist (elmo-msgdb-get-mark-alist
1646 (elmo-folder-msgdb-internal folder)))))
1647 (elmo-living-messages (delq nil
1650 (if (member (nth 1 x) unread-marks)
1653 (elmo-nntp-folder-reads-internal folder))))
1656 (product-provide (provide 'elmo-nntp) (require 'elmo-version))
1658 ;;; elmo-nntp.el ends here