Add arch taglines
[bpt/emacs.git] / lisp / gnus / nnwarchive.el
CommitLineData
c113de23
GM
1;;; nnwarchive.el --- interfacing with web archives
2;; Copyright (C) 1999, 2000 Free Software Foundation, Inc.
3
4;; Author: Shenghuo Zhu <zsh@cs.rochester.edu>
5;; Keywords: news egroups mail-archive
6
7;; This file is part of GNU Emacs.
8
9;; GNU Emacs is free software; you can redistribute it and/or modify
10;; it under the terms of the GNU General Public License as published
11;; by the Free Software Foundation; either version 2, or (at your
12;; option) any later version.
13
14;; GNU Emacs is distributed in the hope that it will be useful, but
15;; WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17;; General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
20;; along with GNU Emacs; see the file COPYING. If not, write to the
21;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22;; Boston, MA 02111-1307, USA.
23
24;;; Commentary:
25
26;; Note: You need to have `url' (w3 0.46) or greater version
27;; installed for this backend to work.
28
a1506d29 29;; Todo:
c113de23
GM
30;; 1. To support more web archives.
31;; 2. Generalize webmail to other MHonArc archive.
32
33;;; Code:
34
35(eval-when-compile (require 'cl))
36
37(require 'nnoo)
38(require 'message)
39(require 'gnus-util)
40(require 'gnus)
41(require 'gnus-bcklg)
42(require 'nnmail)
43(require 'mm-util)
44(require 'mail-source)
45(eval-when-compile
46 (ignore-errors
47 (require 'w3)
48 (require 'url)
49 (require 'w3-forms)
50 (require 'nnweb)))
51;; Report failure to find w3 at load time if appropriate.
52(eval '(progn
53 (require 'w3)
54 (require 'url)
55 (require 'w3-forms)
56 (require 'nnweb)))
57
58(nnoo-declare nnwarchive)
59
60(defvar nnwarchive-type-definition
61 '((egroups
62 (address . "www.egroups.com")
a1506d29
JB
63 (open-url
64 "http://www.egroups.com/login.cgi?&login_email=%s&login_password=%s"
c113de23 65 nnwarchive-login nnwarchive-passwd)
a1506d29 66 (list-url
c113de23
GM
67 "http://www.egroups.com/mygroups")
68 (list-dissect . nnwarchive-egroups-list)
69 (list-groups . nnwarchive-egroups-list-groups)
a1506d29 70 (xover-url
c113de23 71 "http://www.egroups.com/messages/%s/%d" group aux)
a1506d29 72 (xover-last-url
c113de23
GM
73 "http://www.egroups.com/messages/%s/" group)
74 (xover-page-size . 13)
75 (xover-dissect . nnwarchive-egroups-xover)
a1506d29 76 (article-url
c113de23
GM
77 "http://www.egroups.com/message/%s/%d?source=1" group article)
78 (article-dissect . nnwarchive-egroups-article)
79 (authentication . t)
80 (article-offset . 0)
81 (xover-files . nnwarchive-egroups-xover-files))
82 (mail-archive
83 (address . "www.mail-archive.com")
84 (open-url)
a1506d29 85 (list-url
c113de23
GM
86 "http://www.mail-archive.com/lists.html")
87 (list-dissect . nnwarchive-mail-archive-list)
88 (list-groups . nnwarchive-mail-archive-list-groups)
a1506d29 89 (xover-url
c113de23 90 "http://www.mail-archive.com/%s/mail%d.html" group aux)
a1506d29 91 (xover-last-url
c113de23
GM
92 "http://www.mail-archive.com/%s/maillist.html" group)
93 (xover-page-size)
94 (xover-dissect . nnwarchive-mail-archive-xover)
a1506d29 95 (article-url
c113de23
GM
96 "http://www.mail-archive.com/%s/msg%05d.html" group article1)
97 (article-dissect . nnwarchive-mail-archive-article)
98 (xover-files . nnwarchive-mail-archive-xover-files)
99 (authentication)
100 (article-offset . 1))))
101
102(defvar nnwarchive-default-type 'egroups)
103
104(defvoo nnwarchive-directory (nnheader-concat gnus-directory "warchive/")
105 "Where nnwarchive will save its files.")
106
107(defvoo nnwarchive-type nil
108 "The type of nnwarchive.")
109
110(defvoo nnwarchive-address ""
111 "The address of nnwarchive.")
112
113(defvoo nnwarchive-login nil
114 "Your login name for the group.")
115
116(defvoo nnwarchive-passwd nil
117 "Your password for the group.")
118
119(defvoo nnwarchive-groups nil)
120
121(defvoo nnwarchive-headers-cache nil)
122
123(defvoo nnwarchive-authentication nil)
124
125(defvoo nnwarchive-nov-is-evil nil)
126
127(defconst nnwarchive-version "nnwarchive 1.0")
128
129;;; Internal variables
130
131(defvoo nnwarchive-open-url nil)
132(defvoo nnwarchive-open-dissect nil)
133
134(defvoo nnwarchive-list-url nil)
135(defvoo nnwarchive-list-dissect nil)
136(defvoo nnwarchive-list-groups nil)
137
138(defvoo nnwarchive-xover-files nil)
139(defvoo nnwarchive-xover-url nil)
140(defvoo nnwarchive-xover-last-url nil)
141(defvoo nnwarchive-xover-dissect nil)
142(defvoo nnwarchive-xover-page-size nil)
143
144(defvoo nnwarchive-article-url nil)
145(defvoo nnwarchive-article-dissect nil)
146(defvoo nnwarchive-xover-files nil)
147(defvoo nnwarchive-article-offset 0)
148
149(defvoo nnwarchive-buffer nil)
150
151(defvoo nnwarchive-keep-backlog 300)
152(defvar nnwarchive-backlog-articles nil)
153(defvar nnwarchive-backlog-hashtb nil)
154
155(defvoo nnwarchive-headers nil)
156
157
158;;; Interface functions
159
160(nnoo-define-basics nnwarchive)
161
162(defun nnwarchive-set-default (type)
163 (let ((defs (cdr (assq type nnwarchive-type-definition)))
164 def)
165 (dolist (def defs)
a1506d29 166 (set (intern (concat "nnwarchive-" (symbol-name (car def))))
c113de23
GM
167 (cdr def)))))
168
169(defmacro nnwarchive-backlog (&rest form)
170 `(let ((gnus-keep-backlog nnwarchive-keep-backlog)
a1506d29 171 (gnus-backlog-buffer
c113de23
GM
172 (format " *nnwarchive backlog %s*" nnwarchive-address))
173 (gnus-backlog-articles nnwarchive-backlog-articles)
174 (gnus-backlog-hashtb nnwarchive-backlog-hashtb))
175 (unwind-protect
176 (progn ,@form)
177 (setq nnwarchive-backlog-articles gnus-backlog-articles
178 nnwarchive-backlog-hashtb gnus-backlog-hashtb))))
179(put 'nnwarchive-backlog 'lisp-indent-function 0)
180(put 'nnwarchive-backlog 'edebug-form-spec '(form body))
181
182(defun nnwarchive-backlog-enter-article (group number buffer)
183 (nnwarchive-backlog
184 (gnus-backlog-enter-article group number buffer)))
185
a1506d29 186(defun nnwarchive-get-article (article &optional group server buffer)
c113de23
GM
187 (if (numberp article)
188 (if (nnwarchive-backlog
a1506d29 189 (gnus-backlog-request-article group article
c113de23
GM
190 (or buffer nntp-server-buffer)))
191 (cons group article)
192 (let (contents)
193 (save-excursion
194 (set-buffer nnwarchive-buffer)
195 (goto-char (point-min))
196 (let ((article1 (- article nnwarchive-article-offset)))
197 (nnwarchive-url nnwarchive-article-url))
198 (setq contents (funcall nnwarchive-article-dissect group article)))
199 (when contents
200 (save-excursion
201 (set-buffer (or buffer nntp-server-buffer))
202 (erase-buffer)
203 (insert contents)
204 (nnwarchive-backlog-enter-article group article (current-buffer))
205 (nnheader-report 'nnwarchive "Fetched article %s" article)
206 (cons group article)))))
207 nil))
208
209(deffoo nnwarchive-retrieve-headers (articles &optional group server fetch-old)
210 (nnwarchive-possibly-change-server group server)
211 (if (or gnus-nov-is-evil nnwarchive-nov-is-evil)
212 (with-temp-buffer
213 (with-current-buffer nntp-server-buffer
214 (erase-buffer))
215 (let ((buf (current-buffer)) b e)
216 (dolist (art articles)
217 (nnwarchive-get-article art group server buf)
218 (setq b (goto-char (point-min)))
219 (if (search-forward "\n\n" nil t)
220 (forward-char -1)
221 (goto-char (point-max)))
222 (setq e (point))
223 (with-current-buffer nntp-server-buffer
224 (insert (format "221 %d Article retrieved.\n" art))
225 (insert-buffer-substring buf b e)
226 (insert ".\n"))))
227 'headers)
228 (setq nnwarchive-headers (cdr (assoc group nnwarchive-headers-cache)))
229 (save-excursion
230 (set-buffer nnwarchive-buffer)
231 (erase-buffer)
232 (funcall nnwarchive-xover-files group articles))
233 (save-excursion
234 (set-buffer nntp-server-buffer)
235 (erase-buffer)
236 (let (header)
237 (dolist (art articles)
238 (if (setq header (assq art nnwarchive-headers))
239 (nnheader-insert-nov (cdr header))))))
240 (let ((elem (assoc group nnwarchive-headers-cache)))
241 (if elem
242 (setcdr elem nnwarchive-headers)
243 (push (cons group nnwarchive-headers) nnwarchive-headers-cache)))
244 'nov))
245
246(deffoo nnwarchive-request-group (group &optional server dont-check)
247 (nnwarchive-possibly-change-server nil server)
248 (when (and (not dont-check) nnwarchive-list-groups)
249 (funcall nnwarchive-list-groups (list group))
250 (nnwarchive-write-groups))
251 (let ((elem (assoc group nnwarchive-groups)))
252 (cond
253 ((not elem)
254 (nnheader-report 'nnwarchive "Group does not exist"))
255 (t
256 (nnheader-report 'nnwarchive "Opened group %s" group)
257 (nnheader-insert
258 "211 %d %d %d %s\n" (or (cadr elem) 0) 1 (or (cadr elem) 0)
259 (prin1-to-string group))
260 t))))
261
262(deffoo nnwarchive-request-article (article &optional group server buffer)
263 (nnwarchive-possibly-change-server group server)
264 (nnwarchive-get-article article group server buffer))
265
266(deffoo nnwarchive-close-server (&optional server)
267 (when (and (nnwarchive-server-opened server)
268 (gnus-buffer-live-p nnwarchive-buffer))
269 (save-excursion
270 (set-buffer nnwarchive-buffer)
271 (kill-buffer nnwarchive-buffer)))
272 (nnwarchive-backlog
273 (gnus-backlog-shutdown))
274 (nnoo-close-server 'nnwarchive server))
275
276(deffoo nnwarchive-request-list (&optional server)
277 (nnwarchive-possibly-change-server nil server)
278 (save-excursion
279 (set-buffer nnwarchive-buffer)
280 (erase-buffer)
281 (if nnwarchive-list-url
282 (nnwarchive-url nnwarchive-list-url))
283 (if nnwarchive-list-dissect
284 (funcall nnwarchive-list-dissect))
285 (nnwarchive-write-groups)
286 (nnwarchive-generate-active))
287 t)
288
289(deffoo nnwarchive-open-server (server &optional defs connectionless)
290 (nnoo-change-server 'nnwarchive server defs)
291 (nnwarchive-init server)
292 (when nnwarchive-authentication
293 (setq nnwarchive-login
294 (or nnwarchive-login
295 (read-string
296 (format "Login at %s: " server)
297 user-mail-address)))
298 (setq nnwarchive-passwd
299 (or nnwarchive-passwd
300 (mail-source-read-passwd
a1506d29 301 (format "Password for %s at %s: "
c113de23
GM
302 nnwarchive-login server)))))
303 (unless nnwarchive-groups
304 (nnwarchive-read-groups))
305 (save-excursion
306 (set-buffer nnwarchive-buffer)
307 (erase-buffer)
308 (if nnwarchive-open-url
309 (nnwarchive-url nnwarchive-open-url))
310 (if nnwarchive-open-dissect
311 (funcall nnwarchive-open-dissect)))
312 t)
313
314(nnoo-define-skeleton nnwarchive)
315
316;;; Internal functions
317
318(defun nnwarchive-possibly-change-server (&optional group server)
319 (nnwarchive-init server)
320 (when (and server
321 (not (nnwarchive-server-opened server)))
322 (nnwarchive-open-server server)))
323
324(defun nnwarchive-read-groups ()
a1506d29 325 (let ((file (expand-file-name (concat "groups-" nnwarchive-address)
c113de23
GM
326 nnwarchive-directory)))
327 (when (file-exists-p file)
328 (with-temp-buffer
329 (insert-file-contents file)
330 (goto-char (point-min))
331 (setq nnwarchive-groups (read (current-buffer)))))))
332
333(defun nnwarchive-write-groups ()
a1506d29 334 (with-temp-file (expand-file-name (concat "groups-" nnwarchive-address)
c113de23
GM
335 nnwarchive-directory)
336 (prin1 nnwarchive-groups (current-buffer))))
337
338(defun nnwarchive-init (server)
339 "Initialize buffers and such."
340 (let ((type (intern server)) (defs nnwarchive-type-definition) def)
a1506d29 341 (cond
c113de23
GM
342 ((equal server "")
343 (setq type nnwarchive-default-type))
344 ((assq type nnwarchive-type-definition) t)
345 (t
346 (setq type nil)
347 (while (setq def (pop defs))
348 (when (equal (cdr (assq 'address (cdr def))) server)
349 (setq defs nil)
350 (setq type (car def))))
351 (unless type
352 (error "Undefined server %s" server))))
353 (setq nnwarchive-type type))
354 (unless (file-exists-p nnwarchive-directory)
355 (gnus-make-directory nnwarchive-directory))
356 (unless (gnus-buffer-live-p nnwarchive-buffer)
357 (setq nnwarchive-buffer
358 (save-excursion
359 (nnheader-set-temp-buffer
360 (format " *nnwarchive %s %s*" nnwarchive-type server)))))
361 (nnwarchive-set-default nnwarchive-type))
362
363(defun nnwarchive-encode-www-form-urlencoded (pairs)
364 "Return PAIRS encoded for forms."
365 (mapconcat
366 (function
367 (lambda (data)
368 (concat (w3-form-encode-xwfu (car data)) "="
369 (w3-form-encode-xwfu (cdr data)))))
370 pairs "&"))
371
372(defun nnwarchive-fetch-form (url pairs)
373 (let ((url-request-data (nnwarchive-encode-www-form-urlencoded pairs))
374 (url-request-method "POST")
375 (url-request-extra-headers
376 '(("Content-type" . "application/x-www-form-urlencoded"))))
377 (nnweb-insert url))
378 t)
379
380(defun nnwarchive-eval (expr)
381 (cond
382 ((consp expr)
383 (cons (nnwarchive-eval (car expr)) (nnwarchive-eval (cdr expr))))
384 ((symbolp expr)
385 (eval expr))
386 (t
387 expr)))
388
389(defun nnwarchive-url (xurl)
390 (mm-with-unibyte-current-buffer
391 (let ((url-confirmation-func 'identity)
392 (url-cookie-multiple-line nil))
a1506d29 393 (cond
c113de23
GM
394 ((eq (car xurl) 'post)
395 (pop xurl)
396 (nnwarchive-fetch-form (car xurl) (nnwarchive-eval (cdr xurl))))
397 (t
398 (nnweb-insert (apply 'format (nnwarchive-eval xurl))))))))
a1506d29 399
c113de23
GM
400(defun nnwarchive-generate-active ()
401 (save-excursion
402 (set-buffer nntp-server-buffer)
403 (erase-buffer)
404 (dolist (elem nnwarchive-groups)
405 (insert (prin1-to-string (car elem))
406 " " (number-to-string (or (cadr elem) 0)) " 1 y\n"))))
407
408(defun nnwarchive-paged (articles)
409 (let (art narts next)
410 (while (setq art (pop articles))
411 (when (and (>= art (or next 0))
412 (not (assq art nnwarchive-headers)))
413 (push art narts)
414 (setq next (+ art nnwarchive-xover-page-size))))
415 narts))
416
417;; egroups
418
419(defun nnwarchive-egroups-list-groups (groups)
420 (save-excursion
421 (let (articles)
422 (set-buffer nnwarchive-buffer)
a1506d29 423 (dolist (group groups)
c113de23
GM
424 (erase-buffer)
425 (nnwarchive-url nnwarchive-xover-last-url)
426 (goto-char (point-min))
427 (when (re-search-forward "of \\([0-9]+\\)[ \t\n\r]*</title>" nil t)
a1506d29 428 (setq articles (string-to-number (match-string 1))))
c113de23
GM
429 (let ((elem (assoc group nnwarchive-groups)))
430 (if elem
431 (setcar (cdr elem) articles)
432 (push (list group articles "") nnwarchive-groups)))
433 (setq nnwarchive-headers (cdr (assoc group nnwarchive-headers-cache)))
434 (nnwarchive-egroups-xover group)
435 (let ((elem (assoc group nnwarchive-headers-cache)))
436 (if elem
437 (setcdr elem nnwarchive-headers)
438 (push (cons group nnwarchive-headers) nnwarchive-headers-cache)))))))
439
440(defun nnwarchive-egroups-list ()
441 (let ((case-fold-search t)
442 group description elem articles)
443 (goto-char (point-min))
a1506d29 444 (while
c113de23
GM
445 (re-search-forward "href=\"/group/\\([^/\"\> ]+\\)" nil t)
446 (setq group (match-string 1)
447 description (match-string 2))
448 (if (setq elem (assoc group nnwarchive-groups))
449 (setcar (cdr elem) 0)
450 (push (list group articles description) nnwarchive-groups))))
451 t)
452
453(defun nnwarchive-egroups-xover (group)
454 (let (article subject from date)
455 (goto-char (point-min))
456 (while (re-search-forward
457 "<a href=\"/group/\\([^/]+\\)/\\([0-9]+\\)[^>]+>\\([^<]+\\)<"
458 nil t)
459 (setq group (match-string 1)
460 article (string-to-number (match-string 2))
461 subject (match-string 3))
462 (forward-line 1)
463 (unless (assq article nnwarchive-headers)
464 (if (looking-at "<td[^>]+><font[^>]+>\\([^<]+\\)</font>")
465 (setq from (match-string 1)))
466 (forward-line 1)
467 (if (looking-at "<td[^>]+><font[^>]+>\\([^<]+\\)</font>")
468 (setq date (identity (match-string 1))))
469 (push (cons
470 article
471 (make-full-mail-header
a1506d29 472 article
c113de23
GM
473 (nnweb-decode-entities-string subject)
474 (nnweb-decode-entities-string from)
475 date
476 (concat "<" group "%"
a1506d29 477 (number-to-string article)
c113de23
GM
478 "@egroup.com>")
479 ""
480 0 0 "")) nnwarchive-headers))))
481 nnwarchive-headers)
482
483(defun nnwarchive-egroups-article (group articles)
484 (goto-char (point-min))
485 (if (search-forward "<pre>" nil t)
486 (delete-region (point-min) (point)))
487 (goto-char (point-max))
488 (if (search-backward "</pre>" nil t)
489 (delete-region (point) (point-max)))
490 (goto-char (point-min))
491 (while (re-search-forward "<a[^>]+>\\([^<]+\\)</a>" nil t)
492 (replace-match "\\1"))
493 (nnweb-decode-entities)
494 (buffer-string))
495
496(defun nnwarchive-egroups-xover-files (group articles)
497 (let (aux auxs)
498 (setq auxs (nnwarchive-paged (sort articles '<)))
499 (while (setq aux (pop auxs))
500 (goto-char (point-max))
501 (nnwarchive-url nnwarchive-xover-url))
502 (if nnwarchive-xover-dissect
503 (nnwarchive-egroups-xover group))))
504
505;; mail-archive
506
507(defun nnwarchive-mail-archive-list-groups (groups)
508 (save-excursion
509 (let (articles)
510 (set-buffer nnwarchive-buffer)
511 (dolist (group groups)
512 (erase-buffer)
513 (nnwarchive-url nnwarchive-xover-last-url)
514 (goto-char (point-min))
515 (when (re-search-forward "msg\\([0-9]+\\)\\.html" nil t)
516 (setq articles (1+ (string-to-number (match-string 1)))))
517 (let ((elem (assoc group nnwarchive-groups)))
518 (if elem
519 (setcar (cdr elem) articles)
520 (push (list group articles "") nnwarchive-groups)))
521 (setq nnwarchive-headers (cdr (assoc group nnwarchive-headers-cache)))
522 (nnwarchive-mail-archive-xover group)
523 (let ((elem (assoc group nnwarchive-headers-cache)))
524 (if elem
525 (setcdr elem nnwarchive-headers)
a1506d29 526 (push (cons group nnwarchive-headers)
c113de23
GM
527 nnwarchive-headers-cache)))))))
528
529(defun nnwarchive-mail-archive-list ()
530 (let ((case-fold-search t)
531 group description elem articles)
532 (goto-char (point-min))
533 (while (re-search-forward "<a href=\"\\([^/]+\\)/\">\\([^>]+\\)<" nil t)
534 (setq group (match-string 1)
535 description (match-string 2))
536 (forward-line 1)
537 (setq articles 0)
538 (if (setq elem (assoc group nnwarchive-groups))
539 (setcar (cdr elem) articles)
540 (push (list group articles description) nnwarchive-groups))))
541 t)
542
543(defun nnwarchive-mail-archive-xover (group)
544 (let (article subject from date)
545 (goto-char (point-min))
546 (while (re-search-forward
547 "<A[^>]*HREF=\"msg\\([0-9]+\\)\\.html[^>]+>\\([^<]+\\)<"
548 nil t)
549 (setq article (1+ (string-to-number (match-string 1)))
550 subject (match-string 2))
551 (forward-line 1)
552 (unless (assq article nnwarchive-headers)
553 (if (looking-at "<UL><LI><EM>From</EM>:\\([^&]+\\)<\\([^&]+\\)>")
554 (progn
555 (setq from (match-string 1)
556 date (identity (match-string 2))))
557 (setq from "" date ""))
558 (push (cons
559 article
560 (make-full-mail-header
a1506d29 561 article
c113de23
GM
562 (nnweb-decode-entities-string subject)
563 (nnweb-decode-entities-string from)
564 date
565 (format "<%05d%%%s>\n" (1- article) group)
566 ""
567 0 0 "")) nnwarchive-headers))))
568 nnwarchive-headers)
569
570(defun nnwarchive-mail-archive-xover-files (group articles)
571 (unless nnwarchive-headers
572 (erase-buffer)
573 (nnwarchive-url nnwarchive-xover-last-url)
574 (goto-char (point-min))
575 (nnwarchive-mail-archive-xover group))
576 (let ((minart (apply 'min articles))
577 (min (apply 'min (mapcar 'car nnwarchive-headers)))
578 (aux 2))
579 (while (> min minart)
580 (erase-buffer)
581 (nnwarchive-url nnwarchive-xover-url)
582 (nnwarchive-mail-archive-xover group)
583 (setq min (apply 'min (mapcar 'car nnwarchive-headers))))))
584
585(defvar nnwarchive-caesar-translation-table nil
586 "Modified rot13 table. tr/@A-Z[a-z/N-Z[@A-Mn-za-m/.")
587
588(defun nnwarchive-make-caesar-translation-table ()
589 "Create modified rot13 table. tr/@A-Z[a-z/N-Z[@A-Mn-za-m/."
590 (let ((i -1)
591 (table (make-string 256 0))
592 (a (mm-char-int ?a))
593 (A (mm-char-int ?A)))
594 (while (< (incf i) 256)
595 (aset table i i))
596 (concat
597 (substring table 0 (1- A))
598 (substring table (+ A 13) (+ A 27))
599 (substring table (1- A) (+ A 13))
600 (substring table (+ A 27) a)
601 (substring table (+ a 13) (+ a 26))
602 (substring table a (+ a 13))
603 (substring table (+ a 26) 255))))
604
605(defun nnwarchive-from-r13 (from-r13)
606 (when from-r13
607 (with-temp-buffer
608 (insert from-r13)
609 (let ((message-caesar-translation-table
610 (or nnwarchive-caesar-translation-table
a1506d29 611 (setq nnwarchive-caesar-translation-table
c113de23
GM
612 (nnwarchive-make-caesar-translation-table)))))
613 (message-caesar-region (point-min) (point-max))
614 (buffer-string)))))
615
616(defun nnwarchive-mail-archive-article (group article)
a1506d29
JB
617 (let (p refs url mime e
618 from subject date id
c113de23 619 done
379a66de 620 (case-fold-search t))
c113de23
GM
621 (save-restriction
622 (goto-char (point-min))
623 (when (search-forward "X-Head-End" nil t)
624 (beginning-of-line)
625 (narrow-to-region (point-min) (point))
626 (nnweb-decode-entities)
627 (goto-char (point-min))
628 (while (search-forward "<!--X-" nil t)
629 (replace-match ""))
630 (goto-char (point-min))
631 (while (search-forward " -->" nil t)
632 (replace-match ""))
a1506d29 633 (setq from
c113de23 634 (or (mail-fetch-field "from")
a1506d29 635 (nnwarchive-from-r13
c113de23
GM
636 (mail-fetch-field "from-r13"))))
637 (setq date (mail-fetch-field "date"))
638 (setq id (mail-fetch-field "message-id"))
639 (setq subject (mail-fetch-field "subject"))
640 (goto-char (point-max))
641 (widen))
642 (when (search-forward "<ul>" nil t)
643 (forward-line)
644 (delete-region (point-min) (point))
645 (search-forward "</ul>" nil t)
646 (end-of-line)
647 (narrow-to-region (point-min) (point))
648 (nnweb-remove-markup)
649 (nnweb-decode-entities)
650 (goto-char (point-min))
651 (delete-blank-lines)
652 (when from
653 (message-remove-header "from")
654 (goto-char (point-max))
655 (insert "From: " from "\n"))
656 (when subject
657 (message-remove-header "subject")
658 (goto-char (point-max))
659 (insert "Subject: " subject "\n"))
660 (when id
661 (goto-char (point-max))
662 (insert "X-Message-ID: <" id ">\n"))
663 (when date
664 (message-remove-header "date")
665 (goto-char (point-max))
666 (insert "Date: " date "\n"))
667 (goto-char (point-max))
668 (widen)
669 (insert "\n"))
a1506d29 670 (setq p (point))
c113de23
GM
671 (when (search-forward "X-Body-of-Message" nil t)
672 (forward-line)
673 (delete-region p (point))
674 (search-forward "X-Body-of-Message-End" nil t)
675 (beginning-of-line)
676 (save-restriction
677 (narrow-to-region p (point))
678 (goto-char (point-min))
679 (if (> (skip-chars-forward "\040\n\r\t") 0)
680 (delete-region (point-min) (point)))
681 (while (not (eobp))
a1506d29
JB
682 (cond
683 ((looking-at "<PRE>\r?\n?")
c113de23
GM
684 (delete-region (match-beginning 0) (match-end 0))
685 (setq p (point))
686 (when (search-forward "</PRE>" nil t)
687 (delete-region (match-beginning 0) (match-end 0))
688 (save-restriction
689 (narrow-to-region p (point))
690 (nnweb-remove-markup)
691 (nnweb-decode-entities)
692 (goto-char (point-max)))))
693 ((looking-at "<P><A HREF=\"\\([^\"]+\\)")
694 (setq url (match-string 1))
a1506d29 695 (delete-region (match-beginning 0)
c113de23 696 (progn (forward-line) (point)))
a1506d29 697 ;; I hate to download the url encode it, then immediately
c113de23
GM
698 ;; decode it.
699 ;; FixMe: Find a better solution to attach the URL.
700 ;; Maybe do some hack in external part of mml-generate-mim-1.
701 (insert "<#part>"
702 "\n--\nExternal: \n"
a1506d29 703 (format "<URL:http://www.mail-archive.com/%s/%s>"
c113de23
GM
704 group url)
705 "\n--\n"
706 "<#/part>")
707 (setq mime t))
708 (t
709 (setq p (point))
710 (insert "<#part type=\"text/html\" disposition=inline>")
711 (goto-char
a1506d29
JB
712 (if (re-search-forward
713 "[\040\n\r\t]*<PRE>\\|[\040\n\r\t]*<P><A HREF=\""
c113de23
GM
714 nil t)
715 (match-beginning 0)
716 (point-max)))
717 (insert "<#/part>")
718 (setq mime t)))
719 (setq p (point))
720 (if (> (skip-chars-forward "\040\n\r\t") 0)
721 (delete-region p (point))))
722 (goto-char (point-max))))
723 (setq p (point))
724 (when (search-forward "X-References-End" nil t)
725 (setq e (point))
726 (beginning-of-line)
727 (search-backward "X-References" p t)
728 (while (re-search-forward "msg\\([0-9]+\\)\\.html" e t)
729 (push (concat "<" (match-string 1) "%" group ">") refs)))
730 (delete-region p (point-max))
731 (goto-char (point-min))
732 (insert (format "Message-ID: <%05d%%%s>\n" (1- article) group))
733 (when refs
734 (insert "References:")
735 (while refs
736 (insert " " (pop refs)))
737 (insert "\n"))
738 (when mime
a1506d29 739 (unless (looking-at "$")
c113de23
GM
740 (search-forward "\n\n" nil t)
741 (forward-line -1))
742 (narrow-to-region (point) (point-max))
743 (insert "MIME-Version: 1.0\n"
744 (prog1
745 (mml-generate-mime)
746 (delete-region (point-min) (point-max))))
747 (widen)))
748 (buffer-string)))
749
750(provide 'nnwarchive)
751
ab5796a9 752;;; arch-tag: 1ab7a15c-777a-40e0-95c0-0c41b3963578
c113de23 753;;; nnwarchive.el ends here