├── .gitignore ├── util.h ├── config.mk ├── nodegen ├── LICENSE ├── eprintf.c ├── config.def.h ├── Makefile ├── readpassphrase.h ├── arg.h ├── nodes.h ├── ratox.1 ├── readpassphrase.c ├── README ├── queue.h └── ratox.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | ratox 4 | config.h 5 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include "arg.h" 3 | 4 | #define LEN(x) (sizeof (x) / sizeof *(x)) 5 | 6 | extern char *argv0; 7 | 8 | void enprintf(int, const char *, ...); 9 | void eprintf(const char *, ...); 10 | void weprintf(const char *, ...); 11 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # ratox version 2 | VERSION = 0.4 3 | 4 | # paths 5 | PREFIX = /usr/local 6 | MANPREFIX = $(PREFIX)/share/man 7 | 8 | CC = cc 9 | LD = $(CC) 10 | CPPFLAGS = -DVERSION=\"${VERSION}\" 11 | CFLAGS = -I/usr/local/include -Wall -Wunused $(CPPFLAGS) -g 12 | LDFLAGS = -L/usr/local/lib64 -g 13 | LDLIBS = -ltoxcore -lsodium -lopus -lvpx -lm -lpthread 14 | -------------------------------------------------------------------------------- /nodegen: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo 'static struct node nodes[] = {' 4 | 5 | curl -s https://nodes.tox.chat/json | 6 | jq --tab '[.nodes[] | 7 | select(.last_ping > 0) | 8 | select(.tcp_ports | length > 0) | 9 | {".addr4": .ipv4, ".addr6": .ipv6, ".udp_port": .port, ".tcp_port": .tcp_ports[0], ".idstr": .public_key}]' | 10 | sed 's/"-"/NULL/; s/"\(\.[0-9a-z_]\+\)":/\1 =/; 1d; $d' 11 | 12 | echo '};' 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | © 2014 Laslo Hunhold 2 | © 2014-2017 Dimitris Papastamos 3 | © 2016-2017 z3bra 4 | © 2016-2020 pranomostro 5 | 6 | Permission to use, copy, modify, and distribute this software for any 7 | purpose with or without fee is hereby granted, provided that the above 8 | copyright notice and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | -------------------------------------------------------------------------------- /eprintf.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "util.h" 8 | 9 | char *argv0; 10 | 11 | static void venprintf(int, const char *, va_list); 12 | 13 | void 14 | enprintf(int status, const char *fmt, ...) 15 | { 16 | va_list ap; 17 | 18 | va_start(ap, fmt); 19 | venprintf(status, fmt, ap); 20 | va_end(ap); 21 | } 22 | 23 | void 24 | eprintf(const char *fmt, ...) 25 | { 26 | va_list ap; 27 | 28 | va_start(ap, fmt); 29 | venprintf(1, fmt, ap); 30 | va_end(ap); 31 | } 32 | 33 | void 34 | venprintf(int status, const char *fmt, va_list ap) 35 | { 36 | vfprintf(stderr, fmt, ap); 37 | 38 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 39 | fputc(' ', stderr); 40 | perror(NULL); 41 | } 42 | 43 | exit(status); 44 | } 45 | 46 | void 47 | weprintf(const char *fmt, ...) 48 | { 49 | va_list ap; 50 | 51 | va_start(ap, fmt); 52 | vfprintf(stderr, fmt, ap); 53 | va_end(ap); 54 | 55 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 56 | fputc(' ', stderr); 57 | perror(NULL); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /config.def.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | /* Connection delay in seconds */ 4 | #define CONNECTDELAY 3 5 | 6 | /* Ringing delay in seconds */ 7 | #define RINGINGDELAY 16 8 | 9 | /* Maximum number of simultaneous calls */ 10 | #define MAXCALLS 8 11 | 12 | /* Audio settings definition */ 13 | #define AUDIOCHANNELS 1 14 | #define AUDIOBITRATE 32 15 | #define AUDIOFRAME 20 16 | #define AUDIOSAMPLERATE 48000 17 | 18 | /* Video settings definition */ 19 | #define VIDEOWIDTH 1280 20 | #define VIDEOHEIGHT 720 21 | #define VIDEOBITRATE 2500 22 | 23 | static int friendmsg_log = 1; 24 | static int confmsg_log = 0; 25 | 26 | static char *savefile = ".ratox.tox"; 27 | static int encryptsavefile = 0; 28 | 29 | static int ipv6 = 0; 30 | static int tcp = 0; 31 | static int proxy = 0; 32 | static TOX_PROXY_TYPE proxytype = TOX_PROXY_TYPE_SOCKS5; /* NONE, HTTP, SOCKS5 */ 33 | static int quiet = 0; 34 | static char proxyaddr[] = "localhost"; 35 | static uint16_t proxyport = 8080; 36 | 37 | #include "nodes.h" 38 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include config.mk 2 | 3 | .POSIX: 4 | .SUFFIXES: .c .o 5 | 6 | HDR = \ 7 | arg.h \ 8 | config.h \ 9 | nodes.h \ 10 | readpassphrase.h \ 11 | util.h 12 | 13 | LIB = \ 14 | eprintf.o \ 15 | readpassphrase.o 16 | 17 | SRC = \ 18 | ratox.c 19 | 20 | OBJ = $(SRC:.c=.o) $(LIB) 21 | BIN = $(SRC:.c=) 22 | MAN = $(SRC:.c=.1) 23 | 24 | all: $(BIN) 25 | 26 | $(BIN): $(OBJ) util.a 27 | $(OBJ): $(HDR) config.mk 28 | 29 | config.h: 30 | @echo creating $@ from config.def.h 31 | @cp config.def.h $@ 32 | 33 | nodes.h: 34 | @echo creating $@ with nodegen 35 | @./nodegen >$@ 36 | 37 | .o: 38 | @echo LD $@ 39 | @$(LD) -o $@ $< util.a $(LDFLAGS) $(LDLIBS) 40 | 41 | .c.o: 42 | @echo CC $< 43 | @$(CC) -c -o $@ $< $(CFLAGS) 44 | 45 | util.a: $(LIB) 46 | @echo AR $@ 47 | @$(AR) -r -c $@ $(LIB) 48 | @ranlib $@ 49 | 50 | install: all 51 | @echo installing executable to $(DESTDIR)$(PREFIX)/bin 52 | @mkdir -p $(DESTDIR)$(PREFIX)/bin 53 | @cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin 54 | @chmod 755 $(DESTDIR)$(PREFIX)/bin/$(BIN) 55 | @echo installing manual page to $(DESTDIR)$(MANPREFIX)/man1 56 | @mkdir -p $(DESTDIR)$(MANPREFIX)/man1 57 | @cp -f ratox.1 $(DESTDIR)$(MANPREFIX)/man1 58 | 59 | uninstall: 60 | @echo removing executable from $(DESTDIR)$(PREFIX)/bin 61 | @rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN) 62 | @echo removing manual page from $(DESTDIR)$(MANPREFIX)/man1 63 | @rm $(DESTDIR)$(MANPREFIX)/man1/ratox.1 64 | 65 | clean: 66 | @echo cleaning 67 | @rm -f $(BIN) $(OBJ) $(LIB) util.a 68 | 69 | .PHONY: all binlib bin install uninstall clean 70 | -------------------------------------------------------------------------------- /readpassphrase.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2000, 2002 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | * 18 | * Sponsored in part by the Defense Advanced Research Projects 19 | * Agency (DARPA) and Air Force Research Laboratory, Air Force 20 | * Materiel Command, USAF, under agreement number F39502-99-1-0512. 21 | */ 22 | 23 | #define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */ 24 | #define RPP_ECHO_ON 0x01 /* Leave echo on. */ 25 | #define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */ 26 | #define RPP_FORCELOWER 0x04 /* Force input to lower case. */ 27 | #define RPP_FORCEUPPER 0x08 /* Force input to upper case. */ 28 | #define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ 29 | #define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ 30 | 31 | char *readpassphrase(const char *, char *, size_t, int); 32 | -------------------------------------------------------------------------------- /arg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC-License 3 | * 4 | * Copyright 2017 Laslo Hunhold 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #ifndef ARG_H 19 | #define ARG_H 20 | 21 | extern char *argv0; 22 | 23 | /* int main(int argc, char *argv[]) */ 24 | #define ARGBEGIN for (argv0 = *argv, *argv ? (argc--, argv++) : ((void *)0); \ 25 | *argv && (*argv)[0] == '-' && (*argv)[1]; argc--, argv++) { \ 26 | int i_, argused_; \ 27 | if ((*argv)[1] == '-' && !(*argv)[2]) { \ 28 | argc--, argv++; \ 29 | break; \ 30 | } \ 31 | for (i_ = 1, argused_ = 0; (*argv)[i_]; i_++) { \ 32 | switch((*argv)[i_]) 33 | #define ARGEND if (argused_) { \ 34 | if ((*argv)[i_ + 1]) { \ 35 | break; \ 36 | } else { \ 37 | argc--, argv++; \ 38 | break; \ 39 | } \ 40 | } \ 41 | } \ 42 | } 43 | #define ARGC() ((*argv)[i_]) 44 | #define ARGF_(x) (((*argv)[i_ + 1]) ? (argused_ = 1, &((*argv)[i_ + 1])) : \ 45 | (*(argv + 1)) ? (argused_ = 1, *(argv + 1)) : (x)) 46 | #define EARGF(x) ARGF_(((x), exit(1), (char *)0)) 47 | #define ARGF() ARGF_((char *)0) 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /nodes.h: -------------------------------------------------------------------------------- 1 | static struct node nodes[] = { 2 | { 3 | .addr4 = "85.172.30.117", 4 | .addr6 = NULL, 5 | .udp_port = 33445, 6 | .tcp_port = 33445, 7 | .idstr = "8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832" 8 | }, 9 | { 10 | .addr4 = "85.143.221.42", 11 | .addr6 = "2a04:ac00:1:9f00:5054:ff:fe01:becd", 12 | .udp_port = 33445, 13 | .tcp_port = 33445, 14 | .idstr = "DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43" 15 | }, 16 | { 17 | .addr4 = "78.46.73.141", 18 | .addr6 = "2a01:4f8:120:4091::3", 19 | .udp_port = 33445, 20 | .tcp_port = 3389, 21 | .idstr = "02807CF4F8BB8FB390CC3794BDF1E8449E9A8392C5D3F2200019DA9F1E812E46" 22 | }, 23 | { 24 | .addr4 = "tox.initramfs.io", 25 | .addr6 = "tox.initramfs.io", 26 | .udp_port = 33445, 27 | .tcp_port = 3389, 28 | .idstr = "3F0A45A268367C1BEA652F258C85F4A66DA76BCAA667A49E770BCC4917AB6A25" 29 | }, 30 | { 31 | .addr4 = "144.217.167.73", 32 | .addr6 = NULL, 33 | .udp_port = 33445, 34 | .tcp_port = 3389, 35 | .idstr = "7E5668E0EE09E19F320AD47902419331FFEE147BB3606769CFBE921A2A2FD34C" 36 | }, 37 | { 38 | .addr4 = "tox.abilinski.com", 39 | .addr6 = NULL, 40 | .udp_port = 33445, 41 | .tcp_port = 33445, 42 | .idstr = "10C00EB250C3233E343E2AEBA07115A5C28920E9C8D29492F6D00B29049EDC7E" 43 | }, 44 | { 45 | .addr4 = "tox.novg.net", 46 | .addr6 = NULL, 47 | .udp_port = 33445, 48 | .tcp_port = 33445, 49 | .idstr = "D527E5847F8330D628DAB1814F0A422F6DC9D0A300E6C357634EE2DA88C35463" 50 | }, 51 | { 52 | .addr4 = "95.31.18.227", 53 | .addr6 = NULL, 54 | .udp_port = 33445, 55 | .tcp_port = 33445, 56 | .idstr = "257744DBF57BE3E117FE05D145B5F806089428D4DCE4E3D0D50616AA16D9417E" 57 | }, 58 | { 59 | .addr4 = "198.199.98.108", 60 | .addr6 = "2604:a880:1:20::32f:1001", 61 | .udp_port = 33445, 62 | .tcp_port = 33445, 63 | .idstr = "BEF0CFB37AF874BD17B9A8F9FE64C75521DB95A37D33C5BDB00E9CF58659C04F" 64 | }, 65 | { 66 | .addr4 = "tox.kurnevsky.net", 67 | .addr6 = "tox.kurnevsky.net", 68 | .udp_port = 33445, 69 | .tcp_port = 33445, 70 | .idstr = "82EF82BA33445A1F91A7DB27189ECFC0C013E06E3DA71F588ED692BED625EC23" 71 | }, 72 | { 73 | .addr4 = "205.185.115.131", 74 | .addr6 = NULL, 75 | .udp_port = 53, 76 | .tcp_port = 53, 77 | .idstr = "3091C6BEB2A993F1C6300C16549FABA67098FF3D62C6D253828B531470B53D68" 78 | }, 79 | { 80 | .addr4 = "tox2.abilinski.com", 81 | .addr6 = "tox2.abilinski.com", 82 | .udp_port = 33445, 83 | .tcp_port = 33445, 84 | .idstr = "7A6098B590BDC73F9723FC59F82B3F9085A64D1B213AAF8E610FD351930D052D" 85 | }, 86 | { 87 | .addr4 = "51.158.146.76", 88 | .addr6 = "2001:bc8:6010:213:208:a2ff:fe0c:7fee", 89 | .udp_port = 33445, 90 | .tcp_port = 3389, 91 | .idstr = "E940D8FA9B07C1D13EA4ECF9F06B66F565F1CF61F094F60C67FDC8ADD3F4BA59" 92 | }, 93 | { 94 | .addr4 = "46.101.197.175", 95 | .addr6 = "2a03:b0c0:3:d0::ac:5001", 96 | .udp_port = 33445, 97 | .tcp_port = 3389, 98 | .idstr = "CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707" 99 | }, 100 | { 101 | .addr4 = "tox1.mf-net.eu", 102 | .addr6 = "tox1.mf-net.eu", 103 | .udp_port = 33445, 104 | .tcp_port = 3389, 105 | .idstr = "B3E5FA80DC8EBD1149AD2AB35ED8B85BD546DEDE261CA593234C619249419506" 106 | }, 107 | { 108 | .addr4 = "tox2.mf-net.eu", 109 | .addr6 = "tox2.mf-net.eu", 110 | .udp_port = 33445, 111 | .tcp_port = 33445, 112 | .idstr = "70EA214FDE161E7432530605213F18F7427DC773E276B3E317A07531F548545F" 113 | }, 114 | { 115 | .addr4 = "195.201.7.101", 116 | .addr6 = NULL, 117 | .udp_port = 33445, 118 | .tcp_port = 3389, 119 | .idstr = "B84E865125B4EC4C368CD047C72BCE447644A2DC31EF75BD2CDA345BFD310107" 120 | }, 121 | { 122 | .addr4 = "168.138.203.178", 123 | .addr6 = NULL, 124 | .udp_port = 33445, 125 | .tcp_port = 33445, 126 | .idstr = "6D04D8248E553F6F0BFDDB66FBFB03977E3EE54C432D416BC2444986EF02CC17" 127 | }, 128 | { 129 | .addr4 = "5.19.249.240", 130 | .addr6 = NULL, 131 | .udp_port = 38296, 132 | .tcp_port = 38296, 133 | .idstr = "DA98A4C0CD7473A133E115FEA2EBDAEEA2EF4F79FD69325FC070DA4DE4BA3238" 134 | } 135 | }; 136 | -------------------------------------------------------------------------------- /ratox.1: -------------------------------------------------------------------------------- 1 | .Dd December 20, 2019 2 | .Dt RATOX 1 3 | .Os 4 | .Sh NAME 5 | .Nm ratox 6 | .Nd FIFO based tox client 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl 4 | Fl 6 10 | .Op Fl E | Fl e 11 | .Op Fl T | Fl t 12 | .Op Fl P | Fl p 13 | .Op Fl r Ar pwd 14 | .Op Fl q 15 | .Op Ar savefile 16 | .Sh DESCRIPTION 17 | .Nm 18 | is a client implementation of the tox protocol providing only FIFOs, files 19 | and directories as interfaces. 20 | .Sh OPTIONS 21 | .Bl -tag -width Ds 22 | .It Fl 4 6 23 | Use IPv4/IPv6 only. 24 | .It Fl E e 25 | Enable/Disable save file encryption. 26 | .It Fl T t 27 | Enable/Disable TCP mode. Avoiding UDP in tox implies certain security 28 | considerations. 29 | .It Fl P p 30 | Enable/Disable TCP HTTP/SOCKS5 proxy as specified in \fIconfig.h\fR. 31 | .It Fl r Ar pwd 32 | Use 33 | .Ar pwd 34 | as the password for encrypting/decrypting the savefile. 35 | .It Fl q 36 | Enable quiet mode. 37 | .It Ar savefile 38 | Path of the file to load a profile from or create a new one in. 39 | .El 40 | .Sh CONFIGURATION 41 | .Nm 42 | is configured with \fIconfig.h\fR at compile-time. Apart from command line 43 | options and other parameters it contains the list of DHT-nodes. 44 | .Pp 45 | If there is a mismatch between save file status and encryption setting, 46 | .Nm 47 | writes the save file according to the latter. 48 | .Sh INTERFACE 49 | A \fIslot\fR is a set of FIFOs, files and directories interfacing a single 50 | parameter. The set of slots makes up the \fIinterface\fR. 51 | .Ss Global slots 52 | Global slots are directories containing an \fBin\fR FIFO, \fBout\fR and 53 | \fBerr\fR file or directory respectively. 54 | The slot parameter is set by piping data to \fBin\fR and accessed 55 | with \fBout\fR. Any errors are reported in \fBerr\fR. 56 | .Bl -tag -width 13n 57 | .It Ar name/ 58 | Name slot. 59 | .It Ar nospam/ 60 | Nospam slot (8 digit hexadecimal). 61 | .It Ar state/ 62 | State slot (\fBavailable\fR | \fBaway\fR | \fBbusy\fR). 63 | .It Ar status/ 64 | Status message slot. 65 | .It Ar request/ 66 | Request slot. Send a friend request by piping the Tox ID to \fBin\fR. Incoming 67 | requests are listed as FIFOs in \fBout/\fR. Echo \fB1\fR | \fB0\fR to 68 | accept | reject them. 69 | .It Ar conf/ 70 | Conference management slot. A conference is created by writing and flag 71 | and its title to \fBin\fR. The flag is \fBt\fR | \fBa\fR | \fBv\fR for an 72 | text, audio and video conference, followed by a space character. Only 73 | text conferences work at the moment. Invites to conferences are FIFOs 74 | in \fBout/\fR. Their name is id_cookie (the cookie is random data). They 75 | behave like request FIFOs. 76 | .El 77 | .Ss Friend slots 78 | Each friend is represented with a directory in the base-directory named after 79 | their Tox ID without its nospam-value. Each directory contains slots to 80 | interface with the friend. 81 | .Bl -tag -width 13n 82 | .It Ar call_in 83 | Initiate a call by piping data to this FIFO. 84 | .It Ar call_out 85 | Answer an incoming call by opening it for reading. 86 | .It Ar call_state 87 | Reports the call state (\fBnone\fR | \fBpending\fR | \fBactive\fR). 88 | The sample format is \fBmono signed 16-bit little 89 | endian at 48kHz\fR. 90 | The call is \fBterminated\fR if 91 | .Nm 92 | receives both an EPIPE trying to read from call_in 93 | and ENXIO trying to open call_out for writing. 94 | .It Ar file_in 95 | Initiate a file transfer by piping data to this FIFO. 96 | .It Ar file_out 97 | Accept an incoming file transfer by opening it for reading. 98 | .It Ar file_pending 99 | Contains the incoming filename if transfer is pending, empty otherwise. 100 | Given 101 | .Nm 102 | can't know how much data a given pipe is going to provide, it 103 | will send until the pipe is drained or EPIPE received. 104 | That's why it's possible to stream arbitrary data, including 105 | audio and video transmissions, even to other clients. 106 | .It Ar name 107 | Contains the friend's name. 108 | .It Ar online 109 | Contains the friend's online status (\fB1\fR | \fB0\fR). 110 | .It Ar remove 111 | Echo \fB1\fR to remove the friend. 112 | .It Ar state 113 | Contains the friend's state (\fBavailable\fR | \fBaway\fR | \fBbusy\fR) 114 | .It Ar status 115 | Contains the friend's status message. 116 | .It Ar text_in 117 | Send a text message by piping data to this FIFO. 118 | .It Ar text_out 119 | Contains text messages from the friend. 120 | .El 121 | .Ss Conference slots 122 | Each conference is represented with a directory in the directory named after the 123 | 8-digit conference number. The files in the conference directory are an interface 124 | for the respective conference. 125 | .Bl -tag -width 13n 126 | .It Ar members 127 | Contains a list of members of the conference. 128 | .It Ar invite 129 | Write the Tox ID of a friend to this FIFO to invite him to the conference. 130 | .It Ar leave 131 | Write to this file to leave the conference. 132 | .It Ar title_in 133 | Write here to change the title of the conference. 134 | .It Ar title_out 135 | Contains the title of the conference. 136 | .It Ar text_in 137 | Echo message to send a text message to the conference. 138 | .It Ar text_out 139 | Contains the messages send in the conference so far. 140 | .El 141 | .Ss Misc files 142 | .Bl -tag -width 13n 143 | .It Ar id 144 | Contains your Tox ID. 145 | .El 146 | .Sh AUTHORS 147 | .An Dimitris Papastamos Aq Mt sin@2f30.org , 148 | .An Laslo Hunhold Aq Mt dev@frign.de , 149 | .An z3bra Aq Mt contact@z3bra.org , 150 | .An pranomostro Aq Mt pranomostro@posteo.net . 151 | -------------------------------------------------------------------------------- /readpassphrase.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: readpassphrase.c,v 1.25 2015/09/14 10:45:27 guenther Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2000-2002, 2007, 2010 5 | * Todd C. Miller 6 | * 7 | * Permission to use, copy, modify, and distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | * 19 | * Sponsored in part by the Defense Advanced Research Projects 20 | * Agency (DARPA) and Air Force Research Laboratory, Air Force 21 | * Materiel Command, USAF, under agreement number F39502-99-1-0512. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "readpassphrase.h" 35 | 36 | #ifdef TCSASOFT 37 | #define _T_FLUSH (TCSAFLUSH|TCSASOFT) 38 | #else 39 | #define _T_FLUSH (TCSAFLUSH) 40 | #endif 41 | 42 | static volatile sig_atomic_t signo[_NSIG]; 43 | 44 | static void handler(int); 45 | 46 | char * 47 | readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) 48 | { 49 | ssize_t nr; 50 | int input, output, save_errno, i, need_restart; 51 | char ch, *p, *end; 52 | struct termios term, oterm; 53 | struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm; 54 | struct sigaction savetstp, savettin, savettou, savepipe; 55 | 56 | /* I suppose we could alloc on demand in this case (XXX). */ 57 | if (bufsiz == 0) { 58 | errno = EINVAL; 59 | return(NULL); 60 | } 61 | 62 | restart: 63 | for (i = 0; i < _NSIG; i++) 64 | signo[i] = 0; 65 | nr = -1; 66 | save_errno = 0; 67 | need_restart = 0; 68 | /* 69 | * Read and write to /dev/tty if available. If not, read from 70 | * stdin and write to stderr unless a tty is required. 71 | */ 72 | if ((flags & RPP_STDIN) || 73 | (input = output = open(_PATH_TTY, O_RDWR)) == -1) { 74 | if (flags & RPP_REQUIRE_TTY) { 75 | errno = ENOTTY; 76 | return(NULL); 77 | } 78 | input = STDIN_FILENO; 79 | output = STDERR_FILENO; 80 | } 81 | 82 | /* 83 | * Turn off echo if possible. 84 | * If we are using a tty but are not the foreground pgrp this will 85 | * generate SIGTTOU, so do it *before* installing the signal handlers. 86 | */ 87 | if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) { 88 | memcpy(&term, &oterm, sizeof(term)); 89 | if (!(flags & RPP_ECHO_ON)) 90 | term.c_lflag &= ~(ECHO | ECHONL); 91 | #ifdef VSTATUS 92 | if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) 93 | term.c_cc[VSTATUS] = _POSIX_VDISABLE; 94 | #endif 95 | (void)tcsetattr(input, _T_FLUSH, &term); 96 | } else { 97 | memset(&term, 0, sizeof(term)); 98 | term.c_lflag |= ECHO; 99 | memset(&oterm, 0, sizeof(oterm)); 100 | oterm.c_lflag |= ECHO; 101 | } 102 | 103 | /* 104 | * Catch signals that would otherwise cause the user to end 105 | * up with echo turned off in the shell. Don't worry about 106 | * things like SIGXCPU and SIGVTALRM for now. 107 | */ 108 | sigemptyset(&sa.sa_mask); 109 | sa.sa_flags = 0; /* don't restart system calls */ 110 | sa.sa_handler = handler; 111 | (void)sigaction(SIGALRM, &sa, &savealrm); 112 | (void)sigaction(SIGHUP, &sa, &savehup); 113 | (void)sigaction(SIGINT, &sa, &saveint); 114 | (void)sigaction(SIGPIPE, &sa, &savepipe); 115 | (void)sigaction(SIGQUIT, &sa, &savequit); 116 | (void)sigaction(SIGTERM, &sa, &saveterm); 117 | (void)sigaction(SIGTSTP, &sa, &savetstp); 118 | (void)sigaction(SIGTTIN, &sa, &savettin); 119 | (void)sigaction(SIGTTOU, &sa, &savettou); 120 | 121 | if (!(flags & RPP_STDIN)) 122 | (void)write(output, prompt, strlen(prompt)); 123 | end = buf + bufsiz - 1; 124 | p = buf; 125 | while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') { 126 | if (p < end) { 127 | if ((flags & RPP_SEVENBIT)) 128 | ch &= 0x7f; 129 | if (isalpha((unsigned char)ch)) { 130 | if ((flags & RPP_FORCELOWER)) 131 | ch = (char)tolower((unsigned char)ch); 132 | if ((flags & RPP_FORCEUPPER)) 133 | ch = (char)toupper((unsigned char)ch); 134 | } 135 | *p++ = ch; 136 | } 137 | } 138 | *p = '\0'; 139 | save_errno = errno; 140 | if (!(term.c_lflag & ECHO)) 141 | (void)write(output, "\n", 1); 142 | 143 | /* Restore old terminal settings and signals. */ 144 | if (memcmp(&term, &oterm, sizeof(term)) != 0) { 145 | while (tcsetattr(input, _T_FLUSH, &oterm) == -1 && 146 | errno == EINTR && !signo[SIGTTOU]) 147 | continue; 148 | } 149 | (void)sigaction(SIGALRM, &savealrm, NULL); 150 | (void)sigaction(SIGHUP, &savehup, NULL); 151 | (void)sigaction(SIGINT, &saveint, NULL); 152 | (void)sigaction(SIGQUIT, &savequit, NULL); 153 | (void)sigaction(SIGPIPE, &savepipe, NULL); 154 | (void)sigaction(SIGTERM, &saveterm, NULL); 155 | (void)sigaction(SIGTSTP, &savetstp, NULL); 156 | (void)sigaction(SIGTTIN, &savettin, NULL); 157 | (void)sigaction(SIGTTOU, &savettou, NULL); 158 | if (input != STDIN_FILENO) 159 | (void)close(input); 160 | 161 | /* 162 | * If we were interrupted by a signal, resend it to ourselves 163 | * now that we have restored the signal handlers. 164 | */ 165 | for (i = 0; i < _NSIG; i++) { 166 | if (signo[i]) { 167 | kill(getpid(), i); 168 | switch (i) { 169 | case SIGTSTP: 170 | case SIGTTIN: 171 | case SIGTTOU: 172 | need_restart = 1; 173 | } 174 | } 175 | } 176 | if (need_restart) 177 | goto restart; 178 | 179 | if (save_errno) 180 | errno = save_errno; 181 | return(nr == -1 ? NULL : buf); 182 | } 183 | 184 | static void handler(int s) 185 | { 186 | signo[s] = 1; 187 | } 188 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | __ 2 | /\ \__ 3 | _ __ __ \ \ ,_\ ___ __ _ 4 | /\`'__/'__`\\ \ \/ / __`\/\ \/'\ 5 | \ \ \/\ \L\.\\ \ \_/\ \L\ \/> call_in' to initiate a call 43 | | |-- call_out # 'aplay -r 48000 -c 1 -f S16_LE - < call_out' to answer a call 44 | | |-- call_state # (none, pending, active) 45 | | |-- file_in # 'cat foo > file_in' to send a file 46 | | |-- file_out # 'cat file_out > bar' to receive a file 47 | | |-- file_pending # contains filename if transfer pending, empty otherwise 48 | | |-- name # friend's nickname 49 | | |-- online # 1 if friend online, 0 otherwise 50 | | |-- remove # 'echo 1 > remove' to remove a friend 51 | | |-- state # friend's user state; could be any of {none,away,busy} 52 | | |-- status # friend's status message 53 | | |-- text_in # 'echo yo dude > text_in' to send a text to this friend 54 | | `-- text_out # 'tail -f text_out' to dump to stdout any text received 55 | | 56 | |-- 00000000 57 | | |-- members # list of people in the conference 58 | | |-- invite # 'echo 0A734CBA717CEB7883D.... >invite' to invite 59 | | |-- leave # 'echo 1 >leave' to leave the conference 60 | | |-- title_in # 'echo new-title >title_in' to update the conference title 61 | | |-- title_out # contains the current title 62 | | |-- text_in # 'echo blablahumbla >text_in' to message the other conference members 63 | | |-- text_out # contains the messages sent so far in the conference 64 | | 65 | |-- id # 'cat id' to show your own ID, you can give this to your friends 66 | | 67 | |-- conf # managing conferences 68 | | |-- err # conference related errors 69 | | |-- in # 'echo 't group title' >in' for creating a new text group 70 | | |-- out # 'echo 1 >out/ID_COOKIE' for joining a conference 71 | | 72 | |-- name # changing your nick 73 | | |-- err # nickname related errors 74 | | |-- in # 'echo my-new-nick > in' to change your name 75 | | `-- out # 'cat out' to show your name 76 | | 77 | |-- nospam # changing your nospam 78 | | |-- err # nospam related errors 79 | | |-- in # 'echo AABBCCDD > in' to change your nospam 80 | | `-- out # 'cat out' to show your nospam 81 | | 82 | |-- request # send and accept friend requests 83 | | |-- err # request related errors 84 | | |-- in # 'echo LONGASSID yo dude add me > in' to send a friend request 85 | | `-- out # 'echo 1 > out/LONGASSID' to accept the friend request 86 | | 87 | |-- state # changing your user state 88 | | |-- err # user status related errors 89 | | |-- in # 'echo away > in' to change your user state; could be any of {none,away,busy} 90 | | `-- out # 'cat out' to show your user state 91 | | 92 | `-- status # changing your status message 93 | |-- err # status message related errors 94 | |-- in # 'cat I am bored to death > in' to change your status message 95 | `-- out # 'cat out' to show your status message 96 | 97 | 98 | Features 99 | ======== 100 | 101 | 1 v 1 messaging: Yes 102 | File transfer: Yes 103 | Group chat: Yes 104 | Audio: Yes 105 | Video: No 106 | DNS discovery: No 107 | Chat logs: Yes 108 | Proxy support: Yes 109 | Offline message: Yes 110 | Offline transfers: Yes 111 | Contact aliases: No 112 | Contact blocking: No 113 | Save file encryption: Yes 114 | Multilingual: No 115 | Multiprofile: Yes 116 | Typing notification: No 117 | Audio notifications: No 118 | Emoticons: No 119 | Spell check: No 120 | Desktop sharing: No 121 | Inline images: No 122 | File resuming: No 123 | Read receipts: No 124 | Message splitting: Yes 125 | Changing nospam: Yes 126 | toxi URI: No 127 | 128 | NOTE: Some of these features are not intended to be developed 129 | in ratox itself but rather in external scripts[1] that are built upon 130 | ratox. 131 | 132 | Group chats do not have audio yet. 133 | 134 | 135 | Examples 136 | ======== 137 | 138 | SSH over TOX for the practical paranoid 139 | --------------------------------------- 140 | 141 | On the sender side (the client): 142 | 1) cd into the friend's directory (the server) 143 | 2) nc -lv 1234 > file_in < file_out 144 | 145 | On the receiver side (the server): 146 | 1) cd into the friend's directory (the client) 147 | 2) cat < file_out | nc localhost 22 > file_in 148 | 149 | Now on the client run the following: 150 | ssh -o ProxyCommand="nc %h 1234" user@localhost 151 | 152 | This can of course be more easily achieved by using 153 | [tuntox](https://github.com/gjedeer/tuntox). 154 | 155 | Screencasting using ffmpeg and mplayer 156 | -------------------------------------- 157 | 158 | On the sender side: 159 | ffmpeg -f x11grab -r 10 -s 1366x768 -i :0.0 -vcodec libx264 \ 160 | -pix_fmt yuv420p -preset fast -tune zerolatency -b:v 500k \ 161 | -f flv pipe: > file_in 162 | 163 | On the receiver side: 164 | mplayer -cache 1024 file_out 165 | 166 | You may have to play about with the cache size. 167 | 168 | 169 | Portability 170 | =========== 171 | 172 | Builds and works on *BSD and Linux. To build on OSX you need 173 | to apply a patch[2]. 174 | 175 | 176 | Contact 177 | ======= 178 | 179 | You can reach us through the freenode IRC network at #2f30 or 180 | through tech@lists.2f30.org. 181 | 182 | [0] https://tox.chat/ 183 | [1] https://git.2f30.org/ratox-nuggets/ 184 | [2] https://ratox.2f30.org/ratox-os_x.patch 185 | -------------------------------------------------------------------------------- /queue.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: queue.h,v 1.38 2013/07/03 15:05:21 fgsch Exp $ */ 2 | /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ 3 | 4 | /* 5 | * Copyright (c) 1991, 1993 6 | * The Regents of the University of California. All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. Neither the name of the University nor the names of its contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | * 32 | * @(#)queue.h 8.5 (Berkeley) 8/20/94 33 | */ 34 | 35 | #ifndef _SYS_QUEUE_H_ 36 | #define _SYS_QUEUE_H_ 37 | 38 | /* 39 | * This file defines five types of data structures: singly-linked lists, 40 | * lists, simple queues, tail queues, and circular queues. 41 | * 42 | * 43 | * A singly-linked list is headed by a single forward pointer. The elements 44 | * are singly linked for minimum space and pointer manipulation overhead at 45 | * the expense of O(n) removal for arbitrary elements. New elements can be 46 | * added to the list after an existing element or at the head of the list. 47 | * Elements being removed from the head of the list should use the explicit 48 | * macro for this purpose for optimum efficiency. A singly-linked list may 49 | * only be traversed in the forward direction. Singly-linked lists are ideal 50 | * for applications with large datasets and few or no removals or for 51 | * implementing a LIFO queue. 52 | * 53 | * A list is headed by a single forward pointer (or an array of forward 54 | * pointers for a hash table header). The elements are doubly linked 55 | * so that an arbitrary element can be removed without a need to 56 | * traverse the list. New elements can be added to the list before 57 | * or after an existing element or at the head of the list. A list 58 | * may only be traversed in the forward direction. 59 | * 60 | * A simple queue is headed by a pair of pointers, one the head of the 61 | * list and the other to the tail of the list. The elements are singly 62 | * linked to save space, so elements can only be removed from the 63 | * head of the list. New elements can be added to the list before or after 64 | * an existing element, at the head of the list, or at the end of the 65 | * list. A simple queue may only be traversed in the forward direction. 66 | * 67 | * A tail queue is headed by a pair of pointers, one to the head of the 68 | * list and the other to the tail of the list. The elements are doubly 69 | * linked so that an arbitrary element can be removed without a need to 70 | * traverse the list. New elements can be added to the list before or 71 | * after an existing element, at the head of the list, or at the end of 72 | * the list. A tail queue may be traversed in either direction. 73 | * 74 | * A circle queue is headed by a pair of pointers, one to the head of the 75 | * list and the other to the tail of the list. The elements are doubly 76 | * linked so that an arbitrary element can be removed without a need to 77 | * traverse the list. New elements can be added to the list before or after 78 | * an existing element, at the head of the list, or at the end of the list. 79 | * A circle queue may be traversed in either direction, but has a more 80 | * complex end of list detection. 81 | * 82 | * For details on the use of these macros, see the queue(3) manual page. 83 | */ 84 | 85 | #if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) 86 | #define _Q_INVALIDATE(a) (a) = ((void *)-1) 87 | #else 88 | #define _Q_INVALIDATE(a) 89 | #endif 90 | 91 | /* 92 | * Singly-linked List definitions. 93 | */ 94 | #define SLIST_HEAD(name, type) \ 95 | struct name { \ 96 | struct type *slh_first; /* first element */ \ 97 | } 98 | 99 | #define SLIST_HEAD_INITIALIZER(head) \ 100 | { NULL } 101 | 102 | #define SLIST_ENTRY(type) \ 103 | struct { \ 104 | struct type *sle_next; /* next element */ \ 105 | } 106 | 107 | /* 108 | * Singly-linked List access methods. 109 | */ 110 | #define SLIST_FIRST(head) ((head)->slh_first) 111 | #define SLIST_END(head) NULL 112 | #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) 113 | #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) 114 | 115 | #define SLIST_FOREACH(var, head, field) \ 116 | for((var) = SLIST_FIRST(head); \ 117 | (var) != SLIST_END(head); \ 118 | (var) = SLIST_NEXT(var, field)) 119 | 120 | #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ 121 | for ((var) = SLIST_FIRST(head); \ 122 | (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ 123 | (var) = (tvar)) 124 | 125 | /* 126 | * Singly-linked List functions. 127 | */ 128 | #define SLIST_INIT(head) { \ 129 | SLIST_FIRST(head) = SLIST_END(head); \ 130 | } 131 | 132 | #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ 133 | (elm)->field.sle_next = (slistelm)->field.sle_next; \ 134 | (slistelm)->field.sle_next = (elm); \ 135 | } while (0) 136 | 137 | #define SLIST_INSERT_HEAD(head, elm, field) do { \ 138 | (elm)->field.sle_next = (head)->slh_first; \ 139 | (head)->slh_first = (elm); \ 140 | } while (0) 141 | 142 | #define SLIST_REMOVE_AFTER(elm, field) do { \ 143 | (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ 144 | } while (0) 145 | 146 | #define SLIST_REMOVE_HEAD(head, field) do { \ 147 | (head)->slh_first = (head)->slh_first->field.sle_next; \ 148 | } while (0) 149 | 150 | #define SLIST_REMOVE(head, elm, type, field) do { \ 151 | if ((head)->slh_first == (elm)) { \ 152 | SLIST_REMOVE_HEAD((head), field); \ 153 | } else { \ 154 | struct type *curelm = (head)->slh_first; \ 155 | \ 156 | while (curelm->field.sle_next != (elm)) \ 157 | curelm = curelm->field.sle_next; \ 158 | curelm->field.sle_next = \ 159 | curelm->field.sle_next->field.sle_next; \ 160 | _Q_INVALIDATE((elm)->field.sle_next); \ 161 | } \ 162 | } while (0) 163 | 164 | /* 165 | * List definitions. 166 | */ 167 | #define LIST_HEAD(name, type) \ 168 | struct name { \ 169 | struct type *lh_first; /* first element */ \ 170 | } 171 | 172 | #define LIST_HEAD_INITIALIZER(head) \ 173 | { NULL } 174 | 175 | #define LIST_ENTRY(type) \ 176 | struct { \ 177 | struct type *le_next; /* next element */ \ 178 | struct type **le_prev; /* address of previous next element */ \ 179 | } 180 | 181 | /* 182 | * List access methods 183 | */ 184 | #define LIST_FIRST(head) ((head)->lh_first) 185 | #define LIST_END(head) NULL 186 | #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) 187 | #define LIST_NEXT(elm, field) ((elm)->field.le_next) 188 | 189 | #define LIST_FOREACH(var, head, field) \ 190 | for((var) = LIST_FIRST(head); \ 191 | (var)!= LIST_END(head); \ 192 | (var) = LIST_NEXT(var, field)) 193 | 194 | #define LIST_FOREACH_SAFE(var, head, field, tvar) \ 195 | for ((var) = LIST_FIRST(head); \ 196 | (var) && ((tvar) = LIST_NEXT(var, field), 1); \ 197 | (var) = (tvar)) 198 | 199 | /* 200 | * List functions. 201 | */ 202 | #define LIST_INIT(head) do { \ 203 | LIST_FIRST(head) = LIST_END(head); \ 204 | } while (0) 205 | 206 | #define LIST_INSERT_AFTER(listelm, elm, field) do { \ 207 | if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ 208 | (listelm)->field.le_next->field.le_prev = \ 209 | &(elm)->field.le_next; \ 210 | (listelm)->field.le_next = (elm); \ 211 | (elm)->field.le_prev = &(listelm)->field.le_next; \ 212 | } while (0) 213 | 214 | #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ 215 | (elm)->field.le_prev = (listelm)->field.le_prev; \ 216 | (elm)->field.le_next = (listelm); \ 217 | *(listelm)->field.le_prev = (elm); \ 218 | (listelm)->field.le_prev = &(elm)->field.le_next; \ 219 | } while (0) 220 | 221 | #define LIST_INSERT_HEAD(head, elm, field) do { \ 222 | if (((elm)->field.le_next = (head)->lh_first) != NULL) \ 223 | (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ 224 | (head)->lh_first = (elm); \ 225 | (elm)->field.le_prev = &(head)->lh_first; \ 226 | } while (0) 227 | 228 | #define LIST_REMOVE(elm, field) do { \ 229 | if ((elm)->field.le_next != NULL) \ 230 | (elm)->field.le_next->field.le_prev = \ 231 | (elm)->field.le_prev; \ 232 | *(elm)->field.le_prev = (elm)->field.le_next; \ 233 | _Q_INVALIDATE((elm)->field.le_prev); \ 234 | _Q_INVALIDATE((elm)->field.le_next); \ 235 | } while (0) 236 | 237 | #define LIST_REPLACE(elm, elm2, field) do { \ 238 | if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ 239 | (elm2)->field.le_next->field.le_prev = \ 240 | &(elm2)->field.le_next; \ 241 | (elm2)->field.le_prev = (elm)->field.le_prev; \ 242 | *(elm2)->field.le_prev = (elm2); \ 243 | _Q_INVALIDATE((elm)->field.le_prev); \ 244 | _Q_INVALIDATE((elm)->field.le_next); \ 245 | } while (0) 246 | 247 | /* 248 | * Simple queue definitions. 249 | */ 250 | #define SIMPLEQ_HEAD(name, type) \ 251 | struct name { \ 252 | struct type *sqh_first; /* first element */ \ 253 | struct type **sqh_last; /* addr of last next element */ \ 254 | } 255 | 256 | #define SIMPLEQ_HEAD_INITIALIZER(head) \ 257 | { NULL, &(head).sqh_first } 258 | 259 | #define SIMPLEQ_ENTRY(type) \ 260 | struct { \ 261 | struct type *sqe_next; /* next element */ \ 262 | } 263 | 264 | /* 265 | * Simple queue access methods. 266 | */ 267 | #define SIMPLEQ_FIRST(head) ((head)->sqh_first) 268 | #define SIMPLEQ_END(head) NULL 269 | #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) 270 | #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) 271 | 272 | #define SIMPLEQ_FOREACH(var, head, field) \ 273 | for((var) = SIMPLEQ_FIRST(head); \ 274 | (var) != SIMPLEQ_END(head); \ 275 | (var) = SIMPLEQ_NEXT(var, field)) 276 | 277 | #define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ 278 | for ((var) = SIMPLEQ_FIRST(head); \ 279 | (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ 280 | (var) = (tvar)) 281 | 282 | /* 283 | * Simple queue functions. 284 | */ 285 | #define SIMPLEQ_INIT(head) do { \ 286 | (head)->sqh_first = NULL; \ 287 | (head)->sqh_last = &(head)->sqh_first; \ 288 | } while (0) 289 | 290 | #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ 291 | if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ 292 | (head)->sqh_last = &(elm)->field.sqe_next; \ 293 | (head)->sqh_first = (elm); \ 294 | } while (0) 295 | 296 | #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ 297 | (elm)->field.sqe_next = NULL; \ 298 | *(head)->sqh_last = (elm); \ 299 | (head)->sqh_last = &(elm)->field.sqe_next; \ 300 | } while (0) 301 | 302 | #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ 303 | if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ 304 | (head)->sqh_last = &(elm)->field.sqe_next; \ 305 | (listelm)->field.sqe_next = (elm); \ 306 | } while (0) 307 | 308 | #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ 309 | if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ 310 | (head)->sqh_last = &(head)->sqh_first; \ 311 | } while (0) 312 | 313 | #define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ 314 | if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ 315 | == NULL) \ 316 | (head)->sqh_last = &(elm)->field.sqe_next; \ 317 | } while (0) 318 | 319 | /* 320 | * XOR Simple queue definitions. 321 | */ 322 | #define XSIMPLEQ_HEAD(name, type) \ 323 | struct name { \ 324 | struct type *sqx_first; /* first element */ \ 325 | struct type **sqx_last; /* addr of last next element */ \ 326 | unsigned long sqx_cookie; \ 327 | } 328 | 329 | #define XSIMPLEQ_ENTRY(type) \ 330 | struct { \ 331 | struct type *sqx_next; /* next element */ \ 332 | } 333 | 334 | /* 335 | * XOR Simple queue access methods. 336 | */ 337 | #define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ 338 | (unsigned long)(ptr))) 339 | #define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) 340 | #define XSIMPLEQ_END(head) NULL 341 | #define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) 342 | #define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) 343 | 344 | 345 | #define XSIMPLEQ_FOREACH(var, head, field) \ 346 | for ((var) = XSIMPLEQ_FIRST(head); \ 347 | (var) != XSIMPLEQ_END(head); \ 348 | (var) = XSIMPLEQ_NEXT(head, var, field)) 349 | 350 | #define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ 351 | for ((var) = XSIMPLEQ_FIRST(head); \ 352 | (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ 353 | (var) = (tvar)) 354 | 355 | /* 356 | * XOR Simple queue functions. 357 | */ 358 | #define XSIMPLEQ_INIT(head) do { \ 359 | arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ 360 | (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ 361 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ 362 | } while (0) 363 | 364 | #define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ 365 | if (((elm)->field.sqx_next = (head)->sqx_first) == \ 366 | XSIMPLEQ_XOR(head, NULL)) \ 367 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ 368 | (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ 369 | } while (0) 370 | 371 | #define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ 372 | (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ 373 | *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ 374 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ 375 | } while (0) 376 | 377 | #define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ 378 | if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ 379 | XSIMPLEQ_XOR(head, NULL)) \ 380 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ 381 | (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ 382 | } while (0) 383 | 384 | #define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ 385 | if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ 386 | (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ 387 | (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ 388 | } while (0) 389 | 390 | #define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ 391 | if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ 392 | (elm)->field.sqx_next)->field.sqx_next) \ 393 | == XSIMPLEQ_XOR(head, NULL)) \ 394 | (head)->sqx_last = \ 395 | XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ 396 | } while (0) 397 | 398 | 399 | /* 400 | * Tail queue definitions. 401 | */ 402 | #define TAILQ_HEAD(name, type) \ 403 | struct name { \ 404 | struct type *tqh_first; /* first element */ \ 405 | struct type **tqh_last; /* addr of last next element */ \ 406 | } 407 | 408 | #define TAILQ_HEAD_INITIALIZER(head) \ 409 | { NULL, &(head).tqh_first } 410 | 411 | #define TAILQ_ENTRY(type) \ 412 | struct { \ 413 | struct type *tqe_next; /* next element */ \ 414 | struct type **tqe_prev; /* address of previous next element */ \ 415 | } 416 | 417 | /* 418 | * tail queue access methods 419 | */ 420 | #define TAILQ_FIRST(head) ((head)->tqh_first) 421 | #define TAILQ_END(head) NULL 422 | #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) 423 | #define TAILQ_LAST(head, headname) \ 424 | (*(((struct headname *)((head)->tqh_last))->tqh_last)) 425 | /* XXX */ 426 | #define TAILQ_PREV(elm, headname, field) \ 427 | (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) 428 | #define TAILQ_EMPTY(head) \ 429 | (TAILQ_FIRST(head) == TAILQ_END(head)) 430 | 431 | #define TAILQ_FOREACH(var, head, field) \ 432 | for((var) = TAILQ_FIRST(head); \ 433 | (var) != TAILQ_END(head); \ 434 | (var) = TAILQ_NEXT(var, field)) 435 | 436 | #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ 437 | for ((var) = TAILQ_FIRST(head); \ 438 | (var) != TAILQ_END(head) && \ 439 | ((tvar) = TAILQ_NEXT(var, field), 1); \ 440 | (var) = (tvar)) 441 | 442 | 443 | #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ 444 | for((var) = TAILQ_LAST(head, headname); \ 445 | (var) != TAILQ_END(head); \ 446 | (var) = TAILQ_PREV(var, headname, field)) 447 | 448 | #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ 449 | for ((var) = TAILQ_LAST(head, headname); \ 450 | (var) != TAILQ_END(head) && \ 451 | ((tvar) = TAILQ_PREV(var, headname, field), 1); \ 452 | (var) = (tvar)) 453 | 454 | /* 455 | * Tail queue functions. 456 | */ 457 | #define TAILQ_INIT(head) do { \ 458 | (head)->tqh_first = NULL; \ 459 | (head)->tqh_last = &(head)->tqh_first; \ 460 | } while (0) 461 | 462 | #define TAILQ_INSERT_HEAD(head, elm, field) do { \ 463 | if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ 464 | (head)->tqh_first->field.tqe_prev = \ 465 | &(elm)->field.tqe_next; \ 466 | else \ 467 | (head)->tqh_last = &(elm)->field.tqe_next; \ 468 | (head)->tqh_first = (elm); \ 469 | (elm)->field.tqe_prev = &(head)->tqh_first; \ 470 | } while (0) 471 | 472 | #define TAILQ_INSERT_TAIL(head, elm, field) do { \ 473 | (elm)->field.tqe_next = NULL; \ 474 | (elm)->field.tqe_prev = (head)->tqh_last; \ 475 | *(head)->tqh_last = (elm); \ 476 | (head)->tqh_last = &(elm)->field.tqe_next; \ 477 | } while (0) 478 | 479 | #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ 480 | if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ 481 | (elm)->field.tqe_next->field.tqe_prev = \ 482 | &(elm)->field.tqe_next; \ 483 | else \ 484 | (head)->tqh_last = &(elm)->field.tqe_next; \ 485 | (listelm)->field.tqe_next = (elm); \ 486 | (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ 487 | } while (0) 488 | 489 | #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ 490 | (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ 491 | (elm)->field.tqe_next = (listelm); \ 492 | *(listelm)->field.tqe_prev = (elm); \ 493 | (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ 494 | } while (0) 495 | 496 | #define TAILQ_REMOVE(head, elm, field) do { \ 497 | if (((elm)->field.tqe_next) != NULL) \ 498 | (elm)->field.tqe_next->field.tqe_prev = \ 499 | (elm)->field.tqe_prev; \ 500 | else \ 501 | (head)->tqh_last = (elm)->field.tqe_prev; \ 502 | *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ 503 | _Q_INVALIDATE((elm)->field.tqe_prev); \ 504 | _Q_INVALIDATE((elm)->field.tqe_next); \ 505 | } while (0) 506 | 507 | #define TAILQ_REPLACE(head, elm, elm2, field) do { \ 508 | if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ 509 | (elm2)->field.tqe_next->field.tqe_prev = \ 510 | &(elm2)->field.tqe_next; \ 511 | else \ 512 | (head)->tqh_last = &(elm2)->field.tqe_next; \ 513 | (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ 514 | *(elm2)->field.tqe_prev = (elm2); \ 515 | _Q_INVALIDATE((elm)->field.tqe_prev); \ 516 | _Q_INVALIDATE((elm)->field.tqe_next); \ 517 | } while (0) 518 | 519 | /* 520 | * Circular queue definitions. 521 | */ 522 | #define CIRCLEQ_HEAD(name, type) \ 523 | struct name { \ 524 | struct type *cqh_first; /* first element */ \ 525 | struct type *cqh_last; /* last element */ \ 526 | } 527 | 528 | #define CIRCLEQ_HEAD_INITIALIZER(head) \ 529 | { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } 530 | 531 | #define CIRCLEQ_ENTRY(type) \ 532 | struct { \ 533 | struct type *cqe_next; /* next element */ \ 534 | struct type *cqe_prev; /* previous element */ \ 535 | } 536 | 537 | /* 538 | * Circular queue access methods 539 | */ 540 | #define CIRCLEQ_FIRST(head) ((head)->cqh_first) 541 | #define CIRCLEQ_LAST(head) ((head)->cqh_last) 542 | #define CIRCLEQ_END(head) ((void *)(head)) 543 | #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) 544 | #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) 545 | #define CIRCLEQ_EMPTY(head) \ 546 | (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) 547 | 548 | #define CIRCLEQ_FOREACH(var, head, field) \ 549 | for((var) = CIRCLEQ_FIRST(head); \ 550 | (var) != CIRCLEQ_END(head); \ 551 | (var) = CIRCLEQ_NEXT(var, field)) 552 | 553 | #define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \ 554 | for ((var) = CIRCLEQ_FIRST(head); \ 555 | (var) != CIRCLEQ_END(head) && \ 556 | ((tvar) = CIRCLEQ_NEXT(var, field), 1); \ 557 | (var) = (tvar)) 558 | 559 | #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ 560 | for((var) = CIRCLEQ_LAST(head); \ 561 | (var) != CIRCLEQ_END(head); \ 562 | (var) = CIRCLEQ_PREV(var, field)) 563 | 564 | #define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ 565 | for ((var) = CIRCLEQ_LAST(head, headname); \ 566 | (var) != CIRCLEQ_END(head) && \ 567 | ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \ 568 | (var) = (tvar)) 569 | 570 | /* 571 | * Circular queue functions. 572 | */ 573 | #define CIRCLEQ_INIT(head) do { \ 574 | (head)->cqh_first = CIRCLEQ_END(head); \ 575 | (head)->cqh_last = CIRCLEQ_END(head); \ 576 | } while (0) 577 | 578 | #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ 579 | (elm)->field.cqe_next = (listelm)->field.cqe_next; \ 580 | (elm)->field.cqe_prev = (listelm); \ 581 | if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ 582 | (head)->cqh_last = (elm); \ 583 | else \ 584 | (listelm)->field.cqe_next->field.cqe_prev = (elm); \ 585 | (listelm)->field.cqe_next = (elm); \ 586 | } while (0) 587 | 588 | #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ 589 | (elm)->field.cqe_next = (listelm); \ 590 | (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ 591 | if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ 592 | (head)->cqh_first = (elm); \ 593 | else \ 594 | (listelm)->field.cqe_prev->field.cqe_next = (elm); \ 595 | (listelm)->field.cqe_prev = (elm); \ 596 | } while (0) 597 | 598 | #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ 599 | (elm)->field.cqe_next = (head)->cqh_first; \ 600 | (elm)->field.cqe_prev = CIRCLEQ_END(head); \ 601 | if ((head)->cqh_last == CIRCLEQ_END(head)) \ 602 | (head)->cqh_last = (elm); \ 603 | else \ 604 | (head)->cqh_first->field.cqe_prev = (elm); \ 605 | (head)->cqh_first = (elm); \ 606 | } while (0) 607 | 608 | #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ 609 | (elm)->field.cqe_next = CIRCLEQ_END(head); \ 610 | (elm)->field.cqe_prev = (head)->cqh_last; \ 611 | if ((head)->cqh_first == CIRCLEQ_END(head)) \ 612 | (head)->cqh_first = (elm); \ 613 | else \ 614 | (head)->cqh_last->field.cqe_next = (elm); \ 615 | (head)->cqh_last = (elm); \ 616 | } while (0) 617 | 618 | #define CIRCLEQ_REMOVE(head, elm, field) do { \ 619 | if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ 620 | (head)->cqh_last = (elm)->field.cqe_prev; \ 621 | else \ 622 | (elm)->field.cqe_next->field.cqe_prev = \ 623 | (elm)->field.cqe_prev; \ 624 | if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ 625 | (head)->cqh_first = (elm)->field.cqe_next; \ 626 | else \ 627 | (elm)->field.cqe_prev->field.cqe_next = \ 628 | (elm)->field.cqe_next; \ 629 | _Q_INVALIDATE((elm)->field.cqe_prev); \ 630 | _Q_INVALIDATE((elm)->field.cqe_next); \ 631 | } while (0) 632 | 633 | #define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ 634 | if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ 635 | CIRCLEQ_END(head)) \ 636 | (head)->cqh_last = (elm2); \ 637 | else \ 638 | (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ 639 | if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ 640 | CIRCLEQ_END(head)) \ 641 | (head)->cqh_first = (elm2); \ 642 | else \ 643 | (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ 644 | _Q_INVALIDATE((elm)->field.cqe_prev); \ 645 | _Q_INVALIDATE((elm)->field.cqe_next); \ 646 | } while (0) 647 | 648 | #endif /* !_SYS_QUEUE_H_ */ 649 | -------------------------------------------------------------------------------- /ratox.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 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 cleanupcall(struct friend *); 231 | static void cancelcall(struct friend *, char *); 232 | static void sendfriendcalldata(struct friend *); 233 | static void writemembers(struct conference *); 234 | 235 | static void cbconnstatus(Tox *, uint32_t, TOX_CONNECTION, void *); 236 | static void cbfriendmessage(Tox *, uint32_t, TOX_MESSAGE_TYPE, const uint8_t *, size_t, void *); 237 | static void cbfriendrequest(Tox *, const uint8_t *, const uint8_t *, size_t, void *); 238 | static void cbnamechange(Tox *, uint32_t, const uint8_t *, size_t, void *); 239 | static void cbstatusmessage(Tox *, uint32_t, const uint8_t *, size_t, void *); 240 | static void cbfriendstate(Tox *, uint32_t, TOX_USER_STATUS, void *); 241 | static void cbfilecontrol(Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL, void *); 242 | static void cbfilesendreq(Tox *, uint32_t, uint32_t, uint32_t, uint64_t, const uint8_t *, size_t, void *); 243 | static void cbfiledata(Tox *, uint32_t, uint32_t, uint64_t, const uint8_t *, size_t, void *); 244 | 245 | static void cbconfinvite(Tox *, uint32_t, TOX_CONFERENCE_TYPE, const uint8_t *, size_t, void *); 246 | static void cbconfmessage(Tox *, uint32_t, uint32_t, TOX_MESSAGE_TYPE, const uint8_t *, size_t, void *); 247 | static void cbconftitle(Tox *, uint32_t, uint32_t, const uint8_t *, size_t, void *); 248 | static void cbconfmembers(Tox *, uint32_t, void *); 249 | 250 | static void canceltxtransfer(struct friend *); 251 | static void cancelrxtransfer(struct friend *); 252 | static void sendfriendtext(struct friend *); 253 | static void removefriend(struct friend *); 254 | static void invitefriend(struct conference *); 255 | static void sendconftext(struct conference *); 256 | static void updatetitle(struct conference *); 257 | static int readpass(const char *, uint8_t **, uint32_t *); 258 | static void getnewpass(void); 259 | static void dataload(struct Tox_Options *); 260 | static void datasave(void); 261 | static int localinit(void); 262 | static int toxinit(void); 263 | static int toxconnect(void); 264 | static void id2str(uint8_t *, char *); 265 | static void str2id(char *, uint8_t *); 266 | static void friendcreate(uint32_t); 267 | static void confcreate(uint32_t); 268 | static void friendload(void); 269 | static void frienddestroy(struct friend *); 270 | static void confdestroy(struct conference *); 271 | static void loop(void); 272 | static void initshutdown(int); 273 | static void toxshutdown(void); 274 | static void usage(void); 275 | 276 | #define FD_APPEND(fd) do { \ 277 | FD_SET((fd), &rfds); \ 278 | if ((fd) > fdmax) \ 279 | fdmax = (fd); \ 280 | } while (0) 281 | 282 | #undef MIN 283 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) 284 | 285 | static struct timespec 286 | timediff(struct timespec t1, struct timespec t2) 287 | { 288 | struct timespec tmp; 289 | 290 | tmp.tv_sec = t2.tv_sec - t1.tv_sec; 291 | 292 | if ((t2.tv_nsec - t1.tv_nsec) > 0) { 293 | tmp.tv_nsec = (t2.tv_nsec - t1.tv_nsec); 294 | } else { 295 | tmp.tv_nsec = 1E9 - (t1.tv_nsec - t2.tv_nsec); 296 | tmp.tv_sec--; 297 | } 298 | 299 | return tmp; 300 | } 301 | 302 | static void 303 | printrat(void) 304 | { 305 | printf( "\033[31m" 306 | " /y\\ /y\\\n" 307 | " /ver\\ /"VERSION"\\\n" 308 | " yyyyyy\\ /yyyyyy\n" 309 | " \\yyyyyyyyyyyyyyyyyy/\n" 310 | " yyyyyyyyyyyyyyyyyy\n" 311 | " yyyyyyyyyyyyyyyyyy\n" 312 | " yyy'yyyyyyyyyy'yyy\n" 313 | " \\yy yyyyyyyy yy/\n" 314 | " \\yy.yyyyyyyy.yy/\n" 315 | " \\yyyyyyyyyyyy/\n" 316 | " \\yyyyyyyy/\n" 317 | " -------yyyyyyyy-------\n" 318 | " ..---yyyyyy---..\n" 319 | " ..--yyyy--..\n" 320 | "\033[0m\n"); 321 | } 322 | 323 | static void 324 | logmsg(const char *fmt, ...) 325 | { 326 | time_t t; 327 | va_list ap; 328 | char buft[64]; 329 | 330 | va_start(ap, fmt); 331 | t = time(NULL); 332 | strftime(buft, sizeof(buft), "%F %R", localtime(&t)); 333 | printf("%s ", buft); 334 | vfprintf(stdout, fmt, ap); 335 | va_end(ap); 336 | } 337 | 338 | static int 339 | fifoopen(int dirfd, struct file f) 340 | { 341 | int fd; 342 | 343 | fd = openat(dirfd, f.name, f.flags, 0666); 344 | if (fd < 0 && errno != ENXIO) 345 | eprintf("openat %s:", f.name); 346 | return fd; 347 | } 348 | 349 | static void 350 | fiforeset(int dirfd, int *fd, struct file f) 351 | { 352 | ssize_t r; 353 | 354 | r = unlinkat(dirfd, f.name, 0); 355 | if (r < 0 && errno != ENOENT) 356 | eprintf("unlinkat %s:", f.name); 357 | if (*fd != -1) 358 | close(*fd); 359 | r = mkfifoat(dirfd, f.name, 0666); 360 | if (r < 0 && errno != EEXIST) 361 | eprintf("mkfifoat %s:", f.name); 362 | *fd = fifoopen(dirfd, f); 363 | } 364 | 365 | static ssize_t 366 | fiforead(int dirfd, int *fd, struct file f, void *buf, size_t sz) 367 | { 368 | ssize_t r; 369 | 370 | again: 371 | r = read(*fd, buf, sz); 372 | if (r == 0) { 373 | fiforeset(dirfd, fd, f); 374 | return 0; 375 | } else if (r < 0) { 376 | if (errno == EINTR) 377 | goto again; 378 | if (errno == EWOULDBLOCK) 379 | return -1; 380 | eprintf("read %s:", f.name); 381 | } 382 | return r; 383 | } 384 | 385 | static uint32_t 386 | interval(Tox *m, struct ToxAV *av) 387 | { 388 | return MIN(tox_iteration_interval(m), toxav_iteration_interval(av)); 389 | } 390 | 391 | static void 392 | cbcallinvite(ToxAV *av, uint32_t fnum, bool audio, bool video, void *udata) 393 | { 394 | struct friend *f; 395 | 396 | TAILQ_FOREACH(f, &friendhead, entry) 397 | if (f->num == fnum) 398 | break; 399 | if (!f) 400 | return; 401 | 402 | if (!audio) { 403 | if (!toxav_call_control(toxav, f->num, TOXAV_CALL_CONTROL_CANCEL, NULL)) 404 | weprintf("Failed to reject call\n"); 405 | logmsg(": %s : Audio > Rejected (no audio)\n", f->name); 406 | return; 407 | } 408 | 409 | f->av.state |= RINGING; 410 | ftruncate(f->fd[FCALL_STATE], 0); 411 | lseek(f->fd[FCALL_STATE], 0, SEEK_SET); 412 | dprintf(f->fd[FCALL_STATE], "pending\n"); 413 | 414 | logmsg(": %s : Audio > Ringing\n", f->name); 415 | } 416 | 417 | static void 418 | cbcallstate(ToxAV *av, uint32_t fnum, uint32_t state, void *udata) 419 | { 420 | struct friend *f; 421 | 422 | TAILQ_FOREACH(f, &friendhead, entry) 423 | if (f->num == fnum) 424 | break; 425 | if (!f) 426 | return; 427 | 428 | if ((state & TOXAV_FRIEND_CALL_STATE_ERROR) 429 | || (state & TOXAV_FRIEND_CALL_STATE_FINISHED)) { 430 | f->av.state &= ~TRANSMITTING; 431 | logmsg(": %s : Audio > Finished\n", f->name); 432 | cleanupcall(f); 433 | return; 434 | } 435 | 436 | /* 437 | * If we've are ringing a friend, and he sends a control that's 438 | * not FINISHED, it means he accepted the call, so we can start 439 | * transmitting audio frames 440 | */ 441 | if (f->av.state & RINGING) { 442 | f->av.state &= ~RINGING; 443 | f->av.state |= TRANSMITTING; 444 | logmsg(": %s : Audio > Transmitting\n", f->name); 445 | } 446 | } 447 | 448 | static void 449 | cbcalldata(ToxAV *av, uint32_t fnum, const int16_t *data, size_t len, 450 | uint8_t channels, uint32_t rate, void *udata) 451 | { 452 | struct friend *f; 453 | ssize_t n, wrote; 454 | int fd; 455 | uint8_t *buf; 456 | 457 | TAILQ_FOREACH(f, &friendhead, entry) 458 | if (f->num == fnum) 459 | break; 460 | if (!f) 461 | return; 462 | if (!(f->av.state & INCOMING)) { 463 | /* try to open call_out for writing */ 464 | fd = fifoopen(f->dirfd, ffiles[FCALL_OUT]); 465 | if (fd < 0) { 466 | close (fd); 467 | return; 468 | } 469 | if (f->fd[FCALL_OUT] < 0) { 470 | f->fd[FCALL_OUT] = fd; 471 | f->av.state |= INCOMING; 472 | } 473 | } 474 | 475 | buf = (uint8_t *)data; 476 | len *= 2; 477 | wrote = 0; 478 | while (len > 0) { 479 | n = write(f->fd[FCALL_OUT], &buf[wrote], len); 480 | if (n < 0) { 481 | if (errno == EPIPE) 482 | f->av.state &= ~INCOMING; 483 | break; 484 | } else if (n == 0) { 485 | break; 486 | } 487 | wrote += n; 488 | len -= n; 489 | } 490 | } 491 | 492 | static void 493 | cbconfinvite(Tox *m, uint32_t frnum, TOX_CONFERENCE_TYPE type, const uint8_t *cookie, size_t clen, void * udata) 494 | { 495 | size_t i, j, namelen; 496 | struct file invfifo; 497 | struct invite *inv; 498 | uint8_t id[TOX_PUBLIC_KEY_SIZE]; 499 | 500 | if(type != TOX_CONFERENCE_TYPE_TEXT) { 501 | weprintf("Only text conferences supported at the moment\n"); 502 | return; 503 | } 504 | 505 | if (!tox_friend_get_public_key(tox, frnum, id, NULL)) { 506 | weprintf("Failed to get key by friend %i for invite\n", frnum); 507 | return; 508 | } 509 | 510 | inv = calloc(1, sizeof(*inv)); 511 | if (!inv) 512 | eprintf("calloc:"); 513 | inv->fd = -1; 514 | 515 | inv->inviter = frnum; 516 | inv->cookielen = clen; 517 | inv->cookie = malloc(inv->cookielen); 518 | if (!inv->cookie) 519 | eprintf("malloc:"); 520 | 521 | memcpy(inv->cookie, cookie, clen); 522 | 523 | namelen = 2 * TOX_PUBLIC_KEY_SIZE + 1 + 2 * clen + 2; 524 | inv->fifoname = malloc(namelen); 525 | if (!inv->fifoname) 526 | eprintf("malloc:"); 527 | 528 | i = 0; 529 | id2str(id, inv->fifoname); 530 | i += 2 * TOX_PUBLIC_KEY_SIZE; 531 | inv->fifoname[i] = '_'; 532 | i++; 533 | for(j = 0; j < clen; i+=2, j++) 534 | sprintf(inv->fifoname + i, "%02X", cookie[j]); 535 | i++; 536 | inv->fifoname[i] = '\0'; 537 | 538 | invfifo.name = inv->fifoname; 539 | invfifo.flags = O_RDONLY | O_NONBLOCK; 540 | fiforeset(gslots[CONF].fd[OUT], &inv->fd, invfifo); 541 | 542 | TAILQ_INSERT_TAIL(&invhead, inv, entry); 543 | 544 | logmsg("Invite > %s\n", inv->fifoname); 545 | } 546 | 547 | static void 548 | cbconfmessage(Tox *m, uint32_t cnum, uint32_t pnum, TOX_MESSAGE_TYPE type, const uint8_t *data, size_t len, void *udata) 549 | { 550 | struct conference *c; 551 | time_t t; 552 | uint8_t msg[len + 1], namt[TOX_MAX_NAME_LENGTH + 1]; 553 | char buft[64]; 554 | 555 | memcpy(msg, data, len); 556 | msg[len] = '\0'; 557 | 558 | TAILQ_FOREACH(c, &confhead, entry) { 559 | if (c->num == cnum) { 560 | t = time(NULL); 561 | strftime(buft, sizeof(buft), "%F %R", localtime(&t)); 562 | if (!tox_conference_peer_get_name(tox, c->num, pnum, namt, NULL)) { 563 | weprintf("Unable to obtain name for peer %d in conference %s\n", pnum, c->numstr); 564 | return; 565 | } 566 | namt[tox_conference_peer_get_name_size(tox, c->num, pnum, NULL)] = '\0'; 567 | dprintf(c->fd[CTEXT_OUT], "%s <%s> %s\n", buft, namt, msg); 568 | if (confmsg_log) 569 | logmsg("%s : %s <%s> %s\n", c->numstr, buft, namt, msg); 570 | break; 571 | } 572 | } 573 | } 574 | 575 | static void 576 | cbconftitle(Tox *m, uint32_t cnum, uint32_t pnum, const uint8_t *data, size_t len, void * udata) 577 | { 578 | struct conference *c; 579 | char title[TOX_MAX_NAME_LENGTH + 1]; 580 | 581 | memcpy(title, data, len); 582 | title[len] = '\0'; 583 | 584 | TAILQ_FOREACH(c, &confhead, entry) { 585 | if (c->num == cnum) { 586 | ftruncate(c->fd[CTITLE_OUT], 0); 587 | lseek(c->fd[CTITLE_OUT], 0, SEEK_SET); 588 | dprintf(c->fd[CTITLE_OUT], "%s\n", title); 589 | logmsg(": %s : Title > %s\n", c->numstr, title); 590 | break; 591 | } 592 | } 593 | } 594 | 595 | static void 596 | cbconfmembers(Tox *m, uint32_t cnum, void *udata) 597 | { 598 | struct conference *c; 599 | 600 | TAILQ_FOREACH(c, &confhead, entry) { 601 | if (c->num == cnum) { 602 | writemembers(c); 603 | break; 604 | } 605 | } 606 | } 607 | 608 | static void 609 | cleanupcall(struct friend *f) 610 | { 611 | f->av.state = 0; 612 | 613 | /* Cancel Rx side of the call */ 614 | if (f->fd[FCALL_OUT] != -1) { 615 | close(f->fd[FCALL_OUT]); 616 | f->fd[FCALL_OUT] = -1; 617 | } 618 | ftruncate(f->fd[FCALL_STATE], 0); 619 | lseek(f->fd[FCALL_STATE], 0, SEEK_SET); 620 | dprintf(f->fd[FCALL_STATE], "none\n"); 621 | 622 | /* Cancel Tx side of the call */ 623 | free(f->av.frame); 624 | f->av.frame = NULL; 625 | fiforeset(f->dirfd, &f->fd[FCALL_IN], ffiles[FCALL_IN]); 626 | } 627 | 628 | static void 629 | cancelcall(struct friend *f, char *action) 630 | { 631 | logmsg(": %s : Audio > %s\n", f->name, action); 632 | 633 | if (f->av.state & TRANSMITTING || f->av.state & RINGING) { 634 | if (!toxav_call_control(toxav, f->num, TOXAV_CALL_CONTROL_CANCEL, NULL)) 635 | weprintf("Failed to terminate call\n"); 636 | } 637 | cleanupcall(f); 638 | } 639 | 640 | static void 641 | sendfriendcalldata(struct friend *f) 642 | { 643 | struct timespec now, diff; 644 | ssize_t n; 645 | TOXAV_ERR_SEND_FRAME err; 646 | 647 | n = fiforead(f->dirfd, &f->fd[FCALL_IN], ffiles[FCALL_IN], 648 | f->av.frame + (f->av.state & INCOMPLETE ? f->av.n : 0), 649 | framesize * sizeof(int16_t) - (f->av.state & INCOMPLETE ? f->av.n : 0)); 650 | if (n == 0) { 651 | f->av.state &= ~OUTGOING; 652 | f->av.state &= ~INCOMPLETE; 653 | return; 654 | } else if (n < 0 || f->av.state & RINGING) { 655 | /* discard data as long as the call is not established */ 656 | return; 657 | } else if (n == (framesize * sizeof(int16_t) - (f->av.state & INCOMPLETE ? f->av.n : 0))) { 658 | f->av.state &= ~INCOMPLETE; 659 | f->av.n = 0; 660 | } else { 661 | f->av.state |= INCOMPLETE; 662 | f->av.n += n; 663 | return; 664 | } 665 | 666 | clock_gettime(CLOCK_MONOTONIC, &now); 667 | diff = timediff(f->av.lastsent, now); 668 | if (diff.tv_sec == 0 && diff.tv_nsec < (AUDIOFRAME - 1) * 1E6) { 669 | diff.tv_nsec = (AUDIOFRAME - 1) * 1E6 - diff.tv_nsec; 670 | nanosleep(&diff, NULL); 671 | } 672 | clock_gettime(CLOCK_MONOTONIC, &f->av.lastsent); 673 | if (!toxav_audio_send_frame(toxav, f->num, (int16_t *)f->av.frame, 674 | framesize, AUDIOCHANNELS, AUDIOSAMPLERATE, &err)) 675 | weprintf("Failed to send audio frame: %s\n", callerr[err]); 676 | } 677 | 678 | static void 679 | writemembers(struct conference *c) 680 | { 681 | size_t i; 682 | uint32_t peers, pnum; 683 | uint8_t name[TOX_MAX_NAME_LENGTH + 1]; 684 | TOX_ERR_CONFERENCE_PEER_QUERY err; 685 | 686 | /*The peer list is written when we invite the members by the callback*/ 687 | ftruncate(c->fd[CMEMBERS], 0); 688 | peers = tox_conference_peer_count(tox, c->num, &err); 689 | 690 | if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) { 691 | weprintf("Unable to obtain peer count for conference %d\n", c->num); 692 | return; 693 | } 694 | for (pnum = 0; pnum < peers; pnum++) { 695 | if (!tox_conference_peer_get_name(tox, c->num, pnum, name, NULL)) { 696 | weprintf("Unable to obtain the name for peer %d\n", pnum); 697 | } else { 698 | i = tox_conference_peer_get_name_size(tox, c->num, pnum, NULL); 699 | name[i] = '\0'; 700 | dprintf(c->fd[CMEMBERS], "%s\n", name); 701 | } 702 | } 703 | } 704 | 705 | static void 706 | cbconnstatus(Tox *m, uint32_t frnum, TOX_CONNECTION status, void *udata) 707 | { 708 | struct friend *f; 709 | struct request *req, *rtmp; 710 | size_t r; 711 | char name[TOX_MAX_NAME_LENGTH + 1]; 712 | TOX_ERR_FRIEND_QUERY err; 713 | 714 | r = tox_friend_get_name_size(tox, frnum, &err); 715 | if (err != TOX_ERR_FRIEND_QUERY_OK) { 716 | weprintf("Failed to get name for friend number %ld\n", (long)frnum); 717 | return; 718 | } else if (r == 0) { 719 | snprintf(name, sizeof(name), "Anonymous"); 720 | } else { 721 | tox_friend_get_name(tox, frnum, (uint8_t *)name, NULL); 722 | name[r] = '\0'; 723 | } 724 | 725 | logmsg(": %s : Connection > %s\n", name, status == TOX_CONNECTION_NONE ? "Offline" : "Online"); 726 | 727 | TAILQ_FOREACH(f, &friendhead, entry) { 728 | if (f->num == frnum) { 729 | ftruncate(f->fd[FONLINE], 0); 730 | lseek(f->fd[FONLINE], 0, SEEK_SET); 731 | dprintf(f->fd[FONLINE], "%d\n", status); 732 | break; 733 | } 734 | } 735 | 736 | /* Remove the pending request-FIFO if it exists */ 737 | for (req = TAILQ_FIRST(&reqhead); req; req = rtmp) { 738 | rtmp = TAILQ_NEXT(req, entry); 739 | 740 | if (memcmp(f->id, req->id, TOX_PUBLIC_KEY_SIZE)) 741 | continue; 742 | unlinkat(gslots[REQUEST].fd[OUT], req->idstr, 0); 743 | close(req->fd); 744 | TAILQ_REMOVE(&reqhead, req, entry); 745 | free(req->msg); 746 | free(req); 747 | } 748 | } 749 | 750 | static void 751 | cbfriendmessage(Tox *m, uint32_t frnum, TOX_MESSAGE_TYPE type, const uint8_t *data, size_t len, void *udata) 752 | { 753 | struct friend *f; 754 | time_t t; 755 | uint8_t msg[len + 1]; 756 | char buft[64]; 757 | 758 | memcpy(msg, data, len); 759 | msg[len] = '\0'; 760 | 761 | TAILQ_FOREACH(f, &friendhead, entry) { 762 | if (f->num == frnum) { 763 | t = time(NULL); 764 | strftime(buft, sizeof(buft), "%F %R", localtime(&t)); 765 | dprintf(f->fd[FTEXT_OUT], "%s %s\n", buft, msg); 766 | if (friendmsg_log) 767 | logmsg(": %s > %s\n", f->name, msg); 768 | break; 769 | } 770 | } 771 | } 772 | 773 | static void 774 | cbfriendrequest(Tox *m, const uint8_t *id, const uint8_t *data, size_t len, void *udata) 775 | { 776 | struct file reqfifo; 777 | struct request *req; 778 | 779 | req = calloc(1, sizeof(*req)); 780 | if (!req) 781 | eprintf("calloc:"); 782 | req->fd = -1; 783 | 784 | memcpy(req->id, id, TOX_PUBLIC_KEY_SIZE); 785 | id2str(req->id, req->idstr); 786 | 787 | if (len > 0) { 788 | req->msg = malloc(len + 1); 789 | if (!req->msg) 790 | eprintf("malloc:"); 791 | memcpy(req->msg, data, len); 792 | req->msg[len] = '\0'; 793 | } else { 794 | req->msg = "ratox is awesome!"; 795 | } 796 | 797 | reqfifo.name = req->idstr; 798 | reqfifo.flags = O_RDONLY | O_NONBLOCK; 799 | fiforeset(gslots[REQUEST].fd[OUT], &req->fd, reqfifo); 800 | 801 | TAILQ_INSERT_TAIL(&reqhead, req, entry); 802 | 803 | logmsg("Request > %s : %s\n", 804 | req->idstr, req->msg); 805 | } 806 | 807 | static void 808 | cbnamechange(Tox *m, uint32_t frnum, const uint8_t *data, size_t len, void *user) 809 | { 810 | struct friend *f; 811 | uint8_t name[len + 1]; 812 | 813 | memcpy(name, data, len); 814 | name[len] = '\0'; 815 | 816 | TAILQ_FOREACH(f, &friendhead, entry) { 817 | if (f->num == frnum) { 818 | if (memcmp(f->name, name, len + 1) == 0) 819 | break; 820 | ftruncate(f->fd[FNAME], 0); 821 | lseek(f->fd[FNAME], 0, SEEK_SET); 822 | dprintf(f->fd[FNAME], "%s\n", name); 823 | logmsg(": %s : Name > %s\n", f->name, name); 824 | memcpy(f->name, name, len + 1); 825 | break; 826 | } 827 | } 828 | datasave(); 829 | } 830 | 831 | static void 832 | cbstatusmessage(Tox *m, uint32_t frnum, const uint8_t *data, size_t len, void *udata) 833 | { 834 | struct friend *f; 835 | uint8_t status[len + 1]; 836 | 837 | memcpy(status, data, len); 838 | status[len] = '\0'; 839 | 840 | TAILQ_FOREACH(f, &friendhead, entry) { 841 | if (f->num == frnum) { 842 | ftruncate(f->fd[FSTATUS], 0); 843 | lseek(f->fd[FSTATUS], 0, SEEK_SET); 844 | dprintf(f->fd[FSTATUS], "%s\n", status); 845 | logmsg(": %s : Status > %s\n", f->name, status); 846 | break; 847 | } 848 | } 849 | datasave(); 850 | } 851 | 852 | static void 853 | cbfriendstate(Tox *m, uint32_t frnum, TOX_USER_STATUS state, void *udata) 854 | { 855 | struct friend *f; 856 | 857 | if (state >= LEN(ustate)) { 858 | weprintf("Received invalid user status: %d\n", state); 859 | return; 860 | } 861 | 862 | TAILQ_FOREACH(f, &friendhead, entry) { 863 | if (f->num == frnum) { 864 | ftruncate(f->fd[FSTATE], 0); 865 | lseek(f->fd[FSTATE], 0, SEEK_SET); 866 | dprintf(f->fd[FSTATE], "%s\n", ustate[state]); 867 | logmsg(": %s : State > %s\n", f->name, ustate[state]); 868 | break; 869 | } 870 | } 871 | datasave(); 872 | } 873 | 874 | static void 875 | cbfilecontrol(Tox *m, uint32_t frnum, uint32_t fnum, TOX_FILE_CONTROL ctrltype, void *udata) 876 | { 877 | struct friend *f; 878 | 879 | TAILQ_FOREACH(f, &friendhead, entry) 880 | if (f->num == frnum) 881 | break; 882 | if (!f) 883 | return; 884 | 885 | switch (ctrltype) { 886 | case TOX_FILE_CONTROL_RESUME: 887 | if (f->tx.state == TRANSFER_PAUSED) { 888 | logmsg(": %s : Tx > Resumed\n", f->name); 889 | f->tx.state = TRANSFER_INPROGRESS; 890 | } else { 891 | f->tx.fnum = fnum; 892 | f->tx.buf = malloc(TOX_MAX_CUSTOM_PACKET_SIZE); 893 | if (!f->tx.buf) 894 | eprintf("malloc:"); 895 | f->tx.n = 0; 896 | f->tx.pendingbuf = 0; 897 | f->tx.state = TRANSFER_INPROGRESS; 898 | logmsg(": %s : Tx > In Progress\n", f->name); 899 | } 900 | break; 901 | case TOX_FILE_CONTROL_PAUSE: 902 | if (f->tx.state == TRANSFER_INPROGRESS) { 903 | logmsg(": %s : Tx > Paused\n", f->name); 904 | f->tx.state = TRANSFER_PAUSED; 905 | } 906 | break; 907 | case TOX_FILE_CONTROL_CANCEL: 908 | /* Check wether we're sending or receiving */ 909 | if (f->tx.fnum == fnum) { 910 | logmsg(": %s : Tx > Rejected\n", f->name); 911 | f->tx.state = TRANSFER_NONE; 912 | free(f->tx.buf); 913 | f->tx.buf = NULL; 914 | fiforeset(f->dirfd, &f->fd[FFILE_IN], ffiles[FFILE_IN]); 915 | } else { 916 | logmsg(": %s : Rx > Cancelled by Sender\n", f->name); 917 | cancelrxtransfer(f); 918 | } 919 | break; 920 | default: 921 | weprintf("Unhandled file control type: %d\n", ctrltype); 922 | break; 923 | }; 924 | } 925 | 926 | static void 927 | cbfiledatareq(Tox *m, uint32_t frnum, uint32_t fnum, uint64_t pos, size_t flen, void *udata) 928 | { 929 | struct friend *f; 930 | ssize_t n; 931 | 932 | TAILQ_FOREACH(f, &friendhead, entry) 933 | if (f->num == frnum) 934 | break; 935 | 936 | /* Grab another buffer from the FIFO */ 937 | if (!f->tx.pendingbuf) { 938 | n = fiforead(f->dirfd, &f->fd[FFILE_IN], ffiles[FFILE_IN], 939 | f->tx.buf, flen); 940 | f->tx.n = n; 941 | f->tx.pendingbuf = 0; 942 | } 943 | 944 | if (f->tx.n < 0) { 945 | if (errno != EWOULDBLOCK) 946 | weprintf("Reading data for file sending would block\n"); 947 | return; 948 | } 949 | 950 | if (!tox_file_send_chunk(tox, f->num, f->tx.fnum, pos, f->tx.buf, f->tx.n, NULL)) 951 | f->tx.pendingbuf = 1; 952 | 953 | /* 954 | * For streams, core will know that the transfer is finished 955 | * if a chunk with length less than the length requested in the 956 | * callback is sent. 957 | */ 958 | if (!f->tx.pendingbuf && (size_t)f->tx.n < flen) { 959 | logmsg(": %s : Tx > Complete\n", f->name); 960 | f->tx.state = TRANSFER_NONE; 961 | f->tx.fnum = -1; 962 | free(f->tx.buf); 963 | f->tx.buf = NULL; 964 | fiforeset(f->dirfd, &f->fd[FFILE_IN], ffiles[FFILE_IN]); 965 | return; 966 | } 967 | } 968 | 969 | static void 970 | cbfilesendreq(Tox *m, uint32_t frnum, uint32_t fnum, uint32_t kind, uint64_t fsz, 971 | const uint8_t *fname, size_t flen, void *udata) 972 | { 973 | struct friend *f; 974 | uint8_t filename[flen + 1]; 975 | 976 | TAILQ_FOREACH(f, &friendhead, entry) 977 | if (f->num == frnum) 978 | break; 979 | if (!f) 980 | return; 981 | 982 | memcpy(filename, fname, flen); 983 | filename[flen] = '\0'; 984 | 985 | if (kind == TOX_FILE_KIND_AVATAR) { 986 | if (!tox_file_control(tox, f->num, fnum, TOX_FILE_CONTROL_CANCEL, NULL)) 987 | weprintf("Failed to kill avatar transfer\n"); 988 | return; 989 | } 990 | 991 | /* We only support a single transfer at a time */ 992 | if (f->rxstate == TRANSFER_INPROGRESS) { 993 | logmsg(": %s : Rx > Rejected %s, already one in progress\n", 994 | f->name, filename); 995 | if (!tox_file_control(tox, f->num, f->tx.fnum, TOX_FILE_CONTROL_CANCEL, NULL)) 996 | weprintf("Failed to kill new Rx transfer\n"); 997 | return; 998 | } 999 | 1000 | f->tx.fnum = fnum; 1001 | 1002 | ftruncate(f->fd[FFILE_STATE], 0); 1003 | lseek(f->fd[FFILE_STATE], 0, SEEK_SET); 1004 | dprintf(f->fd[FFILE_STATE], "%s\n", filename); 1005 | f->rxstate = TRANSFER_PENDING; 1006 | logmsg(": %s : Rx > Pending %s\n", f->name, filename); 1007 | } 1008 | 1009 | static void 1010 | cbfiledata(Tox *m, uint32_t frnum, uint32_t fnum, uint64_t pos, 1011 | const uint8_t *data, size_t len, void *udata) 1012 | { 1013 | struct friend *f; 1014 | ssize_t n; 1015 | uint16_t wrote = 0; 1016 | 1017 | TAILQ_FOREACH(f, &friendhead, entry) 1018 | if (f->num == frnum) 1019 | break; 1020 | if (!f) 1021 | return; 1022 | 1023 | /* When length is 0, the transfer is finished */ 1024 | if (!len) { 1025 | logmsg(": %s : Rx > Complete\n", f->name); 1026 | if (f->fd[FFILE_OUT] != -1) { 1027 | close(f->fd[FFILE_OUT]); 1028 | f->fd[FFILE_OUT] = -1; 1029 | } 1030 | ftruncate(f->fd[FFILE_STATE], 0); 1031 | lseek(f->fd[FFILE_STATE], 0, SEEK_SET); 1032 | f->rxstate = TRANSFER_NONE; 1033 | return; 1034 | } 1035 | 1036 | while (len > 0) { 1037 | n = write(f->fd[FFILE_OUT], &data[wrote], len); 1038 | if (n < 0) { 1039 | if (errno == EPIPE) { 1040 | cancelrxtransfer(f); 1041 | break; 1042 | } else if (errno == EWOULDBLOCK) { 1043 | continue; 1044 | } 1045 | } 1046 | wrote += n; 1047 | len -= n; 1048 | } 1049 | } 1050 | 1051 | static void 1052 | canceltxtransfer(struct friend *f) 1053 | { 1054 | if (f->tx.state == TRANSFER_NONE) 1055 | return; 1056 | logmsg(": %s : Tx > Cancelling\n", f->name); 1057 | if (!tox_file_control(tox, f->num, f->tx.fnum, TOX_FILE_CONTROL_CANCEL, NULL)) 1058 | weprintf("Failed to kill Tx transfer\n"); 1059 | f->tx.fnum = -1; 1060 | f->tx.state = TRANSFER_NONE; 1061 | free(f->tx.buf); 1062 | f->tx.buf = NULL; 1063 | fiforeset(f->dirfd, &f->fd[FFILE_IN], ffiles[FFILE_IN]); 1064 | } 1065 | 1066 | static void 1067 | cancelrxtransfer(struct friend *f) 1068 | { 1069 | if (f->rxstate == TRANSFER_NONE) 1070 | return; 1071 | logmsg(": %s : Rx > Cancelling\n", f->name); 1072 | if (!tox_file_control(tox, f->num, f->tx.fnum, TOX_FILE_CONTROL_CANCEL, NULL)) 1073 | weprintf("Failed to kill Rx transfer\n"); 1074 | if (f->fd[FFILE_OUT] != -1) { 1075 | close(f->fd[FFILE_OUT]); 1076 | f->fd[FFILE_OUT] = -1; 1077 | } 1078 | ftruncate(f->fd[FFILE_STATE], 0); 1079 | lseek(f->fd[FFILE_STATE], 0, SEEK_SET); 1080 | f->rxstate = TRANSFER_NONE; 1081 | } 1082 | 1083 | static void 1084 | sendfriendtext(struct friend *f) 1085 | { 1086 | ssize_t n; 1087 | time_t t; 1088 | char buft[64]; 1089 | uint8_t buf[TOX_MAX_MESSAGE_LENGTH + 1]; 1090 | TOX_ERR_FRIEND_SEND_MESSAGE err; 1091 | 1092 | n = fiforead(f->dirfd, &f->fd[FTEXT_IN], ffiles[FTEXT_IN], buf, sizeof(buf) - 1); 1093 | if (n <= 0) 1094 | return; 1095 | if (buf[n - 1] == '\n' && n > 1) 1096 | n--; 1097 | tox_friend_send_message(tox, f->num, TOX_MESSAGE_TYPE_NORMAL, buf, n, &err); 1098 | if (err != TOX_ERR_FRIEND_SEND_MESSAGE_OK) 1099 | weprintf("Failed to send message\n"); 1100 | 1101 | buf[n]='\0'; 1102 | t = time(NULL); 1103 | strftime(buft, sizeof(buft), "%F %R", localtime(&t)); 1104 | dprintf(f->fd[FTEXT_OUT], "me %s %s\n", buft, buf); 1105 | } 1106 | 1107 | static void 1108 | removefriend(struct friend *f) 1109 | { 1110 | char c; 1111 | 1112 | if (fiforead(f->dirfd, &f->fd[FREMOVE], ffiles[FREMOVE], &c, 1) != 1 || c != '1') 1113 | return; 1114 | tox_friend_delete(tox, f->num, NULL); 1115 | datasave(); 1116 | logmsg(": %s > Removed\n", f->name); 1117 | frienddestroy(f); 1118 | } 1119 | 1120 | static void 1121 | invitefriend(struct conference *c) 1122 | { 1123 | ssize_t n; 1124 | char buf[2 * TOX_ADDRESS_SIZE + 1]; 1125 | struct friend *f; 1126 | 1127 | n = fiforead(c->dirfd, &c->fd[CINVITE], cfiles[CINVITE], buf, sizeof(buf)); 1128 | 1129 | if (n > sizeof(buf) || n <= 0) 1130 | return; 1131 | if (buf[n - 1] == '\n') 1132 | buf[n - 1] = '\0'; 1133 | 1134 | TAILQ_FOREACH(f, &friendhead, entry) 1135 | if (!memcmp(buf, f->idstr, sizeof(f->idstr)-1)) 1136 | break; 1137 | if (!f) { 1138 | weprintf("No friend with id %s found for %s\n", buf, c->numstr); 1139 | return; 1140 | } 1141 | if (tox_friend_get_connection_status(tox, f->num, NULL) == TOX_CONNECTION_NONE) { 1142 | weprintf("%s not online, can't be invited to %s\n", buf, c->numstr); 1143 | return; 1144 | } 1145 | if (!tox_conference_invite(tox, f->num, c->num, NULL)) 1146 | weprintf("Failed to invite %s\n", buf); 1147 | else 1148 | logmsg("- %s : Invited > %s\n", c->numstr, buf); 1149 | } 1150 | 1151 | static void 1152 | sendconftext(struct conference *c) 1153 | { 1154 | ssize_t n; 1155 | time_t t; 1156 | char buft[64]; 1157 | uint8_t buf[TOX_MAX_MESSAGE_LENGTH + 1], me[TOX_MAX_NAME_LENGTH + 1]; 1158 | 1159 | n = fiforead(c->dirfd, &c->fd[CTEXT_IN], cfiles[CTEXT_IN], buf, sizeof(buf) - 1); 1160 | if (n <= 0) 1161 | return; 1162 | if (buf[n - 1] == '\n' && n > 1) 1163 | n--; 1164 | if (!tox_conference_send_message(tox, c->num, TOX_MESSAGE_TYPE_NORMAL, 1165 | buf, n, NULL)) 1166 | weprintf("Failed to send message to %s\n", c->numstr); 1167 | 1168 | buf[n] = '\0'; 1169 | t = time(NULL); 1170 | strftime(buft, sizeof(buft), "%F %R", localtime(&t)); 1171 | tox_self_get_name(tox, me); 1172 | me[tox_self_get_name_size(tox)] = '\0'; 1173 | dprintf(c->fd[CTEXT_OUT], "%s <%s> %s\n", buft, me, buf); 1174 | } 1175 | 1176 | static void 1177 | updatetitle(struct conference *c) 1178 | { 1179 | ssize_t n; 1180 | uint8_t title[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; 1181 | 1182 | n = fiforead(c->dirfd, &c->fd[CTITLE_IN], cfiles[CTITLE_IN], title, sizeof(title) - 1); 1183 | if (n <= 0) 1184 | return; 1185 | if (title[n - 1] == '\n') 1186 | n--; 1187 | title[n] = '\0'; 1188 | if (!tox_conference_set_title(tox, c->num, title, n, NULL)) { 1189 | weprintf("Failed to set title for %s to \"%s\"\n", c->numstr, title); 1190 | return; 1191 | } 1192 | ftruncate(c->fd[CTITLE_OUT], 0); 1193 | lseek(c->fd[CTITLE_OUT], 0, SEEK_SET); 1194 | dprintf(c->fd[CTITLE_OUT], "%s\n", title); 1195 | logmsg("- %s : Title > %s\n", c->numstr, title); 1196 | } 1197 | 1198 | static int 1199 | readpass(const char *prompt, uint8_t **target, uint32_t *len) 1200 | { 1201 | char pass[BUFSIZ], *p; 1202 | 1203 | p = readpassphrase(prompt, pass, sizeof(pass), RPP_ECHO_OFF); 1204 | if (!p) { 1205 | weprintf("Could not read passphrase"); 1206 | return -1; 1207 | } 1208 | if (p[0] == '\0') 1209 | return -1; 1210 | *target = realloc(*target, strlen(p)); /* not null-terminated */ 1211 | if (!*target) 1212 | eprintf("realloc:"); 1213 | memcpy(*target, p, strlen(p)); 1214 | *len = strlen(p); 1215 | return 0; 1216 | } 1217 | 1218 | static void 1219 | getnewpass(void) 1220 | { 1221 | uint32_t pp2len = 0; 1222 | uint8_t *passphrase2 = NULL; 1223 | reprompt: 1224 | while (readpass("Data : New passphrase > ", &passphrase, &pplen) < 0); 1225 | while (readpass("Data : Re-enter passphrase > ", &passphrase2, &pp2len) < 0); 1226 | 1227 | if (pplen != pp2len || memcmp(passphrase, passphrase2, pplen)) { 1228 | weprintf("Data passphrase mismatch\n"); 1229 | goto reprompt; 1230 | } 1231 | free(passphrase2); 1232 | } 1233 | 1234 | static void 1235 | dataload(struct Tox_Options *toxopt) 1236 | { 1237 | off_t sz; 1238 | int fd, cmdpass = (passphrase != NULL); 1239 | uint8_t *data, * intermediate; 1240 | 1241 | fd = open(savefile, O_RDONLY); 1242 | if (fd < 0) { 1243 | if (encryptsavefile && !cmdpass) 1244 | getnewpass(); 1245 | return; 1246 | } 1247 | 1248 | sz = lseek(fd, 0, SEEK_END); 1249 | lseek(fd, 0, SEEK_SET); 1250 | 1251 | if (sz == 0) { 1252 | weprintf("Datafile %s is empty\n", savefile); 1253 | return; 1254 | } else if (sz < 0) { 1255 | weprintf("Datafile %s can't be seeked\n", savefile); 1256 | return; 1257 | } 1258 | 1259 | intermediate = malloc(sz); 1260 | if (!intermediate) 1261 | eprintf("malloc:"); 1262 | 1263 | if (read(fd, intermediate, sz) != sz) 1264 | eprintf("read %s:", savefile); 1265 | 1266 | if (tox_is_data_encrypted(intermediate)) { 1267 | toxopt->savedata_length = sz-TOX_PASS_ENCRYPTION_EXTRA_LENGTH; 1268 | data = malloc(toxopt->savedata_length); 1269 | if (!data) 1270 | eprintf("malloc:"); 1271 | if (!encryptsavefile) 1272 | logmsg("Data : %s > Encrypted, but saving unencrypted\n", savefile); 1273 | 1274 | while ((!cmdpass && readpass("Data : Passphrase > ", &passphrase, &pplen) < 0) || 1275 | !tox_pass_decrypt(intermediate, sz, passphrase, pplen, data, NULL)) { 1276 | if (cmdpass) { 1277 | weprintf("Datafile %s can't be decrypted\n", savefile); 1278 | return; 1279 | } 1280 | } 1281 | } else { 1282 | toxopt->savedata_length = sz; 1283 | data = malloc(sz); 1284 | if (!data) 1285 | eprintf("malloc:"); 1286 | memcpy(data, intermediate, sz); 1287 | if (encryptsavefile) { 1288 | logmsg("Data : %s > Not encrypted, but saving encrypted\n", savefile); 1289 | if(!cmdpass) 1290 | getnewpass(); 1291 | } 1292 | } 1293 | 1294 | toxopt->savedata_data = data; 1295 | toxopt->savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE; 1296 | 1297 | free(intermediate); 1298 | close(fd); 1299 | } 1300 | 1301 | static void 1302 | datasave(void) 1303 | { 1304 | off_t sz; 1305 | int fd; 1306 | uint8_t *data, *intermediate; 1307 | 1308 | fd = open(savefile, O_WRONLY | O_TRUNC | O_CREAT , 0666); 1309 | if (fd < 0) 1310 | eprintf("open %s:", savefile); 1311 | 1312 | sz = tox_get_savedata_size(tox); 1313 | intermediate = malloc(sz); 1314 | if (!intermediate) 1315 | eprintf("malloc:"); 1316 | 1317 | tox_get_savedata(tox, intermediate); 1318 | 1319 | sz += encryptsavefile ? TOX_PASS_ENCRYPTION_EXTRA_LENGTH : 0; 1320 | data = malloc(sz); 1321 | if (!data) 1322 | eprintf("malloc:"); 1323 | 1324 | if (encryptsavefile){ 1325 | tox_pass_encrypt(intermediate, sz - TOX_PASS_ENCRYPTION_EXTRA_LENGTH, passphrase, pplen, data, NULL); 1326 | } else { 1327 | memcpy(data, intermediate, sz); 1328 | } 1329 | if (write(fd, data, sz) != sz) 1330 | eprintf("write %s:", savefile); 1331 | fsync(fd); 1332 | 1333 | free(data); 1334 | free(intermediate); 1335 | close(fd); 1336 | } 1337 | 1338 | static int 1339 | localinit(void) 1340 | { 1341 | DIR *d; 1342 | size_t i, m; 1343 | int r; 1344 | uint8_t name[TOX_MAX_NAME_LENGTH + 1]; 1345 | uint8_t address[TOX_ADDRESS_SIZE]; 1346 | uint8_t status[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; 1347 | 1348 | for (i = 0; i < LEN(gslots); i++) { 1349 | r = mkdir(gslots[i].name, 0777); 1350 | if (r < 0 && errno != EEXIST) 1351 | eprintf("mkdir %s:", gslots[i].name); 1352 | d = opendir(gslots[i].name); 1353 | if (!d) 1354 | eprintf("opendir %s:", gslots[i].name); 1355 | r = dirfd(d); 1356 | if (r < 0) 1357 | eprintf("dirfd %s:", gslots[i].name); 1358 | gslots[i].dirfd = r; 1359 | 1360 | for (m = 0; m < LEN(gfiles); m++) { 1361 | if (gfiles[m].type == FIFO) { 1362 | fiforeset(gslots[i].dirfd, &gslots[i].fd[m], gfiles[m]); 1363 | } else if (gfiles[m].type == STATIC || (gfiles[m].type == NONE && !gslots[i].outisfolder)) { 1364 | gslots[i].fd[m] = fifoopen(gslots[i].dirfd, gfiles[m]); 1365 | } else if (gfiles[m].type == NONE && gslots[i].outisfolder) { 1366 | r = mkdirat(gslots[i].dirfd, gfiles[m].name, 0777); 1367 | if (r < 0 && errno != EEXIST) 1368 | eprintf("mkdirat %s:", gfiles[m].name); 1369 | r = openat(gslots[i].dirfd, gfiles[m].name, O_RDONLY | O_DIRECTORY); 1370 | if (r < 0) 1371 | eprintf("openat %s:", gfiles[m].name); 1372 | gslots[i].fd[m] = r; 1373 | } 1374 | } 1375 | } 1376 | 1377 | /* Dump current name */ 1378 | r = tox_self_get_name_size(tox); 1379 | if (r == 0) { 1380 | logmsg("Name > Empty\n"); 1381 | } 1382 | tox_self_get_name(tox, name); 1383 | name[r] = '\0'; 1384 | ftruncate(gslots[NAME].fd[OUT], 0); 1385 | dprintf(gslots[NAME].fd[OUT], "%s\n", name); 1386 | 1387 | /* Dump status */ 1388 | r = tox_self_get_status_message_size(tox); 1389 | if (r == 0) { 1390 | logmsg("Status > Empty\n"); 1391 | } 1392 | tox_self_get_status_message(tox, status); 1393 | status[r] = '\0'; 1394 | ftruncate(gslots[STATUS].fd[OUT], 0); 1395 | dprintf(gslots[STATUS].fd[OUT], "%s\n", status); 1396 | 1397 | /* Dump user state */ 1398 | r = tox_self_get_status(tox); 1399 | ftruncate(gslots[STATE].fd[OUT], 0); 1400 | dprintf(gslots[STATE].fd[OUT], "%s\n", ustate[r]); 1401 | 1402 | /* Dump ID */ 1403 | idfd = open("id", O_WRONLY | O_CREAT, 0666); 1404 | if (idfd < 0) 1405 | eprintf("open id:"); 1406 | tox_self_get_address(tox, address); 1407 | for (i = 0; i < TOX_ADDRESS_SIZE; i++) 1408 | dprintf(idfd, "%02X", address[i]); 1409 | dprintf(idfd, "\n"); 1410 | 1411 | /* Dump Nospam */ 1412 | ftruncate(gslots[NOSPAM].fd[OUT], 0); 1413 | dprintf(gslots[NOSPAM].fd[OUT], "%08X\n", tox_self_get_nospam(tox)); 1414 | 1415 | return 0; 1416 | } 1417 | 1418 | static int 1419 | toxinit(void) 1420 | { 1421 | struct Tox_Options toxopt; 1422 | 1423 | tox_options_default(&toxopt); 1424 | 1425 | toxopt.ipv6_enabled = ipv6; 1426 | toxopt.udp_enabled = !tcp; 1427 | if (proxy) { 1428 | tcp = 1; 1429 | toxopt.udp_enabled = !tcp; 1430 | logmsg("Net > Forcing TCP mode\n"); 1431 | toxopt.proxy_host = proxyaddr; 1432 | toxopt.proxy_port = proxyport; 1433 | toxopt.proxy_type = proxytype; 1434 | logmsg("Net > Using proxy %s:%hu\n", proxyaddr, proxyport); 1435 | } 1436 | 1437 | dataload(&toxopt); 1438 | 1439 | tox = tox_new(&toxopt, NULL); 1440 | if (!tox) 1441 | eprintf("Core : Tox > Initialization failed\n"); 1442 | 1443 | datasave(); 1444 | 1445 | toxav = toxav_new(tox, NULL); 1446 | if (!toxav) 1447 | eprintf("Core : ToxAV > Initialization failed\n"); 1448 | 1449 | framesize = (AUDIOSAMPLERATE * AUDIOFRAME * AUDIOCHANNELS) / 1000; 1450 | 1451 | tox_callback_friend_connection_status(tox, cbconnstatus); 1452 | tox_callback_friend_message(tox, cbfriendmessage); 1453 | tox_callback_friend_request(tox, cbfriendrequest); 1454 | tox_callback_friend_name(tox, cbnamechange); 1455 | tox_callback_friend_status_message(tox, cbstatusmessage); 1456 | tox_callback_friend_status(tox, cbfriendstate); 1457 | tox_callback_file_recv_control(tox, cbfilecontrol); 1458 | tox_callback_file_recv(tox, cbfilesendreq); 1459 | tox_callback_file_recv_chunk(tox, cbfiledata); 1460 | tox_callback_file_chunk_request(tox, cbfiledatareq); 1461 | 1462 | toxav_callback_call(toxav, cbcallinvite, NULL); 1463 | toxav_callback_call_state(toxav, cbcallstate, NULL); 1464 | 1465 | toxav_callback_audio_receive_frame(toxav, cbcalldata, NULL); 1466 | 1467 | tox_callback_conference_invite(tox, cbconfinvite); 1468 | tox_callback_conference_message(tox, cbconfmessage); 1469 | tox_callback_conference_title(tox, cbconftitle); 1470 | tox_callback_conference_peer_list_changed(tox, cbconfmembers); 1471 | 1472 | if (toxopt.savedata_data) 1473 | free((void *)toxopt.savedata_data); 1474 | 1475 | return 0; 1476 | } 1477 | 1478 | static int 1479 | toxconnect(void) 1480 | { 1481 | struct node *n; 1482 | struct node tmp; 1483 | size_t i, j; 1484 | bool r; 1485 | uint8_t id[TOX_ADDRESS_SIZE]; 1486 | 1487 | srand(time(NULL)); 1488 | 1489 | /* shuffle it to minimize load on nodes */ 1490 | for (i = LEN(nodes) - 1; i > 0; i--) { 1491 | j = rand() % LEN(nodes); 1492 | tmp = nodes[j]; 1493 | nodes[j] = nodes[i]; 1494 | nodes[i] = tmp; 1495 | } 1496 | 1497 | for (i = 0; i < LEN(nodes); i++) { 1498 | n = &nodes[i]; 1499 | if (ipv6 && !n->addr6) 1500 | continue; 1501 | str2id(n->idstr, id); 1502 | r = tox_bootstrap(tox, ipv6 ? n->addr6 : n->addr4, n->udp_port, id, NULL); 1503 | if (!r) 1504 | weprintf("Bootstrap failed for %s\n", ipv6 ? n->addr6 : n->addr4); 1505 | str2id(n->idstr, id); 1506 | r += tox_add_tcp_relay(tox, ipv6 ? n->addr6 : n->addr4, n->tcp_port, id, NULL); 1507 | if (!r) 1508 | weprintf("Adding a relay failed for %s\n", ipv6 ? n->addr6 : n->addr4); 1509 | } 1510 | return 0; 1511 | } 1512 | 1513 | /* Caller has to ensure `idstr' is big enough */ 1514 | static void 1515 | id2str(uint8_t *id, char *idstr) 1516 | { 1517 | int i; 1518 | char hex[] = "0123456789ABCDEF"; 1519 | 1520 | for (i = 0; i < TOX_PUBLIC_KEY_SIZE; i++) { 1521 | *idstr++ = hex[(id[i] >> 4) & 0xf]; 1522 | *idstr++ = hex[id[i] & 0xf]; 1523 | } 1524 | *idstr = '\0'; 1525 | } 1526 | 1527 | /* Caller has to ensure that `id' is big enough */ 1528 | static void 1529 | str2id(char *idstr, uint8_t *id) 1530 | { 1531 | size_t i, len = strlen(idstr) / 2; 1532 | char *p = idstr; 1533 | 1534 | for (i = 0; i < len; ++i, p += 2) 1535 | sscanf(p, "%2hhx", &id[i]); 1536 | } 1537 | 1538 | static void 1539 | friendcreate(uint32_t frnum) 1540 | { 1541 | struct friend *f; 1542 | DIR *d; 1543 | size_t i; 1544 | int r; 1545 | uint8_t status[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; 1546 | TOX_ERR_FRIEND_QUERY err; 1547 | 1548 | f = calloc(1, sizeof(*f)); 1549 | if (!f) 1550 | eprintf("calloc:"); 1551 | 1552 | i = tox_friend_get_name_size(tox, frnum, &err); 1553 | if (err != TOX_ERR_FRIEND_QUERY_OK) { 1554 | weprintf("Failed to get name for %ld\n", (long)frnum); 1555 | return; 1556 | } else if (i == 0) { 1557 | snprintf(f->name, sizeof(f->name), "Anonymous"); 1558 | } else { 1559 | tox_friend_get_name(tox, frnum, (uint8_t *)f->name, NULL); 1560 | f->name[i] = '\0'; 1561 | } 1562 | 1563 | f->num = frnum; 1564 | if (!tox_friend_get_public_key(tox, f->num, f->id, NULL)) { 1565 | weprintf("Failed to get key for %s\n", f->name); 1566 | return; 1567 | } 1568 | id2str(f->id, f->idstr); 1569 | 1570 | r = mkdir(f->idstr, 0777); 1571 | if (r < 0 && errno != EEXIST) 1572 | eprintf("mkdir %s:", f->idstr); 1573 | 1574 | d = opendir(f->idstr); 1575 | if (!d) 1576 | eprintf("opendir %s:", f->idstr); 1577 | 1578 | r = dirfd(d); 1579 | if (r < 0) 1580 | eprintf("dirfd %s:", f->idstr); 1581 | f->dirfd = r; 1582 | 1583 | for (i = 0; i < LEN(ffiles); i++) { 1584 | f->fd[i] = -1; 1585 | if (ffiles[i].type == FIFO) { 1586 | fiforeset(f->dirfd, &f->fd[i], ffiles[i]); 1587 | } else if (ffiles[i].type == STATIC) { 1588 | f->fd[i] = fifoopen(f->dirfd, ffiles[i]); 1589 | } 1590 | } 1591 | 1592 | /* Dump name */ 1593 | ftruncate(f->fd[FNAME], 0); 1594 | dprintf(f->fd[FNAME], "%s\n", f->name); 1595 | 1596 | /* Dump online state */ 1597 | ftruncate(f->fd[FONLINE], 0); 1598 | dprintf(f->fd[FONLINE], "%d\n", 1599 | tox_friend_get_connection_status(tox, frnum, NULL)); 1600 | 1601 | /* Dump status */ 1602 | i = tox_friend_get_status_message_size(tox, frnum, NULL); 1603 | if (i == SIZE_MAX) { 1604 | weprintf("Failed to get status for %s\n", f->name); 1605 | i = 0; 1606 | } 1607 | tox_friend_get_status_message(tox, frnum, status, NULL); 1608 | status[i] = '\0'; 1609 | ftruncate(f->fd[FSTATUS], 0); 1610 | dprintf(f->fd[FSTATUS], "%s\n", status); 1611 | 1612 | /* Dump user state */ 1613 | ftruncate(f->fd[FSTATE], 0); 1614 | dprintf(f->fd[FSTATE], "%s\n", ustate[tox_friend_get_status(tox, frnum, NULL)]); 1615 | 1616 | /* Dump file pending state */ 1617 | ftruncate(f->fd[FFILE_STATE], 0); 1618 | 1619 | /* Dump call pending state */ 1620 | ftruncate(f->fd[FCALL_STATE], 0); 1621 | dprintf(f->fd[FCALL_STATE], "none\n"); 1622 | 1623 | f->av.state = 0; 1624 | 1625 | TAILQ_INSERT_TAIL(&friendhead, f, entry); 1626 | } 1627 | 1628 | static void 1629 | confcreate(uint32_t cnum) 1630 | { 1631 | struct conference *c; 1632 | DIR *d; 1633 | size_t i; 1634 | int r; 1635 | uint8_t title[TOX_MAX_NAME_LENGTH + 1]; 1636 | TOX_ERR_CONFERENCE_TITLE err; 1637 | 1638 | c = calloc(1, sizeof(*c)); 1639 | if(!c) 1640 | eprintf("calloc:"); 1641 | c->num = cnum; 1642 | sprintf(c->numstr, "%08X", c->num); 1643 | r = mkdir(c->numstr, 0777); 1644 | if(r < 0 && errno != EEXIST) 1645 | eprintf("mkdir %s:", c->numstr); 1646 | 1647 | d = opendir(c->numstr); 1648 | if (!d) 1649 | eprintf("opendir %s:", c->numstr); 1650 | 1651 | r = dirfd(d); 1652 | if (r < 0) 1653 | eprintf("dirfd %s:", c->numstr); 1654 | c->dirfd = r; 1655 | 1656 | for (i = 0; i < LEN(cfiles); i++) { 1657 | c->fd[i] = -1; 1658 | if (cfiles[i].type == FIFO) { 1659 | fiforeset(c->dirfd, &c->fd[i], cfiles[i]); 1660 | } else if (cfiles[i].type == STATIC) { 1661 | c->fd[i] = fifoopen(c->dirfd, cfiles[i]); 1662 | } 1663 | } 1664 | 1665 | writemembers(c); 1666 | 1667 | /* No warning is printed here in the case of an error 1668 | * because this always fails when joining after an invite, 1669 | * but cbconftitle() is called in the next iteration afterwards, 1670 | * so it doesn't matter after all. 1671 | */ 1672 | 1673 | i = tox_conference_get_title_size(tox, c->num, &err); 1674 | if (err != TOX_ERR_CONFERENCE_TITLE_OK) 1675 | i = 0; 1676 | tox_conference_get_title(tox, c->num, title, NULL); 1677 | title[i] = '\0'; 1678 | ftruncate(c->fd[CTITLE_OUT], 0); 1679 | dprintf(c->fd[CTITLE_OUT], "%s\n", title); 1680 | 1681 | TAILQ_INSERT_TAIL(&confhead, c, entry); 1682 | 1683 | logmsg("- %s > Created\n", c->numstr); 1684 | } 1685 | 1686 | static void 1687 | frienddestroy(struct friend *f) 1688 | { 1689 | size_t i; 1690 | 1691 | canceltxtransfer(f); 1692 | cancelrxtransfer(f); 1693 | if (f->av.state > 0) 1694 | cancelcall(f, "Destroying"); 1695 | for (i = 0; i < LEN(ffiles); i++) { 1696 | if (f->dirfd != -1) { 1697 | unlinkat(f->dirfd, ffiles[i].name, 0); 1698 | if (f->fd[i] != -1) 1699 | close(f->fd[i]); 1700 | } 1701 | } 1702 | rmdir(f->idstr); 1703 | TAILQ_REMOVE(&friendhead, f, entry); 1704 | } 1705 | 1706 | static void 1707 | confdestroy(struct conference *c) 1708 | { 1709 | size_t i; 1710 | 1711 | for (i = 0; i dirfd != -1) { 1713 | unlinkat(c->dirfd, cfiles[i].name, 0); 1714 | if (c->fd[i] != -1) 1715 | close(c->fd[i]); 1716 | } 1717 | } 1718 | rmdir(c->numstr); 1719 | TAILQ_REMOVE(&confhead, c, entry); 1720 | } 1721 | 1722 | static void 1723 | friendload(void) 1724 | { 1725 | size_t sz; 1726 | uint32_t i; 1727 | uint32_t *frnums; 1728 | 1729 | sz = tox_self_get_friend_list_size(tox); 1730 | frnums = malloc(sz * sizeof(*frnums)); 1731 | if (!frnums) 1732 | eprintf("malloc:"); 1733 | 1734 | tox_self_get_friend_list(tox, frnums); 1735 | 1736 | for (i = 0; i < sz; i++) 1737 | friendcreate(frnums[i]); 1738 | 1739 | free(frnums); 1740 | } 1741 | 1742 | static void 1743 | setname(void *data) 1744 | { 1745 | ssize_t n; 1746 | int r; 1747 | char name[TOX_MAX_NAME_LENGTH + 1]; 1748 | 1749 | ftruncate(gslots[NAME].fd[ERR], 0); 1750 | lseek(gslots[NAME].fd[ERR], 0, SEEK_SET); 1751 | 1752 | n = fiforead(gslots[NAME].dirfd, &gslots[NAME].fd[IN], 1753 | gfiles[IN], name, sizeof(name) - 1); 1754 | if (n <= 0) 1755 | return; 1756 | if (name[n - 1] == '\n') 1757 | n--; 1758 | name[n] = '\0'; 1759 | r = tox_self_set_name(tox, (uint8_t *)name, n, NULL); 1760 | if (r < 0) { 1761 | dprintf(gslots[STATE].fd[ERR], "Failed to set name to \"%s\"\n", name); 1762 | weprintf("Failed to set name to \"%s\"\n", name); 1763 | return; 1764 | } 1765 | datasave(); 1766 | logmsg("Name > %s\n", name); 1767 | ftruncate(gslots[NAME].fd[OUT], 0); 1768 | lseek(gslots[NAME].fd[OUT], 0, SEEK_SET); 1769 | dprintf(gslots[NAME].fd[OUT], "%s\n", name); 1770 | } 1771 | 1772 | static void 1773 | setstatus(void *data) 1774 | { 1775 | ssize_t n; 1776 | int r; 1777 | uint8_t status[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; 1778 | 1779 | ftruncate(gslots[STATUS].fd[ERR], 0); 1780 | lseek(gslots[STATUS].fd[ERR], 0, SEEK_SET); 1781 | 1782 | n = fiforead(gslots[STATUS].dirfd, &gslots[STATUS].fd[IN], gfiles[IN], 1783 | status, sizeof(status) - 1); 1784 | if (n <= 0) 1785 | return; 1786 | if (status[n - 1] == '\n') 1787 | n--; 1788 | status[n] = '\0'; 1789 | r = tox_self_set_status_message(tox, status, n, NULL); 1790 | if (r < 0) { 1791 | dprintf(gslots[STATUS].fd[ERR], "Failed so set status message to \"%s\"\n", status); 1792 | weprintf("Failed to set status message to \"%s\"\n", status); 1793 | return; 1794 | } 1795 | datasave(); 1796 | logmsg("Status > %s\n", status); 1797 | ftruncate(gslots[STATUS].fd[OUT], 0); 1798 | lseek(gslots[STATUS].fd[OUT], 0, SEEK_SET); 1799 | dprintf(gslots[STATUS].fd[OUT], "%s\n", status); 1800 | } 1801 | 1802 | static void 1803 | setuserstate(void *data) 1804 | { 1805 | size_t i; 1806 | ssize_t n; 1807 | char buf[PIPE_BUF]; 1808 | 1809 | ftruncate(gslots[STATE].fd[ERR], 0); 1810 | lseek(gslots[STATE].fd[ERR], 0, SEEK_SET); 1811 | 1812 | n = fiforead(gslots[STATE].dirfd, &gslots[STATE].fd[IN], gfiles[IN], 1813 | buf, sizeof(buf) - 1); 1814 | if (n <= 0) 1815 | return; 1816 | if (buf[n - 1] == '\n') 1817 | n--; 1818 | buf[n] = '\0'; 1819 | for (i = 0; i < LEN(ustate); i++) { 1820 | if (strcmp(buf, ustate[i]) == 0) { 1821 | tox_self_set_status(tox, i); 1822 | break; 1823 | } 1824 | } 1825 | if (i == LEN(ustate)) { 1826 | dprintf(gslots[STATE].fd[ERR], "Invalid state %s\n", buf); 1827 | weprintf("Invalid state %s\n", buf); 1828 | return; 1829 | } 1830 | 1831 | ftruncate(gslots[STATE].fd[OUT], 0); 1832 | lseek(gslots[STATE].fd[OUT], 0, SEEK_SET); 1833 | dprintf(gslots[STATE].fd[OUT], "%s\n", buf); 1834 | datasave(); 1835 | logmsg("State > %s\n", buf); 1836 | } 1837 | 1838 | static void 1839 | sendfriendreq(void *data) 1840 | { 1841 | ssize_t n; 1842 | uint32_t r; 1843 | char buf[PIPE_BUF], *p; 1844 | char *msg = "ratox is awesome!"; 1845 | uint8_t id[TOX_ADDRESS_SIZE]; 1846 | TOX_ERR_FRIEND_ADD err; 1847 | 1848 | ftruncate(gslots[REQUEST].fd[ERR], 0); 1849 | lseek(gslots[REQUEST].fd[ERR], 0, SEEK_SET); 1850 | 1851 | n = fiforead(gslots[REQUEST].dirfd, &gslots[REQUEST].fd[IN], gfiles[IN], 1852 | buf, sizeof(buf) - 1); 1853 | if (n <= 0) 1854 | return; 1855 | buf[n] = '\0'; 1856 | 1857 | /* locate start of msg */ 1858 | for (p = buf; *p && !isspace(*p); p++) 1859 | ; 1860 | if (*p == '\0') 1861 | goto out; /* no msg */ 1862 | *p++ = '\0'; 1863 | if (*p == '\0') { 1864 | goto out; /* no msg */ 1865 | } else { 1866 | msg = p; 1867 | if (msg[strlen(msg) - 1] == '\n') 1868 | msg[strlen(msg) - 1] = '\0'; 1869 | } 1870 | out: 1871 | if (strlen(buf) != sizeof(id) * 2) { 1872 | dprintf(gslots[REQUEST].fd[ERR], "Invalid friend ID\n"); 1873 | weprintf("Invalid friend ID\n"); 1874 | return; 1875 | } 1876 | str2id(buf, id); 1877 | 1878 | r = tox_friend_add(tox, id, (uint8_t *)msg, strlen(msg), &err); 1879 | 1880 | if (err != TOX_ERR_FRIEND_ADD_OK) { 1881 | dprintf(gslots[REQUEST].fd[ERR], "%s\n", reqerr[err]); 1882 | weprintf("%s\n", reqerr[err]); 1883 | return; 1884 | } 1885 | friendcreate(r); 1886 | datasave(); 1887 | logmsg("Request > Sent\n"); 1888 | } 1889 | 1890 | static void 1891 | setnospam(void *data) 1892 | { 1893 | ssize_t n, i; 1894 | uint32_t nsval; 1895 | uint8_t nospam[2 * sizeof(uint32_t) + 1]; 1896 | uint8_t address[TOX_ADDRESS_SIZE]; 1897 | 1898 | ftruncate(gslots[NOSPAM].fd[ERR], 0); 1899 | lseek(gslots[NOSPAM].fd[ERR], 0, SEEK_SET); 1900 | 1901 | n = fiforead(gslots[NOSPAM].dirfd, &gslots[NOSPAM].fd[IN], gfiles[IN], 1902 | nospam, sizeof(nospam) - 1); 1903 | if (n <= 0) 1904 | return; 1905 | if (nospam[n - 1] == '\n') 1906 | n--; 1907 | nospam[n] = '\0'; 1908 | 1909 | for (i = 0; i < n; i++) { 1910 | if (nospam[i] < '0' || (nospam[i] > '9' && nospam[i] < 'A') || nospam[i] > 'F') { 1911 | dprintf(gslots[NOSPAM].fd[ERR], "Input contains invalid characters ![0-9, A-F]\n"); 1912 | weprintf("Input contains invalid characters ![0-9, A-F]\n"); 1913 | goto end; 1914 | } 1915 | } 1916 | 1917 | nsval = strtoul((char *)nospam, NULL, 16); 1918 | tox_self_set_nospam(tox, nsval); 1919 | datasave(); 1920 | logmsg("Nospam > %08X\n", nsval); 1921 | ftruncate(gslots[NOSPAM].fd[OUT], 0); 1922 | lseek(gslots[NOSPAM].fd[OUT], 0, SEEK_SET); 1923 | dprintf(gslots[NOSPAM].fd[OUT], "%08X\n", nsval); 1924 | 1925 | tox_self_get_address(tox, address); 1926 | ftruncate(idfd, 0); 1927 | lseek(idfd, 0, SEEK_SET); 1928 | for (i = 0; i < TOX_ADDRESS_SIZE; i++) 1929 | dprintf(idfd, "%02X", address[i]); 1930 | dprintf(idfd, "\n"); 1931 | end: 1932 | fiforeset(gslots[NOSPAM].dirfd, &gslots[NOSPAM].fd[IN], gfiles[IN]); 1933 | } 1934 | 1935 | static void 1936 | newconf(void *data) 1937 | { 1938 | uint32_t cnum; 1939 | size_t n; 1940 | char *title, input[TOX_MAX_NAME_LENGTH + 2 + 1]; 1941 | 1942 | ftruncate(gslots[CONF].fd[ERR], 0); 1943 | lseek(gslots[CONF].fd[ERR], 0, SEEK_SET); 1944 | 1945 | n = fiforead(gslots[CONF].dirfd, &gslots[CONF].fd[IN], gfiles[IN], 1946 | input, sizeof(input) - 1); 1947 | if (n <= 0) 1948 | return; 1949 | if (input[n - 1] == '\n') 1950 | n--; 1951 | input[n] = '\0'; 1952 | if(!((input[0] == 't' || input[0] == 'a' || input[0] == 'v') && input[1] == ' ')) { 1953 | dprintf(gslots[CONF].fd[ERR], "No flag t|a|v found in input \"%s\"\n", input); 1954 | weprintf("No flag t|a|v found in input\n"); 1955 | return; 1956 | } 1957 | if(input[0] == 'a' || input[0] == 'v') { 1958 | dprintf(gslots[CONF].fd[ERR], "Conferences other than text not supported yet\n"); 1959 | weprintf("Conferences other than text not supported yet\n"); 1960 | return; 1961 | } 1962 | title = input + 2; 1963 | n -= 2; 1964 | cnum = tox_conference_new(tox, NULL); 1965 | if (cnum == UINT32_MAX) { 1966 | dprintf(gslots[CONF].fd[ERR], "Failed to create new conference\n"); 1967 | weprintf("Failed to create new conference\n"); 1968 | return; 1969 | } 1970 | if (!tox_conference_set_title(tox, cnum, (uint8_t *)title, n, NULL)) 1971 | weprintf("Failed to set conference title to \"%s\"", title); 1972 | confcreate(cnum); 1973 | } 1974 | 1975 | static void 1976 | loop(void) 1977 | { 1978 | struct file reqfifo, invfifo; 1979 | struct friend *f, *ftmp; 1980 | struct request *req, *rtmp; 1981 | struct conference *c, *ctmp; 1982 | struct invite *inv, *itmp; 1983 | struct timeval tv; 1984 | fd_set rfds; 1985 | time_t t0, t1, c0, c1; 1986 | size_t i; 1987 | int connected = 0, n, r, fd, fdmax; 1988 | char tstamp[64], ch; 1989 | uint32_t frnum, cnum; 1990 | 1991 | t0 = time(NULL); 1992 | logmsg("DHT > Connecting\n"); 1993 | toxconnect(); 1994 | while (running) { 1995 | /* Handle connection states */ 1996 | if (tox_self_get_connection_status(tox) != TOX_CONNECTION_NONE) { 1997 | if (!connected) { 1998 | logmsg("DHT > Connected\n"); 1999 | TAILQ_FOREACH(f, &friendhead, entry) { 2000 | canceltxtransfer(f); 2001 | cancelrxtransfer(f); 2002 | } 2003 | connected = 1; 2004 | } 2005 | } else { 2006 | if (connected) { 2007 | logmsg("DHT > Disconnected\n"); 2008 | connected = 0; 2009 | } 2010 | t1 = time(NULL); 2011 | if (t1 > t0 + CONNECTDELAY) { 2012 | t0 = time(NULL); 2013 | logmsg("DHT > Connecting\n"); 2014 | toxconnect(); 2015 | } 2016 | } 2017 | tox_iterate(tox, NULL); 2018 | toxav_iterate(toxav); 2019 | 2020 | /* Prepare select-fd-set */ 2021 | FD_ZERO(&rfds); 2022 | fdmax = -1; 2023 | 2024 | for (i = 0; i < LEN(gslots); i++) 2025 | FD_APPEND(gslots[i].fd[IN]); 2026 | 2027 | TAILQ_FOREACH(req, &reqhead, entry) 2028 | FD_APPEND(req->fd); 2029 | 2030 | TAILQ_FOREACH(inv, &invhead, entry) 2031 | FD_APPEND(inv->fd); 2032 | 2033 | TAILQ_FOREACH(f, &friendhead, entry) { 2034 | /* Only monitor friends that are online */ 2035 | if (tox_friend_get_connection_status(tox, f->num, NULL) != TOX_CONNECTION_NONE) { 2036 | FD_APPEND(f->fd[FTEXT_IN]); 2037 | 2038 | if (f->tx.state == TRANSFER_NONE) 2039 | FD_APPEND(f->fd[FFILE_IN]); 2040 | if (!f->av.state || (f->av.state & TRANSMITTING)) 2041 | FD_APPEND(f->fd[FCALL_IN]); 2042 | } 2043 | FD_APPEND(f->fd[FREMOVE]); 2044 | } 2045 | 2046 | TAILQ_FOREACH(c, &confhead, entry) { 2047 | FD_APPEND(c->fd[CLEAVE]); 2048 | FD_APPEND(c->fd[CTITLE_IN]); 2049 | FD_APPEND(c->fd[CTEXT_IN]); 2050 | FD_APPEND(c->fd[CINVITE]); 2051 | } 2052 | 2053 | tv.tv_sec = 0; 2054 | tv.tv_usec = interval(tox, toxav) * 1000; 2055 | n = select(fdmax + 1, &rfds, NULL, NULL, &tv); 2056 | if (n < 0) { 2057 | if (errno == EINTR) 2058 | continue; 2059 | eprintf("select:"); 2060 | } 2061 | 2062 | /* Check for broken transfers (friend went offline, file_out was closed) */ 2063 | TAILQ_FOREACH(f, &friendhead, entry) { 2064 | if (tox_friend_get_connection_status(tox, f->num, NULL) == TOX_CONNECTION_NONE) { 2065 | canceltxtransfer(f); 2066 | cancelrxtransfer(f); 2067 | } 2068 | if (f->rxstate != TRANSFER_INPROGRESS) 2069 | continue; 2070 | fd = fifoopen(f->dirfd, ffiles[FFILE_OUT]); 2071 | if (fd < 0) { 2072 | cancelrxtransfer(f); 2073 | } else { 2074 | close(fd); 2075 | } 2076 | } 2077 | 2078 | /* Accept pending transfers if any */ 2079 | TAILQ_FOREACH(f, &friendhead, entry) { 2080 | if (tox_friend_get_connection_status(tox, f->num, NULL) == 0) 2081 | continue; 2082 | if (f->rxstate == TRANSFER_NONE) 2083 | continue; 2084 | if (f->fd[FFILE_OUT] >= 0) 2085 | continue; 2086 | r = fifoopen(f->dirfd, ffiles[FFILE_OUT]); 2087 | if (r < 0) 2088 | continue; 2089 | f->fd[FFILE_OUT] = r; 2090 | if (!tox_file_control(tox, f->num, f->tx.fnum, TOX_FILE_CONTROL_RESUME, NULL)) { 2091 | weprintf("Failed to accept transfer from receiver\n"); 2092 | cancelrxtransfer(f); 2093 | } else { 2094 | logmsg(": %s : Rx > Accepted\n", f->name); 2095 | f->rxstate = TRANSFER_INPROGRESS; 2096 | } 2097 | } 2098 | 2099 | 2100 | /* Answer pending calls */ 2101 | TAILQ_FOREACH(f, &friendhead, entry) { 2102 | if (tox_friend_get_connection_status(tox, f->num, NULL) == TOX_CONNECTION_NONE) 2103 | continue; 2104 | if (!f->av.state) 2105 | continue; 2106 | 2107 | fd = fifoopen(f->dirfd, ffiles[FCALL_OUT]); 2108 | if (fd < 0) { 2109 | f->av.state &= ~INCOMING; 2110 | } else { 2111 | f->av.state |= INCOMING; 2112 | if (f->fd[FCALL_OUT] >= 0) 2113 | close(fd); 2114 | else 2115 | f->fd[FCALL_OUT] = fd; 2116 | } 2117 | 2118 | if (f->av.state == TRANSMITTING) 2119 | cancelcall(f, "Hung up"); 2120 | 2121 | if (f->av.state & RINGING) { 2122 | if (f->av.state & OUTGOING) { 2123 | c1 = time(NULL); 2124 | if (c1 > c0 + RINGINGDELAY) 2125 | cancelcall(f, "Timeout"); 2126 | } 2127 | if (!(f->av.state & INCOMING)) 2128 | continue; 2129 | if (!toxav_answer(toxav, f->num, AUDIOBITRATE, 0, NULL)) { 2130 | weprintf("Failed to answer call\n"); 2131 | if (!toxav_call_control(toxav, f->num, TOXAV_CALL_CONTROL_CANCEL, NULL)) 2132 | weprintf("Failed to reject call\n"); 2133 | break; 2134 | } 2135 | f->av.state &= ~RINGING; 2136 | f->av.state |= TRANSMITTING; 2137 | logmsg(": %s : Audio > Answered\n", f->name); 2138 | ftruncate(f->fd[FCALL_STATE], 0); 2139 | lseek(f->fd[FCALL_STATE], 0, SEEK_SET); 2140 | dprintf(f->fd[FCALL_STATE], "transmitting\n"); 2141 | } 2142 | } 2143 | 2144 | if (n == 0) 2145 | continue; 2146 | 2147 | for (i = 0; i < LEN(gslots); i++) { 2148 | if (FD_ISSET(gslots[i].fd[IN], &rfds) == 0) 2149 | continue; 2150 | (*gslots[i].cb)(NULL); 2151 | } 2152 | 2153 | for (req = TAILQ_FIRST(&reqhead); req; req = rtmp) { 2154 | rtmp = TAILQ_NEXT(req, entry); 2155 | if (FD_ISSET(req->fd, &rfds) == 0) 2156 | continue; 2157 | reqfifo.name = req->idstr; 2158 | reqfifo.flags = O_RDONLY | O_NONBLOCK; 2159 | if (fiforead(gslots[REQUEST].fd[OUT], &req->fd, reqfifo, 2160 | &ch, 1) != 1) 2161 | continue; 2162 | if (ch != '0' && ch != '1') 2163 | continue; 2164 | frnum = tox_friend_add_norequest(tox, req->id, NULL); 2165 | if (frnum == UINT32_MAX) { 2166 | weprintf("Failed to add friend %s\n", req->idstr); 2167 | fiforeset(gslots[REQUEST].fd[OUT], &req->fd, reqfifo); 2168 | continue; 2169 | } 2170 | if (ch == '1') { 2171 | friendcreate(frnum); 2172 | logmsg("Request : %s > Accepted\n", req->idstr); 2173 | datasave(); 2174 | } else { 2175 | tox_friend_delete(tox, frnum, NULL); 2176 | logmsg("Request : %s > Rejected\n", req->idstr); 2177 | } 2178 | unlinkat(gslots[REQUEST].fd[OUT], req->idstr, 0); 2179 | close(req->fd); 2180 | TAILQ_REMOVE(&reqhead, req, entry); 2181 | free(req->msg); 2182 | free(req); 2183 | } 2184 | 2185 | for (inv = TAILQ_FIRST(&invhead); inv; inv = itmp) { 2186 | itmp = TAILQ_NEXT(inv, entry); 2187 | if (FD_ISSET(inv->fd, &rfds) == 0) 2188 | continue; 2189 | invfifo.name = inv->fifoname; 2190 | invfifo.flags = O_RDONLY | O_NONBLOCK; 2191 | if (fiforead(gslots[CONF].fd[OUT], &inv->fd, invfifo, 2192 | &ch, 1) != 1) 2193 | continue; 2194 | if (ch != '0' && ch != '1') 2195 | continue; 2196 | else if (ch == '1'){ 2197 | cnum = tox_conference_join(tox, inv->inviter, (uint8_t *)inv->cookie, 2198 | inv->cookielen, NULL); 2199 | if(cnum == UINT32_MAX) 2200 | weprintf("Failed to join conference\n"); 2201 | else 2202 | confcreate(cnum); 2203 | } 2204 | unlinkat(gslots[CONF].fd[OUT], inv->fifoname, 0); 2205 | close(inv->fd); 2206 | TAILQ_REMOVE(&invhead, inv, entry); 2207 | free(inv->fifoname); 2208 | free(inv->cookie); 2209 | free(inv); 2210 | } 2211 | 2212 | for (c = TAILQ_FIRST(&confhead); c; c = ctmp) { 2213 | ctmp = TAILQ_NEXT(c, entry); 2214 | if (FD_ISSET(c->fd[CINVITE], &rfds)) 2215 | invitefriend(c); 2216 | if (FD_ISSET(c->fd[CLEAVE], &rfds)) { 2217 | logmsg("- %s > Leave\n", c->numstr); 2218 | tox_conference_delete(tox, c->num, NULL); 2219 | confdestroy(c); 2220 | } 2221 | if (FD_ISSET(c->fd[CTEXT_IN], &rfds)) 2222 | sendconftext(c); 2223 | if (FD_ISSET(c->fd[CTITLE_IN], &rfds)) 2224 | updatetitle(c); 2225 | } 2226 | 2227 | for (f = TAILQ_FIRST(&friendhead); f; f = ftmp) { 2228 | ftmp = TAILQ_NEXT(f, entry); 2229 | if (FD_ISSET(f->fd[FTEXT_IN], &rfds)) 2230 | sendfriendtext(f); 2231 | if (FD_ISSET(f->fd[FFILE_IN], &rfds) && f->tx.state == TRANSFER_NONE) { 2232 | /* Prepare a new transfer */ 2233 | snprintf(tstamp, sizeof(tstamp), "%lu", (unsigned long)time(NULL)); 2234 | f->tx.fnum = tox_file_send(tox, f->num, TOX_FILE_KIND_DATA, UINT64_MAX, 2235 | NULL, (uint8_t *)tstamp, strlen(tstamp), NULL); 2236 | if (f->tx.fnum == UINT32_MAX) { 2237 | weprintf("Failed to initiate new transfer\n"); 2238 | fiforeset(f->dirfd, &f->fd[FFILE_IN], ffiles[FFILE_IN]); 2239 | } else { 2240 | f->tx.state = TRANSFER_INITIATED; 2241 | logmsg(": %s : Tx > Initiated\n", f->name); 2242 | } 2243 | } 2244 | if (FD_ISSET(f->fd[FCALL_IN], &rfds)) { 2245 | if (!f->av.state) { 2246 | if (!toxav_call(toxav, f->num, AUDIOBITRATE, 0, NULL)) { 2247 | weprintf("Failed to call\n"); 2248 | fiforeset(f->dirfd, &f->fd[FCALL_IN], ffiles[FCALL_IN]); 2249 | break; 2250 | } 2251 | 2252 | f->av.state |= RINGING; 2253 | logmsg(": %s : Audio > Tx Inviting\n", f->name); 2254 | } 2255 | if (!(f->av.state & OUTGOING)) { 2256 | c0 = time(NULL); 2257 | f->av.n = 0; 2258 | f->av.lastsent.tv_sec = 0; 2259 | f->av.lastsent.tv_nsec = 0; 2260 | 2261 | f->av.frame = malloc(sizeof(int16_t) * framesize); 2262 | if (!f->av.frame) 2263 | eprintf("malloc:"); 2264 | 2265 | f->av.state |= OUTGOING; 2266 | } else { 2267 | if (f->av.state & TRANSMITTING) 2268 | sendfriendcalldata(f); 2269 | } 2270 | } 2271 | if (FD_ISSET(f->fd[FREMOVE], &rfds)) 2272 | removefriend(f); 2273 | } 2274 | } 2275 | } 2276 | 2277 | static void 2278 | initshutdown(int sig) 2279 | { 2280 | running = 0; 2281 | } 2282 | 2283 | static void 2284 | toxshutdown(void) 2285 | { 2286 | struct friend *f, *ftmp; 2287 | struct request *r, *rtmp; 2288 | struct conference *c, *ctmp; 2289 | struct invite *i, *itmp; 2290 | size_t s, m; 2291 | 2292 | logmsg("Shutdown\n"); 2293 | 2294 | datasave(); 2295 | 2296 | /* Friends */ 2297 | for (f = TAILQ_FIRST(&friendhead); f; f = ftmp) { 2298 | ftmp = TAILQ_NEXT(f, entry); 2299 | frienddestroy(f); 2300 | } 2301 | 2302 | /* Conferences */ 2303 | for (c = TAILQ_FIRST(&confhead); c; c=ctmp) { 2304 | ctmp = TAILQ_NEXT(c, entry); 2305 | confdestroy(c); 2306 | } 2307 | 2308 | /* Requests */ 2309 | for (r = TAILQ_FIRST(&reqhead); r; r = rtmp) { 2310 | rtmp = TAILQ_NEXT(r, entry); 2311 | 2312 | if (gslots[REQUEST].fd[OUT] != -1) { 2313 | unlinkat(gslots[REQUEST].fd[OUT], r->idstr, 0); 2314 | if (r->fd != -1) 2315 | close(r->fd); 2316 | } 2317 | TAILQ_REMOVE(&reqhead, r, entry); 2318 | free(r->msg); 2319 | free(r); 2320 | } 2321 | 2322 | /* Invites */ 2323 | for (i = TAILQ_FIRST(&invhead); i; i = itmp) { 2324 | itmp = TAILQ_NEXT(i, entry); 2325 | 2326 | if(gslots[CONF].fd[OUT] != -1) { 2327 | unlinkat(gslots[CONF].fd[OUT], i->fifoname, 0); 2328 | if (i->fd != -1) 2329 | close(i->fd); 2330 | } 2331 | TAILQ_REMOVE(&invhead, i, entry); 2332 | free(i->fifoname); 2333 | free(i->cookie); 2334 | free(i); 2335 | } 2336 | 2337 | /* Global files and slots */ 2338 | for (s = 0; s < LEN(gslots); s++) { 2339 | for (m = 0; m < LEN(gfiles); m++) { 2340 | if (gslots[s].dirfd != -1) { 2341 | unlinkat(gslots[s].dirfd, gfiles[m].name, 2342 | (gslots[s].outisfolder && m == OUT) 2343 | ? AT_REMOVEDIR : 0); 2344 | if (gslots[s].fd[m] != -1) 2345 | close(gslots[s].fd[m]); 2346 | } 2347 | } 2348 | rmdir(gslots[s].name); 2349 | } 2350 | unlink("id"); 2351 | if (idfd != -1) 2352 | close(idfd); 2353 | 2354 | toxav_kill(toxav); 2355 | tox_kill(tox); 2356 | } 2357 | 2358 | static void 2359 | usage(void) 2360 | { 2361 | eprintf("usage: %s [-4|-6] [-E|-e] [-T|-t] [-P|-p] [-q] [savefile]\n", argv0); 2362 | } 2363 | 2364 | int 2365 | main(int argc, char *argv[]) 2366 | { 2367 | char* pwd; 2368 | passphrase=NULL; 2369 | ARGBEGIN { 2370 | case '4': 2371 | ipv6 = 0; 2372 | break; 2373 | case '6': 2374 | ipv6 = 1; 2375 | break; 2376 | case 'E': 2377 | encryptsavefile = 1; 2378 | break; 2379 | case 'e': 2380 | encryptsavefile = 0; 2381 | break; 2382 | case 'T': 2383 | tcp = 1; 2384 | break; 2385 | case 't': 2386 | tcp = 0; 2387 | break; 2388 | case 'P': 2389 | proxy = 1; 2390 | break; 2391 | case 'p': 2392 | proxy = 0; 2393 | break; 2394 | case 'q': 2395 | quiet = 1; 2396 | break; 2397 | case 'r': 2398 | pwd=EARGF(usage); 2399 | passphrase = malloc(sizeof(uint8_t)*strlen(pwd)); 2400 | pplen = sizeof(uint8_t)*strlen(pwd); 2401 | memcpy(passphrase, pwd, 1+strlen(pwd)); 2402 | memset(pwd, 0, strlen(pwd)); 2403 | encryptsavefile = 1; 2404 | break; 2405 | default: 2406 | usage(); 2407 | } ARGEND; 2408 | 2409 | if (argc > 1) 2410 | usage(); 2411 | if (argc == 1) 2412 | savefile = *argv; 2413 | 2414 | setbuf(stdout, NULL); 2415 | 2416 | signal(SIGHUP, initshutdown); 2417 | signal(SIGINT, initshutdown); 2418 | signal(SIGQUIT, initshutdown); 2419 | signal(SIGTERM, initshutdown); 2420 | signal(SIGPIPE, SIG_IGN); 2421 | 2422 | if (!quiet) 2423 | printrat(); 2424 | toxinit(); 2425 | localinit(); 2426 | friendload(); 2427 | loop(); 2428 | toxshutdown(); 2429 | 2430 | free(passphrase); 2431 | 2432 | return 0; 2433 | } 2434 | --------------------------------------------------------------------------------