+#include <time.h>
#include <stdio.h>
#include <netdb.h>
#include <ctype.h>
#include <sys/socket.h>
#include "knetfile.h"
+static int socket_wait(int fd, int is_read)
+{
+ fd_set fds, *fdr = 0, *fdw = 0;
+ struct timeval tv;
+ int ret;
+ tv.tv_sec = 5; tv.tv_usec = 0; // 5 seconds time out
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ if (is_read) fdr = &fds;
+ else fdw = &fds;
+ ret = select(fd+1, fdr, fdw, 0, &tv);
+ if (ret == -1) perror("select");
+ return ret;
+}
+
static int kftp_get_response(knetFile *ftp)
{
unsigned char c;
int n = 0;
char *p;
+ if (socket_wait(ftp->ctrl_fd, 1) <= 0) return 0;
while (read(ftp->ctrl_fd, &c, 1)) { // FIXME: this is *VERY BAD* for unbuffered I/O
-// fputc(c, stderr);
+ //fputc(c, stderr);
if (n >= ftp->max_response) {
ftp->max_response = ftp->max_response? ftp->max_response<<1 : 256;
ftp->response = realloc(ftp->response, ftp->max_response);
static int kftp_send_cmd(knetFile *ftp, const char *cmd, int is_get)
{
+ if (socket_wait(ftp->ctrl_fd, 0) <= 0) return -1; // socket is not ready for writing
write(ftp->ctrl_fd, cmd, strlen(cmd));
return is_get? kftp_get_response(ftp) : 0;
}
int kftp_reconnect(knetFile *ftp)
{
- if (ftp->ctrl_fd) {
+ if (ftp->ctrl_fd >= 0) {
close(ftp->ctrl_fd);
- ftp->ctrl_fd = 0;
+ ftp->ctrl_fd = -1;
}
close(ftp->fd);
return kftp_connect(ftp);
}
// initialize ->type, ->host and ->retr
-knetFile *kftp_prep(const char *fn, const char *mode)
+knetFile *kftp_parse_url(const char *fn, const char *mode)
{
knetFile *fp;
char *p;
l = p - fn - 6;
fp = calloc(1, sizeof(knetFile));
fp->type = KNF_TYPE_FTP;
+ fp->fd = -1;
fp->host = calloc(l + 1, 1);
if (strchr(mode, 'c')) fp->no_reconnect = 1;
strncpy(fp->host, fn + 6, l);
fp->retr = calloc(strlen(p) + 8, 1);
sprintf(fp->retr, "RETR %s\r\n", p);
+ fp->seek_offset = -1;
return fp;
}
// place ->fd at offset off
-int kftp_connect_file(knetFile *fp, off_t off)
+int kftp_connect_file(knetFile *fp)
{
- if (fp->fd) {
+ int ret;
+ if (fp->fd >= 0) {
close(fp->fd);
if (fp->no_reconnect) kftp_get_response(fp);
}
kftp_pasv_prep(fp);
- if (off) {
+ if (fp->offset) {
char tmp[32];
- sprintf(tmp, "REST %lld\r\n", (long long)off);
+ sprintf(tmp, "REST %lld\r\n", (long long)fp->offset);
kftp_send_cmd(fp, tmp, 1);
}
kftp_send_cmd(fp, fp->retr, 0);
kftp_pasv_connect(fp);
- kftp_get_response(fp);
- fp->offset = off;
+ ret = kftp_get_response(fp);
+ if (ret != 150) {
+ fprintf(stderr, "[kftp_connect_file] %s\n", fp->response);
+ close(fp->fd);
+ fp->fd = -1;
+ return -1;
+ }
+ fp->is_ready = 1;
return 0;
}
return 0;
}
if (strstr(fn, "ftp://") == fn) {
- fp = kftp_prep(fn, mode);
+ fp = kftp_parse_url(fn, mode);
if (fp == 0) return 0;
if (kftp_connect(fp) == -1) {
knet_close(fp);
return 0;
}
- kftp_connect_file(fp, 0);
+ kftp_connect_file(fp);
+ if (fp->fd < 0) {
+ knet_close(fp);
+ return 0;
+ }
} else {
int fd = open(fn, O_RDONLY);
if (fd == -1) {
off_t knet_read(knetFile *fp, void *buf, off_t len)
{
off_t l = 0;
+ if (fp->fd < 0) return 0;
if (fp->type == KNF_TYPE_LOCAL) {
l = read(fp->fd, buf, len);
fp->offset += l;
} else {
off_t rest = len, curr;
+ if (fp->is_ready == 0) {
+ if (!fp->no_reconnect) kftp_reconnect(fp);
+ kftp_connect_file(fp);
+ fp->is_ready = 1;
+ }
while (rest) {
+ if (socket_wait(fp->fd, 1) <= 0) break; // socket is not ready for reading
curr = read(fp->fd, buf + l, rest);
if (curr == 0) break; // FIXME: end of file or bad network? I do not know...
l += curr; rest -= curr;
return l;
}
-off_t knet_seek(knetFile *fp, off_t off, int whence)
+int knet_seek(knetFile *fp, off_t off, int whence)
{
if (fp->type == KNF_TYPE_LOCAL) {
- fp->offset = lseek(fp->fd, off, whence);
- return fp->offset;
+ if (lseek(fp->fd, off, whence) == -1) {
+ perror("lseek");
+ return -1;
+ }
+ fp->offset = off;
+ return 0;
}
if (fp->type == KNF_TYPE_FTP) {
- if (whence != SEEK_SET) {
+ if (whence != SEEK_SET) { // FIXME: we can surely allow SEEK_CUR and SEEK_END in future
fprintf(stderr, "[knet_seek] only SEEK_SET is supported for FTP. Offset is unchanged.\n");
return -1;
}
- if (!fp->no_reconnect) kftp_reconnect(fp);
- kftp_connect_file(fp, off);
- return fp->offset;
+ fp->offset = off;
+ fp->is_ready = 0;
+ return 0;
}
- return 0;
+ return -1;
}
int knet_close(knetFile *fp)
{
if (fp == 0) return 0;
- if (fp->ctrl_fd > 0) close(fp->ctrl_fd);
- if (fp->fd > 0) close(fp->fd);
+ if (fp->ctrl_fd >= 0) close(fp->ctrl_fd);
+ if (fp->fd >= 0) close(fp->fd);
free(fp->response); free(fp->retr); free(fp->host);
free(fp);
return 0;