From b8f2fda37863955de65903fb5dfae41061439b63 Mon Sep 17 00:00:00 2001 From: ROOterDairyman Date: Sat, 16 Dec 2017 15:23:41 -0800 Subject: [PATCH] GO2017-12-15 GoldenOrb 2017-12-15 --- bwmon/Makefile | 35 + bwmon/files/etc/config/bwmon | 5 + bwmon/files/etc/init.d/bwmon | 15 + bwmon/files/lib/upgrade/keep.d/bwmon | 1 + bwmon/files/opt/WRTbmon/cleanup.lua | 32 + bwmon/files/opt/WRTbmon/process.sh | 28 + bwmon/files/opt/WRTbmon/wrtbwmon.sh | 270 ++ .../usr/lib/lua/luci/controller/bwmon.lua | 7 + .../lib/lua/luci/model/cbi/bwmon/bwmon.lua | 444 +++ dir860l/Makefile | 35 + .../files/etc/hotplug.d/iface/99-dir860-led | 13 + dir860l/files/usr/lib/rooter/special.sh | 8 + ext-buttons/Makefile | 35 + ext-buttons/files/etc/btnaction.sh | 56 + .../files/etc/hotplug.d/button/00-button | 26 + .../files/etc/hotplug.d/button/10-buttonchk | 7 + ext-buttons/files/etc/init.d/buttons | 33 + .../usr/lib/lua/luci/controller/buttons.lua | 8 + .../lua/luci/model/cbi/buttons/buttons.lua | 44 + ext-command/Makefile | 33 + .../usr/lib/lua/luci/controller/commands.lua | 289 ++ .../usr/lib/lua/luci/model/cbi/commands.lua | 37 + .../files/usr/lib/lua/luci/view/cmdedit.htm | 186 + .../files/usr/lib/lua/luci/view/commands.htm | 176 + ext-command/files/usr/lib/scripts/dummy | 1 + ext-extra/Makefile | 35 + ext-extra/files/etc/config/schedule | 7 + .../usr/lib/lua/luci/controller/schedule.lua | 9 + .../luci/model/cbi/admin_system/cronnew.lua | 29 + .../usr/lib/lua/luci/model/cbi/schedule.lua | 133 + .../files/usr/lib/rooter/luci/croncat.sh | 17 + ext-extra/files/usr/lib/rooter/luci/reboot.sh | 27 + ext-hdidle/Makefile | 36 + .../files/etc/uci-defaults/40_luci-hd_idle | 11 + .../usr/lib/lua/luci/controller/hd_idle.lua | 15 + .../usr/lib/lua/luci/model/cbi/hd_idle.lua | 29 + ext-p910nd/Makefile | 36 + .../usr/lib/lua/luci/controller/p910nd.lua | 15 + .../usr/lib/lua/luci/model/cbi/p910nd.lua | 59 + ext-rooter-basic/Makefile | 41 + ext-rooter-basic/files/etc/codename | 1 + ext-rooter-basic/files/etc/config/guestwifi | 8 + ext-rooter-basic/files/etc/config/modem | 18 + .../files/etc/hotplug.d/tty/30-3x | 31 + .../files/etc/hotplug.d/usb/20-usb_mode | 3 + ext-rooter-basic/files/etc/init.d/clear | 18 + ext-rooter-basic/files/etc/init.d/rooter | 9 + ext-rooter-basic/files/etc/init.d/usbmode | 14 + ext-rooter-basic/files/etc/rc.d/S88usbmode | 14 + ext-rooter-basic/files/etc/rc.d/S99rooter | 9 + ext-rooter-basic/files/etc/usb-mode.json | 3538 +++++++++++++++++ ext-rooter-basic/files/etc/usb-mode1.json | 3481 ++++++++++++++++ ext-rooter-basic/files/lib/netifd/proto/3x.sh | 146 + .../files/lib/upgrade/keep.d/cronfiles | 2 + ext-rooter-basic/files/usr/bin/set_gpio | 18 + .../lib/lua/luci/controller/admin/modem.lua | 373 ++ .../usr/lib/lua/luci/controller/guestwifi.lua | 8 + .../usr/lib/lua/luci/controller/poweroff.lua | 13 + .../usr/lib/lua/luci/controller/update.lua | 59 + .../usr/lib/lua/luci/model/cbi/guestwifi.lua | 81 + .../lua/luci/model/cbi/rooter/connection.lua | 122 + .../lua/luci/model/cbi/rooter/connmonitor.lua | 151 + .../lua/luci/model/cbi/rooter/customize.lua | 156 + .../lua/luci/model/cbi/rooter/profiles.lua | 458 +++ .../lua/luci/view/admin_system/poweroff.htm | 34 + .../usr/lib/lua/luci/view/rooter/custom.htm | 125 + .../usr/lib/lua/luci/view/rooter/debug.htm | 86 + .../usr/lib/lua/luci/view/rooter/log.htm | 53 + .../usr/lib/lua/luci/view/rooter/misc.htm | 516 +++ .../lib/lua/luci/view/rooter/net_status.htm | 451 +++ .../usr/lib/lua/luci/view/rooter/update.htm | 102 + .../files/usr/lib/rooter/cdmafind.lua | 55 + .../files/usr/lib/rooter/common/gettype.sh | 153 + .../files/usr/lib/rooter/common/huaweidata.sh | 244 ++ .../files/usr/lib/rooter/common/lockchk.sh | 58 + .../files/usr/lib/rooter/common/modemchk.lua | 157 + .../files/usr/lib/rooter/common/otherdata.sh | 88 + .../files/usr/lib/rooter/common/phone.sh | 54 + .../files/usr/lib/rooter/common/processat.sh | 16 + .../files/usr/lib/rooter/common/sierradata.sh | 198 + .../files/usr/lib/rooter/common/ubloxdata.sh | 235 ++ .../files/usr/lib/rooter/common/ztedata.sh | 164 + .../files/usr/lib/rooter/connect/conmon.sh | 176 + .../usr/lib/rooter/connect/create_connect.sh | 666 ++++ .../usr/lib/rooter/connect/create_hostless.sh | 171 + .../usr/lib/rooter/connect/disablemw3.sh | 13 + .../usr/lib/rooter/connect/disconnect.sh | 74 + .../usr/lib/rooter/connect/reconnect-ppp.sh | 57 + .../files/usr/lib/rooter/connect/reconnect.sh | 11 + .../files/usr/lib/rooter/gcom/auto.gcom | 31 + .../files/usr/lib/rooter/gcom/baseinfo.gcom | 48 + .../files/usr/lib/rooter/gcom/cellinfo.gcom | 38 + .../files/usr/lib/rooter/gcom/cgpaddr.gcom | 46 + .../usr/lib/rooter/gcom/connect-directip.gcom | 64 + .../usr/lib/rooter/gcom/connect-ncm.gcom | 48 + .../usr/lib/rooter/gcom/connect-ppp.gcom | 36 + .../files/usr/lib/rooter/gcom/curc.gcom | 31 + .../files/usr/lib/rooter/gcom/gcom-locked | 39 + .../files/usr/lib/rooter/gcom/gettype.gcom | 43 + .../files/usr/lib/rooter/gcom/huaweiinfo.gcom | 64 + .../files/usr/lib/rooter/gcom/lock-prov.gcom | 38 + .../files/usr/lib/rooter/gcom/otherinfo.gcom | 28 + .../files/usr/lib/rooter/gcom/reset.gcom | 26 + .../files/usr/lib/rooter/gcom/run-at.gcom | 31 + .../files/usr/lib/rooter/gcom/sendsms-at.gcom | 45 + .../files/usr/lib/rooter/gcom/setapn.gcom | 45 + .../files/usr/lib/rooter/gcom/setpin.gcom | 53 + .../files/usr/lib/rooter/gcom/sierrainfo.gcom | 58 + .../files/usr/lib/rooter/gcom/smschk.gcom | 36 + .../files/usr/lib/rooter/gcom/smswrite.gcom | 39 + .../files/usr/lib/rooter/gcom/ubloxinfo.gcom | 43 + .../files/usr/lib/rooter/gcom/ussd.gcom | 24 + .../files/usr/lib/rooter/gcom/zteinfo.gcom | 48 + .../files/usr/lib/rooter/gpiomodel.lua | 125 + .../files/usr/lib/rooter/idown.lua | 22 + .../files/usr/lib/rooter/initialize.sh | 292 ++ .../files/usr/lib/rooter/log/at-logger | 12 + .../files/usr/lib/rooter/log/logger | 17 + .../files/usr/lib/rooter/log/rotate.lua | 32 + .../files/usr/lib/rooter/logprint.sh | 5 + .../files/usr/lib/rooter/luci/atcmd.sh | 19 + .../files/usr/lib/rooter/luci/celltype.sh | 205 + .../files/usr/lib/rooter/luci/getlog.sh | 24 + .../files/usr/lib/rooter/luci/guestwifi.sh | 93 + .../files/usr/lib/rooter/luci/luaops.sh | 8 + .../files/usr/lib/rooter/luci/modechge.sh | 123 + .../files/usr/lib/rooter/luci/modemchge.sh | 44 + .../files/usr/lib/rooter/luci/portchge.sh | 28 + .../files/usr/lib/rooter/luci/wifiradio.sh | 15 + .../files/usr/lib/rooter/mbimfind.lua | 59 + .../files/usr/lib/rooter/modeswitch.sh | 518 +++ .../files/usr/lib/rooter/portchge.sh | 28 + .../files/usr/lib/rooter/ppp/create_ppp.sh | 170 + .../files/usr/lib/rooter/protofind.lua | 250 ++ .../files/usr/lib/rooter/pwrtoggle.sh | 81 + .../files/usr/lib/rooter/signal/basedata.sh | 82 + .../files/usr/lib/rooter/signal/celldata.sh | 72 + .../files/usr/lib/rooter/signal/celltype.lua | 90 + .../usr/lib/rooter/signal/huaweihostless.sh | 198 + .../files/usr/lib/rooter/signal/mccmnc.data | 11 + .../usr/lib/rooter/signal/modemsignal.sh | 174 + .../usr/lib/rooter/signal/otherhostless.sh | 146 + .../files/usr/lib/rooter/signal/status.sh | 41 + .../usr/lib/rooter/signal/ztehostless.sh | 208 + .../files/usr/lib/rooter/sms/check_sms.sh | 25 + ext-rooter-basic/files/usr/lib/rooter/ussd.sh | 27 + ext-rooter4/Makefile | 33 + ext-rooter4/files/etc/config/failover | 15 + ext-rooter4/files/etc/flash | 1 + .../usr/lib/lua/luci/controller/failover.lua | 39 + .../lua/luci/model/cbi/rooter/failover.lua | 139 + .../luci/view/admin_status/index/failover.htm | 1 + .../luci/view/rooter/failover_overview.htm | 120 + .../files/usr/lib/rooter/connect/failover.sh | 183 + .../files/usr/lib/rooter/luci/failchk.sh | 16 + .../files/usr/lib/rooter/removeipv6.sh | 6 + ext-rooter45/Makefile | 32 + .../lua/luci/view/admin_status/indexnew.htm | 710 ++++ .../usr/lib/lua/luci/view/rooter/external.htm | 26 + ext-rooter8/Makefile | 39 + ext-rooter8/files/etc/config/mwan3 | 82 + ext-rooter8/files/etc/flash | 1 + .../files/etc/hotplug.d/block/20-mount | 107 + .../files/etc/hotplug.d/block/99-mount | 30 + .../usr/lib/lua/luci/controller/mwan3.lua | 339 ++ .../luci/model/cbi/mwan/interfaceconfig.lua | 194 + .../lua/luci/view/admin_status/indexnew.htm | 806 ++++ .../usr/lib/lua/luci/view/rooter/external.htm | 26 + ext-rooter8/files/usr/sbin/mwan3track | 79 + ext-rooterbcm/Makefile | 38 + ext-rooterbcm/files/etc/config/mwan3 | 82 + ext-rooterbcm/files/etc/flash | 1 + .../files/etc/hotplug.d/block/20-mount | 106 + .../files/etc/hotplug.d/block/99-mount | 30 + .../usr/lib/lua/luci/controller/mwan3.lua | 339 ++ .../luci/model/cbi/mwan/interfaceconfig.lua | 194 + .../lua/luci/view/admin_status/indexnew.htm | 806 ++++ .../usr/lib/lua/luci/view/rooter/external.htm | 26 + ext-rooterbcm/files/usr/sbin/mwan3track | 79 + ext-rooterbcm8/Makefile | 39 + ext-rooterbcm8/files/etc/config/mwan3 | 82 + ext-rooterbcm8/files/etc/flash | 1 + .../files/etc/hotplug.d/block/20-mount | 106 + .../files/etc/hotplug.d/block/99-mount | 30 + .../usr/lib/lua/luci/controller/mwan3.lua | 339 ++ .../luci/model/cbi/mwan/interfaceconfig.lua | 194 + .../lua/luci/view/admin_status/indexnew.htm | 806 ++++ .../usr/lib/lua/luci/view/rooter/external.htm | 26 + ext-rooterbcm8/files/usr/sbin/mwan3track | 79 + ext-rooterlite/Makefile | 38 + ext-rooterlite/files/etc/config/mwan3 | 82 + ext-rooterlite/files/etc/flash | 1 + .../usr/lib/lua/luci/controller/mwan3.lua | 339 ++ .../luci/model/cbi/mwan/interfaceconfig.lua | 194 + .../lua/luci/view/admin_status/indexnew.htm | 806 ++++ .../usr/lib/lua/luci/view/rooter/external.htm | 26 + ext-rooterlite/files/usr/sbin/mwan3track | 79 + ext-samba/Makefile | 38 + ext-samba/files/etc/hotplug.d/block/30-mount | 58 + ext-sms/Makefile | 35 + .../files/usr/lib/lua/luci/controller/sms.lua | 149 + .../usr/lib/lua/luci/view/rooter/sms.htm | 518 +++ ext-sms/files/usr/lib/sms/delsms.sh | 50 + ext-sms/files/usr/lib/sms/pack7bit.lua | 79 + ext-sms/files/usr/lib/sms/processsms | 116 + ext-sms/files/usr/lib/sms/sendsms.lua | 41 + ext-sms/files/usr/lib/sms/sendsms.sh | 10 + ext-sms/files/usr/lib/sms/smsout.lua | 24 + ext-sms/files/usr/lib/sms/smsout.sh | 42 + ext-sms/files/usr/lib/sms/smsread.lua | 797 ++++ ext-sms/files/usr/lib/sms/sys2sms.lua | 79 + ext-sms/files/usr/lib/sms/sys2sms.sh | 69 + ext-sms/files/usr/lib/sms/toggle.sh | 7 + ext-sms/files/usr/lib/sms/utf8togsm.lua | 155 + ext-umount/Makefile | 33 + ext-umount/files/etc/config/umount | 2 + ext-umount/files/etc/umount | 17 + .../usr/lib/lua/luci/controller/umount.lua | 8 + .../usr/lib/lua/luci/model/cbi/umount.lua | 21 + ext-wifi/Makefile | 32 + luci-app-dnsmasq-ipset/Makefile | 34 + .../files/etc/config/dnsmasq-ipset | 5 + .../files/etc/init.d/dnsmasq-ipset | 45 + .../lib/lua/luci/controller/dnsmasq-ipset.lua | 12 + .../lib/lua/luci/model/cbi/dnsmasq-ipset.lua | 24 + luci-app-hotspot/Makefile | 34 + luci-app-hotspot/files/etc/config/travelmate | 10 + .../etc/hotplug.d/iface/99-travelmate-iface | 26 + luci-app-hotspot/files/etc/init.d/travelmate | 38 + luci-app-hotspot/files/etc/init.d/zhot | 22 + .../files/lib/upgrade/keep.d/hotspot | 1 + .../files/usr/lib/hotspot/band.sh | 27 + .../files/usr/lib/hotspot/copyhot.sh | 9 + .../files/usr/lib/hotspot/dis_hot.sh | 9 + .../files/usr/lib/hotspot/enable.sh | 22 + .../files/usr/lib/hotspot/getssid.sh | 16 + .../files/usr/lib/hotspot/inrange.sh | 64 + .../files/usr/lib/hotspot/manual.sh | 10 + .../files/usr/lib/hotspot/mode.sh | 19 + .../files/usr/lib/hotspot/travelmate.sh | 306 ++ .../usr/lib/lua/luci/controller/hotspot.lua | 283 ++ .../usr/lib/lua/luci/view/hotspot/hotspot.htm | 1005 +++++ luci-app-rooterddns/Makefile | 34 + .../files/etc/uci-defaults/40_luci-ddns | 10 + .../usr/lib/lua/luci/controller/ddns.lua | 308 ++ .../lib/lua/luci/model/cbi/ddns/detail.lua | 1479 +++++++ .../lib/lua/luci/model/cbi/ddns/global.lua | 121 + .../usr/lib/lua/luci/model/cbi/ddns/hints.lua | 166 + .../lib/lua/luci/model/cbi/ddns/overview.lua | 219 + .../files/usr/lib/lua/luci/tools/ddns.lua | 315 ++ .../lua/luci/view/admin_status/index/ddns.htm | 1 + .../lib/lua/luci/view/ddns/detail_logview.htm | 56 + .../lib/lua/luci/view/ddns/detail_lvalue.htm | 23 + .../lib/lua/luci/view/ddns/detail_value.htm | 9 + .../lib/lua/luci/view/ddns/global_value.htm | 34 + .../luci/view/ddns/overview_doubleline.htm | 10 + .../lua/luci/view/ddns/overview_enabled.htm | 16 + .../lua/luci/view/ddns/overview_startstop.htm | 17 + .../lua/luci/view/ddns/overview_status.htm | 180 + .../lib/lua/luci/view/ddns/system_status.htm | 144 + luci-app-rootervpn/Makefile | 34 + .../files/etc/config/rooter_recipes | 293 ++ luci-app-rootervpn/files/etc/init.d/rootervpn | 14 + .../files/etc/openvpn/airvpn/ca.crt | 36 + .../files/etc/openvpn/airvpn/client.crt | 38 + .../files/etc/openvpn/airvpn/client.key | 52 + .../files/etc/openvpn/airvpn/ta.key | 18 + .../files/etc/openvpn/mullvad/mullvad_ca.crt | 109 + .../files/etc/openvpn/mullvad/mullvad_crl.pem | 36 + .../files/etc/openvpn/pia/ca.rsa.2048.crt | 33 + .../files/etc/openvpn/pia/crl.rsa.2048.pem | 15 + .../etc/openvpn/placeholder/placeholder.file | 2 + .../files/etc/openvpn/windscribe/ca.crt | 34 + .../files/etc/openvpn/windscribe/ta.key | 18 + .../files/lib/upgrade/keep.d/rootervpn | 1 + .../files/usr/bin/ovpn-userpass | 16 + .../files/usr/lib/easyrsa/dns.sh | 66 + .../files/usr/lib/easyrsa/firewall.sh | 72 + .../files/usr/lib/easyrsa/generate.sh | 151 + .../files/usr/lib/easyrsa/openvpn | 323 ++ .../files/usr/lib/easyrsa/stop.sh | 11 + .../usr/lib/lua/luci/controller/openvpn.lua | 39 + .../usr/lib/lua/luci/model/cbi/easyrsa.lua | 41 + .../lua/luci/model/cbi/openvpn-advanced.lua | 256 ++ .../lib/lua/luci/model/cbi/openvpn-basic.lua | 99 + .../usr/lib/lua/luci/model/cbi/openvpn.lua | 210 + .../usr/lib/lua/luci/view/easyrsa/easyrsa.htm | 172 + .../view/openvpn/cbi-select-input-add.htm | 11 + .../lib/lua/luci/view/openvpn/pageswitch.htm | 30 + luci-proto-3x/Makefile | 62 + .../luci/model/cbi/admin_network/proto_3x.lua | 146 + .../lib/lua/luci/model/network/proto_3x.lua | 68 + luci-proto-mbim/Makefile | 62 + .../model/cbi/admin_network/proto_mbim.lua | 45 + .../lib/lua/luci/model/network/proto_mbim.lua | 51 + luci-theme-darkmatter/Makefile | 66 + .../files/htdocs/css/style.css | 2021 ++++++++++ .../files/htdocs/favicon.ico | Bin 0 -> 2462 bytes .../files/htdocs/fonts/DIN.eot | Bin 0 -> 10365 bytes .../files/htdocs/fonts/DIN.otf | Bin 0 -> 39656 bytes .../files/htdocs/fonts/DIN.svg | 217 + .../files/htdocs/fonts/DIN.ttf | Bin 0 -> 19116 bytes .../files/htdocs/fonts/DIN.woff | Bin 0 -> 12656 bytes .../files/htdocs/fonts/font.eot | Bin 0 -> 1912 bytes .../files/htdocs/fonts/font.svg | 16 + .../files/htdocs/fonts/font.ttf | Bin 0 -> 1748 bytes .../files/htdocs/fonts/font.woff | Bin 0 -> 1824 bytes .../files/htdocs/js/jquery.min.js | 6 + .../files/htdocs/js/script.js | 233 ++ luci-theme-darkmatter/files/htdocs/logo.png | Bin 0 -> 2224 bytes .../files/templates/footer.htm | 45 + .../files/templates/header.htm | 255 ++ luci-theme-rooter/Makefile | 50 + luci-theme-rooter/files/htdocs/css/style.css | 1504 +++++++ luci-theme-rooter/files/htdocs/favicon.ico | Bin 0 -> 2462 bytes luci-theme-rooter/files/htdocs/fonts/font.eot | Bin 0 -> 1912 bytes luci-theme-rooter/files/htdocs/fonts/font.svg | 16 + luci-theme-rooter/files/htdocs/fonts/font.ttf | Bin 0 -> 1748 bytes .../files/htdocs/fonts/font.woff | Bin 0 -> 1824 bytes .../files/htdocs/js/jquery.min.js | 6 + luci-theme-rooter/files/htdocs/js/script.js | 228 ++ luci-theme-rooter/files/htdocs/logo.png | Bin 0 -> 2224 bytes luci-theme-rooter/files/templates/footer.htm | 51 + luci-theme-rooter/files/templates/header.htm | 243 ++ rmbim/.svn/entries | 65 + rmbim/.svn/text-base/Makefile.svn-base | 45 + rmbim/Makefile | 47 + rmbim/files/lib/netifd/proto/mbim.sh | 405 ++ .../files/usr/lib/rooter/mbim/connectmbim.sh | 232 ++ rmbim/files/usr/lib/rooter/mbim/mbimdata.sh | 110 + rmbim/files/usr/lib/rooter/mbim/monitor.sh | 43 + rqmi/.svn/entries | 65 + rqmi/.svn/text-base/Makefile.svn-base | 50 + rqmi/Makefile | 50 + rqmi/Makefile-4meg | 51 + rqmi/files/usr/lib/rooter/qmi/connectqmi.sh | 107 + 336 files changed, 43683 insertions(+) create mode 100644 bwmon/Makefile create mode 100644 bwmon/files/etc/config/bwmon create mode 100644 bwmon/files/etc/init.d/bwmon create mode 100644 bwmon/files/lib/upgrade/keep.d/bwmon create mode 100644 bwmon/files/opt/WRTbmon/cleanup.lua create mode 100644 bwmon/files/opt/WRTbmon/process.sh create mode 100644 bwmon/files/opt/WRTbmon/wrtbwmon.sh create mode 100644 bwmon/files/usr/lib/lua/luci/controller/bwmon.lua create mode 100644 bwmon/files/usr/lib/lua/luci/model/cbi/bwmon/bwmon.lua create mode 100644 dir860l/Makefile create mode 100644 dir860l/files/etc/hotplug.d/iface/99-dir860-led create mode 100644 dir860l/files/usr/lib/rooter/special.sh create mode 100644 ext-buttons/Makefile create mode 100644 ext-buttons/files/etc/btnaction.sh create mode 100644 ext-buttons/files/etc/hotplug.d/button/00-button create mode 100644 ext-buttons/files/etc/hotplug.d/button/10-buttonchk create mode 100644 ext-buttons/files/etc/init.d/buttons create mode 100644 ext-buttons/files/usr/lib/lua/luci/controller/buttons.lua create mode 100644 ext-buttons/files/usr/lib/lua/luci/model/cbi/buttons/buttons.lua create mode 100644 ext-command/Makefile create mode 100644 ext-command/files/usr/lib/lua/luci/controller/commands.lua create mode 100644 ext-command/files/usr/lib/lua/luci/model/cbi/commands.lua create mode 100644 ext-command/files/usr/lib/lua/luci/view/cmdedit.htm create mode 100644 ext-command/files/usr/lib/lua/luci/view/commands.htm create mode 100644 ext-command/files/usr/lib/scripts/dummy create mode 100644 ext-extra/Makefile create mode 100644 ext-extra/files/etc/config/schedule create mode 100644 ext-extra/files/usr/lib/lua/luci/controller/schedule.lua create mode 100644 ext-extra/files/usr/lib/lua/luci/model/cbi/admin_system/cronnew.lua create mode 100644 ext-extra/files/usr/lib/lua/luci/model/cbi/schedule.lua create mode 100644 ext-extra/files/usr/lib/rooter/luci/croncat.sh create mode 100644 ext-extra/files/usr/lib/rooter/luci/reboot.sh create mode 100644 ext-hdidle/Makefile create mode 100644 ext-hdidle/files/etc/uci-defaults/40_luci-hd_idle create mode 100644 ext-hdidle/files/usr/lib/lua/luci/controller/hd_idle.lua create mode 100644 ext-hdidle/files/usr/lib/lua/luci/model/cbi/hd_idle.lua create mode 100644 ext-p910nd/Makefile create mode 100644 ext-p910nd/files/usr/lib/lua/luci/controller/p910nd.lua create mode 100644 ext-p910nd/files/usr/lib/lua/luci/model/cbi/p910nd.lua create mode 100644 ext-rooter-basic/Makefile create mode 100644 ext-rooter-basic/files/etc/codename create mode 100644 ext-rooter-basic/files/etc/config/guestwifi create mode 100644 ext-rooter-basic/files/etc/config/modem create mode 100644 ext-rooter-basic/files/etc/hotplug.d/tty/30-3x create mode 100644 ext-rooter-basic/files/etc/hotplug.d/usb/20-usb_mode create mode 100644 ext-rooter-basic/files/etc/init.d/clear create mode 100644 ext-rooter-basic/files/etc/init.d/rooter create mode 100644 ext-rooter-basic/files/etc/init.d/usbmode create mode 100644 ext-rooter-basic/files/etc/rc.d/S88usbmode create mode 100644 ext-rooter-basic/files/etc/rc.d/S99rooter create mode 100644 ext-rooter-basic/files/etc/usb-mode.json create mode 100644 ext-rooter-basic/files/etc/usb-mode1.json create mode 100644 ext-rooter-basic/files/lib/netifd/proto/3x.sh create mode 100644 ext-rooter-basic/files/lib/upgrade/keep.d/cronfiles create mode 100644 ext-rooter-basic/files/usr/bin/set_gpio create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/controller/admin/modem.lua create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/controller/guestwifi.lua create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/controller/poweroff.lua create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/controller/update.lua create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/guestwifi.lua create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/connection.lua create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/connmonitor.lua create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/customize.lua create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/profiles.lua create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/view/admin_system/poweroff.htm create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/custom.htm create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/debug.htm create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/log.htm create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/misc.htm create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/net_status.htm create mode 100644 ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/update.htm create mode 100644 ext-rooter-basic/files/usr/lib/rooter/cdmafind.lua create mode 100644 ext-rooter-basic/files/usr/lib/rooter/common/gettype.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/common/huaweidata.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/common/lockchk.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/common/modemchk.lua create mode 100644 ext-rooter-basic/files/usr/lib/rooter/common/otherdata.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/common/phone.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/common/processat.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/common/sierradata.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/common/ubloxdata.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/common/ztedata.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/connect/conmon.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/connect/create_connect.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/connect/create_hostless.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/connect/disablemw3.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/connect/disconnect.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/connect/reconnect-ppp.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/connect/reconnect.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/auto.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/baseinfo.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/cellinfo.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/cgpaddr.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/connect-directip.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/connect-ncm.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/connect-ppp.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/curc.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/gcom-locked create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/gettype.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/huaweiinfo.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/lock-prov.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/otherinfo.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/reset.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/run-at.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/sendsms-at.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/setapn.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/setpin.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/sierrainfo.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/smschk.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/smswrite.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/ubloxinfo.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/ussd.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gcom/zteinfo.gcom create mode 100644 ext-rooter-basic/files/usr/lib/rooter/gpiomodel.lua create mode 100644 ext-rooter-basic/files/usr/lib/rooter/idown.lua create mode 100644 ext-rooter-basic/files/usr/lib/rooter/initialize.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/log/at-logger create mode 100644 ext-rooter-basic/files/usr/lib/rooter/log/logger create mode 100644 ext-rooter-basic/files/usr/lib/rooter/log/rotate.lua create mode 100644 ext-rooter-basic/files/usr/lib/rooter/logprint.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/luci/atcmd.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/luci/celltype.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/luci/getlog.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/luci/guestwifi.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/luci/luaops.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/luci/modechge.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/luci/modemchge.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/luci/portchge.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/luci/wifiradio.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/mbimfind.lua create mode 100644 ext-rooter-basic/files/usr/lib/rooter/modeswitch.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/portchge.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/ppp/create_ppp.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/protofind.lua create mode 100644 ext-rooter-basic/files/usr/lib/rooter/pwrtoggle.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/signal/basedata.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/signal/celldata.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/signal/celltype.lua create mode 100644 ext-rooter-basic/files/usr/lib/rooter/signal/huaweihostless.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/signal/mccmnc.data create mode 100644 ext-rooter-basic/files/usr/lib/rooter/signal/modemsignal.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/signal/otherhostless.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/signal/status.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/signal/ztehostless.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/sms/check_sms.sh create mode 100644 ext-rooter-basic/files/usr/lib/rooter/ussd.sh create mode 100644 ext-rooter4/Makefile create mode 100644 ext-rooter4/files/etc/config/failover create mode 100644 ext-rooter4/files/etc/flash create mode 100644 ext-rooter4/files/usr/lib/lua/luci/controller/failover.lua create mode 100644 ext-rooter4/files/usr/lib/lua/luci/model/cbi/rooter/failover.lua create mode 100644 ext-rooter4/files/usr/lib/lua/luci/view/admin_status/index/failover.htm create mode 100644 ext-rooter4/files/usr/lib/lua/luci/view/rooter/failover_overview.htm create mode 100644 ext-rooter4/files/usr/lib/rooter/connect/failover.sh create mode 100644 ext-rooter4/files/usr/lib/rooter/luci/failchk.sh create mode 100644 ext-rooter4/files/usr/lib/rooter/removeipv6.sh create mode 100644 ext-rooter45/Makefile create mode 100644 ext-rooter45/files/usr/lib/lua/luci/view/admin_status/indexnew.htm create mode 100644 ext-rooter45/files/usr/lib/lua/luci/view/rooter/external.htm create mode 100644 ext-rooter8/Makefile create mode 100644 ext-rooter8/files/etc/config/mwan3 create mode 100644 ext-rooter8/files/etc/flash create mode 100644 ext-rooter8/files/etc/hotplug.d/block/20-mount create mode 100644 ext-rooter8/files/etc/hotplug.d/block/99-mount create mode 100644 ext-rooter8/files/usr/lib/lua/luci/controller/mwan3.lua create mode 100644 ext-rooter8/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua create mode 100644 ext-rooter8/files/usr/lib/lua/luci/view/admin_status/indexnew.htm create mode 100644 ext-rooter8/files/usr/lib/lua/luci/view/rooter/external.htm create mode 100644 ext-rooter8/files/usr/sbin/mwan3track create mode 100644 ext-rooterbcm/Makefile create mode 100644 ext-rooterbcm/files/etc/config/mwan3 create mode 100644 ext-rooterbcm/files/etc/flash create mode 100644 ext-rooterbcm/files/etc/hotplug.d/block/20-mount create mode 100644 ext-rooterbcm/files/etc/hotplug.d/block/99-mount create mode 100644 ext-rooterbcm/files/usr/lib/lua/luci/controller/mwan3.lua create mode 100644 ext-rooterbcm/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua create mode 100644 ext-rooterbcm/files/usr/lib/lua/luci/view/admin_status/indexnew.htm create mode 100644 ext-rooterbcm/files/usr/lib/lua/luci/view/rooter/external.htm create mode 100644 ext-rooterbcm/files/usr/sbin/mwan3track create mode 100644 ext-rooterbcm8/Makefile create mode 100644 ext-rooterbcm8/files/etc/config/mwan3 create mode 100644 ext-rooterbcm8/files/etc/flash create mode 100644 ext-rooterbcm8/files/etc/hotplug.d/block/20-mount create mode 100644 ext-rooterbcm8/files/etc/hotplug.d/block/99-mount create mode 100644 ext-rooterbcm8/files/usr/lib/lua/luci/controller/mwan3.lua create mode 100644 ext-rooterbcm8/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua create mode 100644 ext-rooterbcm8/files/usr/lib/lua/luci/view/admin_status/indexnew.htm create mode 100644 ext-rooterbcm8/files/usr/lib/lua/luci/view/rooter/external.htm create mode 100644 ext-rooterbcm8/files/usr/sbin/mwan3track create mode 100644 ext-rooterlite/Makefile create mode 100644 ext-rooterlite/files/etc/config/mwan3 create mode 100644 ext-rooterlite/files/etc/flash create mode 100644 ext-rooterlite/files/usr/lib/lua/luci/controller/mwan3.lua create mode 100644 ext-rooterlite/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua create mode 100644 ext-rooterlite/files/usr/lib/lua/luci/view/admin_status/indexnew.htm create mode 100644 ext-rooterlite/files/usr/lib/lua/luci/view/rooter/external.htm create mode 100644 ext-rooterlite/files/usr/sbin/mwan3track create mode 100644 ext-samba/Makefile create mode 100644 ext-samba/files/etc/hotplug.d/block/30-mount create mode 100644 ext-sms/Makefile create mode 100644 ext-sms/files/usr/lib/lua/luci/controller/sms.lua create mode 100644 ext-sms/files/usr/lib/lua/luci/view/rooter/sms.htm create mode 100644 ext-sms/files/usr/lib/sms/delsms.sh create mode 100644 ext-sms/files/usr/lib/sms/pack7bit.lua create mode 100644 ext-sms/files/usr/lib/sms/processsms create mode 100644 ext-sms/files/usr/lib/sms/sendsms.lua create mode 100644 ext-sms/files/usr/lib/sms/sendsms.sh create mode 100644 ext-sms/files/usr/lib/sms/smsout.lua create mode 100644 ext-sms/files/usr/lib/sms/smsout.sh create mode 100644 ext-sms/files/usr/lib/sms/smsread.lua create mode 100644 ext-sms/files/usr/lib/sms/sys2sms.lua create mode 100644 ext-sms/files/usr/lib/sms/sys2sms.sh create mode 100644 ext-sms/files/usr/lib/sms/toggle.sh create mode 100644 ext-sms/files/usr/lib/sms/utf8togsm.lua create mode 100644 ext-umount/Makefile create mode 100644 ext-umount/files/etc/config/umount create mode 100644 ext-umount/files/etc/umount create mode 100644 ext-umount/files/usr/lib/lua/luci/controller/umount.lua create mode 100644 ext-umount/files/usr/lib/lua/luci/model/cbi/umount.lua create mode 100644 ext-wifi/Makefile create mode 100644 luci-app-dnsmasq-ipset/Makefile create mode 100644 luci-app-dnsmasq-ipset/files/etc/config/dnsmasq-ipset create mode 100644 luci-app-dnsmasq-ipset/files/etc/init.d/dnsmasq-ipset create mode 100644 luci-app-dnsmasq-ipset/files/usr/lib/lua/luci/controller/dnsmasq-ipset.lua create mode 100644 luci-app-dnsmasq-ipset/files/usr/lib/lua/luci/model/cbi/dnsmasq-ipset.lua create mode 100644 luci-app-hotspot/Makefile create mode 100644 luci-app-hotspot/files/etc/config/travelmate create mode 100644 luci-app-hotspot/files/etc/hotplug.d/iface/99-travelmate-iface create mode 100644 luci-app-hotspot/files/etc/init.d/travelmate create mode 100644 luci-app-hotspot/files/etc/init.d/zhot create mode 100644 luci-app-hotspot/files/lib/upgrade/keep.d/hotspot create mode 100644 luci-app-hotspot/files/usr/lib/hotspot/band.sh create mode 100644 luci-app-hotspot/files/usr/lib/hotspot/copyhot.sh create mode 100644 luci-app-hotspot/files/usr/lib/hotspot/dis_hot.sh create mode 100644 luci-app-hotspot/files/usr/lib/hotspot/enable.sh create mode 100644 luci-app-hotspot/files/usr/lib/hotspot/getssid.sh create mode 100644 luci-app-hotspot/files/usr/lib/hotspot/inrange.sh create mode 100644 luci-app-hotspot/files/usr/lib/hotspot/manual.sh create mode 100644 luci-app-hotspot/files/usr/lib/hotspot/mode.sh create mode 100644 luci-app-hotspot/files/usr/lib/hotspot/travelmate.sh create mode 100644 luci-app-hotspot/files/usr/lib/lua/luci/controller/hotspot.lua create mode 100644 luci-app-hotspot/files/usr/lib/lua/luci/view/hotspot/hotspot.htm create mode 100644 luci-app-rooterddns/Makefile create mode 100644 luci-app-rooterddns/files/etc/uci-defaults/40_luci-ddns create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/controller/ddns.lua create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/detail.lua create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/global.lua create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/hints.lua create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/overview.lua create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/tools/ddns.lua create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/view/admin_status/index/ddns.htm create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/detail_logview.htm create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/detail_lvalue.htm create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/detail_value.htm create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/global_value.htm create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_doubleline.htm create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_enabled.htm create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_startstop.htm create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_status.htm create mode 100644 luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/system_status.htm create mode 100644 luci-app-rootervpn/Makefile create mode 100644 luci-app-rootervpn/files/etc/config/rooter_recipes create mode 100644 luci-app-rootervpn/files/etc/init.d/rootervpn create mode 100644 luci-app-rootervpn/files/etc/openvpn/airvpn/ca.crt create mode 100644 luci-app-rootervpn/files/etc/openvpn/airvpn/client.crt create mode 100644 luci-app-rootervpn/files/etc/openvpn/airvpn/client.key create mode 100644 luci-app-rootervpn/files/etc/openvpn/airvpn/ta.key create mode 100644 luci-app-rootervpn/files/etc/openvpn/mullvad/mullvad_ca.crt create mode 100644 luci-app-rootervpn/files/etc/openvpn/mullvad/mullvad_crl.pem create mode 100644 luci-app-rootervpn/files/etc/openvpn/pia/ca.rsa.2048.crt create mode 100644 luci-app-rootervpn/files/etc/openvpn/pia/crl.rsa.2048.pem create mode 100644 luci-app-rootervpn/files/etc/openvpn/placeholder/placeholder.file create mode 100644 luci-app-rootervpn/files/etc/openvpn/windscribe/ca.crt create mode 100644 luci-app-rootervpn/files/etc/openvpn/windscribe/ta.key create mode 100644 luci-app-rootervpn/files/lib/upgrade/keep.d/rootervpn create mode 100644 luci-app-rootervpn/files/usr/bin/ovpn-userpass create mode 100644 luci-app-rootervpn/files/usr/lib/easyrsa/dns.sh create mode 100644 luci-app-rootervpn/files/usr/lib/easyrsa/firewall.sh create mode 100644 luci-app-rootervpn/files/usr/lib/easyrsa/generate.sh create mode 100644 luci-app-rootervpn/files/usr/lib/easyrsa/openvpn create mode 100644 luci-app-rootervpn/files/usr/lib/easyrsa/stop.sh create mode 100644 luci-app-rootervpn/files/usr/lib/lua/luci/controller/openvpn.lua create mode 100644 luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/easyrsa.lua create mode 100644 luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/openvpn-advanced.lua create mode 100644 luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/openvpn-basic.lua create mode 100644 luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/openvpn.lua create mode 100644 luci-app-rootervpn/files/usr/lib/lua/luci/view/easyrsa/easyrsa.htm create mode 100644 luci-app-rootervpn/files/usr/lib/lua/luci/view/openvpn/cbi-select-input-add.htm create mode 100644 luci-app-rootervpn/files/usr/lib/lua/luci/view/openvpn/pageswitch.htm create mode 100644 luci-proto-3x/Makefile create mode 100644 luci-proto-3x/files/usr/lib/lua/luci/model/cbi/admin_network/proto_3x.lua create mode 100644 luci-proto-3x/files/usr/lib/lua/luci/model/network/proto_3x.lua create mode 100644 luci-proto-mbim/Makefile create mode 100644 luci-proto-mbim/files/usr/lib/lua/luci/model/cbi/admin_network/proto_mbim.lua create mode 100644 luci-proto-mbim/files/usr/lib/lua/luci/model/network/proto_mbim.lua create mode 100644 luci-theme-darkmatter/Makefile create mode 100644 luci-theme-darkmatter/files/htdocs/css/style.css create mode 100644 luci-theme-darkmatter/files/htdocs/favicon.ico create mode 100644 luci-theme-darkmatter/files/htdocs/fonts/DIN.eot create mode 100644 luci-theme-darkmatter/files/htdocs/fonts/DIN.otf create mode 100644 luci-theme-darkmatter/files/htdocs/fonts/DIN.svg create mode 100644 luci-theme-darkmatter/files/htdocs/fonts/DIN.ttf create mode 100644 luci-theme-darkmatter/files/htdocs/fonts/DIN.woff create mode 100644 luci-theme-darkmatter/files/htdocs/fonts/font.eot create mode 100644 luci-theme-darkmatter/files/htdocs/fonts/font.svg create mode 100644 luci-theme-darkmatter/files/htdocs/fonts/font.ttf create mode 100644 luci-theme-darkmatter/files/htdocs/fonts/font.woff create mode 100644 luci-theme-darkmatter/files/htdocs/js/jquery.min.js create mode 100644 luci-theme-darkmatter/files/htdocs/js/script.js create mode 100644 luci-theme-darkmatter/files/htdocs/logo.png create mode 100644 luci-theme-darkmatter/files/templates/footer.htm create mode 100644 luci-theme-darkmatter/files/templates/header.htm create mode 100644 luci-theme-rooter/Makefile create mode 100644 luci-theme-rooter/files/htdocs/css/style.css create mode 100644 luci-theme-rooter/files/htdocs/favicon.ico create mode 100644 luci-theme-rooter/files/htdocs/fonts/font.eot create mode 100644 luci-theme-rooter/files/htdocs/fonts/font.svg create mode 100644 luci-theme-rooter/files/htdocs/fonts/font.ttf create mode 100644 luci-theme-rooter/files/htdocs/fonts/font.woff create mode 100644 luci-theme-rooter/files/htdocs/js/jquery.min.js create mode 100644 luci-theme-rooter/files/htdocs/js/script.js create mode 100644 luci-theme-rooter/files/htdocs/logo.png create mode 100644 luci-theme-rooter/files/templates/footer.htm create mode 100644 luci-theme-rooter/files/templates/header.htm create mode 100644 rmbim/.svn/entries create mode 100644 rmbim/.svn/text-base/Makefile.svn-base create mode 100644 rmbim/Makefile create mode 100644 rmbim/files/lib/netifd/proto/mbim.sh create mode 100644 rmbim/files/usr/lib/rooter/mbim/connectmbim.sh create mode 100644 rmbim/files/usr/lib/rooter/mbim/mbimdata.sh create mode 100644 rmbim/files/usr/lib/rooter/mbim/monitor.sh create mode 100644 rqmi/.svn/entries create mode 100644 rqmi/.svn/text-base/Makefile.svn-base create mode 100644 rqmi/Makefile create mode 100644 rqmi/Makefile-4meg create mode 100644 rqmi/files/usr/lib/rooter/qmi/connectqmi.sh diff --git a/bwmon/Makefile b/bwmon/Makefile new file mode 100644 index 0000000..c413b2f --- /dev/null +++ b/bwmon/Makefile @@ -0,0 +1,35 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=bwmon +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/bwmon + SECTION:=utils + CATEGORY:=ROOter + SUBMENU:=Optional Applications + TITLE:=Install Bandwidth Monitor + PKGARCH:=all +endef + +define Package/bwmon/description + Helper scripts to install Bandwidth Monitor on ROOter +endef + + +define Build/Compile +endef + +define Package/bwmon/install + $(CP) ./files/* $(1)/ + + +endef + +$(eval $(call BuildPackage,bwmon)) diff --git a/bwmon/files/etc/config/bwmon b/bwmon/files/etc/config/bwmon new file mode 100644 index 0000000..a76c539 --- /dev/null +++ b/bwmon/files/etc/config/bwmon @@ -0,0 +1,5 @@ +config bwmon 'bwmon' + option enabled '0' + option unlimited_usage '0' + option unlimited_start '2:00' + option unlimited_end '8:00' \ No newline at end of file diff --git a/bwmon/files/etc/init.d/bwmon b/bwmon/files/etc/init.d/bwmon new file mode 100644 index 0000000..33742b8 --- /dev/null +++ b/bwmon/files/etc/init.d/bwmon @@ -0,0 +1,15 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006 OpenWrt.org + +START=99 + +start() { + ENB=$(uci get bwmon.bwmon.enabled) + if [ $ENB = "1" ]; then + /opt/WRTbmon/wrtbwmon.sh & + fi +} + +stop() { + rmdir -f /tmp/WRTbmon +} \ No newline at end of file diff --git a/bwmon/files/lib/upgrade/keep.d/bwmon b/bwmon/files/lib/upgrade/keep.d/bwmon new file mode 100644 index 0000000..f822e61 --- /dev/null +++ b/bwmon/files/lib/upgrade/keep.d/bwmon @@ -0,0 +1 @@ +/opt/WRTbmon/data/ diff --git a/bwmon/files/opt/WRTbmon/cleanup.lua b/bwmon/files/opt/WRTbmon/cleanup.lua new file mode 100644 index 0000000..6e70812 --- /dev/null +++ b/bwmon/files/opt/WRTbmon/cleanup.lua @@ -0,0 +1,32 @@ +#!/usr/bin/lua + +filepost = "-mac_data.js" +dirname = '/opt/WRTbmon/data' + +function clean() + nummon = 0 + months = {} + f = io.popen('/bin/ls ' .. dirname) + for name in f:lines() do + s, e = name:find(filepost) + if s ~= nil then + nummon = nummon + 1 + months[nummon] = dirname .. "/" .. name + end + end + f:close() + + count = 1 + if nummon > 0 then + for i=nummon,1,-1 do + if count > 3 then + os.execute("rm -f " .. months[i]) + end + count = count + 1 + end + end +end + +clean() +dirname = '/tmp/bwmon/data' +clean() \ No newline at end of file diff --git a/bwmon/files/opt/WRTbmon/process.sh b/bwmon/files/opt/WRTbmon/process.sh new file mode 100644 index 0000000..aa504d5 --- /dev/null +++ b/bwmon/files/opt/WRTbmon/process.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +log() { + logger -t "BWmon Process" "$@" +} + +running=0 +if [ -e "/tmp/WRTbmon" ]; then + running=1 +fi + +ENB=$(uci get bwmon.bwmon.enabled) + +if [ $running = 1 ]; then + if [ $ENB = 0 ]; then + log "Disable BWmon" + rmdir /tmp/WRTbmon + sleep 4 + fi +else + if [ $ENB = 1 ]; then + log "Enable BWmon" + /opt/WRTbmon/wrtbwmon.sh & + fi +fi + + + diff --git a/bwmon/files/opt/WRTbmon/wrtbwmon.sh b/bwmon/files/opt/WRTbmon/wrtbwmon.sh new file mode 100644 index 0000000..3015865 --- /dev/null +++ b/bwmon/files/opt/WRTbmon/wrtbwmon.sh @@ -0,0 +1,270 @@ +#!/bin/sh + +LAN_TYPE=$(uci get network.lan.ipaddr | awk -F. ' { print $1"."$2 }') +LEASES_FILE=/tmp/dhcp.leases +lockDir=/tmp/WRTbmon + +[ ! -d "$lockDir" ] && mkdir "$lockDir" +basePath="/tmp/bwmon/" +mkdir -p $basePath"data" +dataPath=$basePath"data/" +backPath=/opt/WRTbmon/data/ +STARTIMEX=$(date +%s) +STARTIMEY=$(date +%s) +STARTIMEZ=$(date +%s) +cYear=$(date +%Y) +cDay=$(date +%d) +cMonth=$(date +%m) +setup_time=60 +update_time=300 +backup_time=1200 +pause=30 +unlimited="peak" + +log() { + logger -t "wrtbwmon" "$@" +} + +lock() +{ + while [ -f /tmp/wrtbwmon.lock ]; do + if [ ! -d /proc/$(cat /tmp/wrtbwmon.lock) ]; then + log "WARNING : Lockfile detected but process $(cat /tmp/wrtbwmon.lock) does not exist !" + rm -f /tmp/wrtbwmon.lock + fi + sleep 1 + done + echo $$ > /tmp/wrtbwmon.lock +} + +unlock() +{ + rm -f /tmp/wrtbwmon.lock +} + +setup() +{ + #Create the RRDIPT CHAIN (it doesn't matter if it already exists). + iptables -N RRDIPT 2> /dev/null + + #Add the RRDIPT CHAIN to the FORWARD chain (if non existing). + iptables -L FORWARD --line-numbers -n | grep "RRDIPT" | grep "1" > /dev/null + if [ $? -ne 0 ]; then + iptables -L FORWARD -n | grep "RRDIPT" > /dev/null + if [ $? -eq 0 ]; then + log "DEBUG : iptables chain misplaced, recreating it..." + iptables -D FORWARD -j RRDIPT + fi + iptables -I FORWARD -j RRDIPT + fi + + #For each host in the ARP table + grep ${LAN_TYPE} /proc/net/arp | while read IP TYPE FLAGS MAC MASK IFACE + do + #Add iptable rules (if non existing). + iptables -nL RRDIPT | grep "${IP} " > /dev/null + if [ $? -ne 0 ]; then + iptables -I RRDIPT -d ${IP} -j RETURN + iptables -I RRDIPT -s ${IP} -j RETURN + fi + done +} + +update() +{ + [ ! -f "${1}" -a -f /etc/config/usage.db ] && cp /etc/config/usage.db ${1} + lock + + #Read and reset counters + iptables -L RRDIPT -vnxZ -t filter > /tmp/traffic_$$.tmp + wan=$(ubus -S call network.interface.wan status | jsonfilter -e '@.device' ) + if [ -z $wan ]; then + wan="xxxx" + fi + grep -v "0x0" /proc/net/arp | grep ${LAN_TYPE} | grep -v "$wan" | while read IP TYPE FLAGS MAC MASK IFACE + do + grep ${IP} /tmp/traffic_$$.tmp | while read PKTS BYTES TARGET PROT OPT IFIN IFOUT SRC DST + do + [ "${DST}" = "${IP}" ] && echo $((${BYTES}/1000)) > /tmp/in_$$.tmp + [ "${SRC}" = "${IP}" ] && echo $((${BYTES}/1000)) > /tmp/out_$$.tmp + done + IN=$(cat /tmp/in_$$.tmp) + OUT=$(cat /tmp/out_$$.tmp) + rm -f /tmp/in_$$.tmp + rm -f /tmp/out_$$.tmp + if [ ${IN} -gt 0 -o ${OUT} -gt 0 ]; then + LINE=$(grep ${MAC} ${1}) + if [ -z "${LINE}" ]; then + PEAKUSAGE_IN=0 + PEAKUSAGE_OUT=0 + OFFPEAKUSAGE_IN=0 + OFFPEAKUSAGE_OUT=0 + else + PEAKUSAGE_IN=$(echo ${LINE} | cut -f2 -s -d, | awk -F: ' { print $2 }' | sed 's/"//g' ) + PEAKUSAGE_OUT=$(echo ${LINE} | cut -f3 -s -d, | awk -F: ' { print $2 }' | sed 's/"//g' ) + OFFPEAKUSAGE_IN=$(echo ${LINE} | cut -f4 -s -d, | awk -F: ' { print $2 }' | sed 's/"//g' ) + OFFPEAKUSAGE_OUT=$(echo ${LINE} | cut -f5 -s -d, | awk -F: ' { print $2 }' | sed 's/"//g' ) + fi + + if [ "${2}" = "offpeak" ]; then + OFFPEAKUSAGE_IN=$((${OFFPEAKUSAGE_IN}+${IN})) + OFFPEAKUSAGE_OUT=$((${OFFPEAKUSAGE_OUT}+${OUT})) + else + PEAKUSAGE_IN=$((${PEAKUSAGE_IN}+${IN})) + PEAKUSAGE_OUT=$((${PEAKUSAGE_OUT}+${OUT})) + fi + + for USERSFILE in /tmp/dhcp.leases /tmp/dnsmasq.conf /etc/dnsmasq.conf /etc/hosts; do + [ -e "$USERSFILE" ] || continue + case $USERSFILE in + /tmp/dhcp.leases ) + NAME=$(grep -i "$MAC" $USERSFILE | cut -f4 -s -d' ') + ;; + /etc/hosts ) + NAME=$(grep "^$IP " $USERSFILE | cut -f2 -s -d' ') + ;; + * ) + NAME=$(grep -i "$MAC" "$USERSFILE" | cut -f2 -s -d,) + ;; + esac + [ "$NAME" = "*" ] && NAME= + [ -n "$NAME" ] && break + done + + #NAME=$(cat $LEASES_FILE | grep ${LAN_TYPE} | grep ${MAC} | awk -F[\ ] ' { print $4 }') + if [ -z $NAME ]; then + NAME="*" + fi + grep -v "${MAC}" ${1} > /tmp/db_$$.tmp + mv /tmp/db_$$.tmp ${1} + echo "\"mac\":\""${MAC}"\"","\"down\":\""${PEAKUSAGE_IN}"\"","\"up\":\""${PEAKUSAGE_OUT}"\"","\"offdown\":\""${OFFPEAKUSAGE_IN}"\"","\"offup\":\""${OFFPEAKUSAGE_OUT}"\"","\"ip\":\""${IP}"\"","\"name\":\""${NAME}"\"" >> ${1} + fi + done + rm -f /tmp/*_$$.tmp + unlock +} + + +createFiles() +{ + dailyUsageDB="$dataPath$cYear-$cMonth-$cDay-daily_data.js" + dailyUsageBack="$backPath$cYear-$cMonth-$cDay-daily_data.js" + if [ ! -f $dailyUsageBack ]; then + touch $dailyUsageDB + else + cp -f $dailyUsageBack $dailyUsageDB + fi + monthlyUsageDB="$dataPath$cYear-$cMonth-mac_data.js" + monthlyUsageBack="$backPath$cYear-$cMonth-mac_data.js" + if [ -f $monthlyUsageBack ]; then + cp -f $monthlyUsageBack $monthlyUsageDB".bk" + sed "/start day $cDay/,/end day $cDay/d" $monthlyUsageDB".bk" > $monthlyUsageDB + rm -f $monthlyUsageDB".bk" + else + touch $monthlyUsageDB + fi +} + +shutDown() +{ + cp -f $dailyUsageDB $dailyUsageBack + cp -f $monthlyUsageDB $monthlyUsageDB".bk" + echo "start day $cDay" >> $monthlyUsageDB".bk" + cat "$dailyUsageDB" >> $monthlyUsageDB".bk" + echo "end day $cDay" >> $monthlyUsageDB".bk" + cp -f $monthlyUsageDB".bk" $monthlyUsageBack + rm -f $monthlyUsageDB".bk" + log "Cleanup backup" + lua /opt/WRTbmon/cleanup.lua +} + +checkSetup() +{ + CURRTIME=$(date +%s) + let ELAPSE=CURRTIME-STARTIMEX + if [ $ELAPSE -gt $setup_time ]; then + STARTIMEX=$CURRTIME + setup + fi +} + +checkUpdate() +{ + CURRTIME=$(date +%s) + let ELAPSE=CURRTIME-STARTIMEY + if [ $ELAPSE -gt $update_time ]; then + STARTIMEY=$CURRTIME + update $dailyUsageDB $unlimited + fi +} + +checkBackup() +{ + CURRTIME=$(date +%s) + let ELAPSE=CURRTIME-STARTIMEZ + if [ $ELAPSE -gt $backup_time ]; then + STARTIMEZ=$CURRTIME + shutDown + fi +} + +checkTime() +{ + pDay=$(date +%d) + pYear=$(date +%Y) + pMonth=$(date +%m) + if [ "$cDay" -ne "$pDay" ]; then + echo "start day $cDay" >> $monthlyUsageDB + cat "$dailyUsageDB" >> $monthlyUsageDB + echo "end day $cDay" >> $monthlyUsageDB + cp -f $monthlyUsageDB $monthlyUsageBack + cDay=$pDay + rm -f $dataPath[[:digit:]][[:digit:]][[:digit:]][[:digit:]]"-"[[:digit:]][[:digit:]]"-"[[:digit:]][[:digit:]]-daily_data.js + rm -f $backPath[[:digit:]][[:digit:]][[:digit:]][[:digit:]]"-"[[:digit:]][[:digit:]]"-"[[:digit:]][[:digit:]]-daily_data.js + if [ "$cMonth" -ne "$pMonth" ]; then + cMonth=$pMonth + cYear=$pYear + monthlyUsageDB="$dataPath$cYear-$cMonth-mac_data.js" + monthlyUsageBack="$backPath$cYear-$cMonth-mac_data.js" + touch $monthlyUsageDB + fi + rm -f $dailyUsageDB + rm -f $dailyUsageBack + dailyUsageDB="$dataPath$cYear-$cMonth-$cDay-daily_data.js" + touch $dailyUsageDB + dailyUsageBack="$backPath$cYear-$cMonth-$cDay-daily_data.js" + fi + unlimited="peak" + hasUnlimited=$(uci get bwmon.bwmon.unlimited_usage) + if [ $hasUnlimited = 1 ]; then + unlimited_start=$(uci get bwmon.bwmon.unlimited_start) + unlimited_end=$(uci get bwmon.bwmon.unlimited_end) + ul_start=$(date -d "$unlimited_start" +%s); + ul_end=$(date -d "$unlimited_end" +%s); + [ "$ul_end" -lt "$ul_start" ] && ul_start=$((ul_start - 86400)) + currTime=$(date +%s) + inUnlimited=$((currTime >= ul_start && currTime <= ul_end)) + if [ "$inUnlimited" -eq "1" ]; then + unlimited="offpeak" + fi + fi +} + +createFiles +setup +while [ -d $lockDir ]; do + checkSetup + checkTime + checkUpdate + checkBackup + n=0 + while [ true ] ; do + n=$(($n + 1)) + if [ ! -d "$lockDir" ]; then + shutDown + exit 0 + fi + [ "$n" -gt "$pause" ] && break; + sleep 1 + done +done diff --git a/bwmon/files/usr/lib/lua/luci/controller/bwmon.lua b/bwmon/files/usr/lib/lua/luci/controller/bwmon.lua new file mode 100644 index 0000000..277f0c6 --- /dev/null +++ b/bwmon/files/usr/lib/lua/luci/controller/bwmon.lua @@ -0,0 +1,7 @@ +module("luci.controller.bwmon", package.seeall) + +function index() + local page + page = entry({"admin", "services", "bwmon"}, cbi("bwmon/bwmon"), "Bandwidth Monitoring", 70) + page.dependent = true +end \ No newline at end of file diff --git a/bwmon/files/usr/lib/lua/luci/model/cbi/bwmon/bwmon.lua b/bwmon/files/usr/lib/lua/luci/model/cbi/bwmon/bwmon.lua new file mode 100644 index 0000000..800b131 --- /dev/null +++ b/bwmon/files/usr/lib/lua/luci/model/cbi/bwmon/bwmon.lua @@ -0,0 +1,444 @@ +local fs = require "nixio.fs" +local util = require "nixio.util" + +m = Map("bwmon", translate("Bandwidth Monitoring"), translate("Monitor bandwidth by Device")) + +m.on_after_commit = function(self) + luci.sys.call("/opt/WRTbmon/process.sh") + --luci.http.redirect(luci.dispatcher.build_url("admin", "services", "bwmon")) +end + +s=m:section(TypedSection, "bwmon", translate("Settings")) +s.addremove=false +s.anonymous=true + +enable=s:option(Flag, "enabled", translate("Enabled"), translate("Monitor must be enabled in order to gather data displayed on this page")) +enable.rmempty=false + +unlimit = s:option(ListValue, "unlimited_usage", translate("Unlimited Usage Available :"), translate("You are allowed to use unlimited bandwidth without charge at specific times of day. This bandwidth is not included in the totals")) +unlimit.rmempty = true +unlimit:value("0", "No") +unlimit:value("1", "Yes") +unlimit.default = "0" + +sdhour = s:option(ListValue, "unlimited_start", translate("Unlimited Start Time :")) +sdhour.rmempty = true +sdhour:value("0:00", "12:00 AM") +sdhour:value("0:15", "12:15 AM") +sdhour:value("0:30", "12:30 AM") +sdhour:value("0:45", "12:45 AM") +sdhour:value("1:00", "01:00 AM") +sdhour:value("1:15", "01:15 AM") +sdhour:value("1:30", "01:30 AM") +sdhour:value("1:45", "01:45 AM") +sdhour:value("2:00", "02:00 AM") +sdhour:value("2:15", "02:15 AM") +sdhour:value("2:30", "02:30 AM") +sdhour:value("2:45", "02:45 AM") +sdhour:value("3:00", "03:00 AM") +sdhour:value("3:15", "03:15 AM") +sdhour:value("3:30", "03:30 AM") +sdhour:value("3:45", "03:45 AM") +sdhour:value("4:00", "04:00 AM") +sdhour:value("4:15", "04:15 AM") +sdhour:value("4:30", "04:30 AM") +sdhour:value("4:45", "04:45 AM") +sdhour:value("5:00", "05:00 AM") +sdhour:value("5:15", "05:15 AM") +sdhour:value("5:30", "05:30 AM") +sdhour:value("5:45", "05:45 AM") +sdhour:value("6:00", "06:00 AM") +sdhour:value("6:15", "06:15 AM") +sdhour:value("6:30", "06:30 AM") +sdhour:value("6:45", "06:45 AM") +sdhour:value("7:00", "07:00 AM") +sdhour:value("7:15", "07:15 AM") +sdhour:value("7:30", "07:30 AM") +sdhour:value("7:45", "07:45 AM") +sdhour:value("8:00", "08:00 AM") +sdhour:value("8:15", "08:15 AM") +sdhour:value("8:30", "08:30 AM") +sdhour:value("8:45", "08:45 AM") +sdhour:value("9:00", "09:00 AM") +sdhour:value("9:15", "09:15 AM") +sdhour:value("9:30", "09:30 AM") +sdhour:value("9:45", "09:45 AM") +sdhour:value("10:00", "10:00 AM") +sdhour:value("10:15", "10:15 AM") +sdhour:value("10:30", "10:30 AM") +sdhour:value("10:45", "10:45 AM") +sdhour:value("11:00", "11:00 AM") +sdhour:value("11:15", "11:15 AM") +sdhour:value("11:30", "11:30 AM") +sdhour:value("11:45", "11:45 AM") +sdhour:value("12:00", "12:00 PM") +sdhour:value("12:15", "12:15 PM") +sdhour:value("12:30", "12:30 PM") +sdhour:value("12:45", "12:45 PM") +sdhour:value("13:00", "01:00 PM") +sdhour:value("13:15", "01:15 PM") +sdhour:value("13:30", "01:30 PM") +sdhour:value("13:45", "01:45 PM") +sdhour:value("14:00", "02:00 PM") +sdhour:value("14:15", "02:15 PM") +sdhour:value("14:30", "02:30 PM") +sdhour:value("14:45", "02:45 PM") +sdhour:value("15:00", "03:00 PM") +sdhour:value("15:15", "03:15 PM") +sdhour:value("15:30", "03:30 PM") +sdhour:value("15:45", "03:45 PM") +sdhour:value("16:00", "04:00 PM") +sdhour:value("16:15", "04:15 PM") +sdhour:value("16:30", "04:30 PM") +sdhour:value("16:45", "04:45 PM") +sdhour:value("17:00", "05:00 PM") +sdhour:value("17:15", "05:15 PM") +sdhour:value("17:30", "05:30 PM") +sdhour:value("17:45", "05:45 PM") +sdhour:value("18:00", "06:00 PM") +sdhour:value("18:15", "06:15 PM") +sdhour:value("18:30", "06:30 PM") +sdhour:value("18:45", "06:45 PM") +sdhour:value("19:00", "07:00 PM") +sdhour:value("19:15", "07:15 PM") +sdhour:value("19:30", "07:30 PM") +sdhour:value("19:45", "07:45 PM") +sdhour:value("20:00", "08:00 PM") +sdhour:value("20:15", "08:15 PM") +sdhour:value("20:30", "08:30 PM") +sdhour:value("20:45", "08:45 PM") +sdhour:value("21:00", "09:00 PM") +sdhour:value("21:15", "09:15 PM") +sdhour:value("21:30", "09:30 PM") +sdhour:value("21:45", "09:45 PM") +sdhour:value("22:00", "10:00 PM") +sdhour:value("22:15", "10:15 PM") +sdhour:value("22:30", "10:30 PM") +sdhour:value("22:45", "10:45 PM") +sdhour:value("23:00", "11:00 PM") +sdhour:value("23:15", "11:15 PM") +sdhour:value("23:30", "11:30 PM") +sdhour:value("23:45", "11:45 PM") + +sdhour:depends("unlimited_usage", "1") +sdhour.default = "0:00" + +edhour = s:option(ListValue, "unlimited_end", translate("Unlimited End Time :")) +edhour.rmempty = true +edhour:value("0:00", "12:00 AM") +edhour:value("0:15", "12:15 AM") +edhour:value("0:30", "12:30 AM") +edhour:value("0:45", "12:45 AM") +edhour:value("1:00", "01:00 AM") +edhour:value("1:15", "01:15 AM") +edhour:value("1:30", "01:30 AM") +edhour:value("1:45", "01:45 AM") +edhour:value("2:00", "02:00 AM") +edhour:value("2:15", "02:15 AM") +edhour:value("2:30", "02:30 AM") +edhour:value("2:45", "02:45 AM") +edhour:value("3:00", "03:00 AM") +edhour:value("3:15", "03:15 AM") +edhour:value("3:30", "03:30 AM") +edhour:value("3:45", "03:45 AM") +edhour:value("4:00", "04:00 AM") +edhour:value("4:15", "04:15 AM") +edhour:value("4:30", "04:30 AM") +edhour:value("4:45", "04:45 AM") +edhour:value("5:00", "05:00 AM") +edhour:value("5:15", "05:15 AM") +edhour:value("5:30", "05:30 AM") +edhour:value("5:45", "05:45 AM") +edhour:value("6:00", "06:00 AM") +edhour:value("6:15", "06:15 AM") +edhour:value("6:30", "06:30 AM") +edhour:value("6:45", "06:45 AM") +edhour:value("7:00", "07:00 AM") +edhour:value("7:15", "07:15 AM") +edhour:value("7:30", "07:30 AM") +edhour:value("7:45", "07:45 AM") +edhour:value("8:00", "08:00 AM") +edhour:value("8:15", "08:15 AM") +edhour:value("8:30", "08:30 AM") +edhour:value("8:45", "08:45 AM") +edhour:value("9:00", "09:00 AM") +edhour:value("9:15", "09:15 AM") +edhour:value("9:30", "09:30 AM") +edhour:value("9:45", "09:45 AM") +edhour:value("10:00", "10:00 AM") +edhour:value("10:15", "10:15 AM") +edhour:value("10:30", "10:30 AM") +edhour:value("10:45", "10:45 AM") +edhour:value("11:00", "11:00 AM") +edhour:value("11:15", "11:15 AM") +edhour:value("11:30", "11:30 AM") +edhour:value("11:45", "11:45 AM") +edhour:value("12:00", "12:00 PM") +edhour:value("12:15", "12:15 PM") +edhour:value("12:30", "12:30 PM") +edhour:value("12:45", "12:45 PM") +edhour:value("13:00", "01:00 PM") +edhour:value("13:15", "01:15 PM") +edhour:value("13:30", "01:30 PM") +edhour:value("13:45", "01:45 PM") +edhour:value("14:00", "02:00 PM") +edhour:value("14:15", "02:15 PM") +edhour:value("14:30", "02:30 PM") +edhour:value("14:45", "02:45 PM") +edhour:value("15:00", "03:00 PM") +edhour:value("15:15", "03:15 PM") +edhour:value("15:30", "03:30 PM") +edhour:value("15:45", "03:45 PM") +edhour:value("16:00", "04:00 PM") +edhour:value("16:15", "04:15 PM") +edhour:value("16:30", "04:30 PM") +edhour:value("16:45", "04:45 PM") +edhour:value("17:00", "05:00 PM") +edhour:value("17:15", "05:15 PM") +edhour:value("17:30", "05:30 PM") +edhour:value("17:45", "05:45 PM") +edhour:value("18:00", "06:00 PM") +edhour:value("18:15", "06:15 PM") +edhour:value("18:30", "06:30 PM") +edhour:value("18:45", "06:45 PM") +edhour:value("19:00", "07:00 PM") +edhour:value("19:15", "07:15 PM") +edhour:value("19:30", "07:30 PM") +edhour:value("19:45", "07:45 PM") +edhour:value("20:00", "08:00 PM") +edhour:value("20:15", "08:15 PM") +edhour:value("20:30", "08:30 PM") +edhour:value("20:45", "08:45 PM") +edhour:value("21:00", "09:00 PM") +edhour:value("21:15", "09:15 PM") +edhour:value("21:30", "09:30 PM") +edhour:value("21:45", "09:45 PM") +edhour:value("22:00", "10:00 PM") +edhour:value("22:15", "10:15 PM") +edhour:value("22:30", "10:30 PM") +edhour:value("22:45", "10:45 PM") +edhour:value("23:00", "11:00 PM") +edhour:value("23:15", "11:15 PM") +edhour:value("23:30", "11:30 PM") +edhour:value("23:45", "11:45 PM") + +edhour:depends("unlimited_usage", "1") +edhour.default = "8:00" + +--m:section(SimpleSection).template = "rooter/bandw" + +sx = m:section(TypedSection, "bwmon", "Data Usage") +sx.anonymous = true + +function pad(strng, tabs) + len = string.len(strng) + tabx = tabs - math.floor((len) / 4) + tabstr = string.rep("\t", tabx) + --if tabs > 0 then + --tabx = string.rep(" ", 32) + --namstr = string.rep("+", 24) + --if tabx > 0 then + strng = strng .. tabstr + --else + --strng = strng .. "\t" + --end + --strng = strng:sub(1,32) + --strng = strng .. tabstr + --strng = strng:sub(1,(tabs * 8) + 24) .. " " + return strng +end + +function calc(total) + if total < 1000 then + tstr = string.format("%10.2f", total) + tfm = " K" + else + if total < 1000000 then + tstr = string.format("%10.2f", total/1000) + tfm = " MB" + else + tstr = string.format("%10.2f", total/1000000) + tfm = " GB" + end + end + str = tstr .. tfm + str = string.rep(" ", 16) .. str + --str = str:sub(-16) + str = string.sub(str, -14) + return str +end + +function monthly(datafile) + file = io.open(datafile, "r") + i = 0 + dayx = 0 + repeat + line = file:read("*line") + if line == nil then + break + end + s, e = line:find("start day") + if s ~= nil then + dayx = dayx + 1 + repeat + line = file:read("*line") + s, e = line:find("end day") + if s ~= nil then + break + end + s, e = line:find("\"mac\":\"") + bs, be = line:find("\"", e+1) + mac = line:sub(e+1, bs-1) + if bw[mac] == nil then + maclist[i] = mac + i = i + 1 + bw[mac] = {} + bw[mac]['down'] = 0 + bw[mac]['offdown'] = 0 + bw[mac]['up'] = 0 + bw[mac]['offup'] = 0 + end + s, e = line:find("\"down\":\"") + bs, be = line:find("\"", e+1) + down = tonumber(line:sub(e+1, bs-1)) + bw[mac]['down'] = bw[mac]['down'] + down + s, e = line:find("\"up\":\"") + bs, be = line:find("\"", e+1) + up = tonumber(line:sub(e+1, bs-1)) + bw[mac]['up'] = bw[mac]['up'] + up + s, e = line:find("\"offdown\":\"") + bs, be = line:find("\"", e+1) + offdown = tonumber(line:sub(e+1, bs-1)) + bw[mac]['offdown'] = bw[mac]['offdown'] + offdown + s, e = line:find("\"offup\":\"") + bs, be = line:find("\"", e+1) + offup = tonumber(line:sub(e+1, bs-1)) + bw[mac]['offup'] = bw[mac]['offup'] + offup + s, e = line:find("\"ip\":\"") + bs, be = line:find("\"", e+1) + bw[mac]['ip'] = line:sub(e+1, bs-1) + s, e = line:find("\"name\":\"") + bs, be = line:find("\"", e+1) + bw[mac]['name'] = line:sub(e+1, bs-1) + until 1==0 + end + until 1==0 + file:close() + return dayx +end + +function showdevices(bw, maclist) + devices = "---DEVICES---\n\n" .. "IP ADDRESS\t\tMAC ADDRESS\t\tDOWNLOADS\t\t UPLOADS\t\t TOTAL\t\t\tDEVICE NAME\n" .. string.rep("_", 120) .. "\n\n" + k = 0 + while maclist[k] ~= nil do + k = k + 1 + end + if k > 0 then + j = 0 + while maclist[j] ~= nil do + dtot = bw[maclist[j]]['down'] + bw[maclist[j]]['up'] + devices = devices .. pad(bw[maclist[j]]['ip'], 5) .. pad(maclist[j],5) .. "\t" + devices = devices .. pad(calc(bw[maclist[j]]['down']), 2) .. "\t\t" .. pad(calc(bw[maclist[j]]['up']), 2) .. "\t\t" + devices = devices .. pad(calc(dtot), 2) .. "\t\t" .. pad(bw[maclist[j]]['name'], 6) .. "\n" + + j = j + 1 + end + end + return devices +end + +function totals(bw, maclist, dayz) + totaldown = 0 + totalup = 0 + utotaldown = 0 + utotalup = 0 + j=0 + while maclist[j] ~= nil do + totaldown = totaldown + bw[maclist[j]]['down'] + totalup = totalup + bw[maclist[j]]['up'] + utotaldown = utotaldown + bw[maclist[j]]['offdown'] + utotalup = utotalup + bw[maclist[j]]['offup'] + j = j + 1 + end + total = totalup + totaldown + ptotal = (total / dayz) * 30 + totaline = " # of Days : " .. string.format("%d", dayz) .."\n\n Download : " .. calc(totaldown) .. "\t\t Upload : " .. calc(totalup) .. "\t\t Total : " .. calc(total) .. "\t\t Projected Monthly Total : " .. calc(ptotal) .. "\n\n\n" + ttotals = "---METERED TOTALS---\n\n" .. totaline + utotal = utotalup + utotaldown + if utotal > 0 then + utotaline = " DOWNLOAD : " .. calc(utotaldown) .. "\tUPLOAD : " .. calc(utotalup) .. "\tTOTALS : " .. calc(utotal) .. "\n\n\n" + ttotals = ttotals .. "---UNMETERED TOTALS---\n\n" .. utotaline + end + return ttotals +end + +months = {} +days = {} +nummon = 0 +tabname = {} +totline = {} +devline = {} +dirname = '/opt/WRTbmon/data' +filepost = "-mac_data.js" + +f = io.popen('/bin/ls ' .. dirname) +for name in f:lines() do + s, e = name:find(filepost) + if s ~= nil then + nummon = nummon + 1 + months[nummon] = dirname .. "/" .. name + tabname[nummon] = name:sub(1, s-1) + end +end +f:close() + +linex = {} + +if nummon > 0 then + for i=nummon,1,-1 do + bw = {} + maclist = {} + days[i] = monthly(months[i]) + totline[i] = totals(bw, maclist, days[i]) + devline[i] = showdevices(bw, maclist) + monx = string.format("mon%d", i) + sx:tab(monx, translate("Month of " .. tabname[i])) + end +end + +if nummon > 0 then + line1 = sx:taboption("mon1", TextValue, "", translate("")) + line1.readonly=true + line1.wrap = "off" + line1.rows = 30 + line1.rmempty = false + function line1.cfgvalue(self, s) + return totline[1] .. devline[1] + end +else + line0 = sx:option(DummyValue, "", translate("No Data Available")) +end + +if nummon > 1 then + line2 = sx:taboption("mon2", TextValue, "", translate("")) + line2.readonly=true + line2.wrap = "off" + line2.rows = 30 + line2.rmempty = false + function line2.cfgvalue(self, s) + return totline[2] .. devline[2] + end +end + +if nummon > 2 then + line3 = sx:taboption("mon3", TextValue, "", translate("")) + line3.readonly=true + line3.wrap = "off" + line3.rows = 30 + line3.rmempty = false + function line3.cfgvalue(self, s) + return totline[3] .. devline[3] + end +end + +return m \ No newline at end of file diff --git a/dir860l/Makefile b/dir860l/Makefile new file mode 100644 index 0000000..c154f58 --- /dev/null +++ b/dir860l/Makefile @@ -0,0 +1,35 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=dir860l +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/dir860l + SECTION:=utils + CATEGORY:=ROOter + SUBMENU:=Optional Applications + TITLE:=Install scripts for Dlink DIR-860L + PKGARCH:=all +endef + +define Package/dir860l/description + Helper scripts to install scripts for Dlink DIR-860L +endef + + +define Build/Compile +endef + +define Package/dir860l/install + $(CP) ./files/* $(1)/ + + +endef + +$(eval $(call BuildPackage,dir860l)) diff --git a/dir860l/files/etc/hotplug.d/iface/99-dir860-led b/dir860l/files/etc/hotplug.d/iface/99-dir860-led new file mode 100644 index 0000000..682d050 --- /dev/null +++ b/dir860l/files/etc/hotplug.d/iface/99-dir860-led @@ -0,0 +1,13 @@ +#!/bin/sh + +[ "$INTERFACE" != wan ] && exit 0 + +if [ "$ACTION" = ifup ]; then + set_gpio 14 1 + set_gpio 16 0 +fi + +if [ "$ACTION" = ifdown ]; then + set_gpio 14 0 + set_gpio 16 1 +fi \ No newline at end of file diff --git a/dir860l/files/usr/lib/rooter/special.sh b/dir860l/files/usr/lib/rooter/special.sh new file mode 100644 index 0000000..5ebc3e8 --- /dev/null +++ b/dir860l/files/usr/lib/rooter/special.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# turn power led green +set_gpio 13 1 +set_gpio 15 0 + +# signal that we are waiting for internets +set_gpio 14 0 \ No newline at end of file diff --git a/ext-buttons/Makefile b/ext-buttons/Makefile new file mode 100644 index 0000000..d4ae835 --- /dev/null +++ b/ext-buttons/Makefile @@ -0,0 +1,35 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-buttons +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-buttons + SECTION:=utils + CATEGORY:=ROOter + SUBMENU:=Basic Support + TITLE:=Install button support + PKGARCH:=all +endef + +define Package/ext-buttons/description + Helper scripts to install button support +endef + + +define Build/Compile +endef + +define Package/ext-buttons/install + $(CP) ./files/* $(1)/ + + +endef + +$(eval $(call BuildPackage,ext-buttons)) diff --git a/ext-buttons/files/etc/btnaction.sh b/ext-buttons/files/etc/btnaction.sh new file mode 100644 index 0000000..82a4e4e --- /dev/null +++ b/ext-buttons/files/etc/btnaction.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +FUNC=$1 + +reset_short() { + passwd -d root + reboot -f +} + +reset_long() { + mtd -r erase rootfs_data +} + +wifi() { + STATEFILE="/tmp/wifionoff.state" + + if [ $# -eq 1 ]; then + case $1 in + "up"|"on") + STATE=off + ;; + "down"|"off") + STATE=on + ;; + esac + else + if [ ! -e ${STATEFILE} ]; then + STATE=on + else + . ${STATEFILE} + fi + fi + if [ -z ${STATE} ]; then + STATE=on + fi + + if [ ${STATE} == "on" ]; then + /sbin/wifi down + STATE=off + else + /sbin/wifi up + STATE=on + fi + + echo "STATE=${STATE}" > ${STATEFILE} +} + +if [ $FUNC = "reset_short" ]; then + reset_short +fi +if [ $FUNC = "reset_long" ]; then + reset_long +fi +if [ $FUNC = "wifi" ]; then + wifi $2 +fi diff --git a/ext-buttons/files/etc/hotplug.d/button/00-button b/ext-buttons/files/etc/hotplug.d/button/00-button new file mode 100644 index 0000000..0efab99 --- /dev/null +++ b/ext-buttons/files/etc/hotplug.d/button/00-button @@ -0,0 +1,26 @@ +#!/bin/sh +. /lib/functions.sh + +do_button () { + local button + local action + local handler + local min + local max + + config_get button $1 button + config_get action $1 action + config_get handler $1 handler + config_get min $1 min + config_get max $1 max + + [ "$ACTION" = "$action" -a "$BUTTON" = "$button" -a -n "$handler" ] && { + [ -z "$min" -o -z "$max" ] && eval $handler + [ -n "$min" -a -n "$max" ] && { + [ $min -le $SEEN -a $max -ge $SEEN ] && eval $handler + } + } +} + +config_load system +config_foreach do_button button \ No newline at end of file diff --git a/ext-buttons/files/etc/hotplug.d/button/10-buttonchk b/ext-buttons/files/etc/hotplug.d/button/10-buttonchk new file mode 100644 index 0000000..dc2ca7e --- /dev/null +++ b/ext-buttons/files/etc/hotplug.d/button/10-buttonchk @@ -0,0 +1,7 @@ +#!/bin/sh + +log() { + logger -t "Button Checker " "$@" +} + +log "Button Name : $BUTTON Action : $ACTION" diff --git a/ext-buttons/files/etc/init.d/buttons b/ext-buttons/files/etc/init.d/buttons new file mode 100644 index 0000000..6b478f1 --- /dev/null +++ b/ext-buttons/files/etc/init.d/buttons @@ -0,0 +1,33 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006 OpenWrt.org + +log() { + logger -t "Buttons" "$@" +} + +START=98 + +start() { + local BTN=$(uci get system.@button[-1].button) + if [ -z $BTN ]; then + uci add system button + uci set system.@button[-1].button=reset + uci set system.@button[-1].action=released + uci set system.@button[-1].handler="/etc/btnaction.sh reset_short" + uci set system.@button[-1].min=5 + uci set system.@button[-1].max=15 + + uci add system button + uci set system.@button[-1].button=reset + uci set system.@button[-1].action=released + uci set system.@button[-1].handler="/etc/btnaction.sh reset_long" + uci set system.@button[-1].min=17 + uci set system.@button[-1].max=60 + + uci commit system + fi +} + +stop() { + log "Stopping Buttons" +} \ No newline at end of file diff --git a/ext-buttons/files/usr/lib/lua/luci/controller/buttons.lua b/ext-buttons/files/usr/lib/lua/luci/controller/buttons.lua new file mode 100644 index 0000000..f2feb35 --- /dev/null +++ b/ext-buttons/files/usr/lib/lua/luci/controller/buttons.lua @@ -0,0 +1,8 @@ +module("luci.controller.buttons", package.seeall) + +function index() + local page + + page = entry({"admin", "system", "buttons"}, cbi("buttons/buttons"), _("Buttons"), 65) + page.dependent = true +end diff --git a/ext-buttons/files/usr/lib/lua/luci/model/cbi/buttons/buttons.lua b/ext-buttons/files/usr/lib/lua/luci/model/cbi/buttons/buttons.lua new file mode 100644 index 0000000..4f37569 --- /dev/null +++ b/ext-buttons/files/usr/lib/lua/luci/model/cbi/buttons/buttons.lua @@ -0,0 +1,44 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2008 Steven Barth + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +$Id$ +]]-- + +m = Map("system", translate("Buttons"), + translate("This page allows the configuration of custom button actions")) + +s = m:section(TypedSection, "button", "") +s.anonymous = true +s.addremove = true + +s:option(Value, "button", translate("Name")) + +act = s:option(ListValue, "action", + translate("Action"), + translate("Specifies the button state to handle")) + +act:value("released") +act:value("pressed") +act.default = "released" + +s:option(Value, "handler", + translate("Handler"), + translate("Path to executable which handles the button event")) + +min = s:option(Value, "min", translate("Minimum hold time")) +min.rmempty = true +min:depends("action", "released") + +max = s:option(Value, "max", translate("Maximum hold time")) +max.rmempty = true +max:depends("action", "released") + +return m \ No newline at end of file diff --git a/ext-command/Makefile b/ext-command/Makefile new file mode 100644 index 0000000..828e77a --- /dev/null +++ b/ext-command/Makefile @@ -0,0 +1,33 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-command +PKG_VERSION:=4.500 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-command + SECTION:=utils + CATEGORY:=ROOter + SUBMENU:=Basic Applications + TITLE:=support for Command + PKGARCH:=all +endef + +define Package/ext-command/description + Helper scripts to enable command +endef + + +define Build/Compile +endef + +define Package/ext-command/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,ext-command)) diff --git a/ext-command/files/usr/lib/lua/luci/controller/commands.lua b/ext-command/files/usr/lib/lua/luci/controller/commands.lua new file mode 100644 index 0000000..595b390 --- /dev/null +++ b/ext-command/files/usr/lib/lua/luci/controller/commands.lua @@ -0,0 +1,289 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2012 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +]]-- + +module("luci.controller.commands", package.seeall) + +function index() + entry({"admin", "system", "commands"}, firstchild(), _("Custom Commands"), 80) + entry({"admin", "system", "commands", "dashboard"}, template("commands"), _("Dashboard"), 1) + entry({"admin", "system", "commands", "config"}, cbi("commands"), _("Configure"), 2) + entry({"admin", "system", "commands", "script"}, template("cmdedit"), _("Scripts"), 3) + entry({"admin", "system", "commands", "run"}, call("action_run"), nil, 3).leaf = true + entry({"admin", "system", "commands", "download"}, call("action_download"), nil, 3).leaf = true + + entry({"admin", "system", "load_script"}, call("action_load_script")) + entry({"admin", "system", "save_script"}, call("action_save_script")) + entry({"admin", "system", "del_script"}, call("action_del_script")) + + entry({"command"}, call("action_public"), nil, 1).leaf = true +end + +--- Decode a given string into arguments following shell quoting rules +--- [[abc \def "foo\"bar" abc'def']] -> [[abc def]] [[foo"bar]] [[abcdef]] +local function parse_args(str) + local args = { } + + local function isspace(c) + if c == 9 or c == 10 or c == 11 or c == 12 or c == 13 or c == 32 then + return c + end + end + + local function isquote(c) + if c == 34 or c == 39 or c == 96 then + return c + end + end + + local function isescape(c) + if c == 92 then + return c + end + end + + local function ismeta(c) + if c == 36 or c == 92 or c == 96 then + return c + end + end + + --- Convert given table of byte values into a Lua string and append it to + --- the "args" table. Segment byte value sequence into chunks of 256 values + --- to not trip over the parameter limit for string.char() + local function putstr(bytes) + local chunks = { } + local csz = 256 + local upk = unpack + local chr = string.char + local min = math.min + local len = #bytes + local off + + for off = 1, len, csz do + chunks[#chunks+1] = chr(upk(bytes, off, min(off + csz - 1, len))) + end + + args[#args+1] = table.concat(chunks) + end + + --- Scan substring defined by the indexes [s, e] of the string "str", + --- perform unquoting and de-escaping on the fly and store the result in + --- a table of byte values which is passed to putstr() + local function unquote(s, e) + local off, esc, quote + local res = { } + + for off = s, e do + local byte = str:byte(off) + local q = isquote(byte) + local e = isescape(byte) + local m = ismeta(byte) + + if e then + esc = true + elseif esc then + if m then res[#res+1] = 92 end + res[#res+1] = byte + esc = false + elseif q and quote and q == quote then + quote = nil + elseif q and not quote then + quote = q + else + if m then res[#res+1] = 92 end + res[#res+1] = byte + end + end + + putstr(res) + end + + --- Find substring boundaries in "str". Ignore escaped or quoted + --- whitespace, pass found start- and end-index for each substring + --- to unquote() + local off, esc, start, quote + for off = 1, #str + 1 do + local byte = str:byte(off) + local q = isquote(byte) + local s = isspace(byte) or (off > #str) + local e = isescape(byte) + + if esc then + esc = false + elseif e then + esc = true + elseif q and quote and q == quote then + quote = nil + elseif q and not quote then + start = start or off + quote = q + elseif s and not quote then + if start then + unquote(start, off - 1) + start = nil + end + else + start = start or off + end + end + + --- If the "quote" is still set we encountered an unfinished string + if quote then + unquote(start, #str) + end + + return args +end + +local function parse_cmdline(cmdid, args) + local uci = require "luci.model.uci".cursor() + if uci:get("luci", cmdid) == "command" then + local cmd = uci:get_all("luci", cmdid) + local argv = parse_args(cmd.command) + local i, v + + if cmd.param == "1" and args then + for i, v in ipairs(parse_args(luci.http.urldecode(args))) do + argv[#argv+1] = v + end + end + + for i, v in ipairs(argv) do + if v:match("[^%w%.%-i/]") then + argv[i] = '"%s"' % v:gsub('"', '\\"') + end + end + + return argv + end +end + +function action_run(...) + local fs = require "nixio.fs" + local argv = parse_cmdline(...) + if argv then + local outfile = os.tmpname() + local errfile = os.tmpname() + + local rv = os.execute(table.concat(argv, " ") .. " >%s 2>%s" %{ outfile, errfile }) + local stdout = fs.readfile(outfile, 1024 * 512) or "" + local stderr = fs.readfile(errfile, 1024 * 512) or "" + + fs.unlink(outfile) + fs.unlink(errfile) + + local binary = not not (stdout:match("[%z\1-\8\14-\31]")) + + luci.http.prepare_content("application/json") + luci.http.write_json({ + command = table.concat(argv, " "), + stdout = not binary and stdout, + stderr = stderr, + exitcode = rv, + binary = binary + }) + else + luci.http.status(404, "No such command") + end +end + +function action_download(...) + local fs = require "nixio.fs" + local argv = parse_cmdline(...) + if argv then + local fd = io.popen(table.concat(argv, " ") .. " 2>/dev/null") + if fd then + local chunk = fd:read(4096) or "" + local name + if chunk:match("[%z\1-\8\14-\31]") then + luci.http.header("Content-Disposition", "attachment; filename=%s" + % fs.basename(argv[1]):gsub("%W+", ".") .. ".bin") + luci.http.prepare_content("application/octet-stream") + else + luci.http.header("Content-Disposition", "attachment; filename=%s" + % fs.basename(argv[1]):gsub("%W+", ".") .. ".txt") + luci.http.prepare_content("text/plain") + end + + while chunk do + luci.http.write(chunk) + chunk = fd:read(4096) + end + + fd:close() + else + luci.http.status(500, "Failed to execute command") + end + else + luci.http.status(404, "No such command") + end +end + +function action_public(cmdid, args) + local uci = require "luci.model.uci".cursor() + if cmdid and + uci:get("luci", cmdid) == "command" and + uci:get("luci", cmdid, "public") == "1" + then + action_download(cmdid, args) + else + luci.http.status(403, "Access to command denied") + end +end + +function action_load_script() + local set = luci.http.formvalue("set") + local rv ={} + local file + + file = io.open(set, "r") + if file ~= nil then + local tmp = file:read("*all") + rv["text"] = tmp + file:close() + else + rv["text"] = "No file found" + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_save_script() + local line = luci.http.formvalue("set") + local rv ={} + local file + + s, e = line:find("|") + name = line:sub(1,s-1) + text = line:sub(e+1) + + file = io.open(name, "w") + file:write(text) + file:close() + os.execute("chmod 777 " .. name) + + rv["name"] = name + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_del_script() + local name = luci.http.formvalue("set") + local rv ={} + os.remove(name) + + rv["name"] = name + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end \ No newline at end of file diff --git a/ext-command/files/usr/lib/lua/luci/model/cbi/commands.lua b/ext-command/files/usr/lib/lua/luci/model/cbi/commands.lua new file mode 100644 index 0000000..1359eb2 --- /dev/null +++ b/ext-command/files/usr/lib/lua/luci/model/cbi/commands.lua @@ -0,0 +1,37 @@ +--[[ +LuCI - Lua Configuration Interface + +Copyright 2012 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +]]-- + +local m, s + +m = Map("luci", translate("Custom Commands"), + translate("This page allows you to configure custom shell commands which can be easily invoked from the web interface.")) + +s = m:section(TypedSection, "command", "") +s.template = "cbi/tblsection" +s.anonymous = true +s.addremove = true + + +s:option(Value, "name", translate("Description"), + translate("A short textual description of the configured command")) + +s:option(Value, "command", translate("Command"), + translate("Command line to execute")) + +s:option(Flag, "param", translate("Custom arguments"), + translate("Allow the user to provide additional command line arguments")) + +s:option(Flag, "public", translate("Public access"), + translate("Allow executing the command and downloading its output without prior authentication")) + +return m diff --git a/ext-command/files/usr/lib/lua/luci/view/cmdedit.htm b/ext-command/files/usr/lib/lua/luci/view/cmdedit.htm new file mode 100644 index 0000000..8d6bdc5 --- /dev/null +++ b/ext-command/files/usr/lib/lua/luci/view/cmdedit.htm @@ -0,0 +1,186 @@ +<%+header%> + + + + +
+
+

Script Editing

+
Create, Edit and Save Scripts
+
+ + + + + + +
Script Name :
 
+ + + + + + +
Path to Script :
 
+ + + + + +
+ +
+ + + + + + + + + +
 
+ +
+ +
+
+<%+footer%> \ No newline at end of file diff --git a/ext-command/files/usr/lib/lua/luci/view/commands.htm b/ext-command/files/usr/lib/lua/luci/view/commands.htm new file mode 100644 index 0000000..83792a9 --- /dev/null +++ b/ext-command/files/usr/lib/lua/luci/view/commands.htm @@ -0,0 +1,176 @@ +<%# +LuCI - Lua Configuration Interface +Copyright 2012 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +-%> + +<% css = [[ + +.commandbox { + height: 12em; + width: 30%; + float: left; + height: 12em; + margin: 5px; + position: relative; +} + +.commandbox h3 { + font-size: 1.5em !important; + line-height: 2em !important; + margin: 0 !important; +} + +.commandbox input[type="text"] { + width: 50% !important; +} + +.commandbox div { + position: absolute; + left: 0; + bottom: 1.5em; +} + +]] -%> + +<%+header%> + + + + +<% + local uci = require "luci.model.uci".cursor() + local commands = { } + + uci:foreach("luci", "command", function(s) commands[#commands+1] = s end) +%> + +
"> +
+

<%:Custom Commands%>

+ +
+ <% local _, command; for _, command in ipairs(commands) do %> +
+

<%=pcdata(command.name)%>

+

<%:Command:%> <%=pcdata(command.command)%>

+ <% if command.param == "1" then %> +

<%:Arguments:%>

+ <% end %> +
+ + + <% if command.public == "1" then %> + + <% end %> +
+
+ <% end %> + +

+ +
+
+ + +
+ +<%+footer%> diff --git a/ext-command/files/usr/lib/scripts/dummy b/ext-command/files/usr/lib/scripts/dummy new file mode 100644 index 0000000..c227083 --- /dev/null +++ b/ext-command/files/usr/lib/scripts/dummy @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/ext-extra/Makefile b/ext-extra/Makefile new file mode 100644 index 0000000..07a6c59 --- /dev/null +++ b/ext-extra/Makefile @@ -0,0 +1,35 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-extra +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-extra + SECTION:=utils + CATEGORY:=ROOter + SUBMENU:=Basic Applications + TITLE:=Add Scheduled Reboot + PKGARCH:=all +endef + +define Package/ext-extra/description + Helper scripts to install Scheduled Reboot +endef + + +define Build/Compile +endef + +define Package/ext-extra/install + $(CP) ./files/* $(1)/ + + +endef + +$(eval $(call BuildPackage,ext-extra)) diff --git a/ext-extra/files/etc/config/schedule b/ext-extra/files/etc/config/schedule new file mode 100644 index 0000000..95c1155 --- /dev/null +++ b/ext-extra/files/etc/config/schedule @@ -0,0 +1,7 @@ + +config reboot 'reboot' + option enable '0' + +config timezone 'timezone' + option zonename 'UTC' + diff --git a/ext-extra/files/usr/lib/lua/luci/controller/schedule.lua b/ext-extra/files/usr/lib/lua/luci/controller/schedule.lua new file mode 100644 index 0000000..8c1bda3 --- /dev/null +++ b/ext-extra/files/usr/lib/lua/luci/controller/schedule.lua @@ -0,0 +1,9 @@ +-- Licensed to the public under the Apache License 2.0. + +module("luci.controller.schedule", package.seeall) + +function index() + local page + page = entry({"admin", "services", "schedule"}, cbi("schedule"), _("Scheduled Reboot"), 61) + page.dependent = true +end diff --git a/ext-extra/files/usr/lib/lua/luci/model/cbi/admin_system/cronnew.lua b/ext-extra/files/usr/lib/lua/luci/model/cbi/admin_system/cronnew.lua new file mode 100644 index 0000000..b9e12ae --- /dev/null +++ b/ext-extra/files/usr/lib/lua/luci/model/cbi/admin_system/cronnew.lua @@ -0,0 +1,29 @@ +-- Copyright 2008 Steven Barth +-- Copyright 2008-2013 Jo-Philipp Wich +-- Licensed to the public under the Apache License 2.0. + +local fs = require "nixio.fs" +local cronfile = "/etc/cronuser" + +f = SimpleForm("crontab", translate("Scheduled Tasks"), translate("This is the system crontab in which scheduled tasks can be defined.")) + +t = f:field(TextValue, "crons") +t.rmempty = true +t.rows = 10 +function t.cfgvalue() + return fs.readfile(cronfile) or "" +end + +function f.handle(self, state, data) + if state == FORM_VALID then + if data.crons then + fs.writefile(cronfile, data.crons:gsub("\r\n", "\n")) + luci.sys.call("/usr/lib/rooter/luci/croncat.sh") + else + fs.writefile(cronfile, "") + end + end + return true +end + +return f diff --git a/ext-extra/files/usr/lib/lua/luci/model/cbi/schedule.lua b/ext-extra/files/usr/lib/lua/luci/model/cbi/schedule.lua new file mode 100644 index 0000000..e0043cc --- /dev/null +++ b/ext-extra/files/usr/lib/lua/luci/model/cbi/schedule.lua @@ -0,0 +1,133 @@ +local utl = require "luci.util" + +local sys = require "luci.sys" +local zones = require "luci.sys.zoneinfo" +local fs = require "nixio.fs" +local conf = require "luci.config" + +os.execute("/usr/lib/rooter/luci/reboot.sh 0") + +m = Map("schedule", translate("Scheduled Reboot"), translate("Schedule a Router Reboot at a Specified Time")) + +m.on_after_commit = function(self) + luci.sys.call("/usr/lib/rooter/luci/reboot.sh 1") +end + +d1 = m:section(TypedSection, "timezone", " ") + +o1 = d1:option(DummyValue, "_systime", translate("Local Time : ")) +o1.template = "admin_system/clock_status" + +o = d1:option(DummyValue, "zonename", translate("Timezone : "), translate("Be sure to set your Timezone correctly in System->System")) + +d = m:section(TypedSection, "reboot", " ") + +c1 = d:option(ListValue, "enable", " "); +c1:value("0", "Disabled") +c1:value("1", "Enabled") +c1.default=0 + +sdhour = d:option(ListValue, "sdhour", translate("Reboot Time :")) +sdhour.rmempty = true +sdhour:value("0", "12:00 AM") +sdhour:value("1", "12:15 AM") +sdhour:value("2", "12:30 AM") +sdhour:value("3", "12:45 AM") +sdhour:value("4", "01:00 AM") +sdhour:value("5", "01:15 AM") +sdhour:value("6", "01:30 AM") +sdhour:value("7", "01:45 AM") +sdhour:value("8", "02:00 AM") +sdhour:value("9", "02:15 AM") +sdhour:value("10", "02:30 AM") +sdhour:value("11", "02:45 AM") +sdhour:value("12", "03:00 AM") +sdhour:value("13", "03:15 AM") +sdhour:value("14", "03:30 AM") +sdhour:value("15", "03:45 AM") +sdhour:value("16", "04:00 AM") +sdhour:value("17", "04:15 AM") +sdhour:value("18", "04:30 AM") +sdhour:value("19", "04:45 AM") +sdhour:value("20", "05:00 AM") +sdhour:value("21", "05:15 AM") +sdhour:value("22", "05:30 AM") +sdhour:value("23", "05:45 AM") +sdhour:value("24", "06:00 AM") +sdhour:value("25", "06:15 AM") +sdhour:value("26", "06:30 AM") +sdhour:value("27", "06:45 AM") +sdhour:value("28", "07:00 AM") +sdhour:value("29", "07:15 AM") +sdhour:value("30", "07:30 AM") +sdhour:value("31", "07:45 AM") +sdhour:value("32", "08:00 AM") +sdhour:value("33", "08:15 AM") +sdhour:value("34", "08:30 AM") +sdhour:value("35", "08:45 AM") +sdhour:value("36", "09:00 AM") +sdhour:value("37", "09:15 AM") +sdhour:value("38", "09:30 AM") +sdhour:value("39", "09:45 AM") +sdhour:value("40", "10:00 AM") +sdhour:value("41", "10:15 AM") +sdhour:value("42", "10:30 AM") +sdhour:value("43", "10:45 AM") +sdhour:value("44", "11:00 AM") +sdhour:value("45", "11:15 AM") +sdhour:value("46", "11:30 AM") +sdhour:value("47", "11:45 AM") +sdhour:value("48", "12:00 PM") +sdhour:value("49", "12:15 PM") +sdhour:value("50", "12:30 PM") +sdhour:value("51", "12:45 PM") +sdhour:value("52", "01:00 PM") +sdhour:value("53", "01:15 PM") +sdhour:value("54", "01:30 PM") +sdhour:value("55", "01:45 PM") +sdhour:value("56", "02:00 PM") +sdhour:value("57", "02:15 PM") +sdhour:value("58", "02:30 PM") +sdhour:value("59", "02:45 PM") +sdhour:value("60", "03:00 PM") +sdhour:value("61", "03:15 PM") +sdhour:value("62", "03:30 PM") +sdhour:value("63", "03:45 PM") +sdhour:value("64", "04:00 PM") +sdhour:value("65", "04:15 PM") +sdhour:value("66", "04:30 PM") +sdhour:value("67", "04:45 PM") +sdhour:value("68", "05:00 PM") +sdhour:value("69", "05:15 PM") +sdhour:value("70", "05:30 PM") +sdhour:value("71", "05:45 PM") +sdhour:value("72", "06:00 PM") +sdhour:value("73", "06:15 PM") +sdhour:value("74", "06:30 PM") +sdhour:value("75", "06:45 PM") +sdhour:value("76", "07:00 PM") +sdhour:value("77", "07:15 PM") +sdhour:value("78", "07:30 PM") +sdhour:value("79", "07:45 PM") +sdhour:value("80", "08:00 PM") +sdhour:value("81", "08:15 PM") +sdhour:value("82", "08:30 PM") +sdhour:value("83", "08:45 PM") +sdhour:value("84", "09:00 PM") +sdhour:value("85", "09:15 PM") +sdhour:value("86", "09:30 PM") +sdhour:value("87", "09:45 PM") +sdhour:value("88", "10:00 PM") +sdhour:value("89", "10:15 PM") +sdhour:value("90", "10:30 PM") +sdhour:value("91", "10:45 PM") +sdhour:value("92", "11:00 PM") +sdhour:value("93", "11:15 PM") +sdhour:value("94", "11:30 PM") +sdhour:value("95", "11:45 PM") + +sdhour:depends("enable", "1") +sdhour.default = "0" + +return m + diff --git a/ext-extra/files/usr/lib/rooter/luci/croncat.sh b/ext-extra/files/usr/lib/rooter/luci/croncat.sh new file mode 100644 index 0000000..9911ad5 --- /dev/null +++ b/ext-extra/files/usr/lib/rooter/luci/croncat.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +if [ -f /etc/cronuser ]; then + if [ -f /etc/cronbase ]; then + cat /etc/cronbase /etc/cronuser > /etc/crontabs/root + else + cp /etc/cronuser /etc/crontabs/root + fi +else + if [ -f /etc/cronbase ]; then + cp /etc/cronbase /etc/crontabs/root + else + rm -f /etc/crontabs/root + fi +fi + +/etc/init.d/cron restart \ No newline at end of file diff --git a/ext-extra/files/usr/lib/rooter/luci/reboot.sh b/ext-extra/files/usr/lib/rooter/luci/reboot.sh new file mode 100644 index 0000000..204b364 --- /dev/null +++ b/ext-extra/files/usr/lib/rooter/luci/reboot.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +PARM=$1 + +if [ $PARM = "0" ]; then + HO=$(uci get system.@system[-1].zonename) + if [ -z $HO ]; then + HO="UTC" + fi + uci set schedule.timezone.zonename="$HO" + uci commit schedule +fi + +if [ $PARM = "1" ]; then + EN=$(uci get schedule.reboot.enable) + if [ $EN = "1" ]; then + SDHOUR=$(uci get schedule.reboot.sdhour) + HOUR=`expr $SDHOUR / 4` + let "TH = $HOUR * 4" + let "TMP1 = $SDHOUR - $TH" + let "MIN = $TMP1 * 15" + echo "$MIN $HOUR * * * sleep 70 && touch /etc/banner && reboot -f" > /etc/cronbase + else + rm -f /etc/cronbase + fi + /usr/lib/rooter/luci/croncat.sh +fi diff --git a/ext-hdidle/Makefile b/ext-hdidle/Makefile new file mode 100644 index 0000000..3128697 --- /dev/null +++ b/ext-hdidle/Makefile @@ -0,0 +1,36 @@ +# call BuildPackage - OpenWrt buildroot signature + +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-hdidle +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-hdidle + SECTION:=utils + CATEGORY:=ROOter + SUBMENU:=Optional Applications + DEPENDS:=+hd-idle + TITLE:=Add Hard Drive Idle + PKGARCH:=all +endef + +define Package/ext-hdidle/description + Helper scripts for Hard Drive Idle +endef + + +define Build/Compile +endef + +define Package/ext-hdidle/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,ext-hdidle)) diff --git a/ext-hdidle/files/etc/uci-defaults/40_luci-hd_idle b/ext-hdidle/files/etc/uci-defaults/40_luci-hd_idle new file mode 100644 index 0000000..92f4356 --- /dev/null +++ b/ext-hdidle/files/etc/uci-defaults/40_luci-hd_idle @@ -0,0 +1,11 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@hd-idle[-1] + add ucitrack hd-idle + set ucitrack.@hd-idle[-1].init=hd-idle + commit ucitrack +EOF + +rm -f /tmp/luci-indexcache +exit 0 diff --git a/ext-hdidle/files/usr/lib/lua/luci/controller/hd_idle.lua b/ext-hdidle/files/usr/lib/lua/luci/controller/hd_idle.lua new file mode 100644 index 0000000..3e99da2 --- /dev/null +++ b/ext-hdidle/files/usr/lib/lua/luci/controller/hd_idle.lua @@ -0,0 +1,15 @@ +-- Copyright 2008 Yanira +-- Licensed to the public under the Apache License 2.0. + +module("luci.controller.hd_idle", package.seeall) + +function index() + if not nixio.fs.access("/etc/config/hd-idle") then + return + end + + local page + + page = entry({"admin", "services", "hd_idle"}, cbi("hd_idle"), _("Hard Drive Idle"), 60) + page.dependent = true +end diff --git a/ext-hdidle/files/usr/lib/lua/luci/model/cbi/hd_idle.lua b/ext-hdidle/files/usr/lib/lua/luci/model/cbi/hd_idle.lua new file mode 100644 index 0000000..272e926 --- /dev/null +++ b/ext-hdidle/files/usr/lib/lua/luci/model/cbi/hd_idle.lua @@ -0,0 +1,29 @@ +-- Copyright 2008 Yanira +-- Licensed to the public under the Apache License 2.0. + +require("nixio.fs") + +m = Map("hd-idle", "Hard Drive Idle", + translate("This is a utility program for spinning-down external " .. + "disks after a period of idle time.")) + +s = m:section(TypedSection, "hd-idle", translate("Settings")) +s.anonymous = true + +s:option(Flag, "enabled", translate("Enable")) + +disk = s:option(Value, "disk", translate("Disk")) +disk.rmempty = true +for dev in nixio.fs.glob("/dev/[sh]d[a-z]") do + disk:value(nixio.fs.basename(dev)) +end + +s:option(Value, "idle_time_interval", translate("Idle-time")).default = 10 +s.rmempty = true +unit = s:option(ListValue, "idle_time_unit", translate("Idle-time unit")) +unit.default = "minutes" +unit:value("minutes", translate("min")) +unit:value("hours", translate("h")) +unit.rmempty = true + +return m diff --git a/ext-p910nd/Makefile b/ext-p910nd/Makefile new file mode 100644 index 0000000..7db25a2 --- /dev/null +++ b/ext-p910nd/Makefile @@ -0,0 +1,36 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-p910nd +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-p910nd + SECTION:=utils + CATEGORY:=ROOter + SUBMENU:=Basic Applications + DEPENDS:=+luci-app-p910nd +kmod-usb-printer +kmod-usb-ohci + TITLE:=Install Print Server + PKGARCH:=all +endef + +define Package/ext-p910nd/description + Helper scripts to install print Server +endef + + +define Build/Compile +endef + +define Package/ext-p910nd/install + $(CP) ./files/* $(1)/ + + +endef + +$(eval $(call BuildPackage,ext-p910nd)) diff --git a/ext-p910nd/files/usr/lib/lua/luci/controller/p910nd.lua b/ext-p910nd/files/usr/lib/lua/luci/controller/p910nd.lua new file mode 100644 index 0000000..bb30348 --- /dev/null +++ b/ext-p910nd/files/usr/lib/lua/luci/controller/p910nd.lua @@ -0,0 +1,15 @@ +-- Copyright 2008 Yanira +-- Licensed to the public under the Apache License 2.0. + +module("luci.controller.p910nd", package.seeall) + +function index() + if not nixio.fs.access("/etc/config/p910nd") then + return + end + + local page + + page = entry({"admin", "services", "p910nd"}, cbi("p910nd"), _("Print Server"), 60) + page.dependent = true +end diff --git a/ext-p910nd/files/usr/lib/lua/luci/model/cbi/p910nd.lua b/ext-p910nd/files/usr/lib/lua/luci/model/cbi/p910nd.lua new file mode 100644 index 0000000..12c4a58 --- /dev/null +++ b/ext-p910nd/files/usr/lib/lua/luci/model/cbi/p910nd.lua @@ -0,0 +1,59 @@ +--[[ + +LuCI p910nd +(c) 2008 Yanira +(c) 2012 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +]]-- + +local uci = luci.model.uci.cursor_state() +local net = require "luci.model.network" +local m, s, p, b + +m = Map("p910nd", translate("Print Server"), + translatef("Support for USB Printers.")) + +net = net.init(m.uci) + +s = m:section(TypedSection, "p910nd", translate("Settings")) +s.addremove = true +s.anonymous = true + +s:option(Flag, "enabled", translate("enable")) + +s:option(Value, "device", translate("Device")).rmempty = true + +b = s:option(Value, "bind", translate("Interface"), translate("Specifies the interface to listen on.")) +b.template = "cbi/network_netlist" +b.nocreate = true +b.unspecified = true + +function b.cfgvalue(...) + local v = Value.cfgvalue(...) + if v then + return (net:get_status_by_address(v)) + end +end + +function b.write(self, section, value) + local n = net:get_network(value) + if n and n:ipaddr() then + Value.write(self, section, n:ipaddr()) + end +end + +p = s:option(ListValue, "port", translate("Port"), translate("TCP listener port.")) +p.rmempty = true +for i=0,9 do + p:value(i, 9100+i) +end + +s:option(Flag, "bidirectional", translate("Bidirectional mode")) + +return m diff --git a/ext-rooter-basic/Makefile b/ext-rooter-basic/Makefile new file mode 100644 index 0000000..740d1ce --- /dev/null +++ b/ext-rooter-basic/Makefile @@ -0,0 +1,41 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-rooter-basic +PKG_VERSION:=4.500 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-rooter-basic + SECTION:=utils + CATEGORY:=ROOter + SUBMENU:=Basic Support + DEPENDS:=+kmod-usb-net +kmod-usb-net-huawei-cdc-ncm +kmod-usb-net-cdc-ether \ + +kmod-usb-net-qmi-wwan +kmod-usb-net-rndis +kmod-usb-serial-qualcomm \ + +kmod-usb-net-sierrawireless +kmod-usb-ohci +kmod-usb-serial +kmod-nls-utf8 \ + +kmod-usb-serial-option +kmod-usb-serial-sierrawireless +luci-proto-3x \ + +kmod-usb-uhci +kmod-usb2 +luci-proto-3g +luci +luci-theme-bootstrap \ + +usb-modeswitch +wireless-tools +wpad-mini +rmbim +rqmi +ext-sms +ext-buttons + TITLE:=ROOter support for usbmodems + PKGARCH:=all +endef + +define Package/ext-rooter-basic/description + Helper scripts to enable ROOter to manage usb modem interfaces +endef + + +define Build/Compile +endef + +define Package/ext-rooter-basic/install + $(CP) ./files/* $(1)/ + + +endef + +$(eval $(call BuildPackage,ext-rooter-basic)) diff --git a/ext-rooter-basic/files/etc/codename b/ext-rooter-basic/files/etc/codename new file mode 100644 index 0000000..252dcda --- /dev/null +++ b/ext-rooter-basic/files/etc/codename @@ -0,0 +1 @@ +CODENAME="GoldenOrb-Version-1" \ No newline at end of file diff --git a/ext-rooter-basic/files/etc/config/guestwifi b/ext-rooter-basic/files/etc/config/guestwifi new file mode 100644 index 0000000..7799b29 --- /dev/null +++ b/ext-rooter-basic/files/etc/config/guestwifi @@ -0,0 +1,8 @@ + +config guestwifi 'guestwifi' + option ssid 'guest' + option ip '192.168.3.1' + option limit '0' + option dl '1024' + option ul '128' + diff --git a/ext-rooter-basic/files/etc/config/modem b/ext-rooter-basic/files/etc/config/modem new file mode 100644 index 0000000..617a570 --- /dev/null +++ b/ext-rooter-basic/files/etc/config/modem @@ -0,0 +1,18 @@ + +config info 'general' + option modemnum '1' + option max '6' + +config new 'customize' + +config modem 'modem1' + option empty '1' + +config minfo1 'modeminfo1' + +config modem 'modem2' + option empty '1' + +config minfo2 'modeminfo2' + + diff --git a/ext-rooter-basic/files/etc/hotplug.d/tty/30-3x b/ext-rooter-basic/files/etc/hotplug.d/tty/30-3x new file mode 100644 index 0000000..664d4ae --- /dev/null +++ b/ext-rooter-basic/files/etc/hotplug.d/tty/30-3x @@ -0,0 +1,31 @@ +#!/bin/sh +. /lib/functions.sh +. /lib/netifd/netifd-proto.sh + +find_3g_iface() { + local cfg="$1" + local tty="$2" + local proto + config_get proto "$cfg" proto + [ "$proto" = 3x ] || return 0 + + # bypass state vars here because 00-netstate could clobber .device + local dev=$(uci_get network "$cfg" device) + if [ "${dev##*/}" = "${tty##*/}" ]; then + if [ "$ACTION" = add ]; then + available=1 + else + available=0 + fi + proto_set_available "$cfg" $available + fi +} + +case "$DEVICENAME" in + tty*) + [ -e "/dev/$DEVICENAME" ] || [ "$ACTION" = remove ] || exit 0 + config_load network + config_foreach find_3g_iface interface "/dev/$DEVICENAME" + ;; +esac + diff --git a/ext-rooter-basic/files/etc/hotplug.d/usb/20-usb_mode b/ext-rooter-basic/files/etc/hotplug.d/usb/20-usb_mode new file mode 100644 index 0000000..d0b2a64 --- /dev/null +++ b/ext-rooter-basic/files/etc/hotplug.d/usb/20-usb_mode @@ -0,0 +1,3 @@ +#!/bin/sh + +/usr/lib/rooter/modeswitch.sh \ No newline at end of file diff --git a/ext-rooter-basic/files/etc/init.d/clear b/ext-rooter-basic/files/etc/init.d/clear new file mode 100644 index 0000000..d246d00 --- /dev/null +++ b/ext-rooter-basic/files/etc/init.d/clear @@ -0,0 +1,18 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006 OpenWrt.org + +START=19 + +start() { + COUNTER=1 + while [ $COUNTER -le 5 ]; do + INEX=$(uci get network.wan$COUNTER) + if [ -z $INEX ]; then + break + else + uci delete network.wan$COUNTER + uci commit network + fi + let COUNTER=COUNTER+1 + done +} \ No newline at end of file diff --git a/ext-rooter-basic/files/etc/init.d/rooter b/ext-rooter-basic/files/etc/init.d/rooter new file mode 100644 index 0000000..1199b98 --- /dev/null +++ b/ext-rooter-basic/files/etc/init.d/rooter @@ -0,0 +1,9 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006 OpenWrt.org + +START=99 + +start() { + /usr/lib/rooter/initialize.sh +} + diff --git a/ext-rooter-basic/files/etc/init.d/usbmode b/ext-rooter-basic/files/etc/init.d/usbmode new file mode 100644 index 0000000..59fb18b --- /dev/null +++ b/ext-rooter-basic/files/etc/init.d/usbmode @@ -0,0 +1,14 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2013 OpenWrt.org + +START=88 +USE_PROCD=1 + +log() { + logger -t "usb-modeswitch" "$@" +} + +start_service() +{ + log "Remove Early Modeswitch" +} diff --git a/ext-rooter-basic/files/etc/rc.d/S88usbmode b/ext-rooter-basic/files/etc/rc.d/S88usbmode new file mode 100644 index 0000000..59fb18b --- /dev/null +++ b/ext-rooter-basic/files/etc/rc.d/S88usbmode @@ -0,0 +1,14 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2013 OpenWrt.org + +START=88 +USE_PROCD=1 + +log() { + logger -t "usb-modeswitch" "$@" +} + +start_service() +{ + log "Remove Early Modeswitch" +} diff --git a/ext-rooter-basic/files/etc/rc.d/S99rooter b/ext-rooter-basic/files/etc/rc.d/S99rooter new file mode 100644 index 0000000..1199b98 --- /dev/null +++ b/ext-rooter-basic/files/etc/rc.d/S99rooter @@ -0,0 +1,9 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006 OpenWrt.org + +START=99 + +start() { + /usr/lib/rooter/initialize.sh +} + diff --git a/ext-rooter-basic/files/etc/usb-mode.json b/ext-rooter-basic/files/etc/usb-mode.json new file mode 100644 index 0000000..64107a9 --- /dev/null +++ b/ext-rooter-basic/files/etc/usb-mode.json @@ -0,0 +1,3538 @@ +{ + "messages" : [ + "555342431234567800000000000006d0000000000000000000000000000000", + "55534243123456780002000000000a2a000000003300000100000000000000", + "5553424312345678000000000000061b004600000000000000000000000000", + "0f00010142", + "55534243f0298d8124000000800006bc626563240000000000000000000000", + "0902200001010080fa0904000002080650000705010200020007058102000200", + "55534243785634120100000080000601000000000000000000000000000000", + "55534243123456780000000000000616000000000000000000000000000000", + "55534243123456782400000080000612000024000000000000000000000000", + "5553424312345678000000000000061b000000ff0000000000000000000000", + "5553424368032c882400000080000612000000240000000000000000000000", + "5553424308306384c000000080000671030000000000000000000000000000", + "5553424312345678c00000008000069f140000000000000000000000000000", + "01b0000000000000000000000000000000000000000000000000000000000000", + "555342431234567800000000000006bd000000020000000000000000000000", + "1b5a01", + "5553424312345678c000000080010606f50402527000000000000000000000", + "55534243123456788000000080000606f50402527000000000000000000000", + "555342431234567800000000000006f0010300000000000000000000000000", + "55534243123456780000000000000aff554d53434847000000000000000000", + "555342431234567803000000800006f1010100000000000000000000000000", + "555342431234567800000000000005f1010100000000000000000000000000", + "555342431234567824000000800008ff024445564348470000000000000000", + "555342431234567824000000800008ff020000000000000000000000000000", + "55534243b82e238c24000000800008ff020000000000000000000000000000", + "55534243123456780600000080000601000000000000000000000000000000", + "55534243123456780600000080010a11060000000000000000000000000000", + "55534243123456780000000000000601000000000000000000000000000000", + "555342431234567824000000800008ff524445564348470000000000000000", + "555342431234567824000000800008ff524445564348473100000000000000", + "55534243123456782400000080000dfe524445564348473d4e444953000000", + "55534243d85dd88524000000800008ff524445564348470000000000000000", + "55534243123456702000000080000c85010101180101010101000000000000", + "55534243123456782400000080000685000000240000000000000000000000", + "55534243d8a523862400000080000685000000240000000000000000000000", + "5553424348c4758600000000000010ff000000000000000000000000000000", + "555342431234567824000000800006bc626563240000000000000000000000", + "5553424330f4cf8124000000800108df200000000000000000000000000000", + "5553424312345678c00000008000069f030000000000000000000000000000", + "555342431234567824000000800008FF05B112AEE102000000000000000000", + "55534243123456780000000000000606f50402527000000000000000000000", + "55534243123456780000000080000606f50402527000000000000000000000", + "555342431234567800000000000001ff000000000000000000000000000000", + "55534243123456781200000080000603000000020000000000000000000000", + "55534243123456780000000000000cff020000000000000000000000000000", + "5553424312345678800000008000060619181a207000000000000000000000", + "555342431234567800000000000010ff000000000000000000000000000000", + "555342431234567800000000000008ff000000000000030000000000000000", + "555342431234567824000000800108df200000000000000000000000000000", + "55534243f8d2e6838000000080000606f50402527000000000000000000000", + "555342431234567800000000000003f0010100000000000000000000000000", + "55534243123456780000000000000600000000000000000000000000000000", + "5553424312345679c000000080000671030000000000000000000000000000", + "555342430820298900000000000003f0010100000000000000000000000000", + "55534243123456700000000000000616aa0000000000000000000000000000", + "5553424312345678c000000080000671010000000000000000000000000000", + "5553424340799288C000000080010A16000000C00000000000000000000000", + "555342431234567800000000000006161f6d62706b00000000000000000000", + "5553424398e2c4812400000080000bff524445564348473d43440000000000" + ], + + "devices" : { + "03f0:002a": { + "*": { + "t_class": 7, + "msg": [ 0 ] + } + }, + "03f0:032a": { + "*": { + "t_class": 7, + "msg": [ 0 ] + } + }, + "03f0:521d": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "03f0:531d": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "03f0:541d": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "03f0:581d": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "03f0:631d": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "03f0:641d": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "03f0:681d": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "03f0:911d": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "03f0:931d": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "03f0:9a1d": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "03f0:9d1d": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "0408:1000": { + "*": { + "t_vendor": 1032, + "t_product": [ 59906 ], + "msg": [ 1 ] + } + }, + "0408:ea17": { + "*": { + "t_vendor": 1032, + "t_product": [ 59926 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0408:ea25": { + "*": { + "t_vendor": 1032, + "t_product": [ 59942 ], + "msg": [ ] + } + }, + "0408:ea43": { + "*": { + "t_vendor": 1032, + "t_product": [ 59975, 59977, 59981 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0408:f000": { + "*": { + "t_vendor": 1032, + "t_product": [ 59907 ], + "msg": [ ] + }, + ":uMa=Yota": { + "t_vendor": 1032, + "t_product": [ 53257 ], + "msg": [ 2 ] + } + }, + "0408:f001": { + "*": { + "t_vendor": 1032, + "t_product": [ 59907 ], + "msg": [ ] + } + }, + "0421:060c": { + "*": { + "t_vendor": 1057, + "t_product": [ 1550 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:0610": { + "*": { + "t_vendor": 1057, + "t_product": [ 1554 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:0618": { + "*": { + "t_vendor": 1057, + "t_product": [ 1561 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:061d": { + "*": { + "t_vendor": 1057, + "t_product": [ 1566 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:0622": { + "*": { + "t_vendor": 1057, + "t_product": [ 1571 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:0627": { + "*": { + "t_vendor": 1057, + "t_product": [ 1554, 1577 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:062c": { + "*": { + "t_vendor": 1057, + "t_product": [ 1581, 1583 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:0632": { + "*": { + "t_vendor": 1057, + "t_product": [ 1586 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:0637": { + "*": { + "t_vendor": 1057, + "t_product": [ 1592 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "046d:c261": { + "*": { + "t_class": 3, + "msg_endpoint": 1, + "msg": [ 3 ], + "response_endpoint": 1 + } + }, + "0471:1210": { + ":uMa=Philips": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=Wisue": { + "t_vendor": 7612, + "t_product": [ 5 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0471:1237": { + "*": { + "t_vendor": 1137, + "t_product": [ 4614, 4660 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0482:024d": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "04bb:bccd": { + "*": { + "t_vendor": 1211, + "t_product": [ 2377 ], + "msg": [ 4 ] + } + }, + "04cc:2251": { + "*": { + "t_vendor": 1228, + "t_product": [ 8793, 8814 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "04cc:225c": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "04cc:226e": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "04cc:226f": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "04e8:680c": { + "*": { + "t_vendor": 1256, + "t_product": [ 26514 ], + "msg": [ 5 ] + } + }, + "04e8:689a": { + "*": { + "t_vendor": 1256, + "t_product": [ 26761 ], + "msg": [ 6 ] + } + }, + "04e8:f000": { + ":sMo=U209": { + "t_vendor": 1256, + "t_product": [ 26113 ], + "msg": [ 7 ] + } + }, + "04fc:2140": { + "*": { + "t_vendor": 1276, + "t_product": [ 1557, 4672 ], + "msg": [ 8 ] + } + }, + "057c:62ff": { + "*": { + "t_vendor": 1404, + "t_product": [ 34049, 34050 ], + "msg": [ 9 ] + } + }, + "057c:84ff": { + "*": { + "t_vendor": 1404, + "t_product": [ 33793 ], + "msg": [ 9 ] + } + }, + "0586:2030": { + "*": { + "t_vendor": 1414, + "t_product": [ 13379, 13380 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c6:0010": { + "*": { + "t_vendor": 1478, + "t_product": [ 160 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c6:1000": { + ":sVe=Option": { + "t_vendor": 2800, + "t_product": [ 26881, 26369, 26112 ], + "msg": [ ] + }, + ":uMa=AnyDATA": { + "t_vendor": 5845, + "t_product": [ 25858 ], + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=CELOT": { + "t_vendor": 8479, + "t_product": [ 26625, 26626 ], + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=Co.,Ltd": { + "t_vendor": 7433, + "t_product": [ 17158 ], + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=DGT": { + "t_vendor": 8479, + "t_product": [ 26626 ], + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=Option": { + "t_vendor": 2800, + "t_product": [ 26881 ], + "msg": [ ] + }, + ":uMa=SAMSUNG": { + "t_vendor": 1256, + "t_product": [ 26113 ], + "msg": [ 7 ] + }, + ":uMa=SSE": { + "t_vendor": 1478, + "t_product": [ 24576 ], + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=StrongRising": { + "t_vendor": 650, + "t_product": [ 4102 ], + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=Vertex": { + "t_vendor": 8167, + "t_product": [ 256 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c6:2000": { + "*": { + "t_vendor": 1478, + "t_product": [ 21, 22, 24, 52759 ], + "msg": [ 10 ], + "response": true, + "check": true + } + }, + "05c6:2001": { + "*": { + "t_vendor": 7694, + "t_product": [ 52758, 52759, 52990 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c6:6503": { + "*": { + "t_vendor": 5845, + "t_product": [ 25858 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c6:9024": { + "*": { + "t_vendor": 1478, + "t_product": [ 36901 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c6:98ff": { + "*": { + "t_vendor": 1478, + "t_product": [ 24577 ], + "mode": "Sierra", + "msg": [ ] + } + }, + "05c6:f000": { + "*": { + "t_vendor": 1478, + "t_product": [ 22, 24576, 36864 ], + "mode": "StandardEject", + "msg": [ 11 ] + } + }, + "05c7:1000": { + "*": { + "t_vendor": 1479, + "t_product": [ 24576 ], + "msg": [ 12 ] + } + }, + "0685:2000": { + "*": { + "t_vendor": 7326, + "t_product": [ 38403 ], + "msg": [ 10 ], + "response": true + } + }, + "072f:100d": { + "*": { + "t_vendor": 1839, + "t_product": [ 37068 ], + "msg_endpoint": 2, + "msg": [ 13 ] + } + }, + "07d1:a800": { + "*": { + "t_vendor": 2001, + "t_product": [ 15873, 15874, 32268 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "07d1:a804": { + "*": { + "t_vendor": 2001, + "t_product": [ 32273 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "07d1:f000": { + "*": { + "t_vendor": 2001, + "t_product": [ 32263 ], + "msg": [ 14 ] + } + }, + "0922:1001": { + "*": { + "t_vendor": 2338, + "t_product": [ 4098 ], + "msg_endpoint": 1, + "msg": [ 15 ], + "response_endpoint": 1 + } + }, + "0922:1003": { + "*": { + "t_vendor": 2338, + "t_product": [ 4100 ], + "msg": [ 15 ] + } + }, + "0922:1007": { + "*": { + "t_vendor": 2338, + "t_product": [ 4104 ], + "msg": [ 15 ] + } + }, + "0930:0d46": { + "*": { + "t_vendor": 2352, + "t_product": [ 3397 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0ace:2011": { + "*": { + "mode": "StandardEject", + "msg": [ ] + } + }, + "0ace:20ff": { + "*": { + "mode": "StandardEject", + "msg": [ ] + } + }, + "0af0:4007": { + "*": { + "t_vendor": 2800, + "t_product": [ 16389 ], + "mode": "Sierra", + "msg": [ ] + } + }, + "0af0:6711": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:6731": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:6751": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:6771": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:6791": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:6811": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:6911": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:6951": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:6971": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7011": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7031": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7051": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7071": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7111": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7211": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7251": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7271": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7301": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7311": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7361": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7381": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7401": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7501": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7601": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7701": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7706": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7801": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7901": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7a01": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:7a05": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:8006": { + "*": { + "t_vendor": 2800, + "t_product": [ 37120 ], + "msg": [ ] + } + }, + "0af0:8200": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:8201": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:8300": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:8302": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:8304": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:8400": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:8600": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:8700": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:8800": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:8900": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:9000": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:9200": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:c031": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:c100": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:d001": { + "*": { + "t_vendor": 2800, + "t_product": [ 53591, 53845, 53847 ], + "msg": [ ] + } + }, + "0af0:d013": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:d031": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:d033": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:d035": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:d055": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:d057": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:d058": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:d155": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:d157": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:d255": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:d257": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0af0:d357": { + "*": { + "t_class": 255, + "msg": [ ] + } + }, + "0b3c:c700": { + "*": { + "t_vendor": 2876, + "t_product": [ 49152, 49153, 49154 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0b3c:f000": { + "*": { + "t_vendor": 2876, + "t_product": [ 49155, 49156 ], + "msg": [ 16 ], + "response": true + } + }, + "0b3c:f00c": { + "*": { + "t_vendor": 2876, + "t_product": [ 49162 ], + "msg": [ 17 ] + } + }, + "0b3c:f017": { + "*": { + "t_vendor": 2876, + "t_product": [ 49163 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0bdb:190d": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "0bdb:1910": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "0cf3:20ff": { + "*": { + "t_vendor": 3315, + "t_product": [ 28688 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0d46:45a1": { + "*": { + "t_vendor": 3398, + "t_product": [ 17833 ], + "mode": "Kobil", + "msg": [ ] + } + }, + "0d46:45a5": { + "*": { + "t_vendor": 3398, + "t_product": [ 17837 ], + "mode": "Kobil", + "msg": [ ] + } + }, + "0df7:0800": { + "*": { + "t_class": 255, + "mode": "MobileAction", + "msg": [ ] + } + }, + "0e8d:0002": { + ":uPr=MT": { + "t_vendor": 3725, + "t_product": [ 161, 162, 165 ], + "msg": [ 18 ] + }, + ":uPr=Product": { + "t_vendor": 3725, + "t_product": [ 161, 162, 165 ], + "msg": [ 18 ] + } + }, + "0e8d:7109": { + "*": { + "t_vendor": 3725, + "t_product": [ 28949, 28952 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0fca:8020": { + "*": { + "t_vendor": 4042, + "t_product": [ 32786 ], + "msg": [ ] + } + }, + "0fce:d0cf": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "0fce:d0df": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "0fce:d0e1": { + "*": { + "t_class": 2, + "mode": "Sony", + "msg": [ ], + "config": 2 + } + }, + "0fce:d103": { + "*": { + "t_class": 2, + "mode": "Sony", + "msg": [ ], + "config": 2 + } + }, + "0fd1:1000": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "1004:1000": { + "*": { + "t_class": 255, + "msg": [ 19 ] + } + }, + "1004:607f": { + "*": { + "t_vendor": 4100, + "t_product": [ 24576, 24852 ], + "msg": [ 20 ], + "response": true + } + }, + "1004:610c": { + "*": { + "t_vendor": 4100, + "t_product": [ 24841 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:613a": { + "*": { + "t_vendor": 4100, + "t_product": [ 24868 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:613f": { + "*": { + "t_vendor": 4100, + "t_product": [ 24897 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:614e": { + "*": { + "t_vendor": 4100, + "t_product": [ 24885 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:6156": { + "*": { + "t_vendor": 4100, + "t_product": [ 24919 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:6190": { + "*": { + "t_vendor": 4100, + "t_product": [ 24963, 24999 ], + "mode": "StandardEject", + "msg": [ ], + "wait": 10 + } + }, + "1004:61aa": { + "*": { + "t_vendor": 4100, + "t_product": [ 24999 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:61dd": { + "*": { + "t_vendor": 4100, + "t_product": [ 24975 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:61e7": { + "*": { + "t_vendor": 4100, + "t_product": [ 25062 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:61eb": { + "*": { + "t_vendor": 4100, + "t_product": [ 25066 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:6327": { + "*": { + "t_vendor": 4100, + "t_product": [ 25382 ], + "msg": [ 21 ] + } + }, + "106c:3b03": { + "*": { + "t_vendor": 4204, + "t_product": [ 14101 ], + "msg": [ 22 ] + } + }, + "106c:3b05": { + "*": { + "t_vendor": 4204, + "t_product": [ 14102 ], + "msg": [ 23 ] + } + }, + "106c:3b06": { + "*": { + "t_vendor": 4204, + "t_product": [ 14103 ], + "msg": [ 24 ] + } + }, + "106c:3b11": { + "*": { + "t_vendor": 4204, + "t_product": [ 14104 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "106c:3b14": { + "*": { + "t_vendor": 4204, + "t_product": [ 14113 ], + "msg": [ 22 ] + } + }, + "1076:7f40": { + "*": { + "t_vendor": 4214, + "t_product": [ 32512 ], + "mode": "GCT", + "msg": [ ] + } + }, + "109b:f009": { + "*": { + "t_vendor": 4251, + "t_product": [ 37140 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "10a9:606f": { + "*": { + "t_vendor": 4265, + "t_product": [ 24676, 24692 ], + "msg": [ ] + } + }, + "10a9:6080": { + "*": { + "t_vendor": 4265, + "t_product": [ 24709 ], + "msg": [ ] + } + }, + "1199:0fff": { + "*": { + "t_vendor": 4505, + "t_product": [ 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 274, 288, 536, 544, 548, 769, 26626, 26627, 26628, 26629, 26632, 26633, 26642, 26643, 26645, 26646, 26656, 26657, 26658, 26674, 26675, 26676, 26677, 26680, 26681, 26682, 26683, 26684, 26685, 26686, 26704, 26705, 26706, 26707, 26709, 26710, 26713, 26714, 26752, 26768, 26769, 26770, 26771, 26786, 26787, 26794, 36881, 36882, 36945 ], + "mode": "Sierra", + "msg": [ ] + } + }, + "1199:9011": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:9017": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:901b": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:901c": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:901f": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:9041": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:9051": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:9053": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:9063": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1266:1000": { + "*": { + "t_vendor": 4710, + "t_product": [ 4098, 4099, 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4110, 4111, 4113, 4114 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "12d1:1001": { + "*": { + "t_class": 255, + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1003": { + "*": { + "t_class": 255, + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1009": { + "*": { + "t_class": 255, + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1010": { + "*": { + "t_class": 255, + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:101e": { + "*": { + "t_class": 255, + "msg": [ 25 ] + } + }, + "12d1:1030": { + "*": { + "t_vendor": 4817, + "t_product": [ 4148 ], + "msg": [ 26 ] + } + }, + "12d1:1031": { + "*": { + "t_vendor": 4817, + "t_product": [ 4149 ], + "msg": [ 26 ] + } + }, + "12d1:1413": { + "*": { + "t_class": 255, + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1414": { + "*": { + "t_class": 255, + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1446": { + "*": { + "t_vendor": 4817, + "t_product": [ 4097, 5124, 5126, 5131, 5132, 5138, 5143, 5147, 5161, 5170, 5171, 5174, 5292, 5382, 5388, 5393 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1449": { + "*": { + "t_vendor": 4817, + "t_product": [ 5188 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14ad": { + "*": { + "t_vendor": 4817, + "t_product": [ 5294 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14b5": { + "*": { + "t_vendor": 4817, + "t_product": [ 5288, 5290 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14b7": { + "*": { + "t_vendor": 4817, + "t_product": [ 5324 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14ba": { + "*": { + "t_vendor": 4817, + "t_product": [ 5330 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14c1": { + "*": { + "t_vendor": 4817, + "t_product": [ 5318 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14c3": { + "*": { + "t_vendor": 4817, + "t_product": [ 5320 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14c4": { + "*": { + "t_vendor": 4817, + "t_product": [ 5322 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14c5": { + "*": { + "t_vendor": 4817, + "t_product": [ 5323 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14d1": { + "*": { + "t_vendor": 4817, + "t_product": [ 5321 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14fe": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382, 5391, 5405, 7198 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1505": { + "*": { + "t_vendor": 4817, + "t_product": [ 5131, 5132, 5382, 5391, 5386 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:151a": { + "*": { + "t_vendor": 4817, + "t_product": [ 5403, 5405, 5406 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1520": { + "*": { + "t_vendor": 4817, + "t_product": [ 5221 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1521": { + "*": { + "t_vendor": 4817, + "t_product": [ 5220 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1523": { + "*": { + "t_vendor": 4817, + "t_product": [ 5265 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1526": { + "*": { + "t_vendor": 4817, + "t_product": [ 5327 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1527": { + "*": { + "t_vendor": 4817, + "t_product": [ 5524 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1553": { + "*": { + "t_vendor": 4817, + "t_product": [ 4097 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1557": { + "*": { + "t_vendor": 4817, + "t_product": [ 5285 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:155a": { + "*": { + "t_vendor": 4817, + "t_product": [ 5325 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:155b": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:156a": { + "*": { + "t_vendor": 4817, + "t_product": [ 5483, 5484 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1570": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "12d1:1571": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "12d1:1572": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "12d1:1573": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "12d1:157c": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:157d": { + "*": { + "t_vendor": 4817, + "t_product": [ 5339, 5340 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1580": { + "*": { + "t_vendor": 4817, + "t_product": [ 5509 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1581": { + "*": { + "t_vendor": 4817, + "t_product": [ 5511 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1582": { + "*": { + "t_vendor": 4817, + "t_product": [ 5512 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1583": { + "*": { + "t_vendor": 4817, + "t_product": [ 5513 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1597": { + "*": { + "t_vendor": 4817, + "t_product": [ 5528 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15bb": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "12d1:15c0": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "12d1:15c1": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "12d1:15ca": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15cd": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15ce": { + "*": { + "t_vendor": 4817, + "t_product": [ 5553, 5555 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15cf": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15d0": { + "*": { + "t_vendor": 4817, + "t_product": [ 5585 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15d2": { + "*": { + "t_vendor": 4817, + "t_product": [ 5587 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15e7": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15ec": { + "*": { + "t_vendor": 4817, + "t_product": [ 7206 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15f0": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "12d1:1805": { + "*": { + "t_class": 255, + "msg": [ 25 ] + } + }, + "12d1:1c0b": { + "*": { + "t_vendor": 4817, + "t_product": [ 7173, 7174, 7175, 7176, 7184 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1c1b": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1c24": { + "*": { + "t_vendor": 4817, + "t_product": [ 7186, 7203 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1c25": { + "*": { + "msg": [ ], + "config": 0 + } + }, + "12d1:1d50": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "12d1:1da1": { + "*": { + "t_vendor": 4817, + "t_product": [ 7433 ], + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1f01": { + "*": { + "t_vendor": 4817, + "t_product": [ 5339, 5340 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f02": { + "*": { + "t_vendor": 4817, + "t_product": [ 5340 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f03": { + "*": { + "t_vendor": 4817, + "t_product": [ 5339 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f04": { + "*": { + "t_vendor": 4817, + "t_product": [ 5564 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f05": { + "*": { + "t_vendor": 4817, + "t_product": [ 5565 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f06": { + "*": { + "t_vendor": 4817, + "t_product": [ 5575 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f07": { + "*": { + "t_vendor": 4817, + "t_product": [ 5567 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f09": { + "*": { + "t_vendor": 4817, + "t_product": [ 7248 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f11": { + "*": { + "t_vendor": 4817, + "t_product": [ 5308 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f15": { + "*": { + "t_vendor": 4817, + "t_product": [ 5120, 5367 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f16": { + "*": { + "mode": "MBIM", + "msg": [ ] + } + }, + "12d1:1f17": { + "*": { + "t_vendor": 4817, + "t_product": [ 5494 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f18": { + "*": { + "t_vendor": 4817, + "t_product": [ 5495 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f19": { + "*": { + "t_vendor": 4817, + "t_product": [ 5370, 5493, 5496 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f1b": { + "*": { + "t_vendor": 4817, + "t_product": [ 5497 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f1c": { + "*": { + "t_vendor": 4817, + "t_product": [ 5498, 5520 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f1d": { + "*": { + "t_vendor": 4817, + "t_product": [ 5499, 5521 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f1e": { + "*": { + "t_vendor": 4817, + "t_product": [ 5503, 5522 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:380b": { + "*": { + "t_class": 2, + "mode": "StandardEject", + "msg": [ ] + } + }, + "1307:1169": { + "*": { + "t_vendor": 5041, + "t_product": [ 49 ], + "mode": "Cisco", + "msg": [ ] + } + }, + "1410:5010": { + "*": { + "t_vendor": 5136, + "t_product": [ 16640, 17408, 28720 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5020": { + "*": { + "t_vendor": 5136, + "t_product": [ 24576, 28673 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5023": { + "*": { + "t_vendor": 5136, + "t_product": [ 28720 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5030": { + "*": { + "t_vendor": 5136, + "t_product": [ 24576 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5031": { + "*": { + "t_vendor": 5136, + "t_product": [ 24578 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5041": { + "*": { + "t_vendor": 5136, + "t_product": [ 28673, 28675 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5055": { + "*": { + "t_vendor": 5136, + "t_product": [ 24626 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5059": { + "*": { + "t_vendor": 5136, + "t_product": [ 28721, 28738 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:7001": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:9020": { + "*": { + "msg": [ ], + "config": 4 + } + }, + "148e:a000": { + "*": { + "t_class": 2, + "mode": "Sequans", + "msg": [ ] + } + }, + "148f:2578": { + "*": { + "t_vendor": 5263, + "t_product": [ 36897 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "148f:2878": { + "*": { + "t_vendor": 5263, + "t_product": [ 30209 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "15eb:7153": { + "*": { + "t_vendor": 5611, + "t_product": [ 29010 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1614:0800": { + "*": { + "t_class": 255, + "msg": [ 27 ] + } + }, + "1614:0802": { + "*": { + "t_class": 255, + "msg": [ 27 ] + } + }, + "16d5:f000": { + "*": { + "t_vendor": 5845, + "t_product": [ 26115 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "16d8:6281": { + "*": { + "t_class": 255, + "msg": [ 28 ] + } + }, + "16d8:6803": { + "*": { + "t_class": 2, + "msg": [ 29 ] + } + }, + "16d8:6804": { + "*": { + "t_class": 255, + "msg": [ 28 ] + } + }, + "16d8:700a": { + "*": { + "t_class": 255, + "msg": [ 30 ] + } + }, + "16d8:700b": { + "*": { + "t_class": 255, + "msg": [ 30 ] + } + }, + "16d8:f000": { + "*": { + "t_vendor": 5848, + "t_product": [ 24582 ], + "msg": [ 31 ] + } + }, + "1726:1900": { + "*": { + "t_vendor": 5926, + "t_product": [ 4096 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1726:f00e": { + "*": { + "t_vendor": 5926, + "t_product": [ 40960 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1782:0003": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "1782:0023": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "198a:0003": { + "*": { + "t_vendor": 6538, + "t_product": [ 2 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "198f:bccd": { + "*": { + "t_vendor": 6543, + "t_product": [ 544 ], + "msg": [ 4 ] + } + }, + "19d2:0003": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0026": { + "*": { + "t_vendor": 6610, + "t_product": [ 115, 148, 338 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0033": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0040": { + "*": { + "t_vendor": 6610, + "t_product": [ 34 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0053": { + "*": { + "t_vendor": 6610, + "t_product": [ 49 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0083": { + ":uPr=WCDMA": { + "t_vendor": 6610, + "t_product": [ 292 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0090": { + "*": { + "t_vendor": 6610, + "t_product": [ 52 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0101": { + "*": { + "t_vendor": 6610, + "t_product": [ 260 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0103": { + "*": { + "t_vendor": 6610, + "t_product": [ 49 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0110": { + "*": { + "t_vendor": 6610, + "t_product": [ 289 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0115": { + "*": { + "t_vendor": 6610, + "t_product": [ 278 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0120": { + "*": { + "t_vendor": 6610, + "t_product": [ 121 ], + "detach_storage": false, + "mode": "StandardEject", + "msg": [ ], + "response": false, + "interface": 0 + } + }, + "19d2:0146": { + "*": { + "t_vendor": 6610, + "t_product": [ 322, 323 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0149": { + "*": { + "t_vendor": 6610, + "t_product": [ 292 ], + "mode": "StandardEject", + "msg": [ 32 ] + } + }, + "19d2:0150": { + "*": { + "t_vendor": 6610, + "t_product": [ 292 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0154": { + "*": { + "t_vendor": 6610, + "t_product": [ 23, 279, 8195 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0166": { + "*": { + "t_vendor": 6610, + "t_product": [ 359 ], + "msg": [ 33 ] + } + }, + "19d2:0169": { + "*": { + "t_vendor": 6610, + "t_product": [ 368 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0198": { + "*": { + "t_vendor": 6610, + "t_product": [ 409 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0266": { + "*": { + "t_vendor": 6610, + "t_product": [ 613 ], + "mode": "StandardEject", + "msg": [ 34 ] + } + }, + "19d2:0304": { + "*": { + "t_vendor": 6610, + "t_product": [ 841 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0318": { + "*": { + "t_vendor": 6610, + "t_product": [ 791, 816 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0325": { + "*": { + "t_vendor": 6610, + "t_product": [ 806 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0388": { + "*": { + "t_vendor": 6610, + "t_product": [ 1095 ], + "msg": [ 33 ] + } + }, + "19d2:0413": { + "*": { + "t_vendor": 6610, + "t_product": [ 1042 ], + "msg": [ 34 ] + } + }, + "19d2:1001": { + "*": { + "t_vendor": 6610, + "t_product": [ 4098, 4099 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1007": { + "*": { + "t_vendor": 6610, + "t_product": [ 4104 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1009": { + "*": { + "t_vendor": 6610, + "t_product": [ 4112 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1013": { + "*": { + "t_vendor": 6610, + "t_product": [ 4117 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1017": { + "*": { + "t_vendor": 6610, + "t_product": [ 4120 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1019": { + "*": { + "t_vendor": 6610, + "t_product": [ 4129 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1020": { + "*": { + "t_vendor": 6610, + "t_product": [ 4129 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1022": { + "*": { + "t_vendor": 6610, + "t_product": [ 4131, 4132 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1026": { + "*": { + "t_vendor": 6610, + "t_product": [ 4135, 4136, 4137 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1030": { + "*": { + "t_vendor": 6610, + "t_product": [ 4145, 4146 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1034": { + "*": { + "t_vendor": 6610, + "t_product": [ 4149, 4150, 4151 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1038": { + "*": { + "t_vendor": 6610, + "t_product": [ 4153, 4160 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1042": { + "*": { + "t_vendor": 6610, + "t_product": [ 4163 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1046": { + "*": { + "t_vendor": 6610, + "t_product": [ 4167 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1171": { + "*": { + "t_vendor": 6610, + "t_product": [ 4467 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1175": { + "*": { + "t_vendor": 6610, + "t_product": [ 4471 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1179": { + "*": { + "t_vendor": 6610, + "t_product": [ 4481 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1201": { + "*": { + "t_vendor": 6610, + "t_product": [ 4611 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1207": { + "*": { + "t_vendor": 6610, + "t_product": [ 4616 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1210": { + "*": { + "t_vendor": 6610, + "t_product": [ 4625 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1216": { + "*": { + "t_vendor": 6610, + "t_product": [ 4631 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1219": { + "*": { + "t_vendor": 6610, + "t_product": [ 4640, 4642 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1224": { + "*": { + "t_vendor": 6610, + "t_product": [ 130 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1225": { + "*": { + "t_vendor": 6610, + "t_product": [ 5125 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1227": { + "*": { + "t_vendor": 6610, + "t_product": [ 4690 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1232": { + "*": { + "t_vendor": 6610, + "t_product": [ 4712, 8195 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1233": { + "*": { + "t_vendor": 6610, + "t_product": [ 4720 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1237": { + "*": { + "t_vendor": 6610, + "t_product": [ 23 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1238": { + "*": { + "t_vendor": 6610, + "t_product": [ 23 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1420": { + "*": { + "t_vendor": 6610, + "t_product": [ 5125 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1511": { + "*": { + "t_vendor": 6610, + "t_product": [ 5394 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1514": { + "*": { + "t_vendor": 6610, + "t_product": [ 5397 ], + "msg": [ 35 ] + } + }, + "19d2:1517": { + "*": { + "t_vendor": 6610, + "t_product": [ 5401 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1520": { + "*": { + "t_vendor": 6610, + "t_product": [ 322 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1523": { + "*": { + "t_vendor": 6610, + "t_product": [ 5413 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1528": { + "*": { + "t_vendor": 6610, + "t_product": [ 5415 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1536": { + "*": { + "t_vendor": 6610, + "t_product": [ 5431, 5432 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1542": { + "*": { + "t_vendor": 6610, + "t_product": [ 5444 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1580": { + "*": { + "t_vendor": 6610, + "t_product": [ 5506 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1588": { + "*": { + "t_vendor": 6610, + "t_product": [ 5513, 5521, 5522 ], + "mode": "StandardEject", + "msg": [ 32 ] + } + }, + "19d2:1595": { + "*": { + "t_vendor": 6610, + "t_product": [ 5428, 5522, 5526, 5632 ], + "mode": "StandardEject", + "msg": [ 32 ] + } + }, + "19d2:2000": { + "*": { + "t_vendor": 6610, + "t_product": [ 1, 2, 21, 22, 23, 25, 36, 49, 51, 55, 66, 82, 85, 97, 99, 100, 102, 145, 264, 279, 296, 337, 343, 375, 5122, 8194, 8195 ], + "mode": "StandardEject", + "msg": [ 32 ] + } + }, + "19d2:2004": { + "*": { + "t_vendor": 6610, + "t_product": [ 5122 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:bccd": { + "*": { + "t_vendor": 6610, + "t_product": [ 370 ], + "msg": [ 36 ] + } + }, + "19d2:ffde": { + "*": { + "t_vendor": 6610, + "t_product": [ 65501 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:ffe6": { + "*": { + "t_vendor": 6610, + "t_product": [ 65509 ], + "msg": [ 37 ] + } + }, + "19d2:fff5": { + "*": { + "t_vendor": 6610, + "t_product": [ 65508, 65513, 65521, 65534, 65535 ], + "msg": [ 38 ] + } + }, + "19d2:fff6": { + "*": { + "t_vendor": 6610, + "t_product": [ 65521 ], + "msg": [ 38 ] + } + }, + "1a8d:1000": { + "*": { + "t_vendor": 6797, + "t_product": [ 4098, 4103, 4105, 4109, 8198 ], + "mode": "StandardEject", + "msg": [ ], + "release_delay": 4000, + "response": true + } + }, + "1a8d:2000": { + "*": { + "t_vendor": 6797, + "t_product": [ 8198 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1ab7:5700": { + "*": { + "t_vendor": 6839, + "t_product": [ 8192, 22321 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1b7d:0700": { + "*": { + "t_vendor": 7037, + "t_product": [ 1 ], + "msg": [ 39 ] + } + }, + "1bbb:000f": { + "*": { + "t_vendor": 7099, + "t_product": [ 15 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1bbb:00ca": { + "*": { + "t_class": 255, + "msg": [ 17 ] + } + }, + "1bbb:011f": { + "*": { + "t_vendor": 7099, + "t_product": [ 262 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1bbb:022c": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "1bbb:025e": { + "*": { + "t_vendor": 7099, + "t_product": [ 405 ], + "msg": [ 17 ] + } + }, + "1bbb:f000": { + "*": { + "t_vendor": 7099, + "t_product": [ 0, 23, 183, 286, 401, 405 ], + "msg": [ 17 ] + } + }, + "1bbb:f017": { + "*": { + "t_vendor": 7099, + "t_product": [ 23, 286, 515 ], + "msg": [ 17 ] + } + }, + "1bbb:f052": { + "*": { + "t_vendor": 7099, + "t_product": [ 82 ], + "msg": [ 17 ] + } + }, + "1c9e:1001": { + "*": { + "t_vendor": 7326, + "t_product": [ 24672, 24673 ], + "msg": [ 40 ] + } + }, + "1c9e:6000": { + "*": { + "t_class": 255, + "msg": [ 27 ] + } + }, + "1c9e:6061": { + ":uPr=Storage": { + "t_class": 255, + "msg": [ 40 ] + } + }, + "1c9e:9101": { + "*": { + "t_vendor": 7326, + "t_product": [ 37124 ], + "msg": [ 40 ] + } + }, + "1c9e:9200": { + "*": { + "t_vendor": 7326, + "t_product": [ 37378 ], + "msg": [ 40 ] + } + }, + "1c9e:9401": { + "*": { + "t_vendor": 7326, + "t_product": [ 37892 ], + "msg": [ 40 ] + } + }, + "1c9e:9800": { + "*": { + "t_class": 255, + "msg": [ 17 ] + } + }, + "1c9e:98ff": { + "*": { + "t_vendor": 7326, + "t_product": [ 26625, 38913, 38915 ], + "msg": [ 41 ] + } + }, + "1c9e:9bfe": { + "*": { + "t_vendor": 7326, + "t_product": [ 39681 ], + "msg": [ 40 ] + } + }, + "1c9e:9d00": { + "*": { + "t_class": 255, + "msg": [ 40 ] + } + }, + "1c9e:9e00": { + "*": { + "t_class": 255, + "msg": [ 40 ] + } + }, + "1c9e:9e08": { + "*": { + "t_vendor": 7326, + "t_product": [ 40472 ], + "mode": "Sierra", + "msg": [ ] + } + }, + "1c9e:f000": { + "*": { + "t_vendor": 7326, + "t_product": [ 36864, 38403, 38405, 38407, 39168 ], + "msg": [ 42 ], + "wait": 1 + }, + ":uMa=USB_Modem": { + "t_vendor": 7326, + "t_product": [ 36864, 38403, 38405, 38407, 39168, 39424 ], + "msg": [ 17 ], + "wait": 1 + } + }, + "1c9e:f010": { + "*": { + "t_vendor": 7326, + "t_product": [ 61697 ], + "msg": [ 40 ] + } + }, + "1d09:1000": { + "*": { + "t_vendor": 7433, + "t_product": [ 4112 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1d09:1021": { + "*": { + "t_vendor": 7433, + "t_product": [ 4112 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1d09:1025": { + "*": { + "t_vendor": 7433, + "t_product": [ 4134 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1da5:f000": { + "*": { + "t_vendor": 7589, + "t_product": [ 17682 ], + "mode": "Qisda", + "msg": [ ] + } + }, + "1dbc:0669": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "1dd6:1000": { + "*": { + "t_vendor": 7638, + "t_product": [ 4098 ], + "msg": [ 43 ], + "response": true + } + }, + "1de1:1101": { + "*": { + "t_vendor": 8679, + "t_product": [ 14 ], + "msg": [ 44 ] + } + }, + "1e0e:f000": { + "*": { + "t_vendor": 7694, + "t_product": [ 36864, 37120, 37376 ], + "msg": [ 14 ], + "response": true + } + }, + "1e89:f000": { + "*": { + "t_vendor": 7817, + "t_product": [ 6688 ], + "msg": [ 45 ] + } + }, + "1edf:6003": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "1ee8:0003": { + "*": { + "t_vendor": 7912, + "t_product": [ 4 ], + "msg": [ 46 ], + "response": true + } + }, + "1ee8:0007": { + "*": { + "t_vendor": 7912, + "t_product": [ 11 ], + "msg": [ 46 ] + } + }, + "1ee8:0009": { + "*": { + "t_vendor": 7912, + "t_product": [ 11 ], + "msg": [ 46 ], + "response": true + } + }, + "1ee8:0013": { + "*": { + "t_vendor": 7912, + "t_product": [ 17, 18, 20 ], + "msg": [ 46 ], + "response": true + } + }, + "1ee8:0018": { + "*": { + "t_vendor": 7912, + "t_product": [ 23 ], + "msg": [ 46 ], + "response": true + } + }, + "1ee8:0040": { + "*": { + "t_vendor": 7912, + "t_product": [ 62, 63 ], + "msg": [ 46 ], + "response": true + } + }, + "1ee8:0045": { + "*": { + "t_vendor": 7912, + "t_product": [ 68 ], + "msg": [ 46 ], + "response": true + } + }, + "1ee8:0048": { + "*": { + "t_vendor": 7912, + "t_product": [ 73 ], + "msg": [ 46 ] + } + }, + "1ee8:004a": { + "*": { + "t_vendor": 7912, + "t_product": [ 73 ], + "msg": [ 46 ] + } + }, + "1ee8:004f": { + "*": { + "t_vendor": 7912, + "t_product": [ 78 ], + "msg": [ 46 ], + "response": true + } + }, + "1ee8:0054": { + "*": { + "t_vendor": 7912, + "t_product": [ 83 ], + "msg": [ 46 ], + "response": true + } + }, + "1ee8:0060": { + "*": { + "t_vendor": 7912, + "t_product": [ 95 ], + "msg": [ 47 ] + } + }, + "1ee8:0063": { + "*": { + "t_vendor": 7912, + "t_product": [ 100, 101 ], + "msg": [ 47 ] + } + }, + "1ee8:0068": { + "*": { + "t_vendor": 7912, + "t_product": [ 105 ], + "msg": [ 47 ] + } + }, + "1f28:0021": { + "*": { + "t_vendor": 7976, + "t_product": [ 32 ], + "msg": [ 48 ] + } + }, + "1fac:0032": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "1fac:0130": { + "*": { + "t_vendor": 8108, + "t_product": [ 305 ], + "msg": [ 48 ] + } + }, + "1fac:0150": { + "*": { + "t_vendor": 8108, + "t_product": [ 337 ], + "msg": [ 48 ] + } + }, + "1fac:0151": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "2001:00a6": { + "*": { + "t_vendor": 8193, + "t_product": [ 32002 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:00a7": { + "*": { + "t_vendor": 8193, + "t_product": [ 32014 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:7600": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "2001:98ff": { + "*": { + "t_vendor": 8193, + "t_product": [ 32278 ], + "msg": [ 49 ], + "response": true + } + }, + "2001:a401": { + "*": { + "t_vendor": 8193, + "t_product": [ 32281 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a403": { + "*": { + "t_vendor": 8193, + "t_product": [ 32011, 32012 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a405": { + "*": { + "t_vendor": 8193, + "t_product": [ 32013 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a406": { + "*": { + "t_vendor": 8193, + "t_product": [ 32281 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a407": { + "*": { + "t_vendor": 8193, + "t_product": [ 32014 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a40a": { + "*": { + "t_vendor": 8193, + "t_product": [ 32016 ], + "msg": [ 50 ] + } + }, + "2001:a40d": { + "*": { + "t_vendor": 8193, + "t_product": [ 32312 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a706": { + "*": { + "t_vendor": 8193, + "t_product": [ 32001 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a707": { + "*": { + "t_vendor": 8193, + "t_product": [ 32002 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a708": { + "*": { + "t_vendor": 8193, + "t_product": [ 32003 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a805": { + "*": { + "t_vendor": 8193, + "t_product": [ 32274 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a809": { + "*": { + "t_vendor": 8193, + "t_product": [ 30976 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a80b": { + "*": { + "t_vendor": 8193, + "t_product": [ 32000 ], + "msg": [ 50 ], + "response": true + } + }, + "2001:ab00": { + "*": { + "t_vendor": 8193, + "t_product": [ 32309 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2015:0001": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "201e:1023": { + "*": { + "t_vendor": 8222, + "t_product": [ 4130 ], + "msg": [ 51, 52 ], + "response": true + } + }, + "201e:2009": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "2020:0002": { + "*": { + "t_vendor": 8224, + "t_product": [ 8192, 16384, 16400 ], + "msg": [ 53 ] + } + }, + "2020:f00e": { + "*": { + "t_vendor": 8224, + "t_product": [ 4101, 4104 ], + "mode": "StandardEject", + "msg": [ ], + "wait": 2 + } + }, + "2020:f00f": { + "*": { + "t_vendor": 8224, + "t_product": [ 4101 ], + "mode": "StandardEject", + "msg": [ ], + "wait": 2 + } + }, + "2077:1000": { + "*": { + "t_vendor": 8311, + "t_product": [ 28673, 28688, 28689 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2077:f000": { + "*": { + "t_vendor": 8311, + "t_product": [ 36864, 36962, 40960, 40963 ], + "mode": "StandardEject", + "msg": [ 54 ] + } + }, + "20a6:f00a": { + "*": { + "t_vendor": 8358, + "t_product": [ 4096 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "20a6:f00e": { + "*": { + "t_vendor": 8358, + "t_product": [ 4357 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "20b9:1682": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "21f5:1000": { + "*": { + "t_vendor": 8693, + "t_product": [ 8200 ], + "msg": [ 55 ] + } + }, + "21f5:3010": { + "*": { + "t_vendor": 8693, + "t_product": [ 4353 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2262:0001": { + "*": { + "t_vendor": 8802, + "t_product": [ 2 ], + "msg": [ 56 ] + } + }, + "22de:6801": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "22de:6802": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "22de:6803": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "22f4:0021": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "230d:0001": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "230d:0003": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "230d:0007": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "230d:000b": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "230d:000c": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "230d:000d": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "230d:0101": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "230d:0103": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "2357:0200": { + "*": { + "t_vendor": 9047, + "t_product": [ 513 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2357:f000": { + "*": { + "t_vendor": 9047, + "t_product": [ 36864 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "23a2:1010": { + "*": { + "t_vendor": 9122, + "t_product": [ 4660 ], + "msg": [ 57 ] + } + }, + "257a:a000": { + "*": { + "t_vendor": 9594, + "t_product": [ 5633, 5663, 5679, 9759, 9775, 13855, 13871 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "257a:b000": { + "*": { + "t_vendor": 9594, + "t_product": [ 5633, 5663, 5679, 9759, 9775, 13855, 13871 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "257a:c000": { + "*": { + "t_vendor": 9594, + "t_product": [ 5633, 5663, 5679, 9759, 9775, 13855, 13871 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "257a:d000": { + "*": { + "t_vendor": 9594, + "t_product": [ 5633, 5663, 5679, 9759, 9775, 13855, 13871 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "6000:1000": { + "*": { + "t_vendor": 1478, + "t_product": [ 24576 ], + "msg": [ 55 ] + } + }, + "8888:6500": { + "*": { + "t_vendor": 5848, + "t_product": [ 25907 ], + "msg": [ 58 ] + } + }, + "ed09:1021": { + "*": { + "t_vendor": 60681, + "t_product": [ 4112 ], + "mode": "StandardEject", + "msg": [ ] + } + } + } +} diff --git a/ext-rooter-basic/files/etc/usb-mode1.json b/ext-rooter-basic/files/etc/usb-mode1.json new file mode 100644 index 0000000..c4c0cc8 --- /dev/null +++ b/ext-rooter-basic/files/etc/usb-mode1.json @@ -0,0 +1,3481 @@ +{ + "messages" : [ + "555342431234567800000000000006d0000000000000000000000000000000", + "55534243f0298d8124000000800006bc626563240000000000000000000000", + "0902200001010080fa0904000002080650000705010200020007058102000200", + "55534243785634120100000080000601000000000000000000000000000000", + "55534243123456780000000000000616000000000000000000000000000000", + "55534243123456782400000080000612000024000000000000000000000000", + "55534243123456780000000000000601000000000000000000000000000000", + "5553424368032c882400000080000612000000240000000000000000000000", + "5553424312345678c00000008000069f140000000000000000000000000000", + "01b0000000000000000000000000000000000000000000000000000000000000", + "1b5a01", + "5553424312345678c000000080010606f50402527000000000000000000000", + "55534243123456788000000080000606f50402527000000000000000000000", + "555342431234567800000000000006f0010300000000000000000000000000", + "55534243123456780000000000000aff554d53434847000000000000000000", + "555342431234567803000000800006f1010100000000000000000000000000", + "555342431234567800000000000005f1010100000000000000000000000000", + "555342431234567824000000800008ff024445564348470000000000000000", + "555342431234567824000000800008ff020000000000000000000000000000", + "55534243b82e238c24000000800008ff020000000000000000000000000000", + "55534243123456780000000000000011063000000100010000000000000000", + "55534243123456780600000080010a11060000000000000000000000000000", + "55534243123456780600000080000601000000000000000000000000000000", + "555342431234567824000000800008ff524445564348470000000000000000", + "555342431234567824000000800008ff524445564348473100000000000000", + "55534243123456782400000080000dfe524445564348473d4e444953000000", + "55534243d85dd88524000000800008ff524445564348470000000000000000", + "55534243123456702000000080000c85010101180101010101000000000000", + "55534243123456782400000080000685000000240000000000000000000000", + "55534243d8a523862400000080000685000000240000000000000000000000", + "5553424348c4758600000000000010ff000000000000000000000000000000", + "555342431234567824000000800006bc626563240000000000000000000000", + "5553424330f4cf8124000000800108df200000000000000000000000000000", + "5553424312345678c00000008000069f030000000000000000000000000000", + "555342431234567824000000800008FF05B112AEE102000000000000000000", + "55534243123456780000000000000606f50402527000000000000000000000", + "55534243123456780000000080000606f50402527000000000000000000000", + "555342431234567800000000000001ff000000000000000000000000000000", + "55534243123456781200000080000603000000020000000000000000000000", + "55534243123456780000000000000cff020000000000000000000000000000", + "555342431234567800000000000006bd000000020000000000000000000000", + "5553424312345678800000008000060619181a207000000000000000000000", + "555342431234567800000000000010ff000000000000000000000000000000", + "555342431234567800000000000008ff000000000000030000000000000000", + "555342431234567824000000800108df200000000000000000000000000000", + "55534243f8d2e6838000000080000606f50402527000000000000000000000", + "555342431234567800000000000003f0010100000000000000000000000000", + "55534243123456780000000000000600000000000000000000000000000000", + "5553424312345679c000000080000671030000000000000000000000000000", + "555342430820298900000000000003f0010100000000000000000000000000", + "55534243123456700000000000000616aa0000000000000000000000000000", + "5553424312345678c000000080000671010000000000000000000000000000", + "5553424340799288C000000080010A16000000C00000000000000000000000", + "555342431234567800000000000006161f6d62706b00000000000000000000", + "5553424398e2c4812400000080000bff524445564348473d43440000000000", + "55534243123456780002000000000a2a000000003300000100000000000000", + "5553424312345678000000000000061b004600000000000000000000000000", + "55534243123456780000000000000606f50402527000000000000000000000" + ], + + "devices" : { + "07d1:f000": { + "*": { + "t_vendor": 2001, + "t_product": [ 32263 ], + "msg": [ 40 ] + } + }, + "12d1:101e": { + "*": { + "t_class": 255, + "msg": [ 22 ] + } + }, + "19d2:0388": { + "*": { + "t_vendor": 6610, + "t_product": [ 1095 ], + "msg": [ 28 ] + } + }, + "1c9e:9bfe": { + "*": { + "t_vendor": 7326, + "t_product": [ 39681 ], + "msg": [ 57 ] + } + }, + "1c9e:9d00": { + "*": { + "t_class": 255, + "msg": [ 57 ] + } + }, + "1c9e:9e00": { + "*": { + "t_class": 255, + "msg": [ 57 ] + } + }, + "1c9e:f010": { + "*": { + "t_vendor": 7326, + "t_product": [ 61697 ], + "msg": [ 57 ] + } + }, + "6000:1000": { + "*": { + "t_vendor": 1478, + "t_product": [ 24576 ], + "msg": [ 56 ] + } + }, + "03f0:002a": { + "*": { + "t_class": 7, + "msg": [ 0 ], + "response": true + } + }, + "03f0:032a": { + "*": { + "t_class": 7, + "msg": [ 55 ] + } + }, + "0408:1000": { + "*": { + "t_vendor": 1032, + "t_product": [ 59906 ], + "msg": [ 56 ] + } + }, + "046d:c261": { + "*": { + "t_class": 3, + "msg_endpoint": 1, + "msg": [ 1 ], + "response_endpoint": 1 + } + }, + "04bb:bccd": { + "*": { + "t_vendor": 1211, + "t_product": [ 2377 ], + "msg": [ 1 ] + } + }, + "04e8:680c": { + "*": { + "t_vendor": 1256, + "t_product": [ 26514 ], + "msg": [ 2 ] + } + }, + "04e8:689a": { + "*": { + "t_vendor": 1256, + "t_product": [ 26761 ], + "msg": [ 3 ] + } + }, + "04e8:f000": { + ":sMo=U209": { + "t_vendor": 1256, + "t_product": [ 26113 ], + "msg": [ 4 ] + } + }, + "04fc:2140": { + "*": { + "t_vendor": 1276, + "t_product": [ 1557, 4672 ], + "msg": [ 5 ] + } + }, + "057c:62ff": { + "*": { + "t_vendor": 1404, + "t_product": [ 34049, 34050 ], + "mode": "StandardEject", + "no_driver": true, + "msg": [ ] + } + }, + "057c:84ff": { + "*": { + "t_vendor": 1404, + "t_product": [ 33793 ], + "mode": "StandardEject", + "no_driver": true, + "msg": [ ] + } + }, + "05c6:2000": { + "*": { + "t_vendor": 1478, + "t_product": [ 21, 22, 24, 52759 ], + "msg": [ 7 ], + "response": true, + "check": true + } + }, + "05c6:f000": { + "*": { + "t_vendor": 1478, + "t_product": [ 22, 36864 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c7:1000": { + "*": { + "t_vendor": 1479, + "t_product": [ 24576 ], + "msg": [ 8 ] + } + }, + "0685:2000": { + "*": { + "t_vendor": 7326, + "t_product": [ 38403 ], + "msg": [ 7 ], + "response": true + } + }, + "072f:100d": { + "*": { + "t_vendor": 1839, + "t_product": [ 37068 ], + "no_driver": true, + "msg_endpoint": 2, + "msg": [ 9 ] + } + }, + "0922:1001": { + "*": { + "t_vendor": 2338, + "t_product": [ 4098 ], + "no_driver": true, + "msg_endpoint": 1, + "msg": [ 10 ], + "response_endpoint": 1 + } + }, + "0922:1003": { + "*": { + "t_vendor": 2338, + "t_product": [ 4100 ], + "msg": [ 10 ] + } + }, + "0922:1007": { + "*": { + "t_vendor": 2338, + "t_product": [ 4104 ], + "msg": [ 10 ] + } + }, + "0b3c:f000": { + "*": { + "t_vendor": 2876, + "t_product": [ 49155, 49156 ], + "msg": [ 11 ], + "response": true + } + }, + "0b3c:f00c": { + "*": { + "t_vendor": 2876, + "t_product": [ 49162 ], + "msg": [ 12 ] + } + }, + "0e8d:0002": { + ":uPr=MT": { + "t_vendor": 3725, + "t_product": [ 161, 162, 165 ], + "msg": [ 13 ] + } + }, + "1004:1000": { + "*": { + "t_class": 255, + "msg": [ 14 ] + } + }, + "1004:607f": { + "*": { + "t_vendor": 4100, + "t_product": [ 24576, 24852 ], + "msg": [ 15 ], + "response": true + } + }, + "1004:6327": { + "*": { + "t_vendor": 4100, + "t_product": [ 25382 ], + "msg": [ 16 ] + } + }, + "106c:3b03": { + "*": { + "t_vendor": 4204, + "t_product": [ 14101 ], + "msg": [ 17 ] + } + }, + "106c:3b05": { + "*": { + "t_vendor": 4204, + "t_product": [ 14102 ], + "msg": [ 18 ] + } + }, + "106c:3b06": { + "*": { + "t_vendor": 4204, + "t_product": [ 14103 ], + "msg": [ 19 ] + } + }, + "106c:3b14": { + "*": { + "t_vendor": 4204, + "t_product": [ 14113 ], + "no_driver": true, + "msg": [ 17 ] + } + }, + "12d1:1030": { + "*": { + "t_vendor": 4817, + "t_product": [ 4148 ], + "no_driver": true, + "msg": [ 21 ] + } + }, + "12d1:1031": { + "*": { + "t_vendor": 4817, + "t_product": [ 4149 ], + "no_driver": true, + "msg": [ 21 ] + } + }, + "1614:0800": { + "*": { + "t_class": 255, + "no_driver": true, + "msg": [ 6 ] + } + }, + "1614:0802": { + "*": { + "t_class": 255, + "no_driver": true, + "msg": [ 6 ] + } + }, + "16d8:6281": { + "*": { + "t_class": 255, + "msg": [ 23 ] + } + }, + "16d8:6803": { + "*": { + "t_class": 2, + "msg": [ 24 ] + } + }, + "16d8:6804": { + "*": { + "t_class": 255, + "msg": [ 23 ] + } + }, + "16d8:700a": { + "*": { + "t_class": 255, + "msg": [ 25 ] + } + }, + "16d8:700b": { + "*": { + "t_class": 255, + "msg": [ 25 ] + } + }, + "16d8:f000": { + "*": { + "t_vendor": 5848, + "t_product": [ 24582 ], + "msg": [ 26 ] + } + }, + "198f:bccd": { + "*": { + "t_vendor": 6543, + "t_product": [ 544 ], + "no_driver": true, + "msg": [ 1 ] + } + }, + "19d2:0166": { + "*": { + "t_vendor": 6610, + "t_product": [ 359 ], + "msg": [ 28 ] + } + }, + "19d2:0266": { + "*": { + "t_vendor": 6610, + "t_product": [ 613 ], + "mode": "StandardEject", + "msg": [ 29 ] + } + }, + "19d2:0413": { + "*": { + "t_vendor": 6610, + "t_product": [ 1042 ], + "msg": [ 29 ] + } + }, + "19d2:1588": { + "*": { + "t_vendor": 6610, + "t_product": [ 5513, 5521, 5522 ], + "mode": "StandardEject", + "msg": [ 27 ] + } + }, + "19d2:1595": { + "*": { + "t_vendor": 6610, + "t_product": [ 5428, 5522, 5526, 5632 ], + "mode": "StandardEject", + "msg": [ 27 ] + } + }, + "19d2:2000": { + "*": { + "t_vendor": 6610, + "t_product": [ 1, 2, 21, 22, 23, 25, 36, 49, 51, 55, 66, 82, 85, 97, 99, 100, 102, 145, 264, 279, 296, 337, 343, 375, 5122, 8194, 8195 ], + "mode": "StandardEject", + "msg": [ 27 ] + } + }, + "19d2:bccd": { + "*": { + "t_vendor": 6610, + "t_product": [ 370 ], + "no_driver": true, + "msg": [ 31 ] + } + }, + "19d2:ffe6": { + "*": { + "t_vendor": 6610, + "t_product": [ 65509 ], + "msg": [ 32 ] + } + }, + "19d2:fff5": { + "*": { + "t_vendor": 6610, + "t_product": [ 65508, 65513, 65521, 65534, 65535 ], + "msg": [ 33 ] + } + }, + "19d2:fff6": { + "*": { + "t_vendor": 6610, + "t_product": [ 65521 ], + "msg": [ 33 ] + } + }, + "1b7d:0700": { + "*": { + "t_vendor": 7037, + "t_product": [ 1 ], + "msg": [ 34 ] + } + }, + "1bbb:00ca": { + "*": { + "t_class": 255, + "msg": [ 12 ] + } + }, + "1bbb:f000": { + "*": { + "t_vendor": 7099, + "t_product": [ 0, 23, 183, 286, 401, 405 ], + "msg": [ 12 ] + } + }, + "1bbb:f017": { + "*": { + "t_vendor": 7099, + "t_product": [ 23, 286, 515 ], + "msg": [ 12 ] + } + }, + "1bbb:f052": { + "*": { + "t_vendor": 7099, + "t_product": [ 82 ], + "msg": [ 12 ] + } + }, + "1c9e:1001": { + "*": { + "t_vendor": 7326, + "t_product": [ 24672, 24673 ], + "msg": [ 35 ] + } + }, + "1bbb:025e": { + "*": { + "t_vendor": 7099, + "t_product": [ 405 ], + "msg": [ 12 ] + } + }, + "1c9e:6000": { + "*": { + "t_class": 255, + "no_driver": true, + "msg": [ 6 ] + } + }, + "1c9e:6061": { + ":uPr=Storage": { + "t_class": 255, + "msg": [ 35 ] + } + }, + "1c9e:9101": { + "*": { + "t_vendor": 7326, + "t_product": [ 37124 ], + "msg": [ 35 ] + } + }, + "1c9e:9200": { + "*": { + "t_vendor": 7326, + "t_product": [ 37378 ], + "msg": [ 35 ] + } + }, + "1c9e:9401": { + "*": { + "t_vendor": 7326, + "t_product": [ 37892 ], + "msg": [ 35 ] + } + }, + "1c9e:9800": { + "*": { + "t_class": 255, + "msg": [ 12 ] + } + }, + "1c9e:98ff": { + "*": { + "t_vendor": 7326, + "t_product": [ 26625, 38913, 38915 ], + "msg": [ 36 ] + } + }, + "1c9e:f000": { + "*": { + "t_vendor": 7326, + "t_product": [ 36864, 38403, 38405, 38407, 39168 ], + "msg": [ 37 ], + "wait": 1 + }, + ":uMa=USB_Modem": { + "t_vendor": 7326, + "t_product": [ 36864, 38403, 38405, 38407, 39168, 39424 ], + "msg": [ 12 ], + "wait": 1 + } + }, + "1dd6:1000": { + "*": { + "t_vendor": 7638, + "t_product": [ 4098 ], + "msg": [ 38 ], + "response": true + } + }, + "1de1:1101": { + "*": { + "t_vendor": 8679, + "t_product": [ 14 ], + "msg": [ 39 ] + } + }, + "1e0e:f000": { + "*": { + "t_vendor": 7694, + "t_product": [ 36864, 37120, 37376 ], + "msg": [ 40 ], + "response": true + } + }, + "1e89:f000": { + "*": { + "t_vendor": 7817, + "t_product": [ 6688 ], + "msg": [ 41 ] + } + }, + "1ee8:0003": { + "*": { + "t_vendor": 7912, + "t_product": [ 4 ], + "msg": [ 42 ], + "response": true + } + }, + "1ee8:0007": { + "*": { + "t_vendor": 7912, + "t_product": [ 11 ], + "msg": [ 42 ] + } + }, + "1ee8:0009": { + "*": { + "t_vendor": 7912, + "t_product": [ 11 ], + "msg": [ 42 ], + "response": true + } + }, + "1ee8:0013": { + "*": { + "t_vendor": 7912, + "t_product": [ 17, 18, 20 ], + "msg": [ 42 ], + "response": true + } + }, + "1ee8:0018": { + "*": { + "t_vendor": 7912, + "t_product": [ 23 ], + "msg": [ 42 ], + "response": true + } + }, + "1ee8:0040": { + "*": { + "t_vendor": 7912, + "t_product": [ 62, 63 ], + "msg": [ 42 ], + "response": true + } + }, + "1ee8:0045": { + "*": { + "t_vendor": 7912, + "t_product": [ 68 ], + "msg": [ 42 ], + "response": true + } + }, + "1ee8:0048": { + "*": { + "t_vendor": 7912, + "t_product": [ 73 ], + "msg": [ 42 ] + } + }, + "1ee8:004a": { + "*": { + "t_vendor": 7912, + "t_product": [ 73 ], + "msg": [ 42 ] + } + }, + "1ee8:004f": { + "*": { + "t_vendor": 7912, + "t_product": [ 78 ], + "msg": [ 42 ], + "response": true + } + }, + "1ee8:0054": { + "*": { + "t_vendor": 7912, + "t_product": [ 83 ], + "msg": [ 42 ], + "response": true + } + }, + "1ee8:0060": { + "*": { + "t_vendor": 7912, + "t_product": [ 95 ], + "msg": [ 43 ] + } + }, + "1ee8:0063": { + "*": { + "t_vendor": 7912, + "t_product": [ 100, 101 ], + "msg": [ 43 ] + } + }, + "1ee8:0068": { + "*": { + "t_vendor": 7912, + "t_product": [ 105 ], + "msg": [ 43 ] + } + }, + "1f28:0021": { + "*": { + "t_vendor": 7976, + "t_product": [ 32 ], + "msg": [ 44 ] + } + }, + "1fac:0150": { + "*": { + "t_vendor": 8108, + "t_product": [ 337 ], + "msg": [ 44 ] + } + }, + "2001:98ff": { + "*": { + "t_vendor": 8193, + "t_product": [ 32278 ], + "msg": [ 45 ], + "response": true + } + }, + "2001:a40a": { + "*": { + "t_vendor": 8193, + "t_product": [ 32016 ], + "msg": [ 46 ] + } + }, + "2001:a80b": { + "*": { + "t_vendor": 8193, + "t_product": [ 32000 ], + "msg": [ 46 ], + "response": true + } + }, + "201e:1023": { + "*": { + "t_vendor": 8222, + "t_product": [ 4130 ], + "msg": [ 47, 48 ], + "response": true + } + }, + "2020:0002": { + "*": { + "t_vendor": 8224, + "t_product": [ 8192, 16400 ], + "msg": [ 49 ] + } + }, + "2077:f000": { + "*": { + "t_vendor": 8311, + "t_product": [ 36864, 36962, 40960 ], + "mode": "StandardEject", + "msg": [ 50 ] + } + }, + "21f5:1000": { + "*": { + "t_vendor": 8693, + "t_product": [ 8200 ], + "msg": [ 51 ] + } + }, + "2262:0001": { + "*": { + "t_vendor": 8802, + "t_product": [ 2 ], + "msg": [ 52 ] + } + }, + "23a2:1010": { + "*": { + "t_vendor": 9122, + "t_product": [ 4660 ], + "msg": [ 53 ] + } + }, + "8888:6500": { + "*": { + "t_vendor": 5848, + "t_product": [ 25907 ], + "msg": [ 54 ] + } + }, + "0408:ea17": { + "*": { + "t_vendor": 1032, + "t_product": [ 59926 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0408:ea25": { + "*": { + "t_vendor": 1032, + "t_product": [ 59942 ], + "msg": [ ] + } + }, + "0408:ea43": { + "*": { + "t_vendor": 1032, + "t_product": [ 59975, 59977, 59981 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0408:f000": { + "*": { + "t_vendor": 1032, + "t_product": [ 59907 ], + "msg": [ ] + }, + ":uMa=Yota": { + "t_vendor": 1032, + "t_product": [ 53257 ], + "msg": [ 3 ] + } + }, + "0408:f001": { + "*": { + "t_vendor": 1032, + "t_product": [ 59907 ], + "msg": [ ] + } + }, + "0421:060c": { + "*": { + "t_vendor": 1057, + "t_product": [ 1550 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:0610": { + "*": { + "t_vendor": 1057, + "t_product": [ 1554 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:0618": { + "*": { + "t_vendor": 1057, + "t_product": [ 1561 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:061d": { + "*": { + "t_vendor": 1057, + "t_product": [ 1566 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:0622": { + "*": { + "t_vendor": 1057, + "t_product": [ 1571 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:0627": { + "*": { + "t_vendor": 1057, + "t_product": [ 1554, 1577 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:062c": { + "*": { + "t_vendor": 1057, + "t_product": [ 1581, 1583 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:0632": { + "*": { + "t_vendor": 1057, + "t_product": [ 1586 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0421:0637": { + "*": { + "t_vendor": 1057, + "t_product": [ 1592 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0471:1210": { + ":uMa=Philips": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=Wisue": { + "t_vendor": 7612, + "t_product": [ 5 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0471:1237": { + "*": { + "t_vendor": 1137, + "t_product": [ 4614, 4660 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0482:024d": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "04cc:2251": { + "*": { + "t_vendor": 1228, + "t_product": [ 8793, 8814 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "04cc:225c": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "04cc:226e": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "04cc:226f": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "0586:2030": { + "*": { + "t_vendor": 1414, + "t_product": [ 13379, 13380 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c6:0010": { + "*": { + "t_vendor": 1478, + "t_product": [ 160 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c6:1000": { + ":sVe=Option": { + "t_vendor": 2800, + "t_product": [ 26881, 26369, 26112 ], + "mode": "Option", + "msg": [ ] + }, + ":uMa=AnyDATA": { + "t_vendor": 5845, + "t_product": [ 25858 ], + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=CELOT": { + "t_vendor": 8479, + "t_product": [ 26625, 26626 ], + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=Co.,Ltd": { + "t_vendor": 7433, + "t_product": [ 17158 ], + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=DGT": { + "t_vendor": 8479, + "t_product": [ 26626 ], + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=Option": { + "t_vendor": 2800, + "t_product": [ 26881 ], + "mode": "Option", + "msg": [ ] + }, + ":uMa=SSE": { + "t_vendor": 1478, + "t_product": [ 24576 ], + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=StrongRising": { + "t_vendor": 650, + "t_product": [ 4102 ], + "mode": "StandardEject", + "msg": [ ] + }, + ":uMa=Vertex": { + "t_vendor": 8167, + "t_product": [ 256 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c6:2001": { + "*": { + "t_vendor": 7694, + "t_product": [ 52758, 52759, 52990 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c6:6503": { + "*": { + "t_vendor": 5845, + "t_product": [ 25858 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c6:9024": { + "*": { + "t_vendor": 1478, + "t_product": [ 36901 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "05c6:98ff": { + "*": { + "t_vendor": 1478, + "t_product": [ 24577 ], + "mode": "Sierra", + "msg": [ ] + } + }, + "07d1:a800": { + "*": { + "t_vendor": 2001, + "t_product": [ 15873, 15874, 32268 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "07d1:a804": { + "*": { + "t_vendor": 2001, + "t_product": [ 32273 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0930:0d46": { + "*": { + "t_vendor": 2352, + "t_product": [ 3397 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0ace:2011": { + "*": { + "mode": "StandardEject", + "msg": [ ] + } + }, + "0ace:20ff": { + "*": { + "mode": "StandardEject", + "msg": [ ] + } + }, + "0af0:4007": { + "*": { + "t_vendor": 2800, + "t_product": [ 16389 ], + "mode": "Sierra", + "msg": [ ] + } + }, + "0af0:6711": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:6731": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:6751": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:6771": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:6791": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:6811": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:6911": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:6951": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:6971": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7011": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7031": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7051": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7071": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7111": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7211": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7251": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7271": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7301": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7311": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7361": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7381": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7401": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7501": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7601": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7701": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7706": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7801": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7901": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7a01": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:7a05": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:8006": { + "*": { + "t_vendor": 2800, + "t_product": [ 37120 ], + "mode": "Option", + "msg": [ ] + } + }, + "0af0:8200": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:8201": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:8300": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:8302": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:8304": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:8400": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:8600": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:8700": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:8800": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:8900": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:9000": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:9200": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:c031": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:c100": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d001": { + "*": { + "t_vendor": 2800, + "t_product": [ 53591, 53845, 53847 ], + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d013": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d031": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d033": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d035": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d055": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d057": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d058": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d155": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d157": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d255": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d257": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0af0:d357": { + "*": { + "t_class": 255, + "mode": "Option", + "msg": [ ] + } + }, + "0b3c:c700": { + "*": { + "t_vendor": 2876, + "t_product": [ 49152, 49153, 49154 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0b3c:f017": { + "*": { + "t_vendor": 2876, + "t_product": [ 49163 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0bdb:190d": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "0bdb:1910": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "0cf3:20ff": { + "*": { + "t_vendor": 3315, + "t_product": [ 28688 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0d46:45a1": { + "*": { + "t_vendor": 3398, + "t_product": [ 17833 ], + "mode": "Kobil", + "msg": [ ] + } + }, + "0d46:45a5": { + "*": { + "t_vendor": 3398, + "t_product": [ 17837 ], + "mode": "Kobil", + "msg": [ ] + } + }, + "0df7:0800": { + "*": { + "t_class": 255, + "mode": "MobileAction", + "msg": [ ] + } + }, + "0e8d:7109": { + "*": { + "t_vendor": 3725, + "t_product": [ 28949, 28952 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "0fca:8020": { + "*": { + "t_vendor": 4042, + "t_product": [ 32786 ], + "msg": [ ] + } + }, + "0fce:d0cf": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "0fce:d0df": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "0fce:d0e1": { + "*": { + "t_class": 2, + "mode": "Sony", + "msg": [ ], + "config": 2 + } + }, + "0fce:d103": { + "*": { + "t_class": 2, + "mode": "Sony", + "msg": [ ], + "config": 2 + } + }, + "0fd1:1000": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "1004:610c": { + "*": { + "t_vendor": 4100, + "t_product": [ 24841 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:613a": { + "*": { + "t_vendor": 4100, + "t_product": [ 24868 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:613f": { + "*": { + "t_vendor": 4100, + "t_product": [ 24897 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:614e": { + "*": { + "t_vendor": 4100, + "t_product": [ 24885 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:6156": { + "*": { + "t_vendor": 4100, + "t_product": [ 24919 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:6190": { + "*": { + "t_vendor": 4100, + "t_product": [ 24963, 24999 ], + "mode": "StandardEject", + "msg": [ ], + "wait": 10 + } + }, + "1004:61aa": { + "*": { + "t_vendor": 4100, + "t_product": [ 24999 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:61dd": { + "*": { + "t_vendor": 4100, + "t_product": [ 24975 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:61e7": { + "*": { + "t_vendor": 4100, + "t_product": [ 25062 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1004:61eb": { + "*": { + "t_vendor": 4100, + "t_product": [ 25066 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "106c:3b11": { + "*": { + "t_vendor": 4204, + "t_product": [ 14104 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1076:7f40": { + "*": { + "t_vendor": 4214, + "t_product": [ 32512 ], + "mode": "GCT", + "msg": [ ] + } + }, + "109b:f009": { + "*": { + "t_vendor": 4251, + "t_product": [ 37140 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "10a9:606f": { + "*": { + "t_vendor": 4265, + "t_product": [ 24676, 24692 ], + "msg": [ ] + } + }, + "10a9:6080": { + "*": { + "t_vendor": 4265, + "t_product": [ 24709 ], + "msg": [ ] + } + }, + "1199:0fff": { + "*": { + "t_vendor": 4505, + "t_product": [ 23, 24, 25, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 274, 288, 536, 544, 548, 769, 26626, 26627, 26628, 26629, 26632, 26633, 26642, 26643, 26645, 26646, 26656, 26657, 26658, 26674, 26675, 26676, 26677, 26680, 26681, 26682, 26683, 26684, 26685, 26686, 26704, 26705, 26706, 26707, 26709, 26710, 26713, 26714, 26752, 26768, 26769, 26770, 26771, 26786, 26787, 26794, 36881, 36882, 36945 ], + "mode": "Sierra", + "msg": [ ] + } + }, + "1199:9011": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:9013": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:9017": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:901b": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:901c": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:901f": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:9041": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:9051": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:9053": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1199:9063": { + "*": { + "msg": [ ], + "config": 1 + } + }, + "1266:1000": { + "*": { + "t_vendor": 4710, + "t_product": [ 4098, 4099, 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4110, 4111, 4113, 4114 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "12d1:1001": { + "*": { + "t_class": 255, + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1003": { + "*": { + "t_class": 255, + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1009": { + "*": { + "t_class": 255, + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1010": { + "*": { + "t_class": 255, + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1413": { + "*": { + "t_class": 255, + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1414": { + "*": { + "t_class": 255, + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1446": { + "*": { + "t_vendor": 4817, + "t_product": [ 4097, 5124, 5126, 5131, 5132, 5138, 5143, 5147, 5161, 5170, 5171, 5174, 5292, 5382, 5388, 5393 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1449": { + "*": { + "t_vendor": 4817, + "t_product": [ 5188 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14ad": { + "*": { + "t_vendor": 4817, + "t_product": [ 5294 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14b5": { + "*": { + "t_vendor": 4817, + "t_product": [ 5288, 5290 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14b7": { + "*": { + "t_vendor": 4817, + "t_product": [ 5324 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14ba": { + "*": { + "t_vendor": 4817, + "t_product": [ 5330 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14c1": { + "*": { + "t_vendor": 4817, + "t_product": [ 5318 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14c3": { + "*": { + "t_vendor": 4817, + "t_product": [ 5320 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14c4": { + "*": { + "t_vendor": 4817, + "t_product": [ 5322 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14c5": { + "*": { + "t_vendor": 4817, + "t_product": [ 5323 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14d1": { + "*": { + "t_vendor": 4817, + "t_product": [ 5321 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:14fe": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382, 5391, 5405, 7198 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1505": { + "*": { + "t_vendor": 4817, + "t_product": [ 5131, 5132, 5382, 5391, 5386 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:151a": { + "*": { + "t_vendor": 4817, + "t_product": [ 5403, 5405, 5406 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1520": { + "*": { + "t_vendor": 4817, + "t_product": [ 5221 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1521": { + "*": { + "t_vendor": 4817, + "t_product": [ 5220 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1523": { + "*": { + "t_vendor": 4817, + "t_product": [ 5265 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1526": { + "*": { + "t_vendor": 4817, + "t_product": [ 5327 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1527": { + "*": { + "t_vendor": 4817, + "t_product": [ 5524 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1553": { + "*": { + "t_vendor": 4817, + "t_product": [ 4097 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1557": { + "*": { + "t_vendor": 4817, + "t_product": [ 5285 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:155a": { + "*": { + "t_vendor": 4817, + "t_product": [ 5325 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:155b": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:156a": { + "*": { + "t_vendor": 4817, + "t_product": [ 5483, 5484 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:157c": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:157d": { + "*": { + "t_vendor": 4817, + "t_product": [ 5339, 5340 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1580": { + "*": { + "t_vendor": 4817, + "t_product": [ 5509 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1581": { + "*": { + "t_vendor": 4817, + "t_product": [ 5511 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1582": { + "*": { + "t_vendor": 4817, + "t_product": [ 5512 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1583": { + "*": { + "t_vendor": 4817, + "t_product": [ 5513 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1597": { + "*": { + "t_vendor": 4817, + "t_product": [ 5528 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15ca": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15cd": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15ce": { + "*": { + "t_vendor": 4817, + "t_product": [ 5553, 5555 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15cf": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15d0": { + "*": { + "t_vendor": 4817, + "t_product": [ 5585 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15d2": { + "*": { + "t_vendor": 4817, + "t_product": [ 5587 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15e7": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:15ec": { + "*": { + "t_vendor": 4817, + "t_product": [ 7206 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1805": { + "*": { + "t_class": 255, + "msg": [ 26 ] + } + }, + "12d1:1c0b": { + "*": { + "t_vendor": 4817, + "t_product": [ 7173, 7174, 7175, 7176, 7184 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1c1b": { + "*": { + "t_vendor": 4817, + "t_product": [ 5382 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1c24": { + "*": { + "t_vendor": 4817, + "t_product": [ 7186, 7203 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1d50": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "12d1:1da1": { + "*": { + "t_vendor": 4817, + "t_product": [ 7433 ], + "mode": "Huawei", + "msg": [ ] + } + }, + "12d1:1f01": { + "*": { + "t_vendor": 4817, + "t_product": [ 5339, 5340 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f02": { + "*": { + "t_vendor": 4817, + "t_product": [ 5340 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f03": { + "*": { + "t_vendor": 4817, + "t_product": [ 5339 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f04": { + "*": { + "t_vendor": 4817, + "t_product": [ 5564 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f05": { + "*": { + "t_vendor": 4817, + "t_product": [ 5565 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f06": { + "*": { + "t_vendor": 4817, + "t_product": [ 5575 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f07": { + "*": { + "t_vendor": 4817, + "t_product": [ 5567 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f09": { + "*": { + "t_vendor": 4817, + "t_product": [ 7248 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f11": { + "*": { + "t_vendor": 4817, + "t_product": [ 5308 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f15": { + "*": { + "t_vendor": 4817, + "t_product": [ 5120, 5367 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f16": { + "*": { + "mode": "MBIM", + "msg": [ ] + } + }, + "12d1:1f17": { + "*": { + "t_vendor": 4817, + "t_product": [ 5494 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f18": { + "*": { + "t_vendor": 4817, + "t_product": [ 5495 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f19": { + "*": { + "t_vendor": 4817, + "t_product": [ 5370, 5493, 5496 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f1b": { + "*": { + "t_vendor": 4817, + "t_product": [ 5497 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f1c": { + "*": { + "t_vendor": 4817, + "t_product": [ 5498, 5520 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f1d": { + "*": { + "t_vendor": 4817, + "t_product": [ 5499, 5521 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:1f1e": { + "*": { + "t_vendor": 4817, + "t_product": [ 5503, 5522 ], + "mode": "HuaweiNew", + "msg": [ ] + } + }, + "12d1:380b": { + "*": { + "t_class": 2, + "mode": "StandardEject", + "msg": [ ] + } + }, + "1307:1169": { + "*": { + "t_vendor": 5041, + "t_product": [ 49 ], + "mode": "Cisco", + "msg": [ ] + } + }, + "1410:5010": { + "*": { + "t_vendor": 5136, + "t_product": [ 16640, 17408, 28720 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5020": { + "*": { + "t_vendor": 5136, + "t_product": [ 24576, 28673 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5023": { + "*": { + "t_vendor": 5136, + "t_product": [ 28720 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5030": { + "*": { + "t_vendor": 5136, + "t_product": [ 24576 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5031": { + "*": { + "t_vendor": 5136, + "t_product": [ 24578 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5041": { + "*": { + "t_vendor": 5136, + "t_product": [ 28673, 28675 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5055": { + "*": { + "t_vendor": 5136, + "t_product": [ 24626 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:5059": { + "*": { + "t_vendor": 5136, + "t_product": [ 28721, 28738 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:7001": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "1410:9020": { + "*": { + "msg": [ ], + "config": 4 + } + }, + "148e:a000": { + "*": { + "t_class": 2, + "mode": "Sequans", + "msg": [ ] + } + }, + "148f:2578": { + "*": { + "t_vendor": 5263, + "t_product": [ 36897 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "148f:2878": { + "*": { + "t_vendor": 5263, + "t_product": [ 30209 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "15eb:7153": { + "*": { + "t_vendor": 5611, + "t_product": [ 29010 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "16d5:f000": { + "*": { + "t_vendor": 5845, + "t_product": [ 26115 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1726:1900": { + "*": { + "t_vendor": 5926, + "t_product": [ 4096 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1726:f00e": { + "*": { + "t_vendor": 5926, + "t_product": [ 40960 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1782:0003": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "1782:0023": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "198a:0003": { + "*": { + "t_vendor": 6538, + "t_product": [ 2 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0003": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0026": { + "*": { + "t_vendor": 6610, + "t_product": [ 115, 148, 338 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0033": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0040": { + "*": { + "t_vendor": 6610, + "t_product": [ 34 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0053": { + "*": { + "t_vendor": 6610, + "t_product": [ 49 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0083": { + ":uPr=WCDMA": { + "t_vendor": 6610, + "t_product": [ 292 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0090": { + "*": { + "t_vendor": 6610, + "t_product": [ 52 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0101": { + "*": { + "t_vendor": 6610, + "t_product": [ 260 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0103": { + "*": { + "t_vendor": 6610, + "t_product": [ 49 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0110": { + "*": { + "t_vendor": 6610, + "t_product": [ 289 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0115": { + "*": { + "t_vendor": 6610, + "t_product": [ 278 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0120": { + "*": { + "t_vendor": 6610, + "t_product": [ 121 ], + "detach_storage": false, + "mode": "StandardEject", + "msg": [ ], + "response": false, + "interface": 0 + } + }, + "19d2:0146": { + "*": { + "t_vendor": 6610, + "t_product": [ 322, 323 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0149": { + "*": { + "t_vendor": 6610, + "t_product": [ 292 ], + "mode": "StandardEject", + "msg": [ 33 ] + } + }, + "19d2:0150": { + "*": { + "t_vendor": 6610, + "t_product": [ 292 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0154": { + "*": { + "t_vendor": 6610, + "t_product": [ 23, 279, 8195 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0169": { + "*": { + "t_vendor": 6610, + "t_product": [ 368 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0198": { + "*": { + "t_vendor": 6610, + "t_product": [ 409 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0304": { + "*": { + "t_vendor": 6610, + "t_product": [ 841 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0318": { + "*": { + "t_vendor": 6610, + "t_product": [ 791, 816 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:0325": { + "*": { + "t_vendor": 6610, + "t_product": [ 806 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1001": { + "*": { + "t_vendor": 6610, + "t_product": [ 4098, 4099 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1007": { + "*": { + "t_vendor": 6610, + "t_product": [ 4104 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1009": { + "*": { + "t_vendor": 6610, + "t_product": [ 4112 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1013": { + "*": { + "t_vendor": 6610, + "t_product": [ 4117 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1017": { + "*": { + "t_vendor": 6610, + "t_product": [ 4120 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1019": { + "*": { + "t_vendor": 6610, + "t_product": [ 4129 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1020": { + "*": { + "t_vendor": 6610, + "t_product": [ 4129 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1022": { + "*": { + "t_vendor": 6610, + "t_product": [ 4131, 4132 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1026": { + "*": { + "t_vendor": 6610, + "t_product": [ 4135, 4136, 4137 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1030": { + "*": { + "t_vendor": 6610, + "t_product": [ 4145, 4146 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1034": { + "*": { + "t_vendor": 6610, + "t_product": [ 4149, 4150, 4151 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1038": { + "*": { + "t_vendor": 6610, + "t_product": [ 4153, 4160 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1042": { + "*": { + "t_vendor": 6610, + "t_product": [ 4163 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1046": { + "*": { + "t_vendor": 6610, + "t_product": [ 4167 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1171": { + "*": { + "t_vendor": 6610, + "t_product": [ 4467 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1175": { + "*": { + "t_vendor": 6610, + "t_product": [ 4471 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1179": { + "*": { + "t_vendor": 6610, + "t_product": [ 4481 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1201": { + "*": { + "t_vendor": 6610, + "t_product": [ 4611 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1207": { + "*": { + "t_vendor": 6610, + "t_product": [ 4616 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1210": { + "*": { + "t_vendor": 6610, + "t_product": [ 4625 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1216": { + "*": { + "t_vendor": 6610, + "t_product": [ 4631 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1219": { + "*": { + "t_vendor": 6610, + "t_product": [ 4640, 4642 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1224": { + "*": { + "t_vendor": 6610, + "t_product": [ 130 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1225": { + "*": { + "t_vendor": 6610, + "t_product": [ 5125 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1227": { + "*": { + "t_vendor": 6610, + "t_product": [ 4690 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1232": { + "*": { + "t_vendor": 6610, + "t_product": [ 4712, 8195 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1233": { + "*": { + "t_vendor": 6610, + "t_product": [ 4720 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1237": { + "*": { + "t_vendor": 6610, + "t_product": [ 23 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1238": { + "*": { + "t_vendor": 6610, + "t_product": [ 23 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1420": { + "*": { + "t_vendor": 6610, + "t_product": [ 5125 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1511": { + "*": { + "t_vendor": 6610, + "t_product": [ 5394 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1514": { + "*": { + "t_vendor": 6610, + "t_product": [ 5397 ], + "msg": [ 36 ] + } + }, + "19d2:1517": { + "*": { + "t_vendor": 6610, + "t_product": [ 5401 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1520": { + "*": { + "t_vendor": 6610, + "t_product": [ 322 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1523": { + "*": { + "t_vendor": 6610, + "t_product": [ 5413 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1528": { + "*": { + "t_vendor": 6610, + "t_product": [ 5415 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1536": { + "*": { + "t_vendor": 6610, + "t_product": [ 5431, 5432 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1542": { + "*": { + "t_vendor": 6610, + "t_product": [ 5444 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:1580": { + "*": { + "t_vendor": 6610, + "t_product": [ 5506 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:2004": { + "*": { + "t_vendor": 6610, + "t_product": [ 5122 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "19d2:ffde": { + "*": { + "t_vendor": 6610, + "t_product": [ 65501 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1a8d:1000": { + "*": { + "t_vendor": 6797, + "t_product": [ 4098, 4103, 4105, 4109, 8198 ], + "mode": "StandardEject", + "msg": [ ], + "release_delay": 4000, + "response": true + } + }, + "1a8d:2000": { + "*": { + "t_vendor": 6797, + "t_product": [ 8198 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1ab7:5700": { + "*": { + "t_vendor": 6839, + "t_product": [ 8192, 22321 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1bbb:000f": { + "*": { + "t_vendor": 7099, + "t_product": [ 15 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1bbb:011f": { + "*": { + "t_vendor": 7099, + "t_product": [ 262 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1bbb:022c": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "1c9e:9e08": { + "*": { + "t_vendor": 7326, + "t_product": [ 40472 ], + "mode": "Sierra", + "msg": [ ] + } + }, + "1d09:1000": { + "*": { + "t_vendor": 7433, + "t_product": [ 4112 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1d09:1021": { + "*": { + "t_vendor": 7433, + "t_product": [ 4112 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1d09:1025": { + "*": { + "t_vendor": 7433, + "t_product": [ 4134 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "1da5:f000": { + "*": { + "t_vendor": 7589, + "t_product": [ 17682 ], + "mode": "Qisda", + "msg": [ ] + } + }, + "1dbc:0669": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "1edf:6003": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "1fac:0032": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "1fac:0151": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "2001:00a6": { + "*": { + "t_vendor": 8193, + "t_product": [ 32002 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:00a7": { + "*": { + "t_vendor": 8193, + "t_product": [ 32014 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:7600": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "2001:a401": { + "*": { + "t_vendor": 8193, + "t_product": [ 32281 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a403": { + "*": { + "t_vendor": 8193, + "t_product": [ 32011, 32012 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a405": { + "*": { + "t_vendor": 8193, + "t_product": [ 32013 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a406": { + "*": { + "t_vendor": 8193, + "t_product": [ 32281 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a407": { + "*": { + "t_vendor": 8193, + "t_product": [ 32014 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a40d": { + "*": { + "t_vendor": 8193, + "t_product": [ 32312 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a706": { + "*": { + "t_vendor": 8193, + "t_product": [ 32001 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a707": { + "*": { + "t_vendor": 8193, + "t_product": [ 32002 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a708": { + "*": { + "t_vendor": 8193, + "t_product": [ 32003 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a805": { + "*": { + "t_vendor": 8193, + "t_product": [ 32274 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:a809": { + "*": { + "t_vendor": 8193, + "t_product": [ 30976 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2001:ab00": { + "*": { + "t_vendor": 8193, + "t_product": [ 32309 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2015:0001": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "201e:2009": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "2020:f00e": { + "*": { + "t_vendor": 8224, + "t_product": [ 4101, 4104 ], + "mode": "StandardEject", + "msg": [ ], + "wait": 2 + } + }, + "2020:f00f": { + "*": { + "t_vendor": 8224, + "t_product": [ 4101 ], + "mode": "StandardEject", + "msg": [ ], + "wait": 2 + } + }, + "2077:1000": { + "*": { + "t_vendor": 8311, + "t_product": [ 28673, 28688, 28689 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "20a6:f00a": { + "*": { + "t_vendor": 8358, + "t_product": [ 4096 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "20a6:f00e": { + "*": { + "t_vendor": 8358, + "t_product": [ 4357 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "20b9:1682": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "21f5:3010": { + "*": { + "t_vendor": 8693, + "t_product": [ 4353 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "22de:6801": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "22de:6802": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "22de:6803": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "22f4:0021": { + "*": { + "t_class": 255, + "mode": "StandardEject", + "msg": [ ] + } + }, + "230d:0001": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "230d:0003": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "230d:0007": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "230d:000b": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "230d:000c": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "230d:000d": { + "*": { + "msg": [ ], + "config": 3 + } + }, + "230d:0101": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "230d:0103": { + "*": { + "msg": [ ], + "config": 2 + } + }, + "2357:0200": { + "*": { + "t_vendor": 9047, + "t_product": [ 513 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "2357:f000": { + "*": { + "t_vendor": 9047, + "t_product": [ 36864 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "257a:a000": { + "*": { + "t_vendor": 9594, + "t_product": [ 5633, 5663, 5679, 9759, 9775, 13855, 13871 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "257a:b000": { + "*": { + "t_vendor": 9594, + "t_product": [ 5633, 5663, 5679, 9759, 9775, 13855, 13871 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "257a:c000": { + "*": { + "t_vendor": 9594, + "t_product": [ 5633, 5663, 5679, 9759, 9775, 13855, 13871 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "257a:d000": { + "*": { + "t_vendor": 9594, + "t_product": [ 5633, 5663, 5679, 9759, 9775, 13855, 13871 ], + "mode": "StandardEject", + "msg": [ ] + } + }, + "ed09:1021": { + "*": { + "t_vendor": 60681, + "t_product": [ 4112 ], + "mode": "StandardEject", + "msg": [ ] + } + } + } +} \ No newline at end of file diff --git a/ext-rooter-basic/files/lib/netifd/proto/3x.sh b/ext-rooter-basic/files/lib/netifd/proto/3x.sh new file mode 100644 index 0000000..99ec4e6 --- /dev/null +++ b/ext-rooter-basic/files/lib/netifd/proto/3x.sh @@ -0,0 +1,146 @@ +#!/bin/sh +INCLUDE_ONLY=1 + +. ../netifd-proto.sh +. ./ppp.sh +init_proto "$@" + +ROOTER=/usr/lib/rooter +ROOTER_LINK="/tmp/links" + +log() { + logger -t "Create PPP Connection" "$@" +} + +chcklog() { + OOX=$1 + CLOG=$(uci get modem.modeminfo$CURRMODEM.log) + if [ $CLOG = "1" ]; then + log "$OOX" + fi +} + +proto_3x_init_config() { + no_device=1 + available=1 + ppp_generic_init_config + proto_config_add_string "device" + proto_config_add_string "apn" + proto_config_add_string "service" + proto_config_add_string "pincode" + proto_config_add_string "dialnumber" +} + +proto_3x_setup() { + local interface="$1" + local chat + + if [ ! -f /tmp/bootend.file ]; then + return 0 + fi + + CURRMODEM=${interface:3} + uci set modem.modem$CURRMODEM.connected=0 + uci commit modem + killall -9 getsignal$CURRMODEM + rm -f $ROOTER_LINK/getsignal$CURRMODEM + killall -9 con_monitor$CURRMODEM + rm -f $ROOTER_LINK/con_monitor$CURRMODEM + + json_get_var device device + json_get_var apn apn + json_get_var service service + json_get_var pincode pincode + + [ -e "$device" ] || { + proto_set_available "$interface" 0 + return 1 + } + + if [ $service = "umts" ]; then + idV=$(uci get modem.modem$CURRMODEM.idV) + if [ $idV = 12d1 ]; then + $ROOTER/gcom/gcom-locked "$device" "curc.gcom" "$CURRMODEM" + log "Unsolicited Responses Disabled" + fi + chat="/etc/chatscripts/3g.chat" + if [ -n "$pincode" ]; then + OX=$($ROOTER/gcom/gcom-locked "$device" "setpin.gcom" "$CURRMODEM") + ERROR="ERROR" + if `echo ${OX} | grep "${ERROR}" 1>/dev/null 2>&1` + then + log "Modem $CURRMODEM Failed to Unlock SIM Pin" + chcklog "$OX" + $ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Failed to Connect : Pin Locked" + proto_notify_error "$interface" PIN_FAILED + proto_block_restart "$interface" + return 1 + fi + fi + export SETUSER=$(uci get modem.modeminfo$CURRMODEM.user) + export SETPASS=$(uci get modem.modeminfo$CURRMODEM.pass) + export SETAUTH=$(uci get modem.modeminfo$CURRMODEM.auth) + OX=$($ROOTER/gcom/gcom-locked "$device" "connect-ppp.gcom" "$CURRMODEM") + ERROR="ERROR" + if `echo ${OX} | grep "${ERROR}" 1>/dev/null 2>&1` + then + log "Error for Modem $CURRMODEM on Authorization" + chcklog "$OX" + fi + + if [ -z "$dialnumber" ]; then + dialnumber="*99***1#" + fi + else + chat="/etc/chatscripts/evdo.chat" + fi + + connect="${apn:+USE_APN=$apn }DIALNUMBER=$dialnumber /usr/sbin/chat -t5 -v -E -f $chat" + + ppp_generic_setup "$interface" \ + noaccomp \ + nopcomp \ + novj \ + nobsdcomp \ + noauth \ + lock \ + crtscts \ + 115200 "$device" + + sleep 20 + MAN=$(uci get modem.modem$CURRMODEM.manuf) + MOD=$(uci get modem.modem$CURRMODEM.model) + $ROOTER/log/logger "Modem #$CURRMODEM Connected ($MAN $MOD)" + PROT=$(uci get modem.modem$CURRMODEM.proto) + if [ $service = "umts" ]; then + ln -s $ROOTER/signal/modemsignal.sh $ROOTER_LINK/getsignal$CURRMODEM + $ROOTER_LINK/getsignal$CURRMODEM $CURRMODEM $PROT & + fi + ln -s $ROOTER/connect/reconnect-ppp.sh $ROOTER_LINK/reconnect$CURRMODEM + ln -s $ROOTER/connect/conmon.sh $ROOTER_LINK/con_monitor$CURRMODEM + $ROOTER_LINK/con_monitor$CURRMODEM $CURRMODEM & + uci set modem.modem$CURRMODEM.connected=1 + uci set modem.modem$CURRMODEM.interface="3x-"$interface + uci commit modem + CLB=$(uci get modem.modeminfo$CURRMODEM.lb) + if [ -e /etc/config/mwan3 ]; then + ENB=$(uci get mwan3.wan$CURRMODEM.enabled) + if [ ! -z $ENB ]; then + if [ $CLB = "1" ]; then + uci set mwan3.wan$CURRMODEM.enabled=1 + else + uci set mwan3.wan$CURRMODEM.enabled=0 + fi + uci commit mwan3 + /usr/sbin/mwan3 restart + fi + fi + rm -f /tmp/usbwait + return 0 +} + +proto_3x_teardown() { + proto_kill_command "$interface" +} + +add_protocol 3x diff --git a/ext-rooter-basic/files/lib/upgrade/keep.d/cronfiles b/ext-rooter-basic/files/lib/upgrade/keep.d/cronfiles new file mode 100644 index 0000000..16cdff4 --- /dev/null +++ b/ext-rooter-basic/files/lib/upgrade/keep.d/cronfiles @@ -0,0 +1,2 @@ +/etc/cronuser +/etc/cronbase diff --git a/ext-rooter-basic/files/usr/bin/set_gpio b/ext-rooter-basic/files/usr/bin/set_gpio new file mode 100644 index 0000000..5fddfa7 --- /dev/null +++ b/ext-rooter-basic/files/usr/bin/set_gpio @@ -0,0 +1,18 @@ +#!/bin/sh + +PIN=$1 +VALUE=$2 + +PIN_FILE=/sys/class/gpio/gpio$PIN + +if [ -z "$PIN" -o -z "$VALUE" ]; then + exit 1 +fi + +echo $PIN >/sys/class/gpio/export + +if [ $(cat $PIN_FILE/direction) = "out" ]; then + echo $VALUE >$PIN_FILE/value +fi + +echo $PIN >/sys/class/gpio/unexport \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/controller/admin/modem.lua b/ext-rooter-basic/files/usr/lib/lua/luci/controller/admin/modem.lua new file mode 100644 index 0000000..ee242e9 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/controller/admin/modem.lua @@ -0,0 +1,373 @@ +module("luci.controller.admin.modem", package.seeall) + +function index() + entry({"admin", "modem"}, firstchild(), "Modem", 35).dependent=false + entry({"admin", "modem", "cinfo"}, cbi("rooter/connection", {autoapply=true}), "Connection Info", 1) + entry({"admin", "modem", "conmon"}, cbi("rooter/connmonitor"), "Connection Monitoring", 20) + entry({"admin", "modem", "nets"}, template("rooter/net_status"), "Network Status", 30) + entry({"admin", "modem", "debug"}, template("rooter/debug"), "Debug Information", 50) + entry({"admin", "modem", "cust"}, cbi("rooter/customize"), "Custom Modem Ports", 55) + entry({"admin", "modem", "log"}, template("rooter/log"), "Connection Log", 60) + entry({"admin", "modem", "misc"}, template("rooter/misc"), "Miscellaneous", 40) + + entry({"admin", "modem", "get_csq"}, call("action_get_csq")) + entry({"admin", "modem", "change_port"}, call("action_change_port")) + entry({"admin", "modem", "change_mode"}, call("action_change_mode")) + entry({"admin", "modem", "change_modem"}, call("action_change_modem")) + entry({"admin", "modem", "change_modemdn"}, call("action_change_modemdn")) + entry({"admin", "modem", "change_misc"}, call("action_change_misc")) + entry({"admin", "modem", "change_miscdn"}, call("action_change_miscdn")) + entry({"admin", "modem", "get_log"}, call("action_get_log")) + entry({"admin", "modem", "check_misc"}, call("action_check_misc")) + entry({"admin", "modem", "pwrtoggle"}, call("action_pwrtoggle")) + entry({"admin", "modem", "disconnect"}, call("action_disconnect")) + entry({"admin", "modem", "connect"}, call("action_connect")) + entry({"admin", "modem", "get_atlog"}, call("action_get_atlog")) + entry({"admin", "modem", "send_atcmd"}, call("action_send_atcmd")) + entry({"admin", "modem", "change_rate"}, call("action_change_rate")) + entry({"admin", "modem", "change_phone"}, call("action_change_phone")) + entry({"admin", "modem", "clear_log"}, call("action_clear_log")) + entry({"admin", "modem", "externalip"}, call("action_externalip")) +end + +function trim(s) + return (s:gsub("^%s*(.-)%s*$", "%1")) +end + +function action_get_atlog() + local file + local rv ={} + + file = io.open("/tmp/atlog", "r") + if file ~= nil then + local tmp = file:read("*all") + rv["log"] = tmp + file:close() + else + rv["log"] = "No entries in log file" + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_get_log() + local file + local rv ={} + + file = io.open("/usr/lib/rooter/log/connect.log", "r") + if file ~= nil then + local tmp = file:read("*all") + rv["log"] = tmp + file:close() + else + rv["log"] = "No entries in log file" + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_disconnect() + local set = luci.http.formvalue("set") + os.execute("/usr/lib/rooter/connect/disconnect.sh") +end + +function action_connect() + local set = luci.http.formvalue("set") + miscnum = luci.model.uci.cursor():get("modem", "general", "miscnum") + os.execute("/tmp/links/reconnect" .. miscnum .. " " .. miscnum) +end + +function action_pwrtoggle() + local set = luci.http.formvalue("set") + os.execute("/usr/lib/rooter/pwrtoggle.sh " .. set) +end + +function action_send_atcmd() + local rv ={} + modnum = luci.model.uci.cursor():get("modem", "general", "modemnum") + local file + local set = luci.http.formvalue("set") + fixed = string.gsub(set, "\"", "~") + os.execute("/usr/lib/rooter/luci/atcmd.sh \'" .. fixed .. "\'") + + result = "/tmp/result" .. modnum .. ".at" + file = io.open(result, "r") + if file ~= nil then + rv["result"] = file:read("*all") + file:close() + os.execute("/usr/lib/rooter/luci/luaops.sh delete /tmp/result" .. modnum .. ".at") + else + rv["result"] = " " + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_check_misc() + local rv ={} + local file + local active + local connect + + miscnum = luci.model.uci.cursor():get("modem", "general", "miscnum") + conn = "Modem #" .. miscnum + rv["conntype"] = conn + empty = luci.model.uci.cursor():get("modem", "modem" .. miscnum, "empty") + if empty == "1" then + active = "0" + rv["netmode"] = "-" + else + active = luci.model.uci.cursor():get("modem", "modem" .. miscnum, "active") + if active == "1" then + connect = luci.model.uci.cursor():get("modem", "modem" .. miscnum, "connected") + if connect == "0" then + active = "1" + else + active = "2" + end + end + netmode = luci.model.uci.cursor():get("modem", "modem" .. miscnum, "netmode") + rv["netmode"] = netmode + end + rv["active"] = active + file = io.open("/tmp/gpiopin", "r") + if file == nil then + rv.gpio = "0" + else + rv.gpio = "1" + line = file:read("*line") + line = file:read("*line") + if line ~= nil then + rv.gpio = "2" + end + file:close() + end + file = io.open("/sys/bus/usb/drivers/usb/usb1", "r") + if file == nil then + rv["usb"] = "0" + else + io.close(file) + rv["usb"] = "1" + end + file = io.open("/sys/bus/usb/drivers/usb/usb2", "r") + if file ~= nill then + io.close(file) + rv["usb"] = "2" + end + proto = luci.model.uci.cursor():get("modem", "modem" .. miscnum, "proto") + rv["proto"] = proto + + celltype = luci.model.uci.cursor():get("modem", "modem" .. miscnum, "celltype") + rv["celltype"] = celltype + cmode = luci.model.uci.cursor():get("modem", "modem" .. miscnum, "cmode") + if cmode == "0" then + rv["netmode"] = "10" + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function lshift(x, by) + return x * 2 ^ by +end + +function rshift(x, by) + return math.floor(x / 2 ^ by) +end + +function action_change_mode() + local set = tonumber(luci.http.formvalue("set")) + local modemtype = rshift(set, 4) + local temp = lshift(modemtype, 4) + local netmode = set - temp + os.execute("/usr/lib/rooter/luci/modechge.sh " .. modemtype .. " " .. netmode) +end + +function action_change_port() + local set = tonumber(luci.http.formvalue("set")) + if set ~= nil and set > 0 then + if set == 1 then + os.execute("/usr/lib/rooter/luci/portchge.sh dwn") + else + os.execute("/usr/lib/rooter/luci/portchge.sh up") + end + end +end + +function action_change_misc() + os.execute("/usr/lib/rooter/luci/modemchge.sh misc 1") +end + +function action_change_miscdn() + os.execute("/usr/lib/rooter/luci/modemchge.sh misc 0") +end + +function action_change_modem() + os.execute("/usr/lib/rooter/luci/modemchge.sh modem 1") +end + +function action_change_modemdn() + os.execute("/usr/lib/rooter/luci/modemchge.sh modem 0") +end + +function action_get_csq() + modnum = luci.model.uci.cursor():get("modem", "general", "modemnum") + local file + stat = "/tmp/status" .. modnum .. ".file" + file = io.open(stat, "r") + + local rv ={} + + rv["port"] = file:read("*line") + rv["csq"] = file:read("*line") + rv["per"] = file:read("*line") + rv["rssi"] = file:read("*line") + rv["modem"] = file:read("*line") + rv["cops"] = file:read("*line") + rv["mode"] = file:read("*line") + rv["lac"] = file:read("*line") + rv["lacn"] = file:read("*line") + rv["cid"] = file:read("*line") + rv["cidn"] = file:read("*line") + rv["mcc"] = file:read("*line") + rv["mnc"] = file:read("*line") + rv["rnc"] = file:read("*line") + rv["rncn"] = file:read("*line") + rv["down"] = file:read("*line") + rv["up"] = file:read("*line") + rv["ecio"] = file:read("*line") + rv["rscp"] = file:read("*line") + rv["ecio1"] = file:read("*line") + rv["rscp1"] = file:read("*line") + rv["netmode"] = file:read("*line") + rv["cell"] = file:read("*line") + rv["modtype"] = file:read("*line") + rv["conntype"] = file:read("*line") + rv["channel"] = file:read("*line") + rv["phone"] = file:read("*line") + file:read("*line") + rv["lband"] = file:read("*line") + + file:close() + + cmode = luci.model.uci.cursor():get("modem", "modem" .. modnum, "cmode") + if cmode == "0" then + rv["netmode"] = "10" + end + + rssi = rv["rssi"] + ecio = rv["ecio"] + rscp = rv["rscp"] + ecio1 = rv["ecio1"] + rscp1 = rv["rscp1"] + + if ecio == nil then + ecio = "-" + end + if ecio1 == nil then + ecio1 = "-" + end + if rscp == nil then + rscp = "-" + end + if rscp1 == nil then + rscp1 = "-" + end + + if ecio ~= "-" then + rv["ecio"] = ecio .. " dB" + end + if rscp ~= "-" then + rv["rscp"] = rscp .. " dBm" + end + if ecio1 ~= " " then + rv["ecio1"] = " (" .. ecio1 .. " dB)" + end + if rscp1 ~= " " then + rv["rscp1"] = " (" .. rscp1 .. " dBm)" + end + + if not nixio.fs.access("/etc/netspeed") then + rv["crate"] = "Fast (updated every 10 seconds)" + else + rv["crate"] = "Slow (updated every 60 seconds)" + end + + stat = "/tmp/msimdata" .. modnum + file = io.open(stat, "r") + if file == nil then + rv["modid"] = " " + rv["imei"] = " " + rv["imsi"] = " " + rv["iccid"] = " " + rv["host"] = "0" + else + rv["modid"] = file:read("*line") + rv["imei"] = file:read("*line") + rv["imsi"] = file:read("*line") + rv["iccid"] = file:read("*line") + rv["host"] = file:read("*line") + file:close() + end + stat = "/tmp/msimnum" .. modnum + file = io.open(stat, "r") + if file == nil then + rv["phone"] = "-" + rv["phonen"] = " " + else + rv["phone"] = file:read("*line") + rv["phonen"] = file:read("*line") + file:close() + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_change_rate() + local set = luci.http.formvalue("set") + if set == "1" then + os.execute("rm -f /etc/netspeed") + else + os.execute("echo \"0\" > /etc/netspeed") + end +end + +function action_change_phone() + local set = luci.http.formvalue("set") + s, e = string.find(set, "|") + pno = string.sub(set, 1, s-1) + pnon = string.sub(set, e+1) + modnum = luci.model.uci.cursor():get("modem", "general", "modemnum") + os.execute("/usr/lib/rooter/common/phone.sh " .. modnum .. " " .. pno .. " \"" .. pnon .. "\"") +end + +function action_clear_log() + local file + file = io.open("/usr/lib/rooter/log/connect.log", "w") + file:close() + os.execute("/usr/lib/rooter/log/logger 'Connection Log Cleared by User'") +end + +function action_externalip() + local rv ={} + + os.execute("rm -f /tmp/ipip; wget -O /tmp/ipip http://ipecho.net/plain > /dev/null 2>&1") + file = io.open("/tmp/ipip", "r") + if file == nil then + rv["extip"] = "Not Available" + else + rv["extip"] = file:read("*line") + if rv["extip"] == nil then + rv["extip"] = "Not Available" + end + file:close() + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/controller/guestwifi.lua b/ext-rooter-basic/files/usr/lib/lua/luci/controller/guestwifi.lua new file mode 100644 index 0000000..3c58cda --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/controller/guestwifi.lua @@ -0,0 +1,8 @@ +module("luci.controller.guestwifi", package.seeall) + +function index() + local page + + page = entry({"admin", "network", "guestwifi"}, cbi("guestwifi", {hidesavebtn=true, hideresetbtn=true}), "Guest Wifi", 22) + page.dependent = true +end diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/controller/poweroff.lua b/ext-rooter-basic/files/usr/lib/lua/luci/controller/poweroff.lua new file mode 100644 index 0000000..4f53b35 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/controller/poweroff.lua @@ -0,0 +1,13 @@ +module("luci.controller.poweroff", package.seeall) + +function index() + local page + page = entry({"admin", "system", "poweroff"}, template("admin_system/poweroff"), _("System Stop"), 95) + entry({"admin", "system", "do_poweroff"}, call("action_poweroff")) + page.dependent = true +end + +function action_poweroff() + local set = luci.http.formvalue("set") + os.execute("poweroff") +end diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/controller/update.lua b/ext-rooter-basic/files/usr/lib/lua/luci/controller/update.lua new file mode 100644 index 0000000..c929ce2 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/controller/update.lua @@ -0,0 +1,59 @@ +module("luci.controller.update", package.seeall) + +function index() + local page + + page = entry({"admin", "status", "update"}, template("rooter/update"), _("ROOter Firmware Update")) + page = entry({"admin", "status", "get_ver"}, call("action_get_ver")) + page = entry({"admin", "status", "get_change"}, call("action_get_change")) + page.dependent = true +end + +function read_log() + local file = io.open("/tmp/change.file", "r") + if file ~= nil then + ret = file:read("*all") + file:close() + else + ret = "

*************************

No Change Log Found

*************************

" + end + return ret +end + +function action_get_ver() + local rv ={} + + rv["current"] = luci.model.uci.cursor():get("modem", "Version", "ver") + rv["last"] = luci.model.uci.cursor():get("modem", "Version", "last") + if rv["last"] == nil then + rv["last"] = "Not Checked" + end + + rv["log"] = read_log() + rv["status"] = " " + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_get_change() + local rv ={} + + local err = os.execute("/usr/lib/rooter/luci/getlog.sh") + + rv["current"] = luci.model.uci.cursor():get("modem", "Version", "ver") + rv["last"] = luci.model.uci.cursor():get("modem", "Version", "last") + if rv["last"] == nil then + rv["last"] = "Not Checked" + end + + rv["log"] = read_log() + if err == 0 then + rv["status"] = " " + else + rv["status"] = "An Error occured while fetching the Change Log" + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/guestwifi.lua b/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/guestwifi.lua new file mode 100644 index 0000000..35917a5 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/guestwifi.lua @@ -0,0 +1,81 @@ +local utl = require "luci.util" + +m = Map("guestwifi", "Create a Guest Wifi Network", + translate("Create a Guest Wifi Network with Optional Bandwidth Speed Limiting")) + +m.on_after_commit = function(self) + x = uci.cursor() + sid = x:get("guestwifi", "guestwifi", "ssid") + str = string.gsub(sid, "%s+", "_") + x:set("guestwifi", "guestwifi", "ssid", str) + x:commit("guestwifi") + luci.sys.call("/usr/lib/rooter/luci/guestwifi.sh &") +end + +gw = m:section(TypedSection, "guestwifi", translate("Guest Wifi Information")) +gw.anonymous = true + +luci.sys.call("/usr/lib/rooter/luci/wifiradio.sh") + +radio = gw:option(ListValue, "radio", translate("Wifi Radio")) +radio.rmempty = true +local file = io.open("/tmp/wifi-device", "r") +if file ~= nil then + ix=0 + repeat + local line = file:read("*line") + if line == nil then + break + end + if ix == 0 then + radio.default=line + end + ix=1 + radio:value(line) + until 1==0 + file:close() +end + +--gw1 = m:section(TypedSection, "guestwifi", translate("Guest Network Information")) +--gw1.anonymous = true + +ssid = gw:option(Value, "ssid", translate("Network Name :")); +ssid.optional=false; +ssid.rmempty = true; +ssid.default="guest" + +ip = gw:option(Value, "ip", translate("Network IP Address :"), translate("Must be different subnet than router")); +ip.rmempty = true; +ip.optional=false; +ip.default="192.168.3.1"; +ip.datatype = "ipaddr" + +file = io.open("/etc/config/sqm", "r") +if file ~= nil then + file:close() +-- gw2 = m:section(TypedSection, "guestwifi", translate("Bandwidth Speed Limiting")) +-- gw2.anonymous = true + bl = gw:option(ListValue, "limit", "Enable Bandwidth Speed Limiting :"); + bl:value("0", "Disable") + bl:value("1", "Enable") + bl.default=0 + + dl = gw:option(Value, "dl", "Download Speed (kbit/s) :"); + dl.optional=false; + dl.rmempty = true; + dl.datatype = "and(uinteger,min(1))" + dl:depends("limit", "1") + dl.default=1024 + + ul = gw:option(Value, "ul", "Upload Speed (kbit/s) :"); + ul.optional=false; + ul.rmempty = true; + ul.datatype = "and(uinteger,min(1))" + ul:depends("limit", "1") + ul.default=128 +else + gw2 = m:section(TypedSection, "guestwifi", translate("Bandwidth Speed Limiting Not Supported")) + gw2.anonymous = true +end + +return m diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/connection.lua b/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/connection.lua new file mode 100644 index 0000000..02cd0c5 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/connection.lua @@ -0,0 +1,122 @@ +local utl = require "luci.util" + +local maxmodem = luci.model.uci.cursor():get("modem", "general", "max") + +m = Map("modem", translate("Modem Connection Information"), translate("Please fill out the entries below")) + +m.on_after_commit = function(self) + --luci.sys.call("/etc/modpwr") +end + +-- +-- Individual Modems +-- + +di = {} +ma = {} +mu = {} +mp = {} +mpi = {} +mau = {} +mf = {} +md = {} +ml = {} +mcc = {} +mnc = {} +mdns1 = {} +mdns2 = {} +mlog = {} +mlb = {} +matc = {} +mat = {} + +for i=1,maxmodem do + stri = string.format("%d", i) + minfo = "minfo" .. stri + di[i] = m:section(TypedSection, minfo, "Modem " .. stri .. " Information") + ma[i] = di[i]:option(Value, "apn", "APN :"); + ma[i].rmempty = true; +-- ma[i].default = "modem" .. stri .. "apn" + + mu[i] = di[i]:option(Value, "user", "User Name :"); + mu[i].optional=false; + mu[i].rmempty = true; + + mp[i] = di[i]:option(Value, "passw", "Password :"); + mp[i].optional=false; + mp[i].rmempty = true; + mp[i].password = true + + mpi[i] = di[i]:option(Value, "pincode", "PIN :"); + mpi[i].optional=false; + mpi[i].rmempty = true; + + mau[i] = di[i]:option(ListValue, "auth", "Authentication Protocol :") + mau[i]:value("0", "None") + mau[i]:value("1", "PAP") + mau[i]:value("2", "CHAP") + mau[i].default = "0" + + mf[i] = di[i]:option(ListValue, "ppp", "Force Modem to PPP Protocol :"); + mf[i]:value("0", "No") + mf[i]:value("1", "Yes") + mf[i].default=0 + + md[i] = di[i]:option(Value, "delay", "Connection Delay in Seconds :"); + md[i].optional=false; + md[i].rmempty = false; + md[i].default = 5 + md[i].datatype = "and(uinteger,min(1))" + + ml[i] = di[i]:option(ListValue, "lock", "Lock Connection to a Provider :"); + ml[i]:value("0", "No") + ml[i]:value("1", "Yes") + ml[i].default=0 + + mcc[i] = di[i]:option(Value, "mcc", "Provider Country Code :"); + mcc[i].optional=false; + mcc[i].rmempty = true; + mcc[i].datatype = "and(uinteger,min(1),max(999))" + mcc[i]:depends("lock", "1") + + mnc[i] = di[i]:option(Value, "mnc", "Provider Network Code :"); + mnc[i].optional=false; + mnc[i].rmempty = true; + mnc[i].datatype = "and(uinteger,min(1),max(999))" + mnc[i]:depends("lock", "1") + + mdns1[i] = di[i]:option(Value, "dns1", "Custom DNS Server1 :"); + mdns1[i].rmempty = true; + mdns1[i].optional=false; + mdns1[i].datatype = "ipaddr" + + mdns2[i] = di[i]:option(Value, "dns2", "Custom DNS Server2 :"); + mdns2[i].rmempty = true; + mdns2[i].optional=false; + mdns2[i].datatype = "ipaddr" + + mlog[i] = di[i]:option(ListValue, "log", "Enable Connection Logging :"); + mlog[i]:value("0", "No") + mlog[i]:value("1", "Yes") + mlog[i].default=0 + + if nixio.fs.access("/etc/config/mwan3") then + mlb[i] = di[i]:option(ListValue, "lb", "Enable Load Balancing at Connection :"); + mlb[i]:value("0", "No") + mlb[i]:value("1", "Yes") + mlb[i].default=0 + end + + mat[i] = di[i]:option(ListValue, "at", "Enable Custom AT Startup Command at Connection :"); + mat[i]:value("0", "No") + mat[i]:value("1", "Yes") + mat[i].default=0 + + matc[i] = di[i]:option(Value, "atc", "Custom AT Startup Command :"); + matc[i].optional=false; + matc[i].rmempty = true; + --matc[i]:depends("at", "1") + +end + +return m \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/connmonitor.lua b/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/connmonitor.lua new file mode 100644 index 0000000..dfb927b --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/connmonitor.lua @@ -0,0 +1,151 @@ +local utl = require "luci.util" + +local maxmodem = luci.model.uci.cursor():get("modem", "general", "max") + +m = Map("modem", translate("Modem Connection Monitoring"), translate("Use Pinging to keep the Modem Connection Working")) + +m.on_after_commit = function(self) + --luci.sys.call("/etc/monitor") + --luci.sys.call("/etc/pingchk") +end + +d = {} +c1 = {} +reliability = {} +interval = {} +timeout = {} +cb2 = {} +down = {} +up = {} +count = {} +packetsize = {} + +for i=1,maxmodem do + stri = string.format("%d", i) + pinfo = "pinfo" .. stri + d[i] = m:section(TypedSection, pinfo, "Modem " .. stri .. " Ping Details") + + c1[i] = d[i]:option(ListValue, "alive", "MONITOR WITH OPTIONAL RECONNECT :"); + c1[i]:value("0", "Disabled") + c1[i]:value("1", "Enabled") + c1[i]:value("2", "Enabled with Router Reboot") + c1[i]:value("3", "Enabled with Modem Reconnect") + c1[i]:value("4", "Enabled with Power Toggle or Modem Reconnect") + c1[i].default=0 + + reliability[i] = d[i]:option(Value, "reliability", translate("Tracking reliability"), + translate("Acceptable values: 1-100. This many Tracking IP addresses must respond for the link to be deemed up")) + reliability[i].datatype = "range(1, 100)" + reliability[i].default = "1" + reliability[i]:depends("alive", "1") + reliability[i]:depends("alive", "2") + reliability[i]:depends("alive", "3") + reliability[i]:depends("alive", "4") + + count[i] = d[i]:option(ListValue, "count", translate("Ping count")) + count[i].default = "1" + count[i]:value("1") + count[i]:value("2") + count[i]:value("3") + count[i]:value("4") + count[i]:value("5") + count[i]:depends("alive", "1") + count[i]:depends("alive", "2") + count[i]:depends("alive", "3") + count[i]:depends("alive", "4") + + interval[i] = d[i]:option(ListValue, "pingtime", translate("Ping interval"), + translate("Amount of time between tracking tests")) + interval[i].default = "5" + interval[i]:value("5", translate("5 seconds")) + interval[i]:value("10", translate("10 seconds")) + interval[i]:value("20", translate("20 seconds")) + interval[i]:value("30", translate("30 seconds")) + interval[i]:value("60", translate("1 minute")) + interval[i]:value("180", translate("3 minute")) + interval[i]:value("300", translate("5 minutes")) + interval[i]:value("600", translate("10 minutes")) + interval[i]:value("900", translate("15 minutes")) + interval[i]:value("1800", translate("30 minutes")) + interval[i]:value("3600", translate("1 hour")) + interval[i]:depends("alive", "1") + interval[i]:depends("alive", "2") + interval[i]:depends("alive", "3") + interval[i]:depends("alive", "4") + + timeout[i] = d[i]:option(ListValue, "pingwait", translate("Ping timeout")) + timeout[i].default = "2" + timeout[i]:value("1", translate("1 second")) + timeout[i]:value("2", translate("2 seconds")) + timeout[i]:value("3", translate("3 seconds")) + timeout[i]:value("4", translate("4 seconds")) + timeout[i]:value("5", translate("5 seconds")) + timeout[i]:value("6", translate("6 seconds")) + timeout[i]:value("7", translate("7 seconds")) + timeout[i]:value("8", translate("8 seconds")) + timeout[i]:value("9", translate("9 seconds")) + timeout[i]:value("10", translate("10 seconds")) + timeout[i]:depends("alive", "1") + timeout[i]:depends("alive", "2") + timeout[i]:depends("alive", "3") + timeout[i]:depends("alive", "4") + + packetsize[i] = d[i]:option(Value, "packetsize", translate("Ping packet size in bytes"), + translate("Acceptable values: 4-56. Number of data bytes to send in ping packets. This may have to be adjusted for certain ISPs")) + packetsize[i].datatype = "range(4, 56)" + packetsize[i].default = "56" + packetsize[i]:depends("alive", "1") + packetsize[i]:depends("alive", "2") + packetsize[i]:depends("alive", "3") + packetsize[i]:depends("alive", "4") + + down[i] = d[i]:option(ListValue, "down", translate("Interface down"), + translate("Interface will be deemed down after this many failed ping tests")) + down[i].default = "3" + down[i]:value("1") + down[i]:value("2") + down[i]:value("3") + down[i]:value("4") + down[i]:value("5") + down[i]:value("6") + down[i]:value("7") + down[i]:value("8") + down[i]:value("9") + down[i]:value("10") + down[i]:depends("alive", "1") + down[i]:depends("alive", "2") + down[i]:depends("alive", "3") + down[i]:depends("alive", "4") + + up[i] = d[i]:option(ListValue, "up", translate("Interface up"), + translate("Downed interface will be deemed up after this many successful ping tests")) + up[i].default = "3" + up[i]:value("1") + up[i]:value("2") + up[i]:value("3") + up[i]:value("4") + up[i]:value("5") + up[i]:value("6") + up[i]:value("7") + up[i]:value("8") + up[i]:value("9") + up[i]:value("10") + up[i]:depends("alive", "1") + up[i]:depends("alive", "2") + up[i]:depends("alive", "3") + up[i]:depends("alive", "4") + + cb2[i] = d[i]:option(DynamicList, "trackip", translate("Tracking IP"), + translate("This IP address will be pinged to dermine if the link is up or down.")) + cb2[i].datatype = "ipaddr" + cb2[i].default="8.8.8.8" + cb2[i]:depends("alive", "1") + cb2[i]:depends("alive", "2") + cb2[i]:depends("alive", "3") + cb2[i]:depends("alive", "4") + cb2[i].optional=false; + +end + +return m + diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/customize.lua b/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/customize.lua new file mode 100644 index 0000000..5780cab --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/customize.lua @@ -0,0 +1,156 @@ +local utl = require "luci.util" + +local modemfile = "/etc/config/modem.data" +local modemdata = {} +local count +local tabdata = {} + +function process_line(xline, cnt) + local data = {} + local pline = xline + local start = 1 + for i=1,3 do + s, e = string.find(pline, " ") + data[i] = string.sub(pline, start, s-1) + pline = string.sub(pline, e+1) + end + data[4] = pline + modemdata[cnt] = data +end + +function read_modem() + count = 0 + local file = io.open(modemfile, "r") + if file == nil then + return + end + repeat + local line = file:read("*line") + if line == nil then + break + end + if string.len(line) < 5 then + break + end + count = count + 1 + process_line(line, count) + until 1==0 + file:close() +end + +function insert_modem() + local location = count + 1 + + if count > 0 then + for j=1,count do + local mdata = modemdata[j] + if mdata[1] == tabdata[1] and mdata[2] == tabdata[2] then + location = j + end + end + end + + if count == 0 then + count = 1 + else + if location > count then + count = count + 1 + end + end + modemdata[location] = tabdata +end + +function write_modem() + os.remove(modemfile) + local file = io.open(modemfile, "w") + for k=1,count do + local mdata = modemdata[k] + for l=1,3 do + file:write(mdata[l], " ") + end + file:write(mdata[4], "\n") + end + file:close() +end + +function process_tabdata() + if tabdata[1] ~= "nil" and tabdata[2] ~= "nil" then + read_modem() + insert_modem() + write_modem() + end +end + +m = Map("modem", translate("Custom Modem Support"), translate("Change the Data and Communication Ports used by a Specific Modem")) + +m.on_after_commit = function(self) + -- all written config names are in self.parsechain + local sobj + for _, sobj in ipairs(self.children) do + local sids + if utl.instanceof(sobj, NamedSection) then + sids = { sobj.section } + elseif utl.instanceof(sobj, TypedSection) then + sids = sobj:cfgsections() + end + local sid, fld, fln + + if not utl.instanceof(sobj, SimpleSection) then + for _, sid in ipairs(sids) do + for fln, fld in ipairs(sobj.children) do + local val = fld:formvalue(sid) + if val == nil or string.len(val) == 0 then + val = "nil" + end + tabdata[fln] = val + end + end + end + end + process_tabdata() +end + +-- +-- Vid Pid Dataport Commport +-- + +e = m:section(TypedSection, "new", "Modem Ports") + +a1 = e:option(Value, "vid", "Switched Vendor ID :"); +a1.optional=false; + +b1 = e:option(Value, "pid", "Switched Product ID :"); +b1.optional=false; + +p3 = e:option(ListValue, "port", "PPP Modem Data Port :") +p3:value("tty", "default") +p3:value("tty0", "/dev/ttyUSB0") +p3:value("tty1", "/dev/ttyUSB1") +p3:value("tty2", "/dev/ttyUSB2") +p3:value("tty3", "/dev/ttyUSB3") +p3:value("tty4", "/dev/ttyUSB4") +p3:value("tty5", "/dev/ttyUSB5") +p3.default = "tty" + +p4 = e:option(ListValue, "comm", "Communication Port :") +p4:value("tty", "default") +p4:value("tty0", "/dev/ttyUSB0") +p4:value("tty1", "/dev/ttyUSB1") +p4:value("tty2", "/dev/ttyUSB2") +p4:value("tty3", "/dev/ttyUSB3") +p4:value("tty4", "/dev/ttyUSB4") +p4:value("tty5", "/dev/ttyUSB5") +p4.default = "tty" + +b3 = e:option(DummyValue, "blank", " "); + +btn = e:option(Button, "_btn", translate(" ")) +btn.inputtitle = translate("Delete Modem Port Database") +btn.inputstyle = "apply" +function btn.write() + luci.sys.call("/usr/lib/rooter/luci/luaops.sh delete /etc/config/modem.data") +end + +m:section(SimpleSection).template = "rooter/custom" + +return m \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/profiles.lua b/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/profiles.lua new file mode 100644 index 0000000..452cfea --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/model/cbi/rooter/profiles.lua @@ -0,0 +1,458 @@ +local utl = require "luci.util" + +local maxmodem = luci.model.uci.cursor():get("modem", "general", "max") + +m = Map("modem", translate("Modem Connection Profiles"), + translate("Create Profiles used to provide information at connection time")) + +m.on_after_commit = function(self) + --luci.sys.call("/etc/modpwr") +end + +-- +-- Default profile +-- + +di = m:section(TypedSection, "default", translate("Default Profile")) +di.anonymous = true +di:tab("default", translate("General")) +di:tab("advance", translate("Advanced")) +di:tab("connect", translate("Connection Monitoring")) + +this_tab = "default" + +ma = di:taboption(this_tab, Value, "apn", "APN :"); +ma.rmempty = true; + +mu = di:taboption(this_tab, Value, "user", "User Name :"); +mu.optional=false; +mu.rmempty = true; + +mp = di:taboption(this_tab, Value, "passw", "Password :"); +mp.optional=false; +mp.rmempty = true; +mp.password = true + +mpi = di:taboption(this_tab, Value, "pincode", "PIN :"); +mpi.optional=false; +mpi.rmempty = true; + +mau = di:taboption(this_tab, ListValue, "auth", "Authentication Protocol :") +mau:value("0", "None") +mau:value("1", "PAP") +mau:value("2", "CHAP") +mau.default = "0" + +this_taba = "advance" + +mf = di:taboption(this_taba, ListValue, "ppp", "Force Modem to PPP Protocol :"); +mf:value("0", "No") +mf:value("1", "Yes") +mf.default=0 + +md = di:taboption(this_taba, Value, "delay", "Connection Delay in Seconds :"); +md.optional=false; +md.rmempty = false; +md.default = 20 +md.datatype = "and(uinteger,min(20))" + +ml = di:taboption(this_taba, ListValue, "lock", "Lock Connection to a Provider :"); +ml:value("0", "No") +ml:value("1", "Yes") +ml.default=0 + +mcc = di:taboption(this_taba, Value, "mcc", "Provider Country Code :"); +mcc.optional=false; +mcc.rmempty = true; +mcc.datatype = "and(uinteger,min(1),max(999))" +mcc:depends("lock", "1") + +mnc = di:taboption(this_taba, Value, "mnc", "Provider Network Code :"); +mnc.optional=false; +mnc.rmempty = true; +mnc.datatype = "and(uinteger,min(1),max(999))" +mnc:depends("lock", "1") + +mdns1 = di:taboption(this_taba, Value, "dns1", "Custom DNS Server1 :"); +mdns1.rmempty = true; +mdns1.optional=false; +mdns1.datatype = "ipaddr" + +mdns2 = di:taboption(this_taba, Value, "dns2", "Custom DNS Server2 :"); +mdns2.rmempty = true; +mdns2.optional=false; +mdns2.datatype = "ipaddr" + +mlog = di:taboption(this_taba, ListValue, "log", "Enable Connection Logging :"); +mlog:value("0", "No") +mlog:value("1", "Yes") +mlog.default=0 + +if nixio.fs.access("/etc/config/mwan3") then + mlb = di:taboption(this_taba, ListValue, "lb", "Enable Load Balancing at Connection :"); + mlb:value("0", "No") + mlb:value("1", "Yes") + mlb.default=0 +end + +-- +-- Default Connection Monitoring +-- + +this_tab = "connect" + +alive = di:taboption(this_tab, ListValue, "alive", "Connection Monitoring Status :"); +alive.rmempty = true; +alive:value("0", "Disabled") +alive:value("1", "Enabled") +alive:value("2", "Enabled with Router Reboot") +alive:value("3", "Enabled with Modem Reconnect") +alive:value("4", "Enabled with Power Toggle or Modem Reconnect") +alive.default=0 + +reliability = di:taboption(this_tab, Value, "reliability", translate("Tracking reliability"), + translate("Acceptable values: 1-100. This many Tracking IP addresses must respond for the link to be deemed up")) +reliability.datatype = "range(1, 100)" +reliability.default = "1" +reliability:depends("alive", "1") +reliability:depends("alive", "2") +reliability:depends("alive", "3") +reliability:depends("alive", "4") + +count = di:taboption(this_tab, ListValue, "count", translate("Ping count")) +count.default = "1" +count:value("1") +count:value("2") +count:value("3") +count:value("4") +count:value("5") +count:depends("alive", "1") +count:depends("alive", "2") +count:depends("alive", "3") +count:depends("alive", "4") + +interval = di:taboption(this_tab, ListValue, "pingtime", translate("Ping interval"), + translate("Amount of time between tracking tests")) +interval.default = "10" +interval:value("5", translate("5 seconds")) +interval:value("10", translate("10 seconds")) +interval:value("20", translate("20 seconds")) +interval:value("30", translate("30 seconds")) +interval:value("60", translate("1 minute")) +interval:value("300", translate("5 minutes")) +interval:value("600", translate("10 minutes")) +interval:value("900", translate("15 minutes")) +interval:value("1800", translate("30 minutes")) +interval:value("3600", translate("1 hour")) +interval:depends("alive", "1") +interval:depends("alive", "2") +interval:depends("alive", "3") +interval:depends("alive", "4") + +timeout = di:taboption(this_tab, ListValue, "pingwait", translate("Ping timeout")) +timeout.default = "2" +timeout:value("1", translate("1 second")) +timeout:value("2", translate("2 seconds")) +timeout:value("3", translate("3 seconds")) +timeout:value("4", translate("4 seconds")) +timeout:value("5", translate("5 seconds")) +timeout:value("6", translate("6 seconds")) +timeout:value("7", translate("7 seconds")) +timeout:value("8", translate("8 seconds")) +timeout:value("9", translate("9 seconds")) +timeout:value("10", translate("10 seconds")) +timeout:depends("alive", "1") +timeout:depends("alive", "2") +timeout:depends("alive", "3") +timeout:depends("alive", "4") + +down = di:taboption(this_tab, ListValue, "down", translate("Interface down"), + translate("Interface will be deemed down after this many failed ping tests")) +down.default = "3" +down:value("1") +down:value("2") +down:value("3") +down:value("4") +down:value("5") +down:value("6") +down:value("7") +down:value("8") +down:value("9") +down:value("10") +down:depends("alive", "1") +down:depends("alive", "2") +down:depends("alive", "3") +down:depends("alive", "4") + +up = di:taboption(this_tab, ListValue, "up", translate("Interface up"), + translate("Downed interface will be deemed up after this many successful ping tests")) +up.default = "3" +up:value("1") +up:value("2") +up:value("3") +up:value("4") +up:value("5") +up:value("6") +up:value("7") +up:value("8") +up:value("9") +up:value("10") +up:depends("alive", "1") +up:depends("alive", "2") +up:depends("alive", "3") +up:depends("alive", "4") + +cb2 = di:taboption(this_tab, DynamicList, "trackip", translate("Tracking IP"), + translate("This IP address will be pinged to dermine if the link is up or down. Leave blank to assume interface is always online")) +cb2.datatype = "ipaddr" +cb2:depends("alive", "1") +cb2:depends("alive", "2") +cb2:depends("alive", "3") +cb2:depends("alive", "4") +cb2.optional=false; + +-- +-- Custom profile +-- + +s = m:section(TypedSection, "custom", translate("Custom Profiles")) +s.anonymous = true +s.addremove = true +s:tab("custom", translate("General")) +s:tab("cadvanced", translate("Advanced")) +s:tab("cconnect", translate("Connection Monitoring")) + +this_ctab = "custom" + +select = s:taboption(this_ctab, ListValue, "select", "Select Modem by :"); +select:value("0", "Modem ID") +select:value("1", "Modem IMEI") +select:value("2", "Model Name") +select:value("3", "SIM IMSI") +select:value("4", "SIM ICCID") +select.default=0 + +idV = s:taboption(this_ctab, Value, "vid", "Switched Vendor ID :"); +idV.optional=false; +idV:depends("select", "0") +idV.default="xxxx" + +idP = s:taboption(this_ctab, Value, "pid", "Switched Product ID :"); +idP.optional=false; +idP:depends("select", "0") +idP.default="xxxx" + +imei = s:taboption(this_ctab, Value, "imei", "Modem IMEI Number :"); +imei.optional=false; +imei:depends("select", "1") +imei.datatype = "uinteger" +imei.default="1234567" + +model = s:taboption(this_ctab, Value, "model", "Modem Model Name contains :"); +model.optional=false; +model:depends("select", "2") +model.default="xxxx" + +imsi = s:taboption(this_ctab, Value, "imsi", "SIM IMSI Number :"); +imsi.optional=false; +imsi:depends("select", "3") +imsi.datatype = "uinteger" +imsi.default="1234567" + +iccid = s:taboption(this_ctab, Value, "iccid", "SIM ICCID Number :"); +iccid.optional=false; +iccid:depends("select", "4") +iccid.datatype = "uinteger" +iccid.default="1234567" + +cma = s:taboption(this_ctab, Value, "apn", "APN :"); +cma.rmempty = true; + +cmu = s:taboption(this_ctab, Value, "user", "User Name :"); +cmu.optional=false; +cmu.rmempty = true; + +cmp = s:taboption(this_ctab, Value, "passw", "Password :"); +cmp.optional=false; +cmp.rmempty = true; +cmp.password = true + +cmpi = s:taboption(this_ctab, Value, "pincode", "PIN :"); +cmpi.optional=false; +cmpi.rmempty = true; + +cmau = s:taboption(this_ctab, ListValue, "auth", "Authentication Protocol :") +cmau:value("0", "None") +cmau:value("1", "PAP") +cmau:value("2", "CHAP") +cmau.default = "0" + +this_ctaba = "cadvanced" + +cmf = s:taboption(this_ctaba, ListValue, "ppp", "Force Modem to PPP Protocol :"); +cmf:value("0", "No") +cmf:value("1", "Yes") +cmf.default=0 + +cmd = s:taboption(this_ctaba, Value, "delay", "Connection Delay in Seconds :"); +cmd.optional=false; +cmd.rmempty = false; +cmd.default = 20 +cmd.datatype = "and(uinteger,min(20))" + +cml = s:taboption(this_ctaba, ListValue, "lock", "Lock Connection to a Provider :"); +cml:value("0", "No") +cml:value("1", "Yes") +cml.default=0 + +cmcc = s:taboption(this_ctaba, Value, "mcc", "Provider Country Code :"); +cmcc.optional=false; +cmcc.rmempty = true; +cmcc.datatype = "and(uinteger,min(1),max(999))" +cmcc:depends("lock", "1") + +cmnc = s:taboption(this_ctaba, Value, "mnc", "Provider Network Code :"); +cmnc.optional=false; +cmnc.rmempty = true; +cmnc.datatype = "and(uinteger,min(1),max(999))" +cmnc:depends("lock", "1") + +cmdns1 = s:taboption(this_ctaba, Value, "dns1", "Custom DNS Server1 :"); +cmdns1.rmempty = true; +cmdns1.optional=false; +cmdns1.datatype = "ipaddr" + +cmdns2 = s:taboption(this_ctaba, Value, "dns2", "Custom DNS Server2 :"); +cmdns2.rmempty = true; +cmdns2.optional=false; +cmdns2.datatype = "ipaddr" + +cmlog = s:taboption(this_ctaba, ListValue, "log", "Enable Connection Logging :"); +cmlog:value("0", "No") +cmlog:value("1", "Yes") +cmlog.default=0 + +if nixio.fs.access("/etc/config/mwan3") then + cmlb = s:taboption(this_ctaba, ListValue, "lb", "Enable Load Balancing at Connection :"); + cmlb:value("0", "No") + cmlb:value("1", "Yes") + cmlb.default=0 +end + +-- +-- Custom Connection Monitoring +-- + +this_ctab = "cconnect" + +calive = s:taboption(this_ctab, ListValue, "alive", "Connection Monitoring Status :"); +calive.rmempty = true; +calive:value("0", "Disabled") +calive:value("1", "Enabled") +calive:value("2", "Enabled with Router Reboot") +calive:value("3", "Enabled with Modem Reconnect") +calive:value("4", "Enabled with Power Toggle or Modem Reconnect") +calive.default=0 + +reliability = s:taboption(this_ctab, Value, "reliability", translate("Tracking reliability"), + translate("Acceptable values: 1-100. This many Tracking IP addresses must respond for the link to be deemed up")) +reliability.datatype = "range(1, 100)" +reliability.default = "1" +reliability:depends("alive", "1") +reliability:depends("alive", "2") +reliability:depends("alive", "3") +reliability:depends("alive", "4") + +count = s:taboption(this_ctab, ListValue, "count", translate("Ping count")) +count.default = "1" +count:value("1") +count:value("2") +count:value("3") +count:value("4") +count:value("5") +count:depends("alive", "1") +count:depends("alive", "2") +count:depends("alive", "3") +count:depends("alive", "4") + +interval = s:taboption(this_ctab, ListValue, "pingtime", translate("Ping interval"), + translate("Amount of time between tracking tests")) +interval.default = "10" +interval:value("5", translate("5 seconds")) +interval:value("10", translate("10 seconds")) +interval:value("20", translate("20 seconds")) +interval:value("30", translate("30 seconds")) +interval:value("60", translate("1 minute")) +interval:value("300", translate("5 minutes")) +interval:value("600", translate("10 minutes")) +interval:value("900", translate("15 minutes")) +interval:value("1800", translate("30 minutes")) +interval:value("3600", translate("1 hour")) +interval:depends("alive", "1") +interval:depends("alive", "2") +interval:depends("alive", "3") +interval:depends("alive", "4") + +timeout = s:taboption(this_ctab, ListValue, "pingwait", translate("Ping timeout")) +timeout.default = "2" +timeout:value("1", translate("1 second")) +timeout:value("2", translate("2 seconds")) +timeout:value("3", translate("3 seconds")) +timeout:value("4", translate("4 seconds")) +timeout:value("5", translate("5 seconds")) +timeout:value("6", translate("6 seconds")) +timeout:value("7", translate("7 seconds")) +timeout:value("8", translate("8 seconds")) +timeout:value("9", translate("9 seconds")) +timeout:value("10", translate("10 seconds")) +timeout:depends("alive", "1") +timeout:depends("alive", "2") +timeout:depends("alive", "3") +timeout:depends("alive", "4") + +down = s:taboption(this_ctab, ListValue, "down", translate("Interface down"), + translate("Interface will be deemed down after this many failed ping tests")) +down.default = "3" +down:value("1") +down:value("2") +down:value("3") +down:value("4") +down:value("5") +down:value("6") +down:value("7") +down:value("8") +down:value("9") +down:value("10") +down:depends("alive", "1") +down:depends("alive", "2") +down:depends("alive", "3") +down:depends("alive", "4") + +up = s:taboption(this_ctab, ListValue, "up", translate("Interface up"), + translate("Downed interface will be deemed up after this many successful ping tests")) +up.default = "3" +up:value("1") +up:value("2") +up:value("3") +up:value("4") +up:value("5") +up:value("6") +up:value("7") +up:value("8") +up:value("9") +up:value("10") +up:depends("alive", "1") +up:depends("alive", "2") +up:depends("alive", "3") +up:depends("alive", "4") + +cb2 = s:taboption(this_ctab, DynamicList, "trackip", translate("Tracking IP"), + translate("This IP address will be pinged to dermine if the link is up or down. Leave blank to assume interface is always online")) +cb2.datatype = "ipaddr" +cb2:depends("alive", "1") +cb2:depends("alive", "2") +cb2:depends("alive", "3") +cb2:depends("alive", "4") +cb2.optional=false; + +return m \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/view/admin_system/poweroff.htm b/ext-rooter-basic/files/usr/lib/lua/luci/view/admin_system/poweroff.htm new file mode 100644 index 0000000..30d9333 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/view/admin_system/poweroff.htm @@ -0,0 +1,34 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<%+header%> + + + + +

<%:System%>

+

<%:System Stop%>

+

<%:Graceful system stop. De-power then re-power to restart.%>

+

+
    +<%+footer%> \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/custom.htm b/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/custom.htm new file mode 100644 index 0000000..c856716 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/custom.htm @@ -0,0 +1,125 @@ +<% +local modemfile = "/etc/config/modem.data" +local modemdata = {} +local count + +function process_line(xline, cnt) + local data = {} + local pline = xline + local start = 1 + for i=1,3 do + s, e = string.find(pline, " ") + data[i] = string.sub(pline, start, s-1) + pline = string.sub(pline, e+1) + end + data[4] = pline + modemdata[cnt] = data +end + +function read_modem() + count = 0 + local file = io.open(modemfile, "r") + if file == nil then + return + end + repeat + local line = file:read("*line") + if line == nil then + break + end + if string.len(line) < 5 then + break + end + count = count + 1 + process_line(line, count) + until 1==0 + file:close() +end + +function process_family(index) + local t = { } + if count == 0 then + return t + end + local mdata = modemdata[index] + if mdata[1] ~= "nil" and mdata[2] ~= "nil" then + t[1] = mdata[1] .. ":" .. mdata[2] + if mdata[3] == "tty" then + t[2] = "default" + end + if mdata[3] == "tty0" then + t[2] = "ttyUSB0" + end + if mdata[3] == "tty1" then + t[2] = "ttyUSB1" + end + if mdata[3] == "tty2" then + t[2] = "ttyUSB2" + end + if mdata[3] == "tty3" then + t[2] = "ttyUSB3" + end + if mdata[3] == "tty4" then + t[2] = "ttyUSB4" + end + if mdata[3] == "tty5" then + t[2] = "ttyUSB5" + end + + if mdata[4] == "tty" then + t[3] = "default" + end + if mdata[4] == "tty0" then + t[3] = "ttyUSB0" + end + if mdata[4] == "tty1" then + t[3] = "ttyUSB1" + end + if mdata[4] == "tty2" then + t[3] = "ttyUSB2" + end + if mdata[4] == "tty3" then + t[3] = "ttyUSB3" + end + if mdata[4] == "tty4" then + t[3] = "ttyUSB4" + end + if mdata[4] == "tty5" then + t[3] = "ttyUSB5" + end + + end + return t +end + +read_modem() + +%> + +
    + Custom Modem Port Database + + + + + + + <% + if count > 0 then + for i=1,count do + t = process_family(i) %> + + + + + + <% + end + else %> + + + + <% + end %> +
    ModemData PortCommunication Port
    <%=t[1]%><%=t[2]%><%=t[3]%>
    No Entries  
    +
    diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/debug.htm b/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/debug.htm new file mode 100644 index 0000000..fb8ead8 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/debug.htm @@ -0,0 +1,86 @@ +<%+header%> + +<% +local cnt = 0 +local tt = {} + +os.execute("cat /sys/kernel/debug/usb/devices > /tmp/modstat 2>&1") +local file = io.open("/tmp/modstat", "r") +repeat + local line = file:read("*line") + if line == nil then + break + end + if string.len(line) < 3 then + line = "********************************************************************************************************" + end + tt[cnt] = line + cnt = cnt + 1 +until 1 == 0 +cnt = cnt - 1 +file:close() +os.execute("/usr/lib/rooter/luci/luaops.sh delete /tmp/modstat") +%> + + + + +
    +

    Modem Debug Information

    +
    +
    + AT-Command Execution + + + + +
    + +
    + +
    + +
    + Device Information + + + + + + + <% + for i=1,cnt do + s, e = string.find(tt[i], "Vendor") + s1, e = string.find(tt[i], "Manufacturer") + s2, e = string.find(tt[i], "Product") + if s ~= nil or s1 ~= nil or s2 ~= nil then + %> + + + + + + <% else + %> + + + + + + <% end + end %> +
    <%=tt[i]%>
    <%=tt[i]%>
    +
    +

    +
    + +<%+footer%> \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/log.htm b/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/log.htm new file mode 100644 index 0000000..49aa8bd --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/log.htm @@ -0,0 +1,53 @@ +<%+header%> + + + + +
    +
    +

    Connection Log

    +
    + +
    + + + + + + + +
    + +
    + +
    + +
    + +
    +
    +<%+footer%> \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/misc.htm b/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/misc.htm new file mode 100644 index 0000000..c1c5b26 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/misc.htm @@ -0,0 +1,516 @@ +<%+header%> + + + + +
    +
    +

    Miscellaneous Modem Features

    +
    A Collection of Modem Features
    +
    + Current Modem + + + + + + + +
      + + + +
      +
      +
      + Connection/Disconnection + + + + + + + +
      + + + + + + + +
      +
      + +
      + AT-Command Terminal + + + + + + + + + +
      AT-Command :
      + + + + + +
      + +
      + +
      + +
      + Cellular Mode + + + + + + + + + + + + + +
      Current Cellular Mode :
       
      Change Cellular Mode :
      + + + +  
      +
      + +
      +
      +<%+footer%> diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/net_status.htm b/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/net_status.htm new file mode 100644 index 0000000..67c1ea5 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/net_status.htm @@ -0,0 +1,451 @@ +<%+header%> + + + + +
      +
      +

      Signal/Cell Information

      +
      Current Connection
      + +
      + General Information + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Modem :
        + + + +
        Modem ID :
            
          Provider :
              
            Comm Port :
              + + + +
              +
              + +
              + Modem/SIM Information + + + + + + + + + + + + + + + + + + + + + + +
              + +    
              Modem IMEI :
                  
                SIM IMSI :
                  SIM ICCID :
                    + + + + + + + + + + + + +
                    SIM Phone Number :
                    + +   
                    SIM Name :
                    +
                    + +
                    + Signal Information + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                    Network :
                       
                      CSQ :
                         
                        Signal Strength :
                           
                          RSSI :
                             
                            ECIO :
                             
                            RSCP :
                             
                            Connection Monitoring Status :
                               
                              +
                              + +
                              + Cell Information + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                              MCC MNC :
                               
                              RNC/eNB ID :
                               
                              LAC/TAC :
                               
                              Cell ID :
                               
                              UMTS Channel :
                                 
                                LTE Band :
                                   
                                  Maximum Qos :
                                   
                                  +
                                  + +
                                  + Refresh Rate + + + + + + + + + + + + + +
                                  Current Refresh Rate :
                                   
                                  Change Refresh Rate :
                                  + +  
                                  +
                                  + +
                                  +
                                  +<%+footer%> diff --git a/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/update.htm b/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/update.htm new file mode 100644 index 0000000..eee073a --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/lua/luci/view/rooter/update.htm @@ -0,0 +1,102 @@ +<%+header%> + + + + +
                                  +
                                  +

                                  Firmware Updating

                                  +
                                  Check if your current firmware is the latest available
                                  +
                                  + Versions + + + + + + + + + + + + + + +
                                  Current Firmware :
                                     
                                     
                                    Previous Checked Firmware :
                                       
                                      + + +
                                      + +
                                      + + + + + + + + + + + + + + + + +
                                          
                                       
                                         
                                         
                                        +
                                        + +
                                        + Last Update Change Log + + + + + + +
                                           
                                          +
                                          + + +
                                          +
                                          +<%+footer%> \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/cdmafind.lua b/ext-rooter-basic/files/usr/lib/rooter/cdmafind.lua new file mode 100644 index 0000000..b1f9922 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/cdmafind.lua @@ -0,0 +1,55 @@ +#!/usr/bin/lua + +drv = {} +idV = arg[1] +idP = arg[2] + +retval = 0 + +function trim(s) + return (s:gsub("^%s*(.-)%s*$", "%1")) +end + +-- MAIN + +local i=0 +local file = io.open("/tmp/cdma", "r") +repeat + local line = file:read("*line") + if line == nil then + break + end + if string.len(line) > 5 then + s, e = line:find("Vendor=") + if s ~= nil then + cs, ce = line:find(" ", e) + m_idV = trim(line:sub(e+1, cs-1)) + s, e = line:find("ProdID=") + cs, ce = line:find(" ", e) + m_idP = trim(line:sub(e+1, cs-1)) + if m_idV == idV and m_idP == idP then + repeat + line = file:read("*line") + if line == nil then + break + end + if string.len(line) > 5 then + s, e = line:find("Product=") + if s ~= nil then + s, e = line:find(" CDMA") + if s ~= nil then + retval = 1 + break + end + end + end + until 1==0 + break + end + end + end +until 1==0 +file:close() + + +os.exit(retval) \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/common/gettype.sh b/ext-rooter-basic/files/usr/lib/rooter/common/gettype.sh new file mode 100644 index 0000000..fed86e0 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/common/gettype.sh @@ -0,0 +1,153 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +CURRMODEM=$1 +CPORT=$(uci get modem.modem$CURRMODEM.commport) + +sleep 10 + +OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "gettype.gcom" "$CURRMODEM") +OX=$($ROOTER/common/processat.sh "$OX") + +MANUF=$(echo "$OX" | awk -F[:] '/Manufacturer:/ { print $2}') + +if [ -z "$MANUF" ]; then + ATCMDD="AT+CGMI" + MANUF=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + MANUF=$(echo -e "$MANUF" | { read _V ; read _V ; echo $_V ; }) +fi + +if [ "x$MANUF" = "x" ]; then + MANUF=$(uci get modem.modem$CURRMODEM.manuf) +fi + +MODEL=$(echo "$OX" | awk -F[,\ ] '/^\+MODEL:/ {print $2}') + +if [ -z "$MODEL" ]; then + ATCMDD="AT+CGMM" + MODEL=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + MODEL=$(echo -e "$MODEL" | { read _V ; read _V ; echo $_V ; }) +fi + +if [ "x$MODEL" != "x" ]; then + MODEL=$(echo "$MODEL" | sed -e 's/"//g') + if [ $MODEL = 0 ]; then + MODEL = "mf820" + fi +else + MODEL=$(uci get modem.modem$CURRMODEM.model) +fi + +uci set modem.modem$CURRMODEM.manuf=$MANUF +uci set modem.modem$CURRMODEM.model=$MODEL +uci commit modem + +$ROOTER/signal/status.sh $CURRMODEM "$MANUF $MODEL" "Connecting" + +IMEI=$(echo "$OX" | awk -F[,\ ] '/^\IMEI:/ {print $2}') + +if [ -z "$IMEI" ]; then + ATCMDD="AT+CGSN" + IMEI=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + IMEI=$(echo $IMEI | grep -o "[0-9]\{15\}") +fi + +if [ -z "$IMEI" ]; then + ATCMDD="ATI5" + IMEI=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + IMEI=$(echo $IMEI | grep -o "[0-9]\{15\}") +fi + +if [ -n "$IMEI" ]; then + IMEI=$(echo "$IMEI" | sed -e 's/"//g') + IMEI=${IMEI:0:15} +else + IMEI="Unknown" +fi + +IDP=$(uci get modem.modem$CURRMODEM.idP) +IDV=$(uci get modem.modem$CURRMODEM.idV) + +echo $IDV" : "$IDP > /tmp/msimdatax$CURRMODEM +echo "$IMEI" >> /tmp/msimdatax$CURRMODEM + +lua $ROOTER/signal/celltype.lua "$MODEL" $CURRMODEM +source /tmp/celltype$CURRMODEM +rm -f /tmp/celltype$CURRMODEM + +uci set modem.modem$CURRMODEM.celltype=$CELL +uci commit modem + +$ROOTER/luci/celltype.sh $CURRMODEM + +M2=$(echo "$OX" | sed -e "s/+CNUM: /+CNUM:,/g") +CNUM=$(echo "$M2" | awk -F[,] '/^\+CNUM:/ {print $3}') +if [ "x$CNUM" != "x" ]; then + CNUM=$(echo ${CNUM%%$'\n'*} | sed -e 's/"//g') +else + CNUM="*" +fi +CNUMx=$(echo "$M2" | awk -F[,] '/^\+CNUM:/ {print $2}') +if [ "x$CNUMx" != "x" ]; then + CNUMx=$(echo ${CNUMx%%$'\n'*} | sed -e 's/"//g') +else + CNUMx="*" +fi + +NLEN=$(echo "$OX" | awk -F[,\ ] '/^\+CPBR:/ {print $4}') +if [ "x$NLEN" != "x" ]; then + NLEN=$(echo "$NLEN" | sed -e 's/"//g') +else + NLEN="14" +fi +echo 'NLEN="'"$NLEN"'"' > /tmp/namelen$CURRMODEM + +ATCMDD="AT+CIMI" +OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") +OX=$($ROOTER/common/processat.sh "$OX") +ERROR="ERROR" +if `echo ${OX} | grep "${ERROR}" 1>/dev/null 2>&1` +then + IMSI="Unknown" +else + OX=${OX//[!0-9]/} + IMSIL=${#OX} + IMSI=${OX:0:$IMSIL} +fi +echo "$IMSI" >> /tmp/msimdatax$CURRMODEM + +ATCMDD="AT+CRSM=176,12258,0,0,10" +OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") +OX=$($ROOTER/common/processat.sh "$OX") +ERROR="ERROR" +if `echo ${OX} | grep "${ERROR}" 1>/dev/null 2>&1` +then + ICCID="Unknown" +else + ICCID=$(echo "$OX" | awk -F[,\ ] '/^\+CRSM:/ {print $4}') + if [ "x$ICCID" != "x" ]; then + sstring=$(echo "$ICCID" | sed -e 's/"//g') + length=${#sstring} + xstring= + i=0 + while [ $i -lt $length ]; do + c1=${sstring:$i:1} + let 'j=i+1' + c2=${sstring:$j:1} + xstring=$xstring$c2$c1 + let 'i=i+2' + done + ICCID=$xstring + else + ICCID="Unknown" + fi +fi +echo "$ICCID" >> /tmp/msimdatax$CURRMODEM +echo "0" >> /tmp/msimdatax$CURRMODEM +echo "$CNUM" > /tmp/msimnumx$CURRMODEM +echo "$CNUMx" >> /tmp/msimnumx$CURRMODEM + +mv -f /tmp/msimdatax$CURRMODEM /tmp/msimdata$CURRMODEM +mv -f /tmp/msimnumx$CURRMODEM /tmp/msimnum$CURRMODEM + diff --git a/ext-rooter-basic/files/usr/lib/rooter/common/huaweidata.sh b/ext-rooter-basic/files/usr/lib/rooter/common/huaweidata.sh new file mode 100644 index 0000000..4dff503 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/common/huaweidata.sh @@ -0,0 +1,244 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "Huawei Data" "$@" +} + +CURRMODEM=$1 +COMMPORT=$2 + +fix_data() { + O=$($ROOTER/common/processat.sh "$OY") +} + +process_csq() { + CSQ=$(echo "$O" | awk -F[,\ ] '/^\+CSQ:/ {print $2}') + [ "x$CSQ" = "x" ] && CSQ=-1 + if [ $CSQ -ge 0 -a $CSQ -le 31 ]; then + CSQ_PER=$(($CSQ * 100/31)) + CSQ_RSSI=$((2 * CSQ - 113)) + CSQX=$CSQ_RSSI + [ $CSQ -eq 0 ] && CSQ_RSSI="<= "$CSQ_RSSI + [ $CSQ -eq 31 ] && CSQ_RSSI=">= "$CSQ_RSSI + CSQ_PER=$CSQ_PER"%" + CSQ_RSSI=$CSQ_RSSI" dBm" + else + CSQ="-" + CSQ_PER="-" + CSQ_RSSI="-" + fi +} + +process_huawei() { + CSNR=$(echo "$O" | awk -F[,\ ] '/^\^CSNR:/ {print $2}') + if [ "x$CSNR" != "x" ]; then + RSCP=$CSNR + CSNR=$(echo "$O" | awk -F[,\ ] '/^\^CSNR:/ {print $3}') + if [ "x$CSNR" != "x" ]; then + ECIO=$CSNR + else + ECIO=`expr $RSCP - $CSQX` + fi + else + EC=$(echo "$O" | awk -F[,\ ] '/^\+CSQ:/ {print $4}') + if [ "x$EC" != "x" ]; then + ECIO=$EC + EX=$(printf %.0f $ECIO) + RSCP=`expr $CSQX + $EX` + fi + fi + + LTERSRP=$(echo "$O" | awk -F[,\ ] '/^\^LTERSRP:/ {print $2}') + if [ "x$LTERSRP" != "x" ]; then + RSCP=$LTERSRP" (RSRP)" + LTERSRP=$(echo "$O" | awk -F[,\ ] '/^\^LTERSRP:/ {print $3}') + if [ "x$LTERSRP" != "x" ]; then + ECIO=$LTERSRP" (RSRQ)" + else + ECIO=`expr $RSCP - $CSQX` + fi + fi + + LBANDS=$(echo $O | grep -o "\^HFREQINFO:[0-9,]\+") + LBAND="" + printf '%s\n' "$LBANDS" | while read LBANDL; do + BWU=$(echo $LBANDL | cut -d, -f9) + if [ -z "$BWU" ]; then + LBAND="" + else + BWU=$(($(echo $BWU) / 1000)) + BWD=$(($(echo $LBANDL | cut -d, -f6) / 1000)) + LBANDL=$(echo $LBANDL | cut -d, -f3) + if [ -z "$LBANDL" ]; then + LBAND="" + else + if [ -n "$LBAND" ]; then + LBAND=$LBAND" aggregated with:
                                          " + fi + LBAND=$LBAND"B"$LBANDL" (Bandwidth $BWD MHz Down | $BWU MHz Up)" + fi + fi + echo "$LBAND" > /tmp/lbandvar + done + if [ -e /tmp/lbandvar ]; then + read LBAND < /tmp/lbandvar + rm /tmp/lbandvar + fi + if [ -z "$LBAND" ]; then + LBAND="-" + fi + + NETMODE="0" + SYSCFG=$(echo "$O" | awk -F[,\"] '/^\^SYSCFGEX:/ {print $2}') + if [ "x$SYSCFG" != "x" ]; then + MODTYPE="3" + case $SYSCFG in + "00" ) + NETMODE="1" + ;; + "01" ) + NETMODE="3" + ;; + "03" ) + NETMODE="7" + ;; + * ) + ACQ=${SYSCFG:0:2} + case $ACQ in + "01" ) + NETMODE="2" + ;; + "02" ) + NETMODE="4" + ;; + "03" ) + NETMODE="6" + ;; + esac + ;; + esac + else + SYSCFG=$(echo "$O" | awk -F[,\ ] '/^\^SYSCFG:/ {print $2}') + if [ "x$SYSCFG" != "x" ]; then + MODTYPE="4" + case $SYSCFG in + "7" ) + NETMODE="1" + ;; + "13" ) + NETMODE="3" + ;; + "14" ) + NETMODE="5" + ;; + * ) + SYSCFG=$(echo "$O" | awk -F[,\ ] '/^\^SYSCFG:/ {print $3}') + case $SYSCFG in + "0" ) + NETMODE="1" + ;; + "1" ) + NETMODE="2" + ;; + "2" ) + NETMODE="4" + ;; + esac + ;; + esac + fi + fi + + + MODE="-" + TECH=$(echo "$O" | awk -F[,] '/^\^SYSINFOEX:/ {print $9}' | sed 's/"//g') + if [ "x$TECH" != "x" ]; then + MODE="$TECH" + fi + + if [ "x$MODE" = "x-" ]; then + TECH=$(echo "$O" | awk -F[,\ ] '/^\^SYSINFO:/ {print $7}') + if [ "x$TECH" != "x" ]; then + case $TECH in + 17*) MODE="HSPA+ (64QAM)";; + 18*) MODE="HSPA+ (MIMO)";; + 1*) MODE="GSM";; + 2*) MODE="GPRS";; + 3*) MODE="EDGE";; + 4*) MODE="UMTS";; + 5*) MODE="HSDPA";; + 6*) MODE="HSUPA";; + 7*) MODE="HSPA";; + 9*) MODE="HSPA+";; + *) MODE=$TECH;; + esac + fi + fi + + CMODE=$(uci get modem.modem$CURRMODEM.cmode) + if [ $CMODE = 0 ]; then + NETMODE="10" + fi +} + +CSQ="-" +CSQ_PER="-" +CSQ_RSSI="-" +ECIO="-" +RSCP="-" +ECIO1=" " +RSCP1=" " +MODE="-" +MODETYPE="-" +NETMODE="-" + +OY=$($ROOTER/gcom/gcom-locked "$COMMPORT" "huaweiinfo.gcom" "$CURRMODEM") + +fix_data +process_csq +process_huawei + +echo 'CSQ="'"$CSQ"'"' > /tmp/signal$CURRMODEM.file +echo 'CSQ_PER="'"$CSQ_PER"'"' >> /tmp/signal$CURRMODEM.file +echo 'CSQ_RSSI="'"$CSQ_RSSI"'"' >> /tmp/signal$CURRMODEM.file +echo 'ECIO="'"$ECIO"'"' >> /tmp/signal$CURRMODEM.file +echo 'RSCP="'"$RSCP"'"' >> /tmp/signal$CURRMODEM.file +echo 'ECIO1="'"$ECIO1"'"' >> /tmp/signal$CURRMODEM.file +echo 'RSCP1="'"$RSCP1"'"' >> /tmp/signal$CURRMODEM.file +echo 'MODE="'"$MODE"'"' >> /tmp/signal$CURRMODEM.file +echo 'MODTYPE="'"$MODTYPE"'"' >> /tmp/signal$CURRMODEM.file +echo 'NETMODE="'"$NETMODE"'"' >> /tmp/signal$CURRMODEM.file +echo 'LBAND="'"$LBAND"'"' >> /tmp/signal$CURRMODEM.file + +CONNECT=$(uci get modem.modem$CURRMODEM.connected) +if [ $CONNECT -eq 0 ]; then + exit 0 +fi + +if [ $CSQ = "-" ]; then + log "$OY" +fi + +ENB="0" +if [ -e /etc/config/failover ]; then + ENB=$(uci get failover.enabled.enabled) +fi +if [ $ENB = "1" ]; then + exit 0 +fi + +WWANX=$(uci get modem.modem$CURRMODEM.interface) +OPER=$(cat /sys/class/net/$WWANX/operstate 2>/dev/null) + +if [ ! $OPER ]; then + exit 0 +fi +if echo $OPER | grep -q "unknown"; then + exit 0 +fi + +if echo $OPER | grep -q "down"; then + echo "1" > "/tmp/connstat"$CURRMODEM +fi diff --git a/ext-rooter-basic/files/usr/lib/rooter/common/lockchk.sh b/ext-rooter-basic/files/usr/lib/rooter/common/lockchk.sh new file mode 100644 index 0000000..157ecc8 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/common/lockchk.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "Lock Provider" "$@" +} + +setautocops() { + ATCMDD="AT+COPS=0" + OX=$($ROOTER/gcom/gcom-locked "$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + CLOG=$(uci get modem.modeminfo$CURRMODEM.log) + if [ $CLOG = "1" ]; then + log "$OX" + fi + exit 0 +} + +CURRMODEM=$1 +CPORT=/dev/ttyUSB$(uci get modem.modem$CURRMODEM.commport) + +LOCK=$(uci get modem.modeminfo$CURRMODEM.lock) +if [ -z $LOCK ]; then + setautocops +fi + +MCC=$(uci get modem.modeminfo$CURRMODEM.mcc) +if [ -z $MCC ]; then + setautocops +fi +LMCC=`expr length $MCC` +if [ $LMCC -ne 3 ]; then + setautocops +fi +MNC=$(uci get modem.modeminfo$CURRMODEM.mnc) +if [ -z $MNC ]; then + setautocops +fi +LMNC=`expr length $MNC` +if [ $LMNC -eq 1 ]; then + MNC=0$MNC +fi + +export MCCMNC=$MCC$MNC + +OX=$($ROOTER/gcom/gcom-locked "$CPORT" "lock-prov.gcom" "$CURRMODEM") +CLOG=$(uci get modem.modeminfo$CURRMODEM.log) +if [ $CLOG = "1" ]; then + log "$OX" +fi +ERROR="ERROR" +if `echo ${OX} | grep "${ERROR}" 1>/dev/null 2>&1` +then + log "Error While Locking to Provider" +else + log "Locked to Provider $MCC $MNC" +fi + diff --git a/ext-rooter-basic/files/usr/lib/rooter/common/modemchk.lua b/ext-rooter-basic/files/usr/lib/rooter/common/modemchk.lua new file mode 100644 index 0000000..935fda9 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/common/modemchk.lua @@ -0,0 +1,157 @@ +#!/usr/bin/lua + +local uvid = arg[1] +local upid = arg[2] +local dport = arg[3] +local cport = arg[4] + +local modemfile = "/etc/config/modem.data" +local modemdata = {} +local count +local retval +local cretval + +function process_line(xline, cnt) + local data = {} + local pline = xline + local start = 1 + for i=1,3 do + s, e = string.find(pline, " ") + data[i] = string.sub(pline, start, s-1) + pline = string.sub(pline, e+1) + end + data[4] = pline + modemdata[cnt] = data +end + +function read_modem() + count = 0 + local file = io.open(modemfile, "r") + if file == nil then + return + end + repeat + local line = file:read("*line") + if line == nil then + break + end + if string.len(line) < 5 then + break + end + count = count + 1 + process_line(line, count) + until 1==0 + file:close() +end + +function process_port() + for l=1,count do + local mdata = modemdata[l] + if mdata[1] == uvid and mdata[2] == upid then + retval = 0 + if mdata[3] == "tty0" then + retval = 1 + end + if mdata[3] == "tty1" then + retval = 2 + end + if mdata[3] == "tty2" then + retval = 3 + end + if mdata[3] == "tty3" then + retval = 4 + end + if mdata[3] == "tty4" then + retval = 5 + end + if mdata[3] == "tty5" then + retval = 6 + end + + cretval = 0 + if mdata[4] == "tty0" then + cretval = 1 + end + if mdata[4] == "tty1" then + cretval = 2 + end + if mdata[4] == "tty2" then + cretval = 3 + end + if mdata[4] == "tty3" then + cretval = 4 + end + if mdata[4] == "tty4" then + cretval = 5 + end + if mdata[4] == "tty5" then + cretval = 6 + end + + break + end + end +end + +if uvid == "12d1" then + if upid == "14cc" or upid == "1464" or upid == "151b" then + cport = "0" + end + if upid == "14ac" then + cport = "1" + end + if upid == "140c" then + cport = "1" + end +end + +read_modem() +retval = 0 +cretval = 0 +if count > 0 then + process_port() +end +if retval > 0 then + retval = retval - 1 +else + retval = tonumber(dport) +end +if cretval > 0 then + cretval = cretval - 1 +else + cretval = tonumber(cport) +end + +if uvid == "2001" and upid == "7e35" then + cretval = 2 + retval = 1 +end + +-- +-- may need echo "2c7c 0125" > /sys/bus/usb/drivers/qmi_wwan/new_id +-- +if uvid == "2c7c" and upid == "0125" then + cretval = 3 + retval = 2 +end + +if uvid == "05c6" and upid == "9215" then + cretval = 3 + retval = 2 +end +-- +-- may need echo "2c7c 0121" > /sys/bus/usb/drivers/qmi_wwan/new_id +-- +if uvid == "2c7c" and upid == "0121" then + cretval = 3 + retval = 2 +end + +dret = string.format("%d", retval) +cret = string.format("%d", cretval) +local file = io.open("/tmp/parmpass", "w") +file:write("DPORT=\"" .. dret .. "\"\n") +file:write("CPORT=\"" .. cret .. "\"\n") +file:close() + +os.exit(retval) diff --git a/ext-rooter-basic/files/usr/lib/rooter/common/otherdata.sh b/ext-rooter-basic/files/usr/lib/rooter/common/otherdata.sh new file mode 100644 index 0000000..aa856d4 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/common/otherdata.sh @@ -0,0 +1,88 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "Other Data" "$@" +} + +CURRMODEM=$1 +COMMPORT=$2 + +fix_data() { + O=$($ROOTER/common/processat.sh "$OY") +} + +process_csq() { + CSQ=$(echo "$O" | awk -F[,\ ] '/^\+CSQ:/ {print $2}') + [ "x$CSQ" = "x" ] && CSQ=-1 + if [ $CSQ -ge 0 -a $CSQ -le 31 ]; then + CSQ_PER=$(($CSQ * 100/31)) + CSQ_RSSI=$((2 * CSQ - 113)) + CSQX=$CSQ_RSSI + [ $CSQ -eq 0 ] && CSQ_RSSI="<= "$CSQ_RSSI + [ $CSQ -eq 31 ] && CSQ_RSSI=">= "$CSQ_RSSI + CSQ_PER=$CSQ_PER"%" + CSQ_RSSI=$CSQ_RSSI" dBm" + else + CSQ="-" + CSQ_PER="-" + CSQ_RSSI="-" + fi +} + +CSQ="-" +CSQ_PER="-" +CSQ_RSSI="-" +ECIO="-" +RSCP="-" +ECIO1=" " +RSCP1=" " +MODE="-" +MODETYPE="-" +NETMODE="-" +LBAND="-" + +OY=$($ROOTER/gcom/gcom-locked "$COMMPORT" "otherinfo.gcom" "$CURRMODEM") + +fix_data +process_csq + +echo 'CSQ="'"$CSQ"'"' > /tmp/signal$CURRMODEM.file +echo 'CSQ_PER="'"$CSQ_PER"'"' >> /tmp/signal$CURRMODEM.file +echo 'CSQ_RSSI="'"$CSQ_RSSI"'"' >> /tmp/signal$CURRMODEM.file +echo 'ECIO="'"$ECIO"'"' >> /tmp/signal$CURRMODEM.file +echo 'RSCP="'"$RSCP"'"' >> /tmp/signal$CURRMODEM.file +echo 'ECIO1="'"$ECIO1"'"' >> /tmp/signal$CURRMODEM.file +echo 'RSCP1="'"$RSCP1"'"' >> /tmp/signal$CURRMODEM.file +echo 'MODE="'"$MODE"'"' >> /tmp/signal$CURRMODEM.file +echo 'MODTYPE="'"$MODTYPE"'"' >> /tmp/signal$CURRMODEM.file +echo 'NETMODE="'"$NETMODE"'"' >> /tmp/signal$CURRMODEM.file +echo 'LBAND="'"$LBAND"'"' >> /tmp/signal$CURRMODEM.file + +CONNECT=$(uci get modem.modem$CURRMODEM.connected) +if [ $CONNECT -eq 0 ]; then + exit 0 +fi + +ENB="0" +if [ -e /etc/config/failover ]; then + ENB=$(uci get failover.enabled.enabled) +fi +if [ $ENB = "1" ]; then + exit 0 +fi + +WWANX=$(uci get modem.modem$CURRMODEM.interface) +OPER=$(cat /sys/class/net/$WWANX/operstate 2>/dev/null) + +if [ ! $OPER ]; then + exit 0 +fi +if echo $OPER | grep -q "unknown"; then + exit 0 +fi + +if echo $OPER | grep -q "down"; then + echo "1" > "/tmp/connstat"$CURRMODEM +fi diff --git a/ext-rooter-basic/files/usr/lib/rooter/common/phone.sh b/ext-rooter-basic/files/usr/lib/rooter/common/phone.sh new file mode 100644 index 0000000..9bbb894 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/common/phone.sh @@ -0,0 +1,54 @@ + +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "Phone Change" "$@" +} + +CURRMODEM=$1 +PHONE=$2 +NAME=$3 + +CPORT=$(uci get modem.modem$CURRMODEM.commport) +PHONE=$(echo "$PHONE" | sed -e 's/ //g') + +log "Change Modem $CURRMODEM SIM phone number to $PHONE, name to $NAME" + +INTER=${PHONE:0:1} +if [ $INTER = "+" ]; then + TON="145" +else + TON="129" +fi + +ATCMDD="AT+CPBS=\"ON\";+CPBS?" +OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") +OX=$($ROOTER/common/processat.sh "$OX") + +ON=$(echo "$OX" | awk -F[,\ ] '/^\+CPBS:/ {print $2}') +if [ $ON = "\"ON\"" ]; then + ATCMDD="AT+CPBW=1,\"$PHONE\",$TON,\"$NAME\"" + OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + ATCMDD="AT+CNUM" + OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + OX=$($ROOTER/common/processat.sh "$OX") + M2=$(echo "$OX" | sed -e "s/+CNUM: /+CNUM:,/g") + CNUM=$(echo "$M2" | awk -F[,] '/^\+CNUM:/ {print $3}') + if [ "x$CNUM" != "x" ]; then + CNUM=$(echo ${CNUM%%$'\n'*} | sed -e 's/"//g') + else + CNUM="*" + fi + CNUMx=$(echo "$M2" | awk -F[,] '/^\+CNUM:/ {print $2}') + if [ "x$CNUMx" != "x" ]; then + CNUMx=$(echo ${CNUMx%%$'\n'*} | sed -e 's/"//g') + else + CNUMx="*" + fi + echo "$CNUM" > /tmp/msimnumx$CURRMODEM + echo "$CNUMx" >> /tmp/msimnumx$CURRMODEM + mv -f /tmp/msimnumx$CURRMODEM /tmp/msimnum$CURRMODEM +fi + diff --git a/ext-rooter-basic/files/usr/lib/rooter/common/processat.sh b/ext-rooter-basic/files/usr/lib/rooter/common/processat.sh new file mode 100644 index 0000000..eaff131 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/common/processat.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +OX=$1 + +M6=$(echo "$OX" | sed -e "s/ / /g") +M5=$(echo "$M6" | sed -e "s/ / /g") +M4=$(echo "$M5" | sed -e "s/ / /g") +M3=$(echo "$M4" | sed -e "s/ / /g") +M2=$(echo "$M3" | sed -e "s/ / /g") +M2=$(echo "$M2" | sed -e "s/TAC:/ /;s/Tx Power:/ /;s/SINR/ /;s!SYSINFOEX:!SYSINFOEX: !;s!CNTI:!CNTI: !;s!SELRAT:!SELRAT: !;s!ZSNT:!ZSNT: !") +M1=$(echo "$M2" | sed -e "s!Car0 Tot Ec/Io!+ECIOx!;s!Car1 Tot Ec/Io!+ECIO1x!;s!Car0 RSCP!+RSCPx!;s!+CGMM: !!") +M2=$(echo "$M1" | sed -e "s!Car1 RSCP!+RSCP1x!;s!CSNR:!CSNR: !;s!RSSI (dBm):!RSSI4: !;s!AirCard !AirCard!;s!USB !USB!") +M3=$(echo "$M2" | sed -e "s!RSRP (dBm):!RSRP4: !;s!RSRQ (dB):!RSRQ4: !;s!LTERSRP:!LTERSRP: !;s!Model:!+MODEL: !;s!SYSCFGEX:!SYSCFGEX: !") +M7=$(echo "$M3" | sed -e "s!RX level Carrier 0 (dBm):!RSSI3: !;s!RX level Carrier 1 (dBm):!RSSI13: !;s!SYSCFG:!SYSCFG: !;s! ! !g") +OX=$(echo "$M7" | sed -e "s!WCDMA channel:!UMTS:!;s!SYSINFO:!SYSINFO: !") +echo "$OX" \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/common/sierradata.sh b/ext-rooter-basic/files/usr/lib/rooter/common/sierradata.sh new file mode 100644 index 0000000..83742f6 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/common/sierradata.sh @@ -0,0 +1,198 @@ +# #!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "Sierra Data" "$@" +} + +CURRMODEM=$1 +COMMPORT=$2 + +get_sierra() { + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "sierrainfo.gcom" "$CURRMODEM") + O=$($ROOTER/common/processat.sh "$OX") +} + +get_sierra + +CSQ=$(echo "$O" | awk -F[,\ ] '/^\+CSQ:/ {print $2}') +[ "x$CSQ" = "x" ] && CSQ=-1 +if [ $CSQ -ge 0 -a $CSQ -le 31 ]; then + CSQ_PER=$(($CSQ * 100/31)) + CSQ_RSSI=$((2 * CSQ - 113)) + CSQX=$CSQ_RSSI + [ $CSQ -eq 0 ] && CSQ_RSSI="<= "$CSQ_RSSI + [ $CSQ -eq 31 ] && CSQ_RSSI=">= "$CSQ_RSSI + CSQ_PER=$CSQ_PER"%" + CSQ_RSSI=$CSQ_RSSI" dBm" +else + CSQ="-" + CSQ_PER="-" + CSQ_RSSI="-" +fi + +LBAND=$(echo $O | tr 'a-z' 'A-Z' | grep -o "LTE BAND:[ ]*B[0-9]\+ LTE BW:[ ]*[0-9]\+ MHZ") +if [ -z "$LBAND" ]; then + LBAND="-" +else + LBAND=$(echo $LBAND | grep -o "[0-9]\+") + LBAND=$(printf "B%d (Bandwidth %d MHz)" $LBAND) +fi + +SLBAND=$(echo $O | tr 'a-z' 'A-Z' | grep -o " ACTIVE LTE SCELL BAND:[ ]*B[0-9]\+ LTE SCELL BW:[ ]*[0-9]\+ MHZ") +if [ -n "$SLBAND" ]; then + SLBAND=$(echo $SLBAND | grep -o "[0-9]\+") + SLBAND=$(printf " aggregated with:
                                          B%d (Bandwidth %d MHz)" $SLBAND) + LBAND=$LBAND$SLBAND +fi + +ECIO=$(echo "$O" | awk -F[\ ] '/^\+ECIOx/ {print $2}') +[ "x$ECIO" = "x" ] && ECIO="-" +ECIO1=$(echo "$O" | awk -F[\ ] '/^\+ECIO1x/ {print $2}') +[ "x$ECIO1" = "x" ] && ECIO1=" " +[ "$ECIO1" = "n/a" ] && ECIO1=" " + +RSCP=$(echo "$O" | awk -F[\ ] '/^\+RSCPx/ {print $2}') +[ "x$RSCP" = "x" ] && RSCP="-" +RSCP1=$(echo "$O" | awk -F[\ ] '/^\+RSCP1x/ {print $2}') +[ "x$RSCP1" = "x" ] && RSCP1=" " +[ "$RSCP1" = "n/a" ] && RSCP1=" " + +RSSI3=$(echo "$O" | awk -F[\ ] '/^\RSSI3/ {print $2}') +if [ "x$RSSI3" != "x" ]; then + CSQ_RSSI=$RSSI3" dBm" +else + if [ "$ECIO" != "-" -a "$RSCP" != "-" ]; then + EX=$(printf %.0f $ECIO) + CSQ_RSSI=`expr $RSCP - $EX` + CSQ_RSSI=$CSQ_RSSI" dBm" + fi +fi + +RSSI4=$(echo "$O" | awk -F[\ ] '/^\RSSI4/ {print $2}') +if [ "x$RSSI4" != "x" ]; then + CSQ_RSSI=$RSSI4" dBm" + RSRP4=$(echo "$O" | awk -F[\ ] '/^\RSRP4/ {print $2}') + if [ "x$RSRP4" != "x" ]; then + RSCP=$RSRP4" (RSRP)" + RSRQ4=$(echo "$O" | awk -F[\ ] '/^\RSRQ4/ {print $2}') + if [ "x$RSRQ4" != "x" ]; then + ECIO=$RSRQ4" (RSRQ)" + fi + fi +fi + +if [ "$RSCP" == "-" ]; then + RSCP=$(echo $O | grep -o "RSRP4: -[0-9]\+") + ECIO=$(echo $O | grep -o "RSRQ4: -[.0-9]\+") + if [ -z "$RSCP" ] || [ -z "$ECIO" ]; then + RSCP="-" + ECIO="-" + else + RSCP=$(echo $RSCP | grep -o " -[0-9]\+") + RSCP=${RSCP%%$'\n'*} + RSCP=$(printf "%s (RSRP)" $RSCP) + ECIO=$(echo $ECIO | grep -o " -[.0-9]\+") + ECIO=${ECIO%%$'\n'*} + ECIO=$(printf "%s (RSRQ)" $ECIO) + fi +fi + +WCHANNEL=$(echo "$O" | awk -F[\ ] '/^\UMTS:/ {print $2}') +if [ "x$WCHANNEL" = "x" ]; then + WCHANNEL="-" +fi + +CHANNEL=$(echo "$O" | awk -F[\ ] '/^\Channel:/ {print $2}') +if [ "x$CHANNEL" = "x" ]; then + CHANNEL="-" +fi + +if [ "$WCHANNEL" != "-" ]; then + CHANNEL=$WCHANNEL" ("$CHANNEL")" +fi + +MODE="-" +TECH=$(echo "$O" | awk -F[,\ ] '/^\*CNTI:/ {print $3}' | sed 's|/|,|g') +if [ "x$TECH" != "x" ]; then + MODE="$TECH" +fi + +SELRAT=$(echo "$O" | awk -F[,\ ] '/^\!SELRAT:/ {print $2}') +if [ "x$SELRAT" != "x" ]; then + MODTYPE="2" + case $SELRAT in + "00" ) + NETMODE="1" + ;; + "01" ) + NETMODE="5" + ;; + "03" ) + NETMODE="4" + ;; + "02" ) + NETMODE="3" + ;; + "04" ) + NETMODE="2" + ;; + "05" ) + NETMODE="4" + ;; + "06" ) + NETMODE="7" + ;; + + esac +fi + +CMODE=$(uci get modem.modem$CURRMODEM.cmode) +if [ $CMODE = 0 ]; then + NETMODE="10" +fi + +echo 'CSQ="'"$CSQ"'"' > /tmp/signal$CURRMODEM.file +echo 'CSQ_PER="'"$CSQ_PER"'"' >> /tmp/signal$CURRMODEM.file +echo 'CSQ_RSSI="'"$CSQ_RSSI"'"' >> /tmp/signal$CURRMODEM.file +echo 'ECIO="'"$ECIO"'"' >> /tmp/signal$CURRMODEM.file +echo 'RSCP="'"$RSCP"'"' >> /tmp/signal$CURRMODEM.file +echo 'ECIO1="'"$ECIO1"'"' >> /tmp/signal$CURRMODEM.file +echo 'RSCP1="'"$RSCP1"'"' >> /tmp/signal$CURRMODEM.file +echo 'MODE="'"$MODE"'"' >> /tmp/signal$CURRMODEM.file +echo 'MODTYPE="'"$MODTYPE"'"' >> /tmp/signal$CURRMODEM.file +echo 'NETMODE="'"$NETMODE"'"' >> /tmp/signal$CURRMODEM.file +echo 'CHANNEL="'"$CHANNEL"'"' >> /tmp/signal$CURRMODEM.file +echo 'LBAND="'"$LBAND"'"' >> /tmp/signal$CURRMODEM.file + +CONNECT=$(uci get modem.modem$CURRMODEM.connected) +if [ $CONNECT -eq 0 ]; then + exit 0 +fi + +if [ $CSQ = "-" ]; then + log "$OX" +fi + +ENB="0" +if [ -e /etc/config/failover ]; then + ENB=$(uci get failover.enabled.enabled) +fi +if [ $ENB = "1" ]; then + exit 0 +fi + +WWANX=$(uci get modem.modem$CURRMODEM.interface) +OPER=$(cat /sys/class/net/$WWANX/operstate 2>/dev/null) + +if [ ! $OPER ]; then + exit 0 +fi +if echo $OPER | grep -q "unknown"; then + exit 0 +fi + +if echo $OPER | grep -q "down"; then + echo "1" > "/tmp/connstat"$CURRMODEM +fi diff --git a/ext-rooter-basic/files/usr/lib/rooter/common/ubloxdata.sh b/ext-rooter-basic/files/usr/lib/rooter/common/ubloxdata.sh new file mode 100644 index 0000000..f62d708 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/common/ubloxdata.sh @@ -0,0 +1,235 @@ +# #!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "ublox Data" "$@" +} + +CURRMODEM=$1 +COMMPORT=$2 + +get_ublox() { + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "ubloxinfo.gcom" "$CURRMODEM" | tr 'a-z' 'A-Z') +} + +get_ublox + +UCGED=$(echo $OX | grep -o "+UCGED: 2") +if [ -z "$UCGED" ]; then + ATCMDD="AT+UCGED=2" + UCGED=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + get_ublox +fi +OX=$OX + +RSRP="" +RSRQ="" +CHANNEL="-" +ECIO="-" +RSCP="-" +ECIO1=" " +RSCP1=" " +MODE="-" +MODTYPE="-" +NETMODE="-" +LBAND="-" + +CSQ=$(echo $OX | grep -o "+CSQ: .\+ +CESQ" | tr " " ",") +CESQ=$(echo $OX | grep -o "+CESQ: .\+ +URAT" | tr " " ",") +URAT=$(echo $OX | grep -o "+URAT: .\+ +UCGED" | tr " " ",") +UCGED=$(echo $OX" " | grep -o "+UCGED: .\+ OK " | tr " " ",") + +CSQ=$(echo $CSQ | cut -d, -f2) +CSQ=$(echo $CSQ | grep -o "[0-9]\{1,2\}") + +if [ "$CSQ" -eq "99" ]; then + CSQ="" +fi +if [ -n "$CSQ" ]; then + CSQ_PER=$(($CSQ * 100/31))"%" + CSQ_RSSI=$((2 * CSQ - 113))" dBm" +else + CSQ="-" + CSQ_PER="-" + CSQ_RSSI="-" +fi + +RAT=$(echo $UCGED | cut -d, -f3) +case "$RAT" in + "2") + MODE="GSM" + LAC=$(echo $UCGED | cut -d, -f11) + LAC=$(echo $LAC | grep -o "[0-9A-F]\{4\}") + CID=$(echo $UCGED | cut -d, -f9) + CID=$(echo $CID | grep -o "[0-9A-F]\{4\}") + ;; + "3") + MODE="UMTS" + CHANNEL=$(echo $UCGED | cut -d, -f7) + LAC=$(echo $UCGED | cut -d, -f10) + LAC=$(echo $LAC | grep -o "[0-9A-F]\{4\}") + CID=$(echo $UCGED | cut -d, -f9) + CID=$(echo $CID | grep -o "[0-9A-F]\{7,8\}") + RSCP=$(echo $CESQ | cut -d, -f4) + RSCP=$(echo $RSCP | grep -o "[0-9]\{1,3\}") + if [ "$RSCP" -eq "255" ]; then + RSCP="" + fi + if [ -n "$RSCP" ]; then + RSCP=$(($RSCP - 121)) + fi + ECIO=$(echo $CESQ | cut -d, -f5) + ECIO=$(echo $ECIO | grep -o "[0-9]\{1,3\}") + if [ "$ECIO" -eq "255" ]; then + ECIO="" + fi + if [ -n "$ECIO" ]; then + ECIO=$((($ECIO / 2) - 24)) + fi + ;; + "4") + MODE="LTE" + LBAND=$(echo $UCGED | cut -d, -f8) + if [ "$LBAND" -eq "255" ]; then + LBAND="" + fi + BWU=$(echo $UCGED | cut -d, -f9) + BWU=$(echo $BWU | grep -o "[0-9]\{1,3\}") + BWD=$(echo $UCGED | cut -d, -f10) + BWD=$(echo $BWD | grep -o "[0-9]\{1,3\}") + if [ -z "$BWD" ]; then + LBAND="" + fi + if [ -z "$BWU" ]; then + LBAND="" + fi + if [ -z "$LBAND" ]; then + LBAND="-" + else + BWU=$(($(echo $BWU) / 5)) + BWD=$(($(echo $BWD) / 5)) + LBAND="B"$LBAND" (Bandwidth $BWD MHz Down | $BWU MHz Up)" + fi + LAC=$(echo $UCGED | cut -d, -f11) + LAC=$(echo $LAC | grep -o "[0-9A-F]\{4\}") + CID=$(echo $UCGED | cut -d, -f12) + CID=$(echo $CID | grep -o "[0-9A-F]\{7,8\}") + RSRP=$(echo $CESQ | cut -d, -f7) + RSRP=$(echo $RSRP | grep -o "[0-9]\{1,3\}") + if [ "$RSRP" -eq "255" ]; then + RSRP="" + fi + if [ -n "$RSRP" ]; then + RSRP=$(($RSRP - 141)) + RSCP=$RSRP" (RSRP)" + fi + RSRQ=$(echo $CESQ | cut -d, -f6) + RSRQ=$(echo $RSRQ | grep -o "[0-9]\{1,3\}") + if [ "$RSRQ" -eq "255" ]; then + RSRQ="" + fi + if [ -n "$RSRQ" ]; then + RSRQ=$((($RSRQ / 2) - 19)) + ECIO=$RSRQ" (RSRQ)" + fi + ;; +esac + +if [ $RAT -eq "2" ]; then + if [ -n "$CID" ]; then + CID_NUM=$(printf "%d" 0x$CID) + CID=$CID" ("$CID_NUM")" + fi +else + CID=$(echo $CID | grep -o "[0-9A-F]\{5,8\}") + if [ -n "$CID" ]; then + LCID=$(printf "%08X" 0x$CID) + LCID_NUM=$(printf "%d" 0x$LCID) + if [ "$RAT" -eq "4" ]; then + RNC=$(printf "${LCID:1:5}") + CID=$(printf "${LCID:6:2}") + else + RNC=$(printf "${LCID:1:3}") + CID=$(printf "${LCID:4:4}") + fi + CID_NUM=$(printf "%d" 0x$CID) + CID=$CID" ("$CID_NUM")" + RNC_NUM=" ("$(printf "%d" 0x$RNC)")" + fi +fi + +if [ -n "$LAC" ]; then + LAC_NUM=$(printf "%d" 0x$LAC) + LAC=$LAC" ("$LAC_NUM")" +else + LAC="-" + LAC_NUM="-" +fi + +URAT1=$(echo $URAT | cut -d, -f2) +URAT2=$(echo $URAT | cut -d, -f3) +if [ -n "$URAT1" ]; then + MODTYPE="5" + case $URAT1 in + "0" ) + NETMODE="3" + ;; + "2" ) + NETMODE="5" + ;; + "3" ) + NETMODE="7" + ;; + * ) + case $URAT2 in + "0" ) + NETMODE="2" + ;; + "2" ) + NETMODE="4" + ;; + "3" ) + NETMODE="1" + ;; + esac + ;; + esac +fi + +echo 'CSQ="'"$CSQ"'"' > /tmp/signal$CURRMODEM.file +echo 'CSQ_PER="'"$CSQ_PER"'"' >> /tmp/signal$CURRMODEM.file +echo 'CSQ_RSSI="'"$CSQ_RSSI"'"' >> /tmp/signal$CURRMODEM.file +echo 'ECIO="'"$ECIO"'"' >> /tmp/signal$CURRMODEM.file +echo 'RSCP="'"$RSCP"'"' >> /tmp/signal$CURRMODEM.file +echo 'ECIO1="'"$ECIO1"'"' >> /tmp/signal$CURRMODEM.file +echo 'RSCP1="'"$RSCP1"'"' >> /tmp/signal$CURRMODEM.file +echo 'MODE="'"$MODE"'"' >> /tmp/signal$CURRMODEM.file +echo 'MODTYPE="'"$MODTYPE"'"' >> /tmp/signal$CURRMODEM.file +echo 'NETMODE="'"$NETMODE"'"' >> /tmp/signal$CURRMODEM.file +echo 'CHANNEL="'"$CHANNEL"'"' >> /tmp/signal$CURRMODEM.file +echo 'LBAND="'"$LBAND"'"' >> /tmp/signal$CURRMODEM.file +echo 'LAC="'"$LAC"'"' >> /tmp/signal$CURRMODEM.file +echo 'LAC_NUM="'""'"' >> /tmp/signal$CURRMODEM.file +echo 'CID="'"$CID"'"' >> /tmp/signal$CURRMODEM.file +echo 'CID_NUM="'""'"' >> /tmp/signal$CURRMODEM.file +echo 'RNC="'"$RNC"'"' >> /tmp/signal$CURRMODEM.file +echo 'RNC_NUM="'"$RNC_NUM"'"' >> /tmp/signal$CURRMODEM.file + +if [ "$CSQ" = "-" ]; then + log "$OX" +fi + +WWANX=$(uci get modem.modem$CURRMODEM.interface) +OPER=$(cat /sys/class/net/$WWANX/operstate 2>/dev/null) + +if [ ! $OPER ]; then + exit 0 +fi +if echo $OPER | grep -q "unknown"; then + exit 0 +fi + +if echo $OPER | grep -q "down"; then + echo "1" > "/tmp/connstat"$CURRMODEM +fi diff --git a/ext-rooter-basic/files/usr/lib/rooter/common/ztedata.sh b/ext-rooter-basic/files/usr/lib/rooter/common/ztedata.sh new file mode 100644 index 0000000..da6f9b3 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/common/ztedata.sh @@ -0,0 +1,164 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "ZTE Data" "$@" +} + +CURRMODEM=$1 +COMMPORT=$2 + +fix_data() { + O=$($ROOTER/common/processat.sh "$OY") +} + +process_csq() { + CSQ=$(echo "$O" | awk -F[,\ ] '/^\+CSQ:/ {print $2}') + [ "x$CSQ" = "x" ] && CSQ=-1 + if [ $CSQ -ge 0 -a $CSQ -le 31 ]; then + CSQ_PER=$(($CSQ * 100/31)) + CSQ_RSSI=$((2 * CSQ - 113)) + CSQX=$CSQ_RSSI + [ $CSQ -eq 0 ] && CSQ_RSSI="<= "$CSQ_RSSI + [ $CSQ -eq 31 ] && CSQ_RSSI=">= "$CSQ_RSSI + CSQ_PER=$CSQ_PER"%" + CSQ_RSSI=$CSQ_RSSI" dBm" + else + CSQ="-" + CSQ_PER="-" + CSQ_RSSI="-" + fi +} + +process_zte() { + ZRSSI=$(echo "$O" | awk -F[,\ ] '/^\+ZRSSI:/ {print $2}') + if [ "x$ZRSSI" != "x" ]; then + TMP_RSSI=$CSQ_RSSI + CSQ_RSSI="-"$ZRSSI" dBm" + ECI=$(echo "$O" | awk -F[,\ ] '/^\+ZRSSI:/ {print $3}') + if [ "x$ECI" != "x" ]; then + ECIO=`expr $ECI / 2` + ECIO="-"$ECIO + RSCP=$(echo "$O" | awk -F[,\ ] '/^\+ZRSSI:/ {print $4}') + if [ "x$RSCP" != "x" ]; then + RSCP=`expr $RSCP / 2` + RSCP="-"$RSCP + else + CSQ_RSSI=$TMP_RSSI + RSCP=$ZRSSI + ECIO=$ECI + fi + else + RSCP=$ZRSSI + CSQ_RSSI=$TMP_RSSI + ECIO=`expr $RSCP - $CSQX` + fi + fi + + MODE="-" + TECH=$(echo "$O" | awk -F[,\ ] '/^\+ZPAS:/ {print $2}' | sed 's/"//g') + if [ "x$TECH" != "x" -a "x$TECH" != "xNo" ]; then + MODE="$TECH" + fi + + ZSNT=$(echo "$O" | awk -F[,\ ] '/^\+ZSNT:/ {print $2}') + if [ "x$ZSNT" != "x" ]; then + MODTYPE="1" + if [ $ZSNT = "0" ]; then + ZSNTX=$(echo "$O" | awk -F[,\ ] '/^\+ZSNT:/ {print $4}') + case $ZSNTX in + "0" ) + NETMODE="1" + ;; + "1" ) + NETMODE="2" + ;; + "2" ) + NETMODE="4" + ;; + "6" ) + NETMODE="6" + ;; + esac + else + case $ZSNT in + "1" ) + NETMODE="3" + ;; + "2" ) + NETMODE="5" + ;; + "6" ) + NETMODE="7" + ;; + esac + fi + fi + + CMODE=$(uci get modem.modem$CURRMODEM.cmode) + if [ $CMODE = 0 ]; then + NETMODE="10" + fi +} + +CSQ="-" +CSQ_PER="-" +CSQ_RSSI="-" +ECIO="-" +RSCP="-" +ECIO1=" " +RSCP1=" " +MODE="-" +MODETYPE="-" +NETMODE="-" +LBAND="-" + +OY=$($ROOTER/gcom/gcom-locked "$COMMPORT" "zteinfo.gcom" "$CURRMODEM") + +fix_data +process_csq +process_zte + +echo 'CSQ="'"$CSQ"'"' > /tmp/signal$CURRMODEM.file +echo 'CSQ_PER="'"$CSQ_PER"'"' >> /tmp/signal$CURRMODEM.file +echo 'CSQ_RSSI="'"$CSQ_RSSI"'"' >> /tmp/signal$CURRMODEM.file +echo 'ECIO="'"$ECIO"'"' >> /tmp/signal$CURRMODEM.file +echo 'RSCP="'"$RSCP"'"' >> /tmp/signal$CURRMODEM.file +echo 'ECIO1="'"$ECIO1"'"' >> /tmp/signal$CURRMODEM.file +echo 'RSCP1="'"$RSCP1"'"' >> /tmp/signal$CURRMODEM.file +echo 'MODE="'"$MODE"'"' >> /tmp/signal$CURRMODEM.file +echo 'MODTYPE="'"$MODTYPE"'"' >> /tmp/signal$CURRMODEM.file +echo 'NETMODE="'"$NETMODE"'"' >> /tmp/signal$CURRMODEM.file +echo 'LBAND="'"$LBAND"'"' >> /tmp/signal$CURRMODEM.file + +CONNECT=$(uci get modem.modem$CURRMODEM.connected) +if [ $CONNECT -eq 0 ]; then + exit 0 +fi + +if [ $CSQ = "-" ]; then + log "$OY" +fi + +ENB="0" +if [ -e /etc/config/failover ]; then + ENB=$(uci get failover.enabled.enabled) +fi +if [ $ENB = "1" ]; then + exit 0 +fi + +WWANX=$(uci get modem.modem$CURRMODEM.interface) +OPER=$(cat /sys/class/net/$WWANX/operstate 2>/dev/null) + +if [ ! $OPER ]; then + exit 0 +fi +if echo $OPER | grep -q "unknown"; then + exit 0 +fi + +if echo $OPER | grep -q "down"; then + echo "1" > "/tmp/connstat"$CURRMODEM +fi diff --git a/ext-rooter-basic/files/usr/lib/rooter/connect/conmon.sh b/ext-rooter-basic/files/usr/lib/rooter/connect/conmon.sh new file mode 100644 index 0000000..423f759 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/connect/conmon.sh @@ -0,0 +1,176 @@ +#!/bin/sh +. /lib/functions.sh + +ROOTER=/usr/lib/rooter +ROOTER_LINK="/tmp/links" + +log() { + logger -t "Connection Monitor" "$@" +} + +CURRMODEM=$1 + +power_toggle() { + if [ -f "/tmp/gpiopin" ]; then + $ROOTER/pwrtoggle.sh 3 + else + if [ $ACTIVE = 4 ]; then + # GPIO power-toggle not supported so re-bind USB driver, but only if power toggle is configured in connection monitoring + if [ -L /sys/bus/usb/drivers/usb/usb1 ]; then + if [ $(uci get modem.pinginfo1.alive) = 4 ]; then + $ROOTER/pwrtoggle.sh 1 + fi + fi + if [ -L /sys/bus/usb/drivers/usb/usb2 ]; then + if [ $(uci get modem.pinginfo2.alive) = 4 ]; then + $ROOTER/pwrtoggle.sh 2 + fi + fi + fi + fi +} + +do_down() { + echo 'MONSTAT="'"DOWN$1"'"' > /tmp/monstat$CURRMODEM + case $ACTIVE in + "1" ) + log "Modem $CURRMODEM Connection is Down$1" + ;; + "2" ) + log "Modem $CURRMODEM Connection is Down$1" + reboot -f + ;; + "3" ) + log "Modem $CURRMODEM Connection is Down$1" + PROT=$(uci get modem.modem$CURRMODEM.proto) + if [ $PROT -eq "30" ]; then + CPORT=$(uci get modem.modem$CURRMODEM.commport) + ATCMDD="AT+CFUN=0;+CFUN=1,1" + $ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD" + sleep 60 + else + if [ -f $ROOTER_LINK/reconnect$CURRMODEM ]; then + $ROOTER_LINK/reconnect$CURRMODEM $CURRMODEM & + fi + fi + ;; + "4" ) + log "Modem $CURRMODEM Connection is Down$1" + power_toggle + ;; + esac +} + + +CURSOR="-" + +log "Start Connection Monitor for Modem $CURRMODEM" + +while [ 1 = 1 ]; do + ACTIVE=$(uci get modem.pinginfo$CURRMODEM.alive) + if [ $ACTIVE = "0" ]; then + echo 'MONSTAT="'"Disabled"'"' > /tmp/monstat$CURRMODEM + sleep 60 + else + track_ips= + INTER=$(uci get modem.modem$CURRMODEM.interface) + TIMEOUT=$(uci get modem.pinginfo$CURRMODEM.pingwait) + INTERVAL=$(uci get modem.pinginfo$CURRMODEM.pingtime) + RELIAB=$(uci get modem.pinginfo$CURRMODEM.reliability) + DOWN=$(uci get modem.pinginfo$CURRMODEM.down) + UP=$(uci get modem.pinginfo$CURRMODEM.up) + COUNT=$(uci get modem.pinginfo$CURRMODEM.count) + PACKETSIZE=$(uci get modem.pinginfo$CURRMODEM.packetsize) + + list_track_ips() { + track_ips="$1 $track_ips" + } + + config_load modem + config_list_foreach "pinginfo$CURRMODEM" "trackip" list_track_ips + + if [ -f "/tmp/connstat$CURRMODEM" ]; then + do_down " from Modem" + rm -f /tmp/connstat$CURRMODEM + sleep 20 + else + ENB="0" + if [ -e /etc/config/failover ]; then + ENB=$(uci get failover.enabled.enabled) + fi + if [ $ENB = "1" ]; then + if [ -e /tmp/mdown$CURRMODEM ]; then + do_down " (using Failover)" + else + echo 'MONSTAT="'"Up ($CURSOR) (using Failover)"'"' > /tmp/monstat$CURRMODEM + fi + sleep 20 + + else + # check to see if modem iface has an IP address, if not try a reconnect/power toggle + if [ -z "$(ifconfig ${INTER} 2>&1 | sed '/inet\ /!d;s/.*r://g;s/\ .*//g')" ]; then + do_down " (no IP address)" + fi + MENABLE=$(uci get mwan3.wan$CURRMODEM.enabled) + MSCR=$(uci get mwan3.wan$CURRMODEM.dwnscript) + if [ $MENABLE = "1" -a $MSCR != nil -a -e $MSCR ]; then + if [ -e /tmp/mdown$CURRMODEM ]; then + do_down " (using Load Balance)" + else + echo 'MONSTAT="'"Up ($CURSOR) (using Load Balance)"'"' > /tmp/monstat$CURRMODEM + fi + sleep $(uci get mwan3.wan${CURRMODEM}.interval) + else + UPDWN="0" + host_up_count=0 + score_up=$UP + score_dwn=$DOWN + lost=0 + while true; do + if [ ! -z "$track_ips" ]; then + for track_ip in $track_ips; do + ping -I $INTER -c $COUNT -W $TIMEOUT -s $PACKETSIZE -q $track_ip &> /dev/null + if [ $? -eq 0 ]; then + let host_up_count++ + else + let lost++ + fi + done + if [ $host_up_count -lt $RELIAB ]; then + let score_dwn-- + score_up=$UP + if [ $score_dwn -eq 0 ]; then + UPDWN="1" + break + fi + else + let score_up-- + score_dwn=$DOWN + if [ $score_up -eq 0 ]; then + UPDWN="0" + break + fi + fi + else + UPDWN="0" + exit + fi + host_up_count=0 + sleep $INTERVAL + done + if [ $UPDWN = "1" ]; then + do_down " (using Ping Test)" + else + echo 'MONSTAT="'"UP ($CURSOR) (using Ping Test)"'"' > /tmp/monstat$CURRMODEM + fi + sleep $INTERVAL + fi + fi + fi + if [ $CURSOR = "-" ]; then + CURSOR="+" + else + CURSOR="-" + fi + fi +done diff --git a/ext-rooter-basic/files/usr/lib/rooter/connect/create_connect.sh b/ext-rooter-basic/files/usr/lib/rooter/connect/create_connect.sh new file mode 100644 index 0000000..dce7314 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/connect/create_connect.sh @@ -0,0 +1,666 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter +ROOTER_LINK="/tmp/links" + +log() { + logger -t "Create Connection" "$@" +} + +handle_timeout(){ + local wget_pid="$1" + local count=0 + TIMEOUT=70 + res=1 + if [ -d /proc/${wget_pid} ]; then + res=0 + fi + while [ "$res" = 0 -a $count -lt "$((TIMEOUT))" ]; do + sleep 1 + count=$((count+1)) + res=1 + if [ -d /proc/${wget_pid} ]; then + res=0 + fi + done + + if [ "$res" = 0 ]; then + log "Killing process on timeout" + kill "$wget_pid" 2> /dev/null + res=1 + if [ -d /proc/${wget_pid} ]; then + res=0 + fi + if [ "$res" = 0 ]; then + log "Killing process on timeout" + kill -9 $wget_pid 2> /dev/null + fi + fi +} + +set_dns() { + local DNS1=$(uci get modem.modeminfo$CURRMODEM.dns1) + local DNS2=$(uci get modem.modeminfo$CURRMODEM.dns2) + if [ -z $DNS1 ]; then + if [ -z $DNS2 ]; then + return + else + uci set network.wan$CURRMODEM.peerdns=0 + uci set network.wan$CURRMODEM.dns=$DNS2 + fi + else + uci set network.wan$CURRMODEM.peerdns=0 + if [ -z $DNS2 ]; then + uci set network.wan$CURRMODEM.dns="$DNS1" + else + uci set network.wan$CURRMODEM.dns="$DNS2 $DNS1" + fi + fi +} + +save_variables() { + echo 'MODSTART="'"$MODSTART"'"' > /tmp/variable.file + echo 'WWAN="'"$WWAN"'"' >> /tmp/variable.file + echo 'USBN="'"$USBN"'"' >> /tmp/variable.file + echo 'ETHN="'"$ETHN"'"' >> /tmp/variable.file + echo 'WDMN="'"$WDMN"'"' >> /tmp/variable.file + echo 'BASEPORT="'"$BASEPORT"'"' >> /tmp/variable.file +} + +chcklog() { + OOX=$1 + CLOG=$(uci get modem.modeminfo$CURRMODEM.log) + if [ $CLOG = "1" ]; then + log "$OOX" + fi +} + +local NAPN NUSER NPASS NAUTH PINCODE + +get_connect() { + NAPN=$(uci get modem.modeminfo$CURRMODEM.apn) + NUSER=$(uci get modem.modeminfo$CURRMODEM.user) + NPASS=$(uci get modem.modeminfo$CURRMODEM.passw) + NAUTH=$(uci get modem.modeminfo$CURRMODEM.auth) + PINC=$(uci get modem.modeminfo$CURRMODEM.pincode) +# +# QMI and MBIM can't handle nil +# + case $PROT in + "2"|"3"|"30" ) + if [ -z $NUSER ]; then + NUSER="NIL" + fi + if [ -z $NPASS ]; then + NPASS="NIL" + fi + ;; + esac + + uci set modem.modem$CURRMODEM.apn=$NAPN + uci set modem.modem$CURRMODEM.user=$NUSER + uci set modem.modem$CURRMODEM.passw=$NPASS + uci set modem.modem$CURRMODEM.auth=$NAUTH + uci set modem.modem$CURRMODEM.pin=$PINC + uci commit modem +} + +CURRMODEM=$1 +RECON=$2 +source /tmp/variable.file + +MAN=$(uci get modem.modem$CURRMODEM.manuf) +MOD=$(uci get modem.modem$CURRMODEM.model) +BASEP=$(uci get modem.modem$CURRMODEM.baseport) +PROT=$(uci get modem.modem$CURRMODEM.proto) + +if [ ! -z $RECON ]; then + $ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "ReConnecting" + uci set modem.modem$CURRMODEM.connected=0 + uci commit modem + killall -9 getsignal$CURRMODEM + rm -f $ROOTER_LINK/getsignal$CURRMODEM + killall -9 con_monitor$CURRMODEM + rm -f $ROOTER_LINK/con_monitor$CURRMODEM + killall -9 mbim_monitor$CURRMODEM + rm -f $ROOTER_LINK/mbim_monitor$CURRMODEM + ifdown wan$CURRMODEM + CPORT=$(uci get modem.modem$CURRMODEM.commport) + WWANX=$(uci get modem.modem$CURRMODEM.wwan) + WDMNX=$(uci get modem.modem$CURRMODEM.wdm) + + case $PROT in + "3"|"30" ) + TIMEOUT=10 + #$ROOTER/mbim/mbim_connect.lua stop wwan$WWANX cdc-wdm$WDMNX $CURRMODEM & + #handle_timeout "$!" + ;; + * ) + $ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "reset.gcom" "$CURRMODEM" + ;; + esac + +else + + DELAY=$(uci get modem.modem$CURRMODEM.delay) + if [ -z $DELAY ]; then + DELAY=5 + fi + + uci delete network.wan$CURRMODEM + uci set network.wan$CURRMODEM=interface + uci set network.wan$CURRMODEM.proto=dhcp + uci set network.wan$CURRMODEM.ifname=wwan$WWAN + uci set network.wan$CURRMODEM._orig_bridge=false + uci set network.wan$CURRMODEM.metric=$CURRMODEM"0" + set_dns + uci commit network + + uci set modem.modem$CURRMODEM.wdm=$WDMN + uci set modem.modem$CURRMODEM.wwan=$WWAN + uci set modem.modem$CURRMODEM.interface=wwan$WWAN + uci commit modem + +# +# QMI, NCM and MBIM use cdc-wdm +# + case $PROT in + "2"|"3"|"30"|"4"|"6"|"7" ) + WDMNX=$WDMN + WDMN=`expr 1 + $WDMN` + ;; + esac + + WWANX=$WWAN + WWAN=`expr 1 + $WWAN` + save_variables + rm -f /tmp/usbwait + + case $PROT in +# +# Sierra Direct-IP modem comm port +# + "1" ) + log "Start Direct-IP Connection" + while [ ! -e /dev/ttyUSB$BASEP ]; do + sleep 1 + done + sleep $DELAY + + OX=$(grep . /sys/class/tty/ttyUSB*/../../../bInterfaceNumber | grep ":03" | cut -d'/' -f5) + if [ $BASEP -eq 0 ]; then + CPORT=$(echo $OX | cut -d' ' -f1) + else + CPORT=$(echo $OX | cut -d' ' -f2) + fi + CPORT=$(echo $CPORT | grep -o "[[:digit:]]\+") + CPORT=`expr $CPORT - $BASEP` + + idV=$(uci get modem.modem$CURRMODEM.idV) + idP=$(uci get modem.modem$CURRMODEM.idP) + lua $ROOTER/common/modemchk.lua "$idV" "$idP" "$CPORT" "$CPORT" + source /tmp/parmpass + CPORT=`expr $CPORT + $BASEP` + + log "Sierra Comm Port : /dev/ttyUSB$CPORT" + ;; +# +# QMI modem comm port +# + "2" ) + log "Start QMI Connection" + while [ ! -e /dev/cdc-wdm$WDMNX ]; do + sleep 1 + done + sleep $DELAY + + idV=$(uci get modem.modem$CURRMODEM.idV) + idP=$(uci get modem.modem$CURRMODEM.idP) + SIERRAID=0 + if [ $idV = 1199 -a $idP = 9071 ]; then + SIERRAID=1 + fi + if [ $idV = 413c -a $idP = 81b6 ]; then + SIERRAID=1 + fi + if [ $idV = 1199 -a $idP = 9079 ]; then + SIERRAID=1 + fi + if [ $idV = 1199 -a $idP = 9041 ]; then + SIERRAID=1 + fi + if [ $idV = 1199 -a $idP = 9051 ]; then + SIERRAID=1 + fi + if [ $SIERRAID -eq 1 ]; then + OX=$(grep . /sys/class/tty/ttyUSB*/../../../bInterfaceNumber | grep ":03" | cut -d'/' -f5) + if [ $BASEP -eq 0 ]; then + CPORT=$(echo $OX | cut -d' ' -f1) + else + CPORT=$(echo $OX | cut -d' ' -f2) + fi + CPORT=$(echo $CPORT | grep -o "[[:digit:]]\+") + CPORT=`expr $CPORT - $BASEP` + else + CPORT=1 + fi + lua $ROOTER/common/modemchk.lua "$idV" "$idP" "$CPORT" "$CPORT" + source /tmp/parmpass + + CPORT=`expr $CPORT + $BASEP` + + log "QMI Comm Port : /dev/ttyUSB$CPORT" + ;; + "3"|"30" ) + log "Start MBIM Connection" + while [ ! -e /dev/cdc-wdm$WDMNX ]; do + sleep 1 + done + sleep $DELAY + ;; +# +# Huawei NCM +# + "4"|"6"|"7"|"24"|"26"|"27" ) + log "Start NCM Connection" + case $PROT in + "4"|"6"|"7" ) + while [ ! -e /dev/cdc-wdm$WDMNX ]; do + sleep 1 + done + ;; + "24"|"26"|"27" ) + while [ ! -e /dev/ttyUSB$BASEP ]; do + sleep 1 + done + ;; + esac + sleep $DELAY + + idV=$(uci get modem.modem$CURRMODEM.idV) + idP=$(uci get modem.modem$CURRMODEM.idP) + if [ $PROT = "4" -o $PROT = "24" ]; then + lua $ROOTER/common/modemchk.lua "$idV" "$idP" "0" "0" + else + if [ $PROT = "6" -o $PROT = "26" ]; then + lua $ROOTER/common/modemchk.lua "$idV" "$idP" "0" "1" + else + lua $ROOTER/common/modemchk.lua "$idV" "$idP" "0" "2" + fi + fi + source /tmp/parmpass + + CPORT=`expr $CPORT + $BASEP` + + log "NCM Comm Port : /dev/ttyUSB$CPORT" + ;; + esac + + uci set modem.modem$CURRMODEM.commport=$CPORT + uci commit modem + +fi + +case $PROT in +# +# Sierra, NCM and QMI support SMS +# + "1"|"2"|"4"|"6"|"7"|"24"|"26"|"27" ) + $ROOTER/sms/check_sms.sh $CURRMODEM & + $ROOTER/common/gettype.sh $CURRMODEM & + ;; +esac + +while [ 1 -lt 6 ]; do + + get_connect + export SETAPN=$NAPN + export SETUSER=$NUSER + export SETPASS=$NPASS + export SETAUTH=$NAUTH + export PINCODE=$PINC + + case $PROT in + "1"|"2"|"4"|"6"|"7"|"24"|"26"|"27" ) + idV=$(uci get modem.modem$CURRMODEM.idV) + if [ $idV = 12d1 ]; then + OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "curc.gcom" "$CURRMODEM") + log "Unsolicited Responses Disabled" + ATCMDD="AT^USSDMODE=0" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + fi + ;; + esac + + case $PROT in + "1" ) + OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "auto.gcom" "$CURRMODEM") + chcklog "$OX" + M7=$(echo "$OX" | sed -e "s/SCPROF:/SCPROF: /;s! ! !g") + AU=$(echo "$M7" | awk -F[,\ ] '/^\!SCPROF:/ {print $4}') + if [ $AU = "1" ]; then + AUTO="1" + log "Autoconnect is Enabled" + else + AUTO="0" + log "Autoconnect is not Enabled" + fi + ;; + esac + uci set modem.modem$CURRMODEM.auto=$AUTO + uci commit modem + + case $PROT in +# +# Check provider Lock +# + "1"|"2"|"4"|"6"|"7"|"24"|"26"|"27" ) + $ROOTER/common/lockchk.sh $CURRMODEM + ;; + * ) + log "No Provider Lock Done" + ;; +esac + + case $PROT in +# +# Sierra and NCM uses separate Pincode setting +# + "1"|"4"|"6"|"7"|"24"|"26"|"27" ) + if [ -n "$PINCODE" ]; then + OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "setpin.gcom" "$CURRMODEM") + chcklog "$OX" + ERROR="ERROR" + if `echo ${OX} | grep "${ERROR}" 1>/dev/null 2>&1` + then + log "Modem $CURRMODEM Failed to Unlock SIM Pin" + $ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Failed to Connect : Pin Locked" + exit 0 + fi + fi + ;; + * ) + log "Pincode in script" + ;; + esac + $ROOTER/log/logger "Attempting to Connect Modem #$CURRMODEM ($MAN $MOD)" + log "Attempting to Connect" + + BRK=0 + case $PROT in +# +# Sierra connect script +# + "1" ) + if [ $AUTO = "0" ]; then + OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "connect-directip.gcom" "$CURRMODEM") + chcklog "$OX" + ERROR="ERROR" + if `echo ${OX} | grep "${ERROR}" 1>/dev/null 2>&1` + then + BRK=1 + $ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Failed to Connect : Retrying" + fi + M7=$(echo "$OX" | sed -e "s/SCACT:/SCACT: /;s! ! !g") + SCACT="!SCACT: 1,1" + if `echo ${M7} | grep "${SCACT}" 1>/dev/null 2>&1` + then + BRK=0 + ifup wan$CURRMODEM + sleep 20 + else + BRK=1 + $ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Failed to Connect : Retrying" + fi + else + ifup wan$CURRMODEM + sleep 20 + fi + ;; +# +# QMI connect script +# + "2" ) + $ROOTER/qmi/connectqmi.sh $CURRMODEM cdc-wdm$WDMNX $NAUTH $NAPN $NUSER $NPASS $PINCODE + if [ -f /tmp/qmigood ]; then + rm -f /tmp/qmigood + ifup wan$CURRMODEM + sleep 20 + else + BRK=1 + $ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Failed to Connect : Retrying" + fi + ;; +# +# NCM connect script +# + "4"|"6"|"7"|"24"|"26"|"27" ) + OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "ati") + E5372=$(echo ${OX} | grep "E5372") + R215=$(echo ${OX} | grep "R215") + if [ -n "$E5372" -o -n "$R215" ]; then + ifup wan$CURRMODEM + BRK=0 + else + OX=$($ROOTER/gcom/gcom-locked "/dev/cdc-wdm$WDMNX" "connect-ncm.gcom" "$CURRMODEM") + chcklog "$OX" + ERROR="ERROR" + if `echo ${OX} | grep "${ERROR}" 1>/dev/null 2>&1` + then + OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "connect-ncm.gcom" "$CURRMODEM") + chcklog "$OX" + fi + ERROR="ERROR" + if `echo ${OX} | grep "${ERROR}" 1>/dev/null 2>&1` + then + BRK=1 + $ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Failed to Connect : Retrying" + else + ifup wan$CURRMODEM + sleep 25 + OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "cgpaddr.gcom" "$CURRMODEM") + chcklog "$OX" + OX=$($ROOTER/common/processat.sh "$OX") + STATUS=$(echo "$OX" | awk -F[,\ ] '/^\^SYSINFOEX:/ {print $2}' | sed 's/"//g') + DOMAIN=$(echo "$OX" | awk -F[,\ ] '/^\^SYSINFOEX:/ {print $3}' | sed 's/"//g') + if [ "x$STATUS" = "x" ]; then + STATUS=$(echo "$OX" | awk -F[,\ ] '/^\^SYSINFO:/ {print $2}') + DOMAIN=$(echo "$OX" | awk -F[,\ ] '/^\^SYSINFO:/ {print $3}') + fi + CGPADDR="+CGPADDR:" + if `echo ${OX} | grep "${CGPADDR}" 1>/dev/null 2>&1` + then + if [ $STATUS = "2" ]; then + if [ $DOMAIN = "1" ]; then + BRK=0 + else + if [ $DOMAIN = "2" ]; then + BRK=0 + else + if [ $DOMAIN = "3" ]; then + BRK=0 + else + BRK=1 + $ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Network Error : Retrying" + fi + fi + fi + else + BRK=1 + $ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Network Error : Retrying" + fi + else + BRK=1 + $ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "No IP Address : Retrying" + fi + fi + fi + ;; +# +# MBIM connect script +# + "3"|"30" ) + idV=$(uci get modem.modem$CURRMODEM.idV) + idP=$(uci get modem.modem$CURRMODEM.idP) + + NETIFD=0 + if [ $idV = 1199 -a $idP = 9071 ]; then + NETIFD=1 + fi + if [ $idV = 413c -a $idP = 81b6 ]; then + NETIFD=1 + fi + if [ $idV = 1199 -a $idP = 9079 ]; then + NETIFD=1 + fi + if [ $idV = 1199 -a $idP = 9041 ]; then + NETIFD=1 + fi + #if [ $idV = 12d1 -a $idP = 15c1 ]; then + # NETIFD=2 + #fi + if [ $NETIFD -ne 0 ]; then + if [ $NETIFD -eq 1 ]; then + OX=$(grep . /sys/class/tty/ttyUSB*/../../../bInterfaceNumber | grep ":03" | cut -d'/' -f5) + if [ $BASEP -eq 0 ]; then + CPORT=$(echo $OX | cut -d' ' -f1) + else + CPORT=$(echo $OX | cut -d' ' -f2) + fi + CPORT=$(echo $CPORT | grep -o "[[:digit:]]\+") + CPORT=`expr $CPORT - $BASEP` + else + CPORT=0 + fi + idV=$(uci get modem.modem$CURRMODEM.idV) + idP=$(uci get modem.modem$CURRMODEM.idP) + lua $ROOTER/common/modemchk.lua "$idV" "$idP" "$CPORT" "$CPORT" + source /tmp/parmpass + CPORT=`expr $CPORT + $BASEP` + uci set modem.modem$CURRMODEM.commport=$CPORT + if [ -n "$CPORT" ]; then + COMMPORT="/dev/ttyUSB"$CPORT + ATCMDD="AT+CGDCONT?" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + if `echo ${OX} | grep "+CGDCONT: 1,\"IPV4V6\",\"$NAPN\"," 1>/dev/null 2>&1` + then + : + else + ATCMDD="AT+CGDCONT=1,\"IPV4V6\",\"$NAPN\";+CFUN=0:+CFUN=1" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + sleep 10 + fi + uci set modem.modem$CURRMODEM.proto="30" + fi + uci commit modem + log "Using Netifd Method" + uci delete network.wan$CURRMODEM + uci set network.wan$CURRMODEM=interface + uci set network.wan$CURRMODEM.proto=mbim + uci set network.wan$CURRMODEM.device=/dev/cdc-wdm$WDMNX + uci set network.wan$CURRMODEM.apn=$NAPN + uci set network.wan$CURRMODEM.auth=$NAUTH + uci set network.wan$CURRMODEM.username=$NUSER + uci set network.wan$CURRMODEM.password=$NPASS + uci set network.wan$CURRMODEM.pincode=$PINC + uci set network.wan$CURRMODEM.metric=$CURRMODEM"0" + uci -q commit network + rm -f /tmp/usbwait + ifup wan$CURRMODEM + exit 0 + else + log "Using Direct Method" + TIMEOUT=70 + $ROOTER/mbim/connectmbim.sh cdc-wdm$WDMNX $CURRMODEM $NAUTH $NAPN $NUSER $NPASS $PINC & + handle_timeout "$!" + if [ -f /tmp/mbimgood ]; then + rm -f /tmp/mbimgood + sleep 20 + else + BRK=1 + $ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Failed to Connect : Retrying" + fi + fi + ;; + esac + + if [ $BRK = 1 ]; then + $ROOTER/log/logger "Retry Connection with Modem #$CURRMODEM" + log "Retry Connection" + sleep 10 + else + $ROOTER/log/logger "Modem #$CURRMODEM Connected" + log "Connected" + break + fi +done + +case $PROT in +# +# Sierra, NCM and QMI use modemsignal.sh and reconnect.sh +# + "1"|"2"|"4"|"6"|"7"|"24"|"26"|"27" ) + ln -s $ROOTER/signal/modemsignal.sh $ROOTER_LINK/getsignal$CURRMODEM + ln -s $ROOTER/connect/reconnect.sh $ROOTER_LINK/reconnect$CURRMODEM + # send custom AT startup command + if [ $(uci get modem.modeminfo$CURRMODEM.at) -eq "1" ]; then + ATCMDD=$(uci get modem.modeminfo$CURRMODEM.atc) + if [ ! -z "${ATCMDD}" ]; then + OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + OX=$($ROOTER/common/processat.sh "$OX") + ERROR="ERROR" + if `echo ${OX} | grep "${ERROR}" 1>/dev/null 2>&1` + then + log "Error sending custom AT command: $ATCMDD with result: $OX" + else + log "Sent custom AT command: $ATCMDD with result: $OX" + fi + fi + fi + ;; + "3" ) + source /tmp/mbimcustom$CURRMODEM + source /tmp/mbimqos$CURRMODEM + source /tmp/mbimmcc$CURRMODEM + source /tmp/mbimsig$CURRMODEM + source /tmp/mbimmode$CURRMODEM + uci set modem.modem$CURRMODEM.custom=$CUSTOM + uci set modem.modem$CURRMODEM.provider=$PROV + uci set modem.modem$CURRMODEM.down=$DOWN" kbps Down | " + uci set modem.modem$CURRMODEM.up=$UP" kbps Up" + uci set modem.modem$CURRMODEM.mcc=$MCC + uci set modem.modem$CURRMODEM.mnc=" "$MNC + uci set modem.modem$CURRMODEM.sig=$CSQ + uci set modem.modem$CURRMODEM.mode=$MODE + uci set modem.modem$CURRMODEM.sms=0 + uci commit modem + rm -f /tmp/mbimcustom$CURRMODEM + rm -f /tmp/mbimqos$CURRMODEM + rm -f /tmp/mbimmcc$CURRMODEM + rm -f /tmp/mbimsig$CURRMODEM + rm -f /tmp/mbimmode$CURRMODEM + + ln -s $ROOTER/mbim/mbimdata.sh $ROOTER_LINK/getsignal$CURRMODEM + ln -s $ROOTER/connect/reconnect.sh $ROOTER_LINK/reconnect$CURRMODEM + ;; +esac + + $ROOTER_LINK/getsignal$CURRMODEM $CURRMODEM $PROT & + ln -s $ROOTER/connect/conmon.sh $ROOTER_LINK/con_monitor$CURRMODEM + $ROOTER_LINK/con_monitor$CURRMODEM $CURRMODEM & + uci set modem.modem$CURRMODEM.connected=1 + uci commit modem + + CLB=$(uci get modem.modeminfo$CURRMODEM.lb) + if [ -e /etc/config/mwan3 ]; then + ENB=$(uci get mwan3.wan$CURRMODEM.enabled) + if [ ! -z $ENB ]; then + if [ $CLB = "1" ]; then + uci set mwan3.wan$CURRMODEM.enabled=1 + else + uci set mwan3.wan$CURRMODEM.enabled=0 + fi + uci commit mwan3 + /usr/sbin/mwan3 restart + fi + fi diff --git a/ext-rooter-basic/files/usr/lib/rooter/connect/create_hostless.sh b/ext-rooter-basic/files/usr/lib/rooter/connect/create_hostless.sh new file mode 100644 index 0000000..85e150f --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/connect/create_hostless.sh @@ -0,0 +1,171 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter +ROOTER_LINK="/tmp/links" + +log() { + logger -t "Create Hostless Connection" "$@" +} + +handle_timeout(){ + local wget_pid="$1" + local count=0 + ps | grep -v grep | grep $wget_pid + res="$?" + while [ "$res" = 0 -a $count -lt "$((TIMEOUT))" ]; do + sleep 1 + count=$((count+1)) + ps | grep -v grep | grep $wget_pid + res="$?" + done + + if [ "$res" = 0 ]; then + log "Killing process on timeout" + kill "$wget_pid" 2> /dev/null + ps | grep -v grep | grep $wget_pid + res="$?" + if [ "$res" = 0 ]; then + log "Killing process on timeout" + kill -9 $wget_pid 2> /dev/null + fi + fi +} + +set_dns() { + local DNS1=$(uci get modem.modeminfo$CURRMODEM.dns1) + local DNS2=$(uci get modem.modeminfo$CURRMODEM.dns2) + if [ -z $DNS1 ]; then + if [ -z $DNS2 ]; then + return + else + uci set network.wan$CURRMODEM.peerdns=0 + uci set network.wan$CURRMODEM.dns=$DNS2 + fi + else + uci set network.wan$CURRMODEM.peerdns=0 + if [ -z $DNS2 ]; then + uci set network.wan$CURRMODEM.dns="$DNS1" + else + uci set network.wan$CURRMODEM.dns="$DNS2 $DNS1" + fi + fi +} + +set_network() { + uci delete network.wan$CURRMODEM + uci set network.wan$CURRMODEM=interface + uci set network.wan$CURRMODEM.proto=dhcp + uci set network.wan$CURRMODEM.ifname=$1 + uci set network.wan$CURRMODEM.metric=$CURRMODEM"0" + set_dns + uci commit network + sleep 5 +} + +save_variables() { + echo 'MODSTART="'"$MODSTART"'"' > /tmp/variable.file + echo 'WWAN="'"$WWAN"'"' >> /tmp/variable.file + echo 'USBN="'"$USBN"'"' >> /tmp/variable.file + echo 'ETHN="'"$ETHN"'"' >> /tmp/variable.file + echo 'WDMN="'"$WDMN"'"' >> /tmp/variable.file + echo 'BASEPORT="'"$BASEPORT"'"' >> /tmp/variable.file +} + +CURRMODEM=$1 +source /tmp/variable.file + +MAN=$(uci get modem.modem$CURRMODEM.manuf) +MOD=$(uci get modem.modem$CURRMODEM.model) +$ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Connecting" + +$ROOTER/log/logger "Attempting to Connect Modem #$CURRMODEM ($MAN $MOD)" +log "Checking Network Interface" +set_network usb$USBN +if + ifconfig usb$USBN +then + log "Using usb$USBN as network interface" + uci set modem.modem$CURRMODEM.interface=usb$USBN + USBN=`expr 1 + $USBN` +else + set_network eth$ETHN + if + ifconfig eth$ETHN + then + log "Using eth$ETHN as network interface" + uci set modem.modem$CURRMODEM.interface=eth$ETHN + ETHN=`expr 1 + $ETHN` + fi +fi +uci commit modem + +BASEP=$(uci get modem.modem$CURRMODEM.baseport) +idV=$(uci get modem.modem$CURRMODEM.idV) +idP=$(uci get modem.modem$CURRMODEM.idP) +if [ $idV = 1546 -a $idP = 1146 ]; then + CPORT=1 + lua $ROOTER/common/modemchk.lua "$idV" "$idP" "$CPORT" "$CPORT" + source /tmp/parmpass + CPORT=`expr $CPORT + $BASEP` + uci set modem.modem$CURRMODEM.commport=$CPORT + uci commit modem + $ROOTER/sms/check_sms.sh $CURRMODEM & + $ROOTER/common/gettype.sh $CURRMODEM & +fi + +save_variables +rm -f /tmp/usbwait + +ifup wan$CURRMODEM +while `ifstatus wan$CURRMODEM | grep -q '"up": false\|"pending": true'`; do + sleep 1 +done +wan_ip=$(expr "`ifstatus wan$CURRMODEM | grep '"nexthop":'`" : '.*"nexthop": "\(.*\)"') +if [ $? -ne 0 ] ; then + wan_ip=192.168.0.1 +fi +uci set modem.modem$CURRMODEM.ip=$wan_ip +uci commit modem + +$ROOTER/log/logger "HostlessModem #$CURRMODEM Connected with IP $wan_ip" + +VENDOR=$(uci get modem.modem$CURRMODEM.idV) +case $VENDOR in +"19d2" ) + TIMEOUT=3 + wget -O /tmp/connect.file http://$wan_ip/goform/goform_set_cmd_process?goformId=CONNECT_NETWORK & + handle_timeout "$!" + ln -s $ROOTER/signal/ztehostless.sh $ROOTER_LINK/getsignal$CURRMODEM + $ROOTER_LINK/getsignal$CURRMODEM $CURRMODEM $PROT & + ;; +"12d1" ) + log "Huawei Hostless" + ln -s $ROOTER/signal/huaweihostless.sh $ROOTER_LINK/getsignal$CURRMODEM + $ROOTER_LINK/getsignal$CURRMODEM $CURRMODEM $PROT & + ;; +* ) + log "Other Hostless" + ln -s $ROOTER/signal/otherhostless.sh $ROOTER_LINK/getsignal$CURRMODEM + $ROOTER_LINK/getsignal$CURRMODEM $CURRMODEM $PROT & + ;; +esac + +ln -s $ROOTER/connect/conmon.sh $ROOTER_LINK/con_monitor$CURRMODEM +$ROOTER_LINK/con_monitor$CURRMODEM $CURRMODEM & + +CLB=$(uci get modem.modeminfo$CURRMODEM.lb) +if [ -e /etc/config/mwan3 ]; then + ENB=$(uci get mwan3.wan$CURRMODEM.enabled) + if [ ! -z $ENB ]; then + if [ $CLB = "1" ]; then + uci set mwan3.wan$CURRMODEM.enabled=1 + else + uci set mwan3.wan$CURRMODEM.enabled=0 + fi + uci commit mwan3 + /usr/sbin/mwan3 restart + fi +fi + + + diff --git a/ext-rooter-basic/files/usr/lib/rooter/connect/disablemw3.sh b/ext-rooter-basic/files/usr/lib/rooter/connect/disablemw3.sh new file mode 100644 index 0000000..03af128 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/connect/disablemw3.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +CURRMODEM=$1 + +if [ -e /etc/config/mwan3 ]; then + ENB=$(uci get mwan3.wan$CURRMODEM.enabled) + if [ ! -z $ENB ]; then + uci set mwan3.wan$CURRMODEM.enabled=0 + uci commit mwan3 + fi +fi \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/connect/disconnect.sh b/ext-rooter-basic/files/usr/lib/rooter/connect/disconnect.sh new file mode 100644 index 0000000..abb91e6 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/connect/disconnect.sh @@ -0,0 +1,74 @@ +#!/bin/sh + +local CURRMODEM + +ROOTER=/usr/lib/rooter +ROOTER_LINK="/tmp/links" +TIMEOUT=10 + +log() { + logger -t "Disconnect Modem" "$@" +} + +handle_timeout(){ + local wget_pid="$1" + local count=0 + res=1 + if [ -d /proc/${wget_pid} ]; then + res=0 + fi + while [ "$res" = 0 -a $count -lt "$((TIMEOUT))" ]; do + sleep 1 + count=$((count+1)) + res=1 + if [ -d /proc/${wget_pid} ]; then + res=0 + fi + done + + if [ "$res" = 0 ]; then + log "Killing process on timeout" + kill "$wget_pid" 2> /dev/null + res=1 + if [ -d /proc/${wget_pid} ]; then + res=0 + fi + if [ "$res" = 0 ]; then + log "Killing process on timeout" + kill -9 $wget_pid 2> /dev/null + fi + fi +} + +CURRMODEM=$(uci get modem.general.miscnum) +uci set modem.modem$CURRMODEM.connected=0 +uci commit modem + +killall -9 getsignal$CURRMODEM +rm -f $ROOTER_LINK/getsignal$CURRMODEM +killall -9 con_monitor$CURRMODEM +rm -f $ROOTER_LINK/con_monitor$CURRMODEM +ifdown wan$CURRMODEM + +MAN=$(uci get modem.modem$CURRMODEM.manuf) +MOD=$(uci get modem.modem$CURRMODEM.model) +$ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Disconnected" + +PROT=$(uci get modem.modem$CURRMODEM.proto) +CPORT=$(uci get modem.modem$CURRMODEM.commport) + +case $PROT in +"30" ) + ATCMDD="AT+CFUN=0;+CFUN=1,1" + $ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + ;; +"3" ) + WDMNX=$(uci get modem.modem$CURRMODEM.wdm) + umbim -n -t 3 -d /dev/cdc-wdm$WDMNX disconnect + ;; +* ) + $ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "reset.gcom" "$CURRMODEM" + ;; +esac + +$ROOTER/log/logger "Modem #$CURRMODEM was Manually Disconnected" diff --git a/ext-rooter-basic/files/usr/lib/rooter/connect/reconnect-ppp.sh b/ext-rooter-basic/files/usr/lib/rooter/connect/reconnect-ppp.sh new file mode 100644 index 0000000..a5b2754 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/connect/reconnect-ppp.sh @@ -0,0 +1,57 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter +ROOTER_LINK="/tmp/links" + +log() { + logger -t "Reconnect Modem" "$@" +} + +CURRMODEM=$1 +uci set modem.modem$CURRMODEM.connected=0 +uci commit modem + +killall -9 getsignal$CURRMODEM +rm -f $ROOTER_LINK/getsignal$CURRMODEM +killall -9 con_monitor$CURRMODEM +rm -f $ROOTER_LINK/con_monitor$CURRMODEM +ifdown wan$CURRMODEM +MAN=$(uci get modem.modem$CURRMODEM.manuf) +MOD=$(uci get modem.modem$CURRMODEM.model) +$ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Reconnecting" +PROT=$(uci get modem.modem$CURRMODEM.proto) + +CPORT=$(uci get modem.modem$CURRMODEM.commport) + +SEVR=$(uci get modem.modem$CURRMODEM.service) + +if [ $SEVR = 0 ]; then + COUNTER=1 + while [ $COUNTER -lt 6 ]; do + OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "reset.gcom" "$CURRMODEM") + ERROR="ERROR" + if `echo ${OX} | grep "${ERROR}" 1>/dev/null 2>&1` + then + log "Retry Reset" + sleep 3 + let COUNTER=COUNTER+1 + else + log "Modem Reset" + sleep 3 + $ROOTER/common/lockchk.sh $CURRMODEM + break + fi + done + if [ $COUNTER -lt 6 ]; then + ifup wan$CURRMODEM + else + log "Reset Failed for Modem $CURRMODEM" + $ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Failed to Reset" + fi +else + ifup wan$CURRMODEM +fi + + + + diff --git a/ext-rooter-basic/files/usr/lib/rooter/connect/reconnect.sh b/ext-rooter-basic/files/usr/lib/rooter/connect/reconnect.sh new file mode 100644 index 0000000..266887f --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/connect/reconnect.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter +ROOTER_LINK="/tmp/links" + +log() { + logger -t "Reconnect Modem" "$@" +} + +CURRMODEM=$1 +$ROOTER_LINK/create_proto$CURRMODEM $CURRMODEM 1 diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/auto.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/auto.gcom new file mode 100644 index 0000000..73544d9 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/auto.gcom @@ -0,0 +1,31 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +:start +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT!SCPROF?1^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s +:continue +exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult + print $s + return diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/baseinfo.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/baseinfo.gcom new file mode 100644 index 0000000..efa6c99 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/baseinfo.gcom @@ -0,0 +1,48 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.02 + waitquiet 1 0.2 + +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT+COPS=3,0;+COPS?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+COPS=3,2;+COPS?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "ATI^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+CGEQNEG=1^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+CNUM^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/cellinfo.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/cellinfo.gcom new file mode 100644 index 0000000..ab6b625 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/cellinfo.gcom @@ -0,0 +1,38 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.02 + waitquiet 0.2 0.2 + +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT+CGREG=2;+CGREG?;+CGREG=0^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+CEREG=2;+CEREG?;+CEREG=0^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+CGEQNEG=1^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/cgpaddr.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/cgpaddr.gcom new file mode 100644 index 0000000..126aef6 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/cgpaddr.gcom @@ -0,0 +1,46 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +:start +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT+CGPADDR=1^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT\^SYSINFOEX^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT\^SYSINFO^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT\^NDISSTATQRY?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + + +:continue + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/connect-directip.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/connect-directip.gcom new file mode 100644 index 0000000..00bc9e4 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/connect-directip.gcom @@ -0,0 +1,64 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +:start +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT!SCACT=0,1^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT!SCDFTPROF=1^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +let $x=$env("SETAPN") +let $y=$env("SETUSER") +let $z=$env("SETPASS") +let $a=$env("SETAUTH") +send "AT+CGDCONT=1,\"IP\",\"" +send $x +send "\"^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT$QCPDPP=1," +send $a +if $a="0" send "^m" +else send ",\"" send $z send "\",\"" send $y send "\"^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT!SCACT=1,1^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT!SCACT?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +:continue + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/connect-ncm.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/connect-ncm.gcom new file mode 100644 index 0000000..1fd4893 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/connect-ncm.gcom @@ -0,0 +1,48 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +:start +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT^^NDISDUP=1,0^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +let $x=$env("SETAPN") +let $y=$env("SETUSER") +let $z=$env("SETPASS") +let $a=$env("SETAUTH") +send "AT^^NDISDUP=1,1,\"" +send $x +if $a="0" send "\"^m" +else send "\",\"" send $y send "\",\"" send $z send "\"," send $a send "^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT\^NDISSTATQRY?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +:continue + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult + diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/connect-ppp.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/connect-ppp.gcom new file mode 100644 index 0000000..fadf2d8 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/connect-ppp.gcom @@ -0,0 +1,36 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +:start +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +let $y=$env("SETUSER") +let $z=$env("SETPASS") +let $a=$env("SETAUTH") +send "AT$QCPDPP=1," +send $a +if $a="0" send "^m" +else send ",\"" send $z send "\",\"" send $y send "\"^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +:continue + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/curc.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/curc.gcom new file mode 100644 index 0000000..59f79da --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/curc.gcom @@ -0,0 +1,31 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +:start +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT\^CURC=0^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +:continue + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult + diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/gcom-locked b/ext-rooter-basic/files/usr/lib/rooter/gcom/gcom-locked new file mode 100644 index 0000000..f04f3d5 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/gcom-locked @@ -0,0 +1,39 @@ +#!/bin/sh + +log() { + logger -t "gcom-locked " "$@" +} + +ROOTER_GCOM="/usr/lib/rooter/gcom/" + + +PORT=$1 +GCOM=$2 +CURRMODEM=$3 +ATC=$4 + +LOCKDIR="/tmp/lockgcom$CURRMODEM" +PIDFILE="${LOCKDIR}/PID" + +while [ 1 -lt 6 ]; do + if mkdir "${LOCKDIR}" &>/dev/null; then + echo "$$" > "${PIDFILE}" + if [ ! -z "$ATC" ]; then + export ATCMD="$ATC" + fi + OX=$(gcom -d $PORT -s $ROOTER_GCOM$GCOM 2>/dev/null) + /usr/lib/rooter/log/at-logger "$PORT $OX" + break + else + OTHERPID="$(cat "${PIDFILE}" 2>/dev/null)" + if [ $? = 0 ]; then + if ! kill -0 $OTHERPID &>/dev/null; then + rm -rf "${LOCKDIR}" + fi + fi + sleep 1 + fi +done + +rm -rf "${LOCKDIR}" +echo "$OX" \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/gettype.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/gettype.gcom new file mode 100644 index 0000000..77d733b --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/gettype.gcom @@ -0,0 +1,43 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.02 + waitquiet 0.2 0.2 + +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT+CFUN=1^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "ATI^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+CNUM^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+CPBR=?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/huaweiinfo.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/huaweiinfo.gcom new file mode 100644 index 0000000..fb317e6 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/huaweiinfo.gcom @@ -0,0 +1,64 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.02 + waitquiet 0.2 0.2 + +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT+CSQ^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT\^CSNR?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT\^SYSINFOEX^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT\^SYSCFGEX?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT\^SYSCFG?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT\^SYSINFO^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT\^HFREQINFO?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT\^LTERSRP?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult + diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/lock-prov.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/lock-prov.gcom new file mode 100644 index 0000000..332890a --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/lock-prov.gcom @@ -0,0 +1,38 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +:start +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT+COPS=0^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +let $x=$env("MCCMNC") +send "AT+COPS=1,2,\"" +send $x +send "\",2^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +:continue + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/otherinfo.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/otherinfo.gcom new file mode 100644 index 0000000..6f851cd --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/otherinfo.gcom @@ -0,0 +1,28 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.02 + waitquiet 0.2 0.2 + +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT+CSQ^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/reset.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/reset.gcom new file mode 100644 index 0000000..8b25ba0 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/reset.gcom @@ -0,0 +1,26 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +//send "ate0^m" +//waitquiet 1 0.2 +send "AT+COPS=2;+CFUN=0^m" +waitfor 5 "OK" +sleep 5 +send "AT+CFUN?^m" +waitfor 5 "+CME ERROR:","+CFUN:" +if % = 0 goto cme_err +get 2 " " $s +if $left($s,1) = "1" goto end +sleep 5 +send "AT+CFUN=1^m" +waitquiet 1 0.2 +exit 0 + +:cme_err +print "+CME ERROR" +:end +exit 0 + diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/run-at.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/run-at.gcom new file mode 100644 index 0000000..b40dd6e --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/run-at.gcom @@ -0,0 +1,31 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +:start +let $g=$env("TIMEOUT") +if $g="" let f=25 +else let f=val($g) + +let $x=$env("ATCMD") +send $x +send "^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s +:continue +exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/sendsms-at.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/sendsms-at.gcom new file mode 100644 index 0000000..7203af5 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/sendsms-at.gcom @@ -0,0 +1,45 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +:start + +let $x=$env("ATCMD") +let $j=$left($x,1) +if $j = "0" let $j=$mid($x,1,2) +else let $j=$left($x,3) +let $r=$mid($x,4,4) +let k=len($x)-9 +let $p=$right($x,k) +send "AT+CMGF=0^m" +waitfor 2 "OK" +send "AT+CMGS=" +send $j +send "^m" +waitfor 3 ">" +send $p +send "^z" +waitfor 20 "+CMGS:" + +if % = 0 gosub sentsub +else gosub failsub + +get 1 "" $s + +:continue + exit 0 + +:sentsub +get 2 "^m^j" $s +let $c=$s +print "SMS sent, reference: "+$c +let $c='echo "SMS sent, reference: "'+$c+' > /tmp/smssendstatus'+$r +system $c +return + +:failsub +system 'echo "SMS sending failed" > /tmp/smssendstatus'+$r +print "SMS sending failed" +return diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/setapn.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/setapn.gcom new file mode 100644 index 0000000..9b29c50 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/setapn.gcom @@ -0,0 +1,45 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +:start +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +let $x=$env("SETAPN") +let $y=$env("SETUSER") +let $z=$env("SETPASS") +let $a=$env("SETAUTH") +send "AT+CGDCONT=1,\"IP\",\"" +send $x +send "\"^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT$QCPDPP=1," +send $a +if $a="0" send "^m" +else send ",\"" send $z send "\",\"" send $y send "\"^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +:continue + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult + diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/setpin.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/setpin.gcom new file mode 100644 index 0000000..9084557 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/setpin.gcom @@ -0,0 +1,53 @@ +# set pin code from evnironment "$PINCODE" +opengt + set com 115200n81 + set senddelay 0.05 + waitquiet 3 0.5 + flash 0.1 + + let c=0 +:start + send "AT+CPIN?^m" + waitfor 15 "SIM PUK","SIM PIN","READY","ERROR","ERR" + if % = -1 goto timeout + if % = 0 goto ready + if % = 1 goto setpin + if % = 2 goto ready + if % = 3 goto checkrepeat + if % = 4 goto checkrepeat + +:checkrepeat + inc c + if c>3 goto pinerror + waitquiet 12 0.5 + goto start + +:timeout + print "ERROR" + exit 1 + +:ready + goto continue + exit 0 + +:setpin + # check if output was "SIM PIN2", that's ok. + waitfor 1 "2" + if % = 0 goto ready + + send "AT+CPIN=\"" + send $env("PINCODE") + send "\"^m" + + waitfor 20 "OK","ERR" + if % = -1 goto pinerror + if % = 0 goto continue + if % = 1 goto pinerror + +:pinerror + print "ERROR" + exit 1 + +:continue + print "OK" + exit 0 diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/sierrainfo.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/sierrainfo.gcom new file mode 100644 index 0000000..81448d5 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/sierrainfo.gcom @@ -0,0 +1,58 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.02 + waitquiet 0.2 0.2 + +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT+CSQ^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT*CNTI=0^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT!SELRAT?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+ECIO?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+RSCP?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT!UMTSCHAN?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT!GSTATUS?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/smschk.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/smschk.gcom new file mode 100644 index 0000000..f5d9e82 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/smschk.gcom @@ -0,0 +1,36 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +:start +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT+CMGS=?;+CMGL=?;+CMGR=?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send 'AT+CPMS="SM","SM","SM"^m' +waitfor 5 "OK" + +send 'AT+CGSMS=2^m' +waitfor 5 "OK" + +:continue + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/smswrite.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/smswrite.gcom new file mode 100644 index 0000000..a65bb3c --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/smswrite.gcom @@ -0,0 +1,39 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 + +:start + +let $x=$env("ATCMD") +let $j=$left($x,1) +if $j = "0" let $j=$mid($x,1,2) +else let $j=$left($x,3) +let $d=$mid($x,4,2) +let $t=$mid($x,7,1) +let k=len($x)-9 +let $p=$right($x,k) +send "AT+CMGF=0^m" +waitfor 2 "OK" +send 'AT+CPMS="SM"' +send ',"' +send $d +send '"^m' +waitfor 2 "OK" +send "AT+CMGW=" +send $j +send "," +send $t +send "^m" +waitfor 3 ">" +send $p +send "^z" +waitfor 25 "+CMGW:" +if % = 0 print "+CMGW:" +else print "AT+CMGW - TIMEOUT" +get 1 "" $s +print $s + +:continue + exit 0 diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/ubloxinfo.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/ubloxinfo.gcom new file mode 100644 index 0000000..9ebe9dc --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/ubloxinfo.gcom @@ -0,0 +1,43 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.02 + waitquiet 0.2 0.2 + +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT+CSQ^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+CESQ^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+URAT?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+UCGED?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/ussd.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/ussd.gcom new file mode 100644 index 0000000..1bfb48f --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/ussd.gcom @@ -0,0 +1,24 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.05 + waitquiet 1 0.2 +:start +let f=20 +let $x=$env("ATCMD") +send $x+"^m" +waitfor 2 "OK" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s +:continue +exit 0 +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $mid($s,0,6)="^jERROR" return +if $right(" "+$s,4) = '",15' return +if time()>t return +goto getresult diff --git a/ext-rooter-basic/files/usr/lib/rooter/gcom/zteinfo.gcom b/ext-rooter-basic/files/usr/lib/rooter/gcom/zteinfo.gcom new file mode 100644 index 0000000..64c09f4 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gcom/zteinfo.gcom @@ -0,0 +1,48 @@ +opengt + set com 115200n81 + set comecho off + set senddelay 0.02 + waitquiet 0.2 0.2 + +let $g=$env("TIMEOUT") +if $g = "" let f=25 +else let f = val($g) + +send "AT+CSQ^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+ZPAS?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+ZRSSI^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+ZRSSI?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + +send "AT+ZSNT?^m" +let t=time()+f +gosub getresult +if $s="^mTIMEOUT ERROR" print $s + + exit 0 + +:getresult +get 1 "^m" $s +let x=len($s) +if x=0 let $s="^mTIMEOUT ERROR" +else print $s +if $s="^jOK" return +if $mid($s,0,6)="^jERROR" return +if $mid($s,0,8)="^jCOMMAND" return +if $mid($s,0,11)="^j+CME ERROR" return +if time()>t return +goto getresult \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/gpiomodel.lua b/ext-rooter-basic/files/usr/lib/rooter/gpiomodel.lua new file mode 100644 index 0000000..7e6e7bb --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/gpiomodel.lua @@ -0,0 +1,125 @@ +#!/usr/bin/lua + +mfile = "/tmp/sysinfo/model" +echo = 1 +model = {} +gpio = {} +gpio2 = {} + +pin = nil +pin2 = nil + +model[1] = "703n" +gpio[1] = 8 +model[2] = "3020" +gpio[2] = 8 +model[3] = "11u" +gpio[3] = 8 +model[4] = "3040" +gpio[4] = 18 +model[5] = "3220" +gpio[5] = 6 +model[6] = "3420" +gpio[6] = 6 +model[7] = "wdr3500" +gpio[7] = 12 +model[8] = "wdr3600" +gpio[8] = 22 +gpio2[8] = 21 +model[9] = "wdr4300" +gpio[9] = 22 +gpio2[9] = 21 +model[10] = "wdr4310" +gpio[10] = 22 +gpio2[10] = 21 +model[11] = "842" +gpio[11] = 6 +model[12] = "13u" +gpio[12] = 18 +model[13] = "710n" +gpio[13] = 8 +model[14] = "10u" +gpio[14] = 18 +model[15] = "oolite" +gpio[15] = 18 +model[16] = "720" +gpio[16] = 8 +model[17] = "1043" +gpio[17] = 21 +model[18] = "4530" +gpio[18] = 22 +model[19] = "archer" +gpio[19] = 22 +gpio2[19] = 21 +model[20] = "ar150" +gpio[20] = 6 +model[21] = "domino" +gpio[21] = 6 +model[22] = "300a" +gpio[22] = 0 +model[23] = "300n" +gpio[23] = 7 +model[24] = "wdr4900" +gpio[24] = 10 + +numodel = 24 + +local file = io.open(mfile, "r") +if file == nil then + return +end + +line = file:read("*line") +file:close() +line = line:lower() + +for i=1,numodel do + start, ends = line:find(model[i]) + if start ~= nil then + if model[i] == "3420" then + start, ends = line:find("v1") + if start ~= nil then + pin = gpio[i] + pin2 = nil + else + pin = 4 + pin2 = nil + end + else + if model[i] == "3220" then + start, ends = line:find("v1") + if start ~= nil then + pin = gpio[i] + pin2 = nil + else + pin = 8 + pin2 = nil + end + else + if model[i] == "1043" then + start, ends = line:find("v2") + if start ~= nil then + pin = gpio[i] + pin2 = nil + end + else + pin = gpio[i] + pin2 = gpio2[i] + end + end + end + + break + end +end + +if pin ~= nil then + local tfile = io.open("/tmp/gpiopin", "w") + if pin2 ~= nil then + tfile:write("GPIOPIN=\"", pin, "\"\n") + tfile:write("GPIOPIN2=\"", pin2, "\"") + else + tfile:write("GPIOPIN=\"", pin, "\"") + end + tfile:close() +end diff --git a/ext-rooter-basic/files/usr/lib/rooter/idown.lua b/ext-rooter-basic/files/usr/lib/rooter/idown.lua new file mode 100644 index 0000000..cda3061 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/idown.lua @@ -0,0 +1,22 @@ +#!/usr/bin/lua + +cmd = arg[1] +interface = arg[2] + +function trim(s) + return (s:gsub("^%s*(.-)%s*$", "%1")) +end + +if interface ~= "wan" and interface ~= "wwan" then + s, e = interface:find("wan") + if s ~= nil then + mnum = trim(interface:sub(e+1)) + if cmd == "1" then + os.execute("/usr/lib/rooter/connect/disablemw3.sh " .. mnum) + line = "echo \"0\" > /tmp/mdown" .. mnum + else + line = "rm -f /tmp/mdown" .. mnum + end + os.execute(line) + end +end \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/initialize.sh b/ext-rooter-basic/files/usr/lib/rooter/initialize.sh new file mode 100644 index 0000000..6ee2c0d --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/initialize.sh @@ -0,0 +1,292 @@ +#!/bin/sh +. /lib/functions.sh + +ROOTER=/usr/lib/rooter +ROOTER_LINK="/tmp/links" + +CODENAME="ROOter " +if [ -f "/etc/codename" ]; then + source /etc/codename +fi + +# +# Set the maximum number of modems supported +# +MAX_MODEMS=2 +MODCNT=$MAX_MODEMS + +log() { + logger -t "ROOter Initialize" "$@" +} + +do_zone() { + local config=$1 + local name + local network + + config_get name $1 name + config_get network $1 network + newnet=$network + if [ $name = wan ]; then + WAN1=$(echo $network | grep "wan1") + if [ -z $WAN1 ]; then + COUNTER=1 + while [ $COUNTER -le $MODCNT ]; do + newnet="$newnet wan$COUNTER" + let COUNTER=COUNTER+1 + done + uci_set firewall "$config" network "$newnet" + uci_commit firewall + /etc/init.d/firewall restart + fi + fi +} + +firstboot() { + HO=$(uci get system.@system[-1].hostname) + if [ $HO = "OpenWrt" ]; then + uci set system.@system[-1].hostname="ROOter" + echo "ROOter" > /proc/sys/kernel/hostname + fi + if [ $HO = "LEDE" ]; then + uci set system.@system[-1].hostname="ROOter" + echo "ROOter" > /proc/sys/kernel/hostname + fi + uci set system.@system[-1].cronloglevel="9" + uci commit system + + log "ROOter First Boot finalized" + + config_load firewall + config_foreach do_zone zone + + uci set luci.main.mediaurlbase="/luci-static/rooter" + uci commit luci +} + +if [ -e /tmp/installing ]; then + exit 0 +fi + +log " Initializing Rooter" + +sed -i -e 's|/etc/savevar|#removed line|g' /etc/rc.local + +[ -f "/etc/firstboot" ] || { + firstboot +} + +mkdir -p $ROOTER_LINK + +uci delete modem.Version +uci set modem.Version=version +uci set modem.Version.ver=$CODENAME +uci commit modem + +source /etc/openwrt_release +rm -f /etc/openwrt_release +DISTRIB_DESCRIPTION=$(uci get modem.Version.ver)" ( "$DISTRIB_ID" "$DISTRIB_RELEASE" "$DISTRIB_REVISION" )" +echo 'DISTRIB_ID="'"$DISTRIB_ID"'"' >> /etc/openwrt_release +echo 'DISTRIB_RELEASE="'"$DISTRIB_RELEASE"'"' >> /etc/openwrt_release +echo 'DISTRIB_REVISION="'"$DISTRIB_REVISION"'"' >> /etc/openwrt_release +echo 'DISTRIB_CODENAME="'"$DISTRIB_CODENAME"'"' >> /etc/openwrt_release +echo 'DISTRIB_TARGET="'"$DISTRIB_TARGET"'"' >> /etc/openwrt_release +echo 'DISTRIB_DESCRIPTION="'"$DISTRIB_DESCRIPTION"'"' >> /etc/openwrt_release + +if `cat /tmp/sysinfo/model | grep "A5-V11" 1>/dev/null 2>&1` +then + swconfig dev switch0 port 1 set disable 1 + swconfig dev switch0 port 2 set disable 1 + swconfig dev switch0 port 3 set disable 1 + swconfig dev switch0 port 4 set disable 1 + swconfig dev switch0 set apply + uci delete system.led_wifi + uci set system.led_wifi=led + uci set system.led_wifi.default="0" + uci set system.led_wifi.name="WIFI" + uci set system.led_wifi.sysfs="a5-v11:blue:system" + uci set system.led_wifi.trigger="netdev" + uci set system.led_wifi.dev="wlan0" + uci set system.led_wifi.mode="link tx rx" + uci commit system +fi +#if `cat /tmp/sysinfo/model | grep "Mikrotik" 1>/dev/null 2>&1` +#then +# PS=$(uci get system.usb_power_switch.value) +# if [ ! $PS = "0" ]; then +# uci set system.usb_power_switch.value="0" +# uci commit system +# #reboot +# fi +#fi + +MODSTART=1 +WWAN=0 +USBN=0 +ETHN=1 +BASEPORT=0 +WDMN=0 +if + ifconfig eth1 +then + if [ -e "/sys/class/net/eth1/device/bInterfaceProtocol" ]; then + ETHN=1 + else + ETHN=2 + fi +fi + +echo 'MODSTART="'"$MODSTART"'"' > /tmp/variable.file +echo 'WWAN="'"$WWAN"'"' >> /tmp/variable.file +echo 'USBN="'"$USBN"'"' >> /tmp/variable.file +echo 'ETHN="'"$ETHN"'"' >> /tmp/variable.file +echo 'WDMN="'"$WDMN"'"' >> /tmp/variable.file +echo 'BASEPORT="'"$BASEPORT"'"' >> /tmp/variable.file + +echo 'MODCNTX="'"$MODCNT"'"' > /tmp/modcnt +uci set modem.general.max=$MODCNT +uci set modem.general.modemnum=1 +uci set modem.general.smsnum=1 +uci set modem.general.miscnum=1 + +OPING=$(uci get modem.ping.alive) +if [ ! -z $OPING ]; then + uci delete modem.ping +fi + +COUNTER=1 +while [ $COUNTER -le $MODCNT ]; do + uci delete modem.modem$COUNTER + uci set modem.modem$COUNTER=modem + uci set modem.modem$COUNTER.empty=1 + + IPEX=$(uci get modem.pinginfo$COUNTER.alive) + if [ -z $IPEX ]; then + uci set modem.pinginfo$COUNTER=pinfo$COUNTER + uci set modem.pinginfo$COUNTER.alive="0" + fi + + INEX=$(uci get modem.modeminfo$COUNTER) + if [ -z $INEX ]; then + uci set modem.modeminfo$COUNTER=minfo$COUNTER + fi + + rm -f $ROOTER_LINK/getsignal$COUNTER + rm -f $ROOTER_LINK/reconnect$COUNTER + rm -f $ROOTER_LINK/create_proto$COUNTER + $ROOTER/signal/status.sh $COUNTER "No Modem Present" + + uci delete network.wan$COUNTER + uci set network.wan$COUNTER=interface + uci set network.wan$COUNTER.proto=dhcp + uci set network.wan$COUNTER.metric=$COUNTER"0" + + if [ -e /etc/config/mwan3 ]; then + ENB=$(uci get mwan3.wan$COUNTER.enabled) + if [ ! -z $ENB ]; then + uci set mwan3.wan$COUNTER.enabled=0 + fi + fi + + if [ -e /etc/config/failover ]; then + uci delete failover.Modem$COUNTER + uci set failover.Modem$COUNTER=member + fi + + let COUNTER=COUNTER+1 +done + +if [ -e /etc/config/failover ]; then + uci delete failover.Wan + EXX=$(uci get network.wan) + if [ ! -z $EXX ]; then + uci set failover.Wan=member + fi + uci delete failover.Hotspot + uci set failover.Hotspot=member + uci commit failover + ENB=$(uci get failover.enabled.enabled) + if [ $ENB = "1" ]; then + if [ -e $ROOTER/connect/failover.sh ]; then + log "Starting Failover System" + $ROOTER/connect/failover.sh & + fi + fi +fi + +PRO=$(uci get network.wan.proto) +if [ ! -z $PRO ]; then + uci set network.wan.metric="1" +fi + +SM=$(uci get modem.sms) +if [ -z $SM ]; then + uci set modem.sms="sms" + uci set modem.sms.menable="0" + uci set modem.sms.slots="0" +fi + +uci commit modem +uci commit network +if [ -e /etc/config/mwan3 ]; then + uci commit mwan3 +fi + +if [ -e $ROOTER/removeipv6.sh ]; then + $ROOTER/removeipv6.sh +fi + +if [ -e /etc/hotplug.d/10-motion ]; then + rm -f /etc/hotplug.d/10-motion +fi +if [ -e /etc/hotplug.d/20-mjpg-streamer ]; then + rm -f /etc/hotplug.d/20-mjpg-streamer +fi +if [ -e /etc/hotplug.d/50-printer ]; then + rm -f /etc/hotplug.d/50-printer +fi +if [ -e $ROOTER/special.sh ]; then + $ROOTER/special.sh +fi + +lua $ROOTER/gpiomodel.lua + +HO=$(uci get system.@system[-1].hostname) +if [ $HO = "OpenWrt" ]; then + uci set system.@system[-1].hostname="ROOter" + uci commit system +fi + +if [ -e /usr/lib/lua/luci/model/cbi/admin_system/cronnew.lua ]; then + mv -f /usr/lib/lua/luci/model/cbi/admin_system/cronnew.lua /usr/lib/lua/luci/model/cbi/admin_system/crontab.lua +fi +if [ -e /usr/lib/lua/luci/view/admin_status/indexnew.htm ]; then + mv -f /usr/lib/lua/luci/view/admin_status/indexnew.htm /usr/lib/lua/luci/view/admin_status/index.htm +fi + +if [ -f "/etc/firstboot" ]; then + echo 'FIRSTBOOT="'"1"'"' > /etc/firstboot +else + echo 'FIRSTBOOT="'"0"'"' > /etc/firstboot + echo 'BOOTTIME="'"$(date +%s)"'"' > /tmp/boottime +fi + +# +# Added modems to various drivers +# +echo "2001 7e35" > /sys/bus/usb-serial/drivers/option1/new_id +echo "2001 7e35" > /sys/bus/usb/drivers/qmi_wwan/new_id +if [ "$DISTRIB_ID" = "OpenWrt" ]; then + echo "1199 9071" > /sys/bus/usb-serial/drivers/option1/new_id + echo "1199 9079" > /sys/bus/usb-serial/drivers/option1/new_id + echo "1199 9041" > /sys/bus/usb-serial/drivers/option1/new_id +fi +echo "413c 81b6" > /sys/bus/usb-serial/drivers/option1/new_id +echo "1e0e 9001" > /sys/bus/usb/drivers/qmi_wwan/new_id +echo "1546 1146" > /sys/bus/usb-serial/drivers/option1/new_id +echo "106c 3718" > /sys/bus/usb-serial/drivers/option1/new_id + +# end of booup +echo "0" > /tmp/bootend.file + + diff --git a/ext-rooter-basic/files/usr/lib/rooter/log/at-logger b/ext-rooter-basic/files/usr/lib/rooter/log/at-logger new file mode 100644 index 0000000..ca7d6e6 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/log/at-logger @@ -0,0 +1,12 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +TEXT=$1 +DATE=$(date +%c) + +echo " " >> /tmp/atlog +echo "$DATE : $TEXT" >> /tmp/atlog +lua $ROOTER/log/rotate.lua /tmp/atlog /tmp/attlog +mv /tmp/attlog /tmp/atlog + diff --git a/ext-rooter-basic/files/usr/lib/rooter/log/logger b/ext-rooter-basic/files/usr/lib/rooter/log/logger new file mode 100644 index 0000000..e78b574 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/log/logger @@ -0,0 +1,17 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +TEXT=$1 +DATE=$(date +%c) + +wc -l $ROOTER/log/connect.log > /tmp/linecnt +read lcnt fle < /tmp/linecnt +rm -f /tmp/linecnt +if [ $lcnt -ge 20 ]; then + start=$((lcnt-1)) + tail +$start $ROOTER/log/connect.log > /tmp/connect.log + mv /tmp/connect.log $ROOTER/log/connect.log +fi + +echo "$DATE : $TEXT" >> $ROOTER/log/connect.log diff --git a/ext-rooter-basic/files/usr/lib/rooter/log/rotate.lua b/ext-rooter-basic/files/usr/lib/rooter/log/rotate.lua new file mode 100644 index 0000000..ccb9ebe --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/log/rotate.lua @@ -0,0 +1,32 @@ +#!/usr/bin/lua + +logfile = {} +infile = arg[1] +outfile = arg[2] + +i=0 +ifile = io.open(infile, "r") +if ifile == nil then + return +end +repeat + local line = ifile:read("*line") + if line == nil then + break + end + if string.len(line) > 1 then + i = i + 1 + logfile[i] = line + end +until 1==0 +ifile:close() +if i < 50 then + j = 1 +else + j = i - 49 +end +ofile = io.open(outfile, "w") +for k=j,i do + ofile:write(logfile[k] .. "\n") +end +ofile:close() \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/logprint.sh b/ext-rooter-basic/files/usr/lib/rooter/logprint.sh new file mode 100644 index 0000000..130bc3e --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/logprint.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# + +logger -t "Log Print " "$@" +exit 0 diff --git a/ext-rooter-basic/files/usr/lib/rooter/luci/atcmd.sh b/ext-rooter-basic/files/usr/lib/rooter/luci/atcmd.sh new file mode 100644 index 0000000..656e766 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/luci/atcmd.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter +ATCMDD=$1 + +CURRMODEM=$(uci get modem.general.modemnum) +COMMPORT="/dev/ttyUSB"$(uci get modem.modem$CURRMODEM.commport) + +M2=$(echo "$ATCMDD" | sed -e "s#~#\"#g") +COPS="at+cops=?" +M3=$(echo "$M2" | awk '{print tolower($0)}') +if `echo ${M3} | grep "${COPS}" 1>/dev/null 2>&1` +then + export TIMEOUT="75" +else + export TIMEOUT="5" +fi +OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$M2") +echo "$OX" > /tmp/result$CURRMODEM.at diff --git a/ext-rooter-basic/files/usr/lib/rooter/luci/celltype.sh b/ext-rooter-basic/files/usr/lib/rooter/luci/celltype.sh new file mode 100644 index 0000000..e225826 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/luci/celltype.sh @@ -0,0 +1,205 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "Cell type" "$@" +} + +CURRMODEM=$1 +COMMPORT="/dev/ttyUSB"$(uci get modem.modem$CURRMODEM.commport) + +VENDOR=$(uci get modem.modem$CURRMODEM.idV) + +case $VENDOR in + "1199"|"0f3d" ) + ATCMDD="AT!SELRAT?" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + OX=$($ROOTER/common/processat.sh "$OX") + SELRAT=$(echo "$OX" | awk -F[,\ ] '/^\!SELRAT:/ {print $2}') + if [ "x$SELRAT" != "x" ]; then + NETMODE="-" + case $SELRAT in + "00" ) + NETMODE="1" + ;; + "01" ) + NETMODE="5" + ;; + "03" ) + NETMODE="4" + ;; + "02" ) + NETMODE="3" + ;; + "04" ) + NETMODE="2" + ;; + "05" ) + NETMODE="6" + ;; + "06" ) + NETMODE="7" + ;; + esac + fi + uci set modem.modem$CURRMODEM.modemtype="2" + uci set modem.modem$CURRMODEM.netmode=$NETMODE + uci commit modem + ;; + "19d2" ) + ATCMDD="AT+ZSNT?" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + OX=$($ROOTER/common/processat.sh "$OX") + ZSNT=$(echo "$OX" | awk -F[,\ ] '/^\+ZSNT:/ {print $2}') + if [ "x$ZSNT" != "x" ]; then + NETMODE="-" + if [ $ZSNT = "0" ]; then + ZSNTX=$(echo "$OX" | awk -F[,\ ] '/^\+ZSNT:/ {print $4}') + case $ZSNTX in + "0" ) + NETMODE="1" + ;; + "1" ) + NETMODE="2" + ;; + "2" ) + NETMODE="4" + ;; + "6" ) + NETMODE="6" + ;; + esac + else + case $ZSNT in + "1" ) + NETMODE="3" + ;; + "2" ) + NETMODE="5" + ;; + "6" ) + NETMODE="7" + ;; + esac + fi + fi + uci set modem.modem$CURRMODEM.modemtype="1" + uci set modem.modem$CURRMODEM.netmode=$NETMODE + uci commit modem + ;; + "12d1" ) + ATCMDD="AT^SYSCFGEX?" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + OX=$($ROOTER/common/processat.sh "$OX") + SYSCFG=$(echo "$OX" | awk -F[,\"] '/^\^SYSCFGEX:/ {print $2}') + if [ "x$SYSCFG" != "x" ]; then + NETMODE="-" + case $SYSCFG in + "00" ) + NETMODE="1" + ;; + "01" ) + NETMODE="3" + ;; + "03" ) + NETMODE="7" + ;; + * ) + ACQ=${SYSCFG:0:2} + case $ACQ in + "01" ) + NETMODE="2" + ;; + "02" ) + NETMODE="4" + ;; + "03" ) + NETMODE="6" + ;; + esac + ;; + esac + uci set modem.modem$CURRMODEM.modemtype="3" + else + ATCMDD="AT^SYSCFG?" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + OX=$($ROOTER/common/processat.sh "$OX") + SYSCFG=$(echo "$OX" | awk -F[,\ ] '/^\^SYSCFG:/ {print $2}') + if [ "x$SYSCFG" != "x" ]; then + NETMODE="-" + case $SYSCFG in + "7" ) + NETMODE="1" + ;; + "13" ) + NETMODE="3" + ;; + "14" ) + NETMODE="5" + ;; + * ) + SYSCFG=$(echo "$OX" | awk -F[,\ ] '/^\^SYSCFG:/ {print $3}') + case $SYSCFG in + "0" ) + NETMODE="1" + ;; + "1" ) + NETMODE="2" + ;; + "2" ) + NETMODE="4" + ;; + esac + ;; + esac + uci set modem.modem$CURRMODEM.modemtype="4" + fi + fi + uci set modem.modem$CURRMODEM.netmode=$NETMODE + uci commit modem + ;; + "1546" ) + ATCMDD="AT+URAT?" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + URAT=$(echo $OX" " | grep -o "+URAT: .\+ OK " | tr " " ",") + URAT1=$(echo $URAT | cut -d, -f2) + URAT2=$(echo $URAT | cut -d, -f3) + if [ -n "$URAT1" ]; then + MODTYPE="5" + NETMODE="-" + case $URAT1 in + "0" ) + NETMODE="3" + ;; + "2" ) + NETMODE="5" + ;; + "3" ) + NETMODE="7" + ;; + * ) + case $URAT2 in + "0" ) + NETMODE="2" + ;; + "2" ) + NETMODE="4" + ;; + "3" ) + NETMODE="1" + ;; + esac + ;; + esac + uci set modem.modem$CURRMODEM.modemtype="5" + fi + uci set modem.modem$CURRMODEM.netmode=$NETMODE + uci commit modem + ;; + * ) + NETMODE="-" + uci set modem.modem$CURRMODEM.netmode=$NETMODE + uci commit modem + ;; +esac diff --git a/ext-rooter-basic/files/usr/lib/rooter/luci/getlog.sh b/ext-rooter-basic/files/usr/lib/rooter/luci/getlog.sh new file mode 100644 index 0000000..fd42e9e --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/luci/getlog.sh @@ -0,0 +1,24 @@ +#!/bin/sh + + +wget -O /tmp/version.file http://www.ofmodemsandmen.com/download/version.roo +ret=$? + +if [ $ret = "1" ]; then + exit 1 +else + rm -f /tmp/change.file + wget -O /tmp/change.file http://www.ofmodemsandmen.com/download/change.roo + cret=$? + if [ $cret = "1" ]; then + rm -f /tmp/version.file + exit 1 + else + source /tmp/version.file + uci set modem.Version.last="$VER" + uci commit modem + rm -f /tmp/version.file + fi +fi + +exit 0 diff --git a/ext-rooter-basic/files/usr/lib/rooter/luci/guestwifi.sh b/ext-rooter-basic/files/usr/lib/rooter/luci/guestwifi.sh new file mode 100644 index 0000000..6717634 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/luci/guestwifi.sh @@ -0,0 +1,93 @@ +#!/bin/sh + +GUEST=$(uci get guestwifi.guestwifi.ssid) + +IP=$(uci get guestwifi.guestwifi.ip) +RADIO=$(uci get guestwifi.guestwifi.radio) +LIMIT=$(uci get guestwifi.guestwifi.limit) +DL=$(uci get guestwifi.guestwifi.dl) +UL=$(uci get guestwifi.guestwifi.ul) + +# Configure guest network +uci delete network.$GUEST +uci set network.$GUEST=interface +uci set network.$GUEST.proto=static +uci set network.$GUEST.ipaddr=$IP +uci set network.$GUEST.netmask=255.255.255.0 + +# Configure guest Wi-Fi +uci delete wireless.$GUEST +uci set wireless.$GUEST=wifi-iface +uci set wireless.$GUEST.device=$RADIO +uci set wireless.$GUEST.mode=ap +uci set wireless.$GUEST.network=$GUEST +uci set wireless.$GUEST.ssid=$GUEST +uci set wireless.$GUEST.encryption=none + +# Configure DHCP for guest network +uci delete dhcp.$GUEST +uci set dhcp.$GUEST=dhcp +uci set dhcp.$GUEST.interface=$GUEST +uci set dhcp.$GUEST.start=50 +uci set dhcp.$GUEST.limit=200 +uci set dhcp.$GUEST.leasetime=1h + +# Configure firewall for guest network +## Configure guest zone +uci delete firewall.$GUEST"_zone" +uci set firewall.$GUEST"_zone"=zone +uci set firewall.$GUEST"_zone".name=$GUEST +uci set firewall.$GUEST"_zone".network=$GUEST +uci set firewall.$GUEST"_zone".input=REJECT +uci set firewall.$GUEST"_zone".forward=REJECT +uci set firewall.$GUEST"_zone".output=ACCEPT +## Allow Guest -> Internet +uci delete firewall.$GUEST"_forwarding" +uci set firewall.$GUEST"_forwarding"=forwarding +uci set firewall.$GUEST"_forwarding".src=$GUEST +uci set firewall.$GUEST"_forwarding".dest=wan +## Allow DNS Guest -> Router +uci delete firewall.$GUEST"_rule_dns" +uci set firewall.$GUEST"_rule_dns"=rule +uci set firewall.$GUEST"_rule_dns".name="Allow "$GUEST" DNS Queries" +uci set firewall.$GUEST"_rule_dns".src=$GUEST +uci set firewall.$GUEST"_rule_dns".dest_port=53 +uci set firewall.$GUEST"_rule_dns".proto=tcpudp +uci set firewall.$GUEST"_rule_dns".target=ACCEPT +## Allow DHCP Guest -> Router +uci delete firewall.$GUEST"_rule_dhcp" +uci set firewall.$GUEST"_rule_dhcp"=rule +uci set firewall.$GUEST"_rule_dhcp".name="Allow "$GUEST" DHCP request" +uci set firewall.$GUEST"_rule_dhcp".src=$GUEST +uci set firewall.$GUEST"_rule_dhcp".src_port=68 +uci set firewall.$GUEST"_rule_dhcp".dest_port=67 +uci set firewall.$GUEST"_rule_dhcp".proto=udp +uci set firewall.$GUEST"_rule_dhcp".target=ACCEPT + +uci commit +/etc/init.d/network restart +/etc/init.d/dnsmasq restart +/etc/init.d/firewall restart + +if [ -e /etc/config/sqm ]; then + IFACE="$(iwinfo | grep "ESSID" | grep $GUEST)" + WI=${IFACE% *} + WI=${WI% *} + uci delete sqm.$GUEST + if [ $LIMIT = "1" ]; then + uci set sqm.$GUEST=queue + uci set sqm.$GUEST.interface=$WI + uci set sqm.$GUEST.enabled=1 + uci set sqm.$GUEST.upload=$UL + uci set sqm.$GUEST.download=$DL + uci set sqm.$GUEST.qdisc='cake' + uci set sqm.$GUEST.script='layer_cake.qos' + uci set sqm.$GUEST.qdisc_advanced='0' + uci set sqm.$GUEST.linklayer='none' + uci commit sqm + /etc/init.d/sqm start + /etc/init.d/sqm enable + fi +fi + + diff --git a/ext-rooter-basic/files/usr/lib/rooter/luci/luaops.sh b/ext-rooter-basic/files/usr/lib/rooter/luci/luaops.sh new file mode 100644 index 0000000..b05855f --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/luci/luaops.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +COMMAND=$1 +FILE=$2 + +if [ $COMMAND = delete ]; then + rm -f $FILE +fi \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/luci/modechge.sh b/ext-rooter-basic/files/usr/lib/rooter/luci/modechge.sh new file mode 100644 index 0000000..aea137e --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/luci/modechge.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +MODEMTYPE=$1 +NETMODE=$2 + +log() { + logger -t "ModeChange" "$@" +} + +CURRMODEM=$(uci get modem.general.modemnum) +uci set modem.modem$CURRMODEM.cmode="0" +uci set modem.modem$CURRMODEM.netmode="10" +uci commit modem + +MODEMTYPE=$(uci get modem.modem$CURRMODEM.modemtype) +COMMPORT="/dev/ttyUSB"$(uci get modem.modem$CURRMODEM.commport) + +#ZTE +if [ $MODEMTYPE -eq 1 ]; then + case $NETMODE in + 1*) + ATC="AT+ZSNT=0,0,0" ;; + 2*) + ATC="AT+ZSNT=0,0,1" ;; + 3*) + ATC="AT+ZSNT=1,0,0" ;; + 4*) + ATC="AT+ZSNT=0,0,2" ;; + 5*) + ATC="AT+ZSNT=2,0,0" ;; + 6*) + ATC="AT+ZSNT=0,0,6" ;; + 7*) + ATC="AT+ZSNT=6,0,0" ;; + esac + ATC=$ATC";+ZBANDI=0" +fi + +#SIERRA +if [ $MODEMTYPE -eq 2 ]; then + case $NETMODE in + 1*) + ATC="AT!SELRAT=0" ;; + 2*) + ATC="AT!SELRAT=4" ;; + 3*) + ATC="AT!SELRAT=2" ;; + 4*) + ATC="AT!SELRAT=3" ;; + 5*) + ATC="AT!SELRAT=1" ;; + 6*) + ATC="AT!SELRAT=0" ;; + 7*) + ATC="AT!SELRAT=6" ;; + esac + ATC=$ATC";!BAND=0" +fi + +#Huawei +if [ $MODEMTYPE -eq 3 ]; then + case $NETMODE in + 1*) + ATC="AT^SYSCFGEX=\"00\",3FFFFFFF,2,4,7FFFFFFFFFFFFFFF,," ;; + 2*) + ATC="AT^SYSCFGEX=\"010203\",3FFFFFFF,2,4,7FFFFFFFFFFFFFFF,," ;; + 3*) + ATC="AT^SYSCFGEX=\"01\",3FFFFFFF,2,4,7FFFFFFFFFFFFFFF,," ;; + 4*) + ATC="AT^SYSCFGEX=\"020301\",3FFFFFFF,2,4,7FFFFFFFFFFFFFFF,," ;; + 5*) + ATC="AT^SYSCFGEX=\"02\",3FFFFFFF,2,4,7FFFFFFFFFFFFFFF,," ;; + 6*) + ATC="AT^SYSCFGEX=\"030201\",3FFFFFFF,2,4,7FFFFFFFFFFFFFFF,," ;; + 7*) + ATC="AT^SYSCFGEX=\"03\",3FFFFFFF,2,4,7FFFFFFFFFFFFFFF,," ;; + esac +fi + +#Huawei +if [ $MODEMTYPE -eq 4 ]; then + case $NETMODE in + 1*) + ATC="AT^SYSCFG=2,0,03FFFFFFF,2,4" ;; + 2*) + ATC="AT^SYSCFG=2,1,03FFFFFFF,2,4" ;; + 3*) + ATC="AT^SYSCFG=13,1,03FFFFFFF,2,4" ;; + 4*) + ATC="AT^SYSCFG=2,2,03FFFFFFF,2,4" ;; + 5*) + ATC="AT^SYSCFG=14,2,03FFFFFFF,2,4" ;; + esac +fi + +#ublox +if [ $MODEMTYPE -eq 5 ]; then + case $NETMODE in + 1*) + ATC="AT+CFUN=4;+URAT=4,3;+CFUN=1,1" ;; + 2*) + ATC="AT+CFUN=4;+URAT=4,0;+CFUN=1,1" ;; + 3*) + ATC="AT+CFUN=4;+URAT=0;+CFUN=1,1" ;; + 4*) + ATC="AT+CFUN=4;+URAT=4,2;+CFUN=1,1" ;; + 5*) + ATC="AT+CFUN=4;+URAT=2;+CFUN=1,1" ;; + 6*) + ATC="AT+CFUN=4;+URAT=4,3;+CFUN=1,1" ;; + 7*) + ATC="AT+CFUN=4;+URAT=4,3;+CFUN=1,1" ;; + esac +fi + +ATCMDD="$ATC" +OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + +$ROOTER/luci/celltype.sh $CURRMODEM +uci set modem.modem$CURRMODEM.cmode="1" +uci commit modem diff --git a/ext-rooter-basic/files/usr/lib/rooter/luci/modemchge.sh b/ext-rooter-basic/files/usr/lib/rooter/luci/modemchge.sh new file mode 100644 index 0000000..c0a4a9a --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/luci/modemchge.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +TYPE=$1 +DIREC=$2 +source /tmp/modcnt +MODCNT=$MODCNTX + +case $TYPE in +"modem" ) + MODENUM=$(uci get modem.general.modemnum) + ;; +"sms" ) + MODENUM=$(uci get modem.general.smsnum) + ;; +"misc" ) + MODENUM=$(uci get modem.general.miscnum) + ;; +esac + +if [ $DIREC -eq 1 ]; then + let MODENUM=MODENUM+1 + if [ $MODENUM -gt $MODCNT ]; then + MODENUM=1 + fi +else + if [ $MODENUM -eq 1 ]; then + MODENUM=$MODCNT + else + let MODENUM=MODENUM-1 + fi +fi +case $TYPE in +"modem" ) + uci set modem.general.modemnum=$MODENUM + ;; +"sms" ) + uci set modem.general.smsnum=$MODENUM + ;; +"misc" ) + uci set modem.general.miscnum=$MODENUM + ;; +esac +uci commit modem + diff --git a/ext-rooter-basic/files/usr/lib/rooter/luci/portchge.sh b/ext-rooter-basic/files/usr/lib/rooter/luci/portchge.sh new file mode 100644 index 0000000..968f039 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/luci/portchge.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +DIR=$1 + +log() { + logger -t "port change" "$@" +} + + +CURRMODEM=$(uci get modem.general.modemnum) +BASEP=$(uci get modem.modem$CURRMODEM.baseport) +MAXP=$(uci get modem.modem$CURRMODEM.maxport) +PORT=$(uci get modem.modem$CURRMODEM.commport) + +log "$DIR" + +if [ $DIR = "up" ]; then + if [ $PORT -lt $MAXP ]; then + PORT=`expr $PORT + 1` + echo 'PORT="'"$PORT"'"' > /tmp/port$CURRMODEM.file + fi +else + if [ $PORT -gt $BASEP ]; then + PORT=`expr $PORT - 1` + echo 'PORT="'"$PORT"'"' > /tmp/port$CURRMODEM.file + fi +fi + diff --git a/ext-rooter-basic/files/usr/lib/rooter/luci/wifiradio.sh b/ext-rooter-basic/files/usr/lib/rooter/luci/wifiradio.sh new file mode 100644 index 0000000..1f791a0 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/luci/wifiradio.sh @@ -0,0 +1,15 @@ +#!/bin/sh +. /lib/functions.sh + +config_cb() { + local type="$1" + local name="$2" + if [ ! -z $type ]; then + if [ $type = "wifi-device" ]; then + echo $name >> /tmp/wifi-device + fi + fi +} + +rm -f /tmp/wifi-device +config_load wireless diff --git a/ext-rooter-basic/files/usr/lib/rooter/mbimfind.lua b/ext-rooter-basic/files/usr/lib/rooter/mbimfind.lua new file mode 100644 index 0000000..36f9442 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/mbimfind.lua @@ -0,0 +1,59 @@ +#!/usr/bin/lua + +drv = {} +idV = arg[1] +idP = arg[2] + +retval = 0 + +function trim(s) + return (s:gsub("^%s*(.-)%s*$", "%1")) +end + +-- MAIN + +local i=0 +local file = io.open("/tmp/prembim", "r") +repeat + local line = file:read("*line") + if line == nil then + break + end + if string.len(line) > 5 then + s, e = line:find("Vendor=") + if s ~= nil then + cs, ce = line:find(" ", e) + m_idV = trim(line:sub(e+1, cs-1)) + s, e = line:find("ProdID=") + cs, ce = line:find(" ", e) + m_idP = trim(line:sub(e+1, cs-1)) + if m_idV == idV and m_idP == idP then + repeat + line = file:read("*line") + if line == nil then + break + end + if string.len(line) > 5 then + s, e = line:find("T:") + if s ~= nil then + break + end + s, e = line:find("Cls=02") + if s ~= nil then + s, e = line:find("Sub=0e Prot=00") + if s ~= nil then + retval = 1 + break + end + end + end + until 1==0 + break + end + end + end +until 1==0 +file:close() + + +os.exit(retval) \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/modeswitch.sh b/ext-rooter-basic/files/usr/lib/rooter/modeswitch.sh new file mode 100644 index 0000000..1cf0471 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/modeswitch.sh @@ -0,0 +1,518 @@ +. /lib/functions/procd.sh + +MODCNT=6 + +ROOTER=/usr/lib/rooter +ROOTER_LINK="/tmp/links" + +modeswitch="/usr/bin/usb_modeswitch" + +log() { + logger -t "usb-modeswitch" "$@" +} + +sanitize() { + sed -e 's/[[:space:]]\+$//; s/[[:space:]]\+/_/g' "$@" +} + +find_usb_attrs() { + local usb_dir="/sys$DEVPATH" + [ -f "$usb_dir/idVendor" ] || usb_dir="${usb_dir%/*}" + + uVid=$(cat "$usb_dir/idVendor") + uPid=$(cat "$usb_dir/idProduct") + uMa=$(sanitize "$usb_dir/manufacturer") + uPr=$(sanitize "$usb_dir/product") + uSe=$(sanitize "$usb_dir/serial") +} + +display_top() { + log "*****************************************************************" + log "*" +} + +display_bottom() { + log "*****************************************************************" +} + + +display() { + local line1=$1 + log "* $line1" + log "*" +} + +# +# Save Interface variables +# +save_variables() { + echo 'MODSTART="'"$MODSTART"'"' > /tmp/variable.file + echo 'WWAN="'"$WWAN"'"' >> /tmp/variable.file + echo 'USBN="'"$USBN"'"' >> /tmp/variable.file + echo 'ETHN="'"$ETHN"'"' >> /tmp/variable.file + echo 'WDMN="'"$WDMN"'"' >> /tmp/variable.file + echo 'BASEPORT="'"$BASEPORT"'"' >> /tmp/variable.file +} +# +# delay until ROOter Initialization done +# +bootdelay() { + if [ ! -f /tmp/bootend.file ]; then + log "Delay for boot up" + sleep 10 + while [ ! -f /tmp/bootend.file ]; do + sleep 1 + done + sleep 10 + fi +} + +# +# return modem number based on port number +# 0 is not found +# +find_device() { + DEVN=$1 + COUNTER=1 + while [ $COUNTER -le $MODCNT ]; do + EMPTY=$(uci get modem.modem$COUNTER.empty) + if [ $EMPTY -eq 0 ]; then + DEVS=$(uci get modem.modem$COUNTER.device) + if [ $DEVN = $DEVS ]; then + retresult=$COUNTER + return + fi + fi + let COUNTER=COUNTER+1 + done + retresult=0 +} + +# +# check if all modems are inactive or empty +# delete all if nothing active +# +check_all_empty() { + COUNTER=1 + while [ $COUNTER -le $MODCNT ]; do + EMPTY=$(uci get modem.modem$COUNTER.empty) + if [ $EMPTY -eq 0 ]; then + ACTIVE=$(uci get modem.modem$COUNTER.active) + if [ $ACTIVE -eq 1 ]; then + return + fi + fi + let COUNTER=COUNTER+1 + done + COUNTER=1 + while [ $COUNTER -le $MODCNT ]; do + uci delete modem.modem$COUNTER + uci set modem.modem$COUNTER=modem + uci set modem.modem$COUNTER.empty=1 + let COUNTER=COUNTER+1 + done + uci set modem.general.modemnum=1 + uci commit modem + MODSTART=1 + WWAN=0 + USBN=0 + ETHN=1 + WDMN=0 + BASEPORT=0 + if + ifconfig eth1 + then + if [ -e "/sys/class/net/eth1/device/bInterfaceProtocol" ]; then + ETHN=1 + else + ETHN=2 + fi + fi + save_variables + display_top; display "No Modems present"; display_bottom +} + +# +# Add Modem and connect +# +if [ "$ACTION" = add ]; then + bootdelay + find_usb_attrs + + if echo $DEVICENAME | grep -q ":" ; then + exit 0 + fi + + if [ -z $uMa ]; then + log "Ignoring Unnamed Hub" + exit 0 + fi + + UPR=${uPr} + CT=`echo $UPR | tr '[A-Z]' '[a-z]'` + if echo $CT | grep -q "hub" ; then + log "Ignoring Named Hub" + exit 0 + fi + + if [ $uVid = 1d6b ]; then + log "Ignoring Linux Hub" + exit 0 + fi + + cat /sys/kernel/debug/usb/devices > /tmp/wdrv + lua $ROOTER/protofind.lua $uVid $uPid 0 + retval=$? + + if [ -e /etc/config/mjpg-streamer ]; then + if [ $retval -eq 99 ]; then + log "Start MJPEG Streamer $DEVICENAME" + /etc/init.d/mjpg-streamer start + uci delete mjpg-streamer.camera + uci set mjpg-streamer.camera=mjpg-stream + uci set mjpg-streamer.camera.idv=$DEVICENAME + uci commit mjpg-streamer + exit 0 + fi + fi + if [ -e /etc/config/p910nd ]; then + if [ $retval -eq 98 ]; then + # Check if lp device is plugged in and p910nd is not already started + log "USB Printer device plugged in, starting p910nd" + /etc/init.d/p910nd start + uci delete p910nd.printer + uci set p910nd.printer=printer + uci set p910nd.printer.idv=$DEVICENAME + uci commit p910nd + exit 0 + fi + fi + if [ $retval -eq 97 ]; then + if grep "$uVid:$uPid" /etc/usb-mode.json > /dev/null ; then + log "Modem found" + else + log "Found USB Storage" + exit 0 + fi + fi + + if [ -f /tmp/usbwait ]; then + log "Delay for previous modem" + while [ -f /tmp/usbwait ]; do + sleep 5 + done + fi + echo "1" > /tmp/usbwait + + source /tmp/variable.file + source /tmp/modcnt + MODCNT=$MODCNTX + + reinsert=0 + find_device $DEVICENAME + if [ $retresult -gt 0 ]; then + ACTIVE=$(uci get modem.modem$retresult.active) + if [ $ACTIVE = 1 ]; then + rm -f /tmp/usbwait + exit 0 + else + IDP=$(uci get modem.modem$retresult.uPid) + IDV=$(uci get modem.modem$retresult.uVid) + if [ $uVid = $IDV -a $uPid = $IDP ]; then + reinsert=1 + CURRMODEM=$retresult + else + display_top; display "Reinsert of different Modem not allowed"; display_bottom + rm -f /tmp/usbwait + exit 0 + fi + fi + fi + + log "Add : $DEVICENAME: Manufacturer=${uMa:-?} Product=${uPr:-?} Serial=${uSe:-?} $uVid $uPid" + + if [ $MODSTART -gt $MODCNT ]; then + display_top; display "Exceeded Maximun Number of Modems"; display_bottom + exit 0 + fi + + if [ $reinsert = 0 ]; then + CURRMODEM=$MODSTART + fi + + idV=$uVid + idP=$uPid + + FILEN=$uVid:$uPid + display_top; display "Start of Modem Detection and Connection Information" + display "Product=${uPr:-?} $uVid $uPid"; display_bottom + cat /sys/kernel/debug/usb/devices > /tmp/prembim + lua $ROOTER/mbimfind.lua $uVid $uPid + retval=$? + rm -f /tmp/prembim + if [ ! -e /sbin/umbim ]; then + retval=0 + fi + if [ $idV = 1199 -a $idP = 0fff ]; then + retval=0 + fi + if [ $idV = 12d1 -a $idP = 157d ]; then + retval=0 + fi + if [ $idV = 12d1 -a $idP = 15ec ]; then + retval=0 + fi + if [ $idV = 12d1 -a $idP = 1597 ]; then + retval=0 + fi + if [ $idV = 1199 -a $idP = 9013 ]; then + #echo 1 >/sys/bus/usb/devices/$DEVICENAME/bConfigurationValue + retval=0 + fi + if [ $idV = 12d1 -a $idP = 15c1 ]; then + #echo 2 >/sys/bus/usb/devices/$DEVICENAME/bConfigurationValue + retval=0 + fi + if [ $retval -eq 1 ]; then + if [ $idV = 1199 -a $idP = 9051 ]; then + display_top; display "Found 340U Modem at $DEVICENAME"; display_bottom + echo 1 >/sys/bus/usb/devices/$DEVICENAME/bConfigurationValue + else + display_top; display "Found MBIM Modem at $DEVICENAME"; display_bottom + echo 2 >/sys/bus/usb/devices/$DEVICENAME/bConfigurationValue + fi + else + if grep "$FILEN" /etc/usb-mode.json > /dev/null ; then + procd_open_service "usbmode" + procd_open_instance + procd_set_param command "/sbin/usbmode" -s + procd_close_instance + procd_close_service + else + display_top; display "This device does not have a switch data file" + display "Product=${uPr:-?} $uVid $uPid"; display_bottom + fi + fi + sleep 10 + usb_dir="/sys$DEVPATH" + idV="$(sanitize "$usb_dir/idVendor")" + idP="$(sanitize "$usb_dir/idProduct")" + display_top; display "Switched to : $idV:$idP"; display_bottom + + if [ $idV = 2357 -a $idP = 9000 ]; then + sleep 10 + fi + + cat /sys/kernel/debug/usb/devices > /tmp/wdrv + lua $ROOTER/protofind.lua $idV $idP 1 + retval=$? + display_top; display "ProtoFind returns : $retval"; display_bottom + rm -f /tmp/wdrv + + if [ $reinsert = 0 ]; then + BASEP=$BASEPORT + if [ -f /tmp/drv ]; then + source /tmp/drv + BASEPORT=`expr $PORTN + $BASEPORT` + fi + fi + rm -f /tmp/drv + + FORCE=$(uci get modem.modeminfo$CURRMODEM.ppp) + if [ -n $FORCE ]; then + if [ $FORCE = 1 -a $retval -ne 0 ]; then + log "Forcing PPP mode" + if [ $idV = 12d1 ]; then + retval=10 + else + retval=11 + fi + log "Forced Protcol Value : $retval" + fi + fi + + if [ $idV = 12d1 -a $idP = 15c1 ]; then + retval=27 + fi + if [ $idV = 13b1 -a $idP = 0041 ]; then + retval=0 + fi + + if [ $retval -ne 0 ]; then + log "Found Modem$CURRMODEM" + if [ $reinsert = 0 ]; then + uci set modem.modem$CURRMODEM.empty=0 + uci set modem.modem$CURRMODEM.uVid=$uVid + uci set modem.modem$CURRMODEM.uPid=$uPid + uci set modem.modem$CURRMODEM.idV=$idV + uci set modem.modem$CURRMODEM.idP=$idP + uci set modem.modem$CURRMODEM.device=$DEVICENAME + uci set modem.modem$CURRMODEM.baseport=$BASEP + uci set modem.modem$CURRMODEM.maxport=$BASEPORT + uci set modem.modem$CURRMODEM.proto=$retval + uci set modem.modem$CURRMODEM.maxcontrol=/sys$DEVPATH/descriptors + find_usb_attrs + uci set modem.modem$CURRMODEM.manuf=$uMa + uci set modem.modem$CURRMODEM.model=$uPr + uci set modem.modem$CURRMODEM.serial=$uSe + uci set modem.modem$CURRMODEM.celltype="-" + fi + uci set modem.modem$CURRMODEM.active=1 + uci set modem.modem$CURRMODEM.connected=0 + uci commit modem + fi + + if [ $reinsert = 0 -a $retval != 0 ]; then + MODSTART=`expr $MODSTART + 1` + save_variables + fi + +# +# Handle specific modem models +# + case $retval in + "0" ) + # + # ubox GPS module + # + if [ $idV = 1546 ]; then + if echo $uPr | grep -q "GPS"; then + SYMLINK="gps0" + BASEX=`expr 1 + $BASEP` + ln -s /dev/ttyUSB$BASEX /dev/${SYMLINK} + display_top ; display "Hotplug Symlink from /dev/ttyUSB$BASEX to /dev/${SYMLINK} created" + display_bottom + fi + fi + rm -f /tmp/usbwait + exit 0 + ;; + "1" ) + log "Connecting a Sierra Modem" + ln -s $ROOTER/connect/create_connect.sh $ROOTER_LINK/create_proto$CURRMODEM + $ROOTER_LINK/create_proto$CURRMODEM $CURRMODEM & + ;; + "2" ) + log "Connecting a QMI Modem" + ln -s $ROOTER/connect/create_connect.sh $ROOTER_LINK/create_proto$CURRMODEM + $ROOTER_LINK/create_proto$CURRMODEM $CURRMODEM & + ;; + "3" ) + log "Connecting a MBIM Modem" + ln -s $ROOTER/connect/create_connect.sh $ROOTER_LINK/create_proto$CURRMODEM + $ROOTER_LINK/create_proto$CURRMODEM $CURRMODEM & + ;; + "6"|"4"|"7"|"24"|"26"|"27" ) + log "Connecting a Huawei NCM Modem" + ln -s $ROOTER/connect/create_connect.sh $ROOTER_LINK/create_proto$CURRMODEM + $ROOTER_LINK/create_proto$CURRMODEM $CURRMODEM & + ;; + "5" ) + log "Connecting a Hostless Modem or Phone" + ln -s $ROOTER/connect/create_hostless.sh $ROOTER_LINK/create_proto$CURRMODEM + $ROOTER_LINK/create_proto$CURRMODEM $CURRMODEM & + ;; + "10"|"11"|"12"|"13"|"14"|"15" ) + log "Connecting a PPP Modem" + ln -s $ROOTER/ppp/create_ppp.sh $ROOTER_LINK/create_proto$CURRMODEM + $ROOTER_LINK/create_proto$CURRMODEM $CURRMODEM + ;; + "9" ) + log "PPP HSO Modem" + rm -f /tmp/usbwait + ;; + esac + +fi + +# +# Remove Modem +# +if [ "$ACTION" = remove ]; then + find_usb_attrs + + if echo $DEVICENAME | grep -q ":" ; then + exit 0 + fi + find_device $DEVICENAME + if [ $retresult -gt 0 ]; then + IDP=$(uci get modem.modem$retresult.idP) + IDV=$(uci get modem.modem$retresult.idV) + if [ $uVid = $IDV ]; then + exit 0 + else + uci set modem.modem$retresult.active=0 + uci set modem.modem$retresult.connected=0 + uci commit modem + if [ -e /etc/config/mwan3 ]; then + ENB=$(uci get mwan3.wan$retresult.enabled) + if [ ! -z $ENB ]; then + uci set mwan3.wan$retresult.enabled=0 + uci commit mwan3 + fi + fi + SMS=$(uci get modem.modem$CURRMODEM.sms) + if [ $SMS = 1 ]; then + if [ -e /usr/lib/sms/stopsms ]; then + /usr/lib/sms/stopsms $CURRMODEM + fi + fi + ifdown wan$retresult + uci delete network.wan$retresult + uci set network.wan$retresult=interface + uci set network.wan$retresult.proto=dhcp + uci set network.wan$retresult.ifname=" " + uci set network.wan$retresult.metric=$retresult"0" + uci commit network + killall -9 getsignal$retresult + rm -f $ROOTER_LINK/getsignal$retresult + killall -9 reconnect$retresult + rm -f $ROOTER_LINK/reconnect$retresult + killall -9 create_proto$retresult + rm -f $ROOTER_LINK/create_proto$retresult + killall -9 processsms$retresult + rm -f $ROOTER_LINK/processsms$retresult + killall -9 con_monitor$retresult + rm -f $ROOTER_LINK/con_monitor$retresult + killall -9 mbim_monitor$retresult + rm -f $ROOTER_LINK/mbim_monitor$retresult + $ROOTER/signal/status.sh $retresult "No Modem Present" + $ROOTER/log/logger "Disconnect (Removed) Modem #$retresult" + display_top; display "Remove : $DEVICENAME : Modem$retresult"; display_bottom + check_all_empty + rm -f /tmp/usbwait + rm -f /tmp/mdown$retresult + rm -f /tmp/msimdata$retresult + rm -f /tmp/msimnum$retresult + echo "0" > /tmp/modgone + fi + else + IDV=$(uci get mjpg-streamer.camera.idv) + if [ ! -z $IDV ]; then + if [ $DEVICENAME = $IDV ]; then + uci delete mjpg-streamer.camera + uci commit mjpg-streamer + /etc/init.d/mjpg-streamer stop + log "Stop MJPEG-Streamer" + fi + fi + IDV=$(uci get p910nd.printer.idv) + if [ ! -z $IDV ]; then + if [ $DEVICENAME = $IDV ]; then + uci delete p910nd.printer + uci commit p910nd + if [ ! -d /sys$DEVPATH/*/lp0 -a -f /var/run/p9100d.pid ]; then + log "USB Printer device unplugged, stopping p910nd" + /etc/init.d/p910nd stop + # p910nd does not seem to remove .pid file when stopped, removing it manually + rm /var/run/p9100d.pid + fi + fi + fi + fi +fi + +if [ "$ACTION" = "motion" ]; then + logger webcam motion event +fi + diff --git a/ext-rooter-basic/files/usr/lib/rooter/portchge.sh b/ext-rooter-basic/files/usr/lib/rooter/portchge.sh new file mode 100644 index 0000000..a9ae59b --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/portchge.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +local DIR=$1 + +log() { + logger -t "port change" "$@" +} + + +CURRMODEM=$(uci get modem.general.modemnum) +BASEP=$(uci get modem.modem$CURRMODEM.baseport) +MAXP=$(uci get modem.modem$CURRMODEM.maxport) +PORT=$(uci get modem.modem$CURRMODEM.commport) + +log "$DIR" + +if [ $DIR = "up" ]; then + if [ $PORT -lt $MAXP ]; then + PORT=`expr $PORT + 1` + echo 'PORT="'"$PORT"'"' > /tmp/port$CURRMODEM.file + fi +else + if [ $PORT -gt $BASEP ]; then + PORT=`expr $PORT - 1` + echo 'PORT="'"$PORT"'"' > /tmp/port$CURRMODEM.file + fi +fi + diff --git a/ext-rooter-basic/files/usr/lib/rooter/ppp/create_ppp.sh b/ext-rooter-basic/files/usr/lib/rooter/ppp/create_ppp.sh new file mode 100644 index 0000000..55e4f9d --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/ppp/create_ppp.sh @@ -0,0 +1,170 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter +ROOTER_LINK="/tmp/links" + +log() { + logger -t "Create Connection" "$@" +} + +set_dns() { + local DNS1=$(uci get modem.modeminfo$CURRMODEM.dns1) + local DNS2=$(uci get modem.modeminfo$CURRMODEM.dns2) + if [ -z $DNS1 ]; then + if [ -z $DNS2 ]; then + return + else + uci set network.wan$CURRMODEM.peerdns=0 + uci set network.wan$CURRMODEM.dns=$DNS2 + fi + else + uci set network.wan$CURRMODEM.peerdns=0 + if [ -z $DNS2 ]; then + uci set network.wan$CURRMODEM.dns="$DNS1" + else + uci set network.wan$CURRMODEM.dns="$DNS2 $DNS1" + fi + fi +} + +save_variables() { + echo 'MODSTART="'"$MODSTART"'"' > /tmp/variable.file + echo 'WWAN="'"$WWAN"'"' >> /tmp/variable.file + echo 'USBN="'"$USBN"'"' >> /tmp/variable.file + echo 'ETHN="'"$ETHN"'"' >> /tmp/variable.file + echo 'WDMN="'"$WDMN"'"' >> /tmp/variable.file + echo 'BASEPORT="'"$BASEPORT"'"' >> /tmp/variable.file +} + +get_connect() { + NAPN=$(uci get modem.modeminfo$CURRMODEM.apn) + NUSER=$(uci get modem.modeminfo$CURRMODEM.user) + NPASS=$(uci get modem.modeminfo$CURRMODEM.passw) + NAUTH=$(uci get modem.modeminfo$CURRMODEM.auth) + PINC=$(uci get modem.modeminfo$CURRMODEM.pincode) + + uci set modem.modem$CURRMODEM.apn=$NAPN + uci set modem.modem$CURRMODEM.user=$NUSER + uci set modem.modem$CURRMODEM.pass=$NPASS + uci set modem.modem$CURRMODEM.auth=$NAUTH + uci set modem.modem$CURRMODEM.pin=$PINC + uci commit modem +} + +CURRMODEM=$1 +source /tmp/variable.file + +MAN=$(uci get modem.modem$CURRMODEM.manuf) +MOD=$(uci get modem.modem$CURRMODEM.model) +BASEP=$(uci get modem.modem$CURRMODEM.baseport) +$ROOTER/signal/status.sh $CURRMODEM "$MAN $MOD" "Connecting" +PROT=$(uci get modem.modem$CURRMODEM.proto) + +DELAY=$(uci get modem.modem$CURRMODEM.delay) +if [ -z $DELAY ]; then + DELAY=5 +fi + +idV=$(uci get modem.modem$CURRMODEM.idV) +idP=$(uci get modem.modem$CURRMODEM.idP) + +cat /sys/kernel/debug/usb/devices > /tmp/cdma +lua $ROOTER/cdmafind.lua $idV $idP +retval=$? +rm -f /tmp/cdma +if [ $retval -eq 1 ]; then + log "Found CDMA modem" +fi + +local DP CP +case $PROT in +"10" ) + if [ $retval -eq 0 ]; then + DP=0 + CP=2 + else + DP=0 + CP=0 + fi + ;; +"11"|"12" ) + if [ $retval -eq 0 ]; then + DP=2 + CP=1 + else + DP=0 + CP=0 + fi + ;; +"13" ) + if [ $retval -eq 0 ]; then + DP=4 + CP=3 + else + DP=0 + CP=0 + fi + ;; +"14" ) + if [ $retval -eq 0 ]; then + DP=3 + CP=2 + else + DP=0 + CP=0 + fi + ;; +"15" ) + if [ $retval -eq 0 ]; then + DP=1 + CP=2 + else + DP=0 + CP=0 + fi + ;; +esac +$ROOTER/common/modemchk.lua "$idV" "$idP" "$DP" "$CP" +source /tmp/parmpass + +CPORT=`expr $CPORT + $BASEP` +DPORT=`expr $DPORT + $BASEP` +uci set modem.modem$CURRMODEM.commport=$CPORT +uci set modem.modem$CURRMODEM.dataport=$DPORT +uci set modem.modem$CURRMODEM.service=$retval +uci commit modem + +get_connect + +uci delete network.wan$CURRMODEM +uci set network.wan$CURRMODEM=interface +uci set network.wan$CURRMODEM.ifname=3x-wan$CURRMODEM +uci set network.wan$CURRMODEM.proto=3x +if [ $retval -eq 0 ]; then + uci set network.wan$CURRMODEM.service=umts +else + uci set network.wan$CURRMODEM.service=cdma +fi +uci set network.wan$CURRMODEM.keepalive=10 +uci set network.wan$CURRMODEM.device=/dev/ttyUSB$DPORT +uci set network.wan$CURRMODEM.apn=$NAPN +uci set network.wan$CURRMODEM.username=$NUSER +uci set network.wan$CURRMODEM.auth=$NAUTH +uci set network.wan$CURRMODEM.password=$NPASS +uci set network.wan$CURRMODEM.pincode=$PINC +uci set network.wan$CURRMODEM.metric=$CURRMODEM"0" +uci set network.wan$CURRMODEM.pppd_options="debug noipdefault" +set_dns +uci commit network + +log "PPP Comm Port : /dev/ttyUSB$CPORT" +log "PPP Data Port : /dev/ttyUSB$DPORT" + +if [ $retval -eq 0 ]; then + $ROOTER/common/lockchk.sh $CURRMODEM + $ROOTER/sms/check_sms.sh $CURRMODEM & + $ROOTER/common/gettype.sh $CURRMODEM & +fi + +rm -f /tmp/usbwait +ifup wan$CURRMODEM \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/protofind.lua b/ext-rooter-basic/files/usr/lib/rooter/protofind.lua new file mode 100644 index 0000000..13a92b8 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/protofind.lua @@ -0,0 +1,250 @@ +#!/usr/bin/lua + +drv = {} +idV = arg[1] +idP = arg[2] +pflag = arg[3] +if pflag == nil then + pflag = 1 +end + +special = {} +-- PPP special cases +-- VID PID port # +special[1] = "1bbb" ; special[2] = "0017" ; special[3] = 13 +special[4] = "12d1" ; special[5] = nil ; special[6] = 10 +special[7] = "1546" ; special[8] = "01a6" ; special[9] = 0 +special[10] = "1546" ; special[11] = "01a5" ; special[12] = 0 +special[13] = "1199" ; special[14] = "68a3" ; special[15] = 14 +special[16] = "2001" ; special[17] = "7e35" ; special[18] = 15 + + +retval = 0 +echo = 1 + +printf = function(s,...) + if pflag ~= 0 then + io.write(s:format(...)) + local ss = s:format(...) + if echo == 1 then + os.execute("/usr/lib/rooter/logprint.sh " .. ss) + end + end +end + +function trim(s) + return (s:gsub("^%s*(.-)%s*$", "%1")) +end + +function checkserial() + local got = 0 + j = 1 + repeat + if drv[j] ~= nil then + if drv[j] == "option" or drv[j] == "qcserial" or drv[j] == "usb_serial" or drv[j] == "sierra" then + got = 1 + break + end + j = j + 1 + end + until drv[j] == nil + return got +end + +function countserial() + local got = 0 + j = 1 + repeat + if drv[j] ~= nil then + if drv[j] == "option" or drv[j] == "sierra" or drv[j] == "usb_serial" or drv[j] == "qcserial" then + got = got + 1 + end + j = j + 1 + end + until drv[j] == nil + return got +end + +-- MAIN + +local t = {} + +local i=0 +local file = io.open("/tmp/wdrv", "r") +repeat + local line = file:read("*line") + if line == nil then + break + end + if string.len(line) > 5 then + s, e = line:find("Vendor=") + if s ~= nil then + cs, ce = line:find(" ", e) + m_idV = trim(line:sub(e+1, cs-1)) + s, e = line:find("ProdID=") + cs, ce = line:find(" ", e) + m_idP = trim(line:sub(e+1, cs-1)) + if m_idV == idV and m_idP == idP then + repeat + line = file:read("*line") + if line == nil then + break + end + if string.len(line) > 5 then + s, e = line:find("T:") + if s ~= nil then + break + end + s, e = line:find("Cls=02") + if s ~= nil then + t[i] = trim(line:sub(63)) + i = i + 1 + end + s, e = line:find("Cls=ff") + if s ~= nil then + t[i] = trim(line:sub(63)) + i = i + 1 + end + s, e = line:find("1 Cls=e0") + if s ~= nil then + t[i] = trim(line:sub(63)) + i = i + 1 + end + s, e = line:find("Cls=0a") + if s ~= nil then + t[i] = trim(line:sub(63)) + i = i + 1 + end + s, e = line:find("Cls=0e") + if s ~= nil then + t[i] = trim(line:sub(63)) + i = i + 1 + end + s, e = line:find("Cls=07") + if s ~= nil then + t[i] = trim(line:sub(63)) + i = i + 1 + end + s, e = line:find("Cls=08") + if s ~= nil then + t[i] = trim(line:sub(63)) + i = i + 1 + end + end + until 1==0 + break + end + end + end +until 1==0 +file:close() +if i > 0 then + file = io.open("/tmp/drv", "w") + for j=0,i-1 do + drv[j+1] = t[j] + drver = string.format("%s%d%s%q", "DRIVER", j+1, "=", t[j]) + file:write(drver .. "\n") + end + ports = countserial() + drver = string.format("%s%s%d%s", "PORTN", "=\"", ports, "\"") + file:write(drver .. "\n") + file:close() +end + +i = 1 +repeat + if drv[i] ~= nil then + printf("Driver Name : %d %s\n", i, drv[i]) + i = i + 1 + end +until drv[i] == nil + +i = 1 +repeat + if drv[i] ~= nil then + if drv[i] == "sierra_net" then + retval = 1 + break + end + if drv[i] == "qmi_wwan" then + retval = 2 + break + end + if drv[i] == "cdc_mbim" then + retval = 3 + break + end + if drv[i] == "huawei_cdc_ncm" then + if i == 2 then + retval = 4 + else + if i == 3 then + retval = 6 + else + retval = 7 + end + end + break + end + if drv[i] == "cdc_ncm" then + if i == 2 then + retval = 24 + else + if i == 3 then + retval = 26 + else + retval = 27 + end + end + break + end + if drv[i] == "cdc_ether" or drv[i] == "rndis_host" then + retval = 5 + break + end + if drv[i] == "uvcvideo" then + retval = 99 + break + end + if drv[i] == "usblp" then + retval = 98 + break + end + if drv[i] == "usb-storage" then + if i == 1 then + retval = 97 + break + end + end + if drv[i] == "hso" then + retval = 9 + break + end + i = i + 1 + end +until drv[i] == nil + +if retval == 0 then + if checkserial() == 1 then + retval = 11 + k = 1 + vendor = special[k] + while vendor ~= nil do + if idV == vendor then + if special[k+1] == nil then + retval = special[k+2] + break + else + if special[k+1] == idP then + retval = special[k+2] + break + end + end + end + k = k + 3 + vendor = special[k] + end + end +end + +os.exit(retval) \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/pwrtoggle.sh b/ext-rooter-basic/files/usr/lib/rooter/pwrtoggle.sh new file mode 100644 index 0000000..2a8043a --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/pwrtoggle.sh @@ -0,0 +1,81 @@ +#!/bin/sh + +ROOTER_LINK="/tmp/links" + +log() { + logger -t "Power Toggle" "$@" +} + +waitfor() { + CNTR=0 + while [ ! -e /tmp/modgone ]; do + sleep 1 + CNTR=`expr $CNTR + 1` + if [ $CNTR -gt 60 ]; then + break + fi + done +} + +rebind() { + PORT=$1 + log "Re-binding USB driver on $PORT to reset modem" + echo $PORT > /sys/bus/usb/drivers/usb/unbind + sleep 15 + echo $PORT > /sys/bus/usb/drivers/usb/bind + sleep 20 + CURRMODEM=$(uci get modem.general.modemnum) + if [ -f $ROOTER_LINK/reconnect$CURRMODEM ]; then + $ROOTER_LINK/reconnect$CURRMODEM $CURRMODEM & + fi +} + +power_toggle() { + MODE=$1 + if [ -f "/tmp/gpiopin" ]; then + rm -f /tmp/modgone + source /tmp/gpiopin + echo "$GPIOPIN" > /sys/class/gpio/export + echo "out" > /sys/class/gpio/gpio$GPIOPIN/direction + if [ -z $GPIOPIN2 ]; then + echo 0 > /sys/class/gpio/gpio$GPIOPIN/value + waitfor + echo 1 > /sys/class/gpio/gpio$GPIOPIN/value + else + echo "$GPIOPIN2" > /sys/class/gpio/export + echo "out" > /sys/class/gpio/gpio$GPIOPIN2/direction + if [ $MODE = 1 ]; then + echo 0 > /sys/class/gpio/gpio$GPIOPIN/value + waitfor + echo 1 > /sys/class/gpio/gpio$GPIOPIN/value + fi + if [ $MODE = 2 ]; then + echo 0 > /sys/class/gpio/gpio$GPIOPIN2/value + waitfor + echo 1 > /sys/class/gpio/gpio$GPIOPIN2/value + fi + if [ $MODE = 3 ]; then + echo 0 > /sys/class/gpio/gpio$GPIOPIN/value + echo 0 > /sys/class/gpio/gpio$GPIOPIN2/value + waitfor + echo 1 > /sys/class/gpio/gpio$GPIOPIN/value + echo 1 > /sys/class/gpio/gpio$GPIOPIN2/value + fi + sleep 2 + fi + rm -f /tmp/modgone + else + # unbind/bind driver from USB to reset modem when power toggle is selected, but not available + if [ $MODE = 1 ]; then + PORT="usb1" + rebind $PORT + fi + if [ $MODE = 2 ]; then + PORT="usb2" + rebind $PORT + fi + rm -f /tmp/modgone + fi +} + +power_toggle $1 \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/signal/basedata.sh b/ext-rooter-basic/files/usr/lib/rooter/signal/basedata.sh new file mode 100644 index 0000000..e4292e0 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/signal/basedata.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "basedata" "$@" +} + +CURRMODEM=$1 +COMMPORT=$2 + +get_base() { + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "baseinfo.gcom" "$CURRMODEM") + O=$($ROOTER/common/processat.sh "$OX") +} + +get_base + +COPS="-" +COPS_MCC="-" +COPS_MNC="-" + +COPSX=$(echo "$O" | awk -F[\"] '/^\+COPS: 0,0/ {print $2}') +if [ "x$COPSX" != "x" ]; then + COPS=$COPSX +fi + +COPSX=$(echo "$O" | awk -F[\"] '/^\+COPS: 0,2/ {print $2}') +if [ "x$COPSX" != "x" ]; then + COPS_MCC=${COPSX:0:3} + COPS_MNC=${COPSX:3:3} + if [ "$COPS" = "-" ]; then + COPS=$(awk -F[\;] '/'$COPS'/ {print $2}' $ROOTER/signal/mccmnc.data) + [ "x$COPS" = "x" ] && COPS="-" + fi +fi + +if [ "$COPS" = "-" ]; then + COPS=$(echo "$O" | awk -F[\"] '/^\+COPS: 0,0/ {print $2}') + if [ "x$COPS" = "x" ]; then + COPS="-" + COPS_MCC="-" + COPS_MNC="-" + fi +fi +COPS_MNC=" "$COPS_MNC + +DOWN=$(echo "$O" | awk -F[,] '/\+CGEQNEG:/ {printf "%s", $4}') +if [ "x$DOWN" != "x" ]; then + UP=$(echo "$O" | awk -F[,] '/\+CGEQNEG:/ {printf "%s", $3}') + DOWN=$DOWN" kbps Down | " + UP=$UP" kbps Up" +else + DOWN="-" + UP="-" +fi + +MANUF=$(echo "$O" | awk -F[:] '/Manufacturer:/ { print $2}') +if [ "x$MANUF" = "x" ]; then + MANUF=$(uci get modem.modem$CURRMODEM.manuf) +fi + +MODEL=$(echo "$O" | awk -F[,\ ] '/^\+MODEL:/ {print $2}') +if [ "x$MODEL" != "x" ]; then + MODEL=$(echo "$MODEL" | sed -e 's/"//g') + if [ $MODEL = 0 ]; then + MODEL = "mf820" + fi +else + MODEL=$(uci get modem.modem$CURRMODEM.model) +fi +MODEM=$MANUF" "$MODEL + +echo 'COPS="'"$COPS"'"' > /tmp/base$CURRMODEM.file +echo 'COPS_MCC="'"$COPS_MCC"'"' >> /tmp/base$CURRMODEM.file +echo 'COPS_MNC="'"$COPS_MNC"'"' >> /tmp/base$CURRMODEM.file +echo 'MODEM="'"$MODEM"'"' >> /tmp/base$CURRMODEM.file +echo 'DOWN="'"$DOWN"'"' >> /tmp/base$CURRMODEM.file +echo 'UP="'"$UP"'"' >> /tmp/base$CURRMODEM.file + + + diff --git a/ext-rooter-basic/files/usr/lib/rooter/signal/celldata.sh b/ext-rooter-basic/files/usr/lib/rooter/signal/celldata.sh new file mode 100644 index 0000000..214580d --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/signal/celldata.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +CURRMODEM=$1 +COMMPORT=$2 + +get_cell() { + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "cellinfo.gcom" "$CURRMODEM") + O=$($ROOTER/common/processat.sh "$OX") +} +get_cell +CREG="+CEREG:" +LAC=$(echo "$O" | awk -F[,] '/\'$CREG'/ {printf "%s", toupper($3)}' | sed 's/[^A-F0-9]//g') +if [ "x$LAC" = "x" ]; then + CREG="+CGREG:" + LAC=$(echo "$O" | awk -F[,] '/\'$CREG'/ {printf "%s", toupper($3)}' | sed 's/[^A-F0-9]//g') +fi + +if [ "x$LAC" != "x" ]; then + LAC=$(printf "%04X" 0x$LAC) + LAC_NUM=$(printf "%d" 0x$LAC) +else + LAC="-" + LAC_NUM="-" +fi +LAC_NUM=" ("$LAC_NUM")" +if [ "$CREG" = "+CEREG:" ]; then + CID=$(echo "$O" | grep -o "+CEREG:.\{8,\}" | awk '{print substr(toupper($0),20)}' | grep -o "[0-9A-F]\{3,8\}") +else + CID=$(echo "$O" | awk -F[,] '/\'$CREG'/ {printf "%s", toupper($4)}' | sed 's/[^A-F0-9]//g') +fi +if [ "x$CID" != "x" ]; then + if [ ${#CID} -lt 3 ]; then + LCID="-" + LCID_NUM="-" + RNC="-" + RNC_NUM="-" + else + LCID=$(printf "%08X" 0x$CID) + LCID_NUM=$(printf "%d" 0x$LCID) + if [ "$CREG" = "+CEREG:" ]; then + RNC=$(printf "${LCID:1:5}") + CID=$(printf "${LCID:6:2}") + else + RNC=$(printf "${LCID:1:3}") + CID=$(printf "${LCID:4:4}") + fi + RNC_NUM=$(printf "%d" 0x$RNC) + RNC_NUM=" ($RNC_NUM)" + fi + + CID_NUM=$(printf "%d" 0x$CID) +else + LCID="-" + LCID_NUM="-" + RNC="-" + RNC_NUM="-" + CID="-" + CID_NUM="-" +fi +CID_NUM=" ("$CID_NUM")" + +echo 'LAC="'"$LAC"'"' > /tmp/cell$CURRMODEM.file +echo 'LAC_NUM="'"$LAC_NUM"'"' >> /tmp/cell$CURRMODEM.file +echo 'CID="'"$CID"'"' >> /tmp/cell$CURRMODEM.file +echo 'CID_NUM="'"$CID_NUM"'"' >> /tmp/cell$CURRMODEM.file +echo 'RNC="'"$RNC"'"' >> /tmp/cell$CURRMODEM.file +echo 'RNC_NUM="'"$RNC_NUM"'"' >> /tmp/cell$CURRMODEM.file + + + diff --git a/ext-rooter-basic/files/usr/lib/rooter/signal/celltype.lua b/ext-rooter-basic/files/usr/lib/rooter/signal/celltype.lua new file mode 100644 index 0000000..994249d --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/signal/celltype.lua @@ -0,0 +1,90 @@ +#!/usr/bin/lua + +modem = arg[1] +numb = arg[2] +echo = 0 + +datalist = {} +celllist = {} + +datalist[1] = "320u" +celllist[1] = 2 +datalist[2] = "330u" +celllist[2] = 2 +datalist[3] = "e3276" +celllist[3] = 3 +datalist[4] = "e398" +celllist[4] = 3 +datalist[5] = "e389" +celllist[5] = 3 +datalist[6] = "e392" +celllist[6] = 3 +datalist[7] = "e397" +celllist[7] = 3 +datalist[8] = "e8278" +celllist[8] = 3 +datalist[9] = "mf820" +celllist[9] = 3 +datalist[10] = "mf821" +celllist[10] = 3 +datalist[11] = "k5005" +celllist[11] = 3 +datalist[12] = "k5007" +celllist[12] = 3 +datalist[13] = "l800" +celllist[13] = 3 +datalist[14] = "e398" +celllist[14] = 3 +datalist[15] = "mf880" +celllist[15] = 3 +datalist[16] = "e3272" +celllist[16] = 3 +datalist[17] = "e3372" +celllist[17] = 3 +datalist[18] = "lte" +celllist[18] = 3 +datalist[19] = "340u" +celllist[19] = 2 +datalist[20] = "mf91d" +celllist[20] = 3 +datalist[21] = "mf825a" +celllist[21] = 3 +datalist[22] = "mf826" +celllist[22] = 3 +datalist[23] = "313u" +celllist[23] = 2 +datalist[24] = "341u" +celllist[24] = 2 +datalist[25] = "em74" +celllist[25] = 2 +datalist[26] = "mc74" +celllist[26] = 2 + +printf = function(s,...) + if echo == 0 then + io.write(s:format(...)) + else + ss = s:format(...) + os.execute("/usr/lib/rooter/logprint.sh " .. ss) + end +end + +found = 1 +index = 1 +line = datalist[index] +data = string.lower(modem) + +while line ~= nil do + s, e = string.find(data, line) + if s ~= nil then + found = celllist[index] + break + end + index = index + 1 + line = datalist[index] +end + +file = io.open("/tmp/celltype" .. numb, "w") +cell = string.format("%s%s%s%s", "CELL", "=\"", found, "\"") +file:write(cell.. "\n") +file:close() \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/signal/huaweihostless.sh b/ext-rooter-basic/files/usr/lib/rooter/signal/huaweihostless.sh new file mode 100644 index 0000000..f0653cc --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/signal/huaweihostless.sh @@ -0,0 +1,198 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "hostless " "$@" +} + +TMPFILE="/tmp/XXXXXX" + +handle_timeout(){ + local wget_pid="$1" + local count=0 + ps | grep -v grep | grep $wget_pid + res="$?" + while [ "$res" = 0 -a $count -lt "$((TIMEOUT))" ]; do + sleep 1 + count=$((count+1)) + ps | grep -v grep | grep $wget_pid + res="$?" + done + + if [ "$res" = 0 ]; then + log "Killing process on timeout" + kill "$wget_pid" 2> /dev/null + ps | grep -v grep | grep $wget_pid + res="$?" + if [ "$res" = 0 ]; then + log "Killing process on timeout" + kill -9 $wget_pid 2> /dev/null + fi + fi +} + +make_status() { + echo "$IP" > /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "$CSQ" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "$MODEM" >> /tmp/status$CURRMODEM.file + echo "$COPS" >> /tmp/status$CURRMODEM.file + echo "$NETWORK" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "$COPS_MCC" >> /tmp/status$CURRMODEM.file + echo "$COPS_MNC" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo " " >> /tmp/status$CURRMODEM.file + echo " " >> /tmp/status$CURRMODEM.file + echo "$MONSTAT" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "$CONN" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file +} + +get_signal() { + TIMEOUT=3 + wget http://$IP/api/monitoring/status --load-cookies cookie -O $TMPFILE > /dev/null 2>&1 & + handle_timeout "$!" + local in_CurrentNetworkType="`cat $TMPFILE | grep \"\" | cut -d \">\" -f 2 | cut -d \"<\" -f 1`" + if [ -z $in_CurrentNetworkType ]; then + NETWORK="-" + else + [ "$in_CurrentNetworkType" = "19" ] && NETWORK="LTE" # LTE + [ "$in_CurrentNetworkType" = "9" ] && NETWORK="HSPA+" # HSPA+ + [ "$in_CurrentNetworkType" = "7" ] && NETWORK="HSPA" # HSPA + [ "$in_CurrentNetworkType" = "6" ] && NETWORK="HSUPA" # HSUPA + [ "$in_CurrentNetworkType" = "5" ] && NETWORK="HSDPA" # HSDPA + [ "$in_CurrentNetworkType" = "4" ] && NETWORK="WCDMA" # WCDMA + [ "$in_CurrentNetworkType" = "3" ] && NETWORK="EDGE" # EDGE + [ "$in_CurrentNetworkType" = "2" ] && NETWORK="GPRS" # GPRS + [ "$in_CurrentNetworkType" = "1" ] && NETWORK="GSM" # GSM + fi + SIGNAL="" + local in_SignalStrength="`cat $TMPFILE | grep \"\" | cut -d \">\" -f 2 | cut -d \"<\" -f 1`" + [ "$in_SignalStrength" != "" ] && SIGNAL="$in_SignalStrength" + + if [ "$SIGNAL" = "" ]; then + in_SignalStrength="`cat $TMPFILE | grep \"\" | cut -d \">\" -f 2 | cut -d \"<\" -f 1`" + [ "$in_SignalStrength" != "" ] && SIGNAL="$((in_SignalStrength*20))" + fi + [ -z $SIGNAL ] && SIGNAL=0 + rm -f $TMPFILE +} + +CURRMODEM=$1 +PROTO=$2 +CONN="Modem #"$CURRMODEM + +MANUF=$(uci get modem.modem$CURRMODEM.manuf) +MODEL=$(uci get modem.modem$CURRMODEM.model) +MODEM=$MANUF" "$MODEL +IP=$(uci get modem.modem$CURRMODEM.ip) + +STARTIMEX=$(date +%s) +MONSTAT="Unknown" +rm -f /tmp/monstat$CURRMODEM + +sleep 5 +TIMEOUT=5 +wget -q http://$IP/html/home.html -O nul --save-cookies cookie --keep-session-cookies +sleep 5 +wget http://$IP/api/net/current-plmn -O $TMPFILE --load-cookies cookie > /dev/null 2>&1 & +handle_timeout "$!" + +wget http://$IP/api/device/information -O $TMPFILE --load-cookies cookie > /dev/null 2>&1 & +handle_timeout "$!" +local in_mod="`cat $TMPFILE | grep \"\" | cut -d \">\" -f 2 | cut -d \"<\" -f 1`" +if [ "$in_mod" != "" ]; then + MODEM=$MANUF" "$in_mod +fi +IMEI="Unknown" +IMSI="Unknown" +ICCID="Unknown" +CNUM="*" +CNUMx="*" +in_im="`cat $TMPFILE | grep \"\" | cut -d \">\" -f 2 | cut -d \"<\" -f 1`" +if [ "$in_im" != "" ]; then + IMEI=$in_im +fi +in_im="`cat $TMPFILE | grep \"\" | cut -d \">\" -f 2 | cut -d \"<\" -f 1`" +if [ "$in_im" != "" ]; then + IMSI=$in_im +fi +in_im="`cat $TMPFILE | grep \"\" | cut -d \">\" -f 2 | cut -d \"<\" -f 1`" +if [ "$in_im" != "" ]; then + ICCID=$in_im +fi +in_im="`cat $TMPFILE | grep \"\" | cut -d \">\" -f 2 | cut -d \"<\" -f 1`" +if [ "$in_im" != "" ]; then + CNUM=$in_im +fi + + +IDP=$(uci get modem.modem$CURRMODEM.idP) +IDV=$(uci get modem.modem$CURRMODEM.idV) + +echo $IDV" : "$IDP > /tmp/msimdatax$CURRMODEM +echo "$IMEI" >> /tmp/msimdatax$CURRMODEM +echo "$IMSI" >> /tmp/msimdatax$CURRMODEM +echo "$ICCID" >> /tmp/msimdatax$CURRMODEM +echo "1" >> /tmp/msimdatax$CURRMODEM +mv -f /tmp/msimdatax$CURRMODEM /tmp/msimdata$CURRMODEM +echo "$CNUM" > /tmp/msimnumx$CURRMODEM +echo "$CNUMx" >> /tmp/msimnumx$CURRMODEM +mv -f /tmp/msimnumx$CURRMODEM /tmp/msimnum$CURRMODEM + +sleep 20 +TIMEOUT=20 +wget http://$IP/api/net/current-plmn -O $TMPFILE --load-cookies cookie > /dev/null 2>&1 & +handle_timeout "$!" +COPS="" +local in_provider="`cat $TMPFILE | grep \"\" | cut -d \">\" -f 2 | cut -d \"<\" -f 1`" +[ "$in_provider" != "" ] && COPS="$in_provider" + +if [ "$COPS" = "" ]; then + COPS="-" + in_provider="`cat $TMPFILE | grep \"\" | cut -d \">\" -f 2 | cut -d \"<\" -f 1`" + [ "$in_provider" != "" ] && COPS="$in_provider)" +fi +COPS_MCC="-" +COPS_MNC="-" +local in_mcc="`cat $TMPFILE | grep \"\" | cut -d \">\" -f 2 | cut -d \"<\" -f 1`" +if [ "$in_mcc" != "" ]; then + COPS_MCC=${in_mcc:0:3} + COPS_MNC=${in_mcc:3:3} +fi +COPS_MNC=" "$COPS_MNC +TIMEOUT=5 + +while [ 1 = 1 ]; do + get_signal + CSQ="$SIGNAL" + if [ -e /tmp/monstat$CURRMODEM ]; then + source /tmp/monstat$CURRMODEM + fi + if [ -z $MONSTAT ]; then + MONSTAT="Unknown" + fi + make_status + CURRTIME=$(date +%s) + let ELAPSE=CURRTIME-STARTIMEX + while [ $ELAPSE -lt 10 ]; do + sleep 2 + CURRTIME=$(date +%s) + let ELAPSE=CURRTIME-STARTIMEX + done + STARTIMEX=$CURRTIME +done + diff --git a/ext-rooter-basic/files/usr/lib/rooter/signal/mccmnc.data b/ext-rooter-basic/files/usr/lib/rooter/signal/mccmnc.data new file mode 100644 index 0000000..b4cdb19 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/signal/mccmnc.data @@ -0,0 +1,11 @@ +50501;Telstra +50502;Optus +50503;Vodafone +50506;3 +302220;Telus +302610;Bell +302720;Rogers Communication +24004;SWEDEN +24005;Sweden 3G +24008;Telenor SE +51503;Smart \ No newline at end of file diff --git a/ext-rooter-basic/files/usr/lib/rooter/signal/modemsignal.sh b/ext-rooter-basic/files/usr/lib/rooter/signal/modemsignal.sh new file mode 100644 index 0000000..db67156 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/signal/modemsignal.sh @@ -0,0 +1,174 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "modem signal" "$@" +} + +CURRMODEM=$1 +PROTO=$2 +CONN="Modem #"$CURRMODEM +STARTIME=$(date +%s) +STARTIMEX=$(date +%s) +SMSTIME=0 +COMMPORT="/dev/ttyUSB"$(uci get modem.modem$CURRMODEM.commport) +NUMB=0 +MONSTAT="Unknown" +rm -f /tmp/monstat$CURRMODEM + +make_connect() { + echo "Changing Port" > /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "$MODEM" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo " " >> /tmp/statusx$CURRMODEM.file + echo " " >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "$CONN" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + echo "-" >> /tmp/statusx$CURRMODEM.file + mv -f /tmp/statusx$CURRMODEM.file /tmp/status$CURRMODEM.file +} + +make_signal() { + echo "$COMMPORT" > /tmp/statusx$CURRMODEM.file + echo "$CSQ" >> /tmp/statusx$CURRMODEM.file + echo "$CSQ_PER" >> /tmp/statusx$CURRMODEM.file + echo "$CSQ_RSSI" >> /tmp/statusx$CURRMODEM.file + echo "$MODEM" >> /tmp/statusx$CURRMODEM.file + echo "$COPS" >> /tmp/statusx$CURRMODEM.file + echo "$MODE" >> /tmp/statusx$CURRMODEM.file + echo "$LAC" >> /tmp/statusx$CURRMODEM.file + echo "$LAC_NUM" >> /tmp/statusx$CURRMODEM.file + echo "$CID" >> /tmp/statusx$CURRMODEM.file + echo "$CID_NUM" >> /tmp/statusx$CURRMODEM.file + echo "$COPS_MCC" >> /tmp/statusx$CURRMODEM.file + echo "$COPS_MNC" >> /tmp/statusx$CURRMODEM.file + echo "$RNC" >> /tmp/statusx$CURRMODEM.file + echo "$RNC_NUM" >> /tmp/statusx$CURRMODEM.file + echo "$DOWN" >> /tmp/statusx$CURRMODEM.file + echo "$UP" >> /tmp/statusx$CURRMODEM.file + echo "$ECIO" >> /tmp/statusx$CURRMODEM.file + echo "$RSCP" >> /tmp/statusx$CURRMODEM.file + echo "$ECIO1" >> /tmp/statusx$CURRMODEM.file + echo "$RSCP1" >> /tmp/statusx$CURRMODEM.file + echo "$MONSTAT" >> /tmp/statusx$CURRMODEM.file + echo "$CELL" >> /tmp/statusx$CURRMODEM.file + echo "$MODTYPE" >> /tmp/statusx$CURRMODEM.file + echo "$CONN" >> /tmp/statusx$CURRMODEM.file + echo "$CHANNEL" >> /tmp/statusx$CURRMODEM.file + echo "$CNUM" >> /tmp/statusx$CURRMODEM.file + echo "$CNAM" >> /tmp/statusx$CURRMODEM.file + echo "$LBAND" >> /tmp/statusx$CURRMODEM.file + mv -f /tmp/statusx$CURRMODEM.file /tmp/status$CURRMODEM.file +} + +get_basic() { + $ROOTER/signal/basedata.sh $CURRMODEM $COMMPORT + source /tmp/base$CURRMODEM.file + rm -f /tmp/base$CURRMODEM.file + $ROOTER/signal/celldata.sh $CURRMODEM $COMMPORT + source /tmp/cell$CURRMODEM.file + rm -f /tmp/cell$CURRMODEM.file + lua $ROOTER/signal/celltype.lua "$MODEM" $CURRMODEM + source /tmp/celltype$CURRMODEM + rm -f /tmp/celltype$CURRMODEM +} + +get_basic +while [ 1 = 1 ]; do + if [ -e /tmp/port$CURRMODEM.file ]; then + source /tmp/port$CURRMODEM.file + rm -f /tmp/port$CURRMODEM.file + COMMPORT="/dev/ttyUSB"$PORT + uci set modem.modem$CURRMODEM.commport=$PORT + make_connect + get_basic + STARTIME=$(date +%s) + else + CURRTIME=$(date +%s) + let ELAPSE=CURRTIME-STARTIME + if [ $ELAPSE -ge 60 ]; then + STARTIME=$CURRTIME + $ROOTER/signal/celldata.sh $CURRMODEM $COMMPORT + source /tmp/cell$CURRMODEM.file + rm -f /tmp/cell$CURRMODEM.file + fi + if [ -e /tmp/port$CURRMODEM.file ]; then + source /tmp/port$CURRMODEM.file + rm -f /tmp/port$CURRMODEM.file + COMMPORT="/dev/ttyUSB"$PORT + uci set modem.modem$CURRMODEM.commport=$PORT + make_connect + get_basic + STARTIME=$(date +%s) + else + VENDOR=$(uci get modem.modem$CURRMODEM.idV) + case $VENDOR in + "1199"|"0f3d"|"413c" ) + $ROOTER/common/sierradata.sh $CURRMODEM $COMMPORT + ;; + "19d2" ) + $ROOTER/common/ztedata.sh $CURRMODEM $COMMPORT + ;; + "12d1" ) + $ROOTER/common/huaweidata.sh $CURRMODEM $COMMPORT + ;; + * ) + $ROOTER/common/otherdata.sh $CURRMODEM $COMMPORT + ;; + esac + CHANNEL="-" + source /tmp/signal$CURRMODEM.file + rm -f /tmp/signal$CURRMODEM.file + if [ -e /tmp/phonenumber$CURRMODEM ]; then + source /tmp/phonenumber$CURRMODEM + rm -f /tmp/phonenumber$CURRMODEM + fi + make_signal + uci set modem.modem$CURRMODEM.cmode="1" + uci commit modem + if [ -e /tmp/monstat$CURRMODEM ]; then + source /tmp/monstat$CURRMODEM + fi + if [ -z "$MONSTAT" ]; then + MONSTAT="Unknown" + fi + fi + fi + if [ -e /etc/netspeed ]; then + NETSPEED=60 + else + NETSPEED=10 + fi + CURRTIME=$(date +%s) + let ELAPSE=CURRTIME-STARTIMEX + while [ $ELAPSE -lt $NETSPEED ]; do + sleep 2 + CURRTIME=$(date +%s) + let ELAPSE=CURRTIME-STARTIMEX + done + STARTIMEX=$CURRTIME +done + diff --git a/ext-rooter-basic/files/usr/lib/rooter/signal/otherhostless.sh b/ext-rooter-basic/files/usr/lib/rooter/signal/otherhostless.sh new file mode 100644 index 0000000..b570994 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/signal/otherhostless.sh @@ -0,0 +1,146 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "hostless " "$@" +} + +get_basic() { + # get basic data here + # + # how it is done with other modems + # + COMMPORT="/dev/ttyUSB"$(uci get modem.modem$CURRMODEM.commport) + $ROOTER/signal/basedata.sh $CURRMODEM $COMMPORT + source /tmp/base$CURRMODEM.file + rm -f /tmp/base$CURRMODEM.file + $ROOTER/signal/celldata.sh $CURRMODEM $COMMPORT + source /tmp/cell$CURRMODEM.file + rm -f /tmp/cell$CURRMODEM.file + lua $ROOTER/signal/celltype.lua "$MODEM" $CURRMODEM + source /tmp/celltype$CURRMODEM + rm -f /tmp/celltype$CURRMODEM +} + +make_signal() { + # get signal data here + # + # how it is done with other modems (Sierra) + # + COMMPORT="/dev/ttyUSB"$(uci get modem.modem$CURRMODEM.commport) + MANUF=$(uci get modem.modem$CURRMODEM.manuf) + MODEL=$(uci get modem.modem$CURRMODEM.model) + MODEM=$MANUF" "$MODEL + $ROOTER/common/ubloxdata.sh $CURRMODEM $COMMPORT + source /tmp/signal$CURRMODEM.file + rm -f /tmp/signal$CURRMODEM.file + echo "$COMMPORT" > /tmp/statusx$CURRMODEM.file + echo "$CSQ" >> /tmp/statusx$CURRMODEM.file + echo "$CSQ_PER" >> /tmp/statusx$CURRMODEM.file + echo "$CSQ_RSSI" >> /tmp/statusx$CURRMODEM.file + echo "$MODEM" >> /tmp/statusx$CURRMODEM.file + echo "$COPS" >> /tmp/statusx$CURRMODEM.file + echo "$MODE" >> /tmp/statusx$CURRMODEM.file + echo "$LAC" >> /tmp/statusx$CURRMODEM.file + echo "$LAC_NUM" >> /tmp/statusx$CURRMODEM.file + echo "$CID" >> /tmp/statusx$CURRMODEM.file + echo "$CID_NUM" >> /tmp/statusx$CURRMODEM.file + echo "$COPS_MCC" >> /tmp/statusx$CURRMODEM.file + echo "$COPS_MNC" >> /tmp/statusx$CURRMODEM.file + echo "$RNC" >> /tmp/statusx$CURRMODEM.file + echo "$RNC_NUM" >> /tmp/statusx$CURRMODEM.file + echo "$DOWN" >> /tmp/statusx$CURRMODEM.file + echo "$UP" >> /tmp/statusx$CURRMODEM.file + echo "$ECIO" >> /tmp/statusx$CURRMODEM.file + echo "$RSCP" >> /tmp/statusx$CURRMODEM.file + echo "$ECIO1" >> /tmp/statusx$CURRMODEM.file + echo "$RSCP1" >> /tmp/statusx$CURRMODEM.file + echo "$MONSTAT" >> /tmp/statusx$CURRMODEM.file + echo "$CELL" >> /tmp/statusx$CURRMODEM.file + echo "$MODTYPE" >> /tmp/statusx$CURRMODEM.file + echo "$CONN" >> /tmp/statusx$CURRMODEM.file + echo "$CHANNEL" >> /tmp/statusx$CURRMODEM.file + echo "$CNUM" >> /tmp/statusx$CURRMODEM.file + echo "$CNAM" >> /tmp/statusx$CURRMODEM.file + echo "$LBAND" >> /tmp/statusx$CURRMODEM.file + mv -f /tmp/statusx$CURRMODEM.file /tmp/status$CURRMODEM.file +} + +CURRMODEM=$1 +PROTO=$2 +CONN="Modem #"$CURRMODEM + +MANUF=$(uci get modem.modem$CURRMODEM.manuf) +MODEL=$(uci get modem.modem$CURRMODEM.model) +MODEM=$MANUF" "$MODEL +IP=$(uci get modem.modem$CURRMODEM.ip) +MONSTAT="Unknown" +rm -f /tmp/monstat$CURRMODEM + +idV=$(uci get modem.modem$CURRMODEM.idV) +idP=$(uci get modem.modem$CURRMODEM.idP) +if [ $idV = 1546 -a $idP = 1146 ]; then + get_basic +fi + + +STARTIMEX=$(date +%s) +MONSTAT="Unknown" +rm -f /tmp/monstat$CURRMODEM + +while [ 1 = 1 ]; do + if [ $idV = 1546 -a $idP = 1146 ]; then + make_signal + else + echo "$IP" > /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "$MODEM" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo " " >> /tmp/status$CURRMODEM.file + echo " " >> /tmp/status$CURRMODEM.file + echo "$MONSTAT" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "$CONN" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + fi + if [ -e /tmp/monstat$CURRMODEM ]; then + source /tmp/monstat$CURRMODEM + fi + if [ -z $MONSTAT ]; then + MONSTAT="Unknown" + fi + CURRTIME=$(date +%s) + + if [ -e /etc/netspeed ]; then + NETSPEED=60 + else + NETSPEED=10 + fi + + + + let ELAPSE=CURRTIME-STARTIMEX + while [ $ELAPSE -lt $NETSPEED ]; do + sleep 2 + CURRTIME=$(date +%s) + let ELAPSE=CURRTIME-STARTIMEX + done + STARTIMEX=$CURRTIME +done diff --git a/ext-rooter-basic/files/usr/lib/rooter/signal/status.sh b/ext-rooter-basic/files/usr/lib/rooter/signal/status.sh new file mode 100644 index 0000000..56e47a2 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/signal/status.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +CURRMODEM=$1 +MSG=$2 +MSG1=$3 +COMMPORT="/dev/ttyUSB"$(uci get modem.modem$CURRMODEM.commport) +if [ -z $MSG1 ]; then + MSG1="-" + COMMPORT="-" +fi + +echo "$COMMPORT" > /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "$MSG" >> /tmp/status$CURRMODEM.file +echo "$MSG1" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo " " >> /tmp/status$CURRMODEM.file +echo " " >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "Modem $CURRMODEM" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file +echo "-" >> /tmp/status$CURRMODEM.file + diff --git a/ext-rooter-basic/files/usr/lib/rooter/signal/ztehostless.sh b/ext-rooter-basic/files/usr/lib/rooter/signal/ztehostless.sh new file mode 100644 index 0000000..a3ee323 --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/signal/ztehostless.sh @@ -0,0 +1,208 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "hostless " "$@" +} + +TMPFILE="/tmp/XXXXXX" +TMPFILE1="/tmp/XXXXXX1" + +make_status() { + echo "$IP" > /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "$CSQ" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "$MODEM" >> /tmp/status$CURRMODEM.file + echo "$PROV" >> /tmp/status$CURRMODEM.file + echo "$NETWORK" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo " " >> /tmp/status$CURRMODEM.file + echo " " >> /tmp/status$CURRMODEM.file + echo "$MONSTAT" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file + echo "$CONN" >> /tmp/status$CURRMODEM.file + echo "-" >> /tmp/status$CURRMODEM.file +} + +handle_timeout(){ + local wget_pid="$1" + local count=0 + ps | grep -v grep | grep $wget_pid + res="$?" + while [ "$res" = 0 -a $count -lt "$((TIMEOUT))" ]; do + sleep 1 + count=$((count+1)) + ps | grep -v grep | grep $wget_pid + res="$?" + done + + if [ "$res" = 0 ]; then + log "Killing process on timeout" + kill "$wget_pid" 2> /dev/null + ps | grep -v grep | grep $wget_pid + res="$?" + if [ "$res" = 0 ]; then + log "Killing process on timeout" + kill -9 $wget_pid 2> /dev/null + fi + fi +} + +get_zte_model() { + uci set modem.modem$CURRMODEM.ztemodel=UNKNOWN + uci commit modem + SERIAL=$(uci get modem.modem$CURRMODEM.serial) + if [ $PROD = 1405 ]; then + if [ "`echo $SERIAL | grep P680A1ZTED`" != "" ]; then + uci set modem.modem$CURRMODEM.ztemodel=MF669 + uci set modem.modem$CURRMODEM.model=MF669/MF70 + uci commit modem + else + uci set modem.modem$CURRMODEM.ztemodel=V5 + uci set modem.modem$CURRMODEM.model=MF823/MF825/MF93D + uci commit modem + fi + fi + if [ $PROD = 0349 ]; then + uci set modem.modem$CURRMODEM.ztemodel=MF821D + uci set modem.modem$CURRMODEM.model=MF821D + uci commit modem + fi + if [ $PROD = 0166 ]; then + if [ "`echo $MODEL | grep MF91`" != "" ]; then + uci set modem.modem$CURRMODEM.ztemodel=MF91D + uci set modem.modem$CURRMODEM.model=MF91D + uci commit modem + else + if [ "`echo $MODEL | grep MF91`" != "" ]; then + uci set modem.modem$CURRMODEM.ztemodel=MF91 + uci set modem.modem$CURRMODEM.model=MF91 + uci commit modem + fi + fi + fi + if [ $PROD = 1408 ]; then + if [ "`echo $SERIAL | grep P680A1ZTED`" != "" ]; then + uci set modem.modem$CURRMODEM.ztemodel=V5 + uci set modem.modem$CURRMODEM.model=MF93D + uci commit modem + fi + fi +} + +get_basic(){ + ZTEMOD=$(uci get modem.modem$CURRMODEM.ztemodel) + TIMEOUT=3 + case $ZTEMOD in + "MF669" ) + wget http://$IP/logo_data.asp -O $TMPFILE > /dev/null 2>&1 & + handle_timeout "$!" + LUCKNUM="`cat $TMPFILE | grep lucknum | cut -d \' -f 2`" + ;; + "V5" ) + wget "http://$IP/goform/goform_get_cmd_process?isTest=false&cmd=network_type%2Cwan_ipaddr%2Cppp_status%2Cprefer_dns_auto%2Cstandby_dns_auto%2Csignalbar&multi_data=1&_=1376406501348" -O $TMPFILE > /dev/null 2>&1 & + handle_timeout "$!" + ;; + "MF821D"|"MF91D"|"MF91" ) + wget http://$IP/goform/status_update -O $TMPFILE > /dev/null 2>&1 & + handle_timeout "$!" + ;; + esac + PROV="`cat $TMPFILE | grep network_provider | cut -d \' -f 2`" + [ -z $PROV ] && PROV="-" + rm -f $TMPFILE +} + +get_signal(){ + TIMEOUT=3 + case $ZTEMOD in + "MF669" ) + wget http://$IP/logo_data.asp -O $TMPFILE > /dev/null 2>&1 & + handle_timeout "$!" + SIGNAL="`cat $TMPFILE | grep signalbar | cut -d \' -f 2`" + NETWORK="`cat $TMPFILE | grep network_type | cut -d \' -f 2`" + ;; + "V5" ) + wget "http://$IP/goform/goform_get_cmd_process?isTest=false&cmd=signalbar,wan_csq,network_type,network_provider,ppp_status,modem_main_state,rmcc,rmnc,,domain_stat,cell_id,rssi,rscp,lte_rssi,lte_rsrq,lte_rsrp,lte_snr,ecio,sms_received_flag,sts_received_flag,simcard_roam&multi_data=1&sms_received_flag_flag=0&sts_received_flag_flag=0" -O $TMPFILE > /dev/null 2>&1 & + handle_timeout "$!" + SIGNAL="`cat $TMPFILE | grep signalbar'\"\:\"[^\"]*' -o | cut -d'"' -f 3`" + NETWORK="`cat $TMPFILE | grep network_type'\"\:\"[^\"]*' -o | cut -d'"' -f 3`" + ;; + "MF821D"|"MF91D"|"MF91" ) + wget http://$IP/goform/status_update -O $TMPFILE > /dev/null 2>&1 & + handle_timeout "$!" + SIGNAL="`cat $TMPFILE | cut -d \; -f 1-1`" + net="`cat $TMPFILE | cut -d \; -f 4-4`" + if [ -z $net ]; then + NETWORK="-" + else + [ "$net" = "0" ] && NETWORK="No Service" + [ "$net" = "1" ] && NETWORK="LTE" + [ "$net" = "2" ] && NETWORK="EVDO" + [ "$net" = "3" ] && NETWORK="CDMA" + [ "$net" = "4" ] && NETWORK="WCDMA" + [ "$net" = "5" ] && NETWORK="GSM" + [ "$net" = "6" ] && NETWORK="HSDPA" + [ "$net" = "7" ] && NETWORK="HUSPA" + [ "$net" = "8" ] && NETWORK="HSPA+" + [ "$net" = "10" ] && NETWORK="EDGE" + [ "$net" = "11" ] && NETWORK="GPRS" + fi + ;; + esac + [ -z $SIGNAL ] && SIGNAL=0 + [ -z $NETWORK ] && NETWORK="-" + rm -f $TMPFILE +} + +CURRMODEM=$1 +PROTO=$2 +CONN="Modem #"$CURRMODEM + +PROD=$(uci get modem.modem$CURRMODEM.idP) +get_zte_model +MANUF=$(uci get modem.modem$CURRMODEM.manuf) +MODEL=$(uci get modem.modem$CURRMODEM.model) +MODEM=$MANUF" "$MODEL +IP=$(uci get modem.modem$CURRMODEM.ip) + +get_basic + +STARTIMEX=$(date +%s) +MONSTAT="Unknown" +rm -f /tmp/monstat$CURRMODEM + +while [ 1 = 1 ]; do + get_signal + CSQ="$SIGNAL Bars" + if [ -e /tmp/monstat$CURRMODEM ]; then + source /tmp/monstat$CURRMODEM + fi + if [ -z $MONSTAT ]; then + MONSTAT="Unknown" + fi + make_status + CURRTIME=$(date +%s) + let ELAPSE=CURRTIME-STARTIMEX + while [ $ELAPSE -lt 10 ]; do + sleep 2 + CURRTIME=$(date +%s) + let ELAPSE=CURRTIME-STARTIMEX + done + STARTIMEX=$CURRTIME +done + diff --git a/ext-rooter-basic/files/usr/lib/rooter/sms/check_sms.sh b/ext-rooter-basic/files/usr/lib/rooter/sms/check_sms.sh new file mode 100644 index 0000000..6f651af --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/sms/check_sms.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter +ROOTER_LINK="/tmp/links" + +CURRMODEM=$1 +CPORT=$(uci get modem.modem$CURRMODEM.commport) +sleep 10 +OX=$($ROOTER/gcom/gcom-locked "/dev/ttyUSB$CPORT" "smschk.gcom" "$CURRMODEM") + +ERROR="ERROR" +if `echo ${OX} | grep "${ERROR}" 1>/dev/null 2>&1` +then + uci set modem.modem$CURRMODEM.sms=0 + uci commit modem +else + uci set modem.modem$CURRMODEM.sms=1 + uci commit modem + if [ -e /usr/lib/sms/processsms ]; then + if [ ! -e $ROOTER_LINK/processsms$CURRMODEM ]; then + ln -s /usr/lib/sms/processsms $ROOTER_LINK/processsms$CURRMODEM + $ROOTER_LINK/processsms$CURRMODEM $CURRMODEM & + fi + fi +fi diff --git a/ext-rooter-basic/files/usr/lib/rooter/ussd.sh b/ext-rooter-basic/files/usr/lib/rooter/ussd.sh new file mode 100644 index 0000000..0c0fc9c --- /dev/null +++ b/ext-rooter-basic/files/usr/lib/rooter/ussd.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +CURRMODEM=$(uci get modem.general.modemnum) +COMMPORT="/dev/ttyUSB"$(uci get modem.modem$CURRMODEM.commport) +ROOTER=/usr/lib/rooter + +USSDSTR="$1" + +while true; do + if [ -n "$USSDSTR" ]; then + ATCMDD="AT+CUSD=1,\"$USSDSTR\",15" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "ussd.gcom" "$CURRMODEM" "$ATCMDD" | tr "\n" "\v") + USSD=$(echo "$OX" | grep -o "+CUSD: .\+\",15" | tr "\v" "\n") + USSDL=${#USSD} + if [ $USSDL -ge 14 ]; then + USSDL=$((USSDL - 14)) + USSD=$(printf "${USSD:10:$USSDL}") + echo "$USSD" + fi + fi + printf "(Leave blank to quit.) Enter a USSD string to send: "; read USSDSTR + if [ -z "$USSDSTR" ]; then + break + fi +done +ATCMDD="AT+CUSD=2" +OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") diff --git a/ext-rooter4/Makefile b/ext-rooter4/Makefile new file mode 100644 index 0000000..40e7d03 --- /dev/null +++ b/ext-rooter4/Makefile @@ -0,0 +1,33 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-rooter4 +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-rooter4 + SECTION:=utils + CATEGORY:=ROOter + DEPENDS:=+ext-rooter-basic +ext-extra \ + +picocom +luci-app-hotspot + TITLE:=ROOter support for 4meg routers + PKGARCH:=all +endef + +define Package/ext-rooter4/description + Helper scripts to install ROOter on 4meg routers +endef + +define Build/Compile +endef + +define Package/ext-rooter4/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,ext-rooter4)) diff --git a/ext-rooter4/files/etc/config/failover b/ext-rooter4/files/etc/config/failover new file mode 100644 index 0000000..c626526 --- /dev/null +++ b/ext-rooter4/files/etc/config/failover @@ -0,0 +1,15 @@ + +config policy 'faillist' + +config tracker 'failover' + option reliability '1' + option count '1' + option pingtime '5' + option pingwait '7' + option down '2' + option up '2' + list trackip '8.8.8.8' + +config enabled 'enabled' + option enabled '0' + diff --git a/ext-rooter4/files/etc/flash b/ext-rooter4/files/etc/flash new file mode 100644 index 0000000..8ff4fad --- /dev/null +++ b/ext-rooter4/files/etc/flash @@ -0,0 +1 @@ +FLASH="4" diff --git a/ext-rooter4/files/usr/lib/lua/luci/controller/failover.lua b/ext-rooter4/files/usr/lib/lua/luci/controller/failover.lua new file mode 100644 index 0000000..28bcb0b --- /dev/null +++ b/ext-rooter4/files/usr/lib/lua/luci/controller/failover.lua @@ -0,0 +1,39 @@ +module("luci.controller.failover", package.seeall) + +function index() + local page + + if not nixio.fs.access("/etc/config/failover") then + return + end + + page = entry({"admin", "network", "failover"}, cbi("rooter/failover"), "Internet Failover", 600) + page.dependent = true + + entry({"admin", "network", "get_fstatus"}, call("action_get_fstatus")) +end + +function action_get_fstatus() + local file + mArray = {} + file = io.open("/tmp/wanstatus", "r") + if file == nil then + mArray["wan"] = " " + else + mArray["wan"] = file:read("*line") + mArray["winter"] = file:read("*line") + mArray["wstatus"] = file:read("*line") + file:close() + end + file = io.open("/tmp/modemstatus", "r") + if file == nil then + mArray["modem"] = " " + else + mArray["modem"] = file:read("*line") + mArray["minter"] = file:read("*line") + mArray["mstatus"] = file:read("*line") + file:close() + end + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end diff --git a/ext-rooter4/files/usr/lib/lua/luci/model/cbi/rooter/failover.lua b/ext-rooter4/files/usr/lib/lua/luci/model/cbi/rooter/failover.lua new file mode 100644 index 0000000..1eead5f --- /dev/null +++ b/ext-rooter4/files/usr/lib/lua/luci/model/cbi/rooter/failover.lua @@ -0,0 +1,139 @@ +local utl = require "luci.util" + +function cbiAddWan(field) + i = 0 + uci.cursor():foreach("failover", "member", + function (section) + line = section[".name"] + s, e = line:find("Modem") + if s == nil then + field:value(section[".name"]) + if i == 0 then + field.default = section[".name"] + end + i = 1 + end + end + ) +end + +function cbiAddModem(field) + i = 0 + uci.cursor():foreach("failover", "member", + function (section) + line = section[".name"] + s, e = line:find("Modem") + if s ~= nil then + field:value(section[".name"]) + if i == 0 then + field.default = section[".name"] + end + i = 1 + end + end + ) +end + +m = Map("failover", translate("Internet Connection Failover"), translate("Enable a connection failover system between two Internet sources.")) + +m.on_after_commit = function(self) + luci.sys.call("/usr/lib/rooter/luci/failchk.sh") +end + +enabl = m:section(NamedSection, "enabled", "enabled", "") + enabl.addremove = false + enabl.dynamic = false + +e = enabl:option(Flag, "enabled", translate("Failover Enabled")) +e.rmempty = false + +policy = m:section(NamedSection, "faillist", "policy", "") + policy.addremove = false + policy.dynamic = false + +use_wan = policy:option(ListValue, "use_wan", translate("Primary Internet Source"), + translate("Select Primary Internet source.")) + cbiAddWan(use_wan) + +use_modem = policy:option(ListValue, "use_modem", translate("Secondary Internet Source"), + translate("Select Secondary (Backup) Internet source.")) + cbiAddModem(use_modem) + +tracker = m:section(NamedSection, "failover", "tracker", "") + tracker.addremove = false + tracker.dynamic = false + +reliability = tracker:option(Value, "reliability", translate("Tracking reliability"), + translate("Acceptable values: 1-100. This many Tracking IP addresses must respond for the source to be deemed up")) + reliability.datatype = "range(1, 100)" + reliability.default = "1" + +count = tracker:option(ListValue, "count", translate("Ping count")) + count.default = "1" + count:value("1") + count:value("2") + count:value("3") + count:value("4") + count:value("5") + +interval = tracker:option(ListValue, "pingtime", translate("Ping interval"), + translate("Amount of time between tracking tests")) + interval.default = "5" + interval:value("5", translate("5 seconds")) + interval:value("10", translate("10 seconds")) + interval:value("20", translate("20 seconds")) + interval:value("30", translate("30 seconds")) + interval:value("60", translate("1 minute")) + interval:value("300", translate("5 minutes")) + interval:value("600", translate("10 minutes")) + interval:value("900", translate("15 minutes")) + interval:value("1800", translate("30 minutes")) + interval:value("3600", translate("1 hour")) + +timeout = tracker:option(ListValue, "pingwait", translate("Ping timeout")) + timeout.default = "2" + timeout:value("1", translate("1 second")) + timeout:value("2", translate("2 seconds")) + timeout:value("3", translate("3 seconds")) + timeout:value("4", translate("4 seconds")) + timeout:value("5", translate("5 seconds")) + timeout:value("6", translate("6 seconds")) + timeout:value("7", translate("7 seconds")) + timeout:value("8", translate("8 seconds")) + timeout:value("9", translate("9 seconds")) + timeout:value("10", translate("10 seconds")) + +down = tracker:option(ListValue, "down", translate("Interface down"), + translate("Interface will be deemed down after this many failed ping tests in a row")) + down.default = "2" + down:value("1") + down:value("2") + down:value("3") + down:value("4") + down:value("5") + down:value("6") + down:value("7") + down:value("8") + down:value("9") + down:value("10") + +up = tracker:option(ListValue, "up", translate("Interface up"), + translate("Downed interface will be deemed up after this many successful ping tests in a row")) + up.default = "2" + up:value("1") + up:value("2") + up:value("3") + up:value("4") + up:value("5") + up:value("6") + up:value("7") + up:value("8") + up:value("9") + up:value("10") + +cb2 = tracker:option(DynamicList, "trackip", translate("Tracking IP"), + translate("This IP address will be pinged to dermine if the link is up or down.")) + cb2.datatype = "ipaddr" + +return m + diff --git a/ext-rooter4/files/usr/lib/lua/luci/view/admin_status/index/failover.htm b/ext-rooter4/files/usr/lib/lua/luci/view/admin_status/index/failover.htm new file mode 100644 index 0000000..bab3b9f --- /dev/null +++ b/ext-rooter4/files/usr/lib/lua/luci/view/admin_status/index/failover.htm @@ -0,0 +1 @@ +<%+rooter/failover_overview%> \ No newline at end of file diff --git a/ext-rooter4/files/usr/lib/lua/luci/view/rooter/failover_overview.htm b/ext-rooter4/files/usr/lib/lua/luci/view/rooter/failover_overview.htm new file mode 100644 index 0000000..f836a57 --- /dev/null +++ b/ext-rooter4/files/usr/lib/lua/luci/view/rooter/failover_overview.htm @@ -0,0 +1,120 @@ + + +
                                          + <%:Failover Status%> +
                                          <%:Loading%> Collecting data...
                                          +
                                          + + diff --git a/ext-rooter4/files/usr/lib/rooter/connect/failover.sh b/ext-rooter4/files/usr/lib/rooter/connect/failover.sh new file mode 100644 index 0000000..646b6d8 --- /dev/null +++ b/ext-rooter4/files/usr/lib/rooter/connect/failover.sh @@ -0,0 +1,183 @@ +#!/bin/sh +. /lib/functions.sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "Failover System" "$@" +} + +log "Failover System is Started" + +i=1 +STAT="0" +track_ips= +WAN_STATUS="0" +MODEM_STATUS="0" +MODEM_IFUP=true +rm -f /tmp/wanstatus +rm -f /tmp/modemstatus +rm -f /tmp/mdown$CURRMODEM + +get_interface() { + OX=$1 + case $OX in + "Wan" ) + inter="wan" + ;; + "Hotspot" ) + inter="wwan" + ;; + * ) + inter="wan${OX:5}" + ;; + esac + uci set failover.$OX.interface=$inter +} + +ping_interface() { + interf=$(uci get failover.$1.interface) + if [ $interf = "wwan" ]; then + IFN="$(ubus -S call network.wireless status | jsonfilter -e '@.*.interfaces[@.config.mode="sta"].ifname')" + else + IFN=$(uci get network.$interf.ifname) + fi + if [ ! -z $IFN ]; then + STAT="1" + host_up_count=0 + score_up=$UP + score_dwn=$DOWN + lost=0 + while true; do + if [ ! -z "$track_ips" ]; then + for track_ip in $track_ips; do + ping -I $IFN -c $COUNT -W $TIMEOUT -s 4 -q $track_ip &> /dev/null + if [ $? -eq 0 ]; then + let host_up_count++ + else + let lost++ + fi + done + if [ $host_up_count -lt $RELIAB ]; then + let score_dwn-- + score_up=$UP + if [ $score_dwn -eq 0 ]; then + STAT="1" + break + fi + else + let score_up-- + score_dwn=$DOWN + if [ $score_up -eq 0 ]; then + STAT="2" + break + fi + fi + else + log "No Tracking IP, stopping Failover" + exit + fi + host_up_count=0 + sleep $INTERVAL + done + else + STAT="0" + fi +} + +make_status() { + name=$2 + status=$3 + interface=$(uci get failover.$name.interface) + echo "$name" > /tmp/$1"status" + echo "$interface" >> /tmp/$1"status" + case $status in + "0" ) + echo "disabled" >> /tmp/$1"status" + ;; + "1" ) + echo "offline" >> /tmp/$1"status" + ;; + "2" ) + echo "online" >> /tmp/$1"status" + ;; + "3" ) + echo "onlinedwn" >> /tmp/$1"status" + ;; + esac +} + +list_track_ips() { + track_ips="$1 $track_ips" +} + +rm -f /tmp/wanstatus +rm -f /tmp/modemstatus + +config_load failover +config_list_foreach "failover" "trackip" list_track_ips + +use_wan=$(uci get failover.faillist.use_wan) +get_interface $use_wan +use_modem=$(uci get failover.faillist.use_modem) +get_interface $use_modem +uci commit failover + +interf=$(uci get failover.$use_wan.interface) +uci set network.$interf.metric="99" +uci commit network +ifup $interf + +TIMEOUT=$(uci get failover.failover.pingwait) +INTERVAL=$(uci get failover.failover.pingtime) +RELIAB=$(uci get failover.failover.reliability) +DOWN=$(uci get failover.failover.down) +UP=$(uci get failover.failover.up) +COUNT=$(uci get failover.failover.count) + +while true; do + use_wan=$(uci get failover.faillist.use_wan) + use_modem=$(uci get failover.faillist.use_modem) + ping_interface $use_wan + make_status "wan" $use_wan $STAT + WAN_STATUS=$STAT + if [ $WAN_STATUS = "2" ];then + if [ $MODEM_IFUP = true -a $MODEM_STATUS == "2" ]; then + MODEM_IFUP=false + MODEM_STATUS="3" + ifdown $(uci get failover.$use_modem.interface) + fi + else + if [ $MODEM_IFUP = false -a $MODEM_STATUS != "0" ]; then + MODEM_IFUP=true + MODEM_STATUS="2" + ifup $(uci get failover.$use_modem.interface) + sleep 10 + fi + fi + + if [ $MODEM_STATUS != "3" ]; then + ping_interface $use_modem + make_status "modem" $use_modem $STAT + MODEM_STATUS=$STAT + MODEM_IFUP=true + if [ $STAT = "1" ]; then + echo "0" > /tmp/mdown$CURRMODEM + else + rm -f /tmp/mdown$CURRMODEM + fi + else + interf=$(uci get failover.$use_modem.interface) + IFN=$(uci get network.$interf.ifname) + if [ -z $IFN ]; then + MODEM_IFUP=false + make_status "modem" $use_modem "0" + MODEM_STATUS="0" + else + make_status "modem" $use_modem "3" + MODEM_STATUS="3" + MODEM_IFUP=false + fi + fi + sleep 10 +done \ No newline at end of file diff --git a/ext-rooter4/files/usr/lib/rooter/luci/failchk.sh b/ext-rooter4/files/usr/lib/rooter/luci/failchk.sh new file mode 100644 index 0000000..286ab0c --- /dev/null +++ b/ext-rooter4/files/usr/lib/rooter/luci/failchk.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "Failover Check" "$@" +} + +killall -9 failover.sh +ENB=$(uci get failover.enabled.enabled) +if [ $ENB = "1" ]; then + if [ -e $ROOTER/connect/failover.sh ]; then + log "Restarting Failover System" + $ROOTER/connect/failover.sh & + fi +fi diff --git a/ext-rooter4/files/usr/lib/rooter/removeipv6.sh b/ext-rooter4/files/usr/lib/rooter/removeipv6.sh new file mode 100644 index 0000000..42dbf21 --- /dev/null +++ b/ext-rooter4/files/usr/lib/rooter/removeipv6.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +uci delete network.wan6 +uci commit network + +/etc/init.d/network restart \ No newline at end of file diff --git a/ext-rooter45/Makefile b/ext-rooter45/Makefile new file mode 100644 index 0000000..e1d775f --- /dev/null +++ b/ext-rooter45/Makefile @@ -0,0 +1,32 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-rooter45 +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-rooter45 + SECTION:=utils + CATEGORY:=ROOter + DEPENDS:=+ext-rooter4 +wget + TITLE:=ROOter support for 8/32meg routers + PKGARCH:=all +endef + +define Package/ext-rooter45/description + Helper scripts to install ROOter on 8/32meg routers +endef + +define Build/Compile +endef + +define Package/ext-rooter45/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,ext-rooter45)) diff --git a/ext-rooter45/files/usr/lib/lua/luci/view/admin_status/indexnew.htm b/ext-rooter45/files/usr/lib/lua/luci/view/admin_status/indexnew.htm new file mode 100644 index 0000000..4754d8c --- /dev/null +++ b/ext-rooter45/files/usr/lib/lua/luci/view/admin_status/indexnew.htm @@ -0,0 +1,710 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008-2011 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<% + local fs = require "nixio.fs" + local util = require "luci.util" + local stat = require "luci.tools.status" + local ver = require "luci.version" + + local has_ipv6 = fs.access("/proc/net/ipv6_route") + local has_dhcp = fs.access("/etc/config/dhcp") + local has_wifi = ((fs.stat("/etc/config/wireless", "size") or 0) > 0) + + local sysinfo = luci.util.ubus("system", "info") or { } + local boardinfo = luci.util.ubus("system", "board") or { } + local unameinfo = nixio.uname() or { } + + local meminfo = sysinfo.memory or { + total = 0, + free = 0, + buffered = 0, + shared = 0 + } + + local swapinfo = sysinfo.swap or { + total = 0, + free = 0 + } + + local has_dsl = fs.access("/etc/init.d/dsl_control") + + if luci.http.formvalue("status") == "1" then + local ntm = require "luci.model.network".init() + local wan = ntm:get_wannet() + local wan6 = ntm:get_wan6net() + + local conn_count = tonumber(( + luci.sys.exec("wc -l /proc/net/nf_conntrack") or + luci.sys.exec("wc -l /proc/net/ip_conntrack") or + ""):match("%d+")) or 0 + + local conn_max = tonumber(( + luci.sys.exec("sysctl net.nf_conntrack_max") or + luci.sys.exec("sysctl net.ipv4.netfilter.ip_conntrack_max") or + ""):match("%d+")) or 4096 + + local rv = { + uptime = sysinfo.uptime or 0, + localtime = os.date(), + loadavg = sysinfo.load or { 0, 0, 0 }, + memory = meminfo, + swap = swapinfo, + connmax = conn_max, + conncount = conn_count, + leases = stat.dhcp_leases(), + leases6 = stat.dhcp6_leases(), + wifinets = stat.wifi_networks() + } + + if wan then + rv.wan = { + ipaddr = wan:ipaddr(), + gwaddr = wan:gwaddr(), + netmask = wan:netmask(), + dns = wan:dnsaddrs(), + expires = wan:expires(), + uptime = wan:uptime(), + proto = wan:proto(), + ifname = wan:ifname(), + link = wan:adminlink() + } + end + + if wan6 then + rv.wan6 = { + ip6addr = wan6:ip6addr(), + gw6addr = wan6:gw6addr(), + dns = wan6:dns6addrs(), + uptime = wan6:uptime(), + ifname = wan6:ifname(), + link = wan6:adminlink() + } + end + + if has_dsl then + local dsl_stat = luci.sys.exec("/etc/init.d/dsl_control lucistat") + local dsl_func = loadstring(dsl_stat) + if dsl_func then + rv.dsl = dsl_func() + end + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) + + return + end +-%> + +<%+header%> + + + + +

                                          <%:Status%>

                                          + +
                                          + <%:System%> + + + + + + + + + +
                                          <%:Hostname%><%=luci.sys.hostname() or "?"%>
                                          <%:Model%><%=pcdata(boardinfo.model or boardinfo.system or "?")%>
                                          <%:Firmware Version%> + <%=pcdata(ver.distname)%> <%=pcdata(ver.distversion)%> / + <%=pcdata(ver.luciname)%> (<%=pcdata(ver.luciversion)%>) +
                                          <%:Kernel Version%><%=unameinfo.release or "?"%>
                                          <%:Local Time%>-
                                          <%:Uptime%>-
                                          <%:Load Average%>-
                                          +
                                          + +
                                          + <%:Memory%> + + + + + +
                                          <%:Total Available%>-
                                          <%:Free%>-
                                          <%:Buffered%>-
                                          +
                                          + +<% if swapinfo.total > 0 then %> +
                                          + <%:Swap%> + + + + +
                                          <%:Total Available%>-
                                          <%:Free%>-
                                          +
                                          +<% end %> + +
                                          + <%:Network%> + + + + <% if has_ipv6 then %> + + <% end %> + +
                                          <%:IPv4 WAN Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          <%:IPv6 WAN Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          <%:Active Connections%>-
                                          +
                                          + +<%- + include("rooter/external") +-%> + +<% if has_dhcp then %> +
                                          + <%:DHCP Leases%> + + + + + + + + + + + +
                                          <%:Hostname%><%:IPv4-Address%><%:MAC-Address%><%:Leasetime remaining%>

                                          <%:Collecting data...%>
                                          +
                                          + + +<% end %> + +<% if has_dsl then %> +
                                          + <%:ADSL%> + + +
                                          <%:ADSL Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          +
                                          +<% end %> + +<% if has_wifi then %> +
                                          + <%:Wireless%> + + + +
                                          <%:Collecting data...%>
                                          +
                                          + +
                                          + <%:Associated Stations%> + + + + + + + + + + + + + + +
                                           <%:MAC-Address%><%:Network%><%:Signal%><%:Noise%><%:RX Rate%><%:TX Rate%>

                                          <%:Collecting data...%>
                                          +
                                          +<% end %> + +<%- + local incdir = util.libpath() .. "/view/admin_status/index/" + if fs.access(incdir) then + local inc + for inc in fs.dir(incdir) do + if inc:match("%.htm$") then + include("admin_status/index/" .. inc:gsub("%.htm$", "")) + end + end + end +-%> + +<%+footer%> diff --git a/ext-rooter45/files/usr/lib/lua/luci/view/rooter/external.htm b/ext-rooter45/files/usr/lib/lua/luci/view/rooter/external.htm new file mode 100644 index 0000000..1feee41 --- /dev/null +++ b/ext-rooter45/files/usr/lib/lua/luci/view/rooter/external.htm @@ -0,0 +1,26 @@ + + +
                                          + <%:External Internet IP Address%> + + +
                                          <%:IP Address%><%:Loading%> Collecting data...
                                          +
                                          + diff --git a/ext-rooter8/Makefile b/ext-rooter8/Makefile new file mode 100644 index 0000000..f1efc31 --- /dev/null +++ b/ext-rooter8/Makefile @@ -0,0 +1,39 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-rooter8 +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-rooter8 + SECTION:=utils + CATEGORY:=ROOter + DEPENDS:=+luci +luci-app-sqm +luci-app-rooterddns +luci-app-rootervpn +luci-app-mwan3 \ + +ext-rooter-basic +ext-p910nd +ext-samba \ + +ext-umount +ext-command +luci-theme-darkmatter +kmod-sched-cake \ + +openvpn-easy-rsa +openvpn-openssl +wget +ext-extra \ + +nano +picocom +bwmon +luci-app-hotspot +luci-app-wol +luci-app-dnsmasq-ipset + TITLE:=ROOter support for 8meg and larger routers + PKGARCH:=all +endef + +define Package/ext-rooter8/description + Helper scripts to enable ROOter on 8meg and larger routers +endef + + +define Build/Compile +endef + +define Package/ext-rooter8/install + $(CP) ./files/* $(1)/ + + +endef + +$(eval $(call BuildPackage,ext-rooter8)) diff --git a/ext-rooter8/files/etc/config/mwan3 b/ext-rooter8/files/etc/config/mwan3 new file mode 100644 index 0000000..86b196d --- /dev/null +++ b/ext-rooter8/files/etc/config/mwan3 @@ -0,0 +1,82 @@ + +config interface 'wan1' + option count '1' + option timeout '2' + option reroute '0' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + option up '2' + option reliability '1' + option interval '10' + option down '2' + option enabled '0' + option dwnscript '/usr/lib/rooter/idown.lua' + +config interface 'wan2' + option count '1' + option timeout '2' + option reroute '0' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + option reliability '1' + option enabled '0' + option interval '10' + option down '2' + option up '2' + option dwnscript '/usr/lib/rooter/idown.lua' + +config interface 'wan' + option reliability '1' + option interval '5' + option timeout '3' + option down '5' + option up '1' + option enabled '0' + option count '3' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + +config interface 'wwan' + option reliability '1' + option interval '5' + option timeout '3' + option down '5' + option up '1' + option enabled '0' + option count '3' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + +config member 'wan_m1_w1' + option interface 'wan' + option metric '1' + option weight '1' + +config member 'wwan_m2_w2' + option interface 'wwan' + option metric '2' + option weight '2' + +config member 'wan1_m3_w3' + option interface 'wan1' + option metric '3' + option weight '3' + +config member 'wan2_m4_w4' + option interface 'wan2' + option metric '4' + option weight '4' + +config policy 'failover' + list use_member 'wan_m1_w1' + list use_member 'wwan_m2_w2' + list use_member 'wan1_m3_w3' + list use_member 'wan2_m4_w4' + option last_resort 'unreachable' + +config rule 'rule1' + option dest_ip '0.0.0.0/0' + option proto 'all' + option sticky '0' + option use_policy 'failover' + diff --git a/ext-rooter8/files/etc/flash b/ext-rooter8/files/etc/flash new file mode 100644 index 0000000..c80ef7f --- /dev/null +++ b/ext-rooter8/files/etc/flash @@ -0,0 +1 @@ +FLASH="8" diff --git a/ext-rooter8/files/etc/hotplug.d/block/20-mount b/ext-rooter8/files/etc/hotplug.d/block/20-mount new file mode 100644 index 0000000..211fb07 --- /dev/null +++ b/ext-rooter8/files/etc/hotplug.d/block/20-mount @@ -0,0 +1,107 @@ +#!/bin/sh /etc/rc.common +. /lib/functions.sh + +log() { + logger -t "20-mount" "$@" +} + +do_ap() { + local config=$1 + local passw=$2 + local mode + + config_get mode $1 mode + if [ $mode = "ap" ]; then + uci -q set wireless."${config}".key=$passw + uci -q set wireless."${config}".encryption=psk + fi +} + +if [ ! -f /tmp/bootend.file ]; then + sleep 10 + while [ ! -f /tmp/bootend.file ]; do + sleep 1 + done + log "Delay Block Mount for boot up" +fi + +blkdev=`dirname $DEVPATH` +if [ `basename $blkdev` != "block" ]; then + device=`basename $DEVPATH` + if echo $device | grep -q "mtdblock"; then + exit 0 + fi + if echo $device | grep -q "CF_1GB"; then + exit 0 + fi + if echo $device | grep -q "mmcblk"; then + exit 0 + fi + + case "$ACTION" in + add) + mkdir -p /mnt/$device + # vfat & ntfs-3g check + if [ `which fdisk` ]; then + isntfs=`fdisk -l | grep $device | grep NTFS` + isvfat=`fdisk -l | grep $device | grep FAT` + isfuse=`lsmod | grep fuse` + isntfs3g=`which ntfs-3g` + else + isntfs="" + isvfat="" + fi + # mount with ntfs-3g if possible, else with default mount + if [ "$isntfs" -a "$isfuse" -a "$isntfs3g" ]; then + ntfs-3g -o nls=utf8 /dev/$device /mnt/$device + log "Mount /mnt/$device as NTFS" + elif [ "$isvfat" ]; then + mount -o codepage=437,iocharset=iso8859-1 /dev/$device /mnt/$device + log "Mount /mnt/$device as FAT" + else + mount /dev/$device /mnt/$device + log "Mount /mnt/$device as other" + fi + + chmod -R 777 /mnt/$device + chown -R nobody /mnt/$device + + < /etc/firstboot + fi + COMMENT1 + ;; + esac +fi \ No newline at end of file diff --git a/ext-rooter8/files/etc/hotplug.d/block/99-mount b/ext-rooter8/files/etc/hotplug.d/block/99-mount new file mode 100644 index 0000000..1ad3db3 --- /dev/null +++ b/ext-rooter8/files/etc/hotplug.d/block/99-mount @@ -0,0 +1,30 @@ +#!/bin/sh /etc/rc.common +. /lib/functions.sh + +log() { + logger -t "99-mount" "$@" +} + +if [ ! -f /tmp/bootend.file ]; then + sleep 10 + while [ ! -f /tmp/bootend.file ]; do + sleep 1 + done + log "Delay Block Mount for boot up" +fi + +blkdev=`dirname $DEVPATH` +if [ `basename $blkdev` != "block" ]; then + device=`basename $DEVPATH` + if echo $device | grep -q "mtdblock"; then + exit 0 + fi + + case "$ACTION" in + remove) + log "remove /mnt/$device" + umount -l /mnt/$device + rm -rf /mnt/$device + ;; + esac +fi \ No newline at end of file diff --git a/ext-rooter8/files/usr/lib/lua/luci/controller/mwan3.lua b/ext-rooter8/files/usr/lib/lua/luci/controller/mwan3.lua new file mode 100644 index 0000000..b8c07e2 --- /dev/null +++ b/ext-rooter8/files/usr/lib/lua/luci/controller/mwan3.lua @@ -0,0 +1,339 @@ +module("luci.controller.mwan3", package.seeall) + +sys = require "luci.sys" +ut = require "luci.util" + +ip = "/usr/bin/ip -4 " + +function index() + if not nixio.fs.access("/etc/config/mwan3") then + return + end + + entry({"admin", "network", "mwan"}, + alias("admin", "network", "mwan", "overview"), + _("Load Balancing"), 600) + + entry({"admin", "network", "mwan", "overview"}, + alias("admin", "network", "mwan", "overview", "overview_interface"), + _("Overview"), 10) + entry({"admin", "network", "mwan", "overview", "overview_interface"}, + template("mwan/overview_interface")) + entry({"admin", "network", "mwan", "overview", "interface_status"}, + call("interfaceStatus")) + entry({"admin", "network", "mwan", "overview", "overview_detailed"}, + template("mwan/overview_detailed")) + entry({"admin", "network", "mwan", "overview", "detailed_status"}, + call("detailedStatus")) + + entry({"admin", "network", "mwan", "configuration"}, + alias("admin", "network", "mwan", "configuration", "interface"), + _("Configuration"), 20) + entry({"admin", "network", "mwan", "configuration", "interface"}, + arcombine(cbi("mwan/interface"), cbi("mwan/interfaceconfig")), + _("Interfaces"), 10).leaf = true + entry({"admin", "network", "mwan", "configuration", "member"}, + arcombine(cbi("mwan/member"), cbi("mwan/memberconfig")), + _("Members"), 20).leaf = true + entry({"admin", "network", "mwan", "configuration", "policy"}, + arcombine(cbi("mwan/policy"), cbi("mwan/policyconfig")), + _("Policies"), 30).leaf = true + entry({"admin", "network", "mwan", "configuration", "rule"}, + arcombine(cbi("mwan/rule"), cbi("mwan/ruleconfig")), + _("Rules"), 40).leaf = true + + entry({"admin", "network", "mwan", "advanced"}, + alias("admin", "network", "mwan", "advanced", "hotplugscript"), + _("Advanced"), 100) + entry({"admin", "network", "mwan", "advanced", "hotplugscript"}, + form("mwan/advanced_hotplugscript")) + entry({"admin", "network", "mwan", "advanced", "mwanconfig"}, + form("mwan/advanced_mwanconfig")) + entry({"admin", "network", "mwan", "advanced", "networkconfig"}, + form("mwan/advanced_networkconfig")) + entry({"admin", "network", "mwan", "advanced", "wirelessconfig"}, + form("mwan/advanced_wirelessconfig")) + entry({"admin", "network", "mwan", "advanced", "diagnostics"}, + template("mwan/advanced_diagnostics")) + entry({"admin", "network", "mwan", "advanced", "diagnostics_display"}, + call("diagnosticsData"), nil).leaf = true + entry({"admin", "network", "mwan", "advanced", "troubleshooting"}, + template("mwan/advanced_troubleshooting")) + entry({"admin", "network", "mwan", "advanced", "troubleshooting_display"}, + call("troubleshootingData")) +end + +function getInterfaceStatus(ruleNumber, interfaceName) + if ut.trim(sys.exec("uci -p /var/state get mwan3." .. interfaceName .. ".enabled")) == "1" then + if ut.trim(sys.exec(ip .. "route list table " .. ruleNumber)) ~= "" then + if ut.trim(sys.exec("uci -p /var/state get mwan3." .. interfaceName .. ".track_ip")) ~= "" then + return "online" + else + return "notMonitored" + end + else + return "offline" + end + else + return "notEnabled" + end +end + +function getInterfaceName() + local ruleNumber, status = 0, "" + uci.cursor():foreach("mwan3", "interface", + function (section) + ruleNumber = ruleNumber+1 + status = status .. section[".name"] .. "[" .. getInterfaceStatus(ruleNumber, section[".name"]) .. "]" + end + ) + return status +end + +function interfaceStatus() + local ntm = require "luci.model.network".init() + + local mArray = {} + + -- overview status + local statusString = getInterfaceName() + if statusString ~= "" then + mArray.wans = {} + wansid = {} + + for wanName, interfaceState in string.gfind(statusString, "([^%[]+)%[([^%]]+)%]") do + local wanInterfaceEnabled = ut.trim(sys.exec("uci -p /var/state get mwan3." .. wanName .. ".enabled")) + local wanInterfaceName = "" + if wanInterfaceEnabled == "1" then + if wanName == "wwan" then + wanInterfaceName = "wifi" + else + wanInterfaceName = ut.trim(sys.exec("uci -p /var/state get network." .. wanName .. ".ifname")) + end + end + if wanInterfaceName == "" then + wanInterfaceName = "X" + end + local wanDeviceLink = ntm:get_interface(wanInterfaceName) + wanDeviceLink = wanDeviceLink and wanDeviceLink:get_network() + wanDeviceLink = wanDeviceLink and wanDeviceLink:adminlink() or "#" + wansid[wanName] = #mArray.wans + 1 + mArray.wans[wansid[wanName]] = { name = wanName, link = wanDeviceLink, ifname = wanInterfaceName, status = interfaceState } + end + end + + -- overview status log + local mwanLog = ut.trim(sys.exec("logread | grep mwan3 | tail -n 50 | sed 'x;1!H;$!d;x'")) + if mwanLog ~= "" then + mArray.mwanlog = { mwanLog } + end + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end + +function detailedStatus() + local mArray = {} + + -- detailed mwan status + local detailStatusInfo = ut.trim(sys.exec("/usr/sbin/mwan3 status")) + if detailStatusInfo ~= "" then + mArray.mwandetail = { detailStatusInfo } + end + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end + +function diagnosticsData(interface, tool, task) + function getInterfaceNumber() + local number = 0 + uci.cursor():foreach("mwan3", "interface", + function (section) + number = number+1 + if section[".name"] == interface then + interfaceNumber = number + end + end + ) + end + + local mArray = {} + + local results = "" + if tool == "service" then + os.execute("/usr/sbin/mwan3 " .. task) + if task == "restart" then + results = "MWAN3 restarted" + elseif task == "stop" then + results = "MWAN3 stopped" + else + results = "MWAN3 started" + end + else + local interfaceDevice = ut.trim(sys.exec("uci -p /var/state get network." .. interface .. ".ifname")) + if interfaceDevice ~= "" then + if tool == "ping" then + local gateway = ut.trim(sys.exec("route -n | awk '{if ($8 == \"" .. interfaceDevice .. "\" && $1 == \"0.0.0.0\" && $3 == \"0.0.0.0\") print $2}'")) + if gateway ~= "" then + if task == "gateway" then + local pingCommand = "ping -c 3 -W 2 -I " .. interfaceDevice .. " " .. gateway + results = pingCommand .. "\n\n" .. sys.exec(pingCommand) + else + local tracked = ut.trim(sys.exec("uci -p /var/state get mwan3." .. interface .. ".track_ip")) + if tracked ~= "" then + for z in tracked:gmatch("[^ ]+") do + local pingCommand = "ping -c 3 -W 2 -I " .. interfaceDevice .. " " .. z + results = results .. pingCommand .. "\n\n" .. sys.exec(pingCommand) .. "\n\n" + end + else + results = "No tracking IP addresses configured on " .. interface + end + end + else + results = "No default gateway for " .. interface .. " found. Default route does not exist or is configured incorrectly" + end + elseif tool == "rulechk" then + getInterfaceNumber() + local rule1 = sys.exec(ip .. "rule | grep $(echo $((" .. interfaceNumber .. " + 1000)))") + local rule2 = sys.exec(ip .. "rule | grep $(echo $((" .. interfaceNumber .. " + 2000)))") + if rule1 ~= "" and rule2 ~= "" then + results = "All required interface IP rules found:\n\n" .. rule1 .. rule2 + elseif rule1 ~= "" or rule2 ~= "" then + results = "Missing 1 of the 2 required interface IP rules\n\n\nRules found:\n\n" .. rule1 .. rule2 + else + results = "Missing both of the required interface IP rules" + end + elseif tool == "routechk" then + getInterfaceNumber() + local routeTable = sys.exec(ip .. "route list table " .. interfaceNumber) + if routeTable ~= "" then + results = "Interface routing table " .. interfaceNumber .. " was found:\n\n" .. routeTable + else + results = "Missing required interface routing table " .. interfaceNumber + end + elseif tool == "hotplug" then + if task == "ifup" then + os.execute("/usr/sbin/mwan3 ifup " .. interface) + results = "Hotplug ifup sent to interface " .. interface .. "..." + else + os.execute("/usr/sbin/mwan3 ifdown " .. interface) + results = "Hotplug ifdown sent to interface " .. interface .. "..." + end + end + else + results = "Unable to perform diagnostic tests on " .. interface .. ". There is no physical or virtual device associated with this interface" + end + end + if results ~= "" then + results = ut.trim(results) + mArray.diagnostics = { results } + end + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end + +function troubleshootingData() + local ver = require "luci.version" + + local mArray = {} + + -- software versions + local wrtRelease = ut.trim(ver.distversion) + if wrtRelease ~= "" then + wrtRelease = "OpenWrt - " .. wrtRelease + else + wrtRelease = "OpenWrt - unknown" + end + local luciRelease = ut.trim(ver.luciversion) + if luciRelease ~= "" then + luciRelease = "\nLuCI - " .. luciRelease + else + luciRelease = "\nLuCI - unknown" + end + local mwanVersion = ut.trim(sys.exec("opkg info mwan3 | grep Version | awk '{print $2}'")) + if mwanVersion ~= "" then + mwanVersion = "\n\nmwan3 - " .. mwanVersion + else + mwanVersion = "\n\nmwan3 - unknown" + end + local mwanLuciVersion = ut.trim(sys.exec("opkg info luci-app-mwan3 | grep Version | awk '{print $2}'")) + if mwanLuciVersion ~= "" then + mwanLuciVersion = "\nmwan3-luci - " .. mwanLuciVersion + else + mwanLuciVersion = "\nmwan3-luci - unknown" + end + mArray.versions = { wrtRelease .. luciRelease .. mwanVersion .. mwanLuciVersion } + + -- mwan config + local mwanConfig = ut.trim(sys.exec("cat /etc/config/mwan3")) + if mwanConfig == "" then + mwanConfig = "No data found" + end + mArray.mwanconfig = { mwanConfig } + + -- network config + local networkConfig = ut.trim(sys.exec("cat /etc/config/network | sed -e 's/.*username.*/ USERNAME HIDDEN/' -e 's/.*password.*/ PASSWORD HIDDEN/'")) + if networkConfig == "" then + networkConfig = "No data found" + end + mArray.netconfig = { networkConfig } + + -- wireless config + local wirelessConfig = ut.trim(sys.exec("cat /etc/config/wireless | sed -e 's/.*username.*/ USERNAME HIDDEN/' -e 's/.*password.*/ PASSWORD HIDDEN/' -e 's/.*key.*/ KEY HIDDEN/'")) + if wirelessConfig == "" then + wirelessConfig = "No data found" + end + mArray.wificonfig = { wirelessConfig } + + -- ifconfig + local ifconfig = ut.trim(sys.exec("ifconfig")) + if ifconfig == "" then + ifconfig = "No data found" + end + mArray.ifconfig = { ifconfig } + + -- route -n + local routeShow = ut.trim(sys.exec("route -n")) + if routeShow == "" then + routeShow = "No data found" + end + mArray.routeshow = { routeShow } + + -- ip rule show + local ipRuleShow = ut.trim(sys.exec(ip .. "rule show")) + if ipRuleShow == "" then + ipRuleShow = "No data found" + end + mArray.iprule = { ipRuleShow } + + -- ip route list table 1-250 + local routeList, routeString = ut.trim(sys.exec(ip .. "rule | sed 's/://g' | awk '$1>=2001 && $1<=2250' | awk '{print $NF}'")), "" + if routeList ~= "" then + for line in routeList:gmatch("[^\r\n]+") do + routeString = routeString .. line .. "\n" .. sys.exec(ip .. "route list table " .. line) + end + routeString = ut.trim(routeString) + else + routeString = "No data found" + end + mArray.routelist = { routeString } + + -- default firewall output policy + local firewallOut = ut.trim(sys.exec("uci -p /var/state get firewall.@defaults[0].output")) + if firewallOut == "" then + firewallOut = "No data found" + end + mArray.firewallout = { firewallOut } + + -- iptables + local iptables = ut.trim(sys.exec("iptables -L -t mangle -v -n")) + if iptables == "" then + iptables = "No data found" + end + mArray.iptables = { iptables } + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end diff --git a/ext-rooter8/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua b/ext-rooter8/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua new file mode 100644 index 0000000..f401629 --- /dev/null +++ b/ext-rooter8/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua @@ -0,0 +1,194 @@ +-- ------ extra functions ------ -- + +function interfaceCheck() + metricValue = ut.trim(sys.exec("uci -p /var/state get network." .. arg[1] .. ".metric")) + if metricValue == "" then -- no metric + errorNoMetric = 1 + else -- if metric exists create list of interface metrics to compare against for duplicates + uci.cursor():foreach("mwan3", "interface", + function (section) + local metricValue = ut.trim(sys.exec("uci -p /var/state get network." .. section[".name"] .. ".metric")) + metricList = metricList .. section[".name"] .. " " .. metricValue .. "\n" + end + ) + -- compare metric against list + local metricDuplicateNumbers, metricDuplicates = sys.exec("echo '" .. metricList .. "' | awk '{print $2}' | uniq -d"), "" + for line in metricDuplicateNumbers:gmatch("[^\r\n]+") do + metricDuplicates = sys.exec("echo '" .. metricList .. "' | grep '" .. line .. "' | awk '{print $1}'") + errorDuplicateMetricList = errorDuplicateMetricList .. metricDuplicates + end + if sys.exec("echo '" .. errorDuplicateMetricList .. "' | grep -w " .. arg[1]) ~= "" then + errorDuplicateMetric = 1 + end + end + -- check if this interface has a higher reliability requirement than track IPs configured + local trackingNumber = tonumber(ut.trim(sys.exec("echo $(uci -p /var/state get mwan3." .. arg[1] .. ".track_ip) | wc -w"))) + if trackingNumber > 0 then + local reliabilityNumber = tonumber(ut.trim(sys.exec("uci -p /var/state get mwan3." .. arg[1] .. ".reliability"))) + if reliabilityNumber and reliabilityNumber > trackingNumber then + errorReliability = 1 + end + end + -- check if any interfaces are not properly configured in /etc/config/network or have no default route in main routing table + if ut.trim(sys.exec("uci -p /var/state get network." .. arg[1])) == "interface" then + local interfaceDevice = ut.trim(sys.exec("uci -p /var/state get network." .. arg[1] .. ".ifname")) + if interfaceDevice == "uci: Entry not found" or interfaceDevice == "" then + errorNetConfig = 1 + errorRoute = 1 + else + local routeCheck = ut.trim(sys.exec("route -n | awk '{if ($8 == \"" .. interfaceDevice .. "\" && $1 == \"0.0.0.0\" && $3 == \"0.0.0.0\") print $1}'")) + if routeCheck == "" then + errorRoute = 1 + end + end + else + errorNetConfig = 1 + errorRoute = 1 + end +end + +function interfaceWarnings() -- display warning messages at the top of the page + local warns, lineBreak = "", "" + if errorReliability == 1 then + warns = "WARNING: this interface has a higher reliability requirement than there are tracking IP addresses!" + lineBreak = "

                                          " + end + if errorRoute == 1 then + warns = warns .. lineBreak .. "WARNING: this interface has no default route in the main routing table!" + lineBreak = "

                                          " + end + if errorNetConfig == 1 then + warns = warns .. lineBreak .. "WARNING: this interface is configured incorrectly or not at all in /etc/config/network!" + lineBreak = "

                                          " + end + if errorNoMetric == 1 then + warns = warns .. lineBreak .. "WARNING: this interface has no metric configured in /etc/config/network!" + elseif errorDuplicateMetric == 1 then + warns = warns .. lineBreak .. "WARNING: this and other interfaces have duplicate metrics configured in /etc/config/network!" + end + return warns +end + +-- ------ interface configuration ------ -- + +dsp = require "luci.dispatcher" +sys = require "luci.sys" +ut = require "luci.util" +arg[1] = arg[1] or "" + +metricValue = "" +metricList = "" +errorDuplicateMetricList = "" +errorNoMetric = 0 +errorDuplicateMetric = 0 +errorRoute = 0 +errorNetConfig = 0 +errorReliability = 0 +interfaceCheck() + + +m5 = Map("mwan3", translate("MWAN Interface Configuration - " .. arg[1]), + translate(interfaceWarnings())) + m5.redirect = dsp.build_url("admin", "network", "mwan", "configuration", "interface") + + +mwan_interface = m5:section(NamedSection, arg[1], "interface", "") + mwan_interface.addremove = false + mwan_interface.dynamic = false + + +enabled = mwan_interface:option(ListValue, "enabled", translate("Enabled")) + enabled.default = "1" + enabled:value("1", translate("Yes")) + enabled:value("0", translate("No")) + +track_ip = mwan_interface:option(DynamicList, "track_ip", translate("Tracking IP"), + translate("This IP address will be pinged to dermine if the link is up or down. Leave blank to assume interface is always online")) + track_ip.datatype = "ipaddr" + +reliability = mwan_interface:option(Value, "reliability", translate("Tracking reliability"), + translate("Acceptable values: 1-100. This many Tracking IP addresses must respond for the link to be deemed up")) + reliability.datatype = "range(1, 100)" + reliability.default = "1" + +count = mwan_interface:option(ListValue, "count", translate("Ping count")) + count.default = "1" + count:value("1") + count:value("2") + count:value("3") + count:value("4") + count:value("5") + +timeout = mwan_interface:option(ListValue, "timeout", translate("Ping timeout")) + timeout.default = "2" + timeout:value("1", translate("1 second")) + timeout:value("2", translate("2 seconds")) + timeout:value("3", translate("3 seconds")) + timeout:value("4", translate("4 seconds")) + timeout:value("5", translate("5 seconds")) + timeout:value("6", translate("6 seconds")) + timeout:value("7", translate("7 seconds")) + timeout:value("8", translate("8 seconds")) + timeout:value("9", translate("9 seconds")) + timeout:value("10", translate("10 seconds")) + +interval = mwan_interface:option(ListValue, "interval", translate("Ping interval")) + interval.default = "5" + interval:value("1", translate("1 second")) + interval:value("3", translate("3 seconds")) + interval:value("5", translate("5 seconds")) + interval:value("10", translate("10 seconds")) + interval:value("20", translate("20 seconds")) + interval:value("30", translate("30 seconds")) + interval:value("60", translate("1 minute")) + interval:value("300", translate("5 minutes")) + interval:value("600", translate("10 minutes")) + interval:value("900", translate("15 minutes")) + interval:value("1800", translate("30 minutes")) + interval:value("3600", translate("1 hour")) + +down = mwan_interface:option(ListValue, "down", translate("Interface down"), + translate("Interface will be deemed down after this many failed ping tests")) + down.default = "3" + down:value("1") + down:value("2") + down:value("3") + down:value("4") + down:value("5") + down:value("6") + down:value("7") + down:value("8") + down:value("9") + down:value("10") + +up = mwan_interface:option(ListValue, "up", translate("Interface up"), + translate("Downed interface will be deemed up after this many successful ping tests")) + up.default = "3" + up:value("1") + up:value("2") + up:value("3") + up:value("4") + up:value("5") + up:value("6") + up:value("7") + up:value("8") + up:value("9") + up:value("10") + +dwnscript = mwan_interface:option(Value, "dwnscript", translate("Interface Down Script"), + translate("Script to run when Interface is deemed down")) + dwnscript.default = "/usr/lib/rooter/idown.lua" + +metric = mwan_interface:option(DummyValue, "metric", translate("Metric"), + translate("This displays the metric assigned to this interface in /etc/config/network")) + metric.rawhtml = true + function metric.cfgvalue(self, s) + if errorNoMetric == 0 then + return metricValue + else + return "—" + end + end + + +return m5 diff --git a/ext-rooter8/files/usr/lib/lua/luci/view/admin_status/indexnew.htm b/ext-rooter8/files/usr/lib/lua/luci/view/admin_status/indexnew.htm new file mode 100644 index 0000000..582fcb6 --- /dev/null +++ b/ext-rooter8/files/usr/lib/lua/luci/view/admin_status/indexnew.htm @@ -0,0 +1,806 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008-2011 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<% + local fs = require "nixio.fs" + local util = require "luci.util" + local stat = require "luci.tools.status" + local ver = require "luci.version" + + local has_ipv6 = fs.access("/proc/net/ipv6_route") + local has_dhcp = fs.access("/etc/config/dhcp") + local has_wifi = ((fs.stat("/etc/config/wireless", "size") or 0) > 0) + + local sysinfo = luci.util.ubus("system", "info") or { } + local boardinfo = luci.util.ubus("system", "board") or { } + local unameinfo = nixio.uname() or { } + + local meminfo = sysinfo.memory or { + total = 0, + free = 0, + buffered = 0, + shared = 0 + } + + local swapinfo = sysinfo.swap or { + total = 0, + free = 0 + } + + local has_dsl = fs.access("/etc/init.d/dsl_control") + + if luci.http.formvalue("status") == "1" then + local ntm = require "luci.model.network".init() + local wan = ntm:get_wannet() + local wan6 = ntm:get_wan6net() + + local conn_count = tonumber(( + luci.sys.exec("wc -l /proc/net/nf_conntrack") or + luci.sys.exec("wc -l /proc/net/ip_conntrack") or + ""):match("%d+")) or 0 + + local conn_max = tonumber(( + luci.sys.exec("sysctl net.nf_conntrack_max") or + luci.sys.exec("sysctl net.ipv4.netfilter.ip_conntrack_max") or + ""):match("%d+")) or 4096 + + local rv = { + uptime = sysinfo.uptime or 0, + localtime = os.date(), + loadavg = sysinfo.load or { 0, 0, 0 }, + memory = meminfo, + swap = swapinfo, + connmax = conn_max, + conncount = conn_count, + leases = stat.dhcp_leases(), + leases6 = stat.dhcp6_leases(), + wifinets = stat.wifi_networks() + } + + if wan then + rv.wan = { + ipaddr = wan:ipaddr(), + gwaddr = wan:gwaddr(), + netmask = wan:netmask(), + dns = wan:dnsaddrs(), + expires = wan:expires(), + uptime = wan:uptime(), + proto = wan:proto(), + ifname = wan:ifname(), + link = wan:adminlink() + } + end + + if wan6 then + rv.wan6 = { + ip6addr = wan6:ip6addr(), + gw6addr = wan6:gw6addr(), + dns = wan6:dns6addrs(), + uptime = wan6:uptime(), + ifname = wan6:ifname(), + link = wan6:adminlink() + } + end + + if has_dsl then + local dsl_stat = luci.sys.exec("/etc/init.d/dsl_control lucistat") + local dsl_func = loadstring(dsl_stat) + if dsl_func then + rv.dsl = dsl_func() + end + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) + + return + elseif luci.http.formvalue("hosts") == "1" then + luci.http.prepare_content("application/json") + luci.http.write_json(luci.sys.net.host_hints()) + + return + end +-%> + +<%+header%> + + + + +

                                          <%:Status%>

                                          + +
                                          + <%:System%> + + + + + + + + + +
                                          <%:Hostname%><%=luci.sys.hostname() or "?"%>
                                          <%:Model%><%=pcdata(boardinfo.model or boardinfo.system or "?")%>
                                          <%:Firmware Version%> + <%=pcdata(ver.distname)%> <%=pcdata(ver.distversion)%> / + <%=pcdata(ver.luciname)%> (<%=pcdata(ver.luciversion)%>) +
                                          <%:Kernel Version%><%=unameinfo.release or "?"%>
                                          <%:Local Time%>-
                                          <%:Uptime%>-
                                          <%:Load Average%>-
                                          +
                                          + +
                                          + <%:Memory%> + + + + + +
                                          <%:Total Available%>-
                                          <%:Free%>-
                                          <%:Buffered%>-
                                          +
                                          + +<% if swapinfo.total > 0 then %> +
                                          + <%:Swap%> + + + + +
                                          <%:Total Available%>-
                                          <%:Free%>-
                                          +
                                          +<% end %> + +
                                          + <%:Network%> + + + + <% if has_ipv6 then %> + + <% end %> + +
                                          <%:IPv4 WAN Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          <%:IPv6 WAN Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          <%:Active Connections%>-
                                          +
                                          + +<%- + include("rooter/external") +-%> + +<% if has_dhcp then %> +
                                          + <%:DHCP Leases%> + + + + + + + + + + + +
                                          <%:Hostname%><%:IPv4-Address%><%:MAC-Address%><%:Leasetime remaining%>

                                          <%:Collecting data...%>
                                          +
                                          + + +<% end %> + +<% if has_dsl then %> +
                                          + <%:DSL%> + + +
                                          <%:DSL Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          +
                                          +<% end %> + +<% if has_wifi then %> +
                                          + <%:Wireless%> + + + +
                                          <%:Collecting data...%>
                                          +
                                          + +
                                          + <%:Associated Stations%> + + + + + + + + + + + + + +
                                           <%:Network%><%:MAC-Address%><%:Host%><%:Signal%> / <%:Noise%><%:RX Rate%> / <%:TX Rate%>

                                          <%:Collecting data...%>
                                          +
                                          +<% end %> + +<%- + local incdir = util.libpath() .. "/view/admin_status/index/" + if fs.access(incdir) then + local inc + for inc in fs.dir(incdir) do + if inc:match("%.htm$") then + include("admin_status/index/" .. inc:gsub("%.htm$", "")) + end + end + end +-%> + +<%+footer%> diff --git a/ext-rooter8/files/usr/lib/lua/luci/view/rooter/external.htm b/ext-rooter8/files/usr/lib/lua/luci/view/rooter/external.htm new file mode 100644 index 0000000..1feee41 --- /dev/null +++ b/ext-rooter8/files/usr/lib/lua/luci/view/rooter/external.htm @@ -0,0 +1,26 @@ + + +
                                          + <%:External Internet IP Address%> + + +
                                          <%:IP Address%><%:Loading%> Collecting data...
                                          +
                                          + diff --git a/ext-rooter8/files/usr/sbin/mwan3track b/ext-rooter8/files/usr/sbin/mwan3track new file mode 100644 index 0000000..037c61e --- /dev/null +++ b/ext-rooter8/files/usr/sbin/mwan3track @@ -0,0 +1,79 @@ +#!/bin/sh + +log() { + logger -t "MWan3Track " "$@" +} + +[ -z "$9" ] && echo "Error: should not be started manually" && exit 0 + +if [ -e /var/run/mwan3track-$1.pid ] ; then + kill $(cat /var/run/mwan3track-$1.pid) &> /dev/null + rm /var/run/mwan3track-$1.pid &> /dev/null +fi + +echo "$$" > /var/run/mwan3track-$1.pid + +score=$(($7+$8)) +track_ips=$(echo $* | cut -d ' ' -f 9-99) +host_up_count=0 +lost=0 + +while true; do + + for track_ip in $track_ips; do + ping -I $2 -c $4 -W $5 -s 4 -q $track_ip &> /dev/null + if [ $? -eq 0 ]; then + let host_up_count++ + else + let lost++ + fi + done + + if [ $host_up_count -lt $3 ]; then + let score-- + + if [ $score -lt $8 ]; then score=0 ; fi + if [ $score -eq $8 ]; then + + logger -t mwan3track -p notice "Interface $1 ($2) is offline" + SCR=$(uci get mwan3.$1.dwnscript) + if [ ! -z $SCR ]; then + if [ -e $SCR ]; then + $SCR "1" $1 $2 + fi + fi + env -i ACTION=ifdown INTERFACE=$1 DEVICE=$2 /sbin/hotplug-call iface + score=0 + fi + + else + + if [ $score -lt $(($7+$8)) ] && [ $lost -gt 0 ]; then + + logger -t mwan3track -p info "Lost $(($lost*$4)) ping(s) on interface $1 ($2)" + + fi + + let score++ + lost=0 + + if [ $score -gt $8 ]; then score=$(($7+$8)); fi + if [ $score -eq $8 ]; then + SCR=$(uci get mwan3.$1.dwnscript) + if [ ! -z $SCR ]; then + if [ -e $SCR ]; then + $SCR "2" $1 $2 + fi + fi + logger -t mwan3track -p notice "Interface $1 ($2) is online" + env -i ACTION=ifup INTERFACE=$1 DEVICE=$2 /sbin/hotplug-call iface + rm /var/run/mwan3track-$1.pid + exit 0 + fi + fi + + host_up_count=0 + sleep $6 +done + +exit 1 diff --git a/ext-rooterbcm/Makefile b/ext-rooterbcm/Makefile new file mode 100644 index 0000000..47d6e5e --- /dev/null +++ b/ext-rooterbcm/Makefile @@ -0,0 +1,38 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-rooterbcm +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-rooterbcm + SECTION:=utils + CATEGORY:=ROOter + DEPENDS:=+luci +luci-app-sqm +luci-app-ddns +luci-app-mwan3 \ + +ext-rooter-basic +ext-p910nd +ext-samba \ + +ext-umount +ext-command +kmod-sched-cake \ + +wget +nano +picocom + TITLE:=ROOter support for BRCM47xx 8meg and larger routers + PKGARCH:=all +endef + +define Package/ext-rooterbcm/description + Helper scripts to enable ROOter on 8meg and larger routers +endef + + +define Build/Compile +endef + +define Package/ext-rooterbcm/install + $(CP) ./files/* $(1)/ + + +endef + +$(eval $(call BuildPackage,ext-rooterbcm)) diff --git a/ext-rooterbcm/files/etc/config/mwan3 b/ext-rooterbcm/files/etc/config/mwan3 new file mode 100644 index 0000000..c28bc66 --- /dev/null +++ b/ext-rooterbcm/files/etc/config/mwan3 @@ -0,0 +1,82 @@ + +config interface 'wan1' + option count '1' + option timeout '2' + option reroute '0' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + option up '2' + option reliability '1' + option interval '10' + option down '2' + option enabled '0' + option dwnscript '/usr/lib/rooter/idown.lua' + +config interface 'wan2' + option count '1' + option timeout '2' + option reroute '0' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + option reliability '1' + option enabled '0' + option interval '10' + option down '2' + option up '2' + option dwnscript '/usr/lib/rooter/idown.lua' + +config interface 'wan' + option reliability '1' + option interval '5' + option timeout '3' + option down '5' + option up '1' + option enabled '1' + option count '3' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + +config interface 'wwan' + option reliability '1' + option interval '5' + option timeout '3' + option down '5' + option up '1' + option enabled '1' + option count '3' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + +config member 'wan_m1_w1' + option interface 'wan' + option metric '1' + option weight '1' + +config member 'wwan_m2_w2' + option interface 'wwan' + option metric '2' + option weight '2' + +config member 'wan1_m3_w3' + option interface 'wan1' + option metric '3' + option weight '3' + +config member 'wan2_m4_w4' + option interface 'wan2' + option metric '4' + option weight '4' + +config policy 'failover' + list use_member 'wan_m1_w1' + list use_member 'wwan_m2_w2' + list use_member 'wan1_m3_w3' + list use_member 'wan2_m4_w4' + option last_resort 'unreachable' + +config rule 'rule1' + option dest_ip '0.0.0.0/0' + option proto 'all' + option sticky '0' + option use_policy 'failover' + diff --git a/ext-rooterbcm/files/etc/flash b/ext-rooterbcm/files/etc/flash new file mode 100644 index 0000000..c80ef7f --- /dev/null +++ b/ext-rooterbcm/files/etc/flash @@ -0,0 +1 @@ +FLASH="8" diff --git a/ext-rooterbcm/files/etc/hotplug.d/block/20-mount b/ext-rooterbcm/files/etc/hotplug.d/block/20-mount new file mode 100644 index 0000000..09fe34e --- /dev/null +++ b/ext-rooterbcm/files/etc/hotplug.d/block/20-mount @@ -0,0 +1,106 @@ +#!/bin/sh /etc/rc.common +. /lib/functions.sh + +log() { + logger -t "20-mount" "$@" +} + +do_ap() { + local config=$1 + local passw=$2 + local mode + + config_get mode $1 mode + if [ $mode = "ap" ]; then + uci -q set wireless."${config}".key=$passw + uci -q set wireless."${config}".encryption=psk + fi +} + +if [ ! -f /tmp/bootend.file ]; then + sleep 10 + while [ ! -f /tmp/bootend.file ]; do + sleep 1 + done + log "Delay Block Mount for boot up" +fi + +blkdev=`dirname $DEVPATH` +if [ `basename $blkdev` != "block" ]; then + device=`basename $DEVPATH` + if echo $device | grep -q "mtdblock"; then + exit 0 + fi + if echo $device | grep -q "CF_1GB"; then + exit 0 + fi + if echo $device | grep -q "mmcblk"; then + exit 0 + fi + + case "$ACTION" in + add) + mkdir -p /mnt/$device + # vfat & ntfs-3g check + if [ `which fdisk` ]; then + isntfs=`fdisk -l | grep $device | grep NTFS` + isvfat=`fdisk -l | grep $device | grep FAT` + isfuse=`lsmod | grep fuse` + isntfs3g=`which ntfs-3g` + else + isntfs="" + isvfat="" + fi + # mount with ntfs-3g if possible, else with default mount + if [ "$isntfs" -a "$isfuse" -a "$isntfs3g" ]; then + ntfs-3g -o nls=utf8 /dev/$device /mnt/$device + log "Mount /mnt/$device as NTFS" + elif [ "$isvfat" ]; then + mount -o codepage=437,iocharset=iso8859-1 /dev/$device /mnt/$device + log "Mount /mnt/$device as FAT" + else + mount /dev/$device /mnt/$device + log "Mount /mnt/$device as other" + fi + < /etc/firstboot + fi + COMMENT1 + ;; + esac +fi \ No newline at end of file diff --git a/ext-rooterbcm/files/etc/hotplug.d/block/99-mount b/ext-rooterbcm/files/etc/hotplug.d/block/99-mount new file mode 100644 index 0000000..1ad3db3 --- /dev/null +++ b/ext-rooterbcm/files/etc/hotplug.d/block/99-mount @@ -0,0 +1,30 @@ +#!/bin/sh /etc/rc.common +. /lib/functions.sh + +log() { + logger -t "99-mount" "$@" +} + +if [ ! -f /tmp/bootend.file ]; then + sleep 10 + while [ ! -f /tmp/bootend.file ]; do + sleep 1 + done + log "Delay Block Mount for boot up" +fi + +blkdev=`dirname $DEVPATH` +if [ `basename $blkdev` != "block" ]; then + device=`basename $DEVPATH` + if echo $device | grep -q "mtdblock"; then + exit 0 + fi + + case "$ACTION" in + remove) + log "remove /mnt/$device" + umount -l /mnt/$device + rm -rf /mnt/$device + ;; + esac +fi \ No newline at end of file diff --git a/ext-rooterbcm/files/usr/lib/lua/luci/controller/mwan3.lua b/ext-rooterbcm/files/usr/lib/lua/luci/controller/mwan3.lua new file mode 100644 index 0000000..b8c07e2 --- /dev/null +++ b/ext-rooterbcm/files/usr/lib/lua/luci/controller/mwan3.lua @@ -0,0 +1,339 @@ +module("luci.controller.mwan3", package.seeall) + +sys = require "luci.sys" +ut = require "luci.util" + +ip = "/usr/bin/ip -4 " + +function index() + if not nixio.fs.access("/etc/config/mwan3") then + return + end + + entry({"admin", "network", "mwan"}, + alias("admin", "network", "mwan", "overview"), + _("Load Balancing"), 600) + + entry({"admin", "network", "mwan", "overview"}, + alias("admin", "network", "mwan", "overview", "overview_interface"), + _("Overview"), 10) + entry({"admin", "network", "mwan", "overview", "overview_interface"}, + template("mwan/overview_interface")) + entry({"admin", "network", "mwan", "overview", "interface_status"}, + call("interfaceStatus")) + entry({"admin", "network", "mwan", "overview", "overview_detailed"}, + template("mwan/overview_detailed")) + entry({"admin", "network", "mwan", "overview", "detailed_status"}, + call("detailedStatus")) + + entry({"admin", "network", "mwan", "configuration"}, + alias("admin", "network", "mwan", "configuration", "interface"), + _("Configuration"), 20) + entry({"admin", "network", "mwan", "configuration", "interface"}, + arcombine(cbi("mwan/interface"), cbi("mwan/interfaceconfig")), + _("Interfaces"), 10).leaf = true + entry({"admin", "network", "mwan", "configuration", "member"}, + arcombine(cbi("mwan/member"), cbi("mwan/memberconfig")), + _("Members"), 20).leaf = true + entry({"admin", "network", "mwan", "configuration", "policy"}, + arcombine(cbi("mwan/policy"), cbi("mwan/policyconfig")), + _("Policies"), 30).leaf = true + entry({"admin", "network", "mwan", "configuration", "rule"}, + arcombine(cbi("mwan/rule"), cbi("mwan/ruleconfig")), + _("Rules"), 40).leaf = true + + entry({"admin", "network", "mwan", "advanced"}, + alias("admin", "network", "mwan", "advanced", "hotplugscript"), + _("Advanced"), 100) + entry({"admin", "network", "mwan", "advanced", "hotplugscript"}, + form("mwan/advanced_hotplugscript")) + entry({"admin", "network", "mwan", "advanced", "mwanconfig"}, + form("mwan/advanced_mwanconfig")) + entry({"admin", "network", "mwan", "advanced", "networkconfig"}, + form("mwan/advanced_networkconfig")) + entry({"admin", "network", "mwan", "advanced", "wirelessconfig"}, + form("mwan/advanced_wirelessconfig")) + entry({"admin", "network", "mwan", "advanced", "diagnostics"}, + template("mwan/advanced_diagnostics")) + entry({"admin", "network", "mwan", "advanced", "diagnostics_display"}, + call("diagnosticsData"), nil).leaf = true + entry({"admin", "network", "mwan", "advanced", "troubleshooting"}, + template("mwan/advanced_troubleshooting")) + entry({"admin", "network", "mwan", "advanced", "troubleshooting_display"}, + call("troubleshootingData")) +end + +function getInterfaceStatus(ruleNumber, interfaceName) + if ut.trim(sys.exec("uci -p /var/state get mwan3." .. interfaceName .. ".enabled")) == "1" then + if ut.trim(sys.exec(ip .. "route list table " .. ruleNumber)) ~= "" then + if ut.trim(sys.exec("uci -p /var/state get mwan3." .. interfaceName .. ".track_ip")) ~= "" then + return "online" + else + return "notMonitored" + end + else + return "offline" + end + else + return "notEnabled" + end +end + +function getInterfaceName() + local ruleNumber, status = 0, "" + uci.cursor():foreach("mwan3", "interface", + function (section) + ruleNumber = ruleNumber+1 + status = status .. section[".name"] .. "[" .. getInterfaceStatus(ruleNumber, section[".name"]) .. "]" + end + ) + return status +end + +function interfaceStatus() + local ntm = require "luci.model.network".init() + + local mArray = {} + + -- overview status + local statusString = getInterfaceName() + if statusString ~= "" then + mArray.wans = {} + wansid = {} + + for wanName, interfaceState in string.gfind(statusString, "([^%[]+)%[([^%]]+)%]") do + local wanInterfaceEnabled = ut.trim(sys.exec("uci -p /var/state get mwan3." .. wanName .. ".enabled")) + local wanInterfaceName = "" + if wanInterfaceEnabled == "1" then + if wanName == "wwan" then + wanInterfaceName = "wifi" + else + wanInterfaceName = ut.trim(sys.exec("uci -p /var/state get network." .. wanName .. ".ifname")) + end + end + if wanInterfaceName == "" then + wanInterfaceName = "X" + end + local wanDeviceLink = ntm:get_interface(wanInterfaceName) + wanDeviceLink = wanDeviceLink and wanDeviceLink:get_network() + wanDeviceLink = wanDeviceLink and wanDeviceLink:adminlink() or "#" + wansid[wanName] = #mArray.wans + 1 + mArray.wans[wansid[wanName]] = { name = wanName, link = wanDeviceLink, ifname = wanInterfaceName, status = interfaceState } + end + end + + -- overview status log + local mwanLog = ut.trim(sys.exec("logread | grep mwan3 | tail -n 50 | sed 'x;1!H;$!d;x'")) + if mwanLog ~= "" then + mArray.mwanlog = { mwanLog } + end + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end + +function detailedStatus() + local mArray = {} + + -- detailed mwan status + local detailStatusInfo = ut.trim(sys.exec("/usr/sbin/mwan3 status")) + if detailStatusInfo ~= "" then + mArray.mwandetail = { detailStatusInfo } + end + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end + +function diagnosticsData(interface, tool, task) + function getInterfaceNumber() + local number = 0 + uci.cursor():foreach("mwan3", "interface", + function (section) + number = number+1 + if section[".name"] == interface then + interfaceNumber = number + end + end + ) + end + + local mArray = {} + + local results = "" + if tool == "service" then + os.execute("/usr/sbin/mwan3 " .. task) + if task == "restart" then + results = "MWAN3 restarted" + elseif task == "stop" then + results = "MWAN3 stopped" + else + results = "MWAN3 started" + end + else + local interfaceDevice = ut.trim(sys.exec("uci -p /var/state get network." .. interface .. ".ifname")) + if interfaceDevice ~= "" then + if tool == "ping" then + local gateway = ut.trim(sys.exec("route -n | awk '{if ($8 == \"" .. interfaceDevice .. "\" && $1 == \"0.0.0.0\" && $3 == \"0.0.0.0\") print $2}'")) + if gateway ~= "" then + if task == "gateway" then + local pingCommand = "ping -c 3 -W 2 -I " .. interfaceDevice .. " " .. gateway + results = pingCommand .. "\n\n" .. sys.exec(pingCommand) + else + local tracked = ut.trim(sys.exec("uci -p /var/state get mwan3." .. interface .. ".track_ip")) + if tracked ~= "" then + for z in tracked:gmatch("[^ ]+") do + local pingCommand = "ping -c 3 -W 2 -I " .. interfaceDevice .. " " .. z + results = results .. pingCommand .. "\n\n" .. sys.exec(pingCommand) .. "\n\n" + end + else + results = "No tracking IP addresses configured on " .. interface + end + end + else + results = "No default gateway for " .. interface .. " found. Default route does not exist or is configured incorrectly" + end + elseif tool == "rulechk" then + getInterfaceNumber() + local rule1 = sys.exec(ip .. "rule | grep $(echo $((" .. interfaceNumber .. " + 1000)))") + local rule2 = sys.exec(ip .. "rule | grep $(echo $((" .. interfaceNumber .. " + 2000)))") + if rule1 ~= "" and rule2 ~= "" then + results = "All required interface IP rules found:\n\n" .. rule1 .. rule2 + elseif rule1 ~= "" or rule2 ~= "" then + results = "Missing 1 of the 2 required interface IP rules\n\n\nRules found:\n\n" .. rule1 .. rule2 + else + results = "Missing both of the required interface IP rules" + end + elseif tool == "routechk" then + getInterfaceNumber() + local routeTable = sys.exec(ip .. "route list table " .. interfaceNumber) + if routeTable ~= "" then + results = "Interface routing table " .. interfaceNumber .. " was found:\n\n" .. routeTable + else + results = "Missing required interface routing table " .. interfaceNumber + end + elseif tool == "hotplug" then + if task == "ifup" then + os.execute("/usr/sbin/mwan3 ifup " .. interface) + results = "Hotplug ifup sent to interface " .. interface .. "..." + else + os.execute("/usr/sbin/mwan3 ifdown " .. interface) + results = "Hotplug ifdown sent to interface " .. interface .. "..." + end + end + else + results = "Unable to perform diagnostic tests on " .. interface .. ". There is no physical or virtual device associated with this interface" + end + end + if results ~= "" then + results = ut.trim(results) + mArray.diagnostics = { results } + end + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end + +function troubleshootingData() + local ver = require "luci.version" + + local mArray = {} + + -- software versions + local wrtRelease = ut.trim(ver.distversion) + if wrtRelease ~= "" then + wrtRelease = "OpenWrt - " .. wrtRelease + else + wrtRelease = "OpenWrt - unknown" + end + local luciRelease = ut.trim(ver.luciversion) + if luciRelease ~= "" then + luciRelease = "\nLuCI - " .. luciRelease + else + luciRelease = "\nLuCI - unknown" + end + local mwanVersion = ut.trim(sys.exec("opkg info mwan3 | grep Version | awk '{print $2}'")) + if mwanVersion ~= "" then + mwanVersion = "\n\nmwan3 - " .. mwanVersion + else + mwanVersion = "\n\nmwan3 - unknown" + end + local mwanLuciVersion = ut.trim(sys.exec("opkg info luci-app-mwan3 | grep Version | awk '{print $2}'")) + if mwanLuciVersion ~= "" then + mwanLuciVersion = "\nmwan3-luci - " .. mwanLuciVersion + else + mwanLuciVersion = "\nmwan3-luci - unknown" + end + mArray.versions = { wrtRelease .. luciRelease .. mwanVersion .. mwanLuciVersion } + + -- mwan config + local mwanConfig = ut.trim(sys.exec("cat /etc/config/mwan3")) + if mwanConfig == "" then + mwanConfig = "No data found" + end + mArray.mwanconfig = { mwanConfig } + + -- network config + local networkConfig = ut.trim(sys.exec("cat /etc/config/network | sed -e 's/.*username.*/ USERNAME HIDDEN/' -e 's/.*password.*/ PASSWORD HIDDEN/'")) + if networkConfig == "" then + networkConfig = "No data found" + end + mArray.netconfig = { networkConfig } + + -- wireless config + local wirelessConfig = ut.trim(sys.exec("cat /etc/config/wireless | sed -e 's/.*username.*/ USERNAME HIDDEN/' -e 's/.*password.*/ PASSWORD HIDDEN/' -e 's/.*key.*/ KEY HIDDEN/'")) + if wirelessConfig == "" then + wirelessConfig = "No data found" + end + mArray.wificonfig = { wirelessConfig } + + -- ifconfig + local ifconfig = ut.trim(sys.exec("ifconfig")) + if ifconfig == "" then + ifconfig = "No data found" + end + mArray.ifconfig = { ifconfig } + + -- route -n + local routeShow = ut.trim(sys.exec("route -n")) + if routeShow == "" then + routeShow = "No data found" + end + mArray.routeshow = { routeShow } + + -- ip rule show + local ipRuleShow = ut.trim(sys.exec(ip .. "rule show")) + if ipRuleShow == "" then + ipRuleShow = "No data found" + end + mArray.iprule = { ipRuleShow } + + -- ip route list table 1-250 + local routeList, routeString = ut.trim(sys.exec(ip .. "rule | sed 's/://g' | awk '$1>=2001 && $1<=2250' | awk '{print $NF}'")), "" + if routeList ~= "" then + for line in routeList:gmatch("[^\r\n]+") do + routeString = routeString .. line .. "\n" .. sys.exec(ip .. "route list table " .. line) + end + routeString = ut.trim(routeString) + else + routeString = "No data found" + end + mArray.routelist = { routeString } + + -- default firewall output policy + local firewallOut = ut.trim(sys.exec("uci -p /var/state get firewall.@defaults[0].output")) + if firewallOut == "" then + firewallOut = "No data found" + end + mArray.firewallout = { firewallOut } + + -- iptables + local iptables = ut.trim(sys.exec("iptables -L -t mangle -v -n")) + if iptables == "" then + iptables = "No data found" + end + mArray.iptables = { iptables } + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end diff --git a/ext-rooterbcm/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua b/ext-rooterbcm/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua new file mode 100644 index 0000000..f401629 --- /dev/null +++ b/ext-rooterbcm/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua @@ -0,0 +1,194 @@ +-- ------ extra functions ------ -- + +function interfaceCheck() + metricValue = ut.trim(sys.exec("uci -p /var/state get network." .. arg[1] .. ".metric")) + if metricValue == "" then -- no metric + errorNoMetric = 1 + else -- if metric exists create list of interface metrics to compare against for duplicates + uci.cursor():foreach("mwan3", "interface", + function (section) + local metricValue = ut.trim(sys.exec("uci -p /var/state get network." .. section[".name"] .. ".metric")) + metricList = metricList .. section[".name"] .. " " .. metricValue .. "\n" + end + ) + -- compare metric against list + local metricDuplicateNumbers, metricDuplicates = sys.exec("echo '" .. metricList .. "' | awk '{print $2}' | uniq -d"), "" + for line in metricDuplicateNumbers:gmatch("[^\r\n]+") do + metricDuplicates = sys.exec("echo '" .. metricList .. "' | grep '" .. line .. "' | awk '{print $1}'") + errorDuplicateMetricList = errorDuplicateMetricList .. metricDuplicates + end + if sys.exec("echo '" .. errorDuplicateMetricList .. "' | grep -w " .. arg[1]) ~= "" then + errorDuplicateMetric = 1 + end + end + -- check if this interface has a higher reliability requirement than track IPs configured + local trackingNumber = tonumber(ut.trim(sys.exec("echo $(uci -p /var/state get mwan3." .. arg[1] .. ".track_ip) | wc -w"))) + if trackingNumber > 0 then + local reliabilityNumber = tonumber(ut.trim(sys.exec("uci -p /var/state get mwan3." .. arg[1] .. ".reliability"))) + if reliabilityNumber and reliabilityNumber > trackingNumber then + errorReliability = 1 + end + end + -- check if any interfaces are not properly configured in /etc/config/network or have no default route in main routing table + if ut.trim(sys.exec("uci -p /var/state get network." .. arg[1])) == "interface" then + local interfaceDevice = ut.trim(sys.exec("uci -p /var/state get network." .. arg[1] .. ".ifname")) + if interfaceDevice == "uci: Entry not found" or interfaceDevice == "" then + errorNetConfig = 1 + errorRoute = 1 + else + local routeCheck = ut.trim(sys.exec("route -n | awk '{if ($8 == \"" .. interfaceDevice .. "\" && $1 == \"0.0.0.0\" && $3 == \"0.0.0.0\") print $1}'")) + if routeCheck == "" then + errorRoute = 1 + end + end + else + errorNetConfig = 1 + errorRoute = 1 + end +end + +function interfaceWarnings() -- display warning messages at the top of the page + local warns, lineBreak = "", "" + if errorReliability == 1 then + warns = "WARNING: this interface has a higher reliability requirement than there are tracking IP addresses!" + lineBreak = "

                                          " + end + if errorRoute == 1 then + warns = warns .. lineBreak .. "WARNING: this interface has no default route in the main routing table!" + lineBreak = "

                                          " + end + if errorNetConfig == 1 then + warns = warns .. lineBreak .. "WARNING: this interface is configured incorrectly or not at all in /etc/config/network!" + lineBreak = "

                                          " + end + if errorNoMetric == 1 then + warns = warns .. lineBreak .. "WARNING: this interface has no metric configured in /etc/config/network!" + elseif errorDuplicateMetric == 1 then + warns = warns .. lineBreak .. "WARNING: this and other interfaces have duplicate metrics configured in /etc/config/network!" + end + return warns +end + +-- ------ interface configuration ------ -- + +dsp = require "luci.dispatcher" +sys = require "luci.sys" +ut = require "luci.util" +arg[1] = arg[1] or "" + +metricValue = "" +metricList = "" +errorDuplicateMetricList = "" +errorNoMetric = 0 +errorDuplicateMetric = 0 +errorRoute = 0 +errorNetConfig = 0 +errorReliability = 0 +interfaceCheck() + + +m5 = Map("mwan3", translate("MWAN Interface Configuration - " .. arg[1]), + translate(interfaceWarnings())) + m5.redirect = dsp.build_url("admin", "network", "mwan", "configuration", "interface") + + +mwan_interface = m5:section(NamedSection, arg[1], "interface", "") + mwan_interface.addremove = false + mwan_interface.dynamic = false + + +enabled = mwan_interface:option(ListValue, "enabled", translate("Enabled")) + enabled.default = "1" + enabled:value("1", translate("Yes")) + enabled:value("0", translate("No")) + +track_ip = mwan_interface:option(DynamicList, "track_ip", translate("Tracking IP"), + translate("This IP address will be pinged to dermine if the link is up or down. Leave blank to assume interface is always online")) + track_ip.datatype = "ipaddr" + +reliability = mwan_interface:option(Value, "reliability", translate("Tracking reliability"), + translate("Acceptable values: 1-100. This many Tracking IP addresses must respond for the link to be deemed up")) + reliability.datatype = "range(1, 100)" + reliability.default = "1" + +count = mwan_interface:option(ListValue, "count", translate("Ping count")) + count.default = "1" + count:value("1") + count:value("2") + count:value("3") + count:value("4") + count:value("5") + +timeout = mwan_interface:option(ListValue, "timeout", translate("Ping timeout")) + timeout.default = "2" + timeout:value("1", translate("1 second")) + timeout:value("2", translate("2 seconds")) + timeout:value("3", translate("3 seconds")) + timeout:value("4", translate("4 seconds")) + timeout:value("5", translate("5 seconds")) + timeout:value("6", translate("6 seconds")) + timeout:value("7", translate("7 seconds")) + timeout:value("8", translate("8 seconds")) + timeout:value("9", translate("9 seconds")) + timeout:value("10", translate("10 seconds")) + +interval = mwan_interface:option(ListValue, "interval", translate("Ping interval")) + interval.default = "5" + interval:value("1", translate("1 second")) + interval:value("3", translate("3 seconds")) + interval:value("5", translate("5 seconds")) + interval:value("10", translate("10 seconds")) + interval:value("20", translate("20 seconds")) + interval:value("30", translate("30 seconds")) + interval:value("60", translate("1 minute")) + interval:value("300", translate("5 minutes")) + interval:value("600", translate("10 minutes")) + interval:value("900", translate("15 minutes")) + interval:value("1800", translate("30 minutes")) + interval:value("3600", translate("1 hour")) + +down = mwan_interface:option(ListValue, "down", translate("Interface down"), + translate("Interface will be deemed down after this many failed ping tests")) + down.default = "3" + down:value("1") + down:value("2") + down:value("3") + down:value("4") + down:value("5") + down:value("6") + down:value("7") + down:value("8") + down:value("9") + down:value("10") + +up = mwan_interface:option(ListValue, "up", translate("Interface up"), + translate("Downed interface will be deemed up after this many successful ping tests")) + up.default = "3" + up:value("1") + up:value("2") + up:value("3") + up:value("4") + up:value("5") + up:value("6") + up:value("7") + up:value("8") + up:value("9") + up:value("10") + +dwnscript = mwan_interface:option(Value, "dwnscript", translate("Interface Down Script"), + translate("Script to run when Interface is deemed down")) + dwnscript.default = "/usr/lib/rooter/idown.lua" + +metric = mwan_interface:option(DummyValue, "metric", translate("Metric"), + translate("This displays the metric assigned to this interface in /etc/config/network")) + metric.rawhtml = true + function metric.cfgvalue(self, s) + if errorNoMetric == 0 then + return metricValue + else + return "—" + end + end + + +return m5 diff --git a/ext-rooterbcm/files/usr/lib/lua/luci/view/admin_status/indexnew.htm b/ext-rooterbcm/files/usr/lib/lua/luci/view/admin_status/indexnew.htm new file mode 100644 index 0000000..582fcb6 --- /dev/null +++ b/ext-rooterbcm/files/usr/lib/lua/luci/view/admin_status/indexnew.htm @@ -0,0 +1,806 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008-2011 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<% + local fs = require "nixio.fs" + local util = require "luci.util" + local stat = require "luci.tools.status" + local ver = require "luci.version" + + local has_ipv6 = fs.access("/proc/net/ipv6_route") + local has_dhcp = fs.access("/etc/config/dhcp") + local has_wifi = ((fs.stat("/etc/config/wireless", "size") or 0) > 0) + + local sysinfo = luci.util.ubus("system", "info") or { } + local boardinfo = luci.util.ubus("system", "board") or { } + local unameinfo = nixio.uname() or { } + + local meminfo = sysinfo.memory or { + total = 0, + free = 0, + buffered = 0, + shared = 0 + } + + local swapinfo = sysinfo.swap or { + total = 0, + free = 0 + } + + local has_dsl = fs.access("/etc/init.d/dsl_control") + + if luci.http.formvalue("status") == "1" then + local ntm = require "luci.model.network".init() + local wan = ntm:get_wannet() + local wan6 = ntm:get_wan6net() + + local conn_count = tonumber(( + luci.sys.exec("wc -l /proc/net/nf_conntrack") or + luci.sys.exec("wc -l /proc/net/ip_conntrack") or + ""):match("%d+")) or 0 + + local conn_max = tonumber(( + luci.sys.exec("sysctl net.nf_conntrack_max") or + luci.sys.exec("sysctl net.ipv4.netfilter.ip_conntrack_max") or + ""):match("%d+")) or 4096 + + local rv = { + uptime = sysinfo.uptime or 0, + localtime = os.date(), + loadavg = sysinfo.load or { 0, 0, 0 }, + memory = meminfo, + swap = swapinfo, + connmax = conn_max, + conncount = conn_count, + leases = stat.dhcp_leases(), + leases6 = stat.dhcp6_leases(), + wifinets = stat.wifi_networks() + } + + if wan then + rv.wan = { + ipaddr = wan:ipaddr(), + gwaddr = wan:gwaddr(), + netmask = wan:netmask(), + dns = wan:dnsaddrs(), + expires = wan:expires(), + uptime = wan:uptime(), + proto = wan:proto(), + ifname = wan:ifname(), + link = wan:adminlink() + } + end + + if wan6 then + rv.wan6 = { + ip6addr = wan6:ip6addr(), + gw6addr = wan6:gw6addr(), + dns = wan6:dns6addrs(), + uptime = wan6:uptime(), + ifname = wan6:ifname(), + link = wan6:adminlink() + } + end + + if has_dsl then + local dsl_stat = luci.sys.exec("/etc/init.d/dsl_control lucistat") + local dsl_func = loadstring(dsl_stat) + if dsl_func then + rv.dsl = dsl_func() + end + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) + + return + elseif luci.http.formvalue("hosts") == "1" then + luci.http.prepare_content("application/json") + luci.http.write_json(luci.sys.net.host_hints()) + + return + end +-%> + +<%+header%> + + + + +

                                          <%:Status%>

                                          + +
                                          + <%:System%> + + + + + + + + + +
                                          <%:Hostname%><%=luci.sys.hostname() or "?"%>
                                          <%:Model%><%=pcdata(boardinfo.model or boardinfo.system or "?")%>
                                          <%:Firmware Version%> + <%=pcdata(ver.distname)%> <%=pcdata(ver.distversion)%> / + <%=pcdata(ver.luciname)%> (<%=pcdata(ver.luciversion)%>) +
                                          <%:Kernel Version%><%=unameinfo.release or "?"%>
                                          <%:Local Time%>-
                                          <%:Uptime%>-
                                          <%:Load Average%>-
                                          +
                                          + +
                                          + <%:Memory%> + + + + + +
                                          <%:Total Available%>-
                                          <%:Free%>-
                                          <%:Buffered%>-
                                          +
                                          + +<% if swapinfo.total > 0 then %> +
                                          + <%:Swap%> + + + + +
                                          <%:Total Available%>-
                                          <%:Free%>-
                                          +
                                          +<% end %> + +
                                          + <%:Network%> + + + + <% if has_ipv6 then %> + + <% end %> + +
                                          <%:IPv4 WAN Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          <%:IPv6 WAN Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          <%:Active Connections%>-
                                          +
                                          + +<%- + include("rooter/external") +-%> + +<% if has_dhcp then %> +
                                          + <%:DHCP Leases%> + + + + + + + + + + + +
                                          <%:Hostname%><%:IPv4-Address%><%:MAC-Address%><%:Leasetime remaining%>

                                          <%:Collecting data...%>
                                          +
                                          + + +<% end %> + +<% if has_dsl then %> +
                                          + <%:DSL%> + + +
                                          <%:DSL Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          +
                                          +<% end %> + +<% if has_wifi then %> +
                                          + <%:Wireless%> + + + +
                                          <%:Collecting data...%>
                                          +
                                          + +
                                          + <%:Associated Stations%> + + + + + + + + + + + + + +
                                           <%:Network%><%:MAC-Address%><%:Host%><%:Signal%> / <%:Noise%><%:RX Rate%> / <%:TX Rate%>

                                          <%:Collecting data...%>
                                          +
                                          +<% end %> + +<%- + local incdir = util.libpath() .. "/view/admin_status/index/" + if fs.access(incdir) then + local inc + for inc in fs.dir(incdir) do + if inc:match("%.htm$") then + include("admin_status/index/" .. inc:gsub("%.htm$", "")) + end + end + end +-%> + +<%+footer%> diff --git a/ext-rooterbcm/files/usr/lib/lua/luci/view/rooter/external.htm b/ext-rooterbcm/files/usr/lib/lua/luci/view/rooter/external.htm new file mode 100644 index 0000000..1feee41 --- /dev/null +++ b/ext-rooterbcm/files/usr/lib/lua/luci/view/rooter/external.htm @@ -0,0 +1,26 @@ + + +
                                          + <%:External Internet IP Address%> + + +
                                          <%:IP Address%><%:Loading%> Collecting data...
                                          +
                                          + diff --git a/ext-rooterbcm/files/usr/sbin/mwan3track b/ext-rooterbcm/files/usr/sbin/mwan3track new file mode 100644 index 0000000..037c61e --- /dev/null +++ b/ext-rooterbcm/files/usr/sbin/mwan3track @@ -0,0 +1,79 @@ +#!/bin/sh + +log() { + logger -t "MWan3Track " "$@" +} + +[ -z "$9" ] && echo "Error: should not be started manually" && exit 0 + +if [ -e /var/run/mwan3track-$1.pid ] ; then + kill $(cat /var/run/mwan3track-$1.pid) &> /dev/null + rm /var/run/mwan3track-$1.pid &> /dev/null +fi + +echo "$$" > /var/run/mwan3track-$1.pid + +score=$(($7+$8)) +track_ips=$(echo $* | cut -d ' ' -f 9-99) +host_up_count=0 +lost=0 + +while true; do + + for track_ip in $track_ips; do + ping -I $2 -c $4 -W $5 -s 4 -q $track_ip &> /dev/null + if [ $? -eq 0 ]; then + let host_up_count++ + else + let lost++ + fi + done + + if [ $host_up_count -lt $3 ]; then + let score-- + + if [ $score -lt $8 ]; then score=0 ; fi + if [ $score -eq $8 ]; then + + logger -t mwan3track -p notice "Interface $1 ($2) is offline" + SCR=$(uci get mwan3.$1.dwnscript) + if [ ! -z $SCR ]; then + if [ -e $SCR ]; then + $SCR "1" $1 $2 + fi + fi + env -i ACTION=ifdown INTERFACE=$1 DEVICE=$2 /sbin/hotplug-call iface + score=0 + fi + + else + + if [ $score -lt $(($7+$8)) ] && [ $lost -gt 0 ]; then + + logger -t mwan3track -p info "Lost $(($lost*$4)) ping(s) on interface $1 ($2)" + + fi + + let score++ + lost=0 + + if [ $score -gt $8 ]; then score=$(($7+$8)); fi + if [ $score -eq $8 ]; then + SCR=$(uci get mwan3.$1.dwnscript) + if [ ! -z $SCR ]; then + if [ -e $SCR ]; then + $SCR "2" $1 $2 + fi + fi + logger -t mwan3track -p notice "Interface $1 ($2) is online" + env -i ACTION=ifup INTERFACE=$1 DEVICE=$2 /sbin/hotplug-call iface + rm /var/run/mwan3track-$1.pid + exit 0 + fi + fi + + host_up_count=0 + sleep $6 +done + +exit 1 diff --git a/ext-rooterbcm8/Makefile b/ext-rooterbcm8/Makefile new file mode 100644 index 0000000..4955290 --- /dev/null +++ b/ext-rooterbcm8/Makefile @@ -0,0 +1,39 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-rooterbcm8 +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-rooterbcm8 + SECTION:=utils + CATEGORY:=ROOter + DEPENDS:=+luci +luci-app-sqm +luci-app-ddns +luci-app-rootervpn +luci-app-mwan3 \ + +ext-rooter-basic +ext-p910nd +ext-samba \ + +ext-umount +ext-command +luci-theme-darkmatter +kmod-sched-cake \ + +wget +ext-extra \ + +nano +picocom +bwmon +luci-app-wol + TITLE:=ROOter support for 8meg and larger routers using Broadcom + PKGARCH:=all +endef + +define Package/ext-rooterbcm8/description + Helper scripts to enable ROOter on 8meg and larger routers using Broadcom +endef + + +define Build/Compile +endef + +define Package/ext-rooterbcm8/install + $(CP) ./files/* $(1)/ + + +endef + +$(eval $(call BuildPackage,ext-rooterbcm8)) diff --git a/ext-rooterbcm8/files/etc/config/mwan3 b/ext-rooterbcm8/files/etc/config/mwan3 new file mode 100644 index 0000000..c28bc66 --- /dev/null +++ b/ext-rooterbcm8/files/etc/config/mwan3 @@ -0,0 +1,82 @@ + +config interface 'wan1' + option count '1' + option timeout '2' + option reroute '0' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + option up '2' + option reliability '1' + option interval '10' + option down '2' + option enabled '0' + option dwnscript '/usr/lib/rooter/idown.lua' + +config interface 'wan2' + option count '1' + option timeout '2' + option reroute '0' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + option reliability '1' + option enabled '0' + option interval '10' + option down '2' + option up '2' + option dwnscript '/usr/lib/rooter/idown.lua' + +config interface 'wan' + option reliability '1' + option interval '5' + option timeout '3' + option down '5' + option up '1' + option enabled '1' + option count '3' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + +config interface 'wwan' + option reliability '1' + option interval '5' + option timeout '3' + option down '5' + option up '1' + option enabled '1' + option count '3' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + +config member 'wan_m1_w1' + option interface 'wan' + option metric '1' + option weight '1' + +config member 'wwan_m2_w2' + option interface 'wwan' + option metric '2' + option weight '2' + +config member 'wan1_m3_w3' + option interface 'wan1' + option metric '3' + option weight '3' + +config member 'wan2_m4_w4' + option interface 'wan2' + option metric '4' + option weight '4' + +config policy 'failover' + list use_member 'wan_m1_w1' + list use_member 'wwan_m2_w2' + list use_member 'wan1_m3_w3' + list use_member 'wan2_m4_w4' + option last_resort 'unreachable' + +config rule 'rule1' + option dest_ip '0.0.0.0/0' + option proto 'all' + option sticky '0' + option use_policy 'failover' + diff --git a/ext-rooterbcm8/files/etc/flash b/ext-rooterbcm8/files/etc/flash new file mode 100644 index 0000000..c80ef7f --- /dev/null +++ b/ext-rooterbcm8/files/etc/flash @@ -0,0 +1 @@ +FLASH="8" diff --git a/ext-rooterbcm8/files/etc/hotplug.d/block/20-mount b/ext-rooterbcm8/files/etc/hotplug.d/block/20-mount new file mode 100644 index 0000000..09fe34e --- /dev/null +++ b/ext-rooterbcm8/files/etc/hotplug.d/block/20-mount @@ -0,0 +1,106 @@ +#!/bin/sh /etc/rc.common +. /lib/functions.sh + +log() { + logger -t "20-mount" "$@" +} + +do_ap() { + local config=$1 + local passw=$2 + local mode + + config_get mode $1 mode + if [ $mode = "ap" ]; then + uci -q set wireless."${config}".key=$passw + uci -q set wireless."${config}".encryption=psk + fi +} + +if [ ! -f /tmp/bootend.file ]; then + sleep 10 + while [ ! -f /tmp/bootend.file ]; do + sleep 1 + done + log "Delay Block Mount for boot up" +fi + +blkdev=`dirname $DEVPATH` +if [ `basename $blkdev` != "block" ]; then + device=`basename $DEVPATH` + if echo $device | grep -q "mtdblock"; then + exit 0 + fi + if echo $device | grep -q "CF_1GB"; then + exit 0 + fi + if echo $device | grep -q "mmcblk"; then + exit 0 + fi + + case "$ACTION" in + add) + mkdir -p /mnt/$device + # vfat & ntfs-3g check + if [ `which fdisk` ]; then + isntfs=`fdisk -l | grep $device | grep NTFS` + isvfat=`fdisk -l | grep $device | grep FAT` + isfuse=`lsmod | grep fuse` + isntfs3g=`which ntfs-3g` + else + isntfs="" + isvfat="" + fi + # mount with ntfs-3g if possible, else with default mount + if [ "$isntfs" -a "$isfuse" -a "$isntfs3g" ]; then + ntfs-3g -o nls=utf8 /dev/$device /mnt/$device + log "Mount /mnt/$device as NTFS" + elif [ "$isvfat" ]; then + mount -o codepage=437,iocharset=iso8859-1 /dev/$device /mnt/$device + log "Mount /mnt/$device as FAT" + else + mount /dev/$device /mnt/$device + log "Mount /mnt/$device as other" + fi + < /etc/firstboot + fi + COMMENT1 + ;; + esac +fi \ No newline at end of file diff --git a/ext-rooterbcm8/files/etc/hotplug.d/block/99-mount b/ext-rooterbcm8/files/etc/hotplug.d/block/99-mount new file mode 100644 index 0000000..1ad3db3 --- /dev/null +++ b/ext-rooterbcm8/files/etc/hotplug.d/block/99-mount @@ -0,0 +1,30 @@ +#!/bin/sh /etc/rc.common +. /lib/functions.sh + +log() { + logger -t "99-mount" "$@" +} + +if [ ! -f /tmp/bootend.file ]; then + sleep 10 + while [ ! -f /tmp/bootend.file ]; do + sleep 1 + done + log "Delay Block Mount for boot up" +fi + +blkdev=`dirname $DEVPATH` +if [ `basename $blkdev` != "block" ]; then + device=`basename $DEVPATH` + if echo $device | grep -q "mtdblock"; then + exit 0 + fi + + case "$ACTION" in + remove) + log "remove /mnt/$device" + umount -l /mnt/$device + rm -rf /mnt/$device + ;; + esac +fi \ No newline at end of file diff --git a/ext-rooterbcm8/files/usr/lib/lua/luci/controller/mwan3.lua b/ext-rooterbcm8/files/usr/lib/lua/luci/controller/mwan3.lua new file mode 100644 index 0000000..b8c07e2 --- /dev/null +++ b/ext-rooterbcm8/files/usr/lib/lua/luci/controller/mwan3.lua @@ -0,0 +1,339 @@ +module("luci.controller.mwan3", package.seeall) + +sys = require "luci.sys" +ut = require "luci.util" + +ip = "/usr/bin/ip -4 " + +function index() + if not nixio.fs.access("/etc/config/mwan3") then + return + end + + entry({"admin", "network", "mwan"}, + alias("admin", "network", "mwan", "overview"), + _("Load Balancing"), 600) + + entry({"admin", "network", "mwan", "overview"}, + alias("admin", "network", "mwan", "overview", "overview_interface"), + _("Overview"), 10) + entry({"admin", "network", "mwan", "overview", "overview_interface"}, + template("mwan/overview_interface")) + entry({"admin", "network", "mwan", "overview", "interface_status"}, + call("interfaceStatus")) + entry({"admin", "network", "mwan", "overview", "overview_detailed"}, + template("mwan/overview_detailed")) + entry({"admin", "network", "mwan", "overview", "detailed_status"}, + call("detailedStatus")) + + entry({"admin", "network", "mwan", "configuration"}, + alias("admin", "network", "mwan", "configuration", "interface"), + _("Configuration"), 20) + entry({"admin", "network", "mwan", "configuration", "interface"}, + arcombine(cbi("mwan/interface"), cbi("mwan/interfaceconfig")), + _("Interfaces"), 10).leaf = true + entry({"admin", "network", "mwan", "configuration", "member"}, + arcombine(cbi("mwan/member"), cbi("mwan/memberconfig")), + _("Members"), 20).leaf = true + entry({"admin", "network", "mwan", "configuration", "policy"}, + arcombine(cbi("mwan/policy"), cbi("mwan/policyconfig")), + _("Policies"), 30).leaf = true + entry({"admin", "network", "mwan", "configuration", "rule"}, + arcombine(cbi("mwan/rule"), cbi("mwan/ruleconfig")), + _("Rules"), 40).leaf = true + + entry({"admin", "network", "mwan", "advanced"}, + alias("admin", "network", "mwan", "advanced", "hotplugscript"), + _("Advanced"), 100) + entry({"admin", "network", "mwan", "advanced", "hotplugscript"}, + form("mwan/advanced_hotplugscript")) + entry({"admin", "network", "mwan", "advanced", "mwanconfig"}, + form("mwan/advanced_mwanconfig")) + entry({"admin", "network", "mwan", "advanced", "networkconfig"}, + form("mwan/advanced_networkconfig")) + entry({"admin", "network", "mwan", "advanced", "wirelessconfig"}, + form("mwan/advanced_wirelessconfig")) + entry({"admin", "network", "mwan", "advanced", "diagnostics"}, + template("mwan/advanced_diagnostics")) + entry({"admin", "network", "mwan", "advanced", "diagnostics_display"}, + call("diagnosticsData"), nil).leaf = true + entry({"admin", "network", "mwan", "advanced", "troubleshooting"}, + template("mwan/advanced_troubleshooting")) + entry({"admin", "network", "mwan", "advanced", "troubleshooting_display"}, + call("troubleshootingData")) +end + +function getInterfaceStatus(ruleNumber, interfaceName) + if ut.trim(sys.exec("uci -p /var/state get mwan3." .. interfaceName .. ".enabled")) == "1" then + if ut.trim(sys.exec(ip .. "route list table " .. ruleNumber)) ~= "" then + if ut.trim(sys.exec("uci -p /var/state get mwan3." .. interfaceName .. ".track_ip")) ~= "" then + return "online" + else + return "notMonitored" + end + else + return "offline" + end + else + return "notEnabled" + end +end + +function getInterfaceName() + local ruleNumber, status = 0, "" + uci.cursor():foreach("mwan3", "interface", + function (section) + ruleNumber = ruleNumber+1 + status = status .. section[".name"] .. "[" .. getInterfaceStatus(ruleNumber, section[".name"]) .. "]" + end + ) + return status +end + +function interfaceStatus() + local ntm = require "luci.model.network".init() + + local mArray = {} + + -- overview status + local statusString = getInterfaceName() + if statusString ~= "" then + mArray.wans = {} + wansid = {} + + for wanName, interfaceState in string.gfind(statusString, "([^%[]+)%[([^%]]+)%]") do + local wanInterfaceEnabled = ut.trim(sys.exec("uci -p /var/state get mwan3." .. wanName .. ".enabled")) + local wanInterfaceName = "" + if wanInterfaceEnabled == "1" then + if wanName == "wwan" then + wanInterfaceName = "wifi" + else + wanInterfaceName = ut.trim(sys.exec("uci -p /var/state get network." .. wanName .. ".ifname")) + end + end + if wanInterfaceName == "" then + wanInterfaceName = "X" + end + local wanDeviceLink = ntm:get_interface(wanInterfaceName) + wanDeviceLink = wanDeviceLink and wanDeviceLink:get_network() + wanDeviceLink = wanDeviceLink and wanDeviceLink:adminlink() or "#" + wansid[wanName] = #mArray.wans + 1 + mArray.wans[wansid[wanName]] = { name = wanName, link = wanDeviceLink, ifname = wanInterfaceName, status = interfaceState } + end + end + + -- overview status log + local mwanLog = ut.trim(sys.exec("logread | grep mwan3 | tail -n 50 | sed 'x;1!H;$!d;x'")) + if mwanLog ~= "" then + mArray.mwanlog = { mwanLog } + end + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end + +function detailedStatus() + local mArray = {} + + -- detailed mwan status + local detailStatusInfo = ut.trim(sys.exec("/usr/sbin/mwan3 status")) + if detailStatusInfo ~= "" then + mArray.mwandetail = { detailStatusInfo } + end + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end + +function diagnosticsData(interface, tool, task) + function getInterfaceNumber() + local number = 0 + uci.cursor():foreach("mwan3", "interface", + function (section) + number = number+1 + if section[".name"] == interface then + interfaceNumber = number + end + end + ) + end + + local mArray = {} + + local results = "" + if tool == "service" then + os.execute("/usr/sbin/mwan3 " .. task) + if task == "restart" then + results = "MWAN3 restarted" + elseif task == "stop" then + results = "MWAN3 stopped" + else + results = "MWAN3 started" + end + else + local interfaceDevice = ut.trim(sys.exec("uci -p /var/state get network." .. interface .. ".ifname")) + if interfaceDevice ~= "" then + if tool == "ping" then + local gateway = ut.trim(sys.exec("route -n | awk '{if ($8 == \"" .. interfaceDevice .. "\" && $1 == \"0.0.0.0\" && $3 == \"0.0.0.0\") print $2}'")) + if gateway ~= "" then + if task == "gateway" then + local pingCommand = "ping -c 3 -W 2 -I " .. interfaceDevice .. " " .. gateway + results = pingCommand .. "\n\n" .. sys.exec(pingCommand) + else + local tracked = ut.trim(sys.exec("uci -p /var/state get mwan3." .. interface .. ".track_ip")) + if tracked ~= "" then + for z in tracked:gmatch("[^ ]+") do + local pingCommand = "ping -c 3 -W 2 -I " .. interfaceDevice .. " " .. z + results = results .. pingCommand .. "\n\n" .. sys.exec(pingCommand) .. "\n\n" + end + else + results = "No tracking IP addresses configured on " .. interface + end + end + else + results = "No default gateway for " .. interface .. " found. Default route does not exist or is configured incorrectly" + end + elseif tool == "rulechk" then + getInterfaceNumber() + local rule1 = sys.exec(ip .. "rule | grep $(echo $((" .. interfaceNumber .. " + 1000)))") + local rule2 = sys.exec(ip .. "rule | grep $(echo $((" .. interfaceNumber .. " + 2000)))") + if rule1 ~= "" and rule2 ~= "" then + results = "All required interface IP rules found:\n\n" .. rule1 .. rule2 + elseif rule1 ~= "" or rule2 ~= "" then + results = "Missing 1 of the 2 required interface IP rules\n\n\nRules found:\n\n" .. rule1 .. rule2 + else + results = "Missing both of the required interface IP rules" + end + elseif tool == "routechk" then + getInterfaceNumber() + local routeTable = sys.exec(ip .. "route list table " .. interfaceNumber) + if routeTable ~= "" then + results = "Interface routing table " .. interfaceNumber .. " was found:\n\n" .. routeTable + else + results = "Missing required interface routing table " .. interfaceNumber + end + elseif tool == "hotplug" then + if task == "ifup" then + os.execute("/usr/sbin/mwan3 ifup " .. interface) + results = "Hotplug ifup sent to interface " .. interface .. "..." + else + os.execute("/usr/sbin/mwan3 ifdown " .. interface) + results = "Hotplug ifdown sent to interface " .. interface .. "..." + end + end + else + results = "Unable to perform diagnostic tests on " .. interface .. ". There is no physical or virtual device associated with this interface" + end + end + if results ~= "" then + results = ut.trim(results) + mArray.diagnostics = { results } + end + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end + +function troubleshootingData() + local ver = require "luci.version" + + local mArray = {} + + -- software versions + local wrtRelease = ut.trim(ver.distversion) + if wrtRelease ~= "" then + wrtRelease = "OpenWrt - " .. wrtRelease + else + wrtRelease = "OpenWrt - unknown" + end + local luciRelease = ut.trim(ver.luciversion) + if luciRelease ~= "" then + luciRelease = "\nLuCI - " .. luciRelease + else + luciRelease = "\nLuCI - unknown" + end + local mwanVersion = ut.trim(sys.exec("opkg info mwan3 | grep Version | awk '{print $2}'")) + if mwanVersion ~= "" then + mwanVersion = "\n\nmwan3 - " .. mwanVersion + else + mwanVersion = "\n\nmwan3 - unknown" + end + local mwanLuciVersion = ut.trim(sys.exec("opkg info luci-app-mwan3 | grep Version | awk '{print $2}'")) + if mwanLuciVersion ~= "" then + mwanLuciVersion = "\nmwan3-luci - " .. mwanLuciVersion + else + mwanLuciVersion = "\nmwan3-luci - unknown" + end + mArray.versions = { wrtRelease .. luciRelease .. mwanVersion .. mwanLuciVersion } + + -- mwan config + local mwanConfig = ut.trim(sys.exec("cat /etc/config/mwan3")) + if mwanConfig == "" then + mwanConfig = "No data found" + end + mArray.mwanconfig = { mwanConfig } + + -- network config + local networkConfig = ut.trim(sys.exec("cat /etc/config/network | sed -e 's/.*username.*/ USERNAME HIDDEN/' -e 's/.*password.*/ PASSWORD HIDDEN/'")) + if networkConfig == "" then + networkConfig = "No data found" + end + mArray.netconfig = { networkConfig } + + -- wireless config + local wirelessConfig = ut.trim(sys.exec("cat /etc/config/wireless | sed -e 's/.*username.*/ USERNAME HIDDEN/' -e 's/.*password.*/ PASSWORD HIDDEN/' -e 's/.*key.*/ KEY HIDDEN/'")) + if wirelessConfig == "" then + wirelessConfig = "No data found" + end + mArray.wificonfig = { wirelessConfig } + + -- ifconfig + local ifconfig = ut.trim(sys.exec("ifconfig")) + if ifconfig == "" then + ifconfig = "No data found" + end + mArray.ifconfig = { ifconfig } + + -- route -n + local routeShow = ut.trim(sys.exec("route -n")) + if routeShow == "" then + routeShow = "No data found" + end + mArray.routeshow = { routeShow } + + -- ip rule show + local ipRuleShow = ut.trim(sys.exec(ip .. "rule show")) + if ipRuleShow == "" then + ipRuleShow = "No data found" + end + mArray.iprule = { ipRuleShow } + + -- ip route list table 1-250 + local routeList, routeString = ut.trim(sys.exec(ip .. "rule | sed 's/://g' | awk '$1>=2001 && $1<=2250' | awk '{print $NF}'")), "" + if routeList ~= "" then + for line in routeList:gmatch("[^\r\n]+") do + routeString = routeString .. line .. "\n" .. sys.exec(ip .. "route list table " .. line) + end + routeString = ut.trim(routeString) + else + routeString = "No data found" + end + mArray.routelist = { routeString } + + -- default firewall output policy + local firewallOut = ut.trim(sys.exec("uci -p /var/state get firewall.@defaults[0].output")) + if firewallOut == "" then + firewallOut = "No data found" + end + mArray.firewallout = { firewallOut } + + -- iptables + local iptables = ut.trim(sys.exec("iptables -L -t mangle -v -n")) + if iptables == "" then + iptables = "No data found" + end + mArray.iptables = { iptables } + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end diff --git a/ext-rooterbcm8/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua b/ext-rooterbcm8/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua new file mode 100644 index 0000000..f401629 --- /dev/null +++ b/ext-rooterbcm8/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua @@ -0,0 +1,194 @@ +-- ------ extra functions ------ -- + +function interfaceCheck() + metricValue = ut.trim(sys.exec("uci -p /var/state get network." .. arg[1] .. ".metric")) + if metricValue == "" then -- no metric + errorNoMetric = 1 + else -- if metric exists create list of interface metrics to compare against for duplicates + uci.cursor():foreach("mwan3", "interface", + function (section) + local metricValue = ut.trim(sys.exec("uci -p /var/state get network." .. section[".name"] .. ".metric")) + metricList = metricList .. section[".name"] .. " " .. metricValue .. "\n" + end + ) + -- compare metric against list + local metricDuplicateNumbers, metricDuplicates = sys.exec("echo '" .. metricList .. "' | awk '{print $2}' | uniq -d"), "" + for line in metricDuplicateNumbers:gmatch("[^\r\n]+") do + metricDuplicates = sys.exec("echo '" .. metricList .. "' | grep '" .. line .. "' | awk '{print $1}'") + errorDuplicateMetricList = errorDuplicateMetricList .. metricDuplicates + end + if sys.exec("echo '" .. errorDuplicateMetricList .. "' | grep -w " .. arg[1]) ~= "" then + errorDuplicateMetric = 1 + end + end + -- check if this interface has a higher reliability requirement than track IPs configured + local trackingNumber = tonumber(ut.trim(sys.exec("echo $(uci -p /var/state get mwan3." .. arg[1] .. ".track_ip) | wc -w"))) + if trackingNumber > 0 then + local reliabilityNumber = tonumber(ut.trim(sys.exec("uci -p /var/state get mwan3." .. arg[1] .. ".reliability"))) + if reliabilityNumber and reliabilityNumber > trackingNumber then + errorReliability = 1 + end + end + -- check if any interfaces are not properly configured in /etc/config/network or have no default route in main routing table + if ut.trim(sys.exec("uci -p /var/state get network." .. arg[1])) == "interface" then + local interfaceDevice = ut.trim(sys.exec("uci -p /var/state get network." .. arg[1] .. ".ifname")) + if interfaceDevice == "uci: Entry not found" or interfaceDevice == "" then + errorNetConfig = 1 + errorRoute = 1 + else + local routeCheck = ut.trim(sys.exec("route -n | awk '{if ($8 == \"" .. interfaceDevice .. "\" && $1 == \"0.0.0.0\" && $3 == \"0.0.0.0\") print $1}'")) + if routeCheck == "" then + errorRoute = 1 + end + end + else + errorNetConfig = 1 + errorRoute = 1 + end +end + +function interfaceWarnings() -- display warning messages at the top of the page + local warns, lineBreak = "", "" + if errorReliability == 1 then + warns = "WARNING: this interface has a higher reliability requirement than there are tracking IP addresses!" + lineBreak = "

                                          " + end + if errorRoute == 1 then + warns = warns .. lineBreak .. "WARNING: this interface has no default route in the main routing table!" + lineBreak = "

                                          " + end + if errorNetConfig == 1 then + warns = warns .. lineBreak .. "WARNING: this interface is configured incorrectly or not at all in /etc/config/network!" + lineBreak = "

                                          " + end + if errorNoMetric == 1 then + warns = warns .. lineBreak .. "WARNING: this interface has no metric configured in /etc/config/network!" + elseif errorDuplicateMetric == 1 then + warns = warns .. lineBreak .. "WARNING: this and other interfaces have duplicate metrics configured in /etc/config/network!" + end + return warns +end + +-- ------ interface configuration ------ -- + +dsp = require "luci.dispatcher" +sys = require "luci.sys" +ut = require "luci.util" +arg[1] = arg[1] or "" + +metricValue = "" +metricList = "" +errorDuplicateMetricList = "" +errorNoMetric = 0 +errorDuplicateMetric = 0 +errorRoute = 0 +errorNetConfig = 0 +errorReliability = 0 +interfaceCheck() + + +m5 = Map("mwan3", translate("MWAN Interface Configuration - " .. arg[1]), + translate(interfaceWarnings())) + m5.redirect = dsp.build_url("admin", "network", "mwan", "configuration", "interface") + + +mwan_interface = m5:section(NamedSection, arg[1], "interface", "") + mwan_interface.addremove = false + mwan_interface.dynamic = false + + +enabled = mwan_interface:option(ListValue, "enabled", translate("Enabled")) + enabled.default = "1" + enabled:value("1", translate("Yes")) + enabled:value("0", translate("No")) + +track_ip = mwan_interface:option(DynamicList, "track_ip", translate("Tracking IP"), + translate("This IP address will be pinged to dermine if the link is up or down. Leave blank to assume interface is always online")) + track_ip.datatype = "ipaddr" + +reliability = mwan_interface:option(Value, "reliability", translate("Tracking reliability"), + translate("Acceptable values: 1-100. This many Tracking IP addresses must respond for the link to be deemed up")) + reliability.datatype = "range(1, 100)" + reliability.default = "1" + +count = mwan_interface:option(ListValue, "count", translate("Ping count")) + count.default = "1" + count:value("1") + count:value("2") + count:value("3") + count:value("4") + count:value("5") + +timeout = mwan_interface:option(ListValue, "timeout", translate("Ping timeout")) + timeout.default = "2" + timeout:value("1", translate("1 second")) + timeout:value("2", translate("2 seconds")) + timeout:value("3", translate("3 seconds")) + timeout:value("4", translate("4 seconds")) + timeout:value("5", translate("5 seconds")) + timeout:value("6", translate("6 seconds")) + timeout:value("7", translate("7 seconds")) + timeout:value("8", translate("8 seconds")) + timeout:value("9", translate("9 seconds")) + timeout:value("10", translate("10 seconds")) + +interval = mwan_interface:option(ListValue, "interval", translate("Ping interval")) + interval.default = "5" + interval:value("1", translate("1 second")) + interval:value("3", translate("3 seconds")) + interval:value("5", translate("5 seconds")) + interval:value("10", translate("10 seconds")) + interval:value("20", translate("20 seconds")) + interval:value("30", translate("30 seconds")) + interval:value("60", translate("1 minute")) + interval:value("300", translate("5 minutes")) + interval:value("600", translate("10 minutes")) + interval:value("900", translate("15 minutes")) + interval:value("1800", translate("30 minutes")) + interval:value("3600", translate("1 hour")) + +down = mwan_interface:option(ListValue, "down", translate("Interface down"), + translate("Interface will be deemed down after this many failed ping tests")) + down.default = "3" + down:value("1") + down:value("2") + down:value("3") + down:value("4") + down:value("5") + down:value("6") + down:value("7") + down:value("8") + down:value("9") + down:value("10") + +up = mwan_interface:option(ListValue, "up", translate("Interface up"), + translate("Downed interface will be deemed up after this many successful ping tests")) + up.default = "3" + up:value("1") + up:value("2") + up:value("3") + up:value("4") + up:value("5") + up:value("6") + up:value("7") + up:value("8") + up:value("9") + up:value("10") + +dwnscript = mwan_interface:option(Value, "dwnscript", translate("Interface Down Script"), + translate("Script to run when Interface is deemed down")) + dwnscript.default = "/usr/lib/rooter/idown.lua" + +metric = mwan_interface:option(DummyValue, "metric", translate("Metric"), + translate("This displays the metric assigned to this interface in /etc/config/network")) + metric.rawhtml = true + function metric.cfgvalue(self, s) + if errorNoMetric == 0 then + return metricValue + else + return "—" + end + end + + +return m5 diff --git a/ext-rooterbcm8/files/usr/lib/lua/luci/view/admin_status/indexnew.htm b/ext-rooterbcm8/files/usr/lib/lua/luci/view/admin_status/indexnew.htm new file mode 100644 index 0000000..582fcb6 --- /dev/null +++ b/ext-rooterbcm8/files/usr/lib/lua/luci/view/admin_status/indexnew.htm @@ -0,0 +1,806 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008-2011 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<% + local fs = require "nixio.fs" + local util = require "luci.util" + local stat = require "luci.tools.status" + local ver = require "luci.version" + + local has_ipv6 = fs.access("/proc/net/ipv6_route") + local has_dhcp = fs.access("/etc/config/dhcp") + local has_wifi = ((fs.stat("/etc/config/wireless", "size") or 0) > 0) + + local sysinfo = luci.util.ubus("system", "info") or { } + local boardinfo = luci.util.ubus("system", "board") or { } + local unameinfo = nixio.uname() or { } + + local meminfo = sysinfo.memory or { + total = 0, + free = 0, + buffered = 0, + shared = 0 + } + + local swapinfo = sysinfo.swap or { + total = 0, + free = 0 + } + + local has_dsl = fs.access("/etc/init.d/dsl_control") + + if luci.http.formvalue("status") == "1" then + local ntm = require "luci.model.network".init() + local wan = ntm:get_wannet() + local wan6 = ntm:get_wan6net() + + local conn_count = tonumber(( + luci.sys.exec("wc -l /proc/net/nf_conntrack") or + luci.sys.exec("wc -l /proc/net/ip_conntrack") or + ""):match("%d+")) or 0 + + local conn_max = tonumber(( + luci.sys.exec("sysctl net.nf_conntrack_max") or + luci.sys.exec("sysctl net.ipv4.netfilter.ip_conntrack_max") or + ""):match("%d+")) or 4096 + + local rv = { + uptime = sysinfo.uptime or 0, + localtime = os.date(), + loadavg = sysinfo.load or { 0, 0, 0 }, + memory = meminfo, + swap = swapinfo, + connmax = conn_max, + conncount = conn_count, + leases = stat.dhcp_leases(), + leases6 = stat.dhcp6_leases(), + wifinets = stat.wifi_networks() + } + + if wan then + rv.wan = { + ipaddr = wan:ipaddr(), + gwaddr = wan:gwaddr(), + netmask = wan:netmask(), + dns = wan:dnsaddrs(), + expires = wan:expires(), + uptime = wan:uptime(), + proto = wan:proto(), + ifname = wan:ifname(), + link = wan:adminlink() + } + end + + if wan6 then + rv.wan6 = { + ip6addr = wan6:ip6addr(), + gw6addr = wan6:gw6addr(), + dns = wan6:dns6addrs(), + uptime = wan6:uptime(), + ifname = wan6:ifname(), + link = wan6:adminlink() + } + end + + if has_dsl then + local dsl_stat = luci.sys.exec("/etc/init.d/dsl_control lucistat") + local dsl_func = loadstring(dsl_stat) + if dsl_func then + rv.dsl = dsl_func() + end + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) + + return + elseif luci.http.formvalue("hosts") == "1" then + luci.http.prepare_content("application/json") + luci.http.write_json(luci.sys.net.host_hints()) + + return + end +-%> + +<%+header%> + + + + +

                                          <%:Status%>

                                          + +
                                          + <%:System%> + + + + + + + + + +
                                          <%:Hostname%><%=luci.sys.hostname() or "?"%>
                                          <%:Model%><%=pcdata(boardinfo.model or boardinfo.system or "?")%>
                                          <%:Firmware Version%> + <%=pcdata(ver.distname)%> <%=pcdata(ver.distversion)%> / + <%=pcdata(ver.luciname)%> (<%=pcdata(ver.luciversion)%>) +
                                          <%:Kernel Version%><%=unameinfo.release or "?"%>
                                          <%:Local Time%>-
                                          <%:Uptime%>-
                                          <%:Load Average%>-
                                          +
                                          + +
                                          + <%:Memory%> + + + + + +
                                          <%:Total Available%>-
                                          <%:Free%>-
                                          <%:Buffered%>-
                                          +
                                          + +<% if swapinfo.total > 0 then %> +
                                          + <%:Swap%> + + + + +
                                          <%:Total Available%>-
                                          <%:Free%>-
                                          +
                                          +<% end %> + +
                                          + <%:Network%> + + + + <% if has_ipv6 then %> + + <% end %> + +
                                          <%:IPv4 WAN Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          <%:IPv6 WAN Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          <%:Active Connections%>-
                                          +
                                          + +<%- + include("rooter/external") +-%> + +<% if has_dhcp then %> +
                                          + <%:DHCP Leases%> + + + + + + + + + + + +
                                          <%:Hostname%><%:IPv4-Address%><%:MAC-Address%><%:Leasetime remaining%>

                                          <%:Collecting data...%>
                                          +
                                          + + +<% end %> + +<% if has_dsl then %> +
                                          + <%:DSL%> + + +
                                          <%:DSL Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          +
                                          +<% end %> + +<% if has_wifi then %> +
                                          + <%:Wireless%> + + + +
                                          <%:Collecting data...%>
                                          +
                                          + +
                                          + <%:Associated Stations%> + + + + + + + + + + + + + +
                                           <%:Network%><%:MAC-Address%><%:Host%><%:Signal%> / <%:Noise%><%:RX Rate%> / <%:TX Rate%>

                                          <%:Collecting data...%>
                                          +
                                          +<% end %> + +<%- + local incdir = util.libpath() .. "/view/admin_status/index/" + if fs.access(incdir) then + local inc + for inc in fs.dir(incdir) do + if inc:match("%.htm$") then + include("admin_status/index/" .. inc:gsub("%.htm$", "")) + end + end + end +-%> + +<%+footer%> diff --git a/ext-rooterbcm8/files/usr/lib/lua/luci/view/rooter/external.htm b/ext-rooterbcm8/files/usr/lib/lua/luci/view/rooter/external.htm new file mode 100644 index 0000000..1feee41 --- /dev/null +++ b/ext-rooterbcm8/files/usr/lib/lua/luci/view/rooter/external.htm @@ -0,0 +1,26 @@ + + +
                                          + <%:External Internet IP Address%> + + +
                                          <%:IP Address%><%:Loading%> Collecting data...
                                          +
                                          + diff --git a/ext-rooterbcm8/files/usr/sbin/mwan3track b/ext-rooterbcm8/files/usr/sbin/mwan3track new file mode 100644 index 0000000..037c61e --- /dev/null +++ b/ext-rooterbcm8/files/usr/sbin/mwan3track @@ -0,0 +1,79 @@ +#!/bin/sh + +log() { + logger -t "MWan3Track " "$@" +} + +[ -z "$9" ] && echo "Error: should not be started manually" && exit 0 + +if [ -e /var/run/mwan3track-$1.pid ] ; then + kill $(cat /var/run/mwan3track-$1.pid) &> /dev/null + rm /var/run/mwan3track-$1.pid &> /dev/null +fi + +echo "$$" > /var/run/mwan3track-$1.pid + +score=$(($7+$8)) +track_ips=$(echo $* | cut -d ' ' -f 9-99) +host_up_count=0 +lost=0 + +while true; do + + for track_ip in $track_ips; do + ping -I $2 -c $4 -W $5 -s 4 -q $track_ip &> /dev/null + if [ $? -eq 0 ]; then + let host_up_count++ + else + let lost++ + fi + done + + if [ $host_up_count -lt $3 ]; then + let score-- + + if [ $score -lt $8 ]; then score=0 ; fi + if [ $score -eq $8 ]; then + + logger -t mwan3track -p notice "Interface $1 ($2) is offline" + SCR=$(uci get mwan3.$1.dwnscript) + if [ ! -z $SCR ]; then + if [ -e $SCR ]; then + $SCR "1" $1 $2 + fi + fi + env -i ACTION=ifdown INTERFACE=$1 DEVICE=$2 /sbin/hotplug-call iface + score=0 + fi + + else + + if [ $score -lt $(($7+$8)) ] && [ $lost -gt 0 ]; then + + logger -t mwan3track -p info "Lost $(($lost*$4)) ping(s) on interface $1 ($2)" + + fi + + let score++ + lost=0 + + if [ $score -gt $8 ]; then score=$(($7+$8)); fi + if [ $score -eq $8 ]; then + SCR=$(uci get mwan3.$1.dwnscript) + if [ ! -z $SCR ]; then + if [ -e $SCR ]; then + $SCR "2" $1 $2 + fi + fi + logger -t mwan3track -p notice "Interface $1 ($2) is online" + env -i ACTION=ifup INTERFACE=$1 DEVICE=$2 /sbin/hotplug-call iface + rm /var/run/mwan3track-$1.pid + exit 0 + fi + fi + + host_up_count=0 + sleep $6 +done + +exit 1 diff --git a/ext-rooterlite/Makefile b/ext-rooterlite/Makefile new file mode 100644 index 0000000..148e309 --- /dev/null +++ b/ext-rooterlite/Makefile @@ -0,0 +1,38 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-rooterlite +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-rooterlite + SECTION:=utils + CATEGORY:=ROOter + DEPENDS:=+luci +luci-app-sqm +luci-app-ddns +luci-app-rootervpn +luci-app-mwan3 \ + +ext-command +luci-theme-darkmatter +kmod-sched-cake \ + +wget +ext-extra +luci \ + +nano +picocom +bwmon +luci-app-hotspot +luci-app-wol + TITLE:=ROOter Lite support for 8meg and larger routers + PKGARCH:=all +endef + +define Package/ext-rooterlite/description + Helper scripts to enable ROOter Lite on 8meg and larger routers +endef + + +define Build/Compile +endef + +define Package/ext-rooterlite/install + $(CP) ./files/* $(1)/ + + +endef + +$(eval $(call BuildPackage,ext-rooterlite)) diff --git a/ext-rooterlite/files/etc/config/mwan3 b/ext-rooterlite/files/etc/config/mwan3 new file mode 100644 index 0000000..c28bc66 --- /dev/null +++ b/ext-rooterlite/files/etc/config/mwan3 @@ -0,0 +1,82 @@ + +config interface 'wan1' + option count '1' + option timeout '2' + option reroute '0' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + option up '2' + option reliability '1' + option interval '10' + option down '2' + option enabled '0' + option dwnscript '/usr/lib/rooter/idown.lua' + +config interface 'wan2' + option count '1' + option timeout '2' + option reroute '0' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + option reliability '1' + option enabled '0' + option interval '10' + option down '2' + option up '2' + option dwnscript '/usr/lib/rooter/idown.lua' + +config interface 'wan' + option reliability '1' + option interval '5' + option timeout '3' + option down '5' + option up '1' + option enabled '1' + option count '3' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + +config interface 'wwan' + option reliability '1' + option interval '5' + option timeout '3' + option down '5' + option up '1' + option enabled '1' + option count '3' + list track_ip '8.8.8.8' + list track_ip '8.8.4.4' + +config member 'wan_m1_w1' + option interface 'wan' + option metric '1' + option weight '1' + +config member 'wwan_m2_w2' + option interface 'wwan' + option metric '2' + option weight '2' + +config member 'wan1_m3_w3' + option interface 'wan1' + option metric '3' + option weight '3' + +config member 'wan2_m4_w4' + option interface 'wan2' + option metric '4' + option weight '4' + +config policy 'failover' + list use_member 'wan_m1_w1' + list use_member 'wwan_m2_w2' + list use_member 'wan1_m3_w3' + list use_member 'wan2_m4_w4' + option last_resort 'unreachable' + +config rule 'rule1' + option dest_ip '0.0.0.0/0' + option proto 'all' + option sticky '0' + option use_policy 'failover' + diff --git a/ext-rooterlite/files/etc/flash b/ext-rooterlite/files/etc/flash new file mode 100644 index 0000000..c80ef7f --- /dev/null +++ b/ext-rooterlite/files/etc/flash @@ -0,0 +1 @@ +FLASH="8" diff --git a/ext-rooterlite/files/usr/lib/lua/luci/controller/mwan3.lua b/ext-rooterlite/files/usr/lib/lua/luci/controller/mwan3.lua new file mode 100644 index 0000000..b8c07e2 --- /dev/null +++ b/ext-rooterlite/files/usr/lib/lua/luci/controller/mwan3.lua @@ -0,0 +1,339 @@ +module("luci.controller.mwan3", package.seeall) + +sys = require "luci.sys" +ut = require "luci.util" + +ip = "/usr/bin/ip -4 " + +function index() + if not nixio.fs.access("/etc/config/mwan3") then + return + end + + entry({"admin", "network", "mwan"}, + alias("admin", "network", "mwan", "overview"), + _("Load Balancing"), 600) + + entry({"admin", "network", "mwan", "overview"}, + alias("admin", "network", "mwan", "overview", "overview_interface"), + _("Overview"), 10) + entry({"admin", "network", "mwan", "overview", "overview_interface"}, + template("mwan/overview_interface")) + entry({"admin", "network", "mwan", "overview", "interface_status"}, + call("interfaceStatus")) + entry({"admin", "network", "mwan", "overview", "overview_detailed"}, + template("mwan/overview_detailed")) + entry({"admin", "network", "mwan", "overview", "detailed_status"}, + call("detailedStatus")) + + entry({"admin", "network", "mwan", "configuration"}, + alias("admin", "network", "mwan", "configuration", "interface"), + _("Configuration"), 20) + entry({"admin", "network", "mwan", "configuration", "interface"}, + arcombine(cbi("mwan/interface"), cbi("mwan/interfaceconfig")), + _("Interfaces"), 10).leaf = true + entry({"admin", "network", "mwan", "configuration", "member"}, + arcombine(cbi("mwan/member"), cbi("mwan/memberconfig")), + _("Members"), 20).leaf = true + entry({"admin", "network", "mwan", "configuration", "policy"}, + arcombine(cbi("mwan/policy"), cbi("mwan/policyconfig")), + _("Policies"), 30).leaf = true + entry({"admin", "network", "mwan", "configuration", "rule"}, + arcombine(cbi("mwan/rule"), cbi("mwan/ruleconfig")), + _("Rules"), 40).leaf = true + + entry({"admin", "network", "mwan", "advanced"}, + alias("admin", "network", "mwan", "advanced", "hotplugscript"), + _("Advanced"), 100) + entry({"admin", "network", "mwan", "advanced", "hotplugscript"}, + form("mwan/advanced_hotplugscript")) + entry({"admin", "network", "mwan", "advanced", "mwanconfig"}, + form("mwan/advanced_mwanconfig")) + entry({"admin", "network", "mwan", "advanced", "networkconfig"}, + form("mwan/advanced_networkconfig")) + entry({"admin", "network", "mwan", "advanced", "wirelessconfig"}, + form("mwan/advanced_wirelessconfig")) + entry({"admin", "network", "mwan", "advanced", "diagnostics"}, + template("mwan/advanced_diagnostics")) + entry({"admin", "network", "mwan", "advanced", "diagnostics_display"}, + call("diagnosticsData"), nil).leaf = true + entry({"admin", "network", "mwan", "advanced", "troubleshooting"}, + template("mwan/advanced_troubleshooting")) + entry({"admin", "network", "mwan", "advanced", "troubleshooting_display"}, + call("troubleshootingData")) +end + +function getInterfaceStatus(ruleNumber, interfaceName) + if ut.trim(sys.exec("uci -p /var/state get mwan3." .. interfaceName .. ".enabled")) == "1" then + if ut.trim(sys.exec(ip .. "route list table " .. ruleNumber)) ~= "" then + if ut.trim(sys.exec("uci -p /var/state get mwan3." .. interfaceName .. ".track_ip")) ~= "" then + return "online" + else + return "notMonitored" + end + else + return "offline" + end + else + return "notEnabled" + end +end + +function getInterfaceName() + local ruleNumber, status = 0, "" + uci.cursor():foreach("mwan3", "interface", + function (section) + ruleNumber = ruleNumber+1 + status = status .. section[".name"] .. "[" .. getInterfaceStatus(ruleNumber, section[".name"]) .. "]" + end + ) + return status +end + +function interfaceStatus() + local ntm = require "luci.model.network".init() + + local mArray = {} + + -- overview status + local statusString = getInterfaceName() + if statusString ~= "" then + mArray.wans = {} + wansid = {} + + for wanName, interfaceState in string.gfind(statusString, "([^%[]+)%[([^%]]+)%]") do + local wanInterfaceEnabled = ut.trim(sys.exec("uci -p /var/state get mwan3." .. wanName .. ".enabled")) + local wanInterfaceName = "" + if wanInterfaceEnabled == "1" then + if wanName == "wwan" then + wanInterfaceName = "wifi" + else + wanInterfaceName = ut.trim(sys.exec("uci -p /var/state get network." .. wanName .. ".ifname")) + end + end + if wanInterfaceName == "" then + wanInterfaceName = "X" + end + local wanDeviceLink = ntm:get_interface(wanInterfaceName) + wanDeviceLink = wanDeviceLink and wanDeviceLink:get_network() + wanDeviceLink = wanDeviceLink and wanDeviceLink:adminlink() or "#" + wansid[wanName] = #mArray.wans + 1 + mArray.wans[wansid[wanName]] = { name = wanName, link = wanDeviceLink, ifname = wanInterfaceName, status = interfaceState } + end + end + + -- overview status log + local mwanLog = ut.trim(sys.exec("logread | grep mwan3 | tail -n 50 | sed 'x;1!H;$!d;x'")) + if mwanLog ~= "" then + mArray.mwanlog = { mwanLog } + end + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end + +function detailedStatus() + local mArray = {} + + -- detailed mwan status + local detailStatusInfo = ut.trim(sys.exec("/usr/sbin/mwan3 status")) + if detailStatusInfo ~= "" then + mArray.mwandetail = { detailStatusInfo } + end + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end + +function diagnosticsData(interface, tool, task) + function getInterfaceNumber() + local number = 0 + uci.cursor():foreach("mwan3", "interface", + function (section) + number = number+1 + if section[".name"] == interface then + interfaceNumber = number + end + end + ) + end + + local mArray = {} + + local results = "" + if tool == "service" then + os.execute("/usr/sbin/mwan3 " .. task) + if task == "restart" then + results = "MWAN3 restarted" + elseif task == "stop" then + results = "MWAN3 stopped" + else + results = "MWAN3 started" + end + else + local interfaceDevice = ut.trim(sys.exec("uci -p /var/state get network." .. interface .. ".ifname")) + if interfaceDevice ~= "" then + if tool == "ping" then + local gateway = ut.trim(sys.exec("route -n | awk '{if ($8 == \"" .. interfaceDevice .. "\" && $1 == \"0.0.0.0\" && $3 == \"0.0.0.0\") print $2}'")) + if gateway ~= "" then + if task == "gateway" then + local pingCommand = "ping -c 3 -W 2 -I " .. interfaceDevice .. " " .. gateway + results = pingCommand .. "\n\n" .. sys.exec(pingCommand) + else + local tracked = ut.trim(sys.exec("uci -p /var/state get mwan3." .. interface .. ".track_ip")) + if tracked ~= "" then + for z in tracked:gmatch("[^ ]+") do + local pingCommand = "ping -c 3 -W 2 -I " .. interfaceDevice .. " " .. z + results = results .. pingCommand .. "\n\n" .. sys.exec(pingCommand) .. "\n\n" + end + else + results = "No tracking IP addresses configured on " .. interface + end + end + else + results = "No default gateway for " .. interface .. " found. Default route does not exist or is configured incorrectly" + end + elseif tool == "rulechk" then + getInterfaceNumber() + local rule1 = sys.exec(ip .. "rule | grep $(echo $((" .. interfaceNumber .. " + 1000)))") + local rule2 = sys.exec(ip .. "rule | grep $(echo $((" .. interfaceNumber .. " + 2000)))") + if rule1 ~= "" and rule2 ~= "" then + results = "All required interface IP rules found:\n\n" .. rule1 .. rule2 + elseif rule1 ~= "" or rule2 ~= "" then + results = "Missing 1 of the 2 required interface IP rules\n\n\nRules found:\n\n" .. rule1 .. rule2 + else + results = "Missing both of the required interface IP rules" + end + elseif tool == "routechk" then + getInterfaceNumber() + local routeTable = sys.exec(ip .. "route list table " .. interfaceNumber) + if routeTable ~= "" then + results = "Interface routing table " .. interfaceNumber .. " was found:\n\n" .. routeTable + else + results = "Missing required interface routing table " .. interfaceNumber + end + elseif tool == "hotplug" then + if task == "ifup" then + os.execute("/usr/sbin/mwan3 ifup " .. interface) + results = "Hotplug ifup sent to interface " .. interface .. "..." + else + os.execute("/usr/sbin/mwan3 ifdown " .. interface) + results = "Hotplug ifdown sent to interface " .. interface .. "..." + end + end + else + results = "Unable to perform diagnostic tests on " .. interface .. ". There is no physical or virtual device associated with this interface" + end + end + if results ~= "" then + results = ut.trim(results) + mArray.diagnostics = { results } + end + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end + +function troubleshootingData() + local ver = require "luci.version" + + local mArray = {} + + -- software versions + local wrtRelease = ut.trim(ver.distversion) + if wrtRelease ~= "" then + wrtRelease = "OpenWrt - " .. wrtRelease + else + wrtRelease = "OpenWrt - unknown" + end + local luciRelease = ut.trim(ver.luciversion) + if luciRelease ~= "" then + luciRelease = "\nLuCI - " .. luciRelease + else + luciRelease = "\nLuCI - unknown" + end + local mwanVersion = ut.trim(sys.exec("opkg info mwan3 | grep Version | awk '{print $2}'")) + if mwanVersion ~= "" then + mwanVersion = "\n\nmwan3 - " .. mwanVersion + else + mwanVersion = "\n\nmwan3 - unknown" + end + local mwanLuciVersion = ut.trim(sys.exec("opkg info luci-app-mwan3 | grep Version | awk '{print $2}'")) + if mwanLuciVersion ~= "" then + mwanLuciVersion = "\nmwan3-luci - " .. mwanLuciVersion + else + mwanLuciVersion = "\nmwan3-luci - unknown" + end + mArray.versions = { wrtRelease .. luciRelease .. mwanVersion .. mwanLuciVersion } + + -- mwan config + local mwanConfig = ut.trim(sys.exec("cat /etc/config/mwan3")) + if mwanConfig == "" then + mwanConfig = "No data found" + end + mArray.mwanconfig = { mwanConfig } + + -- network config + local networkConfig = ut.trim(sys.exec("cat /etc/config/network | sed -e 's/.*username.*/ USERNAME HIDDEN/' -e 's/.*password.*/ PASSWORD HIDDEN/'")) + if networkConfig == "" then + networkConfig = "No data found" + end + mArray.netconfig = { networkConfig } + + -- wireless config + local wirelessConfig = ut.trim(sys.exec("cat /etc/config/wireless | sed -e 's/.*username.*/ USERNAME HIDDEN/' -e 's/.*password.*/ PASSWORD HIDDEN/' -e 's/.*key.*/ KEY HIDDEN/'")) + if wirelessConfig == "" then + wirelessConfig = "No data found" + end + mArray.wificonfig = { wirelessConfig } + + -- ifconfig + local ifconfig = ut.trim(sys.exec("ifconfig")) + if ifconfig == "" then + ifconfig = "No data found" + end + mArray.ifconfig = { ifconfig } + + -- route -n + local routeShow = ut.trim(sys.exec("route -n")) + if routeShow == "" then + routeShow = "No data found" + end + mArray.routeshow = { routeShow } + + -- ip rule show + local ipRuleShow = ut.trim(sys.exec(ip .. "rule show")) + if ipRuleShow == "" then + ipRuleShow = "No data found" + end + mArray.iprule = { ipRuleShow } + + -- ip route list table 1-250 + local routeList, routeString = ut.trim(sys.exec(ip .. "rule | sed 's/://g' | awk '$1>=2001 && $1<=2250' | awk '{print $NF}'")), "" + if routeList ~= "" then + for line in routeList:gmatch("[^\r\n]+") do + routeString = routeString .. line .. "\n" .. sys.exec(ip .. "route list table " .. line) + end + routeString = ut.trim(routeString) + else + routeString = "No data found" + end + mArray.routelist = { routeString } + + -- default firewall output policy + local firewallOut = ut.trim(sys.exec("uci -p /var/state get firewall.@defaults[0].output")) + if firewallOut == "" then + firewallOut = "No data found" + end + mArray.firewallout = { firewallOut } + + -- iptables + local iptables = ut.trim(sys.exec("iptables -L -t mangle -v -n")) + if iptables == "" then + iptables = "No data found" + end + mArray.iptables = { iptables } + + luci.http.prepare_content("application/json") + luci.http.write_json(mArray) +end diff --git a/ext-rooterlite/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua b/ext-rooterlite/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua new file mode 100644 index 0000000..f401629 --- /dev/null +++ b/ext-rooterlite/files/usr/lib/lua/luci/model/cbi/mwan/interfaceconfig.lua @@ -0,0 +1,194 @@ +-- ------ extra functions ------ -- + +function interfaceCheck() + metricValue = ut.trim(sys.exec("uci -p /var/state get network." .. arg[1] .. ".metric")) + if metricValue == "" then -- no metric + errorNoMetric = 1 + else -- if metric exists create list of interface metrics to compare against for duplicates + uci.cursor():foreach("mwan3", "interface", + function (section) + local metricValue = ut.trim(sys.exec("uci -p /var/state get network." .. section[".name"] .. ".metric")) + metricList = metricList .. section[".name"] .. " " .. metricValue .. "\n" + end + ) + -- compare metric against list + local metricDuplicateNumbers, metricDuplicates = sys.exec("echo '" .. metricList .. "' | awk '{print $2}' | uniq -d"), "" + for line in metricDuplicateNumbers:gmatch("[^\r\n]+") do + metricDuplicates = sys.exec("echo '" .. metricList .. "' | grep '" .. line .. "' | awk '{print $1}'") + errorDuplicateMetricList = errorDuplicateMetricList .. metricDuplicates + end + if sys.exec("echo '" .. errorDuplicateMetricList .. "' | grep -w " .. arg[1]) ~= "" then + errorDuplicateMetric = 1 + end + end + -- check if this interface has a higher reliability requirement than track IPs configured + local trackingNumber = tonumber(ut.trim(sys.exec("echo $(uci -p /var/state get mwan3." .. arg[1] .. ".track_ip) | wc -w"))) + if trackingNumber > 0 then + local reliabilityNumber = tonumber(ut.trim(sys.exec("uci -p /var/state get mwan3." .. arg[1] .. ".reliability"))) + if reliabilityNumber and reliabilityNumber > trackingNumber then + errorReliability = 1 + end + end + -- check if any interfaces are not properly configured in /etc/config/network or have no default route in main routing table + if ut.trim(sys.exec("uci -p /var/state get network." .. arg[1])) == "interface" then + local interfaceDevice = ut.trim(sys.exec("uci -p /var/state get network." .. arg[1] .. ".ifname")) + if interfaceDevice == "uci: Entry not found" or interfaceDevice == "" then + errorNetConfig = 1 + errorRoute = 1 + else + local routeCheck = ut.trim(sys.exec("route -n | awk '{if ($8 == \"" .. interfaceDevice .. "\" && $1 == \"0.0.0.0\" && $3 == \"0.0.0.0\") print $1}'")) + if routeCheck == "" then + errorRoute = 1 + end + end + else + errorNetConfig = 1 + errorRoute = 1 + end +end + +function interfaceWarnings() -- display warning messages at the top of the page + local warns, lineBreak = "", "" + if errorReliability == 1 then + warns = "WARNING: this interface has a higher reliability requirement than there are tracking IP addresses!" + lineBreak = "

                                          " + end + if errorRoute == 1 then + warns = warns .. lineBreak .. "WARNING: this interface has no default route in the main routing table!" + lineBreak = "

                                          " + end + if errorNetConfig == 1 then + warns = warns .. lineBreak .. "WARNING: this interface is configured incorrectly or not at all in /etc/config/network!" + lineBreak = "

                                          " + end + if errorNoMetric == 1 then + warns = warns .. lineBreak .. "WARNING: this interface has no metric configured in /etc/config/network!" + elseif errorDuplicateMetric == 1 then + warns = warns .. lineBreak .. "WARNING: this and other interfaces have duplicate metrics configured in /etc/config/network!" + end + return warns +end + +-- ------ interface configuration ------ -- + +dsp = require "luci.dispatcher" +sys = require "luci.sys" +ut = require "luci.util" +arg[1] = arg[1] or "" + +metricValue = "" +metricList = "" +errorDuplicateMetricList = "" +errorNoMetric = 0 +errorDuplicateMetric = 0 +errorRoute = 0 +errorNetConfig = 0 +errorReliability = 0 +interfaceCheck() + + +m5 = Map("mwan3", translate("MWAN Interface Configuration - " .. arg[1]), + translate(interfaceWarnings())) + m5.redirect = dsp.build_url("admin", "network", "mwan", "configuration", "interface") + + +mwan_interface = m5:section(NamedSection, arg[1], "interface", "") + mwan_interface.addremove = false + mwan_interface.dynamic = false + + +enabled = mwan_interface:option(ListValue, "enabled", translate("Enabled")) + enabled.default = "1" + enabled:value("1", translate("Yes")) + enabled:value("0", translate("No")) + +track_ip = mwan_interface:option(DynamicList, "track_ip", translate("Tracking IP"), + translate("This IP address will be pinged to dermine if the link is up or down. Leave blank to assume interface is always online")) + track_ip.datatype = "ipaddr" + +reliability = mwan_interface:option(Value, "reliability", translate("Tracking reliability"), + translate("Acceptable values: 1-100. This many Tracking IP addresses must respond for the link to be deemed up")) + reliability.datatype = "range(1, 100)" + reliability.default = "1" + +count = mwan_interface:option(ListValue, "count", translate("Ping count")) + count.default = "1" + count:value("1") + count:value("2") + count:value("3") + count:value("4") + count:value("5") + +timeout = mwan_interface:option(ListValue, "timeout", translate("Ping timeout")) + timeout.default = "2" + timeout:value("1", translate("1 second")) + timeout:value("2", translate("2 seconds")) + timeout:value("3", translate("3 seconds")) + timeout:value("4", translate("4 seconds")) + timeout:value("5", translate("5 seconds")) + timeout:value("6", translate("6 seconds")) + timeout:value("7", translate("7 seconds")) + timeout:value("8", translate("8 seconds")) + timeout:value("9", translate("9 seconds")) + timeout:value("10", translate("10 seconds")) + +interval = mwan_interface:option(ListValue, "interval", translate("Ping interval")) + interval.default = "5" + interval:value("1", translate("1 second")) + interval:value("3", translate("3 seconds")) + interval:value("5", translate("5 seconds")) + interval:value("10", translate("10 seconds")) + interval:value("20", translate("20 seconds")) + interval:value("30", translate("30 seconds")) + interval:value("60", translate("1 minute")) + interval:value("300", translate("5 minutes")) + interval:value("600", translate("10 minutes")) + interval:value("900", translate("15 minutes")) + interval:value("1800", translate("30 minutes")) + interval:value("3600", translate("1 hour")) + +down = mwan_interface:option(ListValue, "down", translate("Interface down"), + translate("Interface will be deemed down after this many failed ping tests")) + down.default = "3" + down:value("1") + down:value("2") + down:value("3") + down:value("4") + down:value("5") + down:value("6") + down:value("7") + down:value("8") + down:value("9") + down:value("10") + +up = mwan_interface:option(ListValue, "up", translate("Interface up"), + translate("Downed interface will be deemed up after this many successful ping tests")) + up.default = "3" + up:value("1") + up:value("2") + up:value("3") + up:value("4") + up:value("5") + up:value("6") + up:value("7") + up:value("8") + up:value("9") + up:value("10") + +dwnscript = mwan_interface:option(Value, "dwnscript", translate("Interface Down Script"), + translate("Script to run when Interface is deemed down")) + dwnscript.default = "/usr/lib/rooter/idown.lua" + +metric = mwan_interface:option(DummyValue, "metric", translate("Metric"), + translate("This displays the metric assigned to this interface in /etc/config/network")) + metric.rawhtml = true + function metric.cfgvalue(self, s) + if errorNoMetric == 0 then + return metricValue + else + return "—" + end + end + + +return m5 diff --git a/ext-rooterlite/files/usr/lib/lua/luci/view/admin_status/indexnew.htm b/ext-rooterlite/files/usr/lib/lua/luci/view/admin_status/indexnew.htm new file mode 100644 index 0000000..582fcb6 --- /dev/null +++ b/ext-rooterlite/files/usr/lib/lua/luci/view/admin_status/indexnew.htm @@ -0,0 +1,806 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008-2011 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +<% + local fs = require "nixio.fs" + local util = require "luci.util" + local stat = require "luci.tools.status" + local ver = require "luci.version" + + local has_ipv6 = fs.access("/proc/net/ipv6_route") + local has_dhcp = fs.access("/etc/config/dhcp") + local has_wifi = ((fs.stat("/etc/config/wireless", "size") or 0) > 0) + + local sysinfo = luci.util.ubus("system", "info") or { } + local boardinfo = luci.util.ubus("system", "board") or { } + local unameinfo = nixio.uname() or { } + + local meminfo = sysinfo.memory or { + total = 0, + free = 0, + buffered = 0, + shared = 0 + } + + local swapinfo = sysinfo.swap or { + total = 0, + free = 0 + } + + local has_dsl = fs.access("/etc/init.d/dsl_control") + + if luci.http.formvalue("status") == "1" then + local ntm = require "luci.model.network".init() + local wan = ntm:get_wannet() + local wan6 = ntm:get_wan6net() + + local conn_count = tonumber(( + luci.sys.exec("wc -l /proc/net/nf_conntrack") or + luci.sys.exec("wc -l /proc/net/ip_conntrack") or + ""):match("%d+")) or 0 + + local conn_max = tonumber(( + luci.sys.exec("sysctl net.nf_conntrack_max") or + luci.sys.exec("sysctl net.ipv4.netfilter.ip_conntrack_max") or + ""):match("%d+")) or 4096 + + local rv = { + uptime = sysinfo.uptime or 0, + localtime = os.date(), + loadavg = sysinfo.load or { 0, 0, 0 }, + memory = meminfo, + swap = swapinfo, + connmax = conn_max, + conncount = conn_count, + leases = stat.dhcp_leases(), + leases6 = stat.dhcp6_leases(), + wifinets = stat.wifi_networks() + } + + if wan then + rv.wan = { + ipaddr = wan:ipaddr(), + gwaddr = wan:gwaddr(), + netmask = wan:netmask(), + dns = wan:dnsaddrs(), + expires = wan:expires(), + uptime = wan:uptime(), + proto = wan:proto(), + ifname = wan:ifname(), + link = wan:adminlink() + } + end + + if wan6 then + rv.wan6 = { + ip6addr = wan6:ip6addr(), + gw6addr = wan6:gw6addr(), + dns = wan6:dns6addrs(), + uptime = wan6:uptime(), + ifname = wan6:ifname(), + link = wan6:adminlink() + } + end + + if has_dsl then + local dsl_stat = luci.sys.exec("/etc/init.d/dsl_control lucistat") + local dsl_func = loadstring(dsl_stat) + if dsl_func then + rv.dsl = dsl_func() + end + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) + + return + elseif luci.http.formvalue("hosts") == "1" then + luci.http.prepare_content("application/json") + luci.http.write_json(luci.sys.net.host_hints()) + + return + end +-%> + +<%+header%> + + + + +

                                          <%:Status%>

                                          + +
                                          + <%:System%> + + + + + + + + + +
                                          <%:Hostname%><%=luci.sys.hostname() or "?"%>
                                          <%:Model%><%=pcdata(boardinfo.model or boardinfo.system or "?")%>
                                          <%:Firmware Version%> + <%=pcdata(ver.distname)%> <%=pcdata(ver.distversion)%> / + <%=pcdata(ver.luciname)%> (<%=pcdata(ver.luciversion)%>) +
                                          <%:Kernel Version%><%=unameinfo.release or "?"%>
                                          <%:Local Time%>-
                                          <%:Uptime%>-
                                          <%:Load Average%>-
                                          +
                                          + +
                                          + <%:Memory%> + + + + + +
                                          <%:Total Available%>-
                                          <%:Free%>-
                                          <%:Buffered%>-
                                          +
                                          + +<% if swapinfo.total > 0 then %> +
                                          + <%:Swap%> + + + + +
                                          <%:Total Available%>-
                                          <%:Free%>-
                                          +
                                          +<% end %> + +
                                          + <%:Network%> + + + + <% if has_ipv6 then %> + + <% end %> + +
                                          <%:IPv4 WAN Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          <%:IPv6 WAN Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          <%:Active Connections%>-
                                          +
                                          + +<%- + include("rooter/external") +-%> + +<% if has_dhcp then %> +
                                          + <%:DHCP Leases%> + + + + + + + + + + + +
                                          <%:Hostname%><%:IPv4-Address%><%:MAC-Address%><%:Leasetime remaining%>

                                          <%:Collecting data...%>
                                          +
                                          + + +<% end %> + +<% if has_dsl then %> +
                                          + <%:DSL%> + + +
                                          <%:DSL Status%> + + + +

                                          ?
                                          <%:Collecting data...%>
                                          +
                                          +
                                          +<% end %> + +<% if has_wifi then %> +
                                          + <%:Wireless%> + + + +
                                          <%:Collecting data...%>
                                          +
                                          + +
                                          + <%:Associated Stations%> + + + + + + + + + + + + + +
                                           <%:Network%><%:MAC-Address%><%:Host%><%:Signal%> / <%:Noise%><%:RX Rate%> / <%:TX Rate%>

                                          <%:Collecting data...%>
                                          +
                                          +<% end %> + +<%- + local incdir = util.libpath() .. "/view/admin_status/index/" + if fs.access(incdir) then + local inc + for inc in fs.dir(incdir) do + if inc:match("%.htm$") then + include("admin_status/index/" .. inc:gsub("%.htm$", "")) + end + end + end +-%> + +<%+footer%> diff --git a/ext-rooterlite/files/usr/lib/lua/luci/view/rooter/external.htm b/ext-rooterlite/files/usr/lib/lua/luci/view/rooter/external.htm new file mode 100644 index 0000000..1feee41 --- /dev/null +++ b/ext-rooterlite/files/usr/lib/lua/luci/view/rooter/external.htm @@ -0,0 +1,26 @@ + + +
                                          + <%:External Internet IP Address%> + + +
                                          <%:IP Address%><%:Loading%> Collecting data...
                                          +
                                          + diff --git a/ext-rooterlite/files/usr/sbin/mwan3track b/ext-rooterlite/files/usr/sbin/mwan3track new file mode 100644 index 0000000..037c61e --- /dev/null +++ b/ext-rooterlite/files/usr/sbin/mwan3track @@ -0,0 +1,79 @@ +#!/bin/sh + +log() { + logger -t "MWan3Track " "$@" +} + +[ -z "$9" ] && echo "Error: should not be started manually" && exit 0 + +if [ -e /var/run/mwan3track-$1.pid ] ; then + kill $(cat /var/run/mwan3track-$1.pid) &> /dev/null + rm /var/run/mwan3track-$1.pid &> /dev/null +fi + +echo "$$" > /var/run/mwan3track-$1.pid + +score=$(($7+$8)) +track_ips=$(echo $* | cut -d ' ' -f 9-99) +host_up_count=0 +lost=0 + +while true; do + + for track_ip in $track_ips; do + ping -I $2 -c $4 -W $5 -s 4 -q $track_ip &> /dev/null + if [ $? -eq 0 ]; then + let host_up_count++ + else + let lost++ + fi + done + + if [ $host_up_count -lt $3 ]; then + let score-- + + if [ $score -lt $8 ]; then score=0 ; fi + if [ $score -eq $8 ]; then + + logger -t mwan3track -p notice "Interface $1 ($2) is offline" + SCR=$(uci get mwan3.$1.dwnscript) + if [ ! -z $SCR ]; then + if [ -e $SCR ]; then + $SCR "1" $1 $2 + fi + fi + env -i ACTION=ifdown INTERFACE=$1 DEVICE=$2 /sbin/hotplug-call iface + score=0 + fi + + else + + if [ $score -lt $(($7+$8)) ] && [ $lost -gt 0 ]; then + + logger -t mwan3track -p info "Lost $(($lost*$4)) ping(s) on interface $1 ($2)" + + fi + + let score++ + lost=0 + + if [ $score -gt $8 ]; then score=$(($7+$8)); fi + if [ $score -eq $8 ]; then + SCR=$(uci get mwan3.$1.dwnscript) + if [ ! -z $SCR ]; then + if [ -e $SCR ]; then + $SCR "2" $1 $2 + fi + fi + logger -t mwan3track -p notice "Interface $1 ($2) is online" + env -i ACTION=ifup INTERFACE=$1 DEVICE=$2 /sbin/hotplug-call iface + rm /var/run/mwan3track-$1.pid + exit 0 + fi + fi + + host_up_count=0 + sleep $6 +done + +exit 1 diff --git a/ext-samba/Makefile b/ext-samba/Makefile new file mode 100644 index 0000000..b790996 --- /dev/null +++ b/ext-samba/Makefile @@ -0,0 +1,38 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-samba +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-samba + SECTION:=utils + CATEGORY:=ROOter + SUBMENU:=Basic Applications + DEPENDS:=+luci-app-samba +ntfs-3g +fdisk +kmod-usb-storage +ext-hdidle \ + +kmod-fs-ext4 +kmod-fs-vfat +kmod-nls-cp437 +kmod-nls-iso8859-1 +kmod-nls-utf8 \ + +block-mount +kmod-fs-exfat +kmod-fs-hfs + TITLE:=Install Samba File Sharing + PKGARCH:=all +endef + +define Package/ext-samba/description + Helper scripts to install Samba File Sharing +endef + + +define Build/Compile +endef + +define Package/ext-samba/install + $(CP) ./files/* $(1)/ + + +endef + +$(eval $(call BuildPackage,ext-samba)) diff --git a/ext-samba/files/etc/hotplug.d/block/30-mount b/ext-samba/files/etc/hotplug.d/block/30-mount new file mode 100644 index 0000000..62b188b --- /dev/null +++ b/ext-samba/files/etc/hotplug.d/block/30-mount @@ -0,0 +1,58 @@ +#!/bin/sh /etc/rc.common +. /lib/functions.sh +# Copyright (C) 2011 OpenWrt.org + +log() { + logger -t "30-mount" "$@" +} + +sanitize() { + sed -e 's/[[:space:]]\+$//; s/[[:space:]]\+/_/g' "$@" +} + +if [ ! -f /tmp/bootend.file ]; then + sleep 10 + while [ ! -f /tmp/bootend.file ]; do + sleep 1 + done + log "Delay Block Mount for boot up" +fi + +blkdev=`dirname $DEVPATH` +if [ `basename $blkdev` != "block" ]; then + device=`basename $DEVPATH` + if echo $device | grep -q "mtdblock"; then + exit 0 + fi + if echo $device | grep -q "mmcblk"; then + exit 0 + fi + if echo $device | grep -q "CF_1GB"; then + exit 0 + fi + + case "$ACTION" in + add) + DEVN=${DEVNAME:0:3} + MODEL=$(sanitize "/sys/block/$DEVN/device/model") + if [ -d /etc/samba ]; then + uci delete samba.$device + uci set samba.$device=sambashare + uci set samba.$device.name=$MODEL$DEVNAME + uci set samba.$device.path=/mnt/$device + uci set samba.$device.read_only=no + uci set samba.$device.guest_ok=yes + uci commit samba + /etc/init.d/samba restart + log "/mnt/$device shared as $MODEL$DEVNAME" + fi + ;; + remove) + log "remove /mnt/$device" + if [ -d /etc/samba ]; then + uci delete samba.$device + uci commit samba + fi + ;; + esac +fi diff --git a/ext-sms/Makefile b/ext-sms/Makefile new file mode 100644 index 0000000..3315be2 --- /dev/null +++ b/ext-sms/Makefile @@ -0,0 +1,35 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-sms +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-sms + SECTION:=utils + CATEGORY:=ROOter + SUBMENU:=Basic Support + TITLE:=Install SMS support + PKGARCH:=all +endef + +define Package/ext-sms/description + Helper scripts to install SMS on ROOter +endef + + +define Build/Compile +endef + +define Package/ext-sms/install + $(CP) ./files/* $(1)/ + + +endef + +$(eval $(call BuildPackage,ext-sms)) diff --git a/ext-sms/files/usr/lib/lua/luci/controller/sms.lua b/ext-sms/files/usr/lib/lua/luci/controller/sms.lua new file mode 100644 index 0000000..82181b6 --- /dev/null +++ b/ext-sms/files/usr/lib/lua/luci/controller/sms.lua @@ -0,0 +1,149 @@ +module("luci.controller.sms", package.seeall) + +function index() + local page + page = entry({"admin", "modem", "sms"}, template("rooter/sms"), "SMS Messaging", 35) + page.dependent = true + + entry({"admin", "modem", "check_read"}, call("action_check_read")) + entry({"admin", "modem", "del_sms"}, call("action_del_sms")) + entry({"admin", "modem", "send_sms"}, call("action_send_sms")) + entry({"admin", "modem", "change_sms"}, call("action_change_sms")) + entry({"admin", "modem", "change_smsdn"}, call("action_change_smsdn")) + entry({"admin", "modem", "change_smsflag"}, call("action_change_smsflag")) +end + +function trim(s) + return (s:gsub("^%s*(.-)%s*$", "%1")) +end + +function action_send_sms() + if package.path:find(";/usr/lib/sms/?.lua") == nil then + package.path = package.path .. ";/usr/lib/sms/?.lua" + end + smsnum = luci.model.uci.cursor():get("modem", "general", "smsnum") + local set = luci.http.formvalue("set") + local number = trim(string.sub(set, 1, 20)) + local txt = string.sub(set, 21) + local utf8togsm = require "utf8togsm" + utf8togsm.chktxt(txt) + local msg = utf8togsm["msg"] + local dcs = utf8togsm["dcs"] + txt = utf8togsm["txt"] + local rv = {} + local file = nil + local k = 1 + local status + local rfname = "/tmp/smssendstatus0000" + if msg == nil then + os.execute("if [ -e " .. rfname .. " ]; then rm " ..rfname .. "; fi") + os.execute("lua /usr/lib/sms/sendsms.lua " .. smsnum .. " " .. number .. " " .. dcs .. " " .. txt .. " 0000") + os.execute("sleep 3") + repeat + file = io.open(rfname, "r") + if file == nil then + os.execute("sleep 1") + end + k = k + 1 + until k > 25 or file ~=nil + if file == nil then + status = 'Sending attempt timed out (fail)' + else + status = file:read("*line") + file:close() + os.remove (rfname) + end + else + status = msg + end + rv["status"] = status + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_del_sms() + local set = tonumber(luci.http.formvalue("set")) + if set ~= nil and set > 0 then + set = set - 1; + smsnum = luci.model.uci.cursor():get("modem", "general", "smsnum") + os.execute("/usr/lib/sms/delsms.sh " .. set .. " " .. smsnum) + end +end + +function action_check_read() + local rv ={} + local file + local line + smsnum = luci.model.uci.cursor():get("modem", "general", "smsnum") + conn = "Modem #" .. smsnum + rv["conntype"] = conn + support = luci.model.uci.cursor():get("modem", "modem" .. smsnum, "sms") + rv["ready"] = "0" + rv["menable"] = "0" + rv["mslots"] = "0" + if support == "1" then + rv["ready"] = "1" + result = "/tmp/smsresult" .. smsnum .. ".at" + file = io.open(result, "r") + if file ~= nil then + file:close() + os.execute("echo " .. smsnum .. " > /tmp/smsmodem") + file = io.open("/tmp/smstext" .. smsnum, "r") + if file == nil then + rv["ready"] = "3" + else + rv["menable"] = luci.model.uci.cursor():get("modem", "sms", "menable") + rv["mslots"] = luci.model.uci.cursor():get("modem", "sms", "slots") + rv["ready"] = "2" + local tmp = file:read("*line") + rv["used"] = tmp + tmp = file:read("*line") + rv["max"] = tmp + full = nil + + repeat + for j = 1, 4 do + line = file:read("*line") + if line ~= nil then + if j == 3 then + full = full .. string.char(29) + local i = tonumber(line) + for k = 1, i do + line = file:read("*line") + full = full .. line + if k < i then + full = full .. '
                                          ' + end + end + else + if full == nil then + full = line + else + full = full .. string.char(29) .. line + end + end + end + end + until line == nil + file:close() + rv["line"] = full + end + end + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_change_sms() + os.execute("/usr/lib/rooter/luci/modemchge.sh sms 1") +end + +function action_change_smsdn() + os.execute("/usr/lib/rooter/luci/modemchge.sh sms 0") +end + +function action_change_smsflag() + local set = tonumber(luci.http.formvalue("set")) + os.execute("/usr/lib/sms/toggle.sh " .. set) +end diff --git a/ext-sms/files/usr/lib/lua/luci/view/rooter/sms.htm b/ext-sms/files/usr/lib/lua/luci/view/rooter/sms.htm new file mode 100644 index 0000000..b550062 --- /dev/null +++ b/ext-sms/files/usr/lib/lua/luci/view/rooter/sms.htm @@ -0,0 +1,518 @@ +<%+header%> + + + + +
                                          +
                                          +

                                          SMS Messaging

                                          +
                                          Send and Receive Text Messages Through Your Modem
                                          +
                                          + Modem Information + + + + + + + +
                                            + + + +
                                            + Received Messages + + + + + + + + + + + + +
                                            Total SIM Message Slots
                                            Used SIM Message Slots
                                             
                                                 
                                                + + + + + + + + + + + + + + + + +
                                                 
                                                Enable SMS transfer from modem to SIM
                                                + +
                                                Unread SMS count on modem
                                                 
                                                   
                                                   
                                                  + + + + + + + + + + + +
                                                  Read
                                                  Sender
                                                  Date
                                                  Time
                                                  Message
                                                  + + + + + +
                                                  + +
                                                  + + + + + + + +
                                                  Message :
                                                     
                                                    + + + + + + + + +
                                                     
                                                    + + Send Messages + + + + + + + +
                                                    Send To :
                                                     
                                                    + + + + + +
                                                    + +
                                                    + + + + + + + +
                                                      
                                                    + + + + + + + +
                                                    Sending Status :
                                                       
                                                      + +
                                                      + +
                                                      +
                                                      +<%+footer%> diff --git a/ext-sms/files/usr/lib/sms/delsms.sh b/ext-sms/files/usr/lib/sms/delsms.sh new file mode 100644 index 0000000..d6a41e5 --- /dev/null +++ b/ext-sms/files/usr/lib/sms/delsms.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "Delete SMS" "$@" +} + +SLOT=$1 +CURRMODEM=$2 +COMMPORT="/dev/ttyUSB"$(uci get modem.modem$CURRMODEM.commport) + +LOCKDIR="/tmp/smslock$CURRMODEM" +PIDFILE="${LOCKDIR}/PID" + +ATCMDD="AT+CMGD=$SLOT" +OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + +while [ 1 -lt 6 ]; do + if mkdir "${LOCKDIR}" &>/dev/null; then + echo "$$" > "${PIDFILE}" + ATCMDD="AT+CPMS=\"SM\";+CMGD=$SLOT" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + ATCMDD="AT+CPMS=\"SM\"" + SX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + M2=$(echo "$SX" | sed -e "s/+CPMS:/+CPMS: /") + SX=$(echo "$M2" | sed -e "s/ / /g") + USED=$(echo "$SX" | awk -F[,\ ] '/^\+CPMS:/ {print $2}') + log "Reread SMS Messages on Modem $CURRMODEM" + echo "$SX" > /tmp/smstmp$CURRMODEM + ATCMDD="AT+CMGL=4" + SX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + SX=$(echo "$SX" | sed -e "s/+CMGL:/+CMGL: /g") + echo "$SX" >> /tmp/smstmp$CURRMODEM + uci set modem.modem$CURRMODEM.smsnum=$USED + uci commit modem + mv /tmp/smstmp$CURRMODEM /tmp/smsresult$CURRMODEM.at + lua /usr/lib/sms/smsread.lua $CURRMODEM + break + else + OTHERPID="$(cat "${PIDFILE}")" + if [ $? = 0 ]; then + if ! kill -0 $OTHERPID &>/dev/null; then + rm -rf "${LOCKDIR}" + fi + fi + sleep 1 + fi +done +rm -rf "${LOCKDIR}" diff --git a/ext-sms/files/usr/lib/sms/pack7bit.lua b/ext-sms/files/usr/lib/sms/pack7bit.lua new file mode 100644 index 0000000..bbd5661 --- /dev/null +++ b/ext-sms/files/usr/lib/sms/pack7bit.lua @@ -0,0 +1,79 @@ +local pack7bit = {} + +function hasbit(x, p) + return x % (p + p) >= p +end + +function bitand(x, y) + local p = 1; local z = 0; local limit = x > y and x or y + while p <= limit do + if hasbit(x, p) and hasbit(y, p) then + z = z + p + end + p = p + p + end + return z +end + +function bitor(x, y) + local p = 1; local z = 0; local limit = x > y and x or y + while p <= limit do + if hasbit(x, p) or hasbit(y, p) then + z = z + p + end + p = p + p + end + return z +end + +function bitright(x, y) + return math.floor(x / 2 ^ y) +end + +function bitleft(x, y) + return x * 2 ^ y +end + +function pack7bit.pack(udl, txt) + maxb = math.ceil((tonumber(udl, 16) / 8) * 7) + udtab = {} + ii = 1 + jj = 1 + kk = 0 + repeat + ch = tonumber(txt:sub(jj, jj + 1), 16) + if ii == 1 then + udtab[kk + 1] = ch + elseif ii == 2 then + udtab[kk] = bitor(bitleft(bitand(ch, 1), 7), udtab[kk]) + udtab[kk + 1] = bitright(bitand(ch, 126), 1) + elseif ii == 3 then + udtab[kk] = bitor(bitleft(bitand(ch, 3), 6), udtab[kk]) + udtab[kk + 1] = bitright(bitand(ch, 124), 2) + elseif ii == 4 then + udtab[kk] = bitor(bitleft(bitand(ch, 7), 5), udtab[kk]) + udtab[kk + 1] = bitright(bitand(ch, 120), 3) + elseif ii == 5 then + udtab[kk] = bitor(bitleft((bitand(ch, 15)), 4), udtab[kk]) + udtab[kk + 1] = bitright(bitand(ch, 112), 4) + elseif ii == 6 then + udtab[kk] = bitor(bitleft(bitand(ch, 31), 3), udtab[kk]) + udtab[kk + 1] = bitright(bitand(ch, 96), 5) + elseif ii == 7 then + udtab[kk] = bitor(bitleft(bitand(ch, 63), 2), udtab[kk]) + udtab[kk + 1] = bitright(bitand(ch, 64), 6) + else + udtab[kk] = bitor(bitleft(ch, 1), udtab[kk]) + ii = 0 + kk = kk - 1 + end + ii = ii + 1 + jj = jj + 2 + kk = kk + 1 + until jj > #txt + for jj = 1, maxb do + udtab[jj] = string.format("%02X", udtab[jj]) + end + pack7bit["pdu"] = table.concat(udtab) +end +return pack7bit diff --git a/ext-sms/files/usr/lib/sms/processsms b/ext-sms/files/usr/lib/sms/processsms new file mode 100644 index 0000000..2d1f757 --- /dev/null +++ b/ext-sms/files/usr/lib/sms/processsms @@ -0,0 +1,116 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "sms process" "$@" +} + +log "SMS Supported" + +CURRMODEM=$1 +COMMPORT="/dev/ttyUSB"$(uci get modem.modem$CURRMODEM.commport) + +LOCKDIR="/tmp/smslock$CURRMODEM" +PIDFILE="${LOCKDIR}/PID" +rm -rf "${LOCKDIR}" + +ATCMDD="AT+CMGF=0" +OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") +sleep 1 +ATCMDD="AT+CPMS=\"SM\",\"SM\",\"SM\"" +OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") +sleep 1 +uci set modem.modem$CURRMODEM.smsnum=999 +uci commit modem +ATCMDD="AT+CPMS=\"ME\"" +OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") +MESC=$(echo "$OX" | grep -o "+CPMS:.*" | awk -F, '{print $1}' | grep -o "[0-9]\{1,3\}") +METC=$(echo "$OX" | grep -o "+CPMS:.*" | awk -F, '{print $2}' | grep -o "[0-9]\{1,3\}") +MESLOT="0" +if [ "x$MESC" = "x" ]; then + MESC="0" +fi +if [ "x$METC" = "x" ]; then + METC="0" + MESC="0" +fi +uci set modem.sms.slots=$MESC +uci commit modem +sleep 1 + +rm -f /tmp/smsresult$CURRMODEM".at" +> /tmp/smsslots$CURRMODEM + +while true; do + SLEEP="20" + while true; do + if mkdir "${LOCKDIR}" &>/dev/null; then + echo "$$" > "${PIDFILE}" + ATCMDD="AT+CPMS=\"SM\"" + SX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + M2=$(echo "$SX" | sed -e "s/+CPMS:/+CPMS: /") + SX=$(echo "$M2" | sed -e "s/ / /g") + USED=$(echo "$SX" | awk -F[,\ ] '/^\+CPMS:/ {print $2}') + MAXED=$(echo "$SX" | awk -F[,\ ] '/^\+CPMS:/ {print $3}') + if [ $USED -eq $(uci get modem.modem$CURRMODEM.smsnum) ]; then + if [ $(uci get modem.sms.menable) -eq 1 ]; then + RSRV=2 + else + RSRV=99999 + fi + if [ $MESC -gt 0 ] && [ $(($USED + $RSRV)) -lt $MAXED ] && [ $MESLOT -lt $METC ]; then + ATCMDD="AT+CPMS=\"ME\";+CMGR=$MESLOT;+CPMS=\"SM\"" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + PDU=$(echo "$OX" | grep -o "[0-9A-F]\{30,\}") + PDUL=$(echo "$OX" | grep -o "+CMGR:.*" | grep -o ",[0-9]\{1,\}" | \ + grep -o "[0-9]\{1,3\}") + if [ ${#PDU} -gt 0 ] && [ ${#PDUL} -gt 0 ]; then + if [ ${#PDUL} -eq 2 ]; then + PDUL="0$PDUL" + fi + ATCMDD="$PDUL,SM,1,$PDU" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "smswrite.gcom" \ + "$CURRMODEM" "$ATCMDD") + MREF=$(echo "$OX" | grep -o "[0-9]\{1,3\}") + if [ ${#MREF} -gt 0 ]; then + echo "$MREF" >> /tmp/smsslots$CURRMODEM + ATCMDD="AT+CPMS=\"ME\";+CMGD=$MESLOT;+CPMS=\"SM\"" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" \ + "$CURRMODEM" "$ATCMDD") + fi + MESC=$(($MESC - 1)) + uci set modem.sms.slots=$MESC + uci commit modem + SLEEP="5" + fi + MESLOT=$(($MESLOT + 1)) + fi + else + log "Reread SMS Messages on Modem $CURRMODEM" + echo "$SX" > /tmp/smstemp$CURRMODEM + ATCMDD="AT+CMGL=4" + SX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "run-at.gcom" "$CURRMODEM" "$ATCMDD") + SX=$(echo "$SX" | sed -e "s/+CMGL:/+CMGL: /g") + echo "$SX" >> /tmp/smstemp$CURRMODEM + uci set modem.modem$CURRMODEM.smsnum=$USED + uci commit modem + mv /tmp/smstemp$CURRMODEM /tmp/smsresult$CURRMODEM.at + lua /usr/lib/sms/smsread.lua $CURRMODEM + SLEEP="5" + fi + break + else + OTHERPID="$(cat "${PIDFILE}")" + if [ $? = 0 ]; then + if ! kill -0 $OTHERPID &>/dev/null; then + rm -rf "${LOCKDIR}" + fi + fi + sleep 1 + fi + done + rm -rf "${LOCKDIR}" + sleep $SLEEP +done + diff --git a/ext-sms/files/usr/lib/sms/sendsms.lua b/ext-sms/files/usr/lib/sms/sendsms.lua new file mode 100644 index 0000000..baee895 --- /dev/null +++ b/ext-sms/files/usr/lib/sms/sendsms.lua @@ -0,0 +1,41 @@ +#!/usr/bin/lua + +modem = arg[1] +addr = arg[2] +dcs = arg[3] +txt = arg[4] +suffix = arg[5] + +if package.path:find(";/usr/lib/sms/?.lua") == nil then + package.path = package.path .. ";/usr/lib/sms/?.lua" +end + +local pack7bit = require "pack7bit" + +udl = string.format("%02X", math.floor(#txt / 2)) +da = "81" +if addr:sub(1, 1) == "+" then + da = "91" + addr = addr:sub(2) +elseif addr:sub(1, 1) == "-" then + addr = addr:sub(2) +end +da = string.format("%02X", #addr) .. da +if (#addr % 2) > 0 then + addr = addr .. "F" +end +k = #addr +j = 1 +repeat + da = da .. addr:sub(j + 1, j + 1) .. addr:sub(j, j) + j = j + 2 +until j > k +if dcs == "00" then + pack7bit.pack(udl, txt) + ud = pack7bit["pdu"] +else + ud = txt +end +pdu = "001100" .. da .. "00" .. dcs .. "AD" .. udl .. ud +pdul = string.format("%03d", (math.floor(#pdu / 2) - 1)) +os.execute("/usr/lib/sms/sendsms.sh " .. modem .. " " .. pdul .. "," .. suffix .. "," .. pdu .. " &") diff --git a/ext-sms/files/usr/lib/sms/sendsms.sh b/ext-sms/files/usr/lib/sms/sendsms.sh new file mode 100644 index 0000000..fad0e9c --- /dev/null +++ b/ext-sms/files/usr/lib/sms/sendsms.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +CURRMODEM=$1 +ATCMDD=$2 + +COMMPORT="/dev/ttyUSB"$(uci get modem.modem$CURRMODEM.commport) + +OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "sendsms-at.gcom" "$CURRMODEM" "$ATCMDD") diff --git a/ext-sms/files/usr/lib/sms/smsout.lua b/ext-sms/files/usr/lib/sms/smsout.lua new file mode 100644 index 0000000..3237ca5 --- /dev/null +++ b/ext-sms/files/usr/lib/sms/smsout.lua @@ -0,0 +1,24 @@ +#!/usr/bin/lua + +local modem = arg[1] +local dest = arg[2] +local txt = arg[3] +local pid = arg[4] +local rfname = "/tmp/smssendstatus" .. pid + +if package.path:find(";/usr/lib/sms/?.lua") == nil then + package.path = package.path .. ";/usr/lib/sms/?.lua" +end + +local utf8togsm = require "utf8togsm" +utf8togsm.chktxt(txt) +local msg = utf8togsm["msg"] +local dcs = utf8togsm["dcs"] +txt = utf8togsm["txt"] + +if msg == nil then + os.execute("if [ -e " .. rfname .. " ]; then rm " ..rfname .. "; fi") + os.execute("lua /usr/lib/sms/sendsms.lua " .. modem .. " " .. dest .. " " .. dcs .. " " .. txt .. " " .. pid) +else + os.execute('echo "SMS sending failed - text is too long" > ' .. rfname) +end diff --git a/ext-sms/files/usr/lib/sms/smsout.sh b/ext-sms/files/usr/lib/sms/smsout.sh new file mode 100644 index 0000000..7801263 --- /dev/null +++ b/ext-sms/files/usr/lib/sms/smsout.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "sms process" "$@" +} + +if [ -e /tmp/smsmodem ]; then + read CURRMODEM < /tmp/smsmodem +else + CURRMODEM=$(uci get modem.general.modemnum) +fi + +DEST="$1" +shift 1 +TXT="$@" +MYPID=$(printf "%05d" $$) +MYPID=$(printf "${MYPID:1:4}") +RESFILE="/tmp/smssendstatus"$MYPID + +lua /usr/lib/sms/smsout.lua $CURRMODEM $DEST "$TXT" $MYPID +sleep 5 +COUNT=0 +XSTATUS=1 +RES="SMS sending failed, timeout reading result" +while [ $COUNT -lt 15 ]; do + if [ -e $RESFILE ]; then + read RES < $RESFILE + if [ "${RES:0:9}" = "SMS sent," ]; then + XSTATUS=0 + fi + COUNT=999 + rm $RESFILE + else + sleep 2 + fi + COUNT=$(($COUNT + 1)) +done +log "$RES" +echo "$RES" +exit $XSTATUS diff --git a/ext-sms/files/usr/lib/sms/smsread.lua b/ext-sms/files/usr/lib/sms/smsread.lua new file mode 100644 index 0000000..4176191 --- /dev/null +++ b/ext-sms/files/usr/lib/sms/smsread.lua @@ -0,0 +1,797 @@ +#!/usr/bin/lua + +modemn = arg[1] + +local smsresult = "/tmp/smsresult" .. modemn .. ".at" +local smsslots = "/tmp/smsslots" .. modemn +local t = {} +local tptr +local m_pdu_ptr +local m_pdu +local m_smsc +local m_with_udh +local m_report +local m_number +local m_replace +local m_alphabet +local m_flash +local m_date +local m_time +local m_text +local m_concat +local m_read +local m_index +local g_table1 = {} +local g_table2 = {} + +local max_smsc = 64 +local max_number = 64 +local max_udh_data = 512 + +function reset() + m_smsc = nil + m_with_udh = 0 + m_report = 0 + m_number = nil + m_replace = 0 + m_alphabet = -1 + m_flash = 0 + m_date = nil + m_time = nil + m_text = nil + m_concat = nil +end + +function hasbit(x, p) + return x % (p + p) >= p +end + +function bitor(x, y) + local p = 1; local z = 0; local limit = x > y and x or y + while p <= limit do + if hasbit(x, p) or hasbit(y, p) then + z = z + p + end + p = p + p + end + return z +end + +function bitand(x, y) + local p = 1; local z = 0; local limit = x > y and x or y + while p <= limit do + if hasbit(x, p) and hasbit(y, p) then + z = z + p + end + p = p + p + end + return z +end + +function bitright(x, y) + return math.floor(x / 2 ^ y) +end + +function bitleft(x, y) + return x * 2 ^ y +end + +function ocount(x, y) + local j = 0 + local i = x:find(y, 0) + while i ~= nil do + i = x:find(y, i + 1) + j = j + 1 + end + return j +end + +printf = function(s,...) + if echo == 0 then + io.write(s:format(...)) + else + ss = s:format(...) + os.execute("/usr/lib/rooter/logprint.sh " .. ss) + end +end + +function isxdigit(digit) + if digit == nil then + return 0 + end + if digit >= 48 and digit <= 57 then + return 1 + end + if digit >= 97 and digit <= 102 then + return 1 + end + if digit >= 65 and digit <= 70 then + return 1 + end + return 0 +end + +function isdigit(digit) + if digit >= 48 and digit <= 57 then + return 1 + end + return 0 +end + +function octet2bin(octet) + result = 0 + if octet:byte(1) > 57 then + result = result + octet:byte(1) - 55 + else + result = result + octet:byte(1) - 48 + end + result = result * 16 + if octet:byte(2) > 57 then + result = result + octet:byte(2) - 55 + else + result = result + octet:byte(2) - 48 + end + return result +end + +function octet2bin_check(octet) + if octet:byte(1) == 0 then + return -1 + end + if octet:byte(2) == 0 then + return -2 + end + if isxdigit(octet:byte(1)) == 0 then + return -3 + end + if isxdigit(octet:byte(2)) == 0 then + return -4 + end + return octet2bin(octet) +end + +function swapchars(sstring) + local length = sstring:len() + local xstring = nil + local i = 1 + while i < length do + c1 = sstring:sub(i, i) + c2 = sstring:sub(i+1, i+1) + if xstring == nil then + xstring = c2 .. c1 + else + xstring = xstring .. c2 .. c1 + end + i = i + 2 + end + return xstring +end + +function parseSMSC() + m_pdu_ptr = m_pdu + local length = octet2bin_check(m_pdu_ptr) + if length < 0 then + return -1 + end + if length == 0 then + m_smsc = "" + m_pdu_ptr = m_pdu_ptr:sub(3) + return 1 + end + if length < 2 or length > max_smsc then + return -1 + end + length = (length * 2) - 2 + local mlen = m_pdu:len() + if mlen < (length + 4) then + return -1 + end + m_pdu_ptr = m_pdu_ptr:sub(3) + local addr_type = octet2bin_check(m_pdu_ptr) + if addr_type < 0 then + return -1 + end + if addr_type < 0x80 then + return -1 + end + m_pdu_ptr = m_pdu_ptr:sub(3) + m_smsc = m_pdu_ptr:sub(0, length) + m_smsc = swapchars(m_smsc) + if addr_type < 0x90 then + for j=1, length do + if isxdigit(m_smsc:byte(j)) == 0 then + return -1 + end + end + else + if m_smsc:byte(length) == 70 then + m_smsc = m_smsc:sub(1, length-1) + end + local leng = m_smsc:len() + for j=1,leng do + if isdigit(m_smsc:byte(j)) == 0 then + return -1 + end + end + end + m_pdu_ptr = m_pdu_ptr:sub(length + 1) + return 1 +end + +function explainAddressType(octet_char, octet_int) + local result + if octet_char ~= nil then + result = octet2bin_check(octet_char) + else + result = octet_int + end + return result +end + +function concatinfo(x) + if x:sub(3, 6) == '0003' and x:sub(9, 12) ~= '0101' then + m_concat = 'Msg# ' .. tonumber(x:sub(7, 8), 16) .. ',' .. tonumber(x:sub(11, 12), 16) + m_concat = m_concat .. '/' .. tonumber(x:sub(9, 10), 16) + elseif x:sub(3, 6) == '0804' and x:sub(11, 14) ~= '0101' then + m_concat = 'Msg# ' .. tonumber(x:sub(7, 10), 16) .. ',' .. tonumber(x:sub(13, 14), 16) + m_concat = m_concat .. '/' .. tonumber(x:sub(11, 12), 16) + end +end + +function pdu2binary(pdu, with_udh) + local skip_octets = 0 + local octetcounter + m_text = '' + local octets = octet2bin_check(pdu) + local last_i = 0 + if octets < 0 then + return -1 + end + if with_udh > 0 then + local pdu2 = pdu:sub(3) + concatinfo(pdu2:sub(1, 14)) + local udhsize = octet2bin_check(pdu2) + if udhsize < 0 then + return -1 + end + skip_octets = udhsize + 1 + end + for octetcounter = 0, (octets - skip_octets - 1) do + local pdu2 = pdu:sub((octetcounter * 2) + 3 + (skip_octets * 2)) + local i = octet2bin_check(pdu2) + if i < 0 then + return -1 + end + if m_alphabet == 2 then + if (2 + octetcounter) % 2 == 0 then + last_i = i + else + m_text = byte2utf8(bitor(bitleft(last_i, 8), i), m_text) + end + else + if i < 32 or i > 127 then + i = 63 + end + m_text = byte2utf8(i, m_text) + end + end + return (octets - skip_octets) +end + +function pdu2text(pdu, with_udh) + local result + local octetcounter + local skip_characters = 0 + local binary = 0 + m_text = '' + local septets = octet2bin_check(pdu) + if septets < 0 then + return -1 + end + if with_udh > 0 then + local pdu2 = pdu:sub(3) + concatinfo(pdu2:sub(1, 14)) + local udhsize = octet2bin_check(pdu2) + skip_characters = math.floor((((udhsize+1)*8)+6)/7) + end + local octets = math.floor((septets * 7 + 7) / 8) + local bitposition = 0 + local byteposition + local byteoffset + local i + local g_table_nbr = 1 + octetcounter = 0 + for charcounter=0,septets-1 do + local c = 0 + for bitcounter=0,6 do + byteposition = math.floor(bitposition / 8) + byteoffset = bitposition % 8 + while (byteposition >= octetcounter) and (octetcounter < octets) do + local pdu2 = pdu:sub((octetcounter * 2) + 3) + i = octet2bin_check(pdu2) + if i < 0 then + return -2 + end + binary = i + octetcounter = octetcounter + 1 + end + if bitand(binary, (2^byteoffset)) > 0 then + c = bitor(c, 128) + end + bitposition = bitposition + 1 + c = bitand(math.floor(c / 2), 127) + end + c = gsm2byte(c) + if charcounter >= skip_characters and c ~= 27 then + m_text = byte2utf8(c, m_text) + end + end + return 1 +end + +function gsm2byte(ch) + if g_table_nbr == 2 then + if g_table2[ch] == nil then + ch = 63 + else + ch = g_table2[ch] + end + g_table_nbr = 1 + else + if ch == 27 then + g_table_nbr = 2 + else + if g_table1[ch] ~= nil then + ch = g_table1[ch] + end + end + end + return ch +end + +function byte2utf8(i, txt) + if i < 0x80 then + txt = txt .. string.char(i) + elseif i < 0x800 then + txt = txt .. string.char(bitor(0xC0, bitright(bitand(i, 0x7C0), 6))) + txt = txt .. string.char(bitor(0x80, bitand(i, 0x3F))) + elseif i <= 0xFFFF then + if i == 0x2029 then + txt = txt .. string.char(10) + else + txt = txt .. string.char(bitor(0xE0, bitright(bitand(i, 0xF000), 12))) + txt = txt .. string.char(bitor(0x80, bitright(bitand(i, 0xFC0), 6))) + txt = txt .. string.char(bitor(0x80, bitand(i, 0x3F))) + end + else + txt = txt .. '?' + end + return txt +end + +function parseDeliver() + if m_pdu_ptr:len() < 4 then + return 0 + end + local padding = 0 + local length = octet2bin_check(m_pdu_ptr) + local timezone + if length < 0 or length > max_number then + return 0 + end +-- Sender Address + if length == 0 then + m_pdu_ptr = m_pdu_ptr:sub(5) + else + padding = length % 2 + m_pdu_ptr = m_pdu_ptr:sub(3) + local addr_type = explainAddressType(m_pdu_ptr, 0) + if addr_type < 0 then + return 0 + end + if addr_type < 0x80 then + return 0 + end + m_pdu_ptr = m_pdu_ptr:sub(3) + if bitand(addr_type, 112) == 80 then + if m_pdu_ptr:len() < (length + padding) then + return 0 + end + local htmp = string.format("%x", math.floor((length * 4) / 7)) + if htmp:len() < 2 then + htmp = "0" .. htmp + end + htmp = htmp:upper() + local tpdu = htmp .. m_pdu_ptr + local res = pdu2text(tpdu, 0) + if res < 0 then + return 0 + end + m_number = string.gsub(m_text, "\n", " ") + m_text = nil + else + m_number = m_pdu_ptr:sub(1, length + padding + 1) + m_number = swapchars(m_number) + if m_number:byte(length + padding) == 70 then + m_number = m_number:sub(1, length + padding - 1) + end + if addr_type == 145 then + m_number = '+' .. m_number + end + end + end + m_pdu_ptr = m_pdu_ptr:sub(length + padding + 1) + if m_pdu_ptr:len() < 20 then + return 0 + end +-- PID + local byte_buf = octet2bin_check(m_pdu_ptr) + if byte_buf < 0 then + return 0 + end + if bitand(byte_buf, 0xF8) == 0x40 then + m_replace = bitand(byte_buf, 0x07) + end + m_pdu_ptr = m_pdu_ptr:sub(3) +-- Alphabet + byte_buf = octet2bin_check(m_pdu_ptr) + if byte_buf < 0 then + return 0 + end + m_alphabet = math.floor(bitand(byte_buf, 0x0C) / 4) + if m_alphabet == 3 then + return 0 + end + if m_alphabet == 0 then + m_alphabet = -1 + end +-- Flash Msg + if bitand(byte_buf, 0x10) > 0 then + if bitand(byte_buf, 0x01) > 0 then + m_flash = 1 + end + end + m_pdu_ptr = m_pdu_ptr:sub(3) +-- Date + local str_buf = m_pdu_ptr:sub(2,2) .. m_pdu_ptr:sub(1,1) .. "-" .. m_pdu_ptr:sub(4,4) .. m_pdu_ptr:sub(3,3) .. "-" .. m_pdu_ptr:sub(6,6) .. m_pdu_ptr:sub(5,5) + if (not isdigit(m_pdu_ptr:byte(1))) or (not isdigit(m_pdu_ptr:byte(2))) or (not isdigit(m_pdu_ptr:byte(3))) or (not isdigit(m_pdu_ptr:byte(4))) or (not isdigit(m_pdu_ptr:byte(5))) or (not isdigit(m_pdu_ptr:byte(6))) then + return 0 + end + m_date = str_buf + m_pdu_ptr = m_pdu_ptr:sub(7) +-- Time + str_buf = m_pdu_ptr:sub(2,2) .. m_pdu_ptr:sub(1,1) .. ":" .. m_pdu_ptr:sub(4,4) .. m_pdu_ptr:sub(3,3) .. ":" .. m_pdu_ptr:sub(6,6) .. m_pdu_ptr:sub(5,5) + if (not isdigit(m_pdu_ptr:byte(1))) or (not isdigit(m_pdu_ptr:byte(2))) or (not isdigit(m_pdu_ptr:byte(3))) or (not isdigit(m_pdu_ptr:byte(4))) or (not isdigit(m_pdu_ptr:byte(5))) or (not isdigit(m_pdu_ptr:byte(6))) then + return 0 + end + if tonumber(m_pdu_ptr:sub(8,8), 16) > 7 then + timezone = '-' .. ((tonumber(m_pdu_ptr:sub(8, 8), 16) - 8) .. tonumber(m_pdu_ptr:sub(7, 7), 16)) / 4 + else + timezone = '+' .. (m_pdu_ptr:sub(8, 8) .. tonumber(m_pdu_ptr:sub(7, 7), 16)) / 4 + end + if timezone:sub(-2, -1) == '.0' then + timezone = timezone:sub(1, -3) + end + m_time = str_buf .. ' ' .. timezone .. 'h' + m_pdu_ptr = m_pdu_ptr:sub(7) + if octet2bin_check(m_pdu_ptr) < 0 then + return 0 + end + m_pdu_ptr = m_pdu_ptr:sub(3) + +-- Text + local result = 0 + local bin_udh = 1 + if m_alphabet <= 0 then + result = pdu2text(m_pdu_ptr, m_with_udh) + return result + else + result = pdu2binary(m_pdu_ptr, m_with_udh) + return result + end + return 1 +end + +function parseStatusReport() + if m_pdu_ptr:len() < 6 then + return 0 + end + local messageid = octet2bin_check(m_pdu_ptr) + if messageid < 0 then + return 0 + end + m_pdu_ptr = m_pdu_ptr:sub(3) + local length = octet2bin_check(m_pdu_ptr) + if length < 1 or length > max_number then + return 0 + end + local padding = length % 2 + m_pdu_ptr = m_pdu_ptr:sub(3) + local addr_type = explainAddressType(m_pdu_ptr, 0) + if addr_type < 0x80 then + return 0 + end + m_pdu_ptr = m_pdu_ptr:sub(3) + if bitand(addr_type, 112) == 80 then + if m_pdu_ptr:len() < (length + padding) then + return 0 + end + local htmp = string.format("%x", math.floor((length * 4) / 7)) + if htmp:len() < 2 then + htmp = "0" .. htmp + end + local tpdu = htmp .. m_pdu_ptr + local res = pdu2text(tpdu, 0) + if res < 0 then + return 0 + end + m_number = m_text + m_text = nil + else + m_number = m_pdu_ptr:sub(1, length + padding + 1) + m_number = swapchars(m_number) + if m_number:byte(length + padding) == 70 then + m_number = m_number:sub(1, length + padding - 1) + end + end + m_pdu_ptr = m_pdu_ptr:sub(length + padding + 1) + if m_pdu_ptr:len() < 14 then + return 0 + end +-- Date + local str_buf = m_pdu_ptr:sub(2,2) .. m_pdu_ptr:sub(1,1) .. "-" .. m_pdu_ptr:sub(4,4) .. m_pdu_ptr:sub(3,3) .. "-" .. m_pdu_ptr:sub(6,6) .. m_pdu_ptr:sub(5,5) + if (not isdigit(m_pdu_ptr:byte(1))) or (not isdigit(m_pdu_ptr:byte(2))) or (not isdigit(m_pdu_ptr:byte(3))) or (not isdigit(m_pdu_ptr:byte(4))) or (not isdigit(m_pdu_ptr:byte(5))) or (not isdigit(m_pdu_ptr:byte(6))) then + return 0 + end + m_date = str_buf + m_pdu_ptr = m_pdu_ptr:sub(7) +-- Time + str_buf = m_pdu_ptr:sub(2,2) .. m_pdu_ptr:sub(1,1) .. ":" .. m_pdu_ptr:sub(4,4) .. m_pdu_ptr:sub(3,3) .. ":" .. m_pdu_ptr:sub(6,6) .. m_pdu_ptr:sub(5,5) + if (not isdigit(m_pdu_ptr:byte(1))) or (not isdigit(m_pdu_ptr:byte(2))) or (not isdigit(m_pdu_ptr:byte(3))) or (not isdigit(m_pdu_ptr:byte(4))) or (not isdigit(m_pdu_ptr:byte(5))) or (not isdigit(m_pdu_ptr:byte(6))) then + return 0 + end + m_time = str_buf + m_pdu_ptr = m_pdu_ptr:sub(7) + if octet2bin_check(m_pdu_ptr) < 0 then + return 0 + end + m_pdu_ptr = m_pdu_ptr:sub(3) +-- Discharge Date + local str_buf = m_pdu_ptr:sub(2,2) .. m_pdu_ptr:sub(1,1) .. "-" .. m_pdu_ptr:sub(4,4) .. m_pdu_ptr:sub(3,3) .. "-" .. m_pdu_ptr:sub(6,6) .. m_pdu_ptr:sub(5,5) + if (not isdigit(m_pdu_ptr:byte(1))) or (not isdigit(m_pdu_ptr:byte(2))) or (not isdigit(m_pdu_ptr:byte(3))) or (not isdigit(m_pdu_ptr:byte(4))) or (not isdigit(m_pdu_ptr:byte(5))) or (not isdigit(m_pdu_ptr:byte(6))) then + return 0 + end + local d_date = str_buf + m_pdu_ptr = m_pdu_ptr:sub(7) +-- Time + str_buf = m_pdu_ptr:sub(2,2) .. m_pdu_ptr:sub(1,1) .. ":" .. m_pdu_ptr:sub(4,4) .. m_pdu_ptr:sub(3,3) .. ":" .. m_pdu_ptr:sub(6,6) .. m_pdu_ptr:sub(5,5) + if (not isdigit(m_pdu_ptr:byte(1))) or (not isdigit(m_pdu_ptr:byte(2))) or (not isdigit(m_pdu_ptr:byte(3))) or (not isdigit(m_pdu_ptr:byte(4))) or (not isdigit(m_pdu_ptr:byte(5))) or (not isdigit(m_pdu_ptr:byte(6))) then + return 0 + end + local d_time = str_buf + m_pdu_ptr = m_pdu_ptr:sub(7) + if octet2bin_check(m_pdu_ptr) < 0 then + return 0 + end + m_pdu_ptr = m_pdu_ptr:sub(3) + local status = octet2bin_check(m_pdu_ptr) + if status < 0 then + return 0 + end + m_text = string.format("Discharge Timestamp: %s %s Message ID: %d Status: %d", d_date, d_time, messageid, status) + return 1 +end + +function parse() + local flag = parseSMSC() + if flag ~= 1 then + return 0 + end + local tmp = octet2bin_check(m_pdu_ptr) + if tmp < 0 then + return 0 + end + if bitand(tmp, 0x40) > 0 then + m_with_udh = 1 + end + if bitand(tmp, 0x20) > 0 then + m_report = 1 + end + local type = bitand(tmp, 3) + if type == 0 then + m_pdu_ptr = m_pdu_ptr:sub(3) + local result = parseDeliver() + if result < 1 then + return 0 + end + else + if type == 2 then + m_pdu_ptr = m_pdu_ptr:sub(3) + local result = parseStatusReport() + return result + else + return 0 + end + end + return 1 +end + +function trim(s) + return (s:gsub("^%s*(.-)%s*$", "%1")) +end + +function readpdu(pdu) + m_pdu = pdu + m_pdu_ptr = m_pdu + reset() + local flag = parse() + if flag > 0 then + t[tptr] = m_index + t[tptr+1] = m_read + t[tptr+2] = m_number + t[tptr+3] = m_date + t[tptr+4] = m_time + if m_concat ~= nil then + m_text = m_concat .. '\n' .. m_text + end + t[tptr+5] = m_text + tptr = tptr + 6 + end +end + +local max_msg = "0" +local used_msg = "0" +tptr = 3 +t[1] = used_msg +t[2] = max_msg +g_table1 = {163, 36, 165, 232, 233, 249, 236, 242, 199, 10, 216, 248, 13, 197, 229, 0x394, 95, 0x3A6, 0x393, 0x39B, 0x3A9, 0x3A0, 0x3A8, 0x3A3, 0x398, 0x39E} +g_table1[0] = 64 +g_table1[28] = 198 +g_table1[29] = 230 +g_table1[30] = 223 +g_table1[31] = 201 +g_table1[36] = 164 +g_table1[64] = 161 +g_table1[91] = 196 +g_table1[92] = 214 +g_table1[93] = 209 +g_table1[94] = 220 +g_table1[95] = 167 +g_table1[96] = 191 +g_table1[123] = 228 +g_table1[124] = 246 +g_table1[125] = 241 +g_table1[126] = 252 +g_table1[127] = 224 +g_table2[20] = 94 +g_table2[40] = 123 +g_table2[41] = 125 +g_table2[47] = 92 +g_table2[60] = 91 +g_table2[61] = 126 +g_table2[62] = 93 +g_table2[64] = 124 +g_table2[101] = 0x20AC +-- +os.execute("touch " .. smsslots) +local slottab = {} +local file = io.open(smsslots, "r") +for k in file:lines() do + slottab[k] = true +end +file:close() +local file = io.open(smsresult, "r") +local m_r = "" +if file ~= nil then + repeat + local s, e, cs, ce, ms, me + local line = file:read("*l") + if line == nil then + break + end + s, e = line:find("+CPMS:") + if s ~= nil then + cs, ce = line:find(",", e) + if cs ~= nil then + used_msg = trim(line:sub(e+1, cs-1)) + t[1] = used_msg + ms, me = line:find(",", ce+1) + if ms ~= nil then + max_msg = trim(line:sub(ce+1, ms-1)) + t[2] = max_msg + end + end + line = file:read("*l") + if line == nil then + break + end + end + s, e = line:find("+CMGL:") + if s ~= nil then + m_index = "0" + cs, ce = line:find(",", e) + if cs ~= nil then + m_index = trim(line:sub(e+1, cs-1)) + end + ds, de = line:find(",", ce+1) + if ds ~= nil then + m_r = trim(line:sub(ce+1, ds-1)) + if m_r == "0" then + m_read = byte2utf8(0x2691, byte2utf8(0x2691, '')) + if not slottab[m_index] then + os.execute("echo " .. m_index .. " >> " .. smsslots) + end + elseif slottab[m_index] then + m_read = byte2utf8(0x2691, ' ') + else + m_read = byte2utf8(0x2713, ' ') + end + else + break + end + line = file:read("*l") + if line == nil then + break + end + readpdu(line) + if m_r == "0" then + if m_text == "::reboot!!" then + os.execute("(sleep 60; reboot -f) &") + elseif m_text == "::pwrtoggle!!" then + os.execute("(sleep 60; /usr/lib/rooter/pwrtoggle.sh 3) &") + end + end + end + until 1==0 + file:close() +end + +local tfname = "/tmp/smstemptext" .. math.random(99) +local tfile = io.open(tfname, "w") +tfile:write(t[1] .. "\n") +tfile:write(t[2] .. "\n") +if tonumber(used_msg) == 0 then + tfile:close() +else + i = 3 + while t[i] ~= nil do + local mtxt = t[i + 5] + tfile:write(t[i] .. "\n") + tfile:write(t[i + 2] .. "\n") + tfile:write((ocount(mtxt, '\n') + 1) .. '\n') + tfile:write(mtxt .. "\n") + local mn = t[i + 2] .. " " + mn = mn:sub(1,20) + local stxt = '' + local j = 0 + local k = 1 + local ch = '' + while j < 20 do + ch = string.byte(mtxt:sub(k, k)) + if ch == nil then + j = 20 + elseif ch == 10 or ch == 13 then + stxt = stxt .. ' ' + k = k + 1 + elseif ch < 127 then + stxt = stxt .. string.char(ch) + k = k + 1 + elseif ch < 0xE0 then + stxt = stxt .. mtxt:sub(k, k + 1) + k = k + 2 + else + stxt = stxt .. mtxt:sub(k, k + 2) + k = k + 3 + end + j = j + 1 + end + if mtxt ~= stxt then + stxt = stxt .. " ..." + end + local msg = t[i + 1] .. " " .. mn .. t[i + 3] .. " " .. t[i + 4] .. " " .. stxt + tfile:write(msg .. "\n") + i = i + 6 + end + tfile:close() +end +os.execute("mv " .. tfname .. " /tmp/smstext" .. modemn) diff --git a/ext-sms/files/usr/lib/sms/sys2sms.lua b/ext-sms/files/usr/lib/sms/sys2sms.lua new file mode 100644 index 0000000..60ee220 --- /dev/null +++ b/ext-sms/files/usr/lib/sms/sys2sms.lua @@ -0,0 +1,79 @@ +#!/usr/bin/lua + +local oaddr = arg[1] +local txt = arg[2] +local pid = arg[3] + +if package.path:find(";/usr/lib/sms/?.lua") == nil then + package.path = package.path .. ";/usr/lib/sms/?.lua" +end + +local utf8togsm = require "utf8togsm" +local pack7bit = require "pack7bit" + +local isok = true + +if #txt == 0 then + txt = "Usage: /tmp/lib/sms/sys2sms.sh 'from' 'text to write here'" +end + +utf8togsm.chktxt(txt) +local msg = utf8togsm["msg"] +local dcs = utf8togsm["dcs"] +local ud = utf8togsm["txt"] + +local udl = string.format("%02X", math.floor(#ud / 2)) + +if msg ~= nil then + isok = false +end + +if isok and dcs == "00" then + pack7bit.pack(udl, ud) + ud = pack7bit["pdu"] +end +if #oaddr == 0 or oaddr == ' ' then + oaddr = "ROOter" +elseif #oaddr > 11 then + oaddr = oaddr:sub(1, 11) +end +if oaddr:sub(-1) == ' ' then + oaddr = oaddr:sub(1, -2) +end +local oaddrl = #oaddr * 2 +if oaddrl > 14 then + oaddrl = oaddrl - 2 +elseif oaddrl > 6 then + oaddrl = oaddrl - 1 +end +oaddrl = string.format("%02X", oaddrl) + +utf8togsm.chktxt(oaddr) +oaddr = utf8togsm["txt"] + +if utf8togsm["dcs"] == "08" then + isok = false +end + +pack7bit.pack(string.format("%02X", math.floor(#oaddr / 2)), oaddr) +oaddr = pack7bit["pdu"] + +dtg = os.date("%y%m%d%H%M%S%z", os.time()) +sign = dtg:sub(13, 13) +tz = dtg:sub(-4) +dtgif = '' +for j = 1, 11, 2 do + dtgif = dtgif .. dtg:sub(j + 1, j + 1) .. dtg:sub(j, j) +end +tz = string.format("%02d", math.floor((tonumber(tz:sub(1, 2) * 4)) + tonumber(tz:sub(3, 4) / 15))) +tz = tz:sub(2, 2) .. tz:sub(1, 1) +if sign == "-" then + tz = tz:sub(1, 1) .. string.format("%X", (tonumber(tz:sub(2, 2)) + 8)) +end + +pdu = "0004" .. oaddrl .. "D0" .. oaddr .. "00" .. dcs .. dtgif .. tz .. udl .. ud + +if isok then + pdul = string.format("%03d", (math.floor(#pdu / 2) - 1)) + os.execute("echo " .. pdul .. " " .. pdu .. " > /tmp/pdu" .. pid) +end diff --git a/ext-sms/files/usr/lib/sms/sys2sms.sh b/ext-sms/files/usr/lib/sms/sys2sms.sh new file mode 100644 index 0000000..41ab018 --- /dev/null +++ b/ext-sms/files/usr/lib/sms/sys2sms.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +ROOTER=/usr/lib/rooter + +log() { + logger -t "sms process" "$@" +} + +ADDR="$1" +shift 1 +TXT="$@" +MYPID=$(printf "%05d" $$) +RESFILE="/tmp/pdu"$MYPID + +if [ -e /tmp/smsmodem ]; then + read CURRMODEM < /tmp/smsmodem +else + CURRMODEM=$(uci get modem.general.modemnum) +fi + +COMMPORT="/dev/ttyUSB"$(uci get modem.modem$CURRMODEM.commport) + +RES="" +XSTATUS=0 + +lua /usr/lib/sms/sys2sms.lua "$ADDR" "$TXT" $MYPID + +if [ -e $RESFILE ]; then + read PDUL PDU < $RESFILE + rm $RESFILE +else + RES="Failed to write SMS - is text too long?" + XSTATUS=1 + PDUL="" + PDU="" +fi + +LOCKDIR="/tmp/smslock$CURRMODEM" +PIDFILE="${LOCKDIR}/PID" + +ATCMDD="$PDUL,SM,0,$PDU" + +while [ $XSTATUS -eq 0 ]; do + if mkdir "${LOCKDIR}" &>/dev/null; then + echo "$$" > "${PIDFILE}" + OX=$($ROOTER/gcom/gcom-locked "$COMMPORT" "smswrite.gcom" "$CURRMODEM" "$ATCMDD") + RES=$(echo "$OX" | grep "+CMGW:") + if [ ${#RES} -eq 0 ]; then + RES="Failed to write SMS - is SIM full?" + XSTATUS=1 + else + RES="New SMS written successfully" + fi + rm -rf "${LOCKDIR}" + break + else + OTHERPID="$(cat "${PIDFILE}")" + if [ $? = 0 ]; then + if ! kill -0 $OTHERPID &>/dev/null; then + rm -rf "${LOCKDIR}" + fi + fi + sleep 1 + fi +done + +log "$RES" +echo "$RES" +exit $XSTATUS diff --git a/ext-sms/files/usr/lib/sms/toggle.sh b/ext-sms/files/usr/lib/sms/toggle.sh new file mode 100644 index 0000000..4c32f74 --- /dev/null +++ b/ext-sms/files/usr/lib/sms/toggle.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +SET=$1 + +uci set modem.sms.menable=$SET +uci commit modem + diff --git a/ext-sms/files/usr/lib/sms/utf8togsm.lua b/ext-sms/files/usr/lib/sms/utf8togsm.lua new file mode 100644 index 0000000..7b72719 --- /dev/null +++ b/ext-sms/files/usr/lib/sms/utf8togsm.lua @@ -0,0 +1,155 @@ +local utf8togsm = {} + +function hasbit(x, p) + return x % (p + p) >= p +end + +function bitand(x, y) + local p = 1; local z = 0; local limit = x > y and x or y + while p <= limit do + if hasbit(x, p) and hasbit(y, p) then + z = z + p + end + p = p + p + end + return z +end + +function bitor(x, y) + local p = 1; local z = 0; local limit = x > y and x or y + while p <= limit do + if hasbit(x, p) or hasbit(y, p) then + z = z + p + end + p = p + p + end + return z +end + +function bitleft(x, y) + return x * 2 ^ y +end + +function utf8togsm.chktxt(txt) + local g7t = {} + g7t[64] = "00" + g7t[163] = "01" + g7t[36] = "02" + g7t[165] = "03" + g7t[232] = "04" + g7t[233] = "05" + g7t[249] = "06" + g7t[236] = "07" + g7t[242] = "08" + g7t[199] = "09" + g7t[216] = "0B" + g7t[248] = "0C" + g7t[197] = "0E" + g7t[229] = "0F" + g7t[0x394] = "10" + g7t[95] = "11" + g7t[0x3A6] = "12" + g7t[0x393] = "13" + g7t[0x39B] = "14" + g7t[0x3A9] = "15" + g7t[0x3A0] = "16" + g7t[0x3A8] = "17" + g7t[0x3A3] = "18" + g7t[0x398] = "19" + g7t[0x39E] = "1A" + g7t[198] = "1C" + g7t[230] = "1D" + g7t[223] = "1E" + g7t[201] = "1F" + g7t[164] = "24" + g7t[161] = "40" + g7t[196] = "5B" + g7t[214] = "5C" + g7t[209] = "5D" + g7t[220] = "5E" + g7t[167] = "5F" + g7t[191] = "60" + g7t[228] = "7B" + g7t[246] = "7C" + g7t[241] = "7D" + g7t[252] = "7E" + g7t[224] = "7F" + g7t[94] = "1B14" + g7t[123] = "1B28" + g7t[125] = "1B29" + g7t[92] = "1B2F" + g7t[91] = "1B3C" + g7t[126] = "1B3D" + g7t[93] = "1B3E" + g7t[124] = "1B40" + g7t[0x20AC] = "1B65" + g7t[96] = "27" + local unicode = '' + local g7hex = '' + local g7isok = true + local j = #txt + local res = nil + local msg = nil + local dcs = "" + local k = 1 + repeat + ch = string.byte(txt, k, k) + if ch >= 0xF0 then + g7hex = g7hex .. '3F' + unicode = unicode .. '003F' + k = k + 3 + elseif ch >= 0xE0 then + ch = bitleft(bitand(ch, 0xF), 12) + ch = bitor(bitleft(bitand(string.byte(txt, k + 1, k + 1), 0x3F), 6), ch) + ch = bitor(bitand(string.byte(txt, k + 2, k + 2), 0x3F), ch) + res = g7t[ch] + if res == nil then + g7isok = false + else + g7hex = g7hex .. res + end + unicode = unicode .. string.format("%04X", ch) + k = k + 2 + elseif ch >= 0xC0 then + ch = bitleft(bitand(ch, 0x3F), 6) + ch = bitor(bitand(string.byte(txt, k + 1, k + 1), 0x3F), ch) + res = g7t[ch] + if res == nil then + g7isok = false + else + g7hex = g7hex .. res + end + unicode = unicode .. string.format("%04X", ch) + k = k + 1 + elseif ch <= 0x7F then + res = g7t[ch] + if res == nil then + g7hex = g7hex .. string.format("%02X", ch) + else + g7hex = g7hex .. res + end + unicode = unicode .. string.format("%04X", ch) + else + g7hex = g7hex .. '3F' + unicode = unicode .. '003F' + end + k = k + 1 + until k > j + if g7isok and #g7hex <= 320 then + dcs = "00" + txt = g7hex + elseif g7isok then + msg = 'Processed text length = ' .. math.floor(#g7hex / 2) .. ' 7-bit characters.\n' + msg = msg .. 'Currently ROOter supports 160 maximum per message.' + elseif #unicode <= 280 then + dcs = "08" + txt = unicode + else + msg = 'Processed text length = ' .. math.floor(#unicode / 4) .. ' 16-bit Unicode characters.\n' + msg = msg .. 'Currently ROOter supports 70 maximum per message.' + end + utf8togsm["msg"] = msg + utf8togsm["dcs"] = dcs + utf8togsm["txt"] = txt +end +return utf8togsm diff --git a/ext-umount/Makefile b/ext-umount/Makefile new file mode 100644 index 0000000..36feb30 --- /dev/null +++ b/ext-umount/Makefile @@ -0,0 +1,33 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-umount +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-umount + SECTION:=utils + CATEGORY:=ROOter + SUBMENU:=Basic Applications + TITLE:=support for drive unmount + PKGARCH:=all +endef + +define Package/ext-umount/description + Helper scripts to enable drive unmount +endef + + +define Build/Compile +endef + +define Package/ext-umount/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,ext-umount)) diff --git a/ext-umount/files/etc/config/umount b/ext-umount/files/etc/config/umount new file mode 100644 index 0000000..ca8f428 --- /dev/null +++ b/ext-umount/files/etc/config/umount @@ -0,0 +1,2 @@ +config umount 'umount' + option drive '' diff --git a/ext-umount/files/etc/umount b/ext-umount/files/etc/umount new file mode 100644 index 0000000..e0e8660 --- /dev/null +++ b/ext-umount/files/etc/umount @@ -0,0 +1,17 @@ +#!/bin/sh + +log() { + logger -t "Umount" "$@" +} + +DRV=$(uci get umount.umount.drive) +if [ -z $DRV ]; then + exit 0 +fi + +umount -l /mnt/$DRV + +rm -rf /mnt/$DRV + +uci set umount.umount.drive='' +uci commit umount diff --git a/ext-umount/files/usr/lib/lua/luci/controller/umount.lua b/ext-umount/files/usr/lib/lua/luci/controller/umount.lua new file mode 100644 index 0000000..dbc085c --- /dev/null +++ b/ext-umount/files/usr/lib/lua/luci/controller/umount.lua @@ -0,0 +1,8 @@ +module("luci.controller.umount", package.seeall) + +function index() + local page + + page = entry({"admin", "services", "umount"}, cbi("umount", {hidesavebtn=true, hideresetbtn=true}), "Safely Eject Drive", 25) + page.dependent = true +end diff --git a/ext-umount/files/usr/lib/lua/luci/model/cbi/umount.lua b/ext-umount/files/usr/lib/lua/luci/model/cbi/umount.lua new file mode 100644 index 0000000..2266746 --- /dev/null +++ b/ext-umount/files/usr/lib/lua/luci/model/cbi/umount.lua @@ -0,0 +1,21 @@ +require("nixio.fs") + +m = Map("umount", "Safely Eject a Drive", + translate("Safely eject a drive from the router")) + +m.on_after_commit = function(self) + luci.sys.call("/etc/umount") + luci.http.redirect(luci.dispatcher.build_url("admin/services/umount")) +end + +drv = m:section(TypedSection, "umount", translate("Currently Mounted Drives")) +drv.anonymous = true + +disk = drv:option(Value, "drive", translate(" "), translate("Click Save and Apply to eject drive")) +disk.rmempty = true +for dev in nixio.fs.glob("/mnt/[sh]d[a-z][1-9]") do + dv = nixio.fs.basename(dev) + disk:value(nixio.fs.basename(dev)) +end + +return m \ No newline at end of file diff --git a/ext-wifi/Makefile b/ext-wifi/Makefile new file mode 100644 index 0000000..eb8421e --- /dev/null +++ b/ext-wifi/Makefile @@ -0,0 +1,32 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=ext-wifi +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/ext-wifi + SECTION:=utils + CATEGORY:=ROOter + SUBMENU:=Wifi + DEPENDS:=+kmod-rt2500-usb +kmod-rt2800-usb +kmod-rt73-usb +kmod-rtl8187 \ + +kmod-rtl8192cu +kmod-rtl8xxxu +kmod-net-rtl8192su + TITLE:=support for wifi + PKGARCH:=all +endef + +define Package/ext-wifi/description + Helper scripts to enable wifi +endef + + +define Build/Compile +endef + + +$(eval $(call BuildPackage,ext-wifi)) diff --git a/luci-app-dnsmasq-ipset/Makefile b/luci-app-dnsmasq-ipset/Makefile new file mode 100644 index 0000000..e6383bd --- /dev/null +++ b/luci-app-dnsmasq-ipset/Makefile @@ -0,0 +1,34 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-dnsmasq-ipset +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/luci-app-dnsmasq-ipset + SECTION:=luci + CATEGORY:=LuCI + DEPENDS:=+dnsmasq-full + SUBMENU:=3. Applications + TITLE:=support for IP Set settings + PKGARCH:=all +endef + +define Package/luci-app-dnsmasq-ipset/description + Helper scripts for IP Set settings +endef + + +define Build/Compile +endef + +define Package/luci-app-dnsmasq-ipset/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,luci-app-dnsmasq-ipset)) diff --git a/luci-app-dnsmasq-ipset/files/etc/config/dnsmasq-ipset b/luci-app-dnsmasq-ipset/files/etc/config/dnsmasq-ipset new file mode 100644 index 0000000..83ec840 --- /dev/null +++ b/luci-app-dnsmasq-ipset/files/etc/config/dnsmasq-ipset @@ -0,0 +1,5 @@ +config ipsets + option ipset_name 'netflix' + option enabled '0' + list managed_domain 'netflix.com' + list managed_domain 'netflix.ca' \ No newline at end of file diff --git a/luci-app-dnsmasq-ipset/files/etc/init.d/dnsmasq-ipset b/luci-app-dnsmasq-ipset/files/etc/init.d/dnsmasq-ipset new file mode 100644 index 0000000..1bef5b9 --- /dev/null +++ b/luci-app-dnsmasq-ipset/files/etc/init.d/dnsmasq-ipset @@ -0,0 +1,45 @@ +#!/bin/sh /etc/rc.common +# Author Qier LU + +START=55 + +DNSMASQ="/etc/dnsmasq.conf" + +gen_config_file() { + local section="${1}" + config_get ipset_name "${section}" "ipset_name" + sname="#$ipset_name-1" + ename="#$ipset_name-2" + sed -i "/$sname/,/$ename/d" "$DNSMASQ" + config_get enabled "${section}" "enabled" + if [ ! ${enabled} ] + then + return + fi + + handle_domain() { + local domain="${1}" + ipline=$ipline"${domain}/" + } + + ipline="ipset=/" + echo "#$ipset_name-1" >> "$DNSMASQ" + config_list_foreach "${section}" "managed_domain" handle_domain + echo "$ipline${ipset_name}" >> "$DNSMASQ" + echo "#$ipset_name-2" >> "$DNSMASQ" + ipset -N -exist "$ipset_name" iphash + /etc/init.d/dnsmasq restart +} + +gen_config_files() { + config_load 'dnsmasq-ipset' + config_foreach gen_config_file 'ipsets' +} + +start() { + gen_config_files +} + +reload() { + gen_config_files +} diff --git a/luci-app-dnsmasq-ipset/files/usr/lib/lua/luci/controller/dnsmasq-ipset.lua b/luci-app-dnsmasq-ipset/files/usr/lib/lua/luci/controller/dnsmasq-ipset.lua new file mode 100644 index 0000000..d0a49b0 --- /dev/null +++ b/luci-app-dnsmasq-ipset/files/usr/lib/lua/luci/controller/dnsmasq-ipset.lua @@ -0,0 +1,12 @@ + +-- Auther Qier LU + +module("luci.controller.dnsmasq-ipset", package.seeall) + +function index() + if not nixio.fs.access("/etc/config/dnsmasq-ipset") then + return + end + + entry({"admin", "network", "dnsmasq-ipset"}, cbi("dnsmasq-ipset"), _("DNSmasq IP-Set"), 60).dependent = true +end diff --git a/luci-app-dnsmasq-ipset/files/usr/lib/lua/luci/model/cbi/dnsmasq-ipset.lua b/luci-app-dnsmasq-ipset/files/usr/lib/lua/luci/model/cbi/dnsmasq-ipset.lua new file mode 100644 index 0000000..125c050 --- /dev/null +++ b/luci-app-dnsmasq-ipset/files/usr/lib/lua/luci/model/cbi/dnsmasq-ipset.lua @@ -0,0 +1,24 @@ +-- Auther Qier LU + +local m, s, o, p + +m = Map("dnsmasq-ipset", translate("DNSmasq IPSet"), translate("IPSet lists for DNSMasq-full")) + +m.on_after_commit = function(self) + luci.sys.call("/etc/init.d/dnsmasq-ipset reload") +end + +s = m:section(TypedSection, "ipsets", translate("IPSet Lists")) +s.anonymous = true +s.addremove = true + +o = s:option(Value, "ipset_name", translate("IPSet Name")) +o.placeholder = "target ipset" +o.default = "rooter" +o.rmempty = false + +o = s:option(Flag, "enabled", translate("Enabled")) + +o = s:option(DynamicList, "managed_domain", translate("Managed Domain List")) + +return m diff --git a/luci-app-hotspot/Makefile b/luci-app-hotspot/Makefile new file mode 100644 index 0000000..eedaaf3 --- /dev/null +++ b/luci-app-hotspot/Makefile @@ -0,0 +1,34 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-hotspot +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/luci-app-hotspot + SECTION:=luci + CATEGORY:=LuCI + DEPENDS:=+iw +iwinfo + SUBMENU:=3. Applications + TITLE:=support for Wifi Hotspot Manager + PKGARCH:=all +endef + +define Package/luci-app-hotspot/description + Helper scripts to enable Wifi Hotspot Manager +endef + + +define Build/Compile +endef + +define Package/luci-app-hotspot/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,luci-app-hotspot)) diff --git a/luci-app-hotspot/files/etc/config/travelmate b/luci-app-hotspot/files/etc/config/travelmate new file mode 100644 index 0000000..c3e68ae --- /dev/null +++ b/luci-app-hotspot/files/etc/config/travelmate @@ -0,0 +1,10 @@ +# travelmate configuration, for further information +# see 'https://github.com/openwrt/packages/blob/master/net/travelmate/files/README.md' + +config travelmate 'global' + option trm_enabled '0' + option trm_debug '1' + option trm_maxwait '20' + option trm_maxretry '3' + option trm_iw '1' + option trm_auto '1' diff --git a/luci-app-hotspot/files/etc/hotplug.d/iface/99-travelmate-iface b/luci-app-hotspot/files/etc/hotplug.d/iface/99-travelmate-iface new file mode 100644 index 0000000..829b058 --- /dev/null +++ b/luci-app-hotspot/files/etc/hotplug.d/iface/99-travelmate-iface @@ -0,0 +1,26 @@ +#!/bin/sh + +if [ "$ACTION" = ifup -a "$INTERFACE" = "wwan" ]; then + exit 1 +fi + +if [ "$ACTION" = ifdown -a "$INTERFACE" = "wwan" ]; then + uci -q set wireless.wwan.ssid="Disconnected" + uci -q set wireless.wwan.encryption="none" + uci -q set wireless.wwan.key= + uci -q set wireless.wwan.disabled=1 + uci -q commit wireless + logger -t TRAVELMATE-DEBUG "Network Reload" + ubus call network reload + debug="$(uci -q get travelmate.global.trm_debug)" + if [ $debug = "1" ]; then + logger -t TRAVELMATE-DEBUG "hotplug (iface): action='$ACTION' interface='$INTERFACE'" + fi + result=`ps | grep -i "travelmate.sh" | grep -v "grep" | wc -l` + if [ $result -ge 1 ] + then + logger -t TRAVELMATE-DEBUG "Travelmate already running" + else + /usr/lib/hotspot/travelmate.sh & + fi +fi \ No newline at end of file diff --git a/luci-app-hotspot/files/etc/init.d/travelmate b/luci-app-hotspot/files/etc/init.d/travelmate new file mode 100644 index 0000000..06d5b01 --- /dev/null +++ b/luci-app-hotspot/files/etc/init.d/travelmate @@ -0,0 +1,38 @@ +#!/bin/sh /etc/rc.common + +. /lib/functions.sh + +START=98 + +log() { + logger -t "Hotspot Initialize" "$@" +} + +do_zone() { + local config=$1 + local name + local network + + config_get name $1 name + config_get network $1 network + newnet=$network + if [ $name = wan ]; then + WWAN=$(echo $network | grep "wwan") + if [ -z $WWAN ]; then + newnet="$newnet wwan" + uci_set firewall "$config" network "$newnet" + uci_commit firewall + /etc/init.d/firewall restart + fi + fi +} + +start() +{ +log "Hotspot Firewall" + + config_load firewall + config_foreach do_zone zone +} + + diff --git a/luci-app-hotspot/files/etc/init.d/zhot b/luci-app-hotspot/files/etc/init.d/zhot new file mode 100644 index 0000000..389bdaa --- /dev/null +++ b/luci-app-hotspot/files/etc/init.d/zhot @@ -0,0 +1,22 @@ +#!/bin/sh /etc/rc.common + +. /lib/functions.sh + +START=99 + +log() { + logger -t "Z-Hotspot " "$@" +} + +start() +{ + PRO=$(uci get network.wwan.proto) + if [ -z $PRO ]; then + uci set network.wwan=interface + uci set network.wwan.proto=dhcp + uci set network.wwan.metric="2" + uci commit network + fi + + /usr/lib/hotspot/travelmate.sh & +} diff --git a/luci-app-hotspot/files/lib/upgrade/keep.d/hotspot b/luci-app-hotspot/files/lib/upgrade/keep.d/hotspot new file mode 100644 index 0000000..5ed46ef --- /dev/null +++ b/luci-app-hotspot/files/lib/upgrade/keep.d/hotspot @@ -0,0 +1 @@ +/etc/hotspot diff --git a/luci-app-hotspot/files/usr/lib/hotspot/band.sh b/luci-app-hotspot/files/usr/lib/hotspot/band.sh new file mode 100644 index 0000000..0854378 --- /dev/null +++ b/luci-app-hotspot/files/usr/lib/hotspot/band.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +log() { + logger -t "band change" "$@" +} + +BAND=$1 + +if [ $BAND = "1" ]; then + WW=$(uci get travelmate.global.radio24) +else + WW=$(uci get travelmate.global.radio5) +fi + +uci set wireless.wwan.device=$WW +uci set wireless.wwan.ssid="Changing Wifi Radio" +uci set wireless.wwan.encryption="none" +uci set wireless.wwan.disabled="1" +uci commit wireless +wifi +result=`ps | grep -i "travelmate.sh" | grep -v "grep" | wc -l` +if [ $result -ge 1 ] +then + logger -t TRAVELMATE-DEBUG "Travelmate already running" +else + /usr/lib/hotspot/travelmate.sh & +fi \ No newline at end of file diff --git a/luci-app-hotspot/files/usr/lib/hotspot/copyhot.sh b/luci-app-hotspot/files/usr/lib/hotspot/copyhot.sh new file mode 100644 index 0000000..83d87ef --- /dev/null +++ b/luci-app-hotspot/files/usr/lib/hotspot/copyhot.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ -f "/tmp/hot" ]; then + rm -f /etc/hotspot + while IFS='|' read -r ssid encrypt key flag + do + echo $ssid"|"$encrypt"|"$key >> /etc/hotspot + done <"/tmp/hot" +fi diff --git a/luci-app-hotspot/files/usr/lib/hotspot/dis_hot.sh b/luci-app-hotspot/files/usr/lib/hotspot/dis_hot.sh new file mode 100644 index 0000000..57f333d --- /dev/null +++ b/luci-app-hotspot/files/usr/lib/hotspot/dis_hot.sh @@ -0,0 +1,9 @@ +#!/bin/sh +. /lib/functions.sh + +rm -f /tmp/hotman +uci -q set wireless.wwan.ssid="Disconnected" +uci -q set wireless.wwan.disabled=1 +uci -q commit wireless +ifdown wwan +ubus call network reload diff --git a/luci-app-hotspot/files/usr/lib/hotspot/enable.sh b/luci-app-hotspot/files/usr/lib/hotspot/enable.sh new file mode 100644 index 0000000..9cc2bdc --- /dev/null +++ b/luci-app-hotspot/files/usr/lib/hotspot/enable.sh @@ -0,0 +1,22 @@ +#!/bin/sh +. /lib/functions.sh + +SET=$1 + +uci set travelmate.global.trm_enabled=$SET +uci commit travelmate + +if [ $SET = "1" ]; then + AU=$(uci get travelmate.global.trm_auto) + killall -9 travelmate.sh + if [ $AU = "1" ]; then + uci -q set wireless.wwan.ssid="Checking for Connection" + uci -q set wireless.wwan.encryption="none" + uci -q set wireless.wwan.key= + uci -q commit wireless + /usr/lib/hotspot/travelmate.sh & + fi +else + killall -9 travelmate.sh + /usr/lib/hotspot/dis_hot.sh +fi \ No newline at end of file diff --git a/luci-app-hotspot/files/usr/lib/hotspot/getssid.sh b/luci-app-hotspot/files/usr/lib/hotspot/getssid.sh new file mode 100644 index 0000000..015d958 --- /dev/null +++ b/luci-app-hotspot/files/usr/lib/hotspot/getssid.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +RADIO=$(uci get wireless.wwan.device) +if [ $RADIO = "radio0" ]; then + ap_list="$(ubus -S call network.wireless status | jsonfilter -e '@.radio0.interfaces[@.config.mode="ap"].ifname')" +else + if [ $RADIO = "radio1" ]; then + ap_list="$(ubus -S call network.wireless status | jsonfilter -e '@.radio1.interfaces[@.config.mode="ap"].ifname')" + fi +fi + +rm -f /tmp/ssidlist +for ap in ${ap_list} +do + iwinfo "${ap}" scan >> /tmp/ssidlist +done diff --git a/luci-app-hotspot/files/usr/lib/hotspot/inrange.sh b/luci-app-hotspot/files/usr/lib/hotspot/inrange.sh new file mode 100644 index 0000000..905fb51 --- /dev/null +++ b/luci-app-hotspot/files/usr/lib/hotspot/inrange.sh @@ -0,0 +1,64 @@ +#!/bin/sh +. "/lib/functions.sh" + +log() { + logger -t "In Range" "$@" +} + +rm -f /tmp/hot1 +cnt=0 +trm_ifstatus="false" +while [ ${cnt} -lt 20 ] +do + trm_ifstatus="$(ubus -S call network.wireless status | jsonfilter -l1 -e '@.*.up')" + if [ "${trm_ifstatus}" = "true" ] + then + break + fi + cnt=$((cnt+1)) + sleep 1 +done + +RADIO=$(uci get wireless.wwan.device) +if [ $RADIO = "radio0" ]; then + ap_list="$(ubus -S call network.wireless status | jsonfilter -e '@.radio0.interfaces[@.config.mode="ap"].ifname')" +else + if [ $RADIO = "radio1" ]; then + ap_list="$(ubus -S call network.wireless status | jsonfilter -e '@.radio1.interfaces[@.config.mode="ap"].ifname')" + fi +fi + +trm_scanner="$(which iw)" +for ap in ${ap_list} +do + ssid_list="$(${trm_scanner} dev "${ap}" scan 2>/dev/null > /tmp/scan + cat /tmp/scan | awk '/SSID: /{if(!seen[$0]++){printf "\"";for(i=2; i<=NF; i++)if(i==2)printf $i;else printf " "$i;printf "\" "}}')" + + if [ -n "${ssid_list}" ] + then + if [ -f "/etc/hotspot" ]; then + while IFS='|' read -r ssid encrypt key + do + ssidq="\"$ssid\"" + if [ -n "$(printf "${ssid_list}" | grep -Fo "${ssidq}")" ] + then + echo $ssid"|"$encrypt"|"$key"|1" >> /tmp/hot1 + else + echo $ssid"|"$encrypt"|"$key"|0" >> /tmp/hot1 + fi + done <"/etc/hotspot" + fi + else + if [ -f "/etc/hotspot" ]; then + while IFS='|' read -r ssid encrypt key + do + echo $ssid"|"$encrypt"|"$key"|0" >> /tmp/hot1 + done <"/etc/hotspot" + fi + fi +done +if [ -f "/tmp/hot1" ]; then + mv -f /tmp/hot1 /tmp/hot +fi + + diff --git a/luci-app-hotspot/files/usr/lib/hotspot/manual.sh b/luci-app-hotspot/files/usr/lib/hotspot/manual.sh new file mode 100644 index 0000000..9b833df --- /dev/null +++ b/luci-app-hotspot/files/usr/lib/hotspot/manual.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +MAN="$1" + +killall -9 travelmate.sh + +/usr/lib/hotspot/dis_hot.sh +echo "$MAN" > /tmp/hotman +/usr/lib/hotspot/travelmate.sh & + diff --git a/luci-app-hotspot/files/usr/lib/hotspot/mode.sh b/luci-app-hotspot/files/usr/lib/hotspot/mode.sh new file mode 100644 index 0000000..d3235a3 --- /dev/null +++ b/luci-app-hotspot/files/usr/lib/hotspot/mode.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +SET=$1 + +rm -f /tmp/hotman +uci set travelmate.global.trm_auto=$SET +uci commit travelmate + +if [ $SET = "1" ]; then + result=`ps | grep -i "travelmate.sh" | grep -v "grep" | wc -l` + if [ $result -ge 1 ] + then + logger -t TRAVELMATE-DEBUG "Travelmate already running" + else + /usr/lib/hotspot/travelmate.sh & + fi +else + killall -9 travelmate.sh +fi \ No newline at end of file diff --git a/luci-app-hotspot/files/usr/lib/hotspot/travelmate.sh b/luci-app-hotspot/files/usr/lib/hotspot/travelmate.sh new file mode 100644 index 0000000..9250478 --- /dev/null +++ b/luci-app-hotspot/files/usr/lib/hotspot/travelmate.sh @@ -0,0 +1,306 @@ +#!/bin/sh + +. /lib/functions.sh + +# travelmate, a wlan connection manager for travel router +# written by Dirk Brenken (dev@brenken.org) + +# This is free software, licensed under the GNU General Public License v3. +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# prepare environment +# +LC_ALL=C +PATH="/usr/sbin:/usr/bin:/sbin:/bin" +trm_ver="0.3.0" +trm_enabled=1 +trm_debug=0 +trm_maxwait=20 +trm_maxretry=3 +trm_iw=1 +trm_auto=0 + +RADIO="radio0" + +do_radio() { + local config=$1 + local channel + + config_get channel $1 channel + if [ $channel -lt 15 ]; then + RADIO=$config + fi +} + +check_wwan() { + while [ ! -e /etc/config/wireless ] + do + sleep 1 + done + sleep 3 + WW=$(uci get wireless.wwan) + if [ -z $WW ]; then + config_load wireless + config_foreach do_radio wifi-device + uci set wireless.wwan=wifi-iface + uci set wireless.wwan.device=$RADIO + uci set wireless.wwan.network="wwan" + uci set wireless.wwan.mode="sta" + uci set wireless.wwan.ssid="No Connection" + uci set wireless.wwan.encryption="none" + uci set wireless.wwan.disabled="1" + uci commit wireless + f_log "info " "status ::: Hotspot Manager restarting Wifi and Network" + #wifi down + wifi up + /etc/init.d/network restart + fi +} + +do_radio24() { + local config=$1 + local channel + + config_get channel $1 channel + if [ $channel -gt 15 ]; then + uci set travelmate.global.radio5=$config + else + uci set travelmate.global.radio24=$config + fi +} + +check_radio() { + WW=$(uci get travelmate.global.radio24) + if [ -z $WW ]; then + config_load wireless + config_foreach do_radio24 wifi-device + uci commit travelmate + fi +} +f_envload() +{ + # source required system libraries + # + if [ -r "/lib/functions.sh" ] + then + . "/lib/functions.sh" + else + f_log "error" "required system library not found" + fi + + # load uci config and check 'enabled' option + # + option_cb() + { + local option="${1}" + local value="${2}" + eval "${option}=\"${value}\"" + } + config_load travelmate + + if [ ${trm_enabled} -ne 1 ] + then + f_log "info " "status ::: Hotspot Manager is currently disabled" + exit 0 + fi + + # check for preferred wireless tool + # + if [ ${trm_iw} -eq 1 ] + then + trm_scanner="$(which iw)" + else + trm_scanner="$(which iwinfo)" + fi + if [ -z "${trm_scanner}" ] + then + f_log "error" "status ::: no wireless tool for wlan scanning found, please install 'iw' or 'iwinfo'" + fi +} + +# function to bring down all STA interfaces +# +f_prepare() +{ + local config="${1}" + local mode="$(uci -q get wireless."${config}".mode)" + local network="$(uci -q get wireless."${config}".network)" + local disabled="$(uci -q get wireless."${config}".disabled)" + + if [ "${mode}" = "sta" ] && [ -n "${network}" ] + then + trm_stalist="${trm_stalist} ${config}_${network}" + if [ -z "${disabled}" ] || [ "${disabled}" = "0" ] + then + uci -q set wireless."${config}".disabled=1 + f_log "debug" "prepare ::: config: ${config}, interface: ${network}" + fi + fi +} + +f_check() +{ + local ifname cnt=0 mode="${1}" + trm_ifstatus="false" + + while [ ${cnt} -lt ${trm_maxwait} ] + do + RADIO=$(uci get wireless.wwan.device) + if [ $RADIO = "radio0" ]; then + ifname="$(ubus -S call network.wireless status | jsonfilter -l 1 -e "@.radio0.interfaces[@.config.mode=\"${mode}\"].ifname")" + else + if [ $RADIO = "radio1" ]; then + ifname="$(ubus -S call network.wireless status | jsonfilter -l 1 -e "@.radio1.interfaces[@.config.mode=\"${mode}\"].ifname")" + fi + fi + if [ -z $ifname ]; then + break + fi + if [ "${mode}" = "sta" ] + then + trm_ifstatus="$(ubus -S call network.interface dump | jsonfilter -e "@.interface[@.device=\"${ifname}\"].up")" + else + trm_ifstatus="$(ubus -S call network.wireless status | jsonfilter -l1 -e '@.*.up')" + fi + if [ "${trm_ifstatus}" = "true" ] + then + break + fi + cnt=$((cnt+1)) + sleep 1 + done + f_log "debug" "check ::: ${mode} name: ${ifname}, status: ${trm_ifstatus}, count: ${cnt}" +} + +# function to write to syslog +# +f_log() +{ + local class="${1}" + local log_msg="${2}" + + if [ -n "${log_msg}" ] && ([ "${class}" != "debug" ] || [ ${trm_debug} -eq 1 ]) + then + logger -t "HOTSPOT-[${trm_ver}] ${class}" "${log_msg}" + if [ "${class}" = "error" ] + then + uci -q set wireless.wwan.ssid="Error during Connection" + uci -q set wireless.wwan.encryption="none" + uci -q set wireless.wwan.key= + uci -q commit wireless + #exit 255 + fi + fi +} + +f_main() +{ + local ap_list ssid_list config network ssid cnt=0 + + f_check "sta" + if [ "${trm_ifstatus}" != "true" ] + then + uci -q set wireless.wwan.ssid="Checking for Connection" + uci -q set wireless.wwan.encryption="none" + uci -q set wireless.wwan.key= + uci -q commit wireless + + config_load wireless + config_foreach f_prepare wifi-iface + if [ -n "$(uci -q changes wireless)" ] + then + uci -q commit wireless + ubus call network reload + fi + f_check "ap" + RADIO=$(uci get wireless.wwan.device) + if [ $RADIO = "radio0" ]; then + ap_list="$(ubus -S call network.wireless status | jsonfilter -e '@.radio0.interfaces[@.config.mode="ap"].ifname')" + else + ap_list="$(ubus -S call network.wireless status | jsonfilter -e '@.radio1.interfaces[@.config.mode="ap"].ifname')" + fi + f_log "debug" "main ::: ap-list: ${ap_list}, sta-list: ${trm_stalist}" + if [ -z "${ap_list}" ] || [ -z "${trm_stalist}" ] + then + f_log "error" "main ::: no usable AP/STA configuration found" + else + for ap in ${ap_list} + do + while [ ${cnt} -lt ${trm_maxretry} ] + do + if [ ${trm_iw} -eq 1 ] + then + ssid_list="$(${trm_scanner} dev "${ap}" scan 2>/dev/null | \ + awk '/SSID: /{if(!seen[$0]++){printf "\"";for(i=2; i<=NF; i++)if(i==2)printf $i;else printf " "$i;printf "\" "}}')" + else + ssid_list="$(${trm_scanner} "${ap}" scan | \ + awk '/ESSID: ".*"/{ORS=" ";if (!seen[$0]++) for(i=2; i<=NF; i++) print $i}')" + fi + f_log "debug" "main ::: scan-tool: ${trm_scanner}, ssidlist: ${ssid_list}" + if [ -n "${ssid_list}" ] + then + if [ "$trm_auto" = "1" ]; then + FILE="/etc/hotspot" + else + FILE="/tmp/hotman" + fi + if [ -f "${FILE}" ]; then + while IFS='|' read -r ssid encrypt key + do + ssidq="\"$ssid\"" + if [ -n "$(printf "${ssid_list}" | grep -Fo "${ssidq}")" ] + then + uci -q set wireless.wwan.ssid="$ssid" + uci -q set wireless.wwan.encryption=$encrypt + uci -q set wireless.wwan.key=$key + uci -q set wireless.wwan.disabled=0 + uci -q commit wireless + ubus call network.interface.wwan up + ubus call network reload + f_log "info " "main ::: wwan interface connected to uplink ${ssid}" + sleep 5 + f_check "sta" + if [ "${trm_ifstatus}" = "true" ] + then + exit 0 + fi + uci -q set wireless.wwan.ssid="Connection Failed" + uci -q set wireless.wwan.encryption="none" + uci -q set wireless.wwan.key= + uci -q set wireless.wwan.disabled=1 + uci -q commit wireless + fi + done <"${FILE}" + fi + fi + cnt=$((cnt+1)) + sleep 5 + done + done + fi + if [ "$trm_auto" = "1" ]; then + uci -q set wireless.wwan.ssid="No Connection Found, Rechecking" + else + uci -q set wireless.wwan.ssid="No Connection Made" + fi + uci -q set wireless.wwan.encryption="none" + uci -q set wireless.wwan.key= + uci -q commit wireless + f_log "info " "main ::: no wwan uplink found" + return 0 + fi + exit 0 +} + +check_wwan +check_radio +f_envload +f_main +while [ "$trm_auto" = "1" ]; do + sleep 20 + f_main + f_envload +done + +exit 0 \ No newline at end of file diff --git a/luci-app-hotspot/files/usr/lib/lua/luci/controller/hotspot.lua b/luci-app-hotspot/files/usr/lib/lua/luci/controller/hotspot.lua new file mode 100644 index 0000000..18dce1c --- /dev/null +++ b/luci-app-hotspot/files/usr/lib/lua/luci/controller/hotspot.lua @@ -0,0 +1,283 @@ +module("luci.controller.hotspot", package.seeall) + +function index() + local page + page = entry({"admin", "services", "hotspot"}, template("hotspot/hotspot"), _("Wifi Hotspot Manager")) + page.dependent = true + + entry({"admin", "services", "check_spot"}, call("action_check_spot")) + entry({"admin", "services", "set_mode"}, call("action_set_mode")) + entry({"admin", "services", "set_enabled"}, call("action_set_enabled")) + entry({"admin", "services", "disconnect"}, call("action_disconnect")) + entry({"admin", "services", "refreshlist"}, call("action_refresh")) + entry({"admin", "services", "moveup"}, call("action_moveup")) + entry({"admin", "services", "movedown"}, call("action_movedown")) + entry({"admin", "services", "addtolist"}, call("action_addtolist")) + entry({"admin", "services", "dellist"}, call("action_dellist")) + entry({"admin", "services", "editlist"}, call("action_editlist")) + entry({"admin", "services", "mancon"}, call("action_mancon")) + entry({"admin", "services", "bandchange"}, call("action_bandchange")) +end + +function readhot() + line = nil + file = io.open("/tmp/hot", "r") + if file ~= nil then + repeat + tmp = file:read("*line") + if tmp ~= nil then + if line == nil then + line = tmp + else + line = line .. "|" .. tmp + end + end + until tmp == nil + file:close() + end + return line +end + +function action_check_spot() + local rv = {} + local set = luci.http.formvalue("set") + auto = luci.model.uci.cursor():get("travelmate", "global", "trm_auto") + rv["auto"] = auto + rv["enabled"] = luci.model.uci.cursor():get("travelmate", "global", "trm_enabled") + + rv["ssid"] = luci.model.uci.cursor():get("wireless", "wwan", "ssid") + if rv["ssid"] == nil then + rv["ssid"] = "No Connection" + end + encr = luci.model.uci.cursor():get("wireless", "wwan", "encryption") + if encr == "none" then + rv["encryp"] = "Open" + else + rv["encryp"] = "Encrypted" + end + rv["disable"] = luci.model.uci.cursor():get("wireless", "wwan", "disabled") + + dual = luci.model.uci.cursor():get("travelmate", "global", "radio5") + if dual == nil then + rv["dual"] = 0 + else + rv["dual"] = 1 + end + device = luci.model.uci.cursor():get("wireless", "wwan", "device") + if device == dual then + rv["band"] = 2 + else + rv["band"] = 1 + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_set_mode() + local set = luci.http.formvalue("set") + os.execute("/usr/lib/hotspot/enable.sh " .. set) +end + +function action_set_enabled() + local set = luci.http.formvalue("set") + os.execute("/usr/lib/hotspot/enable.sh " .. set) +end + +function action_disconnect() + os.execute("/usr/lib/hotspot/dis_hot.sh") +end + +function action_refresh() + local rv = {} + os.execute("/usr/lib/hotspot/inrange.sh") + hotline = readhot() + if hotline == nil then + rv["empty"] = "1" + else + rv["empty"] = "0" + rv["hotline"] = hotline + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function movelist(direc, set, key) + local temp = {} + local rv = {} + local index = 0 + file = io.open("/tmp/hot", "r") + if file ~= nil then + repeat + tmp = file:read("*line") + if tmp ~= nil then + temp[index] = tmp + index = index + 1 + end + until tmp == nil + file:close() + if direc == 0 then + line2 = temp[set-1] + temp[set-1] = temp[set] + temp[set] = line2 + elseif direc == 1 then + line2 = temp[set+1] + temp[set+1] = temp[set] + temp[set] = line2 + elseif direc == 2 then + if index > 0 then + for i=set, index-2 do + temp[i] = temp[i+1] + end + end + index = index - 1 + elseif direc == 3 then + s, e = temp[set]:find("|") + name = temp[set]:sub(1, s-1) + cs, ce = temp[set]:find("|", e+1) + enc = temp[set]:sub(e+1, cs-1) + bs, be = temp[set]:find("|", ce+1) + ko = temp[set]:sub(ce+1, bs-1) + fl = temp[set]:sub(be+1) + temp[set] = name .. "|" .. enc .. "|" .. key .. "|" .. fl + end + if index < 0 then + --os.execute("rm -f /tmp/hot1; rm -f /etc/hotspot") + else + file = io.open("/tmp/hot", "w") + for i=0, index-1 do + file:write(temp[i], "\n") + end + file:close() + os.execute("/usr/lib/hotspot/copyhot.sh") + end + end +end + +function action_moveup() + local set = tonumber(luci.http.formvalue("set")) + local rv = {} + movelist(0, set) + hotline = readhot() + if hotline == nil then + rv["empty"] = "1" + else + rv["empty"] = "0" + rv["hotline"] = hotline + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_movedown() + local set = tonumber(luci.http.formvalue("set")) + local rv = {} + movelist(1, set) + hotline = readhot() + if hotline == nil then + rv["empty"] = "1" + else + rv["empty"] = "0" + rv["hotline"] = hotline + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_dellist() + local set = tonumber(luci.http.formvalue("set")) + local rv = {} + movelist(2, set) + hotline = readhot() + if hotline == nil then + rv["empty"] = "1" + else + rv["empty"] = "0" + rv["hotline"] = hotline + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_addtolist() + local set = luci.http.formvalue("set") + local rv = {} + + s, e = set:find("|") + ssid = set:sub(1, e-1) + + file = io.open("/etc/hotspot", "r") + if file ~= nil then + i = 0 + repeat + line = file:read("*line") + if line == nil then + break + else + s, e = line:find(ssid) + if s ~= nil then + i = 1 + break + end + end + until 1==0 + if i == 0 then + file = io.open("/etc/hotspot", "a") + file:write(set) + file:write("\n") + file:close() + end + else + file = io.open("/etc/hotspot", "w") + file:write(set) + file:write("\n") + file:close() + end + os.execute("/usr/lib/hotspot/inrange.sh") + hotline = readhot() + if hotline == nil then + rv["empty"] = "1" + else + rv["empty"] = "0" + rv["hotline"] = hotline + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_editlist() + local set = luci.http.formvalue("set") + local rv = {} + s, e = set:find("|") + sel = set:sub(1, s-1) + idx = tonumber(sel) + key = set:sub(e+1) + movelist(3, idx, key) + hotline = readhot() + if hotline == nil then + rv["empty"] = "1" + else + rv["empty"] = "0" + rv["hotline"] = hotline + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_mancon() + local set = luci.http.formvalue("set") + local rv = {} + os.execute("/usr/lib/hotspot/manual.sh \"" .. set .. "\"") +end + +function action_bandchange() + local set = luci.http.formvalue("set") + local rv = {} + os.execute("/usr/lib/hotspot/band.sh \"" .. set .. "\"") +end \ No newline at end of file diff --git a/luci-app-hotspot/files/usr/lib/lua/luci/view/hotspot/hotspot.htm b/luci-app-hotspot/files/usr/lib/lua/luci/view/hotspot/hotspot.htm new file mode 100644 index 0000000..8965452 --- /dev/null +++ b/luci-app-hotspot/files/usr/lib/lua/luci/view/hotspot/hotspot.htm @@ -0,0 +1,1005 @@ +<%- + +local sys = require "luci.sys" +local utl = require "luci.util" + +wifi = {} +scan = {} +numnet = 0 + +printf = function(s,...) + io.write(s:format(...)) + local ss = s:format(...) +end + +function trim(s) + return (s:gsub("^%s*(.-)%s*$", "%1")) +end + +function showicon() + return resource .. "/icons/encryption.png" +end + +function guess_wifi_signal(info) + local scale = scan[info]["quality"] + local icon + if scale < 15 then + icon = resource .. "/icons/signal-0.png" + elseif scale < 35 then + icon = resource .. "/icons/signal-0-25.png" + elseif scale < 55 then + icon = resource .. "/icons/signal-25-50.png" + elseif scale < 75 then + icon = resource .. "/icons/signal-50-75.png" + else + icon = resource .. "/icons/signal-75-100.png" + end + + return icon +end + +function scan_open(rrx, rry) + rony = 1 + if rry["encrypt"] == "Open" then + rony = 0 + end + ronx = 1 + if rrx["encrypt"] == "Open" then + ronx = 0 + end + if ronx < rony then + return true + end + if ronx > rony then + return false + end + ronx = rrx["quality"] + rony = rry["quality"] + if ronx > rony then + return true + end + return false +end + +function partition(arr, left, right) + local i = left + local j = right + local pivot = math.floor((left + right) / 2) + local tmp + while i <= j do + while scan_open(arr[i], arr[pivot]) do + i = i + 1 + end + while scan_open(arr[pivot], arr[j]) do + j = j - 1 + end + if i <= j then + tmp = arr[i] + arr[i] = arr[j] + arr[j] = tmp + i = i + 1 + j = j - 1 + end + end + return i, arr +end + +function quicksort(arr, left, right) + index, arr = partition(arr, left, right) + if left < (index - 1) then + quicksort(arr, left, index-1) + end + if index < right then + quicksort(arr, index, right) + end + return arr +end + +function build_scan(j, k) + scan[k]["channel"] = wifi[j]["channel"] + scan[k]["essid"] = wifi[j]["essid"] + scan[k]["signal"] = tonumber(wifi[j]["signal"]) + qc = tonumber(wifi[j]["quality"]) + qm = tonumber(wifi[j]["quality_max"]) + scan[k]["quality"] = math.floor((100 / qm) * qc) + scan[k]["ekey"] = wifi[j]["ekey"] + if wifi[j]["ekey"] == "none" then + scan[k]["encrypt"] = "Open" + scan[k]["encryption"] = wifi[j]["encryption"] + else + scan[k]["encrypt"] = wifi[j]["ekey"] + scan[k]["encryption"] = wifi[j]["encryption"] + end +end + +function get_networks() + luci.util.exec("/usr/lib/hotspot/getssid.sh") + file = io.open("/tmp/ssidlist", "r") + if file == nil then + numnet = 0 + return + end + i = 0 + cont = 2 + line = file:read("*line") + repeat + if line == nil then + cont = 0 + else + s, e = line:find("Cell ") + if s ~= nil then + cont = 1 + essid=nil + i = i + 1 + wifi[i] = {} + encryption = "none" + repeat + line = file:read("*line") + if line == nil then + cont = 0 + break + else + s, e = line:find("Cell ") + if s ~= nil then + break + else + s, e = line:find("ESSID:") + if s ~= nil then + ee = line:len() + essid = trim(line:sub(e+3, ee-1)) + end + s, e = line:find("Mode:") + if s ~= nil then + line1 = trim(line:sub(e+1)) + bs, be = line1:find(" ", 1) + mode = trim(line1:sub(1, bs)) + cs, ce = line1:find(" ") + line2 = trim(line1:sub(ce+1)) + s, e = line2:find("Channel:") + channel = trim(line2:sub(e+2)) + end + s, e = line:find("Signal:") + if s ~= nil then + line1 = trim(line:sub(e+1)) + bs, be = line1:find(" ", 1) + signal = trim(line1:sub(1, bs)) + cs, ce = line1:find(" ") + line2 = trim(line1:sub(ce+1)) + s, e = line2:find("Quality:") + cs, ce = line2:find("/", e) + quality = trim(line2:sub(e+1, ce-1)) + quality_max = trim(line2:sub(ce+1)) + end + s, e = line:find("Encryption:") + if s ~= nil then + cs, ce = line:find("WEP") + if cs == nil then + encrypt = trim(line:sub(e+2)) + s, e = encrypt:find("none") + if s ~= nil then + encryption = "none" + else + s, e = encrypt:find("mixed") + if s ~= nil then + encryption = "psk-mixed" + else + s, e = encrypt:find("WPA2") + if s ~= nil then + encryption = "psk2" + else + encryption = "psk" + end + end + end + else + cont = 2 + i = i - 1 + break + end + end + end + end + until 1==0 + else + line = file:read("*line") + end + end + if cont < 2 then + if i > 0 then + if essid ~= nil and mode == "Master" then + wifi[i]["essid"] = essid + wifi[i]["channel"] = channel + wifi[i]["signal"] = signal + wifi[i]["quality"] = quality + wifi[i]["quality_max"] = quality_max + wifi[i]["ekey"] = encrypt + wifi[i]["encryption"] = encryption + end + end + end + if cont == 0 then + break + end + cont = 2 + until 1==0 + file:close() + + k = 0 + if i > 0 then + for j=1, i do + if wifi[j]["essid"] ~= nil and tonumber(wifi[j]["quality"]) > 0 then + if k < 1 then + k = k+1 + scan[k] = {} + build_scan(j, k) + else + flag = 0 + for l=1, k do + if wifi[j]["essid"] == scan[l]["essid"] and wifi[j]["channel"] == scan[l]["channel"] and wifi[j]["ekey"] == scan[l]["ekey"] then + qc = tonumber(wifi[j]["quality"]) + qm = tonumber(wifi[j]["quality_max"]) + qual = math.floor((100 / qm) * qc) + if qual > scan[l]["quality"] then + build_scan(j, l) + end + flag = 1 + break + end + end + if flag == 0 then + k = k+1 + scan[k] = {} + build_scan(j, k) + end + end + end + end + scan = quicksort(scan, 1, k) + end + numnet = k +end + +-%> +<%+header%> + + + + +
                                                      + +
                                                      +

                                                      Wifi Hotspot Management

                                                      +
                                                      Manage Your Wifi Hotspot Connections
                                                      +
                                                      + + + + + + + + +
                                                      Enable Hotspot Connection :
                                                      + +  
                                                      + + + +
                                                       
                                                      + + + + + + + +
                                                      Wifi Radio Frequency :
                                                      + +  
                                                      + + + + + +
                                                       
                                                      + + + + + + + + +
                                                      Connection Mode :
                                                        
                                                      + + + + + +
                                                       
                                                      + + + + + + + +
                                                      Current Wifi Hotspot :
                                                        + + + + + + + +
                                                        Encryption :
                                                           
                                                          + + + + + +
                                                           
                                                          + + + + + + + +
                                                                + + + + + + + + + +
                                                                In Range
                                                                Wifi Hotspot Networks
                                                                 
                                                                + + + + + +
                                                                + +
                                                                + + + + + + + +
                                                                Hotspot Name :
                                                                   
                                                                  + + + + + + + +
                                                                  Encrypted :
                                                                     
                                                                    + + + + + + + + +
                                                                    Key :
                                                                       
                                                                      + + + + + +
                                                                       
                                                                      + + + + + + + + + + +
                                                                       
                                                                      + + + + + +
                                                                       
                                                                      + + <% get_networks() %> + + + + + +
                                                                      Available Networks : <%=numnet%>
                                                                      + <% if numnet > 0 then + for m=1, numnet do + data = scan[m]["essid"] .. "|" .. scan[m]["encryption"] + %> + + + + + + +
                                                                      + +
                                                                      + <%=scan[m]["quality"]%>% +
                                                                      +
                                                                      + <%=scan[m]["essid"]%>
                                                                      + Channel: <%=scan[m]["channel"]%> | + Encryption: <%=scan[m]["encrypt"]%> +
                                                                      + +
                                                                      + <% end + end %> +
                                                                      + +
                                                                      +
                                                                      + + + +
                                                                      + + + + + +<%+footer%> diff --git a/luci-app-rooterddns/Makefile b/luci-app-rooterddns/Makefile new file mode 100644 index 0000000..4ed1d3b --- /dev/null +++ b/luci-app-rooterddns/Makefile @@ -0,0 +1,34 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-rooterddns +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/luci-app-rooterddns + SECTION:=luci + CATEGORY:=LuCI + DEPENDS:=+ddns-scripts +ddns-scripts_no-ip_com + SUBMENU:=3. Applications + TITLE:=LuCI Support for Dynamic DNS Client + PKGARCH:=all +endef + +define Package/luci-app-rooterddns/description + LuCI Support for Dynamic DNS Client +endef + + +define Build/Compile +endef + +define Package/luci-app-rooterddns/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,luci-app-rooterddns)) diff --git a/luci-app-rooterddns/files/etc/uci-defaults/40_luci-ddns b/luci-app-rooterddns/files/etc/uci-defaults/40_luci-ddns new file mode 100644 index 0000000..a82c1f9 --- /dev/null +++ b/luci-app-rooterddns/files/etc/uci-defaults/40_luci-ddns @@ -0,0 +1,10 @@ +#!/bin/sh + +# no longer needed for "Save and Apply" to restart ddns +uci -q batch <<-EOF >/dev/null + delete ucitrack.@ddns[-1] + commit ucitrack +EOF + +rm -f /tmp/luci-indexcache +return 0 diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/controller/ddns.lua b/luci-app-rooterddns/files/usr/lib/lua/luci/controller/ddns.lua new file mode 100644 index 0000000..89da567 --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/controller/ddns.lua @@ -0,0 +1,308 @@ +-- Copyright 2008 Steven Barth +-- Copyright 2008 Jo-Philipp Wich +-- Copyright 2013 Manuel Munz +-- Copyright 2014-2016 Christian Schoenebeck +-- Licensed to the public under the Apache License 2.0. + +module("luci.controller.ddns", package.seeall) + +local NX = require "nixio" +local NXFS = require "nixio.fs" +local DISP = require "luci.dispatcher" +local HTTP = require "luci.http" +local I18N = require "luci.i18n" -- not globally avalible here +local IPKG = require "luci.model.ipkg" +local SYS = require "luci.sys" +local UCI = require "luci.model.uci" +local UTIL = require "luci.util" +local DDNS = require "luci.tools.ddns" -- ddns multiused functions + +luci_helper = "/usr/lib/ddns/dynamic_dns_lucihelper.sh" + +local srv_name = "ddns-scripts" +local srv_ver_min = "2.7.6" -- minimum version of service required +local srv_ver_cmd = luci_helper .. [[ -V | awk {'print $2'}]] +local app_name = "luci-app-ddns" +local app_title = "Dynamic DNS" +local app_version = "2.4.8-1" + +function index() + local nxfs = require "nixio.fs" -- global definitions not available + local sys = require "luci.sys" -- in function index() + local ddns = require "luci.tools.ddns" -- ddns multiused functions + local muci = require "luci.model.uci" + + -- no config create an empty one + if not nxfs.access("/etc/config/ddns") then + nxfs.writefile("/etc/config/ddns", "") + end + + -- preset new option "lookup_host" if not already defined + local uci = muci.cursor() + local commit = false + uci:foreach("ddns", "service", function (s) + if not s["lookup_host"] and s["domain"] then + uci:set("ddns", s[".name"], "lookup_host", s["domain"]) + commit = true + end + end) + if commit then uci:commit("ddns") end + uci:unload("ddns") + + entry( {"admin", "services", "ddns"}, cbi("ddns/overview"), _("Dynamic DNS"), 59) + entry( {"admin", "services", "ddns", "detail"}, cbi("ddns/detail"), nil ).leaf = true + entry( {"admin", "services", "ddns", "hints"}, cbi("ddns/hints", + {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true}), nil ).leaf = true + entry( {"admin", "services", "ddns", "global"}, cbi("ddns/global"), nil ).leaf = true + entry( {"admin", "services", "ddns", "logview"}, call("logread") ).leaf = true + entry( {"admin", "services", "ddns", "startstop"}, post("startstop") ).leaf = true + entry( {"admin", "services", "ddns", "status"}, call("status") ).leaf = true +end + +-- Application specific information functions +function app_description() + return I18N.translate("Dynamic DNS allows your router to be reached with " .. + "a fixed hostname while having a dynamically changing IP address.") +end +function app_title_back() + return [[]] + .. I18N.translate(app_title) + .. [[]] +end + +-- Standardized application/service functions +function app_title_main() + return [[]] + .. I18N.translate(app_title) + .. [[]] +end +function service_version() + local ver = nil + + ver = UTIL.exec(srv_ver_cmd) + if #ver > 0 then return ver end + + IPKG.list_installed(srv_name, function(n, v, d) + if v and (#v > 0) then ver = v end + end + ) + return ver +end +function service_ok() + return IPKG.compare_versions((service_version() or "0"), ">=", srv_ver_min) +end + +-- internal function to read all sections status and return data array +local function _get_status() + local uci = UCI.cursor() + local service = SYS.init.enabled("ddns") and 1 or 0 + local url_start = DISP.build_url("admin", "system", "startup") + local data = {} -- Array to transfer data to javascript + + data[#data+1] = { + enabled = service, -- service enabled + url_up = url_start, -- link to enable DDS (System-Startup) + } + + uci:foreach("ddns", "service", function (s) + + -- Get section we are looking at + -- and enabled state + local section = s[".name"] + local enabled = tonumber(s["enabled"]) or 0 + local datelast = "_empty_" -- formatted date of last update + local datenext = "_empty_" -- formatted date of next update + + -- get force seconds + local force_seconds = DDNS.calc_seconds( + tonumber(s["force_interval"]) or 72 , + s["force_unit"] or "hours" ) + -- get/validate pid and last update + local pid = DDNS.get_pid(section) + local uptime = SYS.uptime() + local lasttime = DDNS.get_lastupd(section) + if lasttime > uptime then -- /var might not be linked to /tmp + lasttime = 0 -- and/or not cleared on reboot + end + + -- no last update happen + if lasttime == 0 then + datelast = "_never_" + + -- we read last update + else + -- calc last update + -- sys.epoch - sys uptime + lastupdate(uptime) + local epoch = os.time() - uptime + lasttime + -- use linux date to convert epoch + datelast = DDNS.epoch2date(epoch) + -- calc and fill next update + datenext = DDNS.epoch2date(epoch + force_seconds) + end + + -- process running but update needs to happen + -- problems if force_seconds > uptime + force_seconds = (force_seconds > uptime) and uptime or force_seconds + if pid > 0 and ( lasttime + force_seconds - uptime ) <= 0 then + datenext = "_verify_" + + -- run once + elseif force_seconds == 0 then + datenext = "_runonce_" + + -- no process running and NOT enabled + elseif pid == 0 and enabled == 0 then + datenext = "_disabled_" + + -- no process running and enabled + elseif pid == 0 and enabled ~= 0 then + datenext = "_stopped_" + end + + -- get/set monitored interface and IP version + local iface = s["interface"] or "_nonet_" + local use_ipv6 = tonumber(s["use_ipv6"]) or 0 + if iface ~= "_nonet_" then + local ipv = (use_ipv6 == 1) and "IPv6" or "IPv4" + iface = ipv .. " / " .. iface + end + + -- try to get registered IP + local lookup_host = s["lookup_host"] or "_nolookup_" + local dnsserver = s["dns_server"] or "" + local force_ipversion = tonumber(s["force_ipversion"] or 0) + local force_dnstcp = tonumber(s["force_dnstcp"] or 0) + local is_glue = tonumber(s["is_glue"] or 0) + local command = luci_helper .. [[ -]] + if (use_ipv6 == 1) then command = command .. [[6]] end + if (force_ipversion == 1) then command = command .. [[f]] end + if (force_dnstcp == 1) then command = command .. [[t]] end + if (is_glue == 1) then command = command .. [[g]] end + command = command .. [[l ]] .. lookup_host + if (#dnsserver > 0) then command = command .. [[ -d ]] .. dnsserver end + command = command .. [[ -- get_registered_ip]] + local reg_ip = SYS.exec(command) + if reg_ip == "" then + reg_ip = "_nodata_" + end + + -- fill transfer array + data[#data+1] = { + section = section, + enabled = enabled, + iface = iface, + lookup = lookup_host, + reg_ip = reg_ip, + pid = pid, + datelast = datelast, + datenext = datenext + } + end) + + uci:unload("ddns") + return data +end + +-- called by XHR.get from detail_logview.htm +function logread(section) + -- read application settings + local uci = UCI.cursor() + local ldir = uci:get("ddns", "global", "ddns_logdir") or "/var/log/ddns" + local lfile = ldir .. "/" .. section .. ".log" + local ldata = NXFS.readfile(lfile) + + if not ldata or #ldata == 0 then + ldata="_nodata_" + end + uci:unload("ddns") + HTTP.write(ldata) +end + +-- called by XHR.get from overview_status.htm +function startstop(section, enabled) + local uci = UCI.cursor() + local pid = DDNS.get_pid(section) + local data = {} -- Array to transfer data to javascript + + -- if process running we want to stop and return + if pid > 0 then + local tmp = NX.kill(pid, 15) -- terminate + NX.nanosleep(2) -- 2 second "show time" + -- status changed so return full status + data = _get_status() + HTTP.prepare_content("application/json") + HTTP.write_json(data) + return + end + + -- read uncommitted changes + -- we don't save and commit data from other section or other options + -- only enabled will be done + local exec = true + local changed = uci:changes("ddns") + for k_config, v_section in pairs(changed) do + -- security check because uci.changes only gets our config + if k_config ~= "ddns" then + exec = false + break + end + for k_section, v_option in pairs(v_section) do + -- check if only section of button was changed + if k_section ~= section then + exec = false + break + end + for k_option, v_value in pairs(v_option) do + -- check if only enabled was changed + if k_option ~= "enabled" then + exec = false + break + end + end + end + end + + -- we can not execute because other + -- uncommitted changes pending, so exit here + if not exec then + HTTP.write("_uncommitted_") + return + end + + -- save enable state + uci:set("ddns", section, "enabled", ( (enabled == "true") and "1" or "0") ) + uci:save("ddns") + uci:commit("ddns") + uci:unload("ddns") + + -- start ddns-updater for section + local command = luci_helper .. [[ -S ]] .. section .. [[ -- start]] + os.execute(command) + NX.nanosleep(3) -- 3 seconds "show time" + + -- status changed so return full status + data = _get_status() + HTTP.prepare_content("application/json") + HTTP.write_json(data) +end + +-- called by XHR.poll from overview_status.htm +function status() + local data = _get_status() + HTTP.prepare_content("application/json") + HTTP.write_json(data) +end + diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/detail.lua b/luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/detail.lua new file mode 100644 index 0000000..d283c54 --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/detail.lua @@ -0,0 +1,1479 @@ +-- Copyright 2008 Steven Barth +-- Copyright 2008 Jo-Philipp Wich +-- Copyright 2013 Manuel Munz +-- Copyright 2014-2016 Christian Schoenebeck +-- Licensed to the public under the Apache License 2.0. + +local NX = require "nixio" +local NXFS = require "nixio.fs" +local SYS = require "luci.sys" +local UTIL = require "luci.util" +local HTTP = require "luci.http" +local DISP = require "luci.dispatcher" +local WADM = require "luci.tools.webadmin" +local DTYP = require "luci.cbi.datatypes" +local CTRL = require "luci.controller.ddns" -- this application's controller +local DDNS = require "luci.tools.ddns" -- ddns multiused functions + +-- takeover arguments -- ####################################################### +local section = arg[1] + +-- html constants -- ########################################################### +local font_red = "" +local font_off = "" +local bold_on = "" +local bold_off = "" + +-- error text constants -- ##################################################### +local err_ipv6_plain = translate("IPv6 not supported") .. " - " .. + translate("please select 'IPv4' address version") +local err_ipv6_basic = bold_on .. + font_red .. + translate("IPv6 not supported") .. + font_off .. + "
                                                                      " .. translate("please select 'IPv4' address version") .. + bold_off +local err_ipv6_other = bold_on .. + font_red .. + translate("IPv6 not supported") .. + font_off .. + "
                                                                      " .. translate("please select 'IPv4' address version in") .. " " .. + [[]] .. + translate("Basic Settings") .. + [[]] .. + bold_off + +function err_tab_basic(self) + return translate("Basic Settings") .. " - " .. self.title .. ": " +end +function err_tab_adv(self) + return translate("Advanced Settings") .. " - " .. self.title .. ": " +end +function err_tab_timer(self) + return translate("Timer Settings") .. " - " .. self.title .. ": " +end + +-- read services/services_ipv6 files -- ######################################## +local services4 = { } -- IPv4 -- +local fd4 = io.open("/etc/ddns/services", "r") +if fd4 then + local ln, s, t + repeat + ln = fd4:read("*l") + s = ln and ln:match('^%s*".*') -- only handle lines beginning with " + s = s and s:gsub('"','') -- remove " + t = s and UTIL.split(s,"(%s+)",nil,true) -- split on whitespaces + if t then services4[t[1]]=t[2] end + until not ln + fd4:close() +end + +local services6 = { } -- IPv6 -- +local fd6 = io.open("/etc/ddns/services_ipv6", "r") +if fd6 then + local ln, s, t + repeat + ln = fd6:read("*l") + s = ln and ln:match('^%s*".*') -- only handle lines beginning with " + s = s and s:gsub('"','') -- remove " + t = s and UTIL.split(s,"(%s+)",nil,true) -- split on whitespaces + if t then services6[t[1]]=t[2] end + until not ln + fd6:close() +end + +-- multi-used functions -- #################################################### +-- function to verify settings around ip_source +-- will use dynamic_dns_lucihelper to check if +-- local IP can be read +local function _verify_ip_source() + -- section is globally defined here be calling agrument (see above) + local _arg + + local _ipv6 = usev6:formvalue(section) + local _source = (_ipv6 == "1") + and src6:formvalue(section) + or src4:formvalue(section) + + local command = CTRL.luci_helper .. [[ -]] + if (_ipv6 == "1") then command = command .. [[6]] end + + if _source == "network" then + _arg = (_ipv6 == "1") + and ipn6:formvalue(section) + or ipn4:formvalue(section) + command = command .. [[n ]] .. _arg + elseif _source == "web" then + _arg = (_ipv6 == "1") + and iurl6:formvalue(section) + or iurl4:formvalue(section) + command = command .. [[u ]] .. _arg + + -- proxy only needed for checking url + _arg = (pxy) and pxy:formvalue(section) or "" + if (_arg and #_arg > 0) then + command = command .. [[ -p ]] .. _arg + end + elseif _source == "interface" then + command = command .. [[i ]] .. ipi:formvalue(section) + elseif _source == "script" then + command = command .. [[s ]] .. ips:formvalue(section) + end + command = command .. [[ -- get_local_ip]] + return (SYS.call(command) == 0) +end + +-- function to check if option is used inside url or script +-- return -1 on error, 0 NOT required, 1 required +local function _option_used(option, urlscript) + local surl -- search string for url + local ssh -- search string for script + local required -- option used inside url or script + + if option == "domain" then surl, ssh = '%[DOMAIN%]', '%$domain' + elseif option == "username" then surl, ssh = '%[USERNAME%]', '%$username' + elseif option == "password" then surl, ssh = '%[PASSWORD%]', '%$password' + elseif option == "param_enc" then surl, ssh = '%[PARAMENC%]', '%$param_enc' + elseif option == "param_opt" then surl, ssh = '%[PARAMOPT%]', '%$param_opt' + else + error("undefined option") + return -1 -- return on error + end + + local required = false + -- handle url + if urlscript:find('http') then + required = ( urlscript:find(surl) ) + -- handle script + else + if not urlscript:find("/") then + -- might be inside ddns-scripts directory + urlscript = "/usr/lib/ddns/" .. urlscript + end + -- problem with script exit here + if not NXFS.access(urlscript) then return -1 end + + local f = io.input(urlscript) + -- still problem with script exit here + if not f then return -1 end + for l in f:lines() do + repeat + if l:find('^#') then break end -- continue on comment lines + required = ( l:find(surl) or l:find(ssh) ) + until true + if required then break end + end + f:close() + end + return (required and 1 or 0) +end + +-- function to verify if option is valid +local function _option_validate(self, value) + -- section is globally defined here be calling agrument (see above) + local fusev6 = usev6:formvalue(section) or "0" + local fsvc4 = svc4:formvalue(section) or "-" + local fsvc6 = svc6:formvalue(section) or "-" + local urlsh, used + + -- IP-Version dependent custom service selected + if (fusev6 == "0" and fsvc4 == "-") or + (fusev6 == "1" and fsvc6 == "-") then + -- read custom url + urlsh = uurl:formvalue(section) or "" + -- no url then read custom script + if (#urlsh == 0) then + urlsh = ush:formvalue(section) or "" + end + -- IPv4 read from services4 table + elseif (fusev6 == "0") then + urlsh = services4[fsvc4] or "" + -- IPv6 read from services6 table + else + urlsh = services6[fsvc6] or "" + end + -- problem with url or script exit here + -- error handled somewhere else + if (#urlsh == 0) then return "" end + + used = _option_used(self.option, urlsh) + -- on error or not used return empty sting + if used < 1 then return "" end + -- needed but no data then return error + if not value or (#value == 0) then + return nil, err_tab_basic(self) .. translate("missing / required") + end + return value +end + +-- cbi-map definition -- ####################################################### +local m = Map("ddns") +m.title = CTRL.app_title_back() +m.description = CTRL.app_description() +m.redirect = DISP.build_url("admin", "services", "ddns") + +m.on_after_commit = function(self) + if self.changed then -- changes ? + local pid = DDNS.get_pid(section) + if pid > 0 then -- running ? + local tmp = NX.kill(pid, 1) -- send SIGHUP + end + end +end + +-- provider switch was requested, save and reload page +if m:formvalue("cbid.ddns.%s._switch" % section) then -- section == arg[1] + local fsvc + local fusev6 = m:formvalue("cbid.ddns.%s.use_ipv6" % section) or "0" + if fusev6 == "1" then + fsvc = m:formvalue("cbid.ddns.%s.ipv6_service_name" % section) or "" + else + fsvc = m:formvalue("cbid.ddns.%s.ipv4_service_name" % section) or "" + end + + if fusev6 ~= (m:get(section, "use_ipv6") or "0") then -- IPv6 was changed + m:set(section, "use_ipv6", fusev6) -- save it + end + + if fsvc ~= "-" then -- NOT "custom" + m:set(section, "service_name", fsvc) -- save it + else -- else + m:del(section, "service_name") -- delete it + end + m.uci:save(m.config) + + -- reload page + HTTP.redirect( DISP.build_url("admin", "services", "ddns", "detail", section) ) + return +end + +-- read application settings -- ################################################ +-- log directory +local logdir = m.uci:get(m.config, "global", "ddns_logdir") or "/var/log/ddns" + +-- cbi-section definition -- ################################################### +local ns = m:section( NamedSection, section, "service", + translate("Details for") .. ([[: %s]] % section), + translate("Configure here the details for selected Dynamic DNS service.") ) +ns.instance = section -- arg [1] +ns:tab("basic", translate("Basic Settings"), nil ) +ns:tab("advanced", translate("Advanced Settings"), nil ) +ns:tab("timer", translate("Timer Settings"), nil ) +ns:tab("logview", translate("Log File Viewer"), nil ) + +-- TAB: Basic ##################################################################################### +-- enabled -- ################################################################# +en = ns:taboption("basic", Flag, "enabled", + translate("Enabled"), + translate("Enable service") ) +en.orientation = "horizontal" + +-- IPv4/IPv6 - lookup_host -- ################################################# +luh = ns:taboption("basic", Value, "lookup_host", + translate("Lookup Hostname"), + translate("Hostname/FQDN to validate, if an IP update happens or is necessary") ) +luh.rmempty = false +luh.placeholder = "myhost.example.com" +function luh.validate(self, value) + if not value + or not (#value > 0) + or not DTYP.hostname(value) then + return nil, err_tab_basic(self) .. translate("invalid FQDN / required - Sample") .. ": 'myhost.example.com'" + else + return UTIL.trim(value) + end +end +function luh.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- use_ipv6 -- ################################################################ +usev6 = ns:taboption("basic", ListValue, "use_ipv6", + translate("IP address version"), + translate("Defines which IP address 'IPv4/IPv6' is send to the DDNS provider") ) +usev6.widget = "radio" +usev6.default = "0" +usev6:value("0", translate("IPv4-Address") ) +function usev6.cfgvalue(self, section) + local value = AbstractValue.cfgvalue(self, section) or "0" + if DDNS.has_ipv6 or (value == "1" and not DDNS.has_ipv6) then + self:value("1", translate("IPv6-Address") ) + end + if value == "1" and not DDNS.has_ipv6 then + self.description = err_ipv6_basic + end + return value +end +function usev6.validate(self, value) + if (value == "1" and DDNS.has_ipv6) or value == "0" then + return value + end + return nil, err_tab_basic(self) .. err_ipv6_plain +end +function usev6.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4 - service_name -- ##################################################### +svc4 = ns:taboption("basic", ListValue, "ipv4_service_name", + translate("DDNS Service provider") .. " [IPv4]" ) +svc4.default = "-" +svc4:depends("use_ipv6", "0") -- only show on IPv4 +function svc4.cfgvalue(self, section) + local v = DDNS.read_value(self, section, "service_name") + if v and (#v > 0) then + for s, u in UTIL.kspairs(services4) do + if v == s then return v end + end + end + return "-" +end +function svc4.validate(self, value) + if usev6:formvalue(section) ~= "1" then -- do only on IPv4 + return value + else + return "" -- suppress validate error + end +end +function svc4.write(self, section, value) + if usev6:formvalue(section) ~= "1" then -- do only IPv4 here + self.map:del(section, self.option) -- to be shure + if value ~= "-" then -- and write "service_name + self.map:del(section, "update_url") -- delete update_url + self.map:del(section, "update_script") -- delete update_script + return self.map:set(section, "service_name", value) + else + return self.map:del(section, "service_name") + end + end +end +function svc4.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv6 - service_name -- ##################################################### +svc6 = ns:taboption("basic", ListValue, "ipv6_service_name", + translate("DDNS Service provider") .. " [IPv6]" ) +svc6.default = "-" +svc6:depends("use_ipv6", "1") -- only show on IPv6 +if not DDNS.has_ipv6 then + svc6.description = err_ipv6_basic +end +function svc6.cfgvalue(self, section) + local v = DDNS.read_value(self, section, "service_name") + if v and (#v > 0) then + for s, u in UTIL.kspairs(services4) do + if v == s then return v end + end + end + return "-" +end +function svc6.validate(self, value) + if usev6:formvalue(section) == "1" then -- do only on IPv6 + if DDNS.has_ipv6 then return value end + return nil, err_tab_basic(self) .. err_ipv6_plain + else + return "" -- suppress validate error + end +end +function svc6.write(self, section, value) + if usev6:formvalue(section) == "1" then -- do only when IPv6 + self.map:del(section, self.option) -- delete "ipv6_service_name" helper + if value ~= "-" then -- and write "service_name + self.map:del(section, "update_url") -- delete update_url + self.map:del(section, "update_script") -- delete update_script + return self.map:set(section, "service_name", value) + else + return self.map:del(section, "service_name") + end + end +end +function svc6.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4/IPv6 - change Provider -- ############################################# +svs = ns:taboption("basic", Button, "_switch") +svs.title = translate("Really change DDNS provider?") +svs.inputtitle = translate("Change provider") +svs.inputstyle = "apply" + +-- IPv4/IPv6 - update_url -- ################################################## +uurl = ns:taboption("basic", Value, "update_url", + translate("Custom update-URL"), + translate("Update URL to be used for updating your DDNS Provider." .. "
                                                                      " .. + "Follow instructions you will find on their WEB page.") ) +function uurl.validate(self, value) + local fush = ush:formvalue(section) + local fusev6 = usev6:formvalue(section) + + if (fusev6 ~= "1" and svc4:formvalue(section) ~= "-") or + (fusev6 == "1" and svc6:formvalue(section) ~= "-") then + return "" -- suppress validate error + elseif not value or (#value == 0) then + if not fush or (#fush == 0) then + return nil, err_tab_basic(self) .. translate("missing / required") + else + return "" -- suppress validate error / update_script is given + end + elseif (#fush > 0) then + return nil, err_tab_basic(self) .. translate("either url or script could be set") + end + + local url = DDNS.parse_url(value) + if not url.scheme == "http" then + return nil, err_tab_basic(self) .. translate("must start with 'http://'") + elseif not url.query then + return nil, err_tab_basic(self) .. " " .. translate("missing / required") + elseif not url.host then + return nil, err_tab_basic(self) .. " " .. translate("missing / required") + elseif SYS.call([[nslookup ]] .. url.host .. [[ >/dev/null 2>&1]]) ~= 0 then + return nil, err_tab_basic(self) .. translate("can not resolve host: ") .. url.host + end + + return value +end +function uurl.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4/IPv6 - update_script -- ############################################### +ush = ns:taboption("basic", Value, "update_script", + translate("Custom update-script"), + translate("Custom update script to be used for updating your DDNS Provider.") ) +function ush.validate(self, value) + local fuurl = uurl:formvalue(section) + local fusev6 = usev6:formvalue(section) + + if (fusev6 ~= "1" and svc4:formvalue(section) ~= "-") or + (fusev6 == "1" and svc6:formvalue(section) ~= "-") then + return "" -- suppress validate error + elseif not value or (#value == 0) then + if not fuurl or (#fuurl == 0) then + return nil, err_tab_basic(self) .. translate("missing / required") + else + return "" -- suppress validate error / update_url is given + end + elseif (#fuurl > 0) then + return nil, err_tab_basic(self) .. translate("either url or script could be set") + elseif not NXFS.access(value) then + return nil, err_tab_basic(self) .. translate("File not found") + end + return value +end +function ush.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4/IPv6 - domain -- ###################################################### +dom = ns:taboption("basic", Value, "domain", + translate("Domain"), + translate("Replaces [DOMAIN] in Update-URL") ) +dom.placeholder = "myhost.example.com" +function dom.validate(self, value) + return _option_validate(self, value) +end +function dom.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4/IPv6 - username -- #################################################### +user = ns:taboption("basic", Value, "username", + translate("Username"), + translate("Replaces [USERNAME] in Update-URL (URL-encoded)") ) +function user.validate(self, value) + return _option_validate(self, value) +end +function user.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4/IPv6 - password -- #################################################### +pw = ns:taboption("basic", Value, "password", + translate("Password"), + translate("Replaces [PASSWORD] in Update-URL (URL-encoded)") ) +pw.password = true +function pw.validate(self, value) + return _option_validate(self, value) +end +function pw.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4/IPv6 - param_enc -- ################################################### +pe = ns:taboption("basic", Value, "param_enc", + translate("Optional Encoded Parameter"), + translate("Optional: Replaces [PARAMENC] in Update-URL (URL-encoded)") ) +function pe.validate(self, value) + return _option_validate(self, value) +end +function pe.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4/IPv6 - param_enc -- ################################################### +po = ns:taboption("basic", Value, "param_opt", + translate("Optional Parameter"), + translate("Optional: Replaces [PARAMOPT] in Update-URL (NOT URL-encoded)") ) +function po.validate(self, value) + return _option_validate(self, value) +end +function po.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- handled service dependent show/display -- ################################## +-- IPv4 -- +local cv4 = svc4:cfgvalue(section) +if cv4 ~= "-" then + svs:depends ("ipv4_service_name", "-" ) -- show only if "-" + ush:depends ("ipv4_service_name", "?") + uurl:depends("ipv4_service_name", "?") +else + uurl:depends("ipv4_service_name", "-") + ush:depends ("ipv4_service_name", "-") + dom:depends("ipv4_service_name", "-" ) + user:depends("ipv4_service_name", "-" ) + pw:depends("ipv4_service_name", "-" ) + pe:depends("ipv4_service_name", "-" ) + po:depends("ipv4_service_name", "-" ) +end +for s, u in UTIL.kspairs(services4) do + svc4:value(s) -- fill DropDown-List + if cv4 ~= s then + svs:depends("ipv4_service_name", s ) + else + dom:depends ("ipv4_service_name", ((_option_used(dom.option, u) == 1) and s or "?") ) + user:depends("ipv4_service_name", ((_option_used(user.option, u) == 1) and s or "?") ) + pw:depends ("ipv4_service_name", ((_option_used(pw.option, u) == 1) and s or "?") ) + pe:depends ("ipv4_service_name", ((_option_used(pe.option, u) == 1) and s or "?") ) + po:depends ("ipv4_service_name", ((_option_used(po.option, u) == 1) and s or "?") ) + end +end +svc4:value("-", translate("-- custom --") ) + +-- IPv6 -- +local cv6 = svc6:cfgvalue(section) +if cv6 ~= "-" then + svs:depends ("ipv6_service_name", "-" ) + uurl:depends("ipv6_service_name", "?") + ush:depends ("ipv6_service_name", "?") +else + uurl:depends("ipv6_service_name", "-") + ush:depends ("ipv6_service_name", "-") + dom:depends("ipv6_service_name", "-" ) + user:depends("ipv6_service_name", "-" ) + pw:depends("ipv6_service_name", "-" ) + pe:depends("ipv6_service_name", "-" ) + po:depends("ipv6_service_name", "-" ) +end +for s, u in UTIL.kspairs(services6) do + svc6:value(s) -- fill DropDown-List + if cv6 ~= s then + svs:depends("ipv6_service_name", s ) + else + dom:depends ("ipv6_service_name", ((_option_used(dom.option, u) == 1) and s or "?") ) + user:depends("ipv6_service_name", ((_option_used(user.option, u) == 1) and s or "?") ) + pw:depends ("ipv6_service_name", ((_option_used(pw.option, u) == 1) and s or "?") ) + pe:depends ("ipv6_service_name", ((_option_used(pe.option, u) == 1) and s or "?") ) + po:depends ("ipv6_service_name", ((_option_used(po.option, u) == 1) and s or "?") ) + end +end +svc6:value("-", translate("-- custom --") ) + +-- IPv4/IPv6 - use_https -- ################################################### +if DDNS.has_ssl or ( ( m:get(section, "use_https") or "0" ) == "1" ) then + https = ns:taboption("basic", Flag, "use_https", + translate("Use HTTP Secure") ) + https.orientation = "horizontal" + function https.cfgvalue(self, section) + local value = AbstractValue.cfgvalue(self, section) + if not DDNS.has_ssl and value == "1" then + self.description = bold_on .. font_red .. + translate("HTTPS not supported") .. font_off .. "
                                                                      " .. + translate("please disable") .. " !" .. bold_off + else + self.description = translate("Enable secure communication with DDNS provider") + end + return value + end + function https.validate(self, value) + if (value == "1" and DDNS.has_ssl ) or value == "0" then return value end + return nil, err_tab_basic(self) .. translate("HTTPS not supported") .. " !" + end + function https.write(self, section, value) + if value == "1" then + return self.map:set(section, self.option, value) + else + self.map:del(section, "cacert") + return self.map:del(section, self.option) + end + end +end + +-- IPv4/IPv6 - cacert -- ###################################################### +if DDNS.has_ssl then + cert = ns:taboption("basic", Value, "cacert", + translate("Path to CA-Certificate"), + translate("directory or path/file") .. "
                                                                      " .. + translate("or") .. bold_on .. " IGNORE " .. bold_off .. + translate("to run HTTPS without verification of server certificates (insecure)") ) + cert:depends("use_https", "1") + cert.placeholder = "/etc/ssl/certs" + cert.forcewrite = true + function cert.validate(self, value) + if https:formvalue(section) ~= "1" then + return "" -- suppress validate error if NOT https + end + if value then -- otherwise errors in datatype check + if DTYP.directory(value) + or DTYP.file(value) + or (value == "IGNORE") + or (#value == 0) then + return value + end + end + return nil, err_tab_basic(self) .. + translate("file or directory not found or not 'IGNORE'") .. " !" + end + function cert.parse(self, section, novld) + DDNS.value_parse(self, section, novld) + end +end + +-- TAB: Advanced ################################################################################# +-- IPv4 - ip_source -- ######################################################## +src4 = ns:taboption("advanced", ListValue, "ipv4_source", + translate("IP address source") .. " [IPv4]", + translate("Defines the source to read systems IPv4-Address from, that will be send to the DDNS provider") ) +src4:depends("use_ipv6", "0") -- IPv4 selected +src4.default = "network" +src4:value("network", translate("Network")) +src4:value("web", translate("URL")) +src4:value("interface", translate("Interface")) +src4:value("script", translate("Script")) +function src4.cfgvalue(self, section) + return DDNS.read_value(self, section, "ip_source") +end +function src4.validate(self, value) + if usev6:formvalue(section) == "1" then + return "" -- ignore on IPv6 selected + elseif not _verify_ip_source() then + return nil, err_tab_adv(self) .. + translate("can not detect local IP. Please select a different Source combination") + else + return value + end +end +function src4.write(self, section, value) + if usev6:formvalue(section) == "1" then + return true -- ignore on IPv6 selected + elseif value == "network" then + self.map:del(section, "ip_url") -- delete not need parameters + self.map:del(section, "ip_interface") + self.map:del(section, "ip_script") + elseif value == "web" then + self.map:del(section, "ip_network") -- delete not need parameters + self.map:del(section, "ip_interface") + self.map:del(section, "ip_script") + elseif value == "interface" then + self.map:del(section, "ip_network") -- delete not need parameters + self.map:del(section, "ip_url") + self.map:del(section, "ip_script") + elseif value == "script" then + self.map:del(section, "ip_network") + self.map:del(section, "ip_url") -- delete not need parameters + self.map:del(section, "ip_interface") + end + self.map:del(section, self.option) -- delete "ipv4_source" helper + return self.map:set(section, "ip_source", value) -- and write "ip_source +end +function src4.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv6 - ip_source -- ######################################################## +src6 = ns:taboption("advanced", ListValue, "ipv6_source", + translate("IP address source") .. " [IPv6]", + translate("Defines the source to read systems IPv6-Address from, that will be send to the DDNS provider") ) +src6:depends("use_ipv6", 1) -- IPv6 selected +src6.default = "network" +src6:value("network", translate("Network")) +src6:value("web", translate("URL")) +src6:value("interface", translate("Interface")) +src6:value("script", translate("Script")) +if not DDNS.has_ipv6 then + src6.description = err_ipv6_other +end +function src6.cfgvalue(self, section) + return DDNS.read_value(self, section, "ip_source") +end +function src6.validate(self, value) + if usev6:formvalue(section) ~= "1" then + return "" -- ignore on IPv4 selected + elseif not DDNS.has_ipv6 then + return nil, err_tab_adv(self) .. err_ipv6_plain + elseif not _verify_ip_source() then + return nil, err_tab_adv(self) .. + translate("can not detect local IP. Please select a different Source combination") + else + return value + end +end +function src6.write(self, section, value) + if usev6:formvalue(section) ~= "1" then + return true -- ignore on IPv4 selected + elseif value == "network" then + self.map:del(section, "ip_url") -- delete not need parameters + self.map:del(section, "ip_interface") + self.map:del(section, "ip_script") + elseif value == "web" then + self.map:del(section, "ip_network") -- delete not need parameters + self.map:del(section, "ip_interface") + self.map:del(section, "ip_script") + elseif value == "interface" then + self.map:del(section, "ip_network") -- delete not need parameters + self.map:del(section, "ip_url") + self.map:del(section, "ip_script") + elseif value == "script" then + self.map:del(section, "ip_network") + self.map:del(section, "ip_url") -- delete not need parameters + self.map:del(section, "ip_interface") + end + self.map:del(section, self.option) -- delete "ipv4_source" helper + return self.map:set(section, "ip_source", value) -- and write "ip_source +end +function src6.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4 - ip_network (default "wan") -- ####################################### +ipn4 = ns:taboption("advanced", ListValue, "ipv4_network", + translate("Network") .. " [IPv4]", + translate("Defines the network to read systems IPv4-Address from") ) +ipn4:depends("ipv4_source", "network") +ipn4.default = "wan" +WADM.cbi_add_networks(ipn4) +function ipn4.cfgvalue(self, section) + return DDNS.read_value(self, section, "ip_network") +end +function ipn4.validate(self, value) + if usev6:formvalue(section) == "1" + or src4:formvalue(section) ~= "network" then + -- ignore if IPv6 selected OR + -- ignore everything except "network" + return "" + else + return value + end +end +function ipn4.write(self, section, value) + if usev6:formvalue(section) == "1" + or src4:formvalue(section) ~= "network" then + -- ignore if IPv6 selected OR + -- ignore everything except "network" + return true + else + -- set also as "interface" for monitoring events changes/hot-plug + self.map:set(section, "interface", value) + self.map:del(section, self.option) -- delete "ipv4_network" helper + return self.map:set(section, "ip_network", value) -- and write "ip_network" + end +end +function ipn4.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv6 - ip_network (default "wan6") -- ###################################### +ipn6 = ns:taboption("advanced", ListValue, "ipv6_network", + translate("Network") .. " [IPv6]" ) +ipn6:depends("ipv6_source", "network") +ipn6.default = "wan6" +WADM.cbi_add_networks(ipn6) +if DDNS.has_ipv6 then + ipn6.description = translate("Defines the network to read systems IPv6-Address from") +else + ipn6.description = err_ipv6_other +end +function ipn6.cfgvalue(self, section) + return DDNS.read_value(self, section, "ip_network") +end +function ipn6.validate(self, value) + if usev6:formvalue(section) ~= "1" + or src6:formvalue(section) ~= "network" then + -- ignore if IPv4 selected OR + -- ignore everything except "network" + return "" + elseif DDNS.has_ipv6 then + return value + else + return nil, err_tab_adv(self) .. err_ipv6_plain + end +end +function ipn6.write(self, section, value) + if usev6:formvalue(section) ~= "1" + or src6:formvalue(section) ~= "network" then + -- ignore if IPv4 selected OR + -- ignore everything except "network" + return true + else + -- set also as "interface" for monitoring events changes/hotplug + self.map:set(section, "interface", value) + self.map:del(section, self.option) -- delete "ipv6_network" helper + return self.map:set(section, "ip_network", value) -- and write "ip_network" + end +end +function ipn6.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4 - ip_url (default "checkip.dyndns.com") -- ############################ +iurl4 = ns:taboption("advanced", Value, "ipv4_url", + translate("URL to detect") .. " [IPv4]", + translate("Defines the Web page to read systems IPv4-Address from") ) +iurl4:depends("ipv4_source", "web") +iurl4.default = "http://checkip.dyndns.com" +function iurl4.cfgvalue(self, section) + return DDNS.read_value(self, section, "ip_url") +end +function iurl4.validate(self, value) + if usev6:formvalue(section) == "1" + or src4:formvalue(section) ~= "web" then + -- ignore if IPv6 selected OR + -- ignore everything except "web" + return "" + elseif not value or #value == 0 then + return nil, err_tab_adv(self) .. translate("missing / required") + end + + local url = DDNS.parse_url(value) + if not (url.scheme == "http" or url.scheme == "https") then + return nil, err_tab_adv(self) .. translate("must start with 'http://'") + elseif not url.host then + return nil, err_tab_adv(self) .. " " .. translate("missing / required") + elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then + return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host + else + return value + end +end +function iurl4.write(self, section, value) + if usev6:formvalue(section) == "1" + or src4:formvalue(section) ~= "web" then + -- ignore if IPv6 selected OR + -- ignore everything except "web" + return true + else + self.map:del(section, self.option) -- delete "ipv4_url" helper + return self.map:set(section, "ip_url", value) -- and write "ip_url" + end +end +function iurl4.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv6 - ip_url (default "checkipv6.dyndns.com") -- ########################## +iurl6 = ns:taboption("advanced", Value, "ipv6_url", + translate("URL to detect") .. " [IPv6]" ) +iurl6:depends("ipv6_source", "web") +iurl6.default = "http://checkipv6.dyndns.com" +if DDNS.has_ipv6 then + iurl6.description = translate("Defines the Web page to read systems IPv6-Address from") +else + iurl6.description = err_ipv6_other +end +function iurl6.cfgvalue(self, section) + return DDNS.read_value(self, section, "ip_url") +end +function iurl6.validate(self, value) + if usev6:formvalue(section) ~= "1" + or src6:formvalue(section) ~= "web" then + -- ignore if IPv4 selected OR + -- ignore everything except "web" + return "" + elseif not DDNS.has_ipv6 then + return nil, err_tab_adv(self) .. err_ipv6_plain + elseif not value or #value == 0 then + return nil, err_tab_adv(self) .. translate("missing / required") + end + + local url = DDNS.parse_url(value) + if not (url.scheme == "http" or url.scheme == "https") then + return nil, err_tab_adv(self) .. translate("must start with 'http://'") + elseif not url.host then + return nil, err_tab_adv(self) .. " " .. translate("missing / required") + elseif SYS.call([[nslookup ]] .. url.host .. [[>/dev/null 2>&1]]) ~= 0 then + return nil, err_tab_adv(self) .. translate("can not resolve host: ") .. url.host + else + return value + end +end +function iurl6.write(self, section, value) + if usev6:formvalue(section) ~= "1" + or src6:formvalue(section) ~= "web" then + -- ignore if IPv4 selected OR + -- ignore everything except "web" + return true + else + self.map:del(section, self.option) -- delete "ipv6_url" helper + return self.map:set(section, "ip_url", value) -- and write "ip_url" + end +end +function iurl6.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4 + IPv6 - ip_interface -- ############################################## +ipi = ns:taboption("advanced", ListValue, "ip_interface", + translate("Interface"), + translate("Defines the interface to read systems IP-Address from") ) +ipi:depends("ipv4_source", "interface") -- IPv4 +ipi:depends("ipv6_source", "interface") -- or IPv6 +for _, v in pairs(SYS.net.devices()) do + -- show only interface set to a network + -- and ignore loopback + net = WADM.iface_get_network(v) + if net and net ~= "loopback" then + ipi:value(v) + end +end +function ipi.validate(self, value) + local fusev6 = usev6:formvalue(section) + if (fusev6 ~= "1" and src4:formvalue(section) ~= "interface") + or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then + return "" + else + return value + end +end +function ipi.write(self, section, value) + local fusev6 = usev6:formvalue(section) + if (fusev6 ~= "1" and src4:formvalue(section) ~= "interface") + or (fusev6 == "1" and src6:formvalue(section) ~= "interface") then + return true + else + -- get network from device to + -- set also as "interface" for monitoring events changes/hotplug + local net = WADM.iface_get_network(value) + self.map:set(section, "interface", net) + return self.map:set(section, self.option, value) + end +end +function ipi.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4 + IPv6 - ip_script -- ################################################# +ips = ns:taboption("advanced", Value, "ip_script", + translate("Script"), + translate("User defined script to read systems IP-Address") ) +ips:depends("ipv4_source", "script") -- IPv4 +ips:depends("ipv6_source", "script") -- or IPv6 +ips.placeholder = "/path/to/script.sh" +function ips.validate(self, value) + local fusev6 = usev6:formvalue(section) + local split + if value then split = UTIL.split(value, " ") end + + if (fusev6 ~= "1" and src4:formvalue(section) ~= "script") + or (fusev6 == "1" and src6:formvalue(section) ~= "script") then + return "" + elseif not value or not (#value > 0) or not NXFS.access(split[1], "x") then + return nil, err_tab_adv(self) .. + translate("not found or not executable - Sample: '/path/to/script.sh'") + else + return value + end +end +function ips.write(self, section, value) + local fusev6 = usev6:formvalue(section) + if (fusev6 ~= "1" and src4:formvalue(section) ~= "script") + or (fusev6 == "1" and src6:formvalue(section) ~= "script") then + return true + else + return self.map:set(section, self.option, value) + end +end +function ips.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4 - interface - default "wan" -- ######################################## +-- event network to monitor changes/hotplug/dynamic_dns_updater.sh +-- only needs to be set if "ip_source"="web" or "script" +-- if "ip_source"="network" or "interface" we use their network +eif4 = ns:taboption("advanced", ListValue, "ipv4_interface", + translate("Event Network") .. " [IPv4]", + translate("Network on which the ddns-updater scripts will be started") ) +eif4:depends("ipv4_source", "web") +eif4:depends("ipv4_source", "script") +eif4.default = "wan" +WADM.cbi_add_networks(eif4) +function eif4.cfgvalue(self, section) + return DDNS.read_value(self, section, "interface") +end +function eif4.validate(self, value) + local fsrc4 = src4:formvalue(section) or "" + if usev6:formvalue(section) == "1" + or fsrc4 == "network" + or fsrc4 == "interface" then + return "" -- ignore IPv6, network, interface + else + return value + end +end +function eif4.write(self, section, value) + local fsrc4 = src4:formvalue(section) or "" + if usev6:formvalue(section) == "1" + or fsrc4 == "network" + or fsrc4 == "interface" then + return true -- ignore IPv6, network, interface + else + self.map:del(section, self.option) -- delete "ipv4_interface" helper + return self.map:set(section, "interface", value) -- and write "interface" + end +end +function eif4.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv6 - interface - default "wan6" -- ####################################### +-- event network to monitor changes/hotplug +-- only needs to be set if "ip_source"="web" or "script" +-- if "ip_source"="network" or "interface" we use their network +eif6 = ns:taboption("advanced", ListValue, "ipv6_interface", + translate("Event Network") .. " [IPv6]" ) +eif6:depends("ipv6_source", "web") +eif6:depends("ipv6_source", "script") +eif6.default = "wan6" +WADM.cbi_add_networks(eif6) +if not DDNS.has_ipv6 then + eif6.description = err_ipv6_other +else + eif6.description = translate("Network on which the ddns-updater scripts will be started") +end +function eif6.cfgvalue(self, section) + return DDNS.read_value(self, section, "interface") +end +function eif6.validate(self, value) + local fsrc6 = src6:formvalue(section) or "" + if usev6:formvalue(section) ~= "1" + or fsrc6 == "network" + or fsrc6 == "interface" then + return "" -- ignore IPv4, network, interface + elseif not DDNS.has_ipv6 then + return nil, err_tab_adv(self) .. err_ipv6_plain + else + return value + end +end +function eif6.write(self, section, value) + local fsrc6 = src6:formvalue(section) or "" + if usev6:formvalue(section) ~= "1" + or fsrc6 == "network" + or fsrc6 == "interface" then + return true -- ignore IPv4, network, interface + else + self.map:del(section, self.option) -- delete "ipv6_interface" helper + return self.map:set(section, "interface", value) -- and write "interface" + end +end +function eif6.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- IPv4/IPv6 - bind_network -- ################################################ +if DDNS.has_bindnet or ( ( m:get(section, "bind_network") or "" ) ~= "" ) then + bnet = ns:taboption("advanced", ListValue, "bind_network", + translate("Bind Network") ) + bnet:depends("ipv4_source", "web") + bnet:depends("ipv6_source", "web") + bnet.default = "" + bnet:value("", translate("-- default --")) + WADM.cbi_add_networks(bnet) + function bnet.cfgvalue(self, section) + local value = AbstractValue.cfgvalue(self, section) + if not DDNS.has_bindnet and value ~= "" then + self.description = bold_on .. font_red .. + translate("Binding to a specific network not supported") .. font_off .. "
                                                                      " .. + translate("please set to 'default'") .. " !" .. bold_off + else + self.description = translate("OPTIONAL: Network to use for communication") .. + "
                                                                      " .. translate("Casual users should not change this setting") + end + return value + end + function bnet.validate(self, value) + if ( (value ~= "") and DDNS.has_bindnet ) or (value == "") then return value end + return nil, err_tab_adv(self) .. translate("Binding to a specific network not supported") .. " !" + end + function bnet.parse(self, section, novld) + DDNS.value_parse(self, section, novld) + end +end + +-- IPv4 + IPv6 - force_ipversion -- ########################################### +-- optional to force wget/curl and host to use only selected IP version +-- command parameter "-4" or "-6" +if DDNS.has_forceip or ( ( m:get(section, "force_ipversion") or "0" ) ~= "0" ) then + fipv = ns:taboption("advanced", Flag, "force_ipversion", + translate("Force IP Version") ) + fipv.orientation = "horizontal" + function fipv.cfgvalue(self, section) + local value = AbstractValue.cfgvalue(self, section) + if not DDNS.has_forceip and value ~= "0" then + self.description = bold_on .. font_red .. + translate("Force IP Version not supported") .. font_off .. "
                                                                      " .. + translate("please disable") .. " !" .. bold_off + else + self.description = translate("OPTIONAL: Force the usage of pure IPv4/IPv6 only communication.") + end + return value + end + function fipv.validate(self, value) + if (value == "1" and DDNS.has_forceip) or value == "0" then return value end + return nil, err_tab_adv(self) .. translate("Force IP Version not supported") + end +end + +-- IPv4 + IPv6 - dns_server -- ################################################ +-- optional DNS Server to use resolving my IP +if DDNS.has_dnsserver or ( ( m:get(section, "dns_server") or "" ) ~= "" ) then + dns = ns:taboption("advanced", Value, "dns_server", + translate("DNS-Server"), + translate("OPTIONAL: Use non-default DNS-Server to detect 'Registered IP'.") .. "
                                                                      " .. + translate("Format: IP or FQDN")) + dns.placeholder = "mydns.lan" + function dns.validate(self, value) + -- if .datatype is set, then it is checked before calling this function + if not value or (#value == 0) then + return "" -- ignore on empty + elseif not DDNS.has_dnsserver then + return nil, err_tab_adv(self) .. translate("Specifying a DNS-Server is not supported") + elseif not DTYP.host(value) then + return nil, err_tab_adv(self) .. translate("use hostname, FQDN, IPv4- or IPv6-Address") + else + local ipv6 = usev6:formvalue(section) or "0" + local force = fipv:formvalue(section) or "0" + local command = CTRL.luci_helper .. [[ -]] + if (ipv6 == 1) then command = command .. [[6]] end + if (force == 1) then command = command .. [[f]] end + command = command .. [[d ]] .. value .. [[ -- verify_dns]] + + local ret = SYS.call(command) + if ret == 0 then return value -- everything OK + elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host") + elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect") + elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched") + else return nil, err_tab_adv(self) .. translate("unspecific error") + end + end + end + function dns.parse(self, section, novld) + DDNS.value_parse(self, section, novld) + end +end + +-- IPv4 + IPv6 - force_dnstcp -- ############################################## +if DDNS.has_bindhost or ( ( m:get(section, "force_dnstcp") or "0" ) ~= "0" ) then + tcp = ns:taboption("advanced", Flag, "force_dnstcp", + translate("Force TCP on DNS") ) + tcp.orientation = "horizontal" + function tcp.cfgvalue(self, section) + local value = AbstractValue.cfgvalue(self, section) + if not DDNS.has_bindhost and value ~= "0" then + self.description = bold_on .. font_red .. + translate("DNS requests via TCP not supported") .. font_off .. "
                                                                      " .. + translate("please disable") .. " !" .. bold_off + else + self.description = translate("OPTIONAL: Force the use of TCP instead of default UDP on DNS requests.") + end + return value + end + function tcp.validate(self, value) + if (value == "1" and DDNS.has_bindhost ) or value == "0" then + return value + end + return nil, err_tab_adv(self) .. translate("DNS requests via TCP not supported") + end +end + +-- IPv4 + IPv6 - proxy -- ##################################################### +-- optional Proxy to use for http/https requests [user:password@]proxyhost[:port] +if DDNS.has_proxy or ( ( m:get(section, "proxy") or "" ) ~= "" ) then + pxy = ns:taboption("advanced", Value, "proxy", + translate("PROXY-Server") ) + pxy.placeholder="user:password@myproxy.lan:8080" + function pxy.cfgvalue(self, section) + local value = AbstractValue.cfgvalue(self, section) + if not DDNS.has_proxy and value ~= "" then + self.description = bold_on .. font_red .. + translate("PROXY-Server not supported") .. font_off .. "
                                                                      " .. + translate("please remove entry") .. "!" .. bold_off + else + self.description = translate("OPTIONAL: Proxy-Server for detection and updates.") .. "
                                                                      " .. + translate("Format") .. ": " .. bold_on .. "[user:password@]proxyhost:port" .. bold_off .. "
                                                                      " .. + translate("IPv6 address must be given in square brackets") .. ": " .. + bold_on .. " [2001:db8::1]:8080" .. bold_off + end + return value + end + function pxy.validate(self, value) + -- if .datatype is set, then it is checked before calling this function + if not value or (#value == 0) then + return "" -- ignore on empty + elseif DDNS.has_proxy then + local ipv6 = usev6:formvalue(section) or "0" + local force = fipv:formvalue(section) or "0" + local command = CRTL.luci_helper .. [[ -]] + if (ipv6 == 1) then command = command .. [[6]] end + if (force == 1) then command = command .. [[f]] end + command = command .. [[p ]] .. value .. [[ -- verify_proxy]] + local ret = SYS.call(command) + if ret == 0 then return value + elseif ret == 2 then return nil, err_tab_adv(self) .. translate("nslookup can not resolve host") + elseif ret == 3 then return nil, err_tab_adv(self) .. translate("nc (netcat) can not connect") + elseif ret == 4 then return nil, err_tab_adv(self) .. translate("Forced IP Version don't matched") + elseif ret == 5 then return nil, err_tab_adv(self) .. translate("proxy port missing") + else return nil, err_tab_adv(self) .. translate("unspecific error") + end + else + return nil, err_tab_adv(self) .. translate("PROXY-Server not supported") + end + end + function pxy.parse(self, section, novld) + DDNS.value_parse(self, section, novld) + end +end + +-- use_syslog -- ############################################################## +slog = ns:taboption("advanced", ListValue, "use_syslog", + translate("Log to syslog"), + translate("Writes log messages to syslog. Critical Errors will always be written to syslog.") ) +slog.default = "2" +slog:value("0", translate("No logging")) +slog:value("1", translate("Info")) +slog:value("2", translate("Notice")) +slog:value("3", translate("Warning")) +slog:value("4", translate("Error")) +function slog.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- use_logfile -- ############################################################# +logf = ns:taboption("advanced", Flag, "use_logfile", + translate("Log to file"), + translate("Writes detailed messages to log file. File will be truncated automatically.") .. "
                                                                      " .. + translate("File") .. [[: "]] .. logdir .. [[/]] .. section .. [[.log"]] ) +logf.orientation = "horizontal" +logf.default = "1" -- if not defined write to log by default + +-- TAB: Timer #################################################################################### +-- check_interval -- ########################################################## +ci = ns:taboption("timer", Value, "check_interval", + translate("Check Interval") ) +ci.template = "ddns/detail_value" +ci.default = "10" +function ci.validate(self, value) + if not DTYP.uinteger(value) + or tonumber(value) < 1 then + return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds") + end + + local secs = DDNS.calc_seconds(value, cu:formvalue(section)) + if secs >= 300 then + return value + else + return nil, err_tab_timer(self) .. translate("minimum value 5 minutes == 300 seconds") + end +end +function ci.write(self, section, value) + -- remove when default + local secs = DDNS.calc_seconds(value, cu:formvalue(section)) + if secs ~= 600 then --default 10 minutes + return self.map:set(section, self.option, value) + else + self.map:del(section, "check_unit") + return self.map:del(section, self.option) + end +end +function ci.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- check_unit -- ############################################################## +cu = ns:taboption("timer", ListValue, "check_unit", "not displayed, but needed otherwise error", + translate("Interval to check for changed IP" .. "
                                                                      " .. + "Values below 5 minutes == 300 seconds are not supported") ) +cu.template = "ddns/detail_lvalue" +cu.default = "minutes" +cu:value("seconds", translate("seconds")) +cu:value("minutes", translate("minutes")) +cu:value("hours", translate("hours")) +--cu:value("days", translate("days")) +function cu.write(self, section, value) + -- remove when default + local secs = DDNS.calc_seconds(ci:formvalue(section), value) + if secs ~= 600 then --default 10 minutes + return self.map:set(section, self.option, value) + else + return true + end +end +function cu.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- force_interval (modified) -- ############################################### +fi = ns:taboption("timer", Value, "force_interval", + translate("Force Interval") ) +fi.template = "ddns/detail_value" +fi.default = "72" -- see dynamic_dns_updater.sh script +--fi.rmempty = false -- validate ourselves for translatable error messages +function fi.validate(self, value) + if not DTYP.uinteger(value) + or tonumber(value) < 0 then + return nil, err_tab_timer(self) .. translate("minimum value '0'") + end + + local force_s = DDNS.calc_seconds(value, fu:formvalue(section)) + if force_s == 0 then + return value + end + + local ci_value = ci:formvalue(section) + if not DTYP.uinteger(ci_value) then + return "" -- ignore because error in check_interval above + end + + local check_s = DDNS.calc_seconds(ci_value, cu:formvalue(section)) + if force_s >= check_s then + return value + end + + return nil, err_tab_timer(self) .. translate("must be greater or equal 'Check Interval'") +end +function fi.write(self, section, value) + -- simulate rmempty=true remove default + local secs = DDNS.calc_seconds(value, fu:formvalue(section)) + if secs ~= 259200 then --default 72 hours == 3 days + return self.map:set(section, self.option, value) + else + self.map:del(section, "force_unit") + return self.map:del(section, self.option) + end +end +function fi.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- force_unit -- ############################################################## +fu = ns:taboption("timer", ListValue, "force_unit", "not displayed, but needed otherwise error", + translate("Interval to force updates send to DDNS Provider" .. "
                                                                      " .. + "Setting this parameter to 0 will force the script to only run once" .. "
                                                                      " .. + "Values lower 'Check Interval' except '0' are not supported") ) +fu.template = "ddns/detail_lvalue" +fu.default = "hours" +--fu.rmempty = false -- want to control write process +--fu:value("seconds", translate("seconds")) +fu:value("minutes", translate("minutes")) +fu:value("hours", translate("hours")) +fu:value("days", translate("days")) +function fu.write(self, section, value) + -- simulate rmempty=true remove default + local secs = DDNS.calc_seconds(fi:formvalue(section), value) + if secs ~= 259200 and secs ~= 0 then --default 72 hours == 3 days + return self.map:set(section, self.option, value) + else + return true + end +end +function fu.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- retry_count -- ############################################################# +rc = ns:taboption("timer", Value, "retry_count") +rc.title = translate("Error Retry Counter") +rc.description = translate("On Error the script will stop execution after given number of retrys") + .. "
                                                                      " + .. translate("The default setting of '0' will retry infinite.") +rc.default = "0" +function rc.validate(self, value) + if not DTYP.uinteger(value) then + return nil, err_tab_timer(self) .. translate("minimum value '0'") + else + return value + end +end +function rc.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- retry_interval -- ########################################################## +ri = ns:taboption("timer", Value, "retry_interval", + translate("Error Retry Interval") ) +ri.template = "ddns/detail_value" +ri.default = "60" +function ri.validate(self, value) + if not DTYP.uinteger(value) + or tonumber(value) < 1 then + return nil, err_tab_timer(self) .. translate("minimum value '1'") + else + return value + end +end +function ri.write(self, section, value) + -- simulate rmempty=true remove default + local secs = DDNS.calc_seconds(value, ru:formvalue(section)) + if secs ~= 60 then --default 60seconds + return self.map:set(section, self.option, value) + else + self.map:del(section, "retry_unit") + return self.map:del(section, self.option) + end +end +function ri.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- retry_unit -- ############################################################## +ru = ns:taboption("timer", ListValue, "retry_unit", "not displayed, but needed otherwise error", + translate("On Error the script will retry the failed action after given time") ) +ru.template = "ddns/detail_lvalue" +ru.default = "seconds" +--ru.rmempty = false -- want to control write process +ru:value("seconds", translate("seconds")) +ru:value("minutes", translate("minutes")) +--ru:value("hours", translate("hours")) +--ru:value("days", translate("days")) +function ru.write(self, section, value) + -- simulate rmempty=true remove default + local secs = DDNS.calc_seconds(ri:formvalue(section), value) + if secs ~= 60 then --default 60seconds + return self.map:set(section, self.option, value) + else + return true -- will be deleted by retry_interval + end +end +function ru.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- TAB: LogView ################################################################################## +lv = ns:taboption("logview", DummyValue, "_logview") +lv.template = "ddns/detail_logview" +lv.inputtitle = translate("Read / Reread log file") +lv.rows = 50 +function lv.cfgvalue(self, section) + local lfile=logdir .. "/" .. section .. ".log" + if NXFS.access(lfile) then + return lfile .. "\n" .. translate("Please press [Read] button") + end + return lfile .. "\n" .. translate("File not found or empty") +end + +return m diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/global.lua b/luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/global.lua new file mode 100644 index 0000000..9dc0857 --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/global.lua @@ -0,0 +1,121 @@ +-- Copyright 2014 Christian Schoenebeck +-- Licensed to the public under the Apache License 2.0. + +local NX = require "nixio" +local NXFS = require "nixio.fs" +local DISP = require "luci.dispatcher" +local SYS = require "luci.sys" +local CTRL = require "luci.controller.ddns" -- this application's controller +local DDNS = require "luci.tools.ddns" -- ddns multiused functions + +-- cbi-map definition -- ####################################################### +local m = Map("ddns") +m.title = CTRL.app_title_back() +m.description = CTRL.app_description() +m.redirect = DISP.build_url("admin", "services", "ddns") + +function m.commit_handler(self) + if self.changed then -- changes ? + local command = CTRL.luci_helper .. " -- reload" + os.execute(command) -- reload configuration + end +end + +-- cbi-section definition -- ################################################### +local ns = m:section( NamedSection, "global", "ddns", + translate("Global Settings"), + translate("Configure here the details for all Dynamic DNS services including this LuCI application.") + .. [[
                                                                      ]] + .. translate("It is NOT recommended for casual users to change settings on this page.") + .. [[
                                                                      ]] + .. [[]] + .. translate("For detailed information about parameter settings look here.") + .. [[]] + ) + +-- section might not exist +function ns.cfgvalue(self, section) + if not self.map:get(section) then + self.map:set(section, nil, self.sectiontype) + end + return self.map:get(section) +end + +-- upd_privateip -- ########################################################### +local ali = ns:option(Flag, "upd_privateip") +ali.title = translate("Allow non-public IP's") +ali.description = translate("Non-public and by default blocked IP's") .. ":" + .. [[
                                                                      IPv4: ]] + .. "0/8, 10/8, 100.64/10, 127/8, 169.254/16, 172.16/12, 192.168/16" + .. [[
                                                                      IPv6: ]] + .. "::/32, f000::/4" +ali.default = "0" + +-- ddns_dateformat -- ######################################################### +local df = ns:option(Value, "ddns_dateformat") +df.title = translate("Date format") +df.description = [[]] + .. translate("For supported codes look here") + .. [[]] +df.template = "ddns/global_value" +df.default = "%F %R" +df.date_string = "" +function df.cfgvalue(self, section) + local value = AbstractValue.cfgvalue(self, section) or self.default + local epoch = os.time() + self.date_string = DDNS.epoch2date(epoch, value) + return value +end +function df.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- ddns_rundir -- ############################################################# +local rd = ns:option(Value, "ddns_rundir") +rd.title = translate("Status directory") +rd.description = translate("Directory contains PID and other status information for each running section") +rd.default = "/var/run/ddns" +-- no need to validate. if empty default is used everything else created by dns-scripts +function rd.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- ddns_logdir -- ############################################################# +local ld = ns:option(Value, "ddns_logdir") +ld.title = translate("Log directory") +ld.description = translate("Directory contains Log files for each running section") +ld.default = "/var/log/ddns" +-- no need to validate. if empty default is used everything else created by dns-scripts +function ld.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- ddns_loglines -- ########################################################### +local ll = ns:option(Value, "ddns_loglines") +ll.title = translate("Log length") +ll.description = translate("Number of last lines stored in log files") +ll.default = "250" +function ll.validate(self, value) + local n = tonumber(value) + if not n or math.floor(n) ~= n or n < 1 then + return nil, self.title .. ": " .. translate("minimum value '1'") + end + return value +end +function ll.parse(self, section, novld) + DDNS.value_parse(self, section, novld) +end + +-- use_curl -- ################################################################ +if (SYS.call([[ grep -i "\+ssl" /usr/bin/wget >/dev/null 2>&1 ]]) == 0) +and NXFS.access("/usr/bin/curl") then + local pc = ns:option(Flag, "use_curl") + pc.title = translate("Use cURL") + pc.description = translate("If both cURL and GNU Wget are installed, Wget is used by default.") + .. [[
                                                                      ]] + .. translate("To use cURL activate this option.") + pc.orientation = "horizontal" + pc.default = "0" +end + +return m diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/hints.lua b/luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/hints.lua new file mode 100644 index 0000000..df39a3a --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/hints.lua @@ -0,0 +1,166 @@ +-- Copyright 2014-2016 Christian Schoenebeck +-- Licensed to the public under the Apache License 2.0. + +local DISP = require "luci.dispatcher" +local SYS = require "luci.sys" +local CTRL = require "luci.controller.ddns" -- this application's controller +local DDNS = require "luci.tools.ddns" -- ddns multiused functions + +-- html constants +font_red = [[]] +font_off = [[]] +bold_on = [[]] +bold_off = [[]] + +-- cbi-map definition -- ####################################################### +m = Map("ddns") +m.title = CTRL.app_title_back() +m.description = CTRL.app_description() +m.redirect = DISP.build_url("admin", "services", "ddns") + +-- SimpleSection definition -- ################################################# +-- show Hints to optimize installation and script usage +s = m:section( SimpleSection, + translate("Hints"), + translate("Below a list of configuration tips for your system to run Dynamic DNS updates without limitations") ) + +-- ddns-scripts needs to be updated for full functionality +if not CTRL.service_ok() then + local so = s:option(DummyValue, "_update_needed") + so.titleref = DISP.build_url("admin", "system", "packages") + so.rawhtml = true + so.title = font_red .. bold_on .. + translate("Software update required") .. bold_off .. font_off + so.value = translate("The currently installed 'ddns-scripts' package did not support all available settings.") .. + "
                                                                      " .. + translate("Please update to the current version!") +end + +-- DDNS Service disabled +if not SYS.init.enabled("ddns") then + local se = s:option(DummyValue, "_not_enabled") + se.titleref = DISP.build_url("admin", "system", "startup") + se.rawhtml = true + se.title = bold_on .. + translate("DDNS Autostart disabled") .. bold_off + se.value = translate("Currently DDNS updates are not started at boot or on interface events." .. "
                                                                      " .. + "This is the default if you run DDNS scripts by yourself (i.e. via cron with force_interval set to '0')" ) +end + +-- No IPv6 support +if not DDNS.has_ipv6 then + local v6 = s:option(DummyValue, "_no_ipv6") + v6.titleref = 'http://www.openwrt.org" target="_blank' + v6.rawhtml = true + v6.title = bold_on .. + translate("IPv6 not supported") .. bold_off + v6.value = translate("IPv6 is currently not (fully) supported by this system" .. "
                                                                      " .. + "Please follow the instructions on OpenWrt's homepage to enable IPv6 support" .. "
                                                                      " .. + "or update your system to the latest OpenWrt Release") +end + +-- No HTTPS support +if not DDNS.has_ssl then + local sl = s:option(DummyValue, "_no_https") + sl.titleref = DISP.build_url("admin", "system", "packages") + sl.rawhtml = true + sl.title = bold_on .. + translate("HTTPS not supported") .. bold_off + sl.value = translate("Neither GNU Wget with SSL nor cURL installed to support secure updates via HTTPS protocol.") .. + "
                                                                      - " .. + translate("You should install 'wget' or 'curl' or 'uclient-fetch' with 'libustream-*ssl' package.") .. + "
                                                                      - " .. + translate("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.") +end + +-- No bind_network +if not DDNS.has_bindnet then + local bn = s:option(DummyValue, "_no_bind_network") + bn.titleref = DISP.build_url("admin", "system", "packages") + bn.rawhtml = true + bn.title = bold_on .. + translate("Binding to a specific network not supported") .. bold_off + bn.value = translate("Neither GNU Wget with SSL nor cURL installed to select a network to use for communication.") .. + "
                                                                      - " .. + translate("You should install 'wget' or 'curl' package.") .. + "
                                                                      - " .. + translate("GNU Wget will use the IP of given network, cURL will use the physical interface.") .. + "
                                                                      - " .. + translate("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.") +end + +-- currently only cURL possibly without proxy support +if not DDNS.has_proxy then + local px = s:option(DummyValue, "_no_proxy") + px.titleref = DISP.build_url("admin", "system", "packages") + px.rawhtml = true + px.title = bold_on .. + translate("cURL without Proxy Support") .. bold_off + px.value = translate("cURL is installed, but libcurl was compiled without proxy support.") .. + "
                                                                      - " .. + translate("You should install 'wget' or 'uclient-fetch' package or replace libcurl.") .. + "
                                                                      - " .. + translate("In some versions cURL/libcurl in OpenWrt is compiled without proxy support.") +end + +-- "Force IP Version not supported" +if not DDNS.has_forceip then + local fi = s:option(DummyValue, "_no_force_ip") + fi.titleref = DISP.build_url("admin", "system", "packages") + fi.rawhtml = true + fi.title = bold_on .. + translate("Force IP Version not supported") .. bold_off + local value = translate("BusyBox's nslookup and Wget do not support to specify " .. + "the IP version to use for communication with DDNS Provider!") + if not (DDNS.has_wgetssl or DDNS.has_curl or DDNS.has_fetch) then + value = value .. "
                                                                      - " .. + translate("You should install 'wget' or 'curl' or 'uclient-fetch' package.") + end + if not DDNS.has_bindhost then + value = value .. "
                                                                      - " .. + translate("You should install 'bind-host' or 'knot-host' or 'drill' package for DNS requests.") + end + fi.value = value +end + +-- "DNS requests via TCP not supported" +if not DDNS.has_bindhost then + local dt = s:option(DummyValue, "_no_dnstcp") + dt.titleref = DISP.build_url("admin", "system", "packages") + dt.rawhtml = true + dt.title = bold_on .. + translate("DNS requests via TCP not supported") .. bold_off + dt.value = translate("BusyBox's nslookup and hostip do not support to specify to use TCP " .. + "instead of default UDP when requesting DNS server!") .. + "
                                                                      - " .. + translate("You should install 'bind-host' or 'knot-host' or 'drill' package for DNS requests.") +end + +-- nslookup compiled with musl produce problems when using +if not DDNS.has_dnsserver then + local ds = s:option(DummyValue, "_no_dnsserver") + ds.titleref = DISP.build_url("admin", "system", "packages") + ds.rawhtml = true + ds.title = bold_on .. + translate("Using specific DNS Server not supported") .. bold_off + ds.value = translate("BusyBox's nslookup in the current compiled version " .. + "does not handle given DNS Servers correctly!") .. + "
                                                                      - " .. + translate("You should install 'bind-host' or 'knot-host' or 'drill' or 'hostip' package, " .. + "if you need to specify a DNS server to detect your registered IP.") +end + +-- certificates installed +if DDNS.has_ssl and not DDNS.has_cacerts then + local ca = s:option(DummyValue, "_no_certs") + ca.titleref = DISP.build_url("admin", "system", "packages") + ca.rawhtml = true + ca.title = bold_on .. + translate("No certificates found") .. bold_off + ca.value = translate("If using secure communication you should verify server certificates!") .. + "
                                                                      - " .. + translate("Install 'ca-certificates' package or needed certificates " .. + "by hand into /etc/ssl/certs default directory") +end + +return m diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/overview.lua b/luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/overview.lua new file mode 100644 index 0000000..337c6e7 --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/model/cbi/ddns/overview.lua @@ -0,0 +1,219 @@ +-- Copyright 2014-2016 Christian Schoenebeck +-- Licensed to the public under the Apache License 2.0. + +local NXFS = require "nixio.fs" +local DISP = require "luci.dispatcher" +local HTTP = require "luci.http" +local SYS = require "luci.sys" +local CTRL = require "luci.controller.ddns" -- this application's controller +local DDNS = require "luci.tools.ddns" -- ddns multiused functions + +local show_hints = not (DDNS.has_ipv6 -- IPv6 support + and DDNS.has_ssl -- HTTPS support + and DDNS.has_proxy -- Proxy support + and DDNS.has_bindhost -- DNS TCP support + and DDNS.has_forceip -- Force IP version + and DDNS.has_dnsserver -- DNS server support + and DDNS.has_bindnet -- Bind to network/interface + and DDNS.has_cacerts -- certificates installed at /etc/ssl/certs + ) +local not_enabled = not SYS.init.enabled("ddns") +local need_update = not CTRL.service_ok() + +-- html constants +font_red = [[]] +font_off = [[]] +bold_on = [[]] +bold_off = [[]] + +-- cbi-map definition -- ####################################################### +m = Map("ddns") +m.title = CTRL.app_title_main() +m.description = CTRL.app_description() + +m.on_after_commit = function(self) + if self.changed then -- changes ? + local command = CTRL.luci_helper + if SYS.init.enabled("ddns") then -- ddns service enabled, restart all + command = command .. " -- restart" + os.execute(command) + else -- ddns service disabled, send SIGHUP to running + command = command .. " -- reload" + os.execute(command) + end + end +end + +-- SimpleSection definition -- ################################################## +-- with all the JavaScripts we need for "a good Show" +a = m:section( SimpleSection ) +a.template = "ddns/overview_status" + +-- SimpleSection definition -- ################################################# +-- show Hints to optimize installation and script usage +if not_enabled then + + s = m:section( SimpleSection, translate("Hints") ) + + -- DDNS Service disabled + if not_enabled then + local dv = s:option(DummyValue, "_not_enabled") + dv.titleref = DISP.build_url("admin", "system", "startup") + dv.rawhtml = true + dv.title = bold_on .. + translate("DDNS Autostart disabled") .. bold_off + dv.value = translate("Currently DDNS updates are not started at boot or on interface events." .. "
                                                                      " .. + "You can start/stop each configuration here. It will run until next reboot.") + end + +end + +local_web="

                                                                       

                                                                       

                                                                      " + +-- TableSection definition -- ################################################## +ts = m:section( TypedSection, "service", + translate("Overview"), + translate("Below is a list of DDNS configurations and their current state.") + .. "
                                                                      " + .. "
                                                                      " .. local_web ) +ts.sectionhead = translate("Configuration") +ts.template = "cbi/tblsection" +ts.addremove = true +ts.extedit = DISP.build_url("admin", "services", "ddns", "detail", "%s") +function ts.create(self, name) + AbstractSection.create(self, name) + HTTP.redirect( self.extedit:format(name) ) +end + +-- Lookup_Host and registered IP -- ################################################# +dom = ts:option(DummyValue, "_lookupIP", + translate("Lookup Hostname") .. "
                                                                      " .. translate("Registered IP") ) +dom.template = "ddns/overview_doubleline" +function dom.set_one(self, section) + local lookup = self.map:get(section, "lookup_host") or "" + if lookup ~= "" then + return lookup + else + return [[]] .. translate("config error") .. [[]] + end +end +function dom.set_two(self, section) + local lookup_host = self.map:get(section, "lookup_host") or "" + if lookup_host == "" then return "" end + local dnsserver = self.map:get(section, "dnsserver") or "" + local use_ipv6 = tonumber(self.map:get(section, "use_ipv6") or 0) + local force_ipversion = tonumber(self.map:get(section, "force_ipversion") or 0) + local force_dnstcp = tonumber(self.map:get(section, "force_dnstcp") or 0) + local is_glue = tonumber(self.map:get(section, "is_glue") or 0) + local command = CTRL.luci_helper .. [[ -]] + if (use_ipv6 == 1) then command = command .. [[6]] end + if (force_ipversion == 1) then command = command .. [[f]] end + if (force_dnstcp == 1) then command = command .. [[t]] end + if (is_glue == 1) then command = command .. [[g]] end + command = command .. [[l ]] .. lookup_host + if (#dnsserver > 0) then command = command .. [[ -d ]] .. dnsserver end + command = command .. [[ -- get_registered_ip]] + local ip = SYS.exec(command) + if ip == "" then ip = translate("no data") end + return ip +end + +-- enabled +ena = ts:option( Flag, "enabled", + translate("Enabled")) +ena.template = "ddns/overview_enabled" +ena.rmempty = false + +-- show PID and next update +upd = ts:option( DummyValue, "_update", + translate("Last Update") .. "
                                                                      " .. translate("Next Update")) +upd.template = "ddns/overview_doubleline" +function upd.set_one(self, section) -- fill Last Update + -- get/validate last update + local uptime = SYS.uptime() + local lasttime = DDNS.get_lastupd(section) + if lasttime > uptime then -- /var might not be linked to /tmp and cleared on reboot + lasttime = 0 + end + + -- no last update happen + if lasttime == 0 then + return translate("never") + + -- we read last update + else + -- calc last update + -- os.epoch - sys.uptime + lastupdate(uptime) + local epoch = os.time() - uptime + lasttime + -- use linux date to convert epoch + return DDNS.epoch2date(epoch) + end +end +function upd.set_two(self, section) -- fill Next Update + -- get enabled state + local enabled = tonumber(self.map:get(section, "enabled") or 0) + local datenext = translate("unknown error") -- formatted date of next update + + -- get force seconds + local force_interval = tonumber(self.map:get(section, "force_interval") or 72) + local force_unit = self.map:get(section, "force_unit") or "hours" + local force_seconds = DDNS.calc_seconds(force_interval, force_unit) + + -- get last update and get/validate PID + local uptime = SYS.uptime() + local lasttime = DDNS.get_lastupd(section) + if lasttime > uptime then -- /var might not be linked to /tmp and cleared on reboot + lasttime = 0 + end + local pid = DDNS.get_pid(section) + + -- calc next update + if lasttime > 0 then + local epoch = os.time() - uptime + lasttime + force_seconds + -- use linux date to convert epoch + datelast = DDNS.epoch2date(epoch) + end + + -- process running but update needs to happen + if pid > 0 and ( lasttime + force_seconds - uptime ) < 0 then + datenext = translate("Verify") + + -- run once + elseif force_seconds == 0 then + datenext = translate("Run once") + + -- no process running and NOT enabled + elseif pid == 0 and enabled == 0 then + datenext = translate("Disabled") + + -- no process running and NOT + elseif pid == 0 and enabled ~= 0 then + datenext = translate("Stopped") + end + + return datenext +end + +-- start/stop button +btn = ts:option( Button, "_startstop", + translate("Process ID") .. "
                                                                      " .. translate("Start / Stop") ) +btn.template = "ddns/overview_startstop" +function btn.cfgvalue(self, section) + local pid = DDNS.get_pid(section) + if pid > 0 then + btn.inputtitle = "PID: " .. pid + btn.inputstyle = "reset" + btn.disabled = false + elseif (self.map:get(section, "enabled") or "0") ~= "0" then + btn.inputtitle = translate("Start") + btn.inputstyle = "apply" + btn.disabled = false + else + btn.inputtitle = "----------" + btn.inputstyle = "button" + btn.disabled = true + end + return true +end + +return m diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/tools/ddns.lua b/luci-app-rooterddns/files/usr/lib/lua/luci/tools/ddns.lua new file mode 100644 index 0000000..209d9c3 --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/tools/ddns.lua @@ -0,0 +1,315 @@ +-- Copyright 2014-2016 Christian Schoenebeck +-- Licensed to the public under the Apache License 2.0. + +module("luci.tools.ddns", package.seeall) + +local NX = require "nixio" +local NXFS = require "nixio.fs" +local OPKG = require "luci.model.ipkg" +local UCI = require "luci.model.uci" +local SYS = require "luci.sys" +local UTIL = require "luci.util" + +local function _check_certs() + local _, v = NXFS.glob("/etc/ssl/certs/*.crt") + if ( v == 0 ) then _, v = NXFS.glob("/etc/ssl/certs/*.pem") end + return (v > 0) +end + +has_wgetssl = (SYS.call( [[which wget-ssl >/dev/null 2>&1]] ) == 0) -- and true or nil +has_curl = (SYS.call( [[which curl >/dev/null 2>&1]] ) == 0) +has_curlssl = (SYS.call( [[$(which curl) -V 2>&1 | grep "Protocols:" | grep -qF "https"]] ) ~= 0) +has_curlpxy = (SYS.call( [[grep -i "all_proxy" /usr/lib/libcurl.so* >/dev/null 2>&1]] ) == 0) +has_fetch = (SYS.call( [[which uclient-fetch >/dev/null 2>&1]] ) == 0) +has_fetchssl = NXFS.access("/lib/libustream-ssl.so") +has_bbwget = (SYS.call( [[$(which wget) -V 2>&1 | grep -iqF "busybox"]] ) == 0) +has_bindhost = (SYS.call( [[which host >/dev/null 2>&1]] ) == 0) + or (SYS.call( [[which khost >/dev/null 2>&1]] ) == 0) + or (SYS.call( [[which drill >/dev/null 2>&1]] ) == 0) +has_hostip = (SYS.call( [[which hostip >/dev/null 2>&1]] ) == 0) +has_nslookup = (SYS.call( [[$(which nslookup) localhost 2>&1 | grep -qF "(null)"]] ) ~= 0) +has_ipv6 = (NXFS.access("/proc/net/ipv6_route") and NXFS.access("/usr/sbin/ip6tables")) +has_ssl = (has_wgetssl or has_curlssl or (has_fetch and has_fetchssl)) +has_proxy = (has_wgetssl or has_curlpxy or has_fetch or has_bbwget) +has_forceip = (has_wgetssl or has_curl or has_fetch) -- only really needed for transfer +has_dnsserver = (has_bindhost or has_hostip or has_nslookup) +has_bindnet = (has_wgetssl or has_curl) +has_cacerts = _check_certs() + +-- function to calculate seconds from given interval and unit +function calc_seconds(interval, unit) + if not tonumber(interval) then + return nil + elseif unit == "days" then + return (tonumber(interval) * 86400) -- 60 sec * 60 min * 24 h + elseif unit == "hours" then + return (tonumber(interval) * 3600) -- 60 sec * 60 min + elseif unit == "minutes" then + return (tonumber(interval) * 60) -- 60 sec + elseif unit == "seconds" then + return tonumber(interval) + else + return nil + end +end + +-- convert epoch date to given format +function epoch2date(epoch, format) + if not format or #format < 2 then + local uci = UCI.cursor() + format = uci:get("ddns", "global", "ddns_dateformat") or "%F %R" + uci:unload("ddns") + end + format = format:gsub("%%n", "
                                                                      ") -- replace newline + format = format:gsub("%%t", " ") -- replace tab + return os.date(format, epoch) +end + +-- read lastupdate from [section].update file +function get_lastupd(section) + local uci = UCI.cursor() + local rdir = uci:get("ddns", "global", "ddns_rundir") or "/var/run/ddns" + local etime = tonumber(NXFS.readfile("%s/%s.update" % { rdir, section } ) or 0 ) + uci:unload("ddns") + return etime +end + +-- read PID from run file and verify if still running +function get_pid(section) + local uci = UCI.cursor() + local rdir = uci:get("ddns", "global", "ddns_rundir") or "/var/run/ddns" + local pid = tonumber(NXFS.readfile("%s/%s.pid" % { rdir, section } ) or 0 ) + if pid > 0 and not NX.kill(pid, 0) then + pid = 0 + end + uci:unload("ddns") + return pid +end + +-- replacement of build-in read of UCI option +-- modified AbstractValue.cfgvalue(self, section) from cbi.lua +-- needed to read from other option then current value definition +function read_value(self, section, option) + local value + if self.tag_error[section] then + value = self:formvalue(section) + else + value = self.map:get(section, option) + end + + if not value then + return nil + elseif not self.cast or self.cast == type(value) then + return value + elseif self.cast == "string" then + if type(value) == "table" then + return value[1] + end + elseif self.cast == "table" then + return { value } + end +end + +-- replacement of build-in parse of "Value" +-- modified AbstractValue.parse(self, section, novld) from cbi.lua +-- validate is called if rmempty/optional true or false +-- before write check if forcewrite, value eq default, and more +function value_parse(self, section, novld) + local fvalue = self:formvalue(section) + local fexist = ( fvalue and (#fvalue > 0) ) -- not "nil" and "not empty" + local cvalue = self:cfgvalue(section) + local rm_opt = ( self.rmempty or self.optional ) + local eq_cfg -- flag: equal cfgvalue + + -- If favlue and cvalue are both tables and have the same content + -- make them identical + if type(fvalue) == "table" and type(cvalue) == "table" then + eq_cfg = (#fvalue == #cvalue) + if eq_cfg then + for i=1, #fvalue do + if cvalue[i] ~= fvalue[i] then + eq_cfg = false + end + end + end + if eq_cfg then + fvalue = cvalue + end + end + + -- removed parameter "section" from function call because used/accepted nowhere + -- also removed call to function "transfer" + local vvalue, errtxt = self:validate(fvalue) + + -- error handling; validate return "nil" + if not vvalue then + if novld then -- and "novld" set + return -- then exit without raising an error + end + + if fexist then -- and there is a formvalue + self:add_error(section, "invalid", errtxt or self.title .. ": invalid") + return -- so data are invalid + elseif not rm_opt then -- and empty formvalue but NOT (rmempty or optional) set + self:add_error(section, "missing", errtxt or self.title .. ": missing") + return -- so data is missing + elseif errtxt then + self:add_error(section, "invalid", errtxt) + return + end +-- error ("\n option: " .. self.option .. +-- "\n fvalue: " .. tostring(fvalue) .. +-- "\n fexist: " .. tostring(fexist) .. +-- "\n cvalue: " .. tostring(cvalue) .. +-- "\n vvalue: " .. tostring(vvalue) .. +-- "\n vexist: " .. tostring(vexist) .. +-- "\n rm_opt: " .. tostring(rm_opt) .. +-- "\n eq_cfg: " .. tostring(eq_cfg) .. +-- "\n eq_def: " .. tostring(eq_def) .. +-- "\n novld : " .. tostring(novld) .. +-- "\n errtxt: " .. tostring(errtxt) ) + end + + -- lets continue with value returned from validate + eq_cfg = ( vvalue == cvalue ) -- update equal_config flag + local vexist = ( vvalue and (#vvalue > 0) ) and true or false -- not "nil" and "not empty" + local eq_def = ( vvalue == self.default ) -- equal_default flag + + -- (rmempty or optional) and (no data or equal_default) + if rm_opt and (not vexist or eq_def) then + if self:remove(section) then -- remove data from UCI + self.section.changed = true -- and push events + end + return + end + + -- not forcewrite and no changes, so nothing to write + if not self.forcewrite and eq_cfg then + return + end + + -- we should have a valid value here + assert (vvalue, "\n option: " .. self.option .. + "\n fvalue: " .. tostring(fvalue) .. + "\n fexist: " .. tostring(fexist) .. + "\n cvalue: " .. tostring(cvalue) .. + "\n vvalue: " .. tostring(vvalue) .. + "\n vexist: " .. tostring(vexist) .. + "\n rm_opt: " .. tostring(rm_opt) .. + "\n eq_cfg: " .. tostring(eq_cfg) .. + "\n eq_def: " .. tostring(eq_def) .. + "\n errtxt: " .. tostring(errtxt) ) + + -- write data to UCI; raise event only on changes + if self:write(section, vvalue) and not eq_cfg then + self.section.changed = true + end +end + +----------------------------------------------------------------------------- +-- copied from https://svn.nmap.org/nmap/nselib/url.lua +-- @author Diego Nehab +-- @author Eddie Bell +--[[ + URI parsing, composition and relative URL resolution + LuaSocket toolkit. + Author: Diego Nehab + RCS ID: $Id: url.lua,v 1.37 2005/11/22 08:33:29 diego Exp $ + parse_query and build_query added For nmap (Eddie Bell ) +]]-- +--- +-- Parses a URL and returns a table with all its parts according to RFC 2396. +-- +-- The following grammar describes the names given to the URL parts. +-- +-- ::= :///;?# +-- ::= @: +-- ::= [:] +-- :: = {/} +-- +-- +-- The leading / in / is considered part of +-- . +-- @param url URL of request. +-- @param default Table with default values for each field. +-- @return A table with the following fields, where RFC naming conventions have +-- been preserved: +-- scheme, authority, userinfo, +-- user, password, host, +-- port, path, params, +-- query, and fragment. +----------------------------------------------------------------------------- +function parse_url(url) --, default) + -- initialize default parameters + local parsed = {} +-- for i,v in base.pairs(default or parsed) do +-- parsed[i] = v +-- end + + -- remove whitespace +-- url = string.gsub(url, "%s", "") + -- get fragment + url = string.gsub(url, "#(.*)$", + function(f) + parsed.fragment = f + return "" + end) + -- get scheme. Lower-case according to RFC 3986 section 3.1. + url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", + function(s) + parsed.scheme = string.lower(s); + return "" + end) + -- get authority + url = string.gsub(url, "^//([^/]*)", + function(n) + parsed.authority = n + return "" + end) + -- get query stringing + url = string.gsub(url, "%?(.*)", + function(q) + parsed.query = q + return "" + end) + -- get params + url = string.gsub(url, "%;(.*)", + function(p) + parsed.params = p + return "" + end) + -- path is whatever was left + parsed.path = url + + local authority = parsed.authority + if not authority then + return parsed + end + authority = string.gsub(authority,"^([^@]*)@", + function(u) + parsed.userinfo = u; + return "" + end) + authority = string.gsub(authority, ":([0-9]*)$", + function(p) + if p ~= "" then + parsed.port = p + end; + return "" + end) + if authority ~= "" then + parsed.host = authority + end + + local userinfo = parsed.userinfo + if not userinfo then + return parsed + end + userinfo = string.gsub(userinfo, ":([^:]*)$", + function(p) + parsed.password = p; + return "" + end) + parsed.user = userinfo + return parsed +end diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/view/admin_status/index/ddns.htm b/luci-app-rooterddns/files/usr/lib/lua/luci/view/admin_status/index/ddns.htm new file mode 100644 index 0000000..9791065 --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/view/admin_status/index/ddns.htm @@ -0,0 +1 @@ +<%+ddns/system_status%> diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/detail_logview.htm b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/detail_logview.htm new file mode 100644 index 0000000..fd1d5be --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/detail_logview.htm @@ -0,0 +1,56 @@ + + + + +<%+cbi/valueheader%> + +
                                                                      + +<% +-- one button on top, one at the buttom +%> + /> + +

                                                                      + +<% +-- set a readable style taken from openwrt theme for textarea#syslog +-- in openwrt theme there are problems with a width of 100 so we check for theme and set to lower value +%> + +

                                                                      + +<% +-- one button on top, one at the buttom +%> + /> + +<%+cbi/valuefooter%> + diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/detail_lvalue.htm b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/detail_lvalue.htm new file mode 100644 index 0000000..b69d780 --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/detail_lvalue.htm @@ -0,0 +1,23 @@ + + + +  +<% if self.widget == "select" then %> + +<% elseif self.widget == "radio" then + local c = 0 + for i, key in pairs(self.keylist) do + c = c + 1 +%> + /> + > + ><%=self.vallist[i]%> +<% if c == self.size then c = 0 %><% if self.orientation == "horizontal" then %> <% else %>
                                                                      <% end %> +<% end end %> +<% end %> +<%+cbi/valuefooter%> + diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/detail_value.htm b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/detail_value.htm new file mode 100644 index 0000000..cbe76ab --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/detail_value.htm @@ -0,0 +1,9 @@ + + +<%+cbi/valueheader%> + /> + + diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/global_value.htm b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/global_value.htm new file mode 100644 index 0000000..23ec059 --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/global_value.htm @@ -0,0 +1,34 @@ + + +<%+cbi/valueheader%> + + + +/> +
                                                                      +
                                                                      + <%:help%><%=self.description%> +
                                                                      + <%:Current setting%>: <%=self.date_string%> +
                                                                      + + + diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_doubleline.htm b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_doubleline.htm new file mode 100644 index 0000000..1d1b4be --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_doubleline.htm @@ -0,0 +1,10 @@ + + +<%+cbi/valueheader%> + +<%=self:set_one(section)%> +
                                                                      +<%=self:set_two(section)%> + +<%+cbi/valuefooter%> + diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_enabled.htm b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_enabled.htm new file mode 100644 index 0000000..2efc125 --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_enabled.htm @@ -0,0 +1,16 @@ + + +<%+cbi/valueheader%> + + /> + + /> +> + +<%+cbi/valuefooter%> + diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_startstop.htm b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_startstop.htm new file mode 100644 index 0000000..327028c --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_startstop.htm @@ -0,0 +1,17 @@ + + +<%+cbi/valueheader%> + +<% if self:cfgvalue(section) ~= false then +-- We need to garantie that function cfgvalue run first to set missing parameters +%> + + + " style="font-size: 100%;" type="button" onclick="onclick_startstop(this.id)" + <%= + attr("name", section) .. attr("id", cbid) .. attr("value", self.inputtitle) .. ifattr(self.disabled, "disabled") + %> /> +<% end %> + +<%+cbi/valuefooter%> + diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_status.htm b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_status.htm new file mode 100644 index 0000000..b409ed0 --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/overview_status.htm @@ -0,0 +1,180 @@ + + + + + + diff --git a/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/system_status.htm b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/system_status.htm new file mode 100644 index 0000000..5bdcb03 --- /dev/null +++ b/luci-app-rooterddns/files/usr/lib/lua/luci/view/ddns/system_status.htm @@ -0,0 +1,144 @@ + + + + +
                                                                      + <%:Dynamic DNS%> + + + + + + + + + + + + +
                                                                      <%:Configuration%><%:Next Update%><%:Lookup Hostname%><%:Registered IP%><%:Network%>

                                                                      <%:Collecting data...%>
                                                                      +
                                                                      + diff --git a/luci-app-rootervpn/Makefile b/luci-app-rootervpn/Makefile new file mode 100644 index 0000000..f6bf90f --- /dev/null +++ b/luci-app-rootervpn/Makefile @@ -0,0 +1,34 @@ +#Owned by DairyMan@Whirlpool +# +#Copyright GNU act. +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-rootervpn +PKG_VERSION:=1.000 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Created by DM/makefile by Cobia@whirlpool +include $(INCLUDE_DIR)/package.mk + +define Package/luci-app-rootervpn + SECTION:=luci + CATEGORY:=LuCI + DEPENDS:=+openvpn-easy-rsa +openvpn-openssl + SUBMENU:=3. Applications + TITLE:=support for modified OpenVPN + PKGARCH:=all +endef + +define Package/luci-app-rootervpn/description + Helper scripts to enable OpenVPN support +endef + + +define Build/Compile +endef + +define Package/luci-app-rootervpn/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,luci-app-rootervpn)) diff --git a/luci-app-rootervpn/files/etc/config/rooter_recipes b/luci-app-rootervpn/files/etc/config/rooter_recipes new file mode 100644 index 0000000..68b6274 --- /dev/null +++ b/luci-app-rootervpn/files/etc/config/rooter_recipes @@ -0,0 +1,293 @@ +# +# Routed point-to-point server +# +config openvpn_recipe b_server_tun_ptp + option _description "Basic point-to-point TUN VPN Server" + option _role "server" + option dev "tun-server" + option ifconfig "10.0.0.1 10.0.0.2" + option secret "shared-secret.key" + option keepalive "10 60" + option comp_lzo "yes" + option verb "3" + option mssfix "1420" + option topology "p2p" + +# +# Routed point-to-point client +# +config openvpn_recipe b_client_tun_ptp + option _description "Basic point-to-point TUN VPN Client" + option _role "client" + option client "1" + option dev "tun0" + list remote "vpnserver.example.org" + option ifconfig "10.0.0.2 10.0.0.1" + option secret "shared-secret.key" + option nobind "1" + option comp_lzo "yes" + option verb "3" + +# +# Routed multi-client server +# +config openvpn_recipe a_server_tun + option _description "Basic TUN VPN Server" + option _role "server" + option dev "tun-server" + option topology "subnet" + option port "1194" + option proto "udp" + option server "10.0.100.0 255.255.255.0" + option ca "/etc/openvpn/placeholder/placeholder.file" + option cert "/etc/openvpn/placeholder/placeholder.file" + option key "/etc/openvpn/placeholder/placeholder.file" + option dh "/etc/openvpn/placeholder/placeholder.file" + option keepalive "10 60" + option comp_lzo "yes" + option verb "3" + option mssfix "1420" + +# +# Routed client +# +config openvpn_recipe a_client_tun + option _description "Basic TUN VPN Client" + option _role "client" + option client "1" + option dev "tun0" + option proto "udp" + list remote "vpnserver.example.org" + option remote_cert_tls "server" + option comp_lzo "yes" + option nobind "1" + option persist_key "1" + option persist_tun "1" + option verb "3" + option reneg_sec "0" + option float "1" + option auth_user_pass "/etc/openvpn/placeholder/placeholder.file" + option ca "/etc/openvpn/placeholder/placeholder.file" + option cert "/etc/openvpn/placeholder/placeholder.file" + option key "/etc/openvpn/placeholder/placeholder.file" + +# +# Multi-client ethernet bridge server +# +config openvpn_recipe c_server_tap_bridge + option _description "Basic Server-Bridge TAP VPN Server" + option _role "server" + option dev "tap-server" + option port "1194" + option server_bridge "192.168.100.1 255.255.255.0 192.168.100.128 192.168.100.254" + option ca "/etc/openvpn/placeholder/placeholder.file" + option cert "/etc/openvpn/placeholder/placeholder.file" + option key "/etc/openvpn/placeholder/placeholder.file" + option dh "/etc/openvpn/placeholder/placeholder.file" + option keepalive "10 60" + option comp_lzo "yes" + option verb "3" + option mssfix "1420" + +# +# Ethernet bridge client +# +config openvpn_recipe c_client_tap_bridge + option _description "Basic Server-Bridge TAP VPN Client" + option _role "client" + option client "1" + option dev "tap0" + list remote "vpnserver.example.org" + option auth_user_pass "/etc/openvpn/placeholder/placeholder.file" + option ca "/etc/openvpn/placeholder/placeholder.file" + option cert "/etc/openvpn/placeholder/placeholder.file" + option key "/etc/openvpn/placeholder/placeholder.file" + option remote_cert_tls "server" + option comp_lzo "yes" + option nobind "1" + option persist_key "1" + option verb "3" + option reneg_sec "0" + option float "1" + +# +# OVPN Client +# +config openvpn_recipe d_ovpn_client + option _description "Client using OVPN File" + option _role "client" + option config "/etc/openvpn/placeholder/placeholder.file" + option auth_user_pass "/etc/openvpn/placeholder/placeholder.file" + +# +# NordVpn +# +config openvpn_recipe e_nordvpn_client_tun + option _description "Predefined Client for NordVPN" + option _role "client" + option config "/etc/openvpn/placeholder/placeholder.file" + option auth_user_pass "/etc/openvpn/placeholder/placeholder.file" + +# +# Private Internet Access Client +# +config openvpn_recipe e_pia_client_tun + option _description "Predefined Client for Private Internet Access" + option _role "client" + option client "1" + option dev "tun0" + option proto "udp" + list remote "us-example.privateinternetaccess.com" + option port "1198" + option resolv_retry "infinite" + option nobind "1" + option persist_key "1" + option persist_tun "1" + option cipher "aes-128-cbc" + option auth "sha1" + option tls_client "1" + option remote_cert_tls "server" + option auth_user_pass "/etc/openvpn/placeholder/placeholder.file" + option comp_lzo "yes" + option verb "1" + option reneg_sec "0" + option crl_verify "/etc/openvpn/pia/crl.rsa.2048.pem" + option ca "/etc/openvpn/pia/ca.rsa.2048.crt" + option disable_occ "1" + +# +# Windscribe +# +config openvpn_recipe e_windscribe_client_tun + option _description "Predefined Client for Windscribe" + option _role "client" + option client "1" + option dev "tun0" + option proto "udp" + list remote "example.windscribe.com" + option port "443" + option resolv_retry "infinite" + option nobind "1" + option persist_key "1" + option persist_tun "1" + option cipher "aes-256-cbc" + option auth "sha512" + option tls_client "1" + option remote_cert_tls "server" + option key_direction "1" + option auth_user_pass "/etc/openvpn/placeholder/placeholder.file" + option comp_lzo "yes" + option verb "2" + option reneg_sec "432000" + option tls_auth "/etc/openvpn/windscribe/ta.key" + option ca "/etc/openvpn/windscribe/ca.crt" + option redirect_gateway "def1" + +# +# ProtonVPN +# +config openvpn_recipe e_proton_client_tun + option _description "Predefined Client for ProtonVPN" + option _role "client" + option client "1" + option dev "tun0" + option proto "udp" + list remote "example.protonvpn.com" + option port "1194" + option resolv_retry "infinite" + option nobind "1" + option persist_key "1" + option persist_tun "1" + option cipher "aes-256-cbc" + option auth "sha512" + option tls_client "1" + option remote_cert_tls "server" + option key_direction "1" + option auth_user_pass "/etc/openvpn/placeholder/placeholder.file" + option comp_lzo "yes" + option verb "3" + option reneg_sec "0" + option tls_auth "/etc/openvpn/placeholder/placeholder.file" + option ca "/etc/openvpn/placeholder/placeholder.file" + option redirect_gateway "def1" + +# +# Mullvad +# +config openvpn_recipe e_mullvad_client_tun + option _description "Predefined Client for Mullvad" + option _role "client" + option client "1" + option dev "tun0" + option proto "udp" + list remote "example.mullvad.net" + option port "1194" + option resolv_retry "infinite" + option nobind "1" + option persist_key "1" + option persist_tun "1" + option cipher "aes-256-cbc" + option tls_client "1" + option remote_cert_tls "server" + option auth_user_pass "/etc/openvpn/placeholder/placeholder.file" + option comp_lzo "yes" + option verb "3" + option ca "/etc/openvpn/mullvad/mullvad_ca.crt" + option crl_verify "/etc/openvpn/mullvad/mullvad_crl.pem" + option redirect_gateway "def1" + +# +# AirVPN +# +config openvpn_recipe e_airvpn_client_tun + option _description "Predefined Client for AirVPN" + option _role "client" + option client "1" + option dev "tun0" + option proto "udp" + list remote "example.vpn.airdns.org" + option port "443" + option resolv_retry "infinite" + option nobind "1" + option persist_key "1" + option persist_tun "1" + option cipher "aes-256-cbc" + option route_delay "5" + option explicit_exit_notify "5" + option tls_client "1" + option remote_cert_tls "server" + option auth_user_pass "/etc/openvpn/placeholder/placeholder.file" + option comp_lzo "no" + option verb "3" + option key_direction "1" + option ca "/etc/openvpn/airvpn/ca.crt" + option cert "/etc/openvpn/airvpn/client.crt" + option key "/etc/openvpn/airvpn/client.key" + option tls_auth "/etc/openvpn/airvpn/ta.key" + +# +# Tunnelbear +# +config openvpn_recipe e_tunnelbear_client_tun + option _description "Predefined Client for Tunnelbear" + option _role "client" + option dev "tun0" + option proto "udp" + option port "443" + option client "1" + option nobind "1" + option persist_key "1" + option persist_tun "1" + option remote_cert_tls "server" + option reneg_sec "0" + option verb "1" + option comp_lzo "yes" + option auth_user_pass "/etc/openvpn/placeholder/placeholder.file" + option ca "/etc/openvpn/placeholder/placeholder.file" + option cert "/etc/openvpn/placeholder/placeholder.file" + option key "/etc/openvpn/placeholder/placeholder.file" + option remote "example.tunnelbear-ios.com" + option auth "SHA256" + option keysize "256" + option keep_alive "10 30" + option redirect_gateway "def1" diff --git a/luci-app-rootervpn/files/etc/init.d/rootervpn b/luci-app-rootervpn/files/etc/init.d/rootervpn new file mode 100644 index 0000000..3fce0c0 --- /dev/null +++ b/luci-app-rootervpn/files/etc/init.d/rootervpn @@ -0,0 +1,14 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006 OpenWrt.org + +START=19 + +start() { +if [ -e /usr/lib/easyrsa/openvpn ]; then + mv -f /usr/lib/easyrsa/openvpn /etc/init.d/openvpn + fi + if [ -d /etc/luci-uploads ]; then + rm -rfv /etc/luci-uploads + fi + ln -s /etc/openvpn /etc/luci-uploads +} \ No newline at end of file diff --git a/luci-app-rootervpn/files/etc/openvpn/airvpn/ca.crt b/luci-app-rootervpn/files/etc/openvpn/airvpn/ca.crt new file mode 100644 index 0000000..cc37250 --- /dev/null +++ b/luci-app-rootervpn/files/etc/openvpn/airvpn/ca.crt @@ -0,0 +1,36 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- \ No newline at end of file diff --git a/luci-app-rootervpn/files/etc/openvpn/airvpn/client.crt b/luci-app-rootervpn/files/etc/openvpn/airvpn/client.crt new file mode 100644 index 0000000..7c8e434 --- /dev/null +++ b/luci-app-rootervpn/files/etc/openvpn/airvpn/client.crt @@ -0,0 +1,38 @@ +-----BEGIN CERTIFICATE----- +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 +qAZoGjCdOJq/IrQCq/95Cpw= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/luci-app-rootervpn/files/etc/openvpn/airvpn/client.key b/luci-app-rootervpn/files/etc/openvpn/airvpn/client.key new file mode 100644 index 0000000..9f34129 --- /dev/null +++ b/luci-app-rootervpn/files/etc/openvpn/airvpn/client.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +59PFiE0grG3JNZa3g34RjxOtsoNrz3bH +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/luci-app-rootervpn/files/etc/openvpn/airvpn/ta.key b/luci-app-rootervpn/files/etc/openvpn/airvpn/ta.key new file mode 100644 index 0000000..7d53ac5 --- /dev/null +++ b/luci-app-rootervpn/files/etc/openvpn/airvpn/ta.key @@ -0,0 +1,18 @@ +-----BEGIN OpenVPN Static key V1----- +7bb7a23a0f5f28d01e792df68f1764ab +f2688719288808bf58e8a2d4f9354ecf +132625dfb895fc3f6330ae1e868e4dfa +c164c0931593d7f9a7da9595cf353433 +8896e1d0a987a0d19838944af8fea4e5 +215a3a0c76f4c67d5a4aee6a53be66a4 +c88b84f850030840fb30f8550ed8068f +35c1ef34ee8f40a0ea5862dfb6f8d3c5 +7ab5e27ac2799cf93e8765ff63cd8cd8 +6b391b813925cd373bb202796f64d16f +003d042ca828d1b07f18ba1d0cb0323d +df3ee9287e9e084e655699efb3cffa92 +3626946fa372e7beee245e7a95b4c1d8 +7d16cae685218d4b8afc019b22e41083 +476ee9883fe666d236301e55b2062551 +4d91c8a69467a758293994df1e6fa7ae +-----END OpenVPN Static key V1----- \ No newline at end of file diff --git a/luci-app-rootervpn/files/etc/openvpn/mullvad/mullvad_ca.crt b/luci-app-rootervpn/files/etc/openvpn/mullvad/mullvad_ca.crt new file mode 100644 index 0000000..b795d91 --- /dev/null +++ b/luci-app-rootervpn/files/etc/openvpn/mullvad/mullvad_ca.crt @@ -0,0 +1,109 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=NA, ST=None, L=None, O=Mullvad, CN=Mullvad CA/emailAddress=info@mullvad.net + Validity + Not Before: Mar 24 16:19:48 2009 GMT + Not After : Mar 22 16:19:48 2019 GMT + Subject: C=NA, ST=None, L=None, O=Mullvad, CN=master.mullvad.net/emailAddress=info@mullvad.net + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:c5:00:39:5d:fe:9b:0c:b7:ff:76:a4:93:bf:26: + 1b:d6:c8:4a:e5:3c:ce:1c:2c:16:80:a2:61:a6:e9: + 63:4b:70:a1:80:6f:0e:0c:bb:a9:b6:d1:bd:f5:a0: + 78:82:09:4d:94:22:aa:77:7c:09:36:42:cd:a5:a6: + 90:73:27:42:00:31:e4:d4:8b:49:36:65:a3:25:82: + b8:26:d7:d1:f5:b5:a9:be:57:93:9d:7c:d6:1c:df: + 9a:87:81:53:0b:17:81:d1:0d:ca:dc:4d:19:13:fa: + 11:e6:da:68:eb:81:05:39:e3:1e:3a:3f:fc:e2:64: + 3c:98:3c:89:a9:42:b3:30:70:57:56:a1:f5:08:b2: + 75:12:a0:36:93:9d:69:e9:7e:11:71:d9:1c:e8:7d: + ec:03:21:11:7a:0a:7a:03:35:ba:b8:b2:0c:3a:6f: + 57:88:62:45:3d:0c:6c:18:ff:21:49:37:ae:40:78: + 6d:45:52:29:ac:21:ad:4a:01:61:67:0b:01:c4:ac: + b0:88:97:52:ff:cb:3a:21:f0:14:2b:c1:79:8d:79: + 35:14:fc:9c:3f:6c:c9:62:fc:8c:c7:a8:51:34:75: + 1c:23:d5:db:b9:44:08:1c:0c:17:2c:21:2a:b4:29: + db:15:59:e7:a9:1c:d6:19:19:ef:e4:6b:ea:78:6d: + 76:8d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 75:8A:14:92:0D:F3:6E:B7:36:4F:8B:4F:15:6C:3F:18:15:90:64:DE + X509v3 Authority Key Identifier: + keyid:E1:63:B4:3E:55:A3:D2:37:5F:DE:3A:91:48:51:4B:20:1A:F2:9B:C5 + DirName:/C=NA/ST=None/L=None/O=Mullvad/CN=Mullvad CA/emailAddress=info@mullvad.net + serial:84:68:2E:A0:51:2A:BB:D4 + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + a4:b4:62:3d:cb:7e:57:b3:bd:2a:41:e0:3b:94:d0:4c:08:69: + 8a:b1:73:15:13:20:c9:d7:b0:b6:5d:65:4a:4d:1d:27:cc:ca: + 11:0e:86:fa:65:61:26:39:c2:54:8e:da:eb:78:21:37:0e:c7: + a4:d2:17:8a:4b:ad:17:84:25:5e:24:0e:9a:81:ff:d1:1b:0e: + 32:9b:f4:81:e0:07:e9:8f:9d:c1:43:7f:40:30:01:07:7c:02: + c7:c4:9c:05:48:4c:bf:41:69:57:c1:d3:bb:a3:5a:01:17:96: + b0:c9:00:22:57:2f:84:da:45:33:6e:6c:2b:13:c5:af:75:a7: + b2:6b:71:6e:13:2c:97:0e:d9:93:da:6d:d9:34:c6:06:7d:0e: + e2:b8:d2:78:13:79:0f:ac:ac:a8:68:a9:72:73:7a:d8:ab:7b: + 0a:b0:54:b5:f3:ce:29:0d:47:82:0c:b4:d9:20:64:ff:ef:17: + 46:92:de:65:e8:67:ce:3a:92:de:e4:3e:99:73:9f:7a:7c:00: + 72:07:39:78:77:37:62:89:a2:db:24:fd:60:2a:e0:82:57:f6: + 55:94:f6:79:47:19:c9:13:3b:5d:b7:6b:66:14:d4:7d:3c:76: + 75:e9:a3:55:ba:b4:92:30:3b:ad:66:72:0c:39:4b:cc:95:a9: + bc:06:ef:2b +-----BEGIN CERTIFICATE----- +MIIEQjCCAyqgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBzMQswCQYDVQQGEwJOQTEN +MAsGA1UECBMETm9uZTENMAsGA1UEBxMETm9uZTEQMA4GA1UEChMHTXVsbHZhZDET +MBEGA1UEAxMKTXVsbHZhZCBDQTEfMB0GCSqGSIb3DQEJARYQaW5mb0BtdWxsdmFk +Lm5ldDAeFw0wOTAzMjQxNjE5NDhaFw0xOTAzMjIxNjE5NDhaMHsxCzAJBgNVBAYT +Ak5BMQ0wCwYDVQQIEwROb25lMQ0wCwYDVQQHEwROb25lMRAwDgYDVQQKEwdNdWxs +dmFkMRswGQYDVQQDExJtYXN0ZXIubXVsbHZhZC5uZXQxHzAdBgkqhkiG9w0BCQEW +EGluZm9AbXVsbHZhZC5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDFADld/psMt/92pJO/JhvWyErlPM4cLBaAomGm6WNLcKGAbw4Mu6m20b31oHiC +CU2UIqp3fAk2Qs2lppBzJ0IAMeTUi0k2ZaMlgrgm19H1tam+V5OdfNYc35qHgVML +F4HRDcrcTRkT+hHm2mjrgQU54x46P/ziZDyYPImpQrMwcFdWofUIsnUSoDaTnWnp +fhFx2RzofewDIRF6CnoDNbq4sgw6b1eIYkU9DGwY/yFJN65AeG1FUimsIa1KAWFn +CwHErLCIl1L/yzoh8BQrwXmNeTUU/Jw/bMli/IzHqFE0dRwj1du5RAgcDBcsISq0 +KdsVWeepHNYZGe/ka+p4bXaNAgMBAAGjgdgwgdUwHQYDVR0OBBYEFHWKFJIN8263 +Nk+LTxVsPxgVkGTeMIGlBgNVHSMEgZ0wgZqAFOFjtD5Vo9I3X946kUhRSyAa8pvF +oXekdTBzMQswCQYDVQQGEwJOQTENMAsGA1UECBMETm9uZTENMAsGA1UEBxMETm9u +ZTEQMA4GA1UEChMHTXVsbHZhZDETMBEGA1UEAxMKTXVsbHZhZCBDQTEfMB0GCSqG +SIb3DQEJARYQaW5mb0BtdWxsdmFkLm5ldIIJAIRoLqBRKrvUMAwGA1UdEwQFMAMB +Af8wDQYJKoZIhvcNAQEFBQADggEBAKS0Yj3LflezvSpB4DuU0EwIaYqxcxUTIMnX +sLZdZUpNHSfMyhEOhvplYSY5wlSO2ut4ITcOx6TSF4pLrReEJV4kDpqB/9EbDjKb +9IHgB+mPncFDf0AwAQd8AsfEnAVITL9BaVfB07ujWgEXlrDJACJXL4TaRTNubCsT +xa91p7JrcW4TLJcO2ZPabdk0xgZ9DuK40ngTeQ+srKhoqXJzetirewqwVLXzzikN +R4IMtNkgZP/vF0aS3mXoZ846kt7kPplzn3p8AHIHOXh3N2KJotsk/WAq4IJX9lWU +9nlHGckTO123a2YU1H08dnXpo1W6tJIwO61mcgw5S8yVqbwG7ys= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEQjCCAyqgAwIBAgIJAIRoLqBRKrvUMA0GCSqGSIb3DQEBBQUAMHMxCzAJBgNV +BAYTAk5BMQ0wCwYDVQQIEwROb25lMQ0wCwYDVQQHEwROb25lMRAwDgYDVQQKEwdN +dWxsdmFkMRMwEQYDVQQDEwpNdWxsdmFkIENBMR8wHQYJKoZIhvcNAQkBFhBpbmZv +QG11bGx2YWQubmV0MB4XDTA5MDMyNDA2NDcyNVoXDTE5MDMyMjA2NDcyNVowczEL +MAkGA1UEBhMCTkExDTALBgNVBAgTBE5vbmUxDTALBgNVBAcTBE5vbmUxEDAOBgNV +BAoTB011bGx2YWQxEzARBgNVBAMTCk11bGx2YWQgQ0ExHzAdBgkqhkiG9w0BCQEW +EGluZm9AbXVsbHZhZC5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDNNzZOrq+gMaA6wfyWdNFmxlM2OB1czwFgtPiDd9f6F8m6CGYBQog3Q2Wx3yAv +hxt/uchFBCKtYz6Yh59BCxXKfNAQT2uaMC6KAvKFgz0wppi4S8YbWg2KDelNO/Zv +Rb1QT4CBWMbtYzCQZvlJpHr2ZwuXG2OiT477oMyX5Hmf+iT0drmqi+wylRr7CRBs +LBu+fxLZ2LFD5g6MATuL3ql5JLIoVjlSqIgbld74pD4WUnM61HRwFsKoCEjq409Y +QNP1xO7BeaJu3uQvg/HJhXnGZxTatXhqvdCuAPQRppQ4UnkUzxdSTrfgM3hqMony +vX1vy0dX1S8iTQCIeyzAYNObAgMBAAGjgdgwgdUwHQYDVR0OBBYEFOFjtD5Vo9I3 +X946kUhRSyAa8pvFMIGlBgNVHSMEgZ0wgZqAFOFjtD5Vo9I3X946kUhRSyAa8pvF +oXekdTBzMQswCQYDVQQGEwJOQTENMAsGA1UECBMETm9uZTENMAsGA1UEBxMETm9u +ZTEQMA4GA1UEChMHTXVsbHZhZDETMBEGA1UEAxMKTXVsbHZhZCBDQTEfMB0GCSqG +SIb3DQEJARYQaW5mb0BtdWxsdmFkLm5ldIIJAIRoLqBRKrvUMAwGA1UdEwQFMAMB +Af8wDQYJKoZIhvcNAQEFBQADggEBAMjMAFPDeFOrQsvMXD/x+CuARwegS2PDZuB5 +f1Svw3YDF6cB1jlc0F12nh9SZxaYRwKIlpYoolLCOLoUCLwQJ0gsokxLV7G4gVb8 +dzETnNq4HG/QOPwPisjoOCaEmcd0tx1EkyNY0KLqFZTS0VdmDHCn89dDFA/6yuYI +5u04uJs7c/K4qaW7X6ajOOdneqjbtPeVOvx9DWXHxA0xz4Y+/w4laX/OTRD7jySq +K9fLfRliE5zsxzpUr5EWxAnqiABoWL71SiItk5fG8k3MJJ9SVr+YnTHmE7S4KNqu +4wTksvkb0Tmjae1lRSlMd6u2AulAxVcVKAod2QVffhj+hdkYM94= +-----END CERTIFICATE----- diff --git a/luci-app-rootervpn/files/etc/openvpn/mullvad/mullvad_crl.pem b/luci-app-rootervpn/files/etc/openvpn/mullvad/mullvad_crl.pem new file mode 100644 index 0000000..10e26dd --- /dev/null +++ b/luci-app-rootervpn/files/etc/openvpn/mullvad/mullvad_crl.pem @@ -0,0 +1,36 @@ +-----BEGIN X509 CRL----- +MIIGMTCCBRkwDQYJKoZIhvcNAQELBQAwezELMAkGA1UEBhMCTkExDTALBgNVBAgT +BE5vbmUxDTALBgNVBAcTBE5vbmUxEDAOBgNVBAoTB011bGx2YWQxGzAZBgNVBAMT +Em1hc3Rlci5tdWxsdmFkLm5ldDEfMB0GCSqGSIb3DQEJARYQaW5mb0BtdWxsdmFk +Lm5ldBcNMTcwNjIyMTQzMzEyWhcNMjcwNjIwMTQzMzEyWjCCBGswEgIBARcNMTcw +NjIyMTQzMzExWjASAgEDFw0xNzA2MjIxNDMzMTFaMBICASkXDTE3MDYyMjE0MzMx +MVowEwICDasXDTE3MDYyMjE0MzMxMVowEwICDawXDTE3MDYyMjE0MzMxMVowEwIC +Da0XDTE3MDYyMjE0MzMxMVowEwICDx4XDTE3MDYyMjE0MzMxMVowEwICGxsXDTE3 +MDYyMjE0MzMxMVowEwICPf4XDTE3MDYyMjE0MzMxMVowEwICSrUXDTE3MDYyMjE0 +MzMxMVowFAIDAbbXFw0xNzA2MjIxNDMzMTFaMBQCAwaeUBcNMTcwNjIyMTQzMzEx +WjAUAgMGnlUXDTE3MDYyMjE0MzMxMVowFAIDCheTFw0xNzA2MjIxNDMzMTFaMBQC +AwpvDBcNMTcwNjIyMTQzMzExWjAUAgML2jcXDTE3MDYyMjE0MzMxMVowFAIDDCfI +Fw0xNzA2MjIxNDMzMTFaMBQCAwwrKhcNMTcwNjIyMTQzMzExWjAUAgMMNWEXDTE3 +MDYyMjE0MzMxMVowFAIDDDViFw0xNzA2MjIxNDMzMTFaMBQCAwyXhRcNMTcwNjIy +MTQzMzExWjAUAgMM99UXDTE3MDYyMjE0MzMxMVowFAIDDPfWFw0xNzA2MjIxNDMz +MTFaMBQCAwz31xcNMTcwNjIyMTQzMzExWjAUAgMM9+MXDTE3MDYyMjE0MzMxMVow +FAIDDPfkFw0xNzA2MjIxNDMzMTFaMBQCAwz35RcNMTcwNjIyMTQzMzExWjAUAgMN +FHEXDTE3MDYyMjE0MzMxMVowFAIDDRSLFw0xNzA2MjIxNDMzMTFaMBQCAw1FfBcN +MTcwNjIyMTQzMzExWjAUAgMNUWcXDTE3MDYyMjE0MzMxMVowFAIDDVFoFw0xNzA2 +MjIxNDMzMTFaMBQCAw1RbBcNMTcwNjIyMTQzMzExWjAUAgMN2AoXDTE3MDYyMjE0 +MzMxMVowFAIDDdgLFw0xNzA2MjIxNDMzMTFaMBQCAw6G3xcNMTcwNjIyMTQzMzEx +WjAUAgMOkpwXDTE3MDYyMjE0MzMxMVowFAIDDpKdFw0xNzA2MjIxNDMzMTFaMBQC +Aw7DWhcNMTcwNjIyMTQzMzExWjAUAgMPFEEXDTE3MDYyMjE0MzMxMVowFAIDDyaP +Fw0xNzA2MjIxNDMzMTFaMBQCAw9D1xcNMTcwNjIyMTQzMzExWjAUAgMPzJQXDTE3 +MDYyMjE0MzMxMVowFAIDE3pTFw0xNzA2MjIxNDMzMTFaMBQCAxN6VBcNMTcwNjIy +MTQzMzExWjAUAgMTjroXDTE3MDYyMjE0MzMxMVowFAIDFIUDFw0xNzA2MjIxNDMz +MTFaMBQCAx7XBxcNMTcwNjIyMTQzMzExWjAUAgMgFScXDTE3MDYyMjE0MzMxMVow +FAIDImufFw0xNzA2MjIxNDMzMTFaMBQCAyPpNRcNMTcwNjIyMTQzMzExWjAUAgMk +eEgXDTE3MDYyMjE0MzMxMVowDQYJKoZIhvcNAQELBQADggEBAEdGuk5OitTepMQL +O2ugSjKl9le9ttuwiNkXCowYZwMpvOvk98RuhcgqwetDtxdMIi7koMIlbILfbIwA +VYvaV7HzpOzkL9D4RpE8GN3r1xjc+pjz5RN08Q+l/pRI9VeS2Bz6sstujqDMu0kT +LEzxtyiWGgdyYL0ykirahStLxmjc0JLLSsZXHuVJGmdJsphoEw/zgTRR02N7Q0gP +AmL6+i4mXY8OMaUMFWwKikliT+VAT23H1VPPUpS/5n3p+j15Hn5bI+rHyLmO2Ca7 +0UlcBbTVhoNMO25/6js5icRktebiaI0hq3Zofd5U80tGJnH+udIUEAq6CD/wUNqq +VcdW4oc= +-----END X509 CRL----- diff --git a/luci-app-rootervpn/files/etc/openvpn/pia/ca.rsa.2048.crt b/luci-app-rootervpn/files/etc/openvpn/pia/ca.rsa.2048.crt new file mode 100644 index 0000000..6deea60 --- /dev/null +++ b/luci-app-rootervpn/files/etc/openvpn/pia/ca.rsa.2048.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFqzCCBJOgAwIBAgIJAKZ7D5Yv87qDMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNV +BAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIElu +dGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3Mx +IDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkB +FiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzM1 +MThaFw0zNDA0MTIxNzM1MThaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex +EzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQg +QWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UE +AxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50 +ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVy +bmV0YWNjZXNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPXD +L1L9tX6DGf36liA7UBTy5I869z0UVo3lImfOs/GSiFKPtInlesP65577nd7UNzzX +lH/P/CnFPdBWlLp5ze3HRBCc/Avgr5CdMRkEsySL5GHBZsx6w2cayQ2EcRhVTwWp +cdldeNO+pPr9rIgPrtXqT4SWViTQRBeGM8CDxAyTopTsobjSiYZCF9Ta1gunl0G/ +8Vfp+SXfYCC+ZzWvP+L1pFhPRqzQQ8k+wMZIovObK1s+nlwPaLyayzw9a8sUnvWB +/5rGPdIYnQWPgoNlLN9HpSmsAcw2z8DXI9pIxbr74cb3/HSfuYGOLkRqrOk6h4RC +OfuWoTrZup1uEOn+fw8CAwEAAaOCAVQwggFQMB0GA1UdDgQWBBQv63nQ/pJAt5tL +y8VJcbHe22ZOsjCCAR8GA1UdIwSCARYwggESgBQv63nQ/pJAt5tLy8VJcbHe22ZO +sqGB7qSB6zCB6DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRMwEQYDVQQHEwpM +b3NBbmdlbGVzMSAwHgYDVQQKExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4G +A1UECxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAMTF1ByaXZhdGUg +SW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQpExdQcml2YXRlIEludGVybmV0IEFjY2Vz +czEvMC0GCSqGSIb3DQEJARYgc2VjdXJlQHByaXZhdGVpbnRlcm5ldGFjY2Vzcy5j +b22CCQCmew+WL/O6gzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQAn +a5PgrtxfwTumD4+3/SYvwoD66cB8IcK//h1mCzAduU8KgUXocLx7QgJWo9lnZ8xU +ryXvWab2usg4fqk7FPi00bED4f4qVQFVfGfPZIH9QQ7/48bPM9RyfzImZWUCenK3 +7pdw4Bvgoys2rHLHbGen7f28knT2j/cbMxd78tQc20TIObGjo8+ISTRclSTRBtyC +GohseKYpTS9himFERpUgNtefvYHbn70mIOzfOJFTVqfrptf9jXa9N8Mpy3ayfodz +1wiqdteqFXkTYoSDctgKMiZ6GdocK9nMroQipIQtpnwd4yBDWIyC6Bvlkrq5TQUt +YDQ8z9v+DMO6iwyIDRiU +-----END CERTIFICATE----- diff --git a/luci-app-rootervpn/files/etc/openvpn/pia/crl.rsa.2048.pem b/luci-app-rootervpn/files/etc/openvpn/pia/crl.rsa.2048.pem new file mode 100644 index 0000000..a58ef56 --- /dev/null +++ b/luci-app-rootervpn/files/etc/openvpn/pia/crl.rsa.2048.pem @@ -0,0 +1,15 @@ +-----BEGIN X509 CRL----- +MIICWDCCAUAwDQYJKoZIhvcNAQENBQAwgegxCzAJBgNVBAYTAlVTMQswCQYDVQQI +EwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRl +cm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAw +HgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0 +ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRl +aW50ZXJuZXRhY2Nlc3MuY29tFw0xNjA3MDgxOTAwNDZaFw0zNjA3MDMxOTAwNDZa +MCYwEQIBARcMMTYwNzA4MTkwMDQ2MBECAQYXDDE2MDcwODE5MDA0NjANBgkqhkiG +9w0BAQ0FAAOCAQEAQZo9X97ci8EcPYu/uK2HB152OZbeZCINmYyluLDOdcSvg6B5 +jI+ffKN3laDvczsG6CxmY3jNyc79XVpEYUnq4rT3FfveW1+Ralf+Vf38HdpwB8EW +B4hZlQ205+21CALLvZvR8HcPxC9KEnev1mU46wkTiov0EKc+EdRxkj5yMgv0V2Re +ze7AP+NQ9ykvDScH4eYCsmufNpIjBLhpLE2cuZZXBLcPhuRzVoU3l7A9lvzG9mjA +5YijHJGHNjlWFqyrn1CfYS6koa4TGEPngBoAziWRbDGdhEgJABHrpoaFYaL61zqy +MR6jC0K2ps9qyZAN74LEBedEfK7tBOzWMwr58A== +-----END X509 CRL----- diff --git a/luci-app-rootervpn/files/etc/openvpn/placeholder/placeholder.file b/luci-app-rootervpn/files/etc/openvpn/placeholder/placeholder.file new file mode 100644 index 0000000..e88efe6 --- /dev/null +++ b/luci-app-rootervpn/files/etc/openvpn/placeholder/placeholder.file @@ -0,0 +1,2 @@ +placeholder file +placeholder \ No newline at end of file diff --git a/luci-app-rootervpn/files/etc/openvpn/windscribe/ca.crt b/luci-app-rootervpn/files/etc/openvpn/windscribe/ca.crt new file mode 100644 index 0000000..7edc133 --- /dev/null +++ b/luci-app-rootervpn/files/etc/openvpn/windscribe/ca.crt @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF3DCCA8SgAwIBAgIJAMsOivWTmu9fMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNV +BAYTAkNBMQswCQYDVQQIDAJPTjEQMA4GA1UEBwwHVG9yb250bzEbMBkGA1UECgwS +V2luZHNjcmliZSBMaW1pdGVkMRMwEQYDVQQLDApPcGVyYXRpb25zMRswGQYDVQQD +DBJXaW5kc2NyaWJlIE5vZGUgQ0EwHhcNMTYwMzA5MDMyNjIwWhcNNDAxMDI5MDMy +NjIwWjB7MQswCQYDVQQGEwJDQTELMAkGA1UECAwCT04xEDAOBgNVBAcMB1Rvcm9u +dG8xGzAZBgNVBAoMEldpbmRzY3JpYmUgTGltaXRlZDETMBEGA1UECwwKT3BlcmF0 +aW9uczEbMBkGA1UEAwwSV2luZHNjcmliZSBOb2RlIENBMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAruBtLR1Vufd71LeQEqChgHS4AQJ0fSRner0gmZPE +r2TL5uWboOEWXFFoEUTthF+P/N8yy3xRZ8HhG/zKlmJ1xw+7KZRbTADD6shJPj3/ +uvTIO80sU+9LmsyKSWuPhQ1NkgNA7rrMTfz9eHJ2MVDs4XCpYWyX9iuAQrHSY6aP +q+4TpCbUgprkM3Gwjh9RSt9IoDoc4CF2bWSaVepUcL9yz/SXLPzFx2OT9rFrDhL3 +ryHRzJQ/tA+VD8A7lo8bhOcDqiXgEFmVOZNMLw+r167Qq1Ck7X86yr2mnW/6HK2g +JOvY0/SPKukfGJAiYZKdG+fe4ekyYcAVhDfPJg7rF9wUqPwUzejJyAs1K18JwX94 +Y8fnD6vQobjpC3qfHtwQP7Uj2AcI6QC8ytWDegV6UIkHXAMXBQSX5suSQoE11deG +32cy7nyp5vhgy31rTyNoopqlcCAhPm6k0jVVQbvXhLcpTSL8iCCoMdrP28i/xsfv +ktBAkl5giHMdK6hxqWgPI+Bx9uPIhRp3fJ2z8AgFm8g1ARB2ZzQ+OZZ2RUIkJuUK +hi2kUhgKSAQ+eF89aoqDjp/J1miZqGRzt4DovSZfQOeL01RkKHEibAPYCfgHG2ZS +woLoeaxE2vNZiX4dpXiOQYTOIXOwEPZzPvfTQf9T4Kxvx3jzQnt3PzjlMCqKk3Ai +pm8CAwEAAaNjMGEwHQYDVR0OBBYEFEH2v9F2z938Ebngsj9RkVSSgs45MB8GA1Ud +IwQYMBaAFEH2v9F2z938Ebngsj9RkVSSgs45MA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQAgI6NgYkVo5rB6yKStgHjj +ZsINsgEvoMuHwkM0YaV22XtKNiHdsiOmY/PGCRemFobTEHk5XHcvcOTWv/D1qVf8 +fI21WAoNQVH7h8KEsr4uMGKCB6Lu8l6xALXRMjo1xb6JKBWXwIAzUu691rUD2exT +1E+A5t+xw+gzqV8rWTMIoUaH7O1EKjN6ryGW71Khiik8/ETrP3YT32ZbS2P902iM +Kw9rpmuS0wWhnO5k/iO/6YNA1ZMV5JG5oZvZQYEDk7enLD9HvqazofMuy/Sz/n62 +ZCDdQsnabzxl04wwv5Y3JZbV/6bOM520GgdJEoDxviY05ax2Mz05otyBzrAVjFw9 +RZt/Ls8ATifu9BusZ2ootvscdIuE3x+ZCl5lvANcFEnvgGw0qpCeASLpsfxwq1dR +gIn7BOiTauFv4eoeFAQvCD+l+EKGWKu3M2y19DgYX94N2+Xs2bwChroaO5e4iFem +MLMuWKZvYgnqS9OAtRSYWbNX/wliiPz7u13yj+qSWgMfu8WPYNQlMZJXuGWUvKLE +XCUExlu7/o8D4HpsVs30E0pUdaqN0vExB1KegxPWWrmLcYnPG3knXpkC3ZBZ5P/e +l/2eyhZRy9ydiITF8gM3L08E8aeqvzZMw2FDSmousydIzlXgeS5VuEf+lUFA2h8o +ZYGQgrLt+ot8MbLhJlkp4Q== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/luci-app-rootervpn/files/etc/openvpn/windscribe/ta.key b/luci-app-rootervpn/files/etc/openvpn/windscribe/ta.key new file mode 100644 index 0000000..dd556ac --- /dev/null +++ b/luci-app-rootervpn/files/etc/openvpn/windscribe/ta.key @@ -0,0 +1,18 @@ +-----BEGIN OpenVPN Static key V1----- +5801926a57ac2ce27e3dfd1dd6ef8204 +2d82bd4f3f0021296f57734f6f1ea714 +a6623845541c4b0c3dea0a050fe6746c +b66dfab14cda27e5ae09d7c155aa554f +399fa4a863f0e8c1af787e5c602a801d +3a2ec41e395a978d56729457fe6102d7 +d9e9119aa83643210b33c678f9d4109e +3154ac9c759e490cb309b319cf708cae +83ddadc3060a7a26564d1a24411cd552 +fe6620ea16b755697a4fc5e6e9d0cfc0 +c5c4a1874685429046a424c026db672e +4c2c492898052ba59128d46200b40f88 +0027a8b6610a4d559bdc9346d33a0a6b +08e75c7fd43192b162bfd0aef0c716b3 +1584827693f676f9a5047123466f0654 +eade34972586b31c6ce7e395f4b478cb +-----END OpenVPN Static key V1----- \ No newline at end of file diff --git a/luci-app-rootervpn/files/lib/upgrade/keep.d/rootervpn b/luci-app-rootervpn/files/lib/upgrade/keep.d/rootervpn new file mode 100644 index 0000000..30f20da --- /dev/null +++ b/luci-app-rootervpn/files/lib/upgrade/keep.d/rootervpn @@ -0,0 +1 @@ +/etc/openvpn/* diff --git a/luci-app-rootervpn/files/usr/bin/ovpn-userpass b/luci-app-rootervpn/files/usr/bin/ovpn-userpass new file mode 100644 index 0000000..615c85c --- /dev/null +++ b/luci-app-rootervpn/files/usr/bin/ovpn-userpass @@ -0,0 +1,16 @@ +#!/bin/sh + +log() { + logger -t "UserPass : " "$@" +} + +conf="/etc/openvpn/ovpnauth.conf" + +userpass=`cat $1` +username=`echo $userpass | awk '{print $1}'` +password=`echo $userpass | awk '{print $2}'` + +log "$username $password" + + +exit 0 \ No newline at end of file diff --git a/luci-app-rootervpn/files/usr/lib/easyrsa/dns.sh b/luci-app-rootervpn/files/usr/lib/easyrsa/dns.sh new file mode 100644 index 0000000..b3d1e24 --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/easyrsa/dns.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +log() { + logger -t "Firewall Settings : " "$@" +} + +LANOPENDNS=$(uci get openvpn.settings.lanopendns) +if [ -z $LANOPENDNS ]; then + LANOPENDNS="0" +fi +LANGOOGLE=$(uci get openvpn.settings.langoogle) +if [ -z $LANGOOGLE ]; then + LANGOOGLE="0" +fi +WANOPENDNS=$(uci get openvpn.settings.wanopendns) +if [ -z $WANOPENDNS ]; then + WANOPENDNS="0" +fi +WANGOOGLE=$(uci get openvpn.settings.wangoogle) +if [ -z $WANGOOGLE ]; then + WANGOOGLE="0" +fi + +if [ $LANOPENDNS = "1" -a $LANGOOGLE = "0" ]; then + uci del dhcp.lan.dhcp_option + uci add_list dhcp.lan.dhcp_option='6,208.67.222.222,208.67.220.220' +fi +if [ $LANOPENDNS = "0" -a $LANGOOGLE = "1" ]; then + uci del dhcp.lan.dhcp_option + uci add_list dhcp.lan.dhcp_option='6,8.8.8.8,8.8.4.4' +fi +if [ $LANOPENDNS = "1" -a $LANGOOGLE = "1" ]; then + uci del dhcp.lan.dhcp_option + uci add_list dhcp.lan.dhcp_option='6,8.8.8.8,8.8.4.4' + uci add_list dhcp.lan.dhcp_option='6,208.67.222.222,208.67.220.220' +fi +if [ $LANOPENDNS = "0" -a $LANGOOGLE = "0" ]; then + uci del dhcp.lan.dhcp_option +fi + +if [ $WANOPENDNS = "1" -a $WANGOOGLE = "0" ]; then + uci set network.wan.peerdns='0' + uci del network.wan.dns + uci add_list network.wan.dns='208.67.222.222' + uci add_list network.wan.dns='208.67.220.220' +fi +if [ $WANOPENDNS = "0" -a $WANGOOGLE = "1" ]; then + uci set network.wan.peerdns='0' + uci del network.wan.dns + uci add_list network.wan.dns='8.8.8.8' + uci add_list network.wan.dns='8.8.4.4' +fi +if [ $WANOPENDNS = "1" -a $WANGOOGLE = "1" ]; then + uci set network.wan.peerdns='0' + uci del network.wan.dns + uci add_list network.wan.dns='8.8.8.8' + uci add_list network.wan.dns='8.8.4.4' + uci add_list network.wan.dns='208.67.222.222' + uci add_list network.wan.dns='208.67.220.220' +fi +if [ $WANOPENDNS = "0" -a $WANGOOGLE = "0" ]; then + uci set network.wan.peerdns='1' + uci del network.wan.dns +fi +uci commit +/etc/init.d/network restart \ No newline at end of file diff --git a/luci-app-rootervpn/files/usr/lib/easyrsa/firewall.sh b/luci-app-rootervpn/files/usr/lib/easyrsa/firewall.sh new file mode 100644 index 0000000..09fd182 --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/easyrsa/firewall.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +log() { + logger -t "Firewall Settings : " "$@" +} + +VPN2LAN=$(uci get openvpn.settings.vpn2lan) +if [ -z $VPN2LAN ]; then + VPN2LAN="0" +fi +VPNS2LAN=$(uci get openvpn.settings.vpns2lan) +if [ -z $VPNS2LAN ]; then + VPNS2LAN="0" +fi +VPN2WAN=$(uci get openvpn.settings.vpn2wan) +if [ -z $VPN2WAN ]; then + VPN2WAN="0" +fi + +CHANGE="0" +if [ $VPN2LAN = "1" ]; then + WW=$(uci get firewall.vpnforward1) + if [ -z $WW ]; then + uci set firewall.vpnforward1=forwarding + uci set firewall.vpnforward1.dest="lan" + uci set firewall.vpnforward1.src="VPN" + CHANGE="1" + fi +else + WW=$(uci get firewall.vpnforward1) + if [ ! -z $WW ]; then + uci delete firewall.vpnforward1 + CHANGE="1" + fi +fi + +if [ $VPNS2LAN = "1" ]; then + WW=$(uci get firewall.vpnforwards1) + if [ -z $WW ]; then + uci set firewall.vpnforwards1=forwarding + uci set firewall.vpnforwards1.dest="lan" + uci set firewall.vpnforwards1.src="VPNS" + CHANGE="1" + fi +else + WW=$(uci get firewall.vpnforwards1) + if [ ! -z $WW ]; then + uci delete firewall.vpnforwards1 + CHANGE="1" + fi +fi + +if [ $VPN2WAN = "1" ]; then + WW=$(uci get firewall.vpnforward2) + if [ -z $WW ]; then + uci set firewall.vpnforward2=forwarding + uci set firewall.vpnforward2.dest="wan" + uci set firewall.vpnforward2.src="VPNS" + CHANGE="1" + fi +else + WW=$(uci get firewall.vpnforward2) + if [ ! -z $WW ]; then + uci delete firewall.vpnforward2 + CHANGE="1" + fi +fi + +if [ $CHANGE = "1" ]; then + uci commit firewall + /etc/init.d/firewall restart +fi \ No newline at end of file diff --git a/luci-app-rootervpn/files/usr/lib/easyrsa/generate.sh b/luci-app-rootervpn/files/usr/lib/easyrsa/generate.sh new file mode 100644 index 0000000..ad22936 --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/easyrsa/generate.sh @@ -0,0 +1,151 @@ +#!/bin/sh + + rm -f /tmp/easyrsa +### Step 1: Create the PKI directory tree + PKI_DIR="/tmp/openvpn" + + if [ -d "$PKI_DIR" ]; then + rm -rfv "$PKI_DIR" + fi + mkdir -p ${PKI_DIR} + chmod -R 0600 ${PKI_DIR} + mkdir -p ${PKI_DIR}/client + chmod -R 0600 ${PKI_DIR}/client + mkdir -p ${PKI_DIR}/server + chmod -R 0600 ${PKI_DIR}/server + mkdir -p ${PKI_DIR}/package + chmod -R 0600 ${PKI_DIR}/package + if [ -d "/www/package" ]; then + rm -rfv "/www/package" + fi + ln -s ${PKI_DIR}/package /www/package + cd ${PKI_DIR} + + touch index.txt + echo 1000 > serial + mkdir newcerts + + +### Step 2: Start with a clean configuration, and establish the basic variables + cp /etc/ssl/openssl.cnf ${PKI_DIR} + PKI_CNF=${PKI_DIR}/openssl.cnf + + CNT=$(uci get openvpn.settings.country) + CTY=$(uci get openvpn.settings.city) + ORG=$(uci get openvpn.settings.organ) + DAYS=$(uci get openvpn.settings.days) + CNAME=$(uci get openvpn.settings.comm) + EMAIL=$(uci get openvpn.settings.email) + UNIT=$(uci get openvpn.settings.unit) + UNSTRUC=$(uci get openvpn.settings.unstruc) + + sed -i "/^dir/ s:=.*:= ${PKI_DIR}/:" ${PKI_CNF} + sed -i '/.*Name/ s:= match:= optional:' ${PKI_CNF} + + sed -i "/organizationName_default/ s:= .*:= $ORG:" ${PKI_CNF} + sed -i "/stateOrProvinceName_default/ s:= .*:= $CTY:" ${PKI_CNF} + sed -i "/countryName_default/ s:= .*:= $CNT:" ${PKI_CNF} + if [ ! -z $CNAME ]; then + sed -i -e "s/commonName = Common Name (e.g. server FQDN or YOUR name)/commonName = $CNAME/g" ${PKI_CNF} + fi + if [ ! -z $EMAIL ]; then + sed -i -e "s/emailAddress = Email Address/emailAddress = $EMAIL/g" ${PKI_CNF} + fi + if [ ! -z $UNIT ]; then + sed -i -e "s/organizationalUnitName = Organizational Unit Name (eg, section)/organizationalUnitName = $UNIT/g" ${PKI_CNF} + fi + if [ ! -z $UNSTRUC ]; then + sed -i -e "s/unstructuredName = An optional company name/unstructuredName = $UNSTRUC/g" ${PKI_CNF} + fi + + sed -i "/default_days/ s:=.*:= $DAYS:" ${PKI_CNF} + sed -i "/default_bits/ s:=.*:= 2048:" ${PKI_CNF} + + +cat >> ${PKI_CNF} <<"EOF" +############################################################################### +### Check via: openssl x509 -text -noout -in *.crt | grep 509 -A 1 +[ my-server ] +# X509v3 Key Usage: Digital Signature, Key Encipherment +# X509v3 Extended Key Usage: TLS Web Server Authentication + keyUsage = digitalSignature, keyEncipherment + extendedKeyUsage = serverAuth + +[ my-client ] +# X509v3 Key Usage: Digital Signature +# X509v3 Extended Key Usage: TLS Web Client Authentication + keyUsage = digitalSignature + extendedKeyUsage = clientAuth + +EOF + + echo "1" > /tmp/easyrsa + +### Step 3a: Create the CA, Server, and Client certificates (*without* using easy-rsa): +# pkitool --initca ## equivalent to the 'build-ca' script + openssl req -batch -nodes -new -keyout "ca.key" -out "ca.crt" -x509 -config ${PKI_CNF} 2> /dev/null ## x509 (self-signed) for the CA + + echo "2" > /tmp/easyrsa +# pkitool --server my-server ## equivalent to the 'build-key-server' script + openssl req -batch -nodes -new -keyout "$ORG-server.key" -out "$ORG-server.csr" -subj "/CN=$ORG-server" -config ${PKI_CNF} 2> /dev/null + openssl ca -batch -keyfile "ca.key" -cert "ca.crt" -in "$ORG-server.csr" -out "$ORG-server.crt" -config ${PKI_CNF} -extensions my-server 2> /dev/null + + echo "3" > /tmp/easyrsa +# pkitool my-client ## equivalent to the 'build-key' script + openssl req -batch -nodes -new -keyout "$ORG-client.key" -out "$ORG-client.csr" -subj "/CN=$ORG-client" -config ${PKI_CNF} 2> /dev/null + openssl ca -batch -keyfile "ca.key" -cert "ca.crt" -in "$ORG-client.csr" -out "$ORG-client.crt" -config ${PKI_CNF} -extensions my-client 2> /dev/null + + chmod 0600 "ca.key" + chmod 0600 "$ORG-server.key" + chmod 0600 "$ORG-client.key" + + cp "ca.crt" ${PKI_DIR}/client + cp "$ORG-client.key" ${PKI_DIR}/client + cp "$ORG-client.crt" ${PKI_DIR}/client + cp "ca.crt" ${PKI_DIR}/server + cp "$ORG-server.key" ${PKI_DIR}/server + cp "$ORG-server.crt" ${PKI_DIR}/server + + echo "4" > /tmp/easyrsa +### Step 4: Create the Diffie-Hellman parameters + openssl dhparam -out dh2048.pem 2048 2> /dev/null + cp "dh2048.pem" ${PKI_DIR}/server + + echo "5" > /tmp/easyrsa + +### Step 5: Create the client's .ovpn file +### + +OVPN_FILE="${PKI_DIR}/$ORG.ovpn" + +tee "$OVPN_FILE" >/dev/null <' >> ${OVPN_FILE} + cat >> ${OVPN_FILE} < ca.crt + echo '' >> ${OVPN_FILE} + + echo '' >> ${OVPN_FILE} + cat >> ${OVPN_FILE} < $ORG-client.crt + echo '' >> ${OVPN_FILE} + + echo '' >> ${OVPN_FILE} + cat >> ${OVPN_FILE} < $ORG-client.key + echo '' >> ${OVPN_FILE} + + cp ${OVPN_FILE} ${PKI_DIR}/client + + tar -czf ${PKI_DIR}/package/certificates.tar.gz ./client ./server + sleep 7 + echo "6" > /tmp/easyrsa \ No newline at end of file diff --git a/luci-app-rootervpn/files/usr/lib/easyrsa/openvpn b/luci-app-rootervpn/files/usr/lib/easyrsa/openvpn new file mode 100644 index 0000000..90b8b69 --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/easyrsa/openvpn @@ -0,0 +1,323 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2008-2013 OpenWrt.org +# Copyright (C) 2008 Jo-Philipp Wich +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. + +. /lib/functions.sh + +START=90 +STOP=10 + +LIST_SEP=" +" + +UCI_STARTED= +UCI_DISABLED= + +PROG=/usr/sbin/openvpn + +log() { + logger -t "OpenVPN : " "$@" +} + +check_config () { + CHANGE="0" + WW=$(uci get network.VPN) + if [ -z $WW ]; then + uci set network.VPN=interface + uci set network.VPN.proto="none" + uci set network.VPN.ifname="tun0" + CHANGE="1" + fi + WW=$(uci get network.VPNS) + if [ -z $WW ]; then + uci set network.VPNS=interface + uci set network.VPNS.proto="none" + uci set network.VPNS.ifname="tun-server" + CHANGE="1" + fi + WW=$(uci get network.TAP) + if [ -z $WW ]; then + uci set network.TAP=interface + uci set network.TAP.proto="none" + uci set network.TAP.ifname="tap0" + uci set network.TAP.auto="1" + LANIF=$(uci get network.lan.ifname) + TAP0=$(echo $LANIF | grep "tap0") + if [ -z $TAP0 ]; then + uci set network.lan.ifname="$(uci get network.lan.ifname) tap0" + fi + CHANGE="1" + fi + WW=$(uci get network.TAPS) + if [ -z $WW ]; then + uci set network.TAPS=interface + uci set network.TAPS.proto="none" + uci set network.TAPS.ifname="tap-server" + uci set network.TAPS.auto="1" + LANIF=$(uci get network.lan.ifname) + TAP1=$(echo $LANIF | grep "tap-server") + if [ -z $TAP1 ]; then + uci set network.lan.ifname="$(uci get network.lan.ifname) tap-server" + fi + CHANGE="1" + fi + if [ $CHANGE = "1" ]; then + uci commit network + /etc/init.d/network restart + fi + + CHANGE="0" + WW=$(uci get firewall.vpnzone) + if [ -z $WW ]; then + uci set firewall.vpnzone=zone + uci set firewall.vpnzone.name="VPN" + uci set firewall.vpnzone.forward="REJECT" + uci set firewall.vpnzone.output="ACCEPT" + uci set firewall.vpnzone.network="VPN" + uci set firewall.vpnzone.input="REJECT" + uci set firewall.vpnzone.masq="1" + uci set firewall.vpnzone.mtu_fix="1" + uci set firewall.vpnforward=forwarding + uci set firewall.vpnforward.dest="VPN" + uci set firewall.vpnforward.src="lan" + CHANGE="1" + fi + WW=$(uci get firewall.vpnzones) + if [ -z $WW ]; then + uci set firewall.vpnzones=zone + uci set firewall.vpnzones.name="VPNS" + uci set firewall.vpnzones.forward="REJECT" + uci set firewall.vpnzones.output="ACCEPT" + uci set firewall.vpnzones.network="VPNS" + uci set firewall.vpnzones.input="ACCEPT" + uci set firewall.vpnzones.masq="1" + uci set firewall.vpnzones.mtu_fix="1" + uci set firewall.vpnforwards=forwarding + uci set firewall.vpnforwards.dest="VPNS" + uci set firewall.vpnforwards.src="lan" + CHANGE="1" + fi + if [ $CHANGE = "1" ]; then + uci commit firewall + /etc/init.d/firewall restart + fi + WW=$(uci get openvpn.settings) + if [ -z $WW ]; then + uci set openvpn.settings=settings + uci set openvpn.settings.vpn2lan="0" + uci set openvpn.settings.vpns2lan="0" + uci set openvpn.settings.vpn2wan="0" + uci set openvpn.settings.country="CA" + uci set openvpn.settings.city="Abbotsford" + uci set openvpn.settings.organ="ROOter" + uci set openvpn.settings.days="3650" + uci commit openvpn + fi +} + +checkserver() { + local s=$1 + local SERVER="0" + local config=$(uci get openvpn.$s.config) + if [ ! -z $config ]; then + return + fi + local client=$(uci get openvpn.$s.client) + if [ -z $client ]; then + SERVER="1" + else + if [ $client = "0" ]; then + SERVER="1" + fi + fi + + if [ $SERVER = "1" ]; then + port=$(uci get openvpn.$s.port) + if [ -z $port ]; then + PORT="1194" + else + PORT=$port + fi + # look for rule for this port + INB="inbound"$PORT + RULE=$(uci get firewall.$INB) + if [ -z $RULE ]; then + uci set firewall.$INB=rule + uci set firewall.$INB.target=ACCEPT + uci set firewall.$INB.src=* + uci set firewall.$INB.proto=udp + uci set firewall.$INB.dest_port=$PORT + uci commit firewall + /etc/init.d/firewall reload + fi + DEV=$(uci get openvpn.$s.dev) + if [ $DEV = "tun0" ]; then + uci set openvpn.$s.dev="tun-server" + uci commit openvpn + else + + if [ $DEV = "tap0" ]; then + uci set openvpn.$s.dev="tap-server" + uci commit openvpn + fi + fi + else + DEV=$(uci get openvpn.$s.dev) + if [ $DEV = "tun-server" ]; then + uci set openvpn.$s.dev="tun0" + uci commit openvpn + else + if [ $DEV = "tap-server" ]; then + uci set openvpn.$s.dev="tap0" + uci commit openvpn + fi + fi + fi +} + +ovpn() { + local s=$1 + OVPN=$2 + config_get auth_user_pass "$s" auth_user_pass + if [ ! -z $auth_user_pass ]; then + auth_user_pass="${auth_user_pass:+$(readlink -f "$auth_user_pass")}" + PS=$(grep -m 1 "^auth-user-pass" $OVPN) + sed -i -e "s|$PS|auth-user-pass $auth_user_pass|g" "$OVPN" + fi + config_get cert "$s" cert + if [ ! -z $cert ]; then + cert="${cert:+$(readlink -f "$cert")}" + PS=$(grep -m 1 "^cert " $OVPN) + chrlen=${#PS} + if [ $chrlen -gt 5 ]; then + sed -i -e "s|$PS|cert $cert|1" "$OVPN" + fi + fi + config_get ca "$s" ca + if [ ! -z $ca ]; then + ca="${ca:+$(readlink -f "$ca")}" + PS=$(grep -m 1 "^ca " $OVPN) + chrlen=${#PS} + if [ $chrlen -gt 3 ]; then + sed -i -e "s|$PS|ca $ca|1" "$OVPN" + fi + fi + config_get key "$s" key + if [ ! -z $key ]; then + key="${key:+$(readlink -f "$key")}" + PS=$(grep -m 1 "^key " $OVPN) + chrlen=${#PS} + if [ $chrlen -gt 4 ]; then + sed -i -e "s|$PS|key $key|1" "$OVPN" + fi + fi + config_get tls_auth "$s" tls_auth + if [ ! -z $tls_auth ]; then + tls_auth="${tls_auth:+$(readlink -f "$tls_auth")}" + PS=$(grep -m 1 "^tls-auth " $OVPN) + chrlen=${#PS} + if [ $chrlen -gt 4 ]; then + sed -i -e "s|$PS|tls-auth $tls_auth|1" "$OVPN" + fi + fi +} + +append_param() { + local s="$1" + local v="$2" + case "$v" in + *_*_*_*) v=${v%%_*}-${v#*_}; v=${v%%_*}-${v#*_}; v=${v%%_*}-${v#*_} ;; + *_*_*) v=${v%%_*}-${v#*_}; v=${v%%_*}-${v#*_} ;; + *_*) v=${v%%_*}-${v#*_} ;; + esac + echo -n "$v" >> "/var/etc/openvpn-$s.conf" + return 0 +} + +append_bools() { + local p; local v; local s="$1"; shift + for p in $*; do + config_get_bool v "$s" "$p" + [ "$v" = 1 ] && append_param "$s" "$p" && echo >> "/var/etc/openvpn-$s.conf" + done +} + +append_params() { + local p; local v; local s="$1"; shift + for p in $*; do + config_get v "$s" "$p" + IFS="$LIST_SEP" + for v in $v; do + [ -n "$v" ] && [ "$p" != "push" ] && append_param "$s" "$p" && echo " $v" >> "/var/etc/openvpn-$s.conf" + [ -n "$v" ] && [ "$p" == "push" ] && append_param "$s" "$p" && echo " \"$v\"" >> "/var/etc/openvpn-$s.conf" + done + unset IFS + done +} + +section_enabled() { + config_get_bool enable "$1" 'enable' 0 + config_get_bool enabled "$1" 'enabled' 0 + [ $enable -gt 0 ] || [ $enabled -gt 0 ] +} + +openvpn_add_instance() { + local name="$1" + local dir="$2" + local conf="$3" + log "Add Instance $1 $2 $3" + + "$PROG" --syslog "openvpn($name)" --status "/var/run/openvpn.$name.status" --cd "$dir" --config "$conf" & + sleep 5 +} + +start_instance() { + local s="$1" + local name=$2 + if [ -z $name ]; then + config_get_bool bootstart "$s" 'bootstart' 0 + if [ $bootstart -eq 0 ]; then + return + fi + else + if [ $s != $name ]; then + return 1 + fi + fi + + config_get config "$s" config + config="${config:+$(readlink -f "$config")}" + section_enabled "$s" || { + append UCI_DISABLED "$config" "$LIST_SEP" + return 1 + } + + [ ! -d "/var/run" ] && mkdir -p "/var/run" + + if [ ! -z "$config" ]; then + ovpn $s $config + append UCI_STARTED "$config" "$LIST_SEP" + openvpn_add_instance "$s" "${config%/*}" "$config" + return + fi + + + [ ! -d "/var/etc" ] && mkdir -p "/var/etc" + [ -f "/var/etc/openvpn-$s.conf" ] && rm "/var/etc/openvpn-$s.conf" + + append_bools "$s" $OPENVPN_BOOLS + append_params "$s" $OPENVPN_PARAMS + openvpn_add_instance "$s" "/var/etc" "openvpn-$s.conf" +} + +start() { + check_config + checkserver $1 + . /usr/share/openvpn/openvpn.options + config_load 'openvpn' + config_foreach start_instance 'openvpn' $1 +} + + diff --git a/luci-app-rootervpn/files/usr/lib/easyrsa/stop.sh b/luci-app-rootervpn/files/usr/lib/easyrsa/stop.sh new file mode 100644 index 0000000..ce86729 --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/easyrsa/stop.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +result=`ps | grep -i "generate.sh" | grep -v "grep" | wc -l` +if [ $result -ge 1 ]; then + killall -9 generate.sh + rm -f /tmp/easyrsa + PKI_DIR="/etc/openvpn/ssl" + if [ -d "$PKI_DIR" ]; then + rm -rfv "$PKI_DIR" + fi +fi \ No newline at end of file diff --git a/luci-app-rootervpn/files/usr/lib/lua/luci/controller/openvpn.lua b/luci-app-rootervpn/files/usr/lib/lua/luci/controller/openvpn.lua new file mode 100644 index 0000000..117b73f --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/lua/luci/controller/openvpn.lua @@ -0,0 +1,39 @@ +-- Copyright 2008 Steven Barth +-- Copyright 2008 Jo-Philipp Wich +-- Licensed to the public under the Apache License 2.0. + +module("luci.controller.openvpn", package.seeall) + +function index() + entry( {"admin", "services", "openvpn"}, cbi("openvpn"), _("OpenVPN") ) + entry( {"admin", "services", "openvpn", "basic"}, cbi("openvpn-basic"), nil ).leaf = true + entry( {"admin", "services", "openvpn", "advanced"}, cbi("openvpn-advanced"), nil ).leaf = true + + entry({"admin", "services", "rsastatus"}, call("action_status")) + entry({"admin", "services", "rsagenerate"}, call("action_generate")) + entry({"admin", "services", "rsastop"}, call("action_stop")) +end + +function action_status() + local rv = {} + + file = io.open("/tmp/easyrsa", "r") + if file ~= nil then + rv["status"] = file:read("*line") + file:close() + else + rv["status"] = "0" + end + + luci.http.prepare_content("application/json") + luci.http.write_json(rv) +end + +function action_generate() + os.execute("/usr/lib/easyrsa/generate.sh &") +end + +function action_stop() + os.execute("/usr/lib/easyrsa/stop.sh") +end + diff --git a/luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/easyrsa.lua b/luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/easyrsa.lua new file mode 100644 index 0000000..c3e2c33 --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/easyrsa.lua @@ -0,0 +1,41 @@ +local utl = require "luci.util" + +m = Map("easyrsa", "OpenVPN Certificates and Keys", + translate("Create the certificates and keys for OpenVPN Server and Client")) + +m.on_after_commit = function(self) + +end + +gw = m:section(TypedSection, "easyrsa", translate("Certificate and Key Data"), translate("The following data is used to generate unique, random certs and keys.")) +gw.anonymous = true + +country = gw:option(Value, "country", translate("Country Name :"), translate("2 letter country abbreviation")); +country.optional=false; +country.rmempty = true; +country.default="CA" +country.datatype = "rangelength(2, 2)" + +city = gw:option(Value, "city", translate("City Name :")); +city.optional=false; +city.rmempty = true; +city.default="Abbotsford" +city.datatype = "minlength(2)" + +organ = gw:option(Value, "organ", translate("Organization Name :"), translate("name will appear on certs and keys")); +organ.optional=false; +organ.rmempty = true; +organ.default="ROOter" +organ.datatype = "minlength(2)" + +days = gw:option(Value, "days", translate("Days to certify for :"), translate("number of days certs and keys are valid")); +days.optional=false; +days.rmempty = true; +days.default="3650" +days.datatype = "min(1)" + +dmy = gw:option(DummyValue, "_dmy", translate(" ")) + +m:section(SimpleSection).template = "easyrsa/easyrsa" + +return m diff --git a/luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/openvpn-advanced.lua b/luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/openvpn-advanced.lua new file mode 100644 index 0000000..0b2ef0c --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/openvpn-advanced.lua @@ -0,0 +1,256 @@ +-- Copyright 2008 Steven Barth +-- Licensed to the public under the Apache License 2.0. + +require("luci.ip") +require("luci.model.uci") + + +local knownParams = { + -- + -- Widget Name Default(s) Description Option(s) + -- + + { "Service", { + -- initialisation and daemon options + { ListValue, "verb", { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, translate("Set output verbosity") }, + { Flag, "mlock", 0, translate("Disable Paging") }, + { Flag, "disable_occ", 0, translate("Disable options consistency check") }, + { Value, "cd", "/etc/openvpn", translate("Change to directory before initialization") }, + { Value, "chroot", "/var/run", translate("Chroot to directory after initialization") }, + { Flag, "passtos", 0, translate("TOS passthrough (applies to IPv4 only)") }, + { Value, "log", "/var/log/openvpn.log", translate("Write log to file") }, + { Value, "log_append", "/var/log/openvpn.log", translate("Append log to file") }, + { Flag, "suppress_timestamps", 0, translate("Don't log timestamps") }, + { Value, "nice", 0, translate("Change process priority") }, + { Flag, "fast_io", 0, translate("Optimize TUN/TAP/UDP writes") }, + { Value, "echo", "some params echoed to log", translate("Echo parameters to log") }, + { ListValue, "remap_usr1", { "SIGHUP", "SIGTERM" }, translate("Remap SIGUSR1 signals") }, + { Value, "status", "/var/run/openvpn.status 5", translate("Write status to file every n seconds") }, + { Value, "status_version", { 1, 2 }, translate("Status file format version") }, -- status + { Value, "mute", 5, translate("Limit repeated log messages") }, + + { Value, "up", "/usr/bin/ovpn-up", translate("Shell cmd to execute after tun device open") }, + { Value, "up_delay", 5, translate("Delay tun/tap open and up script execution") }, + { Value, "down", "/usr/bin/ovpn-down", translate("Shell cmd to run after tun device close") }, + { Flag, "down_pre", 0, translate("Call down cmd/script before TUN/TAP close") }, + { Flag, "up_restart", 0, translate("Run up/down scripts for all restarts") }, + { Value, "route_up", "/usr/bin/ovpn-routeup", translate("Execute shell cmd after routes are added") }, + { Value, "ipchange", "/usr/bin/ovpn-ipchange", translate("Execute shell command on remote ip change"), { mode="p2p" } }, + { DynamicList, "setenv", { "VAR1 value1", "VAR2 value2" }, translate("Pass environment variables to script") }, + { Value, "tls_verify", "/usr/bin/ovpn-tlsverify", translate("Shell command to verify X509 name") }, + { Value, "client_connect", "/usr/bin/ovpn-clientconnect", translate("Run script cmd on client connection") }, + { Flag, "client_disconnect", 0, translate("Run script cmd on client disconnection") }, + { Value, "learn_address", "/usr/bin/ovpn-learnaddress", translate("Executed in server mode whenever an IPv4 address/route or MAC address is added to OpenVPN's internal routing table") }, + { DummyValue, "auth_user_pass_verify", "/usr/bin/ovpn-userpass via-file", translate("Executed in server mode on new client connections, when the client is still untrusted. The script_security field must be 2 or 3") }, + { ListValue, "script_security", { 2, 0, 1, 3 }, translate("Policy level over usage of external programs and scripts") }, + } }, + + { "Networking", { + -- socket config + { ListValue, "mode", { "p2p", "server" }, translate("Major mode") }, + { Value, "local", "0.0.0.0", translate("Local host name or ip address") }, + { Value, "port", 1194, translate("TCP/UDP port # for both local and remote") }, + { Value, "lport", 1194, translate("TCP/UDP port # for local (default=1194)") }, + { Value, "rport", 1194, translate("TCP/UDP port # for remote (default=1194)") }, + { Flag, "float", 0, translate("Allow remote to change its IP or port") }, + { Flag, "nobind", 0, translate("Do not bind to local address and port") }, + + { ListValue, "dev", { "tun", "tun0", "tun-server", "tap", "tap0", "tap-server" }, translate("tun/tap device") }, + { ListValue, "dev_type", { "tun", "tap" }, translate("Type of used device") }, + { Value, "dev_node", "/dev/net/tun", translate("Use tun/tap device node") }, + { Flag, "tun_ipv6", 0, translate("Make tun device IPv6 capable") }, + + { Value, "ifconfig", "10.200.200.3 10.200.200.1", translate("Set tun/tap adapter parameters") }, + { Flag, "ifconfig_noexec", 0, translate("Don't actually execute ifconfig") }, + { Flag, "ifconfig_nowarn", 0, translate("Don't warn on ifconfig inconsistencies") }, + + { DynamicList, "route", "10.123.0.0 255.255.0.0", translate("Add route after establishing connection") }, + { Value, "route_gateway", "10.234.1.1", translate("Specify a default gateway for routes") }, + { Value, "route_delay", 0, translate("Delay n seconds after connection") }, + { Flag, "route_noexec", 0, translate("Don't add routes automatically") }, + { Flag, "route_nopull", 0, translate("Don't pull routes automatically") }, + + { ListValue, "mtu_disc", { "yes", "maybe", "no" }, translate("Enable Path MTU discovery") }, + { Flag, "mtu_test", 0, translate("Empirically measure MTU") }, + { ListValue, "comp_lzo", { "yes", "no", "adaptive" }, translate("Use fast LZO compression") }, + { Flag, "comp_noadapt", 0, translate("Don't use adaptive lzo compression"), { comp_lzo=1 } }, + { Value, "link_mtu", 1500, translate("Set TCP/UDP MTU") }, + { Value, "tun_mtu", 1500, translate("Set tun/tap device MTU") }, + { Value, "tun_mtu_extra", 1500, translate("Set tun/tap device overhead") }, + { Value, "fragment", 1500, translate("Enable internal datagram fragmentation"), { proto="udp" } }, + { Value, "mssfix", 1500, translate("Set upper bound on TCP MSS"), { proto="udp" } }, + { Value, "sndbuf", 65536, translate("Set the TCP/UDP send buffer size") }, + { Value, "rcvbuf", 65536, translate("Set the TCP/UDP receive buffer size") }, + { Value, "txqueuelen", 100, translate("Set tun/tap TX queue length") }, + { Value, "shaper", 10240, translate("Shaping for peer bandwidth") }, + + { Value, "inactive", 240, translate("tun/tap inactivity timeout") }, + { Value, "keepalive", "10 60", translate("Helper directive to simplify the expression of --ping and --ping-restart in server mode configurations") }, + { Value, "ping", 30, translate("Ping remote every n seconds over TCP/UDP port") }, + { Value, "ping_exit", 120, translate("Remote ping timeout") }, + { Value, "ping_restart", 60, translate("Restart after remote ping timeout") }, + { Flag, "ping_timer_rem", 0, translate("Only process ping timeouts if routes exist") }, + + { Flag, "persist_tun", 0, translate("Keep tun/tap device open on restart") }, + { Flag, "persist_key", 0, translate("Don't re-read key on restart") }, + { Flag, "persist_local_ip", 0, translate("Keep local IP address on restart") }, + { Flag, "persist_remote_ip", 0, translate("Keep remote IP address on restart") }, + + -- management channel + { Value, "management", "127.0.0.1 31194 /etc/openvpn/mngmt-pwds", translate("Enable management interface on IP port") }, + { Flag, "management_query_passwords", 0, translate("Query management channel for private key") }, -- management + { Flag, "management_hold", 0, translate("Start OpenVPN in a hibernating state") }, -- management + { Value, "management_log_cache", 100, translate("Number of lines for log file history") }, -- management + { ListValue, "topology", { "net30", "p2p", "subnet" }, translate("'net30', 'p2p', or 'subnet'"), {dev_type="tun" }, {dev="tun-server" } }, + } }, + + { "VPN", { + { Value, "server", "10.200.200.0 255.255.255.0", translate("Configure server mode"), { client="0" }, { client="" } }, + { Value, "server_bridge", "10.200.200.1 255.255.255.0 10.200.200.200 10.200.200.250", translate("Configure server bridge"), { client="0" }, { client="" } }, + { DynamicList, "push", { "redirect-gateway", "comp-lzo" }, translate("Push options to peer"), { client="0" }, { client="" } }, + { Flag, "push_reset", 0, translate("Don't inherit global push options"), { client="0" }, { client="" } }, + { Flag, "disable", 0, translate("Client is disabled"), { client="0" }, { client="" } }, + { Value, "ifconfig_pool", "10.200.200.100 10.200.200.150 255.255.255.0", translate("Set aside a pool of subnets"), { client="0" }, { client="" } }, + { Value, "ifconfig_pool_persist", "/etc/openvpn/ipp.txt 600", translate("Persist/unpersist ifconfig-pool"), { client="0" }, { client="" } }, + { Value, "ifconfig_push", "10.200.200.1 255.255.255.255", translate("Push an ifconfig option to remote"), { client="0" }, { client="" } }, + { Value, "iroute", "10.200.200.0 255.255.255.0", translate("Route subnet to client"), { client="0" }, { client="" } }, + { Flag, "client_to_client", 0, translate("Allow client-to-client traffic"), { client="0" }, { client="" } }, + { Flag, "duplicate_cn", 0, translate("Allow multiple clients with same certificate"), { client="0" }, { client="" } }, + { Value, "client_config_dir", "/etc/openvpn/ccd", translate("Directory for custom client config files"), { client="0" }, { client="" } }, + { Flag, "ccd_exclusive", 0, translate("Refuse connection if no custom client config"), { client="0" }, { client="" } }, + { Value, "tmp_dir", "/var/run/openvpn", translate("Temporary directory for client-connect return file"), { client="0" }, { client="" } }, + { Value, "hash_size", "256 256", translate("Set size of real and virtual address hash tables"), { client="0" }, { client="" } }, + { Value, "bcast_buffers", 256, translate("Number of allocated broadcast buffers"), { client="0" }, { client="" } }, + { Value, "tcp_queue_limit", 64, translate("Maximum number of queued TCP output packets"), { client="0" }, { client="" } }, + { Value, "max_clients", 10, translate("Allowed maximum of connected clients"), { client="0" }, { client="" } }, + { Value, "max_routes_per_client", 256, translate("Allowed maximum of internal"), { client="0" }, { client="" } }, + { Value, "connect_freq", "3 10", translate("Allowed maximum of new connections"), { client="0" }, { client="" } }, + { Flag, "client_cert_not_required", 0, translate("Don't require client certificate"), { client="0" }, { client="" } }, + { Flag, "username_as_common_name", 0, translate("Use username as common name"), { client="0" }, { client="" } }, + { Flag, "client", 0, translate("Configure client mode")}, + { Flag, "pull", 0, translate("Accept options pushed from server"), { client="1" } }, + { Value, "auth_user_pass", "/etc/openvpn/userpass.txt", translate("Authenticate using username/password"), { client="1" } }, + { ListValue, "auth_retry", { "none", "nointeract", "interact" }, translate("Handling of authentication failures"), { client="1" } }, + { Value, "explicit_exit_notify", 1, translate("Send notification to peer on disconnect"), { client="1" } }, + { DynamicList, "remote", "1.2.3.4", translate("Remote host name or ip address"), { client="1" } }, + { Flag, "remote_random", 1, translate("Randomly choose remote server"), { client="1" } }, + { ListValue, "proto", { "udp", "tcp-client", "tcp-server" }, translate("Use protocol"), { client="1" } }, + { Value, "connect_retry", 5, translate("Connection retry interval"), { proto="tcp-client" }, { client="1" } }, + { Value, "http_proxy", "192.168.1.100 8080", translate("Connect to remote host through an HTTP proxy"), { client="1" } }, + { Flag, "http_proxy_retry", 0, translate("Retry indefinitely on HTTP proxy errors"), { client="1" } }, + { Value, "http_proxy_timeout", 5, translate("Proxy timeout in seconds"), { client="1" } }, + { DynamicList, "http_proxy_option", { "VERSION 1.0", "AGENT OpenVPN/2.0.9" }, translate("Set extended HTTP proxy options"), { client="1" } }, + { Value, "socks_proxy", "192.168.1.200 1080", translate("Connect through Socks5 proxy"), { client="1" } }, + { Value, "socks_proxy_retry", 5, translate("Retry indefinitely on Socks proxy errors"), { client="1" } }, -- client && socks_proxy + { Value, "resolv_retry", "infinite", translate("If hostname resolve fails, retry"), { client="1" } }, + { ListValue, "redirect_gateway", { "", "local", "def1", "local def1" }, translate("Automatically redirect default route"), { client="1" } }, + } }, + + { "Cryptography", { + { FileUpload, "secret", "/etc/openvpn/secret.key", translate("Enable Static Key encryption mode (non-TLS)") }, + { Value, "auth", "SHA1", translate("HMAC authentication for packets") }, -- parse + { Value, "cipher", "BF-CBC", translate("Encryption cipher for packets") }, -- parse + { Value, "keysize", 1024, translate("Size of cipher key") }, -- parse + { Value, "engine", "dynamic", translate("Enable OpenSSL hardware crypto engines") }, -- parse + { Flag, "no_replay", 0, translate("Disable replay protection") }, + { Value, "replay_window", "64 15", translate("Replay protection sliding window size") }, + { Flag, "mute_replay_warnings", 0, translate("Silence the output of replay warnings") }, + { Value, "replay_persist", "/var/run/openvpn-replay-state", translate("Persist replay-protection state") }, + { Flag, "no_iv", 0, translate("Disable cipher initialisation vector") }, + { Flag, "tls_server", 0, translate("Enable TLS and assume server role"), { tls_client="" }, { tls_client="0" } }, + { Flag, "tls_client", 0, translate("Enable TLS and assume client role"), { tls_server="" }, { tls_server="0" } }, + { FileUpload, "ca", "/etc/easy-rsa/keys/ca.crt", translate("Certificate authority") }, + { FileUpload, "dh", "/etc/easy-rsa/keys/dh1024.pem", translate("Diffie Hellman parameters") }, + { FileUpload, "cert", "/etc/easy-rsa/keys/some-client.crt", translate("Local certificate") }, + { FileUpload, "key", "/etc/easy-rsa/keys/some-client.key", translate("Local private key") }, + { FileUpload, "pkcs12", "/etc/easy-rsa/keys/some-client.pk12", translate("PKCS#12 file containing keys") }, + { ListValue, "key_method", { 1, 2 }, translate("Enable TLS and assume client role") }, + { Value, "tls_cipher", "DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DES-CBC3-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:AES128-SHA:RC4-SHA:RC4-MD5:EDH-RSA-DES-CBC-SHA:EDH-DSS-DES-CBC-SHA:DES-CBC-SHA:EXP-EDH-RSA-DES-CBC-SHA:EXP-EDH-DSS-DES-CBC-SHA:EXP-DES-CBC-SHA:EXP-RC2-CBC-MD5:EXP-RC4-MD5", + translate("TLS cipher") }, + { Value, "tls_timeout", 2, translate("Retransmit timeout on TLS control channel") }, + { Value, "reneg_bytes", 1024, translate("Renegotiate data chan. key after bytes") }, + { Value, "reneg_pkts", 100, translate("Renegotiate data chan. key after packets") }, + { Value, "reneg_sec", 3600, translate("Renegotiate data chan. key after seconds") }, + { Value, "hand_window", 60, translate("Timeframe for key exchange") }, + { Value, "tran_window", 3600, translate("Key transition window") }, + { Flag, "single_session", 0, translate("Allow only one session") }, + { Flag, "tls_exit", 0, translate("Exit on TLS negotiation failure") }, + { FileUpload, "tls_auth", "/etc/openvpn/tlsauth.key", translate("Additional authentication over TLS") }, + { Flag, "auth_nocache", 0, translate("Don't cache --askpass or --auth-user-pass passwords") }, + { Value, "tls_remote", "remote_x509_name", translate("Only accept connections from given X509 name") }, + { ListValue, "ns_cert_type", { "client", "server" }, translate("Require explicit designation on certificate") }, + { ListValue, "remote_cert_tls", { "client", "server" }, translate("Require explicit key usage on certificate") }, + { FileUpload, "crl_verify", "/etc/easy-rsa/keys/crl.pem", translate("Check peer certificate against a CRL") }, + { Value, "tls_version_min", "1.0", translate("The lowest supported TLS version") }, + { Value, "tls_version_max", "1.2", translate("The highest supported TLS version") }, + { Value, "key_direction", "1", translate("The key direction for 'tls-auth' and 'secret' options") }, + } } +} + +local cts = { } +local params = { } + +local m = Map("openvpn") +local p = m:section( SimpleSection ) + +p.template = "openvpn/pageswitch" +p.mode = "advanced" +p.instance = arg[1] +p.category = arg[2] or "Service" + +for _, c in ipairs(knownParams) do + cts[#cts+1] = c[1] + if c[1] == p.category then params = c[2] end +end + +p.categories = cts + + +local s = m:section( + NamedSection, arg[1], "openvpn", + translate("%s" % arg[2]) +) + +s.title = translate("%s" % arg[2]) +s.addremove = false +s.anonymous = true + + +for _, option in ipairs(params) do + local o = s:option( + option[1], option[2], + option[2], option[4] + ) + + if option[1] == DummyValue then + o.value = option[3] + else + if option[1] == DynamicList then + function o.cfgvalue(...) + local val = AbstractValue.cfgvalue(...) + return ( val and type(val) ~= "table" ) and { val } or val + end + end + + o.optional = true + + if type(option[3]) == "table" then + if o.optional then o:value("", "-- remove --") end + for _, v in ipairs(option[3]) do + v = tostring(v) + o:value(v) + end + o.default = tostring(option[3][1]) + else + o.default = tostring(option[3]) + end + end + + for i=5,#option do + if type(option[i]) == "table" then + o:depends(option[i]) + end + end +end + +return m diff --git a/luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/openvpn-basic.lua b/luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/openvpn-basic.lua new file mode 100644 index 0000000..03f3a48 --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/openvpn-basic.lua @@ -0,0 +1,99 @@ +-- Copyright 2008 Steven Barth +-- Licensed to the public under the Apache License 2.0. + +require("luci.ip") +require("luci.model.uci") + + +local basicParams = { + -- + -- Widget, Name, Default(s), Description + -- + + { ListValue, "verb", { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, translate("Set output verbosity") }, + { Value, "nice",0, translate("Change process priority") }, + { Value,"port",1194, translate("TCP/UDP port # for both local and remote") }, + { ListValue, "dev", { "tun", "tun0", "tun-server", "tap", "tap0", "tap-server" }, translate("tun/tap device") }, + { ListValue,"dev_type",{ "tun", "tap" }, translate("Type of used device") }, + { Flag,"tun_ipv6",0, translate("Make tun device IPv6 capable") }, + + { Value,"ifconfig","10.200.200.3 10.200.200.1", translate("Set tun/tap adapter parameters") }, + { Value,"server","10.200.200.0 255.255.255.0", translate("Configure server mode") }, + { Value,"server_bridge","192.168.1.1 255.255.255.0 192.168.1.128 192.168.1.254", translate("Configure server bridge") }, + { Flag,"nobind",0, translate("Do not bind to local address and port") }, + + { ListValue,"comp_lzo",{"yes","no","adaptive"}, translate("Use fast LZO compression") }, + { Value,"keepalive","10 60", translate("Helper directive to simplify the expression of --ping and --ping-restart in server mode configurations") }, + + { DynamicList, "push", { "redirect-gateway", "comp-lzo" }, translate("Push options to peer"), { client="0" }, { client="" } }, + { ListValue, "topology", { "net30", "p2p", "subnet" }, translate("'net30', 'p2p', or 'subnet'"), {dev_type="tun" }, {dev="tun-server" } }, + + { ListValue,"proto",{ "udp", "udp6", "tcp", "tcp6" }, translate("Use protocol") }, + + { Flag,"client",0, translate("Configure client mode") }, + { Flag,"client_to_client",0, translate("Allow client-to-client traffic") }, + { DynamicList,"remote","vpnserver.example.org", translate("Remote host name or ip address") }, + { FileUpload, "auth_user_pass", "/etc/openvpn/userpass.txt", translate("Authenticate using username/password") }, + { FileUpload, "crl_verify", "/etc/easy-rsa/keys/crl.pem", translate("Check peer certificate against a CRL") }, + { FileUpload, "tls_auth", "/etc/openvpn/tlsauth.key", translate("Additional authentication over TLS") }, + + { FileUpload,"secret","/etc/openvpn/secret.key", translate("Enable Static Key encryption mode (non-TLS)") }, + { Value,"key_direction","1", translate("The key direction for 'tls-auth' and 'secret' options") }, + { FileUpload,"pkcs12","/etc/easy-rsa/keys/some-client.pk12", translate("PKCS#12 file containing keys") }, + { FileUpload,"ca","/etc/easy-rsa/keys/ca.crt", translate("Certificate authority") }, + { FileUpload,"dh","/etc/easy-rsa/keys/dh1024.pem", translate("Diffie Hellman parameters") }, + { FileUpload,"cert","/etc/easy-rsa/keys/some-client.crt", translate("Local certificate") }, + { FileUpload,"key","/etc/easy-rsa/keys/some-client.key", translate("Local private key") }, + { FileUpload,"config","/etc/openvpn/some-client.ovpn", translate("OVPN file") }, +} + + +local m = Map("openvpn") +local p = m:section( SimpleSection ) + +p.template = "openvpn/pageswitch" +p.mode = "basic" +p.instance = arg[1] + + +local s = m:section( NamedSection, arg[1], "openvpn" ) + +for _, option in ipairs(basicParams) do + local o = s:option( + option[1], option[2], + option[2], option[4] + ) + + o.optional = true + + if option[1] == DummyValue then + o.value = option[3] + else + if option[1] == DynamicList then + function o.cfgvalue(...) + local val = AbstractValue.cfgvalue(...) + return ( val and type(val) ~= "table" ) and { val } or val + end + end + + if type(option[3]) == "table" then + if o.optional then o:value("", "-- remove --") end + for _, v in ipairs(option[3]) do + v = tostring(v) + o:value(v) + end + o.default = tostring(option[3][1]) + else + o.default = tostring(option[3]) + end + end + + for i=5,#option do + if type(option[i]) == "table" then + o:depends(option[i]) + end + end +end + +return m + diff --git a/luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/openvpn.lua b/luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/openvpn.lua new file mode 100644 index 0000000..48bc620 --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/lua/luci/model/cbi/openvpn.lua @@ -0,0 +1,210 @@ +-- Copyright 2008 Steven Barth +-- Licensed to the public under the Apache License 2.0. + +local fs = require "nixio.fs" +local sys = require "luci.sys" +local uci = require "luci.model.uci".cursor() +local testfullps = luci.sys.exec("ps --help 2>&1 | grep BusyBox") --check which ps do we have +local psstring = (string.len(testfullps)>0) and "ps w" or "ps axfw" --set command we use to get pid + +local m = Map("openvpn", translate("OpenVPN"), translate("Set up a VPN Tunnel on your Router")) + +m.on_after_commit = function(self) + luci.sys.call("/usr/lib/easyrsa/firewall.sh") + luci.sys.call("/usr/lib/easyrsa/dns.sh") +end + +local_web="

                                                                       

                                                                      " + +local s = m:section( TypedSection, "openvpn", translate("OpenVPN instances"), translate("Below is a list of configured OpenVPN instances and their current state") ) +s.template = "cbi/tblsection" +s.template_addremove = "openvpn/cbi-select-input-add" +s.addremove = true +s.add_select_options = { } +s.extedit = luci.dispatcher.build_url( + "admin", "services", "openvpn", "basic", "%s" +) + +uci:load("rooter_recipes") +uci:foreach( "rooter_recipes", "openvpn_recipe", + function(section) + s.add_select_options[section['.name']] = + section['_description'] or section['.name'] + end +) + +function s.getPID(section) -- Universal function which returns valid pid # or nil + local pid = sys.exec("%s | grep -w %s | grep openvpn | grep -v grep | awk '{print $1}'" % { psstring,section} ) + if pid and #pid > 0 and tonumber(pid) ~= nil then + return tonumber(pid) + else + return nil + end +end + +function s.parse(self, section) + local recipe = luci.http.formvalue( + luci.cbi.CREATE_PREFIX .. self.config .. "." .. + self.sectiontype .. ".select" + ) + + if recipe and not s.add_select_options[recipe] then + self.invalid_cts = true + else + TypedSection.parse( self, section ) + end +end + +function s.create(self, name) + local recipe = luci.http.formvalue( + luci.cbi.CREATE_PREFIX .. self.config .. "." .. + self.sectiontype .. ".select" + ) + name = luci.http.formvalue( + luci.cbi.CREATE_PREFIX .. self.config .. "." .. + self.sectiontype .. ".text" + ) + if string.len(name)>3 and not name:match("[^a-zA-Z0-9_]") then + uci:section( + "openvpn", "openvpn", name, + uci:get_all( "rooter_recipes", recipe ) + ) + + uci:delete("openvpn", name, "_role") + uci:delete("openvpn", name, "_description") + uci:save("openvpn") + + luci.http.redirect( self.extedit:format(name) ) + else + self.invalid_cts = true + end +end + + +s:option( Flag, "enabled", translate("Enabled") ) +s:option( Flag, "bootstart", translate("Start on Bootup") ) + +local active = s:option( DummyValue, "_active", translate("Started") ) +function active.cfgvalue(self, section) + local pid = s.getPID(section) + if pid ~= nil then + return (sys.process.signal(pid, 0)) + and translatef("yes (%i)", pid) + or translate("no") + end + return translate("no") +end + +local updown = s:option( Button, "_updown", translate("Start/Stop") ) +updown._state = false +updown.redirect = luci.dispatcher.build_url( + "admin", "services", "openvpn" +) +function updown.cbid(self, section) + local pid = s.getPID(section) + self._state = pid ~= nil and sys.process.signal(pid, 0) + self.option = self._state and "stop" or "start" + return AbstractValue.cbid(self, section) +end +function updown.cfgvalue(self, section) + self.title = self._state and "stop" or "start" + self.inputstyle = self._state and "reset" or "reload" +end +function updown.write(self, section, value) + if self.option == "stop" then + local pid = s.getPID(section) + if pid ~= nil then + sys.process.signal(pid,15) + while pid ~= nil do + pid = s.getPID(section) + end + end + else + luci.sys.call("/etc/init.d/openvpn start %s" % section) + end + luci.http.redirect( self.redirect ) +end + + +local port = s:option( DummyValue, "port", translate("Port") ) +function port.cfgvalue(self, section) + local val = AbstractValue.cfgvalue(self, section) + return val or "1194" +end + +local proto = s:option( DummyValue, "proto", translate("Protocol") ) +function proto.cfgvalue(self, section) + local val = AbstractValue.cfgvalue(self, section) + return val or "udp" +end + +--xw = m:section(TypedSection, "settings", translate(" "), translate("Click the button below for a guide on using the Recipes for creating OpenVPN Instances.")) +--xw.anonymous = true +--btn = xw:option(DummyValue, "_dmy", translate(" ") .. local_web); + +gw = m:section(TypedSection, "settings", translate("Advanced Options")) +gw.anonymous = true +gw:tab("default", translate("Custom Firewall Settings")) +gw:tab("dns", translate("Custom DNS Settings")) +gw:tab("key", translate("Key and Certificate Generation")) + +this_tab = "default" + +gw:taboption(this_tab, Flag, "vpn2lan", translate("Forward Client VPN to LAN"), translate("(Client) Allow clients behind the VPN server to connect to computers within your LAN") ) +gw:taboption(this_tab, Flag, "vpns2lan", translate("Forward Server VPN to LAN"), translate("(Server) Allow clients behind the VPN server to connect to computers within your LAN") ) +gw:taboption(this_tab, Flag, "vpn2wan", translate("Forward Server VPN to WAN"), translate("(Server) Allow clients to connect to the internet (WAN) through the tunnel") ) + +this_tab = "dns" + +gw:taboption(this_tab, Flag, "lanopendns", translate("LAN DNS using OpenDNS"), translate("Fixed DNS on LAN interface using OpenDNS") ) +gw:taboption(this_tab, Flag, "langoogle", translate("LAN DNS using Google"), translate("Fixed DNS on LAN interface using Google") ) +gw:taboption(this_tab, Flag, "wanopendns", translate("WAN DNS using OpenDNS"), translate("Fixed DNS on WAN interface using OpenDNS") ) +gw:taboption(this_tab, Flag, "wangoogle", translate("WAN DNS using Google"), translate("Fixed DNS on WAN interface using Google") ) + +this_tab = "key" + +country = gw:taboption(this_tab, Value, "country", translate("Country Name :"), translate("2 letter country abbreviation")); +country.optional=false; +country.rmempty = true; +country.default="CA" +country.datatype = "rangelength(2, 2)" + +city = gw:taboption(this_tab, Value, "city", translate("City Name :")); +city.optional=false; +city.rmempty = true; +city.default="Abbotsford" +city.datatype = "minlength(2)" + +organ = gw:taboption(this_tab, Value, "organ", translate("Organization Name :"), translate("name will appear on certs and keys")); +organ.optional=false; +organ.rmempty = true; +organ.default="ROOter" +organ.datatype = "minlength(2)" + +comm = gw:taboption(this_tab, Value, "comm", translate("Common Name :"), translate("(Optional) Common Name of Organization")); +comm.optional=true; +comm.rmempty = true; + +unit = gw:taboption(this_tab, Value, "unit", translate("Section Name :"), translate("(Optional) Name of Section")); +unit.optional=true; +unit.rmempty = true; + +unstruc = gw:taboption(this_tab, Value, "unstruc", translate("Optional Organization Name :"), translate("(Optional) Another Name for Organization")); +unstruc.optional=true; +unstruc.rmempty = true; + +email = gw:taboption(this_tab, Value, "email", translate("Email Address :"), translate("(Optional) Email Address")); +unit.optional=true; +unit.rmempty = true; + +days = gw:taboption(this_tab, Value, "days", translate("Days to certify for :"), translate("number of days certs and keys are valid")); +days.optional=false; +days.rmempty = true; +days.default="3650" +days.datatype = "min(1)" + +sx = gw:taboption(this_tab, Value, "_dmy1", translate(" ")) +sx.template = "easyrsa/easyrsa" + + +return m diff --git a/luci-app-rootervpn/files/usr/lib/lua/luci/view/easyrsa/easyrsa.htm b/luci-app-rootervpn/files/usr/lib/lua/luci/view/easyrsa/easyrsa.htm new file mode 100644 index 0000000..71cec41 --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/lua/luci/view/easyrsa/easyrsa.htm @@ -0,0 +1,172 @@ + + + +
                                                                      + + + + + + + +
                                                                         
                                                                      +

                                                                       

                                                                      + + + + + + + +
                                                                       Key Generation will take 60 minutes or more so be patient.  
                                                                      +

                                                                       

                                                                      +

                                                                      Status of Key Generation

                                                                      +

                                                                       

                                                                      + + + + +
                                                                      + +
                                                                      + +

                                                                       

                                                                      + + + + + + + + +
                                                                         
                                                                      + +
                                                                      diff --git a/luci-app-rootervpn/files/usr/lib/lua/luci/view/openvpn/cbi-select-input-add.htm b/luci-app-rootervpn/files/usr/lib/lua/luci/view/openvpn/cbi-select-input-add.htm new file mode 100644 index 0000000..0166de7 --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/lua/luci/view/openvpn/cbi-select-input-add.htm @@ -0,0 +1,11 @@ +
                                                                      + <% if self.invalid_cts then -%>
                                                                      <% end %> + + + + <% if self.invalid_cts then %>
                                                                      <%:Invalid%>
                                                                      <% end %> +
                                                                      diff --git a/luci-app-rootervpn/files/usr/lib/lua/luci/view/openvpn/pageswitch.htm b/luci-app-rootervpn/files/usr/lib/lua/luci/view/openvpn/pageswitch.htm new file mode 100644 index 0000000..f22cb68 --- /dev/null +++ b/luci-app-rootervpn/files/usr/lib/lua/luci/view/openvpn/pageswitch.htm @@ -0,0 +1,30 @@ +<%# + Copyright 2008 Steven Barth + Copyright 2008 Jo-Philipp Wich + Licensed to the public under the Apache License 2.0. +-%> + +
                                                                      + + <%:Overview%> » + <%=luci.i18n.translatef("Instance \"%s\"", self.instance)%> + + + <% if self.mode == "basic" then %> + "><%:Switch to advanced configuration »%> + <% else %> + <%:« Switch to basic configuration%> +
                                                                      + <%:Configuration category%>: + <% for i, c in ipairs(self.categories) do %> + <% if c == self.category then %> + <%=translate(c)%> + <% else %> + "><%=translate(c)%> + <% end %> + <% if next(self.categories, i) then %>|<% end %> + <% end %> + <% end %> +
                                                                      diff --git a/luci-proto-3x/Makefile b/luci-proto-3x/Makefile new file mode 100644 index 0000000..a28fb4e --- /dev/null +++ b/luci-proto-3x/Makefile @@ -0,0 +1,62 @@ +# +# Copyright (C) 2007-2013 OpenWrt.org +# Copyright (C) 2010 Vertical Communications +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-proto-3x +PKG_VERSION:=1.0 +PKG_RELEASE:=1 +PKG_MAINTAINER:=Dairyman +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE) + +include $(INCLUDE_DIR)/package.mk + +define Package/luci-proto-3x/Default + VERSION:=$(PKG_VERSION)-$(PKG_RELEASE) + URL:=http://openwrt.org/ + MAINTAINER:=Dairyman +endef + +define Package/luci-proto-3x +$(call Package/luci-proto-3x/Default) + SECTION:=net + CATEGORY:=ROOter + SUBMENU:=Protocols + TITLE:=Support for 3x +endef + +define Package/luci-proto-3x/description + This package contains LuCI support for 3x +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile/Default +endef + +Build/Compile = $(Build/Compile/Default) + +define Package/luci-proto-3x/install + $(INSTALL_DIR) $(1)/usr + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_DIR) $(1)/usr/lib/lua + $(INSTALL_DIR) $(1)/usr/lib/lua/luci + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/network + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi/admin_network + $(INSTALL_DATA) ./files/usr/lib/lua/luci/model/network/proto_3x.lua $(1)/usr/lib/lua/luci/model/network/ + $(INSTALL_DATA) ./files/usr/lib/lua/luci/model/cbi/admin_network/proto_3x.lua $(1)/usr/lib/lua/luci/model/cbi/admin_network/ +endef + +$(eval $(call BuildPackage,luci-proto-3x)) diff --git a/luci-proto-3x/files/usr/lib/lua/luci/model/cbi/admin_network/proto_3x.lua b/luci-proto-3x/files/usr/lib/lua/luci/model/cbi/admin_network/proto_3x.lua new file mode 100644 index 0000000..59bf2f6 --- /dev/null +++ b/luci-proto-3x/files/usr/lib/lua/luci/model/cbi/admin_network/proto_3x.lua @@ -0,0 +1,146 @@ +-- Copyright 2011 Jo-Philipp Wich +-- Licensed to the public under the Apache License 2.0. + +local map, section, net = ... + +local device, apn, service, pincode, username, password, dialnumber +local ipv6, maxwait, defaultroute, metric, peerdns, dns, + keepalive_failure, keepalive_interval, demand + + +device = section:taboption("general", Value, "device", translate("Modem device")) +device.rmempty = false + +local device_suggestions = nixio.fs.glob("/dev/tty[A-Z]*") + or nixio.fs.glob("/dev/tts/*") + +if device_suggestions then + local node + for node in device_suggestions do + device:value(node) + end +end + + +service = section:taboption("general", Value, "service", translate("Service Type")) +service:value("", translate("-- Please choose --")) +service:value("umts", "UMTS/GPRS") +service:value("umts_only", translate("UMTS only")) +service:value("gprs_only", translate("GPRS only")) +service:value("evdo", "CDMA/EV-DO") + + +apn = section:taboption("general", Value, "apn", translate("APN")) + + +pincode = section:taboption("general", Value, "pincode", translate("PIN")) + + +username = section:taboption("general", Value, "username", translate("PAP/CHAP username")) + + +password = section:taboption("general", Value, "password", translate("PAP/CHAP password")) +password.password = true + +dialnumber = section:taboption("general", Value, "dialnumber", translate("Dial number")) +dialnumber.placeholder = "*99***1#" + +if luci.model.network:has_ipv6() then + + ipv6 = section:taboption("advanced", Flag, "ipv6", + translate("Enable IPv6 negotiation on the PPP link")) + + ipv6.default = ipv6.disabled + +end + + +maxwait = section:taboption("advanced", Value, "maxwait", + translate("Modem init timeout"), + translate("Maximum amount of seconds to wait for the modem to become ready")) + +maxwait.placeholder = "20" +maxwait.datatype = "min(1)" + + +defaultroute = section:taboption("advanced", Flag, "defaultroute", + translate("Use default gateway"), + translate("If unchecked, no default route is configured")) + +defaultroute.default = defaultroute.enabled + + +metric = section:taboption("advanced", Value, "metric", + translate("Use gateway metric")) + +metric.placeholder = "0" +metric.datatype = "uinteger" +metric:depends("defaultroute", defaultroute.enabled) + + +peerdns = section:taboption("advanced", Flag, "peerdns", + translate("Use DNS servers advertised by peer"), + translate("If unchecked, the advertised DNS server addresses are ignored")) + +peerdns.default = peerdns.enabled + + +dns = section:taboption("advanced", DynamicList, "dns", + translate("Use custom DNS servers")) + +dns:depends("peerdns", "") +dns.datatype = "ipaddr" +dns.cast = "string" + + +keepalive_failure = section:taboption("advanced", Value, "_keepalive_failure", + translate("LCP echo failure threshold"), + translate("Presume peer to be dead after given amount of LCP echo failures, use 0 to ignore failures")) + +function keepalive_failure.cfgvalue(self, section) + local v = m:get(section, "keepalive") + if v and #v > 0 then + return tonumber(v:match("^(%d+)[ ,]+%d+") or v) + end +end + +function keepalive_failure.write() end +function keepalive_failure.remove() end + +keepalive_failure.placeholder = "0" +keepalive_failure.datatype = "uinteger" + + +keepalive_interval = section:taboption("advanced", Value, "_keepalive_interval", + translate("LCP echo interval"), + translate("Send LCP echo requests at the given interval in seconds, only effective in conjunction with failure threshold")) + +function keepalive_interval.cfgvalue(self, section) + local v = m:get(section, "keepalive") + if v and #v > 0 then + return tonumber(v:match("^%d+[ ,]+(%d+)")) + end +end + +function keepalive_interval.write(self, section, value) + local f = tonumber(keepalive_failure:formvalue(section)) or 0 + local i = tonumber(value) or 5 + if i < 1 then i = 1 end + if f > 0 then + m:set(section, "keepalive", "%d %d" %{ f, i }) + else + m:del(section, "keepalive") + end +end + +keepalive_interval.remove = keepalive_interval.write +keepalive_interval.placeholder = "5" +keepalive_interval.datatype = "min(1)" + + +demand = section:taboption("advanced", Value, "demand", + translate("Inactivity timeout"), + translate("Close inactive connection after the given amount of seconds, use 0 to persist connection")) + +demand.placeholder = "0" +demand.datatype = "uinteger" diff --git a/luci-proto-3x/files/usr/lib/lua/luci/model/network/proto_3x.lua b/luci-proto-3x/files/usr/lib/lua/luci/model/network/proto_3x.lua new file mode 100644 index 0000000..9795a7c --- /dev/null +++ b/luci-proto-3x/files/usr/lib/lua/luci/model/network/proto_3x.lua @@ -0,0 +1,68 @@ +--[[ +LuCI - Network model - 3G, PPP, PPtP, PPPoE and PPPoA protocol extension + +Copyright 2011 Jo-Philipp Wich + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +]]-- + +local netmod = luci.model.network + +local _, p +for _, p in ipairs({"3x"}) do + + local proto = netmod:register_protocol(p) + + function proto.get_i18n(self) + return luci.i18n.translate("UMTS/GPRS/EV-DO") + end + + function proto.ifname(self) + return p .. "-" .. self.sid + end + + function proto.opkg_package(self) + return "comgt" + end + + function proto.is_installed(self) + return nixio.fs.access("/lib/netifd/proto/3x.sh") + end + + function proto.is_floating(self) + return (p ~= "pppoe") + end + + function proto.is_virtual(self) + return true + end + + function proto.get_interfaces(self) + if self:is_floating() then + return nil + else + return netmod.protocol.get_interfaces(self) + end + end + + function proto.contains_interface(self, ifc) + if self:is_floating() then + return (netmod:ifnameof(ifc) == self:ifname()) + else + return netmod.protocol.contains_interface(self, ifc) + end + end + + netmod:register_pattern_virtual("^%s-%%w" % p) +end \ No newline at end of file diff --git a/luci-proto-mbim/Makefile b/luci-proto-mbim/Makefile new file mode 100644 index 0000000..ca3441c --- /dev/null +++ b/luci-proto-mbim/Makefile @@ -0,0 +1,62 @@ +# +# Copyright (C) 2007-2013 OpenWrt.org +# Copyright (C) 2010 Vertical Communications +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-proto-mbim +PKG_VERSION:=1.0 +PKG_RELEASE:=1 +PKG_MAINTAINER:=Dairyman +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE) + +include $(INCLUDE_DIR)/package.mk + +define Package/luci-proto-mbim/Default + VERSION:=$(PKG_VERSION)-$(PKG_RELEASE) + URL:=http://openwrt.org/ + MAINTAINER:=Dairyman +endef + +define Package/luci-proto-mbim +$(call Package/luci-proto-mbim/Default) + SECTION:=net + CATEGORY:=ROOter + SUBMENU:=Protocols + TITLE:=Support for mbim +endef + +define Package/luci-proto-mbim/description + This package contains LuCI support for mbim +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Configure +endef + +define Build/Compile/Default +endef + +Build/Compile = $(Build/Compile/Default) + +define Package/luci-proto-mbim/install + $(INSTALL_DIR) $(1)/usr + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_DIR) $(1)/usr/lib/lua + $(INSTALL_DIR) $(1)/usr/lib/lua/luci + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/network + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi/admin_network + $(INSTALL_DATA) ./files/usr/lib/lua/luci/model/network/proto_mbim.lua $(1)/usr/lib/lua/luci/model/network/ + $(INSTALL_DATA) ./files/usr/lib/lua/luci/model/cbi/admin_network/proto_mbim.lua $(1)/usr/lib/lua/luci/model/cbi/admin_network/ +endef + +$(eval $(call BuildPackage,luci-proto-mbim)) diff --git a/luci-proto-mbim/files/usr/lib/lua/luci/model/cbi/admin_network/proto_mbim.lua b/luci-proto-mbim/files/usr/lib/lua/luci/model/cbi/admin_network/proto_mbim.lua new file mode 100644 index 0000000..380f471 --- /dev/null +++ b/luci-proto-mbim/files/usr/lib/lua/luci/model/cbi/admin_network/proto_mbim.lua @@ -0,0 +1,45 @@ +-- Copyright 2016 David Thornley +-- Licensed to the public under the Apache License 2.0. + +local map, section, net = ... + +local device, apn, pincode, username, password +local auth, ipv6 + + +device = section:taboption("general", Value, "device", translate("Modem device")) +device.rmempty = false + +local device_suggestions = nixio.fs.glob("/dev/cdc-wdm*") + +if device_suggestions then + local node + for node in device_suggestions do + device:value(node) + end +end + + +apn = section:taboption("general", Value, "apn", translate("APN")) + + +pincode = section:taboption("general", Value, "pincode", translate("PIN")) + + +username = section:taboption("general", Value, "username", translate("PAP/CHAP username")) + + +password = section:taboption("general", Value, "password", translate("PAP/CHAP password")) +password.password = true + +auth = section:taboption("general", Value, "auth", translate("Authentication Type")) +auth:value("", translate("-- Please choose --")) +auth:value("both", "PAP/CHAP (both)") +auth:value("pap", "PAP") +auth:value("chap", "CHAP") +auth:value("none", "NONE") + +if luci.model.network:has_ipv6() then + ipv6 = section:taboption("advanced", Flag, "ipv6", translate("Enable IPv6 negotiation")) + ipv6.default = ipv6.disabled +end \ No newline at end of file diff --git a/luci-proto-mbim/files/usr/lib/lua/luci/model/network/proto_mbim.lua b/luci-proto-mbim/files/usr/lib/lua/luci/model/network/proto_mbim.lua new file mode 100644 index 0000000..2cc525a --- /dev/null +++ b/luci-proto-mbim/files/usr/lib/lua/luci/model/network/proto_mbim.lua @@ -0,0 +1,51 @@ +-- Copyright 2016 David Thornley +-- Licensed to the public under the Apache License 2.0. + +local netmod = luci.model.network +local interface = luci.model.network.interface +local proto = netmod:register_protocol("mbim") + +function proto.get_i18n(self) + return luci.i18n.translate("MBIM") +end + +function proto.ifname(self) + local base = netmod._M.protocol + local ifname = base.ifname(self) -- call base class "protocol.ifname(self)" + + -- Note: ifname might be nil if the adapter could not be determined through ubus (default name to qmi-wan in this case) + if ifname == nil then + ifname = "mbim-" .. self.sid + end + return ifname +end + +function proto.get_interface(self) + return interface(self:ifname(), self) +end + +function proto.opkg_package(self) + return "rmbim" +end + +function proto.is_installed(self) + return nixio.fs.access("/lib/netifd/proto/mbim.sh") +end + +function proto.is_floating(self) + return true +end + +function proto.is_virtual(self) + return true +end + +function proto.get_interfaces(self) + return nil +end + +function proto.contains_interface(self, ifc) + return (netmod:ifnameof(ifc) == self:ifname()) +end + +netmod:register_pattern_virtual("^mbim-%w") \ No newline at end of file diff --git a/luci-theme-darkmatter/Makefile b/luci-theme-darkmatter/Makefile new file mode 100644 index 0000000..cdd95e1 --- /dev/null +++ b/luci-theme-darkmatter/Makefile @@ -0,0 +1,66 @@ +# luci-theme-darkmatter +# Copyright 2017 chrono +# +# Darkmatter is an alternative HTML5 theme for LuCI that has evolved from +# luci-theme-bootstrap & luci-theme-material, in an attempt to bring a more +# concise, clean and visually pleasing UX to LEDE/OpenWRT. +# +# Have a bug? Please create an issue here on GitHub! +# https://github.com/apollo-ng/luci-theme-darkmatter/issues +# +# luci-theme-material +# Copyright 2015 Lutty Yang +# luci-theme-bootstrap: +# Copyright 2008 Steven Barth +# Copyright 2008 Jo-Philipp Wich +# Copyright 2012 David Menting +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. + +include $(TOPDIR)/rules.mk + +THEME_NAME:=darkmatter +THEME_TITLE:=Darkmatter + +PKG_NAME:=luci-theme-$(THEME_NAME) +PKG_VERSION:=0.1.77 +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/luci-theme-$(THEME_NAME) + SECTION:=luci + CATEGORY:=LuCI + SUBMENU:=4. Themes + DEPENDS:=+libc + TITLE:=$(THEME_TITLE) Theme + URL:=https://github.com/apollo-ng/luci-theme-darkmatter + PKGARCH:=all +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/luci-theme-$(THEME_NAME)/install + $(INSTALL_DIR) $(1)/etc/uci-defaults + echo "uci set luci.themes.$(THEME_TITLE)=/luci-static/$(THEME_NAME); uci commit luci" > $(1)/etc/uci-defaults/luci-theme-$(THEME_NAME) + $(INSTALL_DIR) $(1)/www/luci-static/$(THEME_NAME) + $(CP) -a ./files/htdocs/* $(1)/www/luci-static/$(THEME_NAME)/ 2>/dev/null || true + $(INSTALL_DIR) $(1)/usr/lib/lua/luci/view/themes/$(THEME_NAME) + $(CP) -a ./files/templates/* $(1)/usr/lib/lua/luci/view/themes/$(THEME_NAME)/ 2>/dev/null || true +endef + +define Package/luci-theme-$(THEME_NAME)/postinst +#!/bin/sh +[ -n "$${IPKG_INSTROOT}" ] || { + ( . /etc/uci-defaults/luci-theme-$(THEME_NAME) ) && rm -f /etc/uci-defaults/luci-theme-$(THEME_NAME) +} +endef + +$(eval $(call BuildPackage,luci-theme-$(THEME_NAME))) diff --git a/luci-theme-darkmatter/files/htdocs/css/style.css b/luci-theme-darkmatter/files/htdocs/css/style.css new file mode 100644 index 0000000..e302252 --- /dev/null +++ b/luci-theme-darkmatter/files/htdocs/css/style.css @@ -0,0 +1,2021 @@ +/** + * luci-theme-darkmatter + * Copyright 2017 chrono + * + * Darkmatter is an alternative HTML5 theme for LuCI that has evolved from + * luci-theme-bootstrap & luci-theme-material, in an attempt to bring a more + * concise, clean and visually pleasing UX to LEDE/OpenWRT. + * + * Have a bug? Please create an issue here on GitHub! + * https://github.com/apollo-ng/luci-theme-darkmatter/issues + * + * luci-theme-material + * Copyright 2015 Lutty Yang + * luci-theme-bootstrap: + * Copyright 2008 Steven Barth + * Copyright 2008 Jo-Philipp Wich + * Copyright 2012 David Menting + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + */ + +@font-face { + font-family: 'icomoon'; + src: url('../fonts/font.eot'); + src: url('../fonts/font.eot') format('embedded-opentype'), + url('../fonts/font.ttf') format('truetype'), + url('../fonts/font.woff') format('woff'), + url('../fonts/font.svg') format('svg'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "DIN"; + src: url("../fonts/DIN.eot"); + src: + url("../fonts/DIN.eot?#iefix") format('embedded-opentype'), + url("../fonts/DIN.woff") format('woff'), + url("../fonts/DIN.ttf") format('truetype'), + url("../fonts/DIN.svg#DIN") format('svg'); + font-weight: 400; + font-style: normal; + font-stretch: normal; + unicode-range: U+0020-F000; +} + +.cbi-button-up, +.cbi-button-down, +.cbi-value-helpicon, +.showSide, +.main > .loading > span { + speak: none; + font-style: normal !important; + font-weight: normal !important; + font-variant: normal !important; + text-transform: none !important; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: inherit; + font-weight: 400; + line-height: 1; + color: inherit; +} + +html { + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +html, body { + margin: 0; + padding: 0; + height: 100%; + font-family: "DIN", Helvetica, Arial, sans-serif; +} + +a, +a:link, +a:active, +a:visited, +a:hover { + color: rgba(117, 137, 12, 0.75); + text-decoration: none; +} + +select, +input { + background-color: #1f1e1a; + color: #fff; + text-shadow: -1px 0 #000, 0 1px #000, 1px 0 #000, 0 -1px #000, 0 0 3px #000; + border: 1px solid #4f4e4a; + outline: 0; + padding: 0; + box-shadow: inset 0 0 12px #000; + border-radius: 0; + background-image: none; + height: 1.9rem; + font-size: 1rem; +} + +input { + padding-left: 4px; +} + +input[type='checkbox'], +input[type='radio'] { + background: transparent; + box-shadow: none; +} + +input[type='file'] { + padding: 0 !important; + height: inherit; +} + +select:not([multiple="multiple"]):focus, +input:focus { + border-color: rgba(117, 136, 12, 0.94); +} + +select[multiple="multiple"] { + height: auto; +} + +code { + color: #0f0e0a; + text-shadow: none; +} + +abbr { + color: rgba(117, 137, 12, 0.85); + text-shadow: 0 0 2px rgba(0,0,0,1), 0 0 2px rgba(0,0,0,1); + text-decoration: none; + cursor: help; +} + +hr { + margin: 1rem 0; + border-color: #1f1e1a; + opacity: 0.1; +} + +header, .main { + width: 100%; + position: absolute; +} + +header { + height: 56px; + background: #1f1e1a url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTA5IiBoZWlnaHQ9IjUwOSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+IDxkZWZzPiA8cGF0dGVybiBpZD0ic3RyaXBlcyIgeD0iMCIgeT0iMCIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIiBwYXR0ZXJuVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBwYXR0ZXJuVHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4gPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwIiBoZWlnaHQ9IjIwIiByeD0iMCIgcnk9IjAiIHN0eWxlPSJzdHJva2U6IG5vbmU7IGZpbGw6ICMyZjJlMmE7Ii8+IDxyZWN0IHg9IjEwIiB5PSIwIiB3aWR0aD0iMTAiIGhlaWdodD0iMjAiIHJ4PSIwIiByeT0iMCIgc3R5bGU9InN0cm9rZTogbm9uZTsgZmlsbDogIzFmMWUxYTsiLz4gPHBhdGggZD0iTTIwLDAgTDIwLDIwIiBzdHlsZT0ic3Ryb2tlOiM1ZjVlNWE7IHN0cm9rZS13aWR0aDogMXB4OyBmaWxsOm5vbmU7IiAvPiA8cGF0aCBkPSJNMTAsMCBMMTAsMjAiIHN0eWxlPSJzdHJva2U6IzBmMGUwYTsgc3Ryb2tlLXdpZHRoOiAxcHg7IGZpbGw6bm9uZTsiIC8+IDwvcGF0dGVybj4gPC9kZWZzPiA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iNTA5IiBoZWlnaHQ9IjUwOSIgcng9IjAiIHJ5PSIwIiBzdHlsZT0ic3Ryb2tlOiBub25lOyBmaWxsOiB1cmwoI3N0cmlwZXMpOyBvcGFjaXR5OiAwLjYiLz4gPC9zdmc+") repeat; + color: #d8d3c5; + box-shadow: 0 2px 5px rgba(0, 0, 0, .26), inset 0 0 42px 0 #000;; + transition: box-shadow .2s; + float: left; + position: fixed; + z-index: 101; +} + +header > .container { + padding: 0 1rem 0 1rem; + height: 56px; + line-height: 56px; +} + +header > .container > .brand { + font-size: 1.5rem; + color: #d8d3c5; + text-decoration: none; + cursor: default; + vertical-align: text-bottom; +} + +footer { + text-align: right; + padding: 1rem; + color: #aaa; + font-size: 0.8rem; + text-shadow: 0px 0px 2px #BBB; +} + +footer > a { + color: #aaa; + text-decoration: none; +} + +.main { + top: 56px; + bottom: 0rem; + position: relative; + height: 100%; + height: calc(100% - 56px); +} + +.main > .loading { + position: fixed; + width: 100%; + height: 100%; + z-index: 1000; + display: block; + background-color: #4f4e4a; + top: 0; +} + +.main > .loading > span { + display: block; + text-align: center; + margin-top: 20%; + color: #d8d3c5; + font-size: 2rem; +} + +.main > .loading > span > .loading-img:before { + font-family: 'icomoon'; + content: "\e603"; +} + +.main > .loading > span > .loading-img { + + animation: anim-rotate 2s infinite linear; + margin-right: 1rem; + display: inline-block; + color: rgba(117, 137, 12, 1); +} + +@keyframes anim-rotate { + 0% { + -webkit-transform: rotate(0); + -ms-transform: rotate(0); + transform: rotate(0); + } + 100% { + -webkit-transform: rotate(360deg); + -ms-transform: rotate(360deg); + transform: rotate(360deg) + } +} + +.main-left { + float: left; + top: 56px; + width: 15%; + width: calc(0% + 15rem); + height: 100%; + height: calc(100% - 56px); + color: #d8d3c5; + background-color: #2f2e2a; + background: #2f2e2a; + background: linear-gradient(to bottom, #2f2e2a 0%, #1f1e1a 100%); + background: -webkit-linear-gradient(top, #2f2e2a 0%, #1f1e1a 100%); + overflow-x: auto; + position: fixed; +} + +.main-right { + width: 85%; + width: calc(100% - 15rem); + float: right; + height: 100%; + font-size: 0.85rem; + background-color: #4f4e4a; + text-shadow: -1px 0 #1f1e1a, 0 1px #1f1e1a, 1px 0 #1f1e1a, 0 -1px #1f1e1a, 0 0 3px #1f1e1a; +} + +.main-right > #maincontent { + background-color: #4f4e4a; + color: #fafafa; +} + +.pull-right { + float: right; +} + +.pull-left { + float: left; +} + +.warning { + background-color: #FF7D60 !important; + color: #FFF; +} + +.errorbox, +.alert-message { + margin: 2rem 0 0 0; + padding: 2rem; + border: 0; + font-weight: normal; + font-style: normal; + line-height: 1; + font-family: inherit; + min-width: inherit; + overflow: auto; + border-radius: 0; + background-color: #FFF; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .16), 0 0 2px 0 rgba(0, 0, 0, .12); +} + +.errorbox { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} + +.error { + color: red; +} + +#maincontent > .container > div:nth-child(1).alert-message.warning > a { + font: inherit; + overflow: visible; + text-transform: none; + display: inline-block; + margin-bottom: 0; + font-weight: 400; + text-align: center; + white-space: nowrap; + vertical-align: middle; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + min-width: 6rem; + padding: 0.5rem 1rem; + font-size: 0.9rem; + line-height: 1.42857143; + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; + margin-top: 2rem; + text-decoration: inherit; +} + +.main > .main-left > .nav > li a { + color: #d8d3c5; + display: block; + text-shadow: -1px 0 #1f1e1a, 0 1px #1f1e1a, 1px 0 #1f1e1a, 0 -1px #1f1e1a, 0 0 3px #1f1e1a; +} + +.main > .main-left > .nav > li:nth-last-child(1) { + background: rgba(175, 46, 17, 0.3); + margin: 0; +} + +.main > .main-left > .nav > li:nth-last-child(1) a { + text-decoration: none; +} + +.main > .main-left > .nav > li { + /* border-top: 1px solid #3c3b38; */ + border-bottom: 1px solid #0f0e0a; + background: #2f2e2a url("data:image/png;base64,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") repeat; + padding: 0.5rem 1rem; + cursor: pointer; +} + +.main > .main-left > .nav > .slide { + padding: 0; +} + +.main > .main-left > .nav > .slide > ul { + display: none; +} + +.main > .main-left > .nav > .slide > .menu { + display: block; + padding-left: 1rem; + text-decoration: none; + cursor: pointer; + font-size: 1.1rem; + line-height: 42px; + height: 42px; +} + +.main > .main-left > .nav > .slide > a.menu.active { + border-bottom: 1px solid #0f0e0a; +} + +.main > .main-left > .nav > .slide > .menu:hover { + background: #4f4e4a url("data:image/png;base64,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") repeat; +} + +.main > .main-left > .nav > .slide > .menu.active { + color: #fafafa; +} + +.main > .main-left > .nav > .slide > .menu.active:hover { + background: transparent; +} + +.main > .main-left > .nav > .slide > .slide-menu > .active { + background: #4f4e4a !important; + border-top: 1px solid #63615e !important; +} + +.main > .main-left > .nav > .slide > .slide-menu.active > li { + padding-left: 2rem; + border-top: 1px solid #4c4b48; + border-bottom: 1px solid #0f0e0a; + background: #3f3e3a url("data:image/png;base64,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") repeat; +} + +.main > .main-left > .nav > .slide > .slide-menu.active > li:nth-last-child(1) { + border-bottom: none; +} + +.main > .main-left > .nav > .slide > .slide-menu.active > li:hover { + background: rgba(117, 137, 12, 0.45) !important; + border-top: 1px solid rgba(129, 140, 72, 0.45); +} + +.main > .main-left > .nav > .slide > .slide-menu > li > a { + text-decoration: none; + white-space: nowrap; + font-size: 0.95rem; + line-height: 41px; +} + +.main > .main-left > .nav > .slide > .slide-menu > .active > a { + color: #fafafa; +} + +.main > .main-left > .nav > .slide > .slide-menu > li:hover { + background: #3f3e3a url("data:image/png;base64,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") repeat; +} + +.main > .main-left > .nav > .slide > .slide-menu > .active:hover { + cursor: hand; +} + +li { + list-style-type: none; +} + +#maincontent > .container { + margin: 0 2rem 1rem 2rem; +} + +h1 { + font-size: 2rem; + padding-bottom: 10px; + border-bottom: 1px solid #eee; +} + +h2 { + margin: 12px 0 0 0; + font-size: 1.6rem; + line-height: 2.5rem; + padding-bottom: 12px; +} + + +h2 > a, +h2 > a:hover { + color: #fafafa; +} + +h3 { + margin: 2rem 0 0 0; + font-size: 1.4rem; + padding-bottom: 10px; +} + +h4 { + +} + +fieldset { + margin-bottom: 1rem; + border: 1px solid rgba(37, 35, 35, 0.25); + font-weight: normal; + font-style: normal; + line-height: 1; + font-family: inherit; + min-width: inherit; + overflow-x: auto; + overflow-y: hidden; + border-radius: 0; + -webkit-overflow-scrolling: touch; + background: #2f2e2a; +} + +.cbi-map-descr + fieldset { + margin-top: 1rem; +} + +fieldset > legend { + display: none !important; +} + +fieldset > fieldset { + margin: 0; + padding: 0; + border: none; + box-shadow: none; +} + +fieldset > button { + margin: 8px; +} + +.panel-title { + width: 100%; + display: block; + line-height: 2.2rem; + font-size: 1.4rem; + padding-bottom: 0; + background: #2f2e2a; + padding-left: 8px; + border-bottom: 1px solid #1f1e1a; + border-top: 1px solid #1f1e1a; +} + +fieldset > .panel-title:nth-of-type(1) { + border-top: none; +} + +table { + border-spacing: 0; + border-collapse: collapse; + width: 100%; + border: none; + margin: 0 !important; +} + +table > tbody > tr > td, table > tbody > tr > th, table > tfoot > tr > td, table > tfoot > tr > th, table > thead > tr > td, table > thead > tr > th { + padding: .5rem; + white-space: nowrap; +} + +table > tbody > tr { + border-top: 1px solid #3f3e3a; + border-bottom: 1px solid #1f1e1a; +} + +/*table > tbody > tr:nth-of-type(2n),*/ +.cbi-rowstyle-1 { + background-color: #3f3e3a; + border-top: 1px solid #4f4e4a; + border-bottom: 1px solid #1f1e1a;; +} + +.cbi-rowstyle-2 { + background-color: #2f2e2a; + border-top: 1px solid #3f3e3a; + border-bottom: 1px solid #1f1e1a; +} + +table > tbody > tr:nth-last-child(1) { + border-bottom: none; +} + +.cbi-section-table-cell { + text-align: center; +} + +.cbi-section-table-row { + text-align: center; +} + +/* fix progress bar */ +#swaptotal > div, +#swapfree > div, +#memfree > div, +#membuff > div, +#conns > div, +#memtotal > div { + width: 100% !important; + height: 1.2rem !important; + background: #1f1e1a; !important; + border: 1px solid #000 !important; +} + +#swaptotal > div > div, +#swapfree > div > div, +#memfree > div > div, +#membuff > div > div, +#conns > div > div, +#memtotal > div > div { + height: 100% !important; + background-color: rgba(117, 137, 12, 0.75) !important; +} + +#swaptotal > div > div > div, +#swapfree > div > div > div, +#memfree > div > div > div, +#membuff > div > div > div, +#conns > div > div > div, +#memtotal > div > div > div { + color: #fafafa !important; + line-height: 17px; +} + +/* fix multiple table */ + +table table { + border: none; +} + +.cbi-value-field table { + border: none; +} + +td > table > tbody > tr > td { + border: none; +} + +.cbi-value-field > table > tbody > tr > td { + border: none; +} + +/* button style */ + +.cbi-button { + -webkit-appearance: none; + display: inline-block; + cursor: pointer; + -ms-touch-action: manipulation; + touch-action: manipulation; + text-align: center; + vertical-align: middle; + white-space: nowrap; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: auto !important; + color: #d8d3c5; + font-size: 0.8rem; + font-family: "DIN"; + font-weight: bold; + text-shadow: 0 1px 1px #000; + text-decoration: none; + border-width: 1px; + border-radius: 0; + border-style: solid; + border-color: #44433f; + border-bottom-color: #3a3935; + box-shadow: inset 0 0 24px 0 rgb(47, 46, 44), 0 0 3px 0 #000000; + background: -webkit-linear-gradient(top, rgb(73, 71, 68) 0%, rgb(47, 46, 44) 100%); + background: linear-gradient(to bottom, rgb(73, 71, 68) 0%, rgb(47, 46, 44) 100%); + padding: 5px; +} + +.cbi-button:hover, +.cbi-button:focus, +.cbi-button:active { + color: rgba(0, 0, 0, 0.87); + outline: 0; + text-decoration: none; + color: rgba(0, 0, 0, 0.87); +} + +.cbi-button:hover, +.cbi-button:focus { + box-shadow: 0 0px 2px rgba(0, 0, 0, 0.12), 0 2px 2px rgba(0, 0, 0, 0.2); +} + +.cbi-button:active { + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23); +} + +.cbi-button:disabled { + cursor: not-allowed; + pointer-events: none; + opacity: 0.60; + box-shadow: none; +} + +form.inline + form.inline, +.cbi-button + .cbi-button { + margin-left: 0.6rem; +} + +.cbi-button-reset, +.cbi-input-remove { + color: #2f2e2a !important; + text-transform: uppercase; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.62); + box-shadow: inset 0 0 14px 0 #db4213, 0 0 3px 0 #db6113 !important; + border: 1px solid rgba(255, 129, 92, 0.65) !important; + background: #dbb013 !important; + background: -moz-radial-gradient(center, ellipse cover, #dbb013 0%, #c69611 100%) !important; + background: -webkit-radial-gradient(center, ellipse cover, #dbb013 0%,#c69611 100%) !important; + background: radial-gradient(ellipse at center, #dbb013 0%,#c69611 100%) !important; +} + +.cbi-input-find, +.cbi-input-save, +.cbi-button-add, +.cbi-button-save, +.cbi-button-find, +.cbi-input-reload, +.cbi-button-reload { + color: #2f2e2a !important; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.62); + text-transform: uppercase; + box-shadow: inset 0 0 24px 0 #606f11, 0 0 3px 0 #9db134 !important; + border: 1px solid rgb(121, 144, 55) !important; + background: #7eaa0f !important; + background: -webkit-radial-gradient(center, ellipse cover, #7eaa0f 0%, #6d8006 100%) !important; + background: -webkit-radial-gradient(center ellipse, #7eaa0f 0%, #6d8006 100%) !important; + background: radial-gradient(ellipse at center, #7eaa0f 0%, #6d8006 100%) !important; +} + +.cbi-input-apply, +.cbi-button-apply, +.cbi-button-edit { + color: #2f2e2a !important; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.62); + text-transform: uppercase; + box-shadow: inset 0 0 24px 0 rgba(62, 62, 62, 0.77), 0 0 4px 0 rgba(236, 232, 219, 0.75); + border: 1px solid #969696 !important; + background: #dbb013 !important; + background: -webkit-radial-gradient(center, ellipse cover, rgb(224, 219, 207) 0%, rgba(224, 219, 204, 0.7) 100%) !important; + background: -webkit-radial-gradient(center ellipse, rgb(224, 219, 207) 0%, rgba(224, 219, 204, 0.7) 100%) !important; + background: radial-gradient(ellipse at center, rgb(224, 219, 207) 0%, rgba(224, 219, 204, 0.7) 100%) !important; +} + +.cbi-input-reset, +.cbi-section-remove > .cbi-button, +.cbi-button-remove { + color: #2f2e2a !important; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.62); + text-transform: uppercase; + box-shadow: inset 0 0 14px 0 #82290e, 0 0 3px 0 #db4213 !important; + border: 1px solid #b73813 !important; + background: #db4213 !important; + background: -moz-radial-gradient(center, ellipse cover, #db4213 0%, #c93612 100%) !important; + background: -webkit-radial-gradient(center, ellipse cover, #db4213 0%,#c93612 100%) !important; + background: radial-gradient(ellipse at center, #db4213 0%,#c93612 100%) !important; +} + +.a-to-btn { + text-decoration: none; +} + +/* table */ + +.tabs { + margin: 0 -2rem; + padding-left: 0.5rem; + background-color: #3f3e3a; + border-bottom: 1px solid #2f2e2a; +} + +.cbi-tabmenu > li, +.tabs > li { + display: inline-block; + height: 42px; + line-height: 42px; +} + +.cbi-tabmenu > li > a, +.tabs > li > a { + text-decoration: none; + color: #d8d3c5; + padding: 0.5rem 0.8rem; +} + +.tabs > li[class~="active"], +.tabs > li:hover { + cursor: pointer; + border-bottom: 0.2rem solid rgba(117, 137, 12, 0.75); + color: rgba(117, 137, 12, 0.9); +} + +.tabs > li[class~="active"] > a { + color: #fafafa; +} + +.tabs > li:hover { + border-bottom: 0.18751rem solid rgba(117, 137, 12, 0.5); +} + +.cbi-tabmenu > li:hover { + border-bottom: 2px solid #d8d3c5; +} + +.cbi-tabmenu > li[class~="cbi-tab"] { + border-bottom: 2px solid rgba(117, 137, 12, 0.85); +} + +.cbi-tabmenu > li[class~="cbi-tab"] > a { + color: #fafafa;; +} + +.cbi-tabmenu { + background: #171612 url("data:image/png;base64,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") repeat; +} + +.cbi-section-remove:nth-of-type(2n), +.cbi-section-node:nth-of-type(2n){ + background-color: #3f3e3a; +} + +.cbi-section-node-tabbed { + padding: 0; + margin-top: 0; + background: #3f3e3a; +} + +.cbi-tabcontainer > .cbi-value:nth-of-type(2n) { + background-color: #2f2e2a; +} + +.cbi-value-field, +.cbi-value-description { + display: table-cell; + line-height: 1.25; +} + +.cbi-value-helpicon > img { + display: none; +} + +.cbi-value-helpicon:before { + font-family: 'icomoon'; + color: rgba(117, 137, 12, 0.5); + content: "\f059"; +} + +.cbi-value-description { + color: #d8d3c5 + font-size: small; + padding: 0.5rem 0 0 0; +} + +.cbi-value-title { + word-wrap: break-word; + padding-top: 0.6rem; + width: 23rem; + float: left; + text-align: right; + padding-right: 2rem; + display: table-cell; +} + +.cbi-value { + padding: 0.3rem 1rem; + display: inline-block; + width: 100%; +} + +.cbi-section-table-descr > .cbi-section-table-cell, +.cbi-section-table-titles > .cbi-section-table-cell { + border: none; +} + +.cbi-rowstyle-2 .cbi-button-up, +.cbi-rowstyle-2 .cbi-button-down { + background-color: #FFF !important; +} + +.cbi-section-table .cbi-section-table-titles .cbi-section-table-cell { + width: auto !important; +} + +.cbi-section-table-titles { + background: #292823 url("data:image/png;base64,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") repeat; +} + +/* desc */ +.cbi-section-descr, +.cbi-map-descr { + padding: 0.5rem; + color: #1f1e1a; + font-size: small; + background: #6f6e6a; + text-shadow: none; +} + +/* luci */ + +.hidden { + display: none +} + +.left { + text-align: left !important; +} + +.right { + text-align: right !important; +} + +.inline { + display: inline; +} + +.cbi-page-actions { + padding: 10px; + border: 1px solid #3f3e3a; + text-align: right; + background: #2f2e2a url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTA5IiBoZWlnaHQ9IjUwOSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+IDxkZWZzPiA8cGF0dGVybiBpZD0ic3RyaXBlcyIgeD0iMCIgeT0iMCIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIiBwYXR0ZXJuVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBwYXR0ZXJuVHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4gPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwIiBoZWlnaHQ9IjIwIiByeD0iMCIgcnk9IjAiIHN0eWxlPSJzdHJva2U6IG5vbmU7IGZpbGw6ICMyZjJlMmE7Ii8+IDxyZWN0IHg9IjEwIiB5PSIwIiB3aWR0aD0iMTAiIGhlaWdodD0iMjAiIHJ4PSIwIiByeT0iMCIgc3R5bGU9InN0cm9rZTogbm9uZTsgZmlsbDogIzFmMWUxYTsiLz4gPHBhdGggZD0iTTIwLDAgTDIwLDIwIiBzdHlsZT0ic3Ryb2tlOiM1ZjVlNWE7IHN0cm9rZS13aWR0aDogMXB4OyBmaWxsOm5vbmU7IiAvPiA8cGF0aCBkPSJNMTAsMCBMMTAsMjAiIHN0eWxlPSJzdHJva2U6IzBmMGUwYTsgc3Ryb2tlLXdpZHRoOiAxcHg7IGZpbGw6bm9uZTsiIC8+IDwvcGF0dGVybj4gPC9kZWZzPiA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iNTA5IiBoZWlnaHQ9IjUwOSIgcng9IjAiIHJ5PSIwIiBzdHlsZT0ic3Ryb2tlOiBub25lOyBmaWxsOiB1cmwoI3N0cmlwZXMpOyBvcGFjaXR5OiAwLjYiLz4gPC9zdmc+") repeat; + box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.6); +} + +/* input */ +.cbi-value input[type="password"], +.cbi-value input[type="text"] { + min-width: 15rem; +} + +/* select */ +.cbi-value-field .cbi-input-select { + min-width: 15rem; +} + +.ifacebadge { + display: inline-flex; + padding: 0.5rem 1rem; + background: rgba(15, 14, 10, 0.8); + box-shadow: inset 0 0 42px 0 #000; +} + +td > .ifacebadge { + font-size: 0.9rem; +} + +.ifacebadge > img { + float: right; + margin: 0 0.3rem; +} + +/*textarea*/ + +.cbi-input-textarea { + width: 100%; + min-height: 14rem; + padding: 0.8rem; + font-size: 0.8rem; + margin-top: 10px; +} + +#syslog { + width: 100%; + min-height: 15rem; + padding: 1rem; + font-size: small; + color: #d8d3c5; + margin-bottom: 20px; + border-radius: 0; +} + +/* change */ + +#cbi-apply-uci-apply img { + display: none; +} + +#cbi-apply-uci-apply #cbi-apply-uci-apply-status { + display: block; + color: #d8d3c5; + padding: 10px; +} + +#cbi-apply-uci-apply #cbi-apply-uci-apply-status:before { + font-family: 'icomoon'; + content: "\e603"; + animation: anim-rotate 2s infinite linear; + margin-right: 1rem; + display: inline-block; + color: rgba(117, 137, 12, 1); +} + +.uci-change-list { + font-family: monospace; + padding: 10px; + color: #fafafa; + font-size: 14px; +} + +.uci-change-list ins, +.uci-change-legend-label ins { + text-decoration: none; + border: 1px solid rgba(117, 137, 12, 0.75); + background-color: #505a18; + display: block; + padding: 2px; +} + +.uci-change-list del, +.uci-change-legend-label del { + text-decoration: none; + border: 1px solid #c93612; + background-color: rgba(175, 46, 17, 0.53); + display: block; + font-style: normal; + padding: 2px; +} + +.uci-change-list var, +.uci-change-legend-label var { + text-decoration: none; + border: 1px solid #3f3e3a; + display: block; + font-style: normal; + padding: 2px; + box-shadow: inset 0 0 24px 0 #000; + background: #1f1e1a; +} + +.uci-change-list var ins, +.uci-change-list var del { + border: none; + white-space: pre; + font-style: normal; + padding: 0px; +} + +.uci-change-legend { + padding: 10px; +} + +.uci-change-legend-label { + width: 150px; + float: left; +} + +.uci-change-legend-label > ins, +.uci-change-legend-label > del, +.uci-change-legend-label > var { + float: left; + margin-right: 4px; + width: 10px; + height: 10px; + display: block; +} + +.uci-change-legend-label var ins, +.uci-change-legend-label var del { + line-height: 6px; + border: none; +} + +.uci-change-list var, +.uci-change-list del, +.uci-change-list ins { + padding: 0.5rem; +} + +/* other fix */ +#iwsvg, +#iwsvg2, +#bwsvg { + border: 1px solid #1f1e1a !important; + border-top: none !important; + box-shadow: inset 0 0 42px 0 #000; + background: #1f1e1a url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTA5IiBoZWlnaHQ9IjUwOSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+IDxkZWZzPiA8cGF0dGVybiBpZD0ic3RyaXBlcyIgeD0iMCIgeT0iMCIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIiBwYXR0ZXJuVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBwYXR0ZXJuVHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4gPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwIiBoZWlnaHQ9IjIwIiByeD0iMCIgcnk9IjAiIHN0eWxlPSJzdHJva2U6IG5vbmU7IGZpbGw6ICMyZjJlMmE7Ii8+IDxyZWN0IHg9IjEwIiB5PSIwIiB3aWR0aD0iMTAiIGhlaWdodD0iMjAiIHJ4PSIwIiByeT0iMCIgc3R5bGU9InN0cm9rZTogbm9uZTsgZmlsbDogIzFmMWUxYTsiLz4gPHBhdGggZD0iTTIwLDAgTDIwLDIwIiBzdHlsZT0ic3Ryb2tlOiM1ZjVlNWE7IHN0cm9rZS13aWR0aDogMXB4OyBmaWxsOm5vbmU7IiAvPiA8cGF0aCBkPSJNMTAsMCBMMTAsMjAiIHN0eWxlPSJzdHJva2U6IzBmMGUwYTsgc3Ryb2tlLXdpZHRoOiAxcHg7IGZpbGw6bm9uZTsiIC8+IDwvcGF0dGVybj4gPC9kZWZzPiA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iNTA5IiBoZWlnaHQ9IjUwOSIgcng9IjAiIHJ5PSIwIiBzdHlsZT0ic3Ryb2tlOiBub25lOyBmaWxsOiB1cmwoI3N0cmlwZXMpOyBvcGFjaXR5OiAwLjYiLz4gPC9zdmc+") repeat !important; +} + +svg { + background: rgba(15, 14, 10, 0.8) !important; +} + +.ifacebox { + border: 1px solid #0f0e0a; + margin: 5px; +} + +.ifacebox-head { + line-height: 20px; + color: #1f1e1a; + text-shadow: none; +} + +.ifacebox-body { + box-shadow: inset 0 0 15px 0 #000; + background: #1f1e1a; + min-height: 40px; + line-height: 18px; + padding-top: 12px; + padding-bottom: 8px; +} + +.cbi-image-button { + margin-left: 0.5rem; +} + +.zonebadge { + padding: 0.2rem 0.5rem; + display: inline-block; + cursor: pointer; +} + +.zonebadge > .ifacebadge { + padding: 0.2rem 1rem; + margin: 0.3rem; +} + +.zonebadge > input[type="text"] { + padding: 0.16rem 1rem; + min-width: 10rem; + margin-top: 0.3rem; +} + +.cbi-value-field .cbi-input-checkbox, +.cbi-value-field .cbi-input-radio { + margin-top: 0.5rem; + height: 1rem; +} + +.cbi-value-field > input + .cbi-value-description { + padding: 0; +} + +.cbi-value-field > ul > li { + display: flex; +} + +.cbi-value-field > ul > li > label { + margin-top: 0.5rem; +} + +.cbi-value-field > ul > li .ifacebadge { + margin-left: 0.4rem; + margin-top: -0.5rem; +} + +.cbi-section-table-row > .cbi-value-field .cbi-input-select { + min-width: 7rem; +} + +.cbi-section-create > .cbi-button-add { + margin: 0.5rem; +} + +.cbi-section-remove { + padding: 0.5rem; +} + +div.cbi-value var, td.cbi-value-field var { + font-style: italic; + color: #0069D6; +} + +small { + font-size: 90%; + white-space: normal; + line-height: 1.42857143; +} + +.cbi-button-up, +.cbi-button-down { + display: inline-block; + min-width: 0; + padding: 0.2rem 0.3rem; + font-size: 1.2rem; +} + +.cbi-optionals { + padding: 1rem 1rem 0 1rem; + border-top: 1px solid #CCC; +} + +#diag-rc-output > pre { + display: block; + padding: 8.5px; + margin: 0 0 18px; + line-height: 1.5rem; + white-space: pre-wrap; + word-wrap: break-word; + font-size: 1.4rem; +} + +input[name="ping"], +input[name="traceroute"], +input[name="nslookup"] { + width: 80%; +} + +header > .container > .pull-right > * { + cursor: pointer; +} + +#xhr_poll_status > .label.success { + color: #fafafa !important; + text-shadow: 0 1px 1px rgba(65, 65, 65, 0.4) !important; + box-shadow: inset 0 0 24px 0 #606f11, 0 0 3px 0 #9db134 !important; + border: 1px solid rgb(121, 144, 55) !important; + background: #7eaa0f !important; + background: -webkit-radial-gradient(center, ellipse cover, #7eaa0f 0%, #6d8006 100%) !important; + background: -webkit-radial-gradient(center ellipse, #7eaa0f 0%, #6d8006 100%) !important; + background: radial-gradient(ellipse at center, #7eaa0f 0%, #6d8006 100%) !important; + margin-right: 4px; + padding: 5px; +} + +.label { + color: #d8d3c5 !important; + font-size: 0.8rem; + text-shadow: 0 1px 1px #000; + text-decoration: none; + border-width: 1px; + border-radius: 0; + border-style: solid; + border-color: #44433f; + border-bottom-color: #3a3935; + box-shadow: inset 0 0 24px 0 rgb(47, 46, 44), 0 0 3px 0 #000000; + background: -webkit-linear-gradient(top, rgb(73, 71, 68) 0%, rgb(47, 46, 44) 100%); + background: linear-gradient(to bottom, rgb(73, 71, 68) 0%, rgb(47, 46, 44) 100%); + padding: 5px; +} + +#modemenu > li .active > .label { + box-shadow: inset 0 0 24px 0 rgba(62, 62, 62, 0.77), 0 0 4px 0 rgba(236, 232, 219, 0.75); + border: 1px solid #969696 !important; + background: #dbb013 !important; + background: -webkit-radial-gradient(center, ellipse cover, rgb(224, 219, 207) 0%, rgba(224, 219, 204, 0.7) 100%) !important; + background: -webkit-radial-gradient(center ellipse, rgb(224, 219, 207) 0%, rgba(224, 219, 204, 0.7) 100%) !important; + background: radial-gradient(ellipse at center, rgb(224, 219, 207) 0%, rgba(224, 219, 204, 0.7) 100%) !important; +} + +#modemenu > li .active > .label > a { + color: #d8d3c5 !important; + text-shadow: 0 1px 1px rgba(10, 9, 4, 0.4) !important; +} + +.notice { + background-color: #5BC0DE; +} + +.showSide { + display: none; +} + +.darkMask { + width: 100%; + height: 100%; + position: fixed; + background-color: rgba(0, 0, 0, 0.56); + content: ""; + z-index: 99; + display: none; +} + +/* fix Main Login*/ +.node-main-login > .main > .main-left { + display: none; +} + +.node-main-login > .main > .main-right { + width: 100%; +} + +.node-main-login > .main fieldset { + padding: 0.5rem; + margin-bottom: 1rem; + display: inline; + background: none; + border: none; + box-shadow: none; + overflow: hidden; + width: 100%; +} + +.node-main-login > .main .cbi-value-title { + width: 7rem; +} + +.node-main-login > .main #maincontent { + + text-align: center; +} + +.node-main-login > .main .container { + display: inline-block; + padding: 2rem 4rem; + margin-top: 3rem !important; + background-color: #1f1e1a; + text-align: left; + border: 1px solid #3f3e3a; + box-shadow: 0 0 42px 0px rgba(0, 0, 0, 0.66); + } +} + +.node-main-login > .main form > div:nth-last-child(1) { + float: right; +} + +.node-main-login > .main .cbi-value { + display: block; +} + +.node-main-login > .main .cbi-value > * { + display: inline-block !important; +} + +.node-main-login > .main .cbi-input-user, +.node-main-login > .main .cbi-input-password { + min-width: 15rem; +} + +.node-main-login footer { + bottom: 0; + position: absolute; + width: 100%; +} + + +.node-main-login table > tbody > tr { + background-color: #2f2e2a; + border-top: 1px solid #3f3e3a; + border-bottom: 1px solid #1f1e1a;; +} + +.node-main-login table > tbody > tr:nth-of-type(2n) { + background-color: #3f3e3a; + border-top: 1px solid #4f4e4a; + border-bottom: 1px solid #1f1e1a;; +} + +.node-main-login > .main fieldset:nth-child(4) td:nth-child(2) { + white-space: normal; +} + +.node-main-login fieldset:nth-child(3n) table table { + box-shadow: inset 0 0 15px 0 #000; + background: #1f1e1a; + border: 1px solid #000; +} + +.node-main-login fieldset:nth-child(3n) table table tr{ + background: transparent; + border: none; +} + +.node-main-login fieldset:nth-child(3n) table table tr td { + padding: 7px !important; +} + +/* fix status overview */ + +.node-status-overview table > tbody > tr { + background-color: #2f2e2a; + border-top: 1px solid #3f3e3a; + border-bottom: 1px solid #1f1e1a;; +} + +.node-status-overview table > tbody > tr:nth-of-type(2n) { + background-color: #3f3e3a; + border-top: 1px solid #4f4e4a; + border-bottom: 1px solid #1f1e1a;; +} + +.node-status-overview table > tbody > tr:last-child { + border: none; +} + +.node-status-overview > .main fieldset:nth-child(4) td:nth-child(2) { + white-space: normal; +} + +.node-status-overview fieldset:nth-child(3n) table table { + box-shadow: inset 0 0 15px 0 #000; + background: #1f1e1a; + border: 1px solid #000; +} + +.node-status-overview fieldset:nth-child(3n) table table tr{ + background: transparent; + border: none; +} + +.node-status-overview fieldset:nth-child(3n) table table tr td { + padding: 7px !important; +} + +/* fix status processes */ + +.node-status-processes > .main table tr td:nth-child(3) { + white-space: normal; +} + +.node-status-iptables > .main div > .cbi-map > form { + margin: 2rem 2rem 0 0; +} + +/* fix system reboot */ + +.node-system-reboot > .main > .main-right p, +.node-system-reboot > .main > .main-right h3 { + padding-left: 2rem; + margin-bottom: 8px; +} + +.node-system-reboot > .main > .main-right p.warning { + padding: 2rem; +} + +.node-system-reboot > .main > .main-right p:nth-last-child(1){ + background: #1f1e1a url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTA5IiBoZWlnaHQ9IjUwOSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+IDxkZWZzPiA8cGF0dGVybiBpZD0ic3RyaXBlcyIgeD0iMCIgeT0iMCIgd2lkdGg9IjIwIiBoZWlnaHQ9IjIwIiBwYXR0ZXJuVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBwYXR0ZXJuVHJhbnNmb3JtPSJyb3RhdGUoNDUpIj4gPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwIiBoZWlnaHQ9IjIwIiByeD0iMCIgcnk9IjAiIHN0eWxlPSJzdHJva2U6IG5vbmU7IGZpbGw6ICMyZjJlMmE7Ii8+IDxyZWN0IHg9IjEwIiB5PSIwIiB3aWR0aD0iMTAiIGhlaWdodD0iMjAiIHJ4PSIwIiByeT0iMCIgc3R5bGU9InN0cm9rZTogbm9uZTsgZmlsbDogIzFmMWUxYTsiLz4gPHBhdGggZD0iTTIwLDAgTDIwLDIwIiBzdHlsZT0ic3Ryb2tlOiM1ZjVlNWE7IHN0cm9rZS13aWR0aDogMXB4OyBmaWxsOm5vbmU7IiAvPiA8cGF0aCBkPSJNMTAsMCBMMTAsMjAiIHN0eWxlPSJzdHJva2U6IzBmMGUwYTsgc3Ryb2tlLXdpZHRoOiAxcHg7IGZpbGw6bm9uZTsiIC8+IDwvcGF0dGVybj4gPC9kZWZzPiA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iNTA5IiBoZWlnaHQ9IjUwOSIgcng9IjAiIHJ5PSIwIiBzdHlsZT0ic3Ryb2tlOiBub25lOyBmaWxsOiB1cmwoI3N0cmlwZXMpOyBvcGFjaXR5OiAwLjYiLz4gPC9zdmc+") repeat; + padding: 2rem; +} + +/* fix Services Network Shares*/ +.node-services-samba > .main .cbi-tabcontainer:nth-child(3) .cbi-value-title { + margin-bottom: 1rem; + width: auto; +} + +.node-services-samba > .main .cbi-tabcontainer:nth-child(3) .cbi-value-field { + display: list-item; +} + +.node-services-samba > .main .cbi-tabcontainer:nth-child(3) .cbi-value-description { + padding-top: 1rem; +} + + +/* fix System Software*/ +.node-system-packages > .main table tr td:nth-child(1) { + width: auto !important; +} + +.node-system-packages > .main table tr td:nth-last-child(1) { + white-space: normal; + font-size: small; +} + +.node-system-packages > .main .cbi-tabmenu > li > a, .tabs > li > a { + padding: 0.5rem 0.8rem; +} + +.node-system-packages > .main .cbi-value > pre { + overflow: auto; +} + +.cbi-tabmenu + .cbi-section { + margin-top: 0; +} + +/* fix network firewall*/ +.node-network-firewall > .main .cbi-section-table-row > .cbi-value-field .cbi-input-select { + min-width: 4rem; +} + +.node-status-iptables fieldset, +.node-system-packages fieldset, +.node-system-flashops fieldset { + margin-top: 0; +} + +.node-status-iptables .cbi-tabmenu, +.node-system-packages .cbi-tabmenu, +.node-system-flashops .cbi-tabmenu { + border: none; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .16), 0 0 2px 0 rgba(0, 0, 0, .12); +} + +.node-system-flashops form.inline + form.inline { + margin-left: 0; +} + +.node-system-flashops br { + display: none; +} + +.node-system-flashops .cbi-section-node { + padding-top: 10px; + padding-bottom: 10px; +} + +.node-system-flashops .cbi-input-apply { + height: 25px; + margin-top: -5px; + font-size: 0.8rem; +} + + +#cbi-firewall-redirect table *, +#cbi-network-switch_vlan table *, +#cbi-firewall-zone table * { + font-size: small; +} + +#cbi-firewall-redirect table input[type="text"], +#cbi-network-switch_vlan table input[type="text"], +#cbi-firewall-zone table input[type="text"] { + width: 5rem; +} + +#cbi-firewall-redirect table select, +#cbi-network-switch_vlan table select, +#cbi-firewall-zone table select { + min-width: 3.5rem; +} + +#cbi-system-led .cbi-section-descr, +#cbi-system-led br { + display: none; +} + +#cbi-system-led .cbi-section-remove, +#cbi-system-led .cbi-section-create { + background: #1f1e1a; + border-bottom: 1px solid #0f0e0a; + border-top: 1px solid #0f0e0a; +} + +/* processes fix */ + +#cbi-processes .cbi-section-descr, +#cbi-processes .cbi-section-table-descr, +.node-status-processes .cbi-page-actions { + display: none; +} + +#cbi-processes .cbi-section-table-titles { + background-color: #1f1e1a; + border-top: none; +} + +/* realtime graphs */ + +.node-status-realtime table { + border: 1px solid #1f1e1a; +} + +.node-status-realtime table > tbody > tr { + background-color: #2f2e2a; + border-top: 1px solid #3f3e3a; + border-bottom: 1px solid #1f1e1a;; +} + +.node-status-realtime table > tbody > tr:nth-of-type(2n) { + background-color: #3f3e3a; + border-top: 1px solid #4f4e4a; + border-bottom: 1px solid #1f1e1a;; +} + +.node-status-realtime fieldset table { + border: none; +} + +.node-status-realtime small { + display: inline-block; + width: 100%; + line-height: 25px; + background: #1f1e1a; + border: 1px solid #1f1e1a; + padding-right: 5px; +} + +.node-status-realtime #cbi-table-table small { + border: none; +} + +.node-status-realtime br { + display: none; +} + +/* system -> system */ + +.node-system-system fieldset br, +.node-system-system .cbi-section-descr { + display: none; +} + +/* system -> admin */ + +.node-system-admin br, +.node-system-admin .cbi-section-table-descr, +.node-system-admin .cbi-section-table-titles, +.node-system-admin fieldset:nth-of-type(1) .cbi-section-descr { + display: none; +} + +/* system -> startup */ + +.node-system-startup .cbi-map br, +.node-system-startup .cbi-section-descr, +.node-system-startup .cbi-section-table-descr, +.node-system-startup form:nth-of-type(1) .cbi-page-actions { + display: none; +} + +.node-system-startup .cbi-map-descr br { + display: block; +} + +/* system -> mounts */ + +.node-system-fstab .cbi-section-table-descr, +.node-system-fstab .cbi-section-descr:nth-of-type(1) { + display: none; +} + +/* system -> flashops */ + +.node-system-flashops fieldset { + margin-top: 10px; + padding: 10px; +} + +/* dnsmasq */ + +.node-network-dhcp .cbi-section-descr, +.node-network-dhcp .cbi-section-table-descr, +.node-network-dhcp #cbi-dhcp-dnsmasq br { + display: none; +} + +/* network -> bmx7 */ + +.node-network-bmx7 .cbi-map-descr, +.node-network-bmx7 center img { + display: none; +} + +.node-network-bmx7 div#extra-info { + margin-bottom: 15px; +} + +/* network -> interfaces */ + +.node-network-network .cbi-section-descr, +.node-network-network fieldset br { + display: none; +} + +.node-network-network fieldset .cbi-section-node table { + box-shadow: inset 0 0 15px 0 #000; + background: #1f1e1a; + border: 1px solid #000; +} + +.node-network-network fieldset .cbi-section-node table tr { + background: transparent; +} + +.node-network-network fieldset .cbi-section-node table td:nth-of-type(1) { + padding: 5px !important; +} + +.node-network-network fieldset .cbi-section-node table td { + padding: 10px !important; +} + +.node-network-network fieldset .cbi-section-table br { + display: initial; +} + +/* network -> wifi */ + +.node-network-wireless fieldset br { + display: none; +} + +.node-network-wireless fieldset .cbi-section-node table br { + display: initial; +} + +.node-network-wireless fieldset .cbi-section-node table { + box-shadow: inset 0 0 15px 0 #000; + background: #1f1e1a; + border: 1px solid #000; +} + +.node-network-wireless fieldset .cbi-section-node table td { + padding: 10px !important; +} + +.node-network-wireless fieldset .cbi-section-table tr:nth-of-type(1) { + background: #292823 url("data:image/png;base64,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") repeat; +} + +.node-network-wireless fieldset .cbi-section-table:nth-of-type(1) br { + display: initial; +} + +/* network -> hosts */ + +.node-network-hosts .cbi-section-descr, +.node-network-hosts .cbi-section-table-descr { + display: none; +} + +/* network -> routes */ + +.node-network-routes .cbi-section-descr { + display: none; +} + +.node-network-routes .cbi-input-select { + width: 100px; + min-width: 100px; +} + +.node-network-routes input { + width: 130px; + max-width: 150px; +} + +.node-network-routes td:nth-of-type(5) input, +.node-network-routes td:nth-of-type(6) input { + width: 50px !important; + max-width: 50px !important; +} + +/* language fix */ +body.lang_pl.node-main-login .cbi-value-title { + width: 12rem; +} + +/* Mode Switch */ + +.breadcrumb li { + display: inline-block; +} + +.info { + background: transparent !important; +} + +pre, textarea { + border: 1px solid #0f0e0a; + padding: 15px; + color: #d8d3c5; + background: rgba(15, 14, 10, 1); + box-shadow: inset 0 0 36px 0 rgba(0, 0, 0, 0.8); + margin-top: 10px; +} + +#content_syslog textarea { + margin-top: 0; +} + +@media screen and (max-width: 1600px) { + .main-left { + width: calc(0% + 13rem); + } + + .main-right { + width: calc(100% - 13rem); + } + + .cbi-button { + padding: 0.3rem 0.6rem; + } + + .label { + + } + + .cbi-value-title { + width: 15rem; + padding-right: 0.6rem; + } + + .cbi-input-textarea { + font-size: small; + } + + .node-status-iptables > .main fieldset li > a { + padding: 0.3rem 0.6rem; + } +} + +@media screen and (max-width: 1280px) { + + .main-left { + width: calc(0% + 13rem); + top: 3.5rem; + height: calc(100% - 3.5rem); + } + + .main-right { + width: calc(100% - 13rem); + } + + .cbi-tabmenu > li > a, .tabs > li > a { + padding: 0.2rem 0.5rem; + } + + .panel-title { + font-size: 1.1rem; + } + + table { + font-size: 0.7rem !important; + width: 100% !important; + } + + +} + +@media screen and (max-width: 992px) { + .main-left { + width: 0; + position: fixed; + z-index: 100; + } + + .main-right { + width: 100%; + } + + .showSide { + padding: 0.1rem; + margin-right: 0.5rem; + display: inline-block; + line-height: 51px; + vertical-align: top; + } + + .showSide:before { + font-family: 'icomoon'; + content: "\e20e"; + font-size: 1.7rem; + } + + .node-main-login .showSide { + display: none !important; + } + + .cbi-value-title { + width: 9rem; + padding-right: 1rem; + } + + .node-network-diagnostics > .main .cbi-map fieldset > div * { + width: 100% !important; + } + + .node-network-diagnostics > .main .cbi-map fieldset > div input[type="text"] { + margin: 3rem 0 0 0 !important; + } + + .node-network-diagnostics > .main .cbi-map fieldset > div:nth-child(4) input[type="text"] { + margin: 0 !important; + } + + .node-network-diagnostics > .main .cbi-map fieldset > div select, + .node-network-diagnostics > .main .cbi-map fieldset > div input[type="button"] { + margin: 1rem 0 0 0; + } + + .node-network-diagnostics > .main .cbi-map fieldset > div { + width: 100% !important; + } + + #diag-rc-output > pre { + font-size: 1rem; + } + + .node-main-login > .main .cbi-value-title { + text-align: left; + } +} + +@media screen and (max-width: 480px) { + body { + font-size: 1rem; + } + + .tabs { + margin: 0 -1rem; + } + + #maincontent > .container { + margin: 0 1rem 1.5rem 1rem; + } + + .cbi-value-title { + width: 100%; + min-width: 0rem !important; + display: block; + margin-top: 1rem; + margin-bottom: 0.5rem; + text-align: left; + } + + .cbi-value-field, .cbi-value-description { + width: 100%; + } + + .cbi-value > .cbi-value-field { + display: inline-block; + } + + .cbi-tabmenu > li, .tabs > li { + padding: 0.6rem 0rem; + } + + .cbi-tabmenu > li > a, .tabs > li > a { + padding: 0.2rem 0.3rem; + font-size: 0.9rem; + } + + .cbi-page-actions > div > input { + display: none; + } + + .node-main-login > .main .container { + padding: 0.5rem 1rem 2rem 1rem; + } + + .node-main-login > .main .cbi-value { + padding: 0; + } + + .node-main-login > .main form > div:nth-last-child(1) { + margin-top: 2rem; + } + + .node-main-login > .main .cbi-value-title { + width: 100% !important; + font-size: 1.2rem; + } + + .node-main-login > .main fieldset { + margin: 0; + padding: 0.5rem; + } + + h2 { + font-size: 2rem; + } + + .tabs > li > a { + font-size: 0.9rem; + } + + select, + input { + font-size: 0.9rem; + } + + .mobile-hide { + display: none; + } + + .panel-title { + font-size: 1.4rem; + } + + .node-system-packages > .main .cbi-value.cbi-value-last > div { + width: 100% !important; + } + + .node-system-packages > .main .cbi-value .cbi-value-field input { + width: 100%; + } + + .node-status-iptables > .main div > .cbi-map > form { + position: static !important; + margin: 0 0 2rem 0; + padding: 2rem; + border: 0; + font-weight: normal; + font-style: normal; + line-height: 1; + font-family: inherit; + min-width: inherit; + overflow-x: auto; + overflow-y: hidden; + border-radius: 0; + background-color: #FFF; + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .16), 0 0 2px 0 rgba(0, 0, 0, .12); + -webkit-overflow-scrolling: touch; + } + + .node-status-iptables > .main div > .cbi-map > form input[type="submit"] { + width: 100% !important; + margin: 0; + } + + .node-status-iptables > .main div > .cbi-map > form input[type="submit"] + input[type="submit"] { + margin-top: 1rem; + } +} + +@media screen and (min-width: 992px) { + .cbi-value input[type="password"], + .cbi-value input[type="text"] { + min-width: 20rem; + } + + .cbi-value-field .cbi-input-select { + min-width: 20rem; + } +} + +@media screen and (min-width: 1280px) { + .cbi-value input[type="password"], + .cbi-value input[type="text"] { + min-width: 22rem; + } + + .cbi-value-field .cbi-input-select { + min-width: 22rem; + } +} + +@media screen and (min-width: 1600px) { + .cbi-value input[type="password"], + .cbi-value input[type="text"] { + min-width: 25rem; + } + + .cbi-value-field .cbi-input-select { + min-width: 25rem; + } +} diff --git a/luci-theme-darkmatter/files/htdocs/favicon.ico b/luci-theme-darkmatter/files/htdocs/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b407d18455773a185059628e632e60c06d6eb858 GIT binary patch literal 2462 zcmcJR&yEaH6o*e6F|iOUG9fZOLP&@Rv5;U2&*0w!;0>@EGb?OJyuet^Lm<@5WC)%> zkg%GqrA0^izRtZ-om6$ws{JP4+;h*l=U2CGb=OQpkDs}@;CDYB?~d3X5yyd4oCfnl zQEO2_{uO*pd}Hi_UInNEw%|dE&oM3}4l_=IeJ4P#cTj-Wx)0ajZQ?p(S9uM+zd!+R z;6|ck+y`BBl4j4l?(e>39j4{~8n1hh&!;5zKDx>?*ZVTOhx+rKXD`V97nvAJBYkDKK6(zC8zOb&vd$Aubw;!E%;jrG`CeXYHE z=rxJ-*noU4#^$Cay}a-2Z#-@P|G9s^3nko27HrO^a0Qz7YtEW$Tsx_D(r^bpz%#fCM-#^8thvUu|04Hz3ZB4A*hmvOF=_Uh3l-%EGlBRqt87^TcB!2LW2|K51tM-tY}S#!-Qm>ahj$|IU(b`hS!7WmH3|G(t-zctsm zwo%LJk~7t>=v9Lu#>!e5Gf}NnqiiyV!O~(3E&xaWo8#PG*^VF8qxilw8$V<3F#iYk Cvy_7X literal 0 HcmV?d00001 diff --git a/luci-theme-darkmatter/files/htdocs/fonts/DIN.eot b/luci-theme-darkmatter/files/htdocs/fonts/DIN.eot new file mode 100644 index 0000000000000000000000000000000000000000..29dd9ce5d9ec9698edf072e285600bf6a5cf3c3d GIT binary patch literal 10365 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 C@vm(F literal 0 HcmV?d00001 diff --git a/luci-theme-darkmatter/files/htdocs/fonts/DIN.otf b/luci-theme-darkmatter/files/htdocs/fonts/DIN.otf new file mode 100644 index 0000000000000000000000000000000000000000..879ef3d39b79a8e58b5e3262711e8015af089b4c GIT binary patch literal 39656 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 Ax&QzG literal 0 HcmV?d00001 diff --git a/luci-theme-darkmatter/files/htdocs/fonts/DIN.svg b/luci-theme-darkmatter/files/htdocs/fonts/DIN.svg new file mode 100644 index 0000000..5967eb3 --- /dev/null +++ b/luci-theme-darkmatter/files/htdocs/fonts/DIN.svg @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/luci-theme-darkmatter/files/htdocs/fonts/DIN.ttf b/luci-theme-darkmatter/files/htdocs/fonts/DIN.ttf new file mode 100644 index 0000000000000000000000000000000000000000..7c91f5d8bd7bd3cfbf3a6e57057c7964f88ea816 GIT binary patch literal 19116 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 A4*&oF literal 0 HcmV?d00001 diff --git a/luci-theme-darkmatter/files/htdocs/fonts/DIN.woff b/luci-theme-darkmatter/files/htdocs/fonts/DIN.woff new file mode 100644 index 0000000000000000000000000000000000000000..ca56ab669b512c5ccbcbe516415e5fe96aaf638a GIT binary patch literal 12656 zcmZvDV{m3o7wr=#Pi)(^CZ5=~ZD(RkY}>Y-iH(WPiESGq z_|faQ$%}~rKmgy3*#Us`UzDo)umArwF)g?CL@U0Dy^nYe4@88UVn| z!QA$nllazrb0E%;8d)6XhR)x);4PEb1pqU*@i6=5Q~&@9UI2i{W)u2w&BD~s z1OT9k`_?dig9GZEgx%ts{N{SU$3)*CLxh7Qv#@n>|K_&8%OZc*;w3y0RIsr({?^gm zf7i_W59kpT{kDeg-*p+1zVnFw1Ly&u#m>;y^qXt?*2sV7vQoLm@H^N$y8r-8;NP72 zH_lQsQ!E^uOuuuP|68kX9su430MLF%kZ%Ac_zI@~i{9jQzyFIf`Dab;S;ht?1_s+; zMlg^_21W+p{mz0Dpoh!=06ZDUch-ONX1=3b{{G1zwA%)zyJp5_pdc7TsmNgv22Mc1 z-fck9Qr+^`yrF?Xt$8P?>OgP*csoc@kUug2R~t(IzkCA&v)=~TkYbQXX`uVe0WWlL z=z>}iLIA4%A%&5;=p6xLrg`QP9v}%&3Frir0;&P6Kfj+>jIgf=_sp+@`=C_ z-@cx|`o5q(k3K%`o?Zf@5tsNh-V7E4#}Je9Tz3FbHgFCQQ8I!7FuEW!V{}zE=XhB< z8@!#~@1Oxc`$-Cnc2N?P=2#k>Zh;X}e`zYLPH{4{)_$~mzCwnG4O5hu9b%-YEpfEC zJ|e`(O)=EiT@vKzZSr^feuDT5^%3VA?jXl2&NA0K-oS*z(N|w&qn1u;wi(PO3`wWf z>eO33tg)Xh*>7}Oj<-~IxL$4!D5Pa2IF@+(`eF!MQV6fZg^)moG_lm3HB@4yT$IWG zg{XI~z{bMKC?()>-#;Ex9*MCxUzzT8h}of&I32VMDXAI0jM?n|%o$V0qL|F!OWc{+ z0nLM>S^){3vRC_W1HbQE_blBwKDj|Aq~H-Qx*bRfj0AdGG}_wZawV>Kg$zL|UU-4I z8j(i?2}=tdhm?F)U4{^Ygy@!)Vb-h3Ft{zzon zO~1Va7M+s4?FEX2%zF2~g#dtjKbxTLS=rh!g398rep{WXBH-}=Vj^>BvQc>AAZdk8 zBqWF{SP(@JMKn>N&g%k;YDyzM~ly9B!LeF8HoFuNqxoXF-X%0pos;SLw-SMzmJxgqJ!i2@Ve(rXrBeFO z>L>L#>$t5p5T6=~Xqi4gP5ef#ul`HUC$taSqw{z2)L1`QwzS`A2XbnlaupY{cP$+zWw@fS)^#L z#7@UkCt)T+P@Vm%nE$|x7Cu>iCNZo{;_4I!A-ErA_AOPSbm>|DRWda*F;SbC!eTO? z`W?0EF!EX+&=R<>F;28o+0ftL&_As%H#VNx(BSg^+|o)<=n?a*HnUh+$4%N&j?tL&vg zfA{ph_^PY*e!!`dq=x2~;A4Ky^Hu+b;!B2O$a5B60l!^y9Hhs@Fc)T*Q1=>D5jO5M zMGSwYJ%aeuz9uG#L37B=nazx3eE%Xg_YvF0#%dC4P1*4h443m8Mi$=<2Z7%g$W-Cp z&xsueA-tmkXJ@rGlJp--nuLKrom@HCFD9^+b?CS~L7%#Q;Xd))aIo>WA@ZEG!SO>a z#ydLQiZagQG(n!?K^DtU&(jduxZU;zj&)SJ&FXhNXSq2$M@Be1Z+TF1wzg4mKIz8H z>KJgq&VzeRZSFDylXd9TPvX5uvj$J1x$xnGmEC(0vJfcU6h0?K<3|4Fz0)527_!=< zNb^o?k`H{E)os#Eij-o8M;~|-D{LFK+U;3l_WTjTMQ`iIAt0ok2G6qY91u`24+ zOr8=Wyg_7CNIT|im7f)ErSvuC#SPn0n&g{Bzf3i0`^*h>P zELmQl%P7O|AW{A@k9w%l95eG&nucbyoiTk~fhj&S=~$9s9v507s?*~xg=piSOHV#| z;_ODI!Jtt?OTrwS+FL?-VxN%@ji`N6o)bMjXO)y$5aDB%Hw$QJo39FtxbG-Knaw~&f@(07j! zBBXM(Ake#&1}RRXLA0_nli@Xyx5iv*99HAn^T>oHL^S3XRNF)?;dtMzWaMO3?VFO_ z*mso!y+ZCiA8OCkDTgpST|c&~$5f+LMMh93x9)x=aB}f51kp(o#S!I;(Hs-J!sG2?A^;6=cwKPb-$<^lMJNdPg3qd-bASRme`beO~KGUH{ zSO2{&wUS8eawLaVUN)0iKglTO0-fmEB zhEr2S4zjdd;3+P8??UDLXa07xUZ<+)h3y4Rla2hD%3|FSN1w9B_uvU&w$d9CCOwB= zwhmM|HK;}yAz4lweaJj1>1d1-zNlLm!2QbzG{tXOp=2d-`519$nLLQ&p*D%nX967NBFJ>vXj=p4(6p7^~MGX7_nG%bAd*#g=#ZL}TgU z`>&hV^ZYNMd(T;^3Gj_I(CPnS`^%`oA+w#w8wn! ztd|3Pg@oZ;ITj`9!vJ*n|TSh???u#pQ+qk7S@G#i(DO0&N z*n_n!4`N4aW-ZfSe^kGIu;@@?LKQyr`xIKz;EUhH2knDXik`Qes`fn^lyn?xMCyis z%~Lf{rSm(ZkOJDFo=SlmR*PhBGZRhT$N72}2gOSQydvzemn`fhLBkAR?~MnJeSGCs z^`T{_AA4SQEFVqf=9>fszL~LArZ;s7n#JVTf>y~;Mqz@TrmzW!>l}2y&IhKi6(uTb zaJEQe9Wdf5ynG(j-kVQ!cUbW{4F~7;FQa`@_iNbS@ zd9H1mjSO#2^g^Z9>CD>>K}9^aOR zJ0Xxo^tOmQUC9@i_2`iRH0~kmL5<2@aMl_??!k7uy;>sc&CdIMn4kk*ALG6XFT)ZV zw#g>BXM`th=9&qlVND}BL%>c4PqU-6ie%cnRw7k#6nE8VsS=#vN9WRkw~AuNi}(HY zv-1`2N5P|`;luX4N21;s?cLO@eoq)7Uw`S;r7S+L&x9djxp1jn?iJVF!1Y4n?3k$N zSg})h*TUZNEA10KzuBQBKZEgSMd>K$*$rGb`t)~E=~OLUk<4mhmpP63@L}CS*nf1J z64$|%mi!B@W!PJ<&jj7@U%QOW@YXB!h5iByDffu)^i)-6NO+jb#OsJ3{i!lP(J zcf^my6n+v|!%ThuY*K2>Z%cf1HSBNYbm3BV9%8#udYz%%f%<&?XYzZYpuQ%KSdsHC z)*l%)mcBAe6O9J(_+{aR4PWnM?-zl~>QqYxHThcwN<{*+c^EmG)BM-nA?>-LW_reDmUM{B9`R^0-+j?^Vi<%C z(A>Y|SFOh@cT5S1m#tIz6c)8}eqS6ad#2b4n$7Z<3v>K@it>H>6NNg*E%s1+JI5%` z7NHefJB=5)nnt5i?K5tjf`@z&vKMs({yrrg##B&PgsSNb<N#gBA<#14%KsnAz5d|^?4hFcluHq@Dm*itvuwL-hkjB#04K=qur3^@bGYDxBY5p_=g)9 zc-?qTcgKKu`Hmmvmy6m5rmbso$!ca~I5-ShwXxDzDY1 z*&{VZJRf$@PQ-YoPf`V5@6xRm$0+1L1Dks|)P5J?Jmxl$%?m2*_W!TJJ>pF z>TU7!E0GX)-B8-mvT{tVjl6%RnYqgkU{Grm8?ItWV33M|Yk{4?y)x)UWF---+U4lB zySJM@NsKAF_t7f5$y6wZma7<^wo7K3Vc$HB02%LX=g=t8>4hV;ofh<%ne|SyQnCQ@ zU7M`EXg`Wsc<2n7@pQAWeiXG=Ro@l9d1$g{v}uxHEFlY&wma1iV(zAgu6b@Knxoyu zs`%JdVKKJ{h+)N6$`i7i=A*cF*3V{yYEBE%bEjk-YV?DVPU_)n`xSVyd_|4)(k)Hb zcjZ>H1rf-dll()ZpOniI9cjWt$7q_;=)7x)=I`q=eizP@hG3DxsneT57#*8HXBZ7Y7VQe=|IsyR?Rfb%6c`yWaF*uif0twWr?R&1&#^2f4iC34bFShug)% ztg%o;6$z3ghH5rIL0_b{%#-T$5YJ`W)f92CzQ=C|Nxzv{(t zy-&tX!A+$0vTvK!T2R&z%JItcAp{U#Rav{C>h)N9>3FRMG2$18Zj!v+@x1-EcS&R) zrAy+ttaTc}zhiE6Y)pg5J!6vXSdbXTFzUkG_4d?FJa+4ylZ?Qpr_TGw-i-ZXVc13# zNc_zgr*RmYADw2JR}0LKw^O*O_Vzd{&dLrpUm9QF<$)jdr|M5ykD_oI4O&sP)Sp<# zjv1ZZWutADsJr7hZhXD4IO|IhyvQ5q?FzW4dpOb=`4qUAtnU%Cq%>jXhlCZMc*A*n z=iUwAvX&o~Ya7ZYBk#$hCa~Cyje|5~a{f-8O)ufs&a8SrMGQ}4E?N8_<)m&yq>C7% zt=-vcG2R-P--EsSyXK!o?mGHgGrQ3@ZHFlVJ|;kn5x2B~=qmA}x>B$Dsl@h{wVaZI zXNU1B{BV$n>v6v?)#qmH z(hxg>Ek^ET^>M$+l!q!jSC6JJTA!0uSnMwu?|IB${lNQ~d!AEpubJ7N3-;(Pk=Nl2 zVFknh(jO5zgUfIt75mZsF#e9g8>(W{-DrcTCx|a{zR2XW15DiECB}iBJ_lM73+9AW zVG3<+TwYJtSEm;zmlx-|yEvK-22AOam7;ykKo7w+5okJDDV>;tg14P5B~lWVF&2Nf zq_4=p2AI(K4|H&ElShP`ycoeBjmuc5cLAAh_&$I*eJBAcIC;C#R(6A4Jj zU?QK5IG%A29}}9DhnSd0*#uxVkK4^%bi^rCLw7q>6EE{0@GZvEWqB>y`wQ(Tf%K%T zASzux0~9}8TVwMsEh+gXDsHI)AxX?9%#-G3&lFS3K7VeH+3MB_jd;Q*D7kKH}W z6COGA5MIwwVU>JAS<6NmSL?%A8XE zH5{79Cn*% zm38i(na^(+8!HSMBF{3v01iB5J z`UG?22?hQTxfy#(!s#CB}=CYMoit8DU4Z^`lP3J15_1GS@g)37a zfV#Q$gimy%PFDZq`hNRKcb6_#-2Y&1ouyV`fsh7VZO0O=pnkc7_54Z&_do4;uFiah zlE=A{CaQJ~&d0FjoD}+15}-8@q2--%y?Uw4f-LEAsHxp@ZLJt~BB#yZL-_$m)+&>d zwZr=d^e4hYEmj8KJRuNnFj2?yG0z1IzuBIqN~4+rR0C4TFASkt%e*)giz6mFe?2q+ z6qX&z8fJH50`MjGD23T9#>qX}rEK1;>;SOiXMlw#*kB|>1MQd|Uoj0^w5YTucDt8m zjKz(OF*hrz1nn;@y|BHXpTeztM5U13)JYXUhJ^koge9Ak*#aOkn*Sl|o7Z0#r=gCz zcX1n8ZzL&Dl$hzg@BJ+(KX6^eo^9gls+u0?5~!anv3w9CA}pSb#icf5QK;m@_G$RG zh58%;qDh_6{Sk^!H;Neq^Frt54_A)cBlg-@%rB=CRwnMRXU~yso6`P|@@q6P{P3z6 zh2*S;tVfcfD=WFF3F$0An&fO>r2_%5PRrhA;(X((qzlEJ7>?Y9b%u^>Pk11%Vtv<%nyOTf`e;nU8?cw zi~L@D?=#}VZ;las+s^>K{<=h9p(T9K7nc13!gHa7x9_2?H=^&4Z-kO$gg)1h{A7Z< zlhGZKR?oz%p&=Og%zu7K-%$u<#k@p>T=`33>l+bZOZKyy!7iAa*a%~+N`QA^P0WfU zEGfD8N^xVkT`T8Ho14p8)YL*ruqD@$k&lRC@(T8?rFHxA%ZU=-yw$EQr z35TuCQ%){shKY-UUr)foAq#<6*JkQHpBwrBE?vO>nCZ8nuxK{Q`#jJ!D5WMv8KH!@ zz9OgB&-{ltM^|9x4uF8z(gs`}+Aiqw*^X2?7Mc-+3jP4qv!lK})%}(-x!jm8Na>zf zND+V*`c`(Y%yTsC z*^6mtT)uvHi+}TuKn49ko^!qG)7QM*k|o^PyO^c#?zGnrf;5o{gNcQ{j}4E>%;eur z<=xyGi9;$g;&tQtx*-cMdZIp#>S-bPn&Clk=Mu;sgaJy!Rl5{6tp`|C^X_SRzgs&x zs@DR8dEV}a2gv={ZK>p>WMpW>imL@=GS8ytPk1HWk^Mw?)uFiALClk5Qjiv$+WcH! zZHxl!Nmn{NVD-pcj>ajj{M)zer4ae?r|tuUcbuJZqXm<}M}!a>De^qENO#hR_{hjAU!t{>0iw0P)ke7dew&)-b# zUfg=$_912Hh1j*P@8;ZiuO}0B%Dk?~EB%Mz>#rw06Q3>7nIb4pdPT3s%NpeOj~VhXllMUgyY|d zfU>etKr;hPH;r*GwgaT0=!mKIEdJSFDVfdyN`Ozz&QZRWO%W;)Z zJjd|%Pec()oetOP#K2P(xWTi;hWj5b@Upc3+8xaxr1%O;i1|M)ADx6vM2i0SO%ul} zN?9CW)9&rR*h*2vwD|)>ey@85?IDXRX6e7weTi7qSpa$ zVUqVG-qWc*vKNc_J{8hkVHPA8w6L2#GeMj2nxu(LwxZ`5t5l@%3HfMfNv&FH7;T%Y z{o>z{hvR%wmPq+q2Q`gsvnmocwM7J>6_kIOvDMnheI0Y)$fji7AaUo4F&M!uy@F42 z(ldVmeQq?>YMLe}wIvQ4k93Z8sfe(o_TB9j<6A}jrjiLBXUcKbF3z#tT}yONN?Cn}N;vaHtsD67J> zL^QhSBF0ErT_d%*K%aqTrT^7{R-o*4v)`Icg0#4r-)rmCYE{>@K!5RfVOaanL*(#; zCgbTk_^aAtnwB=hsw9>sL6n6NgFGo6 z5OP@p9om9_zEAT7tC#uwMtb1|PTi*s9?#9~akg1Vr!0GHeq&%M3WdHwV)*zlB%=Bj ziVy419n)+g$9lJObT;2SLJ1a^4|Jd4*rCG);^7Unb0+)ssD6 zE~;8>UiXZnS#S^zG#EZ6DJx@e<`}Z0rO)muZCI&JMbpG#+78UKtnS&4a zL~rG~X|K0dl0U19@kLb(tu)DnBSx+sc%;q6&G!X1Y)w%<{#`5XLGe=U&bzf%v>pMy z=Ey94uScOnuJScbS6>MBf|BL!0YyW>w0dhB04FHylbYe5m|=#YhDNn6Ua@GrBl)|?Y=$!x{s_)b*URU?*H?UYb}*}PkD=m< zzHVag+N7oEpJVx8M=kggl53&rlYHKmG)ZdVUO1JlQkz7)kM++NdzdZiGp;0t^<7zu z=I~#!z-kD{FYo-NeFN5jFRcc2wbggEqQ=>uJxLv0Dih)Ghm`L+V^U_cPOdR#^%#1! zkC?E8 z49?yBv^%BsqJBQ*uP!GILU$atod!}3v!rc2($stfc*R6*jVoOL)VL4W*J5tr&J`rk z!Td69jNuvM?C7eSZWmLe&fq}^)`KRFT&Ozi6w7FBSkysz86WIwXf;(^eyf=-LxO7# z`YKjUA9@Tkw1n$Zj5kvCt`-I?*}&A^a3w7@grC2KGXvbhn;UU5_TeevQz?*dK)dR? z0$LiMb6PO#*BVX*)KlrT10Ds?(`83nP&>;uqIgC3km=59OwL~Gx*i0oY#=hfI$hZ6 zvoUKkh(oa0rj_Eb_DIkEPXk%}( zDPa^zoxswM8ZPdxsDl+^5XdP)C9!7+k5h(7i5C5Uz_>$`&KZo^YnK1U(7Am zICxH3ThR(bs~YE|$4@Zp=E=>^mOMa9v#1At8vZPG3y-n_e{uWRl&7jz0E8X1b^z z`&EK)zVD>#l;%z6t=zU6CQhIdC3Kj5cr{3>w+hY57PPYE3*c<{#*Pt{a?;P$Of^hc zr{g-V7HyRw_}!Jq#bI_jP2P93{iSF@=}P4Qi8eZ`TQ9?Zn?>iGRbAck5kh7}RXTy` z9(P6$*i_)n@6LB1`H4ztv!q-2>ddqklcfwZo9EU#A^CvY$E7!5F&thyKWOI$xz};StbotQ>qOx9O zs43AxV6KCgqTPWyRLZbxawsF;-Oc^Yh%&JE?LH}4$!*lpas@364?_QA7Xr62Q#U;W+CqXz~Cpy8kZ4=V(%|M?=neUr6cr31aa zeb;_#e(;ZsaDvEa4g`P@Hb|jwUtWT|1*#&_aF37uBeo|^aD)YTUrX3r1eCTmy+)xC zOo;~Dq7h=bEHyXUY$Op0x(L{3he)40BQQXeF#w~}{WKq{7#I*l1sy;oR!>#4)rqCs zer^0XIgy$DVut$s1x^SuA0>SV9kWJGCK2h9hh#UzWV|fPI2BKChHRKD&1n&AS*;zl?j}*)Z;Ig+o#Ox5lQ|go9uN=w>lCqNd)q>1 zsdVF$w5q?_9l~Z5b961k$8hIYz1b67)O$3^B)>toG-_}!iPW_xLp`O)GF_M6qTH}D zxk2dv-L=e_=zrG70zY(7uf6<5U{YP1d5!wCpdSFm@}?hygzUTWIgb|7?o?uR{Y+0rUVBfO`-z5ET#ykSLHE zkYSKHkZq82katij&>x^ipgExJpqF55U{YWaU~^#mV9(%i;6&hT;3nV`;P()85M~f5 z5H%2kkl>JbkTl=!S~Eyj$P~yr$Y&@bC_AVus12xVXe4L~Xenqb=m6+6=nCj|=n3du z=u7BN7%>=6m^_#zAmq2{))klntN;!J_kqu_G_X>zF|fU`w{Qq>VsJWe-f#(UO>lE? zxA4&LYVaoTb?~R~p9pLSQ3wl&07NvzBE%LX5+p065Tq2OB4h+)JY*GQ7vuuuOXN=! z5)?rcJCxrjqbU2RXs8;fy{MmPaA;U)GH4oTztLLIHqlPe9??P3DbS_R)zFR5z0s4< zr_k5X57BQi#4waFN-#z-7BIFk&M~ntMKBdGeKA`w`!T1mNU#{NtgxK0wz1B!p0UBP z1+l%cldvnW2eCJCm~eP;Byd!4ig4<1K5@Bl#c-8y^>8C_TX6ev@9_xmjPUI60`OY! z2Jzd!c=SP4RnaODR#i&bjO$ytkmA2n^GalGJcojibZ{3 z(CJmJ7)GFRo~WHKT2Ddet+?PCde-2I_TmMa&-7+GU@TXVX9)`r^?T@4VD5lkDghyJ z=?swLL3?d5I9P%x@QjAFj`I)+O;w)HnOWs_WA0*@O|yrBklA!?w6eSPYo7UVM~3M~ z=11k8&3o^WXj7unV*Fv$?a+LrERbituzdu}a`qP$4pPVpGAcZ2D|?QYn)NF^dkBl3 z|6R!kqCW+~X(H7}58<({-6g0daE4H5t-Z*2<*4s|Fs8K2Ak>)!#)Yi09k#V_%<4i| zm6n)m@g-s?h}& z$Q?h!f|P?%!YRV&Q8-CBm;(YE@!vKm+B_2}r=5L+-~D1~VS+@$F-X;Pkd#!}7Z(+j z>6;f7)%hOSXB0@I*=JQ)C!3LKi`BHO3#(n%&hiT7ew-Cl+M&2C3(FgZlXyxdRJHQI zO3ljqfx)qj8VDERSf#NIL^I7%6-F~JaqY>**%e4qWExdivY2Gpg`}IM+s6DkOt<$> zbX_?qA>CX(`0XQ=?MM@*v>Sba@OByc5wo^eZ68DZG%nwh%zakFGo@(}ABfRmFNt)a zcfg9sdq3(W&38Abd}R7^8kd^mMZ7Mad(kWtbD%bweAJe#HrBd^1F@uqIrwZ47!9;l zGHE=MTPavkTlc;liY8iusO@x}C0EyV9ye9rXj%6x-2>h__3udB5|);zj{T*dn2sSU Wd5r!*OpT4P8o`m;m_K$==`c84Z6{8{+A(HKZlhvuY>cgr zKFA)%(1*FfM*FnUZZIZ{J&ZBvpfZF#Y)@lIVSRG2hwi1Y4#u`F`<;8QXe6-ja?bgE zzwN>ij! zo>pmzGE@TNr8Jlltw75)0Xj=z>Nm6}M}y=6>u7^S>VN6wGm(a7S2vh%P-e4*%<|74 z-T45bcKGFdW@Q;|Bm8Z&_WaWN)fd`ZeusaHNO&->WpYaP$#+CHu8YruP`(wvhyN-3 z)ANP$nqL?8;r{@?cd3}o2=nYs_)p+_3z@ZLe9%0RR1ZI(XA0Ww7af1Ye-<&`S}v}X zF;AHMCGRIuyn~rYh<#h&Z+ypW*>5z!99(zI7^Yoq=ZPL)7I!PVbY4V3=V9`>F2E}z z8jskHKC=}KRXf?}74Xf-hKS{56ga3mP!HbyReXH8^0ZRngTXngI9B<^|Cj$y|Mga> z;R}pA9?uwniV?%;hGTu$Jc)CL^hp0d9A5GarZvNJ<6ufi3Gpco?d=T-GZmvH1`UH| zm}{8N!4f`UZ>U1NTNno5ddx?}-q;a0gdKdb1FJ*sPETj7DTdm^s|}H!LSzj^@e}6Xo3K zXf!%Jyy+jD4Tn>yMB=%*RO-r=RBGa)o-oe4%#d}+m|4=Q~$QQ44QKuG~z>i4Xv1q8Lj|COk&su!m zB#R{CF*mpnCZx!x7-pM@8!0(V$8jZ+b@ar1M!vd&d^FFaIShFeNRsTjU4j|Sd#3{Z zXChnUcGm@);*d_c-5qUSPhEZ6>5dLdQtxVZJDg384u_&VOeQt$##&BG!nEAl4Grj% zf?bkj$zHI_k|f*NMx?LL=UZ2u=WI$ybv3ANugm3X>D=4hK9vL(>`jZV6X?ab{kvf)^jN}pSC8R2xdumC)3 zVG%fPVH@zOg(aM_9t+FhFId=#{kw^X%;o^2qQ}GnaNfcq@UVq#z&9-{;jj-_SO!03 zVJAiDPgt_+pY=D^L-I_h!b%DZS?d0}MTT%Y6XQicBm D*A6pu literal 0 HcmV?d00001 diff --git a/luci-theme-darkmatter/files/htdocs/fonts/font.svg b/luci-theme-darkmatter/files/htdocs/fonts/font.svg new file mode 100644 index 0000000..d38d057 --- /dev/null +++ b/luci-theme-darkmatter/files/htdocs/fonts/font.svg @@ -0,0 +1,16 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + \ No newline at end of file diff --git a/luci-theme-darkmatter/files/htdocs/fonts/font.ttf b/luci-theme-darkmatter/files/htdocs/fonts/font.ttf new file mode 100644 index 0000000000000000000000000000000000000000..84669323e9ddfdca80c91416b6d4fe31ea1829ab GIT binary patch literal 1748 zcmah}U1%d!6h3n&Gn1cY`jbCwn>0i)RYHn=Nd(_}VjX(rt? z-ECzbiVq^~gR7wIJ}Ik$SP&mXgcU4O@Ijw^C=2dW1t0Wf!4*VZ<9BDKw5tWraPR%j zch0@%%sF?41R`?L28k4Z?)j66)(&?+m@hHriq*pM5AWT1he+s#-Y6GVma(=$zlqge zURuBMOt<$}=(mX2y^2;SDaD815ZSn2x&lJ^TKpFJN6=4Hs*Sav&i0^x2YqmLYKX+Z zh4)?i>i(NQi4QI{A2plC?(n|zF-~ay82lsnd+=JP)cPs*9gSyfz$B1>-K|G#j3{&3 za6R<@9}b5+fwX3MZ5)CURiZq_(cRrqHrq5-65u1iXN>y@0f&$Yu-(xnaWfkO;Qq)* z#NF5yH`q4b*n#elr`Ok;YENPI@o7V(M{qI++s^0z-kX`(%jfy~2$ZLw_^~)dT}1uS z0rN>EGwI<(fDgaVtH^j5jW5RI*{t6`F;QxqJJ%>pOeB+IW1GQ|xmYZh%VeIK&*d&( z&gJGW%*>3B-$2WWnmaZ^=Q=5cP!W|W1*=Ev4Ne;?! z)Dy)zd{P0UUVRb1o7d6YhB`8mB)e{xAja}mPdI)uu{C9PpS39tsmJ39b@_d+maY?_ zkmb_i?(jIA?QIT+qTJ7BHSPLZNy|dC(%N+m=)h}3M^?w~_ggCg@Q$N?gdbxG{B zx-Tq&<|jJo3a&GkgUE(rO)7n2#T7^aXXe0w=PWD&r!8y)UbV1p!rv)lfjj9+m((_t< zWuc~rPsQT+HZ`i#3LId-Ij1N_@nd%#OHOO$)ulomK{X`0N=sCr`mvsqHN6ooYr0l1 zG_+E9emy)@ter=0F`6`DHxRvyV{{{<0yVIfz|GS-<|*R^&YLfQ9MQz1E%`702g#=) A_5c6? literal 0 HcmV?d00001 diff --git a/luci-theme-darkmatter/files/htdocs/fonts/font.woff b/luci-theme-darkmatter/files/htdocs/fonts/font.woff new file mode 100644 index 0000000000000000000000000000000000000000..00cf84ea037c9748eba8fced0acff7dd29c12b2f GIT binary patch literal 1824 zcmah~U1%d!6h3z*Gn1dq^e2DXHffq}n?fO*P4m+=ZK*U=>UMvqZE#n_rpav5(j>cS zx@~12iVq^~gR7vpPs*;oSP-8?SivF%AM{D2EZCG7&Rrx~vs#LD8 z6G>0NAK>b*2>Z*`Jo2P*_+G9;iM>+Jt)s5=Jp7=pDwQg^vIY+G*{fXdJZt;odUXZ8 zDo8Q&L?BVT%Wji|-tGur!++fv+0Qh@Y&`XZu+|ff4#$ZeUJ-X2yEHCB!7-gTOi&;6 zDnMO`aoh#Vv%1Jayytdm1tWk99y$)y!#92uA6{uZZZr@haQ1(2tnt17cmHqxn;lZi z$L30U;t9iuVRnmN#h?YE)H%Ji~}l4#fh3$L$@8 zB@^+8(AyRp6H=J9PPd>@Tz*hhgR$y%{W5Pf*FI~Y9^ z+n%yI&s!9m)a`NwI=vppsm{}ZfSGd2+3vF0+gfclML9^PHEnIZprs*NVSP;l`k-u; zWLdJ7t+FJ^R<;=%9Q64%RQoxL5>}lps>|ziI$L`}{T`#RqrJ^xmt|E|ogPm| zCrxYtUNy0VFSW^2&zRUw zae99-UoBUw6a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+N+"))|)"+L+"*\\]",P=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+O+")*)|.*)\\)|)",Q=new RegExp(L+"+","g"),R=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),S=new RegExp("^"+L+"*,"+L+"*"),T=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),U=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),V=new RegExp(P),W=new RegExp("^"+N+"$"),X={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+O),PSEUDO:new RegExp("^"+P),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,aa=/[+~]/,ba=/'|\\/g,ca=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),da=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ea=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(fa){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],k=b.nodeType,"string"!=typeof a||!a||1!==k&&9!==k&&11!==k)return d;if(!e&&p){if(11!==k&&(f=_.exec(a)))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return H.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName)return H.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=1!==k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(ba,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+ra(o[l]);w=aa.test(a)&&pa(b.parentNode)||b,x=o.join(",")}if(x)try{return H.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function pa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=g.documentElement,e=g.defaultView,e&&e!==e.top&&(e.addEventListener?e.addEventListener("unload",ea,!1):e.attachEvent&&e.attachEvent("onunload",ea)),p=!f(g),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(g.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(g.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!g.getElementsByName||!g.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ca,da);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(g.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1; + +return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
                                                                      a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/\s*$/g,ra={option:[1,""],legend:[1,"
                                                                      ","
                                                                      "],area:[1,"",""],param:[1,"",""],thead:[1,"","
                                                                      "],tr:[2,"","
                                                                      "],col:[2,"","
                                                                      "],td:[3,"","
                                                                      "],_default:k.htmlSerialize?[0,"",""]:[1,"X
                                                                      ","
                                                                      "]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("