]> crepu.dev Git - config.git/blame_incremental - djavu-asus/emacs/elpa/jabber-20230715.456/jabber-httpupload.el
Reorganización de directorios
[config.git] / djavu-asus / emacs / elpa / jabber-20230715.456 / jabber-httpupload.el
... / ...
CommitLineData
1;;; jabber-httpupload.el --- Emacs Jabber HTTP Upload Implementation -*- lexical-binding: t; -*-
2
3;; Copyright 2021 cnngimenez
4;;
5;; Author: cnngimenez
6;; Maintainer: cnngimenez
7;; Version: 0.1.0
8;; Keywords: comm
9;; URL: https://github.com/cnngimenez/emacs-jabber
10;; Package-Requires: ((emacs "26.1") (jabber "0.8.92"))
11
12;; This program is free software: you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
16
17;; This program is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with this program. If not, see <https://www.gnu.org/licenses/>.
24
25
26;;; Commentary:
27
28;; This file implements XEP-0363: HTTP Upload
29;; (https://xmpp.org/extensions/xep-0363.html), providing a way to
30;; send files (images, audio, etc) through XMPP clients by using
31;; server space, and the HTTP protocol to upload and download from it.
32;; The advantage is that the sender user does not need to be connected
33;; after sharing the file, and the receiver may be disconnected while
34;; the sender is uploading.
35
36;; The procedure to send a file is as follows -
37
38;; 1. Use Disco queries to discover if the server supports the HTTP Upload (~urn:xmpp:http:upload~ namespace).
39;; 2. Request a slot to the upload Disco item. The server will answer with a GET and PUT URL.
40;; 3. Upload the file to the HTTP server by using the PUT URL.
41;; 4. Usually, send the GET URL to the other XMPP clients to allow them to access the uploaded file.
42;;
43;; TODO -
44;; 1. Use wget to send the file
45;; 2. Recording audio and sending
46
47;;; Code:
48
49(require 'seq)
50(require 'fsm)
51(require 'mailcap)
52(require 'jabber)
53
54;; * Configuration variables *
55
56(defgroup jabber-httpupload nil "Jabber HTTP Upload Settings."
57 :group 'jabber)
58
59(defcustom jabber-httpupload-upload-function #'jabber-httpupload-put-file-curl
60 "The function used to upload the file.
61 Some functions calls external programs such as Curl and wget, please check their
62 documentation for more information."
63 :group 'jabber-httpupload
64 :type 'function)
65
66(defcustom jabber-httpupload-record-command "sox -d -t ogg $(filename).ogg"
67 "What is the command used to record audio?
68Use $(filename) where the temporal filename should be."
69 :group 'jabber-httpupload
70 :type 'function)
71
72;; Disco is used to discover if HTTP Upload is supported on the server
73;; side. Two queries are used:
74
75;; 1. An IQ Disco items request to get all items supported by the
76;; server.
77;; 2. For each item, an IQ Disco info request to test if the item is
78;; the Upload service.
79
80;; The namespace of the HTTP Upload feature is
81;; "urn:xmpp:http:upload:0". This will be used on the second query to
82;; detect which item is the upload service.
83
84;; For more information, see XML examples at the
85;; [[https://xmpp.org/extensions/xep-0363.html#disco][Discovering
86;; Support section of XEP-0363]].
87
88;; This implementation requires an initialization step to fill the
89;; `jabber-httpupload-support' variable. This variable registers all
90;; connections with their HTTP Upload item. If one of the server
91;; associated to a connection does not support HTTP Upload, then it
92;; will be registered with a `nil' item.
93
94;; * Discovering support *
95
96(defvar jabber-httpupload-support nil
97 "Alist of Jabber connections and the node with HTTP Upload support.
98This is filled by the `jabber-httpupload-test-all-connections-suport'.
99Each element are of the form (jabber-connection . string/nil). If the value is
100a string, it is the upload item IRI, if nil means no support.")
101
102(defun jabber-httpupload-test-all-connections-support ()
103 "Test all connections in `jabber-connections' for HTTP Upload support.
104Store the results at `jabber-httpupload-support'.
105If the connection is already tested, ignore it."
106 (let ((connections (seq-difference jabber-connections
107 (mapcar #'car jabber-httpupload-support))))
108 (dolist (jc connections)
109 (jabber-httpupload-test-connection-support jc))))
110
111(defun jabber-httpupload-test-connection-support (jc)
112 "Test if HTTP Upload is supported on the JC connection's server.
113If it is supported, store the item IRI at `jabber-httpupload-support'.
114
115This function is asynchronous, thus it won't return any results."
116 (jabber-httpupload-apply-to-items jc
117 (lambda (jc result)
118 (jabber-httpupload-test-item-support jc (elt result 1)))))
119
120;; CALLBACK receives three arguments: the jabber connection, extra
121;; data and the query result. The result is a list of features
122;; supported by the server. For example, if the client receives the
123;; following IQ answer:
124;;
125;; <iq from="upload.server.org"
126;; type="result"
127;; to="myjid@server.org/pc1"
128;; id="emacs-iq-24678.666.622936">
129;; <query xmlns="http://jabber.org/protocol/disco#info">
130;; <identity name="HTTP File Upload" type="file" category="store"/>
131;; <feature var="http://jabber.org/protocol/disco#info"/>
132;; <feature var="http://jabber.org/protocol/disco#items"/>
133;; <feature var="urn:xmpp:http:upload:0"/>
134;; <feature var="urn:xmpp:http:upload"/>
135;; <x xmlns="jabber:x:data" type="result">
136;; <field type="hidden" var="FORM_TYPE">
137;; <value>urn:xmpp:http:upload:0</value>
138;; </field>
139;; <field type="text-single" var="max-file-size">
140;; <value>500000</value>
141;; </field>
142;; </x>
143;; <x xmlns="jabber:x:data" type="result">
144;; <field type="hidden" var="FORM_TYPE">
145;; <value>urn:xmpp:http:upload</value>
146;; </field>
147;; <field type="text-single" var="max-file-size">
148;; <value>500000</value>
149;; </field>
150;; </x>
151;; </query>
152;; </iq>
153;;
154;; The result would be:
155;;
156;; ((["HTTP File Upload" "store" "file"])
157;; ("http://jabber.org/protocol/disco#info"
158;; "http://jabber.org/protocol/disco#items"
159;; "urn:xmpp:http:upload:0"
160;; "urn:xmpp:http:upload"))
161;;
162;; This Disco item supports HTTP Upload because the
163;; "urn:xmpp:http:upload" namespace is in the second list.
164
165(defun jabber-httpupload-test-item-support (jc iri)
166 "Test if the IRI Disco item supports HTTP Upload.
167Get the Disco Info from the provided IRI at the current JC jabber connection,
168if the HTTP Upload namespace feature is in the answer, store the IRI
169in `jabber-httpupload-support'."
170 (jabber-disco-get-info jc iri nil
171 (lambda (jc _data result)
172 (when (member "urn:xmpp:http:upload"
173 (nth 1 result))
174 ;; This item supports HTTP Upload... register it!
175 (push (cons jc iri) jabber-httpupload-support)))
176 nil))
177
178;; CALLBACK receives three arguments: the jabber connection, extra
179;; data and the query result. The result is a list of vector with the
180;; node name, its IRI and any other properties.
181;;
182;; For example, if the client receives the following XML:
183;;
184;; <iq from="server.org" type="result" to="myjid@server.org/pc1" id="emacs-iq-24677.56646.166389">
185;; <query xmlns="http://jabber.org/protocol/disco#items">
186;; <item jid="conference.server.org" name="MUC chats!"/>
187;; <item jid="upload.server.org"/>
188;; </query>
189;; </iq>
190;;
191;; The result would be:
192
193;; (["MUC chats!" "conference.server.org" nil] [nil "upload.server.org" nil])
194
195(defun jabber-httpupload-apply-to-items (jc callback)
196 "Retrieve al Disco IRIs from the server connected in JC.
197Return a list of IRI strings.
198
199JC is a jabber connection.
200CALLBACK is a function that receives two arguments: Jabber connection and
201the item vector."
202 (let ((node (plist-get (fsm-get-state-data jc) :server)))
203 (jabber-disco-get-items jc node nil
204 (lambda (jc _data result)
205 (dolist (item result)
206 (message "item: %S" item)
207 (funcall callback jc item)))
208 nil)))
209
210(defun jabber-httpupload-server-has-support (jc)
211 "Check if the server has HTTP Upload support.
212Return the tuple (jabber-connection . upload-url) when there is support from
213the server. Return nil when the server does not support HTTP Upload.
214
215If the server is not in `jabber-httpupload-support', then it is considered as
216it is not supported. It SHOULD be tested on-line with
217`jabber-httpupload-test-connection-support' as soon as the connection and
218authentication is established.
219
220JC is the Jabber Connection to use."
221
222 (seq-find (lambda (tuple)
223 (and (equal jc (car tuple))
224 (cdr tuple)))
225 jabber-httpupload-support))
226
227;; * Requesting a slot *
228
229;; The XEP specifies that the client must ask for a "slot" before
230;; uploading the file to the server. The slot is a fresh URL that will
231;; be enabled for the client to upload the file. The server may give
232;; two URLs in one slot query: the uploading URL and the GET URL to
233;; share.
234
235;; The server may limit the file size to upload.
236
237;; <iq from='upload.montague.tld'
238;; id='step_03'
239;; to='romeo@montague.tld/garden'
240;; type='result'>
241;; <slot xmlns='urn:xmpp:http:upload:0'>
242;; <put url='https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg'>
243;; <header name='Authorization'>Basic Base64String==</header>
244;; <header name='Cookie'>foo=bar; user=romeo</header>
245;; </put>
246;; <get url='https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/tr%C3%A8s%20cool.jpg' />
247;; </slot>
248;; </iq>
249
250(defun jabber-httpupload-parse-slot-answer (xml-data)
251 "Retrieve the slot data from the XML-DATA information.
252The XML-DATA is the stanza receive from the Jabber Connection after requesting
253the slot for a file.
254The returned list has the PUT URL and the GET URL."
255 (list
256 (jabber-xml-get-attribute (jabber-xml-path xml-data '(slot put)) 'url)
257 (jabber-xml-get-attribute (jabber-xml-path xml-data '(slot get)) 'url)))
258
259(defun jabber-httpupload--request-slot-successful (jc xml-data data)
260 "Callback function used when the slot request succeeded.
261XML-DATA is the received XML from the server.
262DATA is a triple (filedata success-callback success-args) where:
263 FILEDATA is a triple (filename size content-type)
264 SUCCESS-CALLBACK is a function to call after parsing and requesting the
265 upload.
266 It should accept following arguments: JC XML-DATA FILEDATA PUT-GET-URLS
267 and SUCCESS-ARGS.
268 SUCCESS-ARGS is a list to pass to the SUCCESS-CALLBACK."
269 (let ((urls (jabber-httpupload-parse-slot-answer xml-data))
270 (filedata (car data))
271 (success-callback (nth 1 data))
272 (success-args (nth 2 data)))
273 (funcall success-callback jc xml-data filedata urls success-args)))
274
275;; Maybe this function should be added as lambda inside the jabber-httpupload-request-slot...
276(defun jabber-httpupload--request-slot-failed (jc xml-data data)
277 "Callback function used when the slot request failed.
278
279DATA is a list (filedata error-callback error-args) where:
280 FILEDATA is a triple (filename size content-type)
281 ERROR-CALLBACK is a function to call. If no error-callback is provided, then
282 `error' is used. Its arguments are JC XML-DATA FILEDATA ERROR-ARGS.
283 ERROR-ARGS is list passed to the ERROR-CALLBACK."
284 (let ((filedata (car data))
285 (error-callback (nth 1 data))
286 (error-args (nth 2 data)))
287 (if error-callback
288 (funcall error-callback jc xml-data filedata error-args)
289 (error (format "The file %s cannot be uploaded: SLOT rejected. %S"
290 (car data) xml-data)))))
291
292;; The XML used to request a slot is similar to the following -
293;; <iq from='romeo@montague.tld/garden'
294;; id='step_03'
295;; to='upload.montague.tld'
296;; type='get'>
297;; <request xmlns='urn:xmpp:http:upload:0'
298;; filename='très cool.jpg'
299;; size='23456'
300;; content-type='image/jpeg' />
301;; </iq>
302
303(defun jabber-httpupload-request-slot (jc filedata success-callback success-args
304 &optional error-callback error-args)
305 "Request a slot for HTTP Upload to the server's connection.
306JC is an active Jabber Connection.
307FILEDATA is a list with (filename size content-type).
308SUCCESS-CALLBACK is a function name to call when the slot is received. Its
309 arguments should be: jc xml-data data and put-get-URLs.
310SUCCESS-ARGS is a list of arguments used by the SUCCESS-CALLBACK
311ERROR-CALLBACK is a function to call on failure. Its arguments should be:
312 jc xml-data.
313ERROR-ARGS is a list with arguments for ERROR-CALLBACK."
314 (let ((filename (file-name-nondirectory (car filedata)))
315 (size (nth 1 filedata))
316 (content-type (nth 2 filedata)))
317 (jabber-send-iq jc (cdr (jabber-httpupload-server-has-support jc)) "get"
318 `(request ((xmlns . "urn:xmpp:http:upload:0")
319 (filename . ,filename)
320 (size . ,size)
321 (content-type . ,content-type)))
322 #'jabber-httpupload--request-slot-successful
323 (list filedata success-callback success-args)
324 #'jabber-httpupload--request-slot-failed
325 (list filedata error-callback error-args))))
326
327;; * Uploading the file *
328
329;; Use the HTTP protocol to upload the file to the PUT URL provided by
330;; the slot.
331
332;; The following functions call the upload programs asynchronously.
333;; When the program ends, a callback function is called with one
334;; argument provided by the caller function.
335
336;; The uploading process supports multiple calls. For example, the
337;; user may call `jabber-httpupload-send-file' again while the upload process of a
338;; previous `jabber-httpupload-send-file' call is still running.
339
340;; Also, a callback can be provided in order to send the URL to the
341;; receiving Jabber client or to perform any other action after
342;; uploading the file.
343
344(defun jabber-httpupload-ignore-certificate (jc)
345 "Should the SSL/TLS certificates be ignore from JC connection?
346Check if JC URL is in the variable `jabber-invalid-certificate-servers', if it
347is the XMPP and HTTPs connection should be established regarding their
348certificate validation status."
349 (member (plist-get (fsm-get-state-data jc) :server)
350 jabber-invalid-certificate-servers))
351
352(defun jabber-httpupload-upload-file (filepath content-type put-url
353 callback callback-arg
354 &optional ignore-cert-problems)
355 "Update the given file at FILEPATH to the provided PUT-URL.
356The CONTENT-TYPE (MIME type) of the file must match the one provided
357to the Jabber Connection with `jabber-httpupload-request-slot'.
358IGNORE-CERT-PROBLEMS allows to connect with HTTPS servers with invalid or
359non-trusted SSL/TLS certificates.
360When the process ends, a callback function is called using the following
361code: (funcall CALLBACK CALLBACK-ARG)"
362 (unless (funcall jabber-httpupload-upload-function filepath content-type put-url
363 callback callback-arg
364 ignore-cert-problems)
365 (error (concat "The upload function failed to PUT the file to the server. "
366 "Try other function or install the required program"))))
367
368;; Multiple files can be uploaded in parallel, and thus multiple
369;; subprocess could be working at the same time. This happens when the
370;; user calls interactively `jabber-httpupload-send-file' twice or while a file is
371;; still uploading.
372
373;; This variable keeps track of the subprocesses and their callback
374;; along with any data required by these functions.
375
376(defvar jabber-httpupload-upload-processes nil
377 "Alist of running processes uploading the file to the server.
378List of running processes uploading the file to the server
379associated with their callback and arguments. Each element has
380the following format: (process . (callback arg))")
381
382;; When the file has been uploaded, the process is still registered
383;; with its callback function. This callback should be called and the
384;; process deleted from the system.
385
386(defun jabber-httpupload-process-ended (process)
387 "What to do when an upload process ends.
388PROCESS is the process that ended.
389First remove the process from `jabber-httpupload-upload-processes',
390then call its callback with the provided argument."
391 (let* ((data (assq process jabber-httpupload-upload-processes))
392 (callback (cadr data))
393 (callback-arg (caddr data)))
394 (setq jabber-httpupload-upload-processes
395 (assq-delete-all process jabber-httpupload-upload-processes))
396 (funcall callback callback-arg)))
397
398;; Using CURL to send the file
399
400;; These functions call curl to send the file to the server. A
401;; sentinel is required to check when the subprocess finishes to call
402;; the next function (usually, send the URL to the other jabber
403;; client).
404
405(defun jabber-httpupload-curl-sentinel (process event)
406 "Detect when Curl ends and act accordingly.
407PROCESS is the asynchronous Curl call.
408EVENT is a string describing the reason the sentinel were called.
409
410When EVENT is \"finished\n\", then the function
411`jabber-httpupload-process-ended' is called."
412 (with-current-buffer (process-buffer process)
413 (let ((inhibit-read-only t))
414 (goto-char (point-max))
415 (insert (format "Sentinel: %S event received." event))))
416 (when (string= event "finished\n")
417 (jabber-httpupload-process-ended process)))
418
419;; This is the function used to send a file to the server by running a curl subprocess.
420(defun jabber-httpupload-put-file-curl (filepath content-type put-url
421 callback callback-arg
422 &optional ignore-cert-problems)
423 "Use the curl command to put the file at FILEPATH into the PUT-URL.
424Send the SIZE and CONTENT-TYPE MIME as headers.
425IGNORE-CERT-PROBLEMS enable the use of HTTPS connections with invalid or
426non-trusted SSL/TLS certificates. If nil, curl will validate the certificate
427provided by the HTTP/S Web server.
428When the process ends, the function CALLBACK is called like the following
429call: (funcall CALLBACK CALLBACK-ARG).
430The process is registered at `jabber-httpupload-upload-processes' AList with
431the provided CALLBACK and CALLBACK-ARG."
432 (let* ((exec-path (executable-find "curl"))
433 (cmd (format "%s %s --upload-file '%s' -H \"content-type: %s\" '%s'"
434 exec-path
435 (if ignore-cert-problems
436 "--insecure"
437 "")
438 filepath content-type put-url)))
439 (when exec-path
440 (with-current-buffer (get-buffer-create "*jabber-httpupload-put-file-curl*")
441 (let ((inhibit-read-only t))
442 (goto-char (point-max))
443 (insert (format "%s Uploading to %s with curl:\n$ %s"
444 (current-time-string)
445 put-url
446 cmd))
447 (let ((process (start-process-shell-command "jabber-httpupload-put-file-curl"
448 (current-buffer)
449 cmd)))
450 (push (cons process (list callback callback-arg))
451 jabber-httpupload-upload-processes)
452 (set-process-sentinel process #'jabber-httpupload-curl-sentinel))
453 (insert "-- done --")
454 t)))))
455
456;; * Send the file URL to the client *
457
458;; The following message is similar to one sent by Conversations -
459;;
460;; <message from="from_jid@fromserver.org/Resource"
461;; id="fc824dcb-c654-4911-a22b-25718dfe4590"
462;; type="chat"
463;; to="to_jid@toserver.org">
464;; <body>https://fromserver.org:5281/upload/kFTT5ET9JeF_CC6s/_IJNy8ZUSRGiKyVxjf5FkA.jpg</body>
465;; <request xmlns="urn:xmpp:receipts"/>
466;; <markable xmlns="urn:xmpp:chat-markers:0"/>
467;; <origin-id id="fc824dcb-c654-4911-a22b-25718dfe4590" xmlns="urn:xmpp:sid:0"/>
468;; <x xmlns="jabber:x:oob">
469;; <url>https://fromserver.org:5281/upload/kFTT5ET9JeF_CC6s/_IJNy8ZUSRGiKyVxjf5FkA.jpg</url>
470;; </x>
471;; <stanza-id xmlns="urn:xmpp:sid:0"
472;; id="7e18d73a-278c-4e5e-bd09-61c12187e5d6"
473;; by="to_jid@toserver.org"/>
474;; </message>
475
476;; The message should add the "body" and "x" tags.
477
478(defun jabber-httpupload-send-file-url (jc jid get-url)
479 "Send the GET URL address to the JID user.
480The message requiers the GET-URL of the slot file, the receiver's JID
481and the JC Jabber Connection."
482 ;; This could be a possibliity, but... cannot send the x tag.
483 ;; (jabber-send-message jc jid nil get-url nil)
484 (let ((fromjid (jabber-connection-original-jid jc))
485 (type (if (assoc jid *jabber-active-groupchats*)
486 "groupchat"
487 "chat")))
488 (jabber-send-sexp jc
489 `(message ((to . ,jid)
490 (from . ,fromjid)
491 (type . ,type))
492 (body () ,get-url)
493 (x ((xmlns . "jabber:x:oob"))
494 (url () ,get-url))))))
495
496;; * Chat buffer *
497
498;; ** Send file (complete process) **
499
500;; The following functions add interactive commands to the chat buffer
501;; to send the GET URL to the current (or selected) client.
502
503(defun jabber-httpupload-send-file (jc jid filepath)
504 "Send the file at FILEPATH to the user JID.
505JC is the Jabber Connection to send the file URL."
506 (interactive (list (jabber-read-account)
507 (jabber-read-jid-completing "Send file to: " nil nil nil 'full t)
508 (read-file-name "File to send:")))
509 (unless (jabber-httpupload-server-has-support jc)
510 (error "The Jabber Connection provided has no HTTP Upload support"))
511 (let* ((size (file-attribute-size (file-attributes filepath)))
512 (content-type (mailcap-extension-to-mime (file-name-extension filepath)))
513 (filedata (list filepath size content-type)))
514 (jabber-httpupload-request-slot jc filedata
515 #'jabber-httpupload--slot-reserved
516 (list jid))))
517
518;; The following functions are callbacks used in the following order:
519
520;; 1. `jabber-httpupload-request-slot' calls `jabber-httpupload--slot-reserved'.
521;; 2. `jabber-httpupload--slot-reserved' calls `jabber-httpupload--upload-done'.
522;; 3. `jabber-httpupload--upload-done' calls `jabber-httpupload-send-file-url'.
523
524;; This form of calling is required because of the asynchronous
525;; behaviour of the upload file function.
526
527(defun jabber-httpupload--upload-done (data)
528 "Callback function used when the upload is done.
529When the upload process finished, a callback function is called with an
530argument.
531This function is expected to be used as the CALLBACK argument for the function
532`jabber-httpupload-upload-file', DATA is its CALLBACK-ARG argument.
533Also, see `jabber-httpupload-process-ended' for more information.
534DATA is expected to have the following foramt: (jc jid get-url).
535After the upload is done, send the get-url to the destined Jabber user JID."
536 (let ((jc (car data))
537 (jid (nth 1 data))
538 (get-url (nth 2 data)))
539 (condition-case err
540 (jabber-httpupload-send-file-url jc jid get-url)
541 (error "Cannot send message. Error: %S" err))))
542
543;; When the slot is reserved, the HTTP upload should be started.
544(defun jabber-httpupload--slot-reserved (jc _xml-data filedata urls extra-data)
545 "Callback function used when the slot request succeeded.
546JC is the current Jabber Connection.
547XML-DATA is the received XML from the server.
548FILEDATA is a triple `(filepath size content-type).
549URLS is a tuple `(put-url get-url).
550EXTRA-DATA is a list `(jid)"
551 (let ((filepath (car filedata))
552 (content-type (nth 2 filedata))
553 (jid (car extra-data))
554 (get-url (cadr urls))
555 (put-url (car urls)))
556 (message "jabber-httpupload: slot PUT and GET URLs: %S" urls)
557 (condition-case err
558 (jabber-httpupload-upload-file (expand-file-name filepath)
559 content-type
560 put-url
561 #'jabber-httpupload--upload-done (list jc jid get-url)
562 (jabber-httpupload-ignore-certificate jc))
563 (error "Cannot upload the file. Error: %S" err))))
564
565;; ** TODO Recording and sending audio **
566
567;; TODO
568(defun jabber-httpupload--record-audio ()
569 "Create a new audio record and save the file into a temporal directory."
570 (let ((process (start-process-shell-command
571 "jabber-httpupload-record-audio"
572 (current-buffer)
573 (replace-string "$(filename"
574 "/tmp/jabber-httpupload-record"
575 jabber-httpupload-record-command))))
576 (set-process-sentinel process #'jabber-httpupload-record-sentinel)))
577
578;; * Add hooks *
579;; Some function should start automatically.
580
581;; ** Test connection support after session is established **
582;; Call `jabber-httpupload-test-connection-support' as soon as
583
584;; * Adding functions to hooks *
585;; ** Test HTTP Upload support after connecting **
586(add-hook 'jabber-post-connect-hooks #'jabber-httpupload-test-connection-support)
587
588(provide 'jabber-httpupload)
589
590;;; jabber-httpupload.el ends here