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