ratox.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 }