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