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 %Ix\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 }
521