From 48a65b2e455c24d2482fd9098c918935714a198f Mon Sep 17 00:00:00 2001 From: Harishankar Date: Fri, 1 May 2020 17:31:12 +0530 Subject: [PATCH] Password protect functionality and also changed file format Added password protection and also changed the file save and load routines to a proper format to make it more robust rather than just dumping the struct to file --- Makefile | 4 +- constantstrings.h | 14 +++- wordblox.c | 119 +++++++++++++++++++++-------- wordblox.h | 185 ++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 279 insertions(+), 43 deletions(-) diff --git a/Makefile b/Makefile index c471226..0842251 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,3 @@ -wordblox: wordblox.c - clang wordblox.c -lgd -o wordblox +wordblox: wordblox.c wordblox.h constantstrings.h + clang wordblox.c -lgd -lcrypt -o wordblox diff --git a/constantstrings.h b/constantstrings.h index 76fede7..89755ba 100644 --- a/constantstrings.h +++ b/constantstrings.h @@ -14,6 +14,7 @@ #define PUZZLE_MENU_TITLE "Edit Puzzle" #define MAIN_MENU_TITLE "Main Menu" #define INPUT_GRID_SIZE "Number of rows/columns: " +#define INPUT_PASSWORD "Enter the password: " #define INPUT_EXPORT_ANSWERS "Export as solution (y/N): " #define INPUT_CHOICE "Your Choice: " #define EXCEED_MAX_GRID_SIZE "Exceeds max puzzle size" @@ -21,6 +22,10 @@ #define ERROR_READING_FILE "Error reading file" #define INVALID_WORD "Word contains illegal characters. Only alphabets allowed!" #define FILE_SAVED "File saved successfully" +#define PASSWORD_SET "Password set successfully" +#define PASSWORD_RESET "Password reset successfully. Puzzle is no longer \ +password protected!" +#define WRONG_PASSWORD "Wrong password!" #define NO_WORD_INDEX "No such word with specified index" #define INPUT_CONFIRM_EXIT "Are you sure you wish to exit? \ Unsaved changes will be lost [y/N]" @@ -47,9 +52,10 @@ char *PUZZLE_EDIT_MENU[] = "7. Set Clue - Across Word", "8. Set Clue - Down Word", "9. Save puzzle", - "10.Reset entire grid", - "11.Export puzzle as PNG image", - "12.Export clues to text file", - "13.Return to main menu" }; + "10.Set puzzle password", + "11.Reset entire grid", + "12.Export puzzle as PNG image", + "13.Export clues to text file", + "14.Return to main menu" }; #endif diff --git a/wordblox.c b/wordblox.c index d840b6d..61de6b6 100644 --- a/wordblox.c +++ b/wordblox.c @@ -65,6 +65,33 @@ void do_reset_puzzle (Puzzle *p) fgets (conf, 3, stdin); if (toupper (conf[0]) == 'Y') init_puzzle (p, grid_size); + + print_puzzle (p); + char ch = getchar (); +} + +/* set the password for the puzzle */ +void do_set_password (Puzzle *p) +{ + printf (INPUT_PASSWORD); + char password[256]; + fgets (password, 256, stdin); + /* if empty reset the password to nothing */ + if (strlen (password) == 1) + { + set_puzzle_password (p, "\0"); + printf (PASSWORD_RESET); + char ch = getchar (); + } + /* set the password */ + else + { + char *passwd = strtok (password, "\n"); + + set_puzzle_password (p, (const char* )passwd); + printf (PASSWORD_SET); + char ch = getchar (); + } } /* set clue for a word - only for frozen grid */ @@ -230,7 +257,7 @@ void puzzle_editor_loop (Puzzle *p, const char *filename) bool loop = true; while (loop) { - print_menu (WHITE, BLUE, PUZZLE_MENU_TITLE, PUZZLE_EDIT_MENU, 13, 50); + print_menu (WHITE, BLUE, PUZZLE_MENU_TITLE, PUZZLE_EDIT_MENU, 14, 50); printf (INPUT_CHOICE); int ch = get_num (); switch (ch) @@ -260,47 +287,79 @@ void puzzle_editor_loop (Puzzle *p, const char *filename) printf ("%s\n",FILE_SAVED); ch = getchar (); break; - case 10: do_reset_puzzle (p); - print_puzzle (p); - ch = getchar (); + case 10: do_set_password (p); + break; + case 11: do_reset_puzzle (p); break; - case 11: do_export_puzzle (p); + case 12: do_export_puzzle (p); break; - case 12: do_export_clues (p); + case 13: do_export_clues (p); break; - case 13: loop = ! do_confirm_exit (); + case 14: loop = ! do_confirm_exit (); break; } } } /* open an existing puzzle */ -void do_open_puzzle () +void do_open_puzzle (const char *filename) { Puzzle p; - printf (INPUT_FILE); - char fname[256]; - fgets(fname, 256, stdin); - if (strlen (fname) == 1) - return; - char* filename = strtok (fname, "\n"); + /* if no filename is provided, get it from command line */ + if (filename == NULL) + { + printf (INPUT_FILE); + char fname[256]; + fgets(fname, 256, stdin); + if (strlen (fname) == 1) + return; + filename = strtok (fname, "\n"); + } + p = load_puzzle (filename); - puzzle_editor_loop (&p, filename); + + if (strcmp (p.hashed_password, "\0") != 0) + { + char passwd[256]; + printf (INPUT_PASSWORD); + fgets (passwd, 256, stdin); + if (strlen (passwd) == 1) + return; + char *pwd = strtok (passwd, "\n"); + + if (verify_password (&p, (const char*) pwd)) + puzzle_editor_loop (&p, filename); + else + { + printf (WRONG_PASSWORD); + char ch = getchar (); + } + } + else + puzzle_editor_loop (&p, filename); } /* create a new blank puzzle */ -void do_new_puzzle () +void do_new_puzzle (char *filename, int size) { Puzzle p; - printf (INPUT_FILE); - char fname[256]; - fgets (fname, 256, stdin); - if (strlen (fname) == 1) - return; - char* filename = strtok (fname, "\n"); - printf (INPUT_GRID_SIZE); - int size; - size = get_num (); + /* if filename is not provided get it from command line */ + if (filename == NULL) + { + printf (INPUT_FILE); + char fname[256]; + fgets (fname, 256, stdin); + if (strlen (fname) == 1) + return; + filename = strtok (fname, "\n"); + } + /* if no size is specified get it from command line */ + if (size == -1) + { + printf (INPUT_GRID_SIZE); + size = get_num (); + } + if (size > MAX_PUZZLE_SIZE) { printf (EXCEED_MAX_GRID_SIZE); @@ -324,9 +383,9 @@ int main_loop () int ch = get_num (); switch (ch) { - case 1: do_new_puzzle (); + case 1: do_new_puzzle (NULL, -1); break; - case 2: do_open_puzzle (); + case 2: do_open_puzzle (NULL); break; case 3: exit (0); } @@ -340,14 +399,12 @@ int main (int argc, char* argv[]) Puzzle p; switch (argc) { - case 2 : p = load_puzzle (argv[1]); - puzzle_editor_loop (&p, argv[1]); + case 2 : do_open_puzzle (argv[1]); break; case 4 : if (strcmp (argv[2], "new") == 0) { int grid_size = atoi (argv[3]); - init_puzzle (&p, grid_size); - puzzle_editor_loop (&p, argv[1]); + do_new_puzzle (argv[1], grid_size); break; } default: fprintf (stderr, USAGE_LINE_1, argv[0]); diff --git a/wordblox.h b/wordblox.h index bd286e3..cabda97 100644 --- a/wordblox.h +++ b/wordblox.h @@ -1,6 +1,7 @@ #ifndef __WORDBLOX_H #define __WORDBLOX_H - +#define _XOPEN_SOURCE +#include #include #include #include @@ -42,6 +43,8 @@ typedef struct { String clue_down[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE]; int grid_size; bool grid_frozen; + char hashed_password[256]; + char salt[256]; } Puzzle; /* get a number from the user */ @@ -53,6 +56,42 @@ int get_num () return n; } +/* verify password */ +bool verify_password (Puzzle *p, const char* password) +{ + /* no password set */ + if (strcmp (p->hashed_password, "\0") == 0) + return true; + + /* hash the user input password and compare it with the stored password */ + char* hashed_password = crypt (password, (const char *)p->salt); + + if (strcmp (p->hashed_password, hashed_password) == 0) + return true; + + return false; +} + +/* Set or reset password for puzzle */ +void set_puzzle_password (Puzzle *p, const char *password) +{ + /* if it is a null string, reset the password */ + if (strcmp (password, "\0") == 0) + { + strcpy (p->hashed_password, "\0"); + strcpy (p->salt, "\0"); + } + else + { + srand (time(NULL)); + char salt[256]; + sprintf (salt, "puzzle%d", rand()%1000); + char* hashedpwd = crypt (password, (const char*)salt); + strcpy (p->hashed_password, hashedpwd); + strcpy (p->salt, salt); + } +} + /* Output the clues to text file */ void export_clues (Puzzle *p, const char *filename) { @@ -269,22 +308,64 @@ void init_puzzle (Puzzle *p, int grid_size) strcpy (p->clue_down[i][j], ""); } } + strcpy (p->hashed_password, "\0"); + strcpy (p->salt, "\0"); + } -/* save the puzzle */ +/* save the puzzle to a file */ void save_puzzle (Puzzle *puzzle, const char* file) { FILE *outfile; - outfile = fopen (file, "wb"); + outfile = fopen (file, "w"); if (outfile == NULL) { fprintf (stderr, "%s\n", ERROR_WRITING_FILE); exit (1); } - fwrite (puzzle, sizeof (*puzzle), 1, outfile); + fprintf (outfile, "%d\n", puzzle->grid_size); + fprintf (outfile, "%d\n", puzzle->grid_frozen); + fprintf (outfile, "%s\n", puzzle->hashed_password); + fprintf (outfile, "%s\n", puzzle->salt); + for (int i = 0; i < puzzle->grid_size; i ++) + { + for (int j = 0; j < puzzle->grid_size; j ++) + fprintf (outfile, "%c", puzzle->chars[i][j]); + fprintf (outfile, "\n"); + } + for (int i = 0; i < puzzle->grid_size; i ++) + { + for (int j = 0; j < puzzle->grid_size; j++) + { + fprintf (outfile, "%d ", puzzle->start_across_word[i][j]); + fprintf (outfile, "%d ", puzzle->start_down_word[i][j]); + } + fprintf (outfile, "\n"); + } + fprintf (outfile, "ACROSS\n"); + for (int i = 0; i < puzzle->grid_size; i ++) + { + for (int j = 0; j < puzzle->grid_size; j++) + { + if (puzzle->start_across_word[i][j] != -1) + fprintf (outfile, "%d\t%s\n", puzzle->start_across_word[i][j], + puzzle->clue_across[i][j]); + } + } + fprintf (outfile, "DOWN\n"); + for (int i = 0; i < puzzle->grid_size; i ++) + { + for (int j = 0; j < puzzle->grid_size; j++) + { + if (puzzle->start_down_word[i][j] != -1) + fprintf (outfile, "%d\t%s\n", puzzle->start_down_word[i][j], + puzzle->clue_down[i][j]); + } + } + fclose (outfile); } -/* read the puzzle */ +/* read the puzzle from a file */ Puzzle load_puzzle (const char* file) { FILE *infile; Puzzle p; @@ -294,7 +375,99 @@ Puzzle load_puzzle (const char* file) { fprintf (stderr, "%s\n", ERROR_READING_FILE); exit (1); } - fread (&p, sizeof(p), 1, infile); + char line[MAX_CLUE_LENGTH+10]; + fgets (line, MAX_CLUE_LENGTH + 10, infile); + p.grid_size = atoi (line); + fgets (line, MAX_CLUE_LENGTH + 10, infile); + p.grid_frozen = atoi (line) == 0 ? false : true ; + fgets (line, MAX_CLUE_LENGTH + 10, infile); + if (strlen (line) != 1) + strcpy (p.hashed_password, strtok (line, "\n")); + else + strcpy (p.hashed_password, "\0"); + fgets (line, MAX_CLUE_LENGTH + 10, infile); + if (strlen (line) != 1) + strcpy (p.salt, strtok (line, "\n")); + else + strcpy (p.salt, "\0"); + + /* read each character of the grid */ + for (int i = 0; i < p.grid_size; i ++ ) + { + fgets (line, MAX_CLUE_LENGTH + 10, infile); + for (int j = 0; j < p.grid_size; j ++) + p.chars[i][j] = line[j]; + } + /* read the word numbers */ + for (int i = 0; i < p.grid_size; i ++) + { + fgets (line, MAX_CLUE_LENGTH + 10, infile); + char *token = strtok (line, " "); + for (int j = 0; j < p.grid_size; j ++) + { + if (token != NULL) + p.start_across_word[i][j] = atoi (token); + token = strtok (NULL, " "); + if (token != NULL) + p.start_down_word[i][j] = atoi (token); + token = strtok (NULL, " "); + } + } + /* read the clues */ + fgets (line, MAX_CLUE_LENGTH + 10, infile); + + /* across clues */ + char clues[100][MAX_CLUE_LENGTH]; + int word_num[100]; + int c = 0; + /* first read the across clues from file */ + while (1) + { + fgets (line, MAX_CLUE_LENGTH + 10, infile); + /* if reached the end of across clues */ + if (strcmp (line, "DOWN\n") == 0) + break; + word_num[c] = atoi (strtok (line, "\t")); + char *cl = strtok (NULL, "\n"); + if (cl != NULL) + strcpy (clues[c], cl); + else + strcpy (clues[c], "\0"); + c++; + } + /* set the clue to the correct cell in grid */ + for (int i = 0; i < p.grid_size; i ++) + { + for (int j = 0; j < p.grid_size; j ++) + { + for (int r = 0; r < c; r ++) + if (p.start_across_word[i][j] == word_num[r]) + strcpy (p.clue_across[i][j], clues[r]); + } + } + + /* down clues */ + c = 0; + while (fgets (line, MAX_CLUE_LENGTH + 10, infile)) + { + word_num[c] = atoi (strtok (line, "\t")); + char* cl = strtok (NULL, "\n"); + if (cl != NULL) + strcpy (clues[c], cl); + else + strcpy (clues[c], "\0"); + c++; + } + for (int i = 0; i < p.grid_size; i ++) + { + for (int j = 0; j < p.grid_size; j ++) + { + for (int r = 0; r < c; r ++) + if (p.start_down_word[i][j] == word_num[r]) + strcpy (p.clue_down[i][j], clues[r]); + } + } + fclose (infile); return p; } -- 2.20.1