ArchBlackmann irc bot - works in ReactOS - happy hacking
[reactos.git] / rosapps / games / 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::Part ( const string& channel, const string& text )
89 {
90 return Send ( "PART " + channel + " :" + text + "\n" );
91 }
92
93 bool
94 IRCClient::Quit ( const string& text )
95 {
96 return Send( "QUIT :" + text + "\n");
97 }
98
99 bool IRCClient::_Recv ( string& buf )
100 {
101 bool b = (recvUntil ( buf, '\n', _timeout ) > 0);
102 if ( b && _debug )
103 {
104 printf ( ">> %s", buf.c_str() );
105 if ( buf[buf.length()-1] != '\n' )
106 printf ( "\n" );
107 }
108 return b;
109 }
110
111 bool IRCClient::Send ( const string& buf )
112 {
113 if ( _debug )
114 {
115 printf ( "<< %s", buf.c_str() );
116 if ( buf[buf.length()-1] != '\n' )
117 printf ( "\n" );
118 }
119 return ( buf.length() == (size_t)send ( *this, buf.c_str(), buf.length(), 0 ) );
120 }
121
122 bool IRCClient::OnPing( const string& text )
123 {
124 return Send( "PONG " + text + "\n" );
125 }
126
127
128 int THREADAPI IRCClient::Callback ( IRCClient* irc )
129 {
130 return irc->Run ( false );
131 }
132
133 int IRCClient::Run ( bool launch_thread )
134 {
135 if ( (SOCKET)*this == INVALID_SOCKET )
136 return 0;
137 if ( _inRun ) return 1;
138 if ( launch_thread )
139 {
140 ThreadPool::Instance().Launch ( (ThreadPoolFunc*)IRCClient::Callback, this );
141 return 1;
142 }
143 _inRun = true;
144 if ( _debug ) printf ( "IRCClient::Run() - waiting for responses\n" );
145 string buf;
146 while ( _Recv(buf) )
147 {
148 if ( !strnicmp ( buf.c_str(), "NOTICE ", 7 ) )
149 {
150 //printf ( "recv'd NOTICE msg...\n" );
151 // TODO...
152 //OnAuth (
153 }
154 else if ( !strnicmp ( buf.c_str(), "PING ", 5 ) )
155 {
156 const char* p = &buf[5]; // point to first char after "PING "
157 while ( *p == ':' ) // then read past the colons
158 p++;
159 const char* p2 = strpbrk ( p, "\r\n" ); // find the end of line
160 string text ( p, p2-p ); // and set the text
161 OnPing( text );
162 }
163 else if ( buf[0] == ':' )
164 {
165 const char* p = &buf[1]; // skip first colon...
166 const char* p2 = strpbrk ( p, " !" );
167 if ( !p2 )
168 {
169 printf ( "!!!:OnRecv failure 0: ", buf.c_str() );
170 continue;
171 }
172 string src ( p, p2-p );
173 if ( !src.length() )
174 {
175 printf ( "!!!:OnRecv failure 0.5: %s", buf.c_str() );
176 continue;
177 }
178 p = p2 + 1;
179 if ( *p2 == '!' )
180 {
181 p2 = strchr ( p, ' ' );
182 if ( !p2 )
183 {
184 printf ( "!!!:OnRecv failure 1: %s", buf.c_str() );
185 continue;
186 }
187 //string srchost ( p, p2-p );
188 p = p2 + 1;
189 }
190 p2 = strchr ( p, ' ' );
191 if ( !p2 )
192 {
193 printf ( "!!!:OnRecv failure 2: %s", buf.c_str() );
194 continue;
195 }
196 string cmd ( p, p2-p );
197 p = p2 + 1;
198 p2 = strpbrk ( p, " :" );
199 if ( !p2 )
200 {
201 printf ( "!!!:OnRecv failure 3: %s", buf.c_str() );
202 continue;
203 }
204 string tgt ( p, p2-p );
205 p = p2 + 1;
206 p += strspn ( p, " " );
207 if ( *p == '=' )
208 {
209 p++;
210 p += strspn ( p, " " );
211 }
212 if ( *p == ':' )
213 p++;
214 p2 = strpbrk ( p, "\r\n" );
215 if ( !p2 )
216 {
217 printf ( "!!!:OnRecv failure 4: %s", buf.c_str() );
218 continue;
219 }
220 string text ( p, p2-p );
221 strlwr ( &cmd[0] );
222 if ( cmd == "privmsg" )
223 {
224 if ( !tgt.length() )
225 {
226 printf ( "!!!:OnRecv failure 5 (PRIVMSG w/o target): %s", buf.c_str() );
227 continue;
228 }
229 if ( tgt[0] == '#' )
230 OnChannelMsg ( tgt, src, text );
231 else
232 OnPrivMsg ( src, text );
233 }
234 else if ( cmd == "mode" )
235 {
236 // two diff. kinds of mode notifications...
237 //printf ( "[MODE] src='%s' cmd='%s' tgt='%s' text='%s'", src.c_str(), cmd.c_str(), tgt.c_str(), text.c_str() );
238 //OnMode (
239 // self mode change:
240 // [MODE] src=Relic3_14 cmd=mode tgt=Relic3_14 text=+i
241 // channel mode change:
242 // [MODE] src=Royce3 cmd=mode tgt=#Royce3 text=+o Relic3_14
243 if ( tgt[0] == '#' )
244 {
245 p = text.c_str();
246 p2 = strchr ( p, ' ' );
247 if ( !p2 )
248 OnChannelMode ( tgt, text );
249 else
250 {
251 string user ( p, p2-p );
252 p = p2 + 1;
253 p += strspn ( p, " " );
254 OnUserModeInChannel ( src, tgt, user, p );
255 }
256 }
257 else
258 OnMode ( tgt, text );
259 }
260 else if ( cmd == "join" )
261 {
262 OnJoin ( src, text );
263 }
264 else if ( isdigit(cmd[0]) )
265 {
266 int i = atoi(cmd.c_str());
267 switch ( i )
268 {
269 case 1: // "Welcome!" - i.e. it's okay to issue commands now...
270 OnConnected();
271 break;
272 case 353: // user list for channel....
273 {
274 p = text.c_str();
275 p2 = strpbrk ( p, " :" );
276 if ( !p2 ) continue;
277 string channel ( p, p2-p );
278 p = strchr ( p2, ':' );
279 if ( !p ) continue;
280 p++;
281 vector<string> users;
282 while ( *p )
283 {
284 p2 = strchr ( p, ' ' );
285 if ( !p2 )
286 p2 = p + strlen(p);
287 users.push_back ( string ( p, p2-p ) );
288 p = p2+1;
289 p += strspn ( p, " " );
290 }
291 OnChannelUsers ( channel, users );
292 }
293 break;
294 case 366: // END of user list for channel
295 {
296 p = text.c_str();
297 p2 = strpbrk ( p, " :" );
298 if ( !p2 ) continue;
299 string channel ( p, p2-p );
300 OnEndChannelUsers ( channel );
301 }
302 break;
303 default:
304 if ( _debug ) printf ( "unknown command %i: %s", i, buf.c_str() );
305 break;
306 }
307 }
308 else
309 {
310 if ( _debug ) printf ( "unrecognized ':' response: %s", buf.c_str() );
311 }
312 }
313 else
314 {
315 if ( _debug ) printf ( "unrecognized irc msg: %s", buf.c_str() );
316 }
317 //OnRecv ( buf );
318 }
319 if ( _debug ) printf ( "IRCClient::Run() - exiting\n" );
320 _inRun = false;
321 return 0;
322 }