]> git.mxchange.org Git - flightgear.git/blobdiff - src/Network/httpd.cxx
NavDisplay - custom symbol support enabled.
[flightgear.git] / src / Network / httpd.cxx
index 7a567a61a53d3884e1b5a6dfd20e005a53e636e6..206b553d62373875627eb96e006dc2f0845d4f9c 100644 (file)
@@ -1,12 +1,9 @@
-// httpd.hxx -- FGFS http property manager interface / external script
+// 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 - curt@flightgear.org
-//
-// Jpeg Image Support added August 2001
-//  by Norman Vine - nhv@cape.com
+// Copyright (C) 2001  Curtis L. Olson - http://www.flightgear.org/~curt
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License as
@@ -20,7 +17,7 @@
 //
 // 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.
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //
 // $Id$
 
 
 #include <simgear/compiler.h>
 
-#include <stdlib.h>            // atoi() atof()
-
-#include STL_STRING
-#include STL_STRSTREAM
+#include <algorithm>           // sort()
+#include <cstdlib>             // atoi() atof()
+#include <cstring>
+#include <string>
 
 #include <simgear/debug/logstream.hxx>
+#include <simgear/io/sg_netChat.hxx>
 #include <simgear/io/iochannel.hxx>
 #include <simgear/math/sg_types.hxx>
-#include <simgear/misc/props.hxx>
+#include <simgear/structure/commands.hxx>
+#include <simgear/props/props.hxx>
 
 #include <Main/fg_props.hxx>
 #include <Main/globals.hxx>
 
 #include "httpd.hxx"
 
-SG_USING_STD(string);
-#if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
-SG_USING_STD(cout);
-SG_USING_STD(istrstream);
-#endif
+using std::string;
+
+/* simple httpd server that makes an hasty stab at following the http
+   1.1 rfc.  */
+
+//////////////////////////////////////////////////////////////
+// class HttpdChannel
+//////////////////////////////////////////////////////////////
+
+class HttpdChannel : public simgear::NetChat
+{
+    simgear::NetBuffer buffer;
+
+    string urlEncode(string);
+    string urlDecode(string);
+
+public:
+
+    HttpdChannel() : buffer(512) { setTerminator("\r\n"); }
+
+    virtual void collectIncomingData (const char* s, int n) {
+        buffer.append(s,n);
+    }
+
+    // Handle the actual http request
+    virtual void foundTerminator(void);
+};
 
 
+//////////////////////////////////////////////////////////////
+// class HttpdServer
+//////////////////////////////////////////////////////////////
+
+class HttpdServer : private simgear::NetChannel
+{
+    virtual bool writable (void) { return false; }
+
+    virtual void handleAccept (void) {
+        simgear::IPAddress addr;
+        int handle = accept ( &addr );
+        SG_LOG( SG_IO, SG_INFO, "Client " << addr.getHost() << ":" << addr.getPort() << " connected" );
+
+        HttpdChannel *hc = new HttpdChannel;
+        hc->setHandle ( handle );
+    }
+
+public:
+
+    HttpdServer ( int port );
+};
+
+HttpdServer::HttpdServer(int port)
+{
+    if (!open())
+    {
+        SG_LOG( SG_IO, SG_ALERT, "Failed to open HTTP port.");
+        return;
+    }
+
+    if (0 != bind( "", port ))
+    {
+        SG_LOG( SG_IO, SG_ALERT, "Failed to bind HTTP port.");
+        return;
+    }
+
+    if (0 != listen( 5 ))
+    {
+        SG_LOG( SG_IO, SG_ALERT, "Failed to listen on HTTP port.");
+        return;
+    }
+
+    SG_LOG(SG_IO, SG_ALERT, "Httpd server started on port " << port);
+}
+
+//////////////////////////////////////////////////////////////
+// class FGHttpd
+//////////////////////////////////////////////////////////////
+
+FGHttpd::FGHttpd(int p) :
+    port(p),
+    server(NULL)
+{
+}
+
+FGHttpd::~FGHttpd()
+{
+    delete server;
+}
+
 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;
+        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( 5 );                // default to processing requests @ 5Hz
+    set_hz( 15 );                // default to processing requests @ 15Hz
     set_enabled( true );
 
     return true;
@@ -70,19 +151,35 @@ bool FGHttpd::open() {
 
 
 bool FGHttpd::process() {
-    netChannel::poll();
+    simgear::NetChannel::poll();
 
     return true;
 }
 
 
 bool FGHttpd::close() {
+    if (!server)
+        return true;
+
+    SG_LOG( SG_IO, SG_INFO, "closing FGHttpd" );   
+
     delete server;
+    server = NULL;
+    set_enabled( false );
 
     return true;
 }
 
 
+class CompareNodes {
+public:
+    bool operator() (const SGPropertyNode *a, const SGPropertyNode *b) const {
+        int r = strcmp(a->getName(), b->getName());
+        return r ? r < 0 : a->getIndex() < b->getIndex();
+    }
+};
+
+
 // Handle http GET requests
 void HttpdChannel::foundTerminator (void) {
     
@@ -91,16 +188,15 @@ void HttpdChannel::foundTerminator (void) {
     const string s = buffer.getData();
 
     if ( s.find( "GET " ) == 0 ) {
-        printf("echo: %s\n", s.c_str());
+        SG_LOG( SG_IO, SG_INFO, "echo: " << s );   
 
         string rest = s.substr(4);
         string request;
         string tmp;
 
-        unsigned int pos = rest.find( " " );
+        string::size_type pos = rest.find( " " );
         if ( pos != string::npos ) {
             request = rest.substr( 0, pos );
-            request = urlDecode(request);
         } else {
             request = "/";
         }
@@ -111,7 +207,8 @@ void HttpdChannel::foundTerminator (void) {
             // 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());
+            SG_LOG( SG_IO, SG_INFO, "'" << request << "' '" << args << "'" );   
+            request = urlDecode(request);
 
             // parse args looking for "value="
             bool done = false;
@@ -126,20 +223,39 @@ void HttpdChannel::foundTerminator (void) {
                     done = true;
                 }
 
-                printf("  arg = %s\n", arg.c_str() );
-                unsigned int apos = arg.find("=");
+                SG_LOG( SG_IO, SG_INFO, "  arg = " << arg );   
+                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 ( a == "value" ) {
-                        fgSetString( request, b );
-                    } 
+                    SG_LOG( SG_IO, SG_INFO, "    a = " << a << "  b = " << b );
+                    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_NETWORK, 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);
+        node = globals->get_props()->getNode(request.c_str());
 
         string response = "";
         response += "<HTML LANG=\"en\">";
@@ -174,19 +290,21 @@ void HttpdChannel::foundTerminator (void) {
             response += "\"</H3>";
             response += getTerminator();
 
-            for (int i = 0; i < node->nChildren(); i++) {
-                SGPropertyNode *child = node->getChild(i);
-                string name = child->getName();
-                if ( node->getChild(name, 1) ) {
-                    char buf[16];
-                    sprintf(buf, "[%d]", child->getIndex());
-                    name += buf;
-                }
+
+            vector<SGPropertyNode *> children;
+            for (int i = 0; i < node->nChildren(); i++)
+                children.push_back(node->getChild(i));
+            std::sort(children.begin(), children.end(), CompareNodes());
+
+            vector<SGPropertyNode *>::iterator it, end = children.end();
+            for (it = children.begin(); it != end; ++it) {
+                SGPropertyNode *child = *it;
+                string name = child->getDisplayName(true);
                 string line = "";
                 if ( child->nChildren() > 0 ) {
                     line += "<B><A HREF=\"";
                     line += request;
-                    if ( request.substr(request.length() - 1, 1) != (string)"/" ) {
+                    if ( request.substr(request.length() - 1, 1) != "/" ) {
                         line += "/";
                     }
                     line += urlEncode(name);
@@ -195,12 +313,12 @@ void HttpdChannel::foundTerminator (void) {
                     line += "</A></B>";
                     line += "/<BR>";
                 } else {
-                    string value = node->getStringValue ( name, "" );
+                    string value = node->getStringValue ( name.c_str(), "" );
                     line += "<B>";
                     line += name;
                     line += "</B> <A HREF=\"";
                     line += request;
-                    if ( request.substr(request.length() - 1, 1) != (string)"/" ) {
+                    if ( request.substr(request.length() - 1, 1) != "/" ) {
                         line += "/";
                     }
                     line += urlEncode(name);
@@ -225,8 +343,7 @@ void HttpdChannel::foundTerminator (void) {
             response += value;
             response += "\" maxlength=2047>";
             response += "<input type=submit value=\"update\" name=\"submit\">";
-            response += "<FORM>";
-            response += "<BR>";
+            response += "</FORM>";
         }
         response += "</BODY>";
         response += getTerminator();
@@ -237,9 +354,9 @@ void HttpdChannel::foundTerminator (void) {
         push( "HTTP/1.1 200 OK" );
         push( getTerminator() );
         
-        printf("size = %d\n", response.length());
+        SG_LOG( SG_IO, SG_INFO, "size = " << response.length() );
         char ctmp[256];
-        sprintf(ctmp, "Content-Length: %d", response.length());
+        sprintf(ctmp, "Content-Length: %u", (unsigned)response.length());
         push( ctmp );
         push( getTerminator() );
 
@@ -257,7 +374,7 @@ void HttpdChannel::foundTerminator (void) {
 }
 
 
-// encode everything but "a-zA-Z0-9_.-/"
+// encode everything but "a-zA-Z0-9_.-/" (see RFC2396)
 string HttpdChannel::urlEncode(string s) {
     string r = "";