diff --git a/client.c b/client.c index 3ba6512..ccc4e42 100644 --- a/client.c +++ b/client.c @@ -143,7 +143,31 @@ static void input_loop(int server_queue_id, const char *client_id) { perror("msgsnd(/leave) failed"); } } else if (strncmp(buffer, "/mute ", 6) == 0) { - // handle mute + char *nickname = buffer + 6; + if (strlen(nickname) == 0) { + printf("Nickname cannot be empty\n"); + continue; + } + + msgbuf_t msg = {.mtype = Mute, .sender = ""}; + strncpy(msg.sender, client_id, COMMAND_LENGTH - 1); + strncpy(msg.command, nickname, COMMAND_LENGTH - 1); + if (send(server_queue_id, &msg) == -1) { + perror("msgsnd(/mute) failed"); + } + } else if (strncmp(buffer, "/unmute ", 8) == 0) { + char *nickname = buffer + 8; + if (strlen(nickname) == 0) { + printf("Nickname cannot be empty\n"); + continue; + } + + msgbuf_t msg = {.mtype = Unmute, .sender = ""}; + strncpy(msg.sender, client_id, COMMAND_LENGTH - 1); + strncpy(msg.command, nickname, COMMAND_LENGTH - 1); + if (send(server_queue_id, &msg) == -1) { + perror("msgsnd(/unmute) failed"); + } } else if (strcmp(buffer, "/quit") == 0 || strcmp(buffer, "/exit") == 0 || strcmp(buffer, "/q") == 0) { return; } else if (strncmp(buffer, "/inbox", 8) == 0) { @@ -210,7 +234,7 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Login failed: User already logged in\n"); cleanup_queue(client_queue_id); return 1; - } else if (resp.stype == ERR_USER_NOT_FOUND) { + } else if (resp.stype == ERR_NOT_FOUND) { fprintf(stderr, "Login failed: User not found\n"); cleanup_queue(client_queue_id); return 1; @@ -222,7 +246,6 @@ int main(int argc, char *argv[]) { client_queue_id_global = client_queue_id; server_queue_id_global = server_queue_id; - heartbeat_pid = fork(); if (heartbeat_pid == 0) { heartbeat_loop(server_queue_id, client_id); @@ -296,7 +319,7 @@ int main(int argc, char *argv[]) { printf("Message sent successfully.\n"); } else if (incoming.stype == ACK_DELIVERED) { printf("Message delivered to %s.\n", incoming.sender); - } else if (incoming.stype == ERR_USER_NOT_FOUND) { + } else if (incoming.stype == ERR_NOT_FOUND) { printf("Your message could not be delivered: User not found.\n"); } else { // Display whatever if I forgot to handle something @@ -306,7 +329,7 @@ int main(int argc, char *argv[]) { case JoinGroup: if (incoming.stype == ACK_ACCEPTED) { printf("Successfully joined the group.\n"); - } else if (incoming.stype == ERR_USER_NOT_FOUND) { + } else if (incoming.stype == ERR_NOT_FOUND) { printf("Group does not exist.\n"); } else if (incoming.stype == ERR_ALREADY_IN_GROUP) { printf("You are already in this group.\n"); @@ -317,12 +340,32 @@ int main(int argc, char *argv[]) { case LeaveGroup: if (incoming.stype == ACK_ACCEPTED) { printf("Successfully left the group.\n"); - } else if (incoming.stype == ERR_USER_NOT_FOUND) { + } else if (incoming.stype == ERR_NOT_FOUND) { printf("Group does not exist.\n"); } else { printf("Failed to leave group: Unknown error (code %d).\n", incoming.stype); } break; + case Mute: + if (incoming.stype == ACK_ACCEPTED) { + printf("Successfully muted %s.\n", incoming.command); + } else if (incoming.stype == ERR_NOT_FOUND) { + printf("%s not found.\n", incoming.command); + } else if (incoming.stype == ERR_ALREADY_IN_GROUP) { + printf("%s is already muted.\n", incoming.command); + } else { + printf("Failed to mute: Unknown error (code %d).\n", incoming.stype); + } + break; + case Unmute: + if (incoming.stype == ACK_ACCEPTED) { + printf("Successfully unmuted %s.\n", incoming.command); + } else if (incoming.stype == ERR_NOT_FOUND) { + printf("%s wasn't muted.\n", incoming.command); + } else { + printf("Failed to unmute: Unknown error (code %d).\n", incoming.stype); + } + break; default: printf("Received unknown command of mtype=%ld stype=%d\n", (long)incoming.mtype, incoming.stype); break; diff --git a/commands.h b/commands.h index e7dc853..e097998 100644 --- a/commands.h +++ b/commands.h @@ -25,8 +25,8 @@ typedef enum { ERR_NOT_LOGGED_IN, ERR_ALREADY_LOGGED_IN, ERR_ALREADY_IN_GROUP, - ERR_USER_NOT_FOUND, - ERR_USER_MUTED, + ERR_NOT_FOUND, + ERR_ALREADY_MUTED, ERR_INVALID_FORMAT } Signal_t; diff --git a/server.c b/server.c index f7e28a6..3894422 100644 --- a/server.c +++ b/server.c @@ -26,7 +26,10 @@ typedef struct { typedef struct { char id[COMMAND_LENGTH]; bool logged_in; - char *muted_clients; + char (*muted_clients)[COMMAND_LENGTH]; + int muted_count; + char (*muted_groups)[COMMAND_LENGTH]; + int muted_groups_count; char nickname[COMMAND_LENGTH]; // Nicknames cannot contain special characters // like # (reserved for groups) int queue_id; @@ -88,7 +91,10 @@ int main(int argc, char **argv) { for (int i = 0; i < client_count; i++) { strncpy(clients[i].id, config.clients[i].client_id, COMMAND_LENGTH - 1); clients[i].logged_in = false; - clients[i].muted_clients = NULL; + clients[i].muted_clients = calloc(client_count, COMMAND_LENGTH); + clients[i].muted_count = 0; + clients[i].muted_groups = calloc(group_count, COMMAND_LENGTH); + clients[i].muted_groups_count = 0; strncpy(clients[i].nickname, config.clients[i].nickname, COMMAND_LENGTH - 1); clients[i].queue_id = -1; clients[i].last_seen = 0; @@ -168,7 +174,7 @@ int main(int argc, char **argv) { printf("User not found: %s\n", msgbuf.sender); msgbuf_t response = { .mtype = Login, - .stype = ERR_USER_NOT_FOUND, + .stype = ERR_NOT_FOUND, }; send(msg_queue_id, &response); @@ -262,7 +268,7 @@ int main(int argc, char **argv) { printf("Target group %s not found\n", msgbuf.command + 1); msgbuf_t response = { .mtype = Message, - .stype = ERR_USER_NOT_FOUND, + .stype = ERR_NOT_FOUND, }; send(client->queue_id, &response); free(recipients); @@ -272,9 +278,27 @@ int main(int argc, char **argv) { // Forward the message to all group members except the sender int recipient_idx = 0; for (int i = 0; i < target_group->client_count; i++) { + if (target_group->members[i] == NULL) + continue; if (strncmp(target_group->members[i]->id, client->id, COMMAND_LENGTH) == 0) { continue; } + + // Check if this member has muted the group + bool has_muted_group = false; + for (int j = 0; j < target_group->members[i]->muted_groups_count; j++) { + if (strcmp(target_group->members[i]->muted_groups[j], target_group->name) == 0) { + has_muted_group = true; + break; + } + } + + if (has_muted_group) { + printf("Group message to %s blocked (group %s is muted)\n", + target_group->members[i]->nickname, target_group->name); + continue; + } + recipients[recipient_idx++] = target_group->members[i]; } @@ -285,12 +309,21 @@ int main(int argc, char **argv) { // Check if we found any recipients if (recipients[0] == NULL) { - printf("Target user %s not found\n", msgbuf.command); - msgbuf_t response = { - .mtype = Signal, - .stype = ERR_USER_NOT_FOUND, - }; - send(client->queue_id, &response); + if (target_type == '#') { + printf("Group message sent but no recipients received it (all muted or group empty)\n"); + msgbuf_t response = { + .mtype = Signal, + .stype = ACK_ACCEPTED, + }; + send(client->queue_id, &response); + } else { + printf("Target user %s not found\n", msgbuf.command); + msgbuf_t response = { + .mtype = Signal, + .stype = ERR_NOT_FOUND, + }; + send(client->queue_id, &response); + } free(recipients); continue; } @@ -302,8 +335,21 @@ int main(int argc, char **argv) { if (target_client == NULL) break; - // Check if the target is online or not, if they are not, store the - // message + // Check if target_client has muted the sender + bool is_muted = false; + for (int j = 0; j < target_client->muted_count; j++) { + if (strcmp(target_client->muted_clients[j], client->nickname) == 0) { + is_muted = true; + break; + } + } + + if (is_muted) { + printf("Message from %s to %s blocked (muted)\n", client->nickname, target_client->nickname); + continue; // Skip this recipient, discard the message + } + + // Check if the target is online or not, if they are not, store the message if (!target_client->logged_in) { saved_message_t *saved_msg = NULL; if (target_client->saved_message_count >= 20) { @@ -534,7 +580,7 @@ int main(int argc, char **argv) { printf("Target group %s not found\n", msgbuf.command); msgbuf_t response = { .mtype = JoinGroup, - .stype = ERR_USER_NOT_FOUND, + .stype = ERR_NOT_FOUND, }; send(client->queue_id, &response); continue; @@ -582,7 +628,7 @@ int main(int argc, char **argv) { if (!added) { msgbuf_t response = { .mtype = JoinGroup, - .stype = ERR_USER_NOT_FOUND, // Reusing errors, why not + .stype = ERR_NOT_FOUND, // Reusing errors, why not }; send(client->queue_id, &response); } @@ -616,7 +662,7 @@ int main(int argc, char **argv) { printf("Target group %s not found\n", msgbuf.command); msgbuf_t response = { .mtype = LeaveGroup, - .stype = ERR_USER_NOT_FOUND, + .stype = ERR_NOT_FOUND, }; send(client->queue_id, &response); continue; @@ -641,16 +687,182 @@ int main(int argc, char **argv) { if (!was_in_group) { msgbuf_t response = { .mtype = LeaveGroup, - .stype = ERR_USER_NOT_FOUND, // Could add ERR_NOT_IN_GROUP error code + .stype = ERR_NOT_FOUND, // Could add ERR_NOT_IN_GROUP error code }; send(client->queue_id, &response); } break; } - case Mute: + case Mute: { + Client_t *client = NULL; + for (int i = 0; i < client_count; i++) { + if (strncmp(clients[i].id, msgbuf.sender, COMMAND_LENGTH) == 0) { + client = &clients[i]; + break; + } + } - case Unmute: + if (client == NULL || !client->logged_in) + continue; + + // Check if muting a group (starts with #) or a user + if (msgbuf.command[0] == '#') { + // Muting a group + char *group_name = msgbuf.command + 1; // Skip the # + + // Find the group + Group_t *target_group = NULL; + for (int i = 0; i < group_count; i++) { + if (strcmp(groups[i].name, group_name) == 0) { + target_group = &groups[i]; + break; + } + } + + if (target_group == NULL) { + msgbuf_t response = {.mtype = Mute, .stype = ERR_NOT_FOUND}; + strncpy(response.command, msgbuf.command, COMMAND_LENGTH); + send(client->queue_id, &response); + continue; + } + + // Check if already muted + bool already_muted = false; + for (int i = 0; i < client->muted_groups_count; i++) { + if (strcmp(client->muted_groups[i], group_name) == 0) { + already_muted = true; + break; + } + } + + if (already_muted) { + msgbuf_t response = {.mtype = Mute, .stype = ERR_ALREADY_IN_GROUP}; + strncpy(response.command, msgbuf.command, COMMAND_LENGTH); + send(client->queue_id, &response); + continue; + } + + // Add to muted groups list + if (client->muted_groups_count < group_count) { + strncpy(client->muted_groups[client->muted_groups_count], group_name, COMMAND_LENGTH - 1); + client->muted_groups_count++; + msgbuf_t response = {.mtype = Mute, .stype = ACK_ACCEPTED}; + strncpy(response.command, msgbuf.command, COMMAND_LENGTH); + send(client->queue_id, &response); + printf("Client %s muted group %s\n", client->id, group_name); + } + } else { + // Muting a user + Client_t *target = NULL; + for (int i = 0; i < client_count; i++) { + if (strcmp(clients[i].nickname, msgbuf.command) == 0) { + target = &clients[i]; + break; + } + } + + if (target == NULL) { + msgbuf_t response = {.mtype = Mute, .stype = ERR_NOT_FOUND}; + send(client->queue_id, &response); + continue; + } + + // Check if already muted + bool already_muted = false; + for (int i = 0; i < client->muted_count; i++) { + if (strcmp(client->muted_clients[i], target->nickname) == 0) { + already_muted = true; + break; + } + } + + if (already_muted) { + msgbuf_t response = {.mtype = Mute, .stype = ERR_ALREADY_IN_GROUP}; + send(client->queue_id, &response); + continue; + } + + // Add to muted list + if (client->muted_count < client_count) { + strncpy(client->muted_clients[client->muted_count], target->nickname, COMMAND_LENGTH - 1); + client->muted_count++; + msgbuf_t response = {.mtype = Mute, .stype = ACK_ACCEPTED}; + strncpy(response.command, msgbuf.command, COMMAND_LENGTH); + send(client->queue_id, &response); + printf("Client %s muted %s\n", client->id, target->nickname); + } + } break; + } + case Unmute: { + Client_t *client = NULL; + for (int i = 0; i < client_count; i++) { + if (strncmp(clients[i].id, msgbuf.sender, COMMAND_LENGTH) == 0) { + client = &clients[i]; + break; + } + } + + if (client == NULL || !client->logged_in) + continue; + + // Check if unmuting a group (starts with #) or a user + if (msgbuf.command[0] == '#') { + char *group_name = msgbuf.command + 1; + int muted_index = -1; + for (int i = 0; i < client->muted_groups_count; i++) { + if (strcmp(client->muted_groups[i], group_name) == 0) { + muted_index = i; + break; + } + } + + if (muted_index == -1) { + msgbuf_t response = {.mtype = Unmute, .stype = ERR_NOT_FOUND}; + strncpy(response.command, msgbuf.command, COMMAND_LENGTH); + send(client->queue_id, &response); + continue; + } + + // Remove from muted groups list by shifting remaining elements + for (int i = muted_index; i < client->muted_groups_count - 1; i++) { + strncpy(client->muted_groups[i], client->muted_groups[i + 1], COMMAND_LENGTH); + } + client->muted_groups_count--; + + msgbuf_t response = {.mtype = Unmute, .stype = ACK_ACCEPTED, .command = ""}; + strncpy(response.command, msgbuf.command, COMMAND_LENGTH); + send(client->queue_id, &response); + printf("Client %s unmuted group %s\n", client->id, group_name); + } else { + int muted_index = -1; + for (int i = 0; i < client->muted_count; i++) { + if (strcmp(client->muted_clients[i], msgbuf.command) == 0) { + muted_index = i; + break; + } + } + + if (muted_index == -1) { + msgbuf_t response = {.mtype = Unmute, .stype = ERR_NOT_FOUND}; + strncpy(response.command, msgbuf.command, COMMAND_LENGTH); + send(client->queue_id, &response); + continue; + } + + // Remove from muted list by shifting remaining elements + for (int i = muted_index; i < client->muted_count - 1; i++) { + strncpy(client->muted_clients[i], client->muted_clients[i + 1], COMMAND_LENGTH); + } + client->muted_count--; + + msgbuf_t response = {.mtype = Unmute, .stype = ACK_ACCEPTED}; + strncpy(response.command, msgbuf.command, COMMAND_LENGTH); + send(client->queue_id, &response); + printf("Client %s unmuted %s\n", client->id, msgbuf.command); + } + break; + } case Signal: // Signals are sent from server to client, not the other way around // so we ignore them here lol