From f97c1262f55d87eb9f16385468b1e5e5337bf5c1 Mon Sep 17 00:00:00 2001
From: Anqi Chen <a.chen49@student.unimelb.edu.au>
Date: Mon, 29 Apr 2019 23:51:06 +1000
Subject: [PATCH] finish

---
 image_tagger.c | 651 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 651 insertions(+)
 create mode 100644 image_tagger.c

diff --git a/image_tagger.c b/image_tagger.c
new file mode 100644
index 0000000..41cc541
--- /dev/null
+++ b/image_tagger.c
@@ -0,0 +1,651 @@
+/*
+** http-server.c
+*/
+/********************************************Libraries**********************************************/
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <strings.h>
+#include <sys/select.h>
+#include <sys/sendfile.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/********************************************constants**********************************************/
+
+#define GUESS_SUFFIX "&guess=Guess"
+#define MAX_BUFF 2049
+
+
+
+/*******isOnline******/
+#define INTRO 0
+#define START 1
+#define FIRST_TURN 2
+#define ACCEPTED 3
+#define DISCARDED 4
+#define ENDGAME 5
+#define GAMEOVER 6
+
+/*******stage******/
+#define ENTER 0
+#define GUESS 1
+#define ENDED 2
+#define QUIT  3
+#define RSTAR 4
+
+
+/********************************************statics**********************************************/
+// cookie for user, formtted with userID
+static char const * const HTTP_200_FORMAT = "HTTP/1.1 200 OK\r\n\
+Content-Length: %ld\r\n\
+Content-Type: text/html\r\n\
+Set-Cookie: cookie_id= %d\r\n\r\n";
+
+
+
+/*******isOnline******/
+#define INTRO 0
+#define START 1
+#define FIRST_TURN 2
+#define ACCEPTED 3
+#define DISCARDED 4
+#define ENDGAME 5
+#define GAMEOVER 6
+
+/*******stage******/
+#define ENTER 0
+#define GUESS 1
+#define ENDED 2
+#define QUIT  3
+#define RSTAR 4
+
+
+/********************************************Cookie**********************************************/
+static char const * const HTTP_400 = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n";
+static int const HTTP_400_LENGTH = 47;
+static char const * const HTTP_404 = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n";
+static int const HTTP_404_LENGTH = 45;
+
+/********************************************Structs**********************************************/
+// enumerate all the methods
+typedef enum
+{
+    GET,
+    POST,
+    UNKNOWN
+} METHOD;
+
+
+
+// struct User
+struct User {
+    int userID;        // userID is the identification of the user, used for cookie  
+    char** keywords;   // an array of guessing words
+    int isOnline;      // Online=1, OffLine=0
+    int stage;         // record the stage of the user
+    int n_word;        // number of guessing words
+};
+
+// secound round flag
+int is_second_round = 0;
+/********************************************Function Signitures**********************************************/
+struct User* newUser();
+void clear_user_keyword(struct User* user);
+int find_cookie(char* buff);
+struct User* find_curr(struct User* user_arr[], char* buff);
+struct User* the_other_user(struct User* curr_user, struct User* user_arr[]);
+bool match(char* w1, char** w2, int n_word);
+void simple_load_html(char* html, int sockfd, char* buff, int n, struct User* curr_user);
+void edit_load_html(char* html, int sockfd, char* buff, char* newword,struct User* curr_user);
+void record_keyword(struct User* curr_user, char *new_guess);
+static bool handle_http_request(int sockfd, struct User* user_arr[]);
+/********************************************Functions**********************************************/
+
+
+
+/******_________________________________user___________________________________*******/
+
+
+// replace '+' with ' '
+char* remove_plus(char* word){
+    for (int i=0; i<strlen(word);i++)
+    {
+        if (word[i]=='+'){
+           word[i]=' ';
+        }
+        return word;
+    }
+}
+
+// malloc a newUser
+struct User* newUser() {
+    struct User* newUser = malloc(sizeof (struct User));
+    newUser->userID = 0;
+    newUser->keywords =malloc(MAX_BUFF*sizeof(char*));
+    for (int i = 0; i < MAX_BUFF; i++)
+    {
+        newUser->keywords[i] = NULL;
+    }
+    newUser->isOnline = 0;
+    newUser->stage = 0;
+    newUser->n_word = 0;
+    return newUser;
+}
+
+// clear user: point the array to NULL, reset the int to 0
+void clear_user_keyword(struct User* user){
+    user->stage=0;
+    user->isOnline=0;
+    user->n_word=0;
+    for (int i = 0; i < MAX_BUFF; i++)
+    {
+        user->keywords[i] = NULL;
+    }
+}
+
+// read the words into the curr_user->keywords
+void record_keyword(struct User* curr_user, char *new_guess){
+    remove_plus(new_guess);
+    for (int i=0; i<=curr_user->n_word; i++){
+        // add to the first NULL bit and malloc 
+        if(curr_user->keywords[curr_user->n_word]==NULL){
+            curr_user->keywords[curr_user->n_word] = malloc((1 + strlen(new_guess)) * sizeof(char*));
+            strcpy(curr_user->keywords[curr_user->n_word],new_guess);
+            printf("record_keyword :curr_user->userID %d the last word stored is %s", curr_user->userID, curr_user->keywords[curr_user->n_word]);
+            curr_user->n_word++;
+            break;
+        }
+    }
+}
+
+
+
+//find the cookie according to id, in buff
+int find_cookie(char* buff)
+{
+    char* cookie_id=strstr(buff, "cookie_id=");
+    char* temp;
+    if(cookie_id == NULL)
+    {
+        return 0;
+    }
+    else
+    {
+        temp = malloc(sizeof(char)*strlen(cookie_id)+1);
+        strcpy(temp, cookie_id);
+        temp+=10;
+    }
+    temp[strlen(temp)-3]='\0';
+    return  atoi(temp);
+}
+
+// find the current user by analyse buffer
+struct User* find_curr(struct User* user_arr[], char* buff)
+{
+    int cookie_id = find_cookie(buff);
+
+    // if the cookie has not set up, assign it with a new id and malloc the space
+        if(cookie_id==0){
+        
+        struct User* curr_user = newUser();
+        
+        //if never accessed before, create new user
+        //check according to cookie and id
+        if ((cookie_id!=1||cookie_id!=2)&& user_arr[0]->userID!=1)
+        {    
+            curr_user->userID = 1;
+            user_arr[0]= curr_user;
+        }
+        else if((cookie_id!=1||cookie_id!=2)&& user_arr[1]->userID!=2){
+            curr_user->userID = 2;
+            user_arr[1]= curr_user;
+        }
+        cookie_id=curr_user->userID;
+        printf(" func curr: cookie_id is %d\n%d\n\n\n", cookie_id,curr_user->userID);
+    }
+    // get the user from array
+    for (int i=0; i<2; i++) {
+        if (user_arr[i]->userID == cookie_id)
+        {               
+            printf("existing curr user %d , with cookie_id %d", i,cookie_id);
+            return user_arr[i];
+            
+        }
+    }
+
+}
+
+
+// the user in arrsy if not this one, then should be that one
+struct User* the_other_user(struct User* curr_user, struct User* user_arr[]){
+    for(int i=0; i<2; i++){
+        if (user_arr[i]->userID!=curr_user->userID){
+            printf("the other user id is %d\n", user_arr[i]->userID);
+            return user_arr[i];
+        }
+    }
+}
+
+// try to match the new guess to the other user keywords
+bool match(char* w1, char** w2, int n_word){
+    for (int i=0; i<n_word; i++){
+        printf("%s - %s \n", w1, w2[i]);
+        if (strcmp(w1, w2[i])==0){
+            printf("all good");
+            return true;
+        }
+    }
+    return false;
+}
+
+
+/******_________________________________html___________________________________*******/
+// only load page
+void simple_load_html(char* html, int sockfd, char* buff, int n, struct User* curr_user){
+    
+    struct stat st;
+    stat(html, &st);
+    // print into buff
+    n = sprintf(buff, HTTP_200_FORMAT, st.st_size,curr_user->userID);
+    if (write(sockfd, buff, n) < 0)
+            {
+                perror("write");
+                return ;
+            }
+    int filefd = open(html, O_RDONLY);
+            do
+            {
+                n = sendfile(sockfd, filefd, NULL, 2048);
+            }
+            while (n > 0);
+            if (n < 0)
+            {
+                perror("sendfile");
+                close(filefd);
+                return ;
+            }
+            close(filefd);
+}
+
+
+
+// load page with new words 
+void edit_load_html(char* html, int sockfd, char* buff, char* newword,struct User* curr_user){
+    // get the size of the file
+    struct stat st;
+    stat(html, &st);
+    remove_plus(newword);
+    // increase file size to accommodate the new guess
+    int added_length= strlen(newword);
+    long size = st.st_size + added_length;
+    int n = sprintf(buff, HTTP_200_FORMAT, size, curr_user->userID);
+    // send the header first
+    if (write(sockfd, buff, n) < 0)
+    {
+        perror("write");
+        return;
+    }
+    // read the content of the HTML file
+    int filefd = open(html, O_RDONLY);
+    n = read(filefd, buff, st.st_size);
+    if (n < 0)
+    {
+        perror("read");
+        close(filefd);
+        return;
+    }
+    close(filefd);
+
+    // create two buffers to read the thing before and after </body>
+    char front_buff[MAX_BUFF], back_buff[MAX_BUFF];
+    int endlen=strstr(buff, "</body>")-buff;
+    // close teh string
+    strcpy(back_buff,"\n\r\r<p> ");
+    back_buff[strlen(back_buff)]='\0';
+
+    //add new word at the back_buff
+    strcat(back_buff, newword);
+    back_buff[strlen(back_buff)]='\0';
+
+    // close back_buff
+    strcat(back_buff, "\r\r</p>\r</body> </html>");
+    back_buff[strlen(back_buff)]='\0';
+    strncpy(front_buff, buff, endlen);
+    front_buff[strlen(front_buff)]='\0';
+
+    //merge
+    strcpy(buff, front_buff);
+    buff[strlen(buff)]='\0';
+    strcat(buff, back_buff);
+  
+    buff[strlen(buff)]='\0';
+
+    if (is_second_round){
+        endlen = strstr(buff,"image-")+6-buff;
+        buff[endlen] = 3;
+    }
+
+
+
+    //write
+    if (write(sockfd, buff, size) < 0){
+      perror("write");
+      return;
+    }
+
+
+}
+
+
+
+
+// handle with every single thread from the client requests
+static bool handle_http_request(int sockfd, struct User* user_arr[])
+{
+    // try to read the request
+    char buff[2049];
+    int n = read(sockfd, buff, 2049);
+    if (n <= 0)
+    {
+        if (n < 0)
+            perror("read");
+        else
+            printf("socket %d close the connection\n", sockfd);
+        return false;
+    }
+
+    // terminate the string
+    buff[n] = 0;
+
+    char * curr = buff;
+
+    // parse the method
+    METHOD method = UNKNOWN;
+    if (strncmp(curr, "GET ", 4) == 0)
+    {
+        curr += 4;
+        method = GET;
+    }
+    else if (strncmp(curr, "POST ", 5) == 0)
+    {
+        curr += 5;
+        method = POST;
+    }
+    else if (write(sockfd, HTTP_400, HTTP_400_LENGTH) < 0)
+    {
+        perror("write");
+        return false;
+    }
+
+    printf("buff1: \n");
+    printf("%s\n",buff);
+
+    struct User* curr_user=find_curr(user_arr,buff);
+
+    // sanitise the URI
+    while (*curr == '.' || *curr == '/')
+        ++curr;
+
+    printf("stage%d\n",curr_user->stage );
+    // assume the only valid request URI is "/" but it can be modified to accept more files
+    if (*curr == ' ' || *curr == '?')
+    {
+
+
+        ///////////// handle GET requests /////////////
+        if (method == GET)
+        {   
+            //first visit to intro
+            if (curr_user->isOnline==INTRO)
+            {
+                simple_load_html("1_intro.html",sockfd,buff,n,curr_user);
+                
+            }
+
+            else if (curr_user->isOnline==START){
+                simple_load_html("3_first_turn.html",sockfd,buff,n,curr_user);
+                curr_user->stage = GUESS;
+            }
+            else if (curr_user->stage==ENDED){
+                simple_load_html("3_first_turn.html",sockfd,buff,n,curr_user);
+                curr_user->stage = 1;
+                if (the_other_user(curr_user, user_arr)->stage==ENDED){
+                    the_other_user(curr_user, user_arr)->stage==RSTAR;
+                }
+            }
+        }
+
+
+
+
+        ///////////// handle POST requests/////////////
+        else if (method == POST){
+
+            if(strstr(buff, "quit=") != NULL)
+            {
+            
+                simple_load_html("7_gameover.html",sockfd,buff,n,curr_user);
+                curr_user->stage=QUIT;
+            }
+
+            // in registeration process: read name into html "2_start.html"
+            else if(curr_user->isOnline == INTRO) 
+            {
+                // get string 
+                char* username = strstr(buff, "user=")+5;
+                char u[MAX_BUFF];
+                strcpy(u,"<br>");
+                strcat(u, username);
+                u[strlen(username)]='\0';
+                curr_user->isOnline = 1;
+                //edit into html
+                edit_load_html("2_start.html", sockfd,buff,username,curr_user);
+            }
+
+            // in guessing process: 
+            else if (curr_user->isOnline == FIRST_TURN ||curr_user->isOnline == START ) {
+                curr_user->isOnline = FIRST_TURN;
+
+                // 1. the other user not ready but not quit: discard curr_user keyword
+                if (curr_user->stage==ENTER || curr_user->stage == RSTAR)
+                {
+                    simple_load_html("5_discarded.html",sockfd,buff,n,curr_user);
+                }
+                else if(the_other_user(curr_user, user_arr)->stage==ENTER && curr_user->stage==GUESS){
+                    simple_load_html("5_discarded.html",sockfd,buff,n,curr_user);
+                }
+                // 2. the other user quit: curr_user quit
+                else if (curr_user->stage==QUIT){
+                    simple_load_html("7_gameover.html",sockfd,buff,n,curr_user);
+                    curr_user->stage = QUIT;
+                }
+                // 3. match word successfully 
+                else if (curr_user->stage == ENDED|| curr_user->stage == RSTAR){
+                    simple_load_html("6_endgame.html",sockfd,buff,n,curr_user);
+                    if (curr_user->stage == RSTAR){
+                        curr_user->stage = ENTER;
+                        curr_user->isOnline = START;
+                        is_second_round = 1;
+
+                    }
+                    else{
+                        curr_user->stage = ENDED;
+                    } 
+                }
+
+                else {
+                    // read the new guess word
+                    char * guess = strstr(buff, "keyword=") + 8;
+                    guess[strlen(guess)-strlen(GUESS_SUFFIX)] = '\0';
+                    if (match(guess, the_other_user(curr_user, user_arr)->keywords, the_other_user(curr_user, user_arr)->n_word)){
+                        // go to end page
+                        curr_user->stage = ENDED;
+                        the_other_user(curr_user, user_arr)->stage = ENDED;
+                        simple_load_html("6_endgame.html",sockfd,buff,n,curr_user);
+                        clear_user_keyword(curr_user);
+                       
+
+
+                    }else{
+                        // read new guess word into curr_user array
+                        record_keyword(curr_user, guess);
+
+                        char guess_buff[MAX_BUFF];
+                        strcpy(guess_buff,"<br>");
+                        // add a null bit at the end of the string
+                        if(curr_user->n_word==1)
+                        {
+                            strcat(guess_buff, guess);
+                            guess_buff[strlen(guess_buff)]='\0';
+
+                        }
+                        // loop through and print every single word 
+                        else
+                        {
+                            for (int i = 0; i < curr_user->n_word; ++i)
+                            {
+                                strcat(guess_buff, curr_user->keywords[i]);
+                                strcat(guess_buff,"<br>");
+                            }
+                        }
+                        edit_load_html("4_accepted.html", sockfd,buff,guess_buff,curr_user);
+
+                    }
+
+                }
+            }
+
+
+        }
+
+        else
+            // never used, just for completeness
+            fprintf(stderr, "no other methods supported");
+    }
+    // send 404
+    else if (write(sockfd, HTTP_404, HTTP_404_LENGTH) < 0)
+    {
+        perror("write");
+        return false;
+    }
+
+    return true;
+}
+
+int main(int argc, char * argv[])
+{
+    if (argc < 3)
+    {
+        fprintf(stderr, "usage: %s ip port\n", argv[0]);
+        return 0;
+    }
+
+    // create TCP socket which only accept IPv4
+    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
+    if (sockfd < 0)
+    {
+        perror("socket");
+        exit(EXIT_FAILURE);
+    }
+
+    // reuse the socket if possible
+    int const reuse = 1;
+    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0)
+    {
+        perror("setsockopt");
+        exit(EXIT_FAILURE);
+    }
+
+    // create and initialise address we will listen on
+    struct sockaddr_in serv_addr;
+    bzero(&serv_addr, sizeof(serv_addr));
+    serv_addr.sin_family = AF_INET;
+    // if ip parameter is not specified
+    serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
+    serv_addr.sin_port = htons(atoi(argv[2]));
+
+    // bind address to socket
+    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
+    {
+        perror("bind");
+        exit(EXIT_FAILURE);
+    }
+
+    // listen on the socket
+    listen(sockfd, 5);
+
+    // initialise an active file descriptors set
+    fd_set masterfds;
+    FD_ZERO(&masterfds);
+    FD_SET(sockfd, &masterfds);
+    // record the maximum socket number
+    int maxfd = sockfd;
+
+    //global variable for 2 users
+    struct User* user_arr[2];
+
+
+
+    while (1)
+    {
+        // monitor file descriptors
+        fd_set readfds = masterfds;
+        if (select(FD_SETSIZE, &readfds, NULL, NULL, NULL) < 0)
+        {
+            perror("select");
+            exit(EXIT_FAILURE);
+        }
+
+        // loop all possible descriptor
+        for (int i = 0; i <= maxfd; ++i)
+            // determine if the current file descriptor is active
+            if (FD_ISSET(i, &readfds))
+            {
+                // create new socket if there is new incoming connection request
+                if (i == sockfd)
+                {
+                    struct sockaddr_in cliaddr;
+                    socklen_t clilen = sizeof(cliaddr);
+                    int newsockfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);
+                    if (newsockfd < 0)
+                        perror("accept");
+                    else
+                    {
+                        // add the socket to the set
+                        FD_SET(newsockfd, &masterfds);
+                        // update the maximum tracker
+                        if (newsockfd > maxfd)
+                            maxfd = newsockfd;
+                        // print out the IP and the socket number
+                        char ip[INET_ADDRSTRLEN];
+                        printf(
+                            "new connection from %s on socket %d\n",
+                            // convert to human readable string
+                            inet_ntop(cliaddr.sin_family, &cliaddr.sin_addr, ip, INET_ADDRSTRLEN),
+                            newsockfd
+                        );
+                    }
+                }
+                // a request is sent from the client
+                else if (!handle_http_request(i, user_arr))
+                {
+                    close(i);
+                    FD_CLR(i, &masterfds);
+                }
+            }
+    }
+
+    return 0;
+}
+
+
-- 
GitLab