]> git.mxchange.org Git - flightgear.git/blob - src/Network/props.cxx
Patch from Julian Foad:
[flightgear.git] / src / Network / props.cxx
1 // \file props.cxx
2 // Property server class.
3 //
4 // Written by Curtis Olson, started September 2000.
5 // Modified by Bernie Bright, May 2002.
6 //
7 // Copyright (C) 2000  Curtis L. Olson - curt@flightgear.org
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License as
11 // published by the Free Software Foundation; either version 2 of the
12 // License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 // General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 //
23 // $Id$
24
25
26 #ifdef HAVE_CONFIG_H
27 #  include <config.h>
28 #endif
29
30 #include <simgear/compiler.h>
31 #include <simgear/debug/logstream.hxx>
32 #include <simgear/misc/strutils.hxx>
33 #include <simgear/misc/props.hxx>
34 #include <simgear/misc/props_io.hxx>
35
36 #include STL_STRSTREAM
37
38 #include <Main/globals.hxx>
39 #include <Main/viewmgr.hxx>
40
41 #include <plib/netChat.h>
42
43 #include "props.hxx"
44
45 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
46 SG_USING_STD(strstream);
47 SG_USING_STD(ends);
48 #endif
49
50 /**
51  * Props connection class.
52  * This class represents a connection to props client.
53  */
54 class PropsChannel : public netChat
55 {
56     netBuffer buffer;
57
58     /**
59      * Current property node name.
60      */
61     string path;
62
63     enum Mode {
64         PROMPT,
65         DATA
66     };
67     Mode mode;
68
69 public:
70     /**
71      * Constructor.
72      */
73     PropsChannel();
74     
75     /**
76      * Append incoming data to our request buffer.
77      *
78      * @param s Character string to append to buffer
79      * @param n Number of characters to append.
80      */
81     void collectIncomingData( const char* s, int n );
82
83     /**
84      * Process a complete request from the props client.
85      */
86     void foundTerminator();
87
88 private:
89     /**
90      * Return a "Node no found" error message to the client.
91      */
92     void node_not_found_error( const string& node_name );
93 };
94
95 /**
96  * 
97  */
98 PropsChannel::PropsChannel()
99     : buffer(512),
100       path("/"),
101       mode(PROMPT)
102 {
103     setTerminator( "\r\n" );
104 }
105
106 /**
107  * 
108  */
109 void
110 PropsChannel::collectIncomingData( const char* s, int n )
111 {
112     buffer.append( s, n );
113 }
114
115 /**
116  * 
117  */
118 void
119 PropsChannel::node_not_found_error( const string& node_name )
120 {
121     string error = "ERR Node \"";
122     error += node_name;
123     error += "\" not found.";
124     push( error.c_str() );
125     push( getTerminator() );
126 }
127
128 // return a human readable form of the value "type"
129 static string
130 getValueTypeString( const SGPropertyNode *node )
131 {
132     string result;
133
134     if ( node == NULL )
135     {
136         return "unknown";
137     }
138
139     SGPropertyNode::Type type = node->getType();
140     if ( type == SGPropertyNode::UNSPECIFIED ) {
141         result = "unspecified";
142     } else if ( type == SGPropertyNode::NONE ) {
143         result = "none";
144     } else if ( type == SGPropertyNode::BOOL ) {
145         result = "bool";
146     } else if ( type == SGPropertyNode::INT ) {
147         result = "int";
148     } else if ( type == SGPropertyNode::LONG ) {
149         result = "long";
150     } else if ( type == SGPropertyNode::FLOAT ) {
151         result = "float";
152     } else if ( type == SGPropertyNode::DOUBLE ) {
153         result = "double";
154     } else if ( type == SGPropertyNode::STRING ) {
155         result = "string";
156     }
157
158     return result;
159 }
160
161 /**
162  * We have a command.
163  * 
164  */
165 void
166 PropsChannel::foundTerminator()
167 {
168     const char* cmd = buffer.getData();
169     SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
170
171     vector<string> tokens = simgear::strutils::split( cmd );
172
173     SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );
174
175     if (!tokens.empty())
176     {
177         string command = tokens[0];
178
179         if (command == "ls")
180         {
181             SGPropertyNode* dir = node;
182             if (tokens.size() == 2)
183             {
184                 if (tokens[1][0] == '/')
185                 {
186                     dir = globals->get_props()->getNode( tokens[1].c_str() );
187                 }
188                 else
189                 {
190                     string s = path;
191                     s += "/";
192                     s += tokens[1];
193                     dir = globals->get_props()->getNode( s.c_str() );
194                 }
195
196                 if (dir == 0)
197                 {
198                     node_not_found_error( tokens[1] );
199                     goto prompt;
200                 }
201             }
202
203             for (int i = 0; i < dir->nChildren(); i++)
204             {
205                 SGPropertyNode * child = dir->getChild(i);
206                 string line = child->getDisplayName(true);
207
208                 if ( child->nChildren() > 0 )
209                 {
210                     line += "/";
211                 }
212                 else
213                 {
214                     if (mode == PROMPT)
215                     {
216                         string value = child->getStringValue();
217                         line += " =\t'" + value + "'\t(";
218                         line += getValueTypeString( child );
219                         line += ")";
220                     }
221                 }
222
223                 line += getTerminator();
224                 push( line.c_str() );
225             }
226         }
227         else if ( command == "dump" )
228         {
229             strstream buf;
230             if ( tokens.size() <= 1 )
231             {
232                 writeProperties( buf, node );
233                 buf << ends; // null terminate the string
234                 push( buf.str() );
235                 push( getTerminator() );
236             }
237             else
238             {
239                 SGPropertyNode *child = node->getNode( tokens[1].c_str() );
240                 if ( child )
241                 {
242                     writeProperties ( buf, child );
243                     buf << ends; // null terminate the string
244                     push( buf.str() );
245                     push( getTerminator() );
246                 }
247                 else
248                 {
249                     node_not_found_error( tokens[1] );
250                 }
251             }
252         }
253         else if ( command == "cd" )
254         {
255             if (tokens.size() == 2)
256             {
257                 try
258                 {
259                     SGPropertyNode* child = node->getNode( tokens[1].c_str() );
260                     if ( child )
261                     {
262                         node = child;
263                         path = node->getPath();
264                     }
265                     else
266                     {
267                         node_not_found_error( tokens[1] );
268                     }
269                 }
270                 catch (...)
271                 {
272                     // Ignore attempt to move past root node with ".."
273                 }
274             }
275         }
276         else if ( command == "pwd" )
277         {
278             string ttt = node->getPath();
279             if (ttt.empty())
280             {
281                 ttt = "/";
282             }
283
284             push( ttt.c_str() );
285             push( getTerminator() );
286         }
287         else if ( command == "get" || command == "show" )
288         {
289             if ( tokens.size() == 2 )
290             {
291                 string tmp;     
292                 string value = node->getStringValue ( tokens[1].c_str(), "" );
293                 if ( mode == PROMPT )
294                 {
295                     tmp = tokens[1];
296                     tmp += " = '";
297                     tmp += value;
298                     tmp += "' (";
299                     tmp += getValueTypeString(
300                                      node->getNode( tokens[1].c_str() ) );
301                     tmp += ")";
302                 }
303                 else
304                 {
305                     tmp = value;
306                 }
307                 push( tmp.c_str() );
308                 push( getTerminator() );
309             }
310         }
311         else if ( command == "set" )
312         {
313             if ( tokens.size() == 3 )
314             {
315                 node->getNode( tokens[1].c_str(), true )->setStringValue(tokens[2].c_str());
316
317                 if ( mode == PROMPT )
318                 {
319                     // now fetch and write out the new value as confirmation
320                     // of the change
321                     string value = node->getStringValue ( tokens[1].c_str(), "" );
322                     string tmp = tokens[1] + " = '" + value + "' (";
323                     tmp += getValueTypeString( node->getNode( tokens[1].c_str() ) );
324                     tmp += ")";
325                     push( tmp.c_str() );
326                     push( getTerminator() );
327                 }
328             }
329         }
330         else if (command == "quit")
331         {
332             close();
333             shouldDelete();
334             return;
335         }
336         else if ( command == "data" )
337         {
338             mode = DATA;
339         }
340         else if ( command == "prompt" )
341         {
342             mode = PROMPT;
343         }
344         else
345         {
346             const char* msg = "\
347 Valid commands are:\r\n\
348 \r\n\
349 cd <dir>           cd to a directory, '..' to move back\r\n\
350 data               switch to raw data mode\r\n\
351 dump               dump current state (in xml)\r\n\
352 get <var>          show the value of a parameter\r\n\
353 help               show this help message\r\n\
354 ls [<dir>]         list directory\r\n\
355 prompt             switch to interactive mode (default)\r\n\
356 pwd                display your current path\r\n\
357 quit               terminate connection\r\n\
358 set <var> <val>    set <var> to a new <val>\r\n\
359 show <var>         synonym for get\r\n";
360             push( msg );
361         }
362     }
363
364  prompt:
365     if (mode == PROMPT)
366     {
367         string prompt = node->getPath();
368         if (prompt.empty())
369         {
370             prompt = "/";
371         }
372         prompt += "> ";
373         push( prompt.c_str() );
374     }
375
376     buffer.remove();
377 }
378
379 /**
380  * 
381  */
382 FGProps::FGProps( const vector<string>& tokens )
383 {
384     // tokens:
385     //   props,port#
386     //   props,medium,direction,hz,hostname,port#,style
387     if (tokens.size() == 2)
388         port = atoi( tokens[1].c_str() );
389     else if (tokens.size() == 7)
390         port = atoi( tokens[5].c_str() );
391     else
392         throw FGProtocolConfigError( "FGProps: incorrect number of configuration arguments" );
393 }
394
395 /**
396  * 
397  */
398 FGProps::~FGProps()
399 {
400 }
401
402 /**
403  * 
404  */
405 bool
406 FGProps::open()
407 {
408     if ( is_enabled() )
409     {
410         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
411                 << "is already in use, ignoring" );
412         return false;
413     }
414
415     netChannel::open();
416     netChannel::bind( "", port );
417     netChannel::listen( 5 );
418     SG_LOG( SG_IO, SG_INFO, "Props server started on port " << port );
419
420     set_hz( 5 );                // default to processing requests @ 5Hz
421     set_enabled( true );
422     return true;
423 }
424
425 /**
426  * 
427  */
428 bool
429 FGProps::close()
430 {
431     return true;
432 }
433
434 /**
435  * 
436  */
437 bool
438 FGProps::process()
439 {
440     netChannel::poll();
441     return true;
442 }
443
444 /**
445  * 
446  */
447 void
448 FGProps::handleAccept()
449 {
450     netAddress addr;
451     int handle = accept( &addr );
452     SG_LOG( SG_IO, SG_INFO, "Props server accepted connection from "
453             << addr.getHost() << ":" << addr.getPort() );
454     PropsChannel* channel = new PropsChannel();
455     channel->setHandle( handle );
456 }