-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathcommands.c
402 lines (349 loc) · 15.4 KB
/
commands.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
/* File that contains the information necessary to implement all of the
* commands in the server.
* Author: YOUR NAME HERE */
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include "server.h"
#include "commands.h"
#include "command_utils.h"
#include "server_utils.h"
#include "user_utils.h"
#include "client_server_utils.h"
/* List of commands the server recognizes. */
char* commands[COMMAND_COUNT] = {"exit", "server_exit", "set_nickname", "clear_nickname",
"rename", "mute", "unmute", "show_status", "show_all_statuses"};
/* List of functions to handle each command. Each function is at the same index
* as the command is in the previous list to allow a command name search to
* give easy access to a function with an extended if else. */
void (*command_functions[COMMAND_COUNT]) (char **args, unsigned count, unsigned n) =
{handle_exit, handle_server_exit, handle_set_nickname, handle_clear_nickname, handle_rename,
handle_mute, handle_unmute, handle_show_status, handle_show_all_statuses};
/* Function that takes in a message that is a command sent by user at index
* n and parses the message into a command name and its arguments. It then
* calls the appropriate function to handle a command with that name. */
void parse_command (char *message, unsigned n) {
int i = 0;
while (message[i] != '\\') {
i++;
}
char *name = strtok (message + i + 1, WHITESPACE_SET);
if (!isword (name)) {
int end = 0;
while (isidentifierpart (name[end])) {
end++;
}
name[end] = 0;
if (isknowncommand (name)) {
handle_invalid_arguments (name, n);
return;
} else {
handle_unknown_command (name, n);
return;
}
}
if (!isknowncommand (name)) {
handle_unknown_command (name, n);
} else {
unsigned arg_limit = 3;
char *args[3];
char *arg;
unsigned count = 0;
while (count < arg_limit && (arg = strtok (NULL, WHITESPACE_SET)) != NULL) {
args[count] = arg;
count++;
if (!isword (arg)) {
handle_invalid_arguments (name, n);
return;
}
}
if (count == arg_limit) {
handle_invalid_arguments (name, n);
return;
}
handle_command (name, args, count, n);
}
}
/* A function that takes a command name, the arguments, the number of
* arguments, and the index of the user who gave the command and calls
* the appropriate function to handle the command. */
void handle_command (char *name, char **args, unsigned count, unsigned n) {
unsigned ctr = 0;
while (ctr < COMMAND_COUNT) {
if (strcmp (name, commands[ctr]) == 0) {
command_functions [ctr] (args, count, n);
return;
}
ctr++;
}
handle_unknown_command (name, n);
}
/* Function that handles the exit command. It sends the client back
* a message to exit using the Exit_Message character (see
* client_server_utils.h) at the start of the message. The
* function takes in no arguments. The function is also passed in
* the total number of args the function was called with, count and
* the index of the user who sent the command. */
void handle_exit (char **args, unsigned count, unsigned n) {
if (count != 0) {
handle_invalid_arguments ("exit", n);
} else {
char message [3];
message[0] = Exit_Message;
message[1] = '\n';
message[2] = 0;
reply (message, n);
}
}
/* Function that handles the server_exit command. It closes all of
* the sockets that are currently open, frees all the messages for
* any of the users who were open and cleans up all of the remaining
* users. The function takes in no arguments. The function is also
* passed in the total number of args the function was called with,
* count and the index of the user who sent the command. */
void handle_server_exit (char **args, unsigned count, unsigned n) {
if (count != 0) {
handle_invalid_arguments ("server_exit", n);
} else {
close (sockets[0]);
socket_total--;
int start = 0;
int ctr = 1;
while (start < socket_total) {
if (sockets[ctr] != -1) {
start++;
close (sockets[ctr]);
sockets[ctr] = -1;
free (messages[ctr]);
if (users[ctr] != NULL) {
cleanup_user (users[ctr]);
}
}
ctr++;
}
exit (0);
}
}
/* ***************************READ ME*******************************
* *
* *
* This begins the section you will edit. If you encounter an error*
* be sure and call the handle_invalid_arguments function and then *
* exit the function. See handle_server_exit above for an example *
* of how to use it. Also be sure and check the error conditions *
* on the spec. *
* *
* A few functions you may find userful: *
* - isvalidname (command_utils.h) *
* - find_user (user_utils.h) *
* - has_nickname (user_utils.h) *
* - create_name (user_utils.h) *
* - output_user_status (command_utils.h) *
* *
* ****************************************************************/
/* Function that handles the set_nickname command. The command takes
* exactly 2 arguments, stored in args. The first is a name which
* must be the name of an existing user. The second is word which
* must be a valid choice for a new name. It sets the user whose name
* is given by the first argument's nickname to the second argument.
* Then all messages from that user should display that nickname.
* The function is also passed in the total number of args the function
* was called with, count and the index of the user who sent the command.
* You do not need to check that any args passed in are either too long
* or consist of invalid characters, this has already been checked for you.*/
void handle_set_nickname (char **args, unsigned count, unsigned n) {
/* HANDLE ANY POSSIBLE ERROR CONDITIONS */
/* IMPLEMENT THE CORE FUNCTIONALITY */
struct user_info *user = NULL; /* REPLACE ME */
/* WE HANDLE MESSAGE OUTPUT FOR YOU */
char *other_messages[6];
other_messages[0] = users[n]->name_info->name;
other_messages[1] = " set ";
other_messages[2] = user->name_info->name;
other_messages[3] = "'s nickname to ";
other_messages[4] = args[1];
other_messages[5] = "\n";
char *message = create_message (other_messages, 6);
share_message (message, n, false);
free (message);
other_messages[0] = "You set ";
if (user == users[n]) {
other_messages[1] = "your";
other_messages[2] = " nickname to ";
} else {
other_messages[1] = other_messages[2];
other_messages[2] = other_messages[3];
}
other_messages[3] = other_messages[4];
other_messages[4] = other_messages[5];
message = create_message (other_messages, 5);
reply (message, n);
free (message);
/* ANYTHING ELSE THAT NEEDS TO BE DONE? DOES ANYTHING
* NEED TO BE FREED? */
}
/* Function to handle the clear_nickname command. It takes in one
* argument, a name, which must be the name of an existing user.
* The function removes the nickname of the user if one exists
* and resets it to that user's name.
* The function is also passed in the total number of args the
* function was called with, count and the index of the user who
* sent the command. You do not need to check that any args passed
* in are either too long or consist of invalid characters, this
* has already been checked for you. */
void handle_clear_nickname (char **args, unsigned count, unsigned n) {
/* HANDLE ANY POSSIBLE ERROR CONDITIONS */
/* IMPLEMENT THE CORE FUNCTIONALITY */
struct user_info *user = NULL; /* REPLACE ME */
/* WE HANDLE MESSAGE OUTPUT FOR YOU */
char *other_messages[4];
other_messages[0] = users[n]->name_info->name;
other_messages[1] = " has cleared ";
other_messages[2] = user->name_info->name;
other_messages[3] = "'s nickname\n";
char *message = create_message (other_messages, 4);
share_message (message, n, false);
free (message);
other_messages[0] = "You have cleared ";
if (user == users[n]) {
other_messages[1] = "your";
other_messages[2] = " nickname\n";
} else {
other_messages[1] = other_messages[2];
other_messages[2] = other_messages[3];
}
message = create_message (other_messages, 3);
reply (message, n);
free (message);
/* ANYTHING ELSE THAT NEEDS TO BE DONE? DOES ANYTHING
* NEED TO BE FREED? */
}
/* Function to handle the rename command. It takes one argument which must
* be a valid name. The user who called the command will have their name
* changed to the name passed in. Then any attempts to access that user
* will need to refer to this new name and not the old one. Also if the
* user does not have a nickname this name should be displayed instead
* of the old name. The function is also passed in the total number of args
* the function was called with, count and the index of the user who sent
* the command. You do not need to check that any args passed in are either
* too long or consist of invalid characters, this has already been checked
* for you.*/
void handle_rename (char **args, unsigned count, unsigned n) {
/* HANDLE ANY POSSIBLE ERROR CONDITIONS */
/* USEFUL VARIABLES PRESET FOR MESSAGE OUTPUT */
char *name = args[0];
struct user_info *user = users[n];
char *old_name = user->name_info->name;
/* IMPLEMENT THE CORE FUCNTIONALITY */
/* WE HANDLE MESSAGE OUTPUT FOR YOU */
char *other_messages[4];
other_messages[0] = old_name;
other_messages[1] =" changed their name to ";
other_messages[2] = name;
other_messages[3] = "\n";
char *message = create_message (other_messages, 4);
share_message (message, n, false);
free (message);
other_messages[0] = "You have changed your name to ";
other_messages[1] = name;
other_messages[2] = "\n";
message = create_message (other_messages, 3);
reply (message, n);
free (message);
/* ANYTHING ELSE THAT NEEDS TO BE DONE? DOES ANYTHING
* NEED TO BE FREED? */
}
/* Function to handle the mute command. It takes one argument, a name, which
* must be the name of an existing user. If the user is not already muted
* by the user who called the command, set that user to by muted by for the
* user who called the command. Then all messages sent by the user who's name
* is the first argument should not be received by the user who issued the
* mute command. The function is also passed in the total number of args the
* function was called with, count and the index of the user who sent the
* command. */
void handle_mute (char **args, unsigned count, unsigned n) {
/* HANDLE ERROR CONDITIONS */
/* IMPLEMENT THE CORE FUNCTIONALITY */
/* WE HANDLE MESSAGE OUTPUT FOR YOU */
char *messages[3];
messages[0] = "User ";
messages[1] = args[0];
messages[2] = " is now muted\n";
char *message = create_message (messages, 3);
reply (message, n);
free (message);
/* ANYTHING ELSE THAT NEEDS TO BE DONE? DOES ANYTHING
* NEED TO BE FREED? */
}
/* Function to handle the unmute command. It takes one argument, a name, which
* must be the name of an existing user. If the user is currently muted by the
* user who called the command. Then that user should resume receiving messages
* from that user. The function is also passed in the total number of args the
* function was called with, count and the index of the user who sent the
* command. You do not need to check that any args passed in are either too long
* or consist of invalid characters, this has already been checked for you.*/
void handle_unmute (char **args, unsigned count, unsigned n) {
/* HANDLE ERROR CONDITIONS */
/* IMPLEMENT THE CORE FUNCTIONALITY */
/* WE HANDLE MESSAGE OUTPUT FOR YOU */
char *other_messages[3];
other_messages[0] = "User ";
other_messages[1] = args[0];
other_messages[2] = " is no longer muted\n";
char *message = create_message (other_messages, 3);
reply (message, n);
free (message);
/* ANYTHING ELSE THAT NEEDS TO BE DONE? DOES ANYTHING
* NEED TO BE FREED? */
}
/* Function that handles the show_status command. It takes 1 argument,
* a name, which must be an existing user. It then display the status
* of that user to the user who called the command. The function is
* also passed in the total number of args the function was called with,
* count and the index of the user who sent the command. You may find
* the function output_user_status helpful. You do not need to check
* that any args passed in are either too long or consist of invalid
* characters, this has already been checked for you. */
void handle_show_status (char **args, unsigned count, unsigned n) {
/* HANDLE ERROR CONDITIONS. */
/* IMPLEMENT THE CORE FUNCTIONALITY */
}
/* Function that handles the show_all_statuses command. It returns the status
* information of all connected users in alphabetical order. It takes no arguments.
* The function is also passed in the total number of args the function was called
* with, count and the index of the user who sent the command. You may find the
* functions sort_users and output_user_status (in command_utils.h)
* helpful. You do not need to check that any args passed in are either too long
* or consist of invalid characters, this has already been checked for you. */
void handle_show_all_statuses (char **args, unsigned count, unsigned n) {
if (count != 0) {
handle_invalid_arguments ("show_all_statuses", n);
} else {
sort_users(&n);
for (int i = 1; i < socket_total; i++) {
output_user_status (users[i], n);
}
}
}
/* Function that handles a known command being called with the wrong
* arguments. It replies to the user at index that the command name
* was called with the wrong arguments. */
void handle_invalid_arguments (char *name, unsigned n) {
char *start = "Incorrect arguments for ";
char *end = " command\n";
char *messages[] = {start, name, end};
char *new_message = create_message (messages, 3);
reply (new_message, n);
free (new_message);
}
/* Function that handles a command that is not known. It replies to
* the user at index n that the command name does not exist. */
void handle_unknown_command (char *name, unsigned n) {
char *start = "Unknown command ";
char *end = "\n";
char *messages[] = {start, name, end};
char *new_message = create_message (messages, 3);
reply (new_message, n);
free (new_message);
}