]>
Commit | Line | Data |
---|---|---|
1 | ;;; jabber-events.el --- Message events (JEP-0022) implementation -*- lexical-binding: t; -*- | |
2 | ||
3 | ;; Copyright (C) 2005, 2008 Magnus Henoch | |
4 | ||
5 | ;; Author: Magnus Henoch <mange@freemail.hu> | |
6 | ||
7 | ;; This file is free software; you can redistribute it and/or modify | |
8 | ;; it under the terms of the GNU General Public License as published by | |
9 | ;; the Free Software Foundation; either version 2, or (at your option) | |
10 | ;; any later version. | |
11 | ||
12 | ;; This file is distributed in the hope that it will be useful, | |
13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | ;; GNU General Public License for more details. | |
16 | ||
17 | ;; You should have received a copy of the GNU General Public License | |
18 | ;; along with GNU Emacs; see the file COPYING. If not, write to | |
19 | ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
20 | ;; Boston, MA 02111-1307, USA. | |
21 | ||
22 | (require 'cl-lib) | |
23 | ||
24 | (defgroup jabber-events nil | |
25 | "Message events and notifications." | |
26 | :group 'jabber) | |
27 | ||
28 | ;;; INCOMING | |
29 | ;;; Code for requesting event notifications from others and handling | |
30 | ;;; them. | |
31 | ||
32 | (defcustom jabber-events-request-these '(offline | |
33 | delivered | |
34 | displayed | |
35 | composing) | |
36 | "Request these kinds of event notifications from others." | |
37 | :type '(set (const :tag "Delivered to offline storage" offline) | |
38 | (const :tag "Delivered to user's client" delivered) | |
39 | (const :tag "Displayed to user" displayed) | |
40 | (const :tag "User is typing a reply" composing)) | |
41 | :group 'jabber-events) | |
42 | ||
43 | (defvar jabber-events-composing-p nil | |
44 | "Is the other person composing a message?") | |
45 | (make-variable-buffer-local 'jabber-events-composing-p) | |
46 | ||
47 | (defvar jabber-events-arrived nil | |
48 | "In what way has the message reached the recipient? | |
49 | Possible values are nil (no information available), offline | |
50 | \(queued for delivery when recipient is online), delivered | |
51 | \(message has reached the client) and displayed (user is | |
52 | probably reading the message).") | |
53 | (make-variable-buffer-local 'jabber-events-arrived) | |
54 | ||
55 | (defvar jabber-events-message "" | |
56 | "Human-readable presentation of event information.") | |
57 | (make-variable-buffer-local 'jabber-events-message) | |
58 | ||
59 | (defun jabber-events-update-message () | |
60 | (setq jabber-events-message | |
61 | (concat (cdr (assq jabber-events-arrived | |
62 | '((offline . "In offline storage") | |
63 | (delivered . "Delivered") | |
64 | (displayed . "Displayed")))) | |
65 | (when jabber-events-composing-p | |
66 | " (typing a message)")))) | |
67 | ||
68 | (add-hook 'jabber-chat-send-hooks 'jabber-events-when-sending) | |
69 | (defun jabber-events-when-sending (text id) | |
70 | (setq jabber-events-arrived nil) | |
71 | (jabber-events-update-message) | |
72 | `((x ((xmlns . "jabber:x:event")) | |
73 | ,@(mapcar #'list jabber-events-request-these)))) | |
74 | ||
75 | ;;; OUTGOING | |
76 | ;;; Code for handling requests for event notifications and providing | |
77 | ;;; them, modulo user preferences. | |
78 | ||
79 | (defcustom jabber-events-confirm-delivered t | |
80 | "Send delivery confirmation if requested?" | |
81 | :group 'jabber-events | |
82 | :type 'boolean) | |
83 | ||
84 | (defcustom jabber-events-confirm-displayed t | |
85 | "Send display confirmation if requested?" | |
86 | :group 'jabber-events | |
87 | :type 'boolean) | |
88 | ||
89 | (defcustom jabber-events-confirm-composing t | |
90 | "Send notifications about typing a reply?" | |
91 | :group 'jabber-events | |
92 | :type 'boolean) | |
93 | ||
94 | (defvar jabber-events-requested () | |
95 | "List of events requested.") | |
96 | (make-variable-buffer-local 'jabber-events-requested) | |
97 | ||
98 | (defvar jabber-events-last-id nil | |
99 | "Id of last message received, or nil if none.") | |
100 | (make-variable-buffer-local 'jabber-events-last-id) | |
101 | ||
102 | (defvar jabber-events-delivery-confirmed nil | |
103 | "Has delivery confirmation been sent?") | |
104 | (make-variable-buffer-local 'jabber-events-delivery-confirmed) | |
105 | ||
106 | (defvar jabber-events-display-confirmed nil | |
107 | "Has display confirmation been sent?") | |
108 | (make-variable-buffer-local 'jabber-events-display-confirmed) | |
109 | ||
110 | (defvar jabber-events-composing-sent nil | |
111 | "Has composing notification been sent? | |
112 | It can be sent and cancelled several times.") | |
113 | ||
114 | (add-hook 'window-configuration-change-hook | |
115 | 'jabber-events-confirm-display) | |
116 | (defun jabber-events-confirm-display () | |
117 | "Send display confirmation if appropriate. | |
118 | That is, if user allows it, if the other user requested it, | |
119 | and it hasn't been sent before." | |
120 | (walk-windows #'jabber-events-confirm-display-in-window)) | |
121 | ||
122 | (defun jabber-events-confirm-display-in-window (window) | |
123 | (with-current-buffer (window-buffer window) | |
124 | (when (and jabber-events-confirm-displayed | |
125 | (not jabber-events-display-confirmed) | |
126 | (memq 'displayed jabber-events-requested) | |
127 | ;; XXX: if jabber-events-requested is non-nil, how can | |
128 | ;; jabber-chatting-with be nil? See | |
129 | ;; http://sourceforge.net/tracker/index.php?func=detail&aid=1872560&group_id=88346&atid=586350 | |
130 | jabber-chatting-with | |
131 | ;; don't send to bare jids | |
132 | (jabber-jid-resource jabber-chatting-with)) | |
133 | (jabber-send-sexp | |
134 | jabber-buffer-connection | |
135 | `(message | |
136 | ((to . ,jabber-chatting-with)) | |
137 | (x ((xmlns . "jabber:x:event")) | |
138 | (displayed) | |
139 | (id () ,jabber-events-last-id)))) | |
140 | (setq jabber-events-display-confirmed t)))) | |
141 | ||
142 | (defun jabber-events-after-change () | |
143 | (let ((composing-now (not (= (point-max) jabber-point-insert)))) | |
144 | (when (and jabber-events-confirm-composing | |
145 | jabber-chatting-with | |
146 | (not (eq composing-now jabber-events-composing-sent))) | |
147 | (jabber-send-sexp | |
148 | jabber-buffer-connection | |
149 | `(message | |
150 | ((to . ,jabber-chatting-with)) | |
151 | (x ((xmlns . "jabber:x:event")) | |
152 | ,@(if composing-now '((composing)) nil) | |
153 | (id () ,jabber-events-last-id)))) | |
154 | (setq jabber-events-composing-sent composing-now)))) | |
155 | ||
156 | ;;; COMMON | |
157 | ||
158 | ;; Add function last in chain, so a chat buffer is already created. | |
159 | (add-to-list 'jabber-message-chain 'jabber-handle-incoming-message-events t) | |
160 | ||
161 | (defun jabber-handle-incoming-message-events (jc xml-data) | |
162 | (when (and (not (jabber-muc-message-p xml-data)) | |
163 | (get-buffer (jabber-chat-get-buffer (jabber-xml-get-attribute xml-data 'from)))) | |
164 | (with-current-buffer (jabber-chat-get-buffer (jabber-xml-get-attribute xml-data 'from)) | |
165 | (let ((x (cl-find "jabber:x:event" | |
166 | (jabber-xml-get-children xml-data 'x) | |
167 | :key #'(lambda (x) (jabber-xml-get-attribute x 'xmlns)) | |
168 | :test #'string=))) | |
169 | (cond | |
170 | ;; If we get an error message, we shouldn't report any | |
171 | ;; events, as the requests are mirrored from us. | |
172 | ((string= (jabber-xml-get-attribute xml-data 'type) "error") | |
173 | (remove-hook 'post-command-hook 'jabber-events-after-change t) | |
174 | (setq jabber-events-requested nil)) | |
175 | ||
176 | ;; If there's a body, it's not an incoming message event. | |
177 | ((jabber-xml-get-children xml-data 'body) | |
178 | ;; User is done composing, obviously. | |
179 | (setq jabber-events-composing-p nil) | |
180 | (jabber-events-update-message) | |
181 | ||
182 | ;; Reset variables | |
183 | (setq jabber-events-display-confirmed nil) | |
184 | (setq jabber-events-delivery-confirmed nil) | |
185 | ||
186 | ;; User requests message events | |
187 | (setq jabber-events-requested | |
188 | ;; There might be empty strings in the XML data, | |
189 | ;; which car chokes on. Having nil values in | |
190 | ;; the list won't hurt, therefore car-safe. | |
191 | (mapcar #'car-safe | |
192 | (jabber-xml-node-children x))) | |
193 | (setq jabber-events-last-id (jabber-xml-get-attribute | |
194 | xml-data 'id)) | |
195 | ||
196 | ;; Send notifications we already know about | |
197 | (cl-flet ((send-notification | |
198 | (type) | |
199 | (jabber-send-sexp | |
200 | jc | |
201 | `(message | |
202 | ((to . ,(jabber-xml-get-attribute xml-data 'from))) | |
203 | (x ((xmlns . "jabber:x:event")) | |
204 | (,type) | |
205 | (id () ,jabber-events-last-id)))))) | |
206 | ;; Send delivery confirmation if appropriate | |
207 | (when (and jabber-events-confirm-delivered | |
208 | (memq 'delivered jabber-events-requested)) | |
209 | (send-notification 'delivered) | |
210 | (setq jabber-events-delivery-confirmed t)) | |
211 | ||
212 | ;; Send display confirmation if appropriate | |
213 | (when (and jabber-events-confirm-displayed | |
214 | (get-buffer-window (current-buffer) 'visible) | |
215 | (memq 'displayed jabber-events-requested)) | |
216 | (send-notification 'displayed) | |
217 | (setq jabber-events-display-confirmed t)) | |
218 | ||
219 | ;; Set up hooks for composition notification | |
220 | (when (and jabber-events-confirm-composing | |
221 | (memq 'composing jabber-events-requested)) | |
222 | (add-hook 'post-command-hook 'jabber-events-after-change | |
223 | nil t)))) | |
224 | (t | |
225 | ;; So it has no body. If it's a message event, | |
226 | ;; the <x/> node should be the only child of the | |
227 | ;; message, and it should contain an <id/> node. | |
228 | ;; We check the latter. | |
229 | (when (and x (jabber-xml-get-children x 'id)) | |
230 | ;; Currently we don't care about the <id/> node. | |
231 | ||
232 | ;; There's only one node except for the id. | |
233 | (unless | |
234 | (cl-dolist (possible-node '(offline delivered displayed)) | |
235 | (when (jabber-xml-get-children x possible-node) | |
236 | (setq jabber-events-arrived possible-node) | |
237 | (jabber-events-update-message) | |
238 | (cl-return t))) | |
239 | ;; Or maybe even zero, which is a negative composing node. | |
240 | (setq jabber-events-composing-p | |
241 | (not (null (jabber-xml-get-children x 'composing)))) | |
242 | (jabber-events-update-message))))))))) | |
243 | ||
244 | (provide 'jabber-events) | |
245 | ;; arch-tag: 7b6e61fe-a9b3-11d9-afca-000a95c2fcd0 |