diff --git a/Makefile b/Makefile index 090b773..42bda48 100644 --- a/Makefile +++ b/Makefile @@ -7,12 +7,17 @@ BIN_DIR := bin SRCS := $(wildcard $(SRC_DIR)/*.c) OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) +OBJS += murmur3.o + $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(SRC_DIR)/%.h $(CC) $(CFLAGS) -c $< -o $@ bbs-server: $(OBJS) $(CC) $(CFLAGS) $^ -o $(BIN_DIR)/$@ +murmur3.o: $(SRC_DIR)/libs/murmur3/murmur3.c $(SRC_DIR)/libs/murmur3/murmur3.h + $(CC) $(CFLAGS) -c $< -o $@ + ifneq (clean, $(MAKECMDGOALS)) -include deps.mk endif diff --git a/src/db.c b/src/db.c index 75d90ac..aa9f86d 100644 --- a/src/db.c +++ b/src/db.c @@ -1,5 +1,10 @@ #include "db.h" +#include "main.h" #include +#include +#include +#include +#include #include #define UNUSED(x) (void)(x) @@ -38,6 +43,16 @@ static int exit_query(int code) { return code; } +static int exit_query_2(int code) { + if (code == 0) + fprintf(stderr, "%s\n", PQerrorMessage(conn)); + + if (res != NULL) + PQclear(res); + + return code; +} + static void processNotice(void *arg, const char *message) { UNUSED(arg); UNUSED(message); @@ -56,6 +71,11 @@ int init_db_connection() { char *user = PQuser(conn); char *db_name = PQdb(conn); + // res = PQexec(conn, "SELECT pg_catalog.set_config('search_path', '', + // false)"); if (PQresultStatus(res) != PGRES_TUPLES_OK) { + // return terminate(2); + // } + printf("Connection established! server version: %d, user: %s, db: %s\n", server_ver, user, db_name); return exit_query(0); @@ -66,19 +86,144 @@ int db_user_auth(i_auth_t *credentials, o_auth_t *response) { paramValues[0] = credentials->name; res = PQexecParams(conn, - "SELECT username, password, privileges " + "SELECT id, username, password, privileges " "FROM users " - "WHERE username= $1", 1, - NULL, paramValues, NULL, NULL, 0); + "WHERE username= $1", + 1, NULL, paramValues, NULL, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res)) return exit_query(1); - const char *pass = PQgetvalue(res, 0, 1); + const char *pass = PQgetvalue(res, 0, 2); if (!strcmp(pass, credentials->pass)) { - response->is_logged = 1; - response->privileges = PQgetvalue(res, 0, 2)[0]; + response->privileges = PQgetvalue(res, 0, 3)[0]; + response->uid = atoi(PQgetvalue(res, 0, 0)); return exit_query(0); } return exit_query(2); +} + +int db_save_collision(uint32_t hash); + +#define BIN 1 +#define TEXT 0 + +int db_save_file(session *s) { + const char *paramValues[6]; + int paramFormats[6]; + int paramLengths[6]; + int collision_id = 0; + session_file *sf = s->file; + + if (sf->seed != FILE_HASH_SEED) { + // TODO: сиды обрабатываются неправильно + collision_id = db_save_collision(sf->hash); + } + + int32_t uid_n = htonl(s->uid); + int64_t size_n = htonl(sf->size); + int32_t coll_n = htonl(collision_id); + int32_t perm_n = htonl(sf->permissions); + + paramValues[0] = (char *)&uid_n; + paramValues[1] = sf->name; + paramValues[2] = (char *)&size_n; + paramValues[3] = (char *)&coll_n; + paramValues[4] = sf->description; + paramValues[5] = (char *)&perm_n; + + paramLengths[0] = sizeof(int32_t); + paramLengths[1] = strlen(sf->name); + paramLengths[2] = sizeof(int64_t); + paramLengths[3] = sizeof(int32_t); + paramLengths[4] = strlen(sf->description); + paramLengths[5] = sizeof(int32_t); + + paramFormats[0] = BIN; + paramFormats[1] = TEXT; + paramFormats[2] = BIN; + paramFormats[3] = BIN; + paramFormats[4] = TEXT; + paramFormats[5] = BIN; + + res = PQexecParams(conn, + "INSERT INTO files(user_id, name, size, created_at, " + "collision_id, description, permissions) " + "VALUES ($1, $2, $3, NOW(), NULLIF($4, 0), $5, $6) " + "RETURNING id", + 6, NULL, paramValues, paramLengths, paramFormats, TEXT); + + if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res)) + return exit_query_2(0); + + int id = atoi(PQgetvalue(res, 0, 0)); + clearRes(); + return id; +} + +int db_save_collision(uint32_t hash) { + const char *paramValues[1]; + int paramLengths[1]; + int paramFormats[1]; + + uint32_t hash_n = htonl(hash); + + paramValues[0] = (char *)&hash_n; + paramLengths[0] = sizeof(uint32_t); + paramFormats[0] = BIN; + + res = PQexecParams(conn, + "INSERT INTO file_collisions(hash) " + "VALUES ($1) " + "RETURNING id", + 1, NULL, paramValues, paramLengths, paramFormats, TEXT); + + if (PQresultStatus(res) != PGRES_TUPLES_OK && !PQntuples(res)) + return exit_query_2(0); + + int id = atoi(PQgetvalue(res, 0, 0)); + clearRes(); + return id; +} + +// TODO: get file +// int db_get_file(i_get_file_db *arg) { + +// } + +int db_get_files(i_get_files_db *arg) { + const char *paramValues[3]; + int paramLengths[3]; + int paramFormats[3]; + int i, j, nFields; + + paramValues[0] = arg->sort_by; + paramValues[1] = (char *)&arg->limit; + paramValues[2] = (char *)&arg->offset; + + paramLengths[0] = strlen(arg->sort_by); + paramLengths[1] = sizeof(arg->limit); + paramLengths[2] = sizeof(arg->offset); + + paramFormats[0] = TEXT; + 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); + + nFields = PQnfields(res); + for (i = 0; i < nFields; i++) + printf("%-15s", PQfname(res, i)); + printf("\n\n"); + + for (i = 0; i < PQntuples(res); i++) { + for (j = 0; j < nFields; j++) + printf("%-15s", PQgetvalue(res, i, j)); + printf("\n"); + } + + clearRes(); + + return 0; } \ No newline at end of file diff --git a/src/db.h b/src/db.h index cf27150..d0bf59a 100644 --- a/src/db.h +++ b/src/db.h @@ -1,12 +1,35 @@ +#include "main.h" +#include + +/* Auth */ + typedef struct { - char *name; - char *pass; + char *name; + char *pass; } i_auth_t; typedef struct { - char privileges; - int is_logged; + char privileges; + uint32_t uid; } o_auth_t; +/* Get file */ + +typedef struct { + int id; + char *name; + 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; + + int init_db_connection(); int db_user_auth(i_auth_t *credentials, o_auth_t *response); +int db_save_file(session *s); diff --git a/src/deps.mk b/src/deps.mk index e69de29..56fb452 100644 --- a/src/deps.mk +++ b/src/deps.mk @@ -0,0 +1,8 @@ +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/libs/murmur3/murmur3.h +main.o: src/main.c src/main.h src/file_p.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/file_p.h src/user.h src/db.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 9c3d63d..9247a87 100644 --- a/src/file_p.c +++ b/src/file_p.c @@ -1,7 +1,6 @@ #include "file_p.h" #include "libs/murmur3/murmur3.h" #include "main.h" -#include "session.h" #include #include #include @@ -53,14 +52,14 @@ void file_list(session *sess, server_data_t *s_d) { } int file_send_prepare(session *sess, char *line, server_data_t *s_d) { - // TODO: рефактор в C++ + sess->file = malloc(sizeof(session_file)); int sd = sess->sd; - char fname[32]; + char fname[50]; sscanf(line, "%*s %*s %s", fname); char filepath[256]; sprintf(filepath, "%s/%s", STORAGE_FOLDER, fname); - size_t fsize; char st_message[256]; + size_t fsize; int file_d = open(filepath, O_RDONLY); @@ -76,13 +75,13 @@ 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->fd = file_d; - sess->fsize = fsize; - sess->f_rest = fsize; - sess->fname = malloc(strlen(fname)); + sess->file->fd = file_d; + sess->file->size = fsize; + sess->file->rest = fsize; + sess->file->name = malloc(strlen(fname)); int fname_len = strlen(fname) - 1; - strncpy(sess->fname, fname, fname_len); - sess->fname[fname_len] = 0; + strncpy(sess->file->name, fname, fname_len); + sess->file->name[fname_len] = 0; return 0; } @@ -111,16 +110,19 @@ int file_receive_prepare(session *sess, char *line, server_data_t *s_d) { size_t fsize; int perm; sscanf(line, "file upload \"%s %zd %d", fname, &fsize, &perm); - sess->fname = malloc(sizeof(char) * strlen(fname)); - sess->f_perm = (char)perm; - strncpy(sess->fname, fname, strlen(fname) - 1); // remove the last \" + sess->file = malloc(sizeof(session_file)); + sess->file->name = malloc(sizeof(char) * strlen(fname)); + // TODO: в конце гуляет \n и \" + sess->file->permissions = (char)perm; + sess->file->description = NULL; + strncpy(sess->file->name, fname, strlen(fname) - 1); // remove the last \" char mes[256]; char mes_len = 0; struct statvfs st_str; statvfs(".", &st_str); size_t available_space = st_str.f_bavail * st_str.f_bsize; - if (strlen(sess->fname) > st_str.f_namemax) { + if (strlen(sess->file->name) > st_str.f_namemax) { mes_len = sprintf(mes, "file name is tool long\n"); write(sd, mes, mes_len); sess->state = OP_WAIT; @@ -136,81 +138,85 @@ int file_receive_prepare(session *sess, char *line, server_data_t *s_d) { char hashed_dir_name[3]; char hashed_name[7]; - int seed = FILE_HASH_SEED; + sess->file->seed = FILE_HASH_SEED; int file_d; for (;;) { - MurmurHash3_x86_32(sess->fname, strlen(sess->fname), FILE_HASH_SEED, - &(sess->hash)); + MurmurHash3_x86_32(sess->file->name, strlen(sess->file->name), sess->file->seed, + &sess->file->hash); - extract_names_from_hash(sess->hash, hashed_dir_name, hashed_name); + extract_names_from_hash(sess->file->hash, hashed_dir_name, hashed_name); - sess->fpath = malloc(sizeof(STORAGE_FOLDER) + 2 + 9); - sprintf(sess->fpath, "%s/%s/%s", STORAGE_FOLDER, hashed_dir_name, + sess->file->path = malloc(sizeof(STORAGE_FOLDER) + 2 + 9); + sprintf(sess->file->path, "%s/%s/%s", STORAGE_FOLDER, hashed_dir_name, hashed_name); + chdir(STORAGE_FOLDER); + if (!directory_exists(hashed_dir_name)) { mkdir(hashed_dir_name, 0700); } - chdir(hashed_dir_name); + chdir("../"); - file_d = open(sess->fpath, O_WRONLY | O_CREAT | O_EXCL, 0666); + file_d = open(sess->file->path, O_WRONLY | O_CREAT | O_EXCL, 0666); if (file_d == -1) { if (errno == EEXIST) { - seed++; - free(sess->fpath); - sess->fpath = NULL; + sess->file->seed++; + free(sess->file->path); + sess->file->path = NULL; continue; } mes_len = sprintf(mes, "Can't create file with such name: \"%s\"\n", - sess->fname); + sess->file->name); write(sd, mes, mes_len); clear_file_from_sess(sess); + sess->file = NULL; return -1; } + break; } mes_len = sprintf(mes, "accept"); - sess->fd = file_d; - sess->fsize = fsize; - sess->f_rest = fsize; + sess->file->fd = file_d; + sess->file->size = fsize; + sess->file->rest = fsize; write(sd, mes, mes_len); return 0; } -void clear_file_from_sess(session *sess) { - if (sess->fname != NULL) { - free(sess->fname); - sess->fname = NULL; +void clear_file_from_sess(session *s) { + session_file * sf = s->file; + if (sf->name != NULL) { + free(sf->name); + sf->name = NULL; } - if (sess->fpath != NULL) { - free(sess->fpath); - sess->fpath = NULL; + if (sf->path != NULL) { + free(sf->path); + sf->path = NULL; } - if (sess->fdesc != NULL) { - free(sess->fdesc); - sess->fdesc = NULL; + if (sf->description != NULL) { + free(sf->description); + sf->description = NULL; } - if (sess->fd > -1) { - close(sess->fd); + if (sf->fd > -1) { + close(sf->fd); } - sess->fd = -1; - sess->fsize = 0; - sess->f_rest = 0; + free(sf); + s->file = NULL; } void file_download_upload(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; + int source_d = f_action == F_DOWNLOAD ? sess->file->fd : sess->sd; + int dest_d = f_action == F_DOWNLOAD ? sess->sd : sess->file->fd; char buf[INBUFSIZE]; int rlen = read(source_d, buf, INBUFSIZE); if (rlen == 0) { - if (sess->f_rest) { + if (sess->file->rest) { if (f_action == F_DOWNLOAD) { - fprintf(stderr, "Error downloading file %s!\n", sess->fname); + fprintf(stderr, "Error downloading file %s!\n", sess->file->name); } else { - fprintf(stderr, "Error uploading file %s!\n", sess->fname); - unlink(sess->fpath); /* remove file */ + fprintf(stderr, "Error uploading file %s!\n", sess->file->name); + unlink(sess->file->path); /* remove file */ } clear_file_from_sess(sess); } @@ -218,13 +224,13 @@ void file_download_upload(session *sess, enum f_actions f_action) { return; } write(dest_d, buf, rlen); - sess->f_rest -= rlen; - if (!sess->f_rest) { + sess->file->rest -= rlen; + if (!sess->file->rest) { if (f_action == F_DOWNLOAD) { - printf("File %s is downloaded from the server\n", sess->fname); + printf("File %s is downloaded from the server\n", sess->file->name); clear_file_from_sess(sess); } else { - printf("File %s is uploaded to the server\n", sess->fname); + printf("File %s is uploaded to the server\n", sess->file->name); } if (f_action == F_UPLOAD) { sess->state = OP_UPLOAD_DESCRIPTION; @@ -256,27 +262,31 @@ size_t get_file_size(char *dir_n, char *file_n) { /* returns 1 if there is :END: ; 0 if opposite */ int file_upload_description(session *sess, char *line, server_data_t *s_d) { - for (;;) { - if (line != NULL && sess->fdesc == NULL) { /* first query */ - sess->fdesc = malloc(strlen(line) + 1); - strcpy(sess->fdesc, line); - } else { /* subsequent queries */ - if (line == NULL) { - if (sess->buf_used > 0) { - query_extract_from_buf(sess, &line); - } else { - return 0; - } - } - sess->fdesc = - realloc(sess->fdesc, strlen(sess->fdesc) + strlen(line) + 1); - strcat(sess->fdesc, line); - } - char *desc_end = strstr(line, ":END:"); - if (desc_end != NULL) { - return 1; - } - } + // for (;;) { + // if (line != NULL && sess->file->description == NULL) { /* first query */ + // sess->file->description = malloc(strlen(line) + 1); + // strcpy(sess->file->description, line); + // } else { /* subsequent queries */ + // if (line == NULL) { + // if (sess->buf_used > 0) { + // query_extract_from_buf(sess, &line); + // } else { + // return 0; + // } + // } + // sess->file->description = + // realloc(sess->file->description, strlen(sess->file->description) + strlen(line) + 1); + // strcat(sess->file->description, line); + // } + // char *desc_end = strstr(line, ":END:"); + // if (desc_end != NULL) { + // return 1; + // } + // } + char _msg[] = "This is a test \n Description!\n"; + sess->file->description = malloc(sizeof(_msg) + 1); + strcpy(sess->file->description, _msg); + return 1; } // TODO: синхронизовать c C++ diff --git a/src/file_p.h b/src/file_p.h index 9c0b58f..2e99311 100644 --- a/src/file_p.h +++ b/src/file_p.h @@ -12,5 +12,5 @@ void file_download_upload(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); -void clear_file_from_sess(session *sess); +void clear_file_from_sess(session *); #endif \ No newline at end of file diff --git a/src/main.h b/src/main.h index 20c71d9..7ec5f89 100644 --- a/src/main.h +++ b/src/main.h @@ -42,6 +42,18 @@ enum error_reason { EXIT, }; +typedef struct { + char *name; + char *path; + char *description; + char permissions; + size_t size; + size_t rest; + uint32_t hash; + int seed; + int fd; +} session_file; + typedef struct session { unsigned long from_ip; unsigned short from_port; @@ -51,16 +63,9 @@ typedef struct session { enum error_reason reason; char privileges; char *uname; + uint32_t uid; int sd; /* session descriptor */ - int fd; /* file descriptor */ - char *fname; - char *fpath; - char f_perm; - char *fdesc; - uint32_t hash; - size_t fsize; - size_t f_rest; - long int userpos; /* hint to the position in user list */ + session_file *file; } session; typedef struct fl_t { diff --git a/src/server.c b/src/server.c index 2ad236e..165f479 100644 --- a/src/server.c +++ b/src/server.c @@ -4,11 +4,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include @@ -35,8 +35,8 @@ void server_main_loop(server_data_t *s_d) { if (i > maxfd) maxfd = i; /* active uploads/downloads */ - int fd = connections[i]->fd; - if (fd > -1) { + if (connections[i]->file && connections[i]->file->fd > -1) { + int fd = connections[i]->file->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] != NULL && connections[i]->fd > -1 && - FD_ISSET(connections[i]->fd, &readfds)) { + if (connections[i]->file && connections[i]->file->fd > -1 && + FD_ISSET(connections[i]->file->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 50e56b5..5ac6486 100644 --- a/src/session.c +++ b/src/session.c @@ -3,10 +3,12 @@ #include "file_p.h" #include "main.h" #include "user.h" +#include "db.h" #include #include #include #include +#include #include #include #include @@ -39,14 +41,8 @@ session *make_new_session(int fd, struct sockaddr_in *from, char *wm) { sess->state = OP_LOGIN_USR; sess->reason = NO_REASON; sess->uname = NULL; - sess->f_perm = 0; - sess->fdesc = NULL; sess->sd = fd; - sess->fd = -1; - sess->fsize = 0; - sess->f_rest = 0; - sess->fname = NULL; - sess->fpath = NULL; + sess->file = NULL; session_send_string(sess, wm); session_send_string(sess, "login> "); return sess; @@ -150,61 +146,22 @@ void perform_session_action(session *sess, char *line, server_data_t *s_d) { case OP_UPLOAD_DESCRIPTION: res = file_upload_description(sess, line, s_d); if (res) { - file_save_db(sess, s_d); - session_send_string(sess, "File is saved!\n"); - sess->state = OP_WAIT; + if (db_save_file(sess)) { + clear_file_from_sess(sess); + session_send_string(sess, "File is saved!\n"); + sess->state = OP_WAIT; + } else { + // TODO: error case + } } break; } /* } */ } -int file_save_db(session *sess, server_data_t *s_d) { - /* crate and fill the new item */ - fl_t *nitem = malloc(sizeof(fl_t)); - nitem->description = sess->fdesc; - nitem->name = sess->fname; - nitem->size = sess->fsize; - nitem->owner = malloc(strlen(sess->uname)); - strcpy(nitem->owner, sess->uname); - nitem->permissions = sess->f_perm; - nitem->next = NULL; - - /* set to NULL and clear */ - sess->fname = NULL; - free(sess->fpath); - sess->fpath = NULL; - sess->fdesc = NULL; - if (sess->fd > -1) { - close(sess->fd); - } - sess->fd = -1; - sess->fsize = 0; - sess->f_rest = 0; - - /* add new item to list */ - s_d->fl_current->next = nitem; - s_d->fl_current = nitem; - - /* save to the db file */ - int fd = open(FILE_DESCRIPTIONS_NAME, O_WRONLY); - lseek(fd, 0, SEEK_END); - char permissions[4]; - snprintf(permissions, 3, "%d", nitem->permissions); - unsigned int res_len = strlen(nitem->name) + 1 + strlen(nitem->owner) + 1 + - strlen(permissions) + 1 + strlen(nitem->description) + - 1; - char *res_str = malloc(res_len); - res_len = sprintf(res_str, "%s\n%s\n%s\n%s", nitem->name, nitem->owner, - permissions, nitem->description); - nitem->description[strlen(nitem->description) - 6] = 0; - write(fd, res_str, res_len); - close(fd); - return 0; -} - void close_session(session *connections[], int sd) { if (connections[sd] != NULL) { + if (connections[sd]->file) clear_file_from_sess(connections[sd]); close(sd); connections[sd]->sd = -1; free(connections[sd]->uname); diff --git a/src/user.c b/src/user.c index b2f4962..e9bf1d8 100644 --- a/src/user.c +++ b/src/user.c @@ -40,10 +40,12 @@ int login(session *sess, char *pass) { o_auth_t response; cred.name = sess->uname; cred.pass = pass; + response.uid = 0; db_user_auth(&cred, &response); - if (response.is_logged) { + if (response.uid) { sess->state = OP_WAIT; sess->privileges = (char)atoi(&response.privileges); + sess->uid = response.uid; sprintf(tmp_string, "Welcome, %s\n", sess->uname); session_send_string(sess, tmp_string); return 0;