;;; hmail.el --- GNU Hyperbole buttons embedded in e-mail messages -*- lexical-binding: t; -*- ;; ;; Author: Bob Weiner ;; ;; Orig-Date: 9-Oct-91 at 18:38:05 ;; Last-Mod: 10-Mar-24 at 11:52:34 by Bob Weiner ;; ;; SPDX-License-Identifier: GPL-3.0-or-later ;; ;; Copyright (C) 1991-2024 Free Software Foundation, Inc. ;; See the HY-COPY (Hyperbole) or BR-COPY (OO-Browser) file for license ;; information. ;; ;; This file is part of GNU Hyperbole. ;; ;;; Commentary: ;; ;; The `hmail' class provides an abstract interface for connecting ;; GNU Emacs-based mail readers and composers to Hyperbole. Its ;; public variables together with supporting classes determine the ;; mail tools that Hyperbole will support. ;; ;; The `rmail' and `lmail' classes provide a set of feature names ;; that Hyperbole packages can call to interface to a user's selected ;; mail reader. Eventually, a full abstract calling interface may be ;; developed. The public features (the ones above the line of dashes) ;; must be redefined for any mail reader. The private features are ;; used only by a particular mail reader. ;; ;; The `smail' class is similar; it connects a mail composer for use ;; with Hyperbole. ;;; Code: ;;; ************************************************************************ ;;; Public declarations ;;; ************************************************************************ (declare-function rmail:msg-widen "hmail") (declare-function hypb:insert-region "hypb") ;;; ************************************************************************ ;;; Public variables ;;; ************************************************************************ (defvar hnews:composer 'news-reply-mode "Major mode for composing USENET news to be sent with Hyperbole buttons.") (defvar hnews:lister 'gnus-summary-mode "Major mode for listing USENET news header summaries with Hyperbole buttons.") (defvar hnews:reader 'gnus-article-mode "Major mode for reading USENET news with Hyperbole buttons.") (defcustom hmail:init-function nil "*Function (a symbol) to initialize Hyperbole support for a mail reader/composer. Valid values are: nil, Mh-init or Rmail-init." :type '(choice (const nil) (const Mh-init) (const Rmail-init)) :group 'hyperbole-commands) (defvar hmail:composer 'message-mode "Major mode for composing mail to be sent with Hyperbole buttons.") (defvar hmail:lister nil "Major mode for listing mail header summaries with Hyperbole buttons.") (defvar hmail:modifier nil "Major mode for editing received mail with Hyperbole buttons.") (defvar hmail:reader nil "Major mode for reading mail with Hyperbole buttons.") ;;; ************************************************************************ ;;; Private variables ;;; ************************************************************************ (defvar hmail:hbdata-sep "\^Lbd" "Text separating e-mail msg from any trailing Hyperbole button data.") ;;; ************************************************************************ ;;; Public functions ;;; ************************************************************************ ;;; ======================================================================== ;;; hmail class - abstract ;;; ======================================================================== (defun hmail:hbdata-start (&optional msg-start msg-end) "Return point immediately before any Hyperbole button data in current msg. Return message end point when no button data is found. Has side-effect of widening buffer. Message's displayable part begins at optional MSG-START and ends at or before MSG-END." (widen) (unless msg-end (setq msg-end (point-max))) (save-excursion (goto-char msg-end) (if (search-backward hmail:hbdata-sep msg-start t) (1- (point)) msg-end))) (defun hmail:hbdata-to-p () "Move point to the start of embedded Hyperbole button data. Return t if button data is found, else nil." (when (cond ((memq major-mode (list hmail:reader hmail:modifier)) (hmail:msg-narrow) t) ((or (hmail:lister-p) (hnews:lister-p)) t) ((or (not buffer-file-name) (memq major-mode (list hmail:composer hnews:reader hnews:composer))) (widen) t)) (goto-char (point-max)) (when (search-backward hmail:hbdata-sep nil t) (forward-line 1) t))) (defun hmail:browser-p () "Return t iff current major mode helps browse received e-mail messages." (memq major-mode (list hmail:reader hmail:lister))) (defun hmail:buffer (&optional buf invisible-flag) "Start composing mail with the contents of optional BUF as the message body. Invisible text is expanded and included in the mail only if INVISIBLE-FLAG is non-nil. BUF defaults to the current buffer and may be a buffer or buffer name." (interactive (list (current-buffer) (y-or-n-p "Include invisible text? "))) (unless buf (setq buf (current-buffer))) (when (stringp buf) (setq buf (get-buffer buf))) (set-buffer buf) (hmail:region (point-min) (point-max) buf invisible-flag)) ;;;###autoload (defun hmail:compose (address expr &optional subject help) "Compose mail with ADDRESS and evaluation of EXPR. Optional SUBJECT and HELP message may also be given." (interactive "sDeliver e-mail to: \nSubject: ") (require 'hactypes) ;; Needed in case EXPR calls hact. (unless (or (stringp help) (stringp subject)) (setq subject "Delete this text but write a detailed subject. Make a statement or ask a question.")) (hmail:invoke address nil subject) (eval expr) (when (re-search-backward "^Subject: " nil t) (goto-char (match-end 0))) (message (if (stringp help) help "Replace subject, compose message, and then mail."))) (defun hmail:composing-dir (key-src) "If button KEY-SRC is a mail/news composure buffer, return composure directory. Otherwise, return nil." (save-excursion (and (bufferp key-src) (progn (set-buffer key-src) (or (eq major-mode hmail:composer) (eq major-mode hnews:composer))) default-directory))) (defun hmail:editor-p () "Return t iff current major mode edits Hyperbole e-mail/news messages." (memq major-mode (list hmail:composer hnews:composer hmail:modifier))) (defun hmail:init (class-prefix func-suffix-list) "Set up CLASS-PREFIX functions with aliases for FUNC-SUFFIX-LIST. `hmail:reader' should be set appropriately before this is called." (when hmail:reader (let* ((reader-name (symbol-name hmail:reader)) (reader-prefix (capitalize (substring reader-name 0 (string-match "-" reader-name)))) hmail-func) (mapcar (lambda (func-suffix) (setq hmail-func (replace-regexp-in-string "Summ-" "" func-suffix nil t)) (defalias (intern (concat class-prefix hmail-func)) (intern (concat reader-prefix "-" func-suffix)))) func-suffix-list)))) (defun hmail:invoke (&optional address cc subject) "Invoke user preferred mail composer: mh-send or mail. Optional arguments are ADDRESS, CC list and SUBJECT of mail." ;; Next 3 lines prevent blank lines between fields due to ;; fill-region-as-paragraph within mail-setup. (when (equal address "") (setq address nil)) (when (equal cc "") (setq cc nil)) (when (equal subject "") (setq subject nil)) (compose-mail address subject (if cc (list (cons "CC" cc))))) (defun hmail:lister-p () "Return t iff current major mode is a Hyperbole e-mail lister mode." (eq major-mode hmail:lister)) (defun hnews:lister-p () "Return t iff current major mode is a Hyperbole news summary lister mode." (eq major-mode hnews:lister)) (defun hmail:mode-is-p () "Return current major mode if a Hyperbole e-mail or news mode, else nil." (car (memq major-mode (list hmail:reader hmail:composer hmail:lister hmail:modifier hnews:reader hnews:composer hnews:lister)))) ;;;###autoload (defun hmail:msg-narrow (&optional msg-start msg-end) "Narrows buffer to displayable part of current message. Its displayable part begins at optional MSG-START and ends at or before MSG-END." (when (hmail:reader-p) (rmail:msg-widen)) (setq msg-start (or msg-start (point-min)) msg-end (or msg-end (point-max))) (narrow-to-region msg-start (hmail:hbdata-start msg-start msg-end))) (defun hmail:reader-p () "Return t iff current major mode show received Hyperbole e-mail messages." (memq major-mode (list hmail:reader hmail:modifier))) (defun hmail:region (start end &optional buf invisible-flag) "Start composing mail with region between START and END included in message. Invisible text is expanded and included in the mail only if INVISIBLE-FLAG is non-nil. Optional BUF contains the region and defaults to the current buffer. It may be a buffer or buffer name." (interactive (list (region-beginning) (region-end) (current-buffer) (y-or-n-p "Include invisible text? "))) (unless buf (setq buf (current-buffer))) (when (stringp buf) (setq buf (get-buffer buf))) (let (mail-buf) (hmail:invoke) (setq mail-buf (current-buffer)) (save-excursion (rfc822-goto-eoh) (forward-line) (set-buffer buf) (hypb:insert-region mail-buf start end invisible-flag)))) ;;; ======================================================================== ;;; rmail class - mail reader interface - abstract ;;; ======================================================================== (defun rmail:init () "Initialize Hyperbole abstract mail interface for a particular mail reader. `hmail:reader' should be set appropriately before this is called." (hmail:init "rmail:" '("msg-hdrs-full" "msg-narrow" "msg-num" "msg-prev" "msg-next" "msg-to-p" ;; 2 args: (mail-msg-id mail-file) "msg-widen" "to")) (hmail:init "lmail:" '("Summ-delete" "Summ-expunge" "Summ-goto" "Summ-to" "Summ-undelete-all"))) (defvar rmail:msg-hdr-prefix "\\(^Date: \\|\n\nFrom [^ \n]+ \\)" "String header preceding an e-mail received message-id.") (defun rmail:msg-id-get () "Return current msg id for an `hmail:reader' buffer as a string, else nil. Signals error when current mail reader is not supported." (let* ((reader (symbol-name hmail:reader)) ;; (toggled) ) (unless (fboundp 'rmail:msg-hdrs-full) (error "(rmail:msg-id-get): Invalid mail reader: %s" reader)) (save-excursion (unwind-protect (progn ;; (setq toggled (rmail:msg-hdrs-full nil)) (goto-char (point-min)) (when (re-search-forward (concat rmail:msg-hdr-prefix "\\(.+\\)")) ;; Found matching msg (buffer-substring (match-beginning 2) (match-end 2)))) ;; (rmail:msg-hdrs-full toggled) ())))) ;;; ------------------------------------------------------------------------ ;;; Each mail reader-specific Hyperbole support module must also define ;;; the following functions, commonly aliased to existing mail reader ;;; functions within the "-init" function of the Hyperbole module. ;;; See "hrmail.el" for examples. ;;; ;;; rmail:get-new, rmail:msg-forward, rmail:summ-msg-to, rmail:summ-new (provide 'hmail) ;;; hmail.el ends here