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)
8 * 01-17-2005 arty -- initial
23 using std::ostringstream
;
25 const char *root_entries
[] = {
29 "HKEY_CURRENT_CONFIG",
34 const HKEY root_handles
[] = {
42 class RequestHandler
{
44 RequestHandler( SOCKET s
) : socket( s
), state( NO_REQUEST_YET
) {}
45 ~RequestHandler() { closesocket( socket
); }
46 void RecvData( string 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; }
55 if( full_input
[space_pos
] != '/' ) { state
= SHOULD_DIE
; return; }
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
;
67 int rv
= send( socket
,
68 remaining_output
.c_str(),
69 remaining_output
.size(), 0 );
75 remaining_output
.substr( rv
, remaining_output
.size() - rv
);
76 if( remaining_output
.size() == 0 ) {
82 SOCKET
GetSocket() const { return socket
; }
84 bool ShouldDie() const {
85 return state
== SHOULD_DIE
;
88 bool WantPollout() const {
89 return state
== REQUEST_RECVD_SENDING_REPLY
;
94 string
urlenc( string in
) {
97 for( string::iterator i
= in
.begin();
100 if( isalnum( *i
) || *i
== '/' )
104 sprintf( minibuf
, "%02x", *i
);
105 out
<< "%" << minibuf
;
112 string
urldec( string in
) {
115 for( string::iterator i
= in
.begin();
123 if( i
!= in
.end() ) {
126 if( i
!= in
.end() ) {
129 sscanf( buf
, "%x", &res
);
130 fprintf( stderr
, "Interpreting %c%c as %02x\n",
142 string
dump_one_line( const char *data
, int llen
, int len
, int addr
) {
146 out
<< setw( 8 ) << setfill( '0' ) << hex
<< addr
<< ": ";
148 for( i
= 0; i
< llen
; i
++ ) {
149 if( i
< len
) out
<< setw( 2 ) << setfill( '0' ) << hex
<<
150 (data
[i
] & 0xff) << " ";
156 for( i
= 0; i
< llen
; i
++ ) {
157 if( i
< len
&& i
< llen
&&
158 data
[i
] >= ' ' && data
[i
] < 0x7f ) out
<< data
[i
]; else out
<< '.';
166 string
bindump( const char *data
, int len
) {
167 const char *end
= data
+ len
;
173 while( data
< end
) {
174 out
+= dump_one_line( data
, 16, end
- data
, addr
);
184 string
present_value( DWORD type
, const char *data
, DWORD len
) {
187 return bindump( data
, len
);
191 void process_valid_request( HKEY open_reg_key
, string key_name
) {
194 ostringstream text_out
;
197 DWORD max_subkey_len
;
199 DWORD max_value_name_len
;
202 char *value_name_buf
;
206 if( RegQueryInfoKey( open_reg_key
,
217 NULL
) != ERROR_SUCCESS
) {
218 process_invalid_request( key_name
);
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];
226 ending_slash
= key_name
.rfind( '/' );
227 if( ending_slash
!= string::npos
)
228 up_level
= key_name
.substr( 0, ending_slash
);
230 text_out
<< "HTTP/1.0 200 OK\r\n"
231 << "Content-Type: text/html\r\n"
233 << "<html><head><title>Registry Key `"
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";
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
,
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";
260 text_out
<< "</table><h2>Values:</h2><table border='1'>\r\n";
262 DWORD value_name_size
;
263 DWORD value_data_size
;
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;
270 RegEnumValue( open_reg_key
,
279 text_out
<< "<tr><td><b>" << string(value_name_buf
,value_name_size
)
281 << present_value( value_type
, value_buf
, value_data_size
)
285 text_out
<< "</ul></body></html>\r\n";
287 delete [] key_name_buf
;
288 delete [] value_name_buf
;
291 remaining_output
= text_out
.str();
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"
299 << "<html><head><title>Can't find registry key `"
301 << "'</title></head><body>\r\n"
302 << "<H1>Can't find registry key `"
305 << "The registry key doesn't exist in the local registry.\r\n"
306 << "</body></html>\r\n";
308 remaining_output
= text_out
.str();
311 void process_root_request() {
312 ostringstream text_out
;
315 text_out
<< "HTTP/1.0 200 OK\r\n"
316 << "Content-Type: text/html\r\n"
318 << "<html><head><title>Registry Browser</title></head>\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";
327 for( i
= 0; root_entries
[i
]; i
++ )
329 << "<a href='/" << urlenc(root_entries
[i
])
330 << "'>" << root_entries
[i
]
333 text_out
<< "</ul></body></html>\r\n";
335 remaining_output
= text_out
.str();
338 void process_request( string reg_key
) {
340 bool is_predefined_key
= true;
342 if( reg_key
== "" ) { process_root_request(); return; }
345 // Parse the key name...
346 size_t slash
= reg_key
.find( '/' );
347 string reg_initial
= "";
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
);
354 fprintf( stderr
, "reg_init = %s, reg_key = %s\n",
358 for( i
= 0; root_entries
[i
]; i
++ )
359 if( reg_initial
== root_entries
[i
] ) hRegKey
= root_handles
[i
];
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
);
366 string reg_open_path
= reg_path
;
368 slash
= reg_open_path
.find( '/' );
369 string reg_single_key
= reg_open_path
;
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() );
377 HKEY oldKey
= hRegKey
;
379 fprintf( stderr
, "Opening %s\n", reg_single_key
.c_str() );
381 if( RegOpenKey( hRegKey
, reg_single_key
.c_str(), &hRegKey
) !=
385 } else RegCloseKey( oldKey
);
387 is_predefined_key
= false;
388 } while( slash
!= string::npos
);
391 if( hRegKey
== 0 ) process_invalid_request( reg_key
);
393 process_valid_request( hRegKey
, reg_key
);
394 if( !is_predefined_key
) RegCloseKey( hRegKey
);
398 typedef enum _RHState
{
400 REQUEST_RECVD_SENDING_REPLY
,
405 string remaining_output
;
410 SOCKET
make_listening_socket( int port
) {
411 struct sockaddr_in sa
;
413 ZeroMemory( &sa
, sizeof( sa
) );
415 sa
.sin_family
= PF_INET
;
416 sa
.sin_port
= ntohs( port
);
418 fprintf( stderr
, "Creating the listener\n" );
419 SOCKET l
= socket( PF_INET
, SOCK_STREAM
, 0 );
420 fprintf( stderr
, "Socket %x\n", l
);
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
;
427 if( listen( l
, 5 ) < 0 ) {
428 fprintf( stderr
, "Listening: %d\n", WSAGetLastError() );
430 return INVALID_SOCKET
;
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
;
442 int port_to_listen
= 80;
443 unsigned int active_fds
= 0;
445 for( i
= 1; i
< argc
; i
++ ) {
446 if( string( "-p" ) == argv
[i
] ) {
448 if( i
< argc
) port_to_listen
= atoi( argv
[i
] );
452 WSAStartup( 0x0101, &wdWinsockData
);
454 listen_socket
= make_listening_socket( port_to_listen
);
455 if( listen_socket
== INVALID_SOCKET
) return 1;
461 active_fds
= listen_socket
+ 1;
463 for( std::map
<SOCKET
,RequestHandler
*>::iterator i
= requests
.begin();
466 if( i
->second
->ShouldDie() ) {
469 i
= requests
.begin();
473 FD_SET(i
->first
,&pollin
);
474 FD_SET(i
->first
,&pollerr
);
476 if( i
->first
> active_fds
) active_fds
= i
->first
+ 1;
478 if( i
->second
->WantPollout() ) FD_SET(i
->first
,&pollout
);
481 FD_SET(listen_socket
,&pollin
);
483 active_fds
= select( active_fds
, &pollin
, &pollout
, &pollerr
, NULL
);
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
) ) );
493 for( std::map
<SOCKET
,RequestHandler
*>::iterator i
= requests
.begin();
496 if( FD_ISSET(i
->first
,&pollin
) ) {
498 int rv
= recv(i
->first
,inbuf
,1024,0);
502 i
= requests
.begin();
504 } else i
->second
->RecvData( string( inbuf
, rv
) );
506 if( FD_ISSET(i
->first
,&pollout
) ) {
507 i
->second
->OkToSend();
509 if( FD_ISSET(i
->first
,&pollerr
) ) {
512 i
= requests
.begin();