1
;;;; pb.el - a perspective broker implementation for emacs
2
;;;; by Allen Short <washort@twistedmatrix.com>
3
;;;; this file is in the public domain.
5
"'We will never run out of things to program as long as there is a
6
single program around.' -- Alan Perlis, Programming Epigram #100"
12
(defconst pb-version 6)
13
(defconst pb-port 8787)
17
(waiting-for-answers (make-hash-table))
21
(local-objects (make-hash-table)))
23
(defsubst hexchar-to-int (hex)
25
((and (<= ?0 hex) (<= hex ?9)) (- hex ?0))
26
((and (<= ?A hex) (<= hex ?F)) (+ (- hex ?A) 10))
27
((and (<= ?a hex) (<= hex ?f)) (+ (- hex ?a) 10))))
30
(let* ((md5str (md5 str))
32
(md5raw (make-string (/ len 2) 0))
35
(aset md5raw j (+ (* (hexchar-to-int (aref md5str i)) 16)
36
(hexchar-to-int (aref md5str (1+ i)))))
41
(defun pb-passport-respond (challenge password)
42
(md5-raw (concat (md5-raw password) challenge)))
44
(defun netjelly-unserialize (broker jelly)
45
(let ((refs (make-hash-table)))
46
(jelly-fixup-refs (netjelly-unserialize-internal broker jelly))))
50
(defun netjelly-unserialize-internal (broker jelly)
51
;; some days i think glyph does these things on purpose, to annoy me
52
(if (or (integerp jelly) (stringp jelly) (floatp jelly) (null jelly))
54
(ecase (intern (car jelly))
56
(mapcar (lambda (i) (netjelly-unserialize-internal broker i)) (cdr jelly)))
58
(let ((ht (make-hash-table)))
61
(let ((k (car pair)) (v (second pair)))
62
(setf (gethash k ht) (netjelly-unserialize-internal broker v))))
65
((integer string float)
66
(lambda () (cdr jelly)))
68
(let ((val (netjelly-unserialize-internal broker (third jelly))))
69
(setf (gethash (second jelly) refs) val)
72
(lexical-let ((-ref-num- (cadr jelly)))
73
(lambda () (gethash -ref-num- refs))))
75
(make-remote-reference nil broker (cadr jelly)))
77
(gethash (cadr jelly) (pb-broker-local-objects broker))))))
81
(defun make-remote-reference (perspective broker luid)
82
(lexical-let ((-perspective- perspective) (-broker- broker) (-luid- luid))
83
(lambda (message args kwargs &optional callback errback)
84
;; remote-serialize(broker val)
85
(if (equal message 'remote-serialize)
87
(unless (eq -broker- (car args))
88
(error "Can't send references to brokers other than their own."))
90
(pb-send-message -broker- -perspective- -luid- message args kwargs callback errback)))))
92
(defun pb-traceback (tb)
93
(let ((b (get-buffer-create "*PB Traceback*")))
98
(defsubst pb-send-call (broker &rest args)
99
(banana-send-encoded (pb-broker-socket broker) args))
101
(defun pb-send-message (broker perspective obj-id message args kwargs callback errback)
102
(if (pb-broker-disconnected broker) (error "calling stale broker"))
103
(let ((net-args (jelly-serialize args (make-jelly)))
104
(net-kw (jelly-serialize-alist kwargs (make-jelly)))
105
(request-id (incf (pb-broker-request-id broker)))
106
(answer-required (if (or callback errback) 1 0)))
107
(puthash request-id (cons callback errback) (pb-broker-waiting-for-answers broker))
108
(pb-send-call broker "message" request-id obj-id message answer-required net-args net-kw)))
110
(defun pb-connect-internal (host port callback)
111
(let ((broker (make-pb-broker))
112
(sock (open-network-stream "pb" nil host (or port pb-port))))
113
(setf (pb-broker-socket broker) sock)
116
(lexical-let ((-broker- broker)
117
(-callback- callback))
119
(lambda (expr) (pb-socket-filter -broker- expr))
120
(lambda () (funcall -callback- -broker-)))))
123
(defun pb-get-object-at (host port callback &optional errback timeout)
124
;; (let ((b (pb-connect-internal host port)))
125
;; (funcall callback (make-remote-reference nil b "root")))
126
(lexical-let ((-callback- callback))
129
(lambda (b) (funcall -callback- (make-remote-reference nil b "root"))))))
131
(defun pb-connect (callback errback host port username password service &optional perspective client timeout)
132
;; the proper indentation of this code is left as an exercise for the reader
133
(lexical-let ((-callback- callback)
135
(-username- username)
136
(-password- password)
138
(-perspective- perspective)
143
(funcall authserv "username"
144
(list -username-) nil
146
(funcall (second chal) "respond"
147
(list (pb-passport-respond (first chal) -password-)) nil
150
(funcall identity "attach"
151
(list -service- (or -perspective- -username-) -client-) nil
154
(funcall -errback- "invalid username or password")))
157
(lambda (error) (funcall -errback- error))
160
(defun pb-socket-filter (broker sexp)
161
(if (listp (car sexp))
162
(mapcar (lambda (s) (pb-dispatch broker s)) sexp)
163
(pb-dispatch broker sexp)))
165
(defun pb-dispatch (broker expr)
166
(let ((fun (symbol-function
167
(intern (concat "pb-proto-" (car expr))))))
168
(apply fun (cons broker (cdr expr)))))
170
(defun pb-proto-version (broker vnum)
171
(unless (eq vnum pb-version) (error "Incompatible protocol versions")))
173
(defun pb-proto-answer (broker request-id net-result)
174
(let ((funs (gethash request-id (pb-broker-waiting-for-answers broker))))
175
(remhash request-id (pb-broker-waiting-for-answers broker))
176
(funcall (car funs) (netjelly-unserialize broker net-result))))
178
(defun pb-shutdown (broker)
179
(let ((h (pb-broker-waiting-for-answers broker)))
180
(maphash (lambda (k v)
182
(funcall (cdr v) "Connection lost"))
184
;;(mapcar #'funcall (pb-broker-disconnects broker))
185
(delete-process (pb-broker-socket broker))
186
(setf (pb-broker-disconnected broker) t))