Fix bytevector->hex-string.
[guile-toxcore.git] / tox.scm
CommitLineData
da3f0655
DT
1;;; guile-toxcore
2;;; Copyright © 2014 David Thompson <davet@gnu.org>
3;;;
4;;; guile-toxcore is free software: you can redistribute it and/or modify it
5;;; under the terms of the GNU General Public License as published by the Free
6;;; Software Foundation, either version 3 of the License, or (at your option)
7;;; any later version.
8;;;
9;;; guile-toxcore is distributed in the hope that it will be useful, but
10;;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11;;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12;;; for more details.
13;;;
14;;; You should have received a copy of the GNU General Public License
15;;; along with this program. If not, see
16;;; <http://www.gnu.org/licenses/>.
17
18;;; Commentary:
19;;
20;; Tox API.
21;;
22;;; Code:
23
24(define-module (tox)
25 #:use-module (ice-9 format)
e46ffa57
DT
26 #:use-module (rnrs bytevectors)
27 #:use-module (srfi srfi-4)
4fd25bd6 28 #:use-module (srfi srfi-9)
da3f0655
DT
29 #:use-module (system foreign)
30 #:use-module ((tox bindings) #:prefix %)
31 #:use-module (tox util)
c44605bc
DT
32 #:export (tox-friend-add-error
33 tox-user-status
a55f7a2e
DT
34 tox-max-name-length tox-max-message-length
35 tox-max-status-message-length
ac7c56dd
DT
36 tox-client-id-size tox-friend-address-size
37 tox-client-id tox-friend-address
636a8e00 38 make-tox tox-kill with-tox
4fd25bd6
DT
39 tox-friend-request-hook tox-message-hook tox-action-hook
40 tox-name-change-hook tox-status-message-hook tox-status-hook
41 tox-typing-hook tox-read-receipt-hook tox-connection-status-hook
40bfbc18 42 tox? tox-connected?
e46ffa57 43 tox-do-interval tox-do
dca6df72 44 tox-size tox-save tox-load! tox-load
734c9b64
DT
45 tox-bootstrap-from-address
46 tox-address
c321b0ec 47 tox-add-friend tox-add-friend-no-request tox-delete-friend
85a2632c 48 tox-friend-number tox-friend-client-id
80af0489 49 tox-friend-connected? tox-friend-exists?
e51538cd 50 tox-send-message tox-send-action
ea95965c 51 set-tox-name tox-name tox-friend-name
ec90a1c3 52 set-tox-status set-tox-status-message
38c3ec34 53 tox-status-message tox-friend-status-message
4634ddc4 54 tox-status tox-friend-status
558bdf01 55 tox-friend-last-online
3642d458 56 set-tox-friend-typing tox-friend-typing?
294adc50 57 set-tox-send-receipts
700b5802
DT
58 tox-friend-count tox-online-friend-count
59 tox-friend-list))
f6de92c3 60
c44605bc
DT
61(define-enumeration tox-friend-add-error
62 (too-long -1)
63 (no-message -2)
64 (own-key -3)
65 (already-sent -4)
66 (unknown -5)
67 (bad-checksum -6)
68 (set-new-no-spam -7)
69 (no-mem -8))
70
71(define-enumeration tox-user-status
72 (none 0)
73 (away 1)
74 (busy 2)
75 (invalid 3))
76
a55f7a2e
DT
77(define tox-max-name-length 128)
78(define tox-max-message-length 1368)
79(define tox-max-status-message-length 1007)
f6de92c3 80(define tox-client-id-size 32)
f6de92c3
DT
81(define tox-friend-address-size
82 (+ tox-client-id-size (sizeof uint32) (sizeof uint16)))
da3f0655 83
ac7c56dd
DT
84(define (tox-client-id id)
85 "Return a newly allocated bytevector of length tox-client-id-size by
86transcoding the hexadecimal string ID."
87 (if (= (string-length id) (* tox-client-id-size 2))
88 (hex-string->bytevector id)
89 (error "Invalid Tox client ID: " id)))
90
91(define (tox-friend-address address)
92 "Return a newly allocated bytevector of length tox-friend-address-size by
93transcoding the hexadecimal string ADDRESS."
94 (if (= (string-length address) (* tox-friend-address-size 2))
95 (hex-string->bytevector address)
96 (error "Invalid Tox friend address: " address)))
97
4fd25bd6
DT
98(define-record-type <tox>
99 (%make-tox pointer friend-request-hook message-hook action-hook
100 name-change-hook status-message-hook status-hook
101 typing-hook read-receipt-hook connection-status-hook)
102 tox?
103 (pointer tox-pointer)
104 (friend-request-hook tox-friend-request-hook)
105 (message-hook tox-message-hook)
106 (action-hook tox-action-hook)
107 (name-change-hook tox-name-change-hook)
108 (status-message-hook tox-status-message-hook)
109 (status-hook tox-status-hook)
110 (typing-hook tox-typing-hook)
111 (read-receipt-hook tox-read-receipt-hook)
112 (connection-status-hook tox-connection-status-hook))
113
114(define (wrap-tox pointer)
115 (let ((tox (%make-tox pointer
3df5be59 116 (make-hook 3)
4fd25bd6
DT
117 (make-hook 3)
118 (make-hook)
119 (make-hook)
120 (make-hook)
121 (make-hook)
122 (make-hook)
123 (make-hook)
124 (make-hook))))
125 ;; Register callbacks to run hooks.
3df5be59
DT
126 (%tox-callback-friend-request
127 pointer
128 (procedure->pointer
129 void
130 (lambda (ptr public-key message length user-data)
131 (run-hook (tox-friend-request-hook tox)
132 tox
133 (pointer->bytevector public-key tox-client-id-size)
134 (utf8-pointer->string message length)))
135 (list '* '* '* uint16 '*))
136 %null-pointer)
137
4fd25bd6
DT
138 (%tox-callback-friend-message
139 pointer
140 (procedure->pointer
141 void
142 (lambda (ptr friend-number message length user-data)
143 (run-hook (tox-message-hook tox)
144 tox
145 friend-number
b4769a82 146 (utf8-pointer->string message length)))
4fd25bd6
DT
147 (list '* int32 '* uint16 '*))
148 %null-pointer)
3df5be59 149
4fd25bd6
DT
150 tox))
151
152(define (unwrap-tox tox)
153 (tox-pointer tox))
da3f0655 154
0f5297b9
DT
155(define-syntax-rule (define/unwrap name docstring proc)
156 (define (name tox)
157 docstring
158 (proc (unwrap-tox tox))))
159
c6d3e310 160(define* (make-tox #:optional (ipv6-enabled? #t))
da3f0655
DT
161 "Return a newly allocated Tox messenger. IPV6-ENABLED? indicates whether to
162create a IPv4 or IPv6 socket. By default, an IPv6 socket is created."
163 (let ((ptr (%tox-new (boolean->number ipv6-enabled?))))
164 (if (null-pointer? ptr)
da081d76 165 (error "Failed to create Tox messenger")
da3f0655
DT
166 (wrap-tox ptr))))
167
0f5297b9 168(define/unwrap tox-kill
da3f0655 169 "Free all memory associated with the messenger TOX."
0f5297b9
DT
170 %tox-kill)
171
636a8e00
DT
172(define-syntax-rule (with-tox tox body ...)
173 "Evaluate BODY ... and ensure that memory for the messenger TOX is properly
174freed when with-tox returns, be it normally or because of an exception."
175 (dynamic-wind
176 (lambda () #t)
177 (lambda ()
178 (let ((results (call-with-values (lambda () body ...) list)))
179 (tox-kill tox)
180 (apply values results)))
181 (lambda ()
182 (tox-kill tox))))
183
0f5297b9
DT
184(define/unwrap tox-do-interval
185 "Return the time in milliseconds before tox-do should be called
186again for optimal performance."
187 %tox-do-interval)
188
189(define/unwrap tox-do
190 "The main loop that needs to be run in intervals of tox-do-interval
191milliseconds."
192 %tox-do)
e46ffa57
DT
193
194(define/unwrap tox-size
195 "Return the size of the Tox messenger data in bytes. Useful for
196saving state."
197 %tox-size)
198
199(define (tox-save tox)
200 "Return a uint8 bytevector containing the state of the messenger
201TOX."
202 (let ((bv (make-u8vector (tox-size tox))))
203 (%tox-save (unwrap-tox tox) (bytevector->pointer bv))
204 bv))
205
8f278448 206(define (tox-load tox state)
e46ffa57
DT
207 "Load the saved data in the bytevector STATE into the messenger
208TOX."
209 (or (zero?
210 (%tox-load (unwrap-tox tox)
211 (bytevector->pointer state)
212 (bytevector-length state)))
213 (error "Failed to load Tox state: " tox)))
6527e449 214
40bfbc18
DT
215(define (tox-connected? tox)
216 "Return #t if the messenger TOX is connected to the DHT, #f
217otherwise."
218 (one? (%tox-isconnected (unwrap-tox tox))))
dca6df72 219
dca6df72
DT
220(define (tox-bootstrap-from-address tox address ipv6-enabled? port public-key)
221 "Resolve ADDRESS into an IP address. If successful, send a 'get
222nodes' request to the given node with IP, PORT, and PUBLIC-KEY to
223setup connections.
224
225ADDRESS can be a hostname or an IP address (IPv4 or IPv6). If
226IPV6-ENABLED? is #f, the resolving sticks strictly to IPv4 addresses.
227If IPV6-ENABLED? is #t, the resolving procedure looks for IPv6
228addresses first, then IPv4 addresses. PUBLIC-KEY is a 32 byte long
229bytevector.
230
231Return #t if ADDRESS could be converted into an IP address, #f
232otherwise."
233 (one? (%tox-bootstrap-from-address
234 (unwrap-tox tox)
235 (string->pointer address)
236 (boolean->number ipv6-enabled?)
237 (htons port)
238 (bytevector->pointer public-key))))
f6de92c3
DT
239
240(define (tox-address tox)
1c158add
DT
241 "Return bytevector containing the friend address for the messenger
242TOX."
f6de92c3
DT
243 (let ((bv (make-bytevector tox-friend-address-size)))
244 (%tox-get-address (unwrap-tox tox) (bytevector->pointer bv))
245 bv))
734c9b64
DT
246
247(define (tox-add-friend tox address message)
248 "Add a friend identified by the bytevector ADDRESS to the messenger TOX.
e26ec140
DT
249Additionally, send a friend request containing the string MESSAGE. ADDRESS
250must be tox-friend-address-size bytes long. Return a friend number on
734c9b64
DT
251success, otherwise return a negative number that corresponds to an error code
252enumerated in tox-friend-add-error."
e26ec140
DT
253 (let ((m (string->utf8 message)))
254 (%tox-add-friend (unwrap-tox tox)
255 (bytevector->pointer address)
256 (bytevector->pointer m)
257 (bytevector-length m))))
12b191ba
DT
258
259(define (tox-add-friend-no-request tox client-id)
260 "Add a friend identified by the bytevector CLIENT-ID to the messenger TOX
3df5be59
DT
261without sending a friend request. Return the friend number if successful, or
262#f otherwise."
abe7dead
DT
263 (false-if-negative
264 (%tox-add-friend-norequest (unwrap-tox tox)
265 (bytevector->pointer client-id))))
266
c321b0ec
DT
267(define (tox-delete-friend tox friend-number)
268 "Remove the friend identified by FRIEND-NUMBER from the messenger TOX.
269Return #t if successful, #f otherwise."
270 (zero? (%tox-del-friend (unwrap-tox tox) friend-number)))
271
abe7dead
DT
272(define (tox-friend-number tox client-id)
273 "Return the friend number associated with the bytevector CLIENT-ID in the
274messenger TOX, or #f if no such friend exists."
275 (false-if-negative
276 (%tox-get-friend-number (unwrap-tox tox)
277 (bytevector->pointer client-id))))
5749a57f
DT
278
279(define (tox-friend-client-id tox friend-number)
280 "Return a bytevector containing the public key associated with FRIEND-NUMBER
281in the messenger TOX, or #f if no such friend exists."
282 (let* ((bv (make-bytevector tox-client-id-size))
283 (result (%tox-get-client-id (unwrap-tox tox)
284 friend-number
285 (bytevector->pointer bv))))
286 (if (negative? result) #f bv)))
85a2632c
DT
287
288(define (tox-friend-connected? tox friend-number)
289 "Return #t if friend identified by FRIEND-NUMBER is online, #f otherwise."
8122d19e 290 (one? (%tox-get-friend-connection-status (unwrap-tox tox) friend-number)))
0d2a3103
DT
291
292(define (tox-friend-exists? tox friend-number)
293 "Return #t if friend identified by FRIEND-NUMBER exists, #f otherwise."
294 (one? (%tox-friend-exists (unwrap-tox tox) friend-number)))
80af0489 295
e14739ad
DT
296(define (tox-send tox send send-with-id friend-number message id)
297 (let* ((tox (unwrap-tox tox))
298 (message (string->utf8 message))
299 (ptr (bytevector->pointer message))
300 (length (bytevector-length message)))
301 (false-if-zero
302 (if id
303 (send-with-id tox friend-number id ptr length)
304 (send tox friend-number ptr length)))))
305
80af0489
DT
306(define* (tox-send-message tox friend-number message #:optional (id #f))
307 "Send the string MESSAGE to the friend identified by FRIEND-NUMBER in the
308messenger TOX. Optionally, a message ID may be given. If omitted, an id is
309automatically generated. MESSAGE length may not exceed
310tox-max-message-length.
311
312Return the message id on success, #f otherwise."
e14739ad
DT
313 (tox-send tox
314 %tox-send-message
315 %tox-send-message-withid
316 friend-number
317 message
318 id))
319
320(define* (tox-send-action tox friend-number action #:optional (id #f))
321 "Send the string ACTION to the friend identified by FRIEND-NUMBER in the
322messenger TOX. Optionally, a message ID may be given. If omitted, an id is
323automatically generated. MESSAGE length may not exceed
324tox-max-message-length.
325
326Return the message id on success, #f otherwise."
327 (tox-send tox
328 %tox-send-action
329 %tox-send-action-withid
330 friend-number
331 action
332 id))
e51538cd
DT
333
334(define (set-tox-name tox name)
335 "Use the nickname NAME for the messenger TOX."
1f500805 336 (let ((n (string->utf8 name)))
e51538cd 337 (if (zero? (%tox-set-name (unwrap-tox tox)
1f500805
DT
338 (bytevector->pointer n)
339 (bytevector-length n)))
e51538cd
DT
340 *unspecified*
341 (error "Invalid nickname: " name))))
342
343(define (tox-name tox)
344 "Return the nickname for the messenger TOX."
345 (let* ((name (make-bytevector tox-max-name-length))
346 (length (%tox-get-self-name (unwrap-tox tox)
347 (bytevector->pointer name))))
348 (if (positive? length)
349 (utf8->string (bytevector-slice name 0 length))
350 (error "Failed to get nickname"))))
0d4f7ec2
DT
351
352(define (tox-friend-name tox friend-number)
353 "Return the nickname of the friend identified by FRIEND-NUMBER for the
354messenger TOX."
355 (let* ((name (make-bytevector tox-max-name-length))
356 (length (%tox-get-name (unwrap-tox tox)
357 friend-number
358 (bytevector->pointer name))))
359 (if (positive? length)
360 (utf8->string (bytevector-slice name 0 length))
361 (error "Failed to get nickname for friend number: " friend-number))))
ea95965c 362
ec90a1c3
DT
363(define (set-tox-status tox status)
364 "Set the user status for the messenger TOX to STATUS."
365 (when (negative? (%tox-set-user-status (unwrap-tox tox) status))
366 (error "Invalid user status: " status)))
367
368(define (set-tox-status-message tox message)
369 "Set the status message for the messenger TOX to the string MESSAGE."
370 (let ((m (string->utf8 message)))
371 (when (negative?
372 (%tox-set-status-message (unwrap-tox tox)
373 (bytevector->pointer m)
374 (bytevector-length m)))
375 (error "Invalid status message: " message))))
2b95cdf3
DT
376
377(define (tox-status-message tox)
378 "Return the status message for the messenger TOX."
379 (let* ((message (make-bytevector tox-max-status-message-length))
380 (length (%tox-get-self-status-message (unwrap-tox tox)
381 (bytevector->pointer message)
382 tox-max-status-message-length)))
383 (if (positive? length)
384 (utf8->string (bytevector-slice message 0 length))
385 (error "Failed to get status message"))))
386
387(define (tox-friend-status-message tox friend-number)
388 "Return the status message for the friend identified by FRIEND-NUMBER i the
389messenger TOX."
390 (let* ((message (make-bytevector tox-max-status-message-length))
391 (length (%tox-get-status-message (unwrap-tox tox)
392 friend-number
393 (bytevector->pointer message)
394 tox-max-status-message-length)))
395 (if (positive? length)
396 (utf8->string (bytevector-slice message 0 length))
397 (error "Failed to get status message for friend number: "
398 friend-number))))
38c3ec34
DT
399
400(define/unwrap tox-status
401 "Return the user status code for the messenger TOX."
402 %tox-get-self-user-status)
403
404(define (tox-friend-status tox friend-number)
405 "Return the user status code for the friend identified by FRIEND-NUMBER in
406the messenger TOX."
407 (%tox-get-user-status (unwrap-tox tox) friend-number))
4634ddc4
DT
408
409(define (tox-friend-last-online tox friend-number)
410 "Return the timestamp of the last time the friend identified by
411FRIEND-NUMBER was seen online, or 0 if never seen."
412 (let ((result (%tox-get-last-online (unwrap-tox tox) friend-number)))
413 (if (negative? result)
414 (error "Invalid friend number: " friend-number)
415 result)))
558bdf01
DT
416
417(define (set-tox-friend-typing tox friend-number typing?)
418 "Set the typing flag for the friend identified by FRIEND-NUMBER in the
419messenger TOX."
420 (if (zero? (%tox-set-user-is-typing (unwrap-tox tox) friend-number typing?))
421 *unspecified*
422 (error "Invalid friend number: " friend-number)))
423
424(define (tox-friend-typing? tox friend-number)
425 "Return #t if the friend identified by FRIEND-NUMBER in the messenger TOX is
426typing, or #f otherwise."
427 (one? (%tox-get-is-typing (unwrap-tox tox) friend-number)))
3642d458
DT
428
429(define (set-tox-send-receipts tox friend-number send-receipts?)
430 "Set whether to send receipts to the friend identified by FRIEND-NUMBER in
431the messenger TOX. SEND-RECEIPTS? should be either #t of #f."
432 (%tox-set-sends-receipts (unwrap-tox tox)
433 friend-number
434 (boolean->number send-receipts?)))
294adc50
DT
435
436(define/unwrap tox-friend-count
437 "Return the number of friends in the friend list for the messenger TOX."
438 %tox-count-friendlist)
444fd427
DT
439
440(define/unwrap tox-online-friend-count
441 "Return the number of online friends in the friend list for the messenger
442TOX."
443 %tox-get-num-online-friends)
700b5802
DT
444
445(define (tox-friend-list tox)
446 "Return a list of all friend numbers for the messenger TOX."
447 (let* ((length (tox-friend-count tox))
448 (bv (make-s32vector length)))
449 (%tox-get-friendlist (unwrap-tox tox)
450 (bytevector->pointer bv)
451 length)
452 (bytevector->sint-list bv (native-endianness) (sizeof int32))))