#include /* standard C i/o facilities */ #include /* Unix System Calls */ #include /* system data type definitions */ #include /* socket specific definitions */ #include /* INET constants and stuff */ #include /* IP address conversion stuff */ #include /* malloc */ #include /* Posix Threads stuff */ #include /* gethostname */ #include /* strdup */ #include "util.h" #include #include /* OS Spring 2005 - HW5 curses based chat client NOTE: This client won't support resizing your window! (so that before starting the client). The client connects to the chat server specified on the command line (hostname, port). The client automatically tries to login with the user name specified on the command line. The chat protocol: CLIENT->SERVER MESSAGES: Each message to the server is a single line of ASCII text (terminated with \n). any message that does not begin with the character '/' is a broadcast message - the entire line will be forwarded to all other active clients. There are other kinds of messages, each starts with the '/' character followed immediately by a command name: "/login username" : requests the specified username. The server can respond with an OK, or with and ERR if the name is already taken (or perhaps if the server is already full). See description below for the format of OK and ERR messages the server sends. "/help" : asks the server for a reminder of what the commands are. The server will respond with some MSG messages containing a list of supported commands. "/quit" : tells the server the client is leaving. "/priv dest_user the message text ..." : send a private message to a single user. dest_user specifies the login name of the user the message should be sent to. The server will send back an ERR (perhaps the dest_user is not valid), or an OK. "/who" : asks the server to send some MSG messages listing all the active users. SERVER->CLIENT MESSAGES The server can generate 3 different kinds of messages, each is a single line of ASCII text terminated by a '\n'. "ERR some error message ...": server sends a messages starting with the text "ERR" when indicating some kinds of error has occurred. "OK some message ...": server is making a positive acknowledgement of some client command (everything is OK). "MSG some text here ..." server is sending a text message that should be shown to the user (without the "MSG" prefix). The server can format the message text any way it wants... (could include info about who sent the message, or might not...). */ void curses_client(int sd, char *loginname); /* command line arguments: hostname port username */ int main(int argc,char **argv) { int port; char *hostname,*username; int sd; if (argc!=4) { printf("Usage: %s hostname port username\n",argv[0]); exit(1); } hostname = argv[1]; port = atoi(argv[2]); username = argv[3]; sd = create_tcp_connection(hostname,port); curses_client(sd,username); return(0); } /* global variables that are used to update the display rows,cols indicate the size of the terminal display is the top window (where messages appear) status is where commands are typed. */ int rows,cols; WINDOW *display,*status; void create_windows() { WINDOW *w; getmaxyx(stdscr,rows,cols); /* find out how big the terminal is */ refresh(); display = newwin(rows-2,cols,0,0); /* create upper window for message display */ mvhline(rows-2,0,'=',cols); /* draw a line between display and command area */ wrefresh(display); status = newwin(1,cols,rows-1,0); wrefresh(status); scrollok(w,TRUE); /* display window should scroll */ idlok(w,TRUE); } /* I didn't get this working... Right now you can't resize the terminal (well you can, but things won't work after that - you have to quit and restart this client before it will notice). */ void winch_handler(int i) { getmaxyx(stdscr,rows,cols); /* find out how big the terminal is now*/ wresize(display,rows-2,cols); wrefresh(display); refresh(); } /* This is called to handle incoming messages (from the server) */ void * incoming_handler(void *arg) { int sd = (int) arg; char *line; message m; int curx, cury; while (1) { /* wait for incoming message */ line = readline(sd); getyx(stdscr,cury,curx); /* save current cursor position */ if (parse_message(line,&m)==-1) { /* server sent a bad message! */ wprintw(display,"ERROR - SERVER SENT A BAD MESSAGE:\n"); wprintw(display," %s\n",line); } else { switch(m.msg_type) { case M_MSG: wprintw(display,"%s\n",m.msg_text); beep();break; case M_ERR: wprintw(display,"ERROR MESSAGE FROM SERVER: %s\n",m.msg_text); beep(); beep(); break; case M_OK: /* wprintw(display,m.msg_text); */break; } } wrefresh(display); move(cury,curx); /* restore the cursor to original position */ refresh(); free(line); } } /* main client code - automatically sends a login request. If the login fails, the server should send a message (that is displayed for the user), but the program doesn't wait for a response - it just goes on as if login worked. */ void curses_client(int sd, char *loginname) { char buff[100]; /* Max line size! */ pthread_t pt; initscr(); create_windows(); // signal(SIGWINCH,winch_handler); /* not working yet... */ /* create a thread to handle incoming stuff */ pthread_create(&pt,NULL,incoming_handler,(void *)sd); /* send login message to server */ if (sock_printf(sd,"/login %s\n",loginname)<0) { perror("Erroring sending initial login"); exit(1); } /* display the prompt */ wmove(display,0,0); mvwprintw(status,0,0,"oschat> "); wrefresh(status); move(rows-1,8); refresh(); /* main look that reads from the user */ while (getnstr(buff,100)!=ERR) { if (strlen(buff)!=0) { /* forward user string to server without modification */ if (sock_printf(sd,"%s\n",buff)<0) { endwin(); perror("Erroring sending message"); exit(1); } } /* handle quit by quitting */ if (strncmp(buff,"/quit",5)==0) { endwin(); exit(0); } /* update the status window */ mvwprintw(status,0,0,"oschat> "); wclrtoeol(status); wrefresh(status); move(rows-1,8); refresh(); } endwin(); }