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
24 using std::ostringstream
;
26 const char *root_entries
[] = {
30 "HKEY_CURRENT_CONFIG",
35 const HKEY root_handles
[] = {
43 class RequestHandler
{
45 RequestHandler( SOCKET s
) : socket( s
), state( NO_REQUEST_YET
) {}
46 ~RequestHandler() { closesocket( socket
); }
47 void RecvData( string 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; }
56 if( full_input
[space_pos
] != '/' ) { state
= SHOULD_DIE
; return; }
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
;
68 int rv
= send( socket
,
69 remaining_output
.c_str(),
70 remaining_output
.size(), 0 );
76 remaining_output
.substr( rv
, remaining_output
.size() - rv
);
77 if( remaining_output
.size() == 0 ) {
83 SOCKET
GetSocket() const { return socket
; }
85 bool ShouldDie() const {
86 return state
== SHOULD_DIE
;
89 bool WantPollout() const {
90 return state
== REQUEST_RECVD_SENDING_REPLY
;
95 string
urlenc( string in
) {
98 for( string::iterator i
= in
.begin();
101 if( isalnum( *i
) || *i
== '/' )
105 sprintf( minibuf
, "%02x", *i
);
106 out
<< "%" << minibuf
;
113 string
urldec( string in
) {
116 for( string::iterator i
= in
.begin();
124 if( i
!= in
.end() ) {
127 if( i
!= in
.end() ) {
130 sscanf( buf
, "%x", &res
);
131 fprintf( stderr
, "Interpreting %c%c as %02x\n",
143 string
dump_one_line( const char *data
, int llen
, int len
, int addr
) {
147 out
<< setw( 8 ) << setfill( '0' ) << hex
<< addr
<< ": ";
149 for( i
= 0; i
< llen
; i
++ ) {
150 if( i
< len
) out
<< setw( 2 ) << setfill( '0' ) << hex
<<
151 (data
[i
] & 0xff) << " ";
157 for( i
= 0; i
< llen
; i
++ ) {
158 if( i
< len
&& i
< llen
&&
159 data
[i
] >= ' ' && data
[i
] < 0x7f ) out
<< data
[i
]; else out
<< '.';
167 string
bindump( const char *data
, int len
) {
168 const char *end
= data
+ len
;
174 while( data
< end
) {
175 out
+= dump_one_line( data
, 16, end
- data
, addr
);
185 string
present_value( DWORD type
, const char *data
, DWORD len
) {
188 return bindump( data
, len
);
192 void process_valid_request( HKEY open_reg_key
, string key_name
) {
195 ostringstream text_out
;
198 DWORD max_subkey_len
;
200 DWORD max_value_name_len
;
203 char *value_name_buf
;
207 if( RegQueryInfoKey( open_reg_key
,
218 NULL
) != ERROR_SUCCESS
) {
219 process_invalid_request( key_name
);
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];
227 ending_slash
= key_name
.rfind( '/' );
228 if( ending_slash
!= string::npos
)
229 up_level
= key_name
.substr( 0, ending_slash
);
231 text_out
<< "HTTP/1.0 200 OK\r\n"
232 << "Content-Type: text/html\r\n"
234 << "<html><head><title>Registry Key `"
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";
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
,
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";
261 text_out
<< "</table><h2>Values:</h2><table border='1'>\r\n";
263 DWORD value_name_size
;
264 DWORD value_data_size
;
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;
271 RegEnumValue( open_reg_key
,
280 text_out
<< "<tr><td><b>" << string(value_name_buf
,value_name_size
)
282 << present_value( value_type
, value_buf
, value_data_size
)
286 text_out
<< "</ul></body></html>\r\n";
288 delete [] key_name_buf
;
289 delete [] value_name_buf
;
292 remaining_output
= text_out
.str();
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"
300 << "<html><head><title>Can't find registry key `"
302 << "'</title></head><body>\r\n"
303 << "<H1>Can't find registry key `"
306 << "The registry key doesn't exist in the local registry.\r\n"
307 << "</body></html>\r\n";
309 remaining_output
= text_out
.str();
312 void process_root_request() {
313 ostringstream text_out
;
316 text_out
<< "HTTP/1.0 200 OK\r\n"
317 << "Content-Type: text/html\r\n"
319 << "<html><head><title>Registry Browser</title></head>\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";
328 for( i
= 0; root_entries
[i
]; i
++ )
330 << "<a href='/" << urlenc(root_entries
[i
])
331 << "'>" << root_entries
[i
]
334 text_out
<< "</ul></body></html>\r\n";
336 remaining_output
= text_out
.str();
339 void process_request( string reg_key
) {
341 bool is_predefined_key
= true;
343 if( reg_key
== "" ) { process_root_request(); return; }
346 // Parse the key name...
347 size_t slash
= reg_key
.find( '/' );
348 string reg_initial
= "";
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
);
355 fprintf( stderr
, "reg_init = %s, reg_key = %s\n",
359 for( i
= 0; root_entries
[i
]; i
++ )
360 if( reg_initial
== root_entries
[i
] ) hRegKey
= root_handles
[i
];
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
);
367 string reg_open_path
= reg_path
;
369 slash
= reg_open_path
.find( '/' );
370 string reg_single_key
= reg_open_path
;
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() );
378 HKEY oldKey
= hRegKey
;
380 fprintf( stderr
, "Opening %s\n", reg_single_key
.c_str() );
382 if( RegOpenKey( hRegKey
, reg_single_key
.c_str(), &hRegKey
) !=
386 } else RegCloseKey( oldKey
);
388 is_predefined_key
= false;
389 } while( slash
!= string::npos
);
392 if( hRegKey
== 0 ) process_invalid_request( reg_key
);
394 process_valid_request( hRegKey
, reg_key
);
395 if( !is_predefined_key
) RegCloseKey( hRegKey
);
399 typedef enum _RHState
{
401 REQUEST_RECVD_SENDING_REPLY
,
406 string remaining_output
;
411 SOCKET
make_listening_socket( int port
) {
412 struct sockaddr_in sa
;
414 ZeroMemory( &sa
, sizeof( sa
) );
416 sa
.sin_family
= PF_INET
;
417 sa
.sin_port
= ntohs( port
);
419 fprintf( stderr
, "Creating the listener\n" );
420 SOCKET l
= socket( PF_INET
, SOCK_STREAM
, 0 );
421 fprintf( stderr
, "Socket %x\n", l
);
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
;
428 if( listen( l
, 5 ) < 0 ) {
429 fprintf( stderr
, "Listening: %d\n", WSAGetLastError() );
431 return INVALID_SOCKET
;
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
;
443 int port_to_listen
= 80;
444 unsigned int active_fds
= 0;
446 for( i
= 1; i
< argc
; i
++ ) {
447 if( string( "-p" ) == argv
[i
] ) {
449 if( i
< argc
) port_to_listen
= atoi( argv
[i
] );
453 WSAStartup( 0x0101, &wdWinsockData
);
455 listen_socket
= make_listening_socket( port_to_listen
);
456 if( listen_socket
== INVALID_SOCKET
) return 1;
462 active_fds
= listen_socket
+ 1;
464 for( std::map
<SOCKET
,RequestHandler
*>::iterator i
= requests
.begin();
467 if( i
->second
->ShouldDie() ) {
470 i
= requests
.begin();
474 FD_SET(i
->first
,&pollin
);
475 FD_SET(i
->first
,&pollerr
);
477 if( i
->first
> active_fds
) active_fds
= i
->first
+ 1;
479 if( i
->second
->WantPollout() ) FD_SET(i
->first
,&pollout
);
482 FD_SET(listen_socket
,&pollin
);
484 active_fds
= select( active_fds
, &pollin
, &pollout
, &pollerr
, NULL
);
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
) ) );
494 for( std::map
<SOCKET
,RequestHandler
*>::iterator i
= requests
.begin();
497 if( FD_ISSET(i
->first
,&pollin
) ) {
499 int rv
= recv(i
->first
,inbuf
,1024,0);
503 i
= requests
.begin();
505 } else i
->second
->RecvData( string( inbuf
, rv
) );
507 if( FD_ISSET(i
->first
,&pollout
) ) {
508 i
->second
->OkToSend();
510 if( FD_ISSET(i
->first
,&pollerr
) ) {
513 i
= requests
.begin();