[ROSAPPS][NETREG] Fix build
[reactos.git] / modules / rosapps / applications / net / netreg / netreg.cpp
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS test application
4 * FILE: apps/net/netreg/netreg.cpp
5 * PURPOSE: HTTP Registry Server
6 * PROGRAMMERS: Art Yerkes (arty@users.sf.net)
7 * REVISIONS:
8 * 01-17-2005 arty -- initial
9 */
10 #include <windows.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <map>
14 #include <string>
15 #include <sstream>
16 #include <iomanip>
17
18 using std::hex;
19 using std::setw;
20 using std::setfill;
21 using std::map;
22 using std::string;
23 using std::ostringstream;
24
25 const char *root_entries[] = {
26 "HKEY_LOCAL_MACHINE",
27 "HKEY_CURRENT_USER",
28 "HKEY_CLASSES_ROOT",
29 "HKEY_CURRENT_CONFIG",
30 "HKEY_USERS",
31 0
32 };
33
34 const HKEY root_handles[] = {
35 HKEY_LOCAL_MACHINE,
36 HKEY_CURRENT_USER,
37 HKEY_CLASSES_ROOT,
38 HKEY_CURRENT_CONFIG,
39 HKEY_USERS
40 };
41
42 class RequestHandler {
43 public:
44 RequestHandler( SOCKET s ) : socket( s ), state( NO_REQUEST_YET ) {}
45 ~RequestHandler() { closesocket( socket ); }
46 void RecvData( string input ) {
47 full_input += input;
48 if( full_input.find( "\r\n\r\n" ) != string::npos ) {
49 // Full request received...
50 size_t space_pos = full_input.find( ' ' );
51 if( space_pos == string::npos ) { state = SHOULD_DIE; return; }
52 string method = full_input.substr( 0, space_pos );
53 if( method != "GET" ) { state = SHOULD_DIE; return; }
54 space_pos++;
55 if( full_input[space_pos] != '/' ) { state = SHOULD_DIE; return; }
56 space_pos++;
57 string reg_key_and_remainder =
58 full_input.substr( space_pos, full_input.size() - space_pos );
59 space_pos = reg_key_and_remainder.find( ' ' );
60 if( space_pos == string::npos ) { state = SHOULD_DIE; return; }
61 string reg_key_name = reg_key_and_remainder.substr( 0, space_pos );
62 process_request( urldec( reg_key_name ) );
63 state = REQUEST_RECVD_SENDING_REPLY;
64 }
65 }
66 void OkToSend() {
67 int rv = send( socket,
68 remaining_output.c_str(),
69 remaining_output.size(), 0 );
70 if( rv < 0 ) {
71 state = SHOULD_DIE;
72 return;
73 } else {
74 remaining_output =
75 remaining_output.substr( rv, remaining_output.size() - rv );
76 if( remaining_output.size() == 0 ) {
77 state = SHOULD_DIE;
78 }
79 }
80 }
81
82 SOCKET GetSocket() const { return socket; }
83
84 bool ShouldDie() const {
85 return state == SHOULD_DIE;
86 }
87
88 bool WantPollout() const {
89 return state == REQUEST_RECVD_SENDING_REPLY;
90 }
91
92
93 private:
94 string urlenc( string in ) {
95 ostringstream out;
96
97 for( string::iterator i = in.begin();
98 i != in.end();
99 i++ ) {
100 if( isalnum( *i ) || *i == '/' )
101 out << *i;
102 else {
103 char minibuf[10];
104 sprintf( minibuf, "%02x", *i );
105 out << "%" << minibuf;
106 }
107 }
108
109 return out.str();
110 }
111
112 string urldec( string in ) {
113 string out;
114
115 for( string::iterator i = in.begin();
116 i != in.end();
117 i++ ) {
118 if( *i == '%' ) {
119 char buf[3];
120 int res = ' ';
121
122 i++;
123 if( i != in.end() ) {
124 buf[0] = *i;
125 i++;
126 if( i != in.end() ) {
127 buf[1] = *i;
128 buf[2] = 0;
129 sscanf( buf, "%x", &res );
130 fprintf( stderr, "Interpreting %c%c as %02x\n",
131 buf[0], buf[1],
132 res );
133 out += (char)res;
134 }
135 }
136 } else out += *i;
137 }
138
139 return out;
140 }
141
142 string dump_one_line( const char *data, int llen, int len, int addr ) {
143 ostringstream out;
144 int i;
145
146 out << setw( 8 ) << setfill( '0' ) << hex << addr << ": ";
147
148 for( i = 0; i < llen; i++ ) {
149 if( i < len ) out << setw( 2 ) << setfill( '0' ) << hex <<
150 (data[i] & 0xff) << " ";
151 else out << " ";
152 }
153
154 out << " : ";
155
156 for( i = 0; i < llen; i++ ) {
157 if( i < len && i < llen &&
158 data[i] >= ' ' && data[i] < 0x7f ) out << data[i]; else out << '.';
159 }
160
161 out << "\n";
162
163 return out.str();
164 }
165
166 string bindump( const char *data, int len ) {
167 const char *end = data + len;
168 string out;
169 int addr = 0;
170
171 out += "<pre>";
172
173 while( data < end ) {
174 out += dump_one_line( data, 16, end - data, addr );
175 addr += 16;
176 data += 16;
177 }
178
179 out += "</pre>";
180
181 return out;
182 }
183
184 string present_value( DWORD type, const char *data, DWORD len ) {
185 switch( type ) {
186 default:
187 return bindump( data, len );
188 }
189 }
190
191 void process_valid_request( HKEY open_reg_key, string key_name ) {
192 size_t ending_slash;
193 string up_level;
194 ostringstream text_out;
195
196 DWORD num_sub_keys;
197 DWORD max_subkey_len;
198 DWORD num_values;
199 DWORD max_value_name_len;
200 DWORD max_value_len;
201
202 char *value_name_buf;
203 char *value_buf;
204 char *key_name_buf;
205
206 if( RegQueryInfoKey( open_reg_key,
207 NULL,
208 NULL,
209 NULL,
210 &num_sub_keys,
211 &max_subkey_len,
212 NULL,
213 &num_values,
214 &max_value_name_len,
215 &max_value_len,
216 NULL,
217 NULL ) != ERROR_SUCCESS ) {
218 process_invalid_request( key_name );
219 return;
220 }
221
222 value_name_buf = new char [max_value_name_len+1];
223 value_buf = new char [max_value_len+1];
224 key_name_buf = new char [max_subkey_len+1];
225
226 ending_slash = key_name.rfind( '/' );
227 if( ending_slash != string::npos )
228 up_level = key_name.substr( 0, ending_slash );
229
230 text_out << "HTTP/1.0 200 OK\r\n"
231 << "Content-Type: text/html\r\n"
232 << "\r\n"
233 << "<html><head><title>Registry Key `"
234 << key_name
235 << "'</title></head><body>\r\n"
236 << "<h1>Registry Key `" << key_name << "'</h1>\r\n"
237 << "<a href='/" << urlenc(up_level)
238 << "'>(Up one level)</a><p>\r\n"
239 << "<h2>Subkeys:</h2><table border='1'>\r\n";
240
241 DWORD which_index;
242 DWORD key_name_size;
243
244 for( which_index = 0; which_index < num_sub_keys; which_index++ ) {
245 key_name_size = max_subkey_len+1;
246 RegEnumKeyEx( open_reg_key,
247 which_index,
248 key_name_buf,
249 &key_name_size,
250 NULL,
251 NULL,
252 NULL,
253 NULL );
254 text_out << "<tr><td><a href='/" << urlenc(key_name) << "/"
255 << urlenc(string(key_name_buf,key_name_size)) << "'>"
256 << string(key_name_buf,key_name_size)
257 << "</a></td></tr>\r\n";
258 }
259
260 text_out << "</table><h2>Values:</h2><table border='1'>\r\n";
261
262 DWORD value_name_size;
263 DWORD value_data_size;
264 DWORD value_type;
265
266 for( which_index = 0; which_index < num_values; which_index++ ) {
267 value_name_size = max_value_name_len+1;
268 value_data_size = max_value_len+1;
269
270 RegEnumValue( open_reg_key,
271 which_index,
272 value_name_buf,
273 &value_name_size,
274 NULL,
275 &value_type,
276 (BYTE *)value_buf,
277 &value_data_size );
278
279 text_out << "<tr><td><b>" << string(value_name_buf,value_name_size)
280 << "</b></td><td>"
281 << present_value( value_type, value_buf, value_data_size )
282 << "</td></tr>";
283 }
284
285 text_out << "</ul></body></html>\r\n";
286
287 delete [] key_name_buf;
288 delete [] value_name_buf;
289 delete [] value_buf;
290
291 remaining_output = text_out.str();
292 }
293
294 void process_invalid_request( string reg_key ) {
295 ostringstream text_out;
296 text_out << "HTTP/1.0 404 Not Found\r\n"
297 << "Content-Type: text/html\r\n"
298 << "\r\n"
299 << "<html><head><title>Can't find registry key `"
300 << reg_key
301 << "'</title></head><body>\r\n"
302 << "<H1>Can't find registry key `"
303 << reg_key
304 << "'</H1>\r\n"
305 << "The registry key doesn't exist in the local registry.\r\n"
306 << "</body></html>\r\n";
307
308 remaining_output = text_out.str();
309 }
310
311 void process_root_request() {
312 ostringstream text_out;
313 int i;
314
315 text_out << "HTTP/1.0 200 OK\r\n"
316 << "Content-Type: text/html\r\n"
317 << "\r\n"
318 << "<html><head><title>Registry Browser</title></head>\r\n"
319 << "<body>\r\n"
320 << "<H1>Registry Browser</H1>"
321 << "You can use this interface to browse the registry."
322 << "You will be presented with one registry key at a time and "
323 << "the decendents.\r\n"
324 << "<h2>Root Level</h2>\r\n"
325 << "Subkeys:<ul>\r\n";
326
327 for( i = 0; root_entries[i]; i++ )
328 text_out << "<li>"
329 << "<a href='/" << urlenc(root_entries[i])
330 << "'>" << root_entries[i]
331 << "</a></li>\r\n";
332
333 text_out << "</ul></body></html>\r\n";
334
335 remaining_output = text_out.str();
336 }
337
338 void process_request( string reg_key ) {
339 int i;
340 bool is_predefined_key = true;
341
342 if( reg_key == "" ) { process_root_request(); return; }
343 HKEY hRegKey = 0;
344
345 // Parse the key name...
346 size_t slash = reg_key.find( '/' );
347 string reg_initial = "";
348
349 if( slash == string::npos ) // A root key...
350 reg_initial = reg_key;
351 else // Any other key
352 reg_initial = reg_key.substr( 0, slash );
353
354 fprintf( stderr, "reg_init = %s, reg_key = %s\n",
355 reg_initial.c_str(),
356 reg_key.c_str() );
357
358 for( i = 0; root_entries[i]; i++ )
359 if( reg_initial == root_entries[i] ) hRegKey = root_handles[i];
360
361 if( hRegKey != 0 && reg_initial != reg_key ) {
362 size_t start_of_reg_path = reg_initial.size() + 1;
363 string reg_path = reg_key.substr( start_of_reg_path,
364 reg_key.size() - start_of_reg_path );
365
366 string reg_open_path = reg_path;
367 do {
368 slash = reg_open_path.find( '/' );
369 string reg_single_key = reg_open_path;
370
371 if( slash != string::npos ) {
372 reg_single_key = reg_open_path.substr( 0, slash );
373 reg_open_path = reg_open_path.substr( slash+1,
374 reg_open_path.size() );
375 }
376
377 HKEY oldKey = hRegKey;
378
379 fprintf( stderr, "Opening %s\n", reg_single_key.c_str() );
380
381 if( RegOpenKey( hRegKey, reg_single_key.c_str(), &hRegKey ) !=
382 ERROR_SUCCESS ) {
383 hRegKey = 0;
384 break;
385 } else RegCloseKey( oldKey );
386
387 is_predefined_key = false;
388 } while( slash != string::npos );
389 }
390
391 if( hRegKey == 0 ) process_invalid_request( reg_key );
392 else {
393 process_valid_request( hRegKey, reg_key );
394 if( !is_predefined_key ) RegCloseKey( hRegKey );
395 }
396 }
397
398 typedef enum _RHState {
399 NO_REQUEST_YET,
400 REQUEST_RECVD_SENDING_REPLY,
401 SHOULD_DIE
402 } RHState;
403
404 string full_input;
405 string remaining_output;
406 SOCKET socket;
407 RHState state;
408 };
409
410 SOCKET make_listening_socket( int port ) {
411 struct sockaddr_in sa;
412
413 ZeroMemory( &sa, sizeof( sa ) );
414
415 sa.sin_family = PF_INET;
416 sa.sin_port = ntohs( port );
417
418 fprintf( stderr, "Creating the listener\n" );
419 SOCKET l = socket( PF_INET, SOCK_STREAM, 0 );
420 fprintf( stderr, "Socket %x\n", l );
421
422 if( l == INVALID_SOCKET ) return l;
423 if( bind( l, (struct sockaddr *)&sa, sizeof( sa ) ) < 0 ) {
424 fprintf( stderr, "Bad response from bind: %d\n", WSAGetLastError() );
425 closesocket( l ); return INVALID_SOCKET;
426 }
427 if( listen( l, 5 ) < 0 ) {
428 fprintf( stderr, "Listening: %d\n", WSAGetLastError() );
429 closesocket( l );
430 return INVALID_SOCKET;
431 }
432
433 return l;
434 }
435
436 int main( int argc, char **argv ) {
437 WSADATA wdWinsockData;
438 map<SOCKET,RequestHandler *> requests;
439 fd_set pollin,pollout,pollerr;
440 SOCKET listen_socket;
441 int i;
442 int port_to_listen = 80;
443 unsigned int active_fds = 0;
444
445 for( i = 1; i < argc; i++ ) {
446 if( string( "-p" ) == argv[i] ) {
447 i++;
448 if( i < argc ) port_to_listen = atoi( argv[i] );
449 }
450 }
451
452 WSAStartup( 0x0101, &wdWinsockData );
453
454 listen_socket = make_listening_socket( port_to_listen );
455 if( listen_socket == INVALID_SOCKET ) return 1;
456
457 while( true ) {
458 FD_ZERO( &pollin );
459 FD_ZERO( &pollout );
460 FD_ZERO( &pollerr );
461 active_fds = listen_socket + 1;
462
463 for( std::map<SOCKET,RequestHandler *>::iterator i = requests.begin();
464 i != requests.end();
465 i++ ) {
466 if( i->second->ShouldDie() ) {
467 delete i->second;
468 requests.erase( i );
469 i = requests.begin();
470 break;
471 }
472
473 FD_SET(i->first,&pollin);
474 FD_SET(i->first,&pollerr);
475
476 if( i->first > active_fds ) active_fds = i->first + 1;
477
478 if( i->second->WantPollout() ) FD_SET(i->first,&pollout);
479 }
480
481 FD_SET(listen_socket,&pollin);
482
483 active_fds = select( active_fds, &pollin, &pollout, &pollerr, NULL );
484
485 if( active_fds > 0 ) {
486 if( FD_ISSET(listen_socket,&pollin) ) {
487 SOCKET ns = accept( listen_socket, NULL, NULL );
488 if( ns != INVALID_SOCKET ) {
489 requests.insert( std::make_pair( ns, new RequestHandler( ns ) ) );
490 }
491 }
492
493 for( std::map<SOCKET,RequestHandler *>::iterator i = requests.begin();
494 i != requests.end();
495 i++ ) {
496 if( FD_ISSET(i->first,&pollin) ) {
497 char inbuf[1024];
498 int rv = recv(i->first,inbuf,1024,0);
499 if( rv < 0 ) {
500 delete i->second;
501 requests.erase( i );
502 i = requests.begin();
503 break;
504 } else i->second->RecvData( string( inbuf, rv ) );
505 }
506 if( FD_ISSET(i->first,&pollout) ) {
507 i->second->OkToSend();
508 }
509 if( FD_ISSET(i->first,&pollerr) ) {
510 delete i->second;
511 requests.erase( i );
512 i = requests.begin();
513 break;
514 }
515 }
516 }
517 }
518
519 WSACleanup();
520 }