diff --git a/fusiontunx/Makefile b/fusiontunx/Makefile new file mode 100644 index 00000000..5d0df58e --- /dev/null +++ b/fusiontunx/Makefile @@ -0,0 +1,137 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=fusiontunx +PKG_VERSION:=1.0.3-4 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=BobbyUnknown +PKG_LICENSE:=MIT + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/bobbyunknown/FusionTunX.git +PKG_SOURCE_VERSION:=dd3605dcb3ad75933c4c27ac28f2276ea2af85aa +PKG_HASH:=skip +#PKG_MIRROR_HASH:=f37155115e9e96661b5c5d41f7c33d0af6babaebe66dbe9017858292d6cb4589 +PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz + +PKG_BUILD_DEPENDS:=node/host golang/host + +MIHOMO_VERSION:=v1.19.18 + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +GO_PKG:=github.com/bobbyunknown/FusionTunX + +# Determine Mihomo arch suffix +MIHOMO_ARCH:=$(GO_ARCH) +ifeq ($(GO_ARCH),arm) + MIHOMO_ARCH:=armv$(or $(GO_ARM),7) +else ifeq ($(findstring mips,$(GO_ARCH)),mips) + MIHOMO_ARCH:=$(GO_ARCH)-$(or $(GO_MIPS),softfloat) +else ifeq ($(GO_ARCH),aarch64) + MIHOMO_ARCH:=arm64 +endif + +define Package/fusiontunx + SECTION:=net + CATEGORY:=Network + TITLE:=FusionTunX Core + Mihomo + URL:=https://github.com/bobbyunknown/FusionTunX + DEPENDS:=+ca-bundle +ip-full +kmod-tun +kmod-nft-core +kmod-nft-nat +kmod-nft-tproxy +kmod-nft-socket +endef + +define Package/fusiontunx/description + FusionTunX + Mihomo Core Binary +endef + +define Build/Compile + @echo "Building Vite dashboard..." + cd $(PKG_BUILD_DIR)/src/dash && \ + npm install && \ + npm run build + + @echo "Preparing static files..." + rm -rf $(PKG_BUILD_DIR)/src/internal/ui/dist + cp -r $(PKG_BUILD_DIR)/src/dash/dist $(PKG_BUILD_DIR)/src/internal/ui/dist + chmod -R 755 $(PKG_BUILD_DIR)/src/internal/ui/dist + + @echo "Installing dependencies and generating Swagger..." + cd $(PKG_BUILD_DIR)/src && \ + go mod tidy && \ + go install github.com/swaggo/swag/cmd/swag@latest && \ + $(HOME)/go/bin/swag init -g cmd/server/main.go -o docs + + @echo "Compiling FusionTunX Go binary..." + cd $(PKG_BUILD_DIR)/src && \ + GOOS=linux \ + GOARCH=$(GO_ARCH) \ + $(if $(findstring arm,$(GO_ARCH)),GOARM=$(or $(GO_ARM),7)) \ + $(if $(findstring mips,$(GO_ARCH)),GOMIPS=$(or $(GO_MIPS),softfloat)) \ + CGO_ENABLED=0 \ + go build -v -trimpath -ldflags="-s -w" -o $(PKG_BUILD_DIR)/fusiontunx ./cmd/server + + @echo "Downloading Mihomo $(MIHOMO_VERSION)..." + wget -q -O $(PKG_BUILD_DIR)/mihomo.gz "https://github.com/MetaCubeX/mihomo/releases/download/$(MIHOMO_VERSION)/mihomo-linux-$(MIHOMO_ARCH)-$(MIHOMO_VERSION).gz" + gunzip -f $(PKG_BUILD_DIR)/mihomo.gz + chmod +x $(PKG_BUILD_DIR)/mihomo + + @echo "Downloading GeoIP assets..." + wget -q -O $(PKG_BUILD_DIR)/country.mmdb https://github.com/rtaserver/meta-rules-dat/releases/latest/download/country.mmdb + wget -q -O $(PKG_BUILD_DIR)/geoip.dat https://github.com/rtaserver/meta-rules-dat/releases/latest/download/geoip.dat + wget -q -O $(PKG_BUILD_DIR)/geosite.dat https://github.com/rtaserver/meta-rules-dat/releases/latest/download/geosite.dat + wget -q -O $(PKG_BUILD_DIR)/geoip.metadb https://github.com/rtaserver/meta-rules-dat/releases/download/latest/geoip.metadb + + @echo "Downloading Zashboard..." + curl -sL -o $(PKG_BUILD_DIR)/zashboard.zip "https://github.com/Zephyruso/zashboard/releases/latest/download/dist.zip" + mkdir -p $(PKG_BUILD_DIR)/ui/zashboard + unzip -q $(PKG_BUILD_DIR)/zashboard.zip -d $(PKG_BUILD_DIR)/temp_zashboard + mv $(PKG_BUILD_DIR)/temp_zashboard/dist/* $(PKG_BUILD_DIR)/ui/zashboard/ + rm -rf $(PKG_BUILD_DIR)/temp_zashboard $(PKG_BUILD_DIR)/zashboard.zip + + @echo "Downloading MetaCubeXD..." + curl -sL -o $(PKG_BUILD_DIR)/metacubexd.tgz "https://github.com/MetaCubeX/metacubexd/releases/latest/download/compressed-dist.tgz" + mkdir -p $(PKG_BUILD_DIR)/ui/metacubexd + tar -xzf $(PKG_BUILD_DIR)/metacubexd.tgz -C $(PKG_BUILD_DIR)/ui/metacubexd + rm $(PKG_BUILD_DIR)/metacubexd.tgz + + @echo "Downloading Yacd..." + curl -sL -o $(PKG_BUILD_DIR)/yacd.zip "https://github.com/MetaCubeX/Yacd-meta/archive/refs/heads/gh-pages.zip" + unzip -q $(PKG_BUILD_DIR)/yacd.zip -d $(PKG_BUILD_DIR)/temp_yacd + mv $(PKG_BUILD_DIR)/temp_yacd/Yacd-meta-gh-pages $(PKG_BUILD_DIR)/ui/yacd + rm -rf $(PKG_BUILD_DIR)/temp_yacd $(PKG_BUILD_DIR)/yacd.zip +endef + +define Package/fusiontunx/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_DIR) $(1)/usr/share/fusiontunx + $(INSTALL_DIR) $(1)/etc/fusiontunx/configs + $(INSTALL_DIR) $(1)/etc/fusiontunx/proxy_providers + $(INSTALL_DIR) $(1)/etc/fusiontunx/rule_providers + $(INSTALL_DIR) $(1)/etc/fusiontunx/ui + + $(INSTALL_BIN) $(PKG_BUILD_DIR)/fusiontunx $(1)/usr/share/fusiontunx/fusiontunx + $(INSTALL_BIN) $(PKG_BUILD_DIR)/mihomo $(1)/usr/bin/mihomo + + $(INSTALL_DATA) $(PKG_BUILD_DIR)/files/app.yaml $(1)/etc/fusiontunx/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/country.mmdb $(1)/etc/fusiontunx/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/geoip.dat $(1)/etc/fusiontunx/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/geosite.dat $(1)/etc/fusiontunx/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/geoip.metadb $(1)/etc/fusiontunx/ + + $(INSTALL_DATA) $(PKG_BUILD_DIR)/files/configs/* $(1)/etc/fusiontunx/configs/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/files/proxy_providers/* $(1)/etc/fusiontunx/proxy_providers/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/files/rule_providers/* $(1)/etc/fusiontunx/rule_providers/ + + $(CP) -r $(PKG_BUILD_DIR)/ui/* $(1)/etc/fusiontunx/ui/ +endef + +define Package/fusiontunx/conffiles +/etc/fusiontunx/app.yaml +/etc/fusiontunx/configs/config.yaml +/etc/fusiontunx/proxy_providers/proxy.yaml +/etc/fusiontunx/rule_providers/rule.yaml +endef + +$(eval $(call BuildPackage,fusiontunx)) diff --git a/fusiontunx/Makefile.backup b/fusiontunx/Makefile.backup new file mode 100644 index 00000000..3e12d0e9 --- /dev/null +++ b/fusiontunx/Makefile.backup @@ -0,0 +1,132 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=fusiontunx +PKG_VERSION:=1.0.3 +PKG_RELEASE:=4 + +PKG_MAINTAINER:=BobbyUnknown +PKG_LICENSE:=MIT + +# Direct download configuration - change GIT_BRANCH to switch branches +GIT_BRANCH:=dev + +# Direct download method (no git) +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://github.com/bobbyunknown/FusionTunX/archive/refs/heads/$(GIT_BRANCH).tar.gz? +PKG_HASH:=skip + +PKG_BUILD_DEPENDS:=node/host golang/host + +MIHOMO_VERSION:=v1.19.17 + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +GO_PKG:=github.com/bobbyunknown/FusionTunX + +define Package/fusiontunx + SECTION:=net + CATEGORY:=Network + TITLE:=FusionTunX Core + Mihomo + URL:=https://github.com/bobbyunknown/FusionTunX + DEPENDS:=+ca-bundle +ip-full +kmod-tun +kmod-nft-core +kmod-nft-nat +kmod-nft-tproxy +kmod-nft-socket +endef + +define Package/fusiontunx/description + FusionTunX + Mihomo Core Binary +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(HOST_TAR) -C $(PKG_BUILD_DIR) --strip-components=1 -xzf $(DL_DIR)/$(PKG_SOURCE) + $(Build/Patch) +endef + +define Build/Compile + @echo "Building Vite dashboard..." + cd $(PKG_BUILD_DIR)/src/dash && \ + npm install && \ + npm run build + + @echo "Preparing static files..." + rm -rf $(PKG_BUILD_DIR)/src/internal/ui/dist + cp -r $(PKG_BUILD_DIR)/src/dash/dist $(PKG_BUILD_DIR)/src/internal/ui/dist + chmod -R 755 $(PKG_BUILD_DIR)/src/internal/ui/dist + + @echo "Installing dependencies and generating Swagger..." + cd $(PKG_BUILD_DIR)/src && \ + go mod tidy && \ + go install github.com/swaggo/swag/cmd/swag@latest && \ + $(HOME)/go/bin/swag init -g cmd/server/main.go -o docs + + @echo "Compiling FusionTunX Go binary..." + cd $(PKG_BUILD_DIR)/src && \ + GOOS=linux GOARCH=$(GO_ARCH) CGO_ENABLED=1 \ + CC=$(TARGET_CC) CXX=$(TARGET_CXX) \ + CGO_CFLAGS="$(TARGET_CFLAGS)" \ + CGO_LDFLAGS="$(TARGET_LDFLAGS)" \ + go build -v -trimpath -ldflags="-s -w" -o $(PKG_BUILD_DIR)/fusiontunx ./cmd/server + + @echo "Downloading Mihomo $(MIHOMO_VERSION)..." + wget -q -O $(PKG_BUILD_DIR)/mihomo.gz "https://github.com/MetaCubeX/mihomo/releases/download/$(MIHOMO_VERSION)/mihomo-linux-$(GO_ARCH)-$(MIHOMO_VERSION).gz" + gunzip -f $(PKG_BUILD_DIR)/mihomo.gz + chmod +x $(PKG_BUILD_DIR)/mihomo + + @echo "Downloading GeoIP assets..." + wget -q -O $(PKG_BUILD_DIR)/country.mmdb https://github.com/rtaserver/meta-rules-dat/releases/latest/download/country.mmdb + wget -q -O $(PKG_BUILD_DIR)/geoip.dat https://github.com/rtaserver/meta-rules-dat/releases/latest/download/geoip.dat + wget -q -O $(PKG_BUILD_DIR)/geosite.dat https://github.com/rtaserver/meta-rules-dat/releases/latest/download/geosite.dat + wget -q -O $(PKG_BUILD_DIR)/geoip.metadb https://github.com/rtaserver/meta-rules-dat/releases/download/latest/geoip.metadb + + @echo "Downloading Zashboard..." + curl -sL -o $(PKG_BUILD_DIR)/zashboard.zip "https://github.com/Zephyruso/zashboard/releases/latest/download/dist.zip" + mkdir -p $(PKG_BUILD_DIR)/ui/zashboard + unzip -q $(PKG_BUILD_DIR)/zashboard.zip -d $(PKG_BUILD_DIR)/temp_zashboard + mv $(PKG_BUILD_DIR)/temp_zashboard/dist/* $(PKG_BUILD_DIR)/ui/zashboard/ + rm -rf $(PKG_BUILD_DIR)/temp_zashboard $(PKG_BUILD_DIR)/zashboard.zip + + @echo "Downloading MetaCubeXD..." + curl -sL -o $(PKG_BUILD_DIR)/metacubexd.tgz "https://github.com/MetaCubeX/metacubexd/releases/latest/download/compressed-dist.tgz" + mkdir -p $(PKG_BUILD_DIR)/ui/metacubexd + tar -xzf $(PKG_BUILD_DIR)/metacubexd.tgz -C $(PKG_BUILD_DIR)/ui/metacubexd + rm $(PKG_BUILD_DIR)/metacubexd.tgz + + @echo "Downloading Yacd..." + curl -sL -o $(PKG_BUILD_DIR)/yacd.zip "https://github.com/MetaCubeX/Yacd-meta/archive/refs/heads/gh-pages.zip" + unzip -q $(PKG_BUILD_DIR)/yacd.zip -d $(PKG_BUILD_DIR)/temp_yacd + mv $(PKG_BUILD_DIR)/temp_yacd/Yacd-meta-gh-pages $(PKG_BUILD_DIR)/ui/yacd + rm -rf $(PKG_BUILD_DIR)/temp_yacd $(PKG_BUILD_DIR)/yacd.zip +endef + +define Package/fusiontunx/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_DIR) $(1)/usr/share/fusiontunx + $(INSTALL_DIR) $(1)/etc/fusiontunx/configs + $(INSTALL_DIR) $(1)/etc/fusiontunx/proxy_providers + $(INSTALL_DIR) $(1)/etc/fusiontunx/rule_providers + $(INSTALL_DIR) $(1)/etc/fusiontunx/ui + + $(INSTALL_BIN) $(PKG_BUILD_DIR)/fusiontunx $(1)/usr/share/fusiontunx/fusiontunx + $(INSTALL_BIN) $(PKG_BUILD_DIR)/mihomo $(1)/usr/bin/mihomo + + $(INSTALL_DATA) $(PKG_BUILD_DIR)/files/app.yaml $(1)/etc/fusiontunx/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/country.mmdb $(1)/etc/fusiontunx/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/geoip.dat $(1)/etc/fusiontunx/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/geosite.dat $(1)/etc/fusiontunx/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/geoip.metadb $(1)/etc/fusiontunx/ + + $(INSTALL_DATA) $(PKG_BUILD_DIR)/files/configs/* $(1)/etc/fusiontunx/configs/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/files/proxy_providers/* $(1)/etc/fusiontunx/proxy_providers/ + $(INSTALL_DATA) $(PKG_BUILD_DIR)/files/rule_providers/* $(1)/etc/fusiontunx/rule_providers/ + + $(CP) -r $(PKG_BUILD_DIR)/ui/* $(1)/etc/fusiontunx/ui/ +endef + +define Package/fusiontunx/conffiles +/etc/fusiontunx/app.yaml +/etc/fusiontunx/configs/config.yaml +/etc/fusiontunx/proxy_providers/proxy.yaml +/etc/fusiontunx/rule_providers/rule.yaml +endef + +$(eval $(call BuildPackage,fusiontunx)) diff --git a/luci-app-fusiontunx/Makefile b/luci-app-fusiontunx/Makefile new file mode 100644 index 00000000..a31e0648 --- /dev/null +++ b/luci-app-fusiontunx/Makefile @@ -0,0 +1,96 @@ +# - Copyright 2024 +# - bobbyunknown +# +# https://opensource.org/license/mit +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-fusiontunx +PKG_VERSION:=1.0.3 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=BobbyUnknown + +LUCI_TITLE:=Luci Simple Tunnel Client for Mihomo +LUCI_DEPENDS:=+fusiontunx +LUCI_PKGARCH:=all + +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE + +RUN_DIR=/etc/fusiontunx + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + CATEGORY:=LuCI + SUBMENU:=3. Applications + TITLE:=$(LUCI_TITLE) + PKGARCH:=$(LUCI_PKGARCH) + DEPENDS:=$(LUCI_DEPENDS) + MAINTAINER:=$(PKG_MAINTAINER) +endef + +define Package/$(PKG_NAME)/description + Luci Simple Tunnel Client for Mihomo (Clash Meta Core). Provides transparent proxy with TUN/TPROXY mode. +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + # 1. Install Init Script + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./root/etc/init.d/fusiontunx $(1)/etc/init.d/fusiontunx + + # 2. Install LuCI Files + $(INSTALL_DIR) $(1)/usr/share/luci/menu.d + $(INSTALL_DATA) ./root/usr/share/luci/menu.d/* $(1)/usr/share/luci/menu.d/ + + $(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d + $(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/* $(1)/usr/share/rpcd/acl.d/ + + $(INSTALL_DIR) $(1)/www/luci-static/resources/view/fusiontunx + $(INSTALL_DATA) ./root/www/luci-static/resources/view/fusiontunx/* $(1)/www/luci-static/resources/view/fusiontunx/ +endef + +define Package/$(PKG_NAME)/postinst +#!/bin/sh +[ -n "$$IPKG_INSTROOT" ] || { + /etc/init.d/fusiontunx enable + /etc/init.d/fusiontunx start +} +endef + +define Package/$(PKG_NAME)/prerm +#!/bin/sh +[ -n "$$IPKG_INSTROOT" ] || { + /etc/init.d/fusiontunx stop + /etc/init.d/fusiontunx disable +} +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) + diff --git a/luci-app-fusiontunx/root/etc/init.d/fusiontunx b/luci-app-fusiontunx/root/etc/init.d/fusiontunx new file mode 100755 index 00000000..783b83f3 --- /dev/null +++ b/luci-app-fusiontunx/root/etc/init.d/fusiontunx @@ -0,0 +1,23 @@ +#!/bin/sh /etc/rc.common + +START=99 +STOP=15 +USE_PROCD=1 + +BIN_PATH="/usr/share/fusiontunx/fusiontunx" +CONFIG_FILE="/etc/fusiontunx/app.yaml" + +start_service() { + procd_open_instance fusiontunx + procd_set_param command "$BIN_PATH" -c "$CONFIG_FILE" + procd_set_param term_timeout 15 + procd_set_param stdout 1 + procd_set_param stderr 1 + + procd_set_param respawn + procd_close_instance +} + +service_triggers() { + procd_add_reload_trigger "fusiontunx" +} \ No newline at end of file diff --git a/luci-app-fusiontunx/root/usr/share/luci/menu.d/luci-app-fusiontunx.json b/luci-app-fusiontunx/root/usr/share/luci/menu.d/luci-app-fusiontunx.json new file mode 100644 index 00000000..e6a7f53d --- /dev/null +++ b/luci-app-fusiontunx/root/usr/share/luci/menu.d/luci-app-fusiontunx.json @@ -0,0 +1,26 @@ +{ + "admin/services/fusiontunx": { + "title": "FusionTunX", + "order": 50, + "action": { + "type": "alias", + "path": "admin/services/fusiontunx/dashboard" + } + }, + "admin/services/fusiontunx/dashboard": { + "title": "Dashboard", + "order": 10, + "action": { + "type": "view", + "path": "fusiontunx/fusiontunx" + } + }, + "admin/services/fusiontunx/server": { + "title": "Server", + "order": 20, + "action": { + "type": "view", + "path": "fusiontunx/server" + } + } +} \ No newline at end of file diff --git a/luci-app-fusiontunx/root/usr/share/rpcd/acl.d/luci-app-fusiontunx.json b/luci-app-fusiontunx/root/usr/share/rpcd/acl.d/luci-app-fusiontunx.json new file mode 100644 index 00000000..f04ce2bf --- /dev/null +++ b/luci-app-fusiontunx/root/usr/share/rpcd/acl.d/luci-app-fusiontunx.json @@ -0,0 +1,31 @@ +{ + "luci-app-fusiontunx": { + "description": "Grant access to FusionTunX status and control", + "read": { + "ubus": { + "service": [ + "list" + ] + }, + "file": { + "/etc/init.d/fusiontunx": [ + "read" + ] + } + }, + "write": { + "file": { + "/etc/init.d/fusiontunx": [ + "exec" + ] + }, + "ubus": { + "service": [ + "start", + "stop", + "delete" + ] + } + } + } +} \ No newline at end of file diff --git a/luci-app-fusiontunx/root/www/luci-static/resources/view/fusiontunx/fusiontunx.js b/luci-app-fusiontunx/root/www/luci-static/resources/view/fusiontunx/fusiontunx.js new file mode 100644 index 00000000..da675d74 --- /dev/null +++ b/luci-app-fusiontunx/root/www/luci-static/resources/view/fusiontunx/fusiontunx.js @@ -0,0 +1,92 @@ +'use strict'; +'require view'; +'require fs'; +'require ui'; +'require rpc'; +'require poll'; + +var callServiceList = rpc.declare({ + object: 'service', + method: 'list', + params: ['name'], + expect: { '': {} } +}); + +return view.extend({ + load: function () { + return fs.read('/etc/fusiontunx/app.yaml').then(function (content) { + var port = '8080'; + if (content) { + var match = content.match(/port:\s*["']?(\d+)["']?/); + if (match) port = match[1]; + } + return port; + }).catch(function () { return '8080'; }); + }, + + render: function (port) { + var url = 'http://' + window.location.hostname + ':' + port; + + var iframe = E('iframe', { + 'src': url, + 'style': 'width: 100%; min-height: 85vh; border: none; display: none;', + 'title': 'FusionTunX Dashboard' + }); + + var warning = E('div', { + 'class': 'cbi-section', + 'style': 'display: none; text-align: center; margin-top: 50px; padding: 30px;' + }, [ + E('h3', { 'style': 'color: #d9534f;' }, _('Service is Not Running')), + E('p', { 'style': 'margin: 15px 0 25px;' }, _('The FusionTunX backend service is currently stopped. Please start the service to access the dashboard.')), + E('div', {}, [ + E('button', { + 'class': 'cbi-button cbi-button-action', + 'click': function () { + window.location.href = L.url('admin', 'services', 'fusiontunx', 'server'); + } + }, _('Go to Server Control')) + ]) + ]); + + var container = E('div', {}, [warning, iframe]); + + var updateStatus = function () { + return callServiceList('fusiontunx').then(function (res) { + var running = false; + try { + var instances = res.fusiontunx.instances; + for (var i in instances) { + if (instances[i].running) { + running = true; + break; + } + } + } catch (e) { } + + if (running) { + if (iframe.style.display === 'none') { + warning.style.display = 'none'; + iframe.style.display = 'block'; + iframe.src = iframe.src; + } + } else { + if (warning.style.display === 'none') { + iframe.style.display = 'none'; + warning.style.display = 'block'; + } + } + }); + }; + + updateStatus(); + + poll.add(updateStatus, 3); + + return container; + }, + + handleSave: null, + handleSaveApply: null, + handleReset: null +}); diff --git a/luci-app-fusiontunx/root/www/luci-static/resources/view/fusiontunx/server.js b/luci-app-fusiontunx/root/www/luci-static/resources/view/fusiontunx/server.js new file mode 100644 index 00000000..03bdd3d4 --- /dev/null +++ b/luci-app-fusiontunx/root/www/luci-static/resources/view/fusiontunx/server.js @@ -0,0 +1,142 @@ +'use strict'; +'require view'; +'require fs'; +'require ui'; +'require rpc'; +'require poll'; + +var callServiceList = rpc.declare({ + object: 'service', + method: 'list', + params: ['name'], + expect: { '': {} } +}); + +return view.extend({ + load: function () { + return fs.read('/etc/fusiontunx/app.yaml').then(function (content) { + var port = '8080'; + if (content) { + var match = content.match(/port:\s*["']?(\d+)["']?/); + if (match) port = match[1]; + } + return port; + }).catch(function () { return '8080'; }); + }, + + render: function (port) { + var statusEl = E('span', { 'class': 'label' }, _('Checking...')); + + var btnStart = E('button', { + 'class': 'cbi-button cbi-button-apply', + 'disabled': true + }, _('Start')); + + var btnRestart = E('button', { + 'class': 'cbi-button cbi-button-save', + 'disabled': true + }, _('Restart')); + + var btnStop = E('button', { + 'class': 'cbi-button cbi-button-reset', + 'disabled': true + }, _('Stop')); + + var btnOpen = E('button', { + 'class': 'cbi-button cbi-button-action', + 'disabled': true, + 'click': function () { + var url = 'http://' + window.location.hostname + ':' + port; + window.open(url, '_blank'); + } + }, _('Open Dashboard')); + + + + var handleAction = function (action) { + return fs.exec('/etc/init.d/fusiontunx', [action]).then(function (res) { + if (res.code !== 0) { + ui.addNotification(null, E('p', _('Command failed: %s').format(res.stderr || res.stdout)), 'error'); + throw new Error(res.stderr || res.stdout); + } else { + window.location.reload(); + } + }); + }; + + var handleStart = function () { + return fs.exec('/etc/init.d/fusiontunx', ['enable']).then(function () { + return handleAction('start'); + }); + }; + + var handleStop = function () { + return handleAction('stop').then(function () { + return fs.exec('/etc/init.d/fusiontunx', ['disable']); + }); + }; + + btnStart.onclick = ui.createHandlerFn(this, handleStart); + btnRestart.onclick = ui.createHandlerFn(this, function () { return handleAction('restart'); }); + btnStop.onclick = ui.createHandlerFn(this, handleStop); + + var updateStatus = function () { + return callServiceList('fusiontunx').then(function (res) { + var running = false; + try { + var instances = res.fusiontunx.instances; + for (var i in instances) { + if (instances[i].running) { + running = true; + break; + } + } + } catch (e) { } + + if (running) { + statusEl.textContent = _('Running'); + statusEl.className = 'label success'; + + btnStart.setAttribute('disabled', 'disabled'); + btnRestart.removeAttribute('disabled'); + btnStop.removeAttribute('disabled'); + btnOpen.removeAttribute('disabled'); + } else { + statusEl.textContent = _('Stopped'); + statusEl.className = 'label important'; + + btnStart.removeAttribute('disabled'); + btnRestart.setAttribute('disabled', 'disabled'); + btnStop.setAttribute('disabled', 'disabled'); + btnOpen.setAttribute('disabled', 'disabled'); + } + }).catch(function () { + statusEl.textContent = _('Error'); + statusEl.className = 'label warning'; + }); + }; + + updateStatus(); + + poll.add(updateStatus, 3); + + return E('div', { 'class': 'cbi-map' }, [ + E('h2', _('FusionTunX Control')), + E('div', { 'class': 'cbi-section' }, [ + E('div', { 'class': 'cbi-value' }, [ + E('label', { 'class': 'cbi-value-title' }, _('Service Status')), + E('div', { 'class': 'cbi-value-field' }, statusEl) + ]), + + E('div', { 'class': 'cbi-section-descr' }, _('Control the FusionTunX backend service.')), + + E('div', { 'class': 'cbi-page-actions' }, [ + btnStart, btnRestart, btnStop, btnOpen + ]) + ]) + ]); + }, + handleSave: null, + handleSaveApply: null, + handleReset: null +}); diff --git a/sendat/Makefile b/sendat/Makefile index 1e7c776b..7dae9d9e 100644 --- a/sendat/Makefile +++ b/sendat/Makefile @@ -1,34 +1,28 @@ -include $(TOPDIR)/rules.mk#将openwrt顶层目录下的rules.mk文件中的内容导入进来 - -PKG_NAME:=sendat#软件包名 -PKG_VERSION:=6.8.2#软件包版本 -PKG_RELEASE:=2 -PKG_BUILD_DIR:= $(BUILD_DIR)/$(PKG_NAME)#真正编译当前软件包的目录 - - -include $(INCLUDE_DIR)/package.mk#将$(TOPDIR)/include目录下的package.mk文件中的内容导入进来 - +include $(TOPDIR)/rules.mk + +PKG_NAME:=sendat +PKG_RELEASE:=3 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/ouyangzq/sendat.git +PKG_SOURCE_DATE:=2024-01-22 +PKG_SOURCE_VERSION:=624c58f6331e5be3eac6d144ed985b4a4af94b10 +PKG_MIRROR_HASH:=skip + +include $(INCLUDE_DIR)/package.mk + define Package/sendat - SECTION:=wrtnode#软件包类型 - CATEGORY:=Daocaoren#menuconfig中软件包所属的一级目录 - SUBMENU :=CPE#menuconfig中软件包所属的二级目录 - TITLE:=Sendat AT#软件包标题 - DEPENDS:=+libpthread#运行本软件依赖的其他包 + SECTION:=net + CATEGORY:=Network + SUBMENU:=WWAN + TITLE:=Send AT commands to serial endef - -define Package/sendat/description #软件包描述 - A sample for sendat test + +MAKE_PATH:=src + +define Package/sendat/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/sendat $(1)/usr/bin/ endef - -define Build/Prepare #编译之前的准备动作 - mkdir -p $(PKG_BUILD_DIR) - $(CP) ./src/* $(PKG_BUILD_DIR)/ -endef - - -define Package/sendat/install #软件包的安装方法,主要就是将一系列编译好的文件、启动脚本、UCI配置文件等拷贝到指定位置 - $(INSTALL_DIR) $(1)/bin - $(INSTALL_BIN) $(PKG_BUILD_DIR)/sendat $(1)/bin/ -endef - -$(eval $(call BuildPackage,sendat)) \ No newline at end of file + +$(eval $(call BuildPackage,sendat)) diff --git a/sendat/README.md b/sendat/README.md deleted file mode 100644 index 00fe05ce..00000000 --- a/sendat/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# sendat -AT 命令工具 -1简化使用AT命令请求方式 -使用命令说明 -sendat 2 'ATI' 2表示 /dev/ttyUSB2 'ATI' 为at命令 diff --git a/sendat/src/Makefile b/sendat/src/Makefile deleted file mode 100644 index 301b189d..00000000 --- a/sendat/src/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -all:sendat - -OBJS = sendat.o - -sendat:$(OBJS) - $(CC) $(CFLAGS) -o $@ $(OBJS) -lpthread - -clean: - rm -f sendat *.o \ No newline at end of file diff --git a/sendat/src/pdu.c b/sendat/src/pdu.c deleted file mode 100644 index df7d9d4b..00000000 --- a/sendat/src/pdu.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * 2017 - 2021 Cezary Jackiewicz - * 2014 lovewilliam - */ -// Copyright 2011 The Avalon Project Authors. All rights reserved. -// Use of this source code is governed by the Apache License 2.0 -// that can be found in the LICENSE file. -// -// SMS encoding/decoding functions, which are based on examples from: -// http://www.dreamfabric.com/sms/ - -#include "pdu.h" - -#include -#include - -enum { - BITMASK_7BITS = 0x7F, - BITMASK_8BITS = 0xFF, - BITMASK_HIGH_4BITS = 0xF0, - BITMASK_LOW_4BITS = 0x0F, - - TYPE_OF_ADDRESS_UNKNOWN = 0x81, - TYPE_OF_ADDRESS_INTERNATIONAL_PHONE = 0x91, - TYPE_OF_ADDRESS_NATIONAL_SUBSCRIBER = 0xC8, - TYPE_OF_ADDRESS_ALPHANUMERIC = 0xD0, - - SMS_DELIVER_ONE_MESSAGE = 0x04, - SMS_SUBMIT = 0x11, - - SMS_MAX_7BIT_TEXT_LENGTH = 160, -}; - -// Swap decimal digits of a number (e.g. 12 -> 21). -static unsigned char -SwapDecimalNibble(const unsigned char x) -{ - return (x / 16) + ((x % 16) * 10); -} - -// Encode/Decode PDU: Translate ASCII 7bit characters to 8bit buffer. -// SMS encoding example from: http://www.dreamfabric.com/sms/. -// -// 7-bit ASCII: "hellohello" -// [0]:h [1]:e [2]:l [3]:l [4]:o [5]:h [6]:e [7]:l [8]:l [9]:o -// 1101000 1100101 1101100 1101100 1101111 1101000 1100101 1101100 1101100 1101111 -// | ||| ||||| | ||||||| |||||| -// /-------------/ ///-------/// /////-///// \------------\ ||||||| \\\\\\ . -// | ||| ||||| | ||||||| |||||| -// input buffer position -// 10000000 22111111 33322222 44443333 55555333 66666655 77777776 98888888 --999999 -// | ||| ||||| | ||||||| |||||| -// 8bit encoded buffer -// 11101000 00110010 10011011 11111101 01000110 10010111 11011001 11101100 00110111 -// E8 32 9B FD 46 97 D9 EC 37 - - -// Encode PDU message by merging 7 bit ASCII characters into 8 bit octets. -int -EncodePDUMessage(const char* sms_text, int sms_text_length, unsigned char* output_buffer, int buffer_size) -{ - // Check if output buffer is big enough. - if ((sms_text_length * 7 + 7) / 8 > buffer_size) - return -1; - - int output_buffer_length = 0; - int carry_on_bits = 1; - int i = 0; - - for (; i < sms_text_length - 1; ++i) { - output_buffer[output_buffer_length++] = - ((sms_text[i] & BITMASK_7BITS) >> (carry_on_bits - 1)) | - ((sms_text[i + 1] & BITMASK_7BITS) << (8 - carry_on_bits)); - carry_on_bits++; - if (carry_on_bits == 8) { - carry_on_bits = 1; - ++i; - } - } - - if (i <= sms_text_length) - output_buffer[output_buffer_length++] = (sms_text[i] & BITMASK_7BITS) >> (carry_on_bits - 1); - - return output_buffer_length; -} - -// Decode PDU message by splitting 8 bit encoded buffer into 7 bit ASCII -// characters. -int -DecodePDUMessage_GSM_7bit(const unsigned char* buffer, int buffer_length, char* output_sms_text, int sms_text_length) -{ - int output_text_length = 0; - if (buffer_length > 0) - output_sms_text[output_text_length++] = BITMASK_7BITS & buffer[0]; - - if (sms_text_length > 1) { - int carry_on_bits = 1; - int i = 1; - for (; i < buffer_length; ++i) { - - output_sms_text[output_text_length++] = BITMASK_7BITS & ((buffer[i] << carry_on_bits) | (buffer[i - 1] >> (8 - carry_on_bits))); - - if (output_text_length == sms_text_length) break; - - carry_on_bits++; - - if (carry_on_bits == 8) { - carry_on_bits = 1; - output_sms_text[output_text_length++] = buffer[i] & BITMASK_7BITS; - if (output_text_length == sms_text_length) break; - } - - } - if (output_text_length < sms_text_length) // Add last remainder. - output_sms_text[output_text_length++] = buffer[i - 1] >> (8 - carry_on_bits); - } - - return output_text_length; -} - -#define GSM_7BITS_ESCAPE 0x1b - -static const unsigned char gsm7bits_to_latin1[128] = { - '@', 0xa3, '$', 0xa5, 0xe8, 0xe9, 0xf9, 0xec, 0xf2, 0xc7, '\n', 0xd8, 0xf8, '\r', 0xc5, 0xe5, - 0, '_', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc6, 0xe6, 0xdf, 0xc9, - ' ', '!', '"', '#', 0xa4, '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', - 0xa1, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0xc4, 0xd6, 0xd1, 0xdc, 0xa7, - 0xbf, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0xe4, 0xf6, 0xf1, 0xfc, 0xe0, -}; - -static const unsigned char gsm7bits_extend_to_latin1[128] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\f', 0, 0, 0, 0, 0, - 0, 0, 0, 0, '^', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, '{', '}', 0, 0, 0, 0, 0,'\\', - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '[', '~', ']', 0, - '|', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -static int -G7bitToAscii(char* buffer, int buffer_length) -{ - int i; - - for (i = 0; i buffer_size) - return -1; - - int i = 0; - for (; i < phone_number_length; ++i) { - - if (phone_number[i] < '0' && phone_number[i] > '9') - return -1; - - if (i % 2 == 0) { - output_buffer[output_buffer_length++] = BITMASK_HIGH_4BITS | (phone_number[i] - '0'); - } else { - output_buffer[output_buffer_length - 1] = - (output_buffer[output_buffer_length - 1] & BITMASK_LOW_4BITS) | - ((phone_number[i] - '0') << 4); - } - } - - return output_buffer_length; -} - -// Decode a digit based phone number for SMS based format. -static int -DecodePhoneNumber(const unsigned char* buffer, int phone_number_length, char* output_phone_number) -{ - int i = 0; - for (; i < phone_number_length; ++i) { - if (i % 2 == 0) - output_phone_number[i] = (buffer[i / 2] & BITMASK_LOW_4BITS) + '0'; - else - output_phone_number[i] = ((buffer[i / 2] & BITMASK_HIGH_4BITS) >> 4) + '0'; - } - output_phone_number[phone_number_length] = '\0'; // Terminate C string. - return phone_number_length; -} - -// Encode a SMS message to PDU -int -pdu_encode(const char* service_center_number, const char* phone_number, const char* sms_text, - unsigned char* output_buffer, int buffer_size) -{ - if (buffer_size < 2) - return -1; - - int output_buffer_length = 0; - - // 1. Set SMS center number. - int length = 0; - if (service_center_number && strlen(service_center_number) > 0) { - output_buffer[1] = TYPE_OF_ADDRESS_INTERNATIONAL_PHONE; - length = EncodePhoneNumber(service_center_number, - output_buffer + 2, buffer_size - 2); - if (length < 0 && length >= 254) - return -1; - length++; // Add type of address. - } - output_buffer[0] = length; - output_buffer_length = length + 1; - if (output_buffer_length + 4 > buffer_size) - return -1; // Check if it has space for four more bytes. - - // 2. Set type of message. - output_buffer[output_buffer_length++] = SMS_SUBMIT; - output_buffer[output_buffer_length++] = 0x00; // Message reference. - - // 3. Set phone number. - output_buffer[output_buffer_length] = strlen(phone_number); - - if (strlen(phone_number) < 6) { - output_buffer[output_buffer_length + 1] = TYPE_OF_ADDRESS_UNKNOWN; - } else { - output_buffer[output_buffer_length + 1] = TYPE_OF_ADDRESS_INTERNATIONAL_PHONE; - } - - length = EncodePhoneNumber(phone_number, - output_buffer + output_buffer_length + 2, - buffer_size - output_buffer_length - 2); - output_buffer_length += length + 2; - if (output_buffer_length + 4 > buffer_size) - return -1; // Check if it has space for four more bytes. - - - // 4. Protocol identifiers. - output_buffer[output_buffer_length++] = 0x00; // TP-PID: Protocol identifier. - output_buffer[output_buffer_length++] = 0x00; // TP-DCS: Data coding scheme. - output_buffer[output_buffer_length++] = 0xB0; // TP-VP: Validity: 10 days - - // 5. SMS message. - int sms_text_length = strlen(sms_text); - char sms_text_7bit[2*SMS_MAX_7BIT_TEXT_LENGTH]; - sms_text_length = AsciiToG7bit(sms_text, sms_text_length, sms_text_7bit); - if (sms_text_length > SMS_MAX_7BIT_TEXT_LENGTH) - return -1; - output_buffer[output_buffer_length++] = sms_text_length; - length = EncodePDUMessage(sms_text_7bit, sms_text_length, - output_buffer + output_buffer_length, - buffer_size - output_buffer_length); - if (length < 0) - return -1; - output_buffer_length += length; - - return output_buffer_length; -} - -int pdu_decode(const unsigned char* buffer, int buffer_length, - time_t* output_sms_time, - char* output_sender_phone_number, int sender_phone_number_size, - char* output_sms_text, int sms_text_size, - int* tp_dcs, - int* ref_number, - int* total_parts, - int* part_number, - int* skip_bytes) -{ - - if (buffer_length <= 0) - return -1; - - const int sms_deliver_start = 1 + buffer[0]; - if (sms_deliver_start + 1 > buffer_length) - return -2; - - const int user_data_header_length = (buffer[sms_deliver_start]>>4); - - const int sender_number_length = buffer[sms_deliver_start + 1]; - if (sender_number_length + 1 > sender_phone_number_size) - return -3; // Buffer too small to hold decoded phone number. - - const int sender_type_of_address = buffer[sms_deliver_start + 2]; - if (sender_type_of_address == TYPE_OF_ADDRESS_ALPHANUMERIC) { - DecodePDUMessage_GSM_7bit(buffer + sms_deliver_start + 3, (sender_number_length + 1) / 2, output_sender_phone_number, sender_number_length); - } else { - DecodePhoneNumber(buffer + sms_deliver_start + 3, sender_number_length, output_sender_phone_number); - } - - const int sms_pid_start = sms_deliver_start + 3 + (buffer[sms_deliver_start + 1] + 1) / 2; - - // Decode timestamp. - struct tm sms_broken_time; - sms_broken_time.tm_year = 100 + SwapDecimalNibble(buffer[sms_pid_start + 2]); - sms_broken_time.tm_mon = SwapDecimalNibble(buffer[sms_pid_start + 3]) - 1; - sms_broken_time.tm_mday = SwapDecimalNibble(buffer[sms_pid_start + 4]); - sms_broken_time.tm_hour = SwapDecimalNibble(buffer[sms_pid_start + 5]); - sms_broken_time.tm_min = SwapDecimalNibble(buffer[sms_pid_start + 6]); - sms_broken_time.tm_sec = SwapDecimalNibble(buffer[sms_pid_start + 7]); - (*output_sms_time) = timegm(&sms_broken_time); - - const int sms_start = sms_pid_start + 2 + 7; - if (sms_start + 1 > buffer_length) return -1; // Invalid input buffer. - - int tmp; - if((user_data_header_length&0x04)==0x04) { - tmp = buffer[sms_start + 1] + 1; - *skip_bytes = tmp; - *ref_number = 0x000000FF&buffer[sms_start + tmp - 2]; - *total_parts = 0x000000FF&buffer[sms_start + tmp - 1]; - *part_number = 0x000000FF&buffer[sms_start + tmp]; - } else { - tmp = 0; - *skip_bytes = tmp; - *ref_number = tmp; - *total_parts = tmp; - *part_number = tmp; - } - - int output_sms_text_length = buffer[sms_start]; - if (sms_text_size < output_sms_text_length) return -1; // Cannot hold decoded buffer. - - const int sms_tp_dcs_start = sms_pid_start + 1; - *tp_dcs = buffer[sms_tp_dcs_start]; - - switch((*tp_dcs / 4) % 4) - { - case 0: - { - // GSM 7 bit - int decoded_sms_text_size = DecodePDUMessage_GSM_7bit(buffer + sms_start + 1, buffer_length - (sms_start + 1), - output_sms_text, output_sms_text_length); - if (decoded_sms_text_size != output_sms_text_length) return -1; // Decoder length is not as expected. - output_sms_text_length = G7bitToAscii(output_sms_text, output_sms_text_length); - break; - } - case 2: - { - // UCS2 - memcpy(output_sms_text, buffer + sms_start + 1, output_sms_text_length); - break; - } - default: - break; - } - - // Add a C string end. - if (output_sms_text_length < sms_text_size) - output_sms_text[output_sms_text_length] = 0; - else - output_sms_text[sms_text_size-1] = 0; - - return output_sms_text_length; -} - diff --git a/sendat/src/pdu.h b/sendat/src/pdu.h deleted file mode 100644 index 734a393b..00000000 --- a/sendat/src/pdu.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 2017 - 2021 Cezary Jackiewicz - * 2014 lovewilliam - */ -// Copyright 2011 The Avalon Project Authors. All rights reserved. -// Use of this source code is governed by the Apache License 2.0 -// that can be found in the LICENSE file. -#ifndef SMS_PDU_H_ -#define SMS_PDU_H_ - -#include - -enum { SMS_MAX_PDU_LENGTH = 256 }; - -/* - * Encode an SMS message. Output the encoded message into output pdu buffer. - * Returns the length of the SMS encoded message in the output buffer or - * a negative number in case encoding failed (for example provided output buffer - * does not have enough space). - */ -int pdu_encode(const char* service_center_number, const char* phone_number, const char* text, - unsigned char* pdu, int pdu_size); - -/* - * Decode an SMS message. Output the decoded message into the sms text buffer. - * Returns the length of the SMS dencoded message or a negative number in - * case encoding failed (for example provided output buffer has not enough - * space). - */ -int pdu_decode(const unsigned char* pdu, int pdu_len, - time_t* sms_time, - char* phone_number, int phone_number_size, - char* text, int text_size, - int* tp_dcs, - int* ref_number, - int* total_parts, - int* part_number, - int* skip_bytes); - -int ucs2_to_utf8 (int ucs2, unsigned char * utf8); - -int DecodePDUMessage_GSM_7bit(const unsigned char* buffer, - int buffer_length, - char* output_sms_text, - int sms_text_length); - -int EncodePDUMessage(const char* sms_text, - int sms_text_length, - unsigned char* output_buffer, - int buffer_size); - -#endif // SMS_SMS_H_ diff --git a/sendat/src/pdu_decoder.c b/sendat/src/pdu_decoder.c deleted file mode 100644 index 9e9e0551..00000000 --- a/sendat/src/pdu_decoder.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 2014 lovewilliam - * SMS PDU Decoder - */ -#include "pdu.h" - -#include -#include -#include -#include -#include - -int ucs2_to_utf8 (int ucs2, unsigned char * utf8); - -int sms_decode() -{ - char buffer[2*SMS_MAX_PDU_LENGTH+4]; - char *p = buffer; - char t[2]; - int d; - do - { - t[0] = getchar(); - if(t[0]=='\n') - { - break; - } - t[1] = getchar(); - if(t[1]=='\n') - { - break; - } - *p = strtol(t,NULL,16); - p++; - }while(1); - - time_t sms_time; - char sms_phone[40]; - char sms_text[161]; - int tp_dcs_type; - int ref_number; - int total_parts; - int part_number; - int skip_bytes; - - int sms_text_length = pdu_decode((const unsigned char*)buffer, - sizeof(buffer), - &sms_time, - sms_phone, sizeof(sms_phone), - sms_text, sizeof(sms_text), - &tp_dcs_type, - &ref_number, - &total_parts, - &part_number, - &skip_bytes); - - printf("From:%s\n",sms_phone); - printf("Textlen=%d\n",sms_text_length); - char time_data_str[64]; - strftime(time_data_str,64,"%D %T", localtime(&sms_time)); - printf("Date/Time:%s\n",time_data_str); - - if (total_parts > 0) { - printf("Reference number: %d\n", ref_number); - printf("SMS segment %d of %d\n", part_number, total_parts); - } - - switch((tp_dcs_type / 4) % 4) - { - case 0: - { - // GSM7 bit - int i = skip_bytes; - if(skip_bytes > 0) i = (skip_bytes*8+6)/7; - for(;i -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// #include "pdu.h" - - -#define MAX_PORTS 4 -/*this array hold information about each port we have opened */ -struct PortInfo { - char name[16]; - int port_fd; -}; -int serial_init(int port, int spd, int databits, int parity, \ - int stopbits, int RTSCTS, int need_line_input); -int serial_write(int fd, void *src, int len); -int serial_read(int fd, char *buf, int len); -int serial_recv(int fd,char *rbuf,int rbuf_len, int timeout); -//串口常用初始化接口 -#define serial_com_init(port, spd, databits, parity, stopbits)\ - serial_init(port, spd, databits, parity, stopbits, 0, 0) -//串口默认初始化接口 -#define serial_def_init(port, spd) serial_init(port, spd, 8, 'n', 1, 0, 1) -#endif - - - -/* -** File: uart.c -** -** Description: -** Provides an RS-232 interface that is very similar to the CVI provided -** interface library -*/ -/*this array hold information about each port we have opened */ -struct PortInfo ports[13] = -{ - {"/dev/ttyUSB0", 0}, - {"/dev/ttyUSB1", 0}, - {"/dev/ttyUSB2", 0}, - {"/dev/ttyUSB3", 0}, - {"/dev/ttyUSB4", 0}, - {"/dev/ttyUSB5", 0}, - {"/dev/ttyUSB6", 0}, - {"/dev/ttyUSB7", 0}, - {"/dev/ttyUSB8", 0}, - {"/dev/ttyUSB9", 0}, - {"/dev/ttyUSB10", 0}, - {"/dev/ttyUSB11", 0}, - {"/dev/ttyUSB12", 0}, -}; -FILE* pf; -FILE* pfi; -int spd_arr[] = {B2000000, B1500000, B576000, B500000, B460800, B230400, B115200, B57600, B38400, B19200, B9600, B4800, B2400}; -int name_arr[] = { 2000000, 1500000, 576000, 500000, 460800, 230400, 115200, 57600, 38400, 19200, 9600, 4800, 2400 }; -/** -*@brief 设置串口通信速率 -*@param fd 类型 int 打开串口的文件句柄 -*@param speed 类型 int 串口速度 -*@return void -*/ -int set_speed(int fd, int speed) -{ - int i; - int status; - struct termios Opt; - tcgetattr(fd, &Opt); - for ( i= 0; i < sizeof(spd_arr) / sizeof(int); i++) { - if(speed == name_arr[i]) { - tcflush(fd, TCIOFLUSH); - cfsetispeed(&Opt, spd_arr[i]); - cfsetospeed(&Opt, spd_arr[i]); - status = tcsetattr(fd, TCSANOW, &Opt); - if (status != 0) { - printf("tcsetattr failed"); - return 1; - } - tcflush(fd,TCIOFLUSH); - } - } - // printf("set_speed\n"); - return 0; -} - -/** -*@brief 设置串口数据位,停止位和效验位 -*@param fd 类型 int 打开的串口文件句柄 -*@param databits 类型 int 数据位 取值为 7 或者8 -*@param stopbits 类型 int 停止位 取值为 1 或者2 -*@param parity 类型 int 效验类型 取值为N,E,O,,S -*/ -int set_Parity(int fd, int databits, int parity, int stopbits, int RTSCTS) -{ - struct termios options; - if ( tcgetattr( fd,&options) != 0) { - perror("SetupSerial 1"); - return -1; - } - options.c_cflag &= ~CSIZE; - switch (databits) /*设置数据位数*/ - { - case 7: - options.c_cflag |= CS7; - break; - case 8: - options.c_cflag |= CS8; - break; - default: - fprintf(stderr,"Unsupported data size\n"); - return -1; - } - - options.c_iflag |= INPCK; - cfmakeraw(&options); - //options.c_lflag |= (ICANON | ECHO | ECHOE); - //options.c_lflag &= ~(ICANON | ECHO | ECHOE); - //options.c_iflag &= ~(IXON | IXOFF); - switch (parity) - { - case 'n': - case 'N': - options.c_cflag &= ~PARENB; /* Clear parity enable */ - options.c_iflag &= ~INPCK; /* Enable parity checking */ - break; - case 'o': - case 'O': - options.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/ - break; - case 'e': - case 'E': - options.c_cflag |= PARENB; /* Enable parity */ - options.c_cflag &= ~PARODD; /* 转换为偶效验*/ - break; - case 'S': - case 's': /*as no parity*/ - options.c_cflag &= ~PARENB; - options.c_cflag &= ~CSTOPB; - break; - default: - fprintf(stderr,"Unsupported parity\n"); - return -1; - } - - /* 设置停止位*/ - switch (stopbits) - { - case 1: - options.c_cflag &= ~CSTOPB; - break; - case 2: - options.c_cflag |= CSTOPB; - break; - default: - fprintf(stderr,"Unsupported stop bits\n"); - return -1; - } - - /* Set rts/cts */ - if (RTSCTS) - { - printf("Set rts/cts"); - options.c_cflag |= CRTSCTS; - } - - tcflush(fd,TCIFLUSH); - options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/ - options.c_cc[VMIN] = 0; /* Update the options and do it NOW */ - if (tcsetattr(fd,TCSANOW,&options) != 0) - { - printf("SetupSerial failed"); - return -1; - } - // printf("set_Parity\n"); - return 0; -} - -//接收消息结尾加换行符 -int serial_set_line_input(int fd) -{ - struct termios options; - if ( tcgetattr( fd,&options) != 0) { - perror("SetupSerial 1"); - return -1; - } - - options.c_lflag |= ICANON; - - tcflush(fd,TCIFLUSH); - options.c_cc[VTIME] = 150; /* 设置超时15 seconds*/ - options.c_cc[VMIN] = 0; /* Update the options and do it NOW */ - if (tcsetattr(fd,TCSANOW,&options) != 0) - { - perror("SetupSerial 3"); - return -1; - } - return 0; -} - -/** -*@brief -*@param port 串口端口号 -*@param spd 串口速度 -*@param databits,parity,stopbits,RTSCTS,分别为数据位,校验位,停止位,rtscts位 -*@param need_line_input接收数据结尾是否加换行符? -*/ -int serial_init(int port, int spd, int databits, int parity, int stopbits, int RTSCTS, int need_line_input) -{ - int fd; - if(port < 13) - { - // printf("open port:%d\n", port); - } - else { - printf("error: port:%d > MAX_PORTS\n", port); - return -1; - } - // - fd = open(ports[port].name, O_RDWR|O_NOCTTY);//O_NONBLOCK 非阻塞, O_WRONLY 只读写, O_RDONLY 只读, O_RDWR 读写,O_NOCTTY 阻塞 - - if (-1 == fd) { - printf("init %s failed\n", ports[port].name); - return -1; - } - pf = fdopen(port, "w"); - pfi = fdopen(port, "r"); - if (!pf || ! pfi) - fprintf(stderr,"open port failed\n"); - - - - set_speed(fd, spd); - - set_Parity(fd, databits, parity, stopbits, RTSCTS); - - if (need_line_input) { - serial_set_line_input(fd); - } - - ports[port].port_fd = fd; - - return fd; -} - -/** -*@brief -*@param fd 串口端口号文件描述符 -*@param src 需要通过串口发送的数据 -*@param len 需要发送的数据长度 -*@param 成功返回0, 否则返回-1 -*/ -int serial_write(int fd, void *src, int len) -{ - int ret = write(fd, src, len); - if (len != ret) { - perror("oh, write serial failed!"); - return -1; - } - return 0; -} - -/** -*@brief -*@param fd 串口端口号文件描述符 -*@param src 串口接收数据的指针 -*@param len 需要接收的数据长度 -*@param 成功返回0, 否则返回-1 -*/ -int serial_read(int fd, char *buf, int len) -{ - int ret = read(fd, buf, len-1); - if (-1 == ret) { - perror("oh, read serial failed!"); - return -1; - } - buf[ret] = '\0'; - return ret; -} - -int serial_recv(int fd,char *rbuf,int rbuf_len, int timeout) -{ - int retval; - fd_set rset; - struct timeval time_out; - if(!rbuf || rbuf_len <= 0) - { - // printf("serial_recv Invalid parameter.\n"); - return -1; - } - if(timeout) //指定延时等待 - { - time_out.tv_sec = (time_t)(timeout / 1000); - time_out.tv_usec = 0; - FD_ZERO(&rset); - FD_SET(fd,&rset); - - retval = select(fd,&rset,NULL,NULL,&time_out); - if(retval < 0) - { - // printf("%s,Select failed:%s\n",strerror(errno)); - return -2; - } - else if(0 == retval) - { - // printf("Time Out.\n"); - return 0; - } - - } - // usleep(1000); - retval = read(fd, rbuf, rbuf_len); - if( retval <= 0) - { - // printf("Read failed:%s\n",strerror(errno)); - return -3; - } - return retval; - -} - - -static void timeout() -{ - fprintf(stderr,"No response from modem.\n"); - exit(2); -} -/*字符包含判断*/ -static int starts_with(const char* prefix, const char* str) -{ - while(*prefix) - { - if (*prefix++ != *str++) - { - return 0; - } - } - return 1; -} -/*判断是否存在*/ -int FileExist(const char* filename) -{ - if (filename && access(filename, F_OK) == 0) { - return 1; - } - return 0; -} -/*字符转小写*/ -char* str_tolower(const char* str) -{ - size_t len = strlen(str); - char *lower = calloc(len+1, sizeof(char)); - for (size_t i = 0; i < len; ++i) { - lower[i] = tolower((unsigned char)str[i]); - } - /*free(upper);*/ - return lower; -} -/*字符转大写*/ -char* str_toupper(const char* str) -{ - size_t len = strlen(str); - char *upper = calloc(len+1, sizeof(char)); - for (size_t i = 0; i < len; ++i) { - upper[i] = toupper((unsigned char)str[i]); - } - /*free(upper);*/ - return upper; -} -/*char*转char[]*/ -char strx_tostrarr(const char* str) -{ - return 0; -} - - -int main(int argc, char **argv) -{ - if(argc<3) - { - printf("ERROR demo: sendat 2 'ATI'\n"); - exit(1); - return 0; - } - int debug = 1; - int port= 0; - sscanf(argv[1], "%d", &port); - if(FileExist(ports[port].name)==0) - { - printf("AT ERROR absent.\n"); - return 0; - } - char *message= argv[2]; - char *nty= "\r\n"; - char buff[1024]; - signal(SIGALRM,timeout); - alarm(2); - /*信号超时3秒自动退出*/ - int fd = serial_def_init(port, 1500000); - if(fd < 0) return 0; - char *send= strcat(message,nty); - serial_write(fd,send, strlen(send)); - while(1) { - int read = serial_read(fd, buff, sizeof(buff)); - if(starts_with("OK", buff)) { - if (debug == 1) - printf("%s", buff); - close(fd);//关闭串口 - exit(0); - } - if(starts_with("ERROR", buff)) { - if (debug == 1) - printf("%s", buff); - close(fd);//关闭串口 - exit(1); - } - if(starts_with("COMMAND NOT SUPPORT", buff)) { - if (debug == 1) - printf("%s", buff); - close(fd);//关闭串口 - exit(1); - } - if(starts_with("+CME ERROR", buff)) { - if (debug == 1) - printf("%s", buff); - close(fd);//关闭串口 - exit(1); - } - printf("%s", buff); - } - - exit(1); - return 0; -} diff --git a/sendat/src/ucs2_to_utf8.c b/sendat/src/ucs2_to_utf8.c deleted file mode 100644 index 5baa433b..00000000 --- a/sendat/src/ucs2_to_utf8.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 2014 lovewilliam - * from http://www.lemoda.net/c/ucs2-to-utf8/ucs2-to-utf8.c - */ -/* Input: a Unicode code point, "ucs2". - - Output: UTF-8 characters in buffer "utf8". - - Return value: the number of bytes written into "utf8", or -1 if - there was an error. - - This adds a zero byte to the end of the string. It assumes that the - buffer "utf8" has at least four bytes of space to write to. */ - -#define UNICODE_SURROGATE_PAIR -2 -#define UNICODE_BAD_INPUT -1 - -int ucs2_to_utf8 (int ucs2, unsigned char * utf8) -{ - if (ucs2 < 0x80) { - utf8[0] = ucs2; - utf8[1] = '\0'; - return 1; - } - if (ucs2 >= 0x80 && ucs2 < 0x800) { - utf8[0] = (ucs2 >> 6) | 0xC0; - utf8[1] = (ucs2 & 0x3F) | 0x80; - utf8[2] = '\0'; - return 2; - } - if (ucs2 >= 0x800 && ucs2 < 0xFFFF) { - if (ucs2 >= 0xD800 && ucs2 <= 0xDFFF) { - /* Ill-formed. */ - return UNICODE_SURROGATE_PAIR; - } - utf8[0] = ((ucs2 >> 12) ) | 0xE0; - utf8[1] = ((ucs2 >> 6 ) & 0x3F) | 0x80; - utf8[2] = ((ucs2 ) & 0x3F) | 0x80; - utf8[3] = '\0'; - return 3; - } - if (ucs2 >= 0x10000 && ucs2 < 0x10FFFF) { - /* http://tidy.sourceforge.net/cgi-bin/lxr/source/src/utf8.c#L380 */ - utf8[0] = 0xF0 | (ucs2 >> 18); - utf8[1] = 0x80 | ((ucs2 >> 12) & 0x3F); - utf8[2] = 0x80 | ((ucs2 >> 6) & 0x3F); - utf8[3] = 0x80 | ((ucs2 & 0x3F)); - utf8[4] = '\0'; - return 4; - } - return UNICODE_BAD_INPUT; -} -