comparison my-news.el @ 78:0abfe9bf83a0

merge
author Henry S. Thompson <ht@inf.ed.ac.uk>
date Thu, 25 Sep 2025 17:57:05 +0100
parents 0508101db40f
children
comparison
equal deleted inserted replaced
77:62fb1a21629a 78:0abfe9bf83a0
1 (message "my-news")
2 ; (debug-on-entry 'gnus-start-news-server)
3 (setq ; see ~/.xemacs/gnus.el for local settings
4 gnus-nntp-server nil ; override local default
5 )
6
7 (setq gnus-use-scoring nil ; not used yet
8 gnus-summary-gather-subject-limit nil
9 gnus-thread-sort-functions
10 '(gnus-thread-sort-by-number gnus-thread-sort-by-simpl-subject)
11 gnus-summary-line-format "%U%R%5N%I%(%[%4L: %-12,12A%]%) %s\n"
12 gnus-summary-make-false-root 'none
13 gnus-mime-display-multipart-related-as-mixed t
14 gnus-simplify-subject-regexp "^\\(re[:;.]\\| \\|fwd:\\)*")
15
16 (defsubst gnus-trim-simplify-subject (text)
17 (if (string-match gnus-simplify-subject-regexp text)
18 (substring text (match-end 0))
19 text))
20
21 (defun gnus-thread-sort-by-simpl-subject (h1 h2)
22 "sort by slightly simplified subject"
23 ; (message (format "%s:%s %s:%s" (mail-header-number (gnus-thread-header h1))(mail-header-subject (gnus-thread-header h1))(mail-header-number (gnus-thread-header h2))(mail-header-subject (gnus-thread-header h2))))
24 (let ((case-fold-search t))
25 (let ((result
26 (string-lessp
27 (downcase (gnus-trim-simplify-subject (mail-header-subject
28 (gnus-thread-header h1))))
29 (downcase (gnus-trim-simplify-subject (mail-header-subject
30 (gnus-thread-header h2)))))))
31 ; (message (format " %s\n" result))
32 result)))
33
34
35 (setq nnfolder-get-new-mail nil
36 nnfolder-inhibit-expiry t
37 gnus-secondary-select-methods
38 '((nnml "ht"
39 (gnus-show-threads nil)
40 (gnus-article-sort-functions
41 (gnus-article-sort-by-subject gnus-article-sort-by-date))
42 )))
43 ;;; fixup clarinews
44 ;(autoload 'gnus-clarinews-fun "clari-clean" "Clean ClariNews articles" t)
45 ;(add-hook 'gnus-article-prepare-hook 'gnus-clarinews-fun)
46
47
48 (defun gnus-Subject-sort-by-subject-and-date (reverse)
49 "Sort subject display buffer by subject alphabetically. `Re:'s are ignored.
50 If case-fold-search is non-nil, case of letters is ignored. Date is used
51 if subjects are equal
52 Argument REVERSE means reverse order."
53 (interactive "P")
54 (gnus-summary-sort-summary
55 (function
56 (lambda (a b)
57 (let ((s-a (gnus-trim-simplify-subject (nntp-header-subject a)))
58 (s-b (gnus-trim-simplify-subject (nntp-header-subject b)))
59 )
60 (or (gnus-string-lessp s-a s-b)
61 (and (gnus-string-equal s-a s-b)
62 (gnus-date-lessp (nntp-header-date a)
63 (nntp-header-date b)))))))
64 reverse
65 ))
66
67 ;(require 'util-mde) ; for string-replace-regexp-2
68
69
70 (defun gnus-string-equal (a b)
71 "Return T if first arg string is equal than second in lexicographic order.
72 If case-fold-search is non-nil, case of letters is ignored."
73 (if case-fold-search
74 (string-equal (downcase a) (downcase b)) (string-equal a b)))
75
76 (defun gnus-Group-update-and-vanish ()
77 "update newsrc and restore config pre-group selection"
78 (interactive)
79 (gnus-group-force-update)
80 (if gnus-pre-config
81 (set-window-configuration gnus-pre-config))
82 ; (setq gnus-pre-config nil)
83 )
84
85 ;; Database stuff
86 (defun open-white ()
87 (setq whitelist-db (open-database (concat my-mail-dir "/white") 'berkeley-db)))
88 (defun save-white ()
89 (close-database whitelist-db)
90 (open-white))
91
92 (defun open-ad ()
93 (setq adlist-db (open-database (concat my-mail-dir "/ad") 'berkeley-db)))
94
95 (defun save-ad ()
96 (close-database adlist-db)
97 (open-ad))
98
99 (defun open-quaker ()
100 (setq quaker-db (open-database (concat my-mail-dir "/quaker") 'berkeley-db)))
101 (defun save-quaker ()
102 (close-database quaker-db)
103 (open-quaker))
104
105 (defvar database-names '(whitelist-db adlist-db quaker-db) "sic")
106
107 (defun db-status (&optional name)
108 "Check on the whereabouts of a name"
109 (interactive)
110 (let ((addr
111 (or name
112 (progn
113 (gnus-summary-goto-article (gnus-summary-article-number))
114 (get-canonical-from-addr (get-current-from-components)))))
115 res)
116 (dolist (dbn database-names)
117 (if (get-database addr (eval dbn))
118 (setq res (cons dbn res))))
119 (if name
120 res
121 (message "%s" res))))
122
123 (defun add-white (&optional dontAddToBBDB)
124 "While reading an article, add to whitelist"
125 (interactive "P")
126 (gnus-summary-goto-article (gnus-summary-article-number))
127 (do-add-white (gnus-fetch-original-field "From") dontAddToBBDB))
128
129 (defun do-add-white (addr &optional dontAddToBBDB)
130 (let* ((components (gnus-extract-address-components addr))
131 (addr (get-canonical-from-addr components)))
132 (if (not dontAddToBBDB)
133 (let ((bbdb-no-duplicates-p t))
134 (condition-case nil
135 (bbdb-create-internal (car components) nil
136 (cadr components) nil nil nil)
137 (error
138 ;; OK, just means already present
139 ))))
140 (if (new-white addr)
141 (save-white))))
142
143 (defun add-ad ()
144 (interactive)
145 (gnus-summary-goto-article (gnus-summary-article-number))
146 (let ((addr (get-current-from-addr)))
147 (if (or (not (get-database addr whitelist-db))
148 (yes-or-no-p "Already white, really convert to ad?"))
149 (if (new-ad addr)
150 (save-ad)))))
151
152 (defun add-quaker()
153 (interactive)
154 (let ((addr (get-addr-before-point)))
155 (when (new-quaker addr)
156 (save-quaker))
157 (quaker-sig-maybe)))
158
159 ; not needed anymore because of gnus-posting-styles (q.v. in mail-from-*)
160 (defun quaker-sig-if-to-quaker ()
161 (let ((message-options))
162 (save-excursion (message-options-set-recipient))
163 (let* ((recipStr (message-options-get 'message-recipients))
164 (recips (split-string (downcase recipStr)
165 ",[ \f\t\n\r\v]+" t)))
166 (while (and recips
167 (not (quaker-sig-if-quaker-1 (car recips))))
168 (setq recips (cdr recips))))))
169
170 (defun to-quaker-p ()
171 (let ((message-options))
172 (save-excursion (message-options-set-recipient))
173 (let* ((recipStr (message-options-get 'message-recipients))
174 (recips (split-string (downcase recipStr)
175 ",[ \f\t\n\r\v]+" t)))
176 (while (and recips
177 (not (get-database (car recips) quaker-db)))
178 (setq recips (cdr recips)))
179 (not (null recips)))))
180
181 (defun quaker-sig-if-quaker ()
182 (quaker-sig-if-quaker-1 (get-addr-before-point)))
183
184 (defun quaker-sig-if-quaker-1 (addr)
185 (if (get-database addr quaker-db)
186 (progn (quaker-sig-maybe)
187 t)))
188
189 (defun kill-white ()
190 (interactive)
191 (gnus-summary-goto-article (gnus-summary-article-number))
192 (let ((addr (downcase (get-current-from-addr))))
193 (rem-white addr)))
194
195 (defun kill-ad ()
196 (interactive)
197 (gnus-summary-goto-article (gnus-summary-article-number))
198 (let ((addr (downcase (get-current-from-addr))))
199 (rem-ad addr)))
200
201 (defun get-from-gnus-addr ()
202 (get-from-addr (gnus-fetch-field "From")))
203
204 (defun get-from-addr (addr)
205 (get-canonical-from-addr (gnus-extract-address-components addr)))
206
207 (defun get-canonical-from-addr (components)
208 (downcase (cadr components)))
209
210 (defun get-current-from-addr ()
211 (with-current-buffer gnus-article-buffer
212 (get-from-gnus-addr)))
213
214 (defun get-current-from-components ()
215 (with-current-buffer gnus-article-buffer
216 (gnus-extract-address-components (gnus-fetch-field "From"))))
217
218 (defun get-addr-before-point ()
219 (let ((cur (point)))
220 (save-excursion
221 (get-from-addr (buffer-substring (+ (search-backward " ") 1) cur)))
222 ))
223
224 (defun blacken-and-delete (group)
225 ;; mis-named now
226 ;; this is part of the expiry processing for xxxSPAM groups, and
227 ;; actually whitens the from addresses of #-marked articles
228 ;; The return value is crucial (and crucially outside of the scope of the if)
229 (if (memq number
230 (with-current-buffer gnus-summary-buffer
231 gnus-newsgroup-processable))
232 (let ((addr (get-from-gnus-addr)))
233 (new-white addr)))
234 'delete)
235
236 (defun unwhiten-and-delete (group)
237 ;; unused except in stale groups -- usable as an expiry
238 (if (memq number
239 (with-current-buffer gnus-summary-buffer
240 gnus-newsgroup-processable))
241 (let ((addr (get-from-gnus-addr)))
242 (remove-database addr whitelist-db)))
243 'delete)
244
245 (defun known-black (list)
246 (if (get-database (get-from-gnus-addr) blacklist-db)
247 list))
248
249 (defun white-spam (list)
250 (if (or (equal (get-database (get-from-gnus-addr) whitelist-db) "t")
251 (let ((case-fold-search t)
252 (subj (gnus-fetch-field "Subject"))
253 (from (get-from-gnus-addr)))
254 (or
255 (and subj (string-match white-subjects subj))
256 (and from
257 (let ((fromDom (substring from (+ 1 (search "@" from)))))
258 (and fromDom (member fromDom white-domains)))))))
259 list))
260
261 (defun ad-spam (list)
262 (if (let ((from (get-from-gnus-addr)))
263 (or
264 (equal (get-database from adlist-db) "t")
265 (and from
266 (let ((fromDom (substring from (+ 1 (search "@" from)))))
267 (and fromDom (member fromDom ad-domains))))
268 ))
269 list))
270
271 (defun bogoNote (group)
272 (if (memq number
273 (with-current-buffer gnus-summary-buffer
274 gnus-newsgroup-processable))
275 (let ((addr (get-from-gnus-addr)))
276 (new-white addr)))
277 (shell-command-on-region (point-min) (point-max)
278 "/afs/inf.ed.ac.uk/user/h/ht/share/bin/local/makeBogo")
279 'delete)
280
281 (defun whiten-recip ()
282 ;;; a hook for outgoing mail
283 (let* ((to (message-fetch-field "To"))
284 (cc (message-fetch-field "cc"))
285 (msg-recipients (concat to (and to cc ", ") cc))
286 (recips (message-tokenize-header msg-recipients))
287 (res (mapcar (function do-add-white) recips)))
288 (while (and res (not (car res)))
289 (setq res (cdr res)))
290 (if res (save-white))))
291
292
293 (defun new-white (addr)
294 (if (get-database addr whitelist-db)
295 nil
296 (put-database addr "t" whitelist-db)
297 (comint-exec (get-buffer-create "*new-white*")
298 "new-white" shell-file-name
299 nil (list shell-command-switch
300 (format "echo '%s' >> %s/new-white.txt" addr my-mail-dir))) t))
301
302 (defun new-ad (addr)
303 (new-white addr)
304 (if (get-database addr adlist-db)
305 nil
306 (put-database addr "t" adlist-db)
307 t))
308
309 (defun rem-ad (addr)
310 (remove-database addr adlist-db)
311 (save-ad))
312
313 (defun new-quaker (addr)
314 (if (get-database addr quaker-db)
315 nil
316 (put-database addr "t" quaker-db)
317 t))
318
319 (defun rem-white (addr)
320 (remove-database (downcase addr) whitelist-db)
321 (save-white))
322
323 (defun bogoOK (group)
324 (shell-command-on-region (point-min) (point-max)
325 "/afs/inf.ed.ac.uk/user/h/ht/share/bin/local/makeNonBogo")
326 'delete)
327
328 (defun del-dups ()
329 (interactive)
330 (gnus-summary-sort-by-subject)
331 (gnus-summary-clear-mark-forward 1)
332 (goto-char (point-min))
333 (let ((pos))
334 (while (setq pos (search-forward "] " nil t))
335 (end-of-line)
336 (let ((subj (buffer-substring pos (point))))
337 (unless (equal subj "")
338 (let ((target (if (< (length subj) 26)
339 (concat "] " subj "\n")
340 (concat "] " (substring subj 0 25))))
341 (done 0)
342 (case-fold-search nil))
343 (while (and (= done 0)
344 (search-forward target nil t))
345 (forward-char -3)
346 (setq done (gnus-summary-mark-as-read-forward 1))))))))
347 (gnus-summary-limit-to-unread)
348 (gnus-summary-sort-by-original))
349
350 (defun mark-and-mark (n)
351 (interactive "p")
352 (while (>= n 1)
353 (gnus-summary-mark-as-read)
354 (gnus-summary-mark-as-processable 1)
355 (setq n (- n 1))))
356
357 (defun split-on-whole-field (field pat list)
358 (goto-char (point-max))
359 (let ((hit (assq pat wsp-cache))
360 rpat)
361 (if hit
362 (setq rpat (cdr hit))
363 (setq rpat
364 (concat "^"
365 field
366 ":\\s-*"
367 (if (stringp pat)
368 pat
369 (cdr (assq pat
370 nnmail-split-abbrev-alist)))
371 "$"))
372 (setq wsp-cache (cons (cons pat rpat) wsp-cache)))
373 (if (re-search-backward rpat nil t)
374 list)))
375
376 (defun ht-gnus-summary-delete-forward ()
377 "REAL delete for nnmail gnus"
378 (interactive)
379 (gnus-summary-delete-article)
380 (gnus-summary-next-unread-article))
381
382 ;; run the first time we make a summary window
383 (defun gnus-summary-mode-fun1 ()
384 "install ht's mods"
385 (define-key gnus-summary-mode-map "D" 'ht-gnus-summary-delete-forward)
386 (define-key gnus-summary-mode-map "~" 'mark-and-mark)
387 (define-key gnus-summary-mode-map "\M-d" 'gnus-edit-and-move-to-diary)
388 (define-key gnus-summary-mode-map "\M-e" 'gnus-extract-attachment)
389 (define-key gnus-summary-mode-map "\M-w" 'add-white)
390 (define-key gnus-summary-mode-map [(control meta w)] 'copy-region-to-kill)
391 (define-key gnus-summary-mode-map "\M-h" 'showMPAhtml)
392 ;(define-key gnus-summary-mode-map [(control meta w)] 'kill-white)
393 (define-key gnus-summary-mode-map "\M-a" 'add-ad)
394 (define-key gnus-summary-mode-map "\M-n" 'ht-next-unseen-maybe)
395 (define-key gnus-summary-mode-map "\M-c" 'ht-catchup-and-next-unseen)
396 (define-key gnus-summary-mime-map "O" 'ht-article-save-parts)
397 (define-key gnus-summary-backend-map "M" 'ht-move-to-pers)
398 (remove-hook 'gnus-summary-mode-hook 'gnus-summary-mode-fun1))
399
400 (defun message-mode-fun1 ()
401 (define-key message-mode-map [(control meta q)] 'add-quaker)
402 (remove-hook 'message-mode-hook 'message-mode-fun1))
403
404 (defun ht-catchup-and-next-unseen ()
405 (interactive)
406 (when (gnus-summary-catchup nil t nil 'fast)
407 (gnus-summary-exit)
408 (previous-line 1)
409 (ht-next-with-unseen 1)))
410
411 (defun ht-next-unseen-maybe (n)
412 (interactive "p")
413 (cond
414 ((eq (gnus-summary-next-unread-subject n) n)
415 (gnus-summary-exit)
416 (previous-line 1)
417 (if (ht-next-with-unseen n)
418 (ht-read-group-unseen-only)))))
419
420 (defun ht-gnus-pers-refresh (n)
421 (interactive "p")
422 (let ((gn (concat "nnml+ht:pers-"
423 (format-time-string "%Y-%m" (current-time))))
424 (jr ht-gnus-just-read))
425 (gnus-group-get-new-news)
426 (let ((nn (gnus-number-of-unseen-articles-in-group gn)))
427 (gnus-group-goto-group gn)
428 (cond
429 ((> nn 0)
430 (gnus-group-read-group nn))
431 ((> n 1)
432 (let ((gnus-auto-select-subject
433 (lambda ()
434 (goto-char (point-max))
435 (previous-line 1))))
436 (gnus-group-read-group nil t)))
437 (t (goto-char (point-min))
438 (ht-next-with-unseen 1))))
439 (message "read: %s" ht-gnus-just-read)
440 ))
441
442 (defun no-select ()
443 (if (member gnus-newsgroup-name no-select-groups)
444 (progn (make-variable-buffer-local 'gnus-auto-select-first)
445 (setq gnus-auto-select-first nil))))
446
447 (defun showMPAhtml ()
448 "Show the text/html parts of an multipart/alternative message using lynx"
449 (interactive)
450 (gnus-summary-select-article)
451 (with-current-buffer gnus-original-article-buffer
452 (shell-command-on-region (point-min) (point-max)
453 ;(expand-file-name
454 "/home/ht/bin/showMPA.sh"
455 ;)
456 ))
457 )
458
459
460 ;; run the first time we make a group window
461 (defun gnus-group-mode-fun1 ()
462 "install ht's mods"
463 (require 'gnus-msg)
464 (define-key gnus-group-mode-map "\M-\C-g" 'ht-gnus-pers-refresh)
465 (define-key gnus-group-mode-map "\M-n" 'ht-next-with-unseen)
466 (define-key gnus-group-mode-map "\M-p" 'ht-previous-with-unseen)
467 (define-key gnus-group-mode-map "\M- " 'ht-read-group-unseen-only)
468 (define-key gnus-send-bounce-map "R" 'resend-to-schemadev)
469 (define-key gnus-send-bounce-map "x" 'flush-all-nogoods)
470 (remove-hook 'gnus-group-mode-hook 'gnus-group-mode-fun1))
471
472 (defun flush-all-nogoods ()
473 (interactive)
474 (while (re-search-forward
475 "] \\(\\(Returned\\|\\([Uu]n\\|[Nn]on-?\\)deliver\\(able\\|ed\\)\\)\\( [Mm]ail\\|:?\\)\\|DELIVERY FAILURE\\|Delivery \\(Notification: Delivery has failed\\|Status Notification .\\(Failure\\|Delay\\).\\)\\|failure \\(notice\\|delivery\\)\\)"
476 nil t)
477 (gnus-summary-mark-as-read)
478 (end-of-line)))
479
480 (defun gnus-user-format-function-t (header)
481 "display the to field (for archive messages)"
482 (let ((n (mail-header-number header)))
483 (with-current-buffer nntp-server-buffer
484 (save-excursion
485 (save-restriction
486 (let ((inhibit-point-motion-hooks t))
487 (goto-char (point-min))
488 (let ((beg (search-forward (format " %d Article retrieved." n)))
489 (end (search-forward "\n.\n")))
490 (narrow-to-region beg end)
491 (goto-char beg)
492 (message-fetch-field "To"))))))))
493
494 (defun gnus-extract-attachment ()
495 "extract attachments from a multi-part mime message"
496 (interactive)
497 (let ((sm gnus-show-mime))
498 (if sm
499 (progn (setq gnus-show-mime nil)
500 (gnus-summary-select-article t 'force))
501 )
502 (gnus-summary-show-all-headers)
503 (with-current-buffer gnus-article-buffer
504 (save-excursion
505 (save-restriction
506 (mime/viewer-mode)
507 (delete-other-windows)
508 (let ((pt 0))
509 (while (progn
510 (mime-viewer/next-content)
511 (and
512 (equal "*Preview-*Article**" (buffer-name (current-buffer)))
513 (not (= pt (point)))))
514 (setq pt (point))
515 (if (looking-at "^\\[[0-9]* \\([^ ]+ \\)+<")
516 (mime-viewer/extract-content)))))))
517 (kill-buffer "*Preview-*Article**")
518 (setq gnus-show-mime sm)
519 ))
520
521 ;;; Why???
522 (make-variable-buffer-local 'gnus-extra-headers)
523 (make-variable-buffer-local 'nnmail-extra-headers)
524
525
526 (defun resend-to-schemadev ()
527 (interactive)
528 (message "forwarding to xmlschema-dev")
529 (gnus-summary-resend-message "xmlschema-dev@w3.org" 1)
530 (gnus-summary-next-unread-article))
531
532 (defun brutal-resend ()
533 (interactive)
534 (message "editing for resend. . .")
535 (unless (eq (gnus-summary-article-number)
536 gnus-current-article)
537 (gnus-summary-select-article t))
538 (gnus-summary-toggle-header 1)
539 (with-current-buffer gnus-article-buffer
540 (toggle-read-only)
541 (gnus-article-date-original)
542 (goto-char (point-min))
543 (replace-regexp "^\\(X-Diagnostic\\|X-Envelope-To\\|X-Original-To\\|Delivered-To\\):.*\n" "")
544 (goto-char (point-min))
545 (gnus-summary-edit-article-done
546 (or (mail-header-references gnus-current-headers) "")
547 (gnus-group-read-only-p) gnus-summary-buffer nil))
548 (call-interactively (function gnus-summary-resend-message))
549 (gnus-summary-next-unread-article))
550
551 ; (unless (fboundp 'builtin-coding-system-p)
552 ; (fset 'builtin-coding-system-p (symbol-function 'coding-system-p))
553 ; (defun coding-system-p (obj)
554 ; (cond
555 ; ((builtin-coding-system-p obj) t)
556 ; ((memq obj '(utf-8 gb2312 koi8-r iso-8859-1))
557 ; (message (format "Coding system: %s" obj))
558 ; t))))
559
560 ;;; dangerous hack to improve display of names and subjects in mail/news
561 (if nil (progn
562 (require 'mm-util)
563 (defun mm-decode-coding-string (str cs)
564 (if (and str (eq cs 'utf-8))
565 (if (or (string-match "Â" str)
566 (string-match "Ã" str))
567 (let* ((r 0) ; read pointer
568 (w 0) ; write pointer
569 (l (length str)))
570 (while (< r l)
571 (let* ((c (aref str r))
572 (i (char-int c)))
573 (cond ((= i 194)
574 (aset str w (aref str (+ r 1)))
575 (setq r (+ r 2)))
576 ((= i 195)
577 (aset str w
578 (int-char
579 (+ 64
580 (char-int (aref str (+ r 1))))))
581 (setq r (+ r 2)))
582 (t
583 (aset str w c)
584 (setq r (+ r 1)))))
585 (setq w (+ w 1)))
586 (substring str 0 w))
587 str)
588 str))
589
590 (defun mm-sort-coding-systems-predicate (a b)
591 ;; from mm-util, abort if no priorities
592 (or (not mm-coding-system-priorities)
593 (let ((priorities
594 (mapcar (lambda (cs)
595 ;; Note: invalid entries are dropped silently
596 (and (setq cs (mm-coding-system-p cs))
597 (coding-system-base cs)))
598 mm-coding-system-priorities)))
599 (and (setq a (mm-coding-system-p a))
600 (if (setq b (mm-coding-system-p b))
601 (> (length (memq (coding-system-base a) priorities))
602 (length (memq (coding-system-base b) priorities)))
603 t)))))))
604
605 (require 'browse-url)
606
607 ;;; This version collects extra lines if you use right-button
608 ;;; to click on a URL
609 (defun browse-url (url &rest args)
610 "Ask a WWW browser to load URL.
611 Prompts for a URL, defaulting to the URL at or before point. Variable
612 `browse-url-browser-function' says which browser to use."
613 (interactive (browse-url-interactive-arg "URL: "))
614 (unless (interactive-p)
615 (setq args (or args (list browse-url-new-window-flag))))
616 (if (and (boundp 'event)(= 3 (event-button event)))
617 (let ((thisLine url))
618 (while (and (progn (forward-char (length thisLine))
619 (eolp))
620 (progn (forward-line 1)
621 (beginning-of-line)
622 (not (looking-at "\\s-"))))
623 (looking-at "\\S-*")
624 (setq thisLine (buffer-substring (match-beginning 0)
625 (match-end 0)))
626 (setq url (concat url thisLine)))))
627 (if (functionp browse-url-browser-function)
628 (apply browse-url-browser-function url args)
629 ;; The `function' can be an alist; look down it for first match
630 ;; and apply the function (which might be a lambda).
631 (catch 'done
632 (dolist (bf browse-url-browser-function)
633 (when (string-match (car bf) url)
634 (apply (cdr bf) url args)
635 (throw 'done t)))
636 (error "No browse-url-browser-function matching URL %s"
637 url))))
638
639 (defun gnus-user-format-function-H (dummy)
640 (format "%c"
641 (cond ((eq gnus-tmp-summary-live ?*)
642 ?*)
643 ((> (gnus-number-of-unseen-articles-in-group gnus-tmp-group) 0)
644 ?.)
645 (t ? ))))
646
647 (defun ht-next-with-unseen (n)
648 (interactive "p")
649 (let* ((gvl (mapcar (function string-to-number)
650 (split-string gnus-version-number "\\.")))
651 (pattern (if (or (> (car gvl) 5)
652 (and (eq (car gvl) 5)
653 (or (> (cadr gvl) 10)
654 (and (eq (cadr gvl) 10)
655 (> (caddr gvl) 7)))))
656 "\\."
657 ":\\.")))
658 (if (looking-at pattern)
659 (if (< n 0)
660 (backward-char 1)
661 (forward-char 1)))
662 (let ((missing 0)
663 (winning (looking-at pattern)))
664 (while (and (zerop missing)
665 (not winning))
666 (setq missing (gnus-group-next-unread-group n))
667 (setq winning (looking-at pattern)))
668 winning)))
669
670 (defun ht-read-group-unseen-only ()
671 (interactive)
672 (gnus-group-read-group
673 (gnus-number-of-unseen-articles-in-group (gnus-group-group-name))))
674
675 (defun ht-previous-with-unseen (n)
676 (interactive "p")
677 (ht-next-with-unseen (- n)))
678
679 (defvar ht-stash-directory (concat my-mail-dir "/stash/"))
680
681 (defun ht-save-part (handle n)
682 (let ((sup-type (mm-handle-media-supertype handle))
683 (sub-type (mm-handle-media-subtype handle)))
684 (message (format "%s %s/%s" n sup-type sub-type))
685 (cond ((and (equal sup-type "multipart")
686 (or (equal sub-type "alternative")
687 (equal sub-type "related")))
688 (let ((alts (cddr handle))
689 (j 0))
690 (while alts
691 (let* ((alt (pop alts))
692 (handle-type (mm-handle-type alt)))
693 (let* ((sub (mm-handle-media-subtype alt))
694 (ext (cdr
695 (assoc sub '(("calendar" . "vcs")
696 ("v-calendar" . "vcs"))))))
697 (setq j (+ j 1))
698 (if (not (or (mail-content-type-get
699 (mm-handle-disposition alt) 'filename)
700 (mail-content-type-get
701 handle-type 'name)))
702 (nconc
703 handle-type
704 (list (cons 'name (format "%s.%s.%s"
705 n j (or ext sub))))))
706 (ht-save-part alt (format "%s.%s" n j)))))))
707 ((and (equal sup-type "text")(not
708 (member sub-type '("html"
709 "v-calendar"
710 "calendar"))))
711 (message "Skipping text part: %s" (mm-handle-disposition handle)))
712 (t
713 (mm-save-part handle)))))
714
715 (defun ht-move-to-pers (n)
716 (interactive "p")
717 (gnus-summary-move-article n
718 (concat
719 "nnml+ht:pers-"
720 (format-time-string "%Y-%m" (current-time)))))
721
722 (defun ht-article-save-parts (n)
723 "Save non t/p MIME parts starting at N, which is the numerical prefix."
724 (interactive "p2")
725 (let ((window (get-buffer-window gnus-article-buffer 'visible))
726 frame)
727 (when window
728 ;; It is necessary to select the article window so that
729 ;; `gnus-article-goto-part' may really move the point.
730 (setq frame (selected-frame))
731 (gnus-select-frame-set-input-focus (window-frame window))
732 (unwind-protect
733 (save-window-excursion
734 (select-window window)
735 (let ((len (length gnus-article-mime-handle-alist)))
736 (setq mm-default-directory ht-stash-directory)
737 (while (<= n len)
738 (gnus-article-goto-part n)
739 (let ((handle (cdr (assq n gnus-article-mime-handle-alist))))
740 (ht-save-part handle n))
741 (setq n (+ n 1))
742 )))
743 (gnus-select-frame-set-input-focus frame))))
744 )
745
746
747 (defun gnus-article-part-wrapper (n function)
748 (let ((window (get-buffer-window gnus-article-buffer 'visible))
749 frame)
750 (when window
751 ;; It is necessary to select the article window so that
752 ;; `gnus-article-goto-part' may really move the point.
753 (setq frame (selected-frame))
754 (gnus-select-frame-set-input-focus (window-frame window))
755 (unwind-protect
756 (save-window-excursion
757 (select-window window)
758 (when (> n (length gnus-article-mime-handle-alist))
759 (error "No such part"))
760 (gnus-article-goto-part n)
761 (let ((handle (cdr (assq n gnus-article-mime-handle-alist))))
762 (funcall function handle)))
763 (gnus-select-frame-set-input-focus frame)))))
764
765 (defun mhstore-me (dir)
766 (interactive (list (read-directory-name "Save parts to " "/tmp" "/tmp" t)))
767 (let ((art (gnus-summary-article-number)))
768 (let* ((grp-parts (split-string gnus-newsgroup-name ":"))
769 (meth (car grp-parts))
770 (grp (cadr grp-parts)))
771 (if (string= meth "nnml+ht")
772 (let ((doit
773 (format (concat "cd %s && mhstore -f "
774 my-mail-dir "/Mail/%s/%s) -auto")
775 dir grp art)))
776 (message doit)
777 (shell-command doit))
778 ))))
779
780 (defun my-message-send-and-exit (&optional arg)
781 (interactive "P")
782 (let ((message-required-mail-headers
783 (if arg
784 (mapcar
785 (lambda(x)
786 (if(and(consp x)(eq(cdr x)'In-Reply-To))
787 (cons 'optional 'xyzzy)
788 x))
789 message-required-mail-headers)
790 message-required-mail-headers)))
791 (orig-message-send-and-exit)))
792
793 (require 'message)
794 (if (not (fboundp 'orig-message-send-and-exit))
795 (progn
796 (fset 'orig-message-send-and-exit (symbol-function 'message-send-and-exit))
797 (fset 'message-send-and-exit (symbol-function 'my-message-send-and-exit))))
798
799 ;; see message-citation-line-function in message.el
800 (defun safe-citation ()
801 (use-text-not-html)
802 (when message-reply-headers
803 (let ((from (mail-header-from message-reply-headers)))
804 (cond ((string-match "^\"?\\([^\"]*\\)\"? <.*>$" from)
805 (insert (match-string 1 from) " writes:\n\n"))
806 ((string-match "^\\([^<@]*\\)@" from)
807 (insert (match-string 1 from) " writes:\n\n"))
808 (t
809 (insert "[anon] writes:\n\n"))))))
810
811 (defun use-text-not-html (&optional clear)
812 (when (and (if clear (looking-at "<html")
813 (looking-at "> <\\(html\\|div\\)"))
814 (bufferp (get-buffer "*Shell Command Output*")))
815 ;; replace HTML only with result of my HTML filter
816 (delete-region (point)(mark t))
817 (insert-buffer "*Shell Command Output*")
818 (when (looking-at "piping")
819 (kill-entire-line)
820 (indent-rigidly (point) (mark t) -3)
821 (if (not clear)
822 (submerge-region (point) (mark t)))))
823 )
824
825 (setq message-citation-line-function (function safe-citation))
826
827
828 (defvar safelink_pat "https://[a-z0-9.]*safelinks.protection.outlook.com/\\?url=\\(\\(ftp\\|https?\\)%3A%2F%2F[^&<>\"]*\\)[^\"<> \n]*")
829
830 (require 'url)
831
832 (defvar url-ok-chars (nconc
833 '(?/ ?& ?% ?+ ?? ?= ?: ?;
834 ?#
835 )
836 url-unreserved-chars))
837
838 (defun unsafelink ()
839 ;; Thanks to Iain Murray for
840 ;; /public/homepages/imurray2/web/code/hacks/unsafelink
841 (let ((url-unreserved-chars url-ok-chars))
842 (while (re-search-forward safelink_pat nil t)
843 (let ((res (match-string 1)))
844 (replace-match "")
845 ;; unhexify uses regex, so trashes match-string :-(
846 (insert (url-hexify-string (url-unhex-string res)))
847 ))
848 ))
849
850 (add-hook 'gnus-article-prepare-hook 'unsafelink)
851
852 (provide 'my-news)