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