]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/chat_api.cpp
clean up Windows #include's and use winsock2
[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 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 iPrefixLen = 0;     // JAS: Get rid of optimized warning
640
641         if(strlen(Line)>=MAXLOCALSTRING)
642         {
643                 return NULL; 
644         }
645         //Nick included....
646         if(iMode==MSG_REMOTE)
647         {
648                 SDL_strlcpy(szRemLine, Line, SDL_arraysize(szRemLine));
649                 //Start by getting the prefix
650                 if(Line[0]==':')
651                 {
652                         //
653                         pszTempStr=GetWordNum(0,Line+1);
654                         SDL_strlcpy(szPrefix, pszTempStr, SDL_arraysize(szPrefix));
655                         SDL_strlcpy(szHackPrefix, pszTempStr, SDL_arraysize(szHackPrefix));
656                         SDL_strlcpy(szRemLine, Line+1+strlen(szPrefix), SDL_arraysize(szRemLine));
657                 }
658                 //Next, get the Nick
659                 pszTempStr=strtok(szHackPrefix,"!");
660                 if(pszTempStr)
661                 {
662                         SDL_strlcpy(szNick, pszTempStr, SDL_arraysize(szNick));
663                 }
664                 else
665                 {
666                         SDL_strlcpy(szNick, szPrefix, SDL_arraysize(szNick));
667                 }
668                 //strcpy(NewMsg.Nickname,szNick);
669                 iPrefixLen=strlen(szPrefix);
670         }
671         else if(iMode==MSG_LOCAL)
672         {
673                 SDL_strlcpy(szRemLine, Line, SDL_arraysize(szRemLine));
674                 SDL_strlcpy(szNick, Nick_name, SDL_arraysize(szNick));
675                 SDL_strlcpy(szPrefix, Nick_name, SDL_arraysize(szPrefix));
676                 //strcpy(NewMsg.Nickname,szNick);
677                 iPrefixLen=-2;
678         }
679         //Next is the command
680         pszTempStr=GetWordNum(0,szRemLine);
681         if(pszTempStr[0])
682         {
683                 SDL_strlcpy(szCmd, pszTempStr, SDL_arraysize(szCmd));
684         }
685         else
686         {
687                 //Shouldn't ever happen, but we can't be sure of what the host will send us.
688                 return NULL;
689         }
690
691         //Move the szRemLine string up
692         SDL_strlcpy(szRemLine, Line+iPrefixLen+strlen(szCmd)+2, SDL_arraysize(szRemLine));
693         //Now parse the commands!
694         //printf("%s",szCmd);
695         if(SDL_strcasecmp(szCmd,NOX("PRIVMSG"))==0)
696         {
697                 pszTempStr=GetWordNum(0,szRemLine);
698                 SDL_strlcpy(szTarget, pszTempStr, SDL_arraysize(szTarget));
699                 SDL_strlcpy(szRemLine, Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4, SDL_arraysize(szRemLine));
700                 if(szRemLine[0]==':')
701                 {
702                         SDL_strlcpy(szCTCPCmd, GetWordNum(0,szRemLine+1), SDL_arraysize(szCTCPCmd));
703                         if(szCTCPCmd[strlen(szCTCPCmd)-1]==0x01) szCTCPCmd[strlen(szCTCPCmd)-1]=0x00;
704
705                 }
706                 else
707                 {
708                         SDL_strlcpy(szCTCPCmd, GetWordNum(0,szRemLine), SDL_arraysize(szCTCPCmd));
709                         if(szCTCPCmd[strlen(szCTCPCmd)-1]==0x01) szCTCPCmd[strlen(szCTCPCmd)-1]=0x00;
710                 }
711                 if(szCTCPCmd[0]==0x01)
712                 {
713                         //Handle ctcp message
714                         SDL_strlcpy(szRemLine, Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+strlen(szCTCPCmd)+6, SDL_arraysize(szRemLine));
715                         szRemLine[strlen(szRemLine)-1]='\0';//null out the ending 0x01
716                         if(SDL_strcasecmp(szCTCPCmd+1,NOX("ACTION"))==0)
717                         {
718                                 //Posture
719                                 SDL_snprintf(szResponse, SDL_arraysize(szResponse), "* %s %s", szNick, szRemLine);
720                                 return szResponse;
721                         }
722                         if(iMode==MSG_LOCAL)
723                         {
724                                 SDL_strlcpy(szHackPrefix, Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4, SDL_arraysize(szHackPrefix));
725                                 szRemLine[strlen(szRemLine)-1]='\0';
726                                 SDL_snprintf(szResponse, SDL_arraysize(szResponse), NOX("** CTCP %s %s %s"), szTarget, szCTCPCmd+1, szRemLine);
727                                 return szResponse;
728                         }
729                         if(SDL_strcasecmp(szCTCPCmd+1,NOX("PING"))==0)
730                         {
731                                 SDL_snprintf(szResponse, SDL_arraysize(szResponse), NOX("/NOTICE %s :\001PING %s\001"), szNick, szRemLine);//Don't need the trailing \001 because szremline has it.
732                                 SendChatString(szResponse,1);
733                                 return NULL;
734                         }
735                         if(SDL_strcasecmp(szCTCPCmd+1,NOX("VERSION"))==0)
736                         {
737                                 //reply with a notice version & copyright
738                                 //sprintf(szTempLine,"NOTICE %s :\001VERSION Copyright(c)\001\n",szNick);
739
740                                 return NULL;
741                         }
742                         SDL_strlcpy(szRemLine, 1 + GetWordNum(0,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4), SDL_arraysize(szRemLine));
743                         szRemLine[strlen(szRemLine)-1]='\0';
744                         SDL_snprintf(szResponse, SDL_arraysize(szResponse), NOX("** CTCP Message from %s (%s)"), szNick, szRemLine);
745                         return szResponse;
746
747                 }
748                 //differentiate between channel and private
749                 if(szTarget[0]=='#')
750                 {
751                         pszTempStr=GetWordNum(0,szRemLine);
752                         SDL_snprintf(szResponse, SDL_arraysize(szResponse), "[%s] %s", szNick, pszTempStr);
753                         return szResponse;
754                 }
755                 else
756                 {
757                         if(iMode == MSG_LOCAL)
758                         {
759                                 pszTempStr=GetWordNum(0,szRemLine);
760                                 SDL_snprintf(szResponse, SDL_arraysize(szResponse), NOX("Private Message to <%s>: %s"), szNick, pszTempStr);
761                         }
762                         else
763                         {
764                                 pszTempStr=GetWordNum(0,szRemLine);
765                                 SDL_snprintf(szResponse, SDL_arraysize(szResponse), NOX("Private Message from <%s>: %s"), szNick, pszTempStr);
766                         }
767                         return szResponse;
768                 }
769
770         }
771         //don't handle any other messages locally.
772         if(iMode==MSG_LOCAL)
773         {
774                 return NULL;
775         }
776
777         if(SDL_strcasecmp(szCmd,NOX("NOTICE"))==0)
778         {
779                 
780
781                 pszTempStr=GetWordNum(0,szRemLine);
782                 SDL_strlcpy(szTarget, pszTempStr, SDL_arraysize(szTarget));
783                 SDL_strlcpy(szRemLine, Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4, SDL_arraysize(szRemLine));
784                 if(szRemLine[0]==':')
785                 {
786                         SDL_strlcpy(szCTCPCmd, GetWordNum(0,szRemLine+1), SDL_arraysize(szCTCPCmd));
787                         if(szCTCPCmd[strlen(szCTCPCmd)-1]==0x01) szCTCPCmd[strlen(szCTCPCmd)-1]=0x00;
788
789                 }
790                 else
791                 {
792                         SDL_strlcpy(szCTCPCmd, GetWordNum(0,szRemLine), SDL_arraysize(szCTCPCmd));
793                         if(szCTCPCmd[strlen(szCTCPCmd)-1]==0x01) szCTCPCmd[strlen(szCTCPCmd)-1]=0x00;
794                 }
795                 if(szCTCPCmd[0]==0x01)
796                 {
797                         //Handle ctcp message
798                         SDL_strlcpy(szRemLine, Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+strlen(szCTCPCmd)+6, SDL_arraysize(szRemLine));
799                         szRemLine[strlen(szRemLine)-1]='\0';//null out the ending 0x01
800                         if(SDL_strcasecmp(szCTCPCmd+1,NOX("PING"))==0)
801                         {
802                                 //This is a ping response, figure out time and print
803                                 //sprintf(NewMsg.Message,"** Ping Response from %s: %ums",szNick,ulping);
804                                 return NULL;
805                         }
806                         
807                         //Default message
808                         SDL_strlcpy(szRemLine, 1 + GetWordNum(0,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+4), SDL_arraysize(szRemLine));
809                         szRemLine[strlen(szRemLine)-1]='\0';
810                         SDL_snprintf(szResponse, SDL_arraysize(szResponse), XSTR("** CTCP Message from %s (%s)",635), szNick, szRemLine);
811                         return szResponse;
812                         
813                 }
814                 SDL_snprintf(szResponse, SDL_arraysize(szResponse), "%s", szRemLine);
815                 return NULL;
816         }
817         if(SDL_strcasecmp(szCmd,NOX("JOIN"))==0)
818         {
819                 //see if it is me!
820                 if(SDL_strcasecmp(Nick_name,szNick)==0)
821                 {
822                         //Yup, it's me!
823                         //if(strcmpi(szChat_channel,GetWordNum(0,szRemLine))==0)
824                         //{
825                                 Joined_channel = 1;
826                                 if(SDL_strcasecmp(szChat_channel,NOX("#autoselect"))==0)
827                                 {
828                                         SDL_strlcpy(szChat_channel, GetWordNum(0,szRemLine), SDL_arraysize(szChat_channel));
829                                         AddChatCommandToQueue(CC_YOURCHANNEL,szChat_channel,strlen(szChat_channel)+1);
830
831                                 }
832                                 //CC_YOURCHANNEL
833                         //}
834                 }
835                                 AddChatUser(szNick);
836
837                 
838                 pszTempStr=GetWordNum(0,szRemLine);
839                 SDL_strlcpy(szTarget, pszTempStr, SDL_arraysize(szTarget));
840                 //strcpy(szRemLine,Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+3);
841
842                 //strcpy(NewMsg.Channel,szTarget);
843
844                 AddChatUser(szNick);
845                 SDL_snprintf(szResponse, SDL_arraysize(szResponse), XSTR("** %s has joined %s",636), szNick, szTarget);
846                 return NULL;//szResponse;
847                 //Add them to the userlist too!
848         }
849         if(SDL_strcasecmp(szCmd,NOX("PART"))==0)
850         {
851                 pszTempStr=GetWordNum(0,szRemLine);
852                 SDL_strlcpy(szTarget, pszTempStr, SDL_arraysize(szTarget));
853                 SDL_strlcpy(szRemLine, Line+iPrefixLen+strlen(szCmd)+strlen(szTarget)+3, SDL_arraysize(szRemLine));
854                 //see if it is me!
855                 if(SDL_strcasecmp(Nick_name,szNick)==0)
856                 {
857                         //Yup, it's me!
858                         //szChat_channel[0]=NULL;
859                         RemoveAllChatUsers();
860                 }
861                 
862                 RemoveChatUser(szNick);
863                 return NULL;
864                 //Remove them to the userlist too!
865         }
866         if(SDL_strcasecmp(szCmd,NOX("KICK"))==0)
867         {
868                 pszTempStr=GetWordNum(0,szRemLine);
869                 SDL_strlcpy(szTarget, pszTempStr, SDL_arraysize(szTarget));
870                 pszTempStr=GetWordNum(1,szRemLine);
871                 SDL_strlcpy(szHackPrefix, pszTempStr, SDL_arraysize(szHackPrefix));
872                 pszTempStr=GetWordNum(2,szRemLine);
873                 //see if it is me!
874                 if(SDL_strcasecmp(Nick_name,GetWordNum(1,szRemLine))==0)
875                 {
876                         //Yup, it's me!
877                         szChat_channel[0]='\0';
878                         //bNewStatus=1;
879                         AddChatCommandToQueue(CC_KICKED,NULL,0);                        
880                         RemoveAllChatUsers();
881                 }
882                 SDL_snprintf(szResponse, SDL_arraysize(szResponse), XSTR("*** %s has kicked %s from channel %s (%s)",637), szNick, szHackPrefix, szTarget, pszTempStr);
883                 //Remove them to the userlist too!
884                 RemoveChatUser(szNick);
885                 return szResponse;
886                 
887         }
888         if(SDL_strcasecmp(szCmd,NOX("NICK"))==0)
889         {
890       //see if it is me!
891                 if(SDL_strcasecmp(Nick_name,szNick)==0)
892                 {
893                         //Yup, it's me!
894                         SDL_strlcpy(Nick_name, GetWordNum(0,szRemLine), SDL_arraysize(Nick_name));
895                 }
896                 char nicks[70];
897                 SDL_snprintf(nicks, SDL_arraysize(nicks), "%s %s", szNick, GetWordNum(0,szRemLine));
898                 AddChatCommandToQueue(CC_NICKCHANGED,nicks,strlen(nicks)+1);
899                 RemoveChatUser(szNick);
900                 AddChatUser(GetWordNum(0,szRemLine));
901           SDL_snprintf(szResponse, SDL_arraysize(szResponse), XSTR("*** %s is now known as %s",638), szNick, GetWordNum(0,szRemLine));
902                 return szResponse;
903         }
904         if(SDL_strcasecmp(szCmd,NOX("PING"))==0)
905         {
906                 //respond with pong (GetWordNum(0,szRemLine))
907                 SDL_snprintf(szResponse, SDL_arraysize(szResponse), NOX("/PONG :%s"), GetWordNum(0,szRemLine));
908                 SendChatString(szResponse,1);
909                 return NULL;
910         }
911         if(SDL_strcasecmp(szCmd,NOX("MODE"))==0)
912         {
913                 //Channel Mode info
914                 return NULL;
915         }
916
917
918         if(SDL_strcasecmp(szCmd,"401")==0)
919         {
920                 //This is whois user info, we can get their tracker info from here.  -5
921                 char szWhoisUser[33];
922                 SDL_strlcpy(szWhoisUser, GetWordNum(1,szRemLine), SDL_arraysize(szWhoisUser));
923                 Getting_user_tracker_error = 1;                 
924                 Getting_user_channel_error = 1;                         
925                                                 
926                 SDL_snprintf(szResponse, SDL_arraysize(szResponse), XSTR("**Error: %s is not online!",639), szWhoisUser);
927                 return szResponse;
928
929         }
930         if(SDL_strcasecmp(szCmd,"311")==0)
931         {
932                 char szWhoisUser[33];
933                 SDL_strlcpy(szWhoisUser, GetWordNum(1,szRemLine), SDL_arraysize(szWhoisUser));
934                 //This is whois user info, we can get their tracker info from here.  -5
935                 //if(strcmpi(Getting_user_tracker_info_for,szWhoisUser)==0)
936                 //{
937                         SDL_strlcpy(User_req_tracker_id, GetWordNum(5,szRemLine), SDL_arraysize(User_req_tracker_id));
938                 //}
939                 return NULL;
940         }
941         if(SDL_strcasecmp(szCmd,"319")==0)
942         {
943                 char szWhoisUser[33];
944                 SDL_strlcpy(szWhoisUser, GetWordNum(1,szRemLine), SDL_arraysize(szWhoisUser));
945                 //This is whois channel info -- what channel they are on                -2
946                 //if(strcmpi(Getting_user_channel_info_for,szWhoisUser)==0)
947                 //{
948                         SDL_strlcpy(User_req_channel, GetWordNum(2,szRemLine), SDL_arraysize(User_req_channel));
949                 //}
950                 return NULL;
951         }
952         
953         //End of whois and we didn't get a channel means they aren't in a channel.
954         if(SDL_strcasecmp(szCmd,"318")==0)
955         {
956                 if(!*User_req_channel)
957                 {
958                         User_req_channel[0] = '*';
959                 }
960         }
961
962
963         if(SDL_strcasecmp(szCmd,"321")==0)
964         {
965                 //start of channel list
966                 FlushChannelList();
967                 GettingChannelList = 1;
968                 return NULL;
969         }
970         if(SDL_strcasecmp(szCmd,"322")==0)
971         {
972                 //channel list data
973                 if(GettingChannelList == 1)
974                 {
975                         char channel_list_name[33];
976                         char sztopic[200];
977                         SDL_strlcpy(sztopic, GetWordNum(3,szRemLine), SDL_arraysize(sztopic));
978                         SDL_strlcpy(channel_list_name, GetWordNum(1,szRemLine), SDL_arraysize(channel_list_name));
979                         AddChannel(channel_list_name,(short)atoi(GetWordNum(2,szRemLine)),sztopic);
980                 }
981                 return NULL;
982         }
983         if(SDL_strcasecmp(szCmd,"323")==0)
984         {
985                 //end of channel list
986                 GettingChannelList = 2;
987                 return NULL;
988         }
989         if(SDL_strcasecmp(szCmd,"324")==0)
990         {
991                 //Channel Mode info
992                 return NULL;
993         }
994
995         if(SDL_strcasecmp(szCmd,"332")==0)
996                 {
997                 //Channel Topic, update status bar.
998                 if(SDL_strcasecmp(szChat_channel,szTarget)==0)
999                 {
1000                         //strncpy(szChanTopic,GetWordNum(2,szRemLine),70);
1001                 }
1002                 //sprintf(NewMsg.Message,"*** %s has changed the topic to: %s",szNick,GetWordNum(2,szRemLine));
1003
1004                 return NULL;
1005         }
1006         if(SDL_strcasecmp(szCmd,NOX("TOPIC"))==0)
1007         {
1008                 //Channel Topic, update status bar.
1009                 if(SDL_strcasecmp(szChat_channel,szTarget)==0)
1010                 {
1011                         //strncpy(szChanTopic,GetWordNum(1,szRemLine),70);
1012                 }
1013                 //sprintf(NewMsg.Message,"*** %s has changed the topic to: %s",szNick,GetWordNum(1,szRemLine));
1014                 return NULL;
1015         }
1016         if(SDL_strcasecmp(szCmd,NOX("QUIT"))==0)
1017         {
1018                 //Remove the user!
1019                 RemoveChatUser(szNick);
1020                 return NULL;
1021         }
1022         if(SDL_strcasecmp(szCmd,"376")==0) //end of motd, trigger autojoin...
1023         {
1024                 if (!Chat_server_connected)
1025                 {
1026                         Chat_server_connected=1;
1027                 }
1028
1029                 // end of motd
1030                 SDL_strlcpy(szResponse, PXO_CHAT_END_OF_MOTD_PREFIX, SDL_arraysize(szResponse));
1031                 return szResponse;
1032         }
1033         if((SDL_strcasecmp(szCmd,"377")==0)||
1034                 (SDL_strcasecmp(szCmd,"372")==0)||
1035                 (SDL_strcasecmp(szCmd,"372")==0)
1036                 
1037                 )
1038         {
1039                 //Stip the message, and display it.
1040                 pszTempStr=GetWordNum(3,Line);          
1041                 SDL_strlcpy(szResponse, PXO_CHAT_MOTD_PREFIX, SDL_arraysize(szResponse));
1042                 SDL_strlcat(szResponse, pszTempStr, SDL_arraysize(szResponse));
1043                 return szResponse;
1044         }
1045         //Ignore these messages
1046         if(((SDL_strcasecmp(szCmd,"366")==0))||
1047                 (SDL_strcasecmp(szCmd,"333")==0) || //Who set the topic
1048                  (SDL_strcasecmp(szCmd,"329")==0))    //Time Channel created
1049                  /*
1050                  (SDL_strcasecmp(szCmd,"305")==0) ||
1051                  (SDL_strcasecmp(szCmd,"306")==0) ||
1052                  (SDL_strcasecmp(szCmd,"311")==0) || //WHOIS stuff
1053                  (SDL_strcasecmp(szCmd,"312")==0) ||
1054                  (SDL_strcasecmp(szCmd,"313")==0) ||
1055                  (SDL_strcasecmp(szCmd,"317")==0) ||
1056                  (SDL_strcasecmp(szCmd,"318")==0) ||
1057                  (SDL_strcasecmp(szCmd,"319")==0) ||
1058                  */
1059
1060         {
1061                 return NULL;
1062         }
1063         if(SDL_strcasecmp(szCmd,"353")==0)
1064         {
1065
1066                 //Names in the channel.
1067                 pszTempStr = GetWordNum(3,Line+iPrefixLen+strlen(szCmd)+2);
1068                 SDL_strlcpy(szRemLine, pszTempStr, SDL_arraysize(szRemLine));
1069                 pszTempStr = strtok(szRemLine," ");
1070
1071                 while(pszTempStr)
1072                 {
1073                         if(pszTempStr[0]=='@')
1074                         {
1075                                 AddChatUser(pszTempStr+1);
1076                         }
1077                         else if(pszTempStr[0]=='+')
1078                         {
1079                                 AddChatUser(pszTempStr+1);
1080                         }
1081                         else
1082                         {
1083                                 AddChatUser(pszTempStr);
1084                         }
1085                         pszTempStr=strtok(NULL," ");
1086                 }
1087                 return NULL;
1088         }
1089         //MOTD Codes
1090         if((SDL_strcasecmp(szCmd,"001")==0)||
1091            (SDL_strcasecmp(szCmd,"002")==0)||
1092            (SDL_strcasecmp(szCmd,"003")==0)||
1093            (SDL_strcasecmp(szCmd,"004")==0)||
1094            (SDL_strcasecmp(szCmd,"251")==0)||
1095            (SDL_strcasecmp(szCmd,"254")==0)||
1096            (SDL_strcasecmp(szCmd,"255")==0)||
1097            (SDL_strcasecmp(szCmd,"265")==0)||
1098            (SDL_strcasecmp(szCmd,"375")==0)||
1099            (SDL_strcasecmp(szCmd,"372")==0)||
1100            (SDL_strcasecmp(szCmd,"375")==0)
1101            )
1102         {
1103                 // Stip the message, and display it.
1104                 // pszTempStr = GetWordNum(3, Line);
1105                 // strcpy(szResponse, PXO_CHAT_MOTD_PREFIX);
1106                 // strcat(szResponse, pszTempStr);
1107                 return NULL;
1108                 // return szResponse;
1109         }
1110         if(SDL_strcasecmp(szCmd,"432")==0)
1111         {
1112                 //Channel Mode info
1113                 SDL_strlcpy(szResponse, XSTR("Your nickname contains invalid characters",640), SDL_arraysize(szResponse));
1114                 AddChatCommandToQueue(CC_DISCONNECTED,NULL,0);
1115                 return szResponse;
1116         }
1117         if(SDL_strcasecmp(szCmd,"433")==0)
1118         {
1119                 //Channel Mode info
1120                 char new_nick[33];
1121                 SDL_snprintf(new_nick, SDL_arraysize(new_nick), "%s%d", Original_nick_name, Nick_variety);
1122                 SDL_strlcpy(Nick_name, new_nick, SDL_arraysize(Nick_name));
1123                 Nick_variety++;
1124                 SDL_snprintf(szResponse, SDL_arraysize(szResponse), NOX("/NICK %s"), new_nick);
1125                 SendChatString(szResponse,1);
1126                 return NULL;
1127         }
1128         //Default print
1129         SDL_strlcpy(szResponse, Line, SDL_arraysize(szResponse));
1130         //return szResponse;
1131         return NULL;
1132
1133 }
1134
1135
1136 void AddChatCommandToQueue(int command,const void *data,int len)
1137 {
1138         Currcommand = Firstcommand;
1139         if(Firstcommand==NULL)
1140         {
1141                 Firstcommand = (Chat_command *)malloc(sizeof(Chat_command));
1142                 SDL_assert(Firstcommand);
1143                 Firstcommand->next = NULL;
1144                 Currcommand = Firstcommand;
1145         }
1146         else
1147         {
1148                 while(Currcommand->next) 
1149                 {
1150                         Currcommand = Currcommand->next;
1151                 }
1152                 Currcommand->next = (Chat_command *)malloc(sizeof(Chat_command));
1153                 SDL_assert(Currcommand->next);
1154                 Currcommand = Currcommand->next;
1155         }
1156         Currcommand->command = (short)command;
1157         if(len&&data) memcpy(&Currcommand->data,data,len);
1158         Currcommand->next = NULL;
1159         return;
1160 }
1161
1162 Chat_command *GetChatCommandFromQueue(void)
1163 {
1164         static Chat_command response_cmd;
1165         Chat_command *tmp_cmd;
1166         if(!Firstcommand) return NULL;
1167         Currcommand = Firstcommand;
1168         memcpy(&response_cmd,Currcommand,sizeof(Chat_command));
1169         tmp_cmd = Currcommand->next;
1170         free(Firstcommand);
1171         Firstcommand = tmp_cmd;
1172         return &response_cmd;
1173 }
1174
1175 void FlushChatCommandQueue(void)
1176 {
1177         Chat_command *tmp_cmd;
1178         Currcommand = Firstcommand;
1179         
1180         while(Currcommand) 
1181         {
1182                 tmp_cmd = Currcommand->next;
1183                 free(Currcommand);
1184                 Currcommand = tmp_cmd;
1185         }
1186         Firstcommand = NULL;
1187 }
1188
1189
1190 void FlushChannelList(void)
1191 {
1192         Chat_channel *tmp_chan;
1193         Currchannel = Firstchannel;
1194         
1195         while(Currchannel) 
1196         {
1197                 tmp_chan = Currchannel->next;
1198                 free(Currchannel);
1199                 Currchannel = tmp_chan;
1200         }
1201         Firstchannel = NULL;
1202
1203
1204 }
1205 char *GetChannelList(void)
1206 {
1207         int ichan_list_length = 0;
1208         char sznumusers[10];
1209         
1210         if(GettingChannelList != 2) return NULL;
1211         if(!Socket_connected) return NULL;
1212
1213         if(Chan_list)
1214         {
1215                 free(Chan_list);
1216                 Chan_list = NULL;
1217         }
1218         
1219         
1220         Currchannel = Firstchannel;
1221         while(Currchannel) 
1222         {
1223                 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
1224                 Currchannel = Currchannel->next;
1225         }
1226         Currchannel = Firstchannel;
1227         Chan_list = (char *)malloc(ichan_list_length+1);
1228         Chan_list[0] = '\0';
1229         while(Currchannel) 
1230         {
1231                 SDL_strlcat(Chan_list, "$", ichan_list_length+1);
1232                 SDL_strlcat(Chan_list, Currchannel->channel_name, ichan_list_length+1);
1233                 SDL_strlcat(Chan_list, " ", ichan_list_length+1);
1234                 SDL_snprintf(sznumusers, SDL_arraysize(sznumusers), "%d ", Currchannel->users);
1235                 SDL_strlcat(Chan_list, sznumusers, ichan_list_length+1);
1236                 SDL_strlcat(Chan_list, Currchannel->topic, ichan_list_length+1);//fgets
1237                 SDL_strlcat(Chan_list, " ", ichan_list_length+1);
1238                 Currchannel = Currchannel->next;
1239         }
1240         FlushChannelList();
1241         GettingChannelList = 0;
1242         return Chan_list;
1243 }
1244
1245 void AddChannel(char *channel,unsigned short numusers,char *topic)
1246 {
1247         Currchannel = Firstchannel;
1248         if(Firstchannel==NULL)
1249         {
1250                 Firstchannel = (Chat_channel *)malloc(sizeof(Chat_channel));
1251                 SDL_assert(Firstchannel);
1252                 SDL_strlcpy(Firstchannel->channel_name, channel, SDL_arraysize(Firstchannel->channel_name));
1253                 SDL_strlcpy(Firstchannel->topic, topic, SDL_arraysize(Firstchannel->topic));
1254                 Firstchannel->users = numusers;
1255                 Firstchannel->next = NULL;
1256                 Currchannel = Firstchannel;
1257         }
1258         else
1259         {
1260                 while(Currchannel->next) 
1261                 {
1262                         Currchannel = Currchannel->next;
1263                 }
1264                 Currchannel->next = (Chat_channel *)malloc(sizeof(Chat_channel));
1265                 SDL_assert(Currchannel->next);
1266                 Currchannel = Currchannel->next;
1267                 SDL_strlcpy(Currchannel->channel_name, channel, SDL_arraysize(Currchannel->channel_name));
1268                 SDL_strlcpy(Currchannel->topic, topic, SDL_arraysize(Currchannel->topic));
1269                 Currchannel->users = numusers;
1270         }
1271         Currchannel->next = NULL;
1272         return;
1273 }
1274
1275
1276 char *GetTrackerIdByUser(char *nickname)
1277 {
1278         char szWhoisCmd[100];
1279
1280         
1281         if(GettingUserTID)
1282         {
1283                 if(Getting_user_tracker_error)
1284                 {
1285                         Getting_user_tracker_error = 0;
1286                         GettingUserTID = 0;
1287                         return (char *)-1;
1288                 }
1289                 
1290                 if(*User_req_tracker_id)
1291                 {
1292                         GettingUserTID = 0;
1293                         return User_req_tracker_id;
1294                 }
1295         }
1296         else
1297         {
1298                 SDL_strlcpy(Getting_user_tracker_info_for, nickname, SDL_arraysize(Getting_user_tracker_info_for));
1299                 SDL_snprintf(szWhoisCmd, SDL_arraysize(szWhoisCmd), NOX("/WHOIS %s"), nickname);
1300                 User_req_tracker_id[0] = '\0';
1301                 SendChatString(szWhoisCmd,1);           
1302                 GettingUserTID = 1;
1303         }
1304         return NULL;
1305 }
1306
1307 char *GetChannelByUser(char *nickname)
1308 {
1309         char szWhoisCmd[100];
1310         
1311         if(GettingUserChannel)
1312         {
1313                 if(Getting_user_channel_error)
1314                 {
1315                         Getting_user_channel_error = 0;
1316                         GettingUserChannel = 0;
1317                         return (char *)-1;
1318                 }
1319                 if(*User_req_channel)
1320                 {
1321                         GettingUserChannel = 0;
1322                         return User_req_channel;
1323                 }
1324         }
1325         else
1326         {
1327                 SDL_strlcpy(Getting_user_channel_info_for, nickname, SDL_arraysize(Getting_user_channel_info_for));
1328                 User_req_channel[0] = '\0';
1329                 SDL_snprintf(szWhoisCmd, SDL_arraysize(szWhoisCmd), NOX("/WHOIS %s"), nickname);
1330                 SendChatString(szWhoisCmd,1);
1331                 GettingUserChannel = 1;
1332         }
1333         return NULL;
1334 }
1335