SMS-Control

sms-control.are
/* DESC: This script will execute commands received by SMS.
 * Copyright (C) 2012 NetModule AG, Switzerland
 * 
 * sms-commands:
 * <reboot>: reboot netmodule router
 * <connect>: configure wanlink 0 permanent
 * <disconnect>: configure wanlink 0 disable 
 * <status>: get system status summary
 * <output [1|2] [on|off]> set dio n on or off
 */
 
INTERVAL        = 10;                           /* only run every 10 seconds                    */
MAXMSG          = 5;                            /* process max. 5 msgs                          */
MAXAGE          = 300;                          /* message mustn't be older than 5 mins         */
MAXLINES        = 32;                           /* max. number of lines in msg (incl. header)   */
AUTH            = 1;                            /* perform authentication                       */
ADMPWD          = "";                           /* password used for authentication             */
IGNORECASE      = 1;                            /* ignore upper-/lower-case                     */
SENDERS         = mkarray(/* "+123456789" */);  /* allowed senders                              */
 
/* check if we should perform authentication */
if (argc == 2 && argv[1] == "noauth") {
    AUTH = 0;
}
 
/* retrieve password */
if (AUTH) {
    if (strlen(ADMPWD) == 0) {
        /* use configured admin password */
        ADMPWD = nb_config_get("admin.password");
        if (strlen(ADMPWD) > 0) {
            nb_syslog("using admin password for authentication");
        } else {
            /* not there -> use a default password instead */
            nb_syslog("using default password for authentication");
            ADMPWD = "admin01";
        }
    }
    if (IGNORECASE) ADMPWD = tolower(ADMPWD);
}
 
/* split a message into an array */
array split(string msg) {
  args=mkarray();
  pos=strchr(msg, "\r");
  if(pos==0) msg="";
  if(pos>0) msg=left(msg, pos);
  pos=strchr(msg, "\n");
  if(pos==0) msg="";
  if(pos>0) msg=left(msg, pos);
  for(n=0;;) {
    pos=strchr(msg, " ");
    if(is_void(pos)) pos=strchr(msg, "\t");
    if(is_void(pos)) pos=strchr(msg, "\f");
    if(pos==0) {
      msg=trim(msg);
    } else if(pos>0) {
      args[n++]=trim(left(msg, pos));
      msg=trim(substr(msg, pos));
    } else {
      if(msg!="") args[n++]=trim(msg);
      break;
    }
  }
  return args;
}
 
/* parse message */
array parse (string msg)
{
    /* read by line */
    lnr = 0;
    ishdr = 1;
    tlnr = 0;
    allowed = (length(SENDERS) == 0) ? 1 : 0;
 
    lp = msg;
    for (lnr = 0; lnr < MAXLINES && strlen(lp) > 0; lnr++) {
        pos = strchr(lp, "\n");
        if (is_void(pos)) pos = strlen(lp);
 
        line = left(lp, pos);
        lp = substr(lp, pos + 1);
 
        if (strlen(line) == 0) {
            /* saw header separator */
            ishdr = 0;
            continue;
        }
        if (ishdr) {
            /* saw header line */
            if (left(line, 5) == "Sent:") {
                /* check age of message */
                sentdate = trim(substr(line, 5));
                sent = strptime(sentdate, "%Y-%m-%d %H:%M:%S");
 
                if (!is_void(sent)) {
                    /* got a valid sent date */
                    now = localtime(time());
                    age = mktime(now) - mktime(sent);
                    nb_syslog("message has been sent %ds ago", age);
                    if (age > MAXAGE) {
                        nb_syslog("rejecting too old message");
                        return mkarray();
                    }
                } else {
                    nb_syslog("time check has been omitted");
                }
            } else if (left(line, 5) == "From:") {
                from = substr(line, 6);
                if (length(SENDERS) > 0) {
                    for (s = 0; s < length(SENDERS); s++) {
                        sender = SENDERS[s]; 
                        if (left(from, strlen(sender)) == sender) {
                            allowed = 1;
                            break;
                        }
                    }
                }
                if (allowed == 0) {
                    nb_syslog("rejecting message from unknown sender %s", from);
                    return mkarray();
                } else {
                    nb_syslog("sender %s can pass", from);
                }
            }
        } else {
            /* saw text line */
            if (IGNORECASE) line = tolower(line);
 
            if (AUTH && tlnr == 0) {
                /* first line of message must contain the password */
                if (left(line, strlen(ADMPWD)) != ADMPWD) {
                    nb_syslog("authentication failed");
                    return mkarray();
                } else {
                    nb_syslog("authentication succeeded");
                }
            } else if ((AUTH && tlnr == 1) || (!AUTH && tlnr == 0)) {
                /* this line must contain the command */
                return split(line);
            } else {
                break;
            }
            tlnr++;
        }
    }
 
    nb_syslog("no command detected");
 
    return mkarray();
}
 
int setdio (string port, string newstate) 
{
    if (port != "1" && port != "2") {
        nb_syslog("invalid DIO port %s\n", port);        
        return -1;
    }     
    if (newstate == "on") { 
        st = 1;
    } else if (newstate == "off") { 
        st = 0;
    } else  {         
        nb_syslog("invalid new DIO state %s\n", newstate);
        return -1;
    }        
 
    rc = nb_dio_set(sprintf("out%s", port), st);
    if (rc) {
        nb_syslog("Unable to set state %s for DIO output port %s\n", newstate, port);
        return -1;
    } else {            
        nb_syslog("Setting state %s for DIO output port %s\n", newstate, port);
        return 0; 
    }
}
 
/* check if we got stressed */
LASTFILE = "/tmp/sms-control.last";
last = 0;
 
fp = fopen(LASTFILE, "r");
if (fp) {
    /* we have been run at least one time */
    str = fread(fp, 32);
    if (str) last = (int) str;
    fclose(fp);
}
 
now = mktime(localtime(time()));
elapsed = now - last;
if (elapsed > 0 && elapsed < INTERVAL) {
    nb_syslog("we have ben run %d seconds ago. skipping.", elapsed);
    exit(0);
}
 
/* record timestamp */
fp = fopen(LASTFILE, "w+");
if (fp) {
    str = sprintf("%d", now);
    fwrite(fp, str);
    fclose(fp);
}
 
/* read inbox */
msgs = nb_sms_list();
nr_msgs = length(msgs);
 
if (nr_msgs == 0) {
    nb_syslog("there are no messages in your inbox");
    exit(0);
}
 
nb_syslog("you have %d message(s) in your inbox", nr_msgs);
 
/* track states */
reboot = 0;
 
/* only process latest messages */
start = nr_msgs - MAXMSG;
if (start < 0) start = 0;
 
for (i=start; i<nr_msgs; i++) {
    msg = nb_sms_retrieve(msgs[i]);
    if (!msg) continue;
 
    nb_syslog("processing message %d of %d (ID %s)", 
               i, nr_msgs, msgs[i]);
 
    args = parse(msg);
    if ((is_array(args)) && (length(args) > 0)) {
        if (args[0] == "") {
            nb_syslog("no command");
        } else if ((args[0] == "reboot") && (length(args) == 1)) {   
            nb_syslog("reboot command received");
            reboot = 1;
        } else if ((args[0] == "connect") && (length(args) == 1)) {
            nb_syslog("connect command received");
            if (nb_config_get("wanlink.0.mode") == "1") {
                nb_syslog("already connecting");
            } else {
                /* enable first wanlink */
                nb_config_set("wanlink.0.mode=1");
            }
        } else if ((args[0] == "disconnect") && (length(args) == 1)) {
            nb_syslog("disconnect command received");
            if (nb_config_get("wanlink.0.mode") == "0") {
                nb_syslog("already disconnecting");
            } else {
                /* disable first wanlink */
                nb_config_set("wanlink.0.mode=0");
            }
        } else if ((args[0] == "status") && (length(args) == 1)) {
            nb_syslog("status command received");
            rcpt = nb_sms_header(msgs[i], "From");
            if (rcpt) {
                id = nb_sms_send(rcpt, nb_status_summary());
                if (!id) {
                    nb_syslog("unable to send status message to %s", rcpt);
                } else {
                    nb_syslog("successfully queued status message to %s (ID %s)", rcpt, id);
                }
            }
        } else if ((args[0] == "output") && (length(args) == 3)) {
            nb_syslog("dio out command received");
            setdio(args[1], args[2]);
        } else {
            nb_syslog("command <%s> not supported", msg);
        }
    } else {
        nb_syslog("ignoring invalid message");
    }
 
    /* delete message */
    ret = nb_sms_delete(msgs[i]);
    if (ret == 0) nb_syslog("deleted message %s", msgs[i]);
}
 
if (reboot == 1) {
    /* trigger reboot */
    nb_syslog("rebooting system");
    nb_reboot();
}
 
exit(0);