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