Version 0.8.90
[emacs-jabber.git] / jabber-feature-neg.el
blob1fb8853236f2fb601450e2252f490fa90f05b054
1 ;; jabber-feature-neg.el - Feature Negotiation by JEP-0020
3 ;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
4 ;; Copyright (C) 2003, 2004 - Magnus Henoch - mange@freemail.hu
6 ;; This file is a part of jabber.el.
8 ;; This program is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 2 of the License, or
11 ;; (at your option) any later version.
13 ;; This program is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this program; if not, write to the Free Software
20 ;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 (require 'jabber-disco)
23 (require 'cl)
25 (add-to-list 'jabber-advertised-features "http://jabber.org/protocol/feature-neg")
27 (defun jabber-fn-parse (xml-data type)
28 "Parse a Feature Negotiation request, return alist representation.
29 XML-DATA should have one child element, <x/>, in the jabber:x:data
30 namespace.
32 TYPE is either 'request or 'response.
34 Returned alist has field name as key, and value is a list of offered
35 alternatives."
36 (let ((x (car (jabber-xml-get-children xml-data 'x))))
37 (unless (and x
38 (string= (jabber-xml-get-attribute x 'xmlns) "jabber:x:data"))
39 (jabber-signal-error "modify" 'bad-request "Malformed Feature Negotiation"))
41 (let (alist
42 (fields (jabber-xml-get-children x 'field)))
43 (dolist (field fields)
44 (let ((var (jabber-xml-get-attribute field 'var))
45 (value (car (jabber-xml-get-children field 'value)))
46 (options (jabber-xml-get-children field 'option)))
47 (setq alist (cons
48 (cons var
49 (cond
50 ((eq type 'request)
51 (mapcar #'(lambda (option)
52 (car (jabber-xml-node-children
53 (car (jabber-xml-get-children
54 option 'value)))))
55 options))
56 ((eq type 'response)
57 (jabber-xml-node-children value))
59 (error "Incorrect Feature Negotiation type: %s" type))))
60 alist))))
61 ;; return alist
62 alist)))
64 (defun jabber-fn-encode (alist type)
65 "Transform a feature alist into an <x/> node int the jabber:x:data namespace.
66 Note that this is not the reverse of `jabber-fn-parse'.
68 TYPE is either 'request or 'response."
69 (let ((requestp (eq type 'request)))
70 `(x ((xmlns . "jabber:x:data")
71 (type . ,(if requestp "form" "submit")))
72 ,@(mapcar #'(lambda (field)
73 `(field
74 ((type . "list-single")
75 (var . ,(car field)))
76 ,@(if requestp
77 (mapcar
78 #'(lambda (option)
79 `(option nil (value nil ,option)))
80 (cdr field))
81 (list `(value nil ,(cadr field))))))
82 alist))))
84 (defun jabber-fn-intersection (mine theirs)
85 "Find values acceptable to both parties.
87 MINE and THEIRS are alists, as returned by `jabber-fn-parse'.
89 An alist is returned, where the keys are the negotiated variables,
90 and the values are lists containing the preferred option. If
91 negotiation is impossible, an error is signalled. The errors are as
92 specified in JEP-0020, and not necessarily the ones of higher-level
93 protocols."
95 (let ((vars (mapcar #'car mine))
96 (their-vars (mapcar #'car theirs)))
98 ;; are the same variables being negotiated?
99 (sort vars 'string-lessp)
100 (sort their-vars 'string-lessp)
101 (let ((mine-but-not-theirs (set-difference vars their-vars :test 'string=))
102 (theirs-but-not-mine (set-difference their-vars vars :test 'string=)))
103 (when mine-but-not-theirs
104 (jabber-signal-error "modify" 'not-acceptable (car mine-but-not-theirs)))
105 (when theirs-but-not-mine
106 (jabber-signal-error "cancel" 'feature-not-implemented (car theirs-but-not-mine))))
108 (let (alist)
109 (dolist (var vars)
110 (let ((my-options (cdr (assoc var mine)))
111 (their-options (cdr (assoc var theirs))))
112 (let ((common-options (intersection my-options their-options :test 'string=)))
113 (if common-options
114 ;; we have a match; but which one to use?
115 ;; the first one will probably work
116 (setq alist
117 (cons (list var (car common-options))
118 alist))
119 ;; no match
120 (jabber-signal-error "modify" 'not-acceptable var)))))
121 alist)))
123 (provide 'jabber-feature-neg)
125 ;;; arch-tag: 65b2cdcc-7a5f-476b-a613-84ec8e590186