Many config-loading memory fixes and group join/leave
This commit is contained in:
@@ -6,7 +6,7 @@ all: client server
|
|||||||
|
|
||||||
client:
|
client:
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
$(CC) $(CFLAGS) -o build/client client.c util/util.c
|
$(CC) $(CFLAGS) -o build/client client.c
|
||||||
|
|
||||||
server:
|
server:
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
|
|||||||
@@ -116,6 +116,32 @@ static void input_loop(int server_queue_id, const char *client_id) {
|
|||||||
if (send(server_queue_id, &msg) == -1) {
|
if (send(server_queue_id, &msg) == -1) {
|
||||||
perror("msgsnd(/list) failed");
|
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) {
|
} else if (strncmp(buffer, "/mute ", 6) == 0) {
|
||||||
// handle mute
|
// handle mute
|
||||||
} else if (strcmp(buffer, "/quit") == 0 || strcmp(buffer, "/exit") == 0 || strcmp(buffer, "/q") == 0) {
|
} 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);
|
printf("Unhandled signal - code %d.\n", incoming.stype);
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
printf("Received unknown command of mtype=%ld stype=%d\n", (long)incoming.mtype, incoming.stype);
|
printf("Received unknown command of mtype=%ld stype=%d\n", (long)incoming.mtype, incoming.stype);
|
||||||
break;
|
break;
|
||||||
|
|||||||
+3
-1
@@ -10,7 +10,8 @@ typedef enum {
|
|||||||
Logout,
|
Logout,
|
||||||
Hearbeat,
|
Hearbeat,
|
||||||
List,
|
List,
|
||||||
Group,
|
JoinGroup,
|
||||||
|
LeaveGroup,
|
||||||
Mute,
|
Mute,
|
||||||
Unmute,
|
Unmute,
|
||||||
Signal,
|
Signal,
|
||||||
@@ -23,6 +24,7 @@ typedef enum {
|
|||||||
ERR_UNKNOWN_COMMAND,
|
ERR_UNKNOWN_COMMAND,
|
||||||
ERR_NOT_LOGGED_IN,
|
ERR_NOT_LOGGED_IN,
|
||||||
ERR_ALREADY_LOGGED_IN,
|
ERR_ALREADY_LOGGED_IN,
|
||||||
|
ERR_ALREADY_IN_GROUP,
|
||||||
ERR_USER_NOT_FOUND,
|
ERR_USER_NOT_FOUND,
|
||||||
ERR_USER_MUTED,
|
ERR_USER_MUTED,
|
||||||
ERR_INVALID_FORMAT
|
ERR_INVALID_FORMAT
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#include "bits/types/struct_timeval.h"
|
|
||||||
#include "sys/ipc.h"
|
#include "sys/ipc.h"
|
||||||
#include "sys/shm.h"
|
#include "sys/shm.h"
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@@ -98,8 +97,9 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
for (int i = 0; i < group_count; i++) {
|
for (int i = 0; i < group_count; i++) {
|
||||||
strncpy(groups[i].name, config.groups[i].name, COMMAND_LENGTH - 2);
|
strncpy(groups[i].name, config.groups[i].name, COMMAND_LENGTH - 2);
|
||||||
groups[i].client_count = config.groups[i].member_count;
|
// Allocate space for all clients to be able to join, not just initial members
|
||||||
groups[i].members = malloc(sizeof(Client_t *) * config.groups[i].member_count);
|
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++) {
|
for (int j = 0; j < config.groups[i].member_count; j++) {
|
||||||
// Find the client with the given id
|
// Find the client with the given id
|
||||||
for (int k = 0; k < client_count; k++) {
|
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
|
// 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
|
// group. A group is indicated by a # at the start of the command
|
||||||
char target_type = msgbuf.command[0];
|
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 != '#') {
|
if (target_type != '#') {
|
||||||
|
recipients[0] = NULL; // Initialize to NULL in case no match is found
|
||||||
for (int i = 0; i < client_count; i++) {
|
for (int i = 0; i < client_count; i++) {
|
||||||
if (strncmp(clients[i].nickname, msgbuf.command, COMMAND_LENGTH) == 0) {
|
if (strncmp(clients[i].nickname, msgbuf.command, COMMAND_LENGTH) == 0) {
|
||||||
recipients[0] = &clients[i];
|
recipients[0] = &clients[i];
|
||||||
|
recipients[1] = NULL; // Mark end of list
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,6 +265,7 @@ int main(int argc, char **argv) {
|
|||||||
.stype = ERR_USER_NOT_FOUND,
|
.stype = ERR_USER_NOT_FOUND,
|
||||||
};
|
};
|
||||||
send(client->queue_id, &response);
|
send(client->queue_id, &response);
|
||||||
|
free(recipients);
|
||||||
continue;
|
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
|
// Now forward the message to all recipients
|
||||||
for (int i = 0; i < client_count - 1; i++) {
|
for (int i = 0; i < client_count - 1; i++) {
|
||||||
Client_t *target_client = recipients[i];
|
Client_t *target_client = recipients[i];
|
||||||
@@ -329,6 +344,7 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
printf("Forwarding message: %s\n", msgbuf.message);
|
printf("Forwarding message: %s\n", msgbuf.message);
|
||||||
}
|
}
|
||||||
|
free(recipients);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Logout: {
|
case Logout: {
|
||||||
@@ -381,7 +397,7 @@ int main(int argc, char **argv) {
|
|||||||
for (int i = 0; i < group_count; i++) {
|
for (int i = 0; i < group_count; i++) {
|
||||||
bool isMember = false;
|
bool isMember = false;
|
||||||
for (int j = 0; j < groups[i].client_count; j++) {
|
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;
|
isMember = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -487,7 +503,152 @@ int main(int argc, char **argv) {
|
|||||||
client->saved_message_count = 0;
|
client->saved_message_count = 0;
|
||||||
break;
|
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 Mute:
|
||||||
|
|
||||||
case Unmute:
|
case Unmute:
|
||||||
break;
|
break;
|
||||||
case Signal:
|
case Signal:
|
||||||
|
|||||||
Reference in New Issue
Block a user