diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..cb8e7532cea0d850d16c2f4676bcb0dd1750c146
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,11 @@
+CC=gcc
+CPPFLAGS=-Wall  -Werror -O3
+
+SRC=image_tagger.c
+TARGET=image_tagger
+
+all: $(SRC)
+	$(CC) -o $(TARGET) $(SRC) $(CPPFLAGS)
+
+clean:
+	rm -f $(TARGET) *.o
\ No newline at end of file
diff --git a/image_tagger.c b/image_tagger.c
new file mode 100644
index 0000000000000000000000000000000000000000..65edecf39a1832019edd20b962797c4f79b10236
--- /dev/null
+++ b/image_tagger.c
@@ -0,0 +1,354 @@
+/*
+* image_tagger.c
+* source code from Lab 5/6 of COMP30023
+*/
+
+#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/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// constants
+static char const * const HTTP_200_FORMAT = "HTTP/1.1 200 OK\r\n\
+Content-Type: text/html\r\n\
+Content-Length: %ld\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;
+
+//static char * PLAYER_1_NAME;
+//static char * PLAYER_2_NAME;
+//static int PLAYER_1_GAME_STATE = 0;
+//static int PLAYER_2_GAME_STATE = 0;
+
+static char* KEYWORD_LIST_P1[100];
+static char* KEYWORD_LIST_P2[100];
+static int NUM_KEYWORD_P1 = 0;
+static int NUM_KEYWORD_P2 = 0;
+
+// represents the types of method
+typedef enum
+{
+    GET,
+    POST,
+    UNKNOWN
+} METHOD;
+
+static char* getURL(char* curr) {
+	char* url;
+	if (strncmp(curr, "1_intro.html ", 13) == 0 || strncmp(curr, " HTTP/1.1", 9) == 0)
+    {
+        url = "1_intro.html";
+    } else if(strncmp(curr, "2_start.html ", 13) == 0)
+    {
+        url = "2_start.html";
+    } else if(strncmp(curr, "3_first_turn.html ", 18) == 0 || strncmp(curr, "?start=Start ", 13) == 0)
+    {
+        url = "3_first_turn.html";
+    } else if(strncmp(curr, "4_accepted.html ", 16) == 0)
+    {
+        url = "4_accepted.html";
+    } else if(strncmp(curr, "5_discarded.html ", 17) == 0)
+    {
+        url = "5_discarded.html";
+    } else if(strncmp(curr, "6_endgame.html ", 15) == 0)
+    {
+        url = "6_endgame.html";
+    } else if(strncmp(curr, "7_gameover.html ", 16) == 0)
+    {
+        url = "7_gameover.html";
+    } else {
+		url = " ";
+	}
+	return url;
+}
+
+static bool addKeyword(char * keyword, int playerNum) {
+	int i = 0;
+	bool match = false;
+	if(playerNum == 1) {
+		while(KEYWORD_LIST_P2[i] != NULL) {
+			if((strcmp(KEYWORD_LIST_P2[i], keyword)) == 0) {
+				match = true;
+			}
+			i++;
+		}
+		KEYWORD_LIST_P1[NUM_KEYWORD_P1] = keyword;
+		NUM_KEYWORD_P1++;
+	} else {
+		while(KEYWORD_LIST_P1[i] != NULL) {
+			if((strcmp(KEYWORD_LIST_P1[i], keyword)) == 0) {
+				match = true;
+			}
+			i++;
+		}
+		KEYWORD_LIST_P2[NUM_KEYWORD_P2] = keyword;
+		NUM_KEYWORD_P2++;
+	}
+	return match;
+}
+
+static bool getRequest(int sockfd, char* buff, char* url, int n) {
+	// get the size of the file
+    struct stat st;
+    stat(url, &st);
+    n = sprintf(buff, HTTP_200_FORMAT, st.st_size);
+    // send the header first
+    if (write(sockfd, buff, n) < 0)
+    {
+        perror("write");
+        return false;
+    }
+    // send the file
+    int filefd = open(url, O_RDONLY);
+    do
+    {
+        n = sendfile(sockfd, filefd, NULL, 2048);
+    }
+    while (n > 0);
+    if (n < 0)
+    {
+        perror("sendfile");
+        close(filefd);
+        return false;
+    }
+    close(filefd);
+	return true;
+}
+
+static bool postRequest(int sockfd, char* buff, char* url, int n) {
+	char * file;
+	char * post = "hello";
+	if(strcmp(url,"1_intro.html")) {
+		file = "2_start.html";
+	} else if(strcmp(url,"2_start.html")) {
+		//file = "7_gameover.html";
+	} else if(strcmp(url,"3_first_turn.html")) {
+		if(addKeyword(post,1)) {
+			file = "6_endgame.html";
+		} else {
+			file = "4_accepted.html";
+		}
+	} 
+	
+	struct stat st;
+    stat(file, &st);
+    n = sprintf(buff, HTTP_200_FORMAT, st.st_size);
+    // send the header first
+    if (write(sockfd, buff, n) < 0)
+    {
+        perror("write");
+        return false;
+    }
+    // send the file
+    int filefd = open(file, O_RDONLY);
+    do
+    {
+        n = sendfile(sockfd, filefd, NULL, 2048);
+    }
+    while (n > 0);
+    if (n < 0)
+    {
+        perror("sendfile");
+        close(filefd);
+        return false;
+    }
+    close(filefd);
+	return true;
+}
+
+static bool handle_http_request(int sockfd)
+{
+    // try to read the request
+    char buff[2049];
+	char * url;
+    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;
+	printf("%s /n", buff);
+    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;
+    }
+
+    // sanitise the URI
+    while (*curr == '.' || *curr == '/') {
+        ++curr;
+    // assume the only valid request URI is "/" but it can be modified to accept more files
+	}
+	
+    if (*curr != '\0') 
+	{
+		url = getURL(curr);
+		if(strcmp(url," ") == 0) {
+			perror("read");
+			return false;
+		}
+        if (method == GET)
+        {
+            if(getRequest(sockfd,buff,url,n) == false) {
+				return false;
+			}
+        }
+        else if (method == POST)
+        {
+            if(postRequest(sockfd,buff,url,n) == false) {
+				return false;
+			}
+        }
+        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;
+	
+	char str[INET_ADDRSTRLEN];
+	printf("image_tagger server is now running at IP: %s on port %d\n", inet_ntop(AF_INET, &serv_addr.sin_addr, str, INET_ADDRSTRLEN), ntohs(serv_addr.sin_port));
+
+    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))
+                { 
+                    close(i);
+                    FD_CLR(i, &masterfds);
+                }
+            }
+    }
+
+    return 0;
+}