[HEADERS]
[reactos.git] / irc / ArchBlackmann / IRCClient.cpp
1 // IRCClient.cpp
2 // This file is (C) 2004 Royce Mitchell III
3 // and released under the BSD & LGPL licenses
4
5 #ifdef _MSC_VER
6 #pragma warning ( disable : 4786 )
7 #endif//_MSC_VER
8
9 #include <vector>
10 #include <sstream>
11
12 #include "IRCClient.h"
13 #include "MD5.h"
14 #include "cram_md5.h"
15 #include "trim.h"
16 #include "chomp.h"
17 #include "SplitJoin.h"
18 #include "base64.h"
19 #include "config.h"
20
21 using std::string;
22 using std::stringstream;
23 using std::vector;
24
25 bool IRCClient::_debug = true;
26
27 // see rfc1459 for IRC-Protocoll Reference
28
29 IRCClient::IRCClient()
30 : _timeout(10*60*1000), _inRun(false)
31 {
32 }
33
34 bool IRCClient::Connect ( const string& server, short port )
35 {
36 string buf;
37 Close();
38 Attach ( suTcpSocket() );
39 if ( !suConnect ( *this, server.c_str(), port ) )
40 return false;
41 return true;
42 }
43
44 bool
45 IRCClient::User ( const string& user, const string& mode,
46 const string& network, const string& realname )
47 {
48 string buf;
49 buf = "USER " + user + " \"" + mode + "\" \"" + network + "\" :" + realname + "\n";
50 return Send ( buf );
51 }
52
53 bool
54 IRCClient::Nick ( const string& nick )
55 {
56 _nick = nick;
57 Send ( "NICK " + _nick + "\n" );
58 PrivMsg ("NickServ", "IDENTIFY " + (string)PASS);
59 return true;
60 }
61
62 bool
63 IRCClient::Mode ( const string& mode )
64 {
65 return Send ( "MODE " + _nick + " " + mode + "\n" );
66 }
67
68 bool
69 IRCClient::Names ( const string& channel )
70 {
71 return Send ( "NAMES " + channel + "\n" );
72 }
73
74 bool
75 IRCClient::Mode ( const string& channel, const string& mode, const string& target )
76 {
77 return Send ( "MODE " + channel + " " + mode + " " + target + "\n" );
78 }
79
80 bool
81 IRCClient::Join ( const string& channel )
82 {
83 return Send("JOIN " + channel + "\n");
84 }
85
86 bool
87 IRCClient::PrivMsg ( const string& to, const string& text )
88 {
89 return Send ( "PRIVMSG " + to + " :" + text + '\n' );
90 }
91
92 bool
93 IRCClient::Action ( const string& to, const string& text )
94 {
95 return Send ( "PRIVMSG " + to + " :" + (char)1 + "ACTION " + text + (char)1 + '\n' );
96 }
97
98 bool
99 IRCClient::Part ( const string& channel, const string& text )
100 {
101 return Send ( "PART " + channel + " :" + text + "\n" );
102 }
103
104 bool
105 IRCClient::Quit ( const string& text )
106 {
107 return Send( "QUIT :" + text + "\n");
108 }
109
110 bool IRCClient::_Recv ( string& buf )
111 {
112 bool b = (recvUntil ( buf, '\n', _timeout ) > 0);
113 if ( b && _debug )
114 {
115 printf ( ">> %s", buf.c_str() );
116 if ( buf[buf.length()-1] != '\n' )
117 printf ( "\n" );
118 }
119 return b;
120 }
121
122 bool IRCClient::Send ( const string& buf )
123 {
124 if ( _debug )
125 {
126 printf ( "<< %s", buf.c_str() );
127 if ( buf[buf.length()-1] != '\n' )
128 printf ( "\n" );
129 }
130 return ( buf.length() == (size_t)send ( *this, buf.c_str(), buf.length(), 0 ) );
131 }
132
133 bool IRCClient::OnPing( const string& text )
134 {
135 return Send( "PONG " + text + "\n" );
136 }
137
138
139 int THREADAPI IRCClient::Callback ( IRCClient* irc )
140 {
141 return irc->Run ( false );
142 }
143
144 int IRCClient::Run ( bool launch_thread )
145 {
146 if ( (SOCKET)*this == INVALID_SOCKET )
147 return 0;
148 if ( _inRun ) return 1;
149 if ( launch_thread )
150 {
151 ThreadPool::Instance().Launch ( (ThreadPoolFunc*)IRCClient::Callback, this );
152 return 1;
153 }
154 _inRun = true;
155 if ( _debug ) printf ( "IRCClient::Run() - waiting for responses\n" );
156 string buf;
157 while ( _Recv(buf) )
158 {
159 if ( !strnicmp ( buf.c_str(), "NOTICE ", 7 ) )
160 {
161 //printf ( "recv'd NOTICE msg...\n" );
162 // TODO...
163 //OnAuth (
164 }
165 else if ( !strnicmp ( buf.c_str(), "PING ", 5 ) )
166 {
167 const char* p = &buf[5]; // point to first char after "PING "
168 while ( *p == ':' ) // then read past the colons
169 p++;
170 const char* p2 = strpbrk ( p, "\r\n" ); // find the end of line
171 string text ( p, p2-p ); // and set the text
172 OnPing( text );
173 }
174 else if ( buf[0] == ':' )
175 {
176 const char* p = &buf[1]; // skip first colon...
177 const char* p2 = strpbrk ( p, " !" );
178 if ( !p2 )
179 {
180 printf ( "!!!:OnRecv failure 0: ", buf.c_str() );
181 continue;
182 }
183 string src ( p, p2-p );
184 if ( !src.length() )
185 {
186 printf ( "!!!:OnRecv failure 0.5: %s", buf.c_str() );
187 continue;
188 }
189 p = p2 + 1;
190 if ( *p2 == '!' )
191 {
192 p2 = strchr ( p, ' ' );
193 if ( !p2 )
194 {
195 printf ( "!!!:OnRecv failure 1: %s", buf.c_str() );
196 continue;
197 }
198 //string srchost ( p, p2-p );
199 p = p2 + 1;
200 }
201 p2 = strchr ( p, ' ' );
202 if ( !p2 )
203 {
204 printf ( "!!!:OnRecv failure 2: %s", buf.c_str() );
205 continue;
206 }
207 string cmd ( p, p2-p );
208 p = p2 + 1;
209 p2 = strpbrk ( p, " :" );
210 if ( !p2 )
211 {
212 printf ( "!!!:OnRecv failure 3: %s", buf.c_str() );
213 continue;
214 }
215 string tgt ( p, p2-p );
216 p = p2 + 1;
217 p += strspn ( p, " " );
218 if ( *p == '=' )
219 {
220 p++;
221 p += strspn ( p, " " );
222 }
223 if ( *p == ':' )
224 p++;
225 p2 = strpbrk ( p, "\r\n" );
226 if ( !p2 )
227 {
228 printf ( "!!!:OnRecv failure 4: %s", buf.c_str() );
229 continue;
230 }
231 string text ( p, p2-p );
232 strlwr ( &cmd[0] );
233 if ( cmd == "privmsg" )
234 {
235 if ( !tgt.length() )
236 {
237 printf ( "!!!:OnRecv failure 5 (PRIVMSG w/o target): %s", buf.c_str() );
238 continue;
239 }
240 if ( *p == 1 )
241 {
242 p++;
243 p2 = strchr ( p, ' ' );
244 if ( !p2 ) p2 = p + strlen(p);
245 cmd = string ( p, p2-p );
246 strlwr ( &cmd[0] );
247 p = p2 + 1;
248 p2 = strchr ( p, 1 );
249 if ( !p2 )
250 {
251 printf ( "!!!:OnRecv failure 6 (no terminating \x01 for initial \x01 found: %s", buf.c_str() );
252 continue;
253 }
254 text = string ( p, p2-p );
255 if ( cmd == "action" )
256 {
257 if ( tgt[0] == '#' )
258 OnChannelAction ( tgt, src, text );
259 else
260 OnPrivAction ( src, text );
261 }
262 else
263 {
264 printf ( "!!!:OnRecv failure 7 (unrecognized \x01 command '%s': %s", cmd.c_str(), buf.c_str() );
265 continue;
266 }
267 }
268 else
269 {
270 if ( tgt[0] == '#' )
271 OnChannelMsg ( tgt, src, text );
272 else
273 OnPrivMsg ( src, text );
274 }
275 }
276 else if ( cmd == "mode" )
277 {
278 // two diff. kinds of mode notifications...
279 //printf ( "[MODE] src='%s' cmd='%s' tgt='%s' text='%s'", src.c_str(), cmd.c_str(), tgt.c_str(), text.c_str() );
280 //OnMode (
281 // self mode change:
282 // [MODE] src=Nick cmd=mode tgt=Nick text=+i
283 // channel mode change:
284 // [MODE] src=Nick cmd=mode tgt=#Channel text=+o Nick
285 if ( tgt[0] == '#' )
286 {
287 p = text.c_str();
288 p2 = strchr ( p, ' ' );
289 if ( p2 && *p2 )
290 {
291 string mode ( p, p2-p );
292 p = p2 + 1;
293 p += strspn ( p, " " );
294 OnUserModeInChannel ( src, tgt, mode, trim(p) );
295 }
296 else
297 OnChannelMode ( tgt, text );
298 }
299 else
300 OnMode ( tgt, text );
301 }
302 else if ( cmd == "join" )
303 {
304 mychannel = text;
305 OnJoin ( src, text );
306 }
307 else if ( cmd == "part" )
308 {
309 OnPart ( src, text );
310 }
311 else if ( cmd == "nick" )
312 {
313 OnNick ( src, text );
314 }
315 else if ( cmd == "kick" )
316 {
317 OnKick ();
318 }
319 else if ( isdigit(cmd[0]) )
320 {
321 int i = atoi(cmd.c_str());
322 switch ( i )
323 {
324
325 case 1: // "Welcome!" - i.e. it's okay to issue commands now...
326 OnConnected();
327 break;
328
329 case 353: // user list for channel....
330 {
331 p = text.c_str();
332 p2 = strpbrk ( p, " :" );
333 if ( !p2 ) continue;
334 string channel ( p, p2-p );
335 p = strchr ( p2, ':' );
336 if ( !p ) continue;
337 p++;
338 vector<string> users;
339 while ( *p )
340 {
341 p2 = strchr ( p, ' ' );
342 if ( !p2 )
343 p2 = p + strlen(p);
344 users.push_back ( string ( p, p2-p ) );
345 p = p2+1;
346 p += strspn ( p, " " );
347 }
348 OnChannelUsers ( channel, users );
349 }
350 break;
351
352 case 366: // END of user list for channel
353 {
354 p = text.c_str();
355 p2 = strpbrk ( p, " :" );
356 if ( !p2 ) continue;
357 string channel ( p, p2-p );
358 OnEndChannelUsers ( channel );
359 }
360 break;
361
362 case 474: // You are banned
363 {
364 p = text.c_str();
365 p2 = strpbrk ( p, " :" );
366 if ( !p2 ) continue;
367 string channel ( p, p2-p );
368 OnBanned ( channel );
369 }
370 break;
371
372 case 433: // Nick in Use
373 {
374 string nick = _nick;
375 Nick (nick + "_");
376
377 PrivMsg ("NickServ", "GHOST " + nick + " " + PASS);
378
379 // HACK HACK HACK
380 Mode ( "+i" );
381 Join ( CHANNEL ); // this is because IRC client does not review if his commands were sucessfull
382
383 Sleep ( 1000 );
384 Nick ( nick );
385 }
386 break;
387
388 case 2: //MOTD
389 case 376: //MOTD
390 case 372:
391 break;
392
393 default:
394 if ( _debug ) printf ( "unknown command %i: %s", i, buf.c_str() );
395 break;
396 }
397 }
398 else
399 {
400 if ( strstr ( buf.c_str(), "ACTION" ) )
401 {
402 printf ( "ACTION: " );
403 for ( int i = 0; i < buf.size(); i++ )
404 printf ( "%c(%xh)", buf[i], (unsigned)(unsigned char)buf[i] );
405 }
406 else if ( _debug ) printf ( "unrecognized ':' response: %s", buf.c_str() );
407 }
408 }
409 else
410 {
411 if ( _debug ) printf ( "unrecognized irc msg: %s", buf.c_str() );
412 }
413 //OnRecv ( buf );
414 }
415 if ( _debug ) printf ( "IRCClient::Run() - exiting\n" );
416 _inRun = false;
417 return 0;
418 }