Mercurial > hg > xemacs
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/my-news.el Thu Sep 25 17:57:05 2025 +0100 @@ -0,0 +1,852 @@ +(message "my-news") +; (debug-on-entry 'gnus-start-news-server) +(setq ; see ~/.xemacs/gnus.el for local settings + gnus-nntp-server nil ; override local default + ) + +(setq gnus-use-scoring nil ; not used yet + gnus-summary-gather-subject-limit nil + gnus-thread-sort-functions + '(gnus-thread-sort-by-number gnus-thread-sort-by-simpl-subject) + gnus-summary-line-format "%U%R%5N%I%(%[%4L: %-12,12A%]%) %s\n" + gnus-summary-make-false-root 'none + gnus-mime-display-multipart-related-as-mixed t + gnus-simplify-subject-regexp "^\\(re[:;.]\\| \\|fwd:\\)*") + +(defsubst gnus-trim-simplify-subject (text) + (if (string-match gnus-simplify-subject-regexp text) + (substring text (match-end 0)) + text)) + +(defun gnus-thread-sort-by-simpl-subject (h1 h2) + "sort by slightly simplified subject" +; (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)))) + (let ((case-fold-search t)) + (let ((result + (string-lessp + (downcase (gnus-trim-simplify-subject (mail-header-subject + (gnus-thread-header h1)))) + (downcase (gnus-trim-simplify-subject (mail-header-subject + (gnus-thread-header h2))))))) +; (message (format " %s\n" result)) + result))) + + +(setq nnfolder-get-new-mail nil + nnfolder-inhibit-expiry t + gnus-secondary-select-methods + '((nnml "ht" + (gnus-show-threads nil) + (gnus-article-sort-functions + (gnus-article-sort-by-subject gnus-article-sort-by-date)) + ))) +;;; fixup clarinews +;(autoload 'gnus-clarinews-fun "clari-clean" "Clean ClariNews articles" t) +;(add-hook 'gnus-article-prepare-hook 'gnus-clarinews-fun) + + +(defun gnus-Subject-sort-by-subject-and-date (reverse) + "Sort subject display buffer by subject alphabetically. `Re:'s are ignored. +If case-fold-search is non-nil, case of letters is ignored. Date is used +if subjects are equal +Argument REVERSE means reverse order." + (interactive "P") + (gnus-summary-sort-summary + (function + (lambda (a b) + (let ((s-a (gnus-trim-simplify-subject (nntp-header-subject a))) + (s-b (gnus-trim-simplify-subject (nntp-header-subject b))) + ) + (or (gnus-string-lessp s-a s-b) + (and (gnus-string-equal s-a s-b) + (gnus-date-lessp (nntp-header-date a) + (nntp-header-date b))))))) + reverse + )) + +;(require 'util-mde) ; for string-replace-regexp-2 + + +(defun gnus-string-equal (a b) + "Return T if first arg string is equal than second in lexicographic order. +If case-fold-search is non-nil, case of letters is ignored." + (if case-fold-search + (string-equal (downcase a) (downcase b)) (string-equal a b))) + +(defun gnus-Group-update-and-vanish () + "update newsrc and restore config pre-group selection" + (interactive) + (gnus-group-force-update) + (if gnus-pre-config + (set-window-configuration gnus-pre-config)) +; (setq gnus-pre-config nil) + ) + +;; Database stuff +(defun open-white () + (setq whitelist-db (open-database (concat my-mail-dir "/white") 'berkeley-db))) +(defun save-white () + (close-database whitelist-db) + (open-white)) + +(defun open-ad () + (setq adlist-db (open-database (concat my-mail-dir "/ad") 'berkeley-db))) + +(defun save-ad () + (close-database adlist-db) + (open-ad)) + +(defun open-quaker () + (setq quaker-db (open-database (concat my-mail-dir "/quaker") 'berkeley-db))) +(defun save-quaker () + (close-database quaker-db) + (open-quaker)) + +(defvar database-names '(whitelist-db adlist-db quaker-db) "sic") + +(defun db-status (&optional name) + "Check on the whereabouts of a name" + (interactive) + (let ((addr + (or name + (progn + (gnus-summary-goto-article (gnus-summary-article-number)) + (get-canonical-from-addr (get-current-from-components))))) + res) + (dolist (dbn database-names) + (if (get-database addr (eval dbn)) + (setq res (cons dbn res)))) + (if name + res + (message "%s" res)))) + +(defun add-white (&optional dontAddToBBDB) + "While reading an article, add to whitelist" + (interactive "P") + (gnus-summary-goto-article (gnus-summary-article-number)) + (do-add-white (gnus-fetch-original-field "From") dontAddToBBDB)) + +(defun do-add-white (addr &optional dontAddToBBDB) + (let* ((components (gnus-extract-address-components addr)) + (addr (get-canonical-from-addr components))) + (if (not dontAddToBBDB) + (let ((bbdb-no-duplicates-p t)) + (condition-case nil + (bbdb-create-internal (car components) nil + (cadr components) nil nil nil) + (error + ;; OK, just means already present + )))) + (if (new-white addr) + (save-white)))) + +(defun add-ad () + (interactive) + (gnus-summary-goto-article (gnus-summary-article-number)) + (let ((addr (get-current-from-addr))) + (if (or (not (get-database addr whitelist-db)) + (yes-or-no-p "Already white, really convert to ad?")) + (if (new-ad addr) + (save-ad))))) + +(defun add-quaker() + (interactive) + (let ((addr (get-addr-before-point))) + (when (new-quaker addr) + (save-quaker)) + (quaker-sig-maybe))) + +; not needed anymore because of gnus-posting-styles (q.v. in mail-from-*) +(defun quaker-sig-if-to-quaker () + (let ((message-options)) + (save-excursion (message-options-set-recipient)) + (let* ((recipStr (message-options-get 'message-recipients)) + (recips (split-string (downcase recipStr) + ",[ \f\t\n\r\v]+" t))) + (while (and recips + (not (quaker-sig-if-quaker-1 (car recips)))) + (setq recips (cdr recips)))))) + +(defun to-quaker-p () + (let ((message-options)) + (save-excursion (message-options-set-recipient)) + (let* ((recipStr (message-options-get 'message-recipients)) + (recips (split-string (downcase recipStr) + ",[ \f\t\n\r\v]+" t))) + (while (and recips + (not (get-database (car recips) quaker-db))) + (setq recips (cdr recips))) + (not (null recips))))) + +(defun quaker-sig-if-quaker () + (quaker-sig-if-quaker-1 (get-addr-before-point))) + +(defun quaker-sig-if-quaker-1 (addr) + (if (get-database addr quaker-db) + (progn (quaker-sig-maybe) + t))) + +(defun kill-white () + (interactive) + (gnus-summary-goto-article (gnus-summary-article-number)) + (let ((addr (downcase (get-current-from-addr)))) + (rem-white addr))) + +(defun kill-ad () + (interactive) + (gnus-summary-goto-article (gnus-summary-article-number)) + (let ((addr (downcase (get-current-from-addr)))) + (rem-ad addr))) + +(defun get-from-gnus-addr () + (get-from-addr (gnus-fetch-field "From"))) + +(defun get-from-addr (addr) + (get-canonical-from-addr (gnus-extract-address-components addr))) + +(defun get-canonical-from-addr (components) + (downcase (cadr components))) + +(defun get-current-from-addr () + (with-current-buffer gnus-article-buffer + (get-from-gnus-addr))) + +(defun get-current-from-components () + (with-current-buffer gnus-article-buffer + (gnus-extract-address-components (gnus-fetch-field "From")))) + +(defun get-addr-before-point () + (let ((cur (point))) + (save-excursion + (get-from-addr (buffer-substring (+ (search-backward " ") 1) cur))) + )) + +(defun blacken-and-delete (group) + ;; mis-named now + ;; this is part of the expiry processing for xxxSPAM groups, and + ;; actually whitens the from addresses of #-marked articles + ;; The return value is crucial (and crucially outside of the scope of the if) + (if (memq number + (with-current-buffer gnus-summary-buffer + gnus-newsgroup-processable)) + (let ((addr (get-from-gnus-addr))) + (new-white addr))) + 'delete) + +(defun unwhiten-and-delete (group) + ;; unused except in stale groups -- usable as an expiry + (if (memq number + (with-current-buffer gnus-summary-buffer + gnus-newsgroup-processable)) + (let ((addr (get-from-gnus-addr))) + (remove-database addr whitelist-db))) + 'delete) + +(defun known-black (list) + (if (get-database (get-from-gnus-addr) blacklist-db) + list)) + +(defun white-spam (list) + (if (or (equal (get-database (get-from-gnus-addr) whitelist-db) "t") + (let ((case-fold-search t) + (subj (gnus-fetch-field "Subject")) + (from (get-from-gnus-addr))) + (or + (and subj (string-match white-subjects subj)) + (and from + (let ((fromDom (substring from (+ 1 (search "@" from))))) + (and fromDom (member fromDom white-domains))))))) + list)) + +(defun ad-spam (list) + (if (let ((from (get-from-gnus-addr))) + (or + (equal (get-database from adlist-db) "t") + (and from + (let ((fromDom (substring from (+ 1 (search "@" from))))) + (and fromDom (member fromDom ad-domains)))) + )) + list)) + +(defun bogoNote (group) + (if (memq number + (with-current-buffer gnus-summary-buffer + gnus-newsgroup-processable)) + (let ((addr (get-from-gnus-addr))) + (new-white addr))) + (shell-command-on-region (point-min) (point-max) + "/afs/inf.ed.ac.uk/user/h/ht/share/bin/local/makeBogo") + 'delete) + +(defun whiten-recip () + ;;; a hook for outgoing mail + (let* ((to (message-fetch-field "To")) + (cc (message-fetch-field "cc")) + (msg-recipients (concat to (and to cc ", ") cc)) + (recips (message-tokenize-header msg-recipients)) + (res (mapcar (function do-add-white) recips))) + (while (and res (not (car res))) + (setq res (cdr res))) + (if res (save-white)))) + + +(defun new-white (addr) + (if (get-database addr whitelist-db) + nil + (put-database addr "t" whitelist-db) + (comint-exec (get-buffer-create "*new-white*") + "new-white" shell-file-name + nil (list shell-command-switch + (format "echo '%s' >> %s/new-white.txt" addr my-mail-dir))) t)) + +(defun new-ad (addr) + (new-white addr) + (if (get-database addr adlist-db) + nil + (put-database addr "t" adlist-db) + t)) + +(defun rem-ad (addr) + (remove-database addr adlist-db) + (save-ad)) + +(defun new-quaker (addr) + (if (get-database addr quaker-db) + nil + (put-database addr "t" quaker-db) + t)) + +(defun rem-white (addr) + (remove-database (downcase addr) whitelist-db) + (save-white)) + +(defun bogoOK (group) + (shell-command-on-region (point-min) (point-max) + "/afs/inf.ed.ac.uk/user/h/ht/share/bin/local/makeNonBogo") + 'delete) + +(defun del-dups () + (interactive) + (gnus-summary-sort-by-subject) + (gnus-summary-clear-mark-forward 1) + (goto-char (point-min)) + (let ((pos)) + (while (setq pos (search-forward "] " nil t)) + (end-of-line) + (let ((subj (buffer-substring pos (point)))) + (unless (equal subj "") + (let ((target (if (< (length subj) 26) + (concat "] " subj "\n") + (concat "] " (substring subj 0 25)))) + (done 0) + (case-fold-search nil)) + (while (and (= done 0) + (search-forward target nil t)) + (forward-char -3) + (setq done (gnus-summary-mark-as-read-forward 1)))))))) + (gnus-summary-limit-to-unread) + (gnus-summary-sort-by-original)) + +(defun mark-and-mark (n) + (interactive "p") + (while (>= n 1) + (gnus-summary-mark-as-read) + (gnus-summary-mark-as-processable 1) + (setq n (- n 1)))) + +(defun split-on-whole-field (field pat list) + (goto-char (point-max)) + (let ((hit (assq pat wsp-cache)) + rpat) + (if hit + (setq rpat (cdr hit)) + (setq rpat + (concat "^" + field + ":\\s-*" + (if (stringp pat) + pat + (cdr (assq pat + nnmail-split-abbrev-alist))) + "$")) + (setq wsp-cache (cons (cons pat rpat) wsp-cache))) + (if (re-search-backward rpat nil t) + list))) + +(defun ht-gnus-summary-delete-forward () + "REAL delete for nnmail gnus" + (interactive) + (gnus-summary-delete-article) + (gnus-summary-next-unread-article)) + +;; run the first time we make a summary window +(defun gnus-summary-mode-fun1 () + "install ht's mods" + (define-key gnus-summary-mode-map "D" 'ht-gnus-summary-delete-forward) + (define-key gnus-summary-mode-map "~" 'mark-and-mark) + (define-key gnus-summary-mode-map "\M-d" 'gnus-edit-and-move-to-diary) + (define-key gnus-summary-mode-map "\M-e" 'gnus-extract-attachment) + (define-key gnus-summary-mode-map "\M-w" 'add-white) + (define-key gnus-summary-mode-map [(control meta w)] 'copy-region-to-kill) + (define-key gnus-summary-mode-map "\M-h" 'showMPAhtml) + ;(define-key gnus-summary-mode-map [(control meta w)] 'kill-white) + (define-key gnus-summary-mode-map "\M-a" 'add-ad) + (define-key gnus-summary-mode-map "\M-n" 'ht-next-unseen-maybe) + (define-key gnus-summary-mode-map "\M-c" 'ht-catchup-and-next-unseen) + (define-key gnus-summary-mime-map "O" 'ht-article-save-parts) + (define-key gnus-summary-backend-map "M" 'ht-move-to-pers) + (remove-hook 'gnus-summary-mode-hook 'gnus-summary-mode-fun1)) + +(defun message-mode-fun1 () + (define-key message-mode-map [(control meta q)] 'add-quaker) + (remove-hook 'message-mode-hook 'message-mode-fun1)) + +(defun ht-catchup-and-next-unseen () + (interactive) + (when (gnus-summary-catchup nil t nil 'fast) + (gnus-summary-exit) + (previous-line 1) + (ht-next-with-unseen 1))) + +(defun ht-next-unseen-maybe (n) + (interactive "p") + (cond + ((eq (gnus-summary-next-unread-subject n) n) + (gnus-summary-exit) + (previous-line 1) + (if (ht-next-with-unseen n) + (ht-read-group-unseen-only))))) + +(defun ht-gnus-pers-refresh (n) + (interactive "p") + (let ((gn (concat "nnml+ht:pers-" + (format-time-string "%Y-%m" (current-time)))) + (jr ht-gnus-just-read)) + (gnus-group-get-new-news) + (let ((nn (gnus-number-of-unseen-articles-in-group gn))) + (gnus-group-goto-group gn) + (cond + ((> nn 0) + (gnus-group-read-group nn)) + ((> n 1) + (let ((gnus-auto-select-subject + (lambda () + (goto-char (point-max)) + (previous-line 1)))) + (gnus-group-read-group nil t))) + (t (goto-char (point-min)) + (ht-next-with-unseen 1)))) + (message "read: %s" ht-gnus-just-read) + )) + +(defun no-select () + (if (member gnus-newsgroup-name no-select-groups) + (progn (make-variable-buffer-local 'gnus-auto-select-first) + (setq gnus-auto-select-first nil)))) + +(defun showMPAhtml () + "Show the text/html parts of an multipart/alternative message using lynx" + (interactive) + (gnus-summary-select-article) + (with-current-buffer gnus-original-article-buffer + (shell-command-on-region (point-min) (point-max) + ;(expand-file-name + "/home/ht/bin/showMPA.sh" + ;) + )) + ) + + +;; run the first time we make a group window +(defun gnus-group-mode-fun1 () + "install ht's mods" + (require 'gnus-msg) + (define-key gnus-group-mode-map "\M-\C-g" 'ht-gnus-pers-refresh) + (define-key gnus-group-mode-map "\M-n" 'ht-next-with-unseen) + (define-key gnus-group-mode-map "\M-p" 'ht-previous-with-unseen) + (define-key gnus-group-mode-map "\M- " 'ht-read-group-unseen-only) + (define-key gnus-send-bounce-map "R" 'resend-to-schemadev) + (define-key gnus-send-bounce-map "x" 'flush-all-nogoods) + (remove-hook 'gnus-group-mode-hook 'gnus-group-mode-fun1)) + +(defun flush-all-nogoods () + (interactive) + (while (re-search-forward + "] \\(\\(Returned\\|\\([Uu]n\\|[Nn]on-?\\)deliver\\(able\\|ed\\)\\)\\( [Mm]ail\\|:?\\)\\|DELIVERY FAILURE\\|Delivery \\(Notification: Delivery has failed\\|Status Notification .\\(Failure\\|Delay\\).\\)\\|failure \\(notice\\|delivery\\)\\)" + nil t) + (gnus-summary-mark-as-read) + (end-of-line))) + +(defun gnus-user-format-function-t (header) + "display the to field (for archive messages)" + (let ((n (mail-header-number header))) + (with-current-buffer nntp-server-buffer + (save-excursion + (save-restriction + (let ((inhibit-point-motion-hooks t)) + (goto-char (point-min)) + (let ((beg (search-forward (format " %d Article retrieved." n))) + (end (search-forward "\n.\n"))) + (narrow-to-region beg end) + (goto-char beg) + (message-fetch-field "To")))))))) + +(defun gnus-extract-attachment () + "extract attachments from a multi-part mime message" + (interactive) + (let ((sm gnus-show-mime)) + (if sm + (progn (setq gnus-show-mime nil) + (gnus-summary-select-article t 'force)) + ) + (gnus-summary-show-all-headers) + (with-current-buffer gnus-article-buffer + (save-excursion + (save-restriction + (mime/viewer-mode) + (delete-other-windows) + (let ((pt 0)) + (while (progn + (mime-viewer/next-content) + (and + (equal "*Preview-*Article**" (buffer-name (current-buffer))) + (not (= pt (point))))) + (setq pt (point)) + (if (looking-at "^\\[[0-9]* \\([^ ]+ \\)+<") + (mime-viewer/extract-content))))))) + (kill-buffer "*Preview-*Article**") + (setq gnus-show-mime sm) + )) + +;;; Why??? +(make-variable-buffer-local 'gnus-extra-headers) +(make-variable-buffer-local 'nnmail-extra-headers) + + +(defun resend-to-schemadev () + (interactive) + (message "forwarding to xmlschema-dev") + (gnus-summary-resend-message "xmlschema-dev@w3.org" 1) + (gnus-summary-next-unread-article)) + +(defun brutal-resend () + (interactive) + (message "editing for resend. . .") + (unless (eq (gnus-summary-article-number) + gnus-current-article) + (gnus-summary-select-article t)) + (gnus-summary-toggle-header 1) + (with-current-buffer gnus-article-buffer + (toggle-read-only) + (gnus-article-date-original) + (goto-char (point-min)) + (replace-regexp "^\\(X-Diagnostic\\|X-Envelope-To\\|X-Original-To\\|Delivered-To\\):.*\n" "") + (goto-char (point-min)) + (gnus-summary-edit-article-done + (or (mail-header-references gnus-current-headers) "") + (gnus-group-read-only-p) gnus-summary-buffer nil)) + (call-interactively (function gnus-summary-resend-message)) + (gnus-summary-next-unread-article)) + +; (unless (fboundp 'builtin-coding-system-p) +; (fset 'builtin-coding-system-p (symbol-function 'coding-system-p)) +; (defun coding-system-p (obj) +; (cond +; ((builtin-coding-system-p obj) t) +; ((memq obj '(utf-8 gb2312 koi8-r iso-8859-1)) +; (message (format "Coding system: %s" obj)) +; t)))) + +;;; dangerous hack to improve display of names and subjects in mail/news +(if nil (progn +(require 'mm-util) +(defun mm-decode-coding-string (str cs) + (if (and str (eq cs 'utf-8)) + (if (or (string-match "Â" str) + (string-match "Ã" str)) + (let* ((r 0) ; read pointer + (w 0) ; write pointer + (l (length str))) + (while (< r l) + (let* ((c (aref str r)) + (i (char-int c))) + (cond ((= i 194) + (aset str w (aref str (+ r 1))) + (setq r (+ r 2))) + ((= i 195) + (aset str w + (int-char + (+ 64 + (char-int (aref str (+ r 1)))))) + (setq r (+ r 2))) + (t + (aset str w c) + (setq r (+ r 1))))) + (setq w (+ w 1))) + (substring str 0 w)) + str) + str)) + +(defun mm-sort-coding-systems-predicate (a b) + ;; from mm-util, abort if no priorities + (or (not mm-coding-system-priorities) + (let ((priorities + (mapcar (lambda (cs) + ;; Note: invalid entries are dropped silently + (and (setq cs (mm-coding-system-p cs)) + (coding-system-base cs))) + mm-coding-system-priorities))) + (and (setq a (mm-coding-system-p a)) + (if (setq b (mm-coding-system-p b)) + (> (length (memq (coding-system-base a) priorities)) + (length (memq (coding-system-base b) priorities))) + t))))))) + +(require 'browse-url) + +;;; This version collects extra lines if you use right-button +;;; to click on a URL +(defun browse-url (url &rest args) + "Ask a WWW browser to load URL. +Prompts for a URL, defaulting to the URL at or before point. Variable +`browse-url-browser-function' says which browser to use." + (interactive (browse-url-interactive-arg "URL: ")) + (unless (interactive-p) + (setq args (or args (list browse-url-new-window-flag)))) + (if (and (boundp 'event)(= 3 (event-button event))) + (let ((thisLine url)) + (while (and (progn (forward-char (length thisLine)) + (eolp)) + (progn (forward-line 1) + (beginning-of-line) + (not (looking-at "\\s-")))) + (looking-at "\\S-*") + (setq thisLine (buffer-substring (match-beginning 0) + (match-end 0))) + (setq url (concat url thisLine))))) + (if (functionp browse-url-browser-function) + (apply browse-url-browser-function url args) + ;; The `function' can be an alist; look down it for first match + ;; and apply the function (which might be a lambda). + (catch 'done + (dolist (bf browse-url-browser-function) + (when (string-match (car bf) url) + (apply (cdr bf) url args) + (throw 'done t))) + (error "No browse-url-browser-function matching URL %s" + url)))) + +(defun gnus-user-format-function-H (dummy) + (format "%c" + (cond ((eq gnus-tmp-summary-live ?*) + ?*) + ((> (gnus-number-of-unseen-articles-in-group gnus-tmp-group) 0) + ?.) + (t ? )))) + +(defun ht-next-with-unseen (n) + (interactive "p") + (let* ((gvl (mapcar (function string-to-number) + (split-string gnus-version-number "\\."))) + (pattern (if (or (> (car gvl) 5) + (and (eq (car gvl) 5) + (or (> (cadr gvl) 10) + (and (eq (cadr gvl) 10) + (> (caddr gvl) 7))))) + "\\." + ":\\."))) + (if (looking-at pattern) + (if (< n 0) + (backward-char 1) + (forward-char 1))) + (let ((missing 0) + (winning (looking-at pattern))) + (while (and (zerop missing) + (not winning)) + (setq missing (gnus-group-next-unread-group n)) + (setq winning (looking-at pattern))) + winning))) + +(defun ht-read-group-unseen-only () + (interactive) + (gnus-group-read-group + (gnus-number-of-unseen-articles-in-group (gnus-group-group-name)))) + +(defun ht-previous-with-unseen (n) + (interactive "p") + (ht-next-with-unseen (- n))) + +(defvar ht-stash-directory (concat my-mail-dir "/stash/")) + +(defun ht-save-part (handle n) + (let ((sup-type (mm-handle-media-supertype handle)) + (sub-type (mm-handle-media-subtype handle))) + (message (format "%s %s/%s" n sup-type sub-type)) + (cond ((and (equal sup-type "multipart") + (or (equal sub-type "alternative") + (equal sub-type "related"))) + (let ((alts (cddr handle)) + (j 0)) + (while alts + (let* ((alt (pop alts)) + (handle-type (mm-handle-type alt))) + (let* ((sub (mm-handle-media-subtype alt)) + (ext (cdr + (assoc sub '(("calendar" . "vcs") + ("v-calendar" . "vcs")))))) + (setq j (+ j 1)) + (if (not (or (mail-content-type-get + (mm-handle-disposition alt) 'filename) + (mail-content-type-get + handle-type 'name))) + (nconc + handle-type + (list (cons 'name (format "%s.%s.%s" + n j (or ext sub)))))) + (ht-save-part alt (format "%s.%s" n j))))))) + ((and (equal sup-type "text")(not + (member sub-type '("html" + "v-calendar" + "calendar")))) + (message "Skipping text part: %s" (mm-handle-disposition handle))) + (t + (mm-save-part handle))))) + +(defun ht-move-to-pers (n) + (interactive "p") + (gnus-summary-move-article n + (concat + "nnml+ht:pers-" + (format-time-string "%Y-%m" (current-time))))) + +(defun ht-article-save-parts (n) + "Save non t/p MIME parts starting at N, which is the numerical prefix." + (interactive "p2") + (let ((window (get-buffer-window gnus-article-buffer 'visible)) + frame) + (when window + ;; It is necessary to select the article window so that + ;; `gnus-article-goto-part' may really move the point. + (setq frame (selected-frame)) + (gnus-select-frame-set-input-focus (window-frame window)) + (unwind-protect + (save-window-excursion + (select-window window) + (let ((len (length gnus-article-mime-handle-alist))) + (setq mm-default-directory ht-stash-directory) + (while (<= n len) + (gnus-article-goto-part n) + (let ((handle (cdr (assq n gnus-article-mime-handle-alist)))) + (ht-save-part handle n)) + (setq n (+ n 1)) + ))) + (gnus-select-frame-set-input-focus frame)))) + ) + + +(defun gnus-article-part-wrapper (n function) + (let ((window (get-buffer-window gnus-article-buffer 'visible)) + frame) + (when window + ;; It is necessary to select the article window so that + ;; `gnus-article-goto-part' may really move the point. + (setq frame (selected-frame)) + (gnus-select-frame-set-input-focus (window-frame window)) + (unwind-protect + (save-window-excursion + (select-window window) + (when (> n (length gnus-article-mime-handle-alist)) + (error "No such part")) + (gnus-article-goto-part n) + (let ((handle (cdr (assq n gnus-article-mime-handle-alist)))) + (funcall function handle))) + (gnus-select-frame-set-input-focus frame))))) + +(defun mhstore-me (dir) + (interactive (list (read-directory-name "Save parts to " "/tmp" "/tmp" t))) + (let ((art (gnus-summary-article-number))) + (let* ((grp-parts (split-string gnus-newsgroup-name ":")) + (meth (car grp-parts)) + (grp (cadr grp-parts))) + (if (string= meth "nnml+ht") + (let ((doit + (format (concat "cd %s && mhstore -f " + my-mail-dir "/Mail/%s/%s) -auto") + dir grp art))) + (message doit) + (shell-command doit)) + )))) + +(defun my-message-send-and-exit (&optional arg) + (interactive "P") + (let ((message-required-mail-headers + (if arg + (mapcar + (lambda(x) + (if(and(consp x)(eq(cdr x)'In-Reply-To)) + (cons 'optional 'xyzzy) + x)) + message-required-mail-headers) + message-required-mail-headers))) + (orig-message-send-and-exit))) + +(require 'message) +(if (not (fboundp 'orig-message-send-and-exit)) + (progn + (fset 'orig-message-send-and-exit (symbol-function 'message-send-and-exit)) + (fset 'message-send-and-exit (symbol-function 'my-message-send-and-exit)))) + +;; see message-citation-line-function in message.el +(defun safe-citation () + (use-text-not-html) + (when message-reply-headers + (let ((from (mail-header-from message-reply-headers))) + (cond ((string-match "^\"?\\([^\"]*\\)\"? <.*>$" from) + (insert (match-string 1 from) " writes:\n\n")) + ((string-match "^\\([^<@]*\\)@" from) + (insert (match-string 1 from) " writes:\n\n")) + (t + (insert "[anon] writes:\n\n")))))) + +(defun use-text-not-html (&optional clear) + (when (and (if clear (looking-at "<html") + (looking-at "> <\\(html\\|div\\)")) + (bufferp (get-buffer "*Shell Command Output*"))) + ;; replace HTML only with result of my HTML filter + (delete-region (point)(mark t)) + (insert-buffer "*Shell Command Output*") + (when (looking-at "piping") + (kill-entire-line) + (indent-rigidly (point) (mark t) -3) + (if (not clear) + (submerge-region (point) (mark t))))) + ) + +(setq message-citation-line-function (function safe-citation)) + + +(defvar safelink_pat "https://[a-z0-9.]*safelinks.protection.outlook.com/\\?url=\\(\\(ftp\\|https?\\)%3A%2F%2F[^&<>\"]*\\)[^\"<> \n]*") + +(require 'url) + +(defvar url-ok-chars (nconc + '(?/ ?& ?% ?+ ?? ?= ?: ?; + ?# + ) + url-unreserved-chars)) + +(defun unsafelink () + ;; Thanks to Iain Murray for + ;; /public/homepages/imurray2/web/code/hacks/unsafelink + (let ((url-unreserved-chars url-ok-chars)) + (while (re-search-forward safelink_pat nil t) + (let ((res (match-string 1))) + (replace-match "") + ;; unhexify uses regex, so trashes match-string :-( + (insert (url-hexify-string (url-unhex-string res))) + )) + )) + +(add-hook 'gnus-article-prepare-hook 'unsafelink) + +(provide 'my-news)
