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