]> git.mxchange.org Git - flightgear.git/blob - src/Network/httpd.cxx
FGCom[-sa]: add IAX denoiser and auto gain + set silence threshold
[flightgear.git] / src / Network / httpd.cxx
1 // httpd.cxx -- FGFS http property manager interface / external script
2 //              and control class
3 //
4 // Written by Curtis Olson, started June 2001.
5 //
6 // Copyright (C) 2001  Curtis L. Olson - http://www.flightgear.org/~curt
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 // $Id$
23
24
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28
29 #include <simgear/compiler.h>
30
31 #include <algorithm>            // sort()
32 #include <cstdlib>              // atoi() atof()
33 #include <cstring>
34 #include <cstdio>
35 #include <string>
36
37 #include <simgear/debug/logstream.hxx>
38 #include <simgear/io/sg_netChat.hxx>
39 #include <simgear/io/iochannel.hxx>
40 #include <simgear/math/sg_types.hxx>
41 #include <simgear/structure/commands.hxx>
42 #include <simgear/props/props.hxx>
43
44 #include <Main/fg_props.hxx>
45 #include <Main/globals.hxx>
46
47 #include "httpd.hxx"
48
49 using std::string;
50
51 /* simple httpd server that makes an hasty stab at following the http
52    1.1 rfc.  */
53
54 //////////////////////////////////////////////////////////////
55 // class HttpdChannel
56 //////////////////////////////////////////////////////////////
57
58 class HttpdChannel : public simgear::NetChat
59 {
60     simgear::NetBuffer buffer;
61     
62     string urlEncode(string);
63     string urlDecode(string);
64
65 public:
66
67     HttpdChannel() : buffer(512) { setTerminator("\r\n"); }
68
69     virtual void collectIncomingData (const char* s, int n) {
70         buffer.append(s,n);
71     }
72
73     // Handle the actual http request
74     virtual void foundTerminator(void);
75 };
76
77
78 //////////////////////////////////////////////////////////////
79 // class HttpdServer
80 //////////////////////////////////////////////////////////////
81
82 class HttpdServer : private simgear::NetChannel
83 {
84     virtual bool writable (void) { return false; }
85
86     virtual void handleAccept (void) {
87         simgear::IPAddress addr;
88         int handle = accept ( &addr );
89         SG_LOG( SG_IO, SG_INFO, "Client " << addr.getHost() << ":" << addr.getPort() << " connected" );
90
91         HttpdChannel *hc = new HttpdChannel;
92         hc->setHandle ( handle );
93         poller.addChannel( hc );
94     }
95
96     simgear::NetChannelPoller poller;
97 public:
98
99     HttpdServer ( int port );
100     
101     void poll()
102     {
103         poller.poll();
104     }
105 };
106
107 HttpdServer::HttpdServer(int port)
108 {
109     if (!open())
110     {
111         SG_LOG( SG_IO, SG_ALERT, "Failed to open HTTP port.");
112         return;
113     }
114
115     if (0 != bind( "", port ))
116     {
117         SG_LOG( SG_IO, SG_ALERT, "Failed to bind HTTP port.");
118         return;
119     }
120
121     if (0 != listen( 5 ))
122     {
123         SG_LOG( SG_IO, SG_ALERT, "Failed to listen on HTTP port.");
124         return;
125     }
126     
127     poller.addChannel(this);
128
129     SG_LOG(SG_IO, SG_ALERT, "Httpd server started on port " << port);
130 }
131
132 //////////////////////////////////////////////////////////////
133 // class FGHttpd
134 //////////////////////////////////////////////////////////////
135
136 FGHttpd::FGHttpd(int p) :
137     port(p),
138     server(NULL)
139 {
140 }
141
142 FGHttpd::~FGHttpd()
143 {
144     delete server;
145 }
146
147 bool FGHttpd::open() {
148     if ( is_enabled() ) {
149         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
150                 << "is already in use, ignoring" );
151         return false;
152     }
153
154     server = new HttpdServer( port );
155     
156     set_hz( 15 );                // default to processing requests @ 15Hz
157     set_enabled( true );
158
159     return true;
160 }
161
162
163 bool FGHttpd::process() {
164     server->poll();
165     return true;
166 }
167
168
169 bool FGHttpd::close() {
170     if (!server)
171         return true;
172
173     SG_LOG( SG_IO, SG_INFO, "closing FGHttpd" );   
174
175     delete server;
176     server = NULL;
177     set_enabled( false );
178
179     return true;
180 }
181
182
183 class CompareNodes {
184 public:
185     bool operator() (const SGPropertyNode *a, const SGPropertyNode *b) const {
186         int r = strcmp(a->getName(), b->getName());
187         return r ? r < 0 : a->getIndex() < b->getIndex();
188     }
189 };
190
191
192 // Handle http GET requests
193 void HttpdChannel::foundTerminator (void) {
194     
195     closeWhenDone ();
196
197     const string s = buffer.getData();
198
199     if ( s.find( "GET " ) == 0 ) {
200         SG_LOG( SG_IO, SG_INFO, "echo: " << s );   
201
202         string rest = s.substr(4);
203         string request;
204         string tmp;
205
206         string::size_type pos = rest.find( " " );
207         if ( pos != string::npos ) {
208             request = rest.substr( 0, pos );
209         } else {
210             request = "/";
211         }
212
213         SGPropertyNode *node = NULL;
214         pos = request.find( "?" );
215         if ( pos != string::npos ) {
216             // request to update property value
217             string args = request.substr( pos + 1 );
218             request = request.substr( 0, pos );
219             SG_LOG( SG_IO, SG_INFO, "'" << request << "' '" << args << "'" );   
220             request = urlDecode(request);
221
222             // parse args looking for "value="
223             bool done = false;
224             while ( ! done ) {
225                 string arg;
226                 pos = args.find("&");
227                 if ( pos != string::npos ) {
228                     arg = args.substr( 0, pos );
229                     args = args.substr( pos + 1 );
230                 } else {
231                     arg = args;
232                     done = true;
233                 }
234
235                 SG_LOG( SG_IO, SG_INFO, "  arg = " << arg );   
236                 string::size_type apos = arg.find("=");
237                 if ( apos != string::npos ) {
238                     string a = arg.substr( 0, apos );
239                     string b = arg.substr( apos + 1 );
240                     SG_LOG( SG_IO, SG_INFO, "    a = " << a << "  b = " << b );
241                     if ( request == "/run.cgi" ) {
242                         // execute a command
243                         if ( a == "value" ) {
244                             SGPropertyNode args;
245                             if ( !globals->get_commands()
246                                  ->execute(urlDecode(b).c_str(), &args) )
247                             {
248                                 SG_LOG( SG_NETWORK, SG_ALERT,
249                                         "Command " << urlDecode(b)
250                                         << " failed.");
251                             }
252
253                         }
254                     } else {
255                         if ( a == "value" ) {
256                             // update a property value
257                             fgSetString( request.c_str(),
258                                          urlDecode(b).c_str() );
259                         }
260                     }
261                 }
262             }
263         } else {
264             request = urlDecode(request);
265         }
266
267         node = globals->get_props()->getNode(request.c_str());
268
269         string response = "";
270         response += "<HTML LANG=\"en\">";
271         response += getTerminator();
272
273         response += "<HEAD>";
274         response += getTerminator();
275
276         response += "<TITLE>FlightGear - ";
277         response += request;
278         response += "</TITLE>";
279         response += getTerminator();
280
281         response += "</HEAD>";
282         response += getTerminator();
283
284         response += "<BODY>";
285         response += getTerminator();
286
287         if (node == NULL) {
288             response += "<H3>Non-existent node requested!</H3>";
289             response += getTerminator();
290
291             response += "<B>";
292             response += request.c_str();
293             response += "</B> does not exist.";
294             response += getTerminator();
295         } else if ( node->nChildren() > 0 ) {
296             // request is a path with children
297             response += "<H3>Contents of \"";
298             response += request;
299             response += "\"</H3>";
300             response += getTerminator();
301
302
303             vector<SGPropertyNode *> children;
304             for (int i = 0; i < node->nChildren(); i++)
305                 children.push_back(node->getChild(i));
306             std::sort(children.begin(), children.end(), CompareNodes());
307
308             vector<SGPropertyNode *>::iterator it, end = children.end();
309             for (it = children.begin(); it != end; ++it) {
310                 SGPropertyNode *child = *it;
311                 string name = child->getDisplayName(true);
312                 string line = "";
313                 if ( child->nChildren() > 0 ) {
314                     line += "<B><A HREF=\"";
315                     line += request;
316                     if ( request.substr(request.length() - 1, 1) != "/" ) {
317                         line += "/";
318                     }
319                     line += urlEncode(name);
320                     line += "\">";
321                     line += name;
322                     line += "</A></B>";
323                     line += "/<BR>";
324                 } else {
325                     string value = node->getStringValue ( name.c_str(), "" );
326                     line += "<B>";
327                     line += name;
328                     line += "</B> <A HREF=\"";
329                     line += request;
330                     if ( request.substr(request.length() - 1, 1) != "/" ) {
331                         line += "/";
332                     }
333                     line += urlEncode(name);
334                     line += "\">(";
335                     line += value;
336                     line += ")</A><BR>";
337                 }
338                 response += line;
339                 response += getTerminator();
340             }
341         } else {
342             // request for an individual data member
343             string value = node->getStringValue();
344             
345             response += "<form method=\"GET\" action=\"";
346             response += urlEncode(request);
347             response += "\">";
348             response += "<B>";
349             response += request;
350             response += "</B> = ";
351             response += "<input type=text name=value size=\"15\" value=\"";
352             response += value;
353             response += "\" maxlength=2047>";
354             response += "<input type=submit value=\"update\" name=\"submit\">";
355             response += "</FORM>";
356         }
357         response += "</BODY>";
358         response += getTerminator();
359
360         response += "</HTML>";
361         response += getTerminator();
362
363         push( "HTTP/1.1 200 OK" );
364         push( getTerminator() );
365         
366         SG_LOG( SG_IO, SG_INFO, "size = " << response.length() );
367         char ctmp[256];
368         sprintf(ctmp, "Content-Length: %u", (unsigned)response.length());
369         push( ctmp );
370         push( getTerminator() );
371
372         push( "Connection: close" );
373         push( getTerminator() );
374
375         push( "Content-Type: text/html" );
376         push( getTerminator() );
377         push( getTerminator() );
378                 
379         push( response.c_str() );
380     }
381
382     buffer.remove();
383 }
384
385
386 // encode everything but "a-zA-Z0-9_.-/" (see RFC2396)
387 string HttpdChannel::urlEncode(string s) {
388     string r = "";
389     
390     for ( int i = 0; i < (int)s.length(); i++ ) {
391         if ( isalnum(s[i]) || s[i] == '_' || s[i] == '.'
392                 || s[i] == '-' || s[i] == '/' ) {
393             r += s[i];
394         } else {
395             char buf[16];
396             sprintf(buf, "%%%02X", (unsigned char)s[i]);
397             r += buf;
398         }
399     }
400     return r;
401 }
402
403
404 string HttpdChannel::urlDecode(string s) {
405     string r = "";
406     int max = s.length();
407     int a, b;
408
409     for ( int i = 0; i < max; i++ ) {
410         if ( s[i] == '+' ) {
411             r += ' ';
412         } else if ( s[i] == '%' && i + 2 < max
413                 && isxdigit(s[i + 1])
414                 && isxdigit(s[i + 2]) ) {
415             i++;
416             a = isdigit(s[i]) ? s[i] - '0' : toupper(s[i]) - 'A' + 10;
417             i++;
418             b = isdigit(s[i]) ? s[i] - '0' : toupper(s[i]) - 'A' + 10;
419             r += (char)(a * 16 + b);
420         } else {
421             r += s[i];
422         }
423     }
424     return r;
425 }