/** Sets the successor of a Menuitem item to itemid (for wizzards) i.e. the * menuitem to go to after hitting "Enter" on item. Checks that a matching * menu item can be found. Checks if item is not a menu. (If you would * redefine the meaning of "Enter" for a menu it would not be useful * anymore.) * * @return 0 on success and -1 otherwise */ int set_successor(MenuItem *item, char *itemid, Client *c) { // no sense to call this function on a null item assert(item != NULL); debug(RPT_DEBUG, "%s(%s, %s, %d)", __FUNCTION__, item->id, itemid, c->sock); // handle these special if (strcmp("_quit_", itemid) != 0 && strcmp("_close_", itemid) != 0 && strcmp("_none_", itemid) != 0) { MenuItem *successor = menuitem_search(itemid, c); if ( ! successor) { sock_printf_error(c->sock, "Cannot find successor '%s'" " for item '%s'\n", itemid, item->id); return -1; } } if (item->type == MENUITEM_MENU) { sock_printf_error(c->sock, "Cannot set successor of '%s':" " wrong type '%s'\n", item->id, menuitem_type_to_typename(item->type)); return -1; } debug (RPT_DEBUG, "%s( Client [%d], ... )" " setting '%s's successor from '%s' to '%s'", __FUNCTION__, c->sock, item->id, item->successor_id, itemid); if (item->successor_id) free(item->successor_id); item->successor_id = strdup(itemid); return 0; }
/** Sets the predecessor of a Menuitem item to itemid (for wizzards) * i.e. the menuitem to go to after hitting "Enter" on item. * * @return 0 on success and -1 otherwise */ int set_predecessor(MenuItem *item, char *itemid, Client *c) { // no sense to call this function on a null item assert(item != NULL); debug(RPT_DEBUG, "%s(%s, %s, %d)", __FUNCTION__, item->id, itemid, c->sock); // handle these special if (strcmp("_quit_", itemid) != 0 && strcmp("_close_", itemid) != 0 && strcmp("_none_", itemid) != 0) { MenuItem *predecessor = menuitem_search(itemid, c); if ( ! predecessor) { sock_printf_error(c->sock, "Cannot find predecessor '%s'" " for item '%s'\n", itemid, item->id); return -1; } } debug(RPT_DEBUG, "%s( Client [%d], ... )" " setting '%s's predecessor from '%s' to '%s'", __FUNCTION__, c->sock, item->id, item->predecessor_id, itemid); if (item->predecessor_id) free(item->predecessor_id); item->predecessor_id = strdup(itemid); return 0; }
/** * The sleep_func was intended to make the server sleep for some seconds. * This function is currently ignored as making the server sleep actually * stalls it and disrupts other clients. * *\verbatim * Usage: sleep <seconds> *\endverbatim */ int sleep_func(Client *c, int argc, char **argv) { int secs; long out; char *endptr; #define MAX_SECS 60 #define MIN_SECS 1 if (c->state != ACTIVE) return 1; if (argc != 2) { sock_send_error(c->sock, "Usage: sleep <secs>\n"); return 0; } /* set errno to be able to detect errors in strtol() */ errno = 0; out = strtol(argv[1], &endptr, 0); /* From the man page for strtol(3) * * In particular, if *nptr is not `\0' but **endptr is * `\0' on return, the entire string is valid. * * In this case, argv[1] is *nptr, and &endptr is **endptr. */ if (errno) { sock_printf_error(c->sock, "number argument: %s\n", strerror(errno)); return 0; } else if ((*argv[1] != '\0') && (*endptr == '\0')) { /* limit seconds to range: MIN_SECS - MAX_SECS */ out = (out > MAX_SECS) ? MAX_SECS : out; out = (out < MIN_SECS) ? MIN_SECS : out; secs = out; } else { sock_send_error(c->sock, "invalid parameter...\n"); return 0; } /* Repeat until no more remains - should normally be zero * on exit the first time...*/ sock_printf(c->sock, "sleeping %d seconds\n", secs); /* whoops.... if this takes place as planned, ALL screens * will "freeze" for the alloted time... * * while ((secs = sleep(secs)) > 0) */ ; sock_send_error(c->sock, "ignored (not fully implemented)\n"); return 0; }
/** * Sets the state of the output port (such as on MtxOrb LCDs) * *\verbatim * Usage: output <on|off|int> *\endverbatim */ int output_func(Client *c, int argc, char **argv) { if (c->state != ACTIVE) return 1; if (argc != 2) { sock_send_error(c->sock, "Usage: output {on|off|<num>}\n"); return 0; } if (0 == strcmp(argv[1], "on")) output_state = ALL_OUTPUTS_ON; else if (0 == strcmp(argv[1], "off")) output_state = ALL_OUTPUTS_OFF; else { long out; char *endptr; /* Note that there is no valid range set for * output_state; thus a value in the 12 digits * is not considered out of range. */ /* set errno to be able to detect errors in strtol() */ errno = 0; out = strtol(argv[1], &endptr, 0); if (errno) { sock_printf_error(c->sock, "number argument: %s\n", strerror(errno)); return 0; } else if ((*argv[1] != '\0') && (*endptr == '\0')) { output_state = out; } else { sock_send_error(c->sock, "invalid parameter...\n"); return 0; } } sock_send_string(c->sock, "success\n"); /* Makes sense to me to set the output immediately; * however, the outputs are currently set in * draw_screen(screen *s, int timer) * Whatever for? */ /* drivers_output(output_state); */ report(RPT_NOTICE, "output states changed"); return 0; }
static int parse_message(const char *str, Client *c) { typedef enum { ST_INITIAL, ST_WHITESPACE, ST_ARGUMENT, ST_FINAL } State; State state = ST_INITIAL; int error = 0; char quote = '\0'; /* The quote used to open a quote string */ int pos = 0; char *arg_space; int argc = 0; char *argv[MAX_ARGUMENTS]; int argpos = 0; CommandFunc function = NULL; debug(RPT_DEBUG, "%s(str=\"%.120s\", client=[%d])", __FUNCTION__, str, c->sock); /* We will create a list of strings that is shorter or equally long as * the original string str. */ arg_space = malloc(strlen(str)+1); if (arg_space == NULL) { report(RPT_ERR, "%s: Could not allocate memory", __FUNCTION__); sock_send_error(c->sock, "error allocating memory!\n"); } argv[0] = arg_space; while ((state != ST_FINAL) && !error) { char ch = str[pos++]; switch (state) { case ST_INITIAL: case ST_WHITESPACE: if (is_whitespace(ch)) break; if (is_final(ch)) { state = ST_FINAL; break; } /* otherwise fall through */ state = ST_ARGUMENT; case ST_ARGUMENT: if (is_final(ch)) { if (quote) error = 2; if (argc >= MAX_ARGUMENTS-1) { error = 1; } else { argv[argc][argpos] = '\0'; argv[argc+1] = argv[argc] + argpos + 1; argc++; argpos = 0; } state = ST_FINAL; } else if (ch == '\\') { if (str[pos]) { /* We solve quoted chars here right away */ const char escape_chars[] = "nrt"; const char escape_trans[] = "\n\r\t"; char *p = strchr(escape_chars, str[pos]); /* Is it wise to have the characters \n, \r & \t expanded ? * Can the displays deal with them ? */ if (p != NULL) { /* Insert a replacement for the code */ argv[argc][argpos++] = escape_trans[p - escape_chars]; } else { /* Copy char literally */ argv[argc][argpos++] = str[pos]; } pos++; } else { error = 2; /* alternative: argv[argc][argpos++] = ch; */ if (argc >= MAX_ARGUMENTS-1) { error = 1; } else { argv[argc][argpos] = '\0'; argv[argc+1] = argv[argc] + argpos + 1; argc++; argpos = 0; } state = ST_FINAL; } } else if (is_opening_quote(ch, quote)) { quote = ch; } else if (is_closing_quote(ch, quote)) { quote = '\0'; if (argc >= MAX_ARGUMENTS-1) { error = 1; } else { argv[argc][argpos] = '\0'; argv[argc+1] = argv[argc] + argpos + 1; argc++; argpos = 0; } state = ST_WHITESPACE; } else if (is_whitespace(ch) && (quote == '\0')) { if (argc >= MAX_ARGUMENTS-1) { error = 1; } else { argv[argc][argpos] = '\0'; argv[argc+1] = argv[argc] + argpos + 1; argc++; argpos = 0; } state = ST_WHITESPACE; } else { argv[argc][argpos++] = ch; } break; case ST_FINAL: /* This will never be reached */ break; } } if (argc < MAX_ARGUMENTS) argv[argc] = NULL; else error = 1; if (error) { sock_send_error(c->sock, "Could not parse command\n"); free(arg_space); return 0; } #if 0 /* show what we have parsed */ int i; for (i = 0; i < argc; i++) { printf("%s%c", argv[i], (i == argc-1) ? '\n' : ' '); } #endif /* Now find and call the appropriate function...*/ function = get_command_function(argv[0]); if (function != NULL) { error = function(c, argc, argv); if (error) { sock_printf_error(c->sock, "Function returned error \"%.40s\"\n", argv[0]); report(RPT_WARNING, "Command function returned an error after command from client on socket %d: %.40s", c->sock, str); } } else { sock_printf_error(c->sock, "Invalid command \"%.40s\"\n", argv[0]); report(RPT_WARNING, "Invalid command from client on socket %d: %.40s", c->sock, str); } free(arg_space); return 0; }
/************************************************************************** * menu_set_item_func * Sets the info about a menu item * * For example, text displayed, value, etc... * * Usage: menu_set_item <menuid> <itemid> {<option>}+ * The following parameters can be set per item: * (you should include the - in the option) * * For all types: * -text "text" ("") * Sets the visible text. * -is_hidden false|true (false) * If the item currently should not appear in a menu. * -prev id () * Sets the predecessor of this item (what happens after "Escape") * * For all except menus: * -next id () * Sets the successor of this item (what happens after "Enter") * * action: * -menu_result none|close|quit (none) * Sets what to do with the menu when this action is selected: * - none: the menu stays as it is. * - close: the menu closes and returns to a higher level. * - quit: quits the menu completely so you can foreground your app. * * checkbox: * -value off|on|gray (off) * Sets its current value. * -allow_gray false|true (false) * Sets if a grayed checkbox is allowed. * * ring: * -value <int> (0) * Sets the index in the stringlist that is currently selected. * -strings <string> (empty) * The subsequent strings that can be selected. They should be * tab-separated in ONE string. * * slider: * -value <int> (0) * Sets its current value. * -mintext <string> ("") * -maxtex <string> ("") * Text at the minimal and maximal side. On small displays these might * not be displayed. * -minvalue <int> (0) * -maxvalue <int> (100) * The minimum and maximum value of the slider. * -stepsize <int> (1) * The stepsize of the slider. If you use 0, you can control it yourself * completely. * * numeric: * -value <int> (0) * Sets its current value. * -minvalue <int> (0) * -maxvalue <int> (100) * The minimum and maximum value that are allowed. If you make one of * them negative, the user will be able to enter negative numbers too. * Maybe floats will work too in the future. * * alpha: * -value <string> * Sets its current value. ("") * -password_char <char> (none) * -minlength <int> (0) * -maxlength <int> (10) * Set the minimum and maximum allowed length. * -allow_caps false|true (true) * -allow_noncaps false|true (false) * -allow_numbers false|true (true) * Allows these groups of characters. * -allowed_extra <string> ("") * The chars in this string are also allowed. * * ip: * -value <string> * Sets its current value. ("") * -v6 false|true * * Hmm, this is getting very big. We might need a some real parser after all. */ int menu_set_item_func (Client * c, int argc, char **argv) { typedef enum AttrType { NOVALUE, BOOLEAN, CHECKBOX_VALUE, SHORT, INT, FLOAT, STRING } AttrType; /* This table generalizes the options. * The table lists which options can exist for which menu items, * what kind of parameter they should have and where this scanned * parameter should be stored. */ struct OptionTable { MenuItemType menuitem_type; /* For what MenuItem type is the option ? Use -1 for ALL types. */ char * name; /* The option concerned */ AttrType attr_type; /* Type of value */ int attr_offset; /* Where to put the value in the structure. Use -1 to process it yourself. */ /* Watch out with STRING, it will free() the current value * and reallocate for the new value !! If you don't want * that, use -1 for offset, to process it yourself. */ } option_table[] = { { -1, "text", STRING, offsetof(MenuItem,text) }, { -1, "is_hidden", BOOLEAN, offsetof(MenuItem,is_hidden) }, { -1, "prev", STRING, -1 }, { -1, "next", STRING, -1 }, { MENUITEM_ACTION, "menu_result", STRING, -1 }, { MENUITEM_CHECKBOX, "value", CHECKBOX_VALUE, offsetof(MenuItem,data.checkbox.value) }, { MENUITEM_CHECKBOX, "allow_gray", BOOLEAN, offsetof(MenuItem,data.checkbox.allow_gray) }, { MENUITEM_RING, "value", SHORT, offsetof(MenuItem,data.ring.value) }, { MENUITEM_RING, "strings", STRING, -1 }, { MENUITEM_SLIDER, "value", INT, offsetof(MenuItem,data.slider.value) }, { MENUITEM_SLIDER, "minvalue", INT, offsetof(MenuItem,data.slider.minvalue) }, { MENUITEM_SLIDER, "maxvalue", INT, offsetof(MenuItem,data.slider.maxvalue) }, { MENUITEM_SLIDER, "stepsize", INT, offsetof(MenuItem,data.slider.stepsize) }, { MENUITEM_SLIDER, "mintext", STRING, offsetof(MenuItem,data.slider.mintext) }, { MENUITEM_SLIDER, "maxtext", STRING, offsetof(MenuItem,data.slider.maxtext) }, { MENUITEM_NUMERIC, "value", INT, offsetof(MenuItem,data.numeric.value) }, { MENUITEM_NUMERIC, "minvalue", INT, offsetof(MenuItem,data.numeric.minvalue) }, { MENUITEM_NUMERIC, "maxvalue", INT, offsetof(MenuItem,data.numeric.maxvalue) }, /*{ MENUITEM_NUMERIC, "allow_decimals",BOOLEAN, offsetof(MenuItem,data.numeric.allow_decimals) },*/ { MENUITEM_ALPHA, "value", STRING, -1 /*offsetof(MenuItem,data.alpha.value)*/ }, { MENUITEM_ALPHA, "minlength", SHORT, offsetof(MenuItem,data.alpha.minlength) }, { MENUITEM_ALPHA, "maxlength", SHORT, offsetof(MenuItem,data.alpha.maxlength) }, { MENUITEM_ALPHA, "password_char",STRING, -1 }, { MENUITEM_ALPHA, "allow_caps", BOOLEAN, offsetof(MenuItem,data.alpha.allow_caps) }, { MENUITEM_ALPHA, "allow_noncaps",BOOLEAN, offsetof(MenuItem,data.alpha.allow_noncaps) }, { MENUITEM_ALPHA, "allow_numbers",BOOLEAN, offsetof(MenuItem,data.alpha.allow_numbers) }, { MENUITEM_ALPHA, "allowed_extra",STRING, offsetof(MenuItem,data.alpha.allowed_extra) }, { MENUITEM_IP, "v6", BOOLEAN, offsetof(MenuItem,data.ip.v6) }, { MENUITEM_IP, "value", STRING, -1 /*offsetof(MenuItem,data.ip.value)*/ }, { -1, NULL, -1, -1 } }; debug (RPT_DEBUG, "%s( Client [%d]: %s)", __FUNCTION__, c->sock, argv2string(argc, argv)); bool bool_value = false; CheckboxValue checkbox_value = CHECKBOX_OFF; short short_value = 0; int int_value = 0; float float_value = 0; char * string_value = NULL; Menu * menu; MenuItem * item; char * menu_id; char * item_id; int argnr; if (!c->ack) return 1; if (argc < 4 ) { sock_send_error(c->sock, "Usage: menu_set_item <menuid> <itemid> {<option>}+\n"); return 0; } menu_id = argv[1]; item_id = argv[2]; if ( menu_id[0] == 0 ) { /* No menu specified = client's main menu */ menu = c->menu; } else { /* A specified menu */ menu = menu_find_item (c->menu, menu_id, true); } if (!menu) { sock_send_error(c->sock, "Cannot find menu id\n"); return 0; } item = menu_find_item (c->menu, item_id, true); if (!item) { sock_send_error(c->sock, "Cannot find item\n"); return 0; } /* Scan all arguments */ for( argnr = 3; argnr < argc; argnr ++) { int option_nr = -1; int found_option_name = 0; int error = 0; void * location; char * p; /* Find the option in the table */ if( argv[argnr][0] == '-' ) { int i; for( i=0; option_table[i].name; i++ ) { if( strcmp( argv[argnr]+1, option_table[i].name ) == 0 ) { found_option_name = 1; if( item->type == option_table[i].menuitem_type || option_table[i].menuitem_type == -1 ) { option_nr = i; } } } } else { sock_printf_error(c->sock, "Found non-option: \"%.40s\"\n", argv[argnr]); continue; /* Skip to next arg */ } if( option_nr == -1 ) { if( found_option_name ) { sock_printf_error(c->sock, "Option not valid for menuitem type: \"%.40s\"\n", argv[argnr]); } else { sock_printf_error(c->sock, "Unknown option: \"%.40s\"\n", argv[argnr]); } continue; /* Skip to next arg */ } /* OK, we now know we have an option that is valid for the item type. */ /* Check for value */ if( option_table[option_nr].attr_type != NOVALUE ) { if( argnr + 1 >= argc ) { sock_printf_error(c->sock, "Missing value at option: \"%.40s\"\n", argv[argnr]); continue; /* Skip to next arg (probably is not existing :) */ } } /* Process the value that goes with the option */ location = (void*)item + option_table[option_nr].attr_offset; switch( option_table[option_nr].attr_type ) { case NOVALUE: break; case BOOLEAN: if( strcmp( argv[argnr+1], "false" ) == 0 ) { bool_value = false; } else if( strcmp( argv[argnr+1], "true" ) == 0 ) { bool_value = true; } else { error = 1; break; } if( option_table[option_nr].attr_offset != -1 ) { *(bool *)location = bool_value; } break; case CHECKBOX_VALUE: if( strcmp( argv[argnr+1], "off" ) == 0 ) { checkbox_value = CHECKBOX_OFF; } else if( strcmp( argv[argnr+1], "on" ) == 0 ) { checkbox_value = CHECKBOX_ON; } else if( strcmp( argv[argnr+1], "gray" ) == 0 ) { checkbox_value = CHECKBOX_GRAY; } else { error = 1; break; } if( option_table[option_nr].attr_offset != -1 ) { *(CheckboxValue *)location = checkbox_value; } break; case SHORT: short_value = strtol( argv[argnr+1], &p, 0 ); if( argv[argnr+1][0] == '\0' || *p != '\0' ) { error = 1; break; } if( option_table[option_nr].attr_offset != -1 ) { *(short*)location = short_value; } break; case INT: int_value = strtol( argv[argnr+1], &p, 0 ); if( argv[argnr+1][0] == '\0' || *p != '\0' ) { error = 1; break; } if( option_table[option_nr].attr_offset != -1 ) { *(int*)location = int_value; } break; case FLOAT: float_value = strtod( argv[argnr+1], &p ); if( argv[argnr+1][0] == '\0' || *p != '\0' ) { error = 1; break; } if( option_table[option_nr].attr_offset != -1 ) { *(float*)location = float_value; } break; case STRING: string_value = argv[argnr+1]; if( option_table[option_nr].attr_offset != -1 ) { free( *(char**)location ); *(char**)location = strdup( string_value ); } else if (strcmp(argv[argnr], "-prev") == 0) { set_predecessor(item, string_value, c); } else if (strcmp(argv[argnr], "-next") == 0) { set_successor(item, string_value, c); } break; } switch( error ) { case 1: sock_printf_error(c->sock, "Could not interpret value at option: \"%.40s\"\n", argv[argnr]); argnr ++; continue; /* Skip current option and the invalid value */ } /* And at last process extra things for certain options. * Most useful for the attr_offset==-1 stuff. */ switch (item->type) { case MENUITEM_ACTION: if( strcmp( argv[argnr]+1, "menu_result" ) == 0 ) { if( strcmp( argv[argnr+1], "none" ) == 0 ) { set_successor(item, "_none_", c); } else if( strcmp( argv[argnr+1], "close" ) == 0 ) { set_successor(item, "_close_", c); } else if( strcmp( argv[argnr+1], "quit" ) == 0 ) { set_successor(item, "_quit_", c); } else { error = 1; } } break; case MENUITEM_SLIDER: if( item->data.slider.value < item->data.slider.minvalue ) { item->data.slider.value = item->data.slider.minvalue; } else if( item->data.slider.value > item->data.slider.maxvalue ) { item->data.slider.value = item->data.slider.maxvalue; } break; case MENUITEM_RING: if( strcmp( argv[argnr]+1, "strings" ) == 0 ) { free (item->data.ring.strings); item->data.ring.strings = tablist2linkedlist (string_value); } item->data.ring.value %= LL_Length( item->data.ring.strings ); break; case MENUITEM_NUMERIC: menuitem_reset (item); break; case MENUITEM_ALPHA: if( strcmp( argv[argnr]+1, "password_char" ) == 0 ) { item->data.alpha.password_char = string_value[0]; } else if( strcmp( argv[argnr]+1, "maxlength" ) == 0 ) { char * new_buf; if( short_value < 0 || short_value > 1000 ) { error = 2; break; } new_buf = malloc( short_value + 1 ); strncpy( new_buf, item->data.alpha.value, short_value ); new_buf[short_value] = '\0'; /* terminate */ free( item->data.alpha.value ); item->data.alpha.value = new_buf; free( item->data.alpha.edit_str ); item->data.alpha.edit_str = malloc( short_value + 1 ); item->data.alpha.edit_str[0] = '\0'; } else if( strcmp( argv[argnr]+1, "value" ) == 0 ) { strncpy( item->data.alpha.value, string_value, item->data.alpha.maxlength ); item->data.alpha.value[ item->data.alpha.maxlength ] = 0; /* terminate */ } menuitem_reset (item); break; case MENUITEM_IP: if( strcmp( argv[argnr]+1, "v6" ) == 0 ) { char * new_buf; /* set max lenth depending ob boolean option v6 */ item->data.ip.maxlength = (bool_value == 0) ? 15 : 39; new_buf = malloc(item->data.ip.maxlength + 1 ); strncpy( new_buf, item->data.ip.value, item->data.ip.maxlength); new_buf[item->data.ip.maxlength] = '\0'; /* terminate */ free( item->data.ip.value ); item->data.ip.value = new_buf; free( item->data.ip.edit_str ); item->data.ip.edit_str = malloc( item->data.ip.maxlength +1); item->data.ip.edit_str[0] = '\0'; } else if( strcmp( argv[argnr]+1, "value" ) == 0 ) { strncpy( item->data.ip.value, string_value, item->data.ip.maxlength ); item->data.ip.value[item->data.ip.maxlength] = '\0'; /* terminate */ } menuitem_reset (item); break; default: break; } switch( error ) { case 1: sock_printf_error(c->sock, "Could not interpret value at option: \"%.40s\"\n", argv[argnr]); continue; /* Skip to next arg and retry it as an option */ case 2: sock_printf_error(c->sock, "Value out of range at option: \"%.40s\"\n", argv[argnr]); argnr ++; continue; /* Skip current option and the invalid value */ } menuscreen_inform_item_modified (item); if( option_table[option_nr].attr_type != NOVALUE ) { /* Skip the now used argument */ argnr ++; } } sock_send_string(c->sock, "success\n"); return 0; }
/** * Send an already formatted error message to the client. * \param fd socket * \param message the message to send (without the "huh? ") */ int sock_send_error(int fd, char* message) { // simple: performance penalty isn't worth more work... return sock_printf_error(fd, message); }