]>
Commit | Line | Data |
---|---|---|
1 | ;; jabber-feature-neg.el - Feature Negotiation by JEP-0020 -*- lexical-binding: t; -*- | |
2 | ||
3 | ;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net | |
4 | ;; Copyright (C) 2003, 2004 - Magnus Henoch - mange@freemail.hu | |
5 | ||
6 | ;; This file is a part of jabber.el. | |
7 | ||
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. | |
12 | ||
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. | |
17 | ||
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 | |
21 | ||
22 | (require 'jabber-disco) | |
23 | (require 'cl-lib) | |
24 | ||
25 | (jabber-disco-advertise-feature "http://jabber.org/protocol/feature-neg") | |
26 | ||
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. | |
31 | ||
32 | TYPE is either 'request or 'response. | |
33 | ||
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")) | |
40 | ||
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)) | |
58 | (t | |
59 | (error "Incorrect Feature Negotiation type: %s" type)))) | |
60 | alist)))) | |
61 | ;; return alist | |
62 | alist))) | |
63 | ||
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'. | |
67 | ||
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)))) | |
83 | ||
84 | (defun jabber-fn-intersection (mine theirs) | |
85 | "Find values acceptable to both parties. | |
86 | ||
87 | MINE and THEIRS are alists, as returned by `jabber-fn-parse'. | |
88 | ||
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 XEP-0020, and not necessarily the ones of higher-level | |
93 | protocols." | |
94 | ||
95 | (let ((vars (mapcar #'car mine)) | |
96 | (their-vars (mapcar #'car theirs))) | |
97 | ||
98 | ;; are the same variables being negotiated? | |
99 | (sort vars 'string-lessp) | |
100 | (sort their-vars 'string-lessp) | |
101 | (let ((mine-but-not-theirs (cl-set-difference vars their-vars :test 'string=)) | |
102 | (theirs-but-not-mine (cl-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)))) | |
107 | ||
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 (cl-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))) | |
122 | ||
123 | (provide 'jabber-feature-neg) | |
124 | ||
125 | ;;; arch-tag: 65b2cdcc-7a5f-476b-a613-84ec8e590186 |