/* Next File Transfer Client
Copyright (C) 2020 Ian Cowburn
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static const char *name;
static void *Malloc(size_t len)
{
void *p;
p = malloc(len);
if (!p)
{
perror(name);
exit(EXIT_FAILURE);
}
return p;
}
static char *GetLine(char *buff, size_t len)
{
size_t l;
if (feof(stdin))
{
return NULL;
}
printf("NFT> ");
if (!fgets(buff, len, stdin))
{
return NULL;
}
l = strlen(buff);
if (l > 0 && buff[l-1]=='\n')
{
buff[l-1]=0;
}
if (l)
{
return buff;
}
else
{
return GetLine(buff, len);
}
}
static int Connect(const char *n, int p)
{
struct hostent *remote;
struct sockaddr_in addr;
int sock;
if (!(remote = gethostbyname(n)))
{
fprintf(stderr,"%s: unknown host %s\n",name,n);
exit(EXIT_FAILURE);
}
memcpy(&addr.sin_addr, remote->h_addr, remote->h_length);
if ((sock = socket(AF_INET, SOCK_STREAM, 0))==-1)
{
perror(name);
exit(EXIT_FAILURE);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(p);
if (connect(sock,(void*)&addr,sizeof(addr)) == -1)
{
perror(name);
exit(EXIT_FAILURE);
}
return sock;
}
static int Read(int sock, char *buff, size_t len)
{
while(len > 0)
{
size_t rd;
rd = read(sock, buff, len);
if (rd == -1)
{
perror(name);
return 0;
}
else if (rd == 0)
{
return 0;
}
else
{
len -= rd;
buff += rd;
}
}
return 1;
}
static int Write(int sock, const char *buff, size_t len)
{
while(len > 0)
{
size_t wr;
wr = write(sock, buff, len);
if (wr == -1)
{
perror(name);
return 0;
}
else if (wr == 0)
{
return 0;
}
else
{
len -= wr;
buff += wr;
}
}
return 1;
}
static int WriteBlock(int sock, const char *block, size_t len)
{
char buff[32];
if (len > 999999)
{
fprintf(stderr, "%s: length of block %zu too large\n", name, len);
return 0;
}
snprintf(buff, sizeof buff, "%.6zu", len);
if (!Write(sock, buff, 6))
{
return 0;
}
if (!Write(sock, block, len))
{
return 0;
}
return 1;
}
static size_t ReadSize(int sock)
{
char buff[32];
if (!Read(sock, buff, 6))
{
return 0;
}
return atoi(buff);
}
static size_t FileSize(const char *path)
{
struct stat st;
if (stat(path, &st) == 0)
{
return st.st_size;
}
else
{
return 0;
}
}
static int Put(int sock, const char *path)
{
int quit = 0;
char status[3] = {0};
size_t len;
int fd;
if ((len = FileSize(path)) == 0)
{
perror(path);
}
else
{
char *buff;
int fd;
buff = Malloc(len);
fd = open(path, O_RDONLY);
read(fd, buff, len);
close(fd);
if (!Write(sock, "PF", 2))
{
quit = 1;
}
if (!quit && !WriteBlock(sock, path, strlen(path)))
{
quit = 1;
}
if (!quit && !WriteBlock(sock, buff, len))
{
quit = 1;
}
if (!quit && Read(sock, status, 2))
{
if (strcmp(status, "!E") == 0)
{
printf("Command failed\n");
}
else if (strcmp(status, "OK") == 0)
{
printf("OK\n");
}
else
{
printf("Unknown status '%s'\n", status);
}
}
else
{
quit = 1;
}
free(buff);
}
return quit;
}
static int Get(int sock, const char *path)
{
int quit = 0;
char status[3] = {0};
int fd;
if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1)
{
perror(path);
return 0;
}
if (!Write(sock, "GF", 2))
{
quit = 1;
}
if (!quit && !WriteBlock(sock, path, strlen(path)))
{
quit = 1;
}
if (!quit && Read(sock, status, 2))
{
if (strcmp(status, "!E") == 0)
{
printf("Command failed\n");
}
else if (strcmp(status, "OK") == 0)
{
size_t len;
printf("OK\n");
if ((len = ReadSize(sock)) > 0)
{
char *buff;
buff = Malloc(len);
if (Read(sock, buff, len))
{
write(fd, buff, len);
}
else
{
quit = 1;
}
}
}
else
{
printf("Unknown status '%s'\n", status);
}
}
else
{
quit = 1;
}
close(fd);
return quit;
}
static int Cd(int sock, const char *path)
{
int quit = 0;
char status[3] = {0};
size_t len;
int fd;
if (path)
{
if (!Write(sock, "CD", 2))
{
quit = 1;
}
if (!quit && !WriteBlock(sock, path, strlen(path)))
{
quit = 1;
}
if (!quit && Read(sock, status, 2))
{
if (strcmp(status, "!E") == 0)
{
printf("Command failed\n");
}
else if (strcmp(status, "OK") == 0)
{
printf("OK\n");
}
else
{
printf("Unknown status '%s'\n", status);
}
}
else
{
quit = 1;
}
}
else
{
printf("Missing path\n");
}
return quit;
}
static int Mkdir(int sock, const char *path)
{
int quit = 0;
char status[3] = {0};
if (path)
{
if (!Write(sock, "MD", 2))
{
quit = 1;
}
if (!quit && !WriteBlock(sock, path, strlen(path)))
{
quit = 1;
}
if (!quit && Read(sock, status, 2))
{
if (strcmp(status, "!E") == 0)
{
printf("Command failed\n");
}
else if (strcmp(status, "OK") == 0)
{
printf("OK\n");
}
else
{
printf("Unknown status '%s'\n", status);
}
}
else
{
quit = 1;
}
}
else
{
printf("Missing path\n");
}
return quit;
}
int main(int argc, char *argv[])
{
char buff[1024];
int sock;
int quit = 0;
if ((name = strrchr(argv[0], '/')))
{
name++;
}
else
{
name = argv[0];
}
if (argc<3)
{
fprintf(stderr,"%s: usage %s host port\n",name,name);
exit(EXIT_FAILURE);
}
sock = Connect(argv[1],atoi(argv[2]));
while(!quit && GetLine(buff, sizeof buff))
{
char *tok;
tok = strtok(buff, " \t");
if (strcmp(tok, "exit") == 0)
{
break;
}
else if (strcmp(tok, "put") == 0)
{
quit = Put(sock, strtok(NULL, " \t"));
}
else if (strcmp(tok, "get") == 0)
{
quit = Get(sock, strtok(NULL, " \t"));
}
else if (strcmp(tok, "cd") == 0)
{
quit = Cd(sock, strtok(NULL, " \t"));
}
else if (strcmp(tok, "mkdir") == 0)
{
quit = Mkdir(sock, strtok(NULL, " \t"));
}
else
{
printf("Unrecognised command '%s'\n", tok);
}
}
close(sock);
return EXIT_SUCCESS;
}