Merge remote-tracking branch 'sourceforge/master'
[emacs-jabber.git] / jabber-events.el
blob84df937451449b4b4b6190875001d76867a07883
1 ;;; jabber-events.el --- Message events (JEP-0022) implementation
3 ;; Copyright (C) 2005, 2008 Magnus Henoch
5 ;; Author: Magnus Henoch <mange@freemail.hu>
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.
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.
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.
22 (require 'jabber-autoloads)
23 (require 'cl)
25 (defgroup jabber-events nil
26 "Message events and notifications."
27 :group 'jabber)
29 ;;; INCOMING
30 ;;; Code for requesting event notifications from others and handling
31 ;;; them.
33 (defcustom jabber-events-request-these '(offline
34 delivered
35 displayed
36 composing)
37 "Request these kinds of event notifications from others."
38 :type '(set (const :tag "Delivered to offline storage" offline)
39 (const :tag "Delivered to user's client" delivered)
40 (const :tag "Displayed to user" displayed)
41 (const :tag "User is typing a reply" composing))
42 :group 'jabber-events)
44 (defvar jabber-events-composing-p nil
45 "Is the other person composing a message?")
46 (make-variable-buffer-local 'jabber-events-composing-p)
48 (defvar jabber-events-arrived nil
49 "In what way has the message reached the recipient?
50 Possible values are nil (no information available), offline
51 \(queued for delivery when recipient is online), delivered
52 \(message has reached the client) and displayed (user is
53 probably reading the message).")
54 (make-variable-buffer-local 'jabber-events-arrived)
56 (defvar jabber-events-message ""
57 "Human-readable presentation of event information")
58 (make-variable-buffer-local 'jabber-events-message)
60 (defun jabber-events-update-message ()
61 (setq jabber-events-message
62 (concat (cdr (assq jabber-events-arrived
63 '((offline . "In offline storage")
64 (delivered . "Delivered")
65 (displayed . "Displayed"))))
66 (when jabber-events-composing-p
67 " (typing a message)"))))
69 (add-hook 'jabber-chat-send-hooks 'jabber-events-when-sending)
70 (defun jabber-events-when-sending (text id)
71 (setq jabber-events-arrived nil)
72 (jabber-events-update-message)
73 `((x ((xmlns . "jabber:x:event"))
74 ,@(mapcar #'list jabber-events-request-these))))
76 ;;; OUTGOING
77 ;;; Code for handling requests for event notifications and providing
78 ;;; them, modulo user preferences.
80 (defcustom jabber-events-confirm-delivered t
81 "Send delivery confirmation if requested?"
82 :group 'jabber-events
83 :type 'boolean)
85 (defcustom jabber-events-confirm-displayed t
86 "Send display confirmation if requested?"
87 :group 'jabber-events
88 :type 'boolean)
90 (defcustom jabber-events-confirm-composing t
91 "Send notifications about typing a reply?"
92 :group 'jabber-events
93 :type 'boolean)
95 (defvar jabber-events-requested ()
96 "List of events requested")
97 (make-variable-buffer-local 'jabber-events-requested)
99 (defvar jabber-events-last-id nil
100 "Id of last message received, or nil if none.")
101 (make-variable-buffer-local 'jabber-events-last-id)
103 (defvar jabber-events-delivery-confirmed nil
104 "Has delivery confirmation been sent?")
105 (make-variable-buffer-local 'jabber-events-delivery-confirmed)
107 (defvar jabber-events-display-confirmed nil
108 "Has display confirmation been sent?")
109 (make-variable-buffer-local 'jabber-events-display-confirmed)
111 (defvar jabber-events-composing-sent nil
112 "Has composing notification been sent?
113 It can be sent and cancelled several times.")
115 (add-hook 'window-configuration-change-hook
116 'jabber-events-confirm-display)
117 (defun jabber-events-confirm-display ()
118 "Send display confirmation if appropriate.
119 That is, if user allows it, if the other user requested it,
120 and it hasn't been sent before."
121 (walk-windows #'jabber-events-confirm-display-in-window))
123 (defun jabber-events-confirm-display-in-window (window)
124 (with-current-buffer (window-buffer window)
125 (when (and jabber-events-confirm-displayed
126 (not jabber-events-display-confirmed)
127 (memq 'displayed jabber-events-requested)
128 ;; XXX: if jabber-events-requested is non-nil, how can
129 ;; jabber-chatting-with be nil? See
130 ;; http://sourceforge.net/tracker/index.php?func=detail&aid=1872560&group_id=88346&atid=586350
131 jabber-chatting-with
132 ;; don't send to bare jids
133 (jabber-jid-resource jabber-chatting-with))
134 (jabber-send-sexp
135 jabber-buffer-connection
136 `(message
137 ((to . ,jabber-chatting-with))
138 (x ((xmlns . "jabber:x:event"))
139 (displayed)
140 (id () ,jabber-events-last-id))))
141 (setq jabber-events-display-confirmed t))))
143 (defun jabber-events-after-change ()
144 (let ((composing-now (not (= (point-max) jabber-point-insert))))
145 (when (and jabber-events-confirm-composing
146 jabber-chatting-with
147 (not (eq composing-now jabber-events-composing-sent)))
148 (jabber-send-sexp
149 jabber-buffer-connection
150 `(message
151 ((to . ,jabber-chatting-with))
152 (x ((xmlns . "jabber:x:event"))
153 ,@(if composing-now '((composing)) nil)
154 (id () ,jabber-events-last-id))))
155 (setq jabber-events-composing-sent composing-now))))
157 ;;; COMMON
159 ;; Add function last in chain, so a chat buffer is already created.
160 (add-to-list 'jabber-message-chain 'jabber-handle-incoming-message-events t)
162 (defun jabber-handle-incoming-message-events (jc xml-data)
163 (when (and (not (jabber-muc-message-p xml-data))
164 (get-buffer (jabber-chat-get-buffer (jabber-xml-get-attribute xml-data 'from))))
165 (with-current-buffer (jabber-chat-get-buffer (jabber-xml-get-attribute xml-data 'from))
166 (let ((x (find "jabber:x:event"
167 (jabber-xml-get-children xml-data 'x)
168 :key #'(lambda (x) (jabber-xml-get-attribute x 'xmlns))
169 :test #'string=)))
170 (cond
171 ;; If we get an error message, we shouldn't report any
172 ;; events, as the requests are mirrored from us.
173 ((string= (jabber-xml-get-attribute xml-data 'type) "error")
174 (remove-hook 'post-command-hook 'jabber-events-after-change t)
175 (setq jabber-events-requested nil))
177 ;; If there's a body, it's not an incoming message event.
178 ((jabber-xml-get-children xml-data 'body)
179 ;; User is done composing, obviously.
180 (setq jabber-events-composing-p nil)
181 (jabber-events-update-message)
183 ;; Reset variables
184 (setq jabber-events-display-confirmed nil)
185 (setq jabber-events-delivery-confirmed nil)
187 ;; User requests message events
188 (setq jabber-events-requested
189 ;; There might be empty strings in the XML data,
190 ;; which car chokes on. Having nil values in
191 ;; the list won't hurt, therefore car-safe.
192 (mapcar #'car-safe
193 (jabber-xml-node-children x)))
194 (setq jabber-events-last-id (jabber-xml-get-attribute
195 xml-data 'id))
197 ;; Send notifications we already know about
198 (flet ((send-notification
199 (type)
200 (jabber-send-sexp
202 `(message
203 ((to . ,(jabber-xml-get-attribute xml-data 'from)))
204 (x ((xmlns . "jabber:x:event"))
205 (,type)
206 (id () ,jabber-events-last-id))))))
207 ;; Send delivery confirmation if appropriate
208 (when (and jabber-events-confirm-delivered
209 (memq 'delivered jabber-events-requested))
210 (send-notification 'delivered)
211 (setq jabber-events-delivery-confirmed t))
213 ;; Send display confirmation if appropriate
214 (when (and jabber-events-confirm-displayed
215 (get-buffer-window (current-buffer) 'visible)
216 (memq 'displayed jabber-events-requested))
217 (send-notification 'displayed)
218 (setq jabber-events-display-confirmed t))
220 ;; Set up hooks for composition notification
221 (when (and jabber-events-confirm-composing
222 (memq 'composing jabber-events-requested))
223 (add-hook 'post-command-hook 'jabber-events-after-change
224 nil t))))
226 ;; So it has no body. If it's a message event,
227 ;; the <x/> node should be the only child of the
228 ;; message, and it should contain an <id/> node.
229 ;; We check the latter.
230 (when (and x (jabber-xml-get-children x 'id))
231 ;; Currently we don't care about the <id/> node.
233 ;; There's only one node except for the id.
234 (unless
235 (dolist (possible-node '(offline delivered displayed))
236 (when (jabber-xml-get-children x possible-node)
237 (setq jabber-events-arrived possible-node)
238 (jabber-events-update-message)
239 (return t)))
240 ;; Or maybe even zero, which is a negative composing node.
241 (setq jabber-events-composing-p
242 (not (null (jabber-xml-get-children x 'composing))))
243 (jabber-events-update-message)))))))))
245 (provide 'jabber-events)
246 ;; arch-tag: 7b6e61fe-a9b3-11d9-afca-000a95c2fcd0