-
Notifications
You must be signed in to change notification settings - Fork 11
/
hmail.el
286 lines (253 loc) · 10.8 KB
/
hmail.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
;;; 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