void receive_user_data(void) { char buf[BUF_SIZE] = {0}; char message[BUF_SIZE] = {0}; char *name; Client *client; int read_bytes = read(0, buf, BUF_SIZE); assert(read_bytes < BUF_SIZE); if (read_bytes == -1) { warn("Could not read from STDIN data."); return; } if (read_bytes == 1) { /* read only LF symbol */ info("Could not send empty message."); return; } /* remove last LF symbol readed from stdin */ buf[read_bytes - 1] = '\0'; switch(parse_user_command(buf)) { case KILL: name = get_command_argument(buf); if (!name) { warn("Name of client should be provided."); break; } client = vector_delete_first_equal(_s_clients, name, cmp_clients_by_name); info("Server has '%d' clients.", vector_total(_s_clients)); if (client) { sprintf(message, "The client '%s' left the chat.", client->name); send_broadcast_msg(message); info("%s", message); /* send info data to client */ sprintf(message, "server: You was killed."); send_msg(client, message); client_destroy(client); } else { info("Client '%s' was not found.", name); } break; case SHUTDOWN: sprintf(message, "The server is going shutdown. Bye."); send_broadcast_msg(message); _s_runing = 0; info("%s", message); break; case WHO: vector_foreach(_s_clients, message, collect_all_names); info("All clients: '%s'", message); break; default: warn("Unknown command. Allowed commands: _kill <name>, _shutdown, _who"); break; } }
void handle_client_data(Client *a_client, char *a_data, const char *a_address) { char *name = NULL; char message[BUF_SIZE] = {0}; char buf[BUF_SIZE] = {0}; ServerCommand client_cmd = parse_client_command(a_data); if (a_client->connected == 0 && client_cmd != CONNECT) { warn("Client with addres '%s' is not connected." "Message '%s' not allowed.", a_address, a_data); return; } else if (a_client->connected == 1 && client_cmd == CONNECT) { warn("Cannot add client from address '%s'. Client with the same" "address already connected.", a_address); return; } switch (client_cmd) { case CONNECT: name = get_command_argument(a_data); verificate_name(&name); a_client->name = name; sprintf(message, "New client '%s' was connected.", a_client->name); send_broadcast_msg(message); info("%s", message); a_client->connected = 1; break; case QUIT: vector_delete_first_equal(_s_clients, a_client, cmp_clients_by_ptr); sprintf(message, "The client '%s' left the chat.", a_client->name); send_broadcast_msg(message); info("%s", message); client_destroy(a_client); break; case WHO: vector_foreach(_s_clients, buf, collect_all_names); sprintf(message, "All clients: %s", buf); info("Send info about all clients to '%s'.", a_client->name); send_msg(a_client, message); break; case UNDEFINED: /* send received message to all clients */ info("Send '%s' to all clients from '%s'.", a_data, a_client->name); sprintf(message, "%s: %s", a_client->name, a_data); send_broadcast_msg(message); break; default: assert("Unknown command " == NULL); break; } }
static void receive_tcp_client_data(Client *a_client) { char buf[BUF_SIZE] = {0}; char address[64] = {0}; int res; res = connection_tcp_receive(a_client->conn, buf, BUF_SIZE); if (res) { vector_delete_first_equal(_s_clients, a_client, cmp_clients_by_ptr); char message[BUF_SIZE] = {0}; sprintf(message, "Something happened. The client '%s' left the chat.", a_client->name); send_broadcast_msg(message); info("%s", message); client_destroy(a_client); return; } sprintf(address, "%s:%d", connection_get_address_str(a_client->conn), connection_get_port(a_client->conn)); info("From address '%s' recived message '%s'", address, buf); handle_client_data(a_client, buf, address); }
/* * Process a chat message coming from a chat client. */ void process_msg(char *message, int self_sockfd) { char buffer[1024]; regex_t regex_quit; regex_t regex_nick; regex_t regex_msg; regex_t regex_me; regex_t regex_who; int ret; char newnick[20]; char oldnick[20]; char priv_nick[20]; struct list_entry *list_entry = NULL; struct list_entry *nick_list_entry = NULL; struct list_entry *priv_list_entry = NULL; int processed = FALSE; size_t ngroups = 0; size_t len = 0; regmatch_t groups[3]; memset(buffer, 0, 1024); memset(newnick, 0, 20); memset(oldnick, 0, 20); memset(priv_nick, 0, 20); /* Load client info object */ list_entry = llist_find_by_sockfd(&list_start, self_sockfd); /* Remove \r\n from message */ chomp(message); /* Compile regex patterns */ regcomp(®ex_quit, "^/quit$", REG_EXTENDED); regcomp(®ex_nick, "^/nick ([a-zA-Z0-9_]{1,19})$", REG_EXTENDED); regcomp(®ex_msg, "^/msg ([a-zA-Z0-9_]{1,19}) (.*)$", REG_EXTENDED); regcomp(®ex_me, "^/me (.*)$", REG_EXTENDED); regcomp(®ex_who, "^/who$", REG_EXTENDED); /* Check if user wants to quit */ ret = regexec(®ex_quit, message, 0, NULL, 0); if (ret == 0) { /* Notify */ send_broadcast_msg("%sUser %s has left the chat server.%s\r\n", color_magenta, list_entry->client_info->nickname, color_normal); logline(LOG_INFO, "User %s has left the chat server.", list_entry->client_info->nickname); pthread_mutex_lock(&curr_thread_count_mutex); curr_thread_count--; logline(LOG_DEBUG, "process_msg(): Connections used: %d of %d", curr_thread_count, MAX_THREADS); pthread_mutex_unlock(&curr_thread_count_mutex); /* Remove entry from linked list */ logline(LOG_DEBUG, "process_msg(): Removing element with sockfd = %d", self_sockfd); llist_remove_by_sockfd(&list_start, self_sockfd); /* Disconnect client from server */ close(self_sockfd); /* Free memory */ regfree(®ex_quit); regfree(®ex_nick); regfree(®ex_msg); regfree(®ex_me); /* Terminate this thread */ pthread_exit(0); } /* Check if user wants to change nick */ ngroups = 2; ret = regexec(®ex_nick, message, ngroups, groups, 0); if (ret == 0) { processed = TRUE; /* Extract nickname */ len = groups[1].rm_eo - groups[1].rm_so; strncpy(newnick, message + groups[1].rm_so, len); strcpy(oldnick, list_entry->client_info->nickname); strcpy(buffer, "User "); strcat(buffer, oldnick); strcat(buffer, " is now known as "); strcat(buffer, newnick); /* Change nickname. Check if nickname already exists first. */ nick_list_entry = llist_find_by_nickname(&list_start, newnick); if (nick_list_entry == NULL) { change_nickname(oldnick, newnick); send_broadcast_msg("%s%s%s\r\n", color_yellow, buffer, color_normal); logline(LOG_INFO, buffer); } else { send_private_msg(oldnick, "%sCHATSRV: Cannot change nickname. Nickname already in use.%s\r\n", color_yellow, color_normal); logline(LOG_INFO, "Private message from CHATSRV to %s: Cannot change nickname. Nickname already in use", oldnick); } } /* Check if user wants to transmit a private message to another user */ ngroups = 3; ret = regexec(®ex_msg, message, ngroups, groups, 0); if (ret == 0) { processed = TRUE; /* Extract nickname and private message */ len = groups[1].rm_eo - groups[1].rm_so; memcpy(priv_nick, message + groups[1].rm_so, len); len = groups[2].rm_eo - groups[2].rm_so; memcpy(buffer, message + groups[2].rm_so, len); /* Check if nickname exists. If yes, send private message to user. * If not, ignore message. */ priv_list_entry = llist_find_by_nickname(&list_start, priv_nick); if (priv_list_entry != NULL) { send_private_msg(priv_nick, "%s%s:%s %s%s%s\r\n", color_green, list_entry->client_info->nickname, color_normal, color_red, buffer, color_normal); logline(LOG_INFO, "Private message from %s to %s: %s", list_entry->client_info->nickname, priv_nick, buffer); } } /* Check if user wants to say something about himself */ ngroups = 2; ret = regexec(®ex_me, message, ngroups, groups, 0); if (ret == 0) { processed = TRUE; strcpy(buffer, list_entry->client_info->nickname); /* Prepare message */ len = groups[1].rm_eo - groups[1].rm_so; strcat(buffer, " "); strncat(buffer, message + groups[1].rm_so, len); /* Broadcast message */ send_broadcast_msg("%s%s%s\r\n", color_cyan, buffer, color_normal); logline(LOG_INFO, buffer); } /* Check if user wants a listing of currently connected clients */ ret = regexec(®ex_who, message, 0, NULL, 0); if (ret == 0) { processed = TRUE; int socklen = sizeof(list_entry->client_info->address); logline(LOG_INFO, "%s requested the client list", list_entry->client_info->nickname); char **nicks = malloc(sizeof(*nicks) * 1000); int i; for (i = 0; i < 1000; i++) nicks[i] = malloc(sizeof(**nicks) * 30); int count = llist_get_nicknames(&list_start, nicks); memset(buffer, 0, 1024); for (i = 0; i < count; i++) { sprintf(buffer, "%s%s%s%s", buffer, color_magenta, nicks[i], color_normal); if(i != (count - 1)) sprintf(buffer, "%s, ", buffer); if(i == (count - 1)) sprintf(buffer, "%s\r\n", buffer); free(nicks[i]); } sendto(list_entry->client_info->sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&(list_entry->client_info->address), (socklen_t)socklen); free(nicks); } /* Broadcast message */ if (processed == FALSE) { send_broadcast_msg("%s%s:%s %s\r\n", color_green, list_entry->client_info->nickname, color_normal, message); logline(LOG_INFO, "%s: %s", list_entry->client_info->nickname, message); } /* Dump current user list */ llist_show(&list_start); /* Free memory */ regfree(®ex_quit); regfree(®ex_nick); regfree(®ex_msg); regfree(®ex_me); regfree(®ex_who); }
/* * This method is run on a per-connection basis in a separate thred. */ void proc_client(int *arg) { char buffer[1024]; char message[1024]; int ret = 0; int len = 0; int socklen = 0; struct list_entry *list_entry; fd_set readfds; int sockfd = 0; /* Load associated client info */ sockfd = *arg; list_entry = llist_find_by_sockfd(&list_start, sockfd); memset(message, 0, 1024); FD_ZERO(&readfds); FD_SET(sockfd, &readfds); /* Send welcome message */ send_welcome_msg(sockfd); /* Announce to others using broadcast */ send_broadcast_msg("%sUser %s joined the chat.%s\r\n", color_magenta, list_entry->client_info->nickname, color_normal); /* Process requests */ while (1) { ret = select(FD_SETSIZE, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *) 0); if (ret == -1) { logline(LOG_ERROR, "Error calling select() on thread."); perror(strerror(errno)); } else { /* Read data from stream */ memset(buffer, 0, 1024); socklen = sizeof(list_entry->client_info->address); len = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&list_entry->client_info->address, (socklen_t *)&socklen); logline(LOG_DEBUG, "proc_client(): Receive buffer contents = %s", buffer); /* Copy receive buffer to message buffer */ if (sizeof(message) - strlen(message) > strlen(buffer)) { strcat(message, buffer); } /* Check if message buffer contains a full message. A full message * is recognized by its terminating \n character. If a message * is found, process it and clear message buffer afterwards. */ char *pos = strstr(message, "\n"); if (pos != NULL) { chomp(message); logline(LOG_DEBUG, "proc_client(): Message buffer contents = %s", message); logline(LOG_DEBUG, "proc_client(): Complete message received."); /* Process message */ process_msg(message, sockfd); memset(message, 0, 1024); } else { logline(LOG_DEBUG, "proc_client(): Message buffer contents = %s", message); logline(LOG_DEBUG, "proc_client(): Message still incomplete."); } } } }
/* * Process a chat message coming from a chat client. */ void process_msg(char *message, int self_sockfd) { char buffer[BUF_SIZE_1K]; char statsBuf[BUF_SIZE_1K]; regex_t regex_quit; regex_t regex_nick; regex_t regex_msg; //regex_t regex_me; regex_t regex_stats; int ret; char newnick[20]; char oldnick[20]; char priv_nick[20]; struct list_entry *list_entry = NULL; struct list_entry *nick_list_entry = NULL; struct list_entry *priv_list_entry = NULL; int processed = FALSE; size_t ngroups = 0; size_t len = 0; regmatch_t groups[3]; memset(buffer, 0, BUF_SIZE_1K); memset(newnick, 0, 20); memset(oldnick, 0, 20); memset(priv_nick, 0, 20); /* Load client info object */ list_entry = llist_find_by_sockfd(&list_start, self_sockfd); /* Remove \r\n from message */ chomp(message); /* Compile regex patterns */ regcomp(®ex_quit, "^/logout$", REG_EXTENDED); regcomp(®ex_nick, "^/chatName ([a-zA-Z0-9_]{1,19})$", REG_EXTENDED); regcomp(®ex_msg, "^/privateMessage ([a-zA-Z0-9_]{1,19}) (.*)$", REG_EXTENDED); regcomp(®ex_stats, "^/showStats$", REG_EXTENDED); /* Check if user wants to quit */ ret = regexec(®ex_quit, message, 0, NULL, 0); if (ret == 0) { /* Notify */ send_broadcast_msg("\n%sUser: %s has left the chat server.%s\n\r\n", color_magenta, list_entry->client_info->nickname, color_normal); printf("\nUser: %s has left the chat server.\n", list_entry->client_info->nickname); pthread_mutex_lock(&curr_thread_count_mutex); curr_thread_count--; pthread_mutex_unlock(&curr_thread_count_mutex); /* Remove entry from linked list */ llist_remove_by_sockfd(&list_start, self_sockfd); serverStats.num_of_active_connections--; /* Disconnect client from server */ close(self_sockfd); /* Free memory */ regfree(®ex_quit); regfree(®ex_nick); regfree(®ex_msg); regfree(®ex_stats); /* Terminate this thread */ pthread_exit(0); } /* Check if user wants to change nick */ ngroups = 2; ret = regexec(®ex_nick, message, ngroups, groups, 0); if (ret == 0) { processed = TRUE; /* Extract nickname */ len = groups[1].rm_eo - groups[1].rm_so; strncpy(newnick, message + groups[1].rm_so, len); strcpy(oldnick, list_entry->client_info->nickname); strcpy(buffer, "User: "******" is now known as - "); strcat(buffer, newnick); /* Change nickname. Check if nickname already exists first. */ nick_list_entry = llist_find_by_nickname(&list_start, newnick); if (nick_list_entry == NULL) { change_nickname(oldnick, newnick); send_broadcast_msg("\n\n%s\n%s\n%s\n\r\n", color_yellow, buffer, color_normal); printf("Broadcast Message is sent for username change\n"); } else { send_private_msg(oldnick, "%sCHATSRV: Cannot change nickname. Nickname already in use.%s\r\n", color_yellow, color_normal); printf("Private message from CHATSRV to %s: Cannot change nickname. Nickname already in use\n", oldnick); } } /* Check if user wants to transmit a private message to another user */ ngroups = 3; ret = regexec(®ex_msg, message, ngroups, groups, 0); if (ret == 0) { processed = TRUE; /* Extract nickname and private message */ len = groups[1].rm_eo - groups[1].rm_so; memcpy(priv_nick, message + groups[1].rm_so, len); len = groups[2].rm_eo - groups[2].rm_so; memcpy(buffer, message + groups[2].rm_so, len); /* Check if nickname exists. If yes, send private message to user. * If not, ignore message. */ priv_list_entry = llist_find_by_nickname(&list_start, priv_nick); if (priv_list_entry != NULL) { send_private_msg(priv_nick, "%s %s:\n%s %s %s %s\r\n", color_green, list_entry->client_info->nickname, color_normal, color_red, buffer, color_normal); printf("Private message from %s to %s: %s\n", list_entry->client_info->nickname, priv_nick, buffer); } } /* Check if user wants to say something about himself */ ngroups = 2; //TODO: ret = regexec(®ex_stats, message, ngroups, groups, 0); if (ret == 0) { processed = TRUE; int socklen = sizeof(list_entry->client_info->address); printf("%s requested the client list\n", list_entry->client_info->nickname); char **nicks = malloc(sizeof(*nicks) * 1000); int i; for (i = 0; i < 1000; i++) nicks[i] = malloc(sizeof(**nicks) * 30); int count = llist_get_nicknames(&list_start, nicks); memset(buffer, 0, BUF_SIZE_1K); for (i = 0; i < count; i++) { sprintf(buffer, "%s%s%s%s", buffer, color_magenta, nicks[i], color_normal); if(i != (count - 1)) sprintf(buffer, "%s, ", buffer); if(i == (count - 1)) sprintf(buffer, "%s\r\n", buffer); free(nicks[i]); } memset(statsBuf, 0, BUF_SIZE_1K); sprintf(statsBuf, "Max threads: %d\nNumber of active connections: %d\nNumber of clients serviced: %d\nNumber of clients dropped:%d\n",serverStats.max_threads, serverStats.num_of_active_connections,serverStats.num_of_clients_serviced, serverStats.num_of_conn_dropped); printf("Server Stats : \n%s\n", statsBuf); sendto(list_entry->client_info->sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&(list_entry->client_info->address), (socklen_t)socklen); sendto(list_entry->client_info->sockfd, statsBuf, strlen(statsBuf), 0, (struct sockaddr *)&(list_entry->client_info->address), (socklen_t)socklen); free(nicks); } /* Broadcast message */ if (processed == FALSE) { send_broadcast_msg("%s%s:%s %s\r\n", color_green, list_entry->client_info->nickname, color_normal, message); printf("%s: %s", list_entry->client_info->nickname, message); } /* Dump current user list */ llist_show(&list_start); /* Free memory */ regfree(®ex_quit); regfree(®ex_nick); regfree(®ex_msg); regfree(®ex_stats); }
void server_read_client(void *arg) /// This function runs in a thread for every client, and reads incoming data. /// It also writes the incoming data to all other clients. { char buffer[BUF_SIZE_1K]; char message[BUF_SIZE_1K]; int ret = 0; int len = 0; int socklen = 0; struct list_entry *list_entry; fd_set readfds; int sockfd = 0; /* Load associated client info */ sockfd = *(int *)arg; list_entry = llist_find_by_sockfd(&list_start, sockfd); memset(message, 0, BUF_SIZE_1K); FD_ZERO(&readfds); FD_SET(sockfd, &readfds); /* Send welcome message */ server_write_welcome_client(sockfd); /* Announce to others using broadcast */ send_broadcast_msg("\n%sUser: %s joined the chat.%s\n\r\n", color_magenta, list_entry->client_info->nickname, color_normal); /* Process requests */ while (1) { ret = select(FD_SETSIZE, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *) 0); if (ret == -1) { printf("Error calling select() on thread.\n"); } else { /* Read data from stream */ memset(buffer, 0, BUF_SIZE_1K); socklen = sizeof(list_entry->client_info->address); len = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&list_entry->client_info->address, (socklen_t *)&socklen); /* Copy receive buffer to message buffer */ if (sizeof(message) - strlen(message) > strlen(buffer)) { strcat(message, buffer); } /* Check if message buffer contains a full message. A full message * is recognized by its terminating \n character. If a message * is found, process it and clear message buffer afterwards. */ char *pos = strstr(message, "\n"); if (pos != NULL) { chomp(message); /*Code will be useful for Our Project */ /* Process message */ process_msg(message, sockfd); memset(message, 0, BUF_SIZE_1K); } else { printf("Message still incomplete....\n"); } } } }