ratox

FIFO based tox client
git clone git://git.2f30.org/ratox.git
Log | Files | Refs | README | LICENSE

commit d10df26852b6db53156e1f892e29239435c66299
parent 799e250bbc9cf89bb09b7cfa4afbc4dbb9a46657
Author: pranomostro <pranomestro@gmail.com>
Date:   Fri Mar  3 15:39:30 +0100

Merge branch 'toktok'.

This merge updates ratox to toktoks toxcore, which is being developed, and
adds groupchats (although lacking audio group chats at the moment).

Diffstat:
README | 13++++++++++++-
config.def.h | 3+++
nodes.h | 60+++++++++++++++++++++++++++++++++---------------------------
ratox.1 | 24++++++++++++++++++++++++
ratox.c | 500+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
5 files changed, 539 insertions(+), 61 deletions(-)
diff --git a/README b/README @@ -53,6 +53,15 @@ to help explain the semantics of the individual files. | |-- text_in # 'echo yo dude > text_in' to send a text to this friend | `-- text_out # 'tail -f text_out' to dump to stdout any text received | +|-- 00000000 +| |-- members # list of people in the conference +| |-- invite # 'echo 0A734CBA717CEB7883D.... >invite' to invite +| |-- leave # 'echo 1 >leave' to leave the conference +| |-- title_in # 'echo new-title >title_in' to update the conference title +| |-- title_out # contains the current title +| |-- text_in # 'echo blablahumbla >text_in' to message the other conference members +| |-- text_out # contains the messages sent so far in the conference +| |-- id # 'cat id' to show your own ID, you can give this to your friends | |-- name # changing your nick @@ -86,7 +95,7 @@ Features 1 v 1 messaging: Yes File transfer: Yes -Group chat: No +Group chat: Yes Audio: Yes Video: No DNS discovery: No @@ -115,6 +124,8 @@ NOTE: Some of these features are not intended to be developed in ratox itself but rather in external scripts[1] that are built upon ratox. +Group chats do not have audio yet. + Examples ======== diff --git a/config.def.h b/config.def.h @@ -20,6 +20,9 @@ #define VIDEOHEIGHT 720 #define VIDEOBITRATE 2500 +static int friendmsg_log = 1; +static int confmsg_log = 0; + static char *savefile = ".ratox.tox"; static int encryptsavefile = 0; diff --git a/nodes.h b/nodes.h @@ -1,11 +1,5 @@ static struct node nodes[] = { { - .addr4 = "tox.zodiaclabs.org", - .addr6 = "v6.tox.zodiaclabs.org", - .port = 33445, - .idstr = "A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074" - }, - { .addr4 = "biribiri.org", .addr6 = NULL, .port = 33445, @@ -36,12 +30,6 @@ static struct node nodes[] = { .idstr = "1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F" }, { - .addr4 = "108.61.165.198", - .addr6 = "2001:19f0:5000:8121:5054:ff:fe1c:9ded", - .port = 33445, - .idstr = "8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832" - }, - { .addr4 = "194.249.212.109", .addr6 = "2001:1470:fbfe::109", .port = 33445, @@ -132,6 +120,12 @@ static struct node nodes[] = { .idstr = "1A56EA3EDF5DF4C0AEABBF3C2E4E603890F87E983CAC8A0D532A335F2C6E3E1F" }, { + .addr4 = "109.75.40.105", + .addr6 = "2001:470:70d6::1", + .port = 33445, + .idstr = "2B9CD794424FD579044EC2FC5252B23DF8B4AAF239C25074F70B1090C3F8C83A" + }, + { .addr4 = "toxnode.nek0.net", .addr6 = "toxnode.nek0.net", .port = 33445, @@ -198,6 +192,12 @@ static struct node nodes[] = { .idstr = "D3EB45181B343C2C222A5BCF72B760638E15ED87904625AAD351C594EEFAE03E" }, { + .addr4 = "shigure.eve.moe", + .addr6 = "shigure.eve.moe", + .port = 33445, + .idstr = "1A480A53FAF2CBBFCC382D786C43E69EEE23F22C7C23A7CFEB6180A373E23E54" + }, + { .addr4 = "tox.deadteam.org", .addr6 = "tox.deadteam.org", .port = 33445, @@ -222,10 +222,10 @@ static struct node nodes[] = { .idstr = "2555763C8C460495B14157D234DD56B86300A2395554BCAE4621AC345B8C1B1B" }, { - .addr4 = "77.37.160.178", + .addr4 = "77.37.142.179", .addr6 = NULL, - .port = 33440, - .idstr = "CE678DEAFA29182EFD1B0C5B9BC6999E5A20B50A1A6EC18B91C8EBB591712416" + .port = 33445, + .idstr = "98F5830A426C6BF165F895F04B897AFC4F57331B4BE0561F583C9F323194227B" }, { .addr4 = "85.21.144.224", @@ -246,10 +246,16 @@ static struct node nodes[] = { .idstr = "BEB71F97ED9C99C04B8489BB75579EB4DC6AB6F441B603D63533122F1858B51D" }, { - .addr4 = "202.36.75.162", - .addr6 = NULL, + .addr4 = "plfgr.eu.org", + .addr6 = "plfgr.eu.org", .port = 33445, - .idstr = "F202E0936ABEE09067F55B0955C3FF6A84ABEED3C750A9EB930D926D03248F4C" + .idstr = "F5A2E533EC720927FA970F508D949D5958F37889F039F50C905010244842656E" + }, + { + .addr4 = "completelyunoriginal.moe", + .addr6 = "completelyunoriginal.moe", + .port = 33445, + .idstr = "FBC7DED0B0B662D81094D91CC312D6CDF12A7B16C7FFB93817143116B510C13E" }, { .addr4 = "46.101.197.175", @@ -258,21 +264,21 @@ static struct node nodes[] = { .idstr = "CD133B521159541FB1D326DE9850F5E56A6C724B5B8E5EB5CD8D950408E95707" }, { - .addr4 = "shigure.eve.moe", - .addr6 = "shigure.eve.moe", + .addr4 = "tox.zodiaclabs.org", + .addr6 = "v6.tox.zodiaclabs.org", .port = 33445, - .idstr = "1A480A53FAF2CBBFCC382D786C43E69EEE23F22C7C23A7CFEB6180A373E23E54" + .idstr = "A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074" }, { - .addr4 = "81.4.110.149", - .addr6 = "2a00:d880:3:2::8bdc:f19", + .addr4 = "108.61.165.198", + .addr6 = "2001:19f0:5000:8121:5054:ff:fe1c:9ded", .port = 33445, - .idstr = "9E7BD4793FFECA7F32238FA2361040C09025ED3333744483CA6F3039BFF0211E" + .idstr = "8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832" }, { - .addr4 = "192.99.168.140", + .addr4 = "77.37.160.178", .addr6 = NULL, - .port = 33445, - .idstr = "6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F" + .port = 33440, + .idstr = "CE678DEAFA29182EFD1B0C5B9BC6999E5A20B50A1A6EC18B91C8EBB591712416" } }; diff --git a/ratox.1 b/ratox.1 @@ -58,6 +58,10 @@ Status message slot. Request slot. Send a friend request by piping the Tox ID to \fBin\fR. Incoming requests are listed as FIFOs in \fBout/\fR. Echo \fB1\fR | \fB0\fR to accept | reject them. +.It Ar conf/ +Conference management slot. A conference is created by writing it's title to in. Invites +to conferences are FIFOs in \fBout/\fR. Their name is id_cookie (the cookie is random data). +They behave like request FIFOs. .El .Ss Friend slots Each friend is represented with a folder in the base-directory named after @@ -107,6 +111,26 @@ Send a text message by piping data to this FIFO. .It Ar text_out Contains text messages from the friend. .El +.Ss Conference slots +Each conference is represented with a folder in the directory named after the +8-digit conference number. The files in the conference folder are an interface +for the respective conference. +.Bl -tag -width 13n +.It Ar members +Contains a list of members of the conference. +.It Ar invite +Write the ID of a friend to this FIFO to invite him to the conference. +.It Ar leave +Write to this file to leave the conference. +.It Ar title_in +Write here to change the title of the conference. +.It Ar title_out +Contains the title of the conference. +.It Ar text_in +Echo \fBmessage\fR to send a text message to the conference. +.It Ar text_out +Contains the messages send in the conference so far. +.El .Ss Misc files .Bl -tag -width 13n .It Ar id diff --git a/ratox.c b/ratox.c @@ -3,8 +3,6 @@ #include <sys/stat.h> #include <sys/types.h> -#include <arpa/inet.h> - #include <ctype.h> #include <dirent.h> #include <errno.h> @@ -88,8 +86,9 @@ static void setstatus(void *); static void setuserstate(void *); static void sendfriendreq(void *); static void setnospam(void *); +static void newconf(void *); -enum { NAME, STATUS, STATE, REQUEST, NOSPAM }; +enum { NAME, STATUS, STATE, REQUEST, NOSPAM, CONF }; static struct slot gslots[] = { [NAME] = { .name = "name", .cb = setname, .outisfolder = 0, .dirfd = -1, .fd = {-1, -1, -1} }, @@ -97,6 +96,7 @@ static struct slot gslots[] = { [STATE] = { .name = "state", .cb = setuserstate, .outisfolder = 0, .dirfd = -1, .fd = {-1, -1, -1} }, [REQUEST] = { .name = "request", .cb = sendfriendreq, .outisfolder = 1, .dirfd = -1, .fd = {-1, -1, -1} }, [NOSPAM] = { .name = "nospam", .cb = setnospam, .outisfolder = 0, .dirfd = -1, .fd = {-1, -1, -1} }, + [CONF] = { .name = "conf", .cb = newconf, .outisfolder = 1, .dirfd = -1, .fd = {-1, -1, -1} }, }; enum { FTEXT_IN, FFILE_IN, FCALL_IN, FTEXT_OUT, FFILE_OUT, FCALL_OUT, @@ -118,6 +118,18 @@ static struct file ffiles[] = { [FCALL_STATE] = { .type = STATIC, .name = "call_state", .flags = O_WRONLY | O_TRUNC | O_CREAT }, }; +enum { CMEMBERS, CINVITE, CLEAVE, CTITLE_IN, CTITLE_OUT, CTEXT_IN, CTEXT_OUT }; + +static struct file cfiles[] = { + [CMEMBERS] = { .type = STATIC, .name = "members", .flags = O_WRONLY | O_TRUNC | O_CREAT }, + [CINVITE] = { .type = FIFO, .name = "invite", .flags = O_RDONLY | O_NONBLOCK }, + [CLEAVE] = { .type = FIFO, .name = "leave", .flags = O_RDONLY | O_NONBLOCK }, + [CTITLE_IN] = { .type = FIFO, .name = "title_in", .flags = O_RDONLY | O_NONBLOCK }, + [CTITLE_OUT] = { .type = STATIC, .name = "title_out", .flags = O_WRONLY | O_TRUNC | O_CREAT }, + [CTEXT_IN] = { .type = FIFO, .name = "text_in", .flags = O_RDONLY | O_NONBLOCK }, + [CTEXT_OUT] = { .type = STATIC, .name = "text_out", .flags = O_WRONLY | O_APPEND | O_CREAT }, +}; + static char *ustate[] = { [TOX_USER_STATUS_NONE] = "available", [TOX_USER_STATUS_AWAY] = "away", @@ -162,6 +174,14 @@ struct friend { TAILQ_ENTRY(friend) entry; }; +struct conference { + uint32_t num; + char numstr[2 * sizeof(uint32_t) + 1]; + int dirfd; + int fd[LEN(cfiles)]; + TAILQ_ENTRY(conference) entry; +}; + struct request { uint8_t id[TOX_PUBLIC_KEY_SIZE]; char idstr[2 * TOX_PUBLIC_KEY_SIZE + 1]; @@ -170,8 +190,19 @@ struct request { TAILQ_ENTRY(request) entry; }; +struct invite { + char *fifoname; + uint8_t *cookie; + size_t cookielen; + uint32_t inviter; + int fd; + TAILQ_ENTRY(invite) entry; +}; + static TAILQ_HEAD(friendhead, friend) friendhead = TAILQ_HEAD_INITIALIZER(friendhead); +static TAILQ_HEAD(confhead, conference) confhead = TAILQ_HEAD_INITIALIZER(confhead); static TAILQ_HEAD(reqhead, request) reqhead = TAILQ_HEAD_INITIALIZER(reqhead); +static TAILQ_HEAD(invhead, invite) invhead = TAILQ_HEAD_INITIALIZER(invhead); static Tox *tox; static ToxAV *toxav; @@ -197,6 +228,7 @@ static void cbcalldata(ToxAV *, uint32_t, const int16_t *, size_t, uint8_t, uint static void cancelcall(struct friend *, char *); static void sendfriendcalldata(struct friend *); +static void writemembers(struct conference *); static void cbconnstatus(Tox *, uint32_t, TOX_CONNECTION, void *); static void cbfriendmessage(Tox *, uint32_t, TOX_MESSAGE_TYPE, const uint8_t *, size_t, void *); @@ -208,10 +240,18 @@ static void cbfilecontrol(Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL, void *); static void cbfilesendreq(Tox *, uint32_t, uint32_t, uint32_t, uint64_t, const uint8_t *, size_t, void *); static void cbfiledata(Tox *, uint32_t, uint32_t, uint64_t, const uint8_t *, size_t, void *); +static void cbconfinvite(Tox *, uint32_t, TOX_CONFERENCE_TYPE, const uint8_t *, size_t, void *); +static void cbconfmessage(Tox *, uint32_t, uint32_t, TOX_MESSAGE_TYPE, const uint8_t *, size_t, void *); +static void cbconftitle(Tox *, uint32_t, uint32_t, const uint8_t *, size_t, void *); +static void cbconfmembers(Tox *, uint32_t, uint32_t, TOX_CONFERENCE_STATE_CHANGE, void *); + static void canceltxtransfer(struct friend *); static void cancelrxtransfer(struct friend *); static void sendfriendtext(struct friend *); static void removefriend(struct friend *); +static void invitefriend(struct conference *); +static void sendconftext(struct conference *); +static void updatetitle(struct conference *); static int readpass(const char *, uint8_t **, uint32_t *); static void dataload(struct Tox_Options *); static void datasave(void); @@ -221,8 +261,10 @@ static int toxconnect(void); static void id2str(uint8_t *, char *); static void str2id(char *, uint8_t *); static void friendcreate(uint32_t); +static void confcreate(uint32_t); static void friendload(void); static void frienddestroy(struct friend *); +static void confdestroy(struct conference *); static void loop(void); static void initshutdown(int); static void toxshutdown(void); @@ -444,6 +486,122 @@ cbcalldata(ToxAV *av, uint32_t fnum, const int16_t *data, size_t len, } static void +cbconfinvite(Tox *m, uint32_t frnum, TOX_CONFERENCE_TYPE type, const uint8_t *cookie, size_t clen, void * udata) +{ + size_t i, j, namelen; + struct file invfifo; + struct invite *inv; + uint8_t id[TOX_PUBLIC_KEY_SIZE]; + + if(type != TOX_CONFERENCE_TYPE_TEXT) { + weprintf(": %d : Only text conference supported at the moment\n"); + return; + } + + if (!tox_friend_get_public_key(tox, frnum, id, NULL)) { + weprintf(": %d : Key: Failed to get for invite\n", frnum); + return; + } + + inv = calloc(1, sizeof(*inv)); + if (!inv) + eprintf("calloc:"); + inv->fd = -1; + + inv->inviter = frnum; + inv->cookielen = clen; + inv->cookie = malloc(inv->cookielen); + if (!inv->cookie) + eprintf("malloc:"); + + memcpy(inv->cookie, cookie, clen); + + namelen = 2 * TOX_PUBLIC_KEY_SIZE + 1 + 2 * clen + 2; + inv->fifoname = malloc(namelen); + if (!inv->fifoname) + eprintf("malloc:"); + + i = 0; + id2str(id, inv->fifoname); + i += 2 * TOX_PUBLIC_KEY_SIZE; + inv->fifoname[i] = '_'; + i++; + for(j = 0; j < clen; i+=2, j++) + sprintf(inv->fifoname + i, "%02X", cookie[j]); + i++; + inv->fifoname[i] = '\0'; + + invfifo.name = inv->fifoname; + invfifo.flags = O_RDONLY | O_NONBLOCK; + fiforeset(gslots[CONF].fd[OUT], &inv->fd, invfifo); + + TAILQ_INSERT_TAIL(&invhead, inv, entry); + + logmsg("Invite > %s\n", inv->fifoname); +} + +static void +cbconfmessage(Tox *m, uint32_t cnum, uint32_t pnum, TOX_MESSAGE_TYPE type, const uint8_t *data, size_t len, void *udata) +{ + struct conference *c; + time_t t; + uint8_t msg[len + 1], namt[TOX_MAX_NAME_LENGTH + 1]; + char buft[64]; + + memcpy(msg, data, len); + msg[len] = '\0'; + + TAILQ_FOREACH(c, &confhead, entry) { + if (c->num == cnum) { + t = time(NULL); + strftime(buft, sizeof(buft), "%F %R", localtime(&t)); + if (!tox_conference_peer_get_name(tox, c->num, pnum, namt, NULL)) { + weprintf("Unable to obtain name for peer %d in conference %s\n", pnum, c->numstr); + return; + } + namt[tox_conference_peer_get_name_size(tox, c->num, pnum, NULL)] = '\0'; + dprintf(c->fd[CTEXT_OUT], "%s <%s> %s\n", buft, namt, msg); + if (confmsg_log) + logmsg("%s: %s <%s> %s\n", c->numstr, buft, namt, msg); + break; + } + } +} + +static void +cbconftitle(Tox *m, uint32_t cnum, uint32_t pnum, const uint8_t *data, size_t len, void * udata) +{ + struct conference *c; + char title[TOX_MAX_NAME_LENGTH + 1]; + + memcpy(title, data, len); + title[len] = '\0'; + + TAILQ_FOREACH(c, &confhead, entry) { + if (c->num == cnum) { + ftruncate(c->fd[CTITLE_OUT], 0); + lseek(c->fd[CTITLE_OUT], 0, SEEK_SET); + dprintf(c->fd[CTITLE_OUT], "%s\n", title); + logmsg(": %s : Title > %s\n", c->numstr, title); + break; + } + } +} + +static void +cbconfmembers(Tox *m, uint32_t cnum, uint32_t pnum, TOX_CONFERENCE_STATE_CHANGE type, void *udata) +{ + struct conference *c; + + TAILQ_FOREACH(c, &confhead, entry) { + if (c->num == cnum) { + writemembers(c); + break; + } + } +} + +static void cancelcall(struct friend *f, char *action) { logmsg(": %s : Audio > %s\n", f->name, action); @@ -508,6 +666,33 @@ sendfriendcalldata(struct friend *f) } static void +writemembers(struct conference *c) +{ + size_t i; + uint32_t peers, pnum; + uint8_t name[TOX_MAX_NAME_LENGTH + 1]; + TOX_ERR_CONFERENCE_PEER_QUERY err; + + /*The peer list is written when we invite the members by the callback*/ + ftruncate(c->fd[CMEMBERS], 0); + peers = tox_conference_peer_count(tox, c->num, &err); + + if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) { + weprintf("Unable to obtain peer count for conference %d\n", c->num); + return; + } + for (pnum = 0; pnum < peers; pnum++) { + if (!tox_conference_peer_get_name(tox, c->num, pnum, name, NULL)) { + weprintf("Unable to obtain the name for peer %d\n", pnum); + } else { + i = tox_conference_peer_get_name_size(tox, c->num, pnum, NULL); + name[i] = '\0'; + dprintf(c->fd[CMEMBERS], "%s\n", name); + } + } +} + +static void cbconnstatus(Tox *m, uint32_t frnum, TOX_CONNECTION status, void *udata) { struct friend *f; @@ -568,7 +753,8 @@ cbfriendmessage(Tox *m, uint32_t frnum, TOX_MESSAGE_TYPE type, const uint8_t *da t = time(NULL); strftime(buft, sizeof(buft), "%F %R", localtime(&t)); dprintf(f->fd[FTEXT_OUT], "%s %s\n", buft, msg); - logmsg(": %s > %s\n", f->name, msg); + if (friendmsg_log) + logmsg(": %s > %s\n", f->name, msg); break; } } @@ -811,7 +997,8 @@ cbfilesendreq(Tox *m, uint32_t frnum, uint32_t fnum, uint32_t kind, uint64_t fsz } static void -cbfiledata(Tox *m, uint32_t frnum, uint32_t fnum, uint64_t pos, const uint8_t *data, size_t len, void *udata) +cbfiledata(Tox *m, uint32_t frnum, uint32_t fnum, uint64_t pos, + const uint8_t *data, size_t len, void *udata) { struct friend *f; ssize_t n; @@ -913,6 +1100,72 @@ removefriend(struct friend *f) frienddestroy(f); } +static void +invitefriend(struct conference *c) +{ + ssize_t n; + char buf[2 * TOX_ADDRESS_SIZE + 1]; + struct friend *f; + + n = fiforead(c->dirfd, &c->fd[CINVITE], cfiles[CINVITE], buf, sizeof(buf)); + + if (n > sizeof(buf) || n <= 0) + return; + if (buf[n - 1] == '\n') + buf[n - 1] = '\0'; + + TAILQ_FOREACH(f, &friendhead, entry) + if (!memcmp(buf, f->idstr, sizeof(f->idstr))) + break; + if (!f) { + logmsg("Conference %s > no friend with id %s found\n", c->numstr, buf); + return; + } + if (tox_friend_get_connection_status(tox, f->num, NULL) == TOX_CONNECTION_NONE) { + logmsg("Conference %s > %s not online, can't be invited\n", c->numstr, buf); + return; + } + if (!tox_conference_invite(tox, f->num, c->num, NULL)) + weprintf("Failed to invite %s\n", buf); + else + logmsg("Conference %s > Invite %s\n", c->numstr, buf); +} + +static void +sendconftext(struct conference *c) +{ + ssize_t n; + uint8_t buf[TOX_MAX_MESSAGE_LENGTH]; + + n = fiforead(c->dirfd, &c->fd[CTEXT_IN], cfiles[CTEXT_IN], buf, sizeof(buf)); + if (n <= 0) + return; + if (buf[n - 1] == '\n' && n > 1) + n--; + if (!tox_conference_send_message(tox, c->num, TOX_MESSAGE_TYPE_NORMAL, + buf, n, NULL)) + weprintf("%s: Message : Failed to send, error %d\n", c->numstr); +} + +static void +updatetitle(struct conference *c) +{ + ssize_t n; + uint8_t title[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; + + n = fiforead(c->dirfd, &c->fd[CTITLE_IN], cfiles[CTITLE_IN], title, sizeof(title) - 1); + if (n <= 0) + return; + if (title[n - 1] == '\n') + n--; + title[n] = '\0'; + if (!tox_conference_set_title(tox, c->num, title, n, NULL)) { + weprintf("%s : Title : Failed to set to \"%s\"\n", title, c->numstr); + return; + } + logmsg("Conference %s > Title > %s\n", c->numstr, title); +} + static int readpass(const char *prompt, uint8_t **target, uint32_t *len) { @@ -1120,7 +1373,7 @@ localinit(void) /* Dump Nospam */ ftruncate(gslots[NOSPAM].fd[OUT], 0); - dprintf(gslots[NOSPAM].fd[OUT], "%08X\n", ntohl(tox_self_get_nospam(tox))); + dprintf(gslots[NOSPAM].fd[OUT], "%08X\n", tox_self_get_nospam(tox)); return 0; } @@ -1158,22 +1411,27 @@ toxinit(void) framesize = (AUDIOSAMPLERATE * AUDIOFRAME * AUDIOCHANNELS) / 1000; - tox_callback_friend_connection_status(tox, cbconnstatus, NULL); - tox_callback_friend_message(tox, cbfriendmessage, NULL); - tox_callback_friend_request(tox, cbfriendrequest, NULL); - tox_callback_friend_name(tox, cbnamechange, NULL); - tox_callback_friend_status_message(tox, cbstatusmessage, NULL); - tox_callback_friend_status(tox, cbfriendstate, NULL); - tox_callback_file_recv_control(tox, cbfilecontrol, NULL); - tox_callback_file_recv(tox, cbfilesendreq, NULL); - tox_callback_file_recv_chunk(tox, cbfiledata, NULL); - tox_callback_file_chunk_request(tox, cbfiledatareq, NULL); + tox_callback_friend_connection_status(tox, cbconnstatus); + tox_callback_friend_message(tox, cbfriendmessage); + tox_callback_friend_request(tox, cbfriendrequest); + tox_callback_friend_name(tox, cbnamechange); + tox_callback_friend_status_message(tox, cbstatusmessage); + tox_callback_friend_status(tox, cbfriendstate); + tox_callback_file_recv_control(tox, cbfilecontrol); + tox_callback_file_recv(tox, cbfilesendreq); + tox_callback_file_recv_chunk(tox, cbfiledata); + tox_callback_file_chunk_request(tox, cbfiledatareq); toxav_callback_call(toxav, cbcallinvite, NULL); toxav_callback_call_state(toxav, cbcallstate, NULL); toxav_callback_audio_receive_frame(toxav, cbcalldata, NULL); + tox_callback_conference_invite(tox, cbconfinvite); + tox_callback_conference_message(tox, cbconfmessage); + tox_callback_conference_title(tox, cbconftitle); + tox_callback_conference_namelist_change(tox, cbconfmembers); + if (toxopt.savedata_data) free((void *)toxopt.savedata_data); @@ -1326,6 +1584,64 @@ friendcreate(uint32_t frnum) } static void +confcreate(uint32_t cnum) +{ + struct conference *c; + DIR *d; + size_t i; + int r; + uint8_t title[TOX_MAX_NAME_LENGTH + 1]; + TOX_ERR_CONFERENCE_TITLE err; + + c = calloc(1, sizeof(*c)); + if(!c) + eprintf("calloc:"); + c->num = cnum; + sprintf(c->numstr, "%08X", c->num); + r = mkdir(c->numstr, 0777); + if(r < 0 && errno != EEXIST) + eprintf("mkdir %s:", c->numstr); + + d = opendir(c->numstr); + if (!d) + eprintf("opendir %s:", c->numstr); + + r = dirfd(d); + if (r < 0) + eprintf("dirfd %s:", c->numstr); + c->dirfd = r; + + for (i = 0; i < LEN(cfiles); i++) { + c->fd[i] = -1; + if (cfiles[i].type == FIFO) { + fiforeset(c->dirfd, &c->fd[i], cfiles[i]); + } else if (cfiles[i].type == STATIC) { + c->fd[i] = fifoopen(c->dirfd, cfiles[i]); + } + } + + writemembers(c); + + /* No warning is printed here in the case of an error + * because this always fails when joining after an invite, + * but cbconftitle() is called in the next iteration afterwards, + * so it doesn't matter after all. + */ + + i = tox_conference_get_title_size(tox, c->num, &err); + if (err != TOX_ERR_CONFERENCE_TITLE_OK) + i = 0; + tox_conference_get_title(tox, c->num, title, NULL); + title[i] = '\0'; + ftruncate(c->fd[CTITLE_OUT], 0); + dprintf(c->fd[CTITLE_OUT], "%s\n", title); + + TAILQ_INSERT_TAIL(&confhead, c, entry); + + logmsg("Conference %s > Created\n", c->numstr); +} + +static void frienddestroy(struct friend *f) { size_t i; @@ -1346,6 +1662,22 @@ frienddestroy(struct friend *f) } static void +confdestroy(struct conference *c) +{ + size_t i; + + for (i = 0; i <LEN(cfiles); i++) { + if(c->dirfd != -1) { + unlinkat(c->dirfd, cfiles[i].name, 0); + if (c->fd[i] != -1) + close(c->fd[i]); + } + } + rmdir(c->numstr); + TAILQ_REMOVE(&confhead, c, entry); +} + +static void friendload(void) { size_t sz; @@ -1526,7 +1858,7 @@ setnospam(void *data) } nsval = strtoul((char *)nospam, NULL, 16); - tox_self_set_nospam(tox, htonl(nsval)); + tox_self_set_nospam(tox, nsval); datasave(); logmsg("Nospam > %08X\n", nsval); ftruncate(gslots[NOSPAM].fd[OUT], 0); @@ -1544,18 +1876,44 @@ end: } static void +newconf(void *data) +{ + uint32_t cnum; + size_t n; + char title[TOX_MAX_NAME_LENGTH + 1]; + + n = fiforead(gslots[CONF].dirfd, &gslots[CONF].fd[IN], gfiles[IN], + title, sizeof(title) - 1); + if (n <= 0) + return; + if (title[n - 1] == '\n') + n--; + title[n] = '\0'; + cnum = tox_conference_new(tox, NULL); + if (cnum == UINT32_MAX) { + weprintf("Failed to create new conference\n"); + return; + } + if (!tox_conference_set_title(tox, cnum, (uint8_t *)title, n, NULL)) + weprintf("Failed to set conference title to \"%s\"", title); + confcreate(cnum); +} + +static void loop(void) { - struct file reqfifo; + struct file reqfifo, invfifo; struct friend *f, *ftmp; struct request *req, *rtmp; + struct conference *c, *ctmp; + struct invite *inv, *itmp; struct timeval tv; fd_set rfds; time_t t0, t1, c0, c1; size_t i; int connected = 0, n, r, fd, fdmax; - char tstamp[64], c; - uint32_t frnum; + char tstamp[64], ch; + uint32_t frnum, cnum; t0 = time(NULL); logmsg("DHT > Connecting\n"); @@ -1583,7 +1941,7 @@ loop(void) toxconnect(); } } - tox_iterate(tox); + tox_iterate(tox, NULL); toxav_iterate(toxav); /* Prepare select-fd-set */ @@ -1596,6 +1954,9 @@ loop(void) TAILQ_FOREACH(req, &reqhead, entry) FD_APPEND(req->fd); + TAILQ_FOREACH(inv, &invhead, entry) + FD_APPEND(inv->fd); + TAILQ_FOREACH(f, &friendhead, entry) { /* Only monitor friends that are online */ if (tox_friend_get_connection_status(tox, f->num, NULL) != TOX_CONNECTION_NONE) { @@ -1609,6 +1970,13 @@ loop(void) FD_APPEND(f->fd[FREMOVE]); } + TAILQ_FOREACH(c, &confhead, entry) { + FD_APPEND(c->fd[CLEAVE]); + FD_APPEND(c->fd[CTITLE_IN]); + FD_APPEND(c->fd[CTEXT_IN]); + FD_APPEND(c->fd[CINVITE]); + } + tv.tv_sec = 0; tv.tv_usec = interval(tox, toxav) * 1000; n = select(fdmax + 1, &rfds, NULL, NULL, &tv); @@ -1655,6 +2023,7 @@ loop(void) } } + /* Answer pending calls */ TAILQ_FOREACH(f, &friendhead, entry) { if (tox_friend_get_connection_status(tox, f->num, NULL) == TOX_CONNECTION_NONE) @@ -1715,9 +2084,9 @@ loop(void) reqfifo.name = req->idstr; reqfifo.flags = O_RDONLY | O_NONBLOCK; if (fiforead(gslots[REQUEST].fd[OUT], &req->fd, reqfifo, - &c, 1) != 1) + &ch, 1) != 1) continue; - if (c != '0' && c != '1') + if (ch != '0' && ch != '1') continue; frnum = tox_friend_add_norequest(tox, req->id, NULL); if (frnum == UINT32_MAX) { @@ -1725,7 +2094,7 @@ loop(void) fiforeset(gslots[REQUEST].fd[OUT], &req->fd, reqfifo); continue; } - if (c == '1') { + if (ch == '1') { friendcreate(frnum); logmsg("Request : %s > Accepted\n", req->idstr); datasave(); @@ -1740,6 +2109,48 @@ loop(void) free(req); } + for (inv = TAILQ_FIRST(&invhead); inv; inv = itmp) { + itmp = TAILQ_NEXT(inv, entry); + if (FD_ISSET(inv->fd, &rfds) == 0) + continue; + invfifo.name = inv->fifoname; + invfifo.flags = O_RDONLY | O_NONBLOCK; + if (fiforead(gslots[CONF].fd[OUT], &inv->fd, invfifo, + &ch, 1) != 1) + continue; + if (ch != '0' && ch != '1') + continue; + else if (ch == '1'){ + cnum = tox_conference_join(tox, inv->inviter, (uint8_t *)inv->cookie, + inv->cookielen, NULL); + if(cnum == UINT32_MAX) + weprintf("Failed to join conference\n"); + else + confcreate(cnum); + } + unlinkat(gslots[CONF].fd[OUT], inv->fifoname, 0); + close(inv->fd); + TAILQ_REMOVE(&invhead, inv, entry); + free(inv->fifoname); + free(inv->cookie); + free(inv); + } + + for (c = TAILQ_FIRST(&confhead); c; c = ctmp) { + ctmp = TAILQ_NEXT(c, entry); + if (FD_ISSET(c->fd[CINVITE], &rfds)) + invitefriend(c); + if (FD_ISSET(c->fd[CLEAVE], &rfds)) { + logmsg("Conference %s > Leave\n", c->numstr); + tox_conference_delete(tox, c->num, NULL); + confdestroy(c); + } + if (FD_ISSET(c->fd[CTEXT_IN], &rfds)) + sendconftext(c); + if (FD_ISSET(c->fd[CTITLE_IN], &rfds)) + updatetitle(c); + } + for (f = TAILQ_FIRST(&friendhead); f; f = ftmp) { ftmp = TAILQ_NEXT(f, entry); if (FD_ISSET(f->fd[FTEXT_IN], &rfds)) @@ -1805,7 +2216,9 @@ toxshutdown(void) { struct friend *f, *ftmp; struct request *r, *rtmp; - size_t i, m; + struct conference *c, *ctmp; + struct invite *i, *itmp; + size_t s, m; logmsg("Shutdown\n"); @@ -1817,6 +2230,12 @@ toxshutdown(void) frienddestroy(f); } + /* Conferences */ + for (c = TAILQ_FIRST(&confhead); c; c=ctmp) { + ctmp = TAILQ_NEXT(c, entry); + confdestroy(c); + } + /* Requests */ for (r = TAILQ_FIRST(&reqhead); r; r = rtmp) { rtmp = TAILQ_NEXT(r, entry); @@ -1831,18 +2250,33 @@ toxshutdown(void) free(r); } + /* Invites */ + for (i = TAILQ_FIRST(&invhead); i; i = itmp) { + itmp = TAILQ_NEXT(i, entry); + + if(gslots[CONF].fd[OUT] != -1) { + unlinkat(gslots[CONF].fd[OUT], i->fifoname, 0); + if (i->fd != -1) + close(i->fd); + } + TAILQ_REMOVE(&invhead, i, entry); + free(i->fifoname); + free(i->cookie); + free(i); + } + /* Global files and slots */ - for (i = 0; i < LEN(gslots); i++) { + for (s = 0; s < LEN(gslots); s++) { for (m = 0; m < LEN(gfiles); m++) { - if (gslots[i].dirfd != -1) { - unlinkat(gslots[i].dirfd, gfiles[m].name, - (gslots[i].outisfolder && m == OUT) + if (gslots[s].dirfd != -1) { + unlinkat(gslots[s].dirfd, gfiles[m].name, + (gslots[s].outisfolder && m == OUT) ? AT_REMOVEDIR : 0); - if (gslots[i].fd[m] != -1) - close(gslots[i].fd[m]); + if (gslots[s].fd[m] != -1) + close(gslots[s].fd[m]); } - } - rmdir(gslots[i].name); + } + rmdir(gslots[s].name); } unlink("id"); if (idfd != -1)