fgms 0.11.8
The
FlightGear MultiPlayer Server
project
libcli.cxx
Go to the documentation of this file.
1 // This program is free software; you can redistribute it and/or
2 // modify it under the terms of the GNU General Public License as
3 // published by the Free Software Foundation; either version 2 of the
4 // License, or (at your option) any later version.
5 //
6 // This program is distributed in the hope that it will be useful, but
7 // WITHOUT ANY WARRANTY; without even the implied warranty of
8 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 // General Public License for more details.
10 //
11 // You should have received a copy of the GNU General Public License
12 // along with this program; if not, write to the Free Software
13 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, U$
14 //
15 // derived from libcli by David Parrish (david@dparrish.com)
16 // Copyright (C) 2011 Oliver Schroeder
17 //
18 #ifdef HAVE_CONFIG_H
19  #include "config.h"
20 #endif
21 
22 #include <exception>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <memory.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <unistd.h>
31 #include <server/fg_util.hxx>
32 #include "libcli.hxx"
33 
34 #if defined(_MSC_VER) || defined(__CYGWIN__)
35  // some windows quick fixes
36 #ifndef __CYGWIN__
37  #define CTRL(a) ( a & 037 )
38 #endif
39  #ifdef __cplusplus
40  extern "C" {
41  #endif
42  extern char *crypt(const char *key, const char *salt);
43  #ifdef __cplusplus
44  }
45  #endif
46 #endif
47 
48 namespace LIBCLI
49 {
50 
51 using namespace std;
52 
53 #ifndef CALL_MEMBER_FN
54  CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember))
55 #endif
56 
58 {
59  { "begin", "Begin with lines that match" },
60  { "between", "Between lines that match" },
61  { "count", "Count of lines" },
62  { "exclude", "Exclude lines that match" },
63  { "include", "Include lines that match" },
64  { NULL, NULL}
65 };
66 
67 void
69 (
70  c_auth_func callback
71 )
72 {
73  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
74  this->auth_callback = callback;
75  this->cpp_auth_callback = 0;
76 }
77 
78 void
80 (
81  cpp_auth_func callback
82 )
83 {
84  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
85  this->auth_callback = 0;
86  this->cpp_auth_callback = callback;
87 }
88 
89 void
91 (
92  c_enable_func callback
93 )
94 {
95  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
96  this->enable_callback = callback;
97  this->cpp_enable_callback = 0;
98 }
99 
100 void
102 (
103  cpp_enable_func callback
104 )
105 {
106  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
107  this->enable_callback = 0;
108  this->cpp_enable_callback = callback;
109 }
110 
111 void
113 (
114  const string& username,
115  const string& password
116 )
117 {
118  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
119  unp_it u = users.find (username);
120  if (u != users.end())
121  {
122  cerr << "user '" << username << "' already exists!" << endl;
123  return;
124  }
125  users[username] = password;
126 }
127 
128 void
130 (
131  const string& password
132 )
133 {
134  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
135  enable_password = password;
136 }
137 
138 void
140 (
141  const string& username
142 )
143 {
144  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
145  if (users.empty())
146  return;
147  unp_it u = users.find (username);
148  if (u == users.end())
149  return;
150  users.erase (u);
151 }
152 
153 void
155 (
156  const string& banner
157 )
158 {
159  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
160  this->banner = banner;
161 }
162 
163 void
165 (
166  const string& hostname
167 )
168 {
169  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
170  this->hostname = hostname;
171 }
172 
173 void
175 (
176  const string& prompt
177 )
178 {
179  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
180  this->prompt = prompt;
181 }
182 
183 int
185 (
186  Command<CLI> *commands
187 )
188 {
189  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
190  Command<CLI> *c, *p;
191  char* cp, *pp;
192  unsigned int len;
193  for ( c = commands; c; c = c->next )
194  {
195  c->unique_len = strlen ( c->command );
196  if ( ( c->mode != MODE_ANY && c->mode != this->mode )
197  || ( c->privilege > this->privilege ) )
198  {
199  continue;
200  }
201  c->unique_len = 1;
202  for ( p = commands; p; p = p->next )
203  {
204  if ( c == p )
205  {
206  continue;
207  }
208  if ( ( p->mode != MODE_ANY && p->mode != this->mode )
209  || ( p->privilege > this->privilege ) )
210  {
211  continue;
212  }
213  cp = c->command;
214  pp = p->command;
215  len = 0;
216  while ( *cp && *pp && *cp++ == *pp++ )
217  {
218  len++;
219  }
220  if ( len > c->unique_len )
221  {
222  c->unique_len = len;
223  }
224  }
225  if ( c->children )
226  {
227  build_shortest ( c->children );
228  }
229  }
230  return LIBCLI::OK;
231 }
232 
233 int
235 (
236  int priv
237 )
238 {
239  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
240  int old = this->privilege;
241  this->privilege = priv;
242  if ( priv != old )
243  {
244  set_prompt ( priv == LIBCLI::PRIVILEGED ? "# " : "> " );
245  build_shortest ( this->commands );
246  }
247  return old;
248 }
249 
250 void
252 (
253  const string& modestring
254 )
255 {
256  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
257  this->modestring = modestring;
258 }
259 
260 int
262 (
263  int mode,
264  const string& config_desc
265 )
266 {
267  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
268  int old = this->mode;
269  this->mode = mode;
270  if ( mode != old )
271  {
272  if ( !this->mode )
273  {
274  // Not config mode
275  set_modestring ("");
276  }
277  else if ( config_desc != "" )
278  {
279  string s = "(config-" + config_desc + ")";
280  set_modestring (s);
281  }
282  else
283  {
284  set_modestring ( "(config)" );
285  }
286  build_shortest ( this->commands );
287  }
288  return old;
289 }
290 
291 void
293 (
294  Command<CLI>* command,
295  Command<CLI>* parent
296 )
297 {
298  DEBUG d ( CLI_TRACE );
299  if ( ! command )
300  {
301  return;
302  }
303  if ( parent )
304  {
305  command->parent = parent;
306  if ( ! parent->children )
307  {
308  parent->children = command;
309  build_shortest ( command->parent );
310  return;
311  }
312  Command<CLI>* c;
313  for ( c = parent->children; c && c->next; c = c->next )
314  /* intentionally empty */
315  {
316  ;
317  }
318  if ( c )
319  {
320  c->next = command;
321  build_shortest ( parent );
322  return;
323  }
324  cout << "bummer!" << std::endl;
325  }
326  if ( ! this->commands )
327  {
328  this->commands = command;
329  }
330  else
331  {
332  Command<CLI>* c;
333  for ( c = this->commands; c && c->next; c = c->next )
334  /* intentionally empty */
335  {
336  ;
337  }
338  if ( c )
339  {
340  c->next = command;
341  }
342  else
343  {
344  cout << "bummer 2" << std::endl;
345  }
346  }
347  build_shortest ( ( command->parent ) ? command->parent : this->commands );
348 }
349 
350 void
352 (
353  Command<CLI> *cmd
354 )
355 {
356  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
357  Command<CLI> *c,*p;
358  for ( c = cmd->children; c; )
359  {
360  p = c->next;
361  free_command ( c );
362  c = p;
363  }
364  free_z ( cmd->command );
365  if ( cmd->help )
366  {
367  free_z ( cmd->help );
368  }
369  free_z ( cmd );
370 }
371 
372 int
374 (
375  char* command
376 )
377 {
378  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
379  Command<CLI> *c, *p = NULL;
380  if ( ! command )
381  {
382  return -1;
383  }
384  if ( !this->commands )
385  {
386  return LIBCLI::OK;
387  }
388  for ( c = this->commands; c; c = c->next )
389  {
390  if ( strcmp ( c->command, command ) == 0 )
391  {
392  if ( p )
393  {
394  p->next = c->next;
395  }
396  else
397  {
398  this->commands = c->next;
399  }
400  free_command ( c );
401  return LIBCLI::OK;
402  }
403  p = c;
404  }
405  return LIBCLI::OK;
406 }
407 
408 int
410 (
411  UNUSED ( char* command ),
412  UNUSED ( char* argv[] ),
413  UNUSED ( int argc )
414 )
415 {
416  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
417  if ( this->privilege == LIBCLI::PRIVILEGED )
418  {
419  return LIBCLI::OK;
420  }
421  if ( (enable_password == "") && !this->enable_callback && !this->cpp_enable_callback)
422  {
423  /* no password required, set privilege immediately */
424  set_privilege ( LIBCLI::PRIVILEGED );
425  set_configmode ( LIBCLI::MODE_EXEC, "" );
426  }
427  else
428  {
429  /* require password entry */
430  this->state = LIBCLI::STATE_ENABLE_PASSWORD;
431  }
432  return LIBCLI::OK;
433 }
434 
435 int
437 (
438  UNUSED ( char* command ),
439  UNUSED ( char* argv[] ),
440  UNUSED ( int argc )
441 )
442 {
443  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
444  set_privilege ( LIBCLI::UNPRIVILEGED );
445  set_configmode ( LIBCLI::MODE_EXEC, "" );
446  return LIBCLI::OK;
447 }
448 
449 int
451 (
452  UNUSED ( char* command ),
453  UNUSED ( char* argv[] ),
454  UNUSED ( int argc )
455 )
456 {
457  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
458  client << CRLF;
459  client <<
460  "Help may be requested at any point in a command by entering\r\n"
461  "a question mark '?'. If nothing matches, the help list will\r\n"
462  "be empty and you must backup until entering a '?' shows the\r\n"
463  "available options.\r\n"
464  "Two styles of help are provided:\r\n"
465  "1. Full help is available when you are ready to enter a\r\n"
466  " command argument (e.g. 'show ?') and describes each possible\r\n"
467  " argument.\r\n"
468  "2. Partial help is provided when an abbreviated argument is entered\r\n"
469  " and you want to know what arguments match the input\r\n"
470  " (e.g. 'show pr?'.)\r\n"
471  << CRLF;
472  return LIBCLI::OK;
473 }
474 
475 int
477 (
478  UNUSED ( char* command ),
479  UNUSED ( char* argv[] ),
480  UNUSED ( int argc )
481 )
482 {
483  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
484  client << "You are '" << username << "'" << CRLF;
485  return LIBCLI::OK;
486 }
487 
488 int
490 (
491  UNUSED ( char* command ),
492  UNUSED ( char* argv[] ),
493  UNUSED ( int argc )
494 )
495 {
496  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
497  int i;
498  client << CRLF << "Command history:" << CRLF;
499  for ( i = 0; i < LIBCLI::MAX_HISTORY; i++ )
500  {
501  if ( this->history[i] )
502  {
503  client << setw(3) << i << " " << this->history[i] << CRLF;
504  }
505  }
506  return LIBCLI::OK;
507 }
508 
509 int
511 (
512  UNUSED ( char* command ),
513  UNUSED ( char* argv[] ),
514  UNUSED ( int argc )
515 )
516 {
517  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
518  set_privilege ( LIBCLI::UNPRIVILEGED );
519  set_configmode ( LIBCLI::MODE_EXEC, "" );
520  return LIBCLI::QUIT;
521 }
522 
523 int
525 (
526  char* command,
527  char* argv[],
528  int argc
529 )
530 {
531  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
532  if ( this->mode == MODE_EXEC )
533  {
534  return internal_quit ( command, argv, argc );
535  }
536  if ( this->mode > MODE_CONFIG )
537  {
538  set_configmode ( MODE_CONFIG, "" );
539  }
540  else
541  {
542  set_configmode ( MODE_EXEC, "" );
543  }
544  return LIBCLI::OK;
545 }
546 
547 int
549 (
550  UNUSED ( char* command ),
551  UNUSED ( char* argv[] ),
552  UNUSED ( int argc )
553 )
554 {
555  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
556  if (argv[argc-1][0] == '?')
557  {
558  client << " " << left << setfill(' ') << setw(22)
559  << "<cr>" << "switch to configure mode" << CRLF;
560  return LIBCLI::OK;
561  }
562  set_configmode ( MODE_CONFIG, "" );
563  return LIBCLI::OK;
564 }
565 
566 CLI::CLI
567 (
568  int fd
569 ): client (fd)
570 {
571  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
572  length = 0;
573  cursor = 0;
574  cmd = 0;
575  in_history = 0;
576  this->commands = 0;
577  this->auth_callback = 0;
578  this->cpp_auth_callback = 0;
579  this->regular_callback = 0;
580  this->enable_callback = 0;
581  this->cpp_enable_callback = 0;
582  this->from_socket = false;
583  int i;
584  for ( i = 0; i < MAX_HISTORY; i++ )
585  {
586  this->history[i] = 0;
587  }
588  register_command ( new Command<CLI> (
589  this,
590  "help",
594  "Description of the interactive help system"
595  ) );
596  register_command ( new Command<CLI> (
597  this,
598  "whoami",
602  "Show who you are"
603  ) );
604  register_command ( new Command<CLI> (
605  this,
606  "quit",
610  "Disconnect"
611  ) );
612  register_command ( new Command<CLI> (
613  this,
614  "exit",
618  "Exit from current mode"
619  ) );
620  register_command ( new Command<CLI> (
621  this,
622  "history",
626  "Show a list of previously run commands"
627  ) );
628  register_command ( new Command<CLI> (
629  this,
630  "enable",
634  "Turn on privileged commands"
635  ) );
636  register_command ( new Command<CLI> (
637  this,
638  "disable",
642  "Turn off privileged commands"
643  ) );
644  register_command ( new Command<CLI> (
645  this,
646  "configure",
650  "Enter configuration mode"
651  ) );
652  this->privilege = this->mode = -1;
653  set_privilege ( LIBCLI::UNPRIVILEGED );
654  set_configmode ( LIBCLI::MODE_EXEC, "" );
655 }
656 
657 CLI::~CLI
658 ()
659 {
660  users.clear();
661  free_history();
662  unregister_all ( 0 );
663 }
664 
665 void
667 ()
668 {
669 }
670 
671 void
673 (
674  Command<CLI> *command
675 )
676 {
677  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
678  Command<CLI> *c, *p = NULL;
679  if ( ! command )
680  {
681  command = this->commands;
682  }
683  if ( ! command )
684  {
685  return;
686  }
687  for ( c = command; c; )
688  {
689  p = c->next;
690  // Unregister all child commands
691  if ( c->children )
692  {
693  unregister_all ( c->children );
694  }
695  if ( c->command )
696  {
697  free_z ( c->command );
698  }
699  if ( c->help )
700  {
701  free_z ( c->help );
702  }
703  free_z ( c );
704  c = p;
705  }
706 }
707 
708 int
710 (
711  char* cmd
712 )
713 {
714  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
715  int i;
716  for ( i = 0; i < MAX_HISTORY; i++ )
717  {
718  if ( !this->history[i] )
719  {
720  if ( i == 0 || strcasecmp ( this->history[i-1], cmd ) )
721  {
722  if ( ! ( this->history[i] = strdup ( cmd ) ) )
723  {
724  in_history = i + 1;
725  return LIBCLI::ERROR_ANY;
726  }
727  }
728  in_history = i + 1;
729  return LIBCLI::OK;
730  }
731  }
732  // No space found, drop one off the beginning of the list
733  free_z ( this->history[0] );
734  for ( i = 0; i < MAX_HISTORY-1; i++ )
735  {
736  this->history[i] = this->history[i+1];
737  }
738  if ( ! ( this->history[MAX_HISTORY - 1] = strdup ( cmd ) ) )
739  {
740  in_history = MAX_HISTORY;
741  return LIBCLI::ERROR_ANY;
742  }
743  return LIBCLI::OK;
744 }
745 
746 void
748 ()
749 {
750  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
751  int i;
752  for ( i = 0; i < MAX_HISTORY; i++ )
753  {
754  if ( this->history[i] )
755  {
756  free_z ( this->history[i] );
757  }
758  }
759 }
760 
761 int
763 (
764  char* line,
765  char* words[],
766  int max_words
767 )
768 {
769  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
770  int nwords = 0;
771  char* p = line;
772  char* word_start = 0;
773  int inquote = 0;
774  while ( *p )
775  {
776  if ( ! isspace ( *p ) )
777  {
778  word_start = p;
779  break;
780  }
781  p++;
782  }
783  while ( nwords < max_words - 1 )
784  {
785  if ( !*p || *p == inquote
786  || ( word_start && !inquote && ( isspace ( *p ) || *p == '|' ) ) )
787  {
788  if ( word_start )
789  {
790  int len = p - word_start;
791  memcpy ( words[nwords] = ( char* ) malloc ( len + 1 ), word_start, len );
792  words[nwords++][len] = 0;
793  }
794  if ( !*p )
795  {
796  break;
797  }
798  if ( inquote )
799  {
800  p++; /* skip over trailing quote */
801  }
802  inquote = 0;
803  word_start = 0;
804  }
805  else if ( *p == '"' || *p == '\'' )
806  {
807  inquote = *p++;
808  word_start = p;
809  }
810  else
811  {
812  if ( !word_start )
813  {
814  if ( *p == '|' )
815  {
816  if ( ! ( words[nwords++] = strdup ( "|" ) ) )
817  {
818  return 0;
819  }
820  }
821  else if ( !isspace ( *p ) )
822  {
823  word_start = p;
824  }
825  }
826  p++;
827  }
828  }
829  words[nwords] = 0;
830  return nwords;
831 }
832 
833 int
835 (
836  Command<CLI> *commands,
837  int num_words,
838  char* words[],
839  int start_word,
840  int filters[]
841 )
842 {
843  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
844  Command<CLI> *c, *again = NULL;
845  int c_words = num_words;
846  if ( filters[0] )
847  {
848  c_words = filters[0];
849  }
850  // Deal with ? for help
851  if ( ! words[start_word] )
852  {
853  return LIBCLI::ERROR_ANY;
854  }
855  if ( words[start_word][strlen ( words[start_word] ) - 1] == '?' )
856  {
857  int l = strlen ( words[start_word] )-1;
858  for ( c = commands; c; c = c->next )
859  {
860  if ( strncasecmp ( c->command, words[start_word], l ) == 0
861  && ( c->have_callback || c->children )
862  && this->privilege >= c->privilege
863  && ( c->mode == this->mode || c->mode == MODE_ANY ) )
864  {
865 
866  client << " "
867  << left << setfill(' ') << setw(20)
868  << c->command
869  << (c->help ? c->help : "") << CRLF;
870  }
871  }
872  if ( commands->parent && commands->parent->have_callback )
873  {
874  client << " "
875  << left << setfill(' ') << setw(20) << "<br>"
876  << (commands->parent->help ? commands->parent->help : "")
877  << CRLF;
878  }
879  return LIBCLI::OK;
880  }
881  for ( c = commands; c; c = c->next )
882  {
883  if ( this->privilege < c->privilege )
884  {
885  continue;
886  }
887  if ( strncasecmp ( c->command, words[start_word], c->unique_len ) )
888  {
889  continue;
890  }
891  if ( strncasecmp ( c->command, words[start_word], strlen ( words[start_word] ) ) )
892  {
893  continue;
894  }
895 AGAIN:
896  if ( c->mode == this->mode || c->mode == MODE_ANY )
897  {
898  int rc = LIBCLI::OK;
899  int f;
900  filter_t** filt = &client.filters;
901  // Found a word!
902  if ( ! c->children )
903  {
904  // Last word
905  if ( ! c->have_callback )
906  {
907  client << UNFILTERED << "No callback for '" << c->command << "'" << CRLF;
908  return LIBCLI::ERROR_ANY;
909  }
910  }
911  else
912  {
913  if ( start_word == c_words - 1 )
914  {
915  if ( c->have_callback )
916  {
917  goto CORRECT_CHECKS;
918  }
919  client << UNFILTERED << "Incomplete command" << CRLF;
920  return LIBCLI::ERROR_ANY;
921  }
922  rc = find_command ( c->children, num_words, words, start_word + 1, filters );
923  if ( rc == LIBCLI::ERROR_ARG )
924  {
925  if ( c->have_callback )
926  {
927  rc = LIBCLI::OK;
928  goto CORRECT_CHECKS;
929  }
930  else
931  {
932  client << UNFILTERED << "Invalid argument '" << words[start_word + 1] << "'"
933  << CRLF;
934  }
935  }
936  return rc;
937  }
938  if ( ! c->have_callback )
939  {
940  client << UNFILTERED << "Internal server error processing '" << c->command << "'"
941  << CRLF;
942  return LIBCLI::ERROR_ANY;
943  }
944 CORRECT_CHECKS:
945  for ( f = 0; rc == LIBCLI::OK && filters[f]; f++ )
946  {
947  int n = num_words;
948  char** argv;
949  int argc;
950  int len;
951  if ( filters[f+1] )
952  {
953  n = filters[f+1];
954  }
955  if ( filters[f] == n - 1 )
956  {
957  client << UNFILTERED << "Missing filter" << CRLF;
958  return LIBCLI::ERROR_ANY;
959  }
960  argv = words + filters[f] + 1;
961  argc = n - ( filters[f] + 1 );
962  len = strlen ( argv[0] );
963  if ( argv[argc - 1][strlen ( argv[argc - 1] ) - 1] == '?' )
964  {
965  if ( argc == 1 )
966  {
967  int i;
968  for ( i = 0; filter_cmds[i].cmd; i++ )
969  {
970  client << " "
971  << left << setfill(' ') << setw(20)
972  << filter_cmds[i].cmd
973  << filter_cmds[i].help << CRLF;
974  }
975  }
976  else
977  {
978  if ( argv[0][0] != 'c' ) // count
979  {
980  client << " WORD" << CRLF;
981  }
982  if ( argc > 2 || argv[0][0] == 'c' ) // count
983  {
984  client << " <cr>" << CRLF;
985  }
986  }
987  return LIBCLI::OK;
988  }
989  if ( argv[0][0] == 'b' && len < 3 ) // [beg]in, [bet]ween
990  {
991  client << UNFILTERED << "Ambiguous filter '" << argv[0] << "' (begin, between)" << CRLF;
992  return LIBCLI::ERROR_ANY;
993  }
994  *filt = ( filter_t* ) calloc ( sizeof ( filter_t ), 1 );
995  if ( !strncmp ( "include", argv[0], len )
996  || !strncmp ( "exclude", argv[0], len ) )
997  {
998  rc = client.match_filter_init ( argc, argv, *filt );
999  }
1000  else if ( !strncmp ( "begin", argv[0], len )
1001  || !strncmp ( "between", argv[0], len ) )
1002  {
1003  rc = client.range_filter_init ( argc, argv, *filt );
1004  }
1005  else if ( !strncmp ( "count", argv[0], len ) )
1006  {
1007  rc = client.count_filter_init ( argc, argv, *filt );
1008  }
1009  else
1010  {
1011  client << UNFILTERED << "Invalid filter '" << argv[0] << "'" << CRLF;
1012  rc = LIBCLI::ERROR_ANY;
1013  }
1014  if ( rc == LIBCLI::OK )
1015  {
1016  filt = & ( *filt )->next;
1017  }
1018  else
1019  {
1020  free_z ( *filt );
1021  *filt = 0;
1022  }
1023  }
1024  if ( rc == LIBCLI::OK )
1025  {
1026  rc = c->exec ( c->command, words + start_word + 1, c_words - start_word - 1 );
1027  }
1028  while ( client.filters )
1029  {
1030  filter_t* filt = client.filters;
1031  // call one last time to clean up
1032  filt->exec ( client, NULL );
1033  client.filters = filt->next;
1034  free_z ( filt );
1035  }
1036  return rc;
1037  }
1038  else if ( this->mode > MODE_CONFIG && c->mode == MODE_CONFIG )
1039  {
1040  // command matched but from another mode,
1041  // remember it if we fail to find correct command
1042  again = c;
1043  }
1044  }
1045  // drop out of config submode if we have matched command on MODE_CONFIG
1046  if ( again )
1047  {
1048  c = again;
1049  set_configmode ( MODE_CONFIG, "" );
1050  goto AGAIN;
1051  }
1052  if ( start_word == 0 )
1053  {
1054  client << UNFILTERED << "Invalid " << (commands->parent ? "argument" : "command")
1055  << " '" << words[start_word] << "'" << CRLF;
1056  }
1057  return LIBCLI::ERROR_ARG;
1058 }
1059 
1060 int
1063  char* command
1064 )
1065 {
1066  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
1067  int r;
1068  unsigned int num_words, i, f;
1069  char* words[128] = {0};
1070  int filters[128] = {0};
1071  if ( ! command )
1072  {
1073  return LIBCLI::ERROR_ANY;
1074  }
1075  while ( isspace ( *command ) )
1076  {
1077  command++;
1078  }
1079  if ( ! *command )
1080  {
1081  return LIBCLI::OK;
1082  }
1083  num_words = parse_line ( command, words, sizeof ( words ) / sizeof ( words[0] ) );
1084  for ( i = f = 0; i < num_words && f < sizeof ( filters ) / sizeof ( filters[0] ) - 1; i++ )
1085  {
1086  if ( words[i][0] == '|' )
1087  {
1088  filters[f++] = i;
1089  }
1090  }
1091  filters[f] = 0;
1092  if ( num_words )
1093  {
1094  r = find_command ( this->commands, num_words, words, 0, filters );
1095  }
1096  else
1097  {
1098  r = LIBCLI::ERROR_ANY;
1099  }
1100  for ( i = 0; i < num_words; i++ )
1101  {
1102  free ( words[i] );
1103  }
1104  if ( r == LIBCLI::QUIT )
1105  {
1106  return r;
1107  }
1108  return LIBCLI::OK;
1109 }
1110 
1111 int
1114  char* command,
1115  char** completions,
1116  int max_completions
1117 )
1118 {
1119  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
1120  Command<CLI> *c;
1121  Command<CLI> *n;
1122  Command<CLI> *p = 0;
1123  int num_words, i, k=0, j;
1124  int num_words_line;
1125  char* words[128] = {0};
1126  int filter = 0;
1127  if ( ! command )
1128  {
1129  return 0;
1130  }
1131  while ( isspace ( *command ) )
1132  {
1133  command++;
1134  }
1135  num_words_line = parse_line ( command, words, sizeof ( words ) /sizeof ( words[0] ) );
1136  num_words = num_words_line;
1137  if ( !command[0] || command[strlen ( command )-1] == ' ' )
1138  {
1139  num_words++;
1140  }
1141  if ( ! num_words )
1142  {
1143  return 0;
1144  }
1145  for ( i = 0; i < num_words; i++ )
1146  {
1147  if ( words[i] && words[i][0] == '|' )
1148  {
1149  filter = i;
1150  }
1151  }
1152  if ( filter ) // complete filters
1153  {
1154  unsigned len = 0;
1155  if ( filter < num_words - 1 ) // filter already completed
1156  {
1157  return 0;
1158  }
1159  if ( filter == num_words - 1 )
1160  {
1161  len = strlen ( words[num_words-1] );
1162  }
1163  for ( i = 0; filter_cmds[i].cmd && k < max_completions; i++ )
1164  {
1165  if ( !len || ( len < strlen ( filter_cmds[i].cmd )
1166  && !strncmp ( filter_cmds[i].cmd, words[num_words - 1], len ) ) )
1167  {
1168  completions[k++] = ( char* ) filter_cmds[i].cmd;
1169  }
1170  }
1171  completions[k] = NULL;
1172  return k;
1173  }
1174  j = 0;
1175  for ( c = this->commands, i = 0; c && i < num_words && k < max_completions; c = n )
1176  {
1177  n = c->next;
1178  if ( this->privilege < c->privilege )
1179  {
1180  continue;
1181  }
1182  if ( c->mode != this->mode && c->mode != MODE_ANY )
1183  {
1184  continue;
1185  }
1186  if ( words[i] && strncasecmp ( c->command, words[i], strlen ( words[i] ) ) )
1187  {
1188  continue;
1189  }
1190  if ( i < num_words - 1 )
1191  {
1192  if ( strlen ( words[i] ) < c->unique_len )
1193  {
1194  continue;
1195  }
1196  j = 0;
1197  p = c;
1198  n = c->children;
1199  i++;
1200  continue;
1201  }
1202  if ( ( (j > 0) || c->next ) || ( ( p != 0 ) && ( p->have_callback ) ) )
1203  { // more than one completion
1204  if ( j == 0 )
1205  {
1206  client << CRLF;
1207  j++;
1208  }
1209  client << " " << left << setfill(' ') << setw(20)
1210  << c->command
1211  << (c->help ? c->help : "") << CRLF;
1212  }
1213  if (strncmp (command, c->command, strlen (c->command)) != 0)
1214  completions[k++] = c->command;
1215  }
1216  if (completions[k-1])
1217  if ( p != 0 )
1218  if ( k != 0 )
1219  if (strncmp (words[num_words_line - 1], completions[k-1], strlen (words[num_words_line - 1])) != 0)
1220  {
1221  if ( p->have_callback )
1222  {
1223  if ( j == 0 )
1224  {
1225  client << CRLF;
1226  }
1227  client << " "
1228  << left << setfill(' ') << setw(20)
1229  << "<br>"
1230  << (p->help ? p->help : "") << CRLF;
1231  k++;
1232  }
1233  }
1234  return k;
1235 }
1236 
1237 void
1240  char* cmd,
1241  int l,
1242  int cursor
1243 )
1244 {
1245  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
1246  int i;
1247  if ( cursor < l )
1248  {
1249  for ( i = 0; i < ( l - cursor ); i++ )
1250  client.put_char (' ');
1251  }
1252  for ( i = 0; i < l; i++ )
1253  {
1254  cmd[i] = '\b';
1255  }
1256  for ( ; i < l * 2; i++ )
1257  {
1258  cmd[i] = ' ';
1259  }
1260  for ( ; i < l * 3; i++ )
1261  {
1262  cmd[i] = '\b';
1263  }
1264  client << cmd << commit;
1265  memset ( cmd, 0, i );
1266  l = cursor = 0;
1267 }
1268 
1269 void
1272  int ( *callback ) ()
1273 )
1274 {
1275  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
1276  this->regular_callback = callback;
1277 }
1278 
1279 const string DES_PREFIX = "{crypt}"; /* to distinguish clear text from DES crypted */
1280 const string MD5_PREFIX = "$1$";
1281 
1282 int
1285  string pass,
1286  string tried_pass
1287 )
1288 {
1289  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
1290  int des;
1291  int idx = 0;
1292  des = ! pass.compare (0, DES_PREFIX.size(), DES_PREFIX);
1293  if (des)
1294  {
1295  idx = sizeof ( DES_PREFIX )-1;
1296  }
1297  if ( des || (! pass.compare (0, MD5_PREFIX.size(), MD5_PREFIX)))
1298  {
1299  tried_pass = crypt ( (char*) tried_pass.c_str(), (char*) pass.c_str() );
1300  }
1301  return ! pass.compare (idx, pass.size(), tried_pass);
1302 }
1303 
1304 void
1306 ()
1307 {
1308  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
1309 
1310 
1311 
1312  if ( hostname != "" )
1313  {
1314  client << hostname << commit;
1315  }
1316  if ( modestring != "" )
1317  {
1318  client << modestring << commit;
1319  }
1320  client.lines_out = 0;
1321  client << prompt << commit;
1322 }
1323 
1324 unsigned char
1326 ()
1327 {
1328  unsigned char c;
1329  client.read_char (c);
1330  client.read_char (c);
1331  /* remap to readline control codes */
1332  switch ( c )
1333  {
1334  case 'A': /* Up */
1335  c = CTRL ( 'P' );
1336  break;
1337  case 'B': /* Down */
1338  c = CTRL ( 'N' );
1339  break;
1340  case 'C': /* Right */
1341  c = CTRL ( 'F' );
1342  break;
1343  case 'D': /* Left */
1344  c = CTRL ( 'B' );
1345  break;
1346  default:
1347  c = 0;
1348  }
1349  return c;
1350 }
1351 
1352 void
1354 {
1355  unsigned char c;
1356  client.read_char (c);
1357  switch (c)
1358  {
1359  case 0xfb: // WILL
1360  case 0xfc: // WON'T
1361  case 0xfd: // DO
1362  case 0xfe: // DON'T
1363  client.read_char (c);
1364  break;
1365  }
1366 }
1367 
1368 int
1371  unsigned char& c
1372 )
1373 {
1374  int ret = 0;
1375  while (ret == 0)
1376  {
1377  ret = client.wait_for_input(1);
1378  if (ret == SOCKET_ERROR)
1379  { // error
1380  if ( RECOVERABLE_ERROR )
1381  continue;
1382  perror ("read");
1383  return ret;
1384  }
1385  if ( ret == 0 )
1386  { /* timeout every second */
1387  if ( this->regular_callback && this->regular_callback() != LIBCLI::OK )
1388  break;
1389  continue;
1390  }
1391  ret = client.read_char (c);
1392  if (ret == SOCKET_ERROR)
1393  {
1394  if ( errno == EINTR )
1395  continue;
1396  return ret;
1397  }
1398  return ret;
1399  }
1400  return ret;
1401 }
1402 
1403 void
1404 CLI::check_enable ( const char* pass )
1405 {
1406  int allowed = 0;
1407  if ( enable_password != "" )
1408  { // check stored static enable password
1409  if ( pass_matches ( enable_password, pass ) )
1410  {
1411  allowed++;
1412  }
1413  }
1414  if ( !allowed )
1415  {
1416  /* check callback */
1417  if (this->enable_callback)
1418  {
1419  if ( enable_callback ( pass ) )
1420  {
1421  allowed++;
1422  }
1423  }
1424  else if (cpp_enable_callback)
1425  {
1426  if ( CALL_MEMBER_FN ((*this), cpp_enable_callback) ( pass ) )
1427  {
1428  allowed++;
1429  }
1430  }
1431  }
1432  if ( allowed )
1433  {
1434  client << "-ok-" << CRLF;
1435  state = LIBCLI::STATE_ENABLE;
1436  set_privilege ( LIBCLI::PRIVILEGED );
1437  }
1438  else
1439  {
1440  client << CRLF << CRLF << "Access denied" << CRLF;
1441  state = LIBCLI::STATE_NORMAL;
1442  }
1443 }
1444 
1445 void
1448  const string& username,
1449  const string& password
1450 )
1451 {
1452  /* require password */
1453  int allowed = 0;
1454  if ( this->auth_callback )
1455  {
1456  if ( this->auth_callback ( username, password ) == LIBCLI::OK )
1457  {
1458  allowed++;
1459  }
1460  }
1461  else if ( this->cpp_auth_callback )
1462  {
1463  if ( CALL_MEMBER_FN ((*this), cpp_auth_callback) ( username, password ) == LIBCLI::OK )
1464  {
1465  allowed++;
1466  }
1467  }
1468  if ( ! allowed )
1469  {
1470  unp_it u = users.find (username);
1471  if (u != users.end())
1472  {
1473  if (pass_matches (u->second, password))
1474  allowed++;
1475  }
1476  }
1477  if ( allowed )
1478  {
1479  client << "-ok-" << CRLF;
1480  this->state = STATE_NORMAL;
1481  client << "type '?' or 'help' for help." << CRLF << CRLF;
1482  }
1483  else
1484  {
1485  client << CRLF << CRLF << "Access denied" << CRLF;
1486  this->state = LIBCLI::STATE_LOGIN;
1487  }
1488  showprompt = true;
1489 }
1490 
1491 void
1494  const unsigned char c
1495 )
1496 {
1497  int back = 0;
1498 
1499  if ( length == 0 || cursor == 0 )
1500  {
1501  client.put_char ('\a');
1502  return;
1503  }
1504  if ( c == CTRL ( 'W' ) ) /* word */
1505  {
1506  int nc = cursor;
1507  while ( nc && cmd[nc - 1] == ' ' )
1508  {
1509  nc--;
1510  back++;
1511  }
1512  while ( nc && cmd[nc - 1] != ' ' )
1513  {
1514  nc--;
1515  back++;
1516  }
1517  }
1518  else /* char */
1519  {
1520  back = 1;
1521  }
1522  while ( back-- )
1523  {
1524  if ( length == cursor )
1525  {
1526  cmd[--cursor] = 0;
1527  if ( this->state != STATE_PASSWORD && this->state != STATE_ENABLE_PASSWORD )
1528  {
1529  client << "\b \b" << commit;
1530  }
1531  }
1532  else
1533  {
1534  int i;
1535  cursor--;
1536  if ( this->state != STATE_PASSWORD && this->state != STATE_ENABLE_PASSWORD )
1537  {
1538  for ( i = cursor; i <= length; i++ )
1539  {
1540  cmd[i] = cmd[i+1];
1541  }
1542  client.put_char ('\b');
1543  client << (cmd + cursor) << commit;
1544  client.put_char (' ');
1545  for ( i = 0; i <= ( int ) strlen ( cmd + cursor ); i++ )
1546  {
1547  client.put_char ('\b');
1548  }
1549  }
1550  }
1551  length--;
1552  }
1553 }
1554 
1555 void
1557 {
1558  switch ( this->state )
1559  {
1560  case STATE_LOGIN:
1561  client << "Username: " << commit;
1562  break;
1563  case STATE_PASSWORD:
1564  client << "Password: " << commit;
1565  break;
1566  case STATE_NORMAL:
1567  case STATE_ENABLE:
1568  show_prompt ();
1569  client << cmd << commit;
1570  if ( cursor < length )
1571  {
1572  int n = length - cursor;
1573  while ( n-- )
1574  {
1575  client.put_char ('\b');
1576  }
1577  }
1578  break;
1579  case STATE_ENABLE_PASSWORD:
1580  client << "Password: " << commit;
1581  break;
1582  }
1583  showprompt = false;
1584 }
1585 
1586 void
1588 ()
1589 {
1590  if ( this->state == STATE_PASSWORD || this->state == STATE_ENABLE_PASSWORD )
1591  {
1592  return;
1593  }
1594  client << CRLF;
1595  show_prompt ();
1596  client << cmd << commit;
1597  for ( int i = 0; i < (length - cursor); i++ )
1598  {
1599  client.put_char ('\b');
1600  }
1601 }
1602 
1603 void
1605 ()
1606 {
1607  if ( this->state == STATE_PASSWORD || this->state == STATE_ENABLE_PASSWORD )
1608  {
1609  memset ( cmd, 0, length );
1610  }
1611  else
1612  {
1613  clear_line ( cmd, length, cursor );
1614  }
1615  length = cursor = 0;
1616 }
1617 
1618 void
1620 ()
1621 {
1622  if ( cursor == length )
1623  {
1624  return;
1625  }
1626  if ( this->state != STATE_PASSWORD && this->state != STATE_ENABLE_PASSWORD )
1627  {
1628  int c;
1629  for ( c = cursor; c < length; c++ )
1630  {
1631  client.put_char (' ');
1632  }
1633  for ( c = cursor; c < length; c++ )
1634  {
1635  client.put_char ('\b');
1636  }
1637  }
1638  memset ( cmd + cursor, 0, length - cursor );
1639  length = cursor;
1640 }
1641 
1642 bool
1644 ()
1645 {
1646  if ( length )
1647  {
1648  return false;
1649  }
1650  strcpy ( cmd, "quit" );
1651  length = cursor = strlen ( cmd );
1652  client << "quit" << CRLF;
1653  return true;
1654 }
1655 
1656 void
1658 ()
1659 {
1660  if ( this->mode != MODE_EXEC )
1661  {
1662  client << CRLF;
1663  clear_line ( cmd, length, cursor );
1664  set_configmode ( MODE_EXEC, "" );
1665  showprompt = true;
1666  }
1667  else
1668  {
1669  client.put_char ('\a');
1670  }
1671 }
1672 
1673 void
1675 ()
1676 {
1677  char* completions[128];
1678  int num_completions = 0;
1679  if ( this->state == STATE_PASSWORD || this->state == STATE_ENABLE_PASSWORD )
1680  {
1681  return;
1682  }
1683  if ( cursor != length )
1684  {
1685  return;
1686  }
1687  num_completions = get_completions ( cmd, completions, 128 );
1688  showprompt = true;
1689  if ( num_completions == 1 )
1690  { // Single completion
1691  for ( ; length > 0; length--, cursor-- )
1692  {
1693  if ( cmd[length-1] == ' ' || cmd[length-1] == '|' )
1694  {
1695  break;
1696  }
1697  client.put_char ('\b');
1698  }
1699  strcpy ( ( cmd + length ), completions[0] );
1700  length += strlen ( completions[0] );
1701  cmd[length++] = ' ';
1702  cursor = length;
1703  client << CRLF;
1704  }
1705  if ( num_completions == 0 )
1706  {
1707  client << CRLF;
1708  client << " <br>" << CRLF;
1709  }
1710 }
1711 
1712 void
1715  const unsigned char& c
1716 )
1717 {
1718  int history_found = 0;
1719  if ( this->state == STATE_PASSWORD || this->state == STATE_ENABLE_PASSWORD )
1720  {
1721  return;
1722  }
1723  if ( c == CTRL ( 'P' ) ) // Up
1724  {
1725  in_history--;
1726  if ( in_history < 0 )
1727  {
1728  for ( in_history = MAX_HISTORY-1; in_history >= 0; in_history-- )
1729  {
1730  if ( this->history[in_history] )
1731  {
1732  history_found = 1;
1733  break;
1734  }
1735  }
1736  }
1737  else
1738  {
1739  if ( this->history[in_history] )
1740  {
1741  history_found = 1;
1742  }
1743  }
1744  }
1745  else // Down
1746  {
1747  in_history++;
1748  if ( in_history >= MAX_HISTORY || !this->history[in_history] )
1749  {
1750  int i = 0;
1751  for ( i = 0; i < MAX_HISTORY; i++ )
1752  {
1753  if ( this->history[i] )
1754  {
1755  in_history = i;
1756  history_found = 1;
1757  break;
1758  }
1759  }
1760  }
1761  else
1762  {
1763  if ( this->history[in_history] )
1764  {
1765  history_found = 1;
1766  }
1767  }
1768  }
1769  if ( history_found && this->history[in_history] )
1770  {
1771  // Show history item
1772  clear_line ( cmd, length, cursor );
1773  memset ( cmd, 0, 4096 );
1774  strncpy ( cmd, this->history[in_history], 4095 );
1775  length = cursor = strlen ( cmd );
1776  client << cmd << commit;
1777  }
1778 }
1779 
1780 void
1782 ()
1783 {
1784  if ( cursor == 0 )
1785  {
1786  return;
1787  }
1788  if ( this->state != STATE_PASSWORD && this->state != STATE_ENABLE_PASSWORD )
1789  {
1790  client.put_char ('\b');
1791  }
1792  cursor--;
1793 }
1794 
1795 void
1797 ()
1798 {
1799  if ( cursor >= length )
1800  {
1801  return;
1802  }
1803  if ( this->state != STATE_PASSWORD && this->state != STATE_ENABLE_PASSWORD )
1804  {
1805  client.put_char (cmd[cursor]);
1806  }
1807  cursor++;
1808 }
1809 
1810 void
1812 ()
1813 {
1814  if ( cursor )
1815  {
1816  if ( this->state != STATE_PASSWORD && this->state != STATE_ENABLE_PASSWORD )
1817  {
1818  client.put_char ('\r');
1819  show_prompt ();
1820  }
1821  cursor = 0;
1822  }
1823 }
1824 
1825 void
1827 ()
1828 {
1829  if ( cursor < length )
1830  {
1831  if ( this->state != STATE_PASSWORD && this->state != STATE_ENABLE_PASSWORD )
1832  {
1833  client << (cmd + cursor) << commit;
1834  }
1835  cursor = length;
1836  }
1837 }
1838 
1839 bool
1842  const unsigned char& c
1843 )
1844 {
1845  cmd[cursor] = c;
1846  if ( length < 4095 )
1847  {
1848  length++;
1849  cursor++;
1850  cmd[cursor] = 0;
1851  return true;
1852  }
1853  client.put_char ('\a');
1854  return false;
1855 }
1856 
1857 void
1860  const unsigned char& c
1861 )
1862 {
1863  int insertmode = 1; // what keypress could change this?
1864  // Middle of text
1865  if ( insertmode )
1866  {
1867  int i;
1868  // Move everything one character to the right
1869  if ( length >= 4094 )
1870  {
1871  length--;
1872  }
1873  for ( i = length; i >= cursor; i-- )
1874  {
1875  cmd[i + 1] = cmd[i];
1876  }
1877  // Write what we've just added
1878  cmd[cursor] = c;
1879  client << (cmd + cursor) << commit;
1880  for ( i = 0; i < ( length - cursor + 1 ); i++ )
1881  {
1882  client.put_char ('\b');
1883  }
1884  length++;
1885  cmd[length] = 0;
1886  }
1887  else
1888  {
1889  cmd[cursor] = c;
1890  }
1891  cursor++;
1892 }
1893 
1894 int
1895 CLI::loop
1896 ()
1897 {
1898  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
1899  unsigned char c;
1900  bool remember_command;
1901 
1902  this->state = STATE_LOGIN;
1903  free_history ();
1904  if ( ( cmd = ( char* ) malloc ( 4096 ) ) == NULL )
1905  {
1906  return LIBCLI::ERROR_ANY;
1907  }
1908  if ( banner != "" )
1909  {
1910  client << banner << CRLF;
1911  }
1912  /* start off in unprivileged mode */
1913  set_privilege ( LIBCLI::UNPRIVILEGED );
1914  set_configmode ( LIBCLI::MODE_EXEC, "" );
1915  /* no auth required? */
1916  if ( users.empty() && !this->auth_callback && !this->cpp_auth_callback )
1917  {
1918  this->state = STATE_NORMAL;
1919  }
1920  showprompt = true;
1921  cmd[0] = 0; // clear command buffer
1922  while ( 1 )
1923  {
1924  remember_command = false;
1925  if (showprompt == true)
1926  {
1927  prompt_user ();
1928  }
1929  if (get_input (c) == SOCKET_ERROR)
1930  {
1931  length = -1;
1932  break;
1933  }
1934  switch (c)
1935  {
1936  case 255:
1937  handle_telnet_option();
1938  continue;
1939  case 27: // handle ANSI arrows
1940  c = map_esc ();
1941  switch (c)
1942  {
1943  case CTRL ( 'B' ): // cursor left
1944  cursor_left ();
1945  continue;
1946  case CTRL ( 'F' ): // cursor right
1947  cursor_right ();
1948  continue;
1949  case CTRL ( 'P' ): // Cursor Up
1950  case CTRL ( 'N' ): // Cursor Down
1951  do_history (c);
1952  continue;
1953  }
1954  continue;
1955  case '\n':
1956  case '\r':
1957  showprompt = true;
1958  if ( state != STATE_PASSWORD && state != STATE_ENABLE_PASSWORD )
1959  client << CRLF;
1960  break;
1961  case 0:
1962  continue;
1963  case CTRL ( 'C' ):
1964  client.put_char ('\a');
1965  continue;
1966  case CTRL ( 'W' ): // back word
1967  case CTRL ( 'H' ): // backspace
1968  case 0x7f: // delete
1969  delete_backwards (c);
1970  continue;
1971  case CTRL ( 'L' ): // redraw
1972  redraw_line ();
1973  continue;
1974  case CTRL ( 'U' ): // clear line
1975  clear_line ();
1976  continue;
1977  case CTRL ( 'K' ):
1978  clear_to_eol ();
1979  continue;
1980  case CTRL ( 'D' ): // EOT
1981  if (try_logout () == true)
1982  break;
1983  continue;
1984  case CTRL ( 'Z' ): // leave config mode
1985  leave_config_mode ();
1986  continue;
1987  case CTRL ( 'I' ): // TAB completion
1988  list_completions ();
1989  continue;
1990  case CTRL ( 'A' ): // start of line
1991  jump_start_of_line ();
1992  continue;
1993  case CTRL ( 'E' ): // end of line
1994  jump_end_of_line ();
1995  continue;
1996  default: // normal character typed
1997  if ( cursor == length )
1998  {
1999  if (! append (c))
2000  {
2001  continue;
2002  }
2003  }
2004  else
2005  { // Middle of text
2006  insert (c);
2007  }
2008  if ( this->state != STATE_PASSWORD && this->state != STATE_ENABLE_PASSWORD )
2009  {
2010  if ( (c == '?') && (cursor == length) )
2011  {
2012  client << CRLF;
2013  remember_command = true;
2014  showprompt = true;
2015  break;
2016  }
2017  client.put_char (c);
2018  }
2019  continue;
2020  } // switch
2021  if ( !strcasecmp ( cmd, "quit" ) )
2022  {
2023  break;
2024  }
2025  if ( this->state == STATE_LOGIN )
2026  {
2027  if ( length == 0 )
2028  {
2029  continue;
2030  }
2031  /* require login */
2032  username = cmd;
2033  this->state = STATE_PASSWORD;
2034  showprompt = true;
2035  }
2036  else if ( this->state == STATE_PASSWORD )
2037  {
2038  check_user_auth (username, cmd);
2039  }
2040  else if ( this->state == LIBCLI::STATE_ENABLE_PASSWORD )
2041  {
2042  check_enable (cmd);
2043  }
2044  else
2045  {
2046  if ( length == 0 )
2047  {
2048  continue;
2049  }
2050  if ( cmd[length - 1] != '?' && strcasecmp ( cmd, "history" ) != 0 )
2051  {
2052  add_history ( cmd );
2053  }
2054  if ( run_command ( cmd ) == LIBCLI::QUIT )
2055  {
2056  break;
2057  }
2058  if ( (c == '?') && (cursor == length) )
2059  {
2060  cursor = length -1;
2061  length = cursor;
2062  cmd[length] = 0;
2063  }
2064  showprompt = true;
2065  }
2066  if (remember_command == false)
2067  {
2068  memset ( cmd, 0, 4096 );
2069  length = 0;
2070  cursor = 0;
2071  }
2072  }
2073  free_z ( cmd );
2074  return LIBCLI::OK;
2075 }
2076 
2077 int
2078 CLI::file
2080  FILE* fh,
2081  int privilege,
2082  int mode
2083 )
2084 {
2085  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
2086  int oldpriv = set_privilege ( privilege );
2087  int oldmode = set_configmode ( mode, "" );
2088  char buf[4096];
2089  while ( 1 )
2090  {
2091  char* p;
2092  char* cmd;
2093  char* end;
2094  if ( fgets ( buf, sizeof ( buf ), fh ) == NULL )
2095  {
2096  break; /* end of file */
2097  }
2098  if ( ( p = strpbrk ( buf, "#\r\n" ) ) )
2099  {
2100  *p = 0;
2101  }
2102  cmd = buf;
2103  while ( isspace ( *cmd ) )
2104  {
2105  cmd++;
2106  }
2107  if ( !*cmd )
2108  {
2109  continue;
2110  }
2111  for ( p = end = cmd; *p; p++ )
2112  {
2113  if ( !isspace ( *p ) )
2114  {
2115  end = p;
2116  }
2117  }
2118  *++end = 0;
2119  if ( strcasecmp ( cmd, "quit" ) == 0 )
2120  {
2121  break;
2122  }
2123  if ( run_command ( cmd ) == LIBCLI::QUIT )
2124  {
2125  break;
2126  }
2127  }
2128  set_privilege ( oldpriv );
2129  set_configmode ( oldmode, "" /* didn't save desc */ );
2130  return LIBCLI::OK;
2131 }
2132 
2133 int
2134 CLI::pager
2135 ()
2136 {
2137  unsigned char c;
2138  bool done = false;
2139  client << UNFILTERED << "--- more ---" << commit;
2140  c = ' ';
2141  while ( done == false )
2142  {
2143  client.read_char (c);
2144  if (c == 'q')
2145  {
2146  client.lines_out = 0;
2147  client.put_char ('\r');
2148  return 1;
2149  }
2150  if (c == ' ')
2151  {
2152  client.lines_out = 0;
2153  done = true;
2154  }
2155  if ((c == '\r') || (c == '\n'))
2156  { // show next line and reprompt for more
2157  client.lines_out--;
2158  done = true;
2159  }
2160  }
2161  #if 0
2162  client.put_char ('\r');
2163  client << UNFILTERED << " " << commit;
2164  #endif
2165  client.put_char ('\r');
2166  return 0;
2167 }
2168 
2169 bool
2171 ()
2172 {
2173  // ask for a key after 20 lines of output
2174  // FIXME: make this configurable
2175  if (client.lines_out > client.max_screen_lines)
2176  {
2177  if (pager ())
2178  {
2179  return 1;
2180  }
2181  }
2182  return 0;
2183 }
2184 
2185 int
2186 CLI::print
2188  const char* format,
2189  ...
2190 )
2191 {
2192  DEBUG d ( __FUNCTION__,__FILE__,__LINE__ );
2193  if (! format)
2194  {
2195  return 0;
2196  }
2197  va_list ap;
2198  va_start ( ap, format );
2199  char buf[5000];
2200  vsprintf (buf, format, ap);
2201  va_end ( ap );
2202  client << buf << CRLF;
2203  return check_pager();
2204 }
2205 
2206 }; // namespace LIBCLI
2207 
#define RECOVERABLE_ERROR
Definition: netSocket.h:79
int print(const char *format,...)
Definition: libcli.cxx:2187
int int_configure_terminal(UNUSED(char *command), UNUSED(char *argv[]), UNUSED(int argc))
Definition: libcli.cxx:549
unsigned int unique_len
Definition: command.hxx:75
void leave_config_mode()
Definition: libcli.cxx:1658
void jump_start_of_line()
Definition: libcli.cxx:1812
void unregister_all(Command< CLI > *command)
Definition: libcli.cxx:673
void set_auth_callback(c_auth_func callback)
Definition: libcli.cxx:69
const string DES_PREFIX
Definition: libcli.cxx:1279
int parse_line(char *line, char *words[], int max_words)
Definition: libcli.cxx:763
void set_prompt(const string &prompt)
Definition: libcli.cxx:175
#define free_z(p)
Definition: common.hxx:37
char * crypt(char *pw, char *salt)
Definition: crypt-win.c:325
static char f[32]
Definition: crypt-win.c:225
const int MAX_HISTORY
Definition: common.hxx:83
int internal_disable(UNUSED(char *command), UNUSED(char *argv[]), UNUSED(int argc))
Definition: libcli.cxx:437
int internal_whoami(UNUSED(char *command), UNUSED(char *argv[]), UNUSED(int argc))
Definition: libcli.cxx:477
#define UNUSED(d)
Definition: clitest.cxx:17
const char * help
Definition: filter.hxx:29
Client & commit(Client &out)
Definition: cli_client.cxx:430
int pager()
Definition: libcli.cxx:2135
int read_char(unsigned char &c)
Definition: cli_client.cxx:160
void jump_end_of_line()
Definition: libcli.cxx:1827
int find_command(Command< CLI > *commands, int num_words, char *words[], int start_word, int filters[])
Definition: libcli.cxx:835
void delete_backwards(const unsigned char c)
Definition: libcli.cxx:1493
int internal_enable(UNUSED(char *command), UNUSED(char *argv[]), UNUSED(int argc))
Definition: libcli.cxx:410
int file(FILE *fh, int privilege, int mode)
Definition: libcli.cxx:2079
void clear_line()
Definition: libcli.cxx:1605
filter_t * next
Definition: filter.hxx:41
void insert(const unsigned char &c)
Definition: libcli.cxx:1859
filter_cmds_t filter_cmds[]
Definition: libcli.cxx:57
bool check_pager()
Definition: libcli.cxx:2171
bool have_callback
Definition: command.hxx:80
int get_input(unsigned char &c)
Definition: libcli.cxx:1370
void free_command(Command< CLI > *cmd)
Definition: libcli.cxx:352
void prompt_user()
Definition: libcli.cxx:1556
const char * cmd
Definition: filter.hxx:28
int exec(Client &Instance, char *cmd)
Definition: filter.cxx:33
int loop()
Definition: libcli.cxx:1896
void register_command(Command< CLI > *command, Command< CLI > *parent=0)
Definition: libcli.cxx:293
size_t lines_out
Definition: cli_client.hxx:68
Command * next
Definition: command.hxx:76
int build_shortest(Command< CLI > *commands)
Definition: libcli.cxx:185
void redraw_line()
Definition: libcli.cxx:1588
void show_prompt()
Definition: libcli.cxx:1306
bool try_logout()
Definition: libcli.cxx:1644
int set_configmode(int mode, const string &config_desc)
Definition: libcli.cxx:262
#define SOCKET_ERROR
Definition: netSocket.h:80
int exec(char *command, char **argv, int argc)
Definition: command.hxx:243
Client & CRLF(Client &out)
Definition: cli_client.cxx:471
void set_modestring(const string &modestring)
Definition: libcli.cxx:252
bool append(const unsigned char &c)
Definition: libcli.cxx:1841
int internal_help(UNUSED(char *command), UNUSED(char *argv[]), UNUSED(int argc))
Definition: libcli.cxx:451
void set_enable_callback(c_enable_func callback)
Definition: libcli.cxx:91
void handle_telnet_option()
Definition: libcli.cxx:1353
void set_banner(const string &banner)
Definition: libcli.cxx:155
void check_enable(const char *pass)
Definition: libcli.cxx:1404
CLI(int fd)
Definition: libcli.cxx:567
Client & UNFILTERED(Client &out)
Definition: cli_client.cxx:485
int get_completions(char *command, char **completions, int max_completions)
Definition: libcli.cxx:1113
void clear_to_eol()
Definition: libcli.cxx:1620
void do_history(const unsigned char &c)
Definition: libcli.cxx:1714
int add_history(char *cmd)
Definition: libcli.cxx:710
void check_user_auth(const string &username, const string &password)
Definition: libcli.cxx:1447
int internal_exit(char *command, char *argv[], int argc)
Definition: libcli.cxx:525
int run_command(char *command)
Definition: libcli.cxx:1062
void put_char(const char &c)
Definition: cli_client.cxx:178
void allow_enable(const string &password)
Definition: libcli.cxx:130
Command * children
Definition: command.hxx:77
Definition: debug.hxx:31
int internal_quit(UNUSED(char *command), UNUSED(char *argv[]), UNUSED(int argc))
Definition: libcli.cxx:511
void cursor_left()
Definition: libcli.cxx:1782
void destroy()
Definition: libcli.cxx:667
void deny_user(const string &username)
Definition: libcli.cxx:140
int internal_history(UNUSED(char *command), UNUSED(char *argv[]), UNUSED(int argc))
Definition: libcli.cxx:490
#define CLI_TRACE
Definition: debug.hxx:22
PRIVLEVEL privilege
Definition: command.hxx:73
t_Point3D length(const Point3D &P)
Return the length of P.
int set_privilege(int privilege)
Definition: libcli.cxx:235
void cursor_right()
Definition: libcli.cxx:1797
int pass_matches(string pass, string tried_pass)
Definition: libcli.cxx:1284
void list_completions()
Definition: libcli.cxx:1675
void free_history()
Definition: libcli.cxx:748
void allow_user(const string &username, const string &password)
Definition: libcli.cxx:113
unsigned char map_esc()
Definition: libcli.cxx:1326
void set_hostname(const string &hostname)
Definition: libcli.cxx:165
std::map< string, string >::iterator unp_it
Definition: libcli.hxx:40
const string MD5_PREFIX
Definition: libcli.cxx:1280
int unregister_command(char *command)
Definition: libcli.cxx:374
void regular(int(*callback)())
Definition: libcli.cxx:1271
#define CALL_MEMBER_FN(object, ptrToMember)
Definition: common.hxx:36
Command * parent
Definition: command.hxx:78
char * command
Definition: command.hxx:71