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