1 ;; jabber-socks5.el - SOCKS5 bytestreams by JEP-0065
3 ;; Copyright (C) 2003, 2004, 2007 - Magnus Henoch - mange@freemail.hu
4 ;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
6 ;; This file is a part of jabber.el.
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.
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.
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
23 (require 'jabber-disco
)
24 (require 'jabber-si-server
)
25 (require 'jabber-si-client
)
26 (require 'jabber-newdisco
)
29 (eval-when-compile (require 'cl
))
31 (defvar jabber-socks5-pending-sessions nil
32 "List of pending sessions.
34 Each entry is a list, containing:
36 * Full JID of initiator
37 * State machine managing the session")
39 (defvar jabber-socks5-active-sessions nil
40 "List of active sessions.
42 Each entry is a list, containing:
45 * Full JID of initiator
46 * Profile data function")
48 (defcustom jabber-socks5-proxies nil
49 "JIDs of JEP-0065 proxies to use for file transfer.
50 Put preferred ones first."
51 :type
'(repeat string
)
53 ; :set 'jabber-socks5-set-proxies)
56 (defvar jabber-socks5-proxies-data nil
57 "Alist containing information about proxies.
58 Keys of the alist are strings, the JIDs of the proxies.
59 Values are \"streamhost\" XML nodes.")
61 (add-to-list 'jabber-advertised-features
"http://jabber.org/protocol/bytestreams")
63 (add-to-list 'jabber-si-stream-methods
64 (list "http://jabber.org/protocol/bytestreams"
65 'jabber-socks5-client-1
66 'jabber-socks5-accept
))
68 (defun jabber-socks5-set-proxies (symbol value
)
69 "Set `jabber-socks5-proxies' and query proxies.
70 This is the set function of `jabber-socks5-proxies-data'."
71 (set-default symbol value
)
72 (when *jabber-connected
*
73 (jabber-socks5-query-all-proxies)))
75 (defun jabber-socks5-query-all-proxies (jc &optional callback
)
76 "Ask all proxies in `jabber-socks5-proxies' for connection information.
77 If CALLBACK is non-nil, call it with no arguments when all
78 proxies have answered."
79 (interactive (list (jabber-read-account)))
80 (setq jabber-socks5-proxies-data nil
)
81 (dolist (proxy jabber-socks5-proxies
)
82 (jabber-socks5-query-proxy jc proxy callback
)))
84 (defun jabber-socks5-query-proxy (jc jid
&optional callback
)
85 "Query the SOCKS5 proxy specified by JID for IP and port number."
86 (jabber-send-iq jc jid
"get"
87 '(query ((xmlns .
"http://jabber.org/protocol/bytestreams")))
88 #'jabber-socks5-process-proxy-response
(list callback t
)
89 #'jabber-socks5-process-proxy-response
(list callback nil
)))
91 (defun jabber-socks5-process-proxy-response (jc xml-data closure-data
)
92 "Process response from proxy query."
93 (let* ((query (jabber-iq-query xml-data
))
94 (from (jabber-xml-get-attribute xml-data
'from
))
95 (streamhosts (jabber-xml-get-children query
'streamhost
)))
97 (let ((existing-entry (assoc from jabber-socks5-proxies-data
)))
99 (setq jabber-socks5-proxies-data
100 (delq existing-entry jabber-socks5-proxies-data
))))
102 (destructuring-bind (callback successp
) closure-data
104 (setq jabber-socks5-proxies-data
105 (cons (cons from streamhosts
)
106 jabber-socks5-proxies-data
)))
107 (message "%s from %s. %d of %d proxies have answered."
108 (if successp
"Response" "Error") from
109 (length jabber-socks5-proxies-data
) (length jabber-socks5-proxies
))
110 (when (and callback
(= (length jabber-socks5-proxies-data
) (length jabber-socks5-proxies
)))
111 (funcall callback
)))))
113 (define-state-machine jabber-socks5
114 :start
((jc jid sid profile-function role
)
115 "Start JEP-0065 bytestream with JID.
116 SID is the session ID used.
117 PROFILE-FUNCTION is the function to call upon success. See `jabber-si-stream-methods'.
118 ROLE is either :initiator or :target. The initiator sends an IQ
119 set; the target waits for one."
120 (let ((new-state-data (list :jc jc
123 :profile-function profile-function
126 ;; We want information about proxies; it might be needed in
127 ;; various situations.
129 ((null jabber-socks5-proxies
)
130 ;; We know no proxy addresses. Try to find them by disco.
132 ((null jabber-socks5-proxies-data
)
133 ;; We need to query the proxies for addresses.
135 ;; So, we have our proxies.
138 (list new-state new-state-data nil
))))
140 (defun jabber-socks5-accept (jc jid sid profile-function
)
141 "Remember that we are waiting for connection from JID, with stream id SID"
142 ;; asking the user for permission is done in the profile
143 (add-to-list 'jabber-socks5-pending-sessions
144 (list sid jid
(start-jabber-socks5 jc jid sid profile-function
:target
))))
146 (define-enter-state jabber-socks5 seek-proxies
(fsm state-data
)
147 ;; Look for items at the server.
148 (let* ((jc (plist-get state-data
:jc
))
149 (server (jabber-jid-server (jabber-connection-jid jc
))))
150 (jabber-disco-get-items jc
153 (lambda (jc fsm result
)
154 (fsm-send-sync fsm
(cons :items result
)))
156 ;; Spend no more than five seconds looking for a proxy.
159 (define-state jabber-socks5 seek-proxies
(fsm state-data event callback
)
160 "Collect disco results, looking for a bytestreams proxy."
161 ;; We put the number of outstanding requests as :remaining-info in
162 ;; the state-data plist.
164 ;; We're not ready to handle the IQ stanza yet
165 ((eq (car-safe event
) :iq
)
168 ;; Got list of items at the server.
169 ((eq (car-safe event
) :items
)
170 (dolist (entry (cdr event
))
171 ;; Each entry is ["name" "jid" "node"]. We send a disco info
172 ;; request to everything without a node.
173 (when (null (aref entry
2))
174 (lexical-let ((jid (aref entry
1)))
175 (jabber-disco-get-info
176 (plist-get state-data
:jc
)
178 (lambda (jc fsm result
)
179 (fsm-send-sync fsm
(list :info jid result
)))
181 ;; Remember number of requests sent. But if none, we just go on.
183 (list 'seek-proxies
(plist-put state-data
:remaining-info
(length (cdr event
))) :keep
)
184 (list 'initiate state-data nil
)))
186 ;; Got disco info from an item at the server.
187 ((eq (car-safe event
) :info
)
188 (fsm-debug-output "got disco event")
189 ;; Count the response.
190 (plist-put state-data
:remaining-info
(1- (plist-get state-data
:remaining-info
)))
191 (unless (eq (first (third event
)) 'error
)
192 (let ((identities (first (third event
))))
193 ;; Is it a bytestream proxy?
194 (when (dolist (identity identities
)
195 (when (and (string= (aref identity
1) "proxy")
196 (string= (aref identity
2) "bytestreams"))
198 ;; Yes, it is. Add it to the list.
199 (push (second event
) jabber-socks5-proxies
))))
201 ;; Wait for more responses, if any are to be expected.
202 (if (zerop (plist-get state-data
:remaining-info
))
203 ;; No more... go on to querying the proxies.
204 (list 'query-proxies state-data nil
)
205 ;; We expect more responses...
206 (list 'seek-proxies state-data
:keep
)))
209 ;; We can't wait anymore...
210 (list 'query-proxies state-data nil
))))
212 (define-enter-state jabber-socks5 query-proxies
(fsm state-data
)
213 (jabber-socks5-query-all-proxies
214 (plist-get state-data
:jc
)
215 (lexical-let ((fsm fsm
))
216 (lambda () (fsm-send-sync fsm
:proxies
))))
219 (define-state jabber-socks5 query-proxies
(fsm state-data event callback
)
220 "Query proxies in `jabber-socks5-proxies'."
222 ;; Can't handle the iq stanza yet...
223 ((eq (car-safe event
) :iq
)
226 ((eq (car-safe event
) :info
)
227 ;; stray event... do nothing
228 (list 'query-proxies state-data
:keep
))
230 ;; Got response/error from all proxies, or timeout
231 ((memq event
'(:proxies
:timeout
))
232 (list 'initiate state-data nil
))))
234 (define-enter-state jabber-socks5 initiate
(fsm state-data
)
235 ;; Sort the alist jabber-socks5-proxies-data such that the
236 ;; keys are in the same order as in jabber-socks5-proxies.
237 (setq jabber-socks5-proxies-data
238 (sort jabber-socks5-proxies-data
240 (> (length (member (car a
) jabber-socks5-proxies
))
241 (length (member (car b
) jabber-socks5-proxies
))))))
243 ;; If we're the initiator, send initiation stanza.
244 (when (eq (plist-get state-data
:role
) :initiator
)
245 ;; This is where initiation of server sockets would go
248 (plist-get state-data
:jc
)
249 (plist-get state-data
:jid
) "set"
250 `(query ((xmlns .
"http://jabber.org/protocol/bytestreams")
251 (sid .
,(plist-get state-data
:sid
)))
255 #'(lambda (streamhost)
257 (list (cons 'jid
(jabber-xml-get-attribute streamhost
'jid
))
258 (cons 'host
(jabber-xml-get-attribute streamhost
'host
))
259 (cons 'port
(jabber-xml-get-attribute streamhost
'port
)))
260 ;; (proxy ((xmlns . "http://affinix.com/jabber/stream")))
263 jabber-socks5-proxies-data
)
264 ;; (fast ((xmlns . "http://affinix.com/jabber/stream")))
266 (lexical-let ((fsm fsm
))
267 (lambda (jc xml-data closure-data
)
268 (fsm-send-sync fsm
(list :iq xml-data
))))
270 ;; TODO: error handling
271 #'jabber-report-success
"SOCKS5 negotiation"))
273 ;; If we're the target, we just wait for an incoming stanza.
274 (list state-data nil
))
276 (add-to-list 'jabber-iq-set-xmlns-alist
277 (cons "http://jabber.org/protocol/bytestreams" 'jabber-socks5-process
))
278 (defun jabber-socks5-process (jc xml-data
)
279 "Accept IQ get for SOCKS5 bytestream"
280 (let* ((jid (jabber-xml-get-attribute xml-data
'from
))
281 (id (jabber-xml-get-attribute xml-data
'id
))
282 (query (jabber-iq-query xml-data
))
283 (sid (jabber-xml-get-attribute query
'sid
))
284 (session (dolist (pending-session jabber-socks5-pending-sessions
)
285 (when (and (equal sid
(nth 0 pending-session
))
286 (equal jid
(nth 1 pending-session
)))
287 (return pending-session
)))))
288 ;; check that we really are expecting this session
290 (jabber-signal-error "auth" 'not-acceptable
))
292 (setq jabber-socks5-pending-sessions
(delq session jabber-socks5-pending-sessions
))
293 (fsm-send-sync (nth 2 session
) (list :iq xml-data
))
295 ;; find streamhost to connect to
296 ;; (let* ((streamhosts (jabber-xml-get-children query 'streamhost))
297 ;; (streamhost (dolist (streamhost streamhosts)
298 ;; (let ((connection (jabber-socks5-connect streamhost sid jid (concat jabber-username "@" jabber-server "/" jabber-resource))))
300 ;; ;; We select the first streamhost that we are able to connect to.
301 ;; (push (list connection sid jid profile-data-function)
302 ;; jabber-socks5-active-sessions)
303 ;; ;; Now set the filter, for the rest of the output
304 ;; (set-process-filter connection #'jabber-socks5-filter)
305 ;; (set-process-sentinel connection #'jabber-socks5-sentinel)
306 ;; (return streamhost))))))
307 ;; (unless streamhost
308 ;; (jabber-signal-error "cancel" 'item-not-found))
310 ;; ;; tell initiator which streamhost we use
311 ;; (jabber-send-iq jid "result"
312 ;; `(query ((xmlns . "http://jabber.org/protocol/bytestreams"))
313 ;; (streamhost-used ((jid . ,(jabber-xml-get-attribute streamhost 'jid)))))
314 ;; nil nil nil nil id)
315 ;; ;; now, as data is sent, it will be passed to the profile.
319 (define-state jabber-socks5 initiate
(fsm state-data event callback
)
320 (let* ((jc (plist-get state-data
:jc
))
321 (jc-data (fsm-get-state-data jc
))
322 (our-jid (concat (plist-get jc-data
:username
) "@"
323 (plist-get jc-data
:server
) "/"
324 (plist-get jc-data
:resource
)))
325 (their-jid (plist-get state-data
:jid
))
326 (initiator-jid (if (eq (plist-get state-data
:role
) :initiator
) our-jid their-jid
))
327 (target-jid (if (eq (plist-get state-data
:role
) :initiator
) their-jid our-jid
)))
330 ((memq (car-safe event
) '(:proxy
:info
))
331 (list 'initiate state-data
:keep
))
334 ((eq (car-safe event
) :iq
)
335 (let ((xml-data (second event
)))
336 ;; This is either type "set" (with a list of streamhosts to
337 ;; use), or a "result" (indicating the streamhost finally used
338 ;; by the other party).
340 ((string= (jabber-xml-get-attribute xml-data
'type
) "set")
341 ;; A "set" makes sense if we're the initiator and offered
342 ;; Psi's "fast mode". We don't yet, though, so this is only
344 (dolist (streamhost (jabber-xml-get-children (jabber-iq-query xml-data
) 'streamhost
))
345 (jabber-xml-let-attributes
346 (jid host port
) streamhost
347 ;; This is where we would attempt to support zeroconf
348 (when (and jid host port
)
349 (start-jabber-socks5-connection
350 jc initiator-jid target-jid jid
351 (plist-get state-data
:sid
) host port fsm
))))
353 (list 'wait-for-connection
(plist-put state-data
:iq-id
(jabber-xml-get-attribute xml-data
'id
)) 30))
355 ((string= (jabber-xml-get-attribute xml-data
'type
) "result")
356 ;; The other party has decided what streamhost to use.
357 (let* ((proxy-used (jabber-xml-get-attribute (jabber-xml-path xml-data
'(query streamhost-used
)) 'jid
))
358 ;; If JID is our own JID, we have probably already detected
359 ;; what connection to use. But that is a later problem...
360 (streamhosts (cdr (assoc proxy-used jabber-socks5-proxies-data
))))
361 ;; Try to connect to all addresses of this proxy...
362 (dolist (streamhost streamhosts
)
363 (jabber-xml-let-attributes
364 (jid host port
) streamhost
365 (when (and jid host port
)
366 (start-jabber-socks5-connection
367 jc initiator-jid target-jid jid
368 (plist-get state-data
:sid
) host port fsm
)))))
370 (list 'wait-for-connection state-data
30))))))))
372 (define-state-machine jabber-socks5-connection
374 ((jc initiator-jid target-jid streamhost-jid sid host port socks5-fsm
)
375 "Connect to a single JEP-0065 streamhost."
376 (let ((coding-system-for-read 'binary
)
377 (coding-system-for-write 'binary
))
378 ;; make-network-process, which we really want, for asynchronous
379 ;; connection and such, was introduced in Emacs 22.
380 (if (fboundp 'make-network-process
)
382 (make-network-process
386 :service
(string-to-number port
)
388 :filter
(fsm-make-filter fsm
)
389 :sentinel
(fsm-make-sentinel fsm
))))
390 (list 'wait-for-connection
392 :connection connection
393 :initiator-jid initiator-jid
394 :target-jid target-jid
395 :streamhost-jid streamhost-jid
397 :socks5-fsm socks5-fsm
)
399 ;; So we open a stream, and wait for the connection to succeed.
402 (open-network-stream "socks5" nil
403 host
(string-to-number port
))))
404 (set-process-filter connection
(fsm-make-filter fsm
))
405 (set-process-sentinel connection
(fsm-make-sentinel fsm
))
408 :connection connection
409 :initiator-jid initiator-jid
410 :target-jid target-jid
411 :streamhost-jid streamhost-jid
413 :socks5-fsm socks5-fsm
)
415 (error (list 'fail
'() nil
)))))))
417 (define-state jabber-socks5-connection wait-for-connection
418 (fsm state-data event callback
)
420 ((eq (car-safe event
) :sentinel
)
421 (let ((string (third event
)))
423 ;; Connection succeeded
424 ((string= (substring string
0 4) "open")
425 (list 'authenticate state-data nil
))
428 (list 'fail state-data nil
)))))))
430 (define-enter-state jabber-socks5-connection authenticate
432 "Send authenticate command."
433 ;; version: 5. number of auth methods supported: 1.
434 ;; which one: no authentication.
435 (process-send-string (plist-get state-data
:connection
) (string 5 1 0))
436 (list state-data
30))
438 (define-state jabber-socks5-connection authenticate
439 (fsm state-data event callback
)
440 "Receive response to authenticate command."
442 ((eq (car-safe event
) :filter
)
443 (let ((string (third event
)))
445 ;; version: 5. auth method to use: none
446 (if (string= string
(string 5 0))
447 ;; Authenticated. Send connect command.
448 (list 'connect state-data nil
)
449 ;; Authentication failed...
450 (delete-process (second event
))
451 (list 'fail state-data nil
))))
453 ((eq (car-safe event
) :sentinel
)
454 (list 'fail state-data nil
))))
456 (define-enter-state jabber-socks5-connection connect
(fsm state-data
)
457 "Send connect command."
458 (let* ((sid (plist-get state-data
:sid
))
459 (initiator (plist-get state-data
:initiator-jid
))
460 (target (plist-get state-data
:target-jid
))
461 (hash (sha1-string (concat sid initiator target
))))
463 (plist-get state-data
:connection
)
464 (concat (string 5 1 0 3 (length hash
))
467 (list state-data
30)))
469 (define-state jabber-socks5-connection connect
470 (fsm state-data event callback
)
471 "Receive response to connect command."
473 ((eq (car-safe event
) :filter
)
474 (let ((string (third event
)))
475 (if (string= (substring string
0 2) (string 5 0))
476 ;; connection established
478 (fsm-send (plist-get state-data
:socks5-fsm
)
480 (plist-get state-data
:connection
)
481 (plist-get state-data
:streamhost-jid
)))
484 (list 'fail state-data nil
))))
485 ((eq (car-safe event
) :sentinel
)
486 (list 'fail state-data nil
))))
488 (define-state jabber-socks5-connection done
489 (fsm state-data event callback
)
491 (list 'done nil nil
))
493 (define-enter-state jabber-socks5-connection fail
(fsm state-data
)
494 ;; Notify parent fsm about failure
495 (fsm-send (plist-get state-data
:socks5-fsm
)
499 (define-state jabber-socks5-connection fail
500 (fsm state-data event callback
)
502 (list 'fail nil nil
))
504 (define-state jabber-socks5 wait-for-connection
505 (fsm state-data event callback
)
507 ((eq (car-safe event
) :connected
)
508 (destructuring-bind (ignored connection streamhost-jid
) event
509 (setq state-data
(plist-put state-data
:connection connection
))
510 ;; If we are expected to tell which streamhost we chose, do so.
511 (let ((iq-id (plist-get state-data
:iq-id
)))
514 (plist-get state-data
:jc
)
515 (plist-get state-data
:jid
) "result"
516 `(query ((xmlns .
"http://jabber.org/protocol/bytestreams"))
517 (streamhost-used ((jid .
,streamhost-jid
))))
521 ;; If we are the initiator, we should activate the bytestream.
522 (if (eq (plist-get state-data
:role
) :initiator
)
525 (plist-get state-data
:jc
)
527 `(query ((xmlns .
"http://jabber.org/protocol/bytestreams")
528 (sid .
,(plist-get state-data
:sid
)))
529 (activate nil
,(plist-get state-data
:jid
)))
530 (lambda (jc xml-data fsm
) (fsm-send-sync fsm
:activated
)) fsm
531 (lambda (jc xml-data fsm
) (fsm-send-sync fsm
:activation-failed
)) fsm
)
532 (list 'wait-for-activation state-data
10))
533 ;; Otherwise, we just let the data flow.
534 (list 'stream-activated state-data nil
))))
536 ((eq event
:not-connected
)
537 ;; If we were counting the streamhosts, we would know when there
538 ;; are no more chances left.
539 (list 'wait-for-connection state-data
:keep
))
542 (list 'fail
(plist-put state-data
:error
"Timeout when connecting to streamhosts") nil
))))
544 (define-state jabber-socks5 wait-for-activation
545 (fsm state-data event callback
)
547 ((eq event
:activated
)
548 (list 'stream-activated state-data nil
))
549 ((eq event
:activation-failed
)
550 (list 'fail
(plist-put state-data
:error
"Proxy activation failed") nil
))
552 ;; Stray events from earlier state
553 ((eq (car-safe event
) :connected
)
554 ;; We just close the connection
555 (delete-process (second event
))
556 (list 'wait-for-activation state-data
:keep
))
557 ((eq event
:not-connected
)
558 (list 'wait-for-activation state-data
:keep
))))
560 (define-enter-state jabber-socks5 stream-activated
562 (let ((connection (plist-get state-data
:connection
))
563 (jc (plist-get state-data
:jc
))
564 (jid (plist-get state-data
:jid
))
565 (sid (plist-get state-data
:sid
))
566 (profile-function (plist-get state-data
:profile-function
)))
567 (set-process-filter connection
(fsm-make-filter fsm
))
568 (set-process-sentinel connection
(fsm-make-sentinel fsm
))
569 ;; Call the profile function, passing the data send function, and
570 ;; receiving the data receiving function. Put the data receiving
571 ;; function in the plist.
572 (list (plist-put state-data
573 :profile-data-function
574 (funcall profile-function
576 (lexical-let ((fsm fsm
))
578 (fsm-send fsm
(list :send data
))))))
582 (define-state jabber-socks5 stream-activated
583 (fsm state-data event callback
)
584 (let ((jc (plist-get state-data
:jc
))
585 (connection (plist-get state-data
:connection
))
586 (profile-data-function (plist-get state-data
:profile-data-function
))
587 (sid (plist-get state-data
:sid
))
588 (jid (plist-get state-data
:jid
)))
590 ((eq (car-safe event
) :send
)
591 (process-send-string connection
(second event
))
592 (list 'stream-activated state-data nil
))
594 ((eq (car-safe event
) :filter
)
595 ;; Pass data from connection to profile data function
596 ;; If the data function requests it, tear down the connection.
597 (unless (funcall profile-data-function jc jid sid
(third event
))
598 (fsm-send fsm
(list :sentinel
(second event
) "shutdown")))
600 (list 'stream-activated state-data nil
))
602 ((eq (car-safe event
) :sentinel
)
603 ;; Connection terminated. Shuffle together the remaining data,
604 ;; and kill the buffer.
605 (delete-process (second event
))
606 (funcall profile-data-function jc jid sid nil
)
607 (list 'closed nil nil
))
609 ;; Stray events from earlier state
610 ((eq (car-safe event
) :connected
)
611 ;; We just close the connection
612 (delete-process (second event
))
613 (list 'stream-activated state-data nil
))
614 ((eq event
:not-connected
)
615 (list 'stream-activated state-data nil
)))))
617 (define-enter-state jabber-socks5 fail
(fsm state-data
)
618 "Tell our caller that we failed."
619 (let ((jc (plist-get state-data
:jc
))
620 (jid (plist-get state-data
:jid
))
621 (sid (plist-get state-data
:sid
))
622 (profile-function (plist-get state-data
:profile-function
))
623 (iq-id (plist-get state-data
:iq-id
)))
624 (funcall profile-function jc jid sid
(plist-get state-data
:error
))
627 (jabber-send-iq-error jc jid iq-id nil
"cancel"
628 'remote-server-not-found
)))
631 (defun jabber-socks5-client-1 (jc jid sid profile-function
)
632 "Negotiate a SOCKS5 connection with JID.
633 This function simply starts a state machine."
634 (add-to-list 'jabber-socks5-pending-sessions
635 (list sid jid
(start-jabber-socks5 jc jid sid profile-function
:initiator
))))
637 ;; (defun jabber-socks5-client-2 (xml-data jid sid profile-function)
638 ;; "Contact has selected a streamhost to use. Connect to the proxy."
639 ;; (let* ((query (jabber-iq-query xml-data))
640 ;; (streamhost-used (car (jabber-xml-get-children query 'streamhost-used)))
641 ;; (proxy-used (jabber-xml-get-attribute streamhost-used 'jid))
643 ;; (let ((streamhosts-left (cdr (assoc proxy-used jabber-socks5-proxies-data))))
644 ;; (while (and streamhosts-left (not connection))
646 ;; (jabber-socks5-connect (car streamhosts-left)
648 ;; (concat jabber-username "@" jabber-server "/" jabber-resource)
650 ;; (setq streamhosts-left (cdr streamhosts-left))))
651 ;; (unless connection
652 ;; (error "Couldn't connect to proxy %s" proxy-used))
654 ;; ;; Activation is only needed for proxies.
655 ;; (jabber-send-iq proxy-used "set"
656 ;; `(query ((xmlns . "http://jabber.org/protocol/bytestreams")
658 ;; (activate () ,jid))
659 ;; (lexical-let ((jid jid) (sid sid) (profile-function profile-function)
660 ;; (connection connection))
661 ;; (lambda (xml-data closure-data)
662 ;; (jabber-socks5-client-3 xml-data jid sid profile-function connection))) nil
663 ;; ;; TODO: report error to contact?
664 ;; #'jabber-report-success "Proxy activation")))
666 ;; (defun jabber-socks5-client-3 (xml-data jid sid profile-function proxy-connection)
667 ;; "Proxy is activated. Start the transfer."
668 ;; ;; The response from the proxy does not contain any interesting
669 ;; ;; information, beyond success confirmation.
671 ;; (funcall profile-function jid sid
672 ;; (lexical-let ((proxy-connection proxy-connection))
674 ;; (process-send-string proxy-connection data)))))
676 (provide 'jabber-socks5
)
678 ;;; arch-tag: 9e70dfea-2522-40c6-a79f-302c8fb82ac5