ratox

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

commit 2dba7061c7f9226922e076ea83d416f594cdb0ef
parent 95c52ca0d8f631b7101c16e36f93e5c3e87a4693
Author: sin <sin@2f30.org>
Date:   Wed Sep 24 12:55:25 +0100

Add support for receiving files

Diffstat:
README | 2+-
ratox.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
2 files changed, 115 insertions(+), 16 deletions(-)
diff --git a/README b/README @@ -10,7 +10,7 @@ Features ======== 1 v 1 messaging: Yes -File transfer: Sending only +File transfer: Yes Group chat: No Audio: No Video: No diff --git a/ratox.c b/ratox.c @@ -100,21 +100,25 @@ static struct slot gslots[] = { enum { FTEXT_IN, FFILE_IN, + FFILE_OUT, FREMOVE, FONLINE, FNAME, FSTATUS, - FTEXT_OUT + FTEXT_OUT, + FFILE_PENDING, }; static struct file ffiles[] = { - [FTEXT_IN] = { .type = FIFO, .name = "text_in", .flags = O_RDONLY | O_NONBLOCK }, - [FFILE_IN] = { .type = FIFO, .name = "file_in", .flags = O_RDONLY | O_NONBLOCK }, - [FREMOVE] = { .type = FIFO, .name = "remove", .flags = O_RDONLY | O_NONBLOCK }, - [FONLINE] = { .type = STATIC, .name = "online", .flags = O_WRONLY | O_TRUNC | O_CREAT }, - [FNAME] = { .type = STATIC, .name = "name", .flags = O_WRONLY | O_TRUNC | O_CREAT }, - [FSTATUS] = { .type = STATIC, .name = "status", .flags = O_WRONLY | O_TRUNC | O_CREAT }, - [FTEXT_OUT] = { .type = STATIC, .name = "text_out", .flags = O_WRONLY | O_APPEND | O_CREAT }, + [FTEXT_IN] = { .type = FIFO, .name = "text_in", .flags = O_RDONLY | O_NONBLOCK }, + [FFILE_IN] = { .type = FIFO, .name = "file_in", .flags = O_RDONLY | O_NONBLOCK }, + [FFILE_OUT] = { .type = FIFO, .name = "file_out", .flags = O_WRONLY | O_NONBLOCK }, + [FREMOVE] = { .type = FIFO, .name = "remove", .flags = O_RDONLY | O_NONBLOCK }, + [FONLINE] = { .type = STATIC, .name = "online", .flags = O_WRONLY | O_TRUNC | O_CREAT }, + [FNAME] = { .type = STATIC, .name = "name", .flags = O_WRONLY | O_TRUNC | O_CREAT }, + [FSTATUS] = { .type = STATIC, .name = "status", .flags = O_WRONLY | O_TRUNC | O_CREAT }, + [FTEXT_OUT] = { .type = STATIC, .name = "text_out", .flags = O_WRONLY | O_APPEND | O_CREAT }, + [FFILE_PENDING] = { .type = STATIC, .name = "file_pending", .flags = O_WRONLY | O_TRUNC | O_CREAT }, }; enum { @@ -143,6 +147,7 @@ struct friend { char idstr[2 * TOX_CLIENT_ID_SIZE + 1]; int dirfd; int fd[LEN(ffiles)]; + int recvfilepending; struct transfer t; TAILQ_ENTRY(friend) entry; }; @@ -182,6 +187,8 @@ static void cbnamechange(Tox *, int32_t, const uint8_t *, uint16_t, void *); static void cbstatusmessage(Tox *, int32_t, const uint8_t *, uint16_t, void *); static void cbuserstatus(Tox *, int32_t, uint8_t, void *); static void cbfilecontrol(Tox *, int32_t, uint8_t, uint8_t, uint8_t, const uint8_t *, uint16_t, void *); +static void cbfilesendreq(Tox *, int32_t, uint8_t, uint64_t, const uint8_t *, uint16_t, void *); +static void cbfiledata(Tox *, int32_t, uint8_t, const uint8_t *, uint16_t, void *); static void canceltransfer(struct friend *); static void sendfriendfile(struct friend *); static void sendfriendtext(struct friend *); @@ -342,7 +349,7 @@ cbfriendrequest(Tox *m, const uint8_t *id, const uint8_t *data, uint16_t len, vo } r = openat(gslots[REQUEST].fd[OUT], req->idstr, O_RDWR | O_NONBLOCK); if (r < 0) { - perror("open"); + perror("openat"); exit(EXIT_FAILURE); } req->fd = r; @@ -474,10 +481,22 @@ cbfilecontrol(Tox *m, int32_t fid, uint8_t rec_sen, uint8_t fnum, uint8_t ctrlty break; case TOX_FILECONTROL_FINISHED: if (rec_sen == 1) { + /* sending completed */ printout("Transfer complete\n"); f->t.state = TRANSFER_NONE; free(f->t.buf); f->t.buf = NULL; + } else { + /* receiving completed */ + printout("Transfer complete\n"); + tox_file_send_control(tox, f->fid, 1, 0, TOX_FILECONTROL_FINISHED, NULL, 0); + ftruncate(f->fd[FFILE_PENDING], 0); + dprintf(f->fd[FFILE_PENDING], "%d\n", 0); + f->recvfilepending = 0; + if (f->fd[FFILE_OUT] != -1) { + close(f->fd[FFILE_OUT]); + f->fd[FFILE_OUT] = -1; + } } break; default: @@ -487,6 +506,53 @@ cbfilecontrol(Tox *m, int32_t fid, uint8_t rec_sen, uint8_t fnum, uint8_t ctrlty } static void +cbfilesendreq(Tox *m, int32_t fid, uint8_t fnum, uint64_t fsz, + const uint8_t *fname, uint16_t flen, void *udata) +{ + struct friend *f; + + TAILQ_FOREACH(f, &friendhead, entry) + if (f->fid == fid) + break; + if (!f) + return; + + /* we only support a single transfer at a time */ + if (fnum != 0) { + tox_file_send_control(tox, f->fid, 1, 0, TOX_FILECONTROL_KILL, NULL, 0); + return; + } + + ftruncate(f->fd[FFILE_PENDING], 0); + dprintf(f->fd[FFILE_PENDING], "%d\n", 1); + f->recvfilepending = 1; + printout("Pending file transfer request from %s\n", + f->namestr[0] == '\0' ? "Anonymous" : f->namestr); +} + +static void +cbfiledata(Tox *m, int32_t fid, uint8_t fnum, const uint8_t *data, uint16_t len, void *udata) +{ + struct friend *f; + ssize_t n; + + TAILQ_FOREACH(f, &friendhead, entry) + if (f->fid == fid) + break; + if (!f) + return; + + n = write(f->fd[FFILE_OUT], data, len); + if (n < 0) { + tox_file_send_control(tox, f->fid, 1, 0, TOX_FILECONTROL_KILL, NULL, 0); + if (f->fd[FFILE_OUT] != -1) { + close(f->fd[FFILE_OUT]); + f->fd[FFILE_OUT] = -1; + } + } +} + +static void canceltransfer(struct friend *f) { if (f->t.state != TRANSFER_NONE) { @@ -713,14 +779,14 @@ localinit(void) } r = openat(gslots[i].dirfd, gfiles[m].name, gfiles[m].flags, 0644); if (r < 0) { - perror("open"); + perror("openat"); exit(EXIT_FAILURE); } gslots[i].fd[m] = r; } else if (gfiles[m].type == STATIC || (gfiles[m].type == NONE && !gslots[i].outisfolder)) { r = openat(gslots[i].dirfd, gfiles[m].name, gfiles[m].flags, 0644); if (r < 0) { - perror("open"); + perror("openat"); exit(EXIT_FAILURE); } gslots[i].fd[m] = r; @@ -797,6 +863,8 @@ toxinit(void) tox_callback_status_message(tox, cbstatusmessage, NULL); tox_callback_user_status(tox, cbuserstatus, NULL); tox_callback_file_control(tox, cbfilecontrol, NULL); + tox_callback_file_send_request(tox, cbfilesendreq, NULL); + tox_callback_file_data(tox, cbfiledata, NULL); return 0; } @@ -893,13 +961,15 @@ friendcreate(int32_t fid) } r = openat(f->dirfd, ffiles[i].name, ffiles[i].flags, 0644); if (r < 0) { - perror("open"); - exit(EXIT_FAILURE); + if (errno != ENXIO) { + perror("openat"); + exit(EXIT_FAILURE); + } } } else if (ffiles[i].type == STATIC) { r = openat(f->dirfd, ffiles[i].name, ffiles[i].flags, 0644); if (r < 0) { - perror("open"); + perror("openat"); exit(EXIT_FAILURE); } } @@ -908,9 +978,11 @@ friendcreate(int32_t fid) ftruncate(f->fd[FNAME], 0); dprintf(f->fd[FNAME], "%s\n", f->namestr); + ftruncate(f->fd[FONLINE], 0); dprintf(f->fd[FONLINE], "%s\n", tox_get_friend_connection_status(tox, fid) == 0 ? "0" : "1"); + r = tox_get_status_message_size(tox, fid); if (r > sizeof(status) - 1) r = sizeof(status) - 1; @@ -918,6 +990,9 @@ friendcreate(int32_t fid) ftruncate(f->fd[FSTATUS], 0); dprintf(f->fd[FSTATUS], "%s\n", status); + ftruncate(f->fd[FFILE_PENDING], 0); + dprintf(f->fd[FFILE_PENDING], "%d\n", 0); + TAILQ_INSERT_TAIL(&friendhead, f, entry); return f; @@ -1049,7 +1124,7 @@ loop(void) struct request *req, *rtmp; time_t t0, t1, now; int connected = 0; - int i, n; + int i, n, r; int fdmax; char c; fd_set rfds; @@ -1142,6 +1217,30 @@ loop(void) sendfriendfile(f); } + /* Accept pending transfers if any */ + TAILQ_FOREACH(f, &friendhead, entry) { + if (tox_get_friend_connection_status(tox, f->fid) == 0) + continue; + if (f->recvfilepending == 0) + continue; + if (f->fd[FFILE_OUT] == -1) { + r = openat(f->dirfd, ffiles[FFILE_OUT].name, + ffiles[FFILE_OUT].flags, 0644); + if (r < 0) { + if (errno != ENXIO) { + perror("openat"); + exit(EXIT_FAILURE); + } + } else { + f->fd[FFILE_OUT] = r; + tox_file_send_control(tox, f->fid, 1, 0, + TOX_FILECONTROL_ACCEPT, NULL, 0); + printout("Accepted transfer from %s\n", + f->namestr[0] == '\0' ? "Anonymous" : f->namestr); + } + } + } + if (n == 0) continue;