diff --git a/.gitignore b/.gitignore index 845cda6..a31f63b 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,19 @@ dkms.conf # debug information files *.dwo + +# Valgrind debug log +WG_LOG + +#Tags +tags + +#Binaries +bin +build + +#VS code +.vscode + +#Downloads contents +Downloads \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9491643 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +CFLAGS := -ggdb -Wall -pedantic + +SRC_DIR := src +OBJ_DIR := src/obj +BIN_DIR := bin + +SRCS := $(wildcard $(SRC_DIR)/*.c) +OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) + +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(SRC_DIR)/main.h + $(CC) $(CFLAGS) -c $< -o $@ + +bbs-client: $(OBJS) + $(CC) $(CFLAGS) $^ -o $(BIN_DIR)/$@ + +ifneq (clean, $(MAKECMDGOALS)) +-include deps.mk +endif + +deps.mk: $(SRCS) + $(CC) -MM $^ > $(SRC_DIR)/$@ + +clean: + rm -f $(OBJ_DIR)/*.o $(BIN_DIR)/bbs-client + +run: + $(BIN_DIR)/bbs-client + +valgrind: + valgrind --tool=memcheck $(BIN_DIR)/bbs-client \ No newline at end of file diff --git a/README.md b/README.md index aa5b9e8..a3e6b66 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ -# bbs-client-cli -Bulletin Board System (client part) +# Bulletin Board System (Client Side) +This is my sandbox project. I made it test and develop my programming skills. Maybe one day it become a mature and production-ready project. + +## Setup +1. Create necessary file structure by running `$ ./setup.sh`. +It will create all necessary file structure in order to run the program correctly. + +2. Build the binary running `$ make` or `$ make bbs-client` +3. Run it by `$ make run` \ No newline at end of file diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..3f848cb --- /dev/null +++ b/setup.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +mkdir bin +mkdir Downloads \ No newline at end of file diff --git a/src/client.c b/src/client.c new file mode 100644 index 0000000..059dadc --- /dev/null +++ b/src/client.c @@ -0,0 +1,44 @@ +#include "main.h" +#include +#include +#include +#include + +int process_client_command(char *line, int l_len, query_args_t *q_args) { + /* FILE LIST */ + if (!strncmp(line, "file list", sizeof("file list") - 1)) { + write(q_args->sd, "file list", sizeof("file list") - 1); + q_args->state = STATE_FILE_LIST; + return 0; + } + + /* FILE UPLOAD */ + if (!strncmp(line, "file upload", sizeof("file upload") - 1)) { + if (!file_upload_request(line, q_args)) { + q_args->state = STATE_UPLOAD_REQUESTED; + } else { + print_prompt(q_args->params); + q_args->state = WAIT_CLIENT; + } + return 0; + } + + /* FILE DOWNLOAD */ + if (!strncmp(line, "file download", sizeof("file download") - 1)) { + return 0; + } + + /* EXIT */ + if (!strncmp(line, "exit", sizeof("exit") - 1)) { + close(q_args->sd); + printf("Bye!\n"); + exit(0); + } + + return 0; +} + +void print_prompt(params_t *params) { + printf("%s> ", params->uname); + fflush(stdout); +} \ No newline at end of file diff --git a/src/connection.c b/src/connection.c new file mode 100644 index 0000000..4526aa9 --- /dev/null +++ b/src/connection.c @@ -0,0 +1,139 @@ +#include "main.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void connect_to_server(int sd, params_t *params) { + struct sockaddr_in server; + server.sin_family = AF_INET; + server.sin_addr.s_addr = params->addr; + server.sin_port = params->port; + if (-1 == connect(sd, (struct sockaddr *)&server, sizeof(server))) { + perror("connect"); + exit(2); + } +} + +void init_params(params_t *params) { + params->addr = 0; + params->pass = NULL; + params->uname = NULL; + params->privileges = 0; + params->port = htons(SERVER_PORT); +} + +void clear_params(params_t *params) { + params->addr = 0; + params->pass = NULL; + params->uname = NULL; + params->privileges = 0; + params->port = 0; + free(params->uname); + free(params->pass); + params->pass = NULL; + params->uname = NULL; +} + +void get_missing_params(params_t *params) { + size_t lsize; + char *buf = NULL; + struct termios ts_hide, ts_show; + struct sockaddr_in addr; + + if (isatty(0)) { + tcgetattr(0, &ts_show); + memcpy(&ts_hide, &ts_show, sizeof(ts_show)); + ts_hide.c_lflag &= ~ECHO; + } + + if (!params->addr) { + printf("host> "); + getline(&buf, &lsize, stdin); + inet_aton(buf, &(addr.sin_addr)); + params->addr = addr.sin_addr.s_addr; + free(buf); + buf = NULL; + } + if (params->uname == NULL) { + printf("login> "); + getline(&(params->uname), &lsize, stdin); + lsize = strlen(params->uname); + params->uname[lsize - 1] = '\0'; /* get rid of '\n' */ + free(buf); + buf = NULL; + } + if (params->pass == NULL) { + if (isatty(0)) { + tcsetattr(0, TCSANOW, &ts_hide); + } + printf("password> "); + getline(&(params->pass), &lsize, stdin); + if (isatty(0)) { + tcsetattr(0, TCSANOW, &ts_show); + putchar('\n'); + } + lsize = strlen(params->pass); + params->pass[lsize - 1] = '\0'; /* get rid of '\n' */ + free(buf); + buf = NULL; + } +} + +void close_session(int sd) { close(sd); } + +int init_client() { + int res; + int cs = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in client; + + client.sin_family = AF_INET; + client.sin_port = htons(LOCAL_PORT); + client.sin_addr.s_addr = htonl(INADDR_ANY); + + int opt = 1; + setsockopt(cs, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + res = bind(cs, (struct sockaddr *)&client, sizeof(client)); + if (res == -1) { + perror("bind"); + exit(1); + } + return cs; +} + +void analyze_args(int argc, char *argv[], params_t *params) { + struct sockaddr_in addr; + int i; + for (i = 1; i < argc; i++) { + if (!strcmp("-p", argv[i])) { + i++; + params->port = htons(atoi(argv[i])); + continue; + } + if (!strcmp("-u", argv[i])) { + i++; + params->uname = malloc(strlen(argv[i]) + 1); + strcpy(params->uname, argv[i]); + continue; + } + if (!strcmp("--pass", argv[i])) { + i++; + params->pass = malloc(strlen(argv[i]) + 1); + strcpy(params->pass, argv[i]); + continue; + } + if (!strcmp("-h", argv[i])) { + i++; + inet_aton(argv[i], &(addr.sin_addr)); + params->addr = addr.sin_addr.s_addr; + continue; + } + } +} \ No newline at end of file diff --git a/src/deps.mk b/src/deps.mk new file mode 100644 index 0000000..a5b35c9 --- /dev/null +++ b/src/deps.mk @@ -0,0 +1,6 @@ +client.o: src/client.c src/main.h +connection.o: src/connection.c src/main.h +file_processor.o: src/file_processor.c src/main.h +main.o: src/main.c src/main.h +query.o: src/query.c src/main.h +server.o: src/server.c src/main.h diff --git a/src/file_processor.c b/src/file_processor.c new file mode 100644 index 0000000..e5ddf0d --- /dev/null +++ b/src/file_processor.c @@ -0,0 +1,277 @@ +#include "main.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void fl_add(fl_item_t **cur, fl_item_t **start, char *fname); +static fl_item_t *fl_select(fl_item_t *start, int num); +static void fl_clear(fl_item_t **start, fl_item_t **current); + +static void clear_file_in_query(query_args_t *q_args); + +void file_list(file_args_t *f_args, query_args_t *q_args) { + int qlen; + char *query = NULL; + static int idx = 1; + + while ((qlen = query_extract_from_buf(q_args->buf, &(q_args->buf_used), &query))) { + if (!strcmp("list_end\n", query)) { + q_args->state = STATE_FILE_SELECT; + idx = 1; + break; + } + fl_add(&(f_args->l_current), &(f_args->l_start), query); + printf("[%d] [%s | %s]\n%s\n", idx++, f_args->l_current->name, + f_args->l_current->owner, f_args->l_current->description); + free(query); + query = NULL; + } +} + +void file_select(file_args_t *f_args, query_args_t *q_args) { + char *buf = q_args->buf; + fl_item_t *l_start = f_args->l_start; + fl_item_t *l_current = f_args->l_current; + fl_item_t *l_selected; /* from the list */ + fl_item_t *f_selected = + &(f_args->f_selected); /* new copy of file struct (list be cleared) */ + int filenum; + int qlen; + char send_buf[256]; + struct stat st = {0}; + int sres; + if (!(sres = sscanf(buf, "%d", &filenum))) { + printf("Number format is not correct!\nPlease try again!\n"); + return; + } + + if (filenum == 0) { + print_prompt(q_args->params); + q_args->state = WAIT_CLIENT; + return; + } + l_selected = fl_select(l_start, filenum); + + if (l_selected == NULL) { + printf("Selected number is not correct!\nPlease try again!\n"); + return; + } + + memcpy(f_selected, l_selected, sizeof(*f_selected)); + + if (stat(DOWNLOADS_DIR, &st) == -1) { + mkdir(DOWNLOADS_DIR, 0700); + } + char *file_path = + malloc(sizeof(DOWNLOADS_DIR) + strlen(l_selected->name) + 2); // + '/' + '\0' + f_selected->name = NULL; + sprintf(file_path, "%s/%s", DOWNLOADS_DIR, l_selected->name); + f_selected->name = strdup(l_selected->name); + fl_clear(&f_args->l_start, &f_args->l_current); + f_args->file_d = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (f_args->file_d == -1) { + qlen = sprintf(send_buf, "error: %s\n", f_selected->name); + write(q_args->sd, send_buf, qlen); + perror(f_selected->name); + print_prompt(q_args->params); + q_args->state = WAIT_CLIENT; + free(file_path); + return; + } + qlen = sprintf(send_buf, "file download %s\n", f_selected->name); + write(q_args->sd, send_buf, qlen); + q_args->state = STATE_FILE_DOWNLOAD; + free(file_path); +} + +void file_download(file_args_t *f_args, query_args_t *q_args) { + fl_item_t *f_selected = &(f_args->f_selected); + static int it_count = 0; + static size_t it_interval = 0; + if (it_interval == 0) { + it_interval = (f_selected->size / INBUFSIZE / 100) * 10; /* every 10% */ + if (it_interval == 0) + it_interval = 1; + } + static size_t size_rest = 0; + + if (size_rest == 0) + size_rest = f_selected->size; + int qlen = write(f_args->file_d, q_args->buf, q_args->buf_used); + if (qlen) { + it_count++; + if (!(it_count % it_interval)) { + printf("%zu / %zu\n", f_selected->size - size_rest, f_selected->size); + } + if (it_count % it_interval) + q_args->buf_used = 0; + if (size_rest < qlen) { + size_rest = 0; + } else { + size_rest -= qlen; + } + if (size_rest == 0) { + it_count = 0; + it_interval = 0; + f_selected->size = 0; + printf("File %s is downloaded\n", f_selected->name); + close(f_args->file_d); + free(f_selected->name); + print_prompt(q_args->params); + q_args->state = WAIT_CLIENT; + } + } +} + +int file_upload_request(char *query, query_args_t *q_args) { + char qbuf[INBUFSIZE]; + char fpath[512]; char a_perm; + sscanf(query, "file upload \"%s %c", fpath, &a_perm); + fpath[strlen(fpath)-1] = 0; // remove \" + int fd = open(fpath, O_RDONLY); + if (fd == -1) { + perror(fpath); + return -1; + } + size_t fsize = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + char *fname = strrchr(fpath, '/') + 1; + int qlen = snprintf(qbuf, INBUFSIZE, "file upload \"%s\" %zu %c", fname, + fsize, a_perm); + if (qlen > INBUFSIZE) { + fprintf(stderr, "query is too large to be sent\n"); + return -1; + } + q_args->file = malloc(sizeof(p_file_t)); + q_args->file->fd = fd; + q_args->file->name = malloc(strlen(fname) + 1); + strcpy(q_args->file->name, fname); + q_args->file->size = fsize; + q_args->file->rest = fsize; + write(q_args->sd, qbuf, qlen); + return 0; +} + +int file_upload_start(query_args_t *q_args) { + char *query; + query_extract_from_buf(q_args->buf, &(q_args->buf_used), &query); + if (strcmp(query, "accept")) { + clear_file_in_query(q_args); + write(STDERR_FILENO, query, strlen(query)); + return -1; + } + return 0; +} + +int file_upload(query_args_t *q_args) { + /* static size_t it_int = 0; + static int it_count = 0; + + if (it_int == 0) { + it_int = (q_args->file->size / INBUFSIZE / 100) * 10; // every 10% +} */ + + if (q_args->buf_used > 0) { + int wlen = write(q_args->sd, q_args->buf, q_args->buf_used); + if (q_args->buf_used != wlen) { + /* TODO: ошибка */ + return -1; + } else { + q_args->buf[0] = 0; + q_args->buf_used = 0; + q_args->file->rest -= wlen; + } + } else { + /* file upload is finished */ + printf("Upload of %s is finished.\n", q_args->file->name); + /* TODO: ждать ответа от сервера */ + clear_file_in_query(q_args); + return 1; + } + return 0; +} + +static void clear_file_in_query(query_args_t *q_args) { + free(q_args->file->name); + free(q_args->file); + q_args->file = NULL; +} + +void init_file_args(file_args_t *f_args) { + f_args->file_d = 0; + f_args->f_selected.name = NULL; + f_args->f_selected.next = NULL; + f_args->f_selected.size = 0; + f_args->l_current = NULL; + f_args->l_start = NULL; +} + +/* work with file list */ +static void fl_add(fl_item_t **cur, fl_item_t **start, char *line) { + char fname[128]; size_t fsize; char fowner[32]; int h_len; + fl_item_t *fitem = malloc(sizeof(fl_item_t)); + sscanf(line, "%s %zu %s%n", fname, &(fitem->size), fowner, &h_len); + /* file name */ + fitem->name = malloc((sizeof(char))*(strlen(fname)+1)); + strcpy(fitem->name, fname); + /* file owner */ + fitem->owner = malloc((sizeof(char))*(strlen(fowner)+1)); + strcpy(fitem->owner, fowner); + /* file description */ + int d_len = strlen(line)-h_len; + fitem->description = malloc((sizeof(char))*(d_len)); + strcpy(fitem->description, line + h_len+1); // +1 to escape the dividing \32 + int i; + for (i = 0; i < d_len; i++) { + if (fitem->description[i] == '\a') { + fitem->description[i] = '\n'; + } + } + /* fitem->description[strlen(line + line_pos)] = 0; */ + fitem->next = NULL; + if (*cur != NULL) { + (*cur)->next = fitem; + } else { + *start = fitem; + } + *cur = fitem; +} + +static fl_item_t *fl_select(fl_item_t *start, int num) { + fl_item_t *current = start; + for (; num != 1; num--) { + if (current->next != NULL) { + current = current->next; + } else { + return NULL; + } + } + return current; +} + +static void fl_clear(fl_item_t **start, fl_item_t **arg_current) { + if (*start == NULL) + return; + fl_item_t *next, *current; + current = *start; + + do { + next = current->next; + free(current->description); + free(current->name); + free(current); + } while ((current = next) != NULL); + *start = NULL; + *arg_current = NULL; +} \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..4240e1f --- /dev/null +++ b/src/main.c @@ -0,0 +1,23 @@ +#include "main.h" +#include +#include +#include +#include +#include +#include +#include + +/* TODO: перед загрузкой файла спрашивать права доступа + TODO: сделать при интерфес выбора по пунктам меню */ + +int main(int argc, char *argv[]) { + params_t params; + init_params(¶ms); + analyze_args(argc, argv, ¶ms); + get_missing_params(¶ms); + int sd = init_client(); + connect_to_server(sd, ¶ms); + query_loop(sd, ¶ms); + clear_params(¶ms); + return 0; +} \ No newline at end of file diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..33b3d8f --- /dev/null +++ b/src/main.h @@ -0,0 +1,85 @@ +#include + +#define LOCAL_PORT 1999 +#define SERVER_PORT 2000 +#define LISTEN_QLEN 32 +#define INBUFSIZE 1024 +#define DOWNLOADS_DIR "./Downloads" + +typedef struct params { + unsigned short port; + unsigned addr; + char *uname; + char *pass; + char privileges; +} params_t; + +enum state { + WAIT_SERVER_INIT, + WAIT_SERVER, + WAIT_CLIENT, + UPLOAD_FILE, + STATE_FILE_LIST, + STATE_FILE_SELECT, + STATE_FILE_DOWNLOAD, + STATE_UPLOAD_PARAMS, + STATE_UPLOAD_FILE, + STATE_UPLOAD_REQUESTED, + ERR, +}; +typedef struct p_file { + char *name; + size_t size; + size_t rest; + int fd; +} p_file_t; + +typedef struct fl_item { + size_t size; + char *name; + char *description; + char *owner; + struct fl_item *next; +} fl_item_t; + +typedef struct file_args { + int file_d; + fl_item_t f_selected; + fl_item_t *l_start; + fl_item_t *l_current; +} file_args_t; + +typedef struct query_args { + int sd; + int buf_used; + p_file_t *file; + enum state state; + char *buf; + char from_server; + params_t *params; +} query_args_t; + +void init_params(params_t *restrict); +void analyze_args(int argc, char *argv[], params_t *restrict); +void get_missing_params(params_t *restrict); +void connect_to_server(int sd, params_t *params); +int init_client(); +void close_session(int sd); +int process_query(query_args_t *query_args, file_args_t *file_args); +void query_loop(int sd, params_t *); +int query_extract_from_buf(char *buf, int *buf_used, char **output_line); +int process_client_command(char *line, int l_len, query_args_t *q_args); +int process_server_command(char *line, int l_len, query_args_t *q_args); + +void clear_params(params_t *params); + +/* file processor */ +void file_list(file_args_t *f_args, query_args_t *q_args); +void file_download(file_args_t *f_args, query_args_t *q_args); +void file_select(file_args_t *fargs, query_args_t *q_args); +void init_file_args(file_args_t *fargs); +int file_upload_request(char *, query_args_t *q_args); +int file_upload_start(query_args_t *q_args); +int file_upload(query_args_t *q_args); + +void print_prompt(params_t *params); \ No newline at end of file diff --git a/src/query.c b/src/query.c new file mode 100644 index 0000000..4ae3e6a --- /dev/null +++ b/src/query.c @@ -0,0 +1,234 @@ +#include "main.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void init_query_args(query_args_t *q_args, int sd, char *buf, + enum state state, int buf_used, params_t *params); +static void wait_side(query_args_t *q_args); +void user_request_description(query_args_t *q_args); + +void query_loop(int sd, params_t *params) { + fd_set readfds; + + size_t qlen; + int sr; + char buf[INBUFSIZE]; + char *bufs = buf; + static file_args_t file_args; + static query_args_t query_args; + + init_query_args(&query_args, sd, buf, WAIT_SERVER_INIT, 0, params); + init_file_args(&file_args); + + for (;;) { + FD_ZERO(&readfds); + FD_SET(STDIN_FILENO, &readfds); + FD_SET(sd, &readfds); + int maxfd = sd; + if (query_args.state == STATE_UPLOAD_FILE) { + if (query_args.file != NULL) { + FD_SET(query_args.file->fd, &readfds); + if (query_args.file->fd > maxfd) + maxfd = query_args.file->fd; + } + } + sr = select(maxfd + 1, &readfds, NULL, NULL, NULL); + if (sr == -1) { + perror("select"); + exit(3); + } + + if (FD_ISSET(sd, &readfds)) { + /* process request from the server */ + qlen = read(sd, buf, INBUFSIZE); + if (qlen == 0) { + close_session(sd); + printf("\n*** server closed the connection. ***\n"); + exit(1); + } else { + query_args.buf_used = qlen; + query_args.from_server = 1; + if (-1 == process_query(&query_args, &file_args)) { + close_session(sd); + exit(1); + } + } + } + + if (query_args.file && query_args.file->fd > -1 && + FD_ISSET(query_args.file->fd, &readfds)) { + /* process upload/download */ + query_args.buf_used = read(query_args.file->fd, buf, INBUFSIZE); + process_query(&query_args, &file_args); + } + + if (FD_ISSET(STDIN_FILENO, &readfds)) { + /* process request from the client */ + qlen = read(STDIN_FILENO, buf, INBUFSIZE); + if (qlen == 0) { + close_session(sd); + exit(0); + } else { + query_args.buf_used = qlen; + query_args.from_server = 0; + if (-1 == process_query(&query_args, &file_args)) { + close_session(sd); + exit(1); + } + } + } + } +} + +int process_query(query_args_t *query_args, file_args_t *file_args) { + query_args->buf[query_args->buf_used] = 0; + + switch (query_args->state) { + case WAIT_SERVER: + case WAIT_SERVER_INIT: + case WAIT_CLIENT: + wait_side(query_args); + break; + case UPLOAD_FILE: + case ERR: + break; + case STATE_FILE_LIST: + file_list(file_args, query_args); + break; + case STATE_FILE_SELECT: + file_select(file_args, query_args); + break; + case STATE_FILE_DOWNLOAD: + file_download(file_args, query_args); + break; + case STATE_UPLOAD_REQUESTED: + if (!file_upload_start(query_args)) { + query_args->state = STATE_UPLOAD_FILE; + } else { + print_prompt(query_args->params); + query_args->state = WAIT_CLIENT; + } + break; + case STATE_UPLOAD_PARAMS: + case STATE_UPLOAD_FILE: + if (file_upload(query_args)) { + /* upload finishes */ + user_request_description(query_args); + print_prompt(query_args->params); + query_args->state = WAIT_CLIENT; + } + break; + } + return 0; +} + +int query_extract_from_buf(char *buf, int *buf_used, char **output_line) { + char *line; + int pos = -1; + + if (*buf_used > 0) { + char *cptr = strchr(buf, '\n'); + if (cptr != NULL) + pos = cptr - buf; + } else { + return 0; + } + + if (pos == -1) { + int b_used = *buf_used; + *buf_used = 0; + line = malloc(b_used + 1); + strncpy(line, buf, b_used); + + // line[b_used] = 0; + + *output_line = line; + return b_used; + } else { + line = malloc(pos + 2); + strncpy(line, buf, pos + 1); + line[++pos] = 0; + *buf_used -= (pos); + if (!pos) + pos++; + memmove(buf, buf + pos, *buf_used); + buf[*buf_used] = 0; + if (line[pos-1] == '\r') + line[--pos] = 0; + *output_line = line; + } + return pos + 1; +} + +static void wait_side(query_args_t *q_args) { + int qlen; + int buf_used = q_args->buf_used; + char *query = NULL; + + if (q_args->state == WAIT_SERVER || q_args->state == WAIT_SERVER_INIT || q_args->from_server) { + while ((qlen = query_extract_from_buf(q_args->buf, &buf_used, &query))) { + process_server_command(query, qlen, q_args); + free(query); + query = NULL; + } + } else if (q_args->state == WAIT_CLIENT) { + while ((qlen = query_extract_from_buf(q_args->buf, &buf_used, &query))) { + process_client_command(query, qlen, q_args); + free(query); + query = NULL; + } + } +} + +static void init_query_args(query_args_t *q_args, int sd, char *buf, + enum state state, int buf_used, params_t *params) { + q_args->buf = buf; + q_args->sd = sd; + q_args->state = state; + q_args->buf_used = buf_used; + q_args->params = params; + q_args->file = NULL; + q_args->from_server = 0; +} + +void user_request_description(query_args_t *q_args) { + printf("File description > "); + fflush(stdout); + char buf[INBUFSIZE]; /* пока ограничение на буфер */ + char *line_buf = NULL; + int blen = 0; + int line_len = 0; + size_t ll; + while (blen < INBUFSIZE - 6) { /* reserve space for ":END:\n" */ + line_len = getline(&line_buf, &ll, stdin); + if (line_len == 1) { + /* free(line); -> не работает */ + break; + } + if (blen == 0) { + strcpy(buf, line_buf); + } else { + strcat(buf, line_buf); + } + free(line_buf); + line_buf = NULL; + blen += line_len; + } + + strcat(buf, ":END:\n"); + blen += 6; + write(q_args->sd, buf, blen); +} \ No newline at end of file diff --git a/src/server.c b/src/server.c new file mode 100644 index 0000000..abb9177 --- /dev/null +++ b/src/server.c @@ -0,0 +1,88 @@ +#include "main.h" +#include +#include +#include +#include +#include + +int process_server_command(char *line, int l_len, query_args_t *q_args) { + int ws_pos = l_len; + params_t *params = q_args->params; + + char *cptr = strchr(line, ' '); + if (cptr != NULL && cptr > line) + ws_pos = cptr - line; + + /* LOGIN */ + if (!strncmp(line, "login>", ws_pos)) { + write(q_args->sd, params->uname, strlen(params->uname)); + return 0; + } + + /* PASSWORD */ + if (!strncmp(line, "password>", ws_pos)) { + write(q_args->sd, params->pass, strlen(params->pass)); + return 0; + } + + /* LOGIN AGAIN */ + if (!strncmp(line, "login_again>", ws_pos)) { + free(params->uname); + free(params->pass); + params->uname = NULL; + params->pass = NULL; + get_missing_params(params); + write(q_args->sd, params->uname, strlen(params->uname)); + return 0; + } + + /* DOWNLOAD START */ + if (!strncmp(line, "download_start", ws_pos)) { + return 0; + } + + /* WELCOME MES */ + if (!strncmp(line, "Welcome, ", ws_pos)) { + print_prompt(q_args->params); + q_args->state = WAIT_CLIENT; + return 0; + } + + if (q_args->state == WAIT_SERVER_INIT) { + write(STDOUT_FILENO, line, l_len); + } else { + char buf[INBUFSIZE + sizeof("server: ")] = "server: "; + strcat(buf, line); + write(STDOUT_FILENO, buf, l_len + sizeof("server: ")); + if (q_args->state == WAIT_CLIENT) { + print_prompt(q_args->params); + } + } + + return 0; +} + +static void file_receive(int sd, char *line) { + char fname[128]; char command[32]; size_t fsize; + sscanf(line, "%s %s %zu", command, fname, &fsize); + char buf[INBUFSIZE]; + int rlen; + + int file_d = open(fname, O_WRONLY); + + if (file_d == -1) { + char err_mes[256]; + int mlen; + mlen = sprintf(err_mes, "Can't write file named \"%s\"\n", fname); + write(sd, err_mes, mlen); + close(file_d); + return; + } + + while (fsize && (rlen = read(sd, buf, INBUFSIZE))) { + write(file_d, buf, rlen); + fsize -= rlen; + } + + close(file_d); +} \ No newline at end of file