Skip to content
Snippets Groups Projects
Select Git revision
  • 2ee0f797e325d099d1224501471a4b30db54654b
  • master default protected
  • find_previous_work
3 results

new.c

Blame
  • Anqi Chen's avatar
    Anqi Chen authored
    9e8a1563
    History
    new.c 14.28 KiB
    /*
    ** http-server.c
    */
    
    #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>
    
    
    #define MAX_BUFF 2049
    
    
    
    // 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";
    
    
    
    
    
    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;
    
    
    
    typedef enum
    {
        GET,
        POST,
        UNKNOWN
    } METHOD;
    
    
    
    // struct User
    struct User {
        int userID;
        char* username;
        char** keywords;
        int isOnline; // Online=1, OffLine=0
        int stage;
        int n_word;
    };
    
    
    // malloc a newUser
    struct User* newUser() {
        struct User* newUser = malloc(sizeof (struct User));
        newUser->userID = 0;
        //newUser->username = malloc(MAX_BUFF*sizeof(char));
        //newUser->keywords = NULL;
        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;
    }
    
    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;
        }
    }
    
    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;
        }
        printf("find_cookie in buff %s\n", cookie_id);
        //int cookie_length = strlen(username);
        temp[strlen(temp)-3]='\0';
        printf("find_cookie in buff %s\n", cookie_id);
    
    
        return  atoi(temp);
    }
    
    
    struct User* find_curr(struct User* user_arr[], char* buff)
    {
        int cookie_id = find_cookie(buff);
    
        printf("the kmimimji%d  %d\n", cookie_id,user_arr[0]->userID);
        if(cookie_id==0){
            
            struct User* curr_user = newUser();
            
    
    
            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);
        }
        
        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];
                
            }
        }
    
    }
    
    struct User* the_other_user(struct User* curr_user, struct User* user_arr[]){
        for(int i=0; i<2; i++){
            if (user_arr[i]!=curr_user){
                return user_arr[i];
            }
        }
    }
    
    
    bool match(char* w1, char** w2, int n_word){
        for (int i=0; i<n_word; i++){
            if (w2[i]==w1){
                return true;
            }
        }
        return false;
    }
    
    
    void simple_load_html(char* html, int sockfd, char* buff, int n, struct User* curr_user){
        struct stat st;
        stat(html, &st);
        n = sprintf(buff, HTTP_200_FORMAT, st.st_size+6,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);
    }
    
        /* adds the new word in the html (between the <body> */
    void plug_in_html(char* newword, char* old_buffer, char* new_buffer){
    
        // slice the html string into 2 parts
        char start[MAX_BUFF];
        char* end = strstr(old_buffer, "</body>");
    
        // front part
        strncpy(start, old_buffer, strlen(old_buffer)-strlen(end));
        start[strlen(start)] = '\0';
    
        // combine 
        strcpy(new_buffer, start);
        strcat(new_buffer, newword);
        strcat(new_buffer, end);
    
        new_buffer[strlen(new_buffer)] = '\0';
    }
    
    
    
    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);
    
        // 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+6, 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);
    
        // makes new buffer, copies the username into it
        char* new_buffer = (char *) malloc(sizeof(MAX_BUFF));
        plug_in_html(newword, buff, new_buffer);
    
        if (write(sockfd, new_buffer, size) < 0)
        {
            perror("write");
            return;
        }
        free(new_buffer);
    }
    
    void record_keyword(struct User* curr_user, char *new_guess){
        for (int i=0; i<=curr_user->n_word; i++){
            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);
                curr_user->n_word++;
                break;
            }
        }
    }
    
    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 == '?')
    
            //Stage1: when user name is not registed
            if (curr_user->stage==0){
                if (method==GET)
                {
                    printf("curr_user->username%s == %d\n", curr_user->username,curr_user->username==NULL);
                    simple_load_html("1_intro.html",sockfd,buff,n,curr_user);
                }
            
                else if (method == POST)
                {
                    printf("curr_user->username%s == %d\n", curr_user->username,curr_user->username==NULL);
                    // locate the username, it is safe to do so in this sample code, but usually the result is expected to be
                    // copied to another buffer using strcpy or strncpy to ensure that it will not be overwritten.
                    char * user = strstr(buff, "user=") + 5;
                    char username[MAX_BUFF];
                    strcpy(username, "<br>");
                    strcat(username, user);
                    username[strlen(username)] = '\0';
                    edit_load_html("2_start.html", sockfd,buff,username,curr_user);
                    printf("buffer %s\n",buff );
                    curr_user->stage=1;
                }
            }
    
    
            //Stage 1: start the game
            else if(curr_user->stage==1)
            {   
                
                // quit: user not online
                if(strstr(buff, "quit=Quit") != NULL)
                {   curr_user->isOnline=0;
                    simple_load_html("7_gameover.html",sockfd,buff,n,curr_user);
                    curr_user->stage=3;
    
                }
                // start is the only GET mathod for ther stage 1
                else if (method == GET)
                {   curr_user->isOnline=1;
                    simple_load_html("3_first_turn.html",sockfd,buff,n,curr_user);
                    curr_user->stage =2;
                    curr_user->isOnline=1;
                }
            }
    
    
            // Stage 2: in the process of guessing
            else if (curr_user->stage ==2){
    
                char * new_guess = strstr(buff, "keyword=") + 8;
    
                if(strstr(buff, "quit=Quit") != NULL)
                {   curr_user->isOnline=0;
                    simple_load_html("7_gameover.html",sockfd,buff,n,curr_user);
                    clear_user_keyword(curr_user);
                }
    
                else if (the_other_user(curr_user, user_arr)->stage==3){
                    simple_load_html("7_gameover.html",sockfd,buff,n,curr_user);
                    clear_user_keyword(curr_user);
                }
    
                else if(the_other_user(curr_user, user_arr)->isOnline==0){
                    simple_load_html("5_discarded.html",sockfd,buff,n,curr_user);
                    clear_user_keyword(curr_user);
                }
    
    
                else if(new_guess!=NULL){
                    if(match(new_guess, the_other_user(curr_user, user_arr)->keywords, the_other_user(curr_user, user_arr)->n_word))
                    {
                        simple_load_html("6_endgame.html",sockfd,buff,n,curr_user);
                        clear_user_keyword(curr_user);
                    }
                    else{
                        curr_user->stage =2;
                        printf("curr_user->n_word%d\n",curr_user->n_word );
                        // remember new guess
                        record_keyword(curr_user, new_guess);
                        // display new guess
                        char guess[MAX_BUFF];
                        strcpy(guess,"<br>");
                        if (curr_user->n_word==1){
                            strcat(guess, new_guess);
                            guess[strlen(new_guess)]='\0';
                        }else
                        {
                            for (int i = 0; i < curr_user->n_word; i++)
                            {
                                strcat(guess, curr_user->keywords[i]);
                                strcat(guess,"<br>");
                            }
                        }
    
                        edit_load_html("4_accepted.html", sockfd,buff,guess,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;
    }