commit 403cdeb537ab5b000bdf51ba10a3ed6febf01684
parent e00e05d555e3fa031dead6751506aeeba86eb3ed
Author: sin <sin@2f30.org>
Date: Tue, 16 Sep 2014 00:14:00 +0200
Implement file sending
Initially based on a commit by FRIGN.
Diffstat:
M | ratatox.c | | | 145 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 145 insertions(+), 0 deletions(-)
diff --git a/ratatox.c b/ratatox.c
@@ -33,6 +33,7 @@ struct node {
enum {
TEXT_IN_FIFO,
+ FILE_IN_FIFO,
NR_FIFOS
};
@@ -42,6 +43,23 @@ static struct fifo {
mode_t mode;
} fifos[] = {
{ .name = "text_in", .flags = O_RDONLY | O_NONBLOCK, .mode = 0644 },
+ { .name = "file_in", .flags = O_RDONLY | O_NONBLOCK, .mode = 0644 }
+};
+
+enum {
+ TRANSFER_NONE,
+ TRANSFER_INITIATED,
+ TRANSFER_INPROGRESS,
+ TRANSFER_DONE
+};
+
+struct transfer {
+ uint8_t fnum;
+ uint8_t *buf;
+ ssize_t chunksz;
+ ssize_t n;
+ int leftover;
+ int state;
};
struct friend {
@@ -52,6 +70,7 @@ struct friend {
/* null terminated id */
char idstr[2 * TOX_CLIENT_ID_SIZE + 1];
int fd[NR_FIFOS];
+ struct transfer t;
TAILQ_ENTRY(friend) entry;
};
@@ -78,6 +97,8 @@ static void cb_friend_request(Tox *, const uint8_t *, const uint8_t *, uint16_t,
static void cb_name_change(Tox *, int32_t, const uint8_t *, uint16_t, void *);
static void cb_status_message(Tox *, int32_t, const uint8_t *, uint16_t, void *);
static void cb_user_status(Tox *, int32_t, uint8_t, void *);
+static void cb_file_control(Tox *, int32_t, uint8_t, uint8_t, uint8_t, const uint8_t *, uint16_t, void *);
+static void send_friend_file(struct friend *);
static void send_friend_text(struct friend *);
static void dataload(void);
static void datasave(void);
@@ -327,6 +348,90 @@ cb_user_status(Tox *m, int32_t fid, uint8_t status, void *udata)
}
static void
+cb_file_control(Tox *m, int32_t fid, uint8_t rec_sen, uint8_t fnum, uint8_t ctrltype,
+ const uint8_t *data, uint16_t len, void *udata)
+{
+ struct friend *f;
+
+ switch (ctrltype) {
+ case TOX_FILECONTROL_ACCEPT:
+ if (rec_sen == 1) {
+ TAILQ_FOREACH(f, &friendhead, entry) {
+ if (f->fid != fid)
+ continue;
+ f->t.fnum = fnum;
+ f->t.chunksz = tox_file_data_size(tox, fnum);
+ f->t.buf = malloc(f->t.chunksz);
+ if (!f->t.buf) {
+ perror("malloc");
+ exit(EXIT_FAILURE);
+ }
+ f->t.n = 0;
+ f->t.leftover = 0;
+ f->t.state = TRANSFER_INPROGRESS;
+ break;
+ }
+ }
+ break;
+ case TOX_FILECONTROL_FINISHED:
+ if (rec_sen == 1) {
+ TAILQ_FOREACH(f, &friendhead, entry) {
+ if (f->fid != fid)
+ continue;
+ f->t.state = TRANSFER_DONE;
+ printout("Transfer complete\n");
+ break;
+ }
+ }
+ break;
+ default:
+ fprintf(stderr, "Unhandled file control type: %d\n", ctrltype);
+ break;
+ };
+}
+
+static void
+send_friend_file(struct friend *f)
+{
+ ssize_t n;
+
+ if (f->t.leftover == 0) {
+again:
+ n = read(f->fd[FILE_IN_FIFO], f->t.buf, f->t.chunksz);
+ if (n < 0) {
+ if (errno == EINTR)
+ goto again;
+ /* go back to select until the fd is readable */
+ if (errno == EWOULDBLOCK)
+ return;
+ perror("read");
+ exit(EXIT_FAILURE);
+ }
+ if (n == 0) {
+ tox_file_send_control(tox, f->fid, 0, f->t.fnum,
+ TOX_FILECONTROL_FINISHED, NULL, 0);
+ f->t.state = TRANSFER_DONE;
+ return;
+ }
+ f->t.n = n;
+ /* relax - allow for tox_do() to do its job */
+ if (tox_file_send_data(tox, f->fid, f->t.fnum, f->t.buf, f->t.n) == -1) {
+ /* remember to resend the last buffer */
+ f->t.leftover = 1;
+ return;
+ }
+ goto again;
+ } else {
+ if (tox_file_send_data(tox, f->fid, f->t.fnum, f->t.buf, f->t.n) == -1) {
+ /* we might be hitting here too hard, maybe relax()? */
+ return;
+ }
+ f->t.leftover = 0;
+ goto again;
+ }
+}
+
+static void
send_friend_text(struct friend *f)
{
uint8_t buf[TOX_MAX_MESSAGE_LENGTH];
@@ -337,6 +442,9 @@ again:
if (n < 0) {
if (errno == EINTR)
goto again;
+ /* go back to select until the fd is readable */
+ if (errno == EWOULDBLOCK)
+ return;
perror("read");
exit(EXIT_FAILURE);
}
@@ -429,6 +537,7 @@ toxinit(void)
tox_callback_name_change(tox, cb_name_change, NULL);
tox_callback_status_message(tox, cb_status_message, NULL);
tox_callback_user_status(tox, cb_user_status, NULL);
+ tox_callback_file_control(tox, cb_file_control, NULL);
tox_get_address(tox, address);
printf("ID: ");
@@ -798,6 +907,27 @@ loop(void)
perror("select");
exit(EXIT_FAILURE);
}
+
+ /* If we hit the receiver too hard, we will run out of
+ * local buffer slots. In that case tox_file_send_data()
+ * will return -1 and we will have to queue the buffer to
+ * send it later. If this is the last buffer read from
+ * the FIFO, then select() won't make the fd readable again
+ * so we have to check if there's anything pending to be
+ * sent.
+ */
+ TAILQ_FOREACH(f, &friendhead, entry) {
+ if (f->t.state == TRANSFER_NONE)
+ continue;
+ switch (f->t.state) {
+ case TRANSFER_INPROGRESS:
+ send_friend_file(f);
+ if (f->t.state == TRANSFER_DONE)
+ f->t.state = TRANSFER_NONE;
+ break;
+ }
+ }
+
if (n == 0)
continue;
@@ -812,6 +942,21 @@ loop(void)
case TEXT_IN_FIFO:
send_friend_text(f);
break;
+ case FILE_IN_FIFO:
+ switch (f->t.state) {
+ case TRANSFER_NONE:
+ /* prepare a new transfer */
+ f->t.state = TRANSFER_INITIATED;
+ tox_new_file_sender(tox, f->fid,
+ 0, (uint8_t *)"file", strlen("file") + 1);
+ break;
+ case TRANSFER_INPROGRESS:
+ send_friend_file(f);
+ if (f->t.state == TRANSFER_DONE)
+ f->t.state = TRANSFER_NONE;
+ break;
+ }
+ break;
default:
fprintf(stderr, "Unhandled FIFO read\n");
}