commit 9ec6ecb6fc5ef2551b705aa01c01dbe880696881
parent 184804c60c2112538fa3adbe8e4d06c1bdcb29e7
Author: sin <sin@2f30.org>
Date: Thu, 18 Sep 2014 10:45:55 +0100
Rework file-transfers
For aborted transfers, we will have to move the transfer to the
TRANSFER_KILLED state. Then we'll have to wait until the input
FIFO stops being readable (the user ^C the transfer). Only then can
we move to TRANSFER_NONE.
If we move to TRANSFER_NONE immediately then we will initiate a
new transfer which we don't want to do.
The basic idea is:
If we move into the TRANSFER_KILLED state and timeout has expired (50ms?)
without the fd being readable (the user did a ^C on cat file > file_in)
then move the transfer to TRANSFER_NONE state.
Diffstat:
M | ratox.c | | | 81 | +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------- |
1 file changed, 55 insertions(+), 26 deletions(-)
diff --git a/ratox.c b/ratox.c
@@ -108,6 +108,7 @@ struct transfer {
ssize_t n;
int pending;
int state;
+ time_t lastsent;
};
struct friend {
@@ -146,6 +147,7 @@ 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 completefile(struct friend *);
static void sendfriendfile(struct friend *);
static void dataload(void);
static void datasave(void);
@@ -422,6 +424,7 @@ cbfilecontrol(Tox *m, int32_t fid, uint8_t rec_sen, uint8_t fnum, uint8_t ctrlty
f->t.n = 0;
f->t.pending = 0;
f->t.state = TRANSFER_INPROGRESS;
+ f->t.lastsent = time(NULL);
break;
}
}
@@ -443,6 +446,13 @@ cbfilecontrol(Tox *m, int32_t fid, uint8_t rec_sen, uint8_t fnum, uint8_t ctrlty
}
static void
+completefile(struct friend *f)
+{
+ tox_file_send_control(tox, f->fid, 0, f->t.fnum,
+ TOX_FILECONTROL_FINISHED, NULL, 0);
+}
+
+static void
sendfriendfile(struct friend *f)
{
ssize_t n;
@@ -454,6 +464,7 @@ sendfriendfile(struct friend *f)
/* bad luck - we will try again later */
break;
}
+ f->t.lastsent = time(NULL);
f->t.pending = 0;
}
/* grab another buffer from the FIFO */
@@ -467,13 +478,6 @@ sendfriendfile(struct friend *f)
perror("read");
exit(EXIT_FAILURE);
}
- /* we are done */
- if (n == 0) {
- tox_file_send_control(tox, f->fid, 0, f->t.fnum,
- TOX_FILECONTROL_FINISHED, NULL, 0);
- f->t.state = TRANSFER_DONE;
- break;
- }
/* store transfer size in case we can't send it right now */
f->t.n = n;
if (tox_file_send_data(tox, f->fid, f->t.fnum, f->t.buf, f->t.n) == -1) {
@@ -481,6 +485,7 @@ sendfriendfile(struct friend *f)
f->t.pending = 1;
return;
}
+ f->t.lastsent = time(NULL);
}
}
@@ -1029,7 +1034,7 @@ static void
loop(void)
{
struct friend *f;
- time_t t0, t1;
+ time_t t0, t1, now;
int connected = 0;
int i, n;
int fdmax;
@@ -1069,11 +1074,20 @@ loop(void)
TAILQ_FOREACH(f, &friendhead, entry) {
/* Only monitor friends that are online */
if (tox_get_friend_connection_status(tox, f->fid) == 1) {
- for (i = 0; i < NR_FFIFOS; i++) {
- FD_SET(f->fd[i], &rfds);
- if (f->fd[i] > fdmax)
- fdmax = f->fd[i];
- }
+ FD_SET(f->fd[TEXT_IN_FIFO], &rfds);
+ if (f->fd[TEXT_IN_FIFO] > fdmax)
+ fdmax = f->fd[TEXT_IN_FIFO];
+ /* If the transfer has just been initiated
+ * wait until we have a state change before we start
+ * polling. Avoids spinning endlessly while waiting
+ * for the transfer to be accepted by the other
+ * party.
+ */
+ if (f->t.state == TRANSFER_INITIATED)
+ continue;
+ FD_SET(f->fd[FILE_IN_FIFO], &rfds);
+ if (f->fd[FILE_IN_FIFO] > fdmax)
+ fdmax = f->fd[FILE_IN_FIFO];
}
}
@@ -1096,6 +1110,7 @@ loop(void)
printout("Stale transfer detected, friend offline\n");
f->t.state = TRANSFER_NONE;
free(f->t.buf);
+ f->t.buf = NULL;
}
}
}
@@ -1111,18 +1126,37 @@ loop(void)
TAILQ_FOREACH(f, &friendhead, entry) {
if (tox_get_friend_connection_status(tox, f->fid) == 0)
continue;
- if (f->t.state == TRANSFER_NONE)
+ if (f->t.state != TRANSFER_INPROGRESS)
continue;
- if (f->t.pending == 0)
+ if (f->t.pending == 1)
+ sendfriendfile(f);
+ }
+
+ /* Check for completed file transfers */
+ now = time(NULL);
+ TAILQ_FOREACH(f, &friendhead, entry) {
+ if (tox_get_friend_connection_status(tox, f->fid) == 0)
continue;
switch (f->t.state) {
+ case TRANSFER_NONE:
+ case TRANSFER_INITIATED:
+ break;
case TRANSFER_INPROGRESS:
- sendfriendfile(f);
- if (f->t.state == TRANSFER_DONE) {
- printout("Transfer complete\n");
- f->t.state = TRANSFER_NONE;
- free(f->t.buf);
- }
+ /* We use a 1 second grace period, if no reads
+ * happen during that period we consider the transfer
+ * to have completed and signal the end of transmission
+ * control packet. If we knew the size of the file
+ * it would have been easier to do this but since we are
+ * reading from a FIFO this is not really a possibility.
+ */
+ if (now > f->t.lastsent + 1)
+ completefile(f);
+ break;
+ case TRANSFER_DONE:
+ printout("Transfer complete\n");
+ f->t.state = TRANSFER_NONE;
+ free(f->t.buf);
+ f->t.buf = NULL;
break;
}
}
@@ -1159,11 +1193,6 @@ loop(void)
break;
case TRANSFER_INPROGRESS:
sendfriendfile(f);
- if (f->t.state == TRANSFER_DONE) {
- printout("Transfer complete\n");
- f->t.state = TRANSFER_NONE;
- free(f->t.buf);
- }
break;
}
break;