| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 | #!/usr/bin/haserl --shell=/bin/bash  --upload-limit=32768 --upload-dir=/www/tmp <%# upload limit: 32Mb %><%_WWW='/www'_TMP="${_WWW}/tmp"_LOG="${_WWW}/systemupdate.log"  ## safe persistently_DEBUG=1err() {  _ERR="$?"  [[ "$_ERR" -gt 0 ]] || return 0  log "$1"  head "${2:='400'}"  exit "$_ERR"} logThis() {  [[ "$_DEBUG" -gt 0 ]] || return 0  local _TYPE='I:'  [[ "$_ERR" -gt 0 ]] && _TYPE='E:'  local _TIME; printf -v _TIME '%(%d.%m.%Y %H:%M:%S)T' -1  printf '%b\n' "$_TIME  $_TYPE ${@} " >> "$_LOG"  [[ "$_DEBUG" -gt 1 ]] && printf '%b\n' "[verbose] $_TYPE ${1}"}headerPrint() {  case "$1" in 200|'') printf '%b' 'HTTP/1.1 200 OK\r\n';;    301) printf '%b' "HTTP/1.1 301 Moved Permanently\r\nLocation: $HTTP_REFERER\r\n";;    403) printf '%b' 'HTTP/1.1 403 Forbidden\r\n';;    405) printf '%b' 'HTTP/1.1 405 Method Not Allowed\r\n';;    406) printf '%b' 'HTTP/1.1 406 Not Acceptable\r\n';;      *) printf '%b' 'HTTP/1.1 400 Bad Request\r\n';;  esac  printf '%b' 'Content-Type: text/html\r\n\r\n';}setQueryVars() {  env}runSuid() {  local _SID=$(/usr/bin/ps -p $$ -o sid=)  ## pass session id to the child  local _CMD=$@  sudo ./suid.sh $_CMD $_SID 2>/dev/null}getQueryFile() {  local _UPLD="${HASERL_fwupload_path##*/}"  logThis "'multipart': decoding stream"  mv "$_TMP/$_UPLD" "$_TMP/fwupload.bin" 2>/dev/null || _ERR=$?  if [[ $_ERR -gt 0 ]]; then    showMesg 'Firmware upload has failed' 'Reboot your Superglue server and try again'  fi}validIp() {  local _IP=$1  local _RET=1  if [[ $_IP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then    OIFS=$IFS    IFS='.'    _IP=($_IP)    IFS=$OIFS    [[ ${_IP[0]} -le 255 && ${_IP[1]} -le 255 && ${_IP[2]} -le 255 && ${_IP[3]} -le 255 ]]    _RET=$?  fi  return $_RET}pwdChange() {  if [[ ! -z "${_pwd##$_pwdd}" ]]; then     _ERR=1    showMesg 'Passwords did not match'  fi  if [[ ${#_pwd} -lt 6 ]]; then    _ERR=1    showMesg 'Password must be at least 6 characters long'  fi  runSuid "echo -e \"$_pwd\n$_pwd\" | passwd root"  runSuid "echo $(htDigest $_pwd) > $_PWDFILE"  _ERR=$?  if [[ $_ERR -gt 0 ]]; then    showMesg 'Password change failed'  else    showMesg 'Password is changed'  fi}lanAddr() {  logThis "new LAN addr is: $_laddr"  validIp $_laddr || showMesg 'Not valid network address'  doUci set laddr $_laddr  _ERR=$?  if [[ $_ERR -gt 0 ]]; then    showMesg 'Setting network address failed'  else    (sleep 1; doUci commit network; doUci commit wireless;)&    showMesg 'New network address is set' "Your server is now accessible under <a href='http://superglue.local/admin'>http://superglue.local/admin</a>"  fi }wanSet() {  if [[ ! -z $_wanifname ]]; then    ## eth and wlan wan cases are different!    ## eth wan requires:    ##   config interface 'wan'    ##     option ifname 'eth0'    ##    ##   config wifi-iface    ##     option device 'radio0'    ##     option network 'wan'         ##     option disabled '1' (or no 'config wifi-iface' section at all)    ##    ## wlan wan requires:    ##   config interface 'wan'    ##     option proto 'dhcp'    ##     (without 'option ifname' specified!)    ##    ##    config wifi-iface     ##      option device 'radio0'    ##      option network 'wan'    logThis "wan.ifname=$_wanifname"    if [[ $_wanifname == 'eth0' ]]; then      doUci set wanifname $_wanifname      doUci set wanwifacedis '1'    elif [[ $_wanifname == 'wlan1' ]]; then      doUci set wanifname ''      doUci set wanwifacedis ''    fi    if [[ $_wanproto == 'dhcp' ]]; then      doUci set wanproto dhcp    elif [[ $_wanproto == 'static' ]]; then      logThis "wan.ipaddr=$_wanipaddr"      doUci set wanproto static      doUci set wanipaddr $_wanipaddr      doUci set wannetmask $_wannetmask    fi    if [[ $_wanifname == 'wlan1' ]]; then      ssidChange || showMesg 'Wireless changes failed'    fi    ## background the following    doUci commit network &&    showMesg 'Internet connection is configured' 'Waiting for device to get ready' ||    showMesg 'Configuring Internet connection failed'  fi  logThis "new WAN iface is: $_wanifname"}ssidChange() {  ## check for iface  [[ ! $_iface =~ ^('wan'|'lan')$ ]] && showMesg 'Error changing wireless settings' 'unknown/unconfigured interface'  logThis "$_iface is being set"  _p=$_iface  ## default enc for now  local _enc='psk2'  if [[ $_iface == 'wan' ]]; then    local _mode='sta'    local _ssid="${_wanssid}"    local _key="${_wankey}"  else     local _mode='ap'    local _ssid="${_lanssid}"    local _key="${_lankey}"  fi  logThis "ssid: $_ssid [$_mode], key: $_key [$_enc]"  logThis $_wanssid  if [[ ${#_ssid} -lt 4 ]]; then   _ERR=1   showMesg 'SSID must be at least 4 characters long'  fi  doUci set $_p'ssid' "${_ssid}"  _ERR=$?  [[ $_ERR -gt 0 ]] && showMesg 'New SSID is not set'  if [[ -z $_key ]]; then    ## if key is empty set encryption to none and remove key    doUci set $_p'key' && doUci set $_p'enc' 'none'    _ERR=$?  else    if [[ ${#_key} -lt 8 ]]; then     _ERR=1      showMesg 'Passphrase must be at least 8 characters long'    fi    doUci set $_p'key' "${_key}" && doUci set $_p'enc' "${_enc}"    _ERR=$?    [[ $_ERR -gt 0 ]] && showMesg 'Passphrase is not set'  fi  [[ $_ERR -gt 0 ]] && return $_ERR  ##showMesg 'Wireless changes failed'  doUci commit wireless ##&& showMesg 'Wireless changes applied'}#showError() {#  headerPrint 406#  logThis "$@"#  echo "ERROR: $@"#  exit 1#}showMesg() {  logThis "$@"  local _MSG=$1  local _SUBMSG=$2  _MSG=${_MSG:='Not defined'}  _SUBMSG=${_SUBMSG:='back to control panel in a second..'}  if [[ $_ERR -gt 0 ]]; then    local _TYPE='ERROR: '    headerPrint 406  else    local _TYPE='OK: '    headerPrint 200  fi  htmlHead "<meta http-equiv='refresh' content='3;url=${HTTP_REFERER}'>"  echo "<body><h1>Superglue system update</h1><hr><h2 style='display:inline'>$_TYPE $_MSG</h2><span style='display:inline; margin-left: 50px;'>$_SUBMSG</span><hr></body></html>"  exit 0}updateFw() {  logThis "updating fw"  _FWFILE="${_TMP}/fwupload.bin"  logThis "fwfile is: $(ls -lad $_FWFILE)"  _OUT="$(/sbin/sysupgrade -T $_FWFILE 2>&1)"  _ERR=$?  [[ $_ERR -gt 0 ]] && showMesg "$_OUT"  _OUT="$(runSuid /sbin/mtd -e firmware -q write $_FWFILE firmware)"  _ERR=$?  [[ $_ERR -gt 0 ]] && showMesg "update failed, $_OUT"  _OUT="$(/sbin/mtd verify $_FWFILE firmware_2>&1)"  _ERR=$?  [[ $_ERR -gt 0 ]] && showMesg "update check failed, $_OUT"  runSuid reboot  showMesg 'Firmware update is completed, rebooting..' 'this might take up to 60 seconds'}rebootNow() {  logThis "reboot: now!"  runSuid reboot  showMesg 'Rebooting..' 'this might take up to 60 seconds'}doUci() {  local _CMD=''  local _ARG=''  case $1 in    get|set|commit) _CMD=$1;;          *) logThis 'bad UCI command'; headerPrint 405; echo 'bad UCI command'; exit 1 ;;  esac  case $2 in    lanssid) _ARG='wireless.@wifi-iface[0].ssid';;    lanenc) _ARG='wireless.@wifi-iface[0].encryption';;    lankey) _ARG='wireless.@wifi-iface[0].key';;    lanipaddr) _ARG='network.lan.ipaddr';;    wanifname) _ARG='network.wan.ifname';;    wanproto) _ARG='network.wan.proto';;    wanipaddr) _ARG='network.wan.ipaddr';;    wannetmask) _ARG='network.wan.netmask';;    wanwifacedis) _ARG='wireless.@wifi-iface[1].disabled';;    wanssid) _ARG='wireless.@wifi-iface[1].ssid';;    wanenc) _ARG='wireless.@wifi-iface[1].encryption';;    wankey) _ARG='wireless.@wifi-iface[1].key';;    *) if [[ $_CMD == 'commit' ]]; then        _ARG=$2       else         logThis "bad UCI entry: $2"        _ERR=1        showMesg 'bad UCI entry'       fi ;;  esac  if [[ $_CMD == 'get' ]]; then    if [ ! -z $_ARG ]; then      /sbin/uci -q get $_ARG || return $?    fi  fi  if [[ $_CMD == 'set' ]]; then    local _VAL=$3    if [ -z $_VAL ]; then      logThis "empty $_ARG value, removing record"      runSuid /sbin/uci delete $_ARG || ( echo "uci delete $_ARG: error"; exit 1; )    fi    if [ ! -z $_ARG ]; then      logThis "setting $_ARG value"      runSuid /sbin/uci set $_ARG=$_VAL || ( echo "uci set $_ARG: error"; exit 1; )    fi  fi  if [[ $_CMD == 'commit' ]]; then    runSuid /sbin/uci commit $_ARG|| echo "uci commit $_ARG: error"    if [[ "$_ARG" == 'wireless' ]]; then      runSuid /sbin/wifi || echo 'wifi: error'    fi    if [[ "$_ARG" == 'network' ]]; then      runSuid /etc/init.d/dnsmasq restart && runSuid /etc/init.d/network restart || echo 'network: error'    fi  fi}getStat() {  . /usr/share/libubox/jshn.sh  local _IFACE=$1  local _IFSTAT=$(runSuid ubus call network.interface.wan status 2>/dev/null)  logThis "$_IFSTAT"  json_get_type _IFSTAT ipv4_address  if json_get_type _IFSTAT ipv4_address && [[ "$_IFSTAT" == 'array' ]]; then    json_select ipv4_address    json_get_type _IFSTAT 1    if [[ "$_IFSTAT" == 'object' ]]; then      json_select 1      json_get_var IP4 address      json_get_var Subnet4 mask      [[ "$IP4" != '' ]] && [[ "$Subnet4" != '' ]] && IP4="$IP4/$Subnet4"    fi  fi  logThis $IP4}htmlHead() {echo "<!-- obnoxious code below, keep your ports tight --><!doctype html><html><head><title>SuperGlue | System update</title>$@<link rel='stylesheet' type='text/css' href='http://${HTTP_HOST}/resources/admin/admin.css'></head>"}if [[ "${REQUEST_METHOD^^}" == "POST" ]]; then  [[ $CONTENT_LENGTH -gt 0 ]] || err 'content length is zero, 301 back to referer' '301'  case "${CONTENT_TYPE^^}" in     APPLICATION/X-WWW-FORM-URLENCODED*) setQueryVars;;                  MULTIPART/FORM-DATA*) getQueryFile;;                                     *) _ERR=1; _OUT='this is not a post';;  esac  case $REQUEST_URI in                *pwdchange) pwdChange;;               *ssidchange) ssidChange;;                  *lanaddr) lanAddr;;                 *updatefw) updateFw;;                *rebootnow) rebootNow;;                      *wan) wanSet;;                         *) logThis 'bad action'; headerPrint 405;                             echo 'no such thing'; exit 1;;  esacfiheaderPrint 200## html headhtmlHeadsgver=$(cat /etc/superglue_version)devmod=$(cat /etc/superglue_model)openwrt=$(cat /etc/openwrt_version)wanifname=$(doUci get wanifname || echo 'wlan0') ## TODO fix thiswanproto=$(doUci get wanproto)wanipaddr=$(doUci get wanipaddr) wannetmask=$(doUci get wannetmask)wanssid=$(doUci get wanssid)wankey=$(doUci get wankey)echo "<body><img src='/resources/images/superglue-logo.png' id='sg-logo'><h2 style='display:inline'>Superglue system update</h2><span style='display:block;'>System version: $sgver | Device: $devmod</span><span style='display:block;'>$(uptime)</span><hr><span class='title2'>Firmware update:</span><p>The main purpose of Superglue <b>system update mode</b> is to ensure smooth and simple upgrades to the latest versions of Superglue firmware (OS) during the period of active project development. These updates help us get rid of the bugs and introduce new features (and sometimes, the other way around)</p><p>Before you begin, please make sure to obtain the latest firmware by visiting our <a href='http://discourse.superglue.it/category/superglue/l/latest' target='_blank'>online users comminuty</a> or directly via the <a href='http://dev.superglue.it/firmware/latest' target='_blank'>Superglue firmware repository</a>.</p><p>Once you are ready, upload the firmware update file using the form below:</p> <form method='post' action='/systemupdate/updatefw' enctype='multipart/form-data' name='fwupdate'><div id='uploadbox'><input id='uploadfile' placeholder='Choose file' disabled='disabled' style='width:400px'><input id='uploadbtn' name='fwupload' type='file'></div><input type='submit' value='Upload'></form><p>Configuration options below are optional.<br>Unless you upgrade the current firmware any changes done to the setting will not be saved.</p><hr><br><br>Internet connection:<form method='post' action='/systemupdate/wan' name='wan' onchange='formChange();'>  <div style='display:inline-flex'>  <div style='display:inline-block;'>  <select name='wanifname' id='wanifname' style='display:block'>  <option value='eth0' id='eth' $([[ $wanifname =~ ('eth') ]] && echo 'selected')>Wired (WAN port)</option>  <option value='wlan1' id='wlan' $([[ $wanifname =~ ('wlan') ]] && echo 'selected')>Wireless (Wi-Fi)</option>  </select>  <fieldset id='wanwifi' class='hide'>  <input type='text' name='wanssid' value='$wanssid'>  <input type='password' name='wankey' value='$wankey'>  </fieldset>  </div>  <div style='display:inline-block;'>  <select name='wanproto' id='wanproto' style='display:block'>  <option value='dhcp' name='dhcp' id='dhcp' $([[ $wanproto == 'dhcp' ]] && echo 'selected')>Automatic (DHCP)</option>  <option value='stat' name='dhcp' id='stat' $([[ $wanproto == 'static' ]] && echo 'selected')>Manual (Static IP)</option>  </select>  <fieldset id='wanaddr' class='hide' >  <input type='text' name='wanipaddr' id='wanipaddr' value='$wanipaddr'>  <input type='text' name='wangw' id='wannetmask' value='$wannetmask'>  </fieldset>  </div>  </div>  <input type='hidden' name='iface' value='wan' class='inline'>  <input type='submit' value='Apply'></form><hr>Local wireless network:<form method='post' action='/systemupdate/ssidchange'>  <div style='display:inline-flex'>  <div style='display:inline-block;'>    <input type='text' name='lanssid' value='$(doUci get lanssid)'>    <input type='password' name='lankey' value='$(doUci get lankey)'>  </div>  <div style='display:inline-block;'>    <input type='text' name='lanipaddr' value='$(doUci get lanipaddr)'>    <input type='hidden' name='iface' value='lan' class='inline'>  </div>  </div>  <input type='submit' value='Apply'>  </form><hr><form action='/systemupdate/rebootnow' method='post' class='inline'><input type='hidden' name='reboot' value='now' class='inline'><input type='submit' value='Reboot' class='inline'></form><form action='http://logout@${HTTP_HOST}/systemupdate' method='get' class='inline'><input type='submit' value='Logout' class='inline'></form><hr>Memory:<pre>$(free)</pre><hr>Storage:<pre>$(df -h)</pre><hr>Environment:<pre>$(env)</pre><hr></body><script type='text/javascript' src='http://${HTTP_HOST}/resources/admin/admin.js'></script></html>"%>
 |