summaryrefslogtreecommitdiff
path: root/jabber.el
diff options
context:
space:
mode:
authorcontrapunctus <contrapunctus@disroot.org>2021-03-14 22:18:34 +0530
committercontrapunctus <contrapunctus@disroot.org>2021-03-14 22:18:34 +0530
commit75a78f7788d3b6d8e675c78c92c196b09d1ff17b (patch)
tree0694f1dcde68662642de38a7c08fa855f3a46e77 /jabber.el
parenta31d7920d7f93571f7e38a88c0d5f5606d951633 (diff)
Move Entity Capabilities before first use of jabber-disco-advertise-feature
Diffstat (limited to 'jabber.el')
-rw-r--r--jabber.el580
1 files changed, 290 insertions, 290 deletions
diff --git a/jabber.el b/jabber.el
index 7453a5b..0d4de23 100644
--- a/jabber.el
+++ b/jabber.el
@@ -6339,6 +6339,296 @@ Signal an error if there is no JID at point."
(get jid 'groups))
:test 'string=)))))
+;;;###autoload
+(eval-after-load "jabber-core"
+ '(add-to-list 'jabber-presence-chain #'jabber-process-caps))
+
+(defvar jabber-caps-cache (make-hash-table :test 'equal))
+
+(defconst jabber-caps-hash-names
+ (if (fboundp 'secure-hash)
+ '(("sha-1" . sha1)
+ ("sha-224" . sha224)
+ ("sha-256" . sha256)
+ ("sha-384" . sha384)
+ ("sha-512" . sha512))
+ ;; `secure-hash' was introduced in Emacs 24. For Emacs 23, fall
+ ;; back to the `sha1' function, handled specially in
+ ;; `jabber-caps--secure-hash'.
+ '(("sha-1" . sha1)))
+ "Hash function name map.
+Maps names defined in http://www.iana.org/assignments/hash-function-text-names
+to symbols accepted by `secure-hash'.
+
+XEP-0115 currently recommends SHA-1, but let's be future-proof.")
+
+(defun jabber-caps-get-cached (jid)
+ "Get disco info from Entity Capabilities cache.
+JID should be a string containing a full JID.
+Return (IDENTITIES FEATURES), or nil if not in cache."
+ (let* ((symbol (jabber-jid-symbol jid))
+ (resource (or (jabber-jid-resource jid) ""))
+ (resource-plist (cdr (assoc resource (get symbol 'resources))))
+ (key (plist-get resource-plist 'caps)))
+ (when key
+ (let ((cache-entry (gethash key jabber-caps-cache)))
+ (when (and (consp cache-entry) (not (floatp (car cache-entry))))
+ cache-entry)))))
+
+;;;###autoload
+(defun jabber-process-caps (jc xml-data)
+ "Look for entity capabilities in presence stanzas."
+ (let* ((from (jabber-xml-get-attribute xml-data 'from))
+ (type (jabber-xml-get-attribute xml-data 'type))
+ (c (jabber-xml-path xml-data '(("http://jabber.org/protocol/caps" . "c")))))
+ (when (and (null type) c)
+ (jabber-xml-let-attributes
+ (ext hash node ver) c
+ (cond
+ (hash
+ ;; If the <c/> element has a hash attribute, it follows the
+ ;; "modern" version of XEP-0115.
+ (jabber-process-caps-modern jc from hash node ver))
+ (t
+ ;; No hash attribute. Use legacy version of XEP-0115.
+ ;; TODO: do something clever here.
+ ))))))
+
+(defun jabber-process-caps-modern (jc jid hash node ver)
+ (when (assoc hash jabber-caps-hash-names)
+ ;; We support the hash function used.
+ (let* ((key (cons hash ver))
+ (cache-entry (gethash key jabber-caps-cache)))
+ ;; Remember the hash in the JID symbol.
+ (let* ((symbol (jabber-jid-symbol jid))
+ (resource (or (jabber-jid-resource jid) ""))
+ (resource-entry (assoc resource (get symbol 'resources)))
+ (new-resource-plist (plist-put (cdr resource-entry) 'caps key)))
+ (if resource-entry
+ (setf (cdr resource-entry) new-resource-plist)
+ (push (cons resource new-resource-plist) (get symbol 'resources))))
+
+ (flet ((request-disco-info
+ ()
+ (jabber-send-iq
+ jc jid
+ "get"
+ `(query ((xmlns . "http://jabber.org/protocol/disco#info")
+ (node . ,(concat node "#" ver))))
+ #'jabber-process-caps-info-result (list hash node ver)
+ #'jabber-process-caps-info-error (list hash node ver))))
+ (cond
+ ((and (consp cache-entry)
+ (floatp (car cache-entry)))
+ ;; We have a record of asking someone about this hash.
+ (if (< (- (float-time) (car cache-entry)) 10.0)
+ ;; We asked someone about this hash less than 10 seconds ago.
+ ;; Let's add the new JID to the entry, just in case that
+ ;; doesn't work out.
+ (pushnew jid (cdr cache-entry) :test #'string=)
+ ;; We asked someone about it more than 10 seconds ago.
+ ;; They're probably not going to answer. Let's ask
+ ;; this contact about it instead.
+ (setf (car cache-entry) (float-time))
+ (request-disco-info)))
+ ((null cache-entry)
+ ;; We know nothing about this hash. Let's note the
+ ;; fact that we tried to get information about it.
+ (puthash key (list (float-time)) jabber-caps-cache)
+ (request-disco-info))
+ (t
+ ;; We already know what this hash represents, so we
+ ;; can cache info for this contact.
+ (puthash (cons jid nil) cache-entry jabber-disco-info-cache)))))))
+
+(defun jabber-process-caps-info-result (jc xml-data closure-data)
+ (destructuring-bind (hash node ver) closure-data
+ (let* ((key (cons hash ver))
+ (query (jabber-iq-query xml-data))
+ (verification-string (jabber-caps-ver-string query hash)))
+ (if (string= ver verification-string)
+ ;; The hash is correct; save info.
+ (puthash key (jabber-disco-parse-info xml-data) jabber-caps-cache)
+ ;; The hash is incorrect.
+ (jabber-caps-try-next jc hash node ver)))))
+
+(defun jabber-process-caps-info-error (jc xml-data closure-data)
+ (destructuring-bind (hash node ver) closure-data
+ (jabber-caps-try-next jc hash node ver)))
+
+(defun jabber-caps-try-next (jc hash node ver)
+ (let* ((key (cons hash ver))
+ (cache-entry (gethash key jabber-caps-cache)))
+ (when (floatp (car-safe cache-entry))
+ (let ((next-jid (pop (cdr cache-entry))))
+ ;; Do we know someone else we could ask about this hash?
+ (if next-jid
+ (progn
+ (setf (car cache-entry) (float-time))
+ (jabber-send-iq
+ jc next-jid
+ "get"
+ `(query ((xmlns . "http://jabber.org/protocol/disco#info")
+ (node . ,(concat node "#" ver))))
+ #'jabber-process-caps-info-result (list hash node ver)
+ #'jabber-process-caps-info-error (list hash node ver)))
+ ;; No, forget about it for now.
+ (remhash key jabber-caps-cache))))))
+
+(defun jabber-caps-ver-string (query hash)
+ ;; XEP-0115, section 5.1
+ ;; 1. Initialize an empty string S.
+ (with-temp-buffer
+ (let* ((identities (jabber-xml-get-children query 'identity))
+ (disco-features (mapcar (lambda (f) (jabber-xml-get-attribute f 'var))
+ (jabber-xml-get-children query 'feature)))
+ (maybe-forms (jabber-xml-get-children query 'x))
+ (forms (remove-if-not
+ (lambda (x)
+ ;; Keep elements that are forms and have a FORM_TYPE,
+ ;; according to XEP-0128.
+ (and (string= (jabber-xml-get-xmlns x) "jabber:x:data")
+ (jabber-xdata-formtype x)))
+ maybe-forms)))
+ ;; 2. Sort the service discovery identities [15] by category
+ ;; and then by type and then by xml:lang (if it exists),
+ ;; formatted as CATEGORY '/' [TYPE] '/' [LANG] '/'
+ ;; [NAME]. [16] Note that each slash is included even if the
+ ;; LANG or NAME is not included (in accordance with XEP-0030,
+ ;; the category and type MUST be included.
+ (setq identities (sort identities #'jabber-caps-identity-<))
+ ;; 3. For each identity, append the 'category/type/lang/name' to
+ ;; S, followed by the '<' character.
+ (dolist (identity identities)
+ (jabber-xml-let-attributes (category type xml:lang name) identity
+ ;; Use `concat' here instead of passing everything to
+ ;; `insert', since `concat' tolerates nil values.
+ (insert (concat category "/" type "/" xml:lang "/" name "<"))))
+ ;; 4. Sort the supported service discovery features. [17]
+ (setq disco-features (sort disco-features #'string<))
+ ;; 5. For each feature, append the feature to S, followed by the
+ ;; '<' character.
+ (dolist (f disco-features)
+ (insert f "<"))
+ ;; 6. If the service discovery information response includes
+ ;; XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e.,
+ ;; by the XML character data of the <value/> element).
+ (setq forms (sort forms (lambda (a b)
+ (string< (jabber-xdata-formtype a)
+ (jabber-xdata-formtype b)))))
+ ;; 7. For each extended service discovery information form:
+ (dolist (form forms)
+ ;; Append the XML character data of the FORM_TYPE field's
+ ;; <value/> element, followed by the '<' character.
+ (insert (jabber-xdata-formtype form) "<")
+ ;; Sort the fields by the value of the "var" attribute.
+ (let ((fields (sort (jabber-xml-get-children form 'field)
+ (lambda (a b)
+ (string< (jabber-xml-get-attribute a 'var)
+ (jabber-xml-get-attribute b 'var))))))
+ (dolist (field fields)
+ ;; For each field other than FORM_TYPE:
+ (unless (string= (jabber-xml-get-attribute field 'var) "FORM_TYPE")
+ ;; Append the value of the "var" attribute, followed by the '<' character.
+ (insert (jabber-xml-get-attribute field 'var) "<")
+ ;; Sort values by the XML character data of the <value/> element.
+ (let ((values (sort (mapcar (lambda (value)
+ (car (jabber-xml-node-children value)))
+ (jabber-xml-get-children field 'value))
+ #'string<)))
+ ;; For each <value/> element, append the XML character
+ ;; data, followed by the '<' character.
+ (dolist (value values)
+ (insert value "<"))))))))
+
+ ;; 8. Ensure that S is encoded according to the UTF-8 encoding
+ ;; (RFC 3269 [18]).
+ (let ((s (encode-coding-string (buffer-string) 'utf-8 t))
+ (algorithm (cdr (assoc hash jabber-caps-hash-names))))
+ ;; 9. Compute the verification string by hashing S using the
+ ;; algorithm specified in the 'hash' attribute (e.g., SHA-1 as
+ ;; defined in RFC 3174 [19]). The hashed data MUST be generated
+ ;; with binary output and encoded using Base64 as specified in
+ ;; Section 4 of RFC 4648 [20] (note: the Base64 output MUST NOT
+ ;; include whitespace and MUST set padding bits to zero). [21]
+ (base64-encode-string (jabber-caps--secure-hash algorithm s) t))))
+
+(defun jabber-caps--secure-hash (algorithm string)
+ (cond
+ ;; `secure-hash' was introduced in Emacs 24
+ ((fboundp 'secure-hash)
+ (secure-hash algorithm string nil nil t))
+ ((eq algorithm 'sha1)
+ ;; For SHA-1, we can use the `sha1' function.
+ (sha1 string nil nil t))
+ (t
+ (error "Cannot use hash algorithm %s!" algorithm))))
+
+(defun jabber-caps-identity-< (a b)
+ (let ((a-category (jabber-xml-get-attribute a 'category))
+ (b-category (jabber-xml-get-attribute b 'category)))
+ (or (string< a-category b-category)
+ (and (string= a-category b-category)
+ (let ((a-type (jabber-xml-get-attribute a 'type))
+ (b-type (jabber-xml-get-attribute b 'type)))
+ (or (string< a-type b-type)
+ (and (string= a-type b-type)
+ (let ((a-xml:lang (jabber-xml-get-attribute a 'xml:lang))
+ (b-xml:lang (jabber-xml-get-attribute b 'xml:lang)))
+ (string< a-xml:lang b-xml:lang)))))))))
+
+(defvar jabber-caps-default-hash-function "sha-1"
+ "Hash function to use when sending caps in presence stanzas.
+The value should be a key in `jabber-caps-hash-names'.")
+
+(defvar jabber-caps-current-hash nil
+ "The current disco hash we're sending out in presence stanzas.")
+
+(defconst jabber-caps-node "http://emacs-jabber.sourceforge.net")
+
+;;;###autoload
+(defun jabber-disco-advertise-feature (feature)
+ (unless (member feature jabber-advertised-features)
+ (push feature jabber-advertised-features)
+ (when jabber-caps-current-hash
+ (jabber-caps-recalculate-hash)
+ ;; If we're already connected, we need to send updated presence
+ ;; for the new feature.
+ (mapc #'jabber-send-current-presence jabber-connections))))
+
+(defun jabber-caps-recalculate-hash ()
+ "Update `jabber-caps-current-hash' for feature list change.
+Also update `jabber-disco-info-nodes', so we return results for
+the right node."
+ (let* ((old-hash jabber-caps-current-hash)
+ (old-node (and old-hash (concat jabber-caps-node "#" old-hash)))
+ (new-hash
+ (jabber-caps-ver-string `(query () ,@(jabber-disco-return-client-info))
+ jabber-caps-default-hash-function))
+ (new-node (concat jabber-caps-node "#" new-hash)))
+ (when old-node
+ (let ((old-entry (assoc old-node jabber-disco-info-nodes)))
+ (when old-entry
+ (setq jabber-disco-info-nodes (delq old-entry jabber-disco-info-nodes)))))
+ (push (list new-node #'jabber-disco-return-client-info nil)
+ jabber-disco-info-nodes)
+ (setq jabber-caps-current-hash new-hash)))
+
+;;;###autoload
+(defun jabber-caps-presence-element (_jc)
+ (unless jabber-caps-current-hash
+ (jabber-caps-recalculate-hash))
+
+ (list
+ `(c ((xmlns . "http://jabber.org/protocol/caps")
+ (hash . ,jabber-caps-default-hash-function)
+ (node . ,jabber-caps-node)
+ (ver . ,jabber-caps-current-hash)))))
+
+;;;###autoload
+(eval-after-load "jabber-presence"
+ '(add-to-list 'jabber-presence-element-functions #'jabber-caps-presence-element))
+
(defvar jabber-advertised-features
(list "http://jabber.org/protocol/disco#info")
"Features advertised on service discovery requests
@@ -6829,296 +7119,6 @@ accounts."
(dolist (c jabber-connections)
(ignore-errors (jabber-send-string c " "))))
-;;;###autoload
-(eval-after-load "jabber-core"
- '(add-to-list 'jabber-presence-chain #'jabber-process-caps))
-
-(defvar jabber-caps-cache (make-hash-table :test 'equal))
-
-(defconst jabber-caps-hash-names
- (if (fboundp 'secure-hash)
- '(("sha-1" . sha1)
- ("sha-224" . sha224)
- ("sha-256" . sha256)
- ("sha-384" . sha384)
- ("sha-512" . sha512))
- ;; `secure-hash' was introduced in Emacs 24. For Emacs 23, fall
- ;; back to the `sha1' function, handled specially in
- ;; `jabber-caps--secure-hash'.
- '(("sha-1" . sha1)))
- "Hash function name map.
-Maps names defined in http://www.iana.org/assignments/hash-function-text-names
-to symbols accepted by `secure-hash'.
-
-XEP-0115 currently recommends SHA-1, but let's be future-proof.")
-
-(defun jabber-caps-get-cached (jid)
- "Get disco info from Entity Capabilities cache.
-JID should be a string containing a full JID.
-Return (IDENTITIES FEATURES), or nil if not in cache."
- (let* ((symbol (jabber-jid-symbol jid))
- (resource (or (jabber-jid-resource jid) ""))
- (resource-plist (cdr (assoc resource (get symbol 'resources))))
- (key (plist-get resource-plist 'caps)))
- (when key
- (let ((cache-entry (gethash key jabber-caps-cache)))
- (when (and (consp cache-entry) (not (floatp (car cache-entry))))
- cache-entry)))))
-
-;;;###autoload
-(defun jabber-process-caps (jc xml-data)
- "Look for entity capabilities in presence stanzas."
- (let* ((from (jabber-xml-get-attribute xml-data 'from))
- (type (jabber-xml-get-attribute xml-data 'type))
- (c (jabber-xml-path xml-data '(("http://jabber.org/protocol/caps" . "c")))))
- (when (and (null type) c)
- (jabber-xml-let-attributes
- (ext hash node ver) c
- (cond
- (hash
- ;; If the <c/> element has a hash attribute, it follows the
- ;; "modern" version of XEP-0115.
- (jabber-process-caps-modern jc from hash node ver))
- (t
- ;; No hash attribute. Use legacy version of XEP-0115.
- ;; TODO: do something clever here.
- ))))))
-
-(defun jabber-process-caps-modern (jc jid hash node ver)
- (when (assoc hash jabber-caps-hash-names)
- ;; We support the hash function used.
- (let* ((key (cons hash ver))
- (cache-entry (gethash key jabber-caps-cache)))
- ;; Remember the hash in the JID symbol.
- (let* ((symbol (jabber-jid-symbol jid))
- (resource (or (jabber-jid-resource jid) ""))
- (resource-entry (assoc resource (get symbol 'resources)))
- (new-resource-plist (plist-put (cdr resource-entry) 'caps key)))
- (if resource-entry
- (setf (cdr resource-entry) new-resource-plist)
- (push (cons resource new-resource-plist) (get symbol 'resources))))
-
- (flet ((request-disco-info
- ()
- (jabber-send-iq
- jc jid
- "get"
- `(query ((xmlns . "http://jabber.org/protocol/disco#info")
- (node . ,(concat node "#" ver))))
- #'jabber-process-caps-info-result (list hash node ver)
- #'jabber-process-caps-info-error (list hash node ver))))
- (cond
- ((and (consp cache-entry)
- (floatp (car cache-entry)))
- ;; We have a record of asking someone about this hash.
- (if (< (- (float-time) (car cache-entry)) 10.0)
- ;; We asked someone about this hash less than 10 seconds ago.
- ;; Let's add the new JID to the entry, just in case that
- ;; doesn't work out.
- (pushnew jid (cdr cache-entry) :test #'string=)
- ;; We asked someone about it more than 10 seconds ago.
- ;; They're probably not going to answer. Let's ask
- ;; this contact about it instead.
- (setf (car cache-entry) (float-time))
- (request-disco-info)))
- ((null cache-entry)
- ;; We know nothing about this hash. Let's note the
- ;; fact that we tried to get information about it.
- (puthash key (list (float-time)) jabber-caps-cache)
- (request-disco-info))
- (t
- ;; We already know what this hash represents, so we
- ;; can cache info for this contact.
- (puthash (cons jid nil) cache-entry jabber-disco-info-cache)))))))
-
-(defun jabber-process-caps-info-result (jc xml-data closure-data)
- (destructuring-bind (hash node ver) closure-data
- (let* ((key (cons hash ver))
- (query (jabber-iq-query xml-data))
- (verification-string (jabber-caps-ver-string query hash)))
- (if (string= ver verification-string)
- ;; The hash is correct; save info.
- (puthash key (jabber-disco-parse-info xml-data) jabber-caps-cache)
- ;; The hash is incorrect.
- (jabber-caps-try-next jc hash node ver)))))
-
-(defun jabber-process-caps-info-error (jc xml-data closure-data)
- (destructuring-bind (hash node ver) closure-data
- (jabber-caps-try-next jc hash node ver)))
-
-(defun jabber-caps-try-next (jc hash node ver)
- (let* ((key (cons hash ver))
- (cache-entry (gethash key jabber-caps-cache)))
- (when (floatp (car-safe cache-entry))
- (let ((next-jid (pop (cdr cache-entry))))
- ;; Do we know someone else we could ask about this hash?
- (if next-jid
- (progn
- (setf (car cache-entry) (float-time))
- (jabber-send-iq
- jc next-jid
- "get"
- `(query ((xmlns . "http://jabber.org/protocol/disco#info")
- (node . ,(concat node "#" ver))))
- #'jabber-process-caps-info-result (list hash node ver)
- #'jabber-process-caps-info-error (list hash node ver)))
- ;; No, forget about it for now.
- (remhash key jabber-caps-cache))))))
-
-(defun jabber-caps-ver-string (query hash)
- ;; XEP-0115, section 5.1
- ;; 1. Initialize an empty string S.
- (with-temp-buffer
- (let* ((identities (jabber-xml-get-children query 'identity))
- (disco-features (mapcar (lambda (f) (jabber-xml-get-attribute f 'var))
- (jabber-xml-get-children query 'feature)))
- (maybe-forms (jabber-xml-get-children query 'x))
- (forms (remove-if-not
- (lambda (x)
- ;; Keep elements that are forms and have a FORM_TYPE,
- ;; according to XEP-0128.
- (and (string= (jabber-xml-get-xmlns x) "jabber:x:data")
- (jabber-xdata-formtype x)))
- maybe-forms)))
- ;; 2. Sort the service discovery identities [15] by category
- ;; and then by type and then by xml:lang (if it exists),
- ;; formatted as CATEGORY '/' [TYPE] '/' [LANG] '/'
- ;; [NAME]. [16] Note that each slash is included even if the
- ;; LANG or NAME is not included (in accordance with XEP-0030,
- ;; the category and type MUST be included.
- (setq identities (sort identities #'jabber-caps-identity-<))
- ;; 3. For each identity, append the 'category/type/lang/name' to
- ;; S, followed by the '<' character.
- (dolist (identity identities)
- (jabber-xml-let-attributes (category type xml:lang name) identity
- ;; Use `concat' here instead of passing everything to
- ;; `insert', since `concat' tolerates nil values.
- (insert (concat category "/" type "/" xml:lang "/" name "<"))))
- ;; 4. Sort the supported service discovery features. [17]
- (setq disco-features (sort disco-features #'string<))
- ;; 5. For each feature, append the feature to S, followed by the
- ;; '<' character.
- (dolist (f disco-features)
- (insert f "<"))
- ;; 6. If the service discovery information response includes
- ;; XEP-0128 data forms, sort the forms by the FORM_TYPE (i.e.,
- ;; by the XML character data of the <value/> element).
- (setq forms (sort forms (lambda (a b)
- (string< (jabber-xdata-formtype a)
- (jabber-xdata-formtype b)))))
- ;; 7. For each extended service discovery information form:
- (dolist (form forms)
- ;; Append the XML character data of the FORM_TYPE field's
- ;; <value/> element, followed by the '<' character.
- (insert (jabber-xdata-formtype form) "<")
- ;; Sort the fields by the value of the "var" attribute.
- (let ((fields (sort (jabber-xml-get-children form 'field)
- (lambda (a b)
- (string< (jabber-xml-get-attribute a 'var)
- (jabber-xml-get-attribute b 'var))))))
- (dolist (field fields)
- ;; For each field other than FORM_TYPE:
- (unless (string= (jabber-xml-get-attribute field 'var) "FORM_TYPE")
- ;; Append the value of the "var" attribute, followed by the '<' character.
- (insert (jabber-xml-get-attribute field 'var) "<")
- ;; Sort values by the XML character data of the <value/> element.
- (let ((values (sort (mapcar (lambda (value)
- (car (jabber-xml-node-children value)))
- (jabber-xml-get-children field 'value))
- #'string<)))
- ;; For each <value/> element, append the XML character
- ;; data, followed by the '<' character.
- (dolist (value values)
- (insert value "<"))))))))
-
- ;; 8. Ensure that S is encoded according to the UTF-8 encoding
- ;; (RFC 3269 [18]).
- (let ((s (encode-coding-string (buffer-string) 'utf-8 t))
- (algorithm (cdr (assoc hash jabber-caps-hash-names))))
- ;; 9. Compute the verification string by hashing S using the
- ;; algorithm specified in the 'hash' attribute (e.g., SHA-1 as
- ;; defined in RFC 3174 [19]). The hashed data MUST be generated
- ;; with binary output and encoded using Base64 as specified in
- ;; Section 4 of RFC 4648 [20] (note: the Base64 output MUST NOT
- ;; include whitespace and MUST set padding bits to zero). [21]
- (base64-encode-string (jabber-caps--secure-hash algorithm s) t))))
-
-(defun jabber-caps--secure-hash (algorithm string)
- (cond
- ;; `secure-hash' was introduced in Emacs 24
- ((fboundp 'secure-hash)
- (secure-hash algorithm string nil nil t))
- ((eq algorithm 'sha1)
- ;; For SHA-1, we can use the `sha1' function.
- (sha1 string nil nil t))
- (t
- (error "Cannot use hash algorithm %s!" algorithm))))
-
-(defun jabber-caps-identity-< (a b)
- (let ((a-category (jabber-xml-get-attribute a 'category))
- (b-category (jabber-xml-get-attribute b 'category)))
- (or (string< a-category b-category)
- (and (string= a-category b-category)
- (let ((a-type (jabber-xml-get-attribute a 'type))
- (b-type (jabber-xml-get-attribute b 'type)))
- (or (string< a-type b-type)
- (and (string= a-type b-type)
- (let ((a-xml:lang (jabber-xml-get-attribute a 'xml:lang))
- (b-xml:lang (jabber-xml-get-attribute b 'xml:lang)))
- (string< a-xml:lang b-xml:lang)))))))))
-
-(defvar jabber-caps-default-hash-function "sha-1"
- "Hash function to use when sending caps in presence stanzas.
-The value should be a key in `jabber-caps-hash-names'.")
-
-(defvar jabber-caps-current-hash nil
- "The current disco hash we're sending out in presence stanzas.")
-
-(defconst jabber-caps-node "http://emacs-jabber.sourceforge.net")
-
-;;;###autoload
-(defun jabber-disco-advertise-feature (feature)
- (unless (member feature jabber-advertised-features)
- (push feature jabber-advertised-features)
- (when jabber-caps-current-hash
- (jabber-caps-recalculate-hash)
- ;; If we're already connected, we need to send updated presence
- ;; for the new feature.
- (mapc #'jabber-send-current-presence jabber-connections))))
-
-(defun jabber-caps-recalculate-hash ()
- "Update `jabber-caps-current-hash' for feature list change.
-Also update `jabber-disco-info-nodes', so we return results for
-the right node."
- (let* ((old-hash jabber-caps-current-hash)
- (old-node (and old-hash (concat jabber-caps-node "#" old-hash)))
- (new-hash
- (jabber-caps-ver-string `(query () ,@(jabber-disco-return-client-info))
- jabber-caps-default-hash-function))
- (new-node (concat jabber-caps-node "#" new-hash)))
- (when old-node
- (let ((old-entry (assoc old-node jabber-disco-info-nodes)))
- (when old-entry
- (setq jabber-disco-info-nodes (delq old-entry jabber-disco-info-nodes)))))
- (push (list new-node #'jabber-disco-return-client-info nil)
- jabber-disco-info-nodes)
- (setq jabber-caps-current-hash new-hash)))
-
-;;;###autoload
-(defun jabber-caps-presence-element (_jc)
- (unless jabber-caps-current-hash
- (jabber-caps-recalculate-hash))
-
- (list
- `(c ((xmlns . "http://jabber.org/protocol/caps")
- (hash . ,jabber-caps-default-hash-function)
- (node . ,jabber-caps-node)
- (ver . ,jabber-caps-current-hash)))))
-
-;;;###autoload
-(eval-after-load "jabber-presence"
- '(add-to-list 'jabber-presence-element-functions #'jabber-caps-presence-element))
-
(require 'cl)
(jabber-disco-advertise-feature "http://jabber.org/protocol/feature-neg")