Many config-loading memory fixes and group join/leave

This commit is contained in:
2026-01-26 18:56:38 +01:00
parent fe703b6ddd
commit bebc068762
4 changed files with 216 additions and 7 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ all: client server
client:
mkdir -p build
$(CC) $(CFLAGS) -o build/client client.c util/util.c
$(CC) $(CFLAGS) -o build/client client.c
server:
mkdir -p build
+46
View File
@@ -116,6 +116,32 @@ static void input_loop(int server_queue_id, const char *client_id) {
if (send(server_queue_id, &msg) == -1) {
perror("msgsnd(/list) failed");
}
} else if (strncmp(buffer, "/join ", 6) == 0) {
char *group_name = buffer + 6;
if (strlen(group_name) == 0) {
printf("Group name cannot be empty\n");
continue;
}
msgbuf_t msg = {.mtype = JoinGroup, .sender = ""};
strncpy(msg.sender, client_id, COMMAND_LENGTH);
strncpy(msg.command, group_name, COMMAND_LENGTH - 1);
if (send(server_queue_id, &msg) == -1) {
perror("msgsnd(/join) failed");
}
} else if (strncmp(buffer, "/leave ", 7) == 0) {
char *group_name = buffer + 7;
if (strlen(group_name) == 0) {
printf("Group name cannot be empty\n");
continue;
}
msgbuf_t msg = {.mtype = LeaveGroup, .sender = ""};
strncpy(msg.sender, client_id, COMMAND_LENGTH);
strncpy(msg.command, group_name, COMMAND_LENGTH - 1);
if (send(server_queue_id, &msg) == -1) {
perror("msgsnd(/leave) failed");
}
} else if (strncmp(buffer, "/mute ", 6) == 0) {
// handle mute
} else if (strcmp(buffer, "/quit") == 0 || strcmp(buffer, "/exit") == 0 || strcmp(buffer, "/q") == 0) {
@@ -277,6 +303,26 @@ int main(int argc, char *argv[]) {
printf("Unhandled signal - code %d.\n", incoming.stype);
}
break;
case JoinGroup:
if (incoming.stype == ACK_ACCEPTED) {
printf("Successfully joined the group.\n");
} else if (incoming.stype == ERR_USER_NOT_FOUND) {
printf("Group does not exist.\n");
} else if (incoming.stype == ERR_ALREADY_IN_GROUP) {
printf("You are already in this group.\n");
} else {
printf("Failed to join group: Unknown error (code %d).\n", incoming.stype);
}
break;
case LeaveGroup:
if (incoming.stype == ACK_ACCEPTED) {
printf("Successfully left the group.\n");
} else if (incoming.stype == ERR_USER_NOT_FOUND) {
printf("Group does not exist.\n");
} else {
printf("Failed to leave group: 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;
+3 -1
View File
@@ -10,7 +10,8 @@ typedef enum {
Logout,
Hearbeat,
List,
Group,
JoinGroup,
LeaveGroup,
Mute,
Unmute,
Signal,
@@ -23,6 +24,7 @@ typedef enum {
ERR_UNKNOWN_COMMAND,
ERR_NOT_LOGGED_IN,
ERR_ALREADY_LOGGED_IN,
ERR_ALREADY_IN_GROUP,
ERR_USER_NOT_FOUND,
ERR_USER_MUTED,
ERR_INVALID_FORMAT
+166 -5
View File
@@ -1,4 +1,3 @@
#include "bits/types/struct_timeval.h"
#include "sys/ipc.h"
#include "sys/shm.h"
#include <fcntl.h>
@@ -98,8 +97,9 @@ int main(int argc, char **argv) {
for (int i = 0; i < group_count; i++) {
strncpy(groups[i].name, config.groups[i].name, COMMAND_LENGTH - 2);
groups[i].client_count = config.groups[i].member_count;
groups[i].members = malloc(sizeof(Client_t *) * config.groups[i].member_count);
// Allocate space for all clients to be able to join, not just initial members
groups[i].client_count = client_count;
groups[i].members = calloc(client_count, sizeof(Client_t *));
for (int j = 0; j < config.groups[i].member_count; j++) {
// Find the client with the given id
for (int k = 0; k < client_count; k++) {
@@ -236,12 +236,14 @@ int main(int argc, char **argv) {
// Now we have to decode if we are to forward this message to a user or a
// group. A group is indicated by a # at the start of the command
char target_type = msgbuf.command[0];
Client_t **recipients = malloc(sizeof(Client_t *) * client_count - 1); // We won't send to ourselves so one less
Client_t **recipients = malloc(sizeof(Client_t *) * (client_count - 1)); // We won't send to ourselves so one less
if (target_type != '#') {
recipients[0] = NULL; // Initialize to NULL in case no match is found
for (int i = 0; i < client_count; i++) {
if (strncmp(clients[i].nickname, msgbuf.command, COMMAND_LENGTH) == 0) {
recipients[0] = &clients[i];
recipients[1] = NULL; // Mark end of list
break;
}
}
@@ -263,6 +265,7 @@ int main(int argc, char **argv) {
.stype = ERR_USER_NOT_FOUND,
};
send(client->queue_id, &response);
free(recipients);
continue;
}
@@ -280,6 +283,18 @@ 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);
free(recipients);
continue;
}
// Now forward the message to all recipients
for (int i = 0; i < client_count - 1; i++) {
Client_t *target_client = recipients[i];
@@ -329,6 +344,7 @@ int main(int argc, char **argv) {
printf("Forwarding message: %s\n", msgbuf.message);
}
free(recipients);
break;
}
case Logout: {
@@ -381,7 +397,7 @@ int main(int argc, char **argv) {
for (int i = 0; i < group_count; i++) {
bool isMember = false;
for (int j = 0; j < groups[i].client_count; j++) {
if (strncmp(groups[i].members[j]->id, client->id, COMMAND_LENGTH) == 0) {
if (groups[i].members[j] != NULL && strncmp(groups[i].members[j]->id, client->id, COMMAND_LENGTH) == 0) {
isMember = true;
break;
}
@@ -487,7 +503,152 @@ int main(int argc, char **argv) {
client->saved_message_count = 0;
break;
}
// Command field contains the group name
case JoinGroup: {
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;
printf("Request to join for group: %s\n", msgbuf.command);
Group_t *target_group = NULL;
for (int i = 0; i < group_count; i++) {
printf("Comparing group[%d].name='%s' (len=%zu) with command='%s' (len=%zu)\n", i, groups[i].name,
strlen(groups[i].name), msgbuf.command, strlen(msgbuf.command));
if (strcmp(groups[i].name, msgbuf.command) == 0) {
target_group = &groups[i];
printf("Group match found!\n");
break;
}
}
// Target group not found
if (target_group == NULL) {
printf("Target group %s not found\n", msgbuf.command);
msgbuf_t response = {
.mtype = JoinGroup,
.stype = ERR_USER_NOT_FOUND,
};
send(client->queue_id, &response);
continue;
}
// Check if user is already in the group
bool is_in_group = false;
printf("Group '%s' has %d member slots\n", target_group->name, target_group->client_count);
for (int i = 0; i < target_group->client_count; i++) {
if (target_group->members[i] != NULL) {
printf(" Slot %d: %s\n", i, target_group->members[i]->id);
if (strncmp(client->id, target_group->members[i]->id, COMMAND_LENGTH) == 0) {
is_in_group = true;
break;
}
} else {
printf(" Slot %d: NULL\n", i);
}
}
if (is_in_group) {
msgbuf_t response = {.mtype = JoinGroup, .stype = ERR_ALREADY_IN_GROUP};
send(client->queue_id, &response);
continue;
}
// Only after we know that the user isn't in the group, by going over the whole list, we can add them
// We could do that in the loop above, if there was a guarantee of there not being gaps in the array
// This should be a linked list... well it's too late now...
bool added = false;
for (int i = 0; i < target_group->client_count; i++) {
if (target_group->members[i] == NULL) {
target_group->members[i] = client;
msgbuf_t response = {
.mtype = JoinGroup,
.stype = ACK_ACCEPTED,
};
send(client->queue_id, &response);
printf("Client %s joined group %s\n", client->id, target_group->name);
added = true;
break;
}
}
if (!added) {
msgbuf_t response = {
.mtype = JoinGroup,
.stype = ERR_USER_NOT_FOUND, // Reusing errors, why not
};
send(client->queue_id, &response);
}
break;
}
case LeaveGroup: {
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;
printf("Request to leave group: %s\n", msgbuf.command);
Group_t *target_group = NULL;
for (int i = 0; i < group_count; i++) {
printf("Comparing group[%d].name='%s' with command='%s'\n", i, groups[i].name, msgbuf.command);
if (strcmp(groups[i].name, msgbuf.command) == 0) {
target_group = &groups[i];
break;
}
}
// Target group not found
if (target_group == NULL) {
printf("Target group %s not found\n", msgbuf.command);
msgbuf_t response = {
.mtype = LeaveGroup,
.stype = ERR_USER_NOT_FOUND,
};
send(client->queue_id, &response);
continue;
}
bool was_in_group = false;
for (int i = 0; i < target_group->client_count; i++) {
if (target_group->members[i] != NULL &&
strncmp(client->id, target_group->members[i]->id, COMMAND_LENGTH) == 0) {
target_group->members[i] = NULL;
msgbuf_t response = {
.mtype = LeaveGroup,
.stype = ACK_ACCEPTED,
};
send(client->queue_id, &response);
printf("Client %s left group %s\n", client->id, target_group->name);
was_in_group = true;
break;
}
}
if (!was_in_group) {
msgbuf_t response = {
.mtype = LeaveGroup,
.stype = ERR_USER_NOT_FOUND, // Could add ERR_NOT_IN_GROUP error code
};
send(client->queue_id, &response);
}
break;
}
case Mute:
case Unmute:
break;
case Signal: