tratox.c - ratox - FIFO based tox client
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
tratox.c (61256B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <sys/select.h>
3 #include <sys/stat.h>
4 #include <sys/types.h>
5
6 #include <ctype.h>
7 #include <dirent.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <limits.h>
11 #include <signal.h>
12 #include <stdarg.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <time.h>
18 #include <unistd.h>
19
20 #include <tox/tox.h>
21 #include <tox/toxav.h>
22 #include <tox/toxencryptsave.h>
23
24 #include "arg.h"
25 #include "queue.h"
26 #include "readpassphrase.h"
27 #include "util.h"
28
29 const char *reqerr[] = {
30 [TOX_ERR_FRIEND_ADD_NULL] = "One required argument is missing",
31 [TOX_ERR_FRIEND_ADD_TOO_LONG] = "Message is too long",
32 [TOX_ERR_FRIEND_ADD_NO_MESSAGE] = "Please add a message to your request",
33 [TOX_ERR_FRIEND_ADD_OWN_KEY] = "That appears to be your own ID",
34 [TOX_ERR_FRIEND_ADD_ALREADY_SENT] = "Friend request already sent",
35 [TOX_ERR_FRIEND_ADD_BAD_CHECKSUM] = "Bad checksum while verifying address",
36 [TOX_ERR_FRIEND_ADD_SET_NEW_NOSPAM] = "Friend already added but invalid nospam",
37 [TOX_ERR_FRIEND_ADD_MALLOC] = "Error increasing the friend list size"
38 };
39
40 const char *callerr[] = {
41 [TOXAV_ERR_SEND_FRAME_NULL] = "Samples pointer is NULL",
42 [TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND] = "No friend matching this ID",
43 [TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL] = "Currently not in a call",
44 [TOXAV_ERR_SEND_FRAME_SYNC] = "Synchronization error occurred",
45 [TOXAV_ERR_SEND_FRAME_INVALID] = "One of the frame parameters was invalid",
46 [TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED] = "Either friend turned off audio receiving or we turned off sending for the said payload.",
47 [TOXAV_ERR_SEND_FRAME_RTP_FAILED] = "Failed to push frame through rtp interface"
48 };
49
50 struct node {
51 char *addr4;
52 char *addr6;
53 uint16_t udp_port;
54 uint16_t tcp_port;
55 char *idstr;
56 };
57
58 #include "config.h"
59
60 struct file {
61 int type;
62 const char *name;
63 int flags;
64 };
65
66 enum { NONE, FIFO, STATIC };
67 enum { IN, OUT, ERR };
68
69 static struct file gfiles[] = {
70 [IN] = { .type = FIFO, .name = "in", .flags = O_RDONLY | O_NONBLOCK },
71 [OUT] = { .type = NONE, .name = "out", .flags = O_WRONLY | O_TRUNC | O_CREAT },
72 [ERR] = { .type = STATIC, .name = "err", .flags = O_WRONLY | O_TRUNC | O_CREAT },
73 };
74
75 static int idfd = -1;
76
77 struct slot {
78 const char *name;
79 void (*cb)(void *);
80 int outisfolder;
81 int dirfd;
82 int fd[LEN(gfiles)];
83 };
84
85 static void setname(void *);
86 static void setstatus(void *);
87 static void setuserstate(void *);
88 static void sendfriendreq(void *);
89 static void setnospam(void *);
90 static void newconf(void *);
91
92 enum { NAME, STATUS, STATE, REQUEST, NOSPAM, CONF };
93
94 static struct slot gslots[] = {
95 [NAME] = { .name = "name", .cb = setname, .outisfolder = 0, .dirfd = -1, .fd = {-1, -1, -1} },
96 [STATUS] = { .name = "status", .cb = setstatus, .outisfolder = 0, .dirfd = -1, .fd = {-1, -1, -1} },
97 [STATE] = { .name = "state", .cb = setuserstate, .outisfolder = 0, .dirfd = -1, .fd = {-1, -1, -1} },
98 [REQUEST] = { .name = "request", .cb = sendfriendreq, .outisfolder = 1, .dirfd = -1, .fd = {-1, -1, -1} },
99 [NOSPAM] = { .name = "nospam", .cb = setnospam, .outisfolder = 0, .dirfd = -1, .fd = {-1, -1, -1} },
100 [CONF] = { .name = "conf", .cb = newconf, .outisfolder = 1, .dirfd = -1, .fd = {-1, -1, -1} },
101 };
102
103 enum { FTEXT_IN, FFILE_IN, FCALL_IN, FTEXT_OUT, FFILE_OUT, FCALL_OUT,
104 FREMOVE, FONLINE, FNAME, FSTATUS, FSTATE, FFILE_STATE, FCALL_STATE };
105
106 static struct file ffiles[] = {
107 [FTEXT_IN] = { .type = FIFO, .name = "text_in", .flags = O_RDONLY | O_NONBLOCK },
108 [FFILE_IN] = { .type = FIFO, .name = "file_in", .flags = O_RDONLY | O_NONBLOCK },
109 [FCALL_IN] = { .type = FIFO, .name = "call_in", .flags = O_RDONLY | O_NONBLOCK },
110 [FTEXT_OUT] = { .type = STATIC, .name = "text_out", .flags = O_WRONLY | O_APPEND | O_CREAT },
111 [FFILE_OUT] = { .type = FIFO, .name = "file_out", .flags = O_WRONLY | O_NONBLOCK },
112 [FCALL_OUT] = { .type = FIFO, .name = "call_out", .flags = O_WRONLY | O_NONBLOCK },
113 [FREMOVE] = { .type = FIFO, .name = "remove", .flags = O_RDONLY | O_NONBLOCK },
114 [FONLINE] = { .type = STATIC, .name = "online", .flags = O_WRONLY | O_TRUNC | O_CREAT },
115 [FNAME] = { .type = STATIC, .name = "name", .flags = O_WRONLY | O_TRUNC | O_CREAT },
116 [FSTATUS] = { .type = STATIC, .name = "status", .flags = O_WRONLY | O_TRUNC | O_CREAT },
117 [FSTATE] = { .type = STATIC, .name = "state", .flags = O_WRONLY | O_TRUNC | O_CREAT },
118 [FFILE_STATE] = { .type = STATIC, .name = "file_pending", .flags = O_WRONLY | O_TRUNC | O_CREAT },
119 [FCALL_STATE] = { .type = STATIC, .name = "call_state", .flags = O_WRONLY | O_TRUNC | O_CREAT },
120 };
121
122 enum { CMEMBERS, CINVITE, CLEAVE, CTITLE_IN, CTITLE_OUT, CTEXT_IN, CTEXT_OUT };
123
124 static struct file cfiles[] = {
125 [CMEMBERS] = { .type = STATIC, .name = "members", .flags = O_WRONLY | O_TRUNC | O_CREAT },
126 [CINVITE] = { .type = FIFO, .name = "invite", .flags = O_RDONLY | O_NONBLOCK },
127 [CLEAVE] = { .type = FIFO, .name = "leave", .flags = O_RDONLY | O_NONBLOCK },
128 [CTITLE_IN] = { .type = FIFO, .name = "title_in", .flags = O_RDONLY | O_NONBLOCK },
129 [CTITLE_OUT] = { .type = STATIC, .name = "title_out", .flags = O_WRONLY | O_TRUNC | O_CREAT },
130 [CTEXT_IN] = { .type = FIFO, .name = "text_in", .flags = O_RDONLY | O_NONBLOCK },
131 [CTEXT_OUT] = { .type = STATIC, .name = "text_out", .flags = O_WRONLY | O_APPEND | O_CREAT },
132 };
133
134 static char *ustate[] = {
135 [TOX_USER_STATUS_NONE] = "available",
136 [TOX_USER_STATUS_AWAY] = "away",
137 [TOX_USER_STATUS_BUSY] = "busy"
138 };
139
140 enum { TRANSFER_NONE, TRANSFER_INITIATED, TRANSFER_PENDING, TRANSFER_INPROGRESS, TRANSFER_PAUSED };
141
142 struct transfer {
143 uint32_t fnum;
144 uint8_t *buf;
145 ssize_t n;
146 int pendingbuf;
147 int state;
148 };
149
150 enum {
151 OUTGOING = 1 << 0,
152 INCOMING = 1 << 1,
153 TRANSMITTING = 1 << 2,
154 INCOMPLETE = 1 << 3,
155 RINGING = 1 << 4,
156 };
157
158 struct call {
159 int state;
160 uint8_t *frame;
161 ssize_t n;
162 struct timespec lastsent;
163 };
164
165 struct friend {
166 char name[TOX_MAX_NAME_LENGTH + 1];
167 uint32_t num;
168 uint8_t id[TOX_PUBLIC_KEY_SIZE];
169 char idstr[2 * TOX_PUBLIC_KEY_SIZE + 1];
170 int dirfd;
171 int fd[LEN(ffiles)];
172 struct transfer tx;
173 int rxstate;
174 struct call av;
175 TAILQ_ENTRY(friend) entry;
176 };
177
178 struct conference {
179 uint32_t num;
180 char numstr[2 * sizeof(uint32_t) + 1];
181 int dirfd;
182 int fd[LEN(cfiles)];
183 TAILQ_ENTRY(conference) entry;
184 };
185
186 struct request {
187 uint8_t id[TOX_PUBLIC_KEY_SIZE];
188 char idstr[2 * TOX_PUBLIC_KEY_SIZE + 1];
189 char *msg;
190 int fd;
191 TAILQ_ENTRY(request) entry;
192 };
193
194 struct invite {
195 char *fifoname;
196 uint8_t *cookie;
197 size_t cookielen;
198 uint32_t inviter;
199 int fd;
200 TAILQ_ENTRY(invite) entry;
201 };
202
203 static TAILQ_HEAD(friendhead, friend) friendhead = TAILQ_HEAD_INITIALIZER(friendhead);
204 static TAILQ_HEAD(confhead, conference) confhead = TAILQ_HEAD_INITIALIZER(confhead);
205 static TAILQ_HEAD(reqhead, request) reqhead = TAILQ_HEAD_INITIALIZER(reqhead);
206 static TAILQ_HEAD(invhead, invite) invhead = TAILQ_HEAD_INITIALIZER(invhead);
207
208 static Tox *tox;
209 static ToxAV *toxav;
210
211 static int framesize;
212
213 static uint8_t *passphrase;
214 static uint32_t pplen;
215
216 static volatile sig_atomic_t running = 1;
217
218 static struct timespec timediff(struct timespec, struct timespec);
219 static void printrat(void);
220 static void logmsg(const char *, ...);
221 static int fifoopen(int, struct file);
222 static void fiforeset(int, int *, struct file);
223 static ssize_t fiforead(int, int *, struct file, void *, size_t);
224 static uint32_t interval(Tox *, struct ToxAV*);
225
226 static void cbcallinvite(ToxAV *, uint32_t, bool, bool, void *);
227 static void cbcallstate(ToxAV *, uint32_t, uint32_t, void *);
228 static void cbcalldata(ToxAV *, uint32_t, const int16_t *, size_t, uint8_t, uint32_t, void *);
229
230 static void cancelcall(struct friend *, char *);
231 static void sendfriendcalldata(struct friend *);
232 static void writemembers(struct conference *);
233
234 static void cbconnstatus(Tox *, uint32_t, TOX_CONNECTION, void *);
235 static void cbfriendmessage(Tox *, uint32_t, TOX_MESSAGE_TYPE, const uint8_t *, size_t, void *);
236 static void cbfriendrequest(Tox *, const uint8_t *, const uint8_t *, size_t, void *);
237 static void cbnamechange(Tox *, uint32_t, const uint8_t *, size_t, void *);
238 static void cbstatusmessage(Tox *, uint32_t, const uint8_t *, size_t, void *);
239 static void cbfriendstate(Tox *, uint32_t, TOX_USER_STATUS, void *);
240 static void cbfilecontrol(Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL, void *);
241 static void cbfilesendreq(Tox *, uint32_t, uint32_t, uint32_t, uint64_t, const uint8_t *, size_t, void *);
242 static void cbfiledata(Tox *, uint32_t, uint32_t, uint64_t, const uint8_t *, size_t, void *);
243
244 static void cbconfinvite(Tox *, uint32_t, TOX_CONFERENCE_TYPE, const uint8_t *, size_t, void *);
245 static void cbconfmessage(Tox *, uint32_t, uint32_t, TOX_MESSAGE_TYPE, const uint8_t *, size_t, void *);
246 static void cbconftitle(Tox *, uint32_t, uint32_t, const uint8_t *, size_t, void *);
247 static void cbconfmembers(Tox *, uint32_t, void *);
248
249 static void canceltxtransfer(struct friend *);
250 static void cancelrxtransfer(struct friend *);
251 static void sendfriendtext(struct friend *);
252 static void removefriend(struct friend *);
253 static void invitefriend(struct conference *);
254 static void sendconftext(struct conference *);
255 static void updatetitle(struct conference *);
256 static int readpass(const char *, uint8_t **, uint32_t *);
257 static void dataload(struct Tox_Options *);
258 static void datasave(void);
259 static int localinit(void);
260 static int toxinit(void);
261 static int toxconnect(void);
262 static void id2str(uint8_t *, char *);
263 static void str2id(char *, uint8_t *);
264 static void friendcreate(uint32_t);
265 static void confcreate(uint32_t);
266 static void friendload(void);
267 static void frienddestroy(struct friend *);
268 static void confdestroy(struct conference *);
269 static void loop(void);
270 static void initshutdown(int);
271 static void toxshutdown(void);
272 static void usage(void);
273
274 #define FD_APPEND(fd) do { \
275 FD_SET((fd), &rfds); \
276 if ((fd) > fdmax) \
277 fdmax = (fd); \
278 } while (0)
279
280 #undef MIN
281 #define MIN(x, y) ((x) < (y) ? (x) : (y))
282
283 static struct timespec
284 timediff(struct timespec t1, struct timespec t2)
285 {
286 struct timespec tmp;
287
288 tmp.tv_sec = t2.tv_sec - t1.tv_sec;
289
290 if ((t2.tv_nsec - t1.tv_nsec) > 0) {
291 tmp.tv_nsec = (t2.tv_nsec - t1.tv_nsec);
292 } else {
293 tmp.tv_nsec = 1E9 - (t1.tv_nsec - t2.tv_nsec);
294 tmp.tv_sec--;
295 }
296
297 return tmp;
298 }
299
300 static void
301 printrat(void)
302 {
303 printf( "\033[31m"
304 " /y\\ /y\\\n"
305 " /ver\\ /"VERSION"\\\n"
306 " yyyyyy\\ /yyyyyy\n"
307 " \\yyyyyyyyyyyyyyyyyy/\n"
308 " yyyyyyyyyyyyyyyyyy\n"
309 " yyyyyyyyyyyyyyyyyy\n"
310 " yyy'yyyyyyyyyy'yyy\n"
311 " \\yy yyyyyyyy yy/\n"
312 " \\yy.yyyyyyyy.yy/\n"
313 " \\yyyyyyyyyyyy/\n"
314 " \\yyyyyyyy/\n"
315 " -------yyyyyyyy-------\n"
316 " ..---yyyyyy---..\n"
317 " ..--yyyy--..\n"
318 "\033[0m\n");
319 }
320
321 static void
322 logmsg(const char *fmt, ...)
323 {
324 time_t t;
325 va_list ap;
326 char buft[64];
327
328 va_start(ap, fmt);
329 t = time(NULL);
330 strftime(buft, sizeof(buft), "%F %R", localtime(&t));
331 printf("%s ", buft);
332 vfprintf(stdout, fmt, ap);
333 va_end(ap);
334 }
335
336 static int
337 fifoopen(int dirfd, struct file f)
338 {
339 int fd;
340
341 fd = openat(dirfd, f.name, f.flags, 0666);
342 if (fd < 0 && errno != ENXIO)
343 eprintf("openat %s:", f.name);
344 return fd;
345 }
346
347 static void
348 fiforeset(int dirfd, int *fd, struct file f)
349 {
350 ssize_t r;
351
352 r = unlinkat(dirfd, f.name, 0);
353 if (r < 0 && errno != ENOENT)
354 eprintf("unlinkat %s:", f.name);
355 if (*fd != -1)
356 close(*fd);
357 r = mkfifoat(dirfd, f.name, 0666);
358 if (r < 0 && errno != EEXIST)
359 eprintf("mkfifoat %s:", f.name);
360 *fd = fifoopen(dirfd, f);
361 }
362
363 static ssize_t
364 fiforead(int dirfd, int *fd, struct file f, void *buf, size_t sz)
365 {
366 ssize_t r;
367
368 again:
369 r = read(*fd, buf, sz);
370 if (r == 0) {
371 fiforeset(dirfd, fd, f);
372 return 0;
373 } else if (r < 0) {
374 if (errno == EINTR)
375 goto again;
376 if (errno == EWOULDBLOCK)
377 return -1;
378 eprintf("read %s:", f.name);
379 }
380 return r;
381 }
382
383 static uint32_t
384 interval(Tox *m, struct ToxAV *av)
385 {
386 return MIN(tox_iteration_interval(m), toxav_iteration_interval(av));
387 }
388
389 static void
390 cbcallinvite(ToxAV *av, uint32_t fnum, bool audio, bool video, void *udata)
391 {
392 struct friend *f;
393
394 TAILQ_FOREACH(f, &friendhead, entry)
395 if (f->num == fnum)
396 break;
397 if (!f)
398 return;
399
400 if (!audio) {
401 if (!toxav_call_control(toxav, f->num, TOXAV_CALL_CONTROL_CANCEL, NULL))
402 weprintf("Failed to reject call\n");
403 logmsg(": %s : Audio > Rejected (no audio)\n", f->name);
404 return;
405 }
406
407 f->av.state |= RINGING;
408 ftruncate(f->fd[FCALL_STATE], 0);
409 lseek(f->fd[FCALL_STATE], 0, SEEK_SET);
410 dprintf(f->fd[FCALL_STATE], "pending\n");
411
412 logmsg(": %s : Audio > Ringing\n", f->name);
413 }
414
415 static void
416 cbcallstate(ToxAV *av, uint32_t fnum, uint32_t state, void *udata)
417 {
418 struct friend *f;
419
420 TAILQ_FOREACH(f, &friendhead, entry)
421 if (f->num == fnum)
422 break;
423 if (!f)
424 return;
425
426 if ((state & TOXAV_FRIEND_CALL_STATE_ERROR)
427 || (state & TOXAV_FRIEND_CALL_STATE_FINISHED)) {
428 f->av.state &= ~TRANSMITTING;
429 cancelcall(f, "Finished");
430 return;
431 }
432
433 /*
434 * If we've are ringing a friend, and he sends a control that's
435 * not FINISHED, it means he accepted the call, so we can start
436 * transmitting audio frames
437 */
438 if (f->av.state & RINGING) {
439 f->av.state &= ~RINGING;
440 f->av.state |= TRANSMITTING;
441 logmsg(": %s : Audio > Transmitting\n", f->name);
442 }
443 }
444
445 static void
446 cbcalldata(ToxAV *av, uint32_t fnum, const int16_t *data, size_t len,
447 uint8_t channels, uint32_t rate, void *udata)
448 {
449 struct friend *f;
450 ssize_t n, wrote;
451 int fd;
452 uint8_t *buf;
453
454 TAILQ_FOREACH(f, &friendhead, entry)
455 if (f->num == fnum)
456 break;
457 if (!f)
458 return;
459 if (!(f->av.state & INCOMING)) {
460 /* try to open call_out for writing */
461 fd = fifoopen(f->dirfd, ffiles[FCALL_OUT]);
462 if (fd < 0) {
463 close (fd);
464 return;
465 }
466 if (f->fd[FCALL_OUT] < 0) {
467 f->fd[FCALL_OUT] = fd;
468 f->av.state |= INCOMING;
469 }
470 }
471
472 buf = (uint8_t *)data;
473 len *= 2;
474 wrote = 0;
475 while (len > 0) {
476 n = write(f->fd[FCALL_OUT], &buf[wrote], len);
477 if (n < 0) {
478 if (errno == EPIPE)
479 f->av.state &= ~INCOMING;
480 break;
481 } else if (n == 0) {
482 break;
483 }
484 wrote += n;
485 len -= n;
486 }
487 }
488
489 static void
490 cbconfinvite(Tox *m, uint32_t frnum, TOX_CONFERENCE_TYPE type, const uint8_t *cookie, size_t clen, void * udata)
491 {
492 size_t i, j, namelen;
493 struct file invfifo;
494 struct invite *inv;
495 uint8_t id[TOX_PUBLIC_KEY_SIZE];
496
497 if(type != TOX_CONFERENCE_TYPE_TEXT) {
498 weprintf("Only text conferences supported at the moment\n");
499 return;
500 }
501
502 if (!tox_friend_get_public_key(tox, frnum, id, NULL)) {
503 weprintf("Failed to get key by friend %i for invite\n", frnum);
504 return;
505 }
506
507 inv = calloc(1, sizeof(*inv));
508 if (!inv)
509 eprintf("calloc:");
510 inv->fd = -1;
511
512 inv->inviter = frnum;
513 inv->cookielen = clen;
514 inv->cookie = malloc(inv->cookielen);
515 if (!inv->cookie)
516 eprintf("malloc:");
517
518 memcpy(inv->cookie, cookie, clen);
519
520 namelen = 2 * TOX_PUBLIC_KEY_SIZE + 1 + 2 * clen + 2;
521 inv->fifoname = malloc(namelen);
522 if (!inv->fifoname)
523 eprintf("malloc:");
524
525 i = 0;
526 id2str(id, inv->fifoname);
527 i += 2 * TOX_PUBLIC_KEY_SIZE;
528 inv->fifoname[i] = '_';
529 i++;
530 for(j = 0; j < clen; i+=2, j++)
531 sprintf(inv->fifoname + i, "%02X", cookie[j]);
532 i++;
533 inv->fifoname[i] = '\0';
534
535 invfifo.name = inv->fifoname;
536 invfifo.flags = O_RDONLY | O_NONBLOCK;
537 fiforeset(gslots[CONF].fd[OUT], &inv->fd, invfifo);
538
539 TAILQ_INSERT_TAIL(&invhead, inv, entry);
540
541 logmsg("Invite > %s\n", inv->fifoname);
542 }
543
544 static void
545 cbconfmessage(Tox *m, uint32_t cnum, uint32_t pnum, TOX_MESSAGE_TYPE type, const uint8_t *data, size_t len, void *udata)
546 {
547 struct conference *c;
548 time_t t;
549 uint8_t msg[len + 1], namt[TOX_MAX_NAME_LENGTH + 1];
550 char buft[64];
551
552 memcpy(msg, data, len);
553 msg[len] = '\0';
554
555 TAILQ_FOREACH(c, &confhead, entry) {
556 if (c->num == cnum) {
557 t = time(NULL);
558 strftime(buft, sizeof(buft), "%F %R", localtime(&t));
559 if (!tox_conference_peer_get_name(tox, c->num, pnum, namt, NULL)) {
560 weprintf("Unable to obtain name for peer %d in conference %s\n", pnum, c->numstr);
561 return;
562 }
563 namt[tox_conference_peer_get_name_size(tox, c->num, pnum, NULL)] = '\0';
564 dprintf(c->fd[CTEXT_OUT], "%s <%s> %s\n", buft, namt, msg);
565 if (confmsg_log)
566 logmsg("%s : %s <%s> %s\n", c->numstr, buft, namt, msg);
567 break;
568 }
569 }
570 }
571
572 static void
573 cbconftitle(Tox *m, uint32_t cnum, uint32_t pnum, const uint8_t *data, size_t len, void * udata)
574 {
575 struct conference *c;
576 char title[TOX_MAX_NAME_LENGTH + 1];
577
578 memcpy(title, data, len);
579 title[len] = '\0';
580
581 TAILQ_FOREACH(c, &confhead, entry) {
582 if (c->num == cnum) {
583 ftruncate(c->fd[CTITLE_OUT], 0);
584 lseek(c->fd[CTITLE_OUT], 0, SEEK_SET);
585 dprintf(c->fd[CTITLE_OUT], "%s\n", title);
586 logmsg(": %s : Title > %s\n", c->numstr, title);
587 break;
588 }
589 }
590 }
591
592 static void
593 cbconfmembers(Tox *m, uint32_t cnum, void *udata)
594 {
595 struct conference *c;
596
597 TAILQ_FOREACH(c, &confhead, entry) {
598 if (c->num == cnum) {
599 writemembers(c);
600 break;
601 }
602 }
603 }
604
605 static void
606 cancelcall(struct friend *f, char *action)
607 {
608 logmsg(": %s : Audio > %s\n", f->name, action);
609
610 if (f->av.state & TRANSMITTING || f->av.state & RINGING) {
611 if (!toxav_call_control(toxav, f->num, TOXAV_CALL_CONTROL_CANCEL, NULL))
612 weprintf("Failed to terminate call\n");
613 }
614 f->av.state = 0;
615
616 /* Cancel Rx side of the call */
617 if (f->fd[FCALL_OUT] != -1) {
618 close(f->fd[FCALL_OUT]);
619 f->fd[FCALL_OUT] = -1;
620 }
621 ftruncate(f->fd[FCALL_STATE], 0);
622 lseek(f->fd[FCALL_STATE], 0, SEEK_SET);
623 dprintf(f->fd[FCALL_STATE], "none\n");
624
625 /* Cancel Tx side of the call */
626 free(f->av.frame);
627 f->av.frame = NULL;
628 fiforeset(f->dirfd, &f->fd[FCALL_IN], ffiles[FCALL_IN]);
629 }
630
631 static void
632 sendfriendcalldata(struct friend *f)
633 {
634 struct timespec now, diff;
635 ssize_t n;
636 TOXAV_ERR_SEND_FRAME err;
637
638 n = fiforead(f->dirfd, &f->fd[FCALL_IN], ffiles[FCALL_IN],
639 f->av.frame + (f->av.state & INCOMPLETE ? f->av.n : 0),
640 framesize * sizeof(int16_t) - (f->av.state & INCOMPLETE ? f->av.n : 0));
641 if (n == 0) {
642 f->av.state &= ~OUTGOING;
643 f->av.state &= ~INCOMPLETE;
644 return;
645 } else if (n < 0 || f->av.state & RINGING) {
646 /* discard data as long as the call is not established */
647 return;
648 } else if (n == (framesize * sizeof(int16_t) - (f->av.state & INCOMPLETE ? f->av.n : 0))) {
649 f->av.state &= ~INCOMPLETE;
650 f->av.n = 0;
651 } else {
652 f->av.state |= INCOMPLETE;
653 f->av.n += n;
654 return;
655 }
656
657 clock_gettime(CLOCK_MONOTONIC, &now);
658 diff = timediff(f->av.lastsent, now);
659 if (diff.tv_sec == 0 && diff.tv_nsec < (AUDIOFRAME - 1) * 1E6) {
660 diff.tv_nsec = (AUDIOFRAME - 1) * 1E6 - diff.tv_nsec;
661 nanosleep(&diff, NULL);
662 }
663 clock_gettime(CLOCK_MONOTONIC, &f->av.lastsent);
664 if (!toxav_audio_send_frame(toxav, f->num, (int16_t *)f->av.frame,
665 framesize, AUDIOCHANNELS, AUDIOSAMPLERATE, &err))
666 weprintf("Failed to send audio frame: %s\n", callerr[err]);
667 }
668
669 static void
670 writemembers(struct conference *c)
671 {
672 size_t i;
673 uint32_t peers, pnum;
674 uint8_t name[TOX_MAX_NAME_LENGTH + 1];
675 TOX_ERR_CONFERENCE_PEER_QUERY err;
676
677 /*The peer list is written when we invite the members by the callback*/
678 ftruncate(c->fd[CMEMBERS], 0);
679 peers = tox_conference_peer_count(tox, c->num, &err);
680
681 if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
682 weprintf("Unable to obtain peer count for conference %d\n", c->num);
683 return;
684 }
685 for (pnum = 0; pnum < peers; pnum++) {
686 if (!tox_conference_peer_get_name(tox, c->num, pnum, name, NULL)) {
687 weprintf("Unable to obtain the name for peer %d\n", pnum);
688 } else {
689 i = tox_conference_peer_get_name_size(tox, c->num, pnum, NULL);
690 name[i] = '\0';
691 dprintf(c->fd[CMEMBERS], "%s\n", name);
692 }
693 }
694 }
695
696 static void
697 cbconnstatus(Tox *m, uint32_t frnum, TOX_CONNECTION status, void *udata)
698 {
699 struct friend *f;
700 struct request *req, *rtmp;
701 size_t r;
702 char name[TOX_MAX_NAME_LENGTH + 1];
703 TOX_ERR_FRIEND_QUERY err;
704
705 r = tox_friend_get_name_size(tox, frnum, &err);
706 if (err != TOX_ERR_FRIEND_QUERY_OK) {
707 weprintf("Failed to get name for friend number %ld\n", (long)frnum);
708 return;
709 } else if (r == 0) {
710 snprintf(name, sizeof(name), "Anonymous");
711 } else {
712 tox_friend_get_name(tox, frnum, (uint8_t *)name, NULL);
713 name[r] = '\0';
714 }
715
716 logmsg(": %s : Connection > %s\n", name, status == TOX_CONNECTION_NONE ? "Offline" : "Online");
717
718 TAILQ_FOREACH(f, &friendhead, entry) {
719 if (f->num == frnum) {
720 ftruncate(f->fd[FONLINE], 0);
721 lseek(f->fd[FONLINE], 0, SEEK_SET);
722 dprintf(f->fd[FONLINE], "%d\n", status);
723 break;
724 }
725 }
726
727 /* Remove the pending request-FIFO if it exists */
728 for (req = TAILQ_FIRST(&reqhead); req; req = rtmp) {
729 rtmp = TAILQ_NEXT(req, entry);
730
731 if (memcmp(f->id, req->id, TOX_PUBLIC_KEY_SIZE))
732 continue;
733 unlinkat(gslots[REQUEST].fd[OUT], req->idstr, 0);
734 close(req->fd);
735 TAILQ_REMOVE(&reqhead, req, entry);
736 free(req->msg);
737 free(req);
738 }
739 }
740
741 static void
742 cbfriendmessage(Tox *m, uint32_t frnum, TOX_MESSAGE_TYPE type, const uint8_t *data, size_t len, void *udata)
743 {
744 struct friend *f;
745 time_t t;
746 uint8_t msg[len + 1];
747 char buft[64];
748
749 memcpy(msg, data, len);
750 msg[len] = '\0';
751
752 TAILQ_FOREACH(f, &friendhead, entry) {
753 if (f->num == frnum) {
754 t = time(NULL);
755 strftime(buft, sizeof(buft), "%F %R", localtime(&t));
756 dprintf(f->fd[FTEXT_OUT], "%s %s\n", buft, msg);
757 if (friendmsg_log)
758 logmsg(": %s > %s\n", f->name, msg);
759 break;
760 }
761 }
762 }
763
764 static void
765 cbfriendrequest(Tox *m, const uint8_t *id, const uint8_t *data, size_t len, void *udata)
766 {
767 struct file reqfifo;
768 struct request *req;
769
770 req = calloc(1, sizeof(*req));
771 if (!req)
772 eprintf("calloc:");
773 req->fd = -1;
774
775 memcpy(req->id, id, TOX_PUBLIC_KEY_SIZE);
776 id2str(req->id, req->idstr);
777
778 if (len > 0) {
779 req->msg = malloc(len + 1);
780 if (!req->msg)
781 eprintf("malloc:");
782 memcpy(req->msg, data, len);
783 req->msg[len] = '\0';
784 } else {
785 req->msg = "ratox is awesome!";
786 }
787
788 reqfifo.name = req->idstr;
789 reqfifo.flags = O_RDONLY | O_NONBLOCK;
790 fiforeset(gslots[REQUEST].fd[OUT], &req->fd, reqfifo);
791
792 TAILQ_INSERT_TAIL(&reqhead, req, entry);
793
794 logmsg("Request > %s : %s\n",
795 req->idstr, req->msg);
796 }
797
798 static void
799 cbnamechange(Tox *m, uint32_t frnum, const uint8_t *data, size_t len, void *user)
800 {
801 struct friend *f;
802 uint8_t name[len + 1];
803
804 memcpy(name, data, len);
805 name[len] = '\0';
806
807 TAILQ_FOREACH(f, &friendhead, entry) {
808 if (f->num == frnum) {
809 if (memcmp(f->name, name, len + 1) == 0)
810 break;
811 ftruncate(f->fd[FNAME], 0);
812 lseek(f->fd[FNAME], 0, SEEK_SET);
813 dprintf(f->fd[FNAME], "%s\n", name);
814 logmsg(": %s : Name > %s\n", f->name, name);
815 memcpy(f->name, name, len + 1);
816 break;
817 }
818 }
819 datasave();
820 }
821
822 static void
823 cbstatusmessage(Tox *m, uint32_t frnum, const uint8_t *data, size_t len, void *udata)
824 {
825 struct friend *f;
826 uint8_t status[len + 1];
827
828 memcpy(status, data, len);
829 status[len] = '\0';
830
831 TAILQ_FOREACH(f, &friendhead, entry) {
832 if (f->num == frnum) {
833 ftruncate(f->fd[FSTATUS], 0);
834 lseek(f->fd[FSTATUS], 0, SEEK_SET);
835 dprintf(f->fd[FSTATUS], "%s\n", status);
836 logmsg(": %s : Status > %s\n", f->name, status);
837 break;
838 }
839 }
840 datasave();
841 }
842
843 static void
844 cbfriendstate(Tox *m, uint32_t frnum, TOX_USER_STATUS state, void *udata)
845 {
846 struct friend *f;
847
848 if (state >= LEN(ustate)) {
849 weprintf("Received invalid user status: %d\n", state);
850 return;
851 }
852
853 TAILQ_FOREACH(f, &friendhead, entry) {
854 if (f->num == frnum) {
855 ftruncate(f->fd[FSTATE], 0);
856 lseek(f->fd[FSTATE], 0, SEEK_SET);
857 dprintf(f->fd[FSTATE], "%s\n", ustate[state]);
858 logmsg(": %s : State > %s\n", f->name, ustate[state]);
859 break;
860 }
861 }
862 datasave();
863 }
864
865 static void
866 cbfilecontrol(Tox *m, uint32_t frnum, uint32_t fnum, TOX_FILE_CONTROL ctrltype, void *udata)
867 {
868 struct friend *f;
869
870 TAILQ_FOREACH(f, &friendhead, entry)
871 if (f->num == frnum)
872 break;
873 if (!f)
874 return;
875
876 switch (ctrltype) {
877 case TOX_FILE_CONTROL_RESUME:
878 if (f->tx.state == TRANSFER_PAUSED) {
879 logmsg(": %s : Tx > Resumed\n", f->name);
880 f->tx.state = TRANSFER_INPROGRESS;
881 } else {
882 f->tx.fnum = fnum;
883 f->tx.buf = malloc(TOX_MAX_CUSTOM_PACKET_SIZE);
884 if (!f->tx.buf)
885 eprintf("malloc:");
886 f->tx.n = 0;
887 f->tx.pendingbuf = 0;
888 f->tx.state = TRANSFER_INPROGRESS;
889 logmsg(": %s : Tx > In Progress\n", f->name);
890 }
891 break;
892 case TOX_FILE_CONTROL_PAUSE:
893 if (f->tx.state == TRANSFER_INPROGRESS) {
894 logmsg(": %s : Tx > Paused\n", f->name);
895 f->tx.state = TRANSFER_PAUSED;
896 }
897 break;
898 case TOX_FILE_CONTROL_CANCEL:
899 /* Check wether we're sending or receiving */
900 if (f->tx.fnum == fnum) {
901 logmsg(": %s : Tx > Rejected\n", f->name);
902 f->tx.state = TRANSFER_NONE;
903 free(f->tx.buf);
904 f->tx.buf = NULL;
905 fiforeset(f->dirfd, &f->fd[FFILE_IN], ffiles[FFILE_IN]);
906 } else {
907 logmsg(": %s : Rx > Cancelled by Sender\n", f->name);
908 cancelrxtransfer(f);
909 }
910 break;
911 default:
912 weprintf("Unhandled file control type: %d\n", ctrltype);
913 break;
914 };
915 }
916
917 static void
918 cbfiledatareq(Tox *m, uint32_t frnum, uint32_t fnum, uint64_t pos, size_t flen, void *udata)
919 {
920 struct friend *f;
921 ssize_t n;
922
923 TAILQ_FOREACH(f, &friendhead, entry)
924 if (f->num == frnum)
925 break;
926
927 /* Grab another buffer from the FIFO */
928 if (!f->tx.pendingbuf) {
929 n = fiforead(f->dirfd, &f->fd[FFILE_IN], ffiles[FFILE_IN],
930 f->tx.buf, flen);
931 f->tx.n = n;
932 f->tx.pendingbuf = 0;
933 }
934
935 if (f->tx.n < 0) {
936 if (errno != EWOULDBLOCK)
937 weprintf("Reading data for file sending would block\n");
938 return;
939 }
940
941 if (!tox_file_send_chunk(tox, f->num, f->tx.fnum, pos, f->tx.buf, f->tx.n, NULL))
942 f->tx.pendingbuf = 1;
943
944 /*
945 * For streams, core will know that the transfer is finished
946 * if a chunk with length less than the length requested in the
947 * callback is sent.
948 */
949 if (!f->tx.pendingbuf && (size_t)f->tx.n < flen) {
950 logmsg(": %s : Tx > Complete\n", f->name);
951 f->tx.state = TRANSFER_NONE;
952 f->tx.fnum = -1;
953 free(f->tx.buf);
954 f->tx.buf = NULL;
955 fiforeset(f->dirfd, &f->fd[FFILE_IN], ffiles[FFILE_IN]);
956 return;
957 }
958 }
959
960 static void
961 cbfilesendreq(Tox *m, uint32_t frnum, uint32_t fnum, uint32_t kind, uint64_t fsz,
962 const uint8_t *fname, size_t flen, void *udata)
963 {
964 struct friend *f;
965 uint8_t filename[flen + 1];
966
967 TAILQ_FOREACH(f, &friendhead, entry)
968 if (f->num == frnum)
969 break;
970 if (!f)
971 return;
972
973 memcpy(filename, fname, flen);
974 filename[flen] = '\0';
975
976 if (kind == TOX_FILE_KIND_AVATAR) {
977 if (!tox_file_control(tox, f->num, fnum, TOX_FILE_CONTROL_CANCEL, NULL))
978 weprintf("Failed to kill avatar transfer\n");
979 return;
980 }
981
982 /* We only support a single transfer at a time */
983 if (f->rxstate == TRANSFER_INPROGRESS) {
984 logmsg(": %s : Rx > Rejected %s, already one in progress\n",
985 f->name, filename);
986 if (!tox_file_control(tox, f->num, f->tx.fnum, TOX_FILE_CONTROL_CANCEL, NULL))
987 weprintf("Failed to kill new Rx transfer\n");
988 return;
989 }
990
991 f->tx.fnum = fnum;
992
993 ftruncate(f->fd[FFILE_STATE], 0);
994 lseek(f->fd[FFILE_STATE], 0, SEEK_SET);
995 dprintf(f->fd[FFILE_STATE], "%s\n", filename);
996 f->rxstate = TRANSFER_PENDING;
997 logmsg(": %s : Rx > Pending %s\n", f->name, filename);
998 }
999
1000 static void
1001 cbfiledata(Tox *m, uint32_t frnum, uint32_t fnum, uint64_t pos,
1002 const uint8_t *data, size_t len, void *udata)
1003 {
1004 struct friend *f;
1005 ssize_t n;
1006 uint16_t wrote = 0;
1007
1008 TAILQ_FOREACH(f, &friendhead, entry)
1009 if (f->num == frnum)
1010 break;
1011 if (!f)
1012 return;
1013
1014 /* When length is 0, the transfer is finished */
1015 if (!len) {
1016 logmsg(": %s : Rx > Complete\n", f->name);
1017 if (f->fd[FFILE_OUT] != -1) {
1018 close(f->fd[FFILE_OUT]);
1019 f->fd[FFILE_OUT] = -1;
1020 }
1021 ftruncate(f->fd[FFILE_STATE], 0);
1022 lseek(f->fd[FFILE_STATE], 0, SEEK_SET);
1023 f->rxstate = TRANSFER_NONE;
1024 return;
1025 }
1026
1027 while (len > 0) {
1028 n = write(f->fd[FFILE_OUT], &data[wrote], len);
1029 if (n < 0) {
1030 if (errno == EPIPE) {
1031 cancelrxtransfer(f);
1032 break;
1033 } else if (errno == EWOULDBLOCK) {
1034 continue;
1035 }
1036 }
1037 wrote += n;
1038 len -= n;
1039 }
1040 }
1041
1042 static void
1043 canceltxtransfer(struct friend *f)
1044 {
1045 if (f->tx.state == TRANSFER_NONE)
1046 return;
1047 logmsg(": %s : Tx > Cancelling\n", f->name);
1048 if (!tox_file_control(tox, f->num, f->tx.fnum, TOX_FILE_CONTROL_CANCEL, NULL))
1049 weprintf("Failed to kill Tx transfer\n");
1050 f->tx.fnum = -1;
1051 f->tx.state = TRANSFER_NONE;
1052 free(f->tx.buf);
1053 f->tx.buf = NULL;
1054 fiforeset(f->dirfd, &f->fd[FFILE_IN], ffiles[FFILE_IN]);
1055 }
1056
1057 static void
1058 cancelrxtransfer(struct friend *f)
1059 {
1060 if (f->rxstate == TRANSFER_NONE)
1061 return;
1062 logmsg(": %s : Rx > Cancelling\n", f->name);
1063 if (!tox_file_control(tox, f->num, f->tx.fnum, TOX_FILE_CONTROL_CANCEL, NULL))
1064 weprintf("Failed to kill Rx transfer\n");
1065 if (f->fd[FFILE_OUT] != -1) {
1066 close(f->fd[FFILE_OUT]);
1067 f->fd[FFILE_OUT] = -1;
1068 }
1069 ftruncate(f->fd[FFILE_STATE], 0);
1070 lseek(f->fd[FFILE_STATE], 0, SEEK_SET);
1071 f->rxstate = TRANSFER_NONE;
1072 }
1073
1074 static void
1075 sendfriendtext(struct friend *f)
1076 {
1077 ssize_t n;
1078 time_t t;
1079 char buft[64];
1080 uint8_t buf[TOX_MAX_MESSAGE_LENGTH + 1];
1081 TOX_ERR_FRIEND_SEND_MESSAGE err;
1082
1083 n = fiforead(f->dirfd, &f->fd[FTEXT_IN], ffiles[FTEXT_IN], buf, sizeof(buf) - 1);
1084 if (n <= 0)
1085 return;
1086 if (buf[n - 1] == '\n' && n > 1)
1087 n--;
1088 tox_friend_send_message(tox, f->num, TOX_MESSAGE_TYPE_NORMAL, buf, n, &err);
1089 if (err != TOX_ERR_FRIEND_SEND_MESSAGE_OK)
1090 weprintf("Failed to send message\n");
1091
1092 buf[n]='\0';
1093 t = time(NULL);
1094 strftime(buft, sizeof(buft), "%F %R", localtime(&t));
1095 dprintf(f->fd[FTEXT_OUT], "me %s %s\n", buft, buf);
1096 }
1097
1098 static void
1099 removefriend(struct friend *f)
1100 {
1101 char c;
1102
1103 if (fiforead(f->dirfd, &f->fd[FREMOVE], ffiles[FREMOVE], &c, 1) != 1 || c != '1')
1104 return;
1105 tox_friend_delete(tox, f->num, NULL);
1106 datasave();
1107 logmsg(": %s > Removed\n", f->name);
1108 frienddestroy(f);
1109 }
1110
1111 static void
1112 invitefriend(struct conference *c)
1113 {
1114 ssize_t n;
1115 char buf[2 * TOX_ADDRESS_SIZE + 1];
1116 struct friend *f;
1117
1118 n = fiforead(c->dirfd, &c->fd[CINVITE], cfiles[CINVITE], buf, sizeof(buf));
1119
1120 if (n > sizeof(buf) || n <= 0)
1121 return;
1122 if (buf[n - 1] == '\n')
1123 buf[n - 1] = '\0';
1124
1125 TAILQ_FOREACH(f, &friendhead, entry)
1126 if (!memcmp(buf, f->idstr, sizeof(f->idstr)-1))
1127 break;
1128 if (!f) {
1129 weprintf("No friend with id %s found for %s\n", buf, c->numstr);
1130 return;
1131 }
1132 if (tox_friend_get_connection_status(tox, f->num, NULL) == TOX_CONNECTION_NONE) {
1133 weprintf("%s not online, can't be invited to %s\n", buf, c->numstr);
1134 return;
1135 }
1136 if (!tox_conference_invite(tox, f->num, c->num, NULL))
1137 weprintf("Failed to invite %s\n", buf);
1138 else
1139 logmsg("- %s : Invited > %s\n", c->numstr, buf);
1140 }
1141
1142 static void
1143 sendconftext(struct conference *c)
1144 {
1145 ssize_t n;
1146 time_t t;
1147 char buft[64];
1148 uint8_t buf[TOX_MAX_MESSAGE_LENGTH + 1], me[TOX_MAX_NAME_LENGTH + 1];
1149
1150 n = fiforead(c->dirfd, &c->fd[CTEXT_IN], cfiles[CTEXT_IN], buf, sizeof(buf) - 1);
1151 if (n <= 0)
1152 return;
1153 if (buf[n - 1] == '\n' && n > 1)
1154 n--;
1155 if (!tox_conference_send_message(tox, c->num, TOX_MESSAGE_TYPE_NORMAL,
1156 buf, n, NULL))
1157 weprintf("Failed to send message to %s\n", c->numstr);
1158
1159 buf[n] = '\0';
1160 t = time(NULL);
1161 strftime(buft, sizeof(buft), "%F %R", localtime(&t));
1162 tox_self_get_name(tox, me);
1163 me[tox_self_get_name_size(tox)] = '\0';
1164 dprintf(c->fd[CTEXT_OUT], "%s <%s> %s\n", buft, me, buf);
1165 }
1166
1167 static void
1168 updatetitle(struct conference *c)
1169 {
1170 ssize_t n;
1171 uint8_t title[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
1172
1173 n = fiforead(c->dirfd, &c->fd[CTITLE_IN], cfiles[CTITLE_IN], title, sizeof(title) - 1);
1174 if (n <= 0)
1175 return;
1176 if (title[n - 1] == '\n')
1177 n--;
1178 title[n] = '\0';
1179 if (!tox_conference_set_title(tox, c->num, title, n, NULL)) {
1180 weprintf("Failed to set title for %s to \"%s\"\n", c->numstr, title);
1181 return;
1182 }
1183 ftruncate(c->fd[CTITLE_OUT], 0);
1184 lseek(c->fd[CTITLE_OUT], 0, SEEK_SET);
1185 dprintf(c->fd[CTITLE_OUT], "%s\n", title);
1186 logmsg("- %s : Title > %s\n", c->numstr, title);
1187 }
1188
1189 static int
1190 readpass(const char *prompt, uint8_t **target, uint32_t *len)
1191 {
1192 char pass[BUFSIZ], *p;
1193
1194 p = readpassphrase(prompt, pass, sizeof(pass), RPP_ECHO_OFF);
1195 if (!p) {
1196 weprintf("Could not read passphrase");
1197 return -1;
1198 }
1199 if (p[0] == '\0')
1200 return -1;
1201 *target = realloc(*target, strlen(p)); /* not null-terminated */
1202 if (!*target)
1203 eprintf("realloc:");
1204 memcpy(*target, p, strlen(p));
1205 *len = strlen(p);
1206 return 0;
1207 }
1208
1209 static void
1210 dataload(struct Tox_Options *toxopt)
1211 {
1212 off_t sz;
1213 uint32_t pp2len = 0;
1214 int fd;
1215 uint8_t *data, * intermediate, *passphrase2 = NULL;
1216
1217 fd = open(savefile, O_RDONLY);
1218 if (fd < 0) {
1219 if (encryptsavefile) {
1220 reprompt1:
1221 while (readpass("Data : New passphrase > ", &passphrase, &pplen) < 0);
1222 while (readpass("Data : Re-enter passphrase > ", &passphrase2, &pp2len) < 0);
1223
1224 if (pplen != pp2len || memcmp(passphrase, passphrase2, pplen)) {
1225 weprintf("Data passphrase mismatch\n");
1226 goto reprompt1;
1227 }
1228 free(passphrase2);
1229 }
1230 return;
1231 }
1232
1233 sz = lseek(fd, 0, SEEK_END);
1234 lseek(fd, 0, SEEK_SET);
1235
1236 if (sz == 0) {
1237 weprintf("Datafile %s is empty\n", savefile);
1238 return;
1239 } else if (sz < 0) {
1240 weprintf("Datafile %s can't be seeked\n", savefile);
1241 return;
1242 }
1243
1244 intermediate = malloc(sz);
1245 if (!intermediate)
1246 eprintf("malloc:");
1247
1248 if (read(fd, intermediate, sz) != sz)
1249 eprintf("read %s:", savefile);
1250
1251 if (tox_is_data_encrypted(intermediate)) {
1252 toxopt->savedata_length = sz-TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
1253 data = malloc(toxopt->savedata_length);
1254 if (!data)
1255 eprintf("malloc:");
1256 if (!encryptsavefile)
1257 logmsg("Data : %s > Encrypted, but saving unencrypted\n", savefile);
1258 while (readpass("Data : Passphrase > ", &passphrase, &pplen) < 0 ||
1259 !tox_pass_decrypt(intermediate, sz, passphrase, pplen, data, NULL));
1260 } else {
1261 toxopt->savedata_length = sz;
1262 data = malloc(sz);
1263 if (!data)
1264 eprintf("malloc:");
1265 memcpy(data, intermediate, sz);
1266 if (encryptsavefile) {
1267 logmsg("Data : %s > Not encrypted, but saving encrypted\n", savefile);
1268 reprompt2:
1269 while (readpass("Data : New passphrase > ", &passphrase, &pplen) < 0);
1270 while (readpass("Data : Re-enter passphrase > ", &passphrase2, &pp2len) < 0);
1271
1272 if (pplen != pp2len || memcmp(passphrase, passphrase2, pplen)) {
1273 weprintf("Data passphrase mismatch\n");
1274 goto reprompt2;
1275 }
1276 free(passphrase2);
1277 }
1278 }
1279
1280 toxopt->savedata_data = data;
1281 toxopt->savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE;
1282
1283 free(intermediate);
1284 close(fd);
1285 }
1286
1287 static void
1288 datasave(void)
1289 {
1290 off_t sz;
1291 int fd;
1292 uint8_t *data, *intermediate;
1293
1294 fd = open(savefile, O_WRONLY | O_TRUNC | O_CREAT , 0666);
1295 if (fd < 0)
1296 eprintf("open %s:", savefile);
1297
1298 sz = tox_get_savedata_size(tox);
1299 intermediate = malloc(sz);
1300 if (!intermediate)
1301 eprintf("malloc:");
1302
1303 tox_get_savedata(tox, intermediate);
1304
1305 sz += encryptsavefile ? TOX_PASS_ENCRYPTION_EXTRA_LENGTH : 0;
1306 data = malloc(sz);
1307 if (!data)
1308 eprintf("malloc:");
1309
1310 if (encryptsavefile){
1311 tox_pass_encrypt(intermediate, sz - TOX_PASS_ENCRYPTION_EXTRA_LENGTH, passphrase, pplen, data, NULL);
1312 } else {
1313 memcpy(data, intermediate, sz);
1314 }
1315 if (write(fd, data, sz) != sz)
1316 eprintf("write %s:", savefile);
1317 fsync(fd);
1318
1319 free(data);
1320 free(intermediate);
1321 close(fd);
1322 }
1323
1324 static int
1325 localinit(void)
1326 {
1327 DIR *d;
1328 size_t i, m;
1329 int r;
1330 uint8_t name[TOX_MAX_NAME_LENGTH + 1];
1331 uint8_t address[TOX_ADDRESS_SIZE];
1332 uint8_t status[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
1333
1334 for (i = 0; i < LEN(gslots); i++) {
1335 r = mkdir(gslots[i].name, 0777);
1336 if (r < 0 && errno != EEXIST)
1337 eprintf("mkdir %s:", gslots[i].name);
1338 d = opendir(gslots[i].name);
1339 if (!d)
1340 eprintf("opendir %s:", gslots[i].name);
1341 r = dirfd(d);
1342 if (r < 0)
1343 eprintf("dirfd %s:", gslots[i].name);
1344 gslots[i].dirfd = r;
1345
1346 for (m = 0; m < LEN(gfiles); m++) {
1347 if (gfiles[m].type == FIFO) {
1348 fiforeset(gslots[i].dirfd, &gslots[i].fd[m], gfiles[m]);
1349 } else if (gfiles[m].type == STATIC || (gfiles[m].type == NONE && !gslots[i].outisfolder)) {
1350 gslots[i].fd[m] = fifoopen(gslots[i].dirfd, gfiles[m]);
1351 } else if (gfiles[m].type == NONE && gslots[i].outisfolder) {
1352 r = mkdirat(gslots[i].dirfd, gfiles[m].name, 0777);
1353 if (r < 0 && errno != EEXIST)
1354 eprintf("mkdirat %s:", gfiles[m].name);
1355 r = openat(gslots[i].dirfd, gfiles[m].name, O_RDONLY | O_DIRECTORY);
1356 if (r < 0)
1357 eprintf("openat %s:", gfiles[m].name);
1358 gslots[i].fd[m] = r;
1359 }
1360 }
1361 }
1362
1363 /* Dump current name */
1364 r = tox_self_get_name_size(tox);
1365 if (r == 0) {
1366 logmsg("Name > Empty\n");
1367 }
1368 tox_self_get_name(tox, name);
1369 name[r] = '\0';
1370 ftruncate(gslots[NAME].fd[OUT], 0);
1371 dprintf(gslots[NAME].fd[OUT], "%s\n", name);
1372
1373 /* Dump status */
1374 r = tox_self_get_status_message_size(tox);
1375 if (r == 0) {
1376 logmsg("Status > Empty\n");
1377 }
1378 tox_self_get_status_message(tox, status);
1379 status[r] = '\0';
1380 ftruncate(gslots[STATUS].fd[OUT], 0);
1381 dprintf(gslots[STATUS].fd[OUT], "%s\n", status);
1382
1383 /* Dump user state */
1384 r = tox_self_get_status(tox);
1385 ftruncate(gslots[STATE].fd[OUT], 0);
1386 dprintf(gslots[STATE].fd[OUT], "%s\n", ustate[r]);
1387
1388 /* Dump ID */
1389 idfd = open("id", O_WRONLY | O_CREAT, 0666);
1390 if (idfd < 0)
1391 eprintf("open id:");
1392 tox_self_get_address(tox, address);
1393 for (i = 0; i < TOX_ADDRESS_SIZE; i++)
1394 dprintf(idfd, "%02X", address[i]);
1395 dprintf(idfd, "\n");
1396
1397 /* Dump Nospam */
1398 ftruncate(gslots[NOSPAM].fd[OUT], 0);
1399 dprintf(gslots[NOSPAM].fd[OUT], "%08X\n", tox_self_get_nospam(tox));
1400
1401 return 0;
1402 }
1403
1404 static int
1405 toxinit(void)
1406 {
1407 struct Tox_Options toxopt;
1408
1409 tox_options_default(&toxopt);
1410
1411 toxopt.ipv6_enabled = ipv6;
1412 toxopt.udp_enabled = !tcp;
1413 if (proxy) {
1414 tcp = 1;
1415 toxopt.udp_enabled = !tcp;
1416 logmsg("Net > Forcing TCP mode\n");
1417 toxopt.proxy_host = proxyaddr;
1418 toxopt.proxy_port = proxyport;
1419 toxopt.proxy_type = proxytype;
1420 logmsg("Net > Using proxy %s:%hu\n", proxyaddr, proxyport);
1421 }
1422
1423 dataload(&toxopt);
1424
1425 tox = tox_new(&toxopt, NULL);
1426 if (!tox)
1427 eprintf("Core : Tox > Initialization failed\n");
1428
1429 datasave();
1430
1431 toxav = toxav_new(tox, NULL);
1432 if (!toxav)
1433 eprintf("Core : ToxAV > Initialization failed\n");
1434
1435 framesize = (AUDIOSAMPLERATE * AUDIOFRAME * AUDIOCHANNELS) / 1000;
1436
1437 tox_callback_friend_connection_status(tox, cbconnstatus);
1438 tox_callback_friend_message(tox, cbfriendmessage);
1439 tox_callback_friend_request(tox, cbfriendrequest);
1440 tox_callback_friend_name(tox, cbnamechange);
1441 tox_callback_friend_status_message(tox, cbstatusmessage);
1442 tox_callback_friend_status(tox, cbfriendstate);
1443 tox_callback_file_recv_control(tox, cbfilecontrol);
1444 tox_callback_file_recv(tox, cbfilesendreq);
1445 tox_callback_file_recv_chunk(tox, cbfiledata);
1446 tox_callback_file_chunk_request(tox, cbfiledatareq);
1447
1448 toxav_callback_call(toxav, cbcallinvite, NULL);
1449 toxav_callback_call_state(toxav, cbcallstate, NULL);
1450
1451 toxav_callback_audio_receive_frame(toxav, cbcalldata, NULL);
1452
1453 tox_callback_conference_invite(tox, cbconfinvite);
1454 tox_callback_conference_message(tox, cbconfmessage);
1455 tox_callback_conference_title(tox, cbconftitle);
1456 tox_callback_conference_peer_list_changed(tox, cbconfmembers);
1457
1458 if (toxopt.savedata_data)
1459 free((void *)toxopt.savedata_data);
1460
1461 return 0;
1462 }
1463
1464 static int
1465 toxconnect(void)
1466 {
1467 struct node *n;
1468 struct node tmp;
1469 size_t i, j;
1470 bool r;
1471 uint8_t id[TOX_ADDRESS_SIZE];
1472
1473 srand(time(NULL));
1474
1475 /* shuffle it to minimize load on nodes */
1476 for (i = LEN(nodes) - 1; i > 0; i--) {
1477 j = rand() % LEN(nodes);
1478 tmp = nodes[j];
1479 nodes[j] = nodes[i];
1480 nodes[i] = tmp;
1481 }
1482
1483 for (i = 0; i < LEN(nodes); i++) {
1484 n = &nodes[i];
1485 if ((ipv6 && !n->addr6) || (!ipv6 && !n->addr4))
1486 continue;
1487 str2id(n->idstr, id);
1488 r = tox_bootstrap(tox, ipv6 ? n->addr6 : n->addr4, n->udp_port, id, NULL);
1489 if (!r)
1490 weprintf("Bootstrap failed for %s\n", ipv6 ? n->addr6 : n->addr4);
1491 str2id(n->idstr, id);
1492 r += tox_add_tcp_relay(tox, ipv6 ? n->addr6 : n->addr4, n->tcp_port, id, NULL);
1493 if (!r)
1494 weprintf("Adding a relay failed for %s\n", ipv6 ? n->addr6 : n->addr4);
1495 }
1496 return 0;
1497 }
1498
1499 /* Caller has to ensure `idstr' is big enough */
1500 static void
1501 id2str(uint8_t *id, char *idstr)
1502 {
1503 int i;
1504 char hex[] = "0123456789ABCDEF";
1505
1506 for (i = 0; i < TOX_PUBLIC_KEY_SIZE; i++) {
1507 *idstr++ = hex[(id[i] >> 4) & 0xf];
1508 *idstr++ = hex[id[i] & 0xf];
1509 }
1510 *idstr = '\0';
1511 }
1512
1513 /* Caller has to ensure that `id' is big enough */
1514 static void
1515 str2id(char *idstr, uint8_t *id)
1516 {
1517 size_t i, len = strlen(idstr) / 2;
1518 char *p = idstr;
1519
1520 for (i = 0; i < len; ++i, p += 2)
1521 sscanf(p, "%2hhx", &id[i]);
1522 }
1523
1524 static void
1525 friendcreate(uint32_t frnum)
1526 {
1527 struct friend *f;
1528 DIR *d;
1529 size_t i;
1530 int r;
1531 uint8_t status[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
1532 TOX_ERR_FRIEND_QUERY err;
1533
1534 f = calloc(1, sizeof(*f));
1535 if (!f)
1536 eprintf("calloc:");
1537
1538 i = tox_friend_get_name_size(tox, frnum, &err);
1539 if (err != TOX_ERR_FRIEND_QUERY_OK) {
1540 weprintf("Failed to get name for %ld\n", (long)frnum);
1541 return;
1542 } else if (i == 0) {
1543 snprintf(f->name, sizeof(f->name), "Anonymous");
1544 } else {
1545 tox_friend_get_name(tox, frnum, (uint8_t *)f->name, NULL);
1546 f->name[i] = '\0';
1547 }
1548
1549 f->num = frnum;
1550 if (!tox_friend_get_public_key(tox, f->num, f->id, NULL)) {
1551 weprintf("Failed to get key for %s\n", f->name);
1552 return;
1553 }
1554 id2str(f->id, f->idstr);
1555
1556 r = mkdir(f->idstr, 0777);
1557 if (r < 0 && errno != EEXIST)
1558 eprintf("mkdir %s:", f->idstr);
1559
1560 d = opendir(f->idstr);
1561 if (!d)
1562 eprintf("opendir %s:", f->idstr);
1563
1564 r = dirfd(d);
1565 if (r < 0)
1566 eprintf("dirfd %s:", f->idstr);
1567 f->dirfd = r;
1568
1569 for (i = 0; i < LEN(ffiles); i++) {
1570 f->fd[i] = -1;
1571 if (ffiles[i].type == FIFO) {
1572 fiforeset(f->dirfd, &f->fd[i], ffiles[i]);
1573 } else if (ffiles[i].type == STATIC) {
1574 f->fd[i] = fifoopen(f->dirfd, ffiles[i]);
1575 }
1576 }
1577
1578 /* Dump name */
1579 ftruncate(f->fd[FNAME], 0);
1580 dprintf(f->fd[FNAME], "%s\n", f->name);
1581
1582 /* Dump online state */
1583 ftruncate(f->fd[FONLINE], 0);
1584 dprintf(f->fd[FONLINE], "%d\n",
1585 tox_friend_get_connection_status(tox, frnum, NULL));
1586
1587 /* Dump status */
1588 i = tox_friend_get_status_message_size(tox, frnum, NULL);
1589 if (i == SIZE_MAX) {
1590 weprintf("Failed to get status for %s\n", f->name);
1591 i = 0;
1592 }
1593 tox_friend_get_status_message(tox, frnum, status, NULL);
1594 status[i] = '\0';
1595 ftruncate(f->fd[FSTATUS], 0);
1596 dprintf(f->fd[FSTATUS], "%s\n", status);
1597
1598 /* Dump user state */
1599 ftruncate(f->fd[FSTATE], 0);
1600 dprintf(f->fd[FSTATE], "%s\n", ustate[tox_friend_get_status(tox, frnum, NULL)]);
1601
1602 /* Dump file pending state */
1603 ftruncate(f->fd[FFILE_STATE], 0);
1604
1605 /* Dump call pending state */
1606 ftruncate(f->fd[FCALL_STATE], 0);
1607 dprintf(f->fd[FCALL_STATE], "none\n");
1608
1609 f->av.state = 0;
1610
1611 TAILQ_INSERT_TAIL(&friendhead, f, entry);
1612 }
1613
1614 static void
1615 confcreate(uint32_t cnum)
1616 {
1617 struct conference *c;
1618 DIR *d;
1619 size_t i;
1620 int r;
1621 uint8_t title[TOX_MAX_NAME_LENGTH + 1];
1622 TOX_ERR_CONFERENCE_TITLE err;
1623
1624 c = calloc(1, sizeof(*c));
1625 if(!c)
1626 eprintf("calloc:");
1627 c->num = cnum;
1628 sprintf(c->numstr, "%08X", c->num);
1629 r = mkdir(c->numstr, 0777);
1630 if(r < 0 && errno != EEXIST)
1631 eprintf("mkdir %s:", c->numstr);
1632
1633 d = opendir(c->numstr);
1634 if (!d)
1635 eprintf("opendir %s:", c->numstr);
1636
1637 r = dirfd(d);
1638 if (r < 0)
1639 eprintf("dirfd %s:", c->numstr);
1640 c->dirfd = r;
1641
1642 for (i = 0; i < LEN(cfiles); i++) {
1643 c->fd[i] = -1;
1644 if (cfiles[i].type == FIFO) {
1645 fiforeset(c->dirfd, &c->fd[i], cfiles[i]);
1646 } else if (cfiles[i].type == STATIC) {
1647 c->fd[i] = fifoopen(c->dirfd, cfiles[i]);
1648 }
1649 }
1650
1651 writemembers(c);
1652
1653 /* No warning is printed here in the case of an error
1654 * because this always fails when joining after an invite,
1655 * but cbconftitle() is called in the next iteration afterwards,
1656 * so it doesn't matter after all.
1657 */
1658
1659 i = tox_conference_get_title_size(tox, c->num, &err);
1660 if (err != TOX_ERR_CONFERENCE_TITLE_OK)
1661 i = 0;
1662 tox_conference_get_title(tox, c->num, title, NULL);
1663 title[i] = '\0';
1664 ftruncate(c->fd[CTITLE_OUT], 0);
1665 dprintf(c->fd[CTITLE_OUT], "%s\n", title);
1666
1667 TAILQ_INSERT_TAIL(&confhead, c, entry);
1668
1669 logmsg("- %s > Created\n", c->numstr);
1670 }
1671
1672 static void
1673 frienddestroy(struct friend *f)
1674 {
1675 size_t i;
1676
1677 canceltxtransfer(f);
1678 cancelrxtransfer(f);
1679 if (f->av.state > 0)
1680 cancelcall(f, "Destroying");
1681 for (i = 0; i < LEN(ffiles); i++) {
1682 if (f->dirfd != -1) {
1683 unlinkat(f->dirfd, ffiles[i].name, 0);
1684 if (f->fd[i] != -1)
1685 close(f->fd[i]);
1686 }
1687 }
1688 rmdir(f->idstr);
1689 TAILQ_REMOVE(&friendhead, f, entry);
1690 }
1691
1692 static void
1693 confdestroy(struct conference *c)
1694 {
1695 size_t i;
1696
1697 for (i = 0; i <LEN(cfiles); i++) {
1698 if(c->dirfd != -1) {
1699 unlinkat(c->dirfd, cfiles[i].name, 0);
1700 if (c->fd[i] != -1)
1701 close(c->fd[i]);
1702 }
1703 }
1704 rmdir(c->numstr);
1705 TAILQ_REMOVE(&confhead, c, entry);
1706 }
1707
1708 static void
1709 friendload(void)
1710 {
1711 size_t sz;
1712 uint32_t i;
1713 uint32_t *frnums;
1714
1715 sz = tox_self_get_friend_list_size(tox);
1716 frnums = malloc(sz * sizeof(*frnums));
1717 if (!frnums)
1718 eprintf("malloc:");
1719
1720 tox_self_get_friend_list(tox, frnums);
1721
1722 for (i = 0; i < sz; i++)
1723 friendcreate(frnums[i]);
1724
1725 free(frnums);
1726 }
1727
1728 static void
1729 setname(void *data)
1730 {
1731 ssize_t n;
1732 int r;
1733 char name[TOX_MAX_NAME_LENGTH + 1];
1734
1735 ftruncate(gslots[NAME].fd[ERR], 0);
1736 lseek(gslots[NAME].fd[ERR], 0, SEEK_SET);
1737
1738 n = fiforead(gslots[NAME].dirfd, &gslots[NAME].fd[IN],
1739 gfiles[IN], name, sizeof(name) - 1);
1740 if (n <= 0)
1741 return;
1742 if (name[n - 1] == '\n')
1743 n--;
1744 name[n] = '\0';
1745 r = tox_self_set_name(tox, (uint8_t *)name, n, NULL);
1746 if (r < 0) {
1747 dprintf(gslots[STATE].fd[ERR], "Failed to set name to \"%s\"\n", name);
1748 weprintf("Failed to set name to \"%s\"\n", name);
1749 return;
1750 }
1751 datasave();
1752 logmsg("Name > %s\n", name);
1753 ftruncate(gslots[NAME].fd[OUT], 0);
1754 lseek(gslots[NAME].fd[OUT], 0, SEEK_SET);
1755 dprintf(gslots[NAME].fd[OUT], "%s\n", name);
1756 }
1757
1758 static void
1759 setstatus(void *data)
1760 {
1761 ssize_t n;
1762 int r;
1763 uint8_t status[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
1764
1765 ftruncate(gslots[STATUS].fd[ERR], 0);
1766 lseek(gslots[STATUS].fd[ERR], 0, SEEK_SET);
1767
1768 n = fiforead(gslots[STATUS].dirfd, &gslots[STATUS].fd[IN], gfiles[IN],
1769 status, sizeof(status) - 1);
1770 if (n <= 0)
1771 return;
1772 if (status[n - 1] == '\n')
1773 n--;
1774 status[n] = '\0';
1775 r = tox_self_set_status_message(tox, status, n, NULL);
1776 if (r < 0) {
1777 dprintf(gslots[STATUS].fd[ERR], "Failed so set status message to \"%s\"\n", status);
1778 weprintf("Failed to set status message to \"%s\"\n", status);
1779 return;
1780 }
1781 datasave();
1782 logmsg("Status > %s\n", status);
1783 ftruncate(gslots[STATUS].fd[OUT], 0);
1784 lseek(gslots[STATUS].fd[OUT], 0, SEEK_SET);
1785 dprintf(gslots[STATUS].fd[OUT], "%s\n", status);
1786 }
1787
1788 static void
1789 setuserstate(void *data)
1790 {
1791 size_t i;
1792 ssize_t n;
1793 char buf[PIPE_BUF];
1794
1795 ftruncate(gslots[STATE].fd[ERR], 0);
1796 lseek(gslots[STATE].fd[ERR], 0, SEEK_SET);
1797
1798 n = fiforead(gslots[STATE].dirfd, &gslots[STATE].fd[IN], gfiles[IN],
1799 buf, sizeof(buf) - 1);
1800 if (n <= 0)
1801 return;
1802 if (buf[n - 1] == '\n')
1803 n--;
1804 buf[n] = '\0';
1805 for (i = 0; i < LEN(ustate); i++) {
1806 if (strcmp(buf, ustate[i]) == 0) {
1807 tox_self_set_status(tox, i);
1808 break;
1809 }
1810 }
1811 if (i == LEN(ustate)) {
1812 dprintf(gslots[STATE].fd[ERR], "Invalid state %s\n", buf);
1813 weprintf("Invalid state %s\n", buf);
1814 return;
1815 }
1816
1817 ftruncate(gslots[STATE].fd[OUT], 0);
1818 lseek(gslots[STATE].fd[OUT], 0, SEEK_SET);
1819 dprintf(gslots[STATE].fd[OUT], "%s\n", buf);
1820 datasave();
1821 logmsg("State > %s\n", buf);
1822 }
1823
1824 static void
1825 sendfriendreq(void *data)
1826 {
1827 ssize_t n;
1828 uint32_t r;
1829 char buf[PIPE_BUF], *p;
1830 char *msg = "ratox is awesome!";
1831 uint8_t id[TOX_ADDRESS_SIZE];
1832 TOX_ERR_FRIEND_ADD err;
1833
1834 ftruncate(gslots[REQUEST].fd[ERR], 0);
1835 lseek(gslots[REQUEST].fd[ERR], 0, SEEK_SET);
1836
1837 n = fiforead(gslots[REQUEST].dirfd, &gslots[REQUEST].fd[IN], gfiles[IN],
1838 buf, sizeof(buf) - 1);
1839 if (n <= 0)
1840 return;
1841 buf[n] = '\0';
1842
1843 /* locate start of msg */
1844 for (p = buf; *p && !isspace(*p); p++)
1845 ;
1846 if (*p == '\0')
1847 goto out; /* no msg */
1848 *p++ = '\0';
1849 if (*p == '\0') {
1850 goto out; /* no msg */
1851 } else {
1852 msg = p;
1853 if (msg[strlen(msg) - 1] == '\n')
1854 msg[strlen(msg) - 1] = '\0';
1855 }
1856 out:
1857 if (strlen(buf) != sizeof(id) * 2) {
1858 dprintf(gslots[REQUEST].fd[ERR], "Invalid friend ID\n");
1859 weprintf("Invalid friend ID\n");
1860 return;
1861 }
1862 str2id(buf, id);
1863
1864 r = tox_friend_add(tox, id, (uint8_t *)msg, strlen(msg), &err);
1865
1866 if (err != TOX_ERR_FRIEND_ADD_OK) {
1867 dprintf(gslots[REQUEST].fd[ERR], "%s\n", reqerr[err]);
1868 weprintf("%s\n", reqerr[err]);
1869 return;
1870 }
1871 friendcreate(r);
1872 datasave();
1873 logmsg("Request > Sent\n");
1874 }
1875
1876 static void
1877 setnospam(void *data)
1878 {
1879 ssize_t n, i;
1880 uint32_t nsval;
1881 uint8_t nospam[2 * sizeof(uint32_t) + 1];
1882 uint8_t address[TOX_ADDRESS_SIZE];
1883
1884 ftruncate(gslots[NOSPAM].fd[ERR], 0);
1885 lseek(gslots[NOSPAM].fd[ERR], 0, SEEK_SET);
1886
1887 n = fiforead(gslots[NOSPAM].dirfd, &gslots[NOSPAM].fd[IN], gfiles[IN],
1888 nospam, sizeof(nospam) - 1);
1889 if (n <= 0)
1890 return;
1891 if (nospam[n - 1] == '\n')
1892 n--;
1893 nospam[n] = '\0';
1894
1895 for (i = 0; i < n; i++) {
1896 if (nospam[i] < '0' || (nospam[i] > '9' && nospam[i] < 'A') || nospam[i] > 'F') {
1897 dprintf(gslots[NOSPAM].fd[ERR], "Input contains invalid characters ![0-9, A-F]\n");
1898 weprintf("Input contains invalid characters ![0-9, A-F]\n");
1899 goto end;
1900 }
1901 }
1902
1903 nsval = strtoul((char *)nospam, NULL, 16);
1904 tox_self_set_nospam(tox, nsval);
1905 datasave();
1906 logmsg("Nospam > %08X\n", nsval);
1907 ftruncate(gslots[NOSPAM].fd[OUT], 0);
1908 lseek(gslots[NOSPAM].fd[OUT], 0, SEEK_SET);
1909 dprintf(gslots[NOSPAM].fd[OUT], "%08X\n", nsval);
1910
1911 tox_self_get_address(tox, address);
1912 ftruncate(idfd, 0);
1913 lseek(idfd, 0, SEEK_SET);
1914 for (i = 0; i < TOX_ADDRESS_SIZE; i++)
1915 dprintf(idfd, "%02X", address[i]);
1916 dprintf(idfd, "\n");
1917 end:
1918 fiforeset(gslots[NOSPAM].dirfd, &gslots[NOSPAM].fd[IN], gfiles[IN]);
1919 }
1920
1921 static void
1922 newconf(void *data)
1923 {
1924 uint32_t cnum;
1925 size_t n;
1926 char *title, input[TOX_MAX_NAME_LENGTH + 2 + 1];
1927
1928 ftruncate(gslots[CONF].fd[ERR], 0);
1929 lseek(gslots[CONF].fd[ERR], 0, SEEK_SET);
1930
1931 n = fiforead(gslots[CONF].dirfd, &gslots[CONF].fd[IN], gfiles[IN],
1932 input, sizeof(input) - 1);
1933 if (n <= 0)
1934 return;
1935 if (input[n - 1] == '\n')
1936 n--;
1937 input[n] = '\0';
1938 if(!((input[0] == 't' || input[0] == 'a' || input[0] == 'v') && input[1] == ' ')) {
1939 dprintf(gslots[CONF].fd[ERR], "No flag t|a|v found in input \"%s\"\n", input);
1940 weprintf("No flag t|a|v found in input\n");
1941 return;
1942 }
1943 if(input[0] == 'a' || input[0] == 'v') {
1944 dprintf(gslots[CONF].fd[ERR], "Conferences other than text not supported yet\n");
1945 weprintf("Conferences other than text not supported yet\n");
1946 return;
1947 }
1948 title = input + 2;
1949 n -= 2;
1950 cnum = tox_conference_new(tox, NULL);
1951 if (cnum == UINT32_MAX) {
1952 dprintf(gslots[CONF].fd[ERR], "Failed to create new conference\n");
1953 weprintf("Failed to create new conference\n");
1954 return;
1955 }
1956 if (!tox_conference_set_title(tox, cnum, (uint8_t *)title, n, NULL))
1957 weprintf("Failed to set conference title to \"%s\"", title);
1958 confcreate(cnum);
1959 }
1960
1961 static void
1962 loop(void)
1963 {
1964 struct file reqfifo, invfifo;
1965 struct friend *f, *ftmp;
1966 struct request *req, *rtmp;
1967 struct conference *c, *ctmp;
1968 struct invite *inv, *itmp;
1969 struct timeval tv;
1970 fd_set rfds;
1971 time_t t0, t1, c0, c1;
1972 size_t i;
1973 int connected = 0, n, r, fd, fdmax;
1974 char tstamp[64], ch;
1975 uint32_t frnum, cnum;
1976
1977 t0 = time(NULL);
1978 logmsg("DHT > Connecting\n");
1979 toxconnect();
1980 while (running) {
1981 /* Handle connection states */
1982 if (tox_self_get_connection_status(tox) != TOX_CONNECTION_NONE) {
1983 if (!connected) {
1984 logmsg("DHT > Connected\n");
1985 TAILQ_FOREACH(f, &friendhead, entry) {
1986 canceltxtransfer(f);
1987 cancelrxtransfer(f);
1988 }
1989 connected = 1;
1990 }
1991 } else {
1992 if (connected) {
1993 logmsg("DHT > Disconnected\n");
1994 connected = 0;
1995 }
1996 t1 = time(NULL);
1997 if (t1 > t0 + CONNECTDELAY) {
1998 t0 = time(NULL);
1999 logmsg("DHT > Connecting\n");
2000 toxconnect();
2001 }
2002 }
2003 tox_iterate(tox, NULL);
2004 toxav_iterate(toxav);
2005
2006 /* Prepare select-fd-set */
2007 FD_ZERO(&rfds);
2008 fdmax = -1;
2009
2010 for (i = 0; i < LEN(gslots); i++)
2011 FD_APPEND(gslots[i].fd[IN]);
2012
2013 TAILQ_FOREACH(req, &reqhead, entry)
2014 FD_APPEND(req->fd);
2015
2016 TAILQ_FOREACH(inv, &invhead, entry)
2017 FD_APPEND(inv->fd);
2018
2019 TAILQ_FOREACH(f, &friendhead, entry) {
2020 /* Only monitor friends that are online */
2021 if (tox_friend_get_connection_status(tox, f->num, NULL) != TOX_CONNECTION_NONE) {
2022 FD_APPEND(f->fd[FTEXT_IN]);
2023
2024 if (f->tx.state == TRANSFER_NONE)
2025 FD_APPEND(f->fd[FFILE_IN]);
2026 if (!f->av.state || (f->av.state & TRANSMITTING))
2027 FD_APPEND(f->fd[FCALL_IN]);
2028 }
2029 FD_APPEND(f->fd[FREMOVE]);
2030 }
2031
2032 TAILQ_FOREACH(c, &confhead, entry) {
2033 FD_APPEND(c->fd[CLEAVE]);
2034 FD_APPEND(c->fd[CTITLE_IN]);
2035 FD_APPEND(c->fd[CTEXT_IN]);
2036 FD_APPEND(c->fd[CINVITE]);
2037 }
2038
2039 tv.tv_sec = 0;
2040 tv.tv_usec = interval(tox, toxav) * 1000;
2041 n = select(fdmax + 1, &rfds, NULL, NULL, &tv);
2042 if (n < 0) {
2043 if (errno == EINTR)
2044 continue;
2045 eprintf("select:");
2046 }
2047
2048 /* Check for broken transfers (friend went offline, file_out was closed) */
2049 TAILQ_FOREACH(f, &friendhead, entry) {
2050 if (tox_friend_get_connection_status(tox, f->num, NULL) == TOX_CONNECTION_NONE) {
2051 canceltxtransfer(f);
2052 cancelrxtransfer(f);
2053 }
2054 if (f->rxstate != TRANSFER_INPROGRESS)
2055 continue;
2056 fd = fifoopen(f->dirfd, ffiles[FFILE_OUT]);
2057 if (fd < 0) {
2058 cancelrxtransfer(f);
2059 } else {
2060 close(fd);
2061 }
2062 }
2063
2064 /* Accept pending transfers if any */
2065 TAILQ_FOREACH(f, &friendhead, entry) {
2066 if (tox_friend_get_connection_status(tox, f->num, NULL) == 0)
2067 continue;
2068 if (f->rxstate == TRANSFER_NONE)
2069 continue;
2070 if (f->fd[FFILE_OUT] >= 0)
2071 continue;
2072 r = fifoopen(f->dirfd, ffiles[FFILE_OUT]);
2073 if (r < 0)
2074 continue;
2075 f->fd[FFILE_OUT] = r;
2076 if (!tox_file_control(tox, f->num, f->tx.fnum, TOX_FILE_CONTROL_RESUME, NULL)) {
2077 weprintf("Failed to accept transfer from receiver\n");
2078 cancelrxtransfer(f);
2079 } else {
2080 logmsg(": %s : Rx > Accepted\n", f->name);
2081 f->rxstate = TRANSFER_INPROGRESS;
2082 }
2083 }
2084
2085
2086 /* Answer pending calls */
2087 TAILQ_FOREACH(f, &friendhead, entry) {
2088 if (tox_friend_get_connection_status(tox, f->num, NULL) == TOX_CONNECTION_NONE)
2089 continue;
2090 if (!f->av.state)
2091 continue;
2092
2093 fd = fifoopen(f->dirfd, ffiles[FCALL_OUT]);
2094 if (fd < 0) {
2095 f->av.state &= ~INCOMING;
2096 } else {
2097 f->av.state |= INCOMING;
2098 if (f->fd[FCALL_OUT] >= 0)
2099 close(fd);
2100 else
2101 f->fd[FCALL_OUT] = fd;
2102 }
2103
2104 if (f->av.state == TRANSMITTING)
2105 cancelcall(f, "Hung up");
2106
2107 if (f->av.state & RINGING) {
2108 if (f->av.state & OUTGOING) {
2109 c1 = time(NULL);
2110 if (c1 > c0 + RINGINGDELAY)
2111 cancelcall(f, "Timeout");
2112 }
2113 if (!(f->av.state & INCOMING))
2114 continue;
2115 if (!toxav_answer(toxav, f->num, AUDIOBITRATE, 0, NULL)) {
2116 weprintf("Failed to answer call\n");
2117 if (!toxav_call_control(toxav, f->num, TOXAV_CALL_CONTROL_CANCEL, NULL))
2118 weprintf("Failed to reject call\n");
2119 break;
2120 }
2121 f->av.state &= ~RINGING;
2122 f->av.state |= TRANSMITTING;
2123 logmsg(": %s : Audio > Answered\n", f->name);
2124 ftruncate(f->fd[FCALL_STATE], 0);
2125 lseek(f->fd[FCALL_STATE], 0, SEEK_SET);
2126 dprintf(f->fd[FCALL_STATE], "transmitting\n");
2127 }
2128 }
2129
2130 if (n == 0)
2131 continue;
2132
2133 for (i = 0; i < LEN(gslots); i++) {
2134 if (FD_ISSET(gslots[i].fd[IN], &rfds) == 0)
2135 continue;
2136 (*gslots[i].cb)(NULL);
2137 }
2138
2139 for (req = TAILQ_FIRST(&reqhead); req; req = rtmp) {
2140 rtmp = TAILQ_NEXT(req, entry);
2141 if (FD_ISSET(req->fd, &rfds) == 0)
2142 continue;
2143 reqfifo.name = req->idstr;
2144 reqfifo.flags = O_RDONLY | O_NONBLOCK;
2145 if (fiforead(gslots[REQUEST].fd[OUT], &req->fd, reqfifo,
2146 &ch, 1) != 1)
2147 continue;
2148 if (ch != '0' && ch != '1')
2149 continue;
2150 frnum = tox_friend_add_norequest(tox, req->id, NULL);
2151 if (frnum == UINT32_MAX) {
2152 weprintf("Failed to add friend %s\n", req->idstr);
2153 fiforeset(gslots[REQUEST].fd[OUT], &req->fd, reqfifo);
2154 continue;
2155 }
2156 if (ch == '1') {
2157 friendcreate(frnum);
2158 logmsg("Request : %s > Accepted\n", req->idstr);
2159 datasave();
2160 } else {
2161 tox_friend_delete(tox, frnum, NULL);
2162 logmsg("Request : %s > Rejected\n", req->idstr);
2163 }
2164 unlinkat(gslots[REQUEST].fd[OUT], req->idstr, 0);
2165 close(req->fd);
2166 TAILQ_REMOVE(&reqhead, req, entry);
2167 free(req->msg);
2168 free(req);
2169 }
2170
2171 for (inv = TAILQ_FIRST(&invhead); inv; inv = itmp) {
2172 itmp = TAILQ_NEXT(inv, entry);
2173 if (FD_ISSET(inv->fd, &rfds) == 0)
2174 continue;
2175 invfifo.name = inv->fifoname;
2176 invfifo.flags = O_RDONLY | O_NONBLOCK;
2177 if (fiforead(gslots[CONF].fd[OUT], &inv->fd, invfifo,
2178 &ch, 1) != 1)
2179 continue;
2180 if (ch != '0' && ch != '1')
2181 continue;
2182 else if (ch == '1'){
2183 cnum = tox_conference_join(tox, inv->inviter, (uint8_t *)inv->cookie,
2184 inv->cookielen, NULL);
2185 if(cnum == UINT32_MAX)
2186 weprintf("Failed to join conference\n");
2187 else
2188 confcreate(cnum);
2189 }
2190 unlinkat(gslots[CONF].fd[OUT], inv->fifoname, 0);
2191 close(inv->fd);
2192 TAILQ_REMOVE(&invhead, inv, entry);
2193 free(inv->fifoname);
2194 free(inv->cookie);
2195 free(inv);
2196 }
2197
2198 for (c = TAILQ_FIRST(&confhead); c; c = ctmp) {
2199 ctmp = TAILQ_NEXT(c, entry);
2200 if (FD_ISSET(c->fd[CINVITE], &rfds))
2201 invitefriend(c);
2202 if (FD_ISSET(c->fd[CLEAVE], &rfds)) {
2203 logmsg("- %s > Leave\n", c->numstr);
2204 tox_conference_delete(tox, c->num, NULL);
2205 confdestroy(c);
2206 }
2207 if (FD_ISSET(c->fd[CTEXT_IN], &rfds))
2208 sendconftext(c);
2209 if (FD_ISSET(c->fd[CTITLE_IN], &rfds))
2210 updatetitle(c);
2211 }
2212
2213 for (f = TAILQ_FIRST(&friendhead); f; f = ftmp) {
2214 ftmp = TAILQ_NEXT(f, entry);
2215 if (FD_ISSET(f->fd[FTEXT_IN], &rfds))
2216 sendfriendtext(f);
2217 if (FD_ISSET(f->fd[FFILE_IN], &rfds) && f->tx.state == TRANSFER_NONE) {
2218 /* Prepare a new transfer */
2219 snprintf(tstamp, sizeof(tstamp), "%lu", (unsigned long)time(NULL));
2220 f->tx.fnum = tox_file_send(tox, f->num, TOX_FILE_KIND_DATA, UINT64_MAX,
2221 NULL, (uint8_t *)tstamp, strlen(tstamp), NULL);
2222 if (f->tx.fnum == UINT32_MAX) {
2223 weprintf("Failed to initiate new transfer\n");
2224 fiforeset(f->dirfd, &f->fd[FFILE_IN], ffiles[FFILE_IN]);
2225 } else {
2226 f->tx.state = TRANSFER_INITIATED;
2227 logmsg(": %s : Tx > Initiated\n", f->name);
2228 }
2229 }
2230 if (FD_ISSET(f->fd[FCALL_IN], &rfds)) {
2231 if (!f->av.state) {
2232 if (!toxav_call(toxav, f->num, AUDIOBITRATE, 0, NULL)) {
2233 weprintf("Failed to call\n");
2234 fiforeset(f->dirfd, &f->fd[FCALL_IN], ffiles[FCALL_IN]);
2235 break;
2236 }
2237
2238 f->av.state |= RINGING;
2239 logmsg(": %s : Audio > Tx Inviting\n", f->name);
2240 }
2241 if (!(f->av.state & OUTGOING)) {
2242 c0 = time(NULL);
2243 f->av.n = 0;
2244 f->av.lastsent.tv_sec = 0;
2245 f->av.lastsent.tv_nsec = 0;
2246
2247 f->av.frame = malloc(sizeof(int16_t) * framesize);
2248 if (!f->av.frame)
2249 eprintf("malloc:");
2250
2251 f->av.state |= OUTGOING;
2252 } else {
2253 if (f->av.state & TRANSMITTING)
2254 sendfriendcalldata(f);
2255 }
2256 }
2257 if (FD_ISSET(f->fd[FREMOVE], &rfds))
2258 removefriend(f);
2259 }
2260 }
2261 }
2262
2263 static void
2264 initshutdown(int sig)
2265 {
2266 running = 0;
2267 }
2268
2269 static void
2270 toxshutdown(void)
2271 {
2272 struct friend *f, *ftmp;
2273 struct request *r, *rtmp;
2274 struct conference *c, *ctmp;
2275 struct invite *i, *itmp;
2276 size_t s, m;
2277
2278 logmsg("Shutdown\n");
2279
2280 datasave();
2281
2282 /* Friends */
2283 for (f = TAILQ_FIRST(&friendhead); f; f = ftmp) {
2284 ftmp = TAILQ_NEXT(f, entry);
2285 frienddestroy(f);
2286 }
2287
2288 /* Conferences */
2289 for (c = TAILQ_FIRST(&confhead); c; c=ctmp) {
2290 ctmp = TAILQ_NEXT(c, entry);
2291 confdestroy(c);
2292 }
2293
2294 /* Requests */
2295 for (r = TAILQ_FIRST(&reqhead); r; r = rtmp) {
2296 rtmp = TAILQ_NEXT(r, entry);
2297
2298 if (gslots[REQUEST].fd[OUT] != -1) {
2299 unlinkat(gslots[REQUEST].fd[OUT], r->idstr, 0);
2300 if (r->fd != -1)
2301 close(r->fd);
2302 }
2303 TAILQ_REMOVE(&reqhead, r, entry);
2304 free(r->msg);
2305 free(r);
2306 }
2307
2308 /* Invites */
2309 for (i = TAILQ_FIRST(&invhead); i; i = itmp) {
2310 itmp = TAILQ_NEXT(i, entry);
2311
2312 if(gslots[CONF].fd[OUT] != -1) {
2313 unlinkat(gslots[CONF].fd[OUT], i->fifoname, 0);
2314 if (i->fd != -1)
2315 close(i->fd);
2316 }
2317 TAILQ_REMOVE(&invhead, i, entry);
2318 free(i->fifoname);
2319 free(i->cookie);
2320 free(i);
2321 }
2322
2323 /* Global files and slots */
2324 for (s = 0; s < LEN(gslots); s++) {
2325 for (m = 0; m < LEN(gfiles); m++) {
2326 if (gslots[s].dirfd != -1) {
2327 unlinkat(gslots[s].dirfd, gfiles[m].name,
2328 (gslots[s].outisfolder && m == OUT)
2329 ? AT_REMOVEDIR : 0);
2330 if (gslots[s].fd[m] != -1)
2331 close(gslots[s].fd[m]);
2332 }
2333 }
2334 rmdir(gslots[s].name);
2335 }
2336 unlink("id");
2337 if (idfd != -1)
2338 close(idfd);
2339
2340 toxav_kill(toxav);
2341 tox_kill(tox);
2342 }
2343
2344 static void
2345 usage(void)
2346 {
2347 eprintf("usage: %s [-4|-6] [-E|-e] [-T|-t] [-P|-p] [-q] [savefile]\n", argv0);
2348 }
2349
2350 int
2351 main(int argc, char *argv[])
2352 {
2353 ARGBEGIN {
2354 case '4':
2355 ipv6 = 0;
2356 break;
2357 case '6':
2358 ipv6 = 1;
2359 break;
2360 case 'E':
2361 encryptsavefile = 1;
2362 break;
2363 case 'e':
2364 encryptsavefile = 0;
2365 break;
2366 case 'T':
2367 tcp = 1;
2368 break;
2369 case 't':
2370 tcp = 0;
2371 break;
2372 case 'P':
2373 proxy = 1;
2374 break;
2375 case 'p':
2376 proxy = 0;
2377 break;
2378 case 'q':
2379 quiet = 1;
2380 break;
2381 default:
2382 usage();
2383 } ARGEND;
2384
2385 if (argc > 1)
2386 usage();
2387 if (argc == 1)
2388 savefile = *argv;
2389
2390 setbuf(stdout, NULL);
2391
2392 signal(SIGHUP, initshutdown);
2393 signal(SIGINT, initshutdown);
2394 signal(SIGQUIT, initshutdown);
2395 signal(SIGTERM, initshutdown);
2396 signal(SIGPIPE, SIG_IGN);
2397
2398 if (!quiet)
2399 printrat();
2400 toxinit();
2401 localinit();
2402 friendload();
2403 loop();
2404 toxshutdown();
2405 return 0;
2406 }