/*********************************************************************\ |* chat.c *| |* Squirrel Shock's badass curses-based chat server and client *| |* by Pat "Gizmo King" Schieszer and Pat "Big P" Winslow *| |* *| |* Linux: gcc -O2 chat.c -o chat -lcurses *| |* HP-UX: gcc -O2 chat.c -o chat -lcurses *| |* Solaris: gcc -O2 chat.c -o chat -lcurses -lsocket -lnsl *| |* *| |* Usage: chat ( | -server) [port] nickname [room-size] *| |* Server commands: ban, aban, connect, disconnect *| |* *| |* Fresh and good as of: 08/20/00 *| |* Most recent code available at http://chat.sourceforge.net *| |* KNOX FOR FOUR YEARS, UNIX FOR LIFE *| \*********************************************************************/ #include /* string support */ #include #include #include #include /* For atoi(). */ #include /* network */ #include /* network */ #include /* network */ #include /* network */ #include /* To handle signals. */ #include /* For cool text window support. */ #define LINES_OF_LOCAL 3 // Size of local window #define DIVIDER_Y (LINES - LINES_OF_LOCAL) /* Y-value of horizontal line separating local and remote. */ #define DEFAULT_PORT 4096 // Name says it all #define PENDING_CONNECTION_QUEUE_LENGTH 5 // Queue size for pending connections #define BUFFER_LENGTH 256 // Send buffer limit #define ROOM_LIMIT 512 // Ultimate limit on room size #define NAME_LIMIT 32 // Size limit on nicknames #define NAME_B_LIMIT (NAME_LIMIT + 2) /* Size of nickname buffers which allows for ": " in the nickname record */ #define SEND_LENGTH (64 + NAME_B_LIMIT + BUFFER_LENGTH) // Size of send buffers #define FULL_SERVER 1 // Server is full -- DisconnectShutdown #define NICK_IN_USE 2 // Nickname collision -- Dis.Shutdown #define SERVER_TERMINATE 3 // Server quit -- DisconnectShutdown #define ADDRESS_BANNED 4 // Address banned -- DisconnectShutdown #define PASS_THROUGH 0 // Command type: pass string through #define NICK_BAN 1 // Command type: ban by nick #define ADDR_BAN 2 // Command type: ban by address #define SERVER_CONNECT 3 // Command type: connect to other server #define SERVER_DISCONNECT 4 // Command type: disconnect other server /* These need to be global because we don't *\ \* know how to pass anything to signal handlers. */ int socketFileDescriptor; int newSocketFileDescriptor; int maxFD; WINDOW *localInput, *remoteInput; int local_first = 0, remote_first = 0; char result[SEND_LENGTH]; struct { int type; char param[NAME_B_LIMIT]; struct hostent *hostPointer; int port; } response; void start_curses() { int x_loop; initscr(); cbreak(); nonl(); keypad(stdscr, TRUE); for (x_loop = 0; x_loop < COLS; x_loop++) mvwaddch(stdscr, DIVIDER_Y, x_loop, '_'); mvprintw(DIVIDER_Y, 0, "Send Window"); move(DIVIDER_Y + 1, 0); refresh(); localInput = subwin(stdscr, LINES_OF_LOCAL - 1, COLS, DIVIDER_Y + 1, 0); scrollok(localInput, TRUE); remoteInput = subwin(stdscr, DIVIDER_Y - 1, COLS, 0, 0); scrollok(remoteInput, TRUE); touchwin(stdscr); refresh(); werase(localInput); wmove(localInput, 0, 0); wrefresh(localInput); werase(remoteInput); wrefresh(remoteInput); } void end_curses() { delwin(remoteInput); delwin(localInput); touchwin(stdscr); refresh(); endwin(); } void quit_safe(int return_code) { int fd; for (fd = 3; fd <= maxFD; fd++) close(fd); maxFD = 3; exit(return_code); } void RemoteWrit(int num) { if (remote_first == 0) { mvwprintw(remoteInput, 0, 0, "%d", num); remote_first = 1; } else wprintw(remoteInput, "%d", num); wrefresh(remoteInput); } void RemoteWrite(char *string) { if (remote_first == 0) { mvwprintw(remoteInput, 0, 0, string); remote_first = 1; } else wprintw(remoteInput, string); wrefresh(remoteInput); } void Die(int signal) { /* Got a kill signal. */ int fileDescriptor; end_curses(); switch (signal) { case SIGTERM: printf("\nReceived kill signal. Shutting down.\n"); break; case SIGSEGV: printf("\nDamn, we segfaulted! Shutting down.\n"); break; case SIGINT: printf("\nReceived keyboard interrupt. Shutting down.\n"); break; default: break; }; for (fileDescriptor = 3; fileDescriptor <= maxFD; fileDescriptor++) { close(fileDescriptor); }; exit(0); } void DisconnectShutdown(int code) { /* The remote host is no longer connected. */ int fileDescriptor; if (code == NICK_IN_USE) sleep(3); end_curses(); for(fileDescriptor = 3; fileDescriptor <= maxFD; fileDescriptor++) { close(fileDescriptor); }; switch (code) { case FULL_SERVER: printf("\nServer full. Try again later.\n"); break; case NICK_IN_USE: printf("\nThese names are in use. "); printf("Try again with a different one.\n"); break; case SERVER_TERMINATE: printf("\nThe server has terminated the session.\n"); break; case ADDRESS_BANNED: printf("\nFor whatever reason, your address has been banned.\n"); break; default: printf("\nRemote host disconnected. Shutting down.\n"); break; }; exit(0); } int RunAsClient(char *hostname, int port, char nickname[]) { struct hostent *hostPointer; struct sockaddr_in socketAddress; char buffer[BUFFER_LENGTH], serverNick[NAME_B_LIMIT], Nick[NAME_B_LIMIT]; char send_buffer[SEND_LENGTH]; int length, numActiveFDs, fileDescriptor; fd_set readSet, testSet; /* Translate hostname into peer's IP address. */ hostPointer = gethostbyname(hostname); if (!hostPointer) { end_curses(); fprintf(stderr, "chat: unknown host: %s\n", hostname); quit_safe(1); } /* Build address data structure. */ bzero((char *)&socketAddress, sizeof(socketAddress)); socketAddress.sin_family = AF_INET; bcopy(hostPointer->h_addr, (char *)&socketAddress.sin_addr, hostPointer->h_length); socketAddress.sin_port = htons(port); /* Active open. */ if ((socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)) < 0) { end_curses(); perror("chat: socket"); quit_safe(1); }; /* Keep track of the largest file descriptor. */ maxFD = socketFileDescriptor; if (connect(socketFileDescriptor, (struct sockaddr *)&socketAddress, sizeof(socketAddress)) < 0) { end_curses(); perror("chat: connect"); close(socketFileDescriptor); quit_safe(1); }; FD_ZERO(&readSet); FD_SET(socketFileDescriptor, &readSet); FD_SET(0, &readSet); RemoteWrite("***Connection established.***\n"); /* Send the nickname. */ strcat(nickname, ": "); send(socketFileDescriptor, nickname, strlen(nickname), 0); /* Check to see if the server is full. */ recv(socketFileDescriptor, buffer, sizeof(buffer), 0); if (strcasecmp(buffer, "Ok.")) { if (!(strcasecmp(buffer, "Nickname collision."))) { RemoteWrite("\n\n***The following nicks have been taken:\n"); Nick[0] = '\0'; serverNick[0] = '\0'; strncpy(serverNick, " *** \0", sizeof(" *** \0")); while (strcasecmp(Nick, serverNick)) { recv(socketFileDescriptor, Nick, sizeof(Nick), 0); length = (int) strlen(Nick); Nick[length + 1] = '\0'; RemoteWrite(Nick); RemoteWrite("\n"); }; close(socketFileDescriptor); DisconnectShutdown(NICK_IN_USE); } else if (!(strcasecmp(buffer, "Server full."))) { DisconnectShutdown(FULL_SERVER); } else if (!(strcasecmp(buffer, "Address banned."))) { DisconnectShutdown(ADDRESS_BANNED); } else { DisconnectShutdown(0); }; close(socketFileDescriptor); }; while (1) { testSet = readSet; numActiveFDs = select((maxFD + 1), &testSet, (fd_set *)0, (fd_set *)0, (struct timeval *)0); if (numActiveFDs == -1) { end_curses(); perror("chat: select"); quit_safe(1); } for (fileDescriptor = 0; fileDescriptor <= maxFD; fileDescriptor++) { if (FD_ISSET(fileDescriptor, &testSet)) { if (fileDescriptor == socketFileDescriptor) { // from server if (recv(socketFileDescriptor, buffer, sizeof(buffer), 0) == 0) DisconnectShutdown(SERVER_TERMINATE); RemoteWrite(buffer); RemoteWrite("\n"); } else if (fileDescriptor == 0) { // local input getstr(buffer); send_buffer[0] = '\0'; strcpy(send_buffer, nickname); strcat(send_buffer, buffer); send(socketFileDescriptor, send_buffer, strlen(send_buffer), 0); werase(localInput); wrefresh(localInput); move(DIVIDER_Y + 1, 0); RemoteWrite(send_buffer); RemoteWrite("\n"); }; }; }; }; } /* To get an address string from a numeric address for mangler copy an address into a struct in_addr as param to inet_ntoa use result from inet_ntoa as param to gethostbyname use resulting hostent to get name */ void NSpace_Mangler(unsigned int address, char virgin[SEND_LENGTH]) { struct in_addr addr; struct hostent *host; char *dotted, nick[NAME_LIMIT], message[BUFFER_LENGTH]; int virgin_index, nick_index, message_index; addr.s_addr = address; dotted = (char *)inet_ntoa(addr); host = gethostbyname(dotted); virgin_index = 0; for (nick_index = 0; virgin[virgin_index] != ':'; nick_index++) { nick[nick_index] = virgin[virgin_index]; virgin_index++; }; nick[nick_index] = '\0'; if (virgin[++virgin_index] == ' ') virgin_index++; for (message_index = 0; virgin[virgin_index] != '\0'; message_index++) { message[message_index] = virgin[virgin_index]; virgin_index++; }; message[message_index] = '\0'; result[0] = '\0'; strcpy(result, nick); strcat(result, "@"); strcat(result, host->h_name); strcat(result, ": "); strcat(result, message); } void CommandInterpret(char *commandStr) { char param[NAME_B_LIMIT], command[NAME_LIMIT], port[NAME_LIMIT]; int currentcom = 1, currentparam = 0, currentport = 0; while (commandStr[currentcom] != ' ' && commandStr[currentcom] != '\n' && commandStr[currentcom] != '\0') { command[currentcom - 1] = commandStr[currentcom]; currentcom++; }; command[currentcom++ - 1] = '\0'; if (!(strcasecmp(command, "ban"))) { response.type = NICK_BAN; } else if (!(strcasecmp(command, "aban"))) { response.type = ADDR_BAN; } else if (!(strcasecmp(command, "connect"))) { response.type = SERVER_CONNECT; } else if (!(strcasecmp(command, "disconnect"))) { response.type = SERVER_DISCONNECT; } else { response.type = PASS_THROUGH; return; }; while (response.type > PASS_THROUGH && commandStr[currentcom] != '\n' && commandStr[currentcom] != ' ' && commandStr[currentcom] != '\0') { param[currentparam] = commandStr[currentcom]; currentcom++; currentparam++; }; param[currentparam] = '\0'; if (response.type < SERVER_CONNECT && response.type) { param[currentparam++] = ':'; param[currentparam++] = ' '; } else { response.hostPointer = gethostbyname(param); currentcom++; if (response.type == SERVER_CONNECT) { while (commandStr[currentcom] != '\n' && commandStr[currentcom] != ' ' && commandStr[currentcom] != '\0') { port[currentport] = commandStr[currentcom]; currentcom++; currentport++; }; port[currentport] = '\0'; if (strlen(port) > 1) { response.port = atoi(port); } else response.port = DEFAULT_PORT; }; }; strncpy(response.param, param, sizeof(response.param)); return; } int RunAsServer(int port, char nickname[], int connectionLimit) { struct hostent *hostPointer; struct sockaddr_in socketAddress; int length, numActiveFDs, fileDescriptor, fd_write, index, strlength, goahead; fd_set readSet, testSet, writeSet, serverSet; char NickNames[ROOM_LIMIT][NAME_B_LIMIT]; char sendBuffer[SEND_LENGTH], buffer[SEND_LENGTH]; unsigned int Addr[ROOM_LIMIT], BannedAddr[ROOM_LIMIT], serverAddr[NAME_LIMIT]; unsigned int bob, BanTop = 0, serverTop = 0; for (index = 0; index < ROOM_LIMIT; index++) { NickNames[index][0] = '\0'; }; /* Put the server's nickname in the list. */ strcat(nickname, ": "); strcpy(NickNames[0], nickname); /* Set up the address struct. */ bzero((char *)&socketAddress, sizeof(socketAddress)); socketAddress.sin_family = AF_INET; socketAddress.sin_addr.s_addr = INADDR_ANY; socketAddress.sin_port = htons(port); /* htons() to deal with byte-ordering crap. */ /* Create a socket. */ if ((socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)) < 0) { end_curses(); perror("chat: socket"); quit_safe(1); } /* Keep track of the largest file descriptor. */ maxFD = socketFileDescriptor; /* Name the socket. */ if ((bind(socketFileDescriptor, (struct sockaddr *)&socketAddress, sizeof(socketAddress))) < 0) { end_curses(); perror("chat: bind"); quit_safe(1); }; /* Set up the connection queue. */ listen(socketFileDescriptor, PENDING_CONNECTION_QUEUE_LENGTH); /* Initialize the file descriptor sets. */ FD_ZERO(&readSet); FD_ZERO(&testSet); FD_ZERO(&writeSet); FD_ZERO(&serverSet); FD_SET(socketFileDescriptor, &readSet); FD_SET(0, &readSet); /* Add stdin to the set. */ /* Initialize the address arrays */ for (bob = 0; bob < ROOM_LIMIT; bob++) BannedAddr[bob] = Addr[bob] = 0; for (bob = 0; bob < NAME_LIMIT; bob++) serverAddr[bob] = 0; /* The loop! */ while (1) { writeSet = testSet = readSet; /* Use a copy of the set. */ FD_CLR(socketFileDescriptor, &writeSet); numActiveFDs = select((maxFD + 1), &testSet, (fd_set *)0, (fd_set *)0, (struct timeval *)0); if (numActiveFDs == -1) { end_curses(); perror("chat: select"); quit_safe(1); }; for (fileDescriptor = 0; fileDescriptor <= maxFD; fileDescriptor++) { if (FD_ISSET(fileDescriptor, &testSet)) { if (fileDescriptor == 0) { // We are reading from stdin getstr(buffer); buffer[(sizeof(buffer) - 1)] = '\0'; // null-terminating buffer if (buffer[0]== '#') { // is this a command? CommandInterpret(buffer); } else response.type = PASS_THROUGH; if (response.type == PASS_THROUGH) { //not a valid command - send sendBuffer[0] = '\0'; strcat(sendBuffer, NickNames[0]); strcat(sendBuffer, buffer); RemoteWrite(sendBuffer); RemoteWrite("\n"); for (fd_write = 3; fd_write <= maxFD; fd_write++) if (FD_ISSET(fd_write, &writeSet)) { if (FD_ISSET(fd_write, &serverSet)) { NSpace_Mangler(Addr[fd_write - 3], sendBuffer); send(fd_write, result, sizeof(result), 0); } else { send(fd_write, sendBuffer, sizeof(sendBuffer), 0); }; }; } else if (response.type < SERVER_CONNECT) { // some ban command for (fd_write = 3; strcasecmp(response.param, NickNames[fd_write - 3]) && fd_write <= maxFD; fd_write++); if (fd_write <= maxFD) { FD_CLR(fd_write, &readSet); if (fd_write == maxFD) maxFD--; connectionLimit++; close(fd_write); RemoteWrite("***Banned connection for "); RemoteWrite(response.param); RemoteWrite("\n"); sendBuffer[0] = '\0'; if (response.type == ADDR_BAN) BannedAddr[BanTop++] = Addr[fd_write - 3]; Addr[fd_write - 3] = 0; } else { RemoteWrite("***Ban failed, perhaps you misspelled the nickname.\n"); sendBuffer[0] = '\0'; }; } else if (response.type == SERVER_CONNECT) { // server connect if (!response.hostPointer) { RemoteWrite("***Server connect failed, no such host.\n"); } else { bzero((char *)&socketAddress, sizeof(socketAddress)); socketAddress.sin_family = AF_INET; bcopy(response.hostPointer->h_addr, (char *)&socketAddress.sin_addr, response.hostPointer->h_length); socketAddress.sin_port = htons(response.port); /* Active open. */ if ((newSocketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)) < 0) { end_curses(); perror("chat: socket"); quit_safe(1); }; if (connect(newSocketFileDescriptor, (struct sockaddr *)&socketAddress, sizeof(socketAddress)) < 0) { perror("***Error on connection: "); close(newSocketFileDescriptor); } else { send(newSocketFileDescriptor, "\n\0", 2, 0); recv(newSocketFileDescriptor, buffer, sizeof(buffer), 0); if (!strcasecmp(buffer, "Server full.")) { // if other server full close(newSocketFileDescriptor); RemoteWrite("***Unable to server connect -- other server is full.\n"); } else if (!(strcasecmp(buffer, "Ok."))) { // if not address banned FD_SET(newSocketFileDescriptor, &readSet); FD_SET(newSocketFileDescriptor, &serverSet); /* Keep track of the largest file descriptor. */ if (newSocketFileDescriptor > maxFD) maxFD = newSocketFileDescriptor; if (newSocketFileDescriptor > serverTop) serverTop = newSocketFileDescriptor; serverAddr[newSocketFileDescriptor - 3] = socketAddress.sin_addr.s_addr; RemoteWrite("***Server "); RemoteWrite(response.param); RemoteWrite(" successfully connected.\n"); } else { close(newSocketFileDescriptor); RemoteWrite("***Unable to server connect.\n"); }; }; }; } else if (response.type == SERVER_DISCONNECT) { // server disconnect if (!response.hostPointer) { RemoteWrite("***Server disconnect failed, no such host.\n"); } else { bcopy(response.hostPointer->h_addr, (char *)&socketAddress.sin_addr, response.hostPointer->h_length); for (fd_write = 3; (!FD_ISSET(fd_write, &serverSet) || socketAddress.sin_addr.s_addr != serverAddr[fd_write - 3]) && fd_write <= serverTop; fd_write++); if (fd_write <= serverTop) { FD_CLR(fd_write, &readSet); FD_CLR(fd_write, &serverSet); close(fd_write); serverAddr[fd_write - 3] = 0; if (serverTop == (fd_write - 3)) serverTop--; RemoteWrite("***Server disconnected -- "); RemoteWrite(response.param); RemoteWrite("\n"); } else { RemoteWrite("***Not connected to that server.\n"); }; }; } else { // bad command parameters RemoteWrite("***Command failed, perhaps you mispelled the parameter.\n"); sendBuffer[0] = '\0'; }; werase(localInput); wrefresh(localInput); move(DIVIDER_Y + 1, 0); } else if (fileDescriptor == socketFileDescriptor) { // new connection /* Accept a new connection. */ if ((newSocketFileDescriptor = accept(socketFileDescriptor, (struct sockaddr *)&socketAddress, &length)) < 0) { end_curses(); perror("chat: accept"); quit_safe(1); }; goahead = 1; // flag for handshaking for (index = 0; index < sizeof(buffer); index++) buffer[index] = '\0'; recv(newSocketFileDescriptor, buffer, sizeof(buffer), 0); if (connectionLimit == 0) { // test for server full send(newSocketFileDescriptor, "Server full.", sizeof("Server full."), 0); close(newSocketFileDescriptor); goahead = 0; // stop h-s, server full }; if (goahead) { // test for nick collision if (strcasecmp(buffer, "\n\0")) { // if not a server connect for (index = 0; index < maxFD; index++) if (strcasecmp(buffer, NickNames[index]) == 0) { send(newSocketFileDescriptor, "Nickname collision.", sizeof("Nickname collision."), 0); for (index = 3; index <= maxFD; index++) if (FD_ISSET(index, &readSet)) send(newSocketFileDescriptor, NickNames[index - 3], sizeof(NickNames[index - 3]), 0); send(newSocketFileDescriptor, " *** \0", sizeof(" *** \0"), 0); close(newSocketFileDescriptor); goahead = 0; // stop h-s, nick collision }; } else { FD_SET(newSocketFileDescriptor, &serverSet); if (newSocketFileDescriptor > serverTop) serverTop = newSocketFileDescriptor; }; }; if (goahead) { // test for address ban for (index = 0; index < maxFD; index++) if (BannedAddr[index] == socketAddress.sin_addr.s_addr) { send(newSocketFileDescriptor, "Address banned.", sizeof("Address banned."), 0); close(newSocketFileDescriptor); goahead = 0; // stop h-s, address banned }; }; if (goahead) { // if still goahead, complete connection FD_SET(newSocketFileDescriptor, &readSet); /* Keep track of the largest file descriptor. */ if (newSocketFileDescriptor > maxFD) maxFD = newSocketFileDescriptor; RemoteWrite("***New connection on socket.\n"); strlength = (int) strlen(buffer); /* Add the nickname to the table. */ buffer[strlength] = '\0'; strcpy(NickNames[newSocketFileDescriptor - 3], buffer); connectionLimit--; /* Keep track of number of connections */ /* Address added to list */ Addr[newSocketFileDescriptor - 3] = socketAddress.sin_addr.s_addr; send(newSocketFileDescriptor, "Ok.", sizeof("Ok."), 0); } else { if (FD_ISSET(newSocketFileDescriptor, &serverSet)) FD_CLR(newSocketFileDescriptor, &serverSet); serverTop--; }; } else { // reading from socket for (index = 0; index < sizeof(buffer); index++) buffer[index] = '\0'; if (recv(fileDescriptor, buffer, sizeof(buffer), 0) == 0) { FD_CLR(fileDescriptor, &readSet); // client disconnect if (!(FD_ISSET(fileDescriptor, &serverSet))) { RemoteWrite("***Closed connection on socket.\n"); sendBuffer[0] = '\0'; strcpy(sendBuffer, "*** "); // goodbye message strcat(sendBuffer, NickNames[fileDescriptor - 3]); strcat(sendBuffer, "Goodbye!"); RemoteWrite(sendBuffer); RemoteWrite("\n"); for (fd_write = 3; fd_write <= maxFD; fd_write++) if (FD_ISSET(fd_write, &writeSet)) { send(fd_write, sendBuffer, sizeof(sendBuffer), 0); }; strcpy(NickNames[fileDescriptor - 3], ""); // remove from nicks } else { connectionLimit++; close(fileDescriptor); FD_CLR(fileDescriptor, &serverSet); RemoteWrite("***Remote server disconnected.\n"); }; Addr[fileDescriptor - 3] = 0; // remove from addresses if (fileDescriptor == maxFD) maxFD--; } else { // otherwise share with world FD_CLR(fileDescriptor, &writeSet); RemoteWrite(buffer); RemoteWrite("\n"); strlength = strlen(buffer); for (fd_write = 3; fd_write <= maxFD; fd_write++) if (FD_ISSET(fd_write, &writeSet)) { if (FD_ISSET(fd_write, &serverSet)) { NSpace_Mangler(Addr[fd_write - 3], buffer); send(fd_write, result, sizeof(result), 0); } else { send(fd_write, buffer, sizeof(buffer), 0); }; }; }; }; }; }; }; } int main(int argc, char *argv[]) { int port, connectionLimit, isnum = 1, index2 = 0; char *hostname, nickname[NAME_B_LIMIT]; /* Curses stuff. */ start_curses(); if (argc < 3) { end_curses(); fprintf(stderr, "usage: %s ( | -server) [port] nickname [room-size]\n", argv[0]); quit_safe(1); }; /* The almighty sig handlers, powerful and useful, especially to prevent icky core dumps. */ signal(SIGINT, Die); /* If we get a SIGINT, die. */ signal(SIGTERM, Die); /* If we get a SIGTERM, die. */ signal(SIGSEGV, Die); /* To handle those pesky segfaults. */ if (!strcasecmp(argv[1], "-server")) { switch (argc) { case 3: port = DEFAULT_PORT; strncpy(nickname, argv[2], NAME_LIMIT); connectionLimit = -1; break; case 4: while (index2 < strlen(argv[2])) { if (isalpha(argv[2][index2])) { isnum = 0; }; index2++; }; if (isnum == 1) { port = atoi(argv[2]); strncpy(nickname, argv[3], NAME_LIMIT); connectionLimit = -1; } else { port = DEFAULT_PORT; strncpy(nickname, argv[2], NAME_LIMIT); connectionLimit = atoi(argv[3]); }; break; case 5: port = atoi(argv[2]); strncpy(nickname, argv[3], NAME_LIMIT); connectionLimit = atoi(argv[4]); break; }; RunAsServer(port, nickname, connectionLimit); } else { if (argc == 4) { port = atoi(argv[2]); /* Set user specified port. */ strncpy(nickname, argv[3], NAME_LIMIT); } else { port = DEFAULT_PORT; /* Or set the default port. */ strncpy(nickname, argv[2], NAME_LIMIT); } RunAsClient(argv[1], port, nickname); } }