]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/chat_api.cpp
first pass at PXO reintegration
[taylor/freespace2.git] / src / network / chat_api.cpp
1 /*
2  * Copyright (C) Volition, Inc. 2005.  All rights reserved.
3  * 
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on the 
6  * source.
7  *
8 */
9
10
11 #ifdef PLAT_UNIX
12 #include <sys/time.h>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <sys/ioctl.h>
16 #include <netinet/in.h>
17 #include <sys/select.h>
18 #include <errno.h>
19 #include <arpa/inet.h>
20 #include <netdb.h>
21
22 #define WSAGetLastError()  (errno)
23 #else
24 #include <winsock.h>
25 typedef int socklen_t;
26 #endif
27
28 #include "pstypes.h"
29 #include "chat_api.h"
30
31 #define MAXCHATBUFFER   500
32
33 static SOCKET Chatsock;
34 static SOCKADDR_IN Chataddr;
35 static int Socket_connecting = 0;
36 static char Nick_name[33];
37 static char Orignial_nick_name[33];
38 static int Nick_variety = 0;
39 static char szChat_channel[33] = "";
40 static char Input_chat_buffer[MAXCHATBUFFER] = "";
41 static char Chat_tracker_id[33];
42 static char Getting_user_channel_info_for[33] = "";
43 static char Getting_user_tracker_info_for[33] = "";
44 static int Getting_user_channel_error = 0;
45 static int Getting_user_tracker_error = 0;
46 static char User_req_tracker_id[100] = ""; //These are oversized for saftey
47 static char User_req_channel[100] = "";
48 static char *User_list = NULL;
49 static char *Chan_list = NULL;
50 static int Socket_connected = 0;
51 static int Chat_server_connected = 0;
52 static int Joining_channel = 0;
53 static int Joined_channel = 0;
54 static int GettingChannelList = 0;
55 static int GettingUserTID = 0;
56 static int GettingUserChannel = 0;
57
58 static Chat_user *Firstuser,*Curruser;
59 static Chat_command *Firstcommand,*Currcommand;
60 static Chat_channel *Firstchannel,*Currchannel;
61
62 void ChatInit(void)
63 {
64         Socket_connecting = 0;
65         memset(Nick_name, 0, sizeof(Nick_name));
66         memset(Orignial_nick_name, 0, sizeof(Orignial_nick_name));
67         Nick_variety = 0;
68         memset(szChat_channel, 0, sizeof(szChat_channel));
69         memset(Input_chat_buffer, 0, sizeof(Input_chat_buffer));
70         memset(Chat_tracker_id, 0, sizeof(Chat_tracker_id));
71         memset(Getting_user_channel_info_for, 0, sizeof(Getting_user_channel_info_for));
72         memset(Getting_user_tracker_info_for, 0, sizeof(Getting_user_tracker_info_for));
73         Getting_user_channel_error = 0;
74         Getting_user_tracker_error = 0;
75         memset(User_req_tracker_id, 0, sizeof(User_req_tracker_id));
76         memset(User_req_channel, 0, sizeof(User_req_channel));
77         User_list = NULL;
78         Chan_list = NULL;
79         Socket_connected = 0;
80         Chat_server_connected = 0;
81         Joining_channel = 0;
82         Joined_channel = 0;
83         GettingChannelList = 0;
84         GettingUserTID = 0;
85         GettingUserChannel = 0;
86
87 }
88
89
90 // Return codes:
91 //-2 Already connected
92 //-1 Failed to connect
93 // 0 Connecting
94 // 1 Connected
95 // Call it once with the server IP address, and it will return immediately
96 // with 0. Keep calling it until it returns something other than 0
97 // note: the nickname may be changed if someone with that name already
98 // exists (Scourge1 for instance)
99 int ConnectToChatServer(char *serveraddr,char *nickname,char *trackerid)
100 {
101         short chat_port;
102         char chat_server[50];
103         char *p;
104         unsigned long argp = 1;
105         char signon_str[100];
106
107         //if(Socket_connected && ) return -2;
108
109         if(!Socket_connecting)
110         {
111                 unsigned long iaddr;
112
113                 strcpy(Nick_name,nickname);
114                 strcpy(Orignial_nick_name,nickname);
115                 strcpy(Chat_tracker_id,trackerid);
116                 
117                 Firstuser = NULL;
118                 Firstcommand = NULL;
119                 Chat_server_connected = 0;
120                 FlushChatCommandQueue();
121
122                 p = strchr(serveraddr,':');
123
124                 if(NULL==p)
125                 {
126                         //AfxMessageBox("Invalid chat server, must be host.com:port (ie. irc.dal.net:6667)");
127                         return -1;
128                 }
129                 strncpy(chat_server,serveraddr,(p-serveraddr));
130                 chat_server[p-serveraddr]='\0';
131                 chat_port = (short)atoi(p+1);
132                 if(0==chat_port)
133                 {
134                         //AfxMessageBox("Invalid chat port, must be host.com:port (ie. irc.dal.net:6667)");
135                         return -1;
136                 }
137
138                 Chatsock = socket(AF_INET,SOCK_STREAM,0);
139                 if(INVALID_SOCKET == Chatsock)
140                 {
141                         //AfxMessageBox("Unable to open socket!");
142                         return -1;
143                 }
144
145                 memset( &Chataddr, 0, sizeof(SOCKADDR_IN) );
146                 Chataddr.sin_family = AF_INET; 
147                 Chataddr.sin_addr.s_addr = INADDR_ANY; 
148                 Chataddr.sin_port = 0;
149                 
150                 if (SOCKET_ERROR==bind(Chatsock, (SOCKADDR*)&Chataddr, sizeof (sockaddr))) 
151                 {
152                         //AfxMessageBox("Unable to bind socket!");
153                         return -1;
154                 }
155                 ioctlsocket(Chatsock,FIONBIO,&argp);
156                 
157                 // first try and resolve by name
158                 iaddr = inet_addr( chat_server );
159                 if ( iaddr == INADDR_NONE ) {   
160                         HOSTENT *he;
161                         he = gethostbyname(chat_server);
162                         if(!he)
163                         {
164                                 return 0;
165                                 /*
166                                 //AfxMessageBox("Unable to gethostbyname.\n");
167
168                                 // try and resolve by address                   
169                                 unsigned int n_order = inet_addr(chat_server);
170                                 he = gethostbyaddr((char*)&n_order,4,PF_INET);                                  
171
172                                 if(!he){
173                                         return -1;
174                                 }
175                                 */
176                         }
177                         memcpy(&iaddr, he->h_addr_list[0],4);
178                 }
179                 
180                 memcpy(&Chataddr.sin_addr.s_addr, &iaddr,4); //&iaddr, 4);                              
181
182                 
183                 // Chataddr.sin_addr.s_addr = inet_addr(chat_server);
184
185                 Chataddr.sin_port = htons( chat_port );
186
187                 if(SOCKET_ERROR == connect(Chatsock,(SOCKADDR *)&Chataddr,sizeof(SOCKADDR_IN)))
188                 {
189                         if(WSAEWOULDBLOCK == WSAGetLastError())
190                         {
191                                 Socket_connecting = 1;
192                                 return 0;
193                         }
194                 }
195                 else
196                 {
197                         //This should never happen, connect should always return WSAEWOULDBLOCK
198                         Socket_connecting = 1;
199                         Socket_connected = 1;
200                         return 1;
201                 }
202         }
203         else
204         {
205                 if(Chat_server_connected)
206                 {
207                         return 1;
208                 }
209
210                 if(!Socket_connected)
211                 {
212                         //Do a few select to check for an error, or to see if we are writeable (connected)
213                         fd_set write_fds,error_fds;                
214                         TIMEVAL timeout;   
215                         
216                         timeout.tv_sec=0;            
217                         timeout.tv_usec=0;
218                         
219                         FD_ZERO(&write_fds);
220                         FD_SET(Chatsock,&write_fds);    
221                         //Writable -- that means it's connected
222                         if(select(0,NULL,&write_fds,NULL,&timeout))
223                         {
224                                 Socket_connected = 1;
225                                 sprintf(signon_str,NOX("/USER %s %s %s :%s"),NOX("user"),NOX("user"),NOX("user"),Chat_tracker_id);
226                                 SendChatString(signon_str,1);
227                                 sprintf(signon_str,NOX("/NICK %s"),Nick_name);
228                                 SendChatString(signon_str,1);
229                                 return 0;
230                                 //Now we are waiting for Chat_server_connected
231                         }
232                         FD_ZERO(&error_fds);
233                         FD_SET(Chatsock,&error_fds);    
234                         //error -- that means it's not going to connect
235                         if(select(0,NULL,NULL,&error_fds,&timeout))
236                         {
237                                 return -1;
238                         }
239                         return 0;
240                 }
241         }
242
243         return 0;
244 }
245
246 // Call it to close the connection. It returns immediately
247 void DisconnectFromChatServer()
248 {
249         if(!Socket_connected) return;
250         SendChatString(NOX("/QUIT"),1);
251         shutdown(Chatsock,2);
252         closesocket(Chatsock);
253         Socket_connecting = 0;
254         Socket_connected = 0;
255         Input_chat_buffer[0] = '\0';
256         if(User_list)
257         {
258                 free(User_list);
259                 User_list = NULL;
260         }
261         if(Chan_list)
262         {
263                 free(Chan_list);
264                 Chan_list = NULL;
265         }
266         
267         Chat_server_connected = 0;
268         Joining_channel = 0;
269         Joined_channel = 0;
270         RemoveAllChatUsers();
271         FlushChatCommandQueue();
272         return;
273 }
274
275 // returns NULL if no line is there to print, otherwise returns a string to
276 // print (all preformatted of course)
277 char * GetChatText()
278 {
279
280         if(!Socket_connected) return NULL;
281
282         //ChatGetString will do the formatting
283         return ChatGetString();
284
285 }
286
287 // Send a string to be sent as chat, or scanned for messages (/msg <user>
288 // string)
289 const char * SendChatString(const char *line,int raw)
290 {
291         char szCmd[200];
292         char szTarget[50];
293         if(!Socket_connected) return NULL;
294         
295         if(line[0]=='/')
296         {
297
298                 //Start off by getting the command
299                 strcpy(szCmd,GetWordNum(0,line+1));
300                 if(SDL_strcasecmp(szCmd,NOX("msg"))==0)
301                 {
302                         strcpy(szTarget,GetWordNum(1,line+1));
303                         sprintf(szCmd,NOX("PRIVMSG %s :%s\n\r"),szTarget,line+strlen(NOX("/msg "))+strlen(szTarget)+1);
304                         send(Chatsock,szCmd,strlen(szCmd),0);
305                         szCmd[strlen(szCmd)-2]='\0';
306                         return ParseIRCMessage(szCmd,MSG_LOCAL);
307
308                 }
309                 if(SDL_strcasecmp(szCmd,NOX("me"))==0)
310                 {
311                         sprintf(szCmd,NOX("PRIVMSG %s :\001ACTION %s\001\n\r"),szChat_channel,line+strlen(NOX("/me ")));
312                         send(Chatsock,szCmd,strlen(szCmd),0);
313                         szCmd[strlen(szCmd)-2]='\0';
314                         return ParseIRCMessage(szCmd,MSG_LOCAL);
315
316                 }
317                 if(SDL_strcasecmp(szCmd,NOX("xyz"))==0)
318                 {
319                         //Special command to send raw irc commands
320                         sprintf(szCmd,"%s\n\r",line+strlen(NOX("/xyz ")));
321                         send(Chatsock,szCmd,strlen(szCmd),0);
322                         return NULL;
323                 }
324                 if(SDL_strcasecmp(szCmd,NOX("list"))==0)
325                 {
326                         sprintf(szCmd,"%s\n\r",line+1);
327                         send(Chatsock,szCmd,strlen(szCmd),0);
328                         return NULL;
329                 }
330                 if(raw)
331                 {
332                         sprintf(szCmd,"%s\n\r",line+1);
333                         send(Chatsock,szCmd,strlen(szCmd),0);
334                         return NULL;
335                 }
336                 return XSTR("Unrecognized command",634);
337                 
338         }
339         else
340         {
341                 if(szChat_channel[0])
342                 {
343                         /*
344                         CString sndstr;
345                         sndstr.Format("PRIVMSG %s :%s\n\r",szChat_channel,line);
346                         send(Chatsock,LPCSTR(sndstr),sndstr.GetLength(),0);
347                         sndstr = sndstr.Left(sndstr.GetLength()-2);
348                         return ParseIRCMessage((char *)LPCSTR(sndstr),MSG_LOCAL);
349                         */
350
351                         sprintf(szCmd,NOX("PRIVMSG %s :%s\n\r"),szChat_channel,line);
352                         send(Chatsock,szCmd,strlen(szCmd),0);                   
353                         if(strlen(szCmd) >= 2){
354                                 szCmd[strlen(szCmd)-2] = '\0';
355                                 return ParseIRCMessage(szCmd,MSG_LOCAL);
356                         }                       
357
358                         return NULL;
359                 }
360         }
361         
362         return NULL;
363 }
364
365
366 // Returns a structure which contains a command and possible some data (like
367 // a user joining or leaving) if one is waiting
368 // This tells you if you need to add a user from the userlist, remove a user,
369 // etc. Also for status messages, like if you get knocked
370 // off the server for some reason.
371 Chat_command *GetChatCommand()
372 {
373         if(!Socket_connected) return NULL;
374         return GetChatCommandFromQueue();
375 }
376
377 // This function returns a list of users in the current channel, in one
378 // string, separated by spaces, terminated by a null
379 // (Spaces aren't allowed as part of a nickname)
380 char *GetChatUserList()
381 {
382         int iuser_list_length = 0;;
383         if(User_list)
384         {
385                 free(User_list);
386                 User_list = NULL;
387         }
388         if(!Socket_connected) return NULL;
389         
390         Curruser = Firstuser;
391         while(Curruser) 
392         {
393                 iuser_list_length += strlen(Curruser->nick_name)+1;
394                 Curruser = Curruser->next;
395         }
396         Curruser = Firstuser;
397         User_list = (char *)malloc(iuser_list_length+1);
398         User_list[0] = '\0';
399         while(Curruser) 
400         {
401                 strcat(User_list,Curruser->nick_name);
402                 strcat(User_list," ");
403                 Curruser = Curruser->next;
404         }
405
406         return User_list;
407 }
408
409 // Call this to set/join a channel. Since we can't be sure that we will be
410 // able to join that channel, check it for completion
411 // You can't be in more than one channel at a time with this API, so you
412 // leave the current channel before trying to join
413 // a new one. Because of this if the join fails, make sure you try to join
414 // another channel, or the user wont be able to chat
415 //-1 Failed to join
416 // 0 joining
417 // 1 successfully joined
418 int SetNewChatChannel(char *channel)
419 {
420         char partstr[100];
421         if(!Socket_connected) return -1;
422         if(Joining_channel==1) 
423         {
424                 if(Joined_channel==1) 
425                 {
426                         //We made it in!
427                         Joining_channel = 0;
428                         return 1;
429                 }
430                 else if(Joined_channel==-1) 
431                 {
432                         //Error -- we got a message that the channel was invite only, or we were banned or something
433                         Joining_channel = 0;
434                         strcpy(szChat_channel,"");
435                         return -1;
436                 }
437         }
438         else
439         {
440                 if(szChat_channel[0])
441                 {
442                         sprintf(partstr,NOX("/PART %s"),szChat_channel);
443                         SendChatString(partstr,1);
444                 }
445                 strcpy(szChat_channel,channel);
446                 sprintf(partstr,NOX("/JOIN %s"),szChat_channel);
447                 SendChatString(partstr,1);
448                 Joining_channel = 1;
449                 Joined_channel = 0;
450         }
451         
452         return 0;
453 }
454
455
456 char *ChatGetString(void)
457 {
458         fd_set read_fds;                   
459         TIMEVAL timeout; 
460         char ch[2];
461         char *p;
462         int bytesread;
463         static char return_string[MAXCHATBUFFER];
464         
465         timeout.tv_sec=0;            
466         timeout.tv_usec=0;
467         
468         FD_ZERO(&read_fds);
469         FD_SET(Chatsock,&read_fds);    
470         //Writable -- that means it's connected
471         while(select(0,&read_fds,NULL,NULL,&timeout))
472         {
473                 bytesread = recv(Chatsock,ch,1,0);
474                 if(bytesread)
475                 {
476                         ch[1] = '\0';
477                         
478                         if((ch[0] == 0x0a)||(ch[0]==0x0d))
479                         {
480                                 if(Input_chat_buffer[0]=='\0')
481                                 {
482                                         //Blank line, ignore it
483                                         return NULL;
484                                 }
485                                 strcpy(return_string,Input_chat_buffer);
486                                 Input_chat_buffer[0] = '\0';
487                                 
488                                 p = ParseIRCMessage(return_string,MSG_REMOTE);
489                                 
490                                 return p;
491                         }
492                         SDL_assert(strlen(Input_chat_buffer) < MAXCHATBUFFER-1);
493                         strcat(Input_chat_buffer,ch);
494                 }
495                 else
496                 {
497                         //Select said we had read data, but 0 bytes read means disconnected
498                         AddChatCommandToQueue(CC_DISCONNECTED,NULL,0);
499                         return NULL;
500                 }
501                 
502         }
503         return NULL;
504 }
505
506
507 const char * GetWordNum(int num, const char * l_String)
508 {
509         static char strreturn[600];
510         static char ptokstr[600];
511         char seps[10] = NOX(" \n\r\t");
512         char *token,*strstart;
513
514         strstart = ptokstr;
515
516         strcpy(ptokstr,l_String);
517
518         token=strtok(ptokstr,seps);
519
520         for(int i=0;i!=num;i++)
521         {
522                 token=strtok(NULL,seps);
523         }
524         if(token)
525         {
526                 strcpy(strreturn,token);
527         }
528         else
529         {
530                 return "";
531         }
532         //check for the ':' char....
533         if(token[0]==':')
534         {
535                 //Its not pretty, but it works, return the rest of the string
536                 strcpy(strreturn,l_String+((token-strstart)+1));
537         }
538
539         //return the appropriate response.
540         return strreturn;
541 }
542
543 int AddChatUser(const char *nickname)
544 {
545         Curruser = Firstuser;
546         while(Curruser) 
547         {
548                 if(SDL_strcasecmp(nickname,Curruser->nick_name)==0) return 0;
549                 Curruser = Curruser->next;
550         }
551
552         Curruser = Firstuser;
553         if(Firstuser==NULL)
554         {
555                 Firstuser = (Chat_user *)malloc(sizeof(Chat_user));
556                 SDL_assert(Firstuser);
557                 strcpy(Firstuser->nick_name,nickname);
558                 Firstuser->next = NULL;
559                 AddChatCommandToQueue(CC_USER_JOINING,nickname,strlen(nickname)+1);
560                 return 1;
561         }
562         else
563         {
564                 while(Curruser->next) 
565                 {
566                         Curruser = Curruser->next;
567                 }
568                 Curruser->next = (Chat_user *)malloc(sizeof(Chat_user));
569                 Curruser = Curruser->next;
570                 SDL_assert(Curruser);
571                 strcpy(Curruser->nick_name,nickname);
572                 Curruser->next = NULL;
573                 AddChatCommandToQueue(CC_USER_JOINING,nickname,strlen(nickname)+1);
574                 return 1;
575         }
576
577 }
578
579 int RemoveChatUser(char *nickname)
580 {
581         Chat_user *prv_user = NULL;
582         
583         Curruser = Firstuser;
584         while(Curruser) 
585         {
586                 if(SDL_strcasecmp(nickname,Curruser->nick_name)==0)
587                 {
588                         if(prv_user)
589                         {
590                                 prv_user->next = Curruser->next;
591
592                         }
593                         else
594                         {
595                                 Firstuser = Curruser->next;
596                         }
597                         AddChatCommandToQueue(CC_USER_LEAVING,Curruser->nick_name,strlen(Curruser->nick_name)+1);
598                         free(Curruser);
599                         return 1;
600                 }               
601                 prv_user = Curruser;
602                 Curruser = Curruser->next;
603         }
604         return 0;
605
606 }
607
608 void RemoveAllChatUsers(void)
609 {
610         Chat_user *tmp_user = NULL;
611         Curruser = Firstuser;
612         while(Curruser) 
613         {
614                 tmp_user = Curruser->next;
615                 AddChatCommandToQueue(CC_USER_LEAVING,Curruser->nick_name,strlen(Curruser->nick_name)+1);
616                 free(Curruser);
617                 Curruser = tmp_user;
618         }
619         Firstuser = NULL;
620 }
621
622
623 char * ParseIRCMessage(char *Line, int iMode)
624 {
625         char szRemLine[MAXLOCALSTRING] ="";
626         const char *pszTempStr;
627         char szPrefix[MAXLOCALSTRING] = "";
628         char szHackPrefix[MAXLOCALSTRING] = "";
629         char szTarget[MAXLOCALSTRING] = "";
630         char szNick[MAXLOCALSTRING] = "";
631         char szCmd[MAXLOCALSTRING] = "";
632         char szCTCPCmd[MAXLOCALSTRING] = "";
633
634         static char szResponse[MAXLOCALSTRING] = "";
635
636         int iNickLen;
637         int iPrefixLen = 0;     // JAS: Get rid of optimized warning
638
639         if(strlen(Line)>=MAXLOCALSTRING)
640         {
641                 return NULL; 
642         }
643         //Nick included....
644         if(iMode==MSG_REMOTE)
645         {
646                 strcpy(szRemLine,Line);
647                 //Start by getting the prefix
648                 if(Line[0]==':')
649                 {
650                         //
651                         pszTempStr=GetWordNum(0,Line+1);
652                         strcpy(szPrefix,pszTempStr);
653                         strcpy(szHackPrefix,pszTempStr);
654                         strcpy(szRemLine,Line+1+strlen(szPrefix));
655                 }
656                 //Next, get the Nick
657                 pszTempStr=strtok(szHackPrefix,"!");
658                 if(pszTempStr)
659                 {
660                         strcpy(szNick,pszTempStr);
661                 }
662                 else
663                 {
664                         strncpy(szNick,szPrefix,31);
665          szNick[31]=0;
666                 }
667                 //strcpy(NewMsg.Nickname,szNick);
668                 iNickLen=strlen(szNick);
669                 iPrefixLen=strlen(szPrefix);
670         }
671         else if(iMode==MSG_LOCAL)
672         {
673                 strcpy(szRemLine,Line);
674                 strcpy(szNick,Nick_name);
675                 strcpy(szPrefix,Nick_name);
676                 //strcpy(NewMsg.Nickname,szNick);
677                 iNickLen=-2;
678                 iPrefixLen=-2;
679         }
680         //Next is the command
681         pszTempStr=GetWordNum(0,szRemLine);
682         if(pszTempStr[0])
683         {
684                 strcpy(szCmd,pszTempStr);
685         }
686         else
687         {
688                 //Shouldn't ever happen, but we can't be sure of what the host will send us.
689                 return NULL;
690         }
691
692         //Move the szRemLine string up
693         strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+2);
694         //Now parse the commands!
695         //printf("%s",szCmd);
696         if(SDL_strcasecmp(szCmd,NOX("PRIVMSG"))==0)
697         {
698                 pszTempStr=GetWordNum(0,szRemLine);
699                 strcpy(szTarget,pszTempStr);
700                 strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4);
701                 if(szRemLine[0]==':')
702                 {
703                         strcpy(szCTCPCmd,GetWordNum(0,szRemLine+1));
704                         if(szCTCPCmd[strlen(szCTCPCmd)-1]==0x01) szCTCPCmd[strlen(szCTCPCmd)-1]=0x00;
705
706                 }
707                 else
708                 {
709                         strcpy(szCTCPCmd,GetWordNum(0,szRemLine));
710                         if(szCTCPCmd[strlen(szCTCPCmd)-1]==0x01) szCTCPCmd[strlen(szCTCPCmd)-1]=0x00;
711                 }
712                 if(szCTCPCmd[0]==0x01)
713                 {
714                         //Handle ctcp message
715                         strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+strlen(szCTCPCmd)+6);
716                         szRemLine[strlen(szRemLine)-1]='\0';//null out the ending 0x01
717                         if(SDL_strcasecmp(szCTCPCmd+1,NOX("ACTION"))==0)
718                         {
719                                 //Posture
720                                 sprintf(szResponse,"* %s %s",szNick,szRemLine);                                                         
721                                 return szResponse;
722                         }
723                         if(iMode==MSG_LOCAL)
724                         {
725                                 strcpy(szHackPrefix,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4);
726                                 szRemLine[strlen(szRemLine)-1]='\0';
727                                 sprintf(szResponse,NOX("** CTCP %s %s %s"),szTarget,szCTCPCmd+1,szRemLine);
728                                 return szResponse;
729                         }
730                         if(SDL_strcasecmp(szCTCPCmd+1,NOX("PING"))==0)
731                         {
732                                 sprintf(szResponse,NOX("/NOTICE %s :\001PING %s\001"),szNick,szRemLine);//Don't need the trailing \001 because szremline has it.
733                                 SendChatString(szResponse,1);
734                                 return NULL;
735                         }
736                         if(SDL_strcasecmp(szCTCPCmd+1,NOX("VERSION"))==0)
737                         {
738                                 //reply with a notice version & copyright
739                                 //sprintf(szTempLine,"NOTICE %s :\001VERSION Copyright(c)\001\n",szNick);
740
741                                 return NULL;
742                         }
743                         strcpy(szRemLine,1 + GetWordNum(0,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4));
744                         szRemLine[strlen(szRemLine)-1]='\0';
745                         sprintf(szResponse,NOX("** CTCP Message from %s (%s)"),szNick,szRemLine);
746                         return szResponse;
747
748                 }
749                 //differentiate between channel and private
750                 if(szTarget[0]=='#')
751                 {
752                         pszTempStr=GetWordNum(0,szRemLine);
753                         sprintf(szResponse,"[%s] %s",szNick,pszTempStr);                        
754                         return szResponse;
755                 }
756                 else
757                 {
758                         if(iMode == MSG_LOCAL)
759                         {
760                                 pszTempStr=GetWordNum(0,szRemLine);
761                                 sprintf(szResponse,NOX("Private Message to <%s>: %s"),szNick,pszTempStr);                       
762                         }
763                         else
764                         {
765                                 pszTempStr=GetWordNum(0,szRemLine);
766                                 sprintf(szResponse,NOX("Private Message from <%s>: %s"),szNick,pszTempStr);                     
767                         }
768                         return szResponse;
769                 }
770
771         }
772         //don't handle any other messages locally.
773         if(iMode==MSG_LOCAL)
774         {
775                 return NULL;
776         }
777
778         if(SDL_strcasecmp(szCmd,NOX("NOTICE"))==0)
779         {
780                 
781
782                 pszTempStr=GetWordNum(0,szRemLine);
783                 strcpy(szTarget,pszTempStr);
784                 strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4);
785                 if(szRemLine[0]==':')
786                 {
787                         strcpy(szCTCPCmd,GetWordNum(0,szRemLine+1));
788                         if(szCTCPCmd[strlen(szCTCPCmd)-1]==0x01) szCTCPCmd[strlen(szCTCPCmd)-1]=0x00;
789
790                 }
791                 else
792                 {
793                         strcpy(szCTCPCmd,GetWordNum(0,szRemLine));
794                         if(szCTCPCmd[strlen(szCTCPCmd)-1]==0x01) szCTCPCmd[strlen(szCTCPCmd)-1]=0x00;
795                 }
796                 if(szCTCPCmd[0]==0x01)
797                 {
798                         //Handle ctcp message
799                         strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+strlen(szCTCPCmd)+6);
800                         szRemLine[strlen(szRemLine)-1]='\0';//null out the ending 0x01
801                         if(SDL_strcasecmp(szCTCPCmd+1,NOX("PING"))==0)
802                         {
803                                 //This is a ping response, figure out time and print
804                                 //sprintf(NewMsg.Message,"** Ping Response from %s: %ums",szNick,ulping);
805                                 return NULL;
806                         }
807                         
808                         //Default message
809                         strcpy(szRemLine,1 + GetWordNum(0,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4));
810                         szRemLine[strlen(szRemLine)-1]='\0';
811                         sprintf(szResponse,XSTR("** CTCP Message from %s (%s)",635),szNick,szRemLine);
812                         return szResponse;
813                         
814                 }
815                 sprintf(szResponse,"%s",szRemLine);
816                 return NULL;
817         }
818         if(SDL_strcasecmp(szCmd,NOX("JOIN"))==0)
819         {
820                 //see if it is me!
821                 if(SDL_strcasecmp(Nick_name,szNick)==0)
822                 {
823                         //Yup, it's me!
824                         //if(strcmpi(szChat_channel,GetWordNum(0,szRemLine))==0)
825                         //{
826                                 Joined_channel = 1;
827                                 if(SDL_strcasecmp(szChat_channel,NOX("#autoselect"))==0)
828                                 {
829                                         strcpy(szChat_channel,GetWordNum(0,szRemLine));
830                                         AddChatCommandToQueue(CC_YOURCHANNEL,szChat_channel,strlen(szChat_channel)+1);
831
832                                 }
833                                 //CC_YOURCHANNEL
834                         //}
835                 }
836                                 AddChatUser(szNick);
837
838                 
839                 pszTempStr=GetWordNum(0,szRemLine);
840                 strcpy(szTarget,pszTempStr);
841                 //strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+3);
842
843                 //strcpy(NewMsg.Channel,szTarget);
844
845                 AddChatUser(szNick);
846                 sprintf(szResponse,XSTR("** %s has joined %s",636),szNick,szTarget);
847                 return NULL;//szResponse;
848                 //Add them to the userlist too!
849         }
850         if(SDL_strcasecmp(szCmd,NOX("PART"))==0)
851         {
852                 pszTempStr=GetWordNum(0,szRemLine);
853                 strcpy(szTarget,pszTempStr);
854                 strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+3);
855                 //see if it is me!
856                 if(SDL_strcasecmp(Nick_name,szNick)==0)
857                 {
858                         //Yup, it's me!
859                         //szChat_channel[0]=NULL;
860                         RemoveAllChatUsers();
861                 }
862                 
863                 RemoveChatUser(szNick);
864                 return NULL;
865                 //Remove them to the userlist too!
866         }
867         if(SDL_strcasecmp(szCmd,NOX("KICK"))==0)
868         {
869                 pszTempStr=GetWordNum(0,szRemLine);
870                 strcpy(szTarget,pszTempStr);
871                 pszTempStr=GetWordNum(1,szRemLine);
872                 strcpy(szHackPrefix,pszTempStr);
873                 pszTempStr=GetWordNum(2,szRemLine);
874                 //see if it is me!
875                 if(SDL_strcasecmp(Nick_name,GetWordNum(1,szRemLine))==0)
876                 {
877                         //Yup, it's me!
878                         szChat_channel[0]='\0';
879                         //bNewStatus=1;
880                         AddChatCommandToQueue(CC_KICKED,NULL,0);                        
881                         RemoveAllChatUsers();
882                 }
883                 sprintf(szResponse,XSTR("*** %s has kicked %s from channel %s (%s)",637),szNick,szHackPrefix,szTarget,pszTempStr);
884                 //Remove them to the userlist too!
885                 RemoveChatUser(szNick);
886                 return szResponse;
887                 
888         }
889         if(SDL_strcasecmp(szCmd,NOX("NICK"))==0)
890         {
891       //see if it is me!
892                 if(SDL_strcasecmp(Nick_name,szNick)==0)
893                 {
894                         //Yup, it's me!
895                         strcpy(Nick_name,GetWordNum(0,szRemLine));
896                 }
897                 char nicks[70];
898                 sprintf(nicks,"%s %s",szNick,GetWordNum(0,szRemLine));
899                 AddChatCommandToQueue(CC_NICKCHANGED,nicks,strlen(nicks)+1);
900                 RemoveChatUser(szNick);
901                 AddChatUser(GetWordNum(0,szRemLine));
902       sprintf(szResponse,XSTR("*** %s is now known as %s",638),szNick,GetWordNum(0,szRemLine));
903                 return szResponse;
904         }
905         if(SDL_strcasecmp(szCmd,NOX("PING"))==0)
906         {
907                 //respond with pong (GetWordNum(0,szRemLine))
908                 sprintf(szResponse,NOX("/PONG :%s"),GetWordNum(0,szRemLine));
909                 SendChatString(szResponse,1);
910                 return NULL;
911         }
912         if(SDL_strcasecmp(szCmd,NOX("MODE"))==0)
913         {
914                 //Channel Mode info
915                 return NULL;
916         }
917
918
919         if(SDL_strcasecmp(szCmd,"401")==0)
920         {
921                 //This is whois user info, we can get their tracker info from here.  -5
922                 char szWhoisUser[33];
923                 strcpy(szWhoisUser,GetWordNum(1,szRemLine));
924                 Getting_user_tracker_error = 1;                 
925                 Getting_user_channel_error = 1;                         
926                                                 
927                 sprintf(szResponse,XSTR("**Error: %s is not online!",639),szWhoisUser);
928                 return szResponse;
929
930         }
931         if(SDL_strcasecmp(szCmd,"311")==0)
932         {
933                 char szWhoisUser[33];
934                 strcpy(szWhoisUser,GetWordNum(1,szRemLine));
935                 //This is whois user info, we can get their tracker info from here.  -5
936                 //if(strcmpi(Getting_user_tracker_info_for,szWhoisUser)==0)
937                 //{
938                         strcpy(User_req_tracker_id,GetWordNum(5,szRemLine));
939                 //}
940                 return NULL;
941         }
942         if(SDL_strcasecmp(szCmd,"319")==0)
943         {
944                 char szWhoisUser[33];
945                 strcpy(szWhoisUser,GetWordNum(1,szRemLine));
946                 //This is whois channel info -- what channel they are on                -2
947                 //if(strcmpi(Getting_user_channel_info_for,szWhoisUser)==0)
948                 //{
949                         strcpy(User_req_channel,GetWordNum(2,szRemLine));
950                 //}
951                 return NULL;
952         }
953         
954         //End of whois and we didn't get a channel means they aren't in a channel.
955         if(SDL_strcasecmp(szCmd,"318")==0)
956         {
957                 if(!*User_req_channel)
958                 {
959                         User_req_channel[0] = '*';
960                 }
961         }
962
963
964         if(SDL_strcasecmp(szCmd,"321")==0)
965         {
966                 //start of channel list
967                 FlushChannelList();
968                 GettingChannelList = 1;
969                 return NULL;
970         }
971         if(SDL_strcasecmp(szCmd,"322")==0)
972         {
973                 //channel list data
974                 if(GettingChannelList == 1)
975                 {
976                         char channel_list_name[33];
977                         char sztopic[200];
978                         strcpy(sztopic,GetWordNum(3,szRemLine));
979                         strcpy(channel_list_name,GetWordNum(1,szRemLine));
980                         AddChannel(channel_list_name,(short)atoi(GetWordNum(2,szRemLine)),sztopic);
981                 }
982                 return NULL;
983         }
984         if(SDL_strcasecmp(szCmd,"323")==0)
985         {
986                 //end of channel list
987                 GettingChannelList = 2;
988                 return NULL;
989         }
990         if(SDL_strcasecmp(szCmd,"324")==0)
991         {
992                 //Channel Mode info
993                 return NULL;
994         }
995
996         if(SDL_strcasecmp(szCmd,"332")==0)
997                 {
998                 //Channel Topic, update status bar.
999                 if(SDL_strcasecmp(szChat_channel,szTarget)==0)
1000                 {
1001                         //strncpy(szChanTopic,GetWordNum(2,szRemLine),70);
1002                 }
1003                 //sprintf(NewMsg.Message,"*** %s has changed the topic to: %s",szNick,GetWordNum(2,szRemLine));
1004
1005                 return NULL;
1006         }
1007         if(SDL_strcasecmp(szCmd,NOX("TOPIC"))==0)
1008         {
1009                 //Channel Topic, update status bar.
1010                 if(SDL_strcasecmp(szChat_channel,szTarget)==0)
1011                 {
1012                         //strncpy(szChanTopic,GetWordNum(1,szRemLine),70);
1013                 }
1014                 //sprintf(NewMsg.Message,"*** %s has changed the topic to: %s",szNick,GetWordNum(1,szRemLine));
1015                 return NULL;
1016         }
1017         if(SDL_strcasecmp(szCmd,NOX("QUIT"))==0)
1018         {
1019                 //Remove the user!
1020                 RemoveChatUser(szNick);
1021                 return NULL;
1022         }
1023         if(SDL_strcasecmp(szCmd,"376")==0) //end of motd, trigger autojoin...
1024         {
1025                 if (!Chat_server_connected)
1026                 {
1027                         Chat_server_connected=1;
1028                 }
1029
1030                 // end of motd
1031                 strcpy(szResponse, PXO_CHAT_END_OF_MOTD_PREFIX);
1032                 return szResponse;
1033         }
1034         if((SDL_strcasecmp(szCmd,"377")==0)||
1035                 (SDL_strcasecmp(szCmd,"372")==0)||
1036                 (SDL_strcasecmp(szCmd,"372")==0)
1037                 
1038                 )
1039         {
1040                 //Stip the message, and display it.
1041                 pszTempStr=GetWordNum(3,Line);          
1042                 strcpy(szResponse, PXO_CHAT_MOTD_PREFIX);
1043                 strcat(szResponse, pszTempStr);
1044                 return szResponse;
1045         }
1046         //Ignore these messages
1047         if(((SDL_strcasecmp(szCmd,"366")==0))||
1048                 (SDL_strcasecmp(szCmd,"333")==0) || //Who set the topic
1049                  (SDL_strcasecmp(szCmd,"329")==0))    //Time Channel created
1050                  /*
1051                  (SDL_strcasecmp(szCmd,"305")==0) ||
1052                  (SDL_strcasecmp(szCmd,"306")==0) ||
1053                  (SDL_strcasecmp(szCmd,"311")==0) || //WHOIS stuff
1054                  (SDL_strcasecmp(szCmd,"312")==0) ||
1055                  (SDL_strcasecmp(szCmd,"313")==0) ||
1056                  (SDL_strcasecmp(szCmd,"317")==0) ||
1057                  (SDL_strcasecmp(szCmd,"318")==0) ||
1058                  (SDL_strcasecmp(szCmd,"319")==0) ||
1059                  */
1060
1061         {
1062                 return NULL;
1063         }
1064         if(SDL_strcasecmp(szCmd,"353")==0)
1065         {
1066
1067                 //Names in the channel.
1068                 pszTempStr = GetWordNum(3,Line+iPrefixLen+strlen(szCmd)+2);
1069                 strcpy(szRemLine,pszTempStr);
1070                 pszTempStr = strtok(szRemLine," ");
1071
1072                 while(pszTempStr)
1073                 {
1074                         if(pszTempStr[0]=='@')
1075                         {
1076                                 AddChatUser(pszTempStr+1);
1077                         }
1078                         else if(pszTempStr[0]=='+')
1079                         {
1080                                 AddChatUser(pszTempStr+1);
1081                         }
1082                         else
1083                         {
1084                                 AddChatUser(pszTempStr);
1085                         }
1086                         pszTempStr=strtok(NULL," ");
1087                 }
1088                 return NULL;
1089         }
1090         //MOTD Codes
1091         if((SDL_strcasecmp(szCmd,"001")==0)||
1092            (SDL_strcasecmp(szCmd,"002")==0)||
1093            (SDL_strcasecmp(szCmd,"003")==0)||
1094            (SDL_strcasecmp(szCmd,"004")==0)||
1095            (SDL_strcasecmp(szCmd,"251")==0)||
1096            (SDL_strcasecmp(szCmd,"254")==0)||
1097            (SDL_strcasecmp(szCmd,"255")==0)||
1098            (SDL_strcasecmp(szCmd,"265")==0)||
1099            (SDL_strcasecmp(szCmd,"375")==0)||
1100            (SDL_strcasecmp(szCmd,"372")==0)||
1101            (SDL_strcasecmp(szCmd,"375")==0)
1102            )
1103         {
1104                 // Stip the message, and display it.
1105                 // pszTempStr = GetWordNum(3, Line);
1106                 // strcpy(szResponse, PXO_CHAT_MOTD_PREFIX);
1107                 // strcat(szResponse, pszTempStr);
1108                 return NULL;
1109                 // return szResponse;
1110         }
1111         if(SDL_strcasecmp(szCmd,"432")==0)
1112         {
1113                 //Channel Mode info
1114                 strcpy(szResponse,XSTR("Your nickname contains invalid characters",640));
1115                 AddChatCommandToQueue(CC_DISCONNECTED,NULL,0);
1116                 return szResponse;
1117         }
1118         if(SDL_strcasecmp(szCmd,"433")==0)
1119         {
1120                 //Channel Mode info
1121                 char new_nick[33];
1122                 sprintf(new_nick,"%s%d",Orignial_nick_name,Nick_variety);
1123                 strcpy(Nick_name,new_nick);
1124                 Nick_variety++;
1125                 sprintf(szResponse,NOX("/NICK %s"),new_nick);
1126                 SendChatString(szResponse,1);
1127                 return NULL;
1128         }
1129         //Default print
1130         strcpy(szResponse,Line);
1131         //return szResponse;
1132         return NULL;
1133
1134 }
1135
1136
1137 void AddChatCommandToQueue(int command,const void *data,int len)
1138 {
1139         Currcommand = Firstcommand;
1140         if(Firstcommand==NULL)
1141         {
1142                 Firstcommand = (Chat_command *)malloc(sizeof(Chat_command));
1143                 SDL_assert(Firstcommand);
1144                 Firstcommand->next = NULL;
1145                 Currcommand = Firstcommand;
1146         }
1147         else
1148         {
1149                 while(Currcommand->next) 
1150                 {
1151                         Currcommand = Currcommand->next;
1152                 }
1153                 Currcommand->next = (Chat_command *)malloc(sizeof(Chat_command));
1154                 SDL_assert(Currcommand->next);
1155                 Currcommand = Currcommand->next;
1156         }
1157         Currcommand->command = (short)command;
1158         if(len&&data) memcpy(&Currcommand->data,data,len);
1159         Currcommand->next = NULL;
1160         return;
1161 }
1162
1163 Chat_command *GetChatCommandFromQueue(void)
1164 {
1165         static Chat_command response_cmd;
1166         Chat_command *tmp_cmd;
1167         if(!Firstcommand) return NULL;
1168         Currcommand = Firstcommand;
1169         memcpy(&response_cmd,Currcommand,sizeof(Chat_command));
1170         tmp_cmd = Currcommand->next;
1171         free(Firstcommand);
1172         Firstcommand = tmp_cmd;
1173         return &response_cmd;
1174 }
1175
1176 void FlushChatCommandQueue(void)
1177 {
1178         Chat_command *tmp_cmd;
1179         Currcommand = Firstcommand;
1180         
1181         while(Currcommand) 
1182         {
1183                 tmp_cmd = Currcommand->next;
1184                 free(Currcommand);
1185                 Currcommand = tmp_cmd;
1186         }
1187         Firstcommand = NULL;
1188 }
1189
1190
1191 void FlushChannelList(void)
1192 {
1193         Chat_channel *tmp_chan;
1194         Currchannel = Firstchannel;
1195         
1196         while(Currchannel) 
1197         {
1198                 tmp_chan = Currchannel->next;
1199                 free(Currchannel);
1200                 Currchannel = tmp_chan;
1201         }
1202         Firstchannel = NULL;
1203
1204
1205 }
1206 char *GetChannelList(void)
1207 {
1208         int ichan_list_length = 0;
1209         char sznumusers[10];
1210         
1211         if(GettingChannelList != 2) return NULL;
1212         if(!Socket_connected) return NULL;
1213
1214         if(Chan_list)
1215         {
1216                 free(Chan_list);
1217                 Chan_list = NULL;
1218         }
1219         
1220         
1221         Currchannel = Firstchannel;
1222         while(Currchannel) 
1223         {
1224                 ichan_list_length += strlen(Currchannel->topic)+1+strlen(Currchannel->channel_name)+1+5;//1 for the space, and 4 for the number of users 0000-9999 + space
1225                 Currchannel = Currchannel->next;
1226         }
1227         Currchannel = Firstchannel;
1228         Chan_list = (char *)malloc(ichan_list_length+1);
1229         Chan_list[0] = '\0';
1230         while(Currchannel) 
1231         {
1232                 strcat(Chan_list,"$");
1233                 strcat(Chan_list,Currchannel->channel_name);
1234                 strcat(Chan_list," ");
1235                 sprintf(sznumusers,"%d ",Currchannel->users);
1236                 strcat(Chan_list,sznumusers);
1237                 strcat(Chan_list,Currchannel->topic);//fgets
1238                 strcat(Chan_list," ");
1239                 Currchannel = Currchannel->next;
1240         }
1241         FlushChannelList();
1242         GettingChannelList = 0;
1243         return Chan_list;
1244 }
1245
1246 void AddChannel(char *channel,unsigned short numusers,char *topic)
1247 {
1248         Currchannel = Firstchannel;
1249         if(Firstchannel==NULL)
1250         {
1251                 Firstchannel = (Chat_channel *)malloc(sizeof(Chat_channel));
1252                 SDL_assert(Firstchannel);
1253                 strcpy(Firstchannel->channel_name,channel);
1254                 strcpy(Firstchannel->topic,topic);
1255                 Firstchannel->users = numusers;
1256                 Firstchannel->next = NULL;
1257                 Currchannel = Firstchannel;
1258         }
1259         else
1260         {
1261                 while(Currchannel->next) 
1262                 {
1263                         Currchannel = Currchannel->next;
1264                 }
1265                 Currchannel->next = (Chat_channel *)malloc(sizeof(Chat_channel));
1266                 SDL_assert(Currchannel->next);
1267                 Currchannel = Currchannel->next;
1268                 strcpy(Currchannel->channel_name,channel);
1269                 strcpy(Currchannel->topic,topic);
1270                 Currchannel->users = numusers;
1271         }
1272         Currchannel->next = NULL;
1273         return;
1274 }
1275
1276
1277 char *GetTrackerIdByUser(char *nickname)
1278 {
1279         char szWhoisCmd[100];
1280
1281         
1282         if(GettingUserTID)
1283         {
1284                 if(Getting_user_tracker_error)
1285                 {
1286                         Getting_user_tracker_error = 0;
1287                         GettingUserTID = 0;
1288                         return (char *)-1;
1289                 }
1290                 
1291                 if(*User_req_tracker_id)
1292                 {
1293                         GettingUserTID = 0;
1294                         return User_req_tracker_id;
1295                 }
1296         }
1297         else
1298         {
1299                 strcpy(Getting_user_tracker_info_for,nickname);
1300                 sprintf(szWhoisCmd,NOX("/WHOIS %s"),nickname);
1301                 User_req_tracker_id[0] = '\0';
1302                 SendChatString(szWhoisCmd,1);           
1303                 GettingUserTID = 1;
1304         }
1305         return NULL;
1306 }
1307
1308 char *GetChannelByUser(char *nickname)
1309 {
1310         char szWhoisCmd[100];
1311         
1312         if(GettingUserChannel)
1313         {
1314                 if(Getting_user_channel_error)
1315                 {
1316                         Getting_user_channel_error = 0;
1317                         GettingUserChannel = 0;
1318                         return (char *)-1;
1319                 }
1320                 if(*User_req_channel)
1321                 {
1322                         GettingUserChannel = 0;
1323                         return User_req_channel;
1324                 }
1325         }
1326         else
1327         {
1328                 strcpy(Getting_user_channel_info_for,nickname);
1329                 User_req_channel[0] = '\0';
1330                 sprintf(szWhoisCmd,NOX("/WHOIS %s"),nickname);
1331                 SendChatString(szWhoisCmd,1);
1332                 GettingUserChannel = 1;
1333         }
1334         return NULL;
1335 }
1336