]> git.mxchange.org Git - flightgear.git/blob - src/Network/httpd.cxx
Fix a Clang warning, checking signed char as if it was unsigned.
[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 // Jpeg Image Support added August 2001
9 //  by Norman Vine - nhv@cape.com
10 //
11 // This program is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU General Public License as
13 // published by the Free Software Foundation; either version 2 of the
14 // License, or (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful, but
17 // WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 // General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
24 //
25 // $Id$
26
27
28 #ifdef HAVE_CONFIG_H
29 #  include <config.h>
30 #endif
31
32 #include <simgear/compiler.h>
33
34 #include <algorithm>            // sort()
35 #include <cstdlib>              // atoi() atof()
36 #include <cstring>
37 #include <string>
38
39 #include <simgear/debug/logstream.hxx>
40 #include <simgear/io/iochannel.hxx>
41 #include <simgear/math/sg_types.hxx>
42 #include <simgear/structure/commands.hxx>
43 #include <simgear/props/props.hxx>
44
45 #include <Main/fg_props.hxx>
46 #include <Main/globals.hxx>
47
48 #include "httpd.hxx"
49
50 using std::string;
51
52 bool FGHttpd::open() {
53     if ( is_enabled() ) {
54         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
55                 << "is already in use, ignoring" );
56         return false;
57     }
58
59     server = new HttpdServer( port );
60     
61     set_hz( 15 );                // default to processing requests @ 15Hz
62     set_enabled( true );
63
64     return true;
65 }
66
67
68 bool FGHttpd::process() {
69     simgear::NetChannel::poll();
70
71     return true;
72 }
73
74
75 bool FGHttpd::close() {
76     SG_LOG( SG_IO, SG_INFO, "closing FGHttpd" );   
77
78     delete server;
79     set_enabled( false );
80
81     return true;
82 }
83
84
85 class CompareNodes {
86 public:
87     bool operator() (const SGPropertyNode *a, const SGPropertyNode *b) const {
88         int r = strcmp(a->getName(), b->getName());
89         return r ? r < 0 : a->getIndex() < b->getIndex();
90     }
91 };
92
93
94 // Handle http GET requests
95 void HttpdChannel::foundTerminator (void) {
96     
97     closeWhenDone ();
98
99     const string s = buffer.getData();
100
101     if ( s.find( "GET " ) == 0 ) {
102         SG_LOG( SG_IO, SG_INFO, "echo: " << s );   
103
104         string rest = s.substr(4);
105         string request;
106         string tmp;
107
108         string::size_type pos = rest.find( " " );
109         if ( pos != string::npos ) {
110             request = rest.substr( 0, pos );
111         } else {
112             request = "/";
113         }
114
115         SGPropertyNode *node = NULL;
116         pos = request.find( "?" );
117         if ( pos != string::npos ) {
118             // request to update property value
119             string args = request.substr( pos + 1 );
120             request = request.substr( 0, pos );
121             SG_LOG( SG_IO, SG_INFO, "'" << request << "' '" << args << "'" );   
122             request = urlDecode(request);
123
124             // parse args looking for "value="
125             bool done = false;
126             while ( ! done ) {
127                 string arg;
128                 pos = args.find("&");
129                 if ( pos != string::npos ) {
130                     arg = args.substr( 0, pos );
131                     args = args.substr( pos + 1 );
132                 } else {
133                     arg = args;
134                     done = true;
135                 }
136
137                 SG_LOG( SG_IO, SG_INFO, "  arg = " << arg );   
138                 string::size_type apos = arg.find("=");
139                 if ( apos != string::npos ) {
140                     string a = arg.substr( 0, apos );
141                     string b = arg.substr( apos + 1 );
142                     SG_LOG( SG_IO, SG_INFO, "    a = " << a << "  b = " << b );
143                     if ( request == "/run.cgi" ) {
144                         // execute a command
145                         if ( a == "value" ) {
146                             SGPropertyNode args;
147                             if ( !globals->get_commands()
148                                  ->execute(urlDecode(b).c_str(), &args) )
149                             {
150                                 SG_LOG( SG_NETWORK, SG_ALERT,
151                                         "Command " << urlDecode(b)
152                                         << " failed.");
153                             }
154
155                         }
156                     } else {
157                         if ( a == "value" ) {
158                             // update a property value
159                             fgSetString( request.c_str(),
160                                          urlDecode(b).c_str() );
161                         }
162                     }
163                 }
164             }
165         } else {
166             request = urlDecode(request);
167         }
168
169         node = globals->get_props()->getNode(request.c_str());
170
171         string response = "";
172         response += "<HTML LANG=\"en\">";
173         response += getTerminator();
174
175         response += "<HEAD>";
176         response += getTerminator();
177
178         response += "<TITLE>FlightGear - ";
179         response += request;
180         response += "</TITLE>";
181         response += getTerminator();
182
183         response += "</HEAD>";
184         response += getTerminator();
185
186         response += "<BODY>";
187         response += getTerminator();
188
189         if (node == NULL) {
190             response += "<H3>Non-existent node requested!</H3>";
191             response += getTerminator();
192
193             response += "<B>";
194             response += request.c_str();
195             response += "</B> does not exist.";
196             response += getTerminator();
197         } else if ( node->nChildren() > 0 ) {
198             // request is a path with children
199             response += "<H3>Contents of \"";
200             response += request;
201             response += "\"</H3>";
202             response += getTerminator();
203
204
205             vector<SGPropertyNode *> children;
206             for (int i = 0; i < node->nChildren(); i++)
207                 children.push_back(node->getChild(i));
208             std::sort(children.begin(), children.end(), CompareNodes());
209
210             vector<SGPropertyNode *>::iterator it, end = children.end();
211             for (it = children.begin(); it != end; ++it) {
212                 SGPropertyNode *child = *it;
213                 string name = child->getDisplayName(true);
214                 string line = "";
215                 if ( child->nChildren() > 0 ) {
216                     line += "<B><A HREF=\"";
217                     line += request;
218                     if ( request.substr(request.length() - 1, 1) != "/" ) {
219                         line += "/";
220                     }
221                     line += urlEncode(name);
222                     line += "\">";
223                     line += name;
224                     line += "</A></B>";
225                     line += "/<BR>";
226                 } else {
227                     string value = node->getStringValue ( name.c_str(), "" );
228                     line += "<B>";
229                     line += name;
230                     line += "</B> <A HREF=\"";
231                     line += request;
232                     if ( request.substr(request.length() - 1, 1) != "/" ) {
233                         line += "/";
234                     }
235                     line += urlEncode(name);
236                     line += "\">(";
237                     line += value;
238                     line += ")</A><BR>";
239                 }
240                 response += line;
241                 response += getTerminator();
242             }
243         } else {
244             // request for an individual data member
245             string value = node->getStringValue();
246             
247             response += "<form method=\"GET\" action=\"";
248             response += urlEncode(request);
249             response += "\">";
250             response += "<B>";
251             response += request;
252             response += "</B> = ";
253             response += "<input type=text name=value size=\"15\" value=\"";
254             response += value;
255             response += "\" maxlength=2047>";
256             response += "<input type=submit value=\"update\" name=\"submit\">";
257             response += "</FORM>";
258         }
259         response += "</BODY>";
260         response += getTerminator();
261
262         response += "</HTML>";
263         response += getTerminator();
264
265         push( "HTTP/1.1 200 OK" );
266         push( getTerminator() );
267         
268         SG_LOG( SG_IO, SG_INFO, "size = " << response.length() );
269         char ctmp[256];
270         sprintf(ctmp, "Content-Length: %u", (unsigned)response.length());
271         push( ctmp );
272         push( getTerminator() );
273
274         push( "Connection: close" );
275         push( getTerminator() );
276
277         push( "Content-Type: text/html" );
278         push( getTerminator() );
279         push( getTerminator() );
280                 
281         push( response.c_str() );
282     }
283
284     buffer.remove();
285 }
286
287
288 // encode everything but "a-zA-Z0-9_.-/" (see RFC2396)
289 string HttpdChannel::urlEncode(string s) {
290     string r = "";
291     
292     for ( int i = 0; i < (int)s.length(); i++ ) {
293         if ( isalnum(s[i]) || s[i] == '_' || s[i] == '.'
294                 || s[i] == '-' || s[i] == '/' ) {
295             r += s[i];
296         } else {
297             char buf[16];
298             sprintf(buf, "%%%02X", (unsigned char)s[i]);
299             r += buf;
300         }
301     }
302     return r;
303 }
304
305
306 string HttpdChannel::urlDecode(string s) {
307     string r = "";
308     int max = s.length();
309     int a, b;
310
311     for ( int i = 0; i < max; i++ ) {
312         if ( s[i] == '+' ) {
313             r += ' ';
314         } else if ( s[i] == '%' && i + 2 < max
315                 && isxdigit(s[i + 1])
316                 && isxdigit(s[i + 2]) ) {
317             i++;
318             a = isdigit(s[i]) ? s[i] - '0' : toupper(s[i]) - 'A' + 10;
319             i++;
320             b = isdigit(s[i]) ? s[i] - '0' : toupper(s[i]) - 'A' + 10;
321             r += (char)(a * 16 + b);
322         } else {
323             r += s[i];
324         }
325     }
326     return r;
327 }