Update of /project/cl-xmpp/cvsroot/cl-xmpp
In directory common-lisp.net:/tmp/cvs-serv15203
Modified Files:
cl-xmpp.asd cl-xmpp.lisp result.lisp utility.lisp
Log Message:
now depending on ironclad for sha1 generation of digest password
Date: Mon Oct 31 22:07:15 2005
Author: eenge
Index: cl-xmpp/cl-xmpp.asd
diff -u cl-xmpp/cl-xmpp.asd:1.3 cl-xmpp/cl-xmpp.asd:1.4
--- cl-xmpp/cl-xmpp.asd:1.3 Fri Oct 28 23:04:12 2005
+++ cl-xmpp/cl-xmpp.asd Mon Oct 31 22:07:14 2005
@@ -1,5 +1,5 @@
;;;; -*- mode: lisp -*-
-;;;; $Id: cl-xmpp.asd,v 1.3 2005/10/28 21:04:12 eenge Exp $
+;;;; $Id: cl-xmpp.asd,v 1.4 2005/10/31 21:07:14 eenge Exp $
;;;; $Source: /project/cl-xmpp/cvsroot/cl-xmpp/cl-xmpp.asd,v $
;;;; See the LICENSE file for licensing information.
@@ -17,7 +17,7 @@
:version "0.0.1"
:licence "MIT"
:description "Common Lisp XMPP client implementation"
- :depends-on (#+sbcl :sb-bsd-sockets :cxml)
+ :depends-on (#+sbcl :sb-bsd-sockets :cxml :ironclad)
:components ((:file "package")
(:file "variable"
:depends-on ("package"))
Index: cl-xmpp/cl-xmpp.lisp
diff -u cl-xmpp/cl-xmpp.lisp:1.6 cl-xmpp/cl-xmpp.lisp:1.7
--- cl-xmpp/cl-xmpp.lisp:1.6 Mon Oct 31 18:02:04 2005
+++ cl-xmpp/cl-xmpp.lisp Mon Oct 31 22:07:15 2005
@@ -1,4 +1,4 @@
-;;;; $Id: cl-xmpp.lisp,v 1.6 2005/10/31 17:02:04 eenge Exp $
+;;;; $Id: cl-xmpp.lisp,v 1.7 2005/10/31 21:07:15 eenge Exp $
;;;; $Source: /project/cl-xmpp/cvsroot/cl-xmpp/cl-xmpp.lisp,v $
;;;; See the LICENSE file for licensing information.
@@ -17,6 +17,12 @@
(server-xstream
:accessor server-xstream
:initform nil)
+ (stream-id
+ :accessor stream-id
+ :initarg :stream-id
+ :initform nil
+ :documentation "Stream ID attribute of the <stream>
+element as gotten when we call BEGIN-XML-STREAM.")
(hostname
:accessor hostname
:initarg :hostname
@@ -92,17 +98,137 @@
#+(or allegro lispworks) (close (socket connection))
connection)
+;;
+;; Handle
+;;
+
+(defmethod handle ((connection connection) (list list))
+ (dolist (object list)
+ (handle connection object)))
+
+(defmethod handle ((connection connection) object)
+ (format t "~&Received: ~a~%" object))
+
+;;
+;; Produce DOM-ish structure from the XML DOM returned by cxml.
+;;
+
+(defmethod parse-result ((connection connection) (objects list))
+ (dolist (object objects)
+ (parse-result connection object)))
+
+(defmethod parse-result ((connection connection) (document dom-impl::document))
+ (let (objects)
+ (dom:map-node-list #'(lambda (node)
+ (push (parse-result connection node) objects))
+ (dom:child-nodes document))
+ objects))
+
+(defmethod parse-result ((connection connection) (attribute dom-impl::attribute))
+ (let* ((name (dom:node-name attribute))
+ (value (dom:value attribute))
+ (xml-attribute
+ (make-instance 'xml-attribute
+ :name name :value value :node attribute)))
+ xml-attribute))
+
+(defmethod parse-result ((connection connection) (node dom-impl::character-data))
+ (let* ((name (dom:node-name node))
+ (data (dom:data node))
+ (xml-element (make-instance 'xml-element
+ :name name :data data :node node)))
+ xml-element))
+
+(defmethod parse-result ((connection connection) (node dom-impl::node))
+ (let* ((name (intern (string-upcase (dom:node-name node)) :keyword))
+ (xml-element (make-instance 'xml-element :name name :node node)))
+ (dom:do-node-list (attribute (dom:attributes node))
+ (push (parse-result connection attribute) (attributes xml-element)))
+ (dom:do-node-list (child (dom:child-nodes node))
+ (push (parse-result connection child) (elements xml-element)))
+ xml-element))
+
+
+(defmethod xml-element-to-event ((connection connection) (object xml-element) (name (eql :iq)))
+ (let ((id (intern (string-upcase (value (get-attribute object :id))) :keyword)))
+ (if (not (string-equal (value (get-attribute object :type)) "result"))
+ (make-error (get-element object :error))
+ (case id
+ (:error (make-error (get-element object :error)))
+ (:roster_1 (make-roster object))
+ (:reg2 :registration-successful)
+ (:unreg_1 :registration-cancellation-successful)
+ (:change1 :password-changed-succesfully)
+ (:auth2 :authentication-successful)
+ (t (cond
+ ((member id '(info1 info2 info3))
+ (make-disco-info (get-element object :query)))
+ ((member id '(items1 items2 items3 items4))
+ (make-disco-items (get-element object :query)))))))))
+
+(defmethod xml-element-to-event ((connection connection)
+ (object xml-element) (name (eql :error)))
+ (make-error object))
+
+(defmethod xml-element-to-event ((connection connection)
+ (object xml-element) (name (eql :stream\:error)))
+ (make-error object))
+
+(defmethod xml-element-to-event ((connection connection)
+ (object xml-element) (name (eql :stream\:stream)))
+ (setf (stream-id connection) (value (get-attribute object :id)))
+ object)
+
+(defmethod xml-element-to-event ((connection connection) (object xml-element) name)
+ (declare (ignore name))
+ object)
+
+(defmethod dom-to-event ((connection connection) (objects list))
+ (let (list)
+ (dolist (object objects)
+ (push (dom-to-event connection object) list))
+ list))
+
+(defmethod dom-to-event ((connection connection) (object xml-element))
+ (xml-element-to-event
+ connection object (intern (string-upcase (name object)) :keyword)))
+
+;;; XXX: Is the ask attribute of the <presence/> element part of the RFC/JEP?
+(defmethod xml-element-to-event ((connection connection)
+ (object xml-element) (name (eql :presence)))
+ (let ((show (get-element object :show)))
+ (when show
+ (setq show (data (get-element show :\#text))))
+ (make-instance 'presence
+ :xml-element object
+ :from (value (get-attribute object :from))
+ :to (value (get-attribute object :to))
+ :show show
+ :type- (value (get-attribute object :type)))))
+
+;;; XXX: Add support for the <thread/> element. Also note that
+;;; there may be an XHTML version of the body available in the
+;;; original node but as of right now I don't care about it. If
+;;; you do please feel free to submit a patch.
+(defmethod xml-element-to-event ((connection connection)
+ (object xml-element) (name (eql :message)))
+ (make-instance 'message
+ :xml-element object
+ :from (value (get-attribute object :from))
+ :to (value (get-attribute object :to))
+ :body (data (get-element (get-element object :body) :\#text))))
+
+;;
+;; Receive stanzas
+;;
+
(defmethod receive-stanza-loop ((connection connection) &key
(stanza-callback 'default-stanza-callback)
- (init-callback 'default-init-callback)
dom-repr)
(loop
(let* ((stanza (read-stanza connection))
(tagname (dom:tag-name (dom:document-element stanza))))
(cond
- ((equal tagname "stream:stream")
- (when init-callback
- (funcall init-callback stanza connection :dom-repr dom-repr)))
((equal tagname "stream:error")
(when stanza-callback
(funcall stanza-callback stanza connection :dom-repr dom-repr))
@@ -221,11 +347,17 @@
(with-iq-query (connection :id "auth1" :xmlns "jabber:iq:auth")
(cxml:with-element "username" (cxml:text username))))
-;;; XXX: Add support for digest authentication.
-(defmethod auth ((connection connection) username password resource)
+(defmethod auth ((connection connection) username password resource &key digestp)
(with-iq-query (connection :id "auth2" :type "set" :xmlns "jabber:iq:auth")
(cxml:with-element "username" (cxml:text username))
- (cxml:with-element "password" (cxml:text password))
+ (if digestp
+ (if (stream-id connection)
+ (cxml:with-element "digest" (cxml:text
+ (make-digest-password
+ (stream-id connection)
+ password)))
+ (error "stream-id on ~a not set, cannot make digest password" connection))
+ (cxml:with-element "password" (cxml:text password)))
(cxml:with-element "resource" (cxml:text resource))))
(defmethod presence ((connection connection) &key type to)
Index: cl-xmpp/result.lisp
diff -u cl-xmpp/result.lisp:1.6 cl-xmpp/result.lisp:1.7
--- cl-xmpp/result.lisp:1.6 Mon Oct 31 18:02:04 2005
+++ cl-xmpp/result.lisp Mon Oct 31 22:07:15 2005
@@ -1,4 +1,4 @@
-;;;; $Id: result.lisp,v 1.6 2005/10/31 17:02:04 eenge Exp $
+;;;; $Id: result.lisp,v 1.7 2005/10/31 21:07:15 eenge Exp $
;;;; $Source: /project/cl-xmpp/cvsroot/cl-xmpp/result.lisp,v $
;;;; See the LICENSE file for licensing information.
@@ -106,44 +106,6 @@
(format stream "~a=~a" (name object) (value object))))
;;
-;; Produce DOM-ish structure from the XML DOM returned by cxml.
-;;
-
-(defmethod parse-result ((objects list))
- (mapcar #'parse-result objects))
-
-(defmethod parse-result ((document dom-impl::document))
- (let (objects)
- (dom:map-node-list #'(lambda (node)
- (push (parse-result node) objects))
- (dom:child-nodes document))
- objects))
-
-(defmethod parse-result ((attribute dom-impl::attribute))
- (let* ((name (dom:node-name attribute))
- (value (dom:value attribute))
- (xml-attribute
- (make-instance 'xml-attribute
- :name name :value value :node attribute)))
- xml-attribute))
-
-(defmethod parse-result ((node dom-impl::character-data))
- (let* ((name (dom:node-name node))
- (data (dom:data node))
- (xml-element (make-instance 'xml-element
- :name name :data data :node node)))
- xml-element))
-
-(defmethod parse-result ((node dom-impl::node))
- (let* ((name (intern (string-upcase (dom:node-name node)) :keyword))
- (xml-element (make-instance 'xml-element :name name :node node)))
- (dom:do-node-list (attribute (dom:attributes node))
- (push (parse-result attribute) (attributes xml-element)))
- (dom:do-node-list (child (dom:child-nodes node))
- (push (parse-result child) (elements xml-element)))
- xml-element))
-
-;;
;; Event interface
;;
@@ -172,17 +134,6 @@
(print-unreadable-object (object stream :type t :identity t)
(format stream "to:~a from:~a" (to object) (from object))))
-;;; XXX: Add support for the <thread/> element. Also note that
-;;; there may be an XHTML version of the body available in the
-;;; original node but as of right now I don't care about it. If
-;;; you do please feel free to submit a patch.
-(defmethod xml-element-to-event ((object xml-element) (name (eql :message)))
- (make-instance 'message
- :xml-element object
- :from (value (get-attribute object :from))
- :to (value (get-attribute object :to))
- :body (data (get-element (get-element object :body) :\#text))))
-
(defclass presence (event)
((to
:accessor to
@@ -206,18 +157,6 @@
(print-unreadable-object (object stream :type t :identity t)
(format stream "from:~a show:~a" (from object) (show object))))
-;;; XXX: Is the ask attribute of the <presence/> element part of the RFC/JEP?
-(defmethod xml-element-to-event ((object xml-element) (name (eql :presence)))
- (let ((show (get-element object :show)))
- (when show
- (setq show (data (get-element show :\#text))))
- (make-instance 'presence
- :xml-element object
- :from (value (get-attribute object :from))
- :to (value (get-attribute object :to))
- :show show
- :type- (value (get-attribute object :type)))))
-
(defclass contact ()
((jid
:accessor jid
@@ -349,6 +288,11 @@
:accessor name
:initarg :name)))
+(defmethod print-object ((object xmpp-protocol-error) stream)
+ "Print the object for the Lisp reader."
+ (print-unreadable-object (object stream :type t :identity t)
+ (format stream "code:~a name:~a" (code object) (name object))))
+
(defclass xmpp-protocol-error-modify (xmpp-protocol-error) ())
(defclass xmpp-protocol-error-cancel (xmpp-protocol-error) ())
(defclass xmpp-protocol-error-wait (xmpp-protocol-error) ())
@@ -372,48 +316,3 @@
(code (third data))
(class (map-error-type-to-class type)))
(make-instance class :code code :name name :xml-element object)))
-
-(defmethod xml-element-to-event ((object xml-element) (name (eql :iq)))
- (let ((id (intern (string-upcase (value (get-attribute object :id))) :keyword)))
- (if (not (string-equal (value (get-attribute object :type)) "result"))
- (make-error (get-element object :error))
- (case id
- (:error (make-error (get-element object :error)))
- (:roster_1 (make-roster object))
- (:reg2 :registration-successful)
- (:unreg_1 :registration-cancellation-successful)
- (:change1 :password-changed-succesfully)
- (:auth2 :authentication-successful)
- (t (cond
- ((member id '(info1 info2 info3))
- (make-disco-info (get-element object :query)))
- ((member id '(items1 items2 items3 items4))
- (make-disco-items (get-element object :query)))))))))
-
-(defmethod xml-element-to-event ((object xml-element) (name (eql :error)))
- (make-error object))
-
-(defmethod xml-element-to-event ((object xml-element) (name (eql :stream\:error)))
- (make-error object))
-
-(defmethod xml-element-to-event ((object xml-element) name)
- (declare (ignore name))
- object)
-
-(defmethod dom-to-event ((object list))
- (mapcar #'dom-to-event object))
-
-(defmethod dom-to-event ((object xml-element))
- (xml-element-to-event
- object (intern (string-upcase (name object)) :keyword)))
-
-;;
-;; Handle
-;;
-
-(defmethod handle ((connection connection) (object list))
- (dolist (object list)
- (handle connection object)))
-
-(defmethod handle ((connection connection) object)
- (format t "~&Received: ~a~%" object))
Index: cl-xmpp/utility.lisp
diff -u cl-xmpp/utility.lisp:1.4 cl-xmpp/utility.lisp:1.5
--- cl-xmpp/utility.lisp:1.4 Mon Oct 31 18:02:04 2005
+++ cl-xmpp/utility.lisp Mon Oct 31 22:07:15 2005
@@ -1,10 +1,13 @@
-;;;; $Id: utility.lisp,v 1.4 2005/10/31 17:02:04 eenge Exp $
+;;;; $Id: utility.lisp,v 1.5 2005/10/31 21:07:15 eenge Exp $
;;;; $Source: /project/cl-xmpp/cvsroot/cl-xmpp/utility.lisp,v $
;;;; See the LICENSE file for licensing information.
(in-package :xmpp)
+(defmacro fmt (string &rest args)
+ `(format nil ,string ,@args))
+
(defun flatten (list)
(cond
((typep list 'atom) list)
@@ -12,25 +15,40 @@
(flatten (cdr list))))
((typep (car list) 'list) (flatten (append (car list) (cdr list))))))
-(defun string-to-array (string)
- (let ((array (make-array (length string))))
+(defun string-to-array (string &rest args)
+ (let ((array (apply #'make-array (length string) args)))
(dotimes (position (length string))
(setf (aref array position) (char-code (aref string position))))
array))
-(defun default-stanza-callback (stanza connection &key dom-repr)
- (let ((result (parse-result stanza)))
- (if dom-repr
- (handle connection result)
- (handle connection (dom-to-event result)))))
+(defun hex-array-to-ascii-string (array)
+ (let ((string (make-string 0)))
+ (dotimes (position (length array))
+ (let ((element (aref array position))
+ (*print-base* 16))
+ (setq string (fmt "~a~a" string element)))) ; probably inefficient
+ string))
+
+;;; borrowed from ironclad, so Copyright (C) 2004 Nathan Froyd
+(defun ascii-string-to-byte-array (string)
+ (let ((vec (make-array (length string) :element-type '(unsigned-byte 8))))
+ (dotimes (i (length string) vec)
+ (let ((byte (char-code (char string i))))
+ (assert (< byte 256))
+ (setf (aref vec i) byte)))))
+
+(defun digestify-string (string)
+ (hex-array-to-ascii-string
+ (ironclad:digest-sequence
+ :sha1 (ascii-string-to-byte-array string))))
+
+(defun make-digest-password (stream-id password)
+ (string-downcase (digestify-string (fmt "~a~a" stream-id password))))
-;; um, refactor?
-(defun default-init-callback (stanza connection &key dom-repr)
- (let ((result (parse-result stanza)))
+(defun default-stanza-callback (stanza connection &key dom-repr)
+ (let ((result (parse-result connection stanza)))
(if dom-repr
(handle connection result)
- (handle connection (dom-to-event result)))))
+ (handle connection (dom-to-event connection result)))))
-(defmacro fmt (string &rest args)
- `(format nil ,string ,@args))