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