These two programs (chatserver and chat) form a multi-user chat system. 1 Usage of the client ./chat [hostname [username [passwd]]] The default hostname is "localhost" If you supply a user name, it will set your name to be whatever you specify when you connect to the server. If you don't give a name, you will be logged in as an anonymous listener. If you have an administrator account, you can also login by specifying your password on the command line, although you might not want to because it will probably end up in your shell history. Once you are connected, you can issue a number of commands to the server. These all begin with '!'. You just type them into the client as it you were typing a line of chat text. There are also commands to the client. They all begin with '.'. When you are done chatting, press ^C to disconnect, or use the command ".lc" 2 Usage of the server ./chatserver [port] & The chatserver will run and wait for connections. It produces a log on its standard output for debugging (and evesdropping on whisperers :) ) The server has a variety of commands, described later. 2.2 Administrators The server supports administrators with accounts they can log into. They administrator accounts are stored in the file 'passwd' in the same directory as chatserver. This file has the following format: name passwd cmnd1,...,cmndN The name field gives the administrator's login name. The passwd is the (plain text) password that allows that user to aquire administrative privledges. The last item is a comma-seperated list of commands requiring administrative privledges that the admin may execute. This fine-grained control lets you grant different privledges to different administrators. For instance, you may let a less trusted administrator set the topic message and mute/unmute users, but not kick them out or revoke other user's administrator privledges. If the command 'deadmin' is specified in the list, however, the accout is specified as a super-administrator who can execute all commands and revoke the administrator privledges of other administrators temporarily, among other things. The name, password and command list field are seperated by tabs. 2.3 Flood Control The server automatically mutes users who send large amounts of data or many lines of chat in a row. The flood control threshold can be set by an admin. 2.4 Message of the Day The server supports a variable message of the day that can be set with an administrator command. It will be shown to all new users and any time the !motd command is invoked. 2.5 Muting, Kicking, and Deadmining The server has facilites for robustly dealing with neer-do-wells. Admins can (depending on their individual privledges) mute users (so that they may continue to listen but not speak), kick users off the server, and remove the administrative privledges of rogue administrators. 3 Server Commands ---------------------------------------------------------------- !who List all chat participants, along with IP, port, status and administrative level. EXAMPLE: !who | Name | Status | IP | Rank | | bob | muted | 127.0.0.1:40011 | peon | | bryce | active | 127.0.0.1:40010 | superadmin | ------------------------------------------------------------------------------- !able USER COMMAND Tells you if USER can execute COMMAND. Useful for identifying which other users are dangerous to you :). EXAMPLE: !able bryce deadmin bryce is authorized to execute deadmin. !able bob quit bob is not authorized to execute quit. ---------------------------------------------------------------- !wo Turn whispers off: you will not be able to hear whispers anymore. ---------------------------------------------------------------- !ws Turn whispering back on: you will be able to hear whispers again. ---------------------------------------------------------------- !motd Show the message of the day / topic. (It is shown once automatically when you connect to the server.) ---------------------------------------------------------------- !login PASSWORD Log into your administrator account with PASSWORD. ---------------------------------------------------------------- !passwd PASSWORD Change your password temporarily. (Admin only) !passwd USER PASSWORD Change USER's password to PASSWORD. Useful if they have forgotten it. (Superadmin only) ---------------------------------------------------------------- !setmotd MESSAGE... Set the message of the day (greeting users see when they connect) to MESSAGE. (Admin only). ---------------------------------------------------------------- !quit Makes the server exit. (Admin only) ---------------------------------------------------------------- !voice USER Unmute USER. They will be able to talk again. (Admin only) ---------------------------------------------------------------- !quiet USER Mute USER. They will not be able to talk, but can continue to listen to the conversation and issue commands and whisper. (Admin only) ---------------------------------------------------------------- !kick USER Expell USER from the server. (Admin only) ---------------------------------------------------------------- !deadmin USER Temporarily remove administrative privledges from USER. (Superadmin only) [The name stands for de-administrator...] ---------------------------------------------------------------- !flood N Sets flood control to N, an integer. The default is 6000. Higher values are more lenient. Users who post too much too fast will be automatically muted by the server. ---------------------------------------------------------------- 4 Client commands ---------------------------------------------------------------- .wr FILE Write a log of your conversation to FILE. If it exists, it will be blanked. ---------------------------------------------------------------- .ap FILE Write a log of your conversation to FILE. If it already exists, the log will be appended to the end. ---------------------------------------------------------------- .en Close the open log file. ---------------------------------------------------------------- .ws USER MESSAGE Send MESSAGE to USER only. Other users will not see the 'whispered' message. (Although it is visible in the server log.) ---------------------------------------------------------------- .rd FILE Read FILE and post it to the server. Note: unless the file is fairly small, the default flood control will mute you for doing this. ---------------------------------------------------------------- .lc Quits the client, if you don't want to use ^C. ---------------------------------------------------------------- .cmd Temporarily suspends the client, putting you back in the shell. You can use the shell as normal. When you want to go back to the chat client, type 'fg'. (The foreground command). EXAMPLE: .cmd To resume, type 'fg'. [1]+ Stopped ./chat localhost bryce bryce@localhost ~/share/oschat $ ls LOG a.out chat.c chat.h.gch client.c passwd server.c Makefile chat chat.h chatserver commands.c readme.txt test.c bryce@localhost ~/share/oschat $ fg ./chat localhost bryce foo00f Welcome back. !who | Name | Status | IP | Rank | | bob | muted | 127.0.0.1:34210 | peon | | bryce | active | 127.0.0.1:34209 | superadmin | ---------------------------------------------------------------- 5 A Tour of the Code The server code is much nicer than the client code, because I later realized I needed to have a client and put it together in a bit of a rush. (During the development of the server, I used telnet as the client, which you can still do: the protocol is simply plain text.) That said, the client should be reasonably robust, though it is not as well documented. The code is written in C, but in an object oriented style. This was a big win, I'm glad I did it. For example, go to chat.h:22. There is a class called admin_t, with its member variables, followed by all the methods that manipulate that class. They all take a pointer to the class as their first parameter and call it 'this', like in C++ (or rather more like 'self' in python), except for the constructor which returns a pointer to a newly created instance. This style is maintained throughout with few exceptions. The object oriented style extends very far down, to such an extent that it would be possible to modify the program to support multiple chatrooms on a single server without great difficulty. Also here, you will note the use of custom variadic functions using the standard va_args facility. These are used to implement wrappers around printf, so that when communicating with clients, for example, I can say: user_send(user, " no command '%s' exists.\n", argv[0]); This user_send function basically works like printf, except it writes to the send buffer of a user. It can have many arguments and accepts a printf-type format string. Its underlying implementation (at chat.c:307) is a variadic function, but rather than reinventing the wheel and parsing the format string itself, it passes its own variable argument list to vsprintf. (I was very happy to have finally had an opportunity to use that function!) Note that sending text to clients is fully buffered (it does not block the server to send text to a client.) The code contains main examples of linked lists. Some use of linear search is made. A more efficient algorithm would not have been an efficient use of programmer time, as the numbers of users are small (<20) and a hashmap or similar method would only be marginally faster for these small values. Enumerations are used in various places as flags. Bitwise arithmetic on enum values is also used (to check flags in an enumeration, where the values in the enum have been set to powers of 2, a common technique.) Memory management with malloc and free is used extensively. Where there is the possibility of buffer overflow, safe functions like strncpy and fgets have been used rather than strcpy and gets. There are no limits (apart from machine resources) on the number of administrators, concurrent users, and so forth. Disconnected users are quickly removed from the active list, and the memory used to hold the user object is reclaimed. The heart of the server code is in chat.c:232, where it uses the GREAT AND POWERFUL select system call :) This system call enables the server to support talking to many different clients synchronously without any forking or threading required. The select call sets up lists of clients that have sent new data to the server, and a list of clients who have had text put in their output buffers (e.g. by the user_send function) and are ready to receive it. The select call also times out periodically to allow the main loop to accept additional clients. The code takes the lists from the select call and services the clients that need attention, then repeats the main loop again. Fragmentary sends are detected, although they are not currently handled (as you gave us permission to pretend that 'send' always works fine) except by printing a mention of them to the log. The file commands.c implements the various server commands. You can inspect the list at about line 273, which gives the name and privledge level required for each. Function pointers, rather than a cumbersome collection of if statements, are used to match the names of the commands. This approach also makes it very easy to add new commands, which is simply a matter of adding a new function and its corisponding entry in the table. A NULL sentinel marks the end of the list of commands. The client also uses select, so that it can detect messages from the server while also waiting for new input on standard input from the user. Both the client and the server make extensive use of non-blocking IO. Rather than reinventing the shell, the client implements the '.cmd' command by sending itself a suspend (^Z) signal, it can then be woken up with 'fg' in the usual manner. Note that the implementations of the commands use a unix-like convention, taking arguments argc and argv which are exactly like the ones main takes. The server parses the command line and passes it to the function that implements the command in the parsed form, so that commands do not have to redundantly parse the command line. (Similar to how the UNIX shell works.) All commands have extensive error checking. A nice example of the use of variadic functions is the implementation of the '!who' command, at the top. The project was made about 14 hours on Saturday, from 8:00 AM or so to 10:30 PM. (Counting the writing of the documentation.) It contains about 6300 lines, 4327 of them lines of C code. So, I write and mostly debug a little over 500 lines of C code per hour when I'm on a roll, apparently. I wasn't frustrated by the rushed nature of this project though because I designed the structure of the program early on. I am going to take that as a lesson. Apologies for any bugs in the program. Posted below is an example session demonstrating its use, and the server log that goes with it. NOTE: during compilation you may see 'implicit declaration of function 'kill' The function is not actually implicitly defined, the proper header is included, and in any case it does return int so it wouldn't be a problem. I'm not sure why it says that. NOTE: the program is implemented in C99. The make file has the flags for this set so as long as you compile it with the make file it shouldn't matter. ############################ SERVER LOG ############################### usage: server [port] using default 41000. Starting server. connected. [anonymous@127.0.0.1:39565] !name bryce executing. is now known as 'bryce' [bryce] !who executing. [bryce] !login foo00f executing. bryce logs in as administrator [bryce] !who executing. connected. [anonymous@127.0.0.1:39566] !name spammer executing. is now known as 'spammer' [spammer] BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!!BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!! [spammer] BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!! [spammer] [spammer] BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!! [spammer] BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!! [spammer] : spammer muted [spammer] BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!! [spammer] (MUTED) [spammer] (MUTED) BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!! [bryce] shut up [bryce] !kick spammer executing. spammer was kicked by bryce. [bryce] !who executing. connected. [anonymous@127.0.0.1:39567] !name squirrel executing. is now known as 'squirrel' [squirrel] Boycott squirrel fur! [bryce] yeah, amen bro. [squirrel] Do you like squirrels? [bryce] sort of, but they're kinda creepy [squirrel] yeah I guess we are. connected. [anonymous@127.0.0.1:39569] !name brian executing. is now known as 'brian' [brian] !login quux42 executing. brian logs in as administrator [bryce] !who executing. [brian] I hate squirrels [squirrel] don't be a bigot [bryce] yeah [brian] they carry hantavirus! [squirrel] no we don't, that's mice! [brian] shut up [brian] !quiet squirrel executing. squirrel has been silenced by brian. [bryce] that was uncalled for [bryce] !deadmin brian executing. bryce revokes the admin rights of brian! [brian] noo!!!!! [bryce] !who executing. [squirrel] (WHISPER) .ws bryce please unmute me [bryce] !voice squirrel executing. bryce unmutes squirrel. [squirrel] thanks [brian] stupid rodents disconnected. [squirrel] good riddance connected. [anonymous@127.0.0.1:38727] !name spammer executing. is now known as 'spammer' [spammer] BUY SQUIRREL FUR CHEAP CHEAP CHEAP [bryce] !quiet spammer executing. spammer has been silenced by bryce. [spammer] (WHISPER) .ws bryce BUY SQUIRREL FUR CHEAP CHEAP CHEAP [spammer] (WHISPER) .ws bryce BUY SQUIRREL FUR CHEAP CHEAP CHEAP [bryce] !wo executing. [bryce] !who executing. [bryce] !setmotd NO SPAMMING executing. bryce sets a new MOTD: NO SPAMMING [bryce] !motd executing. disconnected. [squirrel] goodnight [bryce] night [squirrel] (WHISPER) .lc disconnected. [bryce] !who executing. disconnected. ####################### bryce's chat session ################### bryce@localhost ~/share/oschat $ ./chat localhost bryce Welcome to the client for Bryce's chat program. Connecting to localhost... (Setting name) [!name bryce ] ---------- Chatting on localhost ------------ Welcome to Bryce Schroeder's Chat Server. connected. is now known as 'bryce' !who | Name | Status | IP | Rank | | bryce | active | 127.0.0.1:39565 | peon | !login foo00f Login accepted. bryce logs in as administrator !who | Name | Status | IP | Rank | | bryce | active | 127.0.0.1:39565 | superadmin | connected. is now known as 'spammer' [spammer] BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!!BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!! [spammer] BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!! [spammer] [spammer] BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!! [spammer] BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!! [spammer] : spammer muted [spammer] BLAH BLAH BLAH BUY SQUIRREL FURS CHEAP WWW.SQUIRRELFURS.COM!!!!!! shut up [bryce] shut up !kick spammer spammer was kicked by bryce. !who | Name | Status | IP | Rank | | bryce | active | 127.0.0.1:39565 | superadmin | connected. is now known as 'squirrel' [squirrel] Boycott squirrel fur! yeah, amen bro. [bryce] yeah, amen bro. [squirrel] Do you like squirrels? sort of, but they're kinda creepy [bryce] sort of, but they're kinda creepy [squirrel] yeah I guess we are. connected. is now known as 'brian' brian logs in as administrator !who | Name | Status | IP | Rank | | brian | active | 127.0.0.1:39569 | admin | | squirrel | active | 127.0.0.1:39567 | peon | | bryce | active | 127.0.0.1:39565 | superadmin | [brian] I hate squirrels [squirrel] don't be a bigot yeah [bryce] yeah [brian] they carry hantavirus! [squirrel] no we don't, that's mice! [brian] shut up squirrel has been silenced by brian. that was uncalled for [bryce] that was uncalled for !deadmin brian bryce revokes the admin rights of brian! [brian] noo!!!!! !who | Name | Status | IP | Rank | | brian | active | 127.0.0.1:39569 | peon | | squirrel | muted | 127.0.0.1:39567 | peon | | bryce | active | 127.0.0.1:39565 | superadmin | please unmute me !voice squirrel bryce unmutes squirrel. [squirrel] thanks [brian] stupid rodents disconnected. [squirrel] good riddance connected. is now known as 'spammer' [spammer] BUY SQUIRREL FUR CHEAP CHEAP CHEAP !quiet spammer spammer has been silenced by bryce. BUY SQUIRREL FUR CHEAP CHEAP CHEAP BUY SQUIRREL FUR CHEAP CHEAP CHEAP !wo you cannot hear whispers anymore. !who | Name | Status | IP | Rank | | spammer | muted | 127.0.0.1:38727 | peon | | squirrel | active | 127.0.0.1:39567 | peon | | bryce | active | 127.0.0.1:39565 | superadmin | !setmotd NO SPAMMING bryce sets a new MOTD: NO SPAMMING !motd NO SPAMMING disconnected. [squirrel] goodnight night [bryce] night disconnected. .cmd To resume, type 'fg'. [1]+ Stopped ./chat localhost bryce bryce@localhost ~/share/oschat $ ps -ax | grep chatserver Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html 2016 pts/8 S+ 0:00 ./chatserver 2043 pts/13 R+ 0:00 grep --colour=auto chatserver bryce@localhost ~/share/oschat $ fg ./chat localhost bryce Welcome back. !who | Name | Status | IP | Rank | | bryce | active | 127.0.0.1:39565 | superadmin | .lc