Merge remote-tracking branch 'sourceforge/master'
[emacs-jabber.git] / jabber-bookmarks.el
blob60562b189e58b76eb55394b16a1aee816b879bf1
1 ;; jabber-bookmarks.el - bookmarks according to XEP-0048
3 ;; Copyright (C) 2007, 2008 - Magnus Henoch - mange@freemail.hu
5 ;; This file is a part of jabber.el.
7 ;; This program 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 of the License, or
10 ;; (at your option) any later version.
12 ;; This program 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 this program; if not, write to the Free Software
19 ;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 (require 'jabber-private)
22 (require 'jabber-widget)
23 (require 'jabber-autoloads)
25 (require 'cl)
27 (defvar jabber-bookmarks (make-hash-table :test 'equal)
28 "Mapping from full JIDs to bookmarks.
29 Bookmarks are what has been retrieved from the server, as list of
30 XML elements. This is nil if bookmarks have not been retrieved,
31 and t if no bookmarks where found.")
33 ;;;###autoload
34 (defun jabber-get-conference-data (jc conference-jid cont &optional key)
35 "Get bookmark data for CONFERENCE-JID.
36 KEY may be nil or one of :name, :autojoin, :nick and :password.
37 If KEY is nil, a plist containing the above keys is returned.
38 CONT is called when the result is available, with JC and the
39 result as arguments. If CONT is nil, return the requested data
40 immediately, and return nil if it is not in the cache."
41 (if (null cont)
42 (let ((cache (jabber-get-bookmarks-from-cache jc)))
43 (if (and cache (listp cache))
44 (jabber-get-conference-data-internal
45 cache conference-jid key)))
46 (jabber-get-bookmarks
48 (lexical-let ((conference-jid conference-jid)
49 (key key)
50 (cont cont))
51 (lambda (jc result)
52 (let ((entry (jabber-get-conference-data-internal result conference-jid key)))
53 (funcall cont jc entry)))))))
55 (defun jabber-get-conference-data-internal (result conference-jid key)
56 (let ((entry (dolist (node result)
57 (when (and (eq (jabber-xml-node-name node) 'conference)
58 (string= (jabber-xml-get-attribute node 'jid) conference-jid))
59 (return (jabber-parse-conference-bookmark node))))))
60 (if key
61 (plist-get entry key)
62 entry)))
64 ;;;###autoload
65 (defun jabber-parse-conference-bookmark (node)
66 "Convert a <conference/> tag into a plist.
67 The plist may contain the keys :jid, :name, :autojoin,
68 :nick and :password."
69 (when (eq (jabber-xml-node-name node) 'conference)
70 (list :jid (jabber-xml-get-attribute node 'jid)
71 :name (jabber-xml-get-attribute node 'name)
72 :autojoin (member (jabber-xml-get-attribute node 'autojoin)
73 '("true" "1"))
74 :nick (car (jabber-xml-node-children
75 (car (jabber-xml-get-children node 'nick))))
76 :password (car (jabber-xml-node-children
77 (car (jabber-xml-get-children node 'password)))))))
79 ;;;###autoload
80 (defun jabber-get-bookmarks (jc cont &optional refresh)
81 "Retrieve bookmarks (if needed) and call CONT.
82 Arguments to CONT are JC and the bookmark list. CONT will be
83 called as the result of a filter function or a timer.
84 If REFRESH is non-nil, always fetch bookmarks."
85 (let ((bookmarks (gethash (jabber-connection-bare-jid jc) jabber-bookmarks)))
86 (if (and (not refresh) bookmarks)
87 (run-with-timer 0 nil cont jc (when (listp bookmarks) bookmarks))
88 (lexical-let* ((cont cont)
89 (callback (lambda (jc result) (jabber-get-bookmarks-1 jc result cont))))
90 (jabber-private-get jc 'storage "storage:bookmarks"
91 callback callback)))))
93 (defun jabber-get-bookmarks-1 (jc result cont)
94 (let ((my-jid (jabber-connection-bare-jid jc))
95 (value
96 (if (eq (jabber-xml-node-name result) 'storage)
97 (or (jabber-xml-node-children result) t)
98 t)))
99 (puthash my-jid value jabber-bookmarks)
100 (funcall cont jc (when (listp value) value))))
102 ;;;###autoload
103 (defun jabber-get-bookmarks-from-cache (jc)
104 "Return cached bookmarks for JC.
105 If bookmarks have not yet been fetched by `jabber-get-bookmarks',
106 return nil."
107 (gethash (jabber-connection-bare-jid jc) jabber-bookmarks))
109 (defun jabber-set-bookmarks (jc bookmarks &optional callback)
110 "Set bookmarks to BOOKMARKS, which is a list of XML elements.
111 If CALLBACK is non-nil, call it with JC and t or nil as arguments
112 on success or failure, respectively."
113 (unless callback
114 (setq callback #'ignore))
115 (jabber-private-set
117 `(storage ((xmlns . "storage:bookmarks"))
118 ,@bookmarks)
119 callback t
120 callback nil))
122 ;;;###autoload
123 (defun jabber-edit-bookmarks (jc)
124 "Create a buffer for editing bookmarks interactively."
125 (interactive (list (jabber-read-account)))
126 (jabber-get-bookmarks jc 'jabber-edit-bookmarks-1 t))
128 (defun jabber-edit-bookmarks-1 (jc bookmarks)
129 (setq bookmarks
130 (mapcar
131 (lambda (e)
132 (case (jabber-xml-node-name e)
133 (url
134 (list 'url (or (jabber-xml-get-attribute e 'url) "")
135 (or (jabber-xml-get-attribute e 'name) "")))
136 (conference
137 (list 'conference
138 (or (jabber-xml-get-attribute e 'jid) "")
139 (or (jabber-xml-get-attribute e 'name) "")
140 (not (not (member (jabber-xml-get-attribute e 'autojoin)
141 '("true" "1"))))
142 (or (jabber-xml-path e '(nick "")) "")
143 (or (jabber-xml-path e '(password "")) "")))))
144 bookmarks))
145 (setq bookmarks (delq nil bookmarks))
146 (with-current-buffer (get-buffer-create "Edit bookmarks")
147 (jabber-init-widget-buffer nil)
148 (setq jabber-buffer-connection jc)
150 (widget-insert (jabber-propertize (concat "Edit bookmarks for "
151 (jabber-connection-bare-jid jc))
152 'face 'jabber-title-large)
153 "\n\n")
155 (when (or (bound-and-true-p jabber-muc-autojoin)
156 (bound-and-true-p jabber-muc-default-nicknames))
157 (widget-insert "The variables `jabber-muc-autojoin' and/or `jabber-muc-default-nicknames'\n"
158 "contain values. They are only available to jabber.el on this machine.\n"
159 "You may want to import them into your bookmarks, to make them available\n"
160 "to any client on any machine.\n")
161 (widget-create 'push-button :notify 'jabber-bookmarks-import "Import values from variables")
162 (widget-insert "\n\n"))
164 (push (cons 'bookmarks
165 (widget-create
166 '(repeat
167 :tag "Bookmarks"
168 (choice
169 (list :tag "Conference"
170 (const :format "" conference)
171 (string :tag "JID") ;XXX: jid widget type?
172 (string :tag "Name")
173 (checkbox :tag "Autojoin" :format "%[%v%] Autojoin?\n")
174 (string :tag "Nick") ;or nil?
175 (string :tag "Password") ;or nil?
177 (list :tag "URL"
178 (const :format "" url)
179 (string :tag "URL")
180 (string :tag "Name"))))
181 :value bookmarks))
182 jabber-widget-alist)
184 (widget-insert "\n")
185 (widget-create 'push-button :notify 'jabber-bookmarks-submit "Submit")
187 (widget-setup)
188 (widget-minor-mode 1)
189 (switch-to-buffer (current-buffer))
190 (goto-char (point-min))))
192 (defun jabber-bookmarks-submit (&rest ignore)
193 (let ((bookmarks (widget-value (cdr (assq 'bookmarks jabber-widget-alist)))))
194 (setq bookmarks
195 (mapcar
196 (lambda (entry)
197 (case (car entry)
198 (url
199 (destructuring-bind (symbol url name) entry
200 `(url ((url . ,url)
201 (name . ,name)))))
202 (conference
203 (destructuring-bind (symbol jid name autojoin nick password)
204 entry
205 `(conference ((jid . ,jid)
206 (name . ,name)
207 (autojoin . ,(if autojoin
209 "0")))
210 ,@(unless (zerop (length nick))
211 `((nick () ,nick)))
212 ,@(unless (zerop (length password))
213 `((password () ,password))))))))
214 bookmarks))
215 (remhash (jabber-connection-bare-jid jabber-buffer-connection) jabber-bookmarks)
216 (jabber-private-set
217 jabber-buffer-connection
218 `(storage ((xmlns . "storage:bookmarks"))
219 ,@bookmarks)
220 'jabber-report-success "Storing bookmarks"
221 'jabber-report-success "Storing bookmarks")))
223 (defun jabber-bookmarks-import (&rest ignore)
224 (let* ((value (widget-value (cdr (assq 'bookmarks jabber-widget-alist))))
225 (conferences (mapcar
226 'cdr
227 (remove-if-not
228 (lambda (entry)
229 (eq (car entry) 'conference))
230 value))))
231 (dolist (default-nickname jabber-muc-default-nicknames)
232 (destructuring-bind (muc-jid . nick) default-nickname
233 (let ((entry (assoc muc-jid conferences)))
234 (if entry
235 (setf (fourth entry) nick)
236 (setq entry (list muc-jid "" nil nick ""))
237 (push entry conferences)
238 (push (cons 'conference entry) value)))))
239 (dolist (autojoin jabber-muc-autojoin)
240 (let ((entry (assoc autojoin conferences)))
241 (if entry
242 (setf (third entry) t)
243 (setq entry (list autojoin "" t "" ""))
244 (push (cons 'conference entry) value))))
245 (widget-value-set (cdr (assq 'bookmarks jabber-widget-alist)) value)
246 (widget-setup)))
248 (provide 'jabber-bookmarks)
249 ;; arch-tag: a7d6f862-bac0-11db-831f-000a95c2fcd0