From c8128ca3a97015b8811e9ef731d4f2fe35ee5d9b Mon Sep 17 00:00:00 2001 From: rekado Date: Mon, 24 Nov 2014 10:04:19 +0100 Subject: initial commit --- xep-0114.scm | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 xep-0114.scm (limited to 'xep-0114.scm') diff --git a/xep-0114.scm b/xep-0114.scm new file mode 100644 index 0000000..f882530 --- /dev/null +++ b/xep-0114.scm @@ -0,0 +1,99 @@ +(define-module (gnubba xep-0114) + #:use-module (gnubba sha1) + #:use-module (ice-9 rdelim) + #:use-module (ice-9 regex) + #:use-module (sxml simple) + #:export (with-component)) + + +(define component-port 5347) + + +(define (stream-header component-name) + (string-concatenate (list ""))) + + +(define (generate-handshake stream-id shared-secret) + (sha1 (string-concatenate (list stream-id shared-secret)))) + + +;; Establish a component connection and execute actions. +(define-syntax with-component + (syntax-rules () + ((with-component domain shared-secret exp ...) + (begin + (define addr (make-socket-address AF_INET INADDR_LOOPBACK component-port)) + (define sock (socket AF_INET SOCK_STREAM 0)) + + (define (get-attribute name response) + (define (get-attributes) + "Extract key value pairs from a string and return as alist." + (map (lambda (res) + (cons (match:substring res 1) + (match:substring res 3))) + (list-matches "([^= ]+)=(['\"])([^ ]+)\\2" response))) + (let ((found (assoc name (get-attributes)))) + (unless found + (throw 'stream-error 'attribute-not-found name response)) + found)) + + (define (close-stream) + ;; TODO: make sure this is understood as "" + (display "" sock) + ;; TODO: wait for other side to also send a closing tag before terminating the connection + (close-port sock)) + + (define (write-to-stream msg) + "Write msg to sock and return a single response tag. Ignore tag." + (when msg + (display msg sock)) + (let ((response-string (read-delimited ">" sock))) + (if (string-match "" sock))) + response-string) + response-string))) + + (catch #t + (lambda () + (connect sock addr) + (let ((response (write-to-stream (stream-header domain)))) + ;; server must reply with either an error or a stream header + ;; TODO: should not match against literal string + (unless (string-match "xml `(handshake ,(generate-handshake stream-id shared-secret)) sock) + (let* ((ns '((stream . "http://etherx.jabber.org/streams"))) + (response (xml->sxml sock #:namespaces ns #:trim-whitespace? #t))) + ;; expect empty handshake element as acknowlegement + (unless (equal? 'handshake (caadr response)) + (throw 'stream-error 'handshake response)) + + (display "Connected to XMPP server!\n") + + ;; Handlers are passed as expressions that + ;; are evaluated in the xmpp monad. + (display "Registering handlers...\n") + (begin + exp ...) + + ;; Blocking wait for stanzas. At this + ;; point, new handler can only be + ;; registered by previously registered + ;; handlers. + (display "Starting listener loop.\n") + (run-handler-loop sock) + + ;; close connection at the end + (display "Closing stream.\n") + (close-stream))))) + (lambda (key . args) + (if (and (equal? 'system-error key) + (string-prefix? "connect" (car args))) + (begin + ;; connection refused or something + (display (format #t "ERROR: ~S\n" (cdr args)))) + (begin + (display (format #t "ERROR: ~S. Closing stream. (~S)\n" key args)) + (close-stream))))))))) -- cgit v1.2.3