// httpd.cxx -- FGFS http property manager interface / external script // and control class // // Written by Curtis Olson, started June 2001. // // Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt // // Jpeg Image Support added August 2001 // by Norman Vine - nhv@cape.com // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // $Id$ #ifdef HAVE_CONFIG_H # include #endif #include #include // atoi() atof() #include STL_STRING #include #include #include #include #include #include
#include
#include "httpd.hxx" SG_USING_STD(string); SG_USING_STD(cout); bool FGHttpd::open() { if ( is_enabled() ) { SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " << "is already in use, ignoring" ); return false; } server = new HttpdServer( port ); set_hz( 15 ); // default to processing requests @ 15Hz set_enabled( true ); return true; } bool FGHttpd::process() { netChannel::poll(); return true; } bool FGHttpd::close() { SG_LOG( SG_IO, SG_INFO, "closing FGHttpd" ); // the following delete causes a seg fault, gdb is not helpful. // delete server; set_enabled( false ); return true; } // Handle http GET requests void HttpdChannel::foundTerminator (void) { closeWhenDone (); const string s = buffer.getData(); if ( s.find( "GET " ) == 0 ) { printf("echo: %s\n", s.c_str()); string rest = s.substr(4); string request; string tmp; string::size_type pos = rest.find( " " ); if ( pos != string::npos ) { request = rest.substr( 0, pos ); } else { request = "/"; } SGPropertyNode *node = NULL; pos = request.find( "?" ); if ( pos != string::npos ) { // request to update property value string args = request.substr( pos + 1 ); request = request.substr( 0, pos ); printf("'%s' '%s'\n", request.c_str(), args.c_str()); request = urlDecode(request); // parse args looking for "value=" bool done = false; while ( ! done ) { string arg; pos = args.find("&"); if ( pos != string::npos ) { arg = args.substr( 0, pos ); args = args.substr( pos + 1 ); } else { arg = args; done = true; } printf(" arg = %s\n", arg.c_str() ); string::size_type apos = arg.find("="); if ( apos != string::npos ) { string a = arg.substr( 0, apos ); string b = arg.substr( apos + 1 ); printf(" a = %s b = %s\n", a.c_str(), b.c_str() ); if ( request == "/run.cgi" ) { // execute a command if ( a == "value" ) { SGPropertyNode args; if ( !globals->get_commands() ->execute(urlDecode(b).c_str(), &args) ) { SG_LOG( SG_GENERAL, SG_ALERT, "Command " << urlDecode(b) << " failed."); } } } else { if ( a == "value" ) { // update a property value fgSetString( request.c_str(), urlDecode(b).c_str() ); } } } } } else { request = urlDecode(request); } node = globals->get_props()->getNode(request.c_str()); string response = ""; response += ""; response += getTerminator(); response += ""; response += getTerminator(); response += "FlightGear - "; response += request; response += ""; response += getTerminator(); response += ""; response += getTerminator(); response += ""; response += getTerminator(); if (node == NULL) { response += "

Non-existent node requested!

"; response += getTerminator(); response += ""; response += request.c_str(); response += " does not exist."; response += getTerminator(); } else if ( node->nChildren() > 0 ) { // request is a path with children response += "

Contents of \""; response += request; response += "\"

"; response += getTerminator(); for (int i = 0; i < node->nChildren(); i++) { SGPropertyNode *child = node->getChild(i); string name = child->getDisplayName(true); string line = ""; if ( child->nChildren() > 0 ) { line += ""; line += name; line += ""; line += "/
"; } else { string value = node->getStringValue ( name.c_str(), "" ); line += ""; line += name; line += " ("; line += value; line += ")
"; } response += line; response += getTerminator(); } } else { // request for an individual data member string value = node->getStringValue(); response += "
"; response += ""; response += request; response += " = "; response += ""; response += ""; response += "
"; } response += ""; response += getTerminator(); response += ""; response += getTerminator(); push( "HTTP/1.1 200 OK" ); push( getTerminator() ); printf("size = %d\n", response.length()); char ctmp[256]; sprintf(ctmp, "Content-Length: %d", response.length()); push( ctmp ); push( getTerminator() ); push( "Connection: close" ); push( getTerminator() ); push( "Content-Type: text/html" ); push( getTerminator() ); push( getTerminator() ); push( response.c_str() ); } buffer.remove(); } // encode everything but "a-zA-Z0-9_.-/" (see RFC2396) string HttpdChannel::urlEncode(string s) { string r = ""; for ( int i = 0; i < (int)s.length(); i++ ) { if ( isalnum(s[i]) || s[i] == '_' || s[i] == '.' || s[i] == '-' || s[i] == '/' ) { r += s[i]; } else { char buf[16]; sprintf(buf, "%%%02X", (unsigned char)s[i]); r += buf; } } return r; } string HttpdChannel::urlDecode(string s) { string r = ""; int max = s.length(); int a, b; for ( int i = 0; i < max; i++ ) { if ( s[i] == '+' ) { r += ' '; } else if ( s[i] == '%' && i + 2 < max && isxdigit(s[i + 1]) && isxdigit(s[i + 2]) ) { i++; a = isdigit(s[i]) ? s[i] - '0' : toupper(s[i]) - 'A' + 10; i++; b = isdigit(s[i]) ? s[i] - '0' : toupper(s[i]) - 'A' + 10; r += (char)(a * 16 + b); } else { r += s[i]; } } return r; }