diff --git a/TODO.md b/TODO.md index b147059..0b539ce 100644 --- a/TODO.md +++ b/TODO.md @@ -3,6 +3,8 @@ ## Features - [ ] Database integration +- [ ] Paging in the file list +- [ ] Get files in FS by their hash - [ ] Registration - [ ] Message to admin - [ ] Advanced folder structure for the storage diff --git a/src/client.c b/src/client.c index e1a8e07..bb82b08 100644 --- a/src/client.c +++ b/src/client.c @@ -25,7 +25,7 @@ void process_client_command(char *line, session *sess, server_data_t *s_d) { /* UPLOAD */ if (!strcmp(arg_2, "upload")) { res = file_receive_prepare(sess, line, s_d); - if (!res) + if (!res) sess->state = OP_UPLOAD; return; } diff --git a/src/db.c b/src/db.c index 64d06e5..a31bdbe 100644 --- a/src/db.c +++ b/src/db.c @@ -1,5 +1,6 @@ #include "db.h" #include "main.h" +#include #include #include #include @@ -150,7 +151,7 @@ int db_save_file(session *s) { if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res)) return exit_query_2(0); - + int id = atoi(PQgetvalue(res, 0, 0)); clearRes(); return id; @@ -161,39 +162,97 @@ int db_save_file(session *s) { // } -int db_get_files(i_get_files_db *arg) { - const char *paramValues[3]; - int paramLengths[3]; - int paramFormats[3]; - int i, j, nFields; +uint64_t db_get_files_data(i_get_files_db *arg, fl_t **fl_start, + uint64_t *full_count) { + fl_t *fl_current; + char query[512]; + char sort_by[16] = "id"; + char sort_dir[5] = "ASC"; + const char *paramValues[2]; + int paramLengths[2]; + int paramFormats[2]; + int i; + uint64_t count; + uint64_t n_limit = htobe64(arg->limit); + uint64_t n_offset = htobe64(arg->offset); - paramValues[0] = arg->sort_by; - paramValues[1] = (char *)&arg->limit; - paramValues[2] = (char *)&arg->offset; + switch (arg->sort_by) { + case NAME: + strcpy(sort_by, "name"); + break; + case CREATED_AT: + strcpy(sort_by, "created_at"); + break; + case USER_ID: + strcpy(sort_by, "user_id"); + break; + case ID: // id is by default + default: + break; + } - paramLengths[0] = strlen(arg->sort_by); - paramLengths[1] = sizeof(arg->limit); - paramLengths[2] = sizeof(arg->offset); + if (arg->sort_direction == DESC) { + strcpy(sort_dir, "DESC"); + } - paramFormats[0] = TEXT; + paramValues[0] = (char *)&n_limit; + paramValues[1] = (char *)&n_offset; + + paramLengths[0] = sizeof(n_limit); + paramLengths[1] = sizeof(n_offset); + + paramFormats[0] = BIN; paramFormats[1] = BIN; - paramFormats[2] = BIN; - res = PQexecParams(conn, "SELECT * FROM files ORDER BY $1 LIMIT $2 OFFSET $3", - 3, NULL, paramValues, paramLengths, paramFormats, TEXT); + sprintf( + query, + "SELECT user_id, name, size, description, permissions, hash, username " + "FROM files JOIN users ON user_id = users.id " + "ORDER BY files.%s %s LIMIT $1 OFFSET $2", + sort_by, sort_dir); - nFields = PQnfields(res); - for (i = 0; i < nFields; i++) - printf("%-15s", PQfname(res, i)); - printf("\n\n"); + res = PQexecParams(conn, query, 2, NULL, paramValues, paramLengths, + paramFormats, TEXT); + + if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res)) + return exit_query_2(0); for (i = 0; i < PQntuples(res); i++) { - for (j = 0; j < nFields; j++) - printf("%-15s", PQgetvalue(res, i, j)); - printf("\n"); + fl_t *l_item = malloc(sizeof(fl_t)); + l_item->next = NULL; + l_item->owner_id = atoi(PQgetvalue(res, i, 0)); + char *name = PQgetvalue(res, i, 1); + l_item->name = malloc(strlen(name) + 1); + strcpy(l_item->name, name); + l_item->size = atoll(PQgetvalue(res, i, 2)); + char *description = PQgetvalue(res, i, 3); + l_item->description = malloc(strlen(description) + 1); + strcpy(l_item->description, description); + l_item->permissions = atoi(PQgetvalue(res, i, 4)); + l_item->hash = atoi(PQgetvalue(res, i, 5)); + char *owner = PQgetvalue(res, i, 6); + l_item->owner = malloc(strlen(owner) + 1); + strcpy(l_item->owner, owner); + + if (*fl_start == NULL) { + *fl_start = l_item; + } else { + fl_current->next = l_item; + } + fl_current = l_item; } + count = PQntuples(res); + clearRes(); - return 0; + res = PQexec(conn, "SELECT COUNT(id) " + "FROM files "); // TODO: finish + + if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res)) + return exit_query_2(0); + + *full_count = atoll(PQgetvalue(res, 0, 0)); + clearRes(); + return count; } \ No newline at end of file diff --git a/src/db.h b/src/db.h index d0bf59a..319592d 100644 --- a/src/db.h +++ b/src/db.h @@ -1,3 +1,5 @@ +#ifndef DB_H +#define DB_H #include "main.h" #include @@ -21,15 +23,21 @@ typedef struct { int user_id; } i_get_file_db; -typedef struct { - int user_id; // TODO: implement - char *name; // TODO: implement - int limit; - int offset; - char sort_by[12]; -} i_get_files_db; +enum sort_direction { ASC, DESC }; +enum sort_by { ID, NAME, CREATED_AT, USER_ID }; +typedef struct { + uint32_t user_id; // TODO: implement + char name[12]; // TODO: implement + uint32_t limit; + uint32_t offset; + enum sort_by sort_by; + enum sort_direction sort_direction; +} i_get_files_db; int init_db_connection(); int db_user_auth(i_auth_t *credentials, o_auth_t *response); int db_save_file(session *s); +uint64_t db_get_files_data(i_get_files_db *arg, fl_t **fl_start, + uint64_t *full_count); +#endif \ No newline at end of file diff --git a/src/deps.mk b/src/deps.mk index e69de29..e602749 100644 --- a/src/deps.mk +++ b/src/deps.mk @@ -0,0 +1,9 @@ +client.o: src/client.c src/client.h src/main.h src/file_p.h src/session.h +db.o: src/db.c src/db.h src/main.h +file_p.o: src/file_p.c src/file_p.h src/main.h src/db.h \ + src/libs/murmur3/murmur3.h src/session.h +main.o: src/main.c src/main.h src/server.h src/db.h +server.o: src/server.c src/main.h src/session.h src/user.h +session.o: src/session.c src/session.h src/main.h src/client.h src/db.h \ + src/file_p.h src/user.h +user.o: src/user.c src/main.h src/session.h src/db.h diff --git a/src/file_p.c b/src/file_p.c index 4b9c578..4737772 100644 --- a/src/file_p.c +++ b/src/file_p.c @@ -1,4 +1,5 @@ #include "file_p.h" +#include "db.h" #include "libs/murmur3/murmur3.h" #include "main.h" #include "session.h" @@ -7,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -23,23 +25,69 @@ char *extract_second_arg(char *); size_t get_file_size(char *, char *); +void clear_list(fl_t *start) { + if (start) { + fl_t *curr = start; + fl_t *next; + do { + next = curr->next; + free(curr->description); + free(curr->name); + free(curr->owner); + free(curr); + } while ((curr = next) != NULL); + } +} + void file_list(session *sess, server_data_t *s_d) { - // TODO: change of format (rewrite in C++) char list_end[] = "list_end\n"; - fl_t *current; - current = s_d->fl_start; + fl_t *fl_start, *fl_current; char item_h[256]; + i_get_files_db args; + uint64_t page = 1; + uint64_t full_count, count, pages_count; + + fl_start = NULL; + fl_current = NULL; + + if (sess->fl_start) { + clear_list(fl_start); + sess->fl_start = NULL; + sess->fl_current = NULL; + } + + args.limit = 15; + args.offset = 15 * (page - 1); + args.sort_by = ID; + args.sort_direction = ASC; + + count = db_get_files_data(&args, &fl_start, &full_count); + char page_info[256]; + + pages_count = full_count / 15; + if (!pages_count) + pages_count = 1; + + sprintf(page_info, "PAGE %lu FROM %lu. COUNT: %lu FROM %lu\n", page, + pages_count, count, full_count); + + if (!fl_start) + return; + + fl_current = fl_start; + do { int n_len = strlen(sess->uname); - if (strncmp(current->owner, sess->uname, n_len) && !current->permissions) { + if (strncmp(fl_current->owner, sess->uname, n_len) && + !fl_current->permissions) { continue; } - int h_len = sprintf(item_h, "%s %zu %s", current->name, current->size, - current->owner); - int d_len = strlen(current->description); + int h_len = sprintf(item_h, "%s %zu %s", fl_current->name, fl_current->size, + fl_current->owner); + int d_len = strlen(fl_current->description); int f_len = h_len + d_len + 2; char *full_str = malloc(sizeof(char) * f_len); - sprintf(full_str, "%s %s", item_h, current->description); + sprintf(full_str, "%s %s", item_h, fl_current->description); int i; for (i = h_len; i < h_len + d_len; i++) { if (full_str[i] == '\n') { @@ -48,8 +96,12 @@ void file_list(session *sess, server_data_t *s_d) { } write(sess->sd, full_str, f_len - 1); // -1 : do not include \0 free(full_str); - } while ((current = current->next) != NULL); + sess->fl_current = fl_current; + } while ((fl_current = fl_current->next) != NULL); + + sess->fl_start = fl_start; write(sess->sd, list_end, sizeof(list_end)); + write(sess->sd, page_info, strlen(page_info)); } int file_send_prepare(session *sess, char *line, server_data_t *s_d) { @@ -76,7 +128,6 @@ int file_send_prepare(session *sess, char *line, server_data_t *s_d) { fsize = lseek(file_d, 0, SEEK_END); lseek(file_d, 0, SEEK_SET); - sess->file->fd = file_d; sess->file->size = fsize; sess->file->rest = fsize; sess->file->name = malloc(strlen(fname)); @@ -113,6 +164,7 @@ int file_receive_prepare(session *sess, char *line, server_data_t *s_d) { sscanf(line, "file upload \"%s %zd %d", fname, &fsize, &perm); sess->file = malloc(sizeof(session_file)); sess->file->name = malloc(sizeof(char) * strlen(fname)); + sess->file->path = malloc(sizeof(STORAGE_FOLDER) + 2 + 9); sess->file->permissions = (char)perm; sess->file->description = NULL; strncpy(sess->file->name, fname, strlen(fname) - 1); @@ -143,12 +195,11 @@ int file_receive_prepare(session *sess, char *line, server_data_t *s_d) { int file_d; for (;;) { - MurmurHash3_x86_32(sess->file->name, strlen(sess->file->name), - seed, &sess->file->hash); + MurmurHash3_x86_32(sess->file->name, strlen(sess->file->name), seed, + &sess->file->hash); extract_names_from_hash(sess->file->hash, hashed_dir_name, hashed_name); - sess->file->path = malloc(sizeof(STORAGE_FOLDER) + 2 + 9); sprintf(sess->file->path, "%s/%s/%s", STORAGE_FOLDER, hashed_dir_name, hashed_name); @@ -163,8 +214,6 @@ int file_receive_prepare(session *sess, char *line, server_data_t *s_d) { if (file_d == -1) { if (errno == EEXIST) { seed++; - free(sess->file->path); - sess->file->path = NULL; continue; } mes_len = sprintf(mes, "Can't create file with such name: \"%s\"\n", @@ -178,7 +227,7 @@ int file_receive_prepare(session *sess, char *line, server_data_t *s_d) { } mes_len = sprintf(mes, "accept"); - sess->file->fd = file_d; + sess->fd = file_d; sess->file->size = fsize; sess->file->rest = fsize; write(sd, mes, mes_len); @@ -199,16 +248,17 @@ void clear_file_from_sess(session *s) { free(sf->description); sf->description = NULL; } - if (sf->fd > -1) { - close(sf->fd); + if (s->fd > -1) { + close(s->fd); + s->fd = -1; } free(sf); s->file = NULL; } -void file_download_upload(session *sess, enum f_actions f_action) { - int source_d = f_action == F_DOWNLOAD ? sess->file->fd : sess->sd; - int dest_d = f_action == F_DOWNLOAD ? sess->sd : sess->file->fd; +void file_load(session *sess, enum f_actions f_action) { + int source_d = f_action == F_DOWNLOAD ? sess->fd : sess->sd; + int dest_d = f_action == F_DOWNLOAD ? sess->sd : sess->fd; char buf[INBUFSIZE]; int rlen = read(source_d, buf, INBUFSIZE); if (rlen == 0) { @@ -282,77 +332,10 @@ int file_upload_description(session *sess, char *line, server_data_t *s_d) { } char *desc_end = strstr(line, ":END:"); if (desc_end != NULL) { - sess->file->description[strlen(sess->file->description) - 6] = 0; // cut the :END: + sess->file->description[strlen(sess->file->description) - 6] = + 0; // cut the :END: return 1; } line = NULL; } -} - -// TODO: синхронизовать c C++ -void get_files_descriptions(server_data_t *s_d) { - int fd = open(FILE_DESCRIPTIONS_NAME, O_RDONLY); - if (-1 == fd) { - perror(FILE_DESCRIPTIONS_NAME); - exit(8); - } - int rlen; - - char buf[INBUFSIZE] = ""; - chdir(STORAGE_FOLDER); - - while ((rlen = read(fd, buf, FILEBUFSIZE)) > 0) { - char *buf_pos = buf; - char tmp[256]; - while (buf_pos[0] != '\0') { - fl_t *fl_item = malloc(sizeof(fl_t)); - fl_item->next = NULL; - - /* file name */ - sscanf(buf_pos, "%s\n", tmp); - buf_pos += strlen(tmp) + 1; - fl_item->name = malloc(sizeof(char) * (strlen(tmp) + 1)); - strcpy(fl_item->name, tmp); - - /* file owner */ - sscanf(buf_pos, "%s\n", tmp); - buf_pos += strlen(tmp) + 1; - fl_item->owner = malloc(sizeof(char) * (strlen(tmp) + 1)); - strcpy(fl_item->owner, tmp); - - /* file permissions */ - sscanf(buf_pos, "%s\n", tmp); - buf_pos += strlen(tmp) + 1; - fl_item->permissions = atoi(tmp); - - /* file size */ - int fd = open(fl_item->name, O_RDONLY); - if (-1 == fd) { - perror(fl_item->name); - free(fl_item->name); - free(fl_item->owner); - free(fl_item); - buf_pos += strstr(buf_pos, ":END:") + 6 - buf_pos; - continue; - } - fl_item->size = lseek(fd, 0, SEEK_END); - close(fd); - - /* file description */ - int d_len = strstr(buf_pos, ":END:\n") - buf_pos; - fl_item->description = malloc(d_len + 1); - strncpy(fl_item->description, buf_pos, d_len); - fl_item->description[d_len] = 0; - buf_pos += d_len + 6; - if (s_d->fl_start == NULL) { - s_d->fl_start = fl_item; - s_d->fl_current = fl_item; - } else { - s_d->fl_current->next = fl_item; - s_d->fl_current = fl_item; - } - } - } - close(fd); - chdir(".."); } \ No newline at end of file diff --git a/src/file_p.h b/src/file_p.h index 2e99311..7441346 100644 --- a/src/file_p.h +++ b/src/file_p.h @@ -1,14 +1,15 @@ #ifndef FILEP_H #define FILEP_H #include "main.h" -void get_files_descriptions(server_data_t *); +void get_files_data(server_data_t *, uint32_t page); +void clear_list(fl_t *start); void file_list(session *, server_data_t *); int file_send_prepare(session *, char *, server_data_t *); enum f_actions { F_UPLOAD, F_DOWNLOAD, }; -void file_download_upload(session *sess, enum f_actions); +void file_load(session *sess, enum f_actions); int file_receive_prepare(session *sess, char *line, server_data_t *s_d); int file_upload_description(session *sess, char *line, server_data_t *s_d); int file_save_db(session *sess, server_data_t *s_d); diff --git a/src/main.c b/src/main.c index 2ebf5a9..9c3adc6 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,4 @@ #include "main.h" -#include "file_p.h" #include "server.h" #include "db.h" @@ -8,10 +7,7 @@ int main(int argc, char *argv[]) { server_data_t server_data; prepare_start(argc, argv); server_data.welcome_message = get_welcome_mes(); - server_data.fl_start = NULL; - server_data.fl_current = NULL; init_db_connection(); - get_files_descriptions(&server_data); server_data.ls = start_server(); server_main_loop(&server_data); return 0; diff --git a/src/main.h b/src/main.h index cdeaf7b..81c9d3d 100644 --- a/src/main.h +++ b/src/main.h @@ -49,9 +49,19 @@ typedef struct { size_t size; size_t rest; uint32_t hash; - int fd; } session_file; +typedef struct fl_t { + char *name; + char *description; + char *owner; + char permissions; + uint32_t owner_id; + uint32_t hash; + size_t size; + struct fl_t *next; +} fl_t; + typedef struct session { unsigned long from_ip; unsigned short from_port; @@ -62,22 +72,14 @@ typedef struct session { char privileges; char *uname; uint32_t uid; + int fd; int sd; /* session descriptor */ session_file *file; -} session; - -typedef struct fl_t { - char *name; - char *description; - char *owner; - char permissions; - size_t size; - struct fl_t *next; -} fl_t; - -typedef struct server_data_t { fl_t *fl_start; fl_t *fl_current; +} session; + +typedef struct server_data_t { char *welcome_message; int ls; } server_data_t; diff --git a/src/server.c b/src/server.c index 165f479..f464a65 100644 --- a/src/server.c +++ b/src/server.c @@ -35,8 +35,8 @@ void server_main_loop(server_data_t *s_d) { if (i > maxfd) maxfd = i; /* active uploads/downloads */ - if (connections[i]->file && connections[i]->file->fd > -1) { - int fd = connections[i]->file->fd; + if (connections[i]->fd > -1) { + int fd = connections[i]->fd; FD_SET(fd, &readfds); if (fd > maxfd) maxfd = fd; @@ -61,8 +61,8 @@ void server_main_loop(server_data_t *s_d) { /* download/upload file */ if (connections[i] != NULL && (connections[i]->state == OP_UPLOAD || connections[i]->state == OP_DOWNLOAD)) { - if (connections[i]->file && connections[i]->file->fd > -1 && - FD_ISSET(connections[i]->file->fd, &readfds)) { + if (connections[i]->fd > -1 && + FD_ISSET(connections[i]->fd, &readfds)) { perform_session_action(connections[i], NULL, s_d); } } else if (connections[i] != NULL && FD_ISSET(i, &readfds)) { diff --git a/src/session.c b/src/session.c index 5ac6486..f79fb49 100644 --- a/src/session.c +++ b/src/session.c @@ -1,9 +1,9 @@ #include "session.h" #include "client.h" +#include "db.h" #include "file_p.h" #include "main.h" #include "user.h" -#include "db.h" #include #include #include @@ -42,7 +42,10 @@ session *make_new_session(int fd, struct sockaddr_in *from, char *wm) { sess->reason = NO_REASON; sess->uname = NULL; sess->sd = fd; + sess->fd = -1; sess->file = NULL; + sess->fl_start = NULL; + sess->fl_current = NULL; session_send_string(sess, wm); session_send_string(sess, "login> "); return sess; @@ -112,10 +115,10 @@ int query_extract_from_buf(session *sess, char **output_line) { pos++; memmove(sess->buf, sess->buf + pos, sess->buf_used); sess->buf[sess->buf_used] = 0; - if (line[pos-2] == '\r') { - line[pos-2] = line[pos-1]; + if (line[pos - 2] == '\r') { + line[pos - 2] = line[pos - 1]; pos--; - line[pos-1] = 0; + line[pos - 1] = 0; } *output_line = line; } @@ -124,7 +127,6 @@ int query_extract_from_buf(session *sess, char **output_line) { void perform_session_action(session *sess, char *line, server_data_t *s_d) { int state = sess->state; - int priv; int res; /* while (sess->buf_used) { */ switch (state) { @@ -138,10 +140,10 @@ void perform_session_action(session *sess, char *line, server_data_t *s_d) { process_client_command(line, sess, s_d); break; case OP_DOWNLOAD: - file_download_upload(sess, F_DOWNLOAD); + file_load(sess, F_DOWNLOAD); break; case OP_UPLOAD: - file_download_upload(sess, F_UPLOAD); + file_load(sess, F_UPLOAD); break; case OP_UPLOAD_DESCRIPTION: res = file_upload_description(sess, line, s_d); @@ -161,10 +163,15 @@ void perform_session_action(session *sess, char *line, server_data_t *s_d) { void close_session(session *connections[], int sd) { if (connections[sd] != NULL) { - if (connections[sd]->file) clear_file_from_sess(connections[sd]); + if (connections[sd]->file != NULL) + clear_file_from_sess(connections[sd]); close(sd); connections[sd]->sd = -1; free(connections[sd]->uname); + if (connections[sd]->fl_start != NULL) { + fl_t *fl_start = connections[sd]->fl_start; + clear_list(fl_start); + } free(connections[sd]); connections[sd] = NULL; }