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