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