commit 833220753c897b1b4647c01aad949397dbe74f69 Author: zhao Date: Sun Jun 1 01:03:40 2025 +0800 initial commit diff --git a/luci-app-oaf/Makefile b/luci-app-oaf/Makefile new file mode 100755 index 0000000..68f600f --- /dev/null +++ b/luci-app-oaf/Makefile @@ -0,0 +1,15 @@ + +# +# This is free software, licensed under the Apache License, Version 2.0 . +# + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=Open App Filter Module +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+appfilter +kmod-oaf +luci-compat +PKG_NAME:=luci-app-oaf +PKG_VERSION:=6.1.1 +PKG_RELEASE:=1 +include $(TOPDIR)/feeds/luci/luci.mk +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10001.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10001.png new file mode 100755 index 0000000..ab5820e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10001.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10002.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10002.png new file mode 100755 index 0000000..fbc17f0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10002.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10003.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10003.png new file mode 100755 index 0000000..81f76f4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10003.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10004.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10004.png new file mode 100755 index 0000000..e3447dd Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10004.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10005.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10005.png new file mode 100755 index 0000000..b208cea Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10005.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10006.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10006.png new file mode 100755 index 0000000..e9e9c4e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10006.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10007.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10007.png new file mode 100755 index 0000000..9d8923b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10007.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10008.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10008.png new file mode 100755 index 0000000..a37cc9b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10008.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1001.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1001.png new file mode 100755 index 0000000..a01fbff Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1001.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10010.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10010.png new file mode 100755 index 0000000..dd25164 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10010.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10011.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10011.png new file mode 100755 index 0000000..6bbdc0e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10011.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10012.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10012.png new file mode 100755 index 0000000..23046e6 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10012.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10013.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10013.png new file mode 100755 index 0000000..cbdc7d0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10013.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10014.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10014.png new file mode 100755 index 0000000..27541b5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10014.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10015.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10015.png new file mode 100755 index 0000000..ebefaf5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10015.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10016.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10016.png new file mode 100755 index 0000000..69a2c96 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10016.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10017.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10017.png new file mode 100755 index 0000000..8cb17a1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10017.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10018.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10018.png new file mode 100755 index 0000000..41a1d52 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10018.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10019.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10019.png new file mode 100755 index 0000000..37a9321 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10019.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1002.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1002.png new file mode 100755 index 0000000..eb3716a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1002.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10020.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10020.png new file mode 100755 index 0000000..5dce396 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10020.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10021.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10021.png new file mode 100755 index 0000000..d4c79dc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10021.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10022.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10022.png new file mode 100755 index 0000000..6bc4e32 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10022.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10023.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10023.png new file mode 100755 index 0000000..97b98d1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10023.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10024.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10024.png new file mode 100755 index 0000000..6e56f2d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10024.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10025.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10025.png new file mode 100755 index 0000000..ac3e1a4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10025.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10026.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10026.png new file mode 100755 index 0000000..b8e5717 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10026.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10027.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10027.png new file mode 100755 index 0000000..c4e451d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10027.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10028.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10028.png new file mode 100755 index 0000000..ad9a1b1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10028.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10029.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10029.png new file mode 100755 index 0000000..492d38c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10029.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1003.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1003.png new file mode 100755 index 0000000..ced0321 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1003.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10030.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10030.png new file mode 100755 index 0000000..a035e2e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10030.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10031.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10031.png new file mode 100755 index 0000000..3a8a3dc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10031.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10032.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10032.png new file mode 100755 index 0000000..2bae1f5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10032.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10033.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10033.png new file mode 100755 index 0000000..24dea10 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10033.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10034.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10034.png new file mode 100755 index 0000000..4825ca4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10034.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10035.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10035.png new file mode 100755 index 0000000..07e56e1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10035.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10036.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10036.png new file mode 100755 index 0000000..c256076 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10036.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10037.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10037.png new file mode 100755 index 0000000..49b20fb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10037.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10038.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10038.png new file mode 100755 index 0000000..bd3e65e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10038.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10039.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10039.png new file mode 100755 index 0000000..f278b98 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10039.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1004.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1004.png new file mode 100755 index 0000000..6d73e95 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1004.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10040.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10040.png new file mode 100755 index 0000000..f503fdb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10040.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1005.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1005.png new file mode 100755 index 0000000..75e702e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1005.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1006.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1006.png new file mode 100755 index 0000000..83430bd Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1006.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1007.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1007.png new file mode 100755 index 0000000..6ff6bbc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1007.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1008.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1008.png new file mode 100755 index 0000000..7728330 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1008.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1009.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1009.png new file mode 100755 index 0000000..9b6c8e5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1009.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/10099.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10099.png new file mode 100755 index 0000000..3ccf82f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/10099.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1010.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1010.png new file mode 100755 index 0000000..0e4e232 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1010.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1011.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1011.png new file mode 100755 index 0000000..56ccfb8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1011.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1012.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1012.png new file mode 100755 index 0000000..526b206 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1012.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1013.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1013.png new file mode 100755 index 0000000..8b30ba1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1013.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1014.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1014.png new file mode 100755 index 0000000..9b06859 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1014.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1016.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1016.png new file mode 100755 index 0000000..ab5820e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1016.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1018.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1018.png new file mode 100755 index 0000000..fbc17f0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1018.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/1019.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1019.png new file mode 100755 index 0000000..7bdfb40 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/1019.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/11001.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/11001.png new file mode 100755 index 0000000..74060db Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/11001.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/11002.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/11002.png new file mode 100755 index 0000000..a8d35fd Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/11002.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/11003.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/11003.png new file mode 100755 index 0000000..090bb5f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/11003.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14001.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14001.png new file mode 100755 index 0000000..7f8e532 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14001.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14002.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14002.png new file mode 100755 index 0000000..67a0424 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14002.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14003.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14003.png new file mode 100755 index 0000000..6a790fe Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14003.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14004.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14004.png new file mode 100755 index 0000000..4c374fa Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14004.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14005.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14005.png new file mode 100755 index 0000000..f92f89f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14005.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14006.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14006.png new file mode 100755 index 0000000..7cb1b8d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14006.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14007.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14007.png new file mode 100755 index 0000000..b9056dc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14007.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14008.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14008.png new file mode 100755 index 0000000..7a3b67e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14008.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14009.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14009.png new file mode 100755 index 0000000..0e6cd8e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14009.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14010.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14010.png new file mode 100755 index 0000000..8b54042 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14010.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14011.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14011.png new file mode 100755 index 0000000..b61b31e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14011.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14012.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14012.png new file mode 100755 index 0000000..64f342b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14012.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14013.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14013.png new file mode 100755 index 0000000..29d301b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14013.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14014.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14014.png new file mode 100755 index 0000000..c283c48 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14014.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14015.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14015.png new file mode 100755 index 0000000..d127d05 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14015.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14016.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14016.png new file mode 100755 index 0000000..4b47576 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14016.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14017.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14017.png new file mode 100755 index 0000000..6bc4dce Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14017.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14018.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14018.png new file mode 100755 index 0000000..aacac0c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14018.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14019.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14019.png new file mode 100755 index 0000000..5e3c496 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14019.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14020.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14020.png new file mode 100755 index 0000000..0931546 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14020.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14021.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14021.png new file mode 100755 index 0000000..b0bba8a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14021.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14022.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14022.png new file mode 100755 index 0000000..f96c1b5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14022.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14023.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14023.png new file mode 100755 index 0000000..40251b5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14023.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14024.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14024.png new file mode 100755 index 0000000..7f6f95d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14024.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14025.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14025.png new file mode 100755 index 0000000..7380450 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14025.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14026.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14026.png new file mode 100755 index 0000000..3e0a0f9 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14026.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/14027.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14027.png new file mode 100755 index 0000000..783a46c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/14027.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2001.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2001.png new file mode 100755 index 0000000..5aedcef Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2001.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2002.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2002.png new file mode 100755 index 0000000..33a34dd Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2002.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2003.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2003.png new file mode 100755 index 0000000..1723dcd Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2003.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2004.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2004.png new file mode 100755 index 0000000..be1225a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2004.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2005.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2005.png new file mode 100755 index 0000000..1aed806 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2005.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2006.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2006.png new file mode 100755 index 0000000..fa6ebaa Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2006.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2007.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2007.png new file mode 100755 index 0000000..924a5a2 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2007.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2008.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2008.png new file mode 100755 index 0000000..d5e1fd4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2008.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2009.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2009.png new file mode 100755 index 0000000..11da662 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2009.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2010.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2010.png new file mode 100755 index 0000000..f86222a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2010.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2011.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2011.png new file mode 100755 index 0000000..2ea2bd4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2011.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2012.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2012.png new file mode 100755 index 0000000..dc012b1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2012.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2013.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2013.png new file mode 100755 index 0000000..8526f28 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2013.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2014.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2014.png new file mode 100755 index 0000000..0f2a8ed Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2014.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2015.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2015.png new file mode 100755 index 0000000..97a9c5c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2015.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2016.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2016.png new file mode 100755 index 0000000..5b3e18d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2016.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2017.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2017.png new file mode 100755 index 0000000..d4db13f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2017.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2018.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2018.png new file mode 100755 index 0000000..24302ee Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2018.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2019.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2019.png new file mode 100755 index 0000000..6780c7f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2019.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2020.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2020.png new file mode 100755 index 0000000..4d35ebc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2020.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2021.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2021.png new file mode 100755 index 0000000..d6a35f0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2021.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2022.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2022.png new file mode 100755 index 0000000..c9c6ba9 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2022.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2023.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2023.png new file mode 100755 index 0000000..14626ce Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2023.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2025.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2025.png new file mode 100755 index 0000000..5d8b5f8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2025.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2026.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2026.png new file mode 100755 index 0000000..e702743 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2026.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2027.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2027.png new file mode 100755 index 0000000..7e907bb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2027.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2028.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2028.png new file mode 100755 index 0000000..9a92371 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2028.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2030.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2030.png new file mode 100755 index 0000000..8f046de Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2030.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2031.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2031.png new file mode 100755 index 0000000..b86a6cf Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2031.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2032.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2032.png new file mode 100755 index 0000000..24932cc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2032.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2033.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2033.png new file mode 100755 index 0000000..a419e2b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2033.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2034.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2034.png new file mode 100755 index 0000000..7863637 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2034.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2035.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2035.png new file mode 100755 index 0000000..dc2dbcb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2035.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2036.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2036.png new file mode 100755 index 0000000..84d7f00 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2036.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2037.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2037.png new file mode 100755 index 0000000..1a87aed Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2037.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2039.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2039.png new file mode 100755 index 0000000..de42991 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2039.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2040.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2040.png new file mode 100755 index 0000000..59dc8fb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2040.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2041.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2041.png new file mode 100755 index 0000000..9eeb0bc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2041.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2042.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2042.png new file mode 100755 index 0000000..a102313 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2042.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2043.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2043.png new file mode 100755 index 0000000..8e352d8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2043.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2050.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2050.png new file mode 100755 index 0000000..e75d5ad Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2050.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2051.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2051.png new file mode 100755 index 0000000..1ce8fbd Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2051.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2052.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2052.png new file mode 100755 index 0000000..a8ff760 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2052.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2053.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2053.png new file mode 100755 index 0000000..9e5cebc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2053.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2054.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2054.png new file mode 100755 index 0000000..3b57316 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2054.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2055.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2055.png new file mode 100755 index 0000000..f0de269 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2055.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2056.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2056.png new file mode 100755 index 0000000..e105fea Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2056.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2057.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2057.png new file mode 100755 index 0000000..4cb2174 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2057.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2058.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2058.png new file mode 100755 index 0000000..614bf3f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2058.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2059.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2059.png new file mode 100755 index 0000000..64582e7 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2059.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2060.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2060.png new file mode 100755 index 0000000..985f335 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2060.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2061.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2061.png new file mode 100755 index 0000000..16d4d85 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2061.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2067.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2067.png new file mode 100755 index 0000000..5a9647e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2067.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2068.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2068.png new file mode 100755 index 0000000..3e3ed9e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2068.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2069.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2069.png new file mode 100755 index 0000000..1ab3593 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2069.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2070.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2070.png new file mode 100755 index 0000000..96cb6fe Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2070.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2071.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2071.png new file mode 100755 index 0000000..419765f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2071.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2072.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2072.png new file mode 100755 index 0000000..1775754 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2072.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2073.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2073.png new file mode 100755 index 0000000..edbebc1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2073.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2074.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2074.png new file mode 100755 index 0000000..479549a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2074.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2075.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2075.png new file mode 100755 index 0000000..c73c4d8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2075.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2076.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2076.png new file mode 100755 index 0000000..b2df1e1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2076.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2077.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2077.png new file mode 100755 index 0000000..64ea2d3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2077.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2078.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2078.png new file mode 100755 index 0000000..b2df1e1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2078.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2079.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2079.png new file mode 100755 index 0000000..e5edcef Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2079.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2080.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2080.png new file mode 100755 index 0000000..d5b4536 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2080.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2081.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2081.png new file mode 100755 index 0000000..e77f73a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2081.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2082.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2082.png new file mode 100755 index 0000000..5443e13 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2082.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2083.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2083.png new file mode 100755 index 0000000..b85fb38 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2083.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2084.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2084.png new file mode 100755 index 0000000..b880031 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2084.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2085.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2085.png new file mode 100755 index 0000000..ee01aad Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2085.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2087.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2087.png new file mode 100755 index 0000000..a465445 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2087.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2088.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2088.png new file mode 100755 index 0000000..e931941 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2088.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2089.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2089.png new file mode 100755 index 0000000..2f2e592 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2089.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2093.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2093.png new file mode 100755 index 0000000..168db66 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2093.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2098.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2098.png new file mode 100755 index 0000000..b3babfe Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2098.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2099.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2099.png new file mode 100755 index 0000000..efef0a7 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2099.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2100.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2100.png new file mode 100755 index 0000000..2123e7b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2100.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2101.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2101.png new file mode 100755 index 0000000..479549a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2101.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2102.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2102.png new file mode 100755 index 0000000..241101f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2102.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2103.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2103.png new file mode 100755 index 0000000..7e112f1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2103.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2104.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2104.png new file mode 100755 index 0000000..aaf93f1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2104.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2105.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2105.png new file mode 100755 index 0000000..4b2aad2 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2105.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2106.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2106.png new file mode 100755 index 0000000..5af8ee9 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2106.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2107.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2107.png new file mode 100755 index 0000000..1405aa0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2107.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2108.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2108.png new file mode 100755 index 0000000..9927ea5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2108.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2109.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2109.png new file mode 100755 index 0000000..f31dc61 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2109.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2110.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2110.png new file mode 100755 index 0000000..04144a1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2110.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2111.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2111.png new file mode 100755 index 0000000..14626ce Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2111.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2112.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2112.png new file mode 100755 index 0000000..515767a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2112.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2113.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2113.png new file mode 100755 index 0000000..f0590bd Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2113.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2115.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2115.png new file mode 100755 index 0000000..169dbfc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2115.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/2116.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2116.png new file mode 100755 index 0000000..824f95e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/2116.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3001.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3001.png new file mode 100755 index 0000000..bc5c054 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3001.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3002.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3002.png new file mode 100755 index 0000000..ea76d6f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3002.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3003.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3003.png new file mode 100755 index 0000000..1de75fc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3003.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3004.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3004.png new file mode 100755 index 0000000..2a87819 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3004.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3005.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3005.png new file mode 100755 index 0000000..8b8b788 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3005.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3006.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3006.png new file mode 100755 index 0000000..5b0eb2b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3006.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3008.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3008.png new file mode 100755 index 0000000..d546b75 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3008.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3009.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3009.png new file mode 100755 index 0000000..94b0928 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3009.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3010.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3010.png new file mode 100755 index 0000000..a1d1705 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3010.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3011.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3011.png new file mode 100755 index 0000000..8c0f1b7 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3011.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3012.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3012.png new file mode 100755 index 0000000..bf399d4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3012.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3014.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3014.png new file mode 100755 index 0000000..2c54515 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3014.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3016.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3016.png new file mode 100755 index 0000000..7ffb263 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3016.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3017.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3017.png new file mode 100755 index 0000000..d7888ba Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3017.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3018.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3018.png new file mode 100755 index 0000000..9f216f0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3018.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3019.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3019.png new file mode 100755 index 0000000..523ec84 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3019.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3020.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3020.png new file mode 100755 index 0000000..3e67dff Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3020.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3021.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3021.png new file mode 100755 index 0000000..e1bb464 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3021.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3022.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3022.png new file mode 100755 index 0000000..47d311e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3022.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3023.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3023.png new file mode 100755 index 0000000..e126261 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3023.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3024.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3024.png new file mode 100755 index 0000000..15791e6 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3024.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3025.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3025.png new file mode 100755 index 0000000..6c35b89 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3025.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3026.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3026.png new file mode 100755 index 0000000..a0f2559 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3026.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3027.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3027.png new file mode 100755 index 0000000..92f9876 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3027.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3028.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3028.png new file mode 100755 index 0000000..1ef08a8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3028.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3029.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3029.png new file mode 100755 index 0000000..630e776 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3029.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3030.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3030.png new file mode 100755 index 0000000..630e776 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3030.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3031.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3031.png new file mode 100755 index 0000000..f91fe46 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3031.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3032.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3032.png new file mode 100755 index 0000000..c20f18a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3032.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3033.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3033.png new file mode 100755 index 0000000..6f7bc91 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3033.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3034.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3034.png new file mode 100755 index 0000000..d600be1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3034.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3035.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3035.png new file mode 100755 index 0000000..707d70a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3035.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3038.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3038.png new file mode 100755 index 0000000..82fd22f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3038.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3039.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3039.png new file mode 100755 index 0000000..07e4f88 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3039.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3042.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3042.png new file mode 100755 index 0000000..70d560f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3042.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3043.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3043.png new file mode 100755 index 0000000..a8aed22 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3043.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3044.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3044.png new file mode 100755 index 0000000..f83c6b3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3044.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3045.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3045.png new file mode 100755 index 0000000..d933e0a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3045.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3046.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3046.png new file mode 100755 index 0000000..8789658 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3046.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3047.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3047.png new file mode 100755 index 0000000..bd21879 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3047.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3048.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3048.png new file mode 100755 index 0000000..f87b833 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3048.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3049.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3049.png new file mode 100755 index 0000000..8647fba Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3049.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3050.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3050.png new file mode 100755 index 0000000..019c4e9 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3050.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3051.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3051.png new file mode 100755 index 0000000..e49cb98 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3051.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3052.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3052.png new file mode 100755 index 0000000..c8c87c6 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3052.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3053.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3053.png new file mode 100755 index 0000000..a4787d4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3053.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3054.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3054.png new file mode 100755 index 0000000..d1bf665 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3054.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3055.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3055.png new file mode 100755 index 0000000..f9313f0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3055.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3056.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3056.png new file mode 100755 index 0000000..ba8ec8d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3056.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3057.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3057.png new file mode 100755 index 0000000..dd40ca0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3057.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3058.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3058.png new file mode 100755 index 0000000..5b3ada8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3058.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3059.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3059.png new file mode 100755 index 0000000..cf72fcc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3059.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3060.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3060.png new file mode 100755 index 0000000..5881e92 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3060.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3061.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3061.png new file mode 100755 index 0000000..9ad8602 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3061.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3062.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3062.png new file mode 100755 index 0000000..5e629a8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3062.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3063.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3063.png new file mode 100755 index 0000000..dfa00cb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3063.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3064.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3064.png new file mode 100755 index 0000000..4ef9626 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3064.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3065.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3065.png new file mode 100755 index 0000000..25283a1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3065.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3066.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3066.png new file mode 100755 index 0000000..13047b0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3066.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3067.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3067.png new file mode 100755 index 0000000..dcb7d8d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3067.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3068.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3068.png new file mode 100755 index 0000000..3c7d73e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3068.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3069.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3069.png new file mode 100755 index 0000000..8cb56a3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3069.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3070.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3070.png new file mode 100755 index 0000000..2a87819 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3070.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3071.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3071.png new file mode 100755 index 0000000..6c71290 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3071.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3072.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3072.png new file mode 100755 index 0000000..3f1220b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3072.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3073.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3073.png new file mode 100755 index 0000000..01b27b6 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3073.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3074.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3074.png new file mode 100755 index 0000000..25312a2 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3074.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3075.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3075.png new file mode 100755 index 0000000..691a790 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3075.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3077.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3077.png new file mode 100755 index 0000000..52932cf Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3077.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3078.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3078.png new file mode 100755 index 0000000..ac075cc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3078.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3079.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3079.png new file mode 100755 index 0000000..cfad8e7 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3079.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3081.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3081.png new file mode 100755 index 0000000..4337bd7 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3081.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3082.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3082.png new file mode 100755 index 0000000..a98e4af Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3082.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3083.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3083.png new file mode 100755 index 0000000..c4768b5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3083.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3084.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3084.png new file mode 100755 index 0000000..694a38f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3084.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3085.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3085.png new file mode 100755 index 0000000..b2df1e1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3085.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3086.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3086.png new file mode 100755 index 0000000..2acf4a4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3086.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3087.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3087.png new file mode 100755 index 0000000..44a906b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3087.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3088.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3088.png new file mode 100755 index 0000000..6d11919 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3088.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3089.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3089.png new file mode 100755 index 0000000..5db0102 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3089.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3090.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3090.png new file mode 100755 index 0000000..84944e7 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3090.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3091.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3091.png new file mode 100755 index 0000000..8e00d90 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3091.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3092.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3092.png new file mode 100755 index 0000000..e5edcef Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3092.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3093.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3093.png new file mode 100755 index 0000000..c54f3e3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3093.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3094.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3094.png new file mode 100755 index 0000000..f7799c9 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3094.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3095.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3095.png new file mode 100755 index 0000000..c158c78 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3095.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3096.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3096.png new file mode 100755 index 0000000..42672eb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3096.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3097.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3097.png new file mode 100755 index 0000000..0aba1be Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3097.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3100.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3100.png new file mode 100755 index 0000000..63c86c3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3100.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3101.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3101.png new file mode 100755 index 0000000..26e95d3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3101.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3102.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3102.png new file mode 100755 index 0000000..0897fef Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3102.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3103.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3103.png new file mode 100755 index 0000000..68dd034 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3103.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3104.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3104.png new file mode 100755 index 0000000..5c95fc1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3104.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3105.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3105.png new file mode 100755 index 0000000..44ea249 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3105.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3106.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3106.png new file mode 100755 index 0000000..fed3d0d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3106.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3107.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3107.png new file mode 100755 index 0000000..dfd7d3a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3107.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3108.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3108.png new file mode 100755 index 0000000..b40e7dd Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3108.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3109.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3109.png new file mode 100755 index 0000000..9a29ae1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3109.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3110.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3110.png new file mode 100755 index 0000000..2c28ac7 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3110.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3111.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3111.png new file mode 100755 index 0000000..f58c82e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3111.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3112.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3112.png new file mode 100755 index 0000000..e1b475f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3112.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3113.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3113.png new file mode 100755 index 0000000..4af981d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3113.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3114.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3114.png new file mode 100755 index 0000000..29ae613 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3114.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3115.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3115.png new file mode 100755 index 0000000..80079fc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3115.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3116.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3116.png new file mode 100755 index 0000000..179f7b0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3116.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3117.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3117.png new file mode 100755 index 0000000..582c16c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3117.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3118.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3118.png new file mode 100755 index 0000000..012434b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3118.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3119.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3119.png new file mode 100755 index 0000000..6e29d08 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3119.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3120.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3120.png new file mode 100755 index 0000000..bbfc4ac Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3120.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3121.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3121.png new file mode 100755 index 0000000..a243e5c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3121.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3122.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3122.png new file mode 100755 index 0000000..f65edc5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3122.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3123.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3123.png new file mode 100755 index 0000000..b3545f5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3123.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3124.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3124.png new file mode 100755 index 0000000..12b0754 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3124.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3125.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3125.png new file mode 100755 index 0000000..30dbd46 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3125.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3126.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3126.png new file mode 100755 index 0000000..ca51e59 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3126.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3127.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3127.png new file mode 100755 index 0000000..a2aa5c1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3127.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3128.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3128.png new file mode 100755 index 0000000..e357bfe Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3128.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3129.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3129.png new file mode 100755 index 0000000..141101a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3129.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3130.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3130.png new file mode 100755 index 0000000..51d1573 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3130.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3131.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3131.png new file mode 100755 index 0000000..554a558 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3131.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3132.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3132.png new file mode 100755 index 0000000..1dd6443 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3132.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3133.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3133.png new file mode 100755 index 0000000..4707500 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3133.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3134.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3134.png new file mode 100755 index 0000000..569fe11 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3134.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3135.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3135.png new file mode 100755 index 0000000..adfa950 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3135.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3136.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3136.png new file mode 100755 index 0000000..c1ce918 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3136.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3137.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3137.png new file mode 100755 index 0000000..4bf918e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3137.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3138.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3138.png new file mode 100755 index 0000000..a8aa15d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3138.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3139.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3139.png new file mode 100755 index 0000000..fedbcc3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3139.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3140.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3140.png new file mode 100755 index 0000000..a185fbb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3140.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3141.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3141.png new file mode 100755 index 0000000..a69a78c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3141.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3142.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3142.png new file mode 100755 index 0000000..52ee3bc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3142.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3143.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3143.png new file mode 100755 index 0000000..df76912 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3143.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3144.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3144.png new file mode 100755 index 0000000..682f9c9 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3144.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3145.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3145.png new file mode 100755 index 0000000..470300a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3145.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3146.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3146.png new file mode 100755 index 0000000..54291e2 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3146.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3147.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3147.png new file mode 100755 index 0000000..028baf2 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3147.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3148.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3148.png new file mode 100755 index 0000000..e7b4ca3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3148.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3149.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3149.png new file mode 100755 index 0000000..aa45d88 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3149.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3150.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3150.png new file mode 100755 index 0000000..4e651cb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3150.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3151.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3151.png new file mode 100755 index 0000000..215efbb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3151.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3152.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3152.png new file mode 100755 index 0000000..5c05670 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3152.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3153.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3153.png new file mode 100755 index 0000000..2aabd8f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3153.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3154.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3154.png new file mode 100755 index 0000000..fdf3ea5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3154.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3155.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3155.png new file mode 100755 index 0000000..563cd36 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3155.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3156.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3156.png new file mode 100755 index 0000000..e7eb1c4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3156.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3157.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3157.png new file mode 100755 index 0000000..c2983af Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3157.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3158.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3158.png new file mode 100755 index 0000000..8ebca89 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3158.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/3159.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3159.png new file mode 100755 index 0000000..e225e59 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/3159.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4001.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4001.png new file mode 100755 index 0000000..dfabf0f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4001.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4002.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4002.png new file mode 100755 index 0000000..51f88a1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4002.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4003.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4003.png new file mode 100755 index 0000000..c19cbd8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4003.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4004.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4004.png new file mode 100755 index 0000000..c8c87c6 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4004.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4005.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4005.png new file mode 100755 index 0000000..2a98324 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4005.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4006.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4006.png new file mode 100755 index 0000000..ad6f7e6 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4006.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4007.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4007.png new file mode 100755 index 0000000..350b751 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4007.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4008.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4008.png new file mode 100755 index 0000000..951310f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4008.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4009.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4009.png new file mode 100755 index 0000000..3bff531 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4009.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4010.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4010.png new file mode 100755 index 0000000..89be88c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4010.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4012.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4012.png new file mode 100755 index 0000000..19aaee6 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4012.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4013.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4013.png new file mode 100755 index 0000000..b3c5380 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4013.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4014.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4014.png new file mode 100755 index 0000000..e3cd785 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4014.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4015.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4015.png new file mode 100755 index 0000000..00ec2e0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4015.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4016.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4016.png new file mode 100755 index 0000000..edee7bf Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4016.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4018.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4018.png new file mode 100755 index 0000000..9a06f47 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4018.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4019.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4019.png new file mode 100755 index 0000000..f573b6e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4019.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4020.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4020.png new file mode 100755 index 0000000..0e175da Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4020.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4021.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4021.png new file mode 100755 index 0000000..c4e5b36 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4021.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4022.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4022.png new file mode 100755 index 0000000..19640f9 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4022.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4023.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4023.png new file mode 100755 index 0000000..45478b3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4023.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4024.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4024.png new file mode 100755 index 0000000..912813d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4024.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4025.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4025.png new file mode 100755 index 0000000..b7257eb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4025.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4026.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4026.png new file mode 100755 index 0000000..49e4999 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4026.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4027.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4027.png new file mode 100755 index 0000000..53e6703 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4027.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4028.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4028.png new file mode 100755 index 0000000..94cf9b4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4028.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4029.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4029.png new file mode 100755 index 0000000..92e72a0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4029.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4030.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4030.png new file mode 100755 index 0000000..f694ede Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4030.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4031.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4031.png new file mode 100755 index 0000000..ab9e5db Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4031.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4032.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4032.png new file mode 100755 index 0000000..a9c7afb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4032.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4033.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4033.png new file mode 100755 index 0000000..dcfbfee Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4033.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4034.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4034.png new file mode 100755 index 0000000..92a1052 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4034.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4035.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4035.png new file mode 100755 index 0000000..08c65c7 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4035.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4036.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4036.png new file mode 100755 index 0000000..cd79796 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4036.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4039.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4039.png new file mode 100755 index 0000000..adfd7f3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4039.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4040.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4040.png new file mode 100755 index 0000000..4ac3afe Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4040.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4041.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4041.png new file mode 100755 index 0000000..18355f2 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4041.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4052.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4052.png new file mode 100755 index 0000000..b7729e7 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4052.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4053.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4053.png new file mode 100755 index 0000000..d65070f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4053.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4054.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4054.png new file mode 100755 index 0000000..33d5fe5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4054.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4055.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4055.png new file mode 100755 index 0000000..3276efd Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4055.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4056.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4056.png new file mode 100755 index 0000000..2bae1f5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4056.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4057.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4057.png new file mode 100755 index 0000000..8cb17a1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4057.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4058.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4058.png new file mode 100755 index 0000000..24dea10 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4058.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/4059.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4059.png new file mode 100755 index 0000000..b8e5717 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/4059.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5001.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5001.png new file mode 100755 index 0000000..8d9bcdc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5001.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5002.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5002.png new file mode 100755 index 0000000..b3d6061 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5002.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5003.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5003.png new file mode 100755 index 0000000..630e776 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5003.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5004.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5004.png new file mode 100755 index 0000000..f2bdb15 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5004.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5005.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5005.png new file mode 100755 index 0000000..7b6f20c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5005.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5006.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5006.png new file mode 100755 index 0000000..12149c1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5006.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5007.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5007.png new file mode 100755 index 0000000..61837fa Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5007.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5008.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5008.png new file mode 100755 index 0000000..20fe9c1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5008.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5009.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5009.png new file mode 100755 index 0000000..26e95d3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5009.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5010.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5010.png new file mode 100755 index 0000000..b1f7d4a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5010.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5011.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5011.png new file mode 100755 index 0000000..fc3cee0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5011.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5012.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5012.png new file mode 100755 index 0000000..662ed39 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5012.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5013.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5013.png new file mode 100755 index 0000000..c024974 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5013.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5014.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5014.png new file mode 100755 index 0000000..cb0f55a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5014.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5015.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5015.png new file mode 100755 index 0000000..0e21226 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5015.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/5016.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5016.png new file mode 100755 index 0000000..52da0be Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/5016.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6001.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6001.png new file mode 100755 index 0000000..bf81ed9 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6001.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6002.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6002.png new file mode 100755 index 0000000..48b4bd3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6002.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6003.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6003.png new file mode 100755 index 0000000..f3e14a0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6003.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6004.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6004.png new file mode 100755 index 0000000..a4a1916 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6004.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6005.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6005.png new file mode 100755 index 0000000..a1b4de7 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6005.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6006.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6006.png new file mode 100755 index 0000000..a286157 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6006.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6007.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6007.png new file mode 100755 index 0000000..08e6f73 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6007.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6008.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6008.png new file mode 100755 index 0000000..2a3eab4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6008.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6009.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6009.png new file mode 100755 index 0000000..81a10ed Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6009.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6010.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6010.png new file mode 100755 index 0000000..d2d7c41 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6010.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6011.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6011.png new file mode 100755 index 0000000..e569905 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6011.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6012.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6012.png new file mode 100755 index 0000000..185bb04 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6012.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6013.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6013.png new file mode 100755 index 0000000..07280c3 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6013.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/6014.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6014.png new file mode 100755 index 0000000..417f303 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/6014.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7001.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7001.png new file mode 100755 index 0000000..e99dd92 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7001.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7002.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7002.png new file mode 100755 index 0000000..3ecd614 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7002.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7003.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7003.png new file mode 100755 index 0000000..74060db Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7003.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7004.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7004.png new file mode 100755 index 0000000..a8d35fd Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7004.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7005.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7005.png new file mode 100755 index 0000000..6607839 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7005.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7006.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7006.png new file mode 100755 index 0000000..5aedcef Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7006.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7007.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7007.png new file mode 100755 index 0000000..8253fd8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7007.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7008.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7008.png new file mode 100755 index 0000000..421a9f0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7008.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7009.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7009.png new file mode 100755 index 0000000..87bcebc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7009.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7010.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7010.png new file mode 100755 index 0000000..d98d56a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7010.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7011.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7011.png new file mode 100755 index 0000000..d62cb63 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7011.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7012.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7012.png new file mode 100755 index 0000000..66f6b05 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7012.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7013.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7013.png new file mode 100755 index 0000000..e6257e7 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7013.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7020.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7020.png new file mode 100755 index 0000000..8e899f2 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7020.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7021.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7021.png new file mode 100755 index 0000000..50d158e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7021.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7030.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7030.png new file mode 100755 index 0000000..9152277 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7030.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7031.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7031.png new file mode 100755 index 0000000..3d61160 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7031.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7032.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7032.png new file mode 100755 index 0000000..d2c8bc5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7032.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7035.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7035.png new file mode 100755 index 0000000..090bb5f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7035.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7036.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7036.png new file mode 100755 index 0000000..40ead5b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7036.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7038.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7038.png new file mode 100755 index 0000000..9aa8916 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7038.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7040.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7040.png new file mode 100755 index 0000000..70adbed Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7040.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7041.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7041.png new file mode 100755 index 0000000..d4c79dc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7041.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7042.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7042.png new file mode 100755 index 0000000..6bc4e32 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7042.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7043.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7043.png new file mode 100755 index 0000000..312979e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7043.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7044.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7044.png new file mode 100755 index 0000000..066ffca Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7044.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7045.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7045.png new file mode 100755 index 0000000..505e919 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7045.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7046.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7046.png new file mode 100755 index 0000000..75b6fb9 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7046.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/7047.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7047.png new file mode 100755 index 0000000..d05d6a9 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/7047.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8001.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8001.png new file mode 100755 index 0000000..b2df1e1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8001.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8002.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8002.png new file mode 100755 index 0000000..51193da Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8002.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8003.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8003.png new file mode 100755 index 0000000..bc90b67 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8003.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8004.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8004.png new file mode 100755 index 0000000..b0c4142 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8004.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8005.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8005.png new file mode 100755 index 0000000..6fd95f7 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8005.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8006.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8006.png new file mode 100755 index 0000000..4c217b2 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8006.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8008.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8008.png new file mode 100755 index 0000000..f3e3a72 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8008.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8009.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8009.png new file mode 100755 index 0000000..479549a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8009.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8010.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8010.png new file mode 100755 index 0000000..ee01aad Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8010.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8017.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8017.png new file mode 100755 index 0000000..6e56f2d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8017.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8018.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8018.png new file mode 100755 index 0000000..ac3e1a4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8018.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8019.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8019.png new file mode 100755 index 0000000..c4e451d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8019.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8020.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8020.png new file mode 100755 index 0000000..5d7bc0a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8020.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8021.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8021.png new file mode 100755 index 0000000..b8e5717 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8021.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8022.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8022.png new file mode 100755 index 0000000..8cb17a1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8022.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8023.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8023.png new file mode 100755 index 0000000..41a1d52 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8023.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8024.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8024.png new file mode 100755 index 0000000..37a9321 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8024.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8025.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8025.png new file mode 100755 index 0000000..5dce396 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8025.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8026.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8026.png new file mode 100755 index 0000000..a16637e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8026.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8027.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8027.png new file mode 100755 index 0000000..9333d45 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8027.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8028.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8028.png new file mode 100755 index 0000000..97b98d1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8028.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8029.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8029.png new file mode 100755 index 0000000..5bc1c77 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8029.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8030.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8030.png new file mode 100755 index 0000000..fd08701 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8030.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8031.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8031.png new file mode 100755 index 0000000..ed3af34 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8031.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8032.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8032.png new file mode 100755 index 0000000..7380450 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8032.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8033.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8033.png new file mode 100755 index 0000000..40251b5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8033.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8034.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8034.png new file mode 100755 index 0000000..7f6f95d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8034.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8035.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8035.png new file mode 100755 index 0000000..cdf5d23 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8035.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8036.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8036.png new file mode 100755 index 0000000..2cb0c32 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8036.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8037.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8037.png new file mode 100755 index 0000000..c1b8dc8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8037.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8038.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8038.png new file mode 100755 index 0000000..f1dff01 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8038.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8039.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8039.png new file mode 100755 index 0000000..cda7743 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8039.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8040.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8040.png new file mode 100755 index 0000000..291f964 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8040.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8041.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8041.png new file mode 100755 index 0000000..e67c0fc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8041.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8042.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8042.png new file mode 100755 index 0000000..72f95eb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8042.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8043.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8043.png new file mode 100755 index 0000000..b208cea Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8043.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8044.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8044.png new file mode 100755 index 0000000..ff615ca Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8044.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8045.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8045.png new file mode 100755 index 0000000..a5540ff Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8045.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8046.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8046.png new file mode 100755 index 0000000..7553757 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8046.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8047.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8047.png new file mode 100755 index 0000000..1d35918 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8047.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8048.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8048.png new file mode 100755 index 0000000..5622b2e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8048.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8049.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8049.png new file mode 100755 index 0000000..3c1c551 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8049.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8050.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8050.png new file mode 100755 index 0000000..e9e9c4e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8050.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8051.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8051.png new file mode 100755 index 0000000..a331722 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8051.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8052.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8052.png new file mode 100755 index 0000000..a1e20b6 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8052.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8053.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8053.png new file mode 100755 index 0000000..c13dad2 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8053.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8054.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8054.png new file mode 100755 index 0000000..27541b5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8054.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8055.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8055.png new file mode 100755 index 0000000..9d8923b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8055.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8056.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8056.png new file mode 100755 index 0000000..6976b3c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8056.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8057.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8057.png new file mode 100755 index 0000000..d41545c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8057.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8058.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8058.png new file mode 100755 index 0000000..a37cc9b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8058.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8059.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8059.png new file mode 100755 index 0000000..ebefaf5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8059.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8060.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8060.png new file mode 100755 index 0000000..69a2c96 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8060.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8061.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8061.png new file mode 100755 index 0000000..7fc7119 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8061.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8062.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8062.png new file mode 100755 index 0000000..92425c8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8062.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8063.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8063.png new file mode 100755 index 0000000..8b35418 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8063.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8064.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8064.png new file mode 100755 index 0000000..33483b6 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8064.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8065.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8065.png new file mode 100755 index 0000000..4f33685 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8065.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8066.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8066.png new file mode 100755 index 0000000..14542a8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8066.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8067.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8067.png new file mode 100755 index 0000000..7f8e532 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8067.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8068.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8068.png new file mode 100755 index 0000000..67a0424 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8068.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8069.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8069.png new file mode 100755 index 0000000..6a790fe Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8069.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8070.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8070.png new file mode 100755 index 0000000..4c374fa Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8070.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8071.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8071.png new file mode 100755 index 0000000..f92f89f Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8071.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8072.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8072.png new file mode 100755 index 0000000..7cb1b8d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8072.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8073.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8073.png new file mode 100755 index 0000000..b9056dc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8073.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8074.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8074.png new file mode 100755 index 0000000..7a3b67e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8074.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8075.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8075.png new file mode 100755 index 0000000..0e6cd8e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8075.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8076.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8076.png new file mode 100755 index 0000000..8b54042 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8076.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8077.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8077.png new file mode 100755 index 0000000..1bb8c08 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8077.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8079.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8079.png new file mode 100755 index 0000000..8d1f6d4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8079.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8087.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8087.png new file mode 100755 index 0000000..f16c0de Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8087.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8089.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8089.png new file mode 100755 index 0000000..0345402 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8089.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8090.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8090.png new file mode 100755 index 0000000..c46f080 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8090.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8092.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8092.png new file mode 100755 index 0000000..7ea6544 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8092.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8093.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8093.png new file mode 100755 index 0000000..ddced49 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8093.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8096.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8096.png new file mode 100755 index 0000000..420dcd8 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8096.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8098.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8098.png new file mode 100755 index 0000000..52ee3bc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8098.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8099.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8099.png new file mode 100755 index 0000000..0815673 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8099.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8100.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8100.png new file mode 100755 index 0000000..dd25164 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8100.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8101.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8101.png new file mode 100755 index 0000000..6bbdc0e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8101.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8102.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8102.png new file mode 100755 index 0000000..b61b31e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8102.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8103.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8103.png new file mode 100755 index 0000000..8340226 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8103.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8104.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8104.png new file mode 100755 index 0000000..8004eb2 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8104.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8105.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8105.png new file mode 100755 index 0000000..ba7f02d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8105.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8106.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8106.png new file mode 100755 index 0000000..11a0790 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8106.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8107.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8107.png new file mode 100755 index 0000000..854b57c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8107.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8108.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8108.png new file mode 100755 index 0000000..a126524 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8108.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8109.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8109.png new file mode 100755 index 0000000..52ee3bc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8109.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8110.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8110.png new file mode 100755 index 0000000..1b6a493 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8110.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8111.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8111.png new file mode 100755 index 0000000..7be639c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8111.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8112.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8112.png new file mode 100755 index 0000000..90c29ab Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8112.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8116.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8116.png new file mode 100755 index 0000000..b0bba8a Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8116.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8118.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8118.png new file mode 100755 index 0000000..f96c1b5 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8118.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8119.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8119.png new file mode 100755 index 0000000..64f342b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8119.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8120.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8120.png new file mode 100755 index 0000000..29d301b Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8120.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8121.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8121.png new file mode 100755 index 0000000..c283c48 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8121.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8122.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8122.png new file mode 100755 index 0000000..d127d05 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8122.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8123.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8123.png new file mode 100755 index 0000000..4b47576 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8123.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8125.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8125.png new file mode 100755 index 0000000..6bc4dce Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8125.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8127.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8127.png new file mode 100755 index 0000000..aacac0c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8127.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8128.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8128.png new file mode 100755 index 0000000..5e3c496 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8128.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8129.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8129.png new file mode 100755 index 0000000..0931546 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8129.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8131.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8131.png new file mode 100755 index 0000000..81f76f4 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8131.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8132.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8132.png new file mode 100755 index 0000000..30e4241 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8132.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8133.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8133.png new file mode 100755 index 0000000..5c1d94e Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8133.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8134.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8134.png new file mode 100755 index 0000000..d2fbadb Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8134.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8135.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8135.png new file mode 100755 index 0000000..0acea2d Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8135.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8136.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8136.png new file mode 100755 index 0000000..226cade Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8136.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8137.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8137.png new file mode 100755 index 0000000..8fd8f6c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8137.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8138.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8138.png new file mode 100755 index 0000000..6ae61c0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8138.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8142.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8142.png new file mode 100755 index 0000000..fd7ae27 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8142.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8143.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8143.png new file mode 100755 index 0000000..2b3d856 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8143.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8144.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8144.png new file mode 100755 index 0000000..90d7dbc Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8144.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8145.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8145.png new file mode 100755 index 0000000..c02b483 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8145.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8146.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8146.png new file mode 100755 index 0000000..b2df1e1 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8146.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/8147.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8147.png new file mode 100755 index 0000000..0d0b96c Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/8147.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/9001.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/9001.png new file mode 100755 index 0000000..66a1f94 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/9001.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/9010.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/9010.png new file mode 100755 index 0000000..fed2afd Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/9010.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/9030.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/9030.png new file mode 100755 index 0000000..a1586b0 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/9030.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/9040.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/9040.png new file mode 100755 index 0000000..b3a4817 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/9040.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/app_icons/default.png b/luci-app-oaf/htdocs/luci-static/resources/app_icons/default.png new file mode 100755 index 0000000..1e06821 Binary files /dev/null and b/luci-app-oaf/htdocs/luci-static/resources/app_icons/default.png differ diff --git a/luci-app-oaf/htdocs/luci-static/resources/echarts.min.js b/luci-app-oaf/htdocs/luci-static/resources/echarts.min.js new file mode 100755 index 0000000..38e4722 --- /dev/null +++ b/luci-app-oaf/htdocs/luci-static/resources/echarts.min.js @@ -0,0 +1,22 @@ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you 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. +*/ + + +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.echarts={})}(this,function(t){"use strict";var e=2311,n=function(){return e++},v="object"==typeof wx&&"function"==typeof wx.getSystemInfoSync?{browser:{},os:{},node:!1,wxa:!0,canvasSupported:!0,svgSupported:!1,touchEventsSupported:!0,domSupported:!1}:"undefined"==typeof document&&"undefined"!=typeof self?{browser:{},os:{},node:!1,worker:!0,canvasSupported:!0,domSupported:!1}:"undefined"==typeof navigator?{browser:{},os:{},node:!0,worker:!1,canvasSupported:!0,svgSupported:!0,domSupported:!1}:function(t){var e={},i=t.match(/Firefox\/([\d.]+)/),n=t.match(/MSIE\s([\d.]+)/)||t.match(/Trident\/.+?rv:(([\d.]+))/),o=t.match(/Edge\/([\d.]+)/),a=/micromessenger/i.test(t);i&&(e.firefox=!0,e.version=i[1]);n&&(e.ie=!0,e.version=n[1]);o&&(e.edge=!0,e.version=o[1]);a&&(e.weChat=!0);return{browser:e,os:{},node:!1,canvasSupported:!!document.createElement("canvas").getContext,svgSupported:"undefined"!=typeof SVGRect,touchEventsSupported:"ontouchstart"in window&&!e.ie&&!e.edge,pointerEventsSupported:"onpointerdown"in window&&(e.edge||e.ie&&11<=e.version),domSupported:"undefined"!=typeof document}}(navigator.userAgent);var s={"[object Function]":1,"[object RegExp]":1,"[object Date]":1,"[object Error]":1,"[object CanvasGradient]":1,"[object CanvasPattern]":1,"[object Image]":1,"[object Canvas]":1},l={"[object Int8Array]":1,"[object Uint8Array]":1,"[object Uint8ClampedArray]":1,"[object Int16Array]":1,"[object Uint16Array]":1,"[object Int32Array]":1,"[object Uint32Array]":1,"[object Float32Array]":1,"[object Float64Array]":1},u=Object.prototype.toString,i=Array.prototype,r=i.forEach,h=i.filter,o=i.slice,c=i.map,d=i.reduce,a={};function f(t,e){"createCanvas"===t&&(g=null),a[t]=e}function k(t){if(null==t||"object"!=typeof t)return t;var e=t,i=u.call(t);if("[object Array]"===i){if(!$(t)){e=[];for(var n=0,o=t.length;n>1)%2;s.cssText=["position: absolute","visibility: hidden","padding: 0","margin: 0","border-width: 0","user-select: none","width:0","height:0",n[l]+":0",o[u]+":0",n[1-l]+":auto",o[1-u]+":auto",""].join("!important;"),t.appendChild(r),i.push(r)}return i}(e,a),a,o);if(r)return r(t,i,n),!0}return!1}function zt(t){return"CANVAS"===t.nodeName.toUpperCase()}var Bt="undefined"!=typeof window&&!!window.addEventListener,Vt=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Gt=[];function Ft(t,e,i,n){return i=i||{},n||!v.canvasSupported?Wt(t,e,i):v.browser.firefox&&null!=e.layerX&&e.layerX!==e.offsetX?(i.zrX=e.layerX,i.zrY=e.layerY):null!=e.offsetX?(i.zrX=e.offsetX,i.zrY=e.offsetY):Wt(t,e,i),i}function Wt(t,e,i){if(v.domSupported&&t.getBoundingClientRect){var n=e.clientX,o=e.clientY;if(zt(t)){var a=t.getBoundingClientRect();return i.zrX=n-a.left,void(i.zrY=o-a.top)}if(Et(Gt,t,n,o))return i.zrX=Gt[0],void(i.zrY=Gt[1])}i.zrX=i.zrY=0}function Ht(t){return t||window.event}function Zt(t,e,i){if(null!=(e=Ht(e)).zrX)return e;var n=e.type;if(n&&0<=n.indexOf("touch")){var o="touchend"!==n?e.targetTouches[0]:e.changedTouches[0];o&&Ft(t,o,e,i)}else Ft(t,e,e,i),e.zrDelta=e.wheelDelta?e.wheelDelta/120:-(e.detail||0)/3;var a=e.button;return null==e.which&&void 0!==a&&Vt.test(e.type)&&(e.which=1&a?1:2&a?3:4&a?2:0),e}function Ut(t,e,i,n){Bt?t.addEventListener(e,i,n):t.attachEvent("on"+e,i)}var Xt=Bt?function(t){t.preventDefault(),t.stopPropagation(),t.cancelBubble=!0}:function(t){t.returnValue=!1,t.cancelBubble=!0};function Yt(t){return 2===t.which||3===t.which}function jt(){this._track=[]}function qt(t){var e=t[1][0]-t[0][0],i=t[1][1]-t[0][1];return Math.sqrt(e*e+i*i)}jt.prototype={constructor:jt,recognize:function(t,e,i){return this._doTrack(t,e,i),this._recognize(t)},clear:function(){return this._track.length=0,this},_doTrack:function(t,e,i){var n=t.touches;if(n){for(var o={points:[],touches:[],target:e,event:t},a=0,r=n.length;an.getWidth()||i<0||i>n.getHeight()}te.prototype={constructor:te,setHandlerProxy:function(e){this.proxy&&this.proxy.dispose(),e&&(R(ee,function(t){e.on&&e.on(t,this[t],this)},this),e.handler=this),this.proxy=e},mousemove:function(t){var e=t.zrX,i=t.zrY,n=ne(this,e,i),o=this._hovered,a=o.target;a&&!a.__zr&&(a=(o=this.findHover(o.x,o.y)).target);var r=this._hovered=n?{x:e,y:i}:this.findHover(e,i),s=r.target,l=this.proxy;l.setCursor&&l.setCursor(s?s.cursor:"default"),a&&s!==a&&this.dispatchToElement(o,"mouseout",t),this.dispatchToElement(r,"mousemove",t),s&&s!==a&&this.dispatchToElement(r,"mouseover",t)},mouseout:function(t){var e=t.zrEventControl,i=t.zrIsToLocalDOM;"only_globalout"!==e&&this.dispatchToElement(this._hovered,"mouseout",t),"no_globalout"!==e&&(i||this.trigger("globalout",{type:"globalout",event:t}))},resize:function(t){this._hovered={}},dispatch:function(t,e){var i=this[t];i&&i.call(this,e)},dispose:function(){this.proxy.dispose(),this.storage=this.proxy=this.painter=null},setCursorStyle:function(t){var e=this.proxy;e.setCursor&&e.setCursor(t)},dispatchToElement:function(t,e,i){var n=(t=t||{}).target;if(!n||!n.silent){for(var o="on"+e,a=function(t,e,i){return{type:t,event:i,target:e.target,topTarget:e.topTarget,cancelBubble:!1,offsetX:i.zrX,offsetY:i.zrY,gestureEvent:i.gestureEvent,pinchX:i.pinchX,pinchY:i.pinchY,pinchScale:i.pinchScale,wheelDelta:i.zrDelta,zrByTouch:i.zrByTouch,which:i.which,stop:Jt}}(e,t,i);n&&(n[o]&&(a.cancelBubble=n[o].call(n,a)),n.trigger(e,a),n=n.parent,!a.cancelBubble););a.cancelBubble||(this.trigger(e,a),this.painter&&this.painter.eachOtherLayer(function(t){"function"==typeof t[o]&&t[o].call(t,a),t.trigger&&t.trigger(e,a)}))}},findHover:function(t,e,i){for(var n=this.storage.getDisplayList(),o={x:t,y:e},a=n.length-1;0<=a;a--){var r;if(n[a]!==i&&!n[a].ignore&&(r=ie(n[a],t,e))&&(o.topTarget||(o.topTarget=n[a]),r!==$t)){o.target=n[a];break}}return o},processGesture:function(t,e){this._gestureMgr||(this._gestureMgr=new jt);var i=this._gestureMgr;"start"===e&&i.clear();var n=i.recognize(t,this.findHover(t.zrX,t.zrY,null).target,this.proxy.dom);if("end"===e&&i.clear(),n){var o=n.type;t.gestureEvent=o,this.dispatchToElement({target:n.target},o,n.event)}}},R(["click","mousedown","mouseup","mousewheel","dblclick","contextmenu"],function(r){te.prototype[r]=function(t){var e,i,n=t.zrX,o=t.zrY,a=ne(this,n,o);if("mouseup"===r&&a||(i=(e=this.findHover(n,o)).target),"mousedown"===r)this._downEl=i,this._downPoint=[t.zrX,t.zrY],this._upEl=i;else if("mouseup"===r)this._upEl=i;else if("click"===r){if(this._downEl!==this._upEl||!this._downPoint||4=this._maxSize&&0>4|(3840&n)>>8,240&n|(240&n)>>4,15&n|(15&n)<<4,1),Ge(t,e),e):void Ee(e,0,0,0,1):7===o.length?0<=(n=parseInt(o.substr(1),16))&&n<=16777215?(Ee(e,(16711680&n)>>16,(65280&n)>>8,255&n,1),Ge(t,e),e):void Ee(e,0,0,0,1):void 0;var a=o.indexOf("("),r=o.indexOf(")");if(-1!==a&&r+1===o.length){var s=o.substr(0,a),l=o.substr(a+1,r-(a+1)).split(","),u=1;switch(s){case"rgba":if(4!==l.length)return void Ee(e,0,0,0,1);u=Ne(l.pop());case"rgb":return 3!==l.length?void Ee(e,0,0,0,1):(Ee(e,Pe(l[0]),Pe(l[1]),Pe(l[2]),u),Ge(t,e),e);case"hsla":return 4!==l.length?void Ee(e,0,0,0,1):(l[3]=Ne(l[3]),We(l,e),Ge(t,e),e);case"hsl":return 3!==l.length?void Ee(e,0,0,0,1):(We(l,e),Ge(t,e),e);default:return}}Ee(e,0,0,0,1)}}function We(t,e){var i=(parseFloat(t[0])%360+360)%360/360,n=Ne(t[1]),o=Ne(t[2]),a=o<=.5?o*(n+1):o+n-o*n,r=2*o-a;return Ee(e=e||[],Le(255*Oe(r,a,i+1/3)),Le(255*Oe(r,a,i)),Le(255*Oe(r,a,i-1/3)),1),4===t.length&&(e[3]=t[3]),e}function He(t,e){var i=Fe(t);if(i){for(var n=0;n<3;n++)i[n]=e<0?i[n]*(1-e)|0:(255-i[n])*e+i[n]|0,255e);i++);i=Math.min(i-1,u-2)}C=e;var n=g[(D=i)+1]-g[i];if(0!=n)if(S=(e-g[i])/n,l)if(I=m[i],M=m[0===i?i:i-1],T=m[u-2=i.x&&t<=i.x+i.width&&e>=i.y&&e<=i.y+i.height},clone:function(){return new Di(this.x,this.y,this.width,this.height)},copy:function(t){this.x=t.x,this.y=t.y,this.width=t.width,this.height=t.height},plain:function(){return{x:this.x,y:this.y,width:this.width,height:this.height}}},Di.create=function(t){return new Di(t.x,t.y,t.width,t.height)};var Ci=function(t){for(var e in t=t||{},_i.call(this,t),t)t.hasOwnProperty(e)&&(this[e]=t[e]);this._children=[],this.__storage=null,this.__dirty=!0};Ci.prototype={constructor:Ci,isGroup:!0,type:"group",silent:!1,children:function(){return this._children.slice()},childAt:function(t){return this._children[t]},childOfName:function(t){for(var e=this._children,i=0;i>>1])<0?l=a:s=1+a;var u=n-s;switch(u){case 3:t[s+3]=t[s+2];case 2:t[s+2]=t[s+1];case 1:t[s+1]=t[s];break;default:for(;0>>1);0>>1);a(t,e[i+h])<0?l=h:r=h+1}return l}function Ei(p,g){var r,s,m=ki,l=0,v=[];function e(t){var e=r[t],i=s[t],n=r[t+1],o=s[t+1];s[t]=i+o,t===l-3&&(r[t+1]=r[t+2],s[t+1]=s[t+2]),l--;var a=Ri(p[n],p,e,i,0,g);e+=a,0!==(i-=a)&&0!==(o=Oi(p[e+i-1],p,n,o,o-1,g))&&(i<=o?function(t,e,i,n){var o=0;for(o=0;os[t+1])break;e(t)}},this.forceMergeRuns=function(){for(;1>=1;return t+e}(o);do{if((a=Pi(t,i,n,e))=e.maxIterations){t+=e.ellipsis;break}var s=0===r?bn(t,o,e.ascCharWidth,e.cnCharWidth):0f)return{lines:[],width:0,height:0};C.textWidth=pn(C.text,w);var S=x.textWidth,M=null==S||"auto"===S;if("string"==typeof S&&"%"===S.charAt(S.length-1))C.percentWidth=S,u.push(C),S=0;else{if(M){S=C.textWidth;var I=x.textBackgroundColor,T=I&&I.image;T&&sn(T=on(T))&&(S=Math.max(S,T.width*b/T.height))}var A=_?_[1]+_[3]:0;S+=A;var D=null!=d?d-v:null;null!=D&&Dn[0]){for(r=0;rt);r++);a=i[n[r]]}if(n.splice(r+1,0,t),!(i[t]=e).virtual)if(a){var l=a.dom;l.nextSibling?s.insertBefore(e.dom,l.nextSibling):s.appendChild(e.dom)}else s.firstChild?s.insertBefore(e.dom,s.firstChild):s.appendChild(e.dom)}else vi("Layer of zlevel "+t+" is not valid")},eachLayer:function(t,e){var i,n,o=this._zlevelList;for(n=0;n=a.length&&a.push({option:t})}}),a}function Zo(t){var r=Q();Ro(t,function(t,e){var i=t.exist;i&&r.set(i.id,t)}),Ro(t,function(t,e){var i=t.option;Y(!i||null==i.id||!r.get(i.id)||r.get(i.id)===t,"id duplicates: "+(i&&i.id)),i&&null!=i.id&&r.set(i.id,t),t.keyInfo||(t.keyInfo={})}),Ro(t,function(t,e){var i=t.exist,n=t.option,o=t.keyInfo;if(Eo(n)){if(o.name=null!=n.name?n.name+"":i?i.name:Bo+e,i)o.id=i.id;else if(null!=n.id)o.id=n.id+"";else for(var a=0;o.id="\0"+o.name+"\0"+a++,r.get(o.id););r.set(o.id,t)}})}function Uo(t){var e=t.name;return!(!e||!e.indexOf(Bo))}function Xo(t){return Eo(t)&&t.id&&0===(t.id+"").indexOf("\0_ec_\0")}function Yo(e,t){return null!=t.dataIndexInside?t.dataIndexInside:null!=t.dataIndex?L(t.dataIndex)?O(t.dataIndex,function(t){return e.indexOfRawIndex(t)}):e.indexOfRawIndex(t.dataIndex):null!=t.name?L(t.name)?O(t.name,function(t){return e.indexOfName(t)}):e.indexOfName(t.name):void 0}function jo(){var e="__\0ec_inner_"+qo+++"_"+Math.random().toFixed(5);return function(t){return t[e]||(t[e]={})}}var qo=0;function Ko(s,l,u){if(E(l)){var t={};t[l+"Index"]=0,l=t}var e=u&&u.defaultMainType;!e||$o(l,e+"Index")||$o(l,e+"Id")||$o(l,e+"Name")||(l[e+"Index"]=0);var h={};return Ro(l,function(t,e){t=l[e];if("dataIndex"!==e&&"dataIndexInside"!==e){var i=e.match(/^(\w+)(Index|Id|Name)$/)||[],n=i[1],o=(i[2]||"").toLowerCase();if(!(!n||!o||null==t||"index"===o&&"none"===t||u&&u.includeMainTypes&&_(u.includeMainTypes,n)<0)){var a={mainType:n};"index"===o&&"all"===t||(a[o]=t);var r=s.queryComponents(a);h[n+"Models"]=r,h[n+"Model"]=r[0]}}else h[e]=t}),h}function $o(t,e){return t&&t.hasOwnProperty(e)}function Jo(t,e,i){t.setAttribute?t.setAttribute(e,i):t[e]=i}function Qo(t){return"auto"===t?v.domSupported?"html":"richText":t||"html"}function ta(t,i){var n=Q(),o=[];return R(t,function(t){var e=i(t);(n.get(e)||(o.push(e),n.set(e,[]))).push(t)}),{keys:o,buckets:n}}var ea=".",ia="___EC__COMPONENT__CONTAINER___";function na(t){var e={main:"",sub:""};return t&&(t=t.split(ea),e.main=t[0]||"",e.sub=t[1]||""),e}function oa(t){(t.$constructor=t).extend=function(t){function e(){t.$constructor?t.$constructor.apply(this,arguments):i.apply(this,arguments)}var i=this;return P(e.prototype,t),e.extend=this.extend,e.superCall=sa,e.superApply=la,w(e,this),e.superClass=i,e}}var aa=0;function ra(t){var e=["__\0is_clz",aa++,Math.random().toFixed(3)].join("_");t.prototype[e]=!0,t.isInstance=function(t){return!(!t||!t[e])}}function sa(t,e){var i=U(arguments,2);return this.superClass.prototype[e].apply(t,i)}function la(t,e,i){return this.superClass.prototype[e].apply(t,i)}function ua(i,t){t=t||{};var o={};if(i.registerClass=function(t,e){if(e)if(function(t){Y(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(t),'componentType "'+t+'" illegal')}(e),(e=na(e)).sub){if(e.sub!==ia){(function(t){var e=o[t.main];e&&e[ia]||((e=o[t.main]={})[ia]=!0);return e})(e)[e.sub]=t}}else o[e.main]=t;return t},i.getClass=function(t,e,i){var n=o[t];if(n&&n[ia]&&(n=e?n[e]:null),i&&!n)throw new Error(e?"Component "+t+"."+(e||"")+" not exists. Load it first.":t+".type should be specified.");return n},i.getClassesByMainType=function(t){t=na(t);var i=[],e=o[t.main];return e&&e[ia]?R(e,function(t,e){e!==ia&&i.push(t)}):i.push(e),i},i.hasClass=function(t){return t=na(t),!!o[t.main]},i.getAllClassMainTypes=function(){var i=[];return R(o,function(t,e){i.push(e)}),i},i.hasSubTypes=function(t){t=na(t);var e=o[t.main];return e&&e[ia]},i.parseClassType=na,t.registerWhenExtend){var n=i.extend;n&&(i.extend=function(t){var e=n.call(this,t);return i.registerClass(e,t.type)})}return i}function ha(s){for(var t=0;tthis._ux||or(e-this._yi)>this._uy||this._len<5;return this.addData(ja.L,t,e),this._ctx&&i&&(this._needsDash()?this._dashedLineTo(t,e):this._ctx.lineTo(t,e)),i&&(this._xi=t,this._yi=e),this},bezierCurveTo:function(t,e,i,n,o,a){return this.addData(ja.C,t,e,i,n,o,a),this._ctx&&(this._needsDash()?this._dashedBezierTo(t,e,i,n,o,a):this._ctx.bezierCurveTo(t,e,i,n,o,a)),this._xi=o,this._yi=a,this},quadraticCurveTo:function(t,e,i,n){return this.addData(ja.Q,t,e,i,n),this._ctx&&(this._needsDash()?this._dashedQuadraticTo(t,e,i,n):this._ctx.quadraticCurveTo(t,e,i,n)),this._xi=i,this._yi=n,this},arc:function(t,e,i,n,o,a){return this.addData(ja.A,t,e,i,i,n,o-n,0,a?0:1),this._ctx&&this._ctx.arc(t,e,i,n,o,a),this._xi=er(o)*i+t,this._yi=ir(o)*i+e,this},arcTo:function(t,e,i,n,o){return this._ctx&&this._ctx.arcTo(t,e,i,n,o),this},rect:function(t,e,i,n){return this._ctx&&this._ctx.rect(t,e,i,n),this.addData(ja.R,t,e,i,n),this},closePath:function(){this.addData(ja.Z);var t=this._ctx,e=this._x0,i=this._y0;return t&&(this._needsDash()&&this._dashedLineTo(e,i),t.closePath()),this._xi=e,this._yi=i,this},fill:function(t){t&&t.fill(),this.toStatic()},stroke:function(t){t&&t.stroke(),this.toStatic()},setLineDash:function(t){if(t instanceof Array){this._lineDash=t;for(var e=this._dashIdx=0,i=0;ie.length&&(this._expandData(),e=this.data);for(var i=0;il||or(r-o)>u||c===h-1)&&(t.lineTo(a,r),n=a,o=r);break;case ja.C:t.bezierCurveTo(s[c++],s[c++],s[c++],s[c++],s[c++],s[c++]),n=s[c-2],o=s[c-1];break;case ja.Q:t.quadraticCurveTo(s[c++],s[c++],s[c++],s[c++]),n=s[c-2],o=s[c-1];break;case ja.A:var f=s[c++],p=s[c++],g=s[c++],m=s[c++],v=s[c++],y=s[c++],x=s[c++],_=s[c++],w=m=yr[n=0]+t&&r<=yr[1]+t?h:0}if(a){l=n;n=cr(o),o=cr(l)}else n=cr(n),o=cr(o);oMath.PI/2&&p<1.5*Math.PI&&(h=-h),c+=h)}}return c}function Sr(t,e,i,n,o){for(var a=0,r=0,s=0,l=0,u=0,h=0;hMath.abs(a[1])?0=e[1])return i[1]}else{if(t>=e[0])return i[0];if(t<=e[1])return i[1]}else{if(t===e[0])return i[0];if(t===e[1])return i[1]}return(t-e[0])/o*a+i[0]}function El(t,e){switch(t){case"center":case"middle":t="50%";break;case"left":case"top":t="0%";break;case"right":case"bottom":t="100%"}return"string"==typeof t?function(t){return t.replace(/^\s+|\s+$/g,"")}(t).match(/%$/)?parseFloat(t)/100*e:parseFloat(t):null==t?NaN:+t}function zl(t,e,i){return null==e&&(e=10),e=Math.min(Math.max(0,e),20),t=(+t).toFixed(e),i?t:+t}function Bl(t){return t.sort(function(t,e){return t-e}),t}function Vl(t){if(t=+t,isNaN(t))return 0;for(var e=1,i=0;Math.round(t*e)/e!==t;)e*=10,i++;return i}function Gl(t){var e=t.toString(),i=e.indexOf("e");if(0h&&(h=u[d],c=d);++s[c],u[c]=0,++l}return s[e]/o}var Hl=9007199254740991;function Zl(t){var e=2*Math.PI;return(t%e+e)%e}function Ul(t){return-Ol"'])/g,ou={"&":"&","<":"<",">":">",'"':""","'":"'"};function au(t){return null==t?"":(t+"").replace(nu,function(t,e){return ou[e]})}function ru(t,e){return"{"+t+(null==e?"":e)+"}"}var su=["a","b","c","d","e","f","g"];function lu(t,e,i){L(e)||(e=[e]);var n=e.length;if(!n)return"";for(var o=e[0].$vars||[],a=0;a':'':{renderMode:o,content:"{marker"+a+"|} ",style:{color:i}}:""}function cu(t,e){return"0000".substr(0,e-(t+="").length)+t}function du(t,e,i){"week"!==t&&"month"!==t&&"quarter"!==t&&"half-year"!==t&&"year"!==t||(t="MM-dd\nyyyy");var n=Yl(e),o=i?"UTC":"",a=n["get"+o+"FullYear"](),r=n["get"+o+"Month"]()+1,s=n["get"+o+"Date"](),l=n["get"+o+"Hours"](),u=n["get"+o+"Minutes"](),h=n["get"+o+"Seconds"](),c=n["get"+o+"Milliseconds"]();return t=t.replace("MM",cu(r,2)).replace("M",r).replace("yyyy",a).replace("yy",a%100).replace("dd",cu(s,2)).replace("d",s).replace("hh",cu(l,2)).replace("h",l).replace("mm",cu(u,2)).replace("m",u).replace("ss",cu(h,2)).replace("s",h).replace("SSS",cu(c,3))}function fu(t){return t?t.charAt(0).toUpperCase()+t.substr(1):t}var pu=xn;function gu(t,e){if("_blank"===e||"blank"===e){var i=window.open();i.opener=null,i.location=t}else window.open(t,e)}var mu=(Object.freeze||Object)({addCommas:tu,toCamelCase:eu,normalizeCssArray:iu,encodeHTML:au,formatTpl:lu,formatTplSimple:uu,getTooltipMarker:hu,formatTime:du,capitalFirst:fu,truncateText:pu,getTextBoundingRect:function(t){return gn(t.text,t.font,t.textAlign,t.textVerticalAlign,t.textPadding,t.textLineHeight,t.rich,t.truncate)},getTextRect:function(t,e,i,n,o,a,r,s){return gn(t,e,i,n,o,s,a,r)},windowOpen:gu}),vu=R,yu=["left","right","top","bottom","width","height"],xu=[["width","left","right"],["height","top","bottom"]];function _u(h,c,d,f,p){var g=0,m=0;null==f&&(f=1/0),null==p&&(p=1/0);var v=0;c.eachChild(function(t,e){var i,n,o=t.position,a=t.getBoundingRect(),r=c.childAt(e+1),s=r&&r.getBoundingRect();if("horizontal"===h){var l=a.width+(s?-s.x+a.x:0);v=f<(i=g+l)||t.newline?(g=0,i=l,m+=v+d,a.height):Math.max(v,a.height)}else{var u=a.height+(s?-s.y+a.y:0);v=p<(n=m+u)||t.newline?(g+=v+d,m=0,n=u,a.width):Math.max(v,a.width)}t.newline||(o[0]=g,o[1]=m,"horizontal"===h?g=i+d:m=n+d)})}var wu=_u;T(_u,"vertical"),T(_u,"horizontal");function bu(t,e,i){i=iu(i||0);var n=e.width,o=e.height,a=El(t.left,n),r=El(t.top,o),s=El(t.right,n),l=El(t.bottom,o),u=El(t.width,n),h=El(t.height,o),c=i[2]+i[0],d=i[1]+i[3],f=t.aspect;switch(isNaN(u)&&(u=n-s-d-a),isNaN(h)&&(h=o-l-c-r),null!=f&&(isNaN(u)&&isNaN(h)&&(n/oe)return t[n];return t[i-1]}(s,i):r;if((l=l||r)&&l.length){var u=l[o];return t&&(a[t]=u),n.colorIdx=(o+1)%l.length,u}}},zu="original",Bu="arrayRows",Vu="objectRows",Gu="keyedColumns",Fu="unknown",Wu="typedArray",Hu="column",Zu="row";function Uu(t){this.fromDataset=t.fromDataset,this.data=t.data||(t.sourceFormat===Gu?{}:[]),this.sourceFormat=t.sourceFormat||Fu,this.seriesLayoutBy=t.seriesLayoutBy||Hu,this.dimensionsDefine=t.dimensionsDefine,this.encodeDefine=t.encodeDefine&&Q(t.encodeDefine),this.startIndex=t.startIndex||0,this.dimensionsDetectCount=t.dimensionsDetectCount}Uu.seriesDataToSource=function(t){return new Uu({data:t,sourceFormat:V(t)?Wu:zu,fromDataset:!1})},ra(Uu);var Xu={Must:1,Might:2,Not:3},Yu=jo();function ju(t){var e=t.option,i=e.data,n=V(i)?Wu:zu,o=!1,a=e.seriesLayoutBy,r=e.sourceHeader,s=e.dimensions,l=Qu(t);if(l){var u=l.option;i=u.source,n=Yu(l).sourceFormat,o=!0,a=a||u.seriesLayoutBy,null==r&&(r=u.sourceHeader),s=s||u.dimensions}var h=function(t,e,i,n,o){if(!t)return{dimensionsDefine:qu(o)};var a,r;if(e===Bu)"auto"===n||null==n?Ku(function(t){null!=t&&"-"!==t&&(E(t)?null==r&&(r=1):r=0)},i,t,10):r=n?1:0,o||1!==r||(o=[],Ku(function(t,e){o[e]=null!=t?t:""},i,t)),a=o?o.length:i===Zu?t.length:t[0]?t[0].length:null;else if(e===Vu)o=o||function(t){var e,i=0;for(;i":"\n",f="richText"===c,p={},g=0;function i(t){return{renderMode:c,content:au(tu(t)),style:p}}var m=this.getData(),a=m.mapDimension("defaultedTooltip",!0),n=a.length,r=this.getRawValue(o),s=L(r),v=m.getItemVisual(o,"color");z(v)&&v.colorStops&&(v=(v.colorStops[0]||{}).color),v=v||"transparent";var l=(1":"",n=i+u.join(i||", ");return{renderMode:c,content:n,style:p}}(r):i(n?Hh(m,o,a[0]):s?r[0]:r)).content,u=d.seriesIndex+"at"+g,y=hu({color:v,type:"item",renderMode:c,markerId:u});p[u]=v,++g;var x=m.getName(o),_=this.name;Uo(this)||(_=""),_=_?au(_)+(h?": ":e):"";var w="string"==typeof y?y:y.content;return{html:h?w+_+l:_+w+(x?au(x)+": "+l:l),markers:p}},isAnimationEnabled:function(){if(v.node)return!1;var t=this.getShallow("animation");return t&&this.getData().count()>this.getShallow("animationThreshold")&&(t=!1),t},restoreData:function(){this.dataTask.dirty()},getColorFromPalette:function(t,e,i){var n=this.ecModel,o=Eu.getColorFromPalette.call(this,t,e,i);return o=o||n.getColorFromPalette(t,e,i)},coordDimToDataDim:function(t){return this.getRawData().mapDimension(t,!0)},getProgressive:function(){return this.get("progressive")},getProgressiveThreshold:function(){return this.get("progressiveThreshold")},getAxisTooltipData:null,getTooltipPosition:null,pipeTask:null,preventIncremental:null,pipelineContext:null});function lc(t){var e=t.name;Uo(t)||(t.name=function(t){var i=t.getRawData(),e=i.mapDimension("seriesName",!0),n=[];return R(e,function(t){var e=i.getDimensionInfo(t);e.displayName&&n.push(e.displayName)}),n.join(" ")}(t)||e)}function uc(t){return t.model.getRawData().count()}function hc(t){var e=t.model;return e.setData(e.getRawData().cloneShallow()),cc}function cc(t,e){e.outputData&&t.end>e.outputData.count()&&e.model.getRawData().cloneShallow(e.outputData)}function dc(e,i){R(e.CHANGABLE_METHODS,function(t){e.wrapMethod(t,T(fc,i))})}function fc(t){var e=pc(t);e&&e.setOutputEnd(this.count())}function pc(t){var e=(t.ecModel||{}).scheduler,i=e&&e.getPipeline(t.uid);if(i){var n=i.currentTask;if(n){var o=n.agentStubMap;o&&(n=o.get(t.uid))}return n}}b(sc,Xh),b(sc,Eu);var gc=function(){this.group=new Ci,this.uid=Nl("viewComponent")};gc.prototype={constructor:gc,init:function(t,e){},render:function(t,e,i,n){},dispose:function(){},filterForExposedEvent:null};var mc=gc.prototype;mc.updateView=mc.updateLayout=mc.updateVisual=function(t,e,i,n){},oa(gc),ua(gc,{registerWhenExtend:!0});function vc(){var s=jo();return function(t){var e=s(t),i=t.pipelineContext,n=e.large,o=e.progressiveRender,a=e.large=i&&i.large,r=e.progressiveRender=i&&i.progressiveRender;return!!(n^a||o^r)&&"reset"}}var yc=jo(),xc=vc();function _c(){this.group=new Ci,this.uid=Nl("viewChart"),this.renderTask=Yh({plan:Mc,reset:Ic}),this.renderTask.context={view:this}}var wc=_c.prototype={type:"chart",init:function(t,e){},render:function(t,e,i,n){},highlight:function(t,e,i,n){Sc(t.getData(),n,"emphasis")},downplay:function(t,e,i,n){Sc(t.getData(),n,"normal")},remove:function(t,e){this.group.removeAll()},dispose:function(){},incrementalPrepareRender:null,incrementalRender:null,updateTransform:null,filterForExposedEvent:null};function bc(t,e,i){if(t&&(t.trigger(e,i),t.isGroup&&!Qs(t)))for(var n=0,o=t.childCount();nc?i+=p(g("data.partialData"),{displayCnt:c}):i+=g("data.allData");for(var r=[],s=0;si.blockIndex?i.step:null,a=n&&n.modDataCount;return{step:o,modBy:null!=a?Math.ceil(a/o):null,modDataCount:a}}},Bc.getPipeline=function(t){return this._pipelineMap.get(t)},Bc.updateStreamModes=function(t,e){var i=this._pipelineMap.get(t.uid),n=t.getData().count(),o=i.progressiveEnabled&&e.incrementalPrepareRender&&n>=i.threshold,a=t.get("large")&&n>=t.get("largeThreshold"),r="mod"===t.get("progressiveChunkMode")?n:null;t.pipelineContext=i.context={progressiveRender:o,modDataCount:r,large:a}},Bc.restorePipelines=function(t){var n=this,o=n._pipelineMap=Q();t.eachSeries(function(t){var e=t.getProgressive(),i=t.uid;o.set(i,{id:i,head:null,tail:null,threshold:t.getProgressiveThreshold(),progressiveEnabled:e&&!(t.preventIncremental&&t.preventIncremental()),blockIndex:-1,step:Math.round(e||700),count:0}),Kc(n,t,t.dataTask)})},Bc.prepareStageTasks=function(){var i=this._stageTaskMap,n=this.ecInstance.getModel(),o=this.api;R(this._allHandlers,function(t){var e=i.get(t.uid)||i.set(t.uid,[]);t.reset&&function(n,o,t,a,r){var s=t.seriesTaskMap||(t.seriesTaskMap=Q()),e=o.seriesType,i=o.getTargetSeries;o.createOnAllSeries?a.eachRawSeries(l):e?a.eachRawSeriesByType(e,l):i&&i(a,r).each(l);function l(t){var e=t.uid,i=s.get(e)||s.set(e,Yh({plan:Uc,reset:Xc,count:qc}));i.context={model:t,ecModel:a,api:r,useClearVisual:o.isVisual&&!o.isLayout,plan:o.plan,reset:o.reset,scheduler:n},Kc(n,t,i)}var u=n._pipelineMap;s.each(function(t,e){u.get(e)||(t.dispose(),s.removeKey(e))})}(this,t,e,n,o),t.overallReset&&function(n,t,e,i,o){var a=e.overallTask=e.overallTask||Yh({reset:Fc});a.context={ecModel:i,api:o,overallReset:t.overallReset,scheduler:n};var r=a.agentStubMap=a.agentStubMap||Q(),s=t.seriesType,l=t.getTargetSeries,u=!0,h=t.modifyOutputEnd;s?i.eachRawSeriesByType(s,c):l?l(i,o).each(c):(u=!1,R(i.getSeries(),c));function c(t){var e=t.uid,i=r.get(e);i||(i=r.set(e,Yh({reset:Wc,onDirty:Zc})),a.dirty()),i.context={model:t,overallProgress:u,modifyOutputEnd:h},i.agent=a,i.__block=u,Kc(n,t,i)}var d=n._pipelineMap;r.each(function(t,e){d.get(e)||(t.dispose(),a.dirty(),r.removeKey(e))})}(this,t,e,n,o)},this)},Bc.prepareView=function(t,e,i,n){var o=t.renderTask,a=o.context;a.model=e,a.ecModel=i,a.api=n,o.__block=!t.incrementalPrepareRender,Kc(this,e,o)},Bc.performDataProcessorTasks=function(t,e){Vc(this,this._dataProcessorHandlers,t,e,{block:!0})},Bc.performVisualTasks=function(t,e,i){Vc(this,this._visualHandlers,t,e,i)},Bc.performSeriesTasks=function(t){var e;t.eachSeries(function(t){e|=t.dataTask.perform()}),this.unfinished|=e},Bc.plan=function(){this._pipelineMap.each(function(t){var e=t.tail;do{if(e.__block){t.blockIndex=e.__idxInPipeline;break}e=e.getUpstream()}while(e)})};var Gc=Bc.updatePayload=function(t,e){"remain"!==e&&(t.context.payload=e)};function Fc(t){t.overallReset(t.ecModel,t.api,t.payload)}function Wc(t,e){return t.overallProgress&&Hc}function Hc(){this.agent.dirty(),this.getDownstream().dirty()}function Zc(){this.agent&&this.agent.dirty()}function Uc(t){return t.plan&&t.plan(t.model,t.ecModel,t.api,t.payload)}function Xc(t){t.useClearVisual&&t.data.clearAllVisual();var e=t.resetDefines=Vo(t.reset(t.model,t.ecModel,t.api,t.payload));return 1'+t.dom+""}),p.painter.getSvgRoot().innerHTML=g,o.connectedBackgroundColor&&p.painter.setBackgroundColor(o.connectedBackgroundColor),p.refreshImmediately(),p.painter.toDataURL()}return o.connectedBackgroundColor&&p.add(new rs({shape:{x:0,y:0,width:t,height:e},style:{fill:o.connectedBackgroundColor}})),Td(f,function(t){var e=new Qn({style:{x:t.left*i-u,y:t.top*i-h,image:t.dom}});p.add(e)}),p.refreshImmediately(),n.toDataURL("image/"+(o&&o.type||"png"))}return this.getDataURL(o)}},zd.convertToPixel=T(Bd,"convertToPixel"),zd.convertFromPixel=T(Bd,"convertFromPixel"),zd.containPixel=function(t,o){var a;if(!this._disposed)return R(t=Ko(this._model,t),function(t,n){0<=n.indexOf("Models")&&R(t,function(t){var e=t.coordinateSystem;if(e&&e.containPoint)a|=!!e.containPoint(o);else if("seriesModels"===n){var i=this._chartsMap[t.__viewId];i&&i.containPoint&&(a|=i.containPoint(o,t))}},this)},this),!!a},zd.getVisual=function(t,e){var i=(t=Ko(this._model,t,{defaultMainType:"series"})).seriesModel.getData(),n=t.hasOwnProperty("dataIndexInside")?t.dataIndexInside:t.hasOwnProperty("dataIndex")?i.indexOfRawIndex(t.dataIndex):null;return null!=n?i.getItemVisual(n,e):i.getVisual(e)},zd.getViewOfComponentModel=function(t){return this._componentsMap[t.__viewId]},zd.getViewOfSeriesModel=function(t){return this._chartsMap[t.__viewId]};var Vd={prepareAndUpdate:function(t){Gd(this),Vd.update.call(this,t)},update:function(t){var e=this._model,i=this._api,n=this._zr,o=this._coordSysMgr,a=this._scheduler;if(e){a.restoreData(e,t),a.performSeriesTasks(e),o.create(e,i),a.performDataProcessorTasks(e,t),Wd(this,e),o.update(e,i),Yd(e),a.performVisualTasks(e,t),jd(this,e,i,t);var r=e.get("backgroundColor")||"transparent";if(v.canvasSupported)n.setBackgroundColor(r);else{var s=Fe(r);r=$e(s,"rgb"),0===s[3]&&(r="transparent")}Kd(e,i)}},updateTransform:function(o){var a=this._model,r=this,s=this._api;if(a){var l=[];a.eachComponent(function(t,e){var i=r.getViewOfComponentModel(e);if(i&&i.__alive)if(i.updateTransform){var n=i.updateTransform(e,a,s,o);n&&n.update&&l.push(i)}else l.push(i)});var n=Q();a.eachSeries(function(t){var e=r._chartsMap[t.__viewId];if(e.updateTransform){var i=e.updateTransform(t,a,s,o);i&&i.update&&n.set(t.uid,1)}else n.set(t.uid,1)}),Yd(a),this._scheduler.performVisualTasks(a,o,{setDirty:!0,dirtyMap:n}),qd(r,a,s,o,n),Kd(a,this._api)}},updateView:function(t){var e=this._model;e&&(_c.markUpdateMethod(t,"updateView"),Yd(e),this._scheduler.performVisualTasks(e,t,{setDirty:!0}),jd(this,this._model,this._api,t),Kd(e,this._api))},updateVisual:function(t){Vd.update.call(this,t)},updateLayout:function(t){Vd.update.call(this,t)}};function Gd(t){var e=t._model,i=t._scheduler;i.restorePipelines(e),i.prepareStageTasks(),Xd(t,"component",e,i),Xd(t,"chart",e,i),i.plan()}function Fd(e,i,n,o,t){var a=e._model;if(o){var r={};r[o+"Id"]=n[o+"Id"],r[o+"Index"]=n[o+"Index"],r[o+"Name"]=n[o+"Name"];var s={mainType:o,query:r};t&&(s.subType=t);var l=n.excludeSeriesId;null!=l&&(l=Q(Vo(l))),a&&a.eachComponent(s,function(t){l&&null!=l.get(t.id)||u(e["series"===o?"_chartsMap":"_componentsMap"][t.__viewId])},e)}else Td(e._componentsViews.concat(e._chartsViews),u);function u(t){t&&t.__alive&&t[i]&&t[i](t.__model,a,e._api,n)}}function Wd(t,e){var i=t._chartsMap,n=t._scheduler;e.eachSeries(function(t){n.updateStreamModes(t,i[t.__viewId])})}function Hd(e,t){var i=e.type,n=e.escapeConnect,o=tf[i],a=o.actionInfo,r=(a.update||"update").split(":"),s=r.pop();r=null!=r[0]&&Cd(r[0]),this[kd]=!0;var l=[e],u=!1;e.batch&&(u=!0,l=O(e.batch,function(t){return(t=D(P({},t),e)).batch=null,t}));var h,c=[],d="highlight"===i||"downplay"===i;Td(l,function(t){(h=(h=o.action(t,this._model,this._api))||P({},t)).type=a.event||h.type,c.push(h),d?Fd(this,s,t,"series"):r&&Fd(this,s,t,r.main,r.sub)},this),"none"===s||d||r||(this[Pd]?(Gd(this),Vd.update.call(this,e),this[Pd]=!1):Vd[s].call(this,e)),h=u?{type:a.event||i,escapeConnect:n,batch:c}:c[0],this[kd]=!1,t||this._messageCenter.trigger(h.type,h)}function Zd(t){for(var e=this._pendingActions;e.length;){var i=e.shift();Hd.call(this,i,t)}}function Ud(t){t||this.trigger("updated")}function Xd(t,e,o,a){for(var r="component"===e,s=r?t._componentsViews:t._chartsViews,l=r?t._componentsMap:t._chartsMap,u=t._zr,h=t._api,i=0;it.get("hoverLayerThreshold")&&!v.node&&t.eachSeries(function(t){if(!t.preventUsingHoverLayer){var e=i._chartsMap[t.__viewId];e.__alive&&e.group.traverse(function(t){t.useHoverLayer=!0})}})}(n,t),Rc(n._zr.dom,t)}function Kd(e,i){Td(af,function(t){t(e,i)})}zd.resize=function(t){if(!this._disposed){this._zr.resize(t);var e=this._model;if(this._loadingFX&&this._loadingFX.resize(),e){var i=e.resetOption("media"),n=t&&t.silent;this[kd]=!0,i&&Gd(this),Vd.update.call(this),this[kd]=!1,Zd.call(this,n),Ud.call(this,n)}}},zd.showLoading=function(t,e){if(!this._disposed&&(Dd(t)&&(e=t,t=""),t=t||"default",this.hideLoading(),lf[t])){var i=lf[t](this._api,e),n=this._zr;this._loadingFX=i,n.add(i)}},zd.hideLoading=function(){this._disposed||(this._loadingFX&&this._zr.remove(this._loadingFX),this._loadingFX=null)},zd.makeActionFromEvent=function(t){var e=P({},t);return e.type=ef[t.type],e},zd.dispatchAction=function(t,e){this._disposed||(Dd(e)||(e={silent:!!e}),tf[t.type]&&this._model&&(this[kd]?this._pendingActions.push(t):(Hd.call(this,t,e.silent),e.flush?this._zr.flush(!0):!1!==e.flush&&v.browser.weChat&&this._throttledZrFlush(),Zd.call(this,e.silent),Ud.call(this,e.silent))))},zd.appendData=function(t){if(!this._disposed){var e=t.seriesIndex;this.getModel().getSeriesByIndex(e).appendData(t),this._scheduler.unfinished=!0}},zd.on=Od("on",!1),zd.off=Od("off",!1),zd.one=Od("one",!1);var $d=["click","dblclick","mouseover","mouseout","mousemove","mousedown","mouseup","globalout","contextmenu"];function Jd(t,e){var i=t.get("z"),n=t.get("zlevel");e.group.traverse(function(t){"group"!==t.type&&(null!=i&&(t.z=i),null!=n&&(t.zlevel=n))})}function Qd(){this.eventInfo}zd._initEvents=function(){Td($d,function(u){function t(t){var e,i=this.getModel(),n=t.target;if("globalout"===u)e={};else if(n&&null!=n.dataIndex){var o=n.dataModel||i.getSeriesByIndex(n.seriesIndex);e=o&&o.getDataParams(n.dataIndex,n.dataType,n)||{}}else n&&n.eventData&&(e=P({},n.eventData));if(e){var a=e.componentType,r=e.componentIndex;"markLine"!==a&&"markPoint"!==a&&"markArea"!==a||(a="series",r=e.seriesIndex);var s=a&&null!=r&&i.getComponent(a,r),l=s&&this["series"===s.mainType?"_chartsMap":"_componentsMap"][s.__viewId];e.event=t,e.type=u,this._ecEventProcessor.eventInfo={targetEl:n,packedEvent:e,model:s,view:l},this.trigger(u,e)}}t.zrEventfulCallAtLast=!0,this._zr.on(u,t,this)},this),Td(ef,function(t,e){this._messageCenter.on(e,function(t){this.trigger(e,t)},this)},this)},zd.isDisposed=function(){return this._disposed},zd.clear=function(){this._disposed||this.setOption({series:[]},!0)},zd.dispose=function(){if(!this._disposed){this._disposed=!0,Jo(this.getDom(),ff,"");var e=this._api,i=this._model;Td(this._componentsViews,function(t){t.dispose(i,e)}),Td(this._chartsViews,function(t){t.dispose(i,e)}),this._zr.dispose(),delete uf[this.id]}},b(Ed,Ct),Qd.prototype={constructor:Qd,normalizeQuery:function(t){var s={},l={},u={};if(E(t)){var e=Cd(t);s.mainType=e.main||null,s.subType=e.sub||null}else{var h=["Index","Name","Id"],c={name:1,dataIndex:1,dataType:1};R(t,function(t,e){for(var i=!1,n=0;nx[1]&&(x[1]=y)}e&&(this._nameList[d]=e[f])}this._rawCount=this._count=l,this._extent={},Kf(this)},jf._initDataFromProvider=function(t,e){if(!(e<=t)){for(var i,n=this._chunkSize,o=this._rawData,a=this._storage,r=this.dimensions,s=r.length,l=this._dimensionInfos,u=this._nameList,h=this._idList,c=this._rawExtent,d=this._nameRepeatCount={},f=this._chunkCount,p=0;pM[1]&&(M[1]=S)}if(!o.pure){var I=u[v];if(m&&null==I)if(null!=m.name)u[v]=I=m.name;else if(null!=i){var T=r[i],A=a[T][y];if(A){I=A[x];var D=l[T].ordinalMeta;D&&D.categories.length&&(I=D.categories[I])}}var C=null==m?null:m.id;null==C&&null!=I&&(d[I]=d[I]||0,0=this._rawCount||t<0)return-1;if(!this._indices)return t;var e=this._indices,i=e[t];if(null!=i&&it))return a;o=a-1}}return-1},jf.indicesOfNearest=function(t,e,i){var n=[];if(!this._storage[t])return n;null==i&&(i=1/0);for(var o=1/0,a=-1,r=0,s=0,l=this.count();st[I][1])&&(M=!1)}M&&(a[r++]=this.getRawIndex(m))}return rw[1]&&(w[1]=_)}}}return o},jf.downSample=function(t,e,i,n){for(var o=ip(this,[t]),a=o._storage,r=[],s=Math.floor(1/e),l=a[t],u=this.count(),h=this._chunkSize,c=o._rawExtent[t],d=new(Hf(this))(u),f=0,p=0;pc[1]&&(c[1]=x),d[f++]=_}return o._count=f,o._indices=d,o.getRawIndex=Qf,o},jf.getItemModel=function(t){var e=this.hostModel;return new Cl(this.getRawDataItem(t),e,e&&e.ecModel)},jf.diff=function(e){var i=this;return new kf(e?e.getIndices():[],this.getIndices(),function(t){return tp(e,t)},function(t){return tp(i,t)})},jf.getVisual=function(t){var e=this._visual;return e&&e[t]},jf.setVisual=function(t,e){if(zf(t))for(var i in t)t.hasOwnProperty(i)&&this.setVisual(i,t[i]);else this._visual=this._visual||{},this._visual[t]=e},jf.setLayout=function(t,e){if(zf(t))for(var i in t)t.hasOwnProperty(i)&&this.setLayout(i,t[i]);else this._layout[t]=e},jf.getLayout=function(t){return this._layout[t]},jf.getItemLayout=function(t){return this._itemLayouts[t]},jf.setItemLayout=function(t,e,i){this._itemLayouts[t]=i?P(this._itemLayouts[t]||{},e):e},jf.clearItemLayouts=function(){this._itemLayouts.length=0},jf.getItemVisual=function(t,e,i){var n=this._itemVisuals[t],o=n&&n[e];return null!=o||i?o:this.getVisual(e)},jf.setItemVisual=function(t,e,i){var n=this._itemVisuals[t]||{},o=this.hasItemVisual;if(this._itemVisuals[t]=n,zf(e))for(var a in e)e.hasOwnProperty(a)&&(n[a]=e[a],o[a]=!0);else n[e]=i,o[e]=!0},jf.clearAllVisual=function(){this._visual={},this._itemVisuals=[],this.hasItemVisual={}};function ap(t){t.seriesIndex=this.seriesIndex,t.dataIndex=this.dataIndex,t.dataType=this.dataType}function rp(t,e,i){Uu.isInstance(e)||(e=Uu.seriesDataToSource(e)),i=i||{},t=(t||[]).slice();for(var n=(i.dimsDef||[]).slice(),o=Q(),a=Q(),l=[],r=function(t,e,i,n){var o=Math.max(t.dimensionsDetectCount||1,e.length,i.length,n||0);return R(e,function(t){var e=t.dimsDef;e&&(o=Math.max(o,e.length))}),o}(e,t,n,i.dimCount),s=0;s=e[0]&&t<=e[1]},mp.prototype.normalize=function(t){var e=this._extent;return e[1]===e[0]?.5:(t-e[0])/(e[1]-e[0])},mp.prototype.scale=function(t){var e=this._extent;return t*(e[1]-e[0])+e[0]},mp.prototype.unionExtent=function(t){var e=this._extent;t[0]e[1]&&(e[1]=t[1])},mp.prototype.unionExtentFromData=function(t,e){this.unionExtent(t.getApproximateExtent(e))},mp.prototype.getExtent=function(){return this._extent.slice()},mp.prototype.setExtent=function(t,e){var i=this._extent;isNaN(t)||(i[0]=t),isNaN(e)||(i[1]=e)},mp.prototype.isBlank=function(){return this._isBlank},mp.prototype.setBlank=function(t){this._isBlank=t},mp.prototype.getLabel=null,oa(mp),ua(mp,{registerWhenExtend:!0}),vp.createByAxisModel=function(t){var e=t.option,i=e.data,n=i&&O(i,_p);return new vp({categories:n,needCollect:!n,deduplication:!1!==e.dedplication})};var yp=vp.prototype;function xp(t){return t._map||(t._map=Q(t.categories))}function _p(t){return z(t)&&null!=t.value?t.value:t+""}yp.getOrdinal=function(t){return xp(this).get(t)},yp.parseAndCollect=function(t){var e,i=this._needCollect;if("string"!=typeof t&&!i)return t;if(i&&!this._deduplication)return e=this.categories.length,this.categories[e]=t,e;var n=xp(this);return null==(e=n.get(t))&&(i?(e=this.categories.length,this.categories[e]=t,n.set(t,e)):e=NaN),e};var wp=mp.prototype,bp=mp.extend({type:"ordinal",init:function(t,e){t&&!L(t)||(t=new vp({categories:t})),this._ordinalMeta=t,this._extent=e||[0,t.categories.length-1]},parse:function(t){return"string"==typeof t?this._ordinalMeta.getOrdinal(t):Math.round(t)},contain:function(t){return t=this.parse(t),wp.contain.call(this,t)&&null!=this._ordinalMeta.categories[t]},normalize:function(t){return wp.normalize.call(this,this.parse(t))},scale:function(t){return Math.round(wp.scale.call(this,t))},getTicks:function(){for(var t=[],e=this._extent,i=e[0];i<=e[1];)t.push(i),i++;return t},getLabel:function(t){if(!this.isBlank())return this._ordinalMeta.categories[t]},count:function(){return this._extent[1]-this._extent[0]+1},unionExtentFromData:function(t,e){this.unionExtent(t.getApproximateExtent(e))},getOrdinalMeta:function(){return this._ordinalMeta},niceTicks:et,niceExtent:et});bp.create=function(){return new bp};var Sp=zl;function Mp(t){return Gl(t)+2}function Ip(t,e,i){t[e]=Math.max(Math.min(t[e],i[1]),i[0])}function Tp(t,e){isFinite(t[0])||(t[0]=e[0]),isFinite(t[1])||(t[1]=e[1]),Ip(t,0,e),Ip(t,1,e),t[0]>t[1]&&(t[0]=t[1])}var Ap=zl,Dp=mp.extend({type:"interval",_interval:0,_intervalPrecision:2,setExtent:function(t,e){var i=this._extent;isNaN(t)||(i[0]=parseFloat(t)),isNaN(e)||(i[1]=parseFloat(e))},unionExtent:function(t){var e=this._extent;t[0]e[1]&&(e[1]=t[1]),Dp.prototype.setExtent.call(this,e[0],e[1])},getInterval:function(){return this._interval},setInterval:function(t){this._interval=t,this._niceExtent=this._extent.slice(),this._intervalPrecision=Mp(t)},getTicks:function(t){var e=this._interval,i=this._extent,n=this._niceExtent,o=this._intervalPrecision,a=[];if(!e)return a;i[0]s&&(t?a.push(Ap(s+e,o)):a.push(i[1])),a},getMinorTicks:function(t){for(var e=this.getTicks(!0),i=[],n=this.getExtent(),o=1;on[0]&&h>>1;t[o][1]>1^-(1&s),l=l>>1^-(1&l),o=s+=o,a=l+=a,n.push([s/i,l/i])}return n}Ag.prototype={constructor:Ag,properties:null,getBoundingRect:function(){var t=this._rect;if(t)return t;for(var e=Number.MAX_VALUE,i=[e,e],n=[-e,-e],o=[],a=[],r=this.geometries,s=0;ss[1];d(e[0].coord,s[0])&&(n?e[0].coord=s[0]:e.shift());n&&d(s[0],e[0].coord)&&e.unshift({coord:s[0]});d(s[1],a.coord)&&(n?a.coord=s[1]:e.pop());n&&d(a.coord,s[1])&&e.push({coord:s[1]});function d(t,e){return t=zl(t),e=zl(e),c?en[0]&&(n[0]=a[0]),a[1]>n[1]&&(n[1]=a[1])}return{min:e?i:n,max:e?n:i}}var xm=Ar.extend({type:"ec-polyline",shape:{points:[],smooth:0,smoothConstraint:!0,smoothMonotone:null,connectNulls:!1},style:{fill:null,stroke:"#000"},brush:Xr(Ar.prototype.brush),buildPath:function(t,e){var i=e.points,n=0,o=i.length,a=ym(i,e.smoothConstraint);if(e.connectNulls){for(;0n)return!1;return!0}(a,e))){var r=e.mapDimension(a.dim),s={};return R(a.getViewLabels(),function(t){s[t.tickValue]=1}),function(t){return!s.hasOwnProperty(e.get(r,t))}}}}function Cm(t,e,i){if("cartesian2d"!==t.type)return bm(t,e,i);var n=t.getBaseAxis().isHorizontal(),o=wm(t,e,i);if(!i.get("clip",!0)){var a=o.shape,r=Math.max(a.width,a.height);n?(a.y-=r,a.height+=2*r):(a.x-=r,a.width+=2*r)}return o}_c.extend({type:"line",init:function(){var t=new Ci,e=new im;this.group.add(e.group),this._symbolDraw=e,this._lineGroup=t},render:function(t,e,i){var n=t.coordinateSystem,o=this.group,a=t.getData(),r=t.getModel("lineStyle"),s=t.getModel("areaStyle"),l=a.mapArray(a.getItemLayout),u="polar"===n.type,h=this._coordSys,c=this._symbolDraw,d=this._polyline,f=this._polygon,p=this._lineGroup,g=t.get("animation"),m=!s.isEmpty(),v=s.get("origin"),y=function(t,e,i){if(!i.valueDim)return[];for(var n=[],o=0,a=e.count();oh[c-1].coord&&(h.reverse(),d.reverse());var f=h[0].coord-10,p=h[c-1].coord+10,g=p-f;if(g<.001)return"transparent";R(h,function(t){t.offset=(t.coord-f)/g}),h.push({offset:c?h[c-1].offset:.5,color:d[1]||"transparent"}),h.unshift({offset:c?h[0].offset:.5,color:d[0]||"transparent"});var m=new gs(0,0,0,0,h,!0);return m[n]=f,m[n+"2"]=p,m}}}(a,n)||a.getVisual("color");d.useStyle(D(r.getLineStyle(),{fill:"none",stroke:M,lineJoin:"bevel"}));var I=t.get("smooth");if(I=Tm(t.get("smooth")),d.setShape({smooth:I,smoothMonotone:t.get("smoothMonotone"),connectNulls:t.get("connectNulls")}),f){var T=a.getCalculationInfo("stackedOnSeries"),A=0;f.useStyle(D(s.getAreaStyle(),{fill:M,opacity:.7,lineJoin:"bevel"})),T&&(A=Tm(T.get("smooth"))),f.setShape({smooth:I,stackedOnSmooth:A,smoothMonotone:t.get("smoothMonotone"),connectNulls:t.get("connectNulls")})}this._data=a,this._coordSys=n,this._stackedOnPoints=y,this._points=l,this._step=S,this._valueOrigin=v},dispose:function(){},highlight:function(t,e,i,n){var o=t.getData(),a=Yo(o,n);if(!(a instanceof Array)&&null!=a&&0<=a){var r=o.getItemGraphicEl(a);if(!r){var s=o.getItemLayout(a);if(!s)return;if(this._clipShapeForSymbol&&!this._clipShapeForSymbol.contain(s[0],s[1]))return;(r=new Xg(o,a)).position=s,r.setZ(t.get("zlevel"),t.get("z")),r.ignore=isNaN(s[0])||isNaN(s[1]),r.__temp=!0,o.setItemGraphicEl(a,r),r.stopSymbolAnimation(!0),this.group.add(r)}r.highlight()}else _c.prototype.highlight.call(this,t,e,i,n)},downplay:function(t,e,i,n){var o=t.getData(),a=Yo(o,n);if(null!=a&&0<=a){var r=o.getItemGraphicEl(a);r&&(r.__temp?(o.setItemGraphicEl(a,null),this.group.remove(r)):r.downplay())}else _c.prototype.downplay.call(this,t,e,i,n)},_newPolyline:function(t){var e=this._polyline;return e&&this._lineGroup.remove(e),e=new xm({shape:{points:t},silent:!0,z2:10}),this._lineGroup.add(e),this._polyline=e},_newPolygon:function(t,e){var i=this._polygon;return i&&this._lineGroup.remove(i),i=new _m({shape:{points:t,stackedOnPoints:e},silent:!0}),this._lineGroup.add(i),this._polygon=i},_updateAnimation:function(t,e,i,n,o,a){var r=this._polyline,s=this._polygon,l=t.hostModel,u=function(t,e,i,n,o,a,r,s){for(var l=function(t,e){var i=[];return e.diff(t).add(function(t){i.push({cmd:"+",idx:t})}).update(function(t,e){i.push({cmd:"=",idx:e,idx1:t})}).remove(function(t){i.push({cmd:"-",idx:t})}).execute(),i}(t,e),u=[],h=[],c=[],d=[],f=[],p=[],g=[],m=sm(o,e,r),v=sm(a,t,s),y=0;ye&&(e=t[i]);return isFinite(e)?e:NaN},min:function(t){for(var e=1/0,i=0;ie[1]&&e.reverse(),e},getOtherAxis:function(){this.grid.getOtherAxis()},pointToData:function(t,e){return this.coordToData(this.toLocalCoord(t["x"===this.dim?0:1]),e)},toLocalCoord:null,toGlobalCoord:null},w(zm,Gg);var Bm={show:!0,zlevel:0,z:0,inverse:!1,name:"",nameLocation:"end",nameRotate:null,nameTruncate:{maxWidth:null,ellipsis:"...",placeholder:"."},nameTextStyle:{},nameGap:15,silent:!1,triggerEvent:!1,tooltip:{show:!1},axisPointer:{},axisLine:{show:!0,onZero:!0,onZeroAxisIndex:null,lineStyle:{color:"#333",width:1,type:"solid"},symbol:["none","none"],symbolSize:[10,15]},axisTick:{show:!0,inside:!1,length:5,lineStyle:{width:1}},axisLabel:{show:!0,inside:!1,rotate:0,showMinLabel:null,showMaxLabel:null,margin:8,fontSize:12},splitLine:{show:!0,lineStyle:{color:["#ccc"],width:1,type:"solid"}},splitArea:{show:!1,areaStyle:{color:["rgba(250,250,250,0.3)","rgba(200,200,200,0.3)"]}}},Vm={};Vm.categoryAxis=m({boundaryGap:!0,deduplication:null,splitLine:{show:!1},axisTick:{alignWithLabel:!1,interval:"auto"},axisLabel:{interval:"auto"}},Bm),Vm.valueAxis=m({boundaryGap:[0,0],splitNumber:5,minorTick:{show:!1,splitNumber:5,length:3,lineStyle:{}},minorSplitLine:{show:!1,lineStyle:{color:"#eee",width:1}}},Bm),Vm.timeAxis=D({scale:!0,min:"dataMin",max:"dataMax"},Vm.valueAxis),Vm.logAxis=D({scale:!0,logBase:10},Vm.valueAxis);function Gm(a,t,r,e){R(Fm,function(o){t.extend({type:a+"Axis."+o,mergeDefaultAndTheme:function(t,e){var i=this.layoutMode,n=i?Iu(t):{};m(t,e.getTheme().get(o+"Axis")),m(t,this.getDefaultOption()),t.type=r(a,t),i&&Mu(t,n,i)},optionUpdated:function(){"category"===this.option.type&&(this.__ordinalMeta=vp.createByAxisModel(this))},getCategories:function(t){var e=this.option;if("category"===e.type)return t?e.data:this.__ordinalMeta.categories},getOrdinalMeta:function(){return this.__ordinalMeta},defaultOption:p([{},Vm[o+"Axis"],e],!0)})}),ku.registerSubTypeDefaulter(a+"Axis",T(r,a))}var Fm=["value","category","time","log"],Wm=ku.extend({type:"cartesian2dAxis",axis:null,init:function(){Wm.superApply(this,"init",arguments),this.resetRange()},mergeOption:function(){Wm.superApply(this,"mergeOption",arguments),this.resetRange()},restoreData:function(){Wm.superApply(this,"restoreData",arguments),this.resetRange()},getCoordSysModel:function(){return this.ecModel.queryComponents({mainType:"grid",index:this.option.gridIndex,id:this.option.gridId})[0]}});function Hm(t,e){return e.type||(e.data?"category":"value")}m(Wm.prototype,dg);var Zm={offset:0};function Um(t,e){return t.getCoordSysModel()===e}function Xm(t,e,i){this._coordsMap={},this._coordsList=[],this._axesMap={},this._axesList=[],this._initCartesian(t,e,i),this.model=t}Gm("x",Wm,Hm,Zm),Gm("y",Wm,Hm,Zm),ku.extend({type:"grid",dependencies:["xAxis","yAxis"],layoutMode:"box",coordinateSystem:null,defaultOption:{show:!1,zlevel:0,z:0,left:"10%",top:60,right:"10%",bottom:60,containLabel:!1,backgroundColor:"rgba(0,0,0,0)",borderWidth:1,borderColor:"#ccc"}});var Ym=Xm.prototype;function jm(t,e,i,n){i.getAxesOnZeroOf=function(){return o?[o]:[]};var o,a=t[e],r=i.model,s=r.get("axisLine.onZero"),l=r.get("axisLine.onZeroAxisIndex");if(s){if(null!=l)qm(a[l])&&(o=a[l]);else for(var u in a)if(a.hasOwnProperty(u)&&qm(a[u])&&!n[h(a[u])]){o=a[u];break}o&&(n[h(o)]=!0)}function h(t){return t.dim+"_"+t.index}}function qm(t){return t&&"category"!==t.type&&"time"!==t.type&&function(t){var e=t.scale.getExtent(),i=e[0],n=e[1];return!(0u[1]?-1:1,c=["start"===a?u[0]-h*l:"end"===a?u[1]+h*l:(u[0]+u[1])/2,sv(a)?t.labelOffset+r*l:0],d=e.get("nameRotate");null!=d&&(d=d*tv/180),sv(a)?n=nv(t.rotation,null!=d?d:t.rotation,r):(n=function(t,e,i,n){var o,a,r=Zl(i-t.rotation),s=n[0]>n[1],l="start"===e&&!s||"start"!==e&&s;o=Ul(r-tv/2)?(a=l?"bottom":"top","center"):Ul(r-1.5*tv)?(a=l?"top":"bottom","center"):(a="middle",r<1.5*tv&&tv/2l[1]&&l.reverse(),(null==r||r>l[1])&&(r=l[1]),r=i.r0}}});var ny=Math.PI/180;function oy(o,t,e,i,n,a,r,s,l,u){function h(t,e,i){for(var n=t;nl+r);n++)if(o[n].y+=i,to[n].y+o[n].height)return void c(n,i/2);c(e-1,i/2)}function c(t,e){for(var i=t;0<=i&&!(o[i].y-eo[i-1].y+o[i-1].height));i--);}function d(t,e,i,n,o,a){for(var r=e?Number.MAX_VALUE:0,s=0,l=t.length;s=e?v.push(o[y]):m.push(o[y]);d(m,!1,t,e,i,n),d(v,!0,t,e,i,n)}function ay(t){return"center"===t.position}function ry(L,k,P,t,N,e){var O,R,E=L.getData(),z=[],B=!1,V=(L.get("minShowLabelAngle")||0)*ny;E.each(function(t){var e=E.getItemLayout(t),i=E.getItemModel(t),n=i.getModel("label"),o=n.get("position")||i.get("emphasis.label.position"),a=n.get("distanceToLabelLine"),r=n.get("alignTo"),s=El(n.get("margin"),P),l=n.get("bleedMargin"),u=n.getFont(),h=i.getModel("labelLine"),c=h.get("length");c=El(c,P);var d=h.get("length2");if(d=El(d,P),!(e.anglei[0]&&isFinite(h)&&isFinite(i[0]););else{var l=o.getTicks().length-1;c":"\n";return au(""===r?this.name:r)+s+O(a,function(t,e){var i=o.get(o.mapDimension(t.dim),n);return au(t.name+" : "+i)}).join(s)},getTooltipPosition:function(t){if(null!=t)for(var e=this.getData(),i=this.coordinateSystem,n=e.getValues(O(i.dimensions,function(t){return e.mapDimension(t)}),t,!0),o=0,a=n.length;o":"\n";return l.join(", ")+d+au(r+" : "+a)},getTooltipPosition:function(t){if(null!=t){var e=this.getData().getName(t),i=this.coordinateSystem,n=i.getRegion(e);return n&&i.dataToPoint(n.center)}},setZoom:function(t){this.option.zoom=t},setCenter:function(t){this.option.center=t},defaultOption:{zlevel:0,z:2,coordinateSystem:"geo",map:"",left:"center",top:"center",aspectScale:.75,showLegendSymbol:!0,dataRangeHoverLink:!0,boundingCoords:null,center:null,zoom:1,scaleLimit:null,label:{show:!1,color:"#000"},itemStyle:{borderWidth:.5,borderColor:"#444",areaColor:"#eee"},emphasis:{label:{show:!0,color:"rgb(100,0,0)"},itemStyle:{areaColor:"rgba(255,215,0,0.8)"}},nameProperty:"name"}}),jv);var Ey="\0_ec_interaction_mutex";function zy(t,e){return!!By(t)[e]}function By(t){return t[Ey]||(t[Ey]={})}function Vy(i){this.pointerChecker,this._zr=i,this._opt={};var t=A,n=t(Gy,this),o=t(Fy,this),a=t(Wy,this),r=t(Hy,this),s=t(Zy,this);Ct.call(this),this.setPointerChecker=function(t){this.pointerChecker=t},this.enable=function(t,e){this.disable(),this._opt=D(k(e)||{},{zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!1,preventDefaultMouseMove:!0}),null==t&&(t=!0),!0!==t&&"move"!==t&&"pan"!==t||(i.on("mousedown",n),i.on("mousemove",o),i.on("mouseup",a)),!0!==t&&"scale"!==t&&"zoom"!==t||(i.on("mousewheel",r),i.on("pinch",s))},this.disable=function(){i.off("mousedown",n),i.off("mousemove",o),i.off("mouseup",a),i.off("mousewheel",r),i.off("pinch",s)},this.dispose=this.disable,this.isDragging=function(){return this._dragging},this.isPinching=function(){return this._pinching}}function Gy(t){if(!(Yt(t)||t.target&&t.target.draggable)){var e=t.offsetX,i=t.offsetY;this.pointerChecker&&this.pointerChecker(t,e,i)&&(this._x=e,this._y=i,this._dragging=!0)}}function Fy(t){if(this._dragging&&Yy("moveOnMouseMove",t,this._opt)&&"pinch"!==t.gestureEvent&&!zy(this._zr,"globalPan")){var e=t.offsetX,i=t.offsetY,n=this._x,o=this._y,a=e-n,r=i-o;this._x=e,this._y=i,this._opt.preventDefaultMouseMove&&Xt(t.event),Xy(this,"pan","moveOnMouseMove",t,{dx:a,dy:r,oldX:n,oldY:o,newX:e,newY:i})}}function Wy(t){Yt(t)||(this._dragging=!1)}function Hy(t){var e=Yy("zoomOnMouseWheel",t,this._opt),i=Yy("moveOnMouseWheel",t,this._opt),n=t.wheelDelta,o=Math.abs(n),a=t.offsetX,r=t.offsetY;if(0!==n&&(e||i)){if(e){var s=3e&&(e=n.height)}this.height=e+1},getNodeById:function(t){if(this.getId()===t)return this;for(var e=0,i=this.children,n=i.length;ei&&(i=t.depth)});var a=t.expandAndCollapse&&0<=t.initialTreeDepth?t.initialTreeDepth:i;return o.root.eachNode("preorder",function(t){var e=t.hostTree.data.getRawDataItem(t.dataIndex);t.isExpand=e&&null!=e.collapsed?!e.collapsed:t.depth<=a}),o.data},getOrient:function(){var t=this.get("orient");return"horizontal"===t?t="LR":"vertical"===t&&(t="TB"),t},setZoom:function(t){this.option.zoom=t},setCenter:function(t){this.option.center=t},formatTooltip:function(t){for(var e=this.getData().tree,i=e.root.children[0],n=e.getNodeByDataIndex(t),o=n.getValue(),a=n.name;n&&n!==i;)a=n.parentNode.name+"."+a,n=n.parentNode;return au(a+(isNaN(o)||null==o?"":" : "+o))},defaultOption:{zlevel:0,z:2,coordinateSystem:"view",left:"12%",top:"12%",right:"12%",bottom:"12%",layout:"orthogonal",edgeShape:"curve",edgeForkPosition:"50%",roam:!1,nodeScaleRatio:.4,center:null,zoom:1,orient:"LR",symbol:"emptyCircle",symbolSize:7,expandAndCollapse:!0,initialTreeDepth:2,lineStyle:{color:"#ccc",width:1.5,curveness:.5},itemStyle:{color:"lightsteelblue",borderColor:"#c23531",borderWidth:1.5},label:{show:!0,color:"#555"},leaves:{label:{show:!0}},animationEasing:"linear",animationDuration:700,animationDurationUpdate:1e3}});var zx=Cs({shape:{parentPoint:[],childPoints:[],orient:"",forkPosition:""},style:{stroke:"#000",fill:null},buildPath:function(t,e){var i=e.childPoints,n=i.length,o=e.parentPoint,a=i[0],r=i[n-1];if(1===n)return t.moveTo(o[0],o[1]),void t.lineTo(a[0],a[1]);var s=e.orient,l="TB"===s||"BT"===s?0:1,u=1-l,h=El(e.forkPosition,1),c=[];c[l]=o[l],c[u]=o[u]+(r[u]-o[u])*h,t.moveTo(o[0],o[1]),t.lineTo(c[0],c[1]),t.moveTo(a[0],a[1]),c[l]=a[l],t.lineTo(c[0],c[1]),c[l]=r[l],t.lineTo(c[0],c[1]),t.lineTo(r[0],r[1]);for(var d=1;dx.x)||(m-=Math.PI);var b=v?"left":"right",S=a.labelModel.get("rotate"),M=S*(Math.PI/180);g.setStyle({textPosition:a.labelModel.get("position")||b,textRotation:null==S?-m:M,textOrigin:"center",verticalAlign:"middle"})}!function(t,e,i,n,o,a,r,s,l){var u=l.edgeShape,h=n.__edge;if("curve"===u)e.parentNode&&e.parentNode!==i&&cl(h=h||(n.__edge=new ds({shape:Wx(l,o,o),style:D({opacity:0,strokeNoScale:!0},l.lineStyle)})),{shape:Wx(l,a,r),style:D({opacity:1},l.lineStyle)},t);else if("polyline"===u&&"orthogonal"===l.layout&&e!==i&&e.children&&0!==e.children.length&&!0===e.isExpand){for(var c=e.children,d=[],f=0;fh.getLayout().x&&(h=t),t.depth>c.depth&&(c=t)});var d=u===h?1:r(u,h)/2,f=d-u.getLayout().x,p=0,g=0,m=0,v=0;if("radial"===n)p=o/(h.getLayout().x+d+f),g=a/(c.depth-1||1),Hx(l,function(t){m=(t.getLayout().x+f)*p,v=(t.depth-1)*g;var e=Px(m,v);t.setLayout({x:e.x,y:e.y,rawX:m,rawY:v},!0)});else{var y=t.getOrient();"RL"===y||"LR"===y?(g=a/(h.getLayout().x+d+f),p=o/(c.depth-1||1),Hx(l,function(t){v=(t.getLayout().x+f)*g,m="LR"===y?(t.depth-1)*p:o-(t.depth-1)*p,t.setLayout({x:m,y:v},!0)})):"TB"!==y&&"BT"!==y||(p=o/(h.getLayout().x+d+f),g=a/(c.depth-1||1),Hx(l,function(t){m=(t.getLayout().x+f)*p,v="TB"===y?(t.depth-1)*g:a-(t.depth-1)*g,t.setLayout({x:m,y:v},!0)}))}}}(t,e)})}),sc.extend({type:"series.treemap",layoutMode:"box",dependencies:["grid","polar"],preventUsingHoverLayer:!0,_viewRoot:null,defaultOption:{progressive:0,left:"center",top:"middle",right:null,bottom:null,width:"80%",height:"80%",sort:!0,clipWindow:"origin",squareRatio:.5*(1+Math.sqrt(5)),leafDepth:null,drillDownIcon:"▶",zoomToNodeRatio:.1024,roam:!0,nodeClick:"zoomToNode",animation:!0,animationDurationUpdate:900,animationEasing:"quinticInOut",breadcrumb:{show:!0,height:22,left:"center",top:"bottom",emptyItemWidth:25,itemStyle:{color:"rgba(0,0,0,0.7)",borderColor:"rgba(255,255,255,0.7)",borderWidth:1,shadowColor:"rgba(150,150,150,1)",shadowBlur:3,shadowOffsetX:0,shadowOffsetY:0,textStyle:{color:"#fff"}},emphasis:{textStyle:{}}},label:{show:!0,distance:0,padding:5,position:"inside",color:"#fff",ellipsis:!0},upperLabel:{show:!1,position:[0,"50%"],height:20,color:"#fff",ellipsis:!0,verticalAlign:"middle"},itemStyle:{color:null,colorAlpha:null,colorSaturation:null,borderWidth:0,gapWidth:0,borderColor:"#fff",borderColorSaturation:null},emphasis:{upperLabel:{show:!0,position:[0,"50%"],color:"#fff",ellipsis:!0,verticalAlign:"middle"}},visualDimension:0,visualMin:null,visualMax:null,color:[],colorAlpha:null,colorSaturation:null,colorMappingBy:"index",visibleMin:10,childrenVisibleMin:null,levels:[]},getInitialData:function(t,e){var i={name:t.name,children:t.data};!function i(t){var n=0;R(t.children,function(t){i(t);var e=t.value;L(e)&&(e=e[0]),n+=e});var e=t.value;L(e)&&(e=e[0]);null!=e&&!isNaN(e)||(e=n);e<0&&(e=0);L(t.value)?t.value[0]=e:t.value=e}(i);var n=t.levels||[],o=new Cl({itemStyle:this.designatedVisualItemStyle={}},this,e),a=O((n=t.levels=function(t,e){var n,i=e.get("color");if(!i)return;if(R(t=t||[],function(t){var e=new Cl(t),i=e.get("color");(e.get("itemStyle.color")||i&&"none"!==i)&&(n=!0)}),!n){(t[0]||(t[0]={})).color=i.slice()}return t}(n,e))||[],function(t){return new Cl(t,o,e)},this),r=Ax.createTree(i,this,function(t){t.wrapMethod("getItemModel",function(t,e){var i=r.getNodeByDataIndex(e),n=a[i.depth];return t.parentModel=n||o,t})});return r.data},optionUpdated:function(){this.resetViewRoot()},formatTooltip:function(t){var e=this.getData(),i=this.getRawValue(t),n=L(i)?tu(i[0]):tu(i);return au(e.getName(t)+": "+n)},getDataParams:function(t){var e=sc.prototype.getDataParams.apply(this,arguments),i=this.getData().tree.getNodeByDataIndex(t);return e.treePathInfo=Yx(i,this),e},setLayoutInfo:function(t){this.layoutInfo=this.layoutInfo||{},P(this.layoutInfo,t)},mapIdToIndex:function(t){var e=this._idIndexMap;e||(e=this._idIndexMap=Q(),this._idIndexMapCount=0);var i=e.get(t);return null==i&&e.set(t,i=this._idIndexMapCount++),i},getViewRoot:function(){return this._viewRoot},resetViewRoot:function(t){t?this._viewRoot=t:t=this._viewRoot;var e=this.getRawData().tree.root;t&&(t===e||e.contains(t))||(this._viewRoot=e)}});var jx=5;function qx(t){this.group=new Ci,t.add(this.group)}function Kx(t,e,i,n,o,a){var r=[[o?t:t-jx,e],[t+i,e],[t+i,e+n],[o?t:t-jx,e+n]];return a||r.splice(2,0,[t+i+jx,e+n/2]),o||r.push([t,e+n/2]),r}qx.prototype={constructor:qx,render:function(t,e,i,n){var o=t.getModel("breadcrumb"),a=this.group;if(a.removeAll(),o.get("show")&&i){var r=o.getModel("itemStyle"),s=r.getModel("textStyle"),l={pos:{left:o.get("left"),right:o.get("right"),top:o.get("top"),bottom:o.get("bottom")},box:{width:e.getWidth(),height:e.getHeight()},emptyItemWidth:o.get("emptyItemWidth"),totalWidth:0,renderList:[]};this._prepare(i,l,s),this._renderContent(t,l,r,s,n),Su(a,l.pos,l.box)}},_prepare:function(t,e,i){for(var n=t;n;n=n.parentNode){var o=n.getModel().get("name"),a=i.getTextRect(o),r=Math.max(a.width+16,e.emptyItemWidth);e.totalWidth+=r+8,e.renderList.push({node:n,text:o,width:r})}},_renderContent:function(t,e,i,n,o){for(var a,r,s=0,l=e.emptyItemWidth,u=t.get("breadcrumb.height"),h=function(t,e,i){var n=e.width,o=e.height,a=El(t.x,n),r=El(t.y,o),s=El(t.x2,n),l=El(t.y2,o);return(isNaN(a)||isNaN(parseFloat(t.x)))&&(a=0),(isNaN(s)||isNaN(parseFloat(t.x2)))&&(s=n),(isNaN(r)||isNaN(parseFloat(t.y)))&&(r=0),(isNaN(l)||isNaN(parseFloat(t.y2)))&&(l=o),i=iu(i||0),{width:Math.max(s-a-i[1]-i[3],0),height:Math.max(l-r-i[0]-i[2],0)}}(e.pos,e.box),c=e.totalWidth,d=e.renderList,f=d.length-1;0<=f;f--){var p=d[f],g=p.node,m=p.width,v=p.text;c>h.width&&(c-=m-l,m=l,v=null);var y=new Qr({shape:{points:Kx(s,0,m,u,f===d.length-1,0===f)},style:D(i.getItemStyle(),{lineJoin:"bevel",text:v,textFill:n.getTextColor(),textFont:n.getFont()}),z:10,onclick:T(o,g)});this.group.add(y),a=t,r=g,y.eventData={componentType:"series",componentSubType:"treemap",componentIndex:a.componentIndex,seriesIndex:a.componentIndex,seriesName:a.name,seriesType:"treemap",selfType:"breadcrumb",nodeData:{dataIndex:r&&r.dataIndex,name:r&&r.name},treePathInfo:r&&Yx(r,a)},s+=m+8}},remove:function(){this.group.removeAll()}};function $x(t){var e=s_(t);return e.stroke=e.fill=e.lineWidth=null,e}var Jx=A,Qx=Ci,t_=rs,e_=R,i_=["label"],n_=["emphasis","label"],o_=["upperLabel"],a_=["emphasis","upperLabel"],r_=10,s_=ha([["fill","color"],["stroke","strokeColor"],["lineWidth","strokeWidth"],["shadowBlur"],["shadowOffsetX"],["shadowOffsetY"],["shadowColor"]]);function l_(h,r,s,l,u,e,c,t,i,n){if(c){var d=c.getLayout(),o=h.getData();if(o.setItemGraphicEl(c.dataIndex,null),d&&d.isInView){var f=d.width,p=d.height,g=d.borderWidth,m=d.invisible,v=c.getRawIndex(),y=t&&t.getRawIndex(),a=c.viewChildren,x=d.upperHeight,_=a&&a.length,w=c.getModel("itemStyle"),b=c.getModel("emphasis.itemStyle"),S=L("nodeGroup",Qx);if(S){if(i.add(S),S.attr("position",[d.x||0,d.y||0]),S.__tmNodeWidth=f,S.__tmNodeHeight=p,d.isAboveViewRoot)return S;var M=c.getModel(),I=L("background",t_,n,1);if(I&&function(t,e,i){if(e.dataIndex=c.dataIndex,e.seriesIndex=h.seriesIndex,e.setShape({x:0,y:0,width:f,height:p}),m)A(e);else{e.invisible=!1;var n=c.getVisual("borderColor",!0),o=b.get("borderColor"),a=$x(w);a.fill=n;var r=s_(b);if(r.fill=o,i){var s=f-2*g;D(a,r,n,s,x,{x:g,y:0,width:s,height:x})}else a.text=r.text=null;e.setStyle(a),Us(e,r)}t.add(e)}(S,I,_&&d.upperLabelHeight),_)Qs(S)&&Js(S,!1),I&&(Js(I,!0),o.setItemGraphicEl(c.dataIndex,I));else{var T=L("content",t_,n,2);T&&function(t,e){e.dataIndex=c.dataIndex,e.seriesIndex=h.seriesIndex;var i=Math.max(f-2*g,0),n=Math.max(p-2*g,0);if(e.culling=!0,e.setShape({x:g,y:g,width:i,height:n}),m)A(e);else{e.invisible=!1;var o=c.getVisual("color",!0),a=$x(w);a.fill=o;var r=s_(b);D(a,r,o,i,n),e.setStyle(a),Us(e,r)}t.add(e)}(S,T),I&&Qs(I)&&Js(I,!1),Js(S,!0),o.setItemGraphicEl(c.dataIndex,S)}return S}}}function A(t){t.invisible||e.push(t)}function D(t,e,i,n,o,a){var r=M.get("name"),s=M.getModel(a?o_:i_),l=M.getModel(a?a_:n_),u=s.getShallow("show");el(t,e,s,l,{defaultText:u?r:null,autoColor:i,isRectText:!0,labelFetcher:h,labelDataIndex:c.dataIndex,labelProp:a?"upperLabel":"label"}),C(t,a,d),C(e,a,d),a&&(t.textRect=k(a)),t.truncate=u&&s.get("ellipsis")?{outerWidth:n,outerHeight:o,minChar:2}:null}function C(t,e,i){var n=t.text;if(!e&&i.isLeafRoot&&null!=n){var o=h.get("drillDownIcon",!0);t.text=o?o+" "+n:n}}function L(t,e,i,n){var o=null!=y&&s[t][y],a=u[t];return o?(s[t][y]=null,function(t,e,i){(t[v]={}).old="nodeGroup"===i?e.position.slice():P({},e.shape)}(a,o,t)):m||((o=new e({z:function(t,e){var i=t*r_+e;return(i-1)/i}(i,n)})).__tmDepth=i,function(t,e,i){var n=t[v]={},o=c.parentNode;if(o&&(!l||"drillDown"===l.direction)){var a=0,r=0,s=u.background[o.getRawIndex()];!l&&s&&s.old&&(a=s.old.width,r=s.old.height),n.old="nodeGroup"===i?[0,r]:{x:a,y:r,width:0,height:0}}n.fadein="nodeGroup"!==i}(a,0,o.__tmStorageName=t)),r[t][v]=o}}Cf({type:"treemap",init:function(t,e){this._containerGroup,this._storage={nodeGroup:[],background:[],content:[]},this._oldTree,this._breadcrumb,this._controller,this._state="ready"},render:function(t,e,i,n){if(!(_(e.findComponents({mainType:"series",subType:"treemap",query:n}),t)<0)){this.seriesModel=t,this.api=i,this.ecModel=e;var o=Zx(n,["treemapZoomToNode","treemapRootToNode"],t),a=n&&n.type,r=t.layoutInfo,s=!this._oldTree,l=this._storage,u="treemapRootToNode"===a&&o&&l?{rootNodeGroup:l.nodeGroup[o.node.getRawIndex()],direction:n.direction}:null,h=this._giveContainerGroup(r),c=this._doRender(h,t,u);s||a&&"treemapZoomToNode"!==a&&"treemapRootToNode"!==a?c.renderFinally():this._doAnimation(h,c,t,u),this._resetController(i),this._renderBreadcrumb(t,i,o)}},_giveContainerGroup:function(t){var e=this._containerGroup;return e||(e=this._containerGroup=new Qx,this._initEvents(e),this.group.add(e)),e.attr("position",[t.x,t.y]),e},_doRender:function(t,e,i){var n=e.getData().tree,o=this._oldTree,a={nodeGroup:[],background:[],content:[]},r={nodeGroup:[],background:[],content:[]},s=this._storage,l=[],c=T(l_,e,r,s,i,a,l);!function a(r,s,l,u,h){u?e_(s=r,function(t,e){t.isRemoved()||i(e,e)}):new kf(s,r,t,t).add(i).update(i).remove(T(i,null)).execute();function t(t){return t.getId()}function i(t,e){var i=null!=t?r[t]:null,n=null!=e?s[e]:null,o=c(i,n,l,h);o&&a(i&&i.viewChildren||[],n&&n.viewChildren||[],o,u,h+1)}}(n.root?[n.root]:[],o&&o.root?[o.root]:[],t,n===o||!o,0);var u,h,d=(h={nodeGroup:[],background:[],content:[]},(u=s)&&e_(u,function(t,e){var i=h[e];e_(t,function(t){t&&(i.push(t),t.__tmWillDelete=1)})}),h);return this._oldTree=n,this._storage=r,{lastsForAnimation:a,willDeleteEls:d,renderFinally:function(){e_(d,function(t){e_(t,function(t){t.parent&&t.parent.remove(t)})}),e_(l,function(t){t.invisible=!0,t.dirty()})}}},_doAnimation:function(t,a,e,s){if(e.get("animation")){var l=e.get("animationDurationUpdate"),u=e.get("animationEasing"),h=function(){var a,r=[],s={};return{add:function(t,e,i,n,o){return E(n)&&(o=n,n=0),!s[t.id]&&(s[t.id]=1,r.push({el:t,target:e,time:i,delay:n,easing:o}),!0)},done:function(t){return a=t,this},start:function(){for(var t=r.length,e=0,i=r.length;e=o.length||t===o[t.depth]){var i=E_(r,l,t,e,g,a);n(t,i,o,a)}})}else c=P_(l),t.setVisual("color",c)}(o,{},t.getViewRoot().getAncestors(),t)}};function k_(i,n,t){var o=P({},n),a=t.designatedVisualItemStyle;return R(["color","colorAlpha","colorSaturation"],function(t){a[t]=n[t];var e=i.get(t);(a[t]=null)!=e&&(o[t]=e)}),o}function P_(t){var e=N_(t,"color");if(e){var i=N_(t,"colorAlpha"),n=N_(t,"colorSaturation");return n&&(e=qe(e,null,null,n)),i&&(e=Ke(e,i)),e}}function N_(t,e){var i=t[e];if(null!=i&&"none"!==i)return i}function O_(t,e,i,n,o,a){if(a&&a.length){var r=R_(e,"color")||null!=o.color&&"none"!==o.color&&(R_(e,"colorAlpha")||R_(e,"colorSaturation"));if(r){var s=e.get("visualMin"),l=e.get("visualMax"),u=i.dataExtent.slice();null!=s&&su[1]&&(u[1]=l);var h=e.get("colorMappingBy"),c={type:r.name,dataExtent:u,visual:r.range};"color"!==c.type||"index"!==h&&"id"!==h?c.mappingMethod="linear":(c.mappingMethod="category",c.loop=!0);var d=new g_(c);return d.__drColorMappingBy=h,d}}}function R_(t,e){var i=t.get(e);return D_(i)&&i.length?{name:e,range:i}:null}function E_(t,e,i,n,o,a){var r=P({},e);if(o){var s=o.type,l="color"===s&&o.__drColorMappingBy,u="index"===l?n:"id"===l?a.mapIdToIndex(i.getId()):i.getValue(t.get("visualDimension"));r[s]=o.mapValueToVisual(u)}return r}var z_=Math.max,B_=Math.min,V_=W,G_=R,F_=["itemStyle","borderWidth"],W_=["itemStyle","gapWidth"],H_=["upperLabel","show"],Z_=["upperLabel","height"],U_={seriesType:"treemap",reset:function(t,e,i,n){var o=i.getWidth(),a=i.getHeight(),r=t.option,s=bu(t.getBoxLayoutParams(),{width:i.getWidth(),height:i.getHeight()}),l=r.size||[],u=El(V_(s.width,l[0]),o),h=El(V_(s.height,l[1]),a),c=n&&n.type,d=Zx(n,["treemapZoomToNode","treemapRootToNode"],t),f="treemapRender"===c||"treemapMove"===c?n.rootRect:null,p=t.getViewRoot(),g=Ux(p);if("treemapMove"!==c){var m="treemapZoomToNode"===c?function(t,e,i,n,o){var a,r=(e||{}).node,s=[n,o];if(!r||r===i)return s;var l=n*o,u=l*t.option.zoomToNodeRatio;for(;a=r.parentNode;){for(var h=0,c=a.children,d=0,f=c.length;ds[1]&&(s[1]=e)})}else s=[NaN,NaN];return{sum:n,dataExtent:s}}(e,r,s);if(0===u.sum)return t.viewChildren=[];if(u.sum=function(t,e,i,n,o){if(!n)return i;for(var a=t.get("visibleMin"),r=o.length,s=r,l=r-1;0<=l;l--){var u=o["asc"===n?r-l-1:l].getValue();u/i*ei[l[r]])&&(h=i[l[r]]);for(var c=0,d=t.length;c "+d)),u++)}var f,p=i.get("coordinateSystem");if("cartesian2d"===p||"polar"===p)f=gp(t,i);else{var g=lh.get(p),m=g&&"view"!==g.type&&g.dimensions||[];_(m,"value")<0&&m.concat(["value"]);var v=lp(t,{coordDimensions:m});(f=new Yf(v,i)).initData(t)}var y=new Yf(["value"],i);return y.initData(l,s),o&&o(f,y),yx({mainData:f,struct:a,structAttr:"graph",datas:{node:f,edge:y},datasAttr:{node:"data",edge:"edgeData"}}),a.update(),a}var nw="--\x3e",ow=function(t){return t.get("autoCurveness")||null},aw=function(t,e){var i=ow(t),n=20,o=[];if("number"==typeof i)n=i;else if(L(i))return void(t.__curvenessList=i);n ")),o.value&&(l+=" : "+au(o.value)),l},_updateCategoriesData:function(){var t=O(this.option.categories||[],function(t){return null!=t.value?t:P({value:0},t)}),e=new Yf(["value"],this);e.initData(t),this._categoriesData=e,this._categoriesModels=e.mapArray(function(t){return e.getItemModel(t,!0)})},setZoom:function(t){this.option.zoom=t},setCenter:function(t){this.option.center=t},isAnimationEnabled:function(){return dw.superCall(this,"isAnimationEnabled")&&!("force"===this.get("layout")&&this.get("force.layoutAnimation"))},defaultOption:{zlevel:0,z:2,coordinateSystem:"view",legendHoverLink:!0,hoverAnimation:!0,layout:null,focusNodeAdjacency:!1,circular:{rotateLabel:!1},force:{initLayout:null,repulsion:[0,50],gravity:.1,friction:.6,edgeLength:30,layoutAnimation:!0},left:"center",top:"center",symbol:"circle",symbolSize:10,edgeSymbol:["none","none"],edgeSymbolSize:10,edgeLabel:{position:"middle",distance:5},draggable:!1,roam:!1,center:null,zoom:1,nodeScaleRatio:.6,label:{show:!1,formatter:"{b}"},itemStyle:{},lineStyle:{color:"#aaa",width:1,opacity:.5},emphasis:{label:{show:!0}}}}),fw=ls.prototype,pw=ds.prototype;function gw(t){return isNaN(+t.cpx1)||isNaN(+t.cpy1)}var mw=Cs({type:"ec-line",style:{stroke:"#000",fill:null},shape:{x1:0,y1:0,x2:0,y2:0,percent:1,cpx1:null,cpy1:null},buildPath:function(t,e){this[gw(e)?"_buildPathLine":"_buildPathCurve"](t,e)},_buildPathLine:fw.buildPath,_buildPathCurve:pw.buildPath,pointAt:function(t){return this[gw(this.shape)?"_pointAtLine":"_pointAtCurve"](t)},_pointAtLine:fw.pointAt,_pointAtCurve:pw.pointAt,tangentAt:function(t){var e=this.shape,i=gw(e)?[e.x2-e.x1,e.y2-e.y1]:this._tangentAtCurve(t);return mt(i,i)},_tangentAtCurve:pw.tangentAt}),vw=["fromSymbol","toSymbol"];function yw(t){return"_"+t+"Type"}function xw(t,e,i){var n=e.getItemVisual(i,t);if(n&&"none"!==n){var o=e.getItemVisual(i,"color"),a=e.getItemVisual(i,t+"Size"),r=e.getItemVisual(i,t+"Rotate");L(a)||(a=[a,a]);var s=wg(n,-a[0]/2,-a[1]/2,a[0],a[1],o);return s.__specifiedRotation=null==r||isNaN(r)?void 0:+r*Math.PI/180||0,s.name=t,s}}function _w(t,e){t.x1=e[0][0],t.y1=e[0][1],t.x2=e[1][0],t.y2=e[1][1],t.percent=1;var i=e[2];i?(t.cpx1=i[0],t.cpy1=i[1]):(t.cpx1=NaN,t.cpy1=NaN)}function ww(t,e,i){Ci.call(this),this._createLine(t,e,i)}var bw=ww.prototype;function Sw(t){this._ctor=t||ww,this.group=new Ci}bw.beforeUpdate=function(){var t=this.childOfName("fromSymbol"),e=this.childOfName("toSymbol"),i=this.childOfName("label");if(t||e||!i.ignore){for(var n=1,o=this.parent;o;)o.scale&&(n/=o.scale[0]),o=o.parent;var a=this.childOfName("line");if(this.__dirty||a.__dirty){var r=a.shape.percent,s=a.pointAt(0),l=a.pointAt(r),u=ht([],l,s);if(mt(u,u),t){if(t.attr("position",s),null==(c=t.__specifiedRotation)){var h=a.tangentAt(0);t.attr("rotation",Math.PI/2-Math.atan2(h[1],h[0]))}else t.attr("rotation",c);t.attr("scale",[n*r,n*r])}if(e){var c;if(e.attr("position",l),null==(c=e.__specifiedRotation)){h=a.tangentAt(1);e.attr("rotation",-Math.PI/2-Math.atan2(h[1],h[0]))}else e.attr("rotation",c);e.attr("scale",[n*r,n*r])}if(!i.ignore){var d,f,p,g;i.attr("position",l);var m=i.__labelDistance,v=m[0]*n,y=m[1]*n,x=r/2,_=[(h=a.tangentAt(x))[1],-h[0]],w=a.pointAt(x);0<_[1]&&(_[0]=-_[0],_[1]=-_[1]);var b,S=h[0]<0?-1:1;if("start"!==i.__position&&"end"!==i.__position){var M=-Math.atan2(h[1],h[0]);l[0]=t&&(0===e?0:n[e-1][0])a&&(e[1-n]=e[n]+c.sign*a),e}function lb(t,e){var i=t[e]-t[1-e];return{span:Math.abs(i),sign:0o*(1-h[0])?(l="jump",r=s-o*(1-h[2])):0<=(r=s-o*h[1])&&(r=s-o*(1-h[1]))<=0&&(r=0),(r*=e.axisExpandWidth/u)?sb(r,n,a,"all"):l="none";else{o=n[1]-n[0];(n=[db(0,a[1]*s/o-o/2)])[1]=cb(a[1],n[0]+o),n[0]=n[1]-o}return{axisExpandWindow:n,behavior:l}}},lh.register("parallel",{create:function(n,o){var a=[];return n.eachComponent("parallel",function(t,e){var i=new vb(t,n,o);i.name="parallel_"+e,i.resize(t,o),(t.coordinateSystem=i).model=t,a.push(i)}),n.eachSeries(function(t){if("parallel"===t.get("coordinateSystem")){var e=n.queryComponents({mainType:"parallel",index:t.get("parallelIndex"),id:t.get("parallelId")})[0];t.coordinateSystem=e.coordinateSystem}}),a}});var xb=ku.extend({type:"baseParallelAxis",axis:null,activeIntervals:[],getAreaSelectStyle:function(){return ha([["fill","color"],["lineWidth","borderWidth"],["stroke","borderColor"],["width","width"],["opacity","opacity"]])(this.getModel("areaSelectStyle"))},setActiveIntervals:function(t){var e=this.activeIntervals=k(t);if(e)for(var i=e.length-1;0<=i;i--)Bl(e[i])},getActiveState:function(t){var e=this.activeIntervals;if(!e.length)return"normal";if(null==t||isNaN(t))return"inactive";if(1===e.length){var i=e[0];if(i[0]<=t&&t<=i[1])return"active"}else for(var n=0,o=e.length;nn.getWidth()||i<0||i>n.getHeight()}(t,e)){var n=t._zr,o=t._covers,a=Fb(t,e,i);if(!t._dragging)for(var r=0;rf&&(f=m.depth),g.setLayout({depth:v?m.depth:c},!0),"vertical"===a?g.setLayout({dy:i},!0):g.setLayout({dx:i},!0);for(var y=0;y "))},preventIncremental:function(){return!!this.get("effect.show")},getProgressive:function(){var t=this.option.progressive;return null==t?this.option.large?1e4:this.get("progressive"):t},getProgressiveThreshold:function(){var t=this.option.progressiveThreshold;return null==t?this.option.large?2e4:this.get("progressiveThreshold"):t},defaultOption:{coordinateSystem:"geo",zlevel:0,z:2,legendHoverLink:!0,hoverAnimation:!0,xAxisIndex:0,yAxisIndex:0,symbol:["none","none"],symbolSize:[10,10],geoIndex:0,effect:{show:!1,period:4,constantSpeed:0,symbol:"circle",symbolSize:3,loop:!0,trailLength:.2},large:!1,largeThreshold:2e3,polyline:!1,clip:!0,label:{show:!1,position:"end"},lineStyle:{opacity:.5}}});function bM(t,e,i){Ci.call(this),this.add(this.createLine(t,e,i)),this._updateEffectSymbol(t,e)}var SM=bM.prototype;function MM(t,e,i){Ci.call(this),this._createPolyline(t,e,i)}SM.createLine=function(t,e,i){return new ww(t,e,i)},SM._updateEffectSymbol=function(t,e){var i=t.getItemModel(e).getModel("effect"),n=i.get("symbolSize"),o=i.get("symbol");L(n)||(n=[n,n]);var a=i.get("color")||t.getItemVisual(e,"color"),r=this.childAt(1);this._symbolType!==o&&(this.remove(r),(r=wg(o,-.5,-.5,1,1,a)).z2=100,r.culling=!0,this.add(r)),r&&(r.setStyle("shadowColor",a),r.setStyle(i.getItemStyle(["color"])),r.attr("scale",n),r.setColor(a),r.attr("scale",n),this._symbolType=o,this._symbolScale=n,this._updateEffectAnimation(t,i,e))},SM._updateEffectAnimation=function(e,t,i){var n=this.childAt(1);if(n){var o=this,a=e.getItemLayout(i),r=1e3*t.get("period"),s=t.get("loop"),l=t.get("constantSpeed"),u=W(t.get("delay"),function(t){return t/e.count()*r/3}),h="function"==typeof u;if(n.ignore=!0,this.updateAnimationPoints(n,a),0e);r++);r=Math.min(r-1,o-2)}wt(t.position,i[r],i[r+1],(e-n[r])/(n[r+1]-n[r]));var s=i[r+1][0]-i[r][0],l=i[r+1][1]-i[r][1];t.rotation=-Math.atan2(l,s)-Math.PI/2,this._lastFrame=r,this._lastFramePercent=e,t.ignore=!1}},w(TM,bM);var DM=Cs({shape:{polyline:!1,curveness:0,segs:[]},buildPath:function(t,e){var i=e.segs,n=e.curveness;if(e.polyline)for(var o=0;o=e[0]&&t<=e[1]}}(y,e.option.range):function(e,n,o){var i=e[1]-e[0],a=(n=O(n,function(t){return{interval:[(t.interval[0]-e[0])/i,(t.interval[1]-e[0])/i]}})).length,r=0;return function(t){for(var e=r;e=e.y&&t[1]<=e.y+e.height:i.contain(i.toLocalCoord(t[1]))&&t[0]>=e.y&&t[0]<=e.y+e.height},pointToData:function(t){var e=this.getAxis();return[e.coordToData(e.toLocalCoord(t["horizontal"===e.orient?0:1]))]},dataToPoint:function(t){var e=this.getAxis(),i=this.getRect(),n=[],o="horizontal"===e.orient?0:1;return t instanceof Array&&(t=t[0]),n[o]=e.toGlobalCoord(e.dataToCoord(+t)),n[1-o]=0==o?i.y+i.height/2:i.x+i.width/2,n}}).dimensions});var sI=["axisLine","axisTickLabel","axisName"],lI=["splitArea","splitLine"],uI=mv.extend({type:"singleAxis",axisPointerClass:"SingleAxisPointer",render:function(e,t,i,n){var o=this.group;o.removeAll();var a=this._axisGroup;this._axisGroup=new Ci;var r=rI(e),s=new Qm(e,r);R(sI,s.add,s),o.add(this._axisGroup),o.add(s.getGroup()),R(lI,function(t){e.get(t+".show")&&this["_"+t](e)},this),ml(a,this._axisGroup,e),uI.superCall(this,"render",e,t,i,n)},remove:function(){bv(this)},_splitLine:function(t){var e=t.axis;if(!e.scale.isBlank()){var i=t.getModel("splitLine"),n=i.getModel("lineStyle"),o=n.get("width"),a=n.get("color");a=a instanceof Array?a:[a];for(var r=t.coordinateSystem.getRect(),s=e.isHorizontal(),l=[],u=0,h=e.getTicksCoords({tickModel:i}),c=[],d=[],f=0;fr)return!0;if(a){var s=fv(t).seriesDataCount,l=n.getExtent();return Math.abs(l[0]-l[1])/s>r}return!1},makeElOption:function(t,e,i,n,o){},createPointerEl:function(t,e,i,n){var o=e.pointer;if(o){var a=AI(t).pointerEl=new bl[o.type](DI(e.pointer));t.add(a)}},createLabelEl:function(t,e,i,n){if(e.label){var o=AI(t).labelEl=new rs(DI(e.label));t.add(o),PI(o,n)}},updatePointerEl:function(t,e,i){var n=AI(t).pointerEl;n&&e.pointer&&(n.setStyle(e.pointer.style),i(n,{shape:e.pointer.shape}))},updateLabelEl:function(t,e,i,n){var o=AI(t).labelEl;o&&(o.setStyle(e.label.style),i(o,{shape:e.label.shape,position:e.label.position}),PI(o,n))},_renderHandle:function(t){if(!this._dragging&&this.updateHandleTransform){var e,i=this._axisPointerModel,n=this._api.getZr(),o=this._handle,a=i.getModel("handle"),r=i.get("status");if(!a.get("show")||!r||"hide"===r)return o&&n.remove(o),void(this._handle=null);this._handle||(e=!0,o=this._handle=yl(a.get("icon"),{cursor:"move",draggable:!0,onmousemove:function(t){Xt(t.event)},onmousedown:CI(this._onHandleDragMove,this,0,0),drift:CI(this._onHandleDragMove,this),ondragend:CI(this._onHandleDragEnd,this)}),n.add(o)),OI(o,i,!1);o.setStyle(a.getItemStyle(null,["color","borderColor","borderWidth","opacity","shadowColor","shadowBlur","shadowOffsetX","shadowOffsetY"]));var s=a.get("size");L(s)||(s=[s,s]),o.attr("scale",[s[0]/2,s[1]/2]),kc(this,"_doDispatchAxisPointer",a.get("throttle")||0,"fixRate"),this._moveHandleToValue(t,e)}},_moveHandleToValue:function(t,e){kI(this._axisPointerModel,!e&&this._moveAnimation,this._handle,NI(this.getHandleTransform(t,this._axisModel,this._axisPointerModel)))},_onHandleDragMove:function(t,e){var i=this._handle;if(i){this._dragging=!0;var n=this.updateHandleTransform(NI(i),[t,e],this._axisModel,this._axisPointerModel);this._payloadInfo=n,i.stopAnimation(),i.attr(NI(n)),AI(i).lastProp=null,this._doDispatchAxisPointer()}},_doDispatchAxisPointer:function(){if(this._handle){var t=this._payloadInfo,e=this._axisModel;this._api.dispatchAction({type:"updateAxisPointer",x:t.cursorPoint[0],y:t.cursorPoint[1],tooltipOption:t.tooltipOption,axesInfo:[{axisDim:e.axis.dim,axisIndex:e.componentIndex}]})}},_onHandleDragEnd:function(t){if(this._dragging=!1,this._handle){var e=this._axisPointerModel.get("value");this._moveHandleToValue(e),this._api.dispatchAction({type:"hideTip"})}},getHandleTransform:null,updateHandleTransform:null,clear:function(t){this._lastValue=null,this._lastStatus=null;var e=t.getZr(),i=this._group,n=this._handle;e&&i&&(this._lastGraphicKey=null,i&&e.remove(i),n&&e.remove(n),this._group=null,this._handle=null,this._payloadInfo=null)},doClear:function(){},buildLabel:function(t,e,i){return{x:t[i=i||0],y:t[1-i],width:e[i],height:e[1-i]}}}).constructor=LI);var HI=LI.extend({makeElOption:function(t,e,i,n,o){var a=i.axis,r=a.grid,s=n.get("type"),l=ZI(r,a).getOtherAxis(a).getGlobalExtent(),u=a.toGlobalCoord(a.dataToCoord(e,!0));if(s&&"none"!==s){var h=RI(n),c=UI[s](a,u,l);c.style=h,t.graphicKey=c.type,t.pointer=c}VI(e,t,_v(r.model,i),i,n,o)},getHandleTransform:function(t,e,i){var n=_v(e.axis.grid.model,e,{labelInside:!1});return n.labelMargin=i.get("handle.margin"),{position:BI(e.axis,t,n),rotation:n.rotation+(n.labelDirection<0?Math.PI:0)}},updateHandleTransform:function(t,e,i,n){var o=i.axis,a=o.grid,r=o.getGlobalExtent(!0),s=ZI(a,o).getOtherAxis(o).getGlobalExtent(),l="x"===o.dim?0:1,u=t.position;u[l]+=e[l],u[l]=Math.min(r[1],u[l]),u[l]=Math.max(r[0],u[l]);var h=(s[1]+s[0])/2,c=[h,h];c[l]=u[l];return{position:u,rotation:t.rotation,cursorPoint:c,tooltipOption:[{verticalAlign:"middle"},{align:"center"}][l]}}});function ZI(t,e){var i={};return i[e.dim+"AxisIndex"]=e.index,t.getCartesian(i)}var UI={line:function(t,e,i){return{type:"Line",subPixelOptimize:!0,shape:GI([e,i[0]],[e,i[1]],XI(t))}},shadow:function(t,e,i){var n=Math.max(1,t.getBandWidth()),o=i[1]-i[0];return{type:"Rect",shape:FI([e-n/2,i[0]],[n,o],XI(t))}}};function XI(t){return"x"===t.dim?0:1}mv.registerAxisPointerClass("CartesianAxisPointer",HI),yf(function(t){if(t){t.axisPointer&&0!==t.axisPointer.length||(t.axisPointer={});var e=t.axisPointer.link;e&&!L(e)&&(t.axisPointer.link=[e])}}),xf(Ld.PROCESSOR.STATISTIC,function(t,e){t.getComponent("axisPointer").coordSysAxesInfo=cv(t,e)}),_f({type:"updateAxisPointer",event:"updateAxisPointer",update:":updateAxisPointer"},function(t,e,i){var n=t.currTrigger,r=[t.x,t.y],o=t,a=t.dispatchAction||A(i.dispatchAction,i),s=e.getComponent("axisPointer").coordSysAxesInfo;if(s){xI(r)&&(r=cI({seriesIndex:o.seriesIndex,dataIndex:o.dataIndex},e).point);var l=xI(r),u=o.axesInfo,h=s.axesInfo,c="leave"===n||xI(r),d={},f={},p={list:[],map:{}},g={showPointer:fI(mI,f),showTooltip:fI(vI,p)};dI(s.coordSysMap,function(t,e){var a=l||t.containPoint(r);dI(s.coordSysAxesInfo[e],function(t,e){var i=t.axis,n=function(t,e){for(var i=0;i<(t||[]).length;i++){var n=t[i];if(e.axis.dim===n.axisDim&&e.axis.model.componentIndex===n.axisIndex)return n}}(u,t);if(!c&&a&&(!u||n)){var o=n&&n.value;null!=o||l||(o=i.pointToData(r)),null!=o&&gI(t,o,g,!1,d)}})});var m={};return dI(h,function(o,t){var a=o.linkGroup;a&&!f[t]&&dI(a.axesInfo,function(t,e){var i=f[e];if(t!==o&&i){var n=i.value;a.mapper&&(n=o.axis.scale.parse(a.mapper(n,yI(t),yI(o)))),m[o.key]=n}})}),dI(m,function(t,e){gI(h[e],t,g,!0,d)}),function(o,t,e){var a=e.axesInfo=[];dI(t,function(t,e){var i=t.axisPointerModel.option,n=o[e];n?(t.useHandle||(i.status="show"),i.value=n.value,i.seriesDataIndices=(n.payloadBatch||[]).slice()):t.useHandle||(i.status="hide"),"show"===i.status&&a.push({axisDim:t.axis.dim,axisIndex:t.axis.model.componentIndex,value:i.value})})}(f,h,d),function(t,e,i,n){if(xI(e)||!t.list.length)return n({type:"hideTip"});var o=((t.list[0].dataByAxis[0]||{}).seriesDataIndices||[])[0]||{};n({type:"showTip",escapeConnect:!0,x:e[0],y:e[1],tooltipOption:i.tooltipOption,position:i.position,dataIndexInside:o.dataIndexInside,dataIndex:o.dataIndex,seriesIndex:o.seriesIndex,dataByCoordSys:t.list})}(p,r,t,a),function(t,e,i){var n=i.getZr(),o="axisPointerLastHighlights",a=pI(n)[o]||{},r=pI(n)[o]={};dI(t,function(t,e){var i=t.axisPointerModel.option;"show"===i.status&&dI(i.seriesDataIndices,function(t){var e=t.seriesIndex+" | "+t.dataIndex;r[e]=t})});var s=[],l=[];R(a,function(t,e){r[e]||l.push(t)}),R(r,function(t,e){a[e]||s.push(t)}),l.length&&i.dispatchAction({type:"downplay",escapeConnect:!0,batch:l}),s.length&&i.dispatchAction({type:"highlight",escapeConnect:!0,batch:s})}(h,0,i),d}});var YI=["x","y"],jI=["width","height"],qI=LI.extend({makeElOption:function(t,e,i,n,o){var a=i.axis,r=a.coordinateSystem,s=JI(r,1-$I(a)),l=r.dataToPoint(e)[0],u=n.get("type");if(u&&"none"!==u){var h=RI(n),c=KI[u](a,l,s);c.style=h,t.graphicKey=c.type,t.pointer=c}VI(e,t,rI(i),i,n,o)},getHandleTransform:function(t,e,i){var n=rI(e,{labelInside:!1});return n.labelMargin=i.get("handle.margin"),{position:BI(e.axis,t,n),rotation:n.rotation+(n.labelDirection<0?Math.PI:0)}},updateHandleTransform:function(t,e,i,n){var o=i.axis,a=o.coordinateSystem,r=$I(o),s=JI(a,r),l=t.position;l[r]+=e[r],l[r]=Math.min(s[1],l[r]),l[r]=Math.max(s[0],l[r]);var u=JI(a,1-r),h=(u[1]+u[0])/2,c=[h,h];return c[r]=l[r],{position:l,rotation:t.rotation,cursorPoint:c,tooltipOption:{verticalAlign:"middle"}}}}),KI={line:function(t,e,i){return{type:"Line",subPixelOptimize:!0,shape:GI([e,i[0]],[e,i[1]],$I(t))}},shadow:function(t,e,i){var n=t.getBandWidth(),o=i[1]-i[0];return{type:"Rect",shape:FI([e-n/2,i[0]],[n,o],$I(t))}}};function $I(t){return t.isHorizontal()?0:1}function JI(t,e){var i=t.getRect();return[i[YI[e]],i[YI[e]]+i[jI[e]]]}mv.registerAxisPointerClass("SingleAxisPointer",qI),Af({type:"single"});var QI=sc.extend({type:"series.themeRiver",dependencies:["singleAxis"],nameMap:null,init:function(t){QI.superApply(this,"init",arguments),this.legendVisualProvider=new qv(A(this.getData,this),A(this.getRawData,this))},fixData:function(t){var e=t.length,i={},n=ta(t,function(t){return i.hasOwnProperty(t[0])||(i[t[0]]=-1),t[2]}),o=[];n.buckets.each(function(t,e){o.push({name:e,dataList:t})});for(var a=o.length,r=0;rMath.PI/2?"right":"left"):x&&"center"!==x?"left"===x?(f=u.r0+y,p>Math.PI/2&&(x="right")):"right"===x&&(f=u.r-y,p>Math.PI/2&&(x="left")):(f=(u.r+u.r0)/2,x="center"),d.attr("style",{text:l,textAlign:x,textVerticalAlign:M("verticalAlign")||"middle",opacity:M("opacity")});var _=f*g+u.cx,w=f*m+u.cy;d.attr("position",[_,w]);var b=M("rotate"),S=0;function M(t){var e=a.get(t);return null==e?o.get(t):e}"radial"===b?(S=-p)<-Math.PI/2&&(S+=Math.PI):"tangential"===b?(S=Math.PI/2-p)>Math.PI/2?S-=Math.PI:S<-Math.PI/2&&(S+=Math.PI):"number"==typeof b&&(S=b*Math.PI/180),d.attr("rotation",S)},sT._initEvents=function(t,e,i,n){t.off("mouseover").off("mouseout").off("emphasis").off("normal");function o(){r.onEmphasis(n)}function a(){r.onNormal()}var r=this;i.isAnimationEnabled()&&t.on("mouseover",o).on("mouseout",a).on("emphasis",o).on("normal",a).on("downplay",function(){r.onDownplay()}).on("highlight",function(){r.onHighlight()})},w(rT,Ci);_c.extend({type:"sunburst",init:function(){},render:function(o,a,t,e){var n=this;this.seriesModel=o,this.api=t,this.ecModel=a;var r=o.getData(),s=r.tree.root,i=o.getViewRoot(),l=this.group,u=o.get("renderLabelForZeroData"),h=[];i.eachNode(function(t){h.push(t)});var c=this._oldChildren||[];if(function(i,n){if(0===i.length&&0===n.length)return;function t(t){return t.getId()}function e(t,e){!function(t,e){u||!t||t.getValue()||(t=null);if(t!==s&&e!==s)if(e&&e.piece)t?(e.piece.updateData(!1,t,"normal",o,a),r.setItemGraphicEl(t.dataIndex,e.piece)):function(t){if(!t)return;t.piece&&(l.remove(t.piece),t.piece=null)}(e);else if(t){var i=new rT(t,o,a);l.add(i),r.setItemGraphicEl(t.dataIndex,i)}}(null==t?null:i[t],null==e?null:n[e])}new kf(n,i,t,t).add(e).update(e).remove(T(e,null)).execute()}(h,c),function(t,e){if(0=i.r0}}});var lT="sunburstRootToNode";_f({type:lT,update:"updateView"},function(o,t){t.eachComponent({mainType:"series",subType:"sunburst",query:o},function(t,e){var i=Zx(o,[lT],t);if(i){var n=t.getViewRoot();n&&(o.direction=Xx(n,i.node)?"rollUp":"drillDown"),t.resetViewRoot(i.node)}})});var uT="sunburstHighlight";_f({type:uT,update:"updateView"},function(n,t){t.eachComponent({mainType:"series",subType:"sunburst",query:n},function(t,e){var i=Zx(n,[uT],t);i&&(n.highlight=i.node)})});_f({type:"sunburstUnhighlight",update:"updateView"},function(i,t){t.eachComponent({mainType:"series",subType:"sunburst",query:i},function(t,e){i.unhighlight=!0})});var hT=Math.PI/180;function cT(t,e){if("function"==typeof e)return t.sort(e);var n="asc"===e;return t.sort(function(t,e){var i=(t.getValue()-e.getValue())*(n?1:-1);return 0==i?(t.dataIndex-e.dataIndex)*(n?-1:1):i})}function dT(a,r){return r=r||[0,0],O(["x","y"],function(t,e){var i=this.getAxis(t),n=r[e],o=a[e]/2;return"category"===i.type?i.getBandWidth():Math.abs(i.dataToCoord(n-o)-i.dataToCoord(n+o))},this)}Sf(T(iy,"sunburst")),bf(T(function(t,e,C,i){e.eachSeriesByType(t,function(t){var e=t.get("center"),i=t.get("radius");L(i)||(i=[0,i]),L(e)||(e=[e,e]);var n=C.getWidth(),o=C.getHeight(),h=Math.min(n,o),c=El(e[0],n),d=El(e[1],o),f=El(i[0],h/2),a=El(i[1],h/2),r=-t.get("startAngle")*hT,p=t.get("minAngle")*hT,g=t.getData().tree.root,s=t.getViewRoot(),m=s.depth,l=t.get("sort");null!=l&&!function e(t,i){var n=t.children||[];t.children=cT(n,i);n.length&&R(t.children,function(t){e(t,i)})}(s,l);var u=0;R(s.children,function(t){isNaN(t.getValue())||u++});var v=s.getValue(),y=Math.PI/(v||u)*2,x=0t[1]&&t.reverse(),{coordSys:{type:"polar",cx:o.cx,cy:o.cy,r:t[1],r0:t[0]},api:{coord:A(function(t){var e=a.dataToRadius(t[0]),i=r.dataToAngle(t[1]),n=o.coordToPoint([e,i]);return n.push(e,i*Math.PI/180),n}),size:A(gT,o)}}},calendar:function(i){var t=i.getRect(),e=i.getRangeInfo();return{coordSys:{type:"calendar",x:t.x,y:t.y,width:t.width,height:t.height,cellWidth:i.getCellWidth(),cellHeight:i.getCellHeight(),rangeInfo:{start:e.start,end:e.end,weeks:e.weeks,dayCount:e.allDay}},api:{coord:function(t,e){return i.dataToPoint(t,e)}}}}};function ST(t,e,i,n,o){null==i[t]||o||(e[t]=i[t],i[t]=n[t])}function MT(a,r,e,t){var i=a.get("renderItem"),n=a.coordinateSystem,o={};n&&(o=n.prepareCustoms?n.prepareCustoms():bT[n.type](n));var s,l,u,h,c,d=D({getWidth:t.getWidth,getHeight:t.getHeight,getZr:t.getZr,getUserPixelRatio:t.getUserPixelRatio,value:function(t,e){return null==e&&(e=s),r.get(r.getDimension(t||0),e)},style:function(t,e){null==e&&(e=s),g(e);var i=l.getModel(vT).getItemStyle();null!=c&&(i.fill=c);var n=r.getItemVisual(e,"opacity");null!=n&&(i.opacity=n);var o=t?CT(t,u):u;return nl(i,o,null,{autoColor:c,isRectText:!0}),i.text=o.getShallow("show")?H(a.getFormattedLabel(e,"normal"),Ug(r,e)):null,t&<(i,t),i},styleEmphasis:function(t,e){null==e&&(e=s),g(e);var i=l.getModel(yT).getItemStyle(),n=t?CT(t,h):h;return nl(i,n,null,{isRectText:!0},!0),i.text=n.getShallow("show")?Z(a.getFormattedLabel(e,"emphasis"),a.getFormattedLabel(e,"normal"),Ug(r,e)):null,t&<(i,t),i},visual:function(t,e){return null==e&&(e=s),r.getItemVisual(e,t)},barLayout:function(t){if(n.getBaseAxis){return function(t){var e=[],i=t.axis;if("category"===i.type){for(var n=i.getBandWidth(),o=0;oe[1]&&e.reverse();var i=t.getExtent(),n=Math.PI/180;return{cx:this.cx,cy:this.cy,r0:e[0],r:e[1],startAngle:-i[0]*n,endAngle:-i[1]*n,clockwise:t.inverse,contain:function(t,e){var i=t-this.cx,n=e-this.cy,o=i*i+n*n,a=this.r,r=this.r0;return o<=a*a&&r*r<=o}}}};var GT=ku.extend({type:"polarAxis",axis:null,getCoordSysModel:function(){return this.ecModel.queryComponents({mainType:"polar",index:this.option.polarIndex,id:this.option.polarId})[0]}});m(GT.prototype,dg);var FT={splitNumber:5};function WT(t,e){return e.type||(e.data?"category":"value")}function HT(t,e){var i=this,n=i.getAngleAxis(),o=i.getRadiusAxis();if(n.scale.setExtent(1/0,-1/0),o.scale.setExtent(1/0,-1/0),t.eachSeries(function(t){if(t.coordinateSystem===i){var e=t.getData();R(e.mapDimension("radius",!0),function(t){o.scale.unionExtentFromData(e,pp(e,t))}),R(e.mapDimension("angle",!0),function(t){n.scale.unionExtentFromData(e,pp(e,t))})}}),rg(n.scale,n.model),rg(o.scale,o.model),"category"===n.type&&!n.onBand){var a=n.getExtent(),r=360/n.scale.count();n.inverse?a[1]+=r:a[1]-=r,n.setExtent(a[0],a[1])}}function ZT(t,e){if(t.type=e.get("type"),t.scale=sg(e),t.onBand=e.get("boundaryGap")&&"category"===t.type,t.inverse=e.get("inverse"),"angleAxis"===e.mainType){t.inverse^=e.get("clockwise");var i=e.get("startAngle");t.setExtent(i,i+(t.inverse?-360:360))}(e.axis=t).model=e}Gm("angle",GT,WT,{startAngle:90,clockwise:!0,splitNumber:12,axisLabel:{rotate:!1}}),Gm("radius",GT,WT,FT),Tf({type:"polar",dependencies:["polarAxis","angleAxis"],coordinateSystem:null,findAxisModel:function(t){var e;return this.ecModel.eachComponent(t,function(t){t.getCoordSysModel()===this&&(e=t)},this),e},defaultOption:{zlevel:0,z:0,center:["50%","50%"],radius:"80%"}}),lh.register("polar",{dimensions:VT.prototype.dimensions,create:function(i,s){var l=[];return i.eachComponent("polar",function(t,e){var i=new VT(e);i.update=HT;var n=i.getRadiusAxis(),o=i.getAngleAxis(),a=t.findAxisModel("radiusAxis"),r=t.findAxisModel("angleAxis");ZT(n,a),ZT(o,r),function(t,e,i){var n=e.get("center"),o=i.getWidth(),a=i.getHeight();t.cx=El(n[0],o),t.cy=El(n[1],a);var r=t.getRadiusAxis(),s=Math.min(o,a)/2,l=e.get("radius");null==l?l=[0,"100%"]:L(l)||(l=[0,l]),l=[El(l[0],s),El(l[1],s)],r.inverse?r.setExtent(l[1],l[0]):r.setExtent(l[0],l[1])}(i,t,s),l.push(i),(t.coordinateSystem=i).model=t}),i.eachSeries(function(t){if("polar"===t.get("coordinateSystem")){var e=i.queryComponents({mainType:"polar",index:t.get("polarIndex"),id:t.get("polarId")})[0];t.coordinateSystem=e.coordinateSystem}}),l}});var UT=["axisLine","axisLabel","axisTick","minorTick","splitLine","minorSplitLine","splitArea"];function XT(t,e,i){e[1]>e[0]&&(e=e.slice().reverse());var n=t.coordToPoint([e[0],i]),o=t.coordToPoint([e[1],i]);return{x1:n[0],y1:n[1],x2:o[0],y2:o[1]}}function YT(t){return t.getRadiusAxis().inverse?0:1}function jT(t){var e=t[0],i=t[t.length-1];e&&i&&Math.abs(Math.abs(e.coord-i.coord)-360)<1e-4&&t.pop()}mv.extend({type:"angleAxis",axisPointerClass:"PolarAxisPointer",render:function(e,t){if(this.group.removeAll(),e.get("show")){var i=e.axis,n=i.polar,o=n.getRadiusAxis().getExtent(),a=i.getTicksCoords(),r=i.getMinorTicksCoords(),s=O(i.getViewLabels(),function(t){return(t=k(t)).coord=i.dataToCoord(t.tickValue),t});jT(s),jT(a),R(UT,function(t){!e.get(t+".show")||i.scale.isBlank()&&"axisLine"!==t||this["_"+t](e,n,a,r,o,s)},this)}},_axisLine:function(t,e,i,n,o){var a,r=t.getModel("axisLine.lineStyle"),s=YT(e),l=s?0:1;(a=0===o[l]?new Yr({shape:{cx:e.cx,cy:e.cy,r:o[s]},style:r.getLineStyle(),z2:1,silent:!0}):new Kr({shape:{cx:e.cx,cy:e.cy,r:o[s],r0:o[l]},style:r.getLineStyle(),z2:1,silent:!0})).style.fill=null,this.group.add(a)},_axisTick:function(t,e,i,n,o){var a=t.getModel("axisTick"),r=(a.get("inside")?-1:1)*a.get("length"),s=o[YT(e)],l=O(i,function(t){return new ls({shape:XT(e,[s,s+r],t.coord)})});this.group.add(Rs(l,{style:D(a.getModel("lineStyle").getLineStyle(),{stroke:t.get("axisLine.lineStyle.color")})}))},_minorTick:function(t,e,i,n,o){if(n.length){for(var a=t.getModel("axisTick"),r=t.getModel("minorTick"),s=(a.get("inside")?-1:1)*r.get("length"),l=o[YT(e)],u=[],h=0;hr?"left":"right",u=Math.abs(a[1]-s)/o<.3?"middle":a[1]>s?"top":"bottom";p&&p[n]&&p[n].textStyle&&(i=new Cl(p[n].textStyle,g,g.ecModel));var h=new Ur({silent:Qm.isLabelSilent(c)});this.group.add(h),nl(h.style,i,{x:a[0],y:a[1],textFill:i.getTextColor()||c.get("axisLine.lineStyle.color"),text:t.formattedLabel,textAlign:l,textVerticalAlign:u}),v&&(h.eventData=Qm.makeAxisEventDataBase(c),h.eventData.targetType="axisLabel",h.eventData.value=t.rawLabel)},this)},_splitLine:function(t,e,i,n,o){var a=t.getModel("splitLine").getModel("lineStyle"),r=a.get("color"),s=0;r=r instanceof Array?r:[r];for(var l=[],u=0;um?"left":"right",h=Math.abs(l[1]-v)/g<.3?"middle":l[1]>v?"top":"bottom"}return{position:l,align:u,verticalAlign:h}}(e,i,0,s,d))}});var JT={line:function(t,e,i,n,o){return"angle"===t.dim?{type:"Line",shape:GI(e.coordToPoint([n[0],i]),e.coordToPoint([n[1],i]))}:{type:"Circle",shape:{cx:e.cx,cy:e.cy,r:i}}},shadow:function(t,e,i,n,o){var a=Math.max(1,t.getBandWidth()),r=Math.PI/180;return"angle"===t.dim?{type:"Sector",shape:WI(e.cx,e.cy,n[0],n[1],(-i-a/2)*r,(a/2-i)*r)}:{type:"Sector",shape:WI(e.cx,e.cy,i-a/2,i+a/2,0,2*Math.PI)}}};function QT(n,t){t.update="updateView",_f(t,function(t,e){var i={};return e.eachComponent({mainType:"geo",query:t},function(e){e[n](t.name),R(e.coordinateSystem.regions,function(t){i[t.name]=e.isSelected(t.name)||!1})}),{selected:i,name:t.name}})}mv.registerAxisPointerClass("PolarAxisPointer",$T),bf(T(function(t,e,i){var N={},O=function(t){var g={};R(t,function(t,e){var i=t.getData(),n=t.coordinateSystem,o=n.getBaseAxis(),a=RT(n,o),r=o.getExtent(),s="category"===o.type?o.getBandWidth():Math.abs(r[1]-r[0])/i.count(),l=g[a]||{bandWidth:s,remainedWidth:s,autoWidthCount:0,categoryGap:"20%",gap:"30%",stacks:{}},u=l.stacks;g[a]=l;var h=OT(t);u[h]||l.autoWidthCount++,u[h]=u[h]||{width:0,maxWidth:0};var c=El(t.get("barWidth"),s),d=El(t.get("barMaxWidth"),s),f=t.get("barGap"),p=t.get("barCategoryGap");c&&!u[h].width&&(c=Math.min(l.remainedWidth,c),u[h].width=c,l.remainedWidth-=c),d&&(u[h].maxWidth=d),null!=f&&(l.gap=f),null!=p&&(l.categoryGap=p)});var d={};return R(g,function(t,i){d[i]={};var e=t.stacks,n=t.bandWidth,o=El(t.categoryGap,n),a=El(t.gap,1),r=t.remainedWidth,s=t.autoWidthCount,l=(r-o)/(s+(s-1)*a);l=Math.max(l,0),R(e,function(t,e){var i=t.maxWidth;i&&i=n.start.time&&i.timea.end.time&&t.reverse(),t},_getRangeInfo:function(t){var e;(t=[this.getDateInfo(t[0]),this.getDateInfo(t[1])])[0].time>t[1].time&&(e=!0,t.reverse());var i=Math.floor(t[1].time/864e5)-Math.floor(t[0].time/864e5)+1,n=new Date(t[0].time),o=n.getDate(),a=t[1].date.getDate();n.setDate(o+i-1);var r=n.getDate();if(r!==a)for(var s=0n.weeks||0===t&&en.lweek)return!1;var o=7*(t-1)-n.fweek+e,a=new Date(n.start.time);return a.setDate(n.start.d+o),this.getDateInfo(a)}},tA.dimensions=tA.prototype.dimensions,tA.getDimensionsInfo=tA.prototype.getDimensionsInfo,tA.create=function(i,n){var o=[];return i.eachComponent("calendar",function(t){var e=new tA(t,i,n);o.push(e),t.coordinateSystem=e}),i.eachSeries(function(t){"calendar"===t.get("coordinateSystem")&&(t.coordinateSystem=o[t.get("calendarIndex")||0])}),o},lh.register("calendar",tA);var iA=ku.extend({type:"calendar",coordinateSystem:null,defaultOption:{zlevel:0,z:2,left:80,top:60,cellSize:20,orient:"horizontal",splitLine:{show:!0,lineStyle:{color:"#000",width:1,type:"solid"}},itemStyle:{color:"#fff",borderWidth:1,borderColor:"#ccc"},dayLabel:{show:!0,firstDay:0,position:"start",margin:"50%",nameMap:"en",color:"#000"},monthLabel:{show:!0,position:"start",margin:5,align:"center",nameMap:"en",formatter:null,color:"#000"},yearLabel:{show:!0,position:null,margin:30,formatter:null,color:"#ccc",fontFamily:"sans-serif",fontWeight:"bolder",fontSize:20}},init:function(t,e,i,n){var o=Iu(t);iA.superApply(this,"init",arguments),nA(t,o)},mergeOption:function(t,e){iA.superApply(this,"mergeOption",arguments),nA(this.option,t)}});function nA(t,e){var i=t.cellSize;L(i)?1===i.length&&(i[1]=i[0]):i=t.cellSize=[i,i];var n=O([0,1],function(t){return function(t,e){return null!=t[xu[e][0]]||null!=t[xu[e][1]]&&null!=t[xu[e][2]]}(e,t)&&(i[t]="auto"),null!=i[t]&&"auto"!==i[t]});Mu(t,e,{type:"box",ignoreSize:n})}var oA={EN:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],CN:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"]},aA={EN:["S","M","T","W","T","F","S"],CN:["日","一","二","三","四","五","六"]};Af({type:"calendar",_tlpoints:null,_blpoints:null,_firstDayOfMonth:null,_firstDayPoints:null,render:function(t,e,i){var n=this.group;n.removeAll();var o=t.coordinateSystem,a=o.getRangeInfo(),r=o.getOrient();this._renderDayRect(t,a,n),this._renderLines(t,a,r,n),this._renderYearText(t,a,r,n),this._renderMonthText(t,r,n),this._renderWeekText(t,a,r,n)},_renderDayRect:function(t,e,i){for(var n=t.coordinateSystem,o=t.getModel("itemStyle").getItemStyle(),a=n.getCellWidth(),r=n.getCellHeight(),s=e.start.time;s<=e.end.time;s=n.getNextNDay(s,1).time){var l=n.dataToRect([s],!1).tl,u=new rs({shape:{x:l[0],y:l[1],width:a,height:r},cursor:"default",style:o});i.add(u)}},_renderLines:function(i,t,n,o){var a=this,r=i.coordinateSystem,s=i.getModel("splitLine.lineStyle").getLineStyle(),l=i.get("splitLine.show"),e=s.lineWidth;this._tlpoints=[],this._blpoints=[],this._firstDayOfMonth=[],this._firstDayPoints=[];for(var u=t.start,h=0;u.time<=t.end.time;h++){d(u.formatedDate),0===h&&(u=r.getDateInfo(t.start.y+"-"+t.start.m));var c=u.date;c.setMonth(c.getMonth()+1),u=r.getDateInfo(c)}function d(t){a._firstDayOfMonth.push(r.getDateInfo(t)),a._firstDayPoints.push(r.dataToRect([t],!1).tl);var e=a._getLinePointsOfOneWeek(i,t,n);a._tlpoints.push(e[0]),a._blpoints.push(e[e.length-1]),l&&a._drawSplitline(e,s,o)}d(r.getNextNDay(t.end.time,1).formatedDate),l&&this._drawSplitline(a._getEdgesPoints(a._tlpoints,e,n),s,o),l&&this._drawSplitline(a._getEdgesPoints(a._blpoints,e,n),s,o)},_getEdgesPoints:function(t,e,i){var n=[t[0].slice(),t[t.length-1].slice()],o="horizontal"===i?0:1;return n[0][o]=n[0][o]-e/2,n[1][o]=n[1][o]+e/2,n},_drawSplitline:function(t,e,i){var n=new ts({z2:20,shape:{points:t},style:e});i.add(n)},_getLinePointsOfOneWeek:function(t,e,i){var n=t.coordinateSystem;e=n.getDateInfo(e);for(var o=[],a=0;a<7;a++){var r=n.getNextNDay(e.time,a),s=n.dataToRect([r.time],!1);o[2*r.day]=s.tl,o[2*r.day+1]=s["horizontal"===i?"bl":"tr"]}return o},_formatterLabel:function(t,e){return"string"==typeof t&&t?uu(t,e):"function"==typeof t?t(e):e.nameMap},_yearTextPositionControl:function(t,e,i,n,o){e=e.slice();var a=["center","bottom"];"bottom"===n?(e[1]+=o,a=["center","top"]):"left"===n?e[0]-=o:"right"===n?(e[0]+=o,a=["center","top"]):e[1]-=o;var r=0;return"left"!==n&&"right"!==n||(r=Math.PI/2),{rotation:r,position:e,style:{textAlign:a[0],textVerticalAlign:a[1]}}},_renderYearText:function(t,e,i,n){var o=t.getModel("yearLabel");if(o.get("show")){var a=o.get("margin"),r=o.get("position");r=r||("horizontal"!==i?"top":"left");var s=[this._tlpoints[this._tlpoints.length-1],this._blpoints[0]],l=(s[0][0]+s[1][0])/2,u=(s[0][1]+s[1][1])/2,h="horizontal"===i?0:1,c={top:[l,s[h][1]],bottom:[l,s[1-h][1]],left:[s[1-h][0],u],right:[s[h][0],u]},d=e.start.y;+e.end.y>+e.start.y&&(d=d+"-"+e.end.y);var f=o.get("formatter"),p={start:e.start.y,end:e.end.y,nameMap:d},g=this._formatterLabel(f,p),m=new Ur({z2:30});nl(m.style,o,{text:g}),m.attr(this._yearTextPositionControl(m,c[r],i,r,a)),n.add(m)}},_monthTextPositionControl:function(t,e,i,n,o){var a="left",r="top",s=t[0],l=t[1];return"horizontal"===i?(l+=o,e&&(a="center"),"start"===n&&(r="bottom")):(s+=o,e&&(r="middle"),"start"===n&&(a="right")),{x:s,y:l,textAlign:a,textVerticalAlign:r}},_renderMonthText:function(t,e,i){var n=t.getModel("monthLabel");if(n.get("show")){var o=n.get("nameMap"),a=n.get("margin"),r=n.get("position"),s=n.get("align"),l=[this._tlpoints,this._blpoints];E(o)&&(o=oA[o.toUpperCase()]||[]);var u="start"===r?0:1,h="horizontal"===e?0:1;a="start"===r?-a:a;for(var c="center"===s,d=0;dd.getHeight()&&(i.textPosition="top",a=!0);var r=a?-5-n.height:p+8;o+n.width/2>d.getWidth()?(i.textPosition=["100%",r],i.textAlign="right"):o-n.width/2<0&&(i.textPosition=[0,r],i.textAlign="left")}})}function t(t,e){var i,n=m[t],o=m[e],a=u[n],r=new Cl(a,h,h.ecModel);if(l&&null!=l.newTitle&&l.featureName===n&&(a.title=l.newTitle),n&&!o){if(function(t){return 0===t.indexOf("my")}(n))i={model:r,onclick:r.option.onclick,featureName:n};else{var s=fA(n);if(!s)return;i=new s(r,c,d)}g[n]=i}else{if(!(i=g[o]))return;i.model=r,i.ecModel=c,i.api=d}n||!o?r.get("show")&&!i.unusable?(function(o,a,t){var r=o.getModel("iconStyle"),s=o.getModel("emphasis.iconStyle"),e=a.getIcons?a.getIcons():o.get("icon"),l=o.get("title")||{};if("string"==typeof e){var i=e,n=l;l={},(e={})[t]=i,l[t]=n}var u=o.iconPaths={};R(e,function(t,e){var i=yl(t,{},{x:-p/2,y:-p/2,width:p,height:p});i.setStyle(r.getItemStyle()),i.hoverStyle=s.getItemStyle(),i.setStyle({text:l[e],textAlign:s.get("textAlign"),textBorderRadius:s.get("textBorderRadius"),textPadding:s.get("textPadding"),textFill:null});var n=h.getModel("tooltip");n&&n.get("show")&&i.attr("tooltip",P({content:l[e],formatter:n.get("formatter",!0)||function(){return l[e]},formatterParams:{componentType:"toolbox",name:e,title:l[e],$vars:["name","title"]},position:n.get("position",!0)||"bottom"},n.option)),$s(i),h.get("showTitle")&&(i.__title=l[e],i.on("mouseover",function(){var t=s.getItemStyle(),e="vertical"===h.get("orient")?null==h.get("right")?"right":"left":null==h.get("bottom")?"bottom":"top";i.setStyle({textFill:s.get("textFill")||t.fill||t.stroke||"#000",textBackgroundColor:s.get("textBackgroundColor"),textPosition:s.get("textPosition")||e})}).on("mouseout",function(){i.setStyle({textFill:null,textBackgroundColor:null})})),i.trigger(o.get("iconStatus."+e)||"normal"),f.add(i),i.on("click",A(a.onclick,a,c,d,e)),u[e]=i})}(r,i,n),r.setIconStatus=function(t,e){var i=this.option,n=this.iconPaths;i.iconStatus=i.iconStatus||{},i.iconStatus[t]=e,n[t]&&n[t].trigger(e)},i.render&&i.render(r,c,d,l)):i.remove&&i.remove(c,d):i.dispose&&i.dispose(c,d)}},updateView:function(t,e,i,n){R(this._features,function(t){t.updateView&&t.updateView(t.model,e,i,n)})},remove:function(e,i){R(this._features,function(t){t.remove&&t.remove(e,i)}),this.group.removeAll()},dispose:function(e,i){R(this._features,function(t){t.dispose&&t.dispose(e,i)})}});var mA=Oc.toolbox.saveAsImage;function vA(t){this.model=t}vA.defaultOption={show:!0,icon:"M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0",title:mA.title,type:"png",connectedBackgroundColor:"#fff",name:"",excludeComponents:["toolbox"],pixelRatio:1,lang:mA.lang.slice()},vA.prototype.unusable=!v.canvasSupported,vA.prototype.onclick=function(t,e){var i=this.model,n=i.get("name")||t.get("title.0.text")||"echarts",o="svg"===e.getZr().painter.getType()?"svg":i.get("type",!0)||"png",a=e.getConnectedDataURL({type:o,backgroundColor:i.get("backgroundColor",!0)||t.get("backgroundColor")||"#fff",connectedBackgroundColor:i.get("connectedBackgroundColor"),excludeComponents:i.get("excludeComponents"),pixelRatio:i.get("pixelRatio")});if("function"!=typeof MouseEvent||v.browser.ie||v.browser.edge)if(window.navigator.msSaveOrOpenBlob){for(var r=atob(a.split(",")[1]),s=r.length,l=new Uint8Array(s);s--;)l[s]=r.charCodeAt(s);var u=new Blob([l]);window.navigator.msSaveOrOpenBlob(u,n+"."+o)}else{var h=i.get("lang"),c='';window.open().document.write(c)}else{var d=document.createElement("a");d.download=n+"."+o,d.target="_blank",d.href=a;var f=new MouseEvent("click",{view:document.defaultView,bubbles:!0,cancelable:!1});d.dispatchEvent(f)}},dA("saveAsImage",vA);var yA=Oc.toolbox.magicType,xA="__ec_magicType_stack__";function _A(t){this.model=t}_A.defaultOption={show:!0,type:[],icon:{line:"M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4",bar:"M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7",stack:"M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z"},title:k(yA.title),option:{},seriesIndex:{}};var wA=_A.prototype;wA.getIcons=function(){var t=this.model,e=t.get("icon"),i={};return R(t.get("type"),function(t){e[t]&&(i[t]=e[t])}),i};var bA={line:function(t,e,i,n){if("bar"===t)return m({id:e,type:"line",data:i.get("data"),stack:i.get("stack"),markPoint:i.get("markPoint"),markLine:i.get("markLine")},n.get("option.line")||{},!0)},bar:function(t,e,i,n){if("line"===t)return m({id:e,type:"bar",data:i.get("data"),stack:i.get("stack"),markPoint:i.get("markPoint"),markLine:i.get("markLine")},n.get("option.bar")||{},!0)},stack:function(t,e,i,n){var o=i.get("stack")===xA;if("line"===t||"bar"===t)return n.setIconStatus("stack",o?"normal":"emphasis"),m({id:e,stack:o?"":xA},n.get("option.stack")||{},!0)}},SA=[["line","bar"],["stack"]];wA.onclick=function(u,t,h){var c=this.model,e=c.get("seriesIndex."+h);if(bA[h]){var i,d={series:[]};if(R(SA,function(t){0<=_(t,h)&&R(t,function(t){c.setIconStatus(t,"normal")})}),c.setIconStatus(h,"emphasis"),u.eachComponent({mainType:"series",query:null==e?null:{seriesIndex:e}},function(t){var e=t.subType,i=t.id,n=bA[h](e,i,t,c);n&&(D(n,t.option),d.series.push(n));var o=t.coordinateSystem;if(o&&"cartesian2d"===o.type&&("line"===h||"bar"===h)){var a=o.getAxesByScale("ordinal")[0];if(a){var r=a.dim+"Axis",s=u.queryComponents({mainType:r,index:t.get(name+"Index"),id:t.get(name+"Id")})[0].componentIndex;d[r]=d[r]||[];for(var l=0;l<=s;l++)d[r][s]=d[r][s]||{};d[r][s].boundaryGap="bar"===h}}}),"stack"===h)i=d.series&&d.series[0]&&d.series[0].stack===xA?m({stack:yA.title.tiled},yA.title):k(yA.title);t.dispatchAction({type:"changeMagicType",currentType:h,newOption:d,newTitle:i,featureName:"magicType"})}},_f({type:"changeMagicType",event:"magicTypeChanged",update:"prepareAndUpdate"},function(t,e){e.mergeOption(t.newOption)}),dA("magicType",_A);var MA=Oc.toolbox.dataView,IA=new Array(60).join("-"),TA="\t";function AA(t){var e=function(t){var o={},a=[],r=[];return t.eachRawSeries(function(t){var e=t.coordinateSystem;if(!e||"cartesian2d"!==e.type&&"polar"!==e.type)a.push(t);else{var i=e.getBaseAxis();if("category"===i.type){var n=i.dim+"_"+i.index;o[n]||(o[n]={categoryAxis:i,valueAxis:e.getOtherAxis(i),series:[]},r.push({axisDim:i.dim,axisIndex:i.index})),o[n].series.push(t)}else a.push(t)}}),{seriesGroupByCategoryAxis:o,other:a,meta:r}}(t);return{value:M([function(t){var h=[];return R(t,function(t,e){var i=t.categoryAxis,n=t.valueAxis.dim,o=[" "].concat(O(t.series,function(t){return t.name})),a=[i.model.getCategories()];R(t.series,function(t){var e=t.getRawData();a.push(t.getRawData().mapArray(e.mapDimension(n),function(t){return t}))});for(var r=[o.join(TA)],s=0;st[1]&&t.reverse(),t}function GA(t,e){return Ko(t,e,{includeMainTypes:EA})}BA.setOutputRanges=function(t,e){this.matchOutputRanges(t,e,function(t,e,i){if((t.coordRanges||(t.coordRanges=[])).push(e),!t.coordRange){t.coordRange=e;var n=ZA[t.brushType](0,i,e);t.__rangeOffset={offset:XA[t.brushType](n.values,t.range,[1,1]),xyMinMax:n.xyMinMax}}})},BA.matchOutputRanges=function(t,n,o){PA(t,function(i){var t=this.findTargetInfo(i,n);t&&!0!==t&&R(t.coordSyses,function(t){var e=ZA[i.brushType](1,t,i.range);o(i,e.values,t,n)})},this)},BA.setInputRanges=function(t,o){PA(t,function(t){var e=this.findTargetInfo(t,o);if(t.range=t.range||[],e&&!0!==e){t.panelId=e.panelId;var i=ZA[t.brushType](0,e.coordSys,t.coordRange),n=t.__rangeOffset;t.range=n?XA[t.brushType](i.values,n.offset,function(t,e){var i=jA(t),n=jA(e),o=[i[0]/n[0],i[1]/n[1]];return isNaN(o[0])&&(o[0]=1),isNaN(o[1])&&(o[1]=1),o}(i.xyMinMax,n.xyMinMax)):i.values}},this)},BA.makePanelOpts=function(i,n){return O(this._targetInfoList,function(t){var e=t.getPanelRect();return{panelId:t.panelId,defaultBrushType:n&&n(t),clipPath:hS(e),isTargetByCursor:dS(e,i,t.coordSysModel),getLinearBrushOtherExtent:cS(e)}})},BA.controlSeries=function(t,e,i){var n=this.findTargetInfo(t,i);return!0===n||n&&0<=NA(n.coordSyses,e.coordinateSystem)},BA.findTargetInfo=function(t,e){for(var i=this._targetInfoList,n=GA(e,t),o=0;on[1]&&(n[1]=e[1])})}),n[1]c[1];if(r&&!s&&!l)return!0;r&&(n=!0),s&&(e=!0),l&&(i=!0)}return n&&e&&i}):rD(h,function(t){if("empty"===o)i.setData(u=u.map(t,function(t){return function(t){return t>=c[0]&&t<=c[1]}(t)?t:NaN}));else{var e={};e[t]=c,u.selectRange(e)}}),rD(h,function(t){u.setApproximateExtent(c,t)}))})}}};var uD=R,hD=nD,cD=Tf({type:"dataZoom",dependencies:["xAxis","yAxis","zAxis","radiusAxis","angleAxis","singleAxis","series"],defaultOption:{zlevel:0,z:4,orient:null,xAxisIndex:null,yAxisIndex:null,filterMode:"filter",throttle:null,start:0,end:100,startValue:null,endValue:null,minSpan:null,maxSpan:null,minValueSpan:null,maxValueSpan:null,rangeMode:null},init:function(t,e,i){this._dataIntervalByAxis={},this._dataInfo={},this._axisProxies={},this.textStyleModel,this._autoThrottle=!0,this._rangePropMode=["percent","percent"];var n=dD(t);this.settledOption=n,this.mergeDefaultAndTheme(t,i),this.doInit(n)},mergeOption:function(t){var e=dD(t);m(this.option,t,!0),m(this.settledOption,e,!0),this.doInit(e)},doInit:function(t){var i=this.option;v.canvasSupported||(i.realtime=!1),this._setDefaultThrottle(t),fD(this,t);var n=this.settledOption;uD([["start","startValue"],["end","endValue"]],function(t,e){"value"===this._rangePropMode[e]&&(i[t[0]]=n[t[0]]=null)},this),this.textStyleModel=this.getModel("textStyle"),this._resetTarget(),this._giveAxisProxies()},_giveAxisProxies:function(){var r=this._axisProxies;this.eachTargetAxis(function(t,e,i,n){var o=this.dependentModels[t.axis][e],a=o.__dzAxisProxy||(o.__dzAxisProxy=new aD(t.name,e,this,n));r[t.name+"_"+e]=a},this)},_resetTarget:function(){var i=this.option,t=this._judgeAutoMode();hD(function(t){var e=t.axisIndex;i[e]=Vo(i[e])},this),"axisIndex"===t?this._autoSetAxisIndex():"orient"===t&&this._autoSetOrient()},_judgeAutoMode:function(){var e=this.option,i=!1;hD(function(t){null!=e[t.axisIndex]&&(i=!0)},this);var t=e.orient;return null==t&&i?"orient":i?void 0:(null==t&&(e.orient="horizontal"),"axisIndex")},_autoSetAxisIndex:function(){var a=!0,e=this.get("orient",!0),r=this.option,t=this.dependentModels;if(a){var i="vertical"===e?"y":"x";t[i+"Axis"].length?(r[i+"AxisIndex"]=[0],a=!1):uD(t.singleAxis,function(t){a&&t.get("orient",!0)===e&&(r.singleAxisIndex=[t.componentIndex],a=!1)})}a&&hD(function(t){if(a){var e=[],i=this.dependentModels[t.axis];if(i.length&&!e.length)for(var n=0,o=i.length;ne[0][1]&&(e[0][1]=a[0]),a[1]e[1][1]&&(e[1][1]=a[1])}return e&&eC(e)}};function eC(t){return new Di(t[0][0],t[1][0],t[0][1]-t[0][0],t[1][1]-t[1][0])}var iC=["#ddd"];Tf({type:"brush",dependencies:["geo","grid","xAxis","yAxis","parallel","series"],defaultOption:{toolbox:null,brushLink:null,seriesIndex:"all",geoIndex:null,xAxisIndex:null,yAxisIndex:null,brushType:"rect",brushMode:"single",transformable:!0,brushStyle:{borderWidth:1,color:"rgba(120,140,180,0.3)",borderColor:"rgba(120,140,180,0.8)"},throttleType:"fixRate",throttleDelay:0,removeOnClick:!0,z:1e4},areas:[],brushType:null,brushOption:{},coordInfoList:[],optionUpdated:function(t,e){var i=this.option;e||WD(i,t,["inBrush","outOfBrush"]);var n=i.inBrush=i.inBrush||{};i.outOfBrush=i.outOfBrush||{color:iC},n.hasOwnProperty("liftZ")||(n.liftZ=5)},setAreas:function(t){t&&(this.areas=O(t,function(t){return nC(this.option,t)},this))},setBrushOption:function(t){this.brushOption=nC(this.option,t),this.brushType=this.brushOption.brushType}});function nC(t,e){return m({brushType:t.brushType,brushMode:t.brushMode,transformable:t.transformable,brushStyle:new Cl(t.brushStyle).getItemStyle(),removeOnClick:t.removeOnClick,z:t.z},e,!0)}function oC(t,e,i,n){n&&n.$from===t.id||this._brushController.setPanels(t.brushTargetManager.makePanelOpts(i)).enableBrush(t.brushOption).updateCovers(t.areas.slice())}Af({type:"brush",init:function(t,e){this.ecModel=t,this.api=e,this.model,(this._brushController=new Ob(e.getZr())).on("brush",A(this._onBrush,this)).mount()},render:function(t){return this.model=t,oC.apply(this,arguments)},updateTransform:function(t,e){return KD(e),oC.apply(this,arguments)},updateView:oC,dispose:function(){this._brushController.dispose()},_onBrush:function(t,e){var i=this.model.id;this.model.brushTargetManager.setOutputRanges(t,this.ecModel),e.isEnd&&!e.removeOnClick||this.api.dispatchAction({type:"brush",brushId:i,areas:k(t),$from:i}),e.isEnd&&this.api.dispatchAction({type:"brushEnd",brushId:i,areas:k(t),$from:i})}}),_f({type:"brush",event:"brush"},function(e,t){t.eachComponent({mainType:"brush",query:e},function(t){t.setAreas(e.areas)})}),_f({type:"brushSelect",event:"brushSelected",update:"none"},function(){}),_f({type:"brushEnd",event:"brushEnd",update:"none"},function(){});var aC=Oc.toolbox.brush;function rC(t,e,i){this.model=t,this.ecModel=e,this.api=i,this._brushType,this._brushMode}rC.defaultOption={show:!0,type:["rect","polygon","lineX","lineY","keep","clear"],icon:{rect:"M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13",polygon:"M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2",lineX:"M15.2,30 M19.7,15.6V1.9H29 M34.8,1.9H40.4 M55.3,15.6V1.9H45.9 M19.7,44.4V58.1H29 M34.8,58.1H40.4 M55.3,44.4 V58.1H45.9 M12.5,20.3l-9.4,9.6l9.6,9.8 M3.1,29.9h16.5 M62.5,20.3l9.4,9.6L62.3,39.7 M71.9,29.9H55.4",lineY:"M38.8,7.7 M52.7,12h13.2v9 M65.9,26.6V32 M52.7,46.3h13.2v-9 M24.9,12H11.8v9 M11.8,26.6V32 M24.9,46.3H11.8v-9 M48.2,5.1l-9.3-9l-9.4,9.2 M38.9-3.9V12 M48.2,53.3l-9.3,9l-9.4-9.2 M38.9,62.3V46.4",keep:"M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z",clear:"M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2"},title:k(aC.title)};var sC=rC.prototype;sC.render=sC.updateView=function(e,t,i){var n,o,a;t.eachComponent({mainType:"brush"},function(t){n=t.brushType,o=t.brushOption.brushMode||"single",a|=t.areas.length}),this._brushType=n,this._brushMode=o,R(e.get("type",!0),function(t){e.setIconStatus(t,("keep"===t?"multiple"===o:"clear"===t?a:t===n)?"emphasis":"normal")})},sC.getIcons=function(){var t=this.model,e=t.get("icon",!0),i={};return R(t.get("type",!0),function(t){e[t]&&(i[t]=e[t])}),i},sC.onclick=function(t,e,i){var n=this._brushType,o=this._brushMode;"clear"===i?(e.dispatchAction({type:"axisAreaSelect",intervals:[]}),e.dispatchAction({type:"brush",command:"clear",areas:[]})):e.dispatchAction({type:"takeGlobalCursor",key:"brush",brushOption:{brushType:"keep"===i?n:n!==i&&i,brushMode:"keep"===i?"multiple"===o?"single":"multiple":o}})},dA("brush",rC),yf(function(t,e){var i=t&&t.brush;if(L(i)||(i=i?[i]:[]),i.length){var n=[];R(i,function(t){var e=t.hasOwnProperty("toolbox")?t.toolbox:[];e instanceof Array&&(n=n.concat(e))});var o=t&&t.toolbox;L(o)&&(o=o[0]),o||(o={feature:{}},t.toolbox=[o]);var a=o.feature||(o.feature={}),r=a.brush||(a.brush={}),s=r.type||(r.type=[]);s.push.apply(s,n),function(i){var e={};R(i,function(t){e[t]=1}),i.length=0,R(e,function(t,e){i.push(e)})}(s),e&&!s.length&&s.push.apply(s,BD)}}),Tf({type:"title",layoutMode:{type:"box",ignoreSize:!0},defaultOption:{zlevel:0,z:6,show:!0,text:"",target:"blank",subtext:"",subtarget:"blank",left:0,top:0,backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,padding:5,itemGap:10,textStyle:{fontSize:18,fontWeight:"bolder",color:"#333"},subtextStyle:{color:"#aaa"}}}),Af({type:"title",render:function(t,e,i){if(this.group.removeAll(),t.get("show")){var n=this.group,o=t.getModel("textStyle"),a=t.getModel("subtextStyle"),r=t.get("textAlign"),s=H(t.get("textBaseline"),t.get("textVerticalAlign")),l=new Ur({style:nl({},o,{text:t.get("text"),textFill:o.getTextColor()},{disableBox:!0}),z2:10}),u=l.getBoundingRect(),h=t.get("subtext"),c=new Ur({style:nl({},a,{text:h,textFill:a.getTextColor(),y:u.height+t.get("itemGap"),textVerticalAlign:"top"},{disableBox:!0}),z2:10}),d=t.get("link"),f=t.get("sublink"),p=t.get("triggerEvent",!0);l.silent=!d&&!p,c.silent=!f&&!p,d&&l.on("click",function(){gu(d,"_"+t.get("target"))}),f&&c.on("click",function(){gu(f,"_"+t.get("subtarget"))}),l.eventData=c.eventData=p?{componentType:"title",componentIndex:t.componentIndex}:null,n.add(l),h&&n.add(c);var g=n.getBoundingRect(),m=t.getBoxLayoutParams();m.width=g.width,m.height=g.height;var v=bu(m,{width:i.getWidth(),height:i.getHeight()},t.get("padding"));r||("middle"===(r=t.get("left")||t.get("right"))&&(r="center"),"right"===r?v.x+=v.width:"center"===r&&(v.x+=v.width/2)),s||("center"===(s=t.get("top")||t.get("bottom"))&&(s="middle"),"bottom"===s?v.y+=v.height:"middle"===s&&(v.y+=v.height/2),s=s||"top"),n.attr("position",[v.x,v.y]);var y={textAlign:r,textVerticalAlign:s};l.setStyle(y),c.setStyle(y),g=n.getBoundingRect();var x=v.margin,_=t.getItemStyle(["color","opacity"]);_.fill=t.get("backgroundColor");var w=new rs({shape:{x:g.x-x[3],y:g.y-x[0],width:g.width+x[1]+x[3],height:g.height+x[0]+x[2],r:t.get("borderRadius")},style:_,subPixelOptimize:!0,silent:!0});n.add(w)}}});function lC(t){var e=t.itemStyle||(t.itemStyle={}),i=e.emphasis||(e.emphasis={}),n=t.label||t.label||{},o=n.normal||(n.normal={}),a={normal:1,emphasis:1};R(n,function(t,e){a[e]||uC(o,e)||(o[e]=t)}),i.label&&!uC(n,"emphasis")&&(n.emphasis=i.label,delete i.label)}function uC(t,e){return t.hasOwnProperty(e)}ku.registerSubTypeDefaulter("timeline",function(){return"slider"}),_f({type:"timelineChange",event:"timelineChanged",update:"prepareAndUpdate"},function(t,e){var i=e.getComponent("timeline");return i&&null!=t.currentIndex&&(i.setCurrentIndex(t.currentIndex),!i.get("loop",!0)&&i.isIndexMax()&&i.setPlayState(!1)),e.resetOption("timeline"),D({currentIndex:i.option.currentIndex},t)}),_f({type:"timelinePlayChange",event:"timelinePlayChanged",update:"update"},function(t,e){var i=e.getComponent("timeline");i&&null!=t.playState&&i.setPlayState(t.playState)});var hC=ku.extend({type:"timeline",layoutMode:"box",defaultOption:{zlevel:0,z:4,show:!0,axisType:"time",realtime:!0,left:"20%",top:null,right:"20%",bottom:0,width:null,height:40,padding:5,controlPosition:"left",autoPlay:!1,rewind:!1,loop:!0,playInterval:2e3,currentIndex:0,itemStyle:{},label:{color:"#000"},data:[]},init:function(t,e,i){this._data,this._names,this.mergeDefaultAndTheme(t,i),this._initData()},mergeOption:function(t){hC.superApply(this,"mergeOption",arguments),this._initData()},setCurrentIndex:function(t){null==t&&(t=this.option.currentIndex);var e=this._data.count();this.option.loop?t=(t%e+e)%e:(e<=t&&(t=e-1),t<0&&(t=0)),this.option.currentIndex=t},getCurrentIndex:function(){return this.option.currentIndex},isIndexMax:function(){return this.getCurrentIndex()>=this._data.count()-1},setPlayState:function(t){this.option.autoPlay=!!t},getPlayState:function(){return!!this.option.autoPlay},_initData:function(){var t=this.option,e=t.data||[],i=t.axisType,o=this._names=[];if("category"===i){var a=[];R(e,function(t,e){var i,n=Wo(t);z(t)?(i=k(t)).value=e:i=e,a.push(i),E(n)||null!=n&&!isNaN(n)||(n=""),o.push(n+"")}),e=a}var n={category:"ordinal",time:"time"}[i]||"number";(this._data=new Yf([{name:"value",type:n}],this)).initData(e,o)},getData:function(){return this._data},getCategories:function(){if("category"===this.get("axisType"))return this._names.slice()}});b(hC.extend({type:"timeline.slider",defaultOption:{backgroundColor:"rgba(0,0,0,0)",borderColor:"#ccc",borderWidth:0,orient:"horizontal",inverse:!1,tooltip:{trigger:"item"},symbol:"emptyCircle",symbolSize:10,lineStyle:{show:!0,width:2,color:"#304654"},label:{position:"auto",show:!0,interval:"auto",rotate:0,color:"#304654"},itemStyle:{color:"#304654",borderWidth:1},checkpointStyle:{symbol:"circle",symbolSize:13,color:"#c23531",borderWidth:5,borderColor:"rgba(194,53,49, 0.5)",animation:!0,animationDuration:300,animationEasing:"quinticInOut"},controlStyle:{show:!0,showPlayBtn:!0,showPrevBtn:!0,showNextBtn:!0,itemSize:22,itemGap:12,position:"left",playIcon:"path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z",stopIcon:"path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z",nextIcon:"path://M18.6,50.8l22.5-22.5c0.2-0.2,0.3-0.4,0.3-0.7c0-0.3-0.1-0.5-0.3-0.7L18.7,4.4c-0.1-0.1-0.2-0.3-0.2-0.5 c0-0.4,0.3-0.8,0.8-0.8c0.2,0,0.5,0.1,0.6,0.3l23.5,23.5l0,0c0.2,0.2,0.3,0.4,0.3,0.7c0,0.3-0.1,0.5-0.3,0.7l-0.1,0.1L19.7,52 c-0.1,0.1-0.3,0.2-0.5,0.2c-0.4,0-0.8-0.3-0.8-0.8C18.4,51.2,18.5,51,18.6,50.8z",prevIcon:"path://M43,52.8L20.4,30.3c-0.2-0.2-0.3-0.4-0.3-0.7c0-0.3,0.1-0.5,0.3-0.7L42.9,6.4c0.1-0.1,0.2-0.3,0.2-0.5 c0-0.4-0.3-0.8-0.8-0.8c-0.2,0-0.5,0.1-0.6,0.3L18.3,28.8l0,0c-0.2,0.2-0.3,0.4-0.3,0.7c0,0.3,0.1,0.5,0.3,0.7l0.1,0.1L41.9,54 c0.1,0.1,0.3,0.2,0.5,0.2c0.4,0,0.8-0.3,0.8-0.8C43.2,53.2,43.1,53,43,52.8z",color:"#304654",borderColor:"#304654",borderWidth:1},emphasis:{label:{show:!0,color:"#c23531"},itemStyle:{color:"#c23531"},controlStyle:{color:"#c23531",borderColor:"#c23531",borderWidth:2}},data:[]}}),Xh);function cC(t,e,i,n){Gg.call(this,t,e,i),this.type=n||"value",this.model=null}var dC=gc.extend({type:"timeline"});cC.prototype={constructor:cC,getLabelModel:function(){return this.model.getModel("label")},isHorizontal:function(){return"horizontal"===this.model.get("orient")}},w(cC,Gg);var fC=A,pC=R,gC=Math.PI;function mC(t,e,i,n,o,a){var r=e.get("color");o?(o.setColor(r),i.add(o),a&&a.onUpdate(o)):((o=wg(t.get("symbol"),-1,-1,2,2,r)).setStyle("strokeNoScale",!0),i.add(o),a&&a.onCreate(o));var s=e.getItemStyle(["color","symbol","symbolSize"]);o.setStyle(s),n=m({rectHover:!0,z2:100},n,!0);var l=t.get("symbolSize");(l=l instanceof Array?l.slice():[+l,+l])[0]/=2,l[1]/=2,n.scale=l;var u=t.get("symbolOffset");if(u){var h=n.position=n.position||[0,0];h[0]+=El(u[0],l[0]),h[1]+=El(u[1],l[1])}var c=t.get("symbolRotate");return n.rotation=(c||0)*Math.PI/180||0,o.attr(n),o.updateTransform(),o}function vC(t,e,i,n,o){if(!t.dragging){var a=n.getModel("checkpointStyle"),r=i.dataToCoord(n.getData().get(["value"],e));o||!a.get("animation",!0)?t.attr({position:[r,0]}):(t.stopAnimation(!0),t.animateTo({position:[r,0]},a.get("animationDuration",!0),a.get("animationEasing",!0)))}}dC.extend({type:"timeline.slider",init:function(t,e){this.api=e,this._axis,this._viewRect,this._timer,this._currentPointer,this._mainGroup,this._labelGroup},render:function(e,t,i,n){if(this.model=e,this.api=i,this.ecModel=t,this.group.removeAll(),e.get("show",!0)){var o=this._layout(e,i),a=this._createGroup("mainGroup"),r=this._createGroup("labelGroup"),s=this._axis=this._createAxis(o,e);e.formatTooltip=function(t){return au(s.scale.getLabel(t))},pC(["AxisLine","AxisTick","Control","CurrentPointer"],function(t){this["_render"+t](o,a,s,e)},this),this._renderAxisLabel(o,r,s,e),this._position(o,e)}this._doPlayStop()},remove:function(){this._clearTimer(),this.group.removeAll()},dispose:function(){this._clearTimer()},_layout:function(t,e){var i=t.get("label.position"),n=t.get("orient"),o=function(t,e){return bu(t.getBoxLayoutParams(),{width:e.getWidth(),height:e.getHeight()},t.get("padding"))}(t,e);null==i||"auto"===i?i="horizontal"===n?o.y+o.height/2n[1]&&(i=n[1]),i":"\n"),s&&(l+=xC(s),null!=a&&(l+=" : ")),null!=a&&(l+=xC(r)),l},getData:function(){return this._data},setData:function(t){this._data=t}});b(wC,Xh),wC.extend({type:"markPoint",defaultOption:{zlevel:0,z:5,symbol:"pin",symbolSize:50,tooltip:{trigger:"item"},label:{show:!0,position:"inside"},itemStyle:{borderWidth:2},emphasis:{label:{show:!0}}}});var bC=_;function SC(t,e,i,n,o,a){var r=[],s=fp(e,n)?e.getCalculationInfo("stackResultDimension"):n,l=LC(e,s,t),u=e.indicesOfNearest(s,l)[0];r[o]=e.get(i,u),r[a]=e.get(s,u);var h=e.get(n,u),c=Vl(e.get(n,u));return 0<=(c=Math.min(c,20))&&(r[a]=+r[a].toFixed(c)),[r,h]}var MC=T,IC={min:MC(SC,"min"),max:MC(SC,"max"),average:MC(SC,"average")};function TC(t,e){var i=t.getData(),n=t.coordinateSystem;if(e&&!function(t){return!isNaN(parseFloat(t.x))&&!isNaN(parseFloat(t.y))}(e)&&!L(e.coord)&&n){var o=n.dimensions,a=AC(e,i,n,t);if((e=k(e)).type&&IC[e.type]&&a.baseAxis&&a.valueAxis){var r=bC(o,a.baseAxis.dim),s=bC(o,a.valueAxis.dim),l=IC[e.type](i,a.baseDataDim,a.valueDataDim,r,s);e.coord=l[0],e.value=l[1]}else{for(var u=[null!=e.xAxis?e.xAxis:e.radiusAxis,null!=e.yAxis?e.yAxis:e.angleAxis],h=0;h<2;h++)IC[u[h]]&&(u[h]=LC(i,i.mapDimension(o[h]),u[h]));e.coord=u}}return e}function AC(t,e,i,n){var o={};return null!=t.valueIndex||null!=t.valueDim?(o.valueDataDim=null!=t.valueIndex?e.getDimension(t.valueIndex):t.valueDim,o.valueAxis=i.getAxis(function(t,e){var i=t.getData(),n=i.dimensions;e=i.getDimension(e);for(var o=0;oi[o],f=[-h.x,-h.y];e||(f[n]=s.position[n]);var p=[0,0],g=[-c.x,-c.y],m=H(t.get("pageButtonGap",!0),t.get("itemGap",!0));d&&("end"===t.get("pageButtonPosition",!0)?g[n]+=i[o]-c[o]:p[n]+=c[o]+m);g[1-n]+=h[a]/2-c[a]/2,s.attr("position",f),l.attr("position",p),u.attr("position",g);var v={x:0,y:0};if(v[o]=d?i[o]:h[o],v[a]=Math.max(h[a],c[a]),v[r]=Math.min(0,c[r]+g[1-n]),l.__rectSize=i[o],d){var y={x:0,y:0};y[o]=Math.max(i[o]-c[o]-m,0),y[a]=v[a],l.setClipPath(new rs({shape:y})),l.__rectSize=y[o]}else u.eachChild(function(t){t.attr({invisible:!0,silent:!0})});var x=this._getPageInfo(t);return null!=x.pageIndex&&cl(s,{position:x.contentPosition},d&&t),this._updatePageInfoView(t,x),v},_pageGo:function(t,e,i){var n=this._getPageInfo(e)[t];null!=n&&i.dispatchAction({type:"legendScroll",scrollDataIndex:n,legendId:e.id})},_updatePageInfoView:function(n,o){var a=this._controllerGroup;R(["pagePrev","pageNext"],function(t){var e=null!=o[t+"DataIndex"],i=a.childOfName(t);i&&(i.setStyle("fill",e?n.get("pageIconColor",!0):n.get("pageIconInactiveColor",!0)),i.cursor=e?"pointer":"default")});var t=a.childOfName("pageText"),e=n.get("pageFormatter"),i=o.pageIndex,r=null!=i?i+1:0,s=o.pageCount;t&&e&&t.setStyle("text",E(e)?e.replace("{current}",r).replace("{total}",s):e({current:r,total:s}))},_getPageInfo:function(t){var e=t.get("scrollDataIndex",!0),i=this.getContentGroup(),n=this._containerGroup.__rectSize,o=t.getOrient().index,a=aL[o],r=rL[o],s=this._findTargetItemIndex(e),l=i.children(),u=l[s],h=l.length,c=h?1:0,d={contentPosition:i.position.slice(),pageCount:c,pageIndex:c-1,pagePrevDataIndex:null,pageNextDataIndex:null};if(!u)return d;var f=y(u);d.contentPosition[o]=-f.s;for(var p=s+1,g=f,m=f,v=null;p<=h;++p)(!(v=y(l[p]))&&m.e>g.s+n||v&&!x(v,g.s))&&(g=m.i>g.i?m:v)&&(null==d.pageNextDataIndex&&(d.pageNextDataIndex=g.i),++d.pageCount),m=v;for(p=s-1,g=f,m=f,v=null;-1<=p;--p)(v=y(l[p]))&&x(m,v.s)||!(g.i=e&&t.s<=e+n}},_findTargetItemIndex:function(n){return this._showController?(this.getContentGroup().eachChild(function(t,e){var i=t.__legendDataIndex;null==a&&null!=i&&(a=e),i===n&&(o=e)}),null!=o?o:a):0;var o,a}});_f("legendScroll","legendscroll",function(t,e){var i=t.scrollDataIndex;null!=i&&e.eachComponent({mainType:"legend",subType:"scroll",query:t},function(t){t.setScrollDataIndex(i)})});cD.extend({type:"dataZoom.slider",layoutMode:"box",defaultOption:{show:!0,right:"ph",top:"ph",width:"ph",height:"ph",left:null,bottom:null,backgroundColor:"rgba(47,69,84,0)",dataBackground:{lineStyle:{color:"#2f4554",width:.5,opacity:.3},areaStyle:{color:"rgba(47,69,84,0.3)",opacity:.3}},borderColor:"#ddd",fillerColor:"rgba(167,183,204,0.4)",handleIcon:"M8.2,13.6V3.9H6.3v9.7H3.1v14.9h3.3v9.7h1.8v-9.7h3.3V13.6H8.2z M9.7,24.4H4.8v-1.4h4.9V24.4z M9.7,19.1H4.8v-1.4h4.9V19.1z",handleSize:"100%",handleStyle:{color:"#a7b7cc"},labelPrecision:null,labelFormatter:null,showDetail:!0,showDataShadow:"auto",realtime:!0,zoomLock:!1,textStyle:{color:"#333"}}});var lL=rs,uL=Rl,hL=Bl,cL=A,dL=R,fL="horizontal",pL="vertical",gL=["line","bar","candlestick","scatter"],mL=pD.extend({type:"dataZoom.slider",init:function(t,e){this._displayables={},this._orient,this._range,this._handleEnds,this._size,this._handleWidth,this._handleHeight,this._location,this._dragging,this._dataShadowInfo,this.api=e},render:function(t,e,i,n){mL.superApply(this,"render",arguments),kc(this,"_dispatchZoomAction",this.dataZoomModel.get("throttle"),"fixRate"),this._orient=t.get("orient"),!1!==this.dataZoomModel.get("show")?(n&&"dataZoom"===n.type&&n.from===this.uid||this._buildView(),this._updateView()):this.group.removeAll()},remove:function(){mL.superApply(this,"remove",arguments),Pc(this,"_dispatchZoomAction")},dispose:function(){mL.superApply(this,"dispose",arguments),Pc(this,"_dispatchZoomAction")},_buildView:function(){var t=this.group;t.removeAll(),this._resetLocation(),this._resetInterval();var e=this._displayables.barGroup=new Ci;this._renderBackground(),this._renderHandle(),this._renderDataShadow(),t.add(e),this._positionGroup()},_resetLocation:function(){var t=this.dataZoomModel,e=this.api,i=this._findCoordRect(),n={width:e.getWidth(),height:e.getHeight()},o=this._orient===fL?{right:n.width-i.x-i.width,top:n.height-30-7,width:i.width,height:30}:{right:7,top:i.y,width:30,height:i.height},a=Iu(t.option);R(["right","top","width","height"],function(t){"ph"===a[t]&&(a[t]=o[t])});var r=bu(a,n,t.padding);this._location={x:r.x,y:r.y},this._size=[r.width,r.height],this._orient===pL&&this._size.reverse()},_positionGroup:function(){var t=this.group,e=this._location,i=this._orient,n=this.dataZoomModel.getFirstTargetAxisModel(),o=n&&n.get("inverse"),a=this._displayables.barGroup,r=(this._dataShadowInfo||{}).otherAxisInverse;a.attr(i!==fL||o?i===fL&&o?{scale:r?[-1,1]:[-1,-1]}:i!==pL||o?{scale:r?[-1,-1]:[-1,1],rotation:Math.PI/2}:{scale:r?[1,-1]:[1,1],rotation:Math.PI/2}:{scale:r?[1,1]:[1,-1]});var s=t.getBoundingRect([a]);t.attr("position",[e.x-s.x,e.y-s.y])},_getViewExtent:function(){return[0,this._size[0]]},_renderBackground:function(){var t=this.dataZoomModel,e=this._size,i=this._displayables.barGroup;i.add(new lL({silent:!0,shape:{x:0,y:0,width:e[0],height:e[1]},style:{fill:t.get("backgroundColor")},z2:-40})),i.add(new lL({shape:{x:0,y:0,width:e[0],height:e[1]},style:{fill:"transparent"},z2:0,onclick:A(this._onClickPanelClick,this)}))},_renderDataShadow:function(){var t=this._dataShadowInfo=this._prepareDataShadowInfo();if(t){var e=this._size,i=t.series,n=i.getRawData(),o=i.getShadowDim?i.getShadowDim():t.otherDim;if(null!=o){var a=n.getDataExtent(o),r=.3*(a[1]-a[0]);a=[a[0]-r,a[1]+r];var s,l=[0,e[1]],u=[0,e[0]],h=[[e[0],0],[0,0]],c=[],d=u[1]/(n.count()-1),f=0,p=Math.round(n.count()/e[0]);n.each([o],function(t,e){if(0e[0]||i[1]<0||i[1]>e[1])){var n=this._handleEnds,o=(n[0]+n[1])/2,a=this._updateInterval("all",i[0]-o);this._updateView(),a&&this._dispatchZoomAction()}},_dispatchZoomAction:function(){var t=this._range;this.api.dispatchAction({type:"dataZoom",from:this.uid,dataZoomId:this.dataZoomModel.id,start:t[0],end:t[1]})},_findCoordRect:function(){var i;if(dL(this.getTargetCoordInfo(),function(t){if(!i&&t.length){var e=t[0].model.coordinateSystem;i=e.getRect&&e.getRect()}}),!i){var t=this.api.getWidth(),e=this.api.getHeight();i={x:.2*t,y:.2*e,width:.6*t,height:.6*e}}return i}});function vL(t){return"vertical"===t?"ns-resize":"ew-resize"}cD.extend({type:"dataZoom.inside",defaultOption:{disabled:!1,zoomLock:!1,zoomOnMouseWheel:!0,moveOnMouseMove:!0,moveOnMouseWheel:!1,preventDefaultMouseMove:!0}});var yL="\0_ec_dataZoom_roams";function xL(t,n){var e=wL(t),o=n.dataZoomId,a=n.coordId;R(e,function(t,e){var i=t.dataZoomInfos;i[o]&&_(n.allCoordIds,a)<0&&(delete i[o],t.count--)}),bL(e);var i=e[a];i||((i=e[a]={coordId:a,dataZoomInfos:{},count:0}).controller=function(t,r){var e=new Vy(t.getZr());return R(["pan","zoom","scrollMove"],function(a){e.on(a,function(n){var o=[];R(r.dataZoomInfos,function(t){if(n.isAvailableBehavior(t.dataZoomModel.option)){var e=(t.getRange||{})[a],i=e&&e(r.controller,n);!t.dataZoomModel.get("disabled",!0)&&i&&o.push({dataZoomId:t.dataZoomId,start:i[0],end:i[1]})}}),o.length&&r.dispatchAction(o)})}),e}(t,i),i.dispatchAction=T(SL,t)),i.dataZoomInfos[o]||i.count++,i.dataZoomInfos[o]=n;var r=function(t){var n,o={type_true:2,type_move:1,type_false:0,type_undefined:-1},a=!0;return R(t,function(t){var e=t.dataZoomModel,i=!e.get("disabled",!0)&&(!e.get("zoomLock",!0)||"move");o["type_"+n]"],L(t)&&(t=t.slice(),n=!0),o=e?t:n?[u(t[0]),u(t[1])]:u(t),E(l)?l.replace("{value}",n?o[0]:o).replace("{value2}",n?o[1]:o):C(l)?n?l(t[0],t[1]):l(t):n?t[0]===s[0]?i[0]+" "+o[1]:t[1]===s[1]?i[1]+" "+o[0]:o[0]+" - "+o[1]:o;function u(t){return t===s[0]?"min":t===s[1]?"max":(+t).toFixed(Math.min(r,20))}},resetExtent:function(){var t=this.option,e=GL([t.min,t.max]);this._dataExtent=e},getDataDimension:function(t){var e=this.option.dimension,i=t.dimensions;if(null!=e||i.length){if(null!=e)return t.getDimension(e);for(var n=t.dimensions,o=n.length-1;0<=o;o--){var a=n[o];if(!t.getDimensionInfo(a).isCalculationCoord)return a}}},getExtent:function(){return this._dataExtent.slice()},completeVisualOption:function(){var t=this.ecModel,e=this.option,i={inRange:e.inRange,outOfRange:e.outOfRange},n=e.target||(e.target={}),o=e.controller||(e.controller={});m(n,i),m(o,i);var u=this.isCategory();function a(n){BL(e.color)&&!n.inRange&&(n.inRange={color:e.color.slice().reverse()}),n.inRange=n.inRange||{color:t.get("gradientColor")},VL(this.stateList,function(t){var e=n[t];if(E(e)){var i=OL(e,"active",u);i?(n[t]={},n[t][e]=i):delete n[t]}},this)}a.call(this,n),a.call(this,o),function(t,e,i){var n=t[e],o=t[i];n&&!o&&(o=t[i]={},VL(n,function(t,e){if(g_.isValidType(e)){var i=OL(e,"inactive",u);null!=i&&(o[e]=i,"color"!==e||o.hasOwnProperty("opacity")||o.hasOwnProperty("colorAlpha")||(o.opacity=[0,0]))}}))}.call(this,n,"inRange","outOfRange"),function(a){var r=(a.inRange||{}).symbol||(a.outOfRange||{}).symbol,s=(a.inRange||{}).symbolSize||(a.outOfRange||{}).symbolSize,l=this.get("inactiveColor");VL(this.stateList,function(t){var e=this.itemSize,i=a[t];null==(i=i||(a[t]={color:u?l:[l]})).symbol&&(i.symbol=r&&k(r)||(u?"roundRect":["roundRect"])),null==i.symbolSize&&(i.symbolSize=s&&k(s)||(u?e[0]:[e[0],e[0]])),i.symbol=EL(i.symbol,function(t){return"none"===t||"square"===t?"roundRect":t});var n=i.symbolSize;if(null!=n){var o=-1/0;zL(n,function(t){oe[1]&&e.reverse(),e[0]=Math.max(e[0],t[0]),e[1]=Math.min(e[1],t[1]))},completeVisualOption:function(){WL.prototype.completeVisualOption.apply(this,arguments),R(this.stateList,function(t){var e=this.option.controller[t].symbolSize;e&&e[0]!==e[1]&&(e[0]=0)},this)},setSelected:function(t){this.option.range=t.slice(),this._resetRange()},getSelected:function(){var t=this.getExtent(),e=Bl((this.get("range")||[]).slice());return e[0]>t[1]&&(e[0]=t[1]),e[1]>t[1]&&(e[1]=t[1]),e[0]=i[1]||t<=e[1])?"inRange":"outOfRange"},findTargetDataIndices:function(n){var o=[];return this.eachTargetSeries(function(t){var i=[],e=t.getData();e.each(this.getDataDimension(e),function(t,e){n[0]<=t&&t<=n[1]&&i.push(e)},this),o.push({seriesId:t.id,dataIndex:i})},this),o},getVisualMeta:function(i){var t=UL(this,"outOfRange",this.getExtent()),e=UL(this,"inRange",this.option.range.slice()),n=[];function o(t,e){n.push({value:t,color:i(t,e)})}for(var a=0,r=0,s=e.length,l=t.length;rt[1])break;i.push({color:this.getControllerVisual(a,"color",e),offset:o/100})}return i.push({color:this.getControllerVisual(t[1],"color",e),offset:1}),i},_createBarPoints:function(t,e){var i=this.visualMapModel.itemSize;return[[i[0]-e[0],t[0]],[i[0],t[0]],[i[0],t[1]],[i[0]-e[1],t[1]]]},_createBarGroup:function(t){var e=this._orient,i=this.visualMapModel.get("inverse");return new Ci("horizontal"!==e||i?"horizontal"===e&&i?{scale:"bottom"===t?[-1,1]:[1,1],rotation:-Math.PI/2}:"vertical"!==e||i?{scale:"left"===t?[1,1]:[-1,1]}:{scale:"left"===t?[1,-1]:[-1,-1]}:{scale:"bottom"===t?[1,1]:[-1,1],rotation:Math.PI/2})},_updateHandle:function(n,o){if(this._useHandle){var a=this._shapes,r=this.visualMapModel,s=a.handleThumbs,l=a.handleLabels;KL([0,1],function(t){var e=s[t];e.setStyle("fill",o.handlesColor[t]),e.position[1]=n[t];var i=pl(a.handleLabelPoints[t],fl(e,this.group));l[t].setStyle({x:i[0],y:i[1],text:r.formatValueText(this._dataInterval[t]),textVerticalAlign:"middle",textAlign:this._applyTransform("horizontal"===this._orient?0===t?"bottom":"top":"left",a.barGroup)})},this)}},_showIndicator:function(t,e,i,n){var o=this.visualMapModel,a=o.getExtent(),r=o.itemSize,s=[0,r[1]],l=qL(t,a,s,!0),u=this._shapes,h=u.indicator;if(h){h.position[1]=l,h.attr("invisible",!1),h.setShape("points",function(t,e,i,n){return t?[[0,-$L(e,JL(i,0))],[6,0],[0,$L(e,JL(n-i,0))]]:[[0,0],[5,-5],[5,5]]}(!!i,n,l,r[1]));var c=this.getControllerVisual(t,"color",{convertOpacityToAlpha:!0});h.setStyle("fill",c);var d=pl(u.indicatorLabelPoint,fl(h,this.group)),f=u.indicatorLabel;f.attr("invisible",!1);var p=this._applyTransform("left",u.barGroup),g=this._orient;f.setStyle({text:(i||"")+o.formatValueText(e),textVerticalAlign:"horizontal"===g?p:"middle",textAlign:"horizontal"===g?"center":p,x:d[0],y:d[1]})}},_enableHoverLinkToSeries:function(){var n=this;this._shapes.barGroup.on("mousemove",function(t){if(n._hovering=!0,!n._dragging){var e=n.visualMapModel.itemSize,i=n._applyTransform([t.offsetX,t.offsetY],n._shapes.barGroup,!0,!0);i[1]=$L(JL(0,i[1]),e[1]),n._doHoverLinkToSeries(i[1],0<=i[0]&&i[0]<=e[0])}}).on("mouseout",function(){n._hovering=!1,n._dragging||n._clearHoverLinkToSeries()})},_enableHoverLinkFromSeries:function(){var t=this.api.getZr();this.visualMapModel.option.hoverLink?(t.on("mouseover",this._hoverLinkFromSeriesMouseOver,this),t.on("mouseout",this._hideIndicator,this)):this._clearHoverLinkFromSeries()},_doHoverLinkToSeries:function(t,e){var i=this.visualMapModel,n=i.itemSize;if(i.option.hoverLink){var o=[0,n[1]],a=i.getExtent();t=$L(JL(o[0],t),o[1]);var r=function(t,e,i){var n=6,o=t.get("hoverLinkDataSize");o&&(n=qL(o,e,i,!0)/2);return n}(i,a,o),s=[t-r,t+r],l=qL(t,o,a,!0),u=[qL(s[0],o,a,!0),qL(s[1],o,a,!0)];s[0] ",r):this._showIndicator(l,l,"≈ ",r));var h=this._hoverLinkDataIndices,c=[];(e||ek(i))&&(c=this._hoverLinkDataIndices=i.findTargetDataIndices(u));var d=function(t,e){var i={},n={};return o(t||[],i),o(e||[],n,i),[a(i),a(n)];function o(t,e,i){for(var n=0,o=t.length;ni&&n([i,e[0]],"outOfRange"),n(e.slice()),i=e[1])},this),{stops:a,outerColors:r}}function n(t,e){var i=s.getRepresentValue({interval:t});e=e||s.getValueState(i);var n=o(i,e);t[0]===-1/0?r[0]=n:t[1]===1/0?r[1]=n:a.push({value:t[0],color:n},{value:t[1],color:n})}}}),ok={splitNumber:function(){var t=this.option,e=this._pieceList,i=Math.min(t.precision,20),n=this.getExtent(),o=t.splitNumber;o=Math.max(parseInt(o,10),1),t.splitNumber=o;for(var a=(n[1]-n[0])/o;+a.toFixed(i)!==a&&i<5;)i++;t.precision=i,a=+a.toFixed(i),t.minOpen&&e.push({interval:[-1/0,n[0]],close:[0,0]});for(var r=0,s=n[0];r","≥"][e[0]]];t.text=t.text||this.formatValueText(null!=t.value?t.value:t.interval,!1,i)},this)}};function ak(t,e){var i=t.inverse;("vertical"===t.orient?!i:i)&&e.reverse()}XL.extend({type:"visualMap.piecewise",doRender:function(){var a=this.group;a.removeAll();var r=this.visualMapModel,s=r.get("textGap"),t=r.textStyleModel,l=t.getFont(),u=t.getTextColor(),h=this._getItemAlign(),c=r.itemSize,e=this._getViewData(),i=e.endsText,d=W(r.get("showLabel",!0),!i);i&&this._renderEndsText(a,i[0],c,d,h),R(e.viewPieceList,function(t){var e=t.piece,i=new Ci;i.onclick=A(this._onItemClick,this,e),this._enableHoverLink(i,t.indexInModelPieceList);var n=r.getRepresentValue(e);if(this._createItemSymbol(i,n,[0,0,c[0],c[1]]),d){var o=this.visualMapModel.getValueState(n);i.add(new Ur({style:{x:"right"===h?-s:c[0]+s,y:c[1]/2,text:e.text,textVerticalAlign:"middle",textAlign:h,textFont:l,textFill:u,opacity:"outOfRange"===o?.5:1}}))}a.add(i)},this),i&&this._renderEndsText(a,i[1],c,d,h),wu(r.get("orient"),a,r.get("itemGap")),this.renderBackground(a),this.positionGroup(a)},_enableHoverLink:function(t,i){function e(t){var e=this.visualMapModel;e.option.hoverLink&&this.api.dispatchAction({type:t,batch:jL(e.findTargetDataIndices(i),e)})}t.on("mouseover",A(e,this,"highlight")).on("mouseout",A(e,this,"downplay"))},_getItemAlign:function(){var t=this.visualMapModel,e=t.option;if("vertical"===e.orient)return YL(t,this.api,t.itemSize);var i=e.align;return i&&"auto"!==i||(i="left"),i},_renderEndsText:function(t,e,i,n,o){if(e){var a=new Ci,r=this.visualMapModel.textStyleModel;a.add(new Ur({style:{x:n?"right"===o?i[0]:0:i[0]/2,y:i[1]/2,textVerticalAlign:"middle",textAlign:n?o:"center",text:e,textFont:r.getFont(),textFill:r.getTextColor()}})),t.add(a)}},_getViewData:function(){var t=this.visualMapModel,e=O(t.getPieceList(),function(t,e){return{piece:t,indexInModelPieceList:e}}),i=t.get("text"),n=t.get("orient"),o=t.get("inverse");return("horizontal"===n?o:!o)?e.reverse():i=i&&i.slice().reverse(),{viewPieceList:e,endsText:i}},_createItemSymbol:function(t,e,i){t.add(wg(this.getControllerVisual(e,"symbol"),i[0],i[1],i[2],i[3],this.getControllerVisual(e,"color")))},_onItemClick:function(t){var e=this.visualMapModel,i=e.option,n=k(i.selected),o=e.getSelectedMapKey(t);"single"===i.selectedMode?(n[o]=!0,R(n,function(t,e){n[e]=e===o})):n[o]=!n[o],this.api.dispatchAction({type:"selectDataRange",from:this.uid,visualMapId:this.visualMapModel.id,selected:n})}});yf(DL);var rk,sk="urn:schemas-microsoft-com:vml",lk="undefined"==typeof window?null:window,uk=!1,hk=lk&&lk.document;function ck(t){return rk(t)}if(hk&&!v.canvasSupported)try{hk.namespaces.zrvml||hk.namespaces.add("zrvml",sk),rk=function(t){return hk.createElement("')}}catch(t){rk=function(t){return hk.createElement("<"+t+' xmlns="'+sk+'" class="zrvml">')}}var dk,fk=rr.CMD,pk=Math.round,gk=Math.sqrt,mk=Math.abs,vk=Math.cos,yk=Math.sin,xk=Math.max;if(!v.canvasSupported){var _k=",",wk="progid:DXImageTransform.Microsoft",bk=21600,Sk=bk/2,Mk=function(t){t.style.cssText="position:absolute;left:0;top:0;width:1px;height:1px;",t.coordsize=bk+","+bk,t.coordorigin="0,0"},Ik=function(t,e,i){return"rgb("+[t,e,i].join(",")+")"},Tk=function(t,e){e&&t&&e.parentNode!==t&&t.appendChild(e)},Ak=function(t,e){e&&t&&e.parentNode===t&&t.removeChild(e)},Dk=function(t,e,i){return 1e5*(parseFloat(t)||0)+1e3*(parseFloat(e)||0)+i},Ck=Yn,Lk=function(t,e,i){var n=Fe(e);i=+i,isNaN(i)&&(i=1),n&&(t.color=Ik(n[0],n[1],n[2]),t.opacity=i*n[3])},kk=function(t,e,i,n){var o="fill"===e,a=t.getElementsByTagName(e)[0];null!=i[e]&&"none"!==i[e]&&(o||!o&&i.lineWidth)?(t[o?"filled":"stroked"]="true",i[e]instanceof cs&&Ak(t,a),a=a||ck(e),o?function(t,e,i){var n,o,a=e.fill;if(null!=a)if(a instanceof cs){var r,s=0,l=[0,0],u=0,h=1,c=i.getBoundingRect(),d=c.width,f=c.height;if("linear"===a.type){r="gradient";var p=i.transform,g=[a.x*d,a.y*f],m=[a.x2*d,a.y2*f];p&&(bt(g,g,p),bt(m,m,p));var v=m[0]-g[0],y=m[1]-g[1];(s=180*Math.atan2(v,y)/Math.PI)<0&&(s+=360),s<1e-6&&(s=0)}else{r="gradientradial";g=[a.x*d,a.y*f],p=i.transform;var x=i.scale,_=d,w=f;l=[(g[0]-c.x)/_,(g[1]-c.y)/w],p&&bt(g,g,p),_/=x[0]*bk,w/=x[1]*bk;var b=xk(_,w);u=0/b,h=2*a.r/b-u}var S=a.colorStops.slice();S.sort(function(t,e){return t.offset-e.offset});for(var M=S.length,I=[],T=[],A=0;A=c&&d<=i+1){for(var n=[],o=0;o=c&&d<=o+1)return _P(h,e.components,u,l);p[t]=e}else p[t]=void 0}var s;f++}for(;f<=e;){var r=a();if(r)return r}},pushComponent:function(t,e,i){var n=t[t.length-1];n&&n.added===e&&n.removed===i?t[t.length-1]={count:n.count+1,added:e,removed:i}:t.push({count:1,added:e,removed:i})},extractCommon:function(t,e,i,n){for(var o=e.length,a=i.length,r=t.newPos,s=r-n,l=0;r+1\n\r<"))}},R(["getLayer","insertLayer","eachLayer","eachBuiltinLayer","eachOtherLayer","getLayers","modLayer","delLayer","clearLayer","pathToImage"],function(t){OP.prototype[t]=function(t){return function(){vi('In SVG mode painter not support method "'+t+'"')}}(t)}),Po("svg",OP),t.version="4.9.0",t.dependencies={zrender:"4.3.2"},t.PRIORITY=Ld,t.init=function(t,e,i){var n=mf(t);if(n)return n;var o=new Ed(t,e,i);return o.id="ec_"+cf++,uf[o.id]=o,Jo(t,ff,o.id),function(n){var o="__connectUpdateStatus";function a(t,e){for(var i=0;i b.latest_time +end + + +function user_status() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local fd = io.open("/proc/net/af_client","r") + status_buf=fd:read('*a') + fd:close() + user_array=json.parse(status_buf) + + local visit_obj=utl.ubus("appfilter", "visit_list", {}); + local user_array=visit_obj.dev_list + local history={} + for i, v in pairs(user_array) do + visit_array=user_array[i].visit_info + for j,s in pairs(visit_array) do + print(user_array[i].mac, user_array[i].ip,visit_array[j].appid, visit_array[j].latest_time) + total_time=visit_array[j].latest_time - visit_array[j].first_time; + history[#history+1]={ + mac=user_array[i].mac, + ip=user_array[i].ip, + hostname=get_hostname_by_mac(user_array[i].mac), + appid=visit_array[j].appid, + appname=get_app_name_by_id(visit_array[j].appid), + total_num=0, + drop_num=0, + latest_action=visit_array[j].latest_action, + latest_time=os.date("%Y/%m/%d %H:%M:%S", visit_array[j].latest_time), + first_time=os.date("%Y/%m/%d %H:%M:%S", visit_array[j].first_time), + total_time=total_time + } + end + end + table.sort(history, cmp_func) + luci.http.write_json(history); +end + + +function get_user_list() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local visit_obj=utl.ubus("appfilter", "dev_list", {}); + luci.http.write_json(visit_obj); +end + +function get_class_list() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local class_obj=utl.ubus("appfilter", "class_list", {}); + llog("get class list"); + luci.http.write_json(class_obj); +end + +function get_all_users() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local flag = luci.http.formvalue("flag") + local page = luci.http.formvalue("page") + local class_obj=utl.ubus("appfilter", "get_all_users", {flag=flag, page=page}); + luci.http.write_json(class_obj); +end + +function get_oaf_status() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local resp_obj=utl.ubus("appfilter", "get_oaf_status", {}); + luci.http.write_json(resp_obj); +end + +function get_app_filter_user() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local resp_obj=utl.ubus("appfilter", "get_app_filter_user", {}); + luci.http.write_json(resp_obj); +end + +function del_app_filter_user() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local req_obj = {} + req_obj.mac = luci.http.formvalue("mac") + llog("del appfilter user "..req_obj.mac); + local resp_obj=utl.ubus("appfilter", "del_app_filter_user", req_obj); + luci.http.write_json(resp_obj); +end + +function add_app_filter_user() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local req_obj = {} + local data_str = luci.http.formvalue("data") + req_obj = json.parse(data_str) + + local resp_obj=utl.ubus("appfilter", "add_app_filter_user", req_obj); + luci.http.write_json(resp_obj); +end + +function get_app_filter() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local resp_obj=utl.ubus("appfilter", "get_app_filter", {}); + luci.http.write_json(resp_obj); +end + +function set_app_filter() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + + local app_list_str = luci.http.formvalue("app_list") + + local app_list = {} + for id in app_list_str:gmatch("([^,]+)") do + table.insert(app_list, tonumber(id)) + end + + local req_obj = { + app_list = app_list + } + + local resp_obj = utl.ubus("appfilter", "set_app_filter", req_obj) + luci.http.write_json(resp_obj) +end + +function set_nickname() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local req_obj = {} + req_obj.mac = luci.http.formvalue("mac") + req_obj.nickname = luci.http.formvalue("nickname") + llog("set nickname "..req_obj.mac.." "..req_obj.nickname); + local resp_obj=utl.ubus("appfilter", "set_nickname", req_obj); + luci.http.write_json(resp_obj); +end + +function get_app_filter_base() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local resp_obj=utl.ubus("appfilter", "get_app_filter_base", {}); + luci.http.write_json(resp_obj); +end + +function set_app_filter_user() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local req_obj = {} + req_obj.mode = luci.http.formvalue("mode") + local resp_obj=utl.ubus("appfilter", "set_app_filter_user", req_obj); + luci.http.write_json(resp_obj); +end + +function set_app_filter_base() + local json = require "luci.jsonc" + llog("set appfilter base"); + luci.http.prepare_content("application/json") + local req_obj = {} + + + local enable = luci.http.formvalue("enable") + local work_mode = luci.http.formvalue("work_mode") + local record_enable = luci.http.formvalue("record_enable") + + llog("enable: "..enable.." work_mode: "..work_mode.." record_enable: "..record_enable) + req_obj.enable = enable + req_obj.work_mode = work_mode + req_obj.record_enable = record_enable + + local resp_obj=utl.ubus("appfilter", "set_app_filter_base", req_obj); + luci.http.write_json(resp_obj); +end + +function get_app_filter_adv() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local resp_obj=utl.ubus("appfilter", "get_app_filter_adv", {}); + luci.http.write_json(resp_obj); +end +function set_app_filter_adv() + local json = require "luci.jsonc" + llog("set appfilter base"); + luci.http.prepare_content("application/json") + local req_obj = {} + req_obj.lan_ifname = luci.http.formvalue("lan_ifname") + req_obj.disable_hnat = luci.http.formvalue("disable_hnat") + req_obj.auto_load_engine = luci.http.formvalue("auto_load_engine") + local resp_obj=utl.ubus("appfilter", "set_app_filter_adv", req_obj); + luci.http.write_json(resp_obj); +end + +-- data: {"mode":1,"weekday_list":[1,2,3,4,5,6,0],"start_time":"22:22","end_time":"12:00","allow_time":30,"deny_time":5} +function set_app_filter_time() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local req_obj = {} + req_obj = json.parse(luci.http.formvalue("data")) + local resp_obj=utl.ubus("appfilter", "set_app_filter_time", req_obj); + luci.http.write_json(resp_obj); +end + +function get_app_filter_time() + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local resp_obj=utl.ubus("appfilter", "get_app_filter_time", {}); + luci.http.write_json(resp_obj); +end + +function get_dev_visit_time(mac) + + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local req_obj = {} + req_obj.mac = mac; + local visit_obj=utl.ubus("appfilter", "dev_visit_time", req_obj); + + local visit_list=visit_obj.list + luci.http.write_json(visit_list); +end + +function get_app_class_visit_time(mac) + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local req_obj = {} + req_obj.mac = mac; + local visit_obj=utl.ubus("appfilter", "app_class_visit_time", req_obj); + local class_array=visit_obj.class_list + luci.http.write_json(class_array); +end + + +function get_dev_visit_list(mac) + local json = require "luci.jsonc" + luci.http.prepare_content("application/json") + local req_obj = {} + req_obj.mac = mac; + local resp_obj=utl.ubus("appfilter", "dev_visit_list", req_obj); + luci.http.write_json(resp_obj); +end + +function handle_file_upload() + local http = require "luci.http" + local fs = require "nixio.fs" + local upload_dir = "/tmp/uploads/" + local file_name = "uploaded_file" + llog("handle_file_upload started"); + + -- Ensure the upload directory exists + if not fs.access(upload_dir) then + fs.mkdir(upload_dir) + end + + llog("Upload directory checked/created"); + + local file_path = upload_dir .. file_name + local fp + + llog("file_path: " .. file_path); + http.setfilehandler( + function(meta, chunk, eof) + -- Log metadata information + llog("File upload metadata: " .. (meta and meta.name or "nil") .. ", " .. (meta and meta.file or "nil")) + llog("File upload chunk size: " .. (chunk and #chunk or 0)) + + if not fp then + fp = io.open(file_path, "w") + llog("File opened for writing: " .. file_path) + end + if fp and chunk then + fp:write(chunk) + llog("Chunk written to file") + end + if fp and eof then + fp:close() + llog("File upload completed and file closed") + -- Ensure the file is processed or moved to the correct location + process_uploaded_file(file_path) + luci.http.prepare_content("application/json") + luci.http.write_json({ success = true, message = "File uploaded successfully" }) + end + end + ) + llog("handle_file_upload setup complete"); +end + +function process_uploaded_file(file_path) + -- Add logic here to process the uploaded file + llog("Processing uploaded file: " .. file_path) + -- Example: Move the file to a permanent location + local permanent_path = "/etc/config/" .. file_name + os.execute("mv " .. file_path .. " " .. permanent_path) + llog("File moved to: " .. permanent_path) +end + +function llog(message) + local log_file = "/tmp/log/oaf_luci.log" -- 日志文件路径 + local fd = io.open(log_file, "a") -- 以追加模式打开文件 + if fd then + local timestamp = os.date("%Y-%m-%d %H:%M:%S") -- 获取当前时间戳 + fd:write(string.format("[%s] %s\n", timestamp, message)) -- 写入时间戳和日志信息 + fd:close() -- 关闭文件 + end +end diff --git a/luci-app-oaf/luasrc/model/cbi/appfilter/advance.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/advance.lua new file mode 100755 index 0000000..966950b --- /dev/null +++ b/luci-app-oaf/luasrc/model/cbi/appfilter/advance.lua @@ -0,0 +1,19 @@ +local ds = require "luci.dispatcher" +local nxo = require "nixio" +local nfs = require "nixio.fs" +local ipc = require "luci.ip" +local sys = require "luci.sys" +local utl = require "luci.util" +local dsp = require "luci.dispatcher" +local uci = require "luci.model.uci" +local lng = require "luci.i18n" +local jsc = require "luci.jsonc" + +local m, s +arg[1] = arg[1] or "" +m = Map("appfilter", translate(""), translate("")) + +local v +v = m:section(SimpleSection) +v.template = "admin_network/advance" +return m diff --git a/luci-app-oaf/luasrc/model/cbi/appfilter/app_filter.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/app_filter.lua new file mode 100755 index 0000000..1961a48 --- /dev/null +++ b/luci-app-oaf/luasrc/model/cbi/appfilter/app_filter.lua @@ -0,0 +1,20 @@ +local ds = require "luci.dispatcher" +local nxo = require "nixio" +local nfs = require "nixio.fs" +local ipc = require "luci.ip" +local sys = require "luci.sys" +local utl = require "luci.util" +local dsp = require "luci.dispatcher" +local uci = require "luci.model.uci" +local lng = require "luci.i18n" +local jsc = require "luci.jsonc" + +local m, s +arg[1] = arg[1] or "" +m = Map("appfilter", translate(""), translate("")) + + +local v +v = m:section(SimpleSection) +v.template = "admin_network/app_filter" +return m diff --git a/luci-app-oaf/luasrc/model/cbi/appfilter/base_setting.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/base_setting.lua new file mode 100755 index 0000000..f79c9e1 --- /dev/null +++ b/luci-app-oaf/luasrc/model/cbi/appfilter/base_setting.lua @@ -0,0 +1,117 @@ +local ds = require "luci.dispatcher" +local nxo = require "nixio" +local nfs = require "nixio.fs" +local ipc = require "luci.ip" +local sys = require "luci.sys" +local utl = require "luci.util" +local dsp = require "luci.dispatcher" +local uci = require "luci.model.uci" +local lng = require "luci.i18n" +local jsc = require "luci.jsonc" +local http = luci.http +local SYS = require "luci.sys" +local m, s + +m = Map("appfilter", translate("App Filter"), translate( + "Please close the modules that may conflict, such as acceleration, ad filtering, and multi-dial")) + +s = m:section(TypedSection, "global", translate("Basic Settings")) +s:option(Flag, "enable", translate("Enable App Filter"), translate("")) +s.anonymous = true + +o=s:option(ListValue, "work_mode", translate("Work Mode"),translate("")) +o.default=0 +o:value(0, translate("Gateway Mode")) +o:value(1,translate("Bypass Mode")) + +local rule_count = 0 +local version = "" + + +-- s = m:section(TypedSection, "appfilter", translate("App Filter Rules"), +-- translate("If there is no app you want, you can add the app by updating the app feature file")) +-- s.anonymous = true +-- s.addremove = false +-- function get_class_i18n_name(class_name) +-- local fd = io.open("/tmp/app_class.txt", "r") +-- if not fd then return end +-- while true do +-- local ln = fd:read("*l") +-- if not ln then +-- break +-- end +-- local id, name1, name2 = ln:match("^(%d+) (%S+) (%S+)") +-- if class_name == name1 then +-- fd:close() +-- return name2 +-- end +-- end +-- fd:close() +-- return nil +-- end +-- local class_fd = io.popen("find /tmp/appfilter/ -type f -name '*.class'") +-- if class_fd then +-- while true do +-- local apps +-- local class +-- local i18n_name +-- local path = class_fd:read("*l") +-- if not path then +-- break +-- end + +-- class = path:match("([^/]+)%.class$") +-- i18n_name=get_class_i18n_name(class) +-- if nil ~= i18n_name then +-- s:tab(class, i18n_name) +-- else +-- s:tab(class, class) +-- end +-- apps = s:taboption(class, MultiValue, class .. "apps", translate("")) +-- apps.rmempty = true +-- apps.widget = "checkbox" +-- apps.size = 10 + +-- local fd = io.open(path) +-- if fd then +-- local line +-- while true do +-- local cmd +-- local cmd_fd +-- line = fd:read("*l") +-- if not line then +-- break +-- end +-- if string.len(line) < 5 then +-- break +-- end +-- if not string.find(line, "#") then +-- cmd = "echo " .. line .. "|awk '{print $1}'" +-- cmd_fd = io.popen(cmd) +-- id = cmd_fd:read("*l"); +-- cmd_fd:close() + +-- cmd = "echo " .. line .. "|awk '{print $2}'" +-- cmd_fd = io.popen(cmd) +-- name = cmd_fd:read("*l") + +-- cmd_fd:close() +-- if not id then +-- break +-- end +-- if not name then +-- break +-- end +-- -- apps:value(id, name) + +-- -- 在选项值中添加图标 +-- apps:value(id, name) +-- end +-- end +-- fd:close() +-- end +-- end +-- class_fd:close() +-- end + +return m diff --git a/luci-app-oaf/luasrc/model/cbi/appfilter/dev_status.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/dev_status.lua new file mode 100755 index 0000000..90d6402 --- /dev/null +++ b/luci-app-oaf/luasrc/model/cbi/appfilter/dev_status.lua @@ -0,0 +1,21 @@ +local ds = require "luci.dispatcher" +local nxo = require "nixio" +local nfs = require "nixio.fs" +local ipc = require "luci.ip" +local sys = require "luci.sys" +local utl = require "luci.util" +local dsp = require "luci.dispatcher" +local uci = require "luci.model.uci" +local lng = require "luci.i18n" +local jsc = require "luci.jsonc" + +local m, s +arg[1] = arg[1] or "" +m = Map("appfilter", translate("Data Statistics") .. "(" .. arg[1] .. ")", translate("")) + +local v +v = m:section(SimpleSection) +v.template = "admin_network/dev_status" +v.mac = arg[1] +m.redirect = luci.dispatcher.build_url("admin", "services", "appfilter") +return m diff --git a/luci-app-oaf/luasrc/model/cbi/appfilter/feature.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/feature.lua new file mode 100755 index 0000000..1ab809a --- /dev/null +++ b/luci-app-oaf/luasrc/model/cbi/appfilter/feature.lua @@ -0,0 +1,144 @@ +local ds = require "luci.dispatcher" +local nxo = require "nixio" +local nfs = require "nixio.fs" +local ipc = require "luci.ip" +local sys = require "luci.sys" +local utl = require "luci.util" +local dsp = require "luci.dispatcher" +local uci = require "luci.model.uci" +local lng = require "luci.i18n" +local jsc = require "luci.jsonc" +local http = luci.http +local SYS = require "luci.sys" +local m, s + +local function llog(message) + local log_file = "/tmp/log/oaf_luci.log" + local fd = io.open(log_file, "a") + if fd then + local timestamp = os.date("%Y-%m-%d %H:%M:%S") + fd:write(string.format("[%s] %s\n", timestamp, message)) + fd:close() + end +end + +m = Map("appfilter", translate(""), + translate("The feature library is used to describe app features, app filtering effect and number-dependent feature library")) + +local rule_count = 0 +local version = "" +local format = "" +if nixio.fs.access("/tmp/feature.cfg") then + rule_count = tonumber(SYS.exec("cat /tmp/feature.cfg | grep -v ^$ |grep -v ^# | wc -l")) + version = SYS.exec("cat /tmp/feature.cfg |grep \"#version\" | awk '{print $2}'") +end +-- format=SYS.exec("uci get appfilter.feature.format") +-- if format == "" then +format="v3.0" +-- end + +local display_str = "" .. + "
" .. + ""..translate("Current version")..": " .. version .. + "
" .. + "
" .. + ""..translate("Feature format")..": " ..format .. + "
" .. + "
" .. + ""..translate("App number")..": " ..rule_count .. + "
" .. + "
" .. + ""..translate("Feature download")..": www.openappfilter.com" .. + "
" +s = m:section(TypedSection, "feature", translate("App Feature"), display_str) + +fu = s:option(FileUpload, "") +fu.template = "cbi/oaf_upload" +s.anonymous = true + +um = s:option(DummyValue, "rule_data") +um.template = "cbi/oaf_dvalue" + +local dir, fd +dir = "/tmp/upload/" +nixio.fs.mkdir(dir) +http.setfilehandler(function(meta, chunk, eof) + local feature_file = "/etc/appfilter/feature.cfg" + local f_format="v3.0" + if not fd then + if not meta then + return + end + if meta and chunk then + fd = nixio.open(dir .. meta.file, "w") + end + if not fd then + return + end + end + if chunk and fd then + fd:write(chunk) + end + if eof and fd then + fd:close() + -- Extract the tar.gz file + local tar_cmd = "tar -zxvf /tmp/upload/" .. meta.file .. " -C /tmp/upload/ >/dev/null" + local success = os.execute(tar_cmd) + if success ~= 0 then + um.value = translate("Failed to update feature file, format error") + return + else + um.value = translate("Update the feature file successfully, please refresh the page") + end + + local feature_dir="/tmp/upload/feature" + local fd2 = io.open("/tmp/upload/feature.cfg") + if not fd2 then + um.value = translate("Failed to extract feature file, file not found") + os.execute("rm /tmp/upload/* -fr") + return + end + local version_line = fd2:read("*l") + local format_line = fd2:read("*l") + fd2:close() + local ret = string.match(version_line, "#version") + if ret ~= nil then + if string.match(format_line, "#format") then + f_format = SYS.exec("echo '"..format_line.."'|awk '{print $2}'") + end + if not string.match(f_format, format) then + um.value = translate("Failed to update feature file, format error"..",feature format:"..f_format) + os.execute("rm /tmp/upload/* -fr") + return + end + local cmd = "cp /tmp/upload/feature.cfg " .. feature_file + os.execute(cmd) + os.execute("rm /www/luci-static/resources/app_icons/* -fr"); + cmd = "cp /tmp/upload/app_icons/* /www/luci-static/resources/app_icons/ -fr >/dev/null" + os.execute(cmd) + os.execute("chmod 666 " .. feature_file) + luci.sys.exec("killall -SIGUSR1 oafd") + um.value = translate("Update the feature file successfully, please refresh the page") + else + um.value = translate("Failed to update feature file, format error") + end + os.execute("rm /tmp/upload/* -fr") + end + +end) + +if luci.http.formvalue("upload") then + local f = luci.http.formvalue("ulfile") + if #f <= 0 then + -- um.value = translate("No specify upload file.") + end +elseif luci.http.formvalue("download") then + Download() +end + +return m + + diff --git a/luci-app-oaf/luasrc/model/cbi/appfilter/overview.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/overview.lua new file mode 100755 index 0000000..f79c9e1 --- /dev/null +++ b/luci-app-oaf/luasrc/model/cbi/appfilter/overview.lua @@ -0,0 +1,117 @@ +local ds = require "luci.dispatcher" +local nxo = require "nixio" +local nfs = require "nixio.fs" +local ipc = require "luci.ip" +local sys = require "luci.sys" +local utl = require "luci.util" +local dsp = require "luci.dispatcher" +local uci = require "luci.model.uci" +local lng = require "luci.i18n" +local jsc = require "luci.jsonc" +local http = luci.http +local SYS = require "luci.sys" +local m, s + +m = Map("appfilter", translate("App Filter"), translate( + "Please close the modules that may conflict, such as acceleration, ad filtering, and multi-dial")) + +s = m:section(TypedSection, "global", translate("Basic Settings")) +s:option(Flag, "enable", translate("Enable App Filter"), translate("")) +s.anonymous = true + +o=s:option(ListValue, "work_mode", translate("Work Mode"),translate("")) +o.default=0 +o:value(0, translate("Gateway Mode")) +o:value(1,translate("Bypass Mode")) + +local rule_count = 0 +local version = "" + + +-- s = m:section(TypedSection, "appfilter", translate("App Filter Rules"), +-- translate("If there is no app you want, you can add the app by updating the app feature file")) +-- s.anonymous = true +-- s.addremove = false +-- function get_class_i18n_name(class_name) +-- local fd = io.open("/tmp/app_class.txt", "r") +-- if not fd then return end +-- while true do +-- local ln = fd:read("*l") +-- if not ln then +-- break +-- end +-- local id, name1, name2 = ln:match("^(%d+) (%S+) (%S+)") +-- if class_name == name1 then +-- fd:close() +-- return name2 +-- end +-- end +-- fd:close() +-- return nil +-- end +-- local class_fd = io.popen("find /tmp/appfilter/ -type f -name '*.class'") +-- if class_fd then +-- while true do +-- local apps +-- local class +-- local i18n_name +-- local path = class_fd:read("*l") +-- if not path then +-- break +-- end + +-- class = path:match("([^/]+)%.class$") +-- i18n_name=get_class_i18n_name(class) +-- if nil ~= i18n_name then +-- s:tab(class, i18n_name) +-- else +-- s:tab(class, class) +-- end +-- apps = s:taboption(class, MultiValue, class .. "apps", translate("")) +-- apps.rmempty = true +-- apps.widget = "checkbox" +-- apps.size = 10 + +-- local fd = io.open(path) +-- if fd then +-- local line +-- while true do +-- local cmd +-- local cmd_fd +-- line = fd:read("*l") +-- if not line then +-- break +-- end +-- if string.len(line) < 5 then +-- break +-- end +-- if not string.find(line, "#") then +-- cmd = "echo " .. line .. "|awk '{print $1}'" +-- cmd_fd = io.popen(cmd) +-- id = cmd_fd:read("*l"); +-- cmd_fd:close() + +-- cmd = "echo " .. line .. "|awk '{print $2}'" +-- cmd_fd = io.popen(cmd) +-- name = cmd_fd:read("*l") + +-- cmd_fd:close() +-- if not id then +-- break +-- end +-- if not name then +-- break +-- end +-- -- apps:value(id, name) + +-- -- 在选项值中添加图标 +-- apps:value(id, name) +-- end +-- end +-- fd:close() +-- end +-- end +-- class_fd:close() +-- end + +return m diff --git a/luci-app-oaf/luasrc/model/cbi/appfilter/time.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/time.lua new file mode 100755 index 0000000..baa1afc --- /dev/null +++ b/luci-app-oaf/luasrc/model/cbi/appfilter/time.lua @@ -0,0 +1,19 @@ +local ds = require "luci.dispatcher" +local nxo = require "nixio" +local nfs = require "nixio.fs" +local ipc = require "luci.ip" +local sys = require "luci.sys" +local utl = require "luci.util" +local dsp = require "luci.dispatcher" +local uci = require "luci.model.uci" +local lng = require "luci.i18n" +local jsc = require "luci.jsonc" + +local m, s +arg[1] = arg[1] or "" +m = Map("appfilter", translate(""), translate("")) + +local v +v = m:section(SimpleSection) +v.template = "admin_network/time" +return m diff --git a/luci-app-oaf/luasrc/model/cbi/appfilter/time_setting.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/time_setting.lua new file mode 100755 index 0000000..52a8aa9 --- /dev/null +++ b/luci-app-oaf/luasrc/model/cbi/appfilter/time_setting.lua @@ -0,0 +1,49 @@ +local ds = require "luci.dispatcher" +local nxo = require "nixio" +local nfs = require "nixio.fs" +local ipc = require "luci.ip" +local sys = require "luci.sys" +local utl = require "luci.util" +local dsp = require "luci.dispatcher" +local uci = require "luci.model.uci" +local lng = require "luci.i18n" +local jsc = require "luci.jsonc" +local http = luci.http +local SYS = require "luci.sys" +local m, s + +m = Map("appfilter", translate(""), translate("")) + +s = m:section(TypedSection, "time", translate("Time Setting"),translate("The second time is optional, the end time must be greater than the start time")) +s.anonymous = true + + +o=s:option(ListValue, "time_mode", translate("Time Mode"),translate("")) +o.default=0 +o:value(0,translate("Blacklist mode")) +o:value(1,translate("Whitelist mode")) + +days = s:option(MultiValue, "days", "", translate("")) +days.widget = "checkbox" +days.size = 10 +days:value("0", translate("Sun")); +days:value("1", translate("Mon")); +days:value("2", translate("Tue")); +days:value("3", translate("Wed")); +days:value("4", translate("Thur")); +days:value("5", translate("Fri")); +days:value("6", translate("Sat")); + +hv = s:option(Value, "start_time", translate("Start Time1"),translate("xx:xx")) +hv.optional = false +hv = s:option(Value, "end_time", translate("End Time1")) +hv.optional = false + +hv = s:option(Value, "start_time2", translate("Start Time2")) +hv.optional = false +hv = s:option(Value, "end_time2", translate("End Time2")) +hv.optional = false + + + +return m diff --git a/luci-app-oaf/luasrc/model/cbi/appfilter/user.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/user.lua new file mode 100755 index 0000000..29c0240 --- /dev/null +++ b/luci-app-oaf/luasrc/model/cbi/appfilter/user.lua @@ -0,0 +1,19 @@ +local ds = require "luci.dispatcher" +local nxo = require "nixio" +local nfs = require "nixio.fs" +local ipc = require "luci.ip" +local sys = require "luci.sys" +local utl = require "luci.util" +local dsp = require "luci.dispatcher" +local uci = require "luci.model.uci" +local lng = require "luci.i18n" +local jsc = require "luci.jsonc" + +local m, s +arg[1] = arg[1] or "" +m = Map("appfilter", translate(""), translate("")) + +local v +v = m:section(SimpleSection) +v.template = "admin_network/user" +return m \ No newline at end of file diff --git a/luci-app-oaf/luasrc/model/cbi/appfilter/user_list.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/user_list.lua new file mode 100755 index 0000000..6a0366b --- /dev/null +++ b/luci-app-oaf/luasrc/model/cbi/appfilter/user_list.lua @@ -0,0 +1,19 @@ +local ds = require "luci.dispatcher" +local nxo = require "nixio" +local nfs = require "nixio.fs" +local ipc = require "luci.ip" +local sys = require "luci.sys" +local utl = require "luci.util" +local dsp = require "luci.dispatcher" +local uci = require "luci.model.uci" +local lng = require "luci.i18n" +local jsc = require "luci.jsonc" +local http = luci.http +local SYS = require "luci.sys" +local m, s + +m = Map("appfilter", translate(""), translate("")) + +m:section(SimpleSection).template = "admin_network/user_status" + +return m diff --git a/luci-app-oaf/luasrc/model/cbi/appfilter/user_setting.lua b/luci-app-oaf/luasrc/model/cbi/appfilter/user_setting.lua new file mode 100755 index 0000000..f8a4aab --- /dev/null +++ b/luci-app-oaf/luasrc/model/cbi/appfilter/user_setting.lua @@ -0,0 +1,91 @@ +local ds = require "luci.dispatcher" +local nxo = require "nixio" +local nfs = require "nixio.fs" +local ipc = require "luci.ip" +local sys = require "luci.sys" +local utl = require "luci.util" +local dsp = require "luci.dispatcher" +local uci = require "luci.model.uci" +local lng = require "luci.i18n" +local jsc = require "luci.jsonc" +local http = luci.http +local SYS = require "luci.sys" +local m, s + +m = Map("appfilter", translate("App Filter"), translate("")) + +function get_hostname_by_mac(dst_mac) + leasefile = "/tmp/dhcp.leases" + local fd = io.open(leasefile, "r") + if not fd then + return + end + while true do + local ln = fd:read("*l") + if not ln then + break + end + local ts, mac, ip, name, duid = ln:match("^(%d+) (%S+) (%S+) (%S+) (%S+)") + print(ln) + if dst_mac == mac then + fd:close() + return name + end + end + fd:close() + return nil +end + +function get_cmd_result(command) + local fd + local result + fd = io.popen(command); + if not fd then + return "" + end + result = fd:read("*l"); + fd:close() + return result +end + +s = m:section(TypedSection, "user", translate("Select users")) +s.anonymous = true +users = s:option(MultiValue, "users", "", translate( + "It takes effect for all users by default, and only takes effect for the selected users when checked")) +users.widget = "checkbox" +-- users.widget="select" +users.size = 1 + +local fd = io.open("/tmp/dev_list", "r") +if not fd then + return m +end +while true do + local line = fd:read("*l") + if not line then + break + end + if not string.match(line, "^Id") then + local ip = get_cmd_result(string.format("echo '%s' | awk '{print $3}'", line)) + local mac = get_cmd_result(string.format("echo '%s' | awk '{print $2}'", line)) + local hostname = get_cmd_result(string.format("echo '%s' | awk '{print $4}'", line)) + if mac ~= nil then + if not hostname or hostname == "*" then + users:value(mac, mac); + else + users:value(mac, hostname .. "(" .. mac .. ")"); + end + end + end +end +fd:close() + +local config_users = m.uci:get_all("appfilter.user.users") +if config_users ~= nil and config_users ~= false then + local r = utl.split(config_users, "%s+", nil, true) + local max = table.getn(r) + for i = 1, max, 1 do + users:value(r[i], r[i]); + end +end +return m diff --git a/luci-app-oaf/luasrc/view/admin_network/advance.htm b/luci-app-oaf/luasrc/view/admin_network/advance.htm new file mode 100755 index 0000000..ba1539e --- /dev/null +++ b/luci-app-oaf/luasrc/view/admin_network/advance.htm @@ -0,0 +1,254 @@ + + + + + + +
+
+ + +
+ + +
+ +
+ 如果采用lede或者闭源驱动固件,会包含软硬件加速模块,这些模块会影响过滤功能,如果测试发现没有效果可以关闭后再测试,在防火墙或者网络加速菜单中可查看关闭状态,开启加速后需要重启设备生效。 +
+ + + + +
+ + +
+ +
+ 如果oaf驱动无法手动卸载或者当前驱动不稳定,可以关闭开机自动加载,然后手动安装合适的驱动。 +
+ + +
+ + +
+ +
+ LAN接口的名称,用于探测终端信息,系统默认都是桥接口(br-lan),如果将lan口修改成了物理接口,请修改为对应的名称,比如eth0。 +
+ + +
+ +
+
+
diff --git a/luci-app-oaf/luasrc/view/admin_network/app_filter.htm b/luci-app-oaf/luasrc/view/admin_network/app_filter.htm new file mode 100755 index 0000000..31e7e10 --- /dev/null +++ b/luci-app-oaf/luasrc/view/admin_network/app_filter.htm @@ -0,0 +1,534 @@ + + + + + + +
+
+ +
+ + 运行中 +
+
+ + + + +
+ +
+ + +
+ + +
+ 应用选择: +
+
+ +
+
+ + +
+ +
+ + + +
+
+
+ +
+
+
diff --git a/luci-app-oaf/luasrc/view/admin_network/dev_status.htm b/luci-app-oaf/luasrc/view/admin_network/dev_status.htm new file mode 100755 index 0000000..b7522c9 --- /dev/null +++ b/luci-app-oaf/luasrc/view/admin_network/dev_status.htm @@ -0,0 +1,393 @@ + + + + + + + + + +
+
+
+
+
+ + + + + + + + + + + + +
+ <%:App Name%> + + <%:Hostname%> + + <%:Mac%> + + <%:Start Time%> + + <%:Visit Time%> + + <%:Filter Status%> +

+ <%:Collecting data...%> +
+ +
\ No newline at end of file diff --git a/luci-app-oaf/luasrc/view/admin_network/time.htm b/luci-app-oaf/luasrc/view/admin_network/time.htm new file mode 100755 index 0000000..70c7835 --- /dev/null +++ b/luci-app-oaf/luasrc/view/admin_network/time.htm @@ -0,0 +1,608 @@ + + + + + + + + +
+ + +
+ + +
+

时间规则

+ +
+ + +
+ + +
+ +
周一
+
周二
+
周三
+
周四
+
周五
+
周六
+
周日
+
+ + +
+ + + + + + + + + + + + +
开始时间结束时间操作
+
+ +
+ + + +
+ +
+ +
+ +
+
\ No newline at end of file diff --git a/luci-app-oaf/luasrc/view/admin_network/user.htm b/luci-app-oaf/luasrc/view/admin_network/user.htm new file mode 100755 index 0000000..67bdbf0 --- /dev/null +++ b/luci-app-oaf/luasrc/view/admin_network/user.htm @@ -0,0 +1,370 @@ + + + + +
+
+
+ 自动模式 + 手动模式 +
+ + +
+ 手动模式下,只有以下添加的终端被管控 +
+ + + + + +
+ +
+ +
+
+ + + + + diff --git a/luci-app-oaf/luasrc/view/admin_network/user_status.htm b/luci-app-oaf/luasrc/view/admin_network/user_status.htm new file mode 100755 index 0000000..ba3c527 --- /dev/null +++ b/luci-app-oaf/luasrc/view/admin_network/user_status.htm @@ -0,0 +1,645 @@ +<% local dsp=require "luci.dispatcher" -%> + + + + + +
+ +
+ +
+ + + + + + + + + + + + +
+ <%:设备信息%> + + <%:Ip%> + + <%:Common App(TOP5)%> + + <%:Online Status%> + + <%:Actions%> +

+ <%:Collecting data...%> +
+ +
+
+
+ + + + + + + + diff --git a/luci-app-oaf/luasrc/view/cbi/oaf_dvalue.htm b/luci-app-oaf/luasrc/view/cbi/oaf_dvalue.htm new file mode 100755 index 0000000..adf6a2b --- /dev/null +++ b/luci-app-oaf/luasrc/view/cbi/oaf_dvalue.htm @@ -0,0 +1,10 @@ +<%+cbi/valueheader%> + +<% + local val = self:cfgvalue(section) or self.default or "" + write(pcdata(val)) +%> + + + +<%+cbi/valuefooter%> diff --git a/luci-app-oaf/luasrc/view/cbi/oaf_upload.htm b/luci-app-oaf/luasrc/view/cbi/oaf_upload.htm new file mode 100755 index 0000000..24238bd --- /dev/null +++ b/luci-app-oaf/luasrc/view/cbi/oaf_upload.htm @@ -0,0 +1,32 @@ +<%+cbi/valueheader%> + +
+ + + +
+ + +
+ 特征库文件可以通过官网下载,下载后上传升级,注意特征码格式版本,需要和当前特征码格式一致! +
+<%+cbi/valuefooter%> + + + + diff --git a/luci-app-oaf/po/zh-cn/oaf.po b/luci-app-oaf/po/zh-cn/oaf.po new file mode 100755 index 0000000..5a7e221 --- /dev/null +++ b/luci-app-oaf/po/zh-cn/oaf.po @@ -0,0 +1,229 @@ + +msgid "website" +msgstr "常用网站" + +msgid "appfilter" +msgstr "应用过滤" + +msgid "App Filter" +msgstr "应用过滤" + +msgid "game" +msgstr "游戏" + +msgid "web" +msgstr "网页" + +msgid "video" +msgstr "视频" + +msgid "chat" +msgstr "聊天" + +msgid "download" +msgstr "下载" + +msgid "p2p" +msgstr "p2p" + +msgid "music" +msgstr "音乐" + +msgid "shopping" +msgstr "购物" + +msgid "working" +msgstr "办公" + +msgid "employee" +msgstr "招聘" + +msgid "Basic Settings" +msgstr "基本设置" + +msgid "App Filter Rules" +msgstr "应用过滤规则" + +msgid "It takes effect for all users by default, and only takes effect for the selected users when checked" +msgstr "默认对所有用户生效,勾选后只对选择的用户生效" + +msgid "Select users" +msgstr "选择用户" + +msgid "Id" +msgstr "编号" + +msgid "Hostname" +msgstr "主机名" + +msgid "Common App(TOP5)" +msgstr "常用APP(TOP5)" + +msgid "Online Status" +msgstr "在线状态" + +msgid "Client List" +msgstr "终端列表" + +msgid "Online" +msgstr "在线" + +msgid "Offline" +msgstr "离线" + +msgid "App Time Statistics" +msgstr "App 时间统计" + +msgid "Filter Status" +msgstr "过滤状态" + +msgid "Data Statistics" +msgstr "数据统计" + + + +msgid "Current Version" +msgstr "当前版本" + +msgid "App Feature Num" +msgstr "特征码个数" + +msgid "Update feature" +msgstr "特征库更新" + +msgid "Time Setting" +msgstr "时间控制" + +msgid "Sun" +msgstr "周日" + +msgid "Mon" +msgstr "周一" + +msgid "Tue" +msgstr "周二" + +msgid "Wed" +msgstr "周三" + +msgid "Thur" +msgstr "周四" + +msgid "Fri" +msgstr "周五" + +msgid "Sat" +msgstr "周六" + +msgid "Update the feature file successfully, please refresh the page" +msgstr "更新特征库成功,请刷新页面!" + +msgid "Failed to update feature file, format error" +msgstr "更新特征库失败,格式错误!" + +msgid "Select feature file:" +msgstr "选择本地特征库文件:" + +msgid "Start Time1" +msgstr "开始时间1" + +msgid "End Time1" +msgstr "结束时间1" + + +msgid "Start Time2" +msgstr "开始时间2" + +msgid "End Time2" +msgstr "结束时间2" + +msgid "App Name" +msgstr "App名称" + +msgid "Visit Time" +msgstr "访问时间" + +msgid "App classification time statistics" +msgstr "App分类时间统计" + +msgid "Percentage" +msgstr "占比" + +msgid "Filtered" +msgstr "已过滤" + +msgid "Unfiltered" +msgstr "未过滤" + +msgid "h" +msgstr "小时" + +msgid "m" +msgstr "分" + +msgid "Enable App Filter" +msgstr "开启应用过滤" + +msgid "App Feature" +msgstr "应用特征库" + +msgid "Effective User" +msgstr "生效用户" + +msgid "Effective Time" +msgstr "生效时间" + +msgid "Basic Settings" +msgstr "基本设置" + +msgid "Please close the modules that may conflict, such as acceleration, ad filtering, and multi-dial" +msgstr "请先关闭加速、广告过滤、多拨等可能冲突的模块" + + +msgid "Work Mode" +msgstr "工作模式" + +msgid "Gateway Mode" +msgstr "网关模式" + +msgid "Bypass Mode" +msgstr "旁路模式" + +msgid "Bypass Mode" +msgstr "旁路模式" + +msgid "Current version" +msgstr "当前版本" + +msgid "Current version" +msgstr "当前版本" + +msgid "App number" +msgstr "App个数" + +msgid "Feature download" +msgstr "特征库下载" + +msgid "The second time is optional, the end time must be greater than the start time" +msgstr "时间2为选填,结束时间要大于开始时间" + +msgid "Time Mode" +msgstr "时间匹配模式" + +msgid "Blacklist mode" +msgstr "黑名单模式" + +msgid "Whitelist mode" +msgstr "白名单模式" + +msgid "The feature library is used to describe app features, app filtering effect and number-dependent feature library" +msgstr "特征库用于描述app特征,app过滤效果和个数依赖特征库" + +msgid "User List" +msgstr "用户列表" + +msgid "If there is no app you want, you can add the app by updating the app feature file" +msgstr "如果没有你想要的APP,可以通过升级特征库增加APP" + +msgid "Feature format" +msgstr "特征码格式" \ No newline at end of file diff --git a/luci-app-oaf/po/zh_Hans/oaf.po b/luci-app-oaf/po/zh_Hans/oaf.po new file mode 100755 index 0000000..8e82be2 --- /dev/null +++ b/luci-app-oaf/po/zh_Hans/oaf.po @@ -0,0 +1,223 @@ + +msgid "website" +msgstr "常用网站" + +msgid "appfilter" +msgstr "应用过滤" + +msgid "App Filter" +msgstr "应用过滤" + +msgid "game" +msgstr "游戏" + +msgid "web" +msgstr "网页" + +msgid "video" +msgstr "视频" + +msgid "chat" +msgstr "聊天" + +msgid "download" +msgstr "下载" + +msgid "p2p" +msgstr "p2p" + +msgid "music" +msgstr "音乐" + +msgid "shopping" +msgstr "购物" + +msgid "working" +msgstr "办公" + +msgid "employee" +msgstr "招聘" + +msgid "Basic Settings" +msgstr "基本设置" + +msgid "App Filter Rules" +msgstr "应用过滤规则" + +msgid "It takes effect for all users by default, and only takes effect for the selected users when checked" +msgstr "默认对所有用户生效,勾选后只对选择的用户生效" + +msgid "Select users" +msgstr "选择用户" + +msgid "Id" +msgstr "编号" + +msgid "Hostname" +msgstr "主机名" + +msgid "Common App(TOP5)" +msgstr "常用APP(TOP5)" + +msgid "Online Status" +msgstr "在线状态" + +msgid "Client List" +msgstr "终端列表" + +msgid "Online" +msgstr "在线" + +msgid "Offline" +msgstr "离线" + +msgid "App Time Statistics" +msgstr "App 时间统计" + +msgid "Filter Status" +msgstr "过滤状态" + +msgid "Data Statistics" +msgstr "数据统计" + + + +msgid "Current Version" +msgstr "当前版本" + +msgid "App Feature Num" +msgstr "特征码个数" + +msgid "Update feature" +msgstr "特征库更新" + +msgid "Time Setting" +msgstr "时间控制" + +msgid "Sun" +msgstr "周日" + +msgid "Mon" +msgstr "周一" + +msgid "Tue" +msgstr "周二" + +msgid "Wed" +msgstr "周三" + +msgid "Thur" +msgstr "周四" + +msgid "Fri" +msgstr "周五" + +msgid "Sat" +msgstr "周六" + +msgid "Update the feature file successfully, please refresh the page" +msgstr "更新特征库成功,请刷新页面!" + +msgid "Failed to update feature file, format error" +msgstr "更新特征库失败,格式错误!" + +msgid "Select feature file:" +msgstr "选择本地特征库文件:" + +msgid "Start Time1" +msgstr "开始时间1" + +msgid "End Time1" +msgstr "结束时间1" + + +msgid "Start Time2" +msgstr "开始时间2" + +msgid "End Time2" +msgstr "结束时间2" + +msgid "App Name" +msgstr "App名称" + +msgid "Visit Time" +msgstr "访问时间" + +msgid "App classification time statistics" +msgstr "App分类时间统计" + +msgid "Percentage" +msgstr "占比" + +msgid "Filtered" +msgstr "已过滤" + +msgid "Unfiltered" +msgstr "未过滤" + +msgid "h" +msgstr "小时" + +msgid "m" +msgstr "分" + +msgid "Enable App Filter" +msgstr "开启应用过滤" + +msgid "App Feature" +msgstr "应用特征库" + +msgid "Effective User" +msgstr "生效用户" + +msgid "Effective Time" +msgstr "生效时间" + +msgid "Basic Settings" +msgstr "基本设置" + +msgid "Please close the modules that may conflict, such as acceleration, ad filtering, and multi-dial" +msgstr "请先关闭加速、广告过滤、多拨等可能冲突的模块" + + +msgid "Work Mode" +msgstr "工作模式" + +msgid "Gateway Mode" +msgstr "网关模式" + +msgid "Bypass Mode" +msgstr "旁路模式" + +msgid "Current version" +msgstr "当前版本" + +msgid "App number" +msgstr "App个数" + +msgid "Feature download" +msgstr "特征库下载" + +msgid "The second time is optional, the end time must be greater than the start time" +msgstr "时间2为选填,结束时间要大于开始时间" + +msgid "Time Mode" +msgstr "时间匹配模式" + +msgid "Blacklist mode" +msgstr "黑名单模式" + +msgid "Whitelist mode" +msgstr "白名单模式" + +msgid "The feature library is used to describe app features, app filtering effect and number-dependent feature library" +msgstr "特征库用于描述app特征,app过滤效果和个数依赖特征库" + +msgid "User List" +msgstr "用户列表" + +msgid "If there is no app you want, you can add the app by updating the app feature file" +msgstr "如果没有你想要的APP,可以通过升级特征库增加APP" + +msgid "Feature format" +msgstr "特征码格式" diff --git a/luci-app-oaf/root/etc/uci-defaults/94_feature_3.0 b/luci-app-oaf/root/etc/uci-defaults/94_feature_3.0 new file mode 100755 index 0000000..1394551 --- /dev/null +++ b/luci-app-oaf/root/etc/uci-defaults/94_feature_3.0 @@ -0,0 +1,11 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + set appfilter.feature.format='v3.0' + set appfilter.rule='rule' + set appfilter.global.disable_hnat='1' + set appfilter.global.tcp_rst='1' + set appfilter.global.lan_ifname='br-lan' + set appfilter.global.auto_load_engine='1' + commit appfilter +EOF \ No newline at end of file diff --git a/oaf/Makefile b/oaf/Makefile new file mode 100755 index 0000000..fe63c56 --- /dev/null +++ b/oaf/Makefile @@ -0,0 +1,43 @@ + +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:=oaf +include $(INCLUDE_DIR)/package.mk + +PKG_AUTOLOAD:=oaf +RSTRIP:=: + +define KernelPackage/oaf + SECTION:=TT Apps + CATEGORY:=TT Apps + TITLE:=open app filter kernel module + FILES:=$(PKG_BUILD_DIR)/oaf.ko + DEPENDS:=+kmod-ipt-conntrack + KCONFIG:= + # AUTOLOAD:=$(call AutoLoad,0,$(PKG_AUTOLOAD)) +endef + +define KernelPackage/oaf/description + open appfilter kernel module +endef + + +EXTRA_CFLAGS:=-Wno-declaration-after-statement -Wno-strict-prototypes -Wno-unused-variable -Wno-implicit-fallthrough -Wno-missing-braces -Wno-parentheses -Wno-format -Wno-missing-prototypes -Wno-missing-declarations + + + +MAKE_OPTS:= \ + $(KERNEL_MAKE_FLAGS) \ + M="$(PKG_BUILD_DIR)" \ + EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \ + $(EXTRA_KCONFIG) + +define Build/Compile + $(MAKE) -C "$(LINUX_DIR)" \ + $(MAKE_OPTS) \ + modules +endef + +$(eval $(call KernelPackage,oaf)) + diff --git a/oaf/src/Makefile b/oaf/src/Makefile new file mode 100755 index 0000000..eedaf28 --- /dev/null +++ b/oaf/src/Makefile @@ -0,0 +1,2 @@ +oaf-objs := app_filter.o af_utils.o regexp.o cJSON.o app_filter_config.o af_log.o af_client.o af_client_fs.o af_conntrack.o +obj-m += oaf.o diff --git a/oaf/src/af_client.c b/oaf/src/af_client.c new file mode 100755 index 0000000..4c1f2c6 --- /dev/null +++ b/oaf/src/af_client.c @@ -0,0 +1,453 @@ +/* + Author:Derry + Date: 2019/11/12 +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "af_client.h" +#include "af_client_fs.h" +#include "af_log.h" +#include "af_utils.h" +#include "app_filter.h" +#include "cJSON.h" + +DEFINE_RWLOCK(af_client_lock); + +u32 total_client = 0; +struct list_head af_client_list_table[MAX_AF_CLIENT_HASH_SIZE]; + +int af_send_msg_to_user(char *pbuf, uint16_t len); + +static void +nf_client_list_init(void) +{ + int i; + AF_CLIENT_LOCK_W(); + for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++) + { + INIT_LIST_HEAD(&af_client_list_table[i]); + } + AF_CLIENT_UNLOCK_W(); + AF_INFO("client list init......ok\n"); +} + +static void +nf_client_list_clear(void) +{ + int i; + af_client_info_t *p = NULL; + char mac_str[32] = {0}; + + AF_DEBUG("clean list\n"); + AF_CLIENT_LOCK_W(); + for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++) + { + while (!list_empty(&af_client_list_table[i])) + { + p = list_first_entry(&af_client_list_table[i], af_client_info_t, hlist); + memset(mac_str, 0x0, sizeof(mac_str)); + sprintf(mac_str, MAC_FMT, MAC_ARRAY(p->mac)); + AF_DEBUG("clean mac:%s\n", mac_str); + list_del(&(p->hlist)); + kfree(p); + } + } + AF_CLIENT_UNLOCK_W(); +} + +void af_client_list_reset_report_num(void) +{ + int i; + af_client_info_t *node = NULL; + AF_CLIENT_LOCK_W(); + for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++) + { + list_for_each_entry(node, &af_client_list_table[i], hlist) + { + node->report_count = 0; + } + } + AF_CLIENT_UNLOCK_W(); +} + +int get_mac_hash_code(unsigned char *mac) +{ + if (!mac) + return 0; + else + return mac[5] & (MAX_AF_CLIENT_HASH_SIZE - 1); +} + +af_client_info_t *find_af_client(unsigned char *mac) +{ + af_client_info_t *node; + unsigned int index; + + index = get_mac_hash_code(mac); + list_for_each_entry(node, &af_client_list_table[index], hlist) + { + if (0 == memcmp(node->mac, mac, 6)) + { + return node; + } + } + return NULL; +} + +af_client_info_t *find_and_add_af_client(unsigned char *mac) +{ + af_client_info_t *nfc; + nfc = find_af_client(mac); + if (!nfc){ + nfc = nf_client_add(mac); + } + return nfc; +} + + +af_client_info_t *find_af_client_by_ip(unsigned int ip) +{ + af_client_info_t *node; + int i; + + for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++) + { + list_for_each_entry(node, &af_client_list_table[i], hlist) + { + if (node->ip == ip) + { + AF_LMT_DEBUG("match node->ip=%pI4, ip=%pI4\n", &node->ip, &ip); + return node; + } + } + } + return NULL; +} + +af_client_info_t * +nf_client_add(unsigned char *mac) +{ + af_client_info_t *node; + int index = 0; + + node = (af_client_info_t *)kmalloc(sizeof(af_client_info_t), GFP_ATOMIC); + if (node == NULL) + { + AF_ERROR("kmalloc failed\n"); + return NULL; + } + + memset(node, 0, sizeof(af_client_info_t)); + memcpy(node->mac, mac, MAC_ADDR_LEN); + + node->create_jiffies = jiffies; + node->update_jiffies = jiffies; + index = get_mac_hash_code(mac); + + AF_LMT_INFO("new client mac=" MAC_FMT "\n", MAC_ARRAY(node->mac)); + total_client++; + list_add(&(node->hlist), &af_client_list_table[index]); + return node; +} + + + + +void check_client_expire(void) +{ + af_client_info_t *node; + int i; + AF_CLIENT_LOCK_W(); + for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++) + { + list_for_each_entry(node, &af_client_list_table[i], hlist) + { + AF_DEBUG("mac:" MAC_FMT " update:%lu interval:%lu\n", MAC_ARRAY(node->mac), + node->update_jiffies, (jiffies - node->update_jiffies) / HZ); + if (jiffies > (node->update_jiffies + MAX_CLIENT_ACTIVE_TIME * HZ)) + { + AF_INFO("del client:" MAC_FMT "\n", MAC_ARRAY(node->mac)); + list_del(&(node->hlist)); + kfree(node); + AF_CLIENT_UNLOCK_W(); + return; + } + } + } + AF_CLIENT_UNLOCK_W(); +} + +#define MAX_EXPIRED_VISIT_INFO_COUNT 10 +void flush_expired_visit_info(af_client_info_t *node) +{ + int i; + int count = 0; + u_int32_t cur_timep = 0; + int timeout = 0; + cur_timep = af_get_timestamp_sec(); + for (i = 0; i < MAX_RECORD_APP_NUM; i++) + { + if (node->visit_info[i].app_id == 0) + { + return; + } + } + for (i = 0; i < MAX_RECORD_APP_NUM; i++) + { + if (count >= MAX_EXPIRED_VISIT_INFO_COUNT) + break; + + if (node->visit_info[i].total_num > 3) + { + timeout = 180; + } + else + { + timeout = 60; + } + + if (cur_timep - node->visit_info[i].latest_time > timeout) + { + // 3?��o?��??3y???? + memset(&node->visit_info[i], 0x0, sizeof(app_visit_info_t)); + count++; + } + } +} + +int __af_visit_info_report(af_client_info_t *node) +{ + unsigned char mac_str[32] = {0}; + unsigned char ip_str[32] = {0}; + int i; + int count = 0; + char *out = NULL; + cJSON *visit_obj = NULL; + cJSON *visit_info_array = NULL; + cJSON *root_obj = NULL; + + root_obj = cJSON_CreateObject(); + if (!root_obj) + { + AF_ERROR("create json obj failed"); + return 0; + } + sprintf(mac_str, MAC_FMT, MAC_ARRAY(node->mac)); + sprintf(ip_str, "%pI4", &node->ip); + cJSON_AddStringToObject(root_obj, "mac", mac_str); + cJSON_AddStringToObject(root_obj, "ip", ip_str); + cJSON_AddNumberToObject(root_obj, "app_num", node->visit_app_num); + visit_info_array = cJSON_CreateArray(); + for (i = 0; i < MAX_RECORD_APP_NUM; i++) + { + if (node->visit_info[i].app_id == 0) + continue; + count++; + visit_obj = cJSON_CreateObject(); + cJSON_AddNumberToObject(visit_obj, "appid", node->visit_info[i].app_id); + cJSON_AddNumberToObject(visit_obj, "latest_action", node->visit_info[i].latest_action); + cJSON_AddNumberToObject(visit_obj, "up_bytes", node->visit_info[i].total_up_bytes); + cJSON_AddNumberToObject(visit_obj, "down_bytes", node->visit_info[i].total_down_bytes); + memset((char *)&node->visit_info[i], 0x0, sizeof(app_visit_info_t)); + cJSON_AddItemToArray(visit_info_array, visit_obj); + } + + cJSON_AddItemToObject(root_obj, "visit_info", visit_info_array); + out = cJSON_Print(root_obj); + if (!out) + return 0; + cJSON_Minify(out); + if (count > 0 || node->report_count == 0) + { + AF_LMT_INFO("report:%s count=%d\n", out, node->report_count); + node->report_count++; + af_send_msg_to_user(out, strlen(out)); + } + cJSON_Delete(root_obj); + kfree(out); + return 0; +} +void af_visit_info_report(void) +{ + af_client_info_t *node; + int i; + AF_CLIENT_LOCK_W(); + for (i = 0; i < MAX_AF_CLIENT_HASH_SIZE; i++) + { + list_for_each_entry(node, &af_client_list_table[i], hlist) + { + // flush_expired_visit_info(node); + AF_INFO("report %s\n", node->mac); + __af_visit_info_report(node); + } + } + AF_CLIENT_UNLOCK_W(); +} +static inline int get_packet_dir(struct net_device *in) +{ + if (strstr(in->name, g_lan_ifname)) + { + return PKT_DIR_UP; + } + else + { + return PKT_DIR_DOWN; + } +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) +static u_int32_t af_client_hook(void *priv, + struct sk_buff *skb, + const struct nf_hook_state *state) +{ +#else +static u_int32_t af_client_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ +#endif + struct ethhdr *ethhdr = NULL; + unsigned char smac[ETH_ALEN]; + af_client_info_t *nfc = NULL; + int pkt_dir = 0; + struct iphdr *iph = NULL; + unsigned int ip = 0; + +// 4.10-->4.11 nfct-->_nfct +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) + struct nf_conn *ct = (struct nf_conn *)skb->_nfct; +#else + struct nf_conn *ct = (struct nf_conn *)skb->nfct; +#endif + if (ct == NULL) + { + return NF_ACCEPT; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) + if (!skb->dev) + return NF_ACCEPT; + + pkt_dir = get_packet_dir(skb->dev); +#else + if (!in) + { + AF_ERROR("in is NULL\n"); + return NF_ACCEPT; + } + pkt_dir = get_packet_dir(in); +#endif + + if (PKT_DIR_UP != pkt_dir) + return NF_ACCEPT; + + ethhdr = eth_hdr(skb); + if (ethhdr) + { + memcpy(smac, ethhdr->h_source, ETH_ALEN); + } + else + { + memcpy(smac, &skb->cb[40], ETH_ALEN); + } + + if (skb->protocol == htons(ETH_P_IP)) { + iph = ip_hdr(skb); + ip = iph->saddr; + } else if (AF_MODE_GATEWAY != af_work_mode) + return NF_ACCEPT; + + AF_CLIENT_LOCK_W(); + nfc = find_af_client(smac); + if (!nfc) + { + if (skb->dev) + AF_DEBUG("from dev:%s %pI4", skb->dev->name, &ip); + nfc = nf_client_add(smac); + } + if (nfc && ip != 0 && nfc->ip != ip) + { + AF_DEBUG("update node " MAC_FMT " ip %pI4--->%pI4\n", MAC_ARRAY(nfc->mac), &nfc->ip, &ip); + nfc->update_jiffies = jiffies; + nfc->ip = ip; + } + AF_CLIENT_UNLOCK_W(); + + return NF_ACCEPT; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +static struct nf_hook_ops af_client_ops[] = { + { + .hook = af_client_hook, + .pf = NFPROTO_INET, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP_PRI_FIRST + 1, + }, +}; +#else +static struct nf_hook_ops af_client_ops[] = { + { + .hook = af_client_hook, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + .owner = THIS_MODULE, +#endif + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP_PRI_FIRST + 1, + }, + { + .hook = af_client_hook, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + .owner = THIS_MODULE, +#endif + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP_PRI_FIRST + 1, + }, +}; +#endif + +int af_client_init(void) +{ + int err; + nf_client_list_init(); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) + err = nf_register_net_hooks(&init_net, af_client_ops, ARRAY_SIZE(af_client_ops)); +#else + err = nf_register_hooks(af_client_ops, ARRAY_SIZE(af_client_ops)); +#endif + if (err) { + AF_ERROR("oaf register client hooks failed!\n"); + } + AF_INFO("init app afclient ........ok\n"); + + return 0; +} + +void af_client_exit(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) + nf_unregister_net_hooks(&init_net, af_client_ops, ARRAY_SIZE(af_client_ops)); +#else + nf_unregister_hooks(af_client_ops, ARRAY_SIZE(af_client_ops)); +#endif + nf_client_list_clear(); + return; +} diff --git a/oaf/src/af_client.h b/oaf/src/af_client.h new file mode 100755 index 0000000..ed83565 --- /dev/null +++ b/oaf/src/af_client.h @@ -0,0 +1,73 @@ +#ifndef __AF_CLIENT_H__ +#define __AF_CLIENT_H__ +#include "app_filter.h" + +extern rwlock_t af_client_lock; + +extern u32 nfc_debug_level; + +#define MAX_AF_CLIENT_HASH_SIZE 64 +#define NF_CLIENT_TIMER_EXPIRE 1 +#define MAX_CLIENT_ACTIVE_TIME 90 + +#define AF_CLIENT_LOCK_R() read_lock_bh(&af_client_lock); +#define AF_CLIENT_UNLOCK_R() read_unlock_bh(&af_client_lock); +#define AF_CLIENT_LOCK_W() write_lock_bh(&af_client_lock); +#define AF_CLIENT_UNLOCK_W() write_unlock_bh(&af_client_lock); + +#define NIPQUAD(addr) \ + ((unsigned char *)&addr)[0], \ + ((unsigned char *)&addr)[1], \ + ((unsigned char *)&addr)[2], \ + ((unsigned char *)&addr)[3] +#define NIPQUAD_FMT "%u.%u.%u.%u" + +enum NFC_PKT_DIR +{ + PKT_DIR_DOWN, + PKT_DIR_UP +}; + +#define MAX_VISIT_HISTORY_TIME 24 +#define MAX_RECORD_APP_NUM 64 + +typedef struct app_visit_info +{ + unsigned int app_id; + unsigned int total_num; + unsigned int drop_num; + unsigned long latest_time; + unsigned int latest_action; + unsigned int total_down_bytes; + unsigned int total_up_bytes; + unsigned long history_time[MAX_VISIT_HISTORY_TIME]; + unsigned int action[MAX_VISIT_HISTORY_TIME]; +} app_visit_info_t; + +typedef struct af_client_info +{ + struct list_head hlist; + unsigned char mac[MAC_ADDR_LEN]; + unsigned int ip; + unsigned long create_jiffies; + unsigned long update_jiffies; + unsigned int visit_app_num; + int report_count; + app_visit_info_t visit_info[MAX_RECORD_APP_NUM]; +} af_client_info_t; + +int af_client_init(void); + +void af_client_exit(void); +af_client_info_t *find_af_client_by_ip(unsigned int ip); +af_client_info_t *find_af_client(unsigned char *mac); + +void check_client_expire(void); + +void af_visit_info_report(void); + +void af_client_list_reset_report_num(void); +af_client_info_t *nf_client_add(unsigned char *mac); +af_client_info_t *find_and_add_af_client(unsigned char *mac); + +#endif diff --git a/oaf/src/af_client_fs.c b/oaf/src/af_client_fs.c new file mode 100755 index 0000000..6037988 --- /dev/null +++ b/oaf/src/af_client_fs.c @@ -0,0 +1,194 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cJSON.h" +#include "af_log.h" +#include "af_client.h" + +extern struct list_head af_client_list_table[MAX_AF_CLIENT_HASH_SIZE]; +struct af_client_iter_state +{ + unsigned int bucket; + void *head; +}; + +static void *af_client_get_first(struct seq_file *seq) +{ + struct af_client_iter_state *st = seq->private; + for (st->bucket = 0; st->bucket < MAX_AF_CLIENT_HASH_SIZE; st->bucket++) + { + if (!list_empty(&(af_client_list_table[st->bucket]))) + { + st->head = &(af_client_list_table[st->bucket]); + return af_client_list_table[st->bucket].next; + } + } + return NULL; +} + +static void *af_client_get_next(struct seq_file *seq, + void *head) +{ + struct af_client_iter_state *st = seq->private; + struct hlist_node *node = (struct hlist_node *)head; + + node = node->next; + if (node != st->head) + { + return node; + } + else + { + st->bucket++; + for (; st->bucket < MAX_AF_CLIENT_HASH_SIZE; st->bucket++) + { + if (!list_empty(&(af_client_list_table[st->bucket]))) + { + st->head = &(af_client_list_table[st->bucket]); + return af_client_list_table[st->bucket].next; + } + } + return NULL; + } +} + +static void *af_client_get_idx(struct seq_file *seq, loff_t pos) +{ + void *head = af_client_get_first(seq); + + if (head) + while (pos && (head = af_client_get_next(seq, head))) + pos--; + + return pos ? NULL : head; +} + +static void *af_client_seq_start(struct seq_file *s, loff_t *pos) +{ + AF_CLIENT_LOCK_R(); + if (*pos == 0) + { + return SEQ_START_TOKEN; + } + + return af_client_get_idx(s, *pos - 1); +} + +static void *af_client_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + (*pos)++; + if (v == SEQ_START_TOKEN) + return af_client_get_idx(s, 0); + + return af_client_get_next(s, v); +} + +static void af_client_seq_stop(struct seq_file *s, void *v) +{ + AF_CLIENT_UNLOCK_R(); +} + +static int af_client_seq_show(struct seq_file *s, void *v) +{ + unsigned char mac_str[32] = {0}; + unsigned char ip_str[32] = {0}; + static int index = 0; + af_client_info_t *node = (af_client_info_t *)v; + if (v == SEQ_START_TOKEN) + { + index = 0; + seq_printf(s, "%-4s %-20s %-20s\n", "Id", "Mac", "Ip"); + return 0; + } + index++; + sprintf(mac_str, MAC_FMT, MAC_ARRAY(node->mac)); + sprintf(ip_str, "%pI4", &node->ip); + seq_printf(s, "%-4d %-20s %-20s\n", index, mac_str, ip_str); + return 0; +} + +static const struct seq_operations nf_client_seq_ops = { + .start = af_client_seq_start, + .next = af_client_seq_next, + .stop = af_client_seq_stop, + .show = af_client_seq_show}; + +static int af_client_open(struct inode *inode, struct file *file) +{ + struct seq_file *seq; + struct af_client_iter_state *iter; + int err; + + iter = kzalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; + + err = seq_open(file, &nf_client_seq_ops); + if (err) + { + kfree(iter); + return err; + } + + seq = file->private_data; + seq->private = iter; + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 5, 0) +static const struct file_operations af_client_fops = { + .owner = THIS_MODULE, + .open = af_client_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; +#else +static const struct proc_ops af_client_fops = { + .proc_flags = PROC_ENTRY_PERMANENT, + .proc_read = seq_read, + .proc_open = af_client_open, + .proc_lseek = seq_lseek, + .proc_release = seq_release_private, +}; +#endif + +#define AF_CLIENT_PROC_STR "af_client" + +int init_af_client_procfs(void) +{ + struct proc_dir_entry *pde; + struct net *net = &init_net; + pde = proc_create(AF_CLIENT_PROC_STR, 0440, net->proc_net, &af_client_fops); + + if (!pde) + { + AF_ERROR("nf_client proc file created error\n"); + return -1; + } + return 0; +} + +void finit_af_client_procfs(void) +{ + struct net *net = &init_net; + remove_proc_entry(AF_CLIENT_PROC_STR, net->proc_net); +} diff --git a/oaf/src/af_client_fs.h b/oaf/src/af_client_fs.h new file mode 100755 index 0000000..0140c27 --- /dev/null +++ b/oaf/src/af_client_fs.h @@ -0,0 +1,7 @@ +#ifndef __AF_CLIENT_FS_H__ +#define __AF_CLIENT_FS_H__ + +int init_af_client_procfs(void); +void finit_af_client_procfs(void); + +#endif diff --git a/oaf/src/af_conntrack.c b/oaf/src/af_conntrack.c new file mode 100644 index 0000000..44bf045 --- /dev/null +++ b/oaf/src/af_conntrack.c @@ -0,0 +1,302 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "af_conntrack.h" +#include "af_log.h" + +struct hlist_head af_conn_table[AF_CONN_HASH_SIZE]; + +DEFINE_SPINLOCK(af_conn_lock); + +static u32 af_conn_hash(u32 src_ip, u32 dst_ip, + u16 src_port, u16 dst_port, + u8 protocol) +{ + return jhash_3words(src_ip, dst_ip, + ((u32)protocol << 16) | src_port, + dst_port) % AF_CONN_HASH_SIZE; +} + + +void af_conn_cleanup(void) +{ + int i; + spin_lock(&af_conn_lock); + af_conn_t *p = NULL; + struct hlist_node *n; + + for (i = 0; i < AF_CONN_HASH_SIZE; i++) + { + hlist_for_each_entry_safe(p, n, &af_conn_table[i], node) + { + hlist_del(&p->node); + kfree(p); + } + } + spin_unlock(&af_conn_lock); +} + +af_conn_t *af_conn_add(u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u8 protocol) +{ + u32 hash; + af_conn_t *conn; + hash = af_conn_hash(src_ip, dst_ip, src_port, dst_port, protocol); + conn = kmalloc(sizeof(af_conn_t), GFP_ATOMIC); + if (!conn) { + return NULL; + } + + conn->src_ip = src_ip; + conn->dst_ip = dst_ip; + conn->src_port = src_port; + conn->dst_port = dst_port; + conn->protocol = protocol; + conn->total_pkts = 0; + conn->app_id = 0; + conn->client_hello = 0; + conn->drop = 0; + conn->state = AF_CONN_NEW; + conn->last_jiffies = jiffies; + hlist_add_head(&conn->node, &af_conn_table[hash]); + AF_LMT_INFO("add new conn ok...%pI4:%d->%pI4:%d %d\n", + &conn->src_ip, conn->src_port, &conn->dst_ip, conn->dst_port, conn->protocol); + return conn; +} + + +af_conn_t* af_conn_find(u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u8 protocol) +{ + u32 hash; + af_conn_t *conn; + + hash = af_conn_hash(src_ip, dst_ip, src_port, dst_port, protocol); + hlist_for_each_entry(conn, &af_conn_table[hash], node) + { + if (conn->src_ip == src_ip && conn->dst_ip == dst_ip && + conn->src_port == src_port && conn->dst_port == dst_port && + conn->protocol == protocol) { + return conn; + } + } + return NULL; +} + + +af_conn_t* af_conn_find_and_add(u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u8 protocol) +{ + af_conn_t *conn; + conn = af_conn_find(src_ip, dst_ip, src_port, dst_port, protocol); + if (!conn) + { + conn = af_conn_add(src_ip, dst_ip, src_port, dst_port, protocol); + } + return conn; +} + + +void af_conn_update(af_conn_t *conn, u32 app_id, u8 drop) +{ + spin_lock(&af_conn_lock); + conn->app_id = app_id; + conn->drop = drop; + conn->last_jiffies = jiffies; + spin_unlock(&af_conn_lock); +} + +#define MAX_AF_CONN_CHECK_COUNT 5 +void af_conn_clean_timeout(void) +{ + int i; + af_conn_t *conn; + struct hlist_node *n; + unsigned long timeout = AF_CONN_TIMEOUT * HZ; + static int last_bucket = 0; + int count = 0; + spin_lock(&af_conn_lock); + for (i = last_bucket; i < AF_CONN_HASH_SIZE; i++) + { + hlist_for_each_entry_safe(conn, n, &af_conn_table[i], node) + { + if (time_after(jiffies, conn->last_jiffies + timeout)) { + AF_LMT_INFO("clean timeout conn ok...%pI4:%d->%pI4:%d %d\n", + &conn->src_ip, conn->src_port, &conn->dst_ip, conn->dst_port, conn->protocol); + hlist_del(&(conn->node)); + kfree(conn); + } + } + last_bucket = i; + count++; + if (count > MAX_AF_CONN_CHECK_COUNT) + break; + } + if (last_bucket == AF_CONN_HASH_SIZE - 1) + { + last_bucket = 0; + } + spin_unlock(&af_conn_lock); +} + +struct af_conn_iter_state +{ + unsigned int bucket; +}; + + +static void *af_conn_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos == 0) + { + return SEQ_START_TOKEN; + } + struct af_conn_iter_state *st = s->private; + while (st->bucket < AF_CONN_HASH_SIZE){ + if (!hlist_empty(&(af_conn_table[st->bucket]))) + { + return &af_conn_table[st->bucket++].first->next; + } + st->bucket++; + } + return NULL; +} + +static void *af_conn_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + (*pos)++; + if (v == SEQ_START_TOKEN){ + return NULL; + } + struct hlist_node *node = (struct hlist_node *)v; + node = node->next; + if (node != NULL) + return node; + return NULL; +} + +static void af_conn_seq_stop(struct seq_file *s, void *v) +{ +} + +static int af_conn_seq_show(struct seq_file *s, void *v) +{ + unsigned char src_ip_str[32] = {0}; + unsigned char dst_ip_str[32] = {0}; + static int index = 0; + af_conn_t *node = (af_conn_t *)v; + if (v == SEQ_START_TOKEN) + { + index = 0; + seq_printf(s, "%-4s %-20s %-20s %-12s %-12s %-12s %-12s %-12s %-12s %-12s\n", + "Id", "src_ip", "dst_ip", "src_port", "dst_port", "protocol", "app_id", "drop", "inactive", "total_pkts"); + return 0; + } + + index++; + sprintf(src_ip_str, "%pI4", &node->src_ip); + sprintf(dst_ip_str, "%pI4", &node->dst_ip); + u_int32_t inactive_time = jiffies - node->last_jiffies; + + seq_printf(s, "%-4d %-20s %-20s %-12d %-12d %-12d %-12d %-12d %-12d %-12d\n", index, src_ip_str, dst_ip_str, + node->src_port, node->dst_port, node->protocol, node->app_id, node->drop, inactive_time, node->total_pkts); + return 0; +} +static const struct seq_operations af_conn_seq_ops = { + .start = af_conn_seq_start, + .next = af_conn_seq_next, + .stop = af_conn_seq_stop, + .show = af_conn_seq_show +}; + + +static int af_conn_open(struct inode *inode, struct file *file) +{ + struct seq_file *seq; + struct af_conn_iter_state *iter; + int err; + + iter = kzalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; + + err = seq_open(file, &af_conn_seq_ops); + if (err) + { + kfree(iter); + return err; + } + + seq = file->private_data; + seq->private = iter; + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 5, 0) +static const struct file_operations af_conn_fops = { + .owner = THIS_MODULE, + .open = af_conn_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; +#else +static const struct proc_ops af_conn_fops = { + .proc_flags = PROC_ENTRY_PERMANENT, + .proc_read = seq_read, + .proc_open = af_conn_open, + .proc_lseek = seq_lseek, + .proc_release = seq_release_private, +}; +#endif + +#define AF_CONN_PROC_STR "af_conn" + +int af_conn_init_procfs(void) +{ + struct proc_dir_entry *pde; + struct net *net = &init_net; + pde = proc_create(AF_CONN_PROC_STR, 0644, net->proc_net, &af_conn_fops); + if (!pde) + { + printk("af_conn seq file created error\n"); + return -1; + } + + return 0; +} + +void af_conn_remove_procfs(void) +{ + struct net *net = &init_net; + remove_proc_entry(AF_CONN_PROC_STR, net->proc_net); +} + + +int af_conn_init(void) +{ + int i; + for (i = 0; i < AF_CONN_HASH_SIZE; i++) + { + INIT_HLIST_HEAD(&af_conn_table[i]); + } + af_conn_init_procfs(); + return 0; +} + +void af_conn_exit(void){ + af_conn_remove_procfs(); + af_conn_cleanup(); +} diff --git a/oaf/src/af_conntrack.h b/oaf/src/af_conntrack.h new file mode 100644 index 0000000..3a7302b --- /dev/null +++ b/oaf/src/af_conntrack.h @@ -0,0 +1,52 @@ +#ifndef __AF_SIMPLE_CONNTRACK_H__ +#define __AF_SIMPLE_CONNTRACK_H__ + +#include +#include +#define AF_CONN_TIMEOUT 30 +#define AF_CONN_HASH_SIZE 256 + +extern spinlock_t af_conn_lock; +typedef enum { + AF_CONN_NEW = 0, + AF_CONN_ESTABLISHED, + AF_CONN_DPI_FINISHED, +} af_conn_state_t; + +typedef struct { + struct hlist_node node; + u32 src_ip; + u32 dst_ip; + u16 src_port; + u16 dst_port; + u8 protocol; + u32 total_pkts; + u32 app_id; + u8 client_hello; + u8 drop; + af_conn_state_t state; + unsigned long last_jiffies; +} af_conn_t; + +int af_conn_init(void); + +void af_conn_cleanup(void); +af_conn_t* af_conn_add(u32 src_ip, u32 dst_ip, + u16 src_port, u16 dst_port, + u8 protocol); + +af_conn_t* af_conn_find(u32 src_ip, u32 dst_ip, + u16 src_port, u16 dst_port, + u8 protocol); + +af_conn_t* af_conn_find_and_add(u32 src_ip, u32 dst_ip, + u16 src_port, u16 dst_port, + u8 protocol); + +void af_conn_update(af_conn_t *conn, u32 app_id, u8 drop); + +void af_conn_clean_timeout(void); + + +void af_conn_exit(void); +#endif diff --git a/oaf/src/af_log.c b/oaf/src/af_log.c new file mode 100755 index 0000000..9416774 --- /dev/null +++ b/oaf/src/af_log.c @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include +#include "app_filter.h" +#include "af_log.h" +int af_log_lvl = 1; +int af_test_mode = 0; +// todo: rename af_log.c +int g_oaf_filter_enable __read_mostly = 0; +int g_oaf_record_enable __read_mostly = 0; +int g_by_pass_accl = 1; +int g_user_mode = 0; +int af_work_mode = AF_MODE_GATEWAY; +unsigned int af_lan_ip = 0; +unsigned int af_lan_mask = 0; +char g_lan_ifname[64] = "br-lan"; +int g_tcp_rst = 1; +int g_feature_init = 0; +char g_oaf_version[64] = AF_VERSION; +/* + cat /proc/sys/oaf/debug +*/ +static struct ctl_table oaf_table[] = { + { + .procname = "debug", + .data = &af_log_lvl, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = proc_dointvec, + }, + { + .procname = "feature_init", + .data = &g_feature_init, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = proc_dointvec, + }, + { + .procname = "version", + .data = g_oaf_version, + .maxlen = 64, + .mode = 0444, + .proc_handler = proc_dostring, + }, + { + .procname = "test_mode", + .data = &af_test_mode, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = proc_dointvec, + }, + { + .procname = "enable", + .data = &g_oaf_filter_enable, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = proc_dointvec, + }, + { + .procname = "by_pass_accl", + .data = &g_by_pass_accl, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = proc_dointvec, + }, + { + .procname = "tcp_rst", + .data = &g_tcp_rst, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = proc_dointvec, + }, + { + .procname = "lan_ifname", + .data = g_lan_ifname, + .maxlen = 64, + .mode = 0666, + .proc_handler = proc_dostring, + }, + { + .procname = "record_enable", + .data = &g_oaf_record_enable, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = proc_dointvec, + }, + { + .procname = "user_mode", + .data = &g_user_mode, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = proc_dointvec, + }, + { + .procname = "work_mode", + .data = &af_work_mode, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = proc_dointvec, + }, + { + .procname = "lan_ip", + .data = &af_lan_ip, + .maxlen = sizeof(unsigned int), + .mode = 0666, + .proc_handler = proc_douintvec, + }, + { + .procname = "lan_mask", + .data = &af_lan_mask, + .maxlen = sizeof(unsigned int), + .mode = 0666, + .proc_handler = proc_douintvec, + }, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 12, 0)) + { + } +#endif +}; +#define OAF_SYS_PROC_DIR "oaf" + +static struct ctl_table oaf_root_table[] = { + { + .procname = OAF_SYS_PROC_DIR, + .mode = 0555, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)) + .child = oaf_table, +#endif + }, + {} +}; +static struct ctl_table_header *oaf_table_header; + + +static int af_init_log_sysctl(void) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)) + oaf_table_header = register_sysctl_table(oaf_root_table); +#else + oaf_table_header = register_sysctl(OAF_SYS_PROC_DIR, oaf_table); +#endif + if (oaf_table_header == NULL){ + printk("init log sysctl...failed\n"); + return -ENOMEM; + } + printk("init oaf sysctl...ok\n"); + return 0; +} + +static int af_fini_log_sysctl(void) +{ + if (oaf_table_header) + unregister_sysctl_table(oaf_table_header); + return 0; +} + +int af_log_init(void){ + af_init_log_sysctl(); + return 0; +} + +int af_log_exit(void){ + af_fini_log_sysctl(); + return 0; +} diff --git a/oaf/src/af_log.h b/oaf/src/af_log.h new file mode 100755 index 0000000..5f50479 --- /dev/null +++ b/oaf/src/af_log.h @@ -0,0 +1,43 @@ +#ifndef __AF_DEBUG_H__ +#define __AF_DEBUG_H__ +extern int af_log_lvl; +extern int af_test_mode; +extern int af_work_mode; +extern int g_oaf_filter_enable; +extern int g_oaf_record_enable; +extern int g_by_pass_accl; +extern unsigned int af_lan_ip; +extern unsigned int af_lan_mask; +extern int g_feature_init; +extern int g_user_mode; + +extern char g_lan_ifname[64]; +extern int g_tcp_rst; +#define LOG(level, fmt, ...) do { \ + if ((level) <= af_log_lvl) { \ + printk(fmt, ##__VA_ARGS__); \ + } \ +} while (0) + +#define LLOG(level, fmt, ...) do { \ + if ((level) <= af_log_lvl) { \ + pr_info_ratelimited(fmt, ##__VA_ARGS__); \ + } \ +} while (0) + + +#define AF_ERROR(...) LOG(0, ##__VA_ARGS__) +#define AF_WARN(...) LOG(1, ##__VA_ARGS__) +#define AF_INFO(...) LOG(2, ##__VA_ARGS__) +#define AF_DEBUG(...) LOG(3, ##__VA_ARGS__) + +#define AF_LMT_ERROR(...) LLOG(0, ##__VA_ARGS__) +#define AF_LMT_WARN(...) LLOG(1, ##__VA_ARGS__) +#define AF_LMT_INFO(...) LLOG(2, ##__VA_ARGS__) +#define AF_LMT_DEBUG(...) LLOG(3, ##__VA_ARGS__) + + +#define TEST_MODE() (af_test_mode) +int af_log_init(void); +int af_log_exit(void); +#endif diff --git a/oaf/src/af_utils.c b/oaf/src/af_utils.c new file mode 100755 index 0000000..c2b3e11 --- /dev/null +++ b/oaf/src/af_utils.c @@ -0,0 +1,383 @@ +#include +#include +#include +#include +#include +#include +#include +#include "af_utils.h" +u_int32_t af_get_timestamp_sec(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,17,0) + struct timespec64 ts; + ktime_get_real_ts64(&ts); + return (u_int32_t)ts.tv_sec; +#else + struct timespec ts; + ts = current_kernel_time(); + return ts.tv_sec; +#endif + +} + +int k_atoi(const char *str) { + int result = 0; + + // Skip whitespace + while (*str == ' ' || *str == '\t') { + str++; + } + + // Convert characters to integer + while (*str >= '0' && *str <= '9') { + result = result * 10 + (*str - '0'); + str++; + } + + return result; +} + +char *k_trim(char *s) +{ + char *start, *last, *bk; + int len; + + start = s; + while (isspace(*start)) + start++; + + bk = last = s + strlen(s) - 1; + while (last > start && isspace(*last)) + last--; + + if ((s != start) || (bk != last)) + { + len = last - start + 1; + strncpy(s, start, len); + s[len] = '\0'; + } + return s; +} + +int check_local_network_ip(unsigned int ip) +{ + if ((ip & 0xffff0000) == 0xc0a80000) + return 1; + else if ((ip & 0xfff00000) == 0xac100000) + return 1; + else if ((ip & 0xff000000) == 0x0a000000) + return 1; + else + return 0; +} + +void dump_str(char *name, unsigned char *p, int len) +{ + #define MAX_DUMP_STR_LEN 64 + char buf[MAX_DUMP_STR_LEN] = {0}; + if (len > MAX_DUMP_STR_LEN) { + len = MAX_DUMP_STR_LEN - 1; + } + printk("%s: ",name); + strncpy(buf, p, len); + printk("[%s]\n", buf); +} +int isprint_char(unsigned char c) +{ + if (c >= 0x20 && c <= 0x7e) + return 1; + else + return 0; +} + +void print_hex_ascii(const unsigned char *data, size_t size) { + size_t i, j; + + for (i = 0; i < size; i += 16) { + printk(KERN_CONT"%08lx ", (unsigned long)i); + for (j = 0; j < 16; ++j) { + if (i + j < size) { + printk(KERN_CONT"%02x ", data[i + j]); + } else { + printk(KERN_CONT" "); + } + } + + printk(KERN_CONT" "); + + for (j = 0; j < 16; ++j) { + if (i + j < size) { + unsigned char c = data[i + j]; + printk(KERN_CONT"%c", isprint_char(c) ? c : '.'); + } + } + + printk(KERN_CONT"\n"); + } + printk(KERN_CONT"---------------------------------------\n"); +} + + +void dump_hex(char *name, unsigned char *p, int len) +{ + #define MAX_DUMP_STR_LEN 64 + int i; + if (len > MAX_DUMP_STR_LEN) { + len = MAX_DUMP_STR_LEN - 1; + } + printk("%s: ",name); + for (i = 0; i < len; i++) { + if (i % 16 == 0) + printk(KERN_CONT "\n"); + printk(KERN_CONT "%02X ",*(p + i)); + } + printk(KERN_CONT "\n"); +} + +#ifndef va_arg +typedef signed int acpi_native_int; +#ifndef _VALIST +#define _VALIST +typedef char *va_list; +#endif + +#define _AUPBND (sizeof (acpi_native_int) - 1) +#define _ADNBND (sizeof (acpi_native_int) - 1) + + +#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd))) +#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND)))) +#define va_end(ap) (void) 0 +#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND)))) + +#endif + +#ifndef TOLOWER +#define TOLOWER(x) ((x) | 0x20) +#endif + + +static long long k_simple_strtoll(const char *cp, char **endp, unsigned int base) +{ + if (*cp == '-') + return -simple_strtoull(cp + 1, endp, base); + + return simple_strtoull(cp, endp, base); +} + +static int skip_atoi(const char **s) +{ + int i=0; + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +char *skip_spaces(const char *str) +{ + while (isspace(*str) && ((unsigned char )*str != 0xa0)) + ++str; + return (char *)str; +} +static int k_vsscanf(const char *buf, const char *fmt, va_list args) +{ + const char *str = buf; + char *next; + char digit; + int num = 0; + u8 qualifier; + u8 base; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22) + int field_width; +#else + s16 field_width; +#endif + bool is_sign; + while (*fmt && *str) { + if (isspace(*fmt)) { + fmt = skip_spaces(++fmt); + str = skip_spaces(str); + } + + if (*fmt != '%' && *fmt) { + if (*fmt++ != *str++) + break; + continue; + } + if (!*fmt) + break; + ++fmt; + + if (*fmt == '*') { + while (!isspace(*fmt) && *fmt != '%' && *fmt) + fmt++; + while (!isspace(*str) && *str) + str++; + continue; + } + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + qualifier = -1; + if (*fmt == 'h' || TOLOWER(*fmt) == 'l' || + TOLOWER(*fmt) == 'z') { + qualifier = *fmt++; + if (unlikely(qualifier == *fmt)) { + if (qualifier == 'h') { + qualifier = 'H'; + fmt++; + } else if (qualifier == 'l') { + qualifier = 'L'; + fmt++; + } + } + } + if (!*fmt || !*str) + break; + base = 10; + is_sign = 0; + switch (*fmt++) { + case 'c': + { + char *s = (char *)va_arg(args, char*); + if (field_width == -1) + field_width = 1; + do { + *s++ = *str++; + } while (--field_width > 0 && *str); + num++; + } + continue; + case 's': + { + char *s = (char *)va_arg(args, char *); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22) + if(field_width == -1) + field_width = INT_MAX; +#else + if (field_width == -1) + field_width = SHRT_MAX; +#endif + str = skip_spaces(str); + + while (*str && (!isspace(*str) || ((unsigned char )*str == 0xA0) )&& field_width--) + *s++ = *str++; + *s = '\0'; + num++; + } + continue; + case 'n': + { + int *i = (int *)va_arg(args, int*); + *i = str - buf; + } + continue; + case 'o': + base = 8; + break; + case 'x': + case 'X': + base = 16; + break; + case 'i': + base = 0; + case 'd': + is_sign = 1; + case 'u': + break; + case '%': + if (*str++ != '%') + return num; + continue; + default: + return num; + } + str = skip_spaces(str); + digit = *str; + if (is_sign && digit == '-') + digit = *(str + 1); + if (!digit + || (base == 16 && !isxdigit(digit)) + || (base == 10 && !isdigit(digit)) + || (base == 8 && (!isdigit(digit) || digit > '7')) + || (base == 0 && !isdigit(digit))) + break; + + switch (qualifier) { + case 'H': + if (is_sign) { + signed char *s = (signed char *)va_arg(args, signed char *); + *s = (signed char)simple_strtol(str, &next, base); + } else { + unsigned char *s = (unsigned char *)va_arg(args, unsigned char *); + *s = (unsigned char)simple_strtoul(str, &next, base); + } + break; + case 'h': + if (is_sign) { + short *s = (short *)va_arg(args, short *); + *s = (short)simple_strtol(str, &next, base); + } else { + unsigned short *s = (unsigned short *)va_arg(args, unsigned short *); + *s = (unsigned short)simple_strtoul(str, &next, base); + } + break; + case 'l': + if (is_sign) { + long *l = (long *)va_arg(args, long *); + *l = simple_strtol(str, &next, base); + } else { + unsigned long *l = (unsigned long *)va_arg(args, unsigned long *); + *l = simple_strtoul(str, &next, base); + } + break; + case 'L': + if (is_sign) { + long long *l = (long long *)va_arg(args, long long *); + *l = k_simple_strtoll(str, &next, base); + } else { + unsigned long long *l = (unsigned long long *)va_arg(args, unsigned long long *); + *l = simple_strtoull(str, &next, base); + } + break; + case 'Z': + case 'z': + { + size_t *s = (size_t *)va_arg(args, size_t *); + *s = (size_t)simple_strtoul(str, &next, base); + } + break; + default: + if (is_sign) { + int *i = (int *)va_arg(args, int *); + *i = (int)simple_strtol(str, &next, base); + } else { + unsigned int *i = (unsigned int *)va_arg(args, unsigned int*); + *i = (unsigned int)simple_strtoul(str, &next, base); + } + break; + } + num++; + if (!next) + break; + str = next; + } + if (*fmt == '%' && *(fmt + 1) == 'n') { + int *p = (int *)va_arg(args, int *); + *p = str - buf; + } + return num; +} + + +int k_sscanf(const char *buf, const char *fmt, ...) +{ + va_list args; + int i; + va_start(args, fmt); + i = k_vsscanf(buf, fmt, args); + va_end(args); + return i; +} + + diff --git a/oaf/src/af_utils.h b/oaf/src/af_utils.h new file mode 100755 index 0000000..47e1e44 --- /dev/null +++ b/oaf/src/af_utils.h @@ -0,0 +1,18 @@ +#ifndef AF_UTILS_H +#define AF_UTILS_H +u_int32_t af_get_timestamp_sec(void); + +char *k_trim(char *s); + +int check_local_network_ip(unsigned int ip); + +void dump_str(char *name, unsigned char *p, int len); + +void dump_hex(char *name, unsigned char *p, int len); + +int k_sscanf(const char *buf, const char *fmt, ...); +int k_atoi(const char *str); +void print_hex_ascii(const unsigned char *data, size_t size); + +#endif + diff --git a/oaf/src/app_filter.c b/oaf/src/app_filter.c new file mode 100755 index 0000000..8a9e5ae --- /dev/null +++ b/oaf/src/app_filter.c @@ -0,0 +1,1692 @@ +/* + author: derry + date:2019/1/10 +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "app_filter.h" +#include "af_utils.h" +#include "af_log.h" +#include "af_client.h" +#include "af_client_fs.h" +#include "cJSON.h" +#include "af_conntrack.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("destan19@126.com"); +MODULE_DESCRIPTION("app filter module"); +MODULE_VERSION(AF_VERSION); +struct list_head af_feature_head = LIST_HEAD_INIT(af_feature_head); + +DEFINE_RWLOCK(af_feature_lock); + +#define feature_list_read_lock() read_lock_bh(&af_feature_lock); +#define feature_list_read_unlock() read_unlock_bh(&af_feature_lock); +#define feature_list_write_lock() write_lock_bh(&af_feature_lock); +#define feature_list_write_unlock() write_unlock_bh(&af_feature_lock); + +#define SET_APPID(mark, appid) (mark = appid) +#define GET_APPID(mark) (mark) +#define MAX_OAF_NETLINK_MSG_LEN 1024 +#define MAX_AF_SUPPORT_DATA_LEN 3000 +#define MAX_HOST_LEN 64 +#define MIN_HOST_LEN 4 + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5,10,197) +extern void nf_send_reset(struct net *net, struct sock *sk, struct sk_buff *oldskb, int hook); +#elif LINUX_VERSION_CODE > KERNEL_VERSION(4,4,1) +extern void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook); +#else +extern void nf_send_reset(sk_buff *oldskb, int hook); +#endif + +int __add_app_feature(char *feature, int appid, char *name, int proto, int src_port, + port_info_t dport_info, char *host_url, char *request_url, char *dict, char *search_str, int ignore) +{ + af_feature_node_t *node = NULL; + char *p = dict; + char *begin = dict; + char pos[32] = {0}; + int index = 0; + int value = 0; + node = kzalloc(sizeof(af_feature_node_t), GFP_KERNEL); + if (node == NULL) + { + printk("malloc feature memory error\n"); + return -1; + } + else + { + node->app_id = appid; + strcpy(node->app_name, name); + node->proto = proto; + node->dport_info = dport_info; + node->sport = src_port; + strcpy(node->host_url, host_url); + strcpy(node->request_url, request_url); + strcpy(node->search_str, search_str); + node->ignore = ignore; + strcpy(node->feature, feature); + // 00:0a-01:11 + p = dict; + begin = dict; + index = 0; + value = 0; + while (*p++) + { + if (*p == '|') + { + memset(pos, 0x0, sizeof(pos)); + strncpy(pos, begin, p - begin); + k_sscanf(pos, "%d:%x", &index, &value); + begin = p + 1; + node->pos_info[node->pos_num].pos = index; + node->pos_info[node->pos_num].value = value; + node->pos_num++; + } + } + + if (begin != dict) + strncpy(pos, begin, p - begin); + else + strcpy(pos, dict); + + int ret = k_sscanf(pos, "%d:%x", &index, &value); + if (ret == 2){ + node->pos_info[node->pos_num].pos = index; + node->pos_info[node->pos_num].value = value; + node->pos_num++; + } + + feature_list_write_lock(); + list_add(&(node->head), &af_feature_head); + feature_list_write_unlock(); + } + return 0; +} +int validate_range_value(char *range_str) +{ + if (!range_str) + return 0; + char *p = range_str; + while (*p) + { + if (*p == ' ' || *p == '!' || *p == '-' || + ((*p >= '0') && (*p <= '9'))) + { + p++; + continue; + } + else + { + return 0; + } + } + return 1; +} + +int parse_range_value(char *range_str, range_value_t *range) +{ + char pure_range[128] = {0}; + if (!validate_range_value(range_str)) + { + printk("validate range str failed, value = %s\n", range_str); + return -1; + } + k_trim(range_str); + if (range_str[0] == '!') + { + range->not = 1; + strcpy(pure_range, range_str + 1); + } + else + { + range->not = 0; + strcpy(pure_range, range_str); + } + k_trim(pure_range); + int start, end; + if (strstr(pure_range, "-")) + { + if (2 != sscanf(pure_range, "%d-%d", &start, &end)) + return -1; + } + else + { + if (1 != sscanf(pure_range, "%d", &start)) + return -1; + end = start; + } + range->start = start; + range->end = end; + return 0; +} + +int parse_port_info(char *port_str, port_info_t *info) +{ + char *p = port_str; + char *begin = port_str; + int param_num = 0; + char one_port_buf[128] = {0}; + k_trim(port_str); + if (strlen(port_str) == 0) + return -1; + + while (*p++) + { + if (*p != '|') + continue; + memset(one_port_buf, 0x0, sizeof(one_port_buf)); + strncpy(one_port_buf, begin, p - begin); + if (0 == parse_range_value(one_port_buf, &info->range_list[info->num])) + { + info->num++; + } + param_num++; + begin = p + 1; + } + memset(one_port_buf, 0x0, sizeof(one_port_buf)); + strncpy(one_port_buf, begin, p - begin); + if (0 == parse_range_value(one_port_buf, &info->range_list[info->num])) + { + info->num++; + } + return 0; +} + +int af_match_port(port_info_t *info, int port) +{ + int i; + int with_not = 0; + if (info->num == 0) + return 1; + for (i = 0; i < info->num; i++) + { + if (info->range_list[i].not ) + { + with_not = 1; + break; + } + } + for (i = 0; i < info->num; i++) + { + if (with_not) + { + if (info->range_list[i].not &&port >= info->range_list[i].start && port <= info->range_list[i].end) + { + return 0; + } + } + else + { + if (port >= info->range_list[i].start && port <= info->range_list[i].end) + { + return 1; + } + } + } + if (with_not) + return 1; + else + return 0; +} +//[tcp;;443;baidu.com;;] +int add_app_feature(int appid, char *name, char *feature) +{ + char proto_str[16] = {0}; + char src_port_str[16] = {0}; + port_info_t dport_info; + char dst_port_str[16] = {0}; + char host_url[32] = {0}; + char request_url[128] = {0}; + char dict[128] = {0}; + int proto = IPPROTO_TCP; + char *p = feature; + char *begin = feature; + int param_num = 0; + int dst_port = 0; + int src_port = 0; + char tmp_buf[128] = {0}; + int ignore = 0; + char search_str[128] = {0}; + + if (!name || !feature) + { + AF_ERROR("error, name or feature is null\n"); + return -1; + } + // tcp;8000;www.sina.com;0:get_name;00:0a-01:11 + memset(&dport_info, 0x0, sizeof(dport_info)); + while (*p++) + { + if (*p != ';') + continue; + + switch (param_num) + { + + case AF_PROTO_PARAM_INDEX: + strncpy(proto_str, begin, p - begin); + break; + case AF_SRC_PORT_PARAM_INDEX: + strncpy(src_port_str, begin, p - begin); + break; + case AF_DST_PORT_PARAM_INDEX: + strncpy(dst_port_str, begin, p - begin); + break; + + case AF_HOST_URL_PARAM_INDEX: + strncpy(host_url, begin, p - begin); + break; + + case AF_REQUEST_URL_PARAM_INDEX: + strncpy(request_url, begin, p - begin); + break; + case AF_DICT_PARAM_INDEX: + strncpy(dict, begin, p - begin); + break; + case AF_STR_PARAM_INDEX: + strncpy(search_str, begin, p - begin); + break; + case AF_IGNORE_PARAM_INDEX: + strncpy(tmp_buf, begin, p - begin); + ignore = k_atoi(tmp_buf); + break; + } + param_num++; + begin = p + 1; + } + + // old version + if (param_num == AF_DICT_PARAM_INDEX){ + strncpy(dict, begin, p - begin); + } + // new version + if (param_num == AF_IGNORE_PARAM_INDEX){ + strncpy(tmp_buf, begin, p - begin); + ignore = k_atoi(tmp_buf); + } + + if (0 == strcmp(proto_str, "tcp")) + proto = IPPROTO_TCP; + else if (0 == strcmp(proto_str, "udp")) + proto = IPPROTO_UDP; + else + { + printk("proto %s is not support, feature = %s\n", proto_str, feature); + return -1; + } + sscanf(src_port_str, "%d", &src_port); + // sscanf(dst_port_str, "%d", &dst_port); + parse_port_info(dst_port_str, &dport_info); + + __add_app_feature(feature, appid, name, proto, src_port, dport_info, host_url, request_url, dict, search_str, ignore); + return 0; +} + +void af_init_feature(char *feature_str) +{ + int app_id; + char app_name[128] = {0}; + char feature_buf[MAX_FEATURE_LINE_LEN] = {0}; + char *p = feature_str; + char *pos = NULL; + int len = 0; + char *begin = NULL; + char feature[MAX_FEATURE_STR_LEN]; + + if (strstr(feature_str, "#")) + return; + + k_sscanf(feature_str, "%d%[^:]", &app_id, app_name); + while (*p++) + { + if (*p == '[') + { + pos = p + 1; + continue; + } + if (*p == ']' && pos != NULL) + { + len = p - pos; + } + } + + if (pos && len) + strncpy(feature_buf, pos, len); + memset(feature, 0x0, sizeof(feature)); + p = feature_buf; + begin = feature_buf; + + while (*p++) + { + if (*p == ',') + { + memset(feature, 0x0, sizeof(feature)); + strncpy((char *)feature, begin, p - begin); + + add_app_feature(app_id, app_name, feature); + begin = p + 1; + } + } + if (p != begin) + { + memset(feature, 0x0, sizeof(feature)); + strncpy((char *)feature, begin, p - begin); + add_app_feature(app_id, app_name, feature); + } +} + +void load_feature_buf_from_file(char **config_buf) +{ + struct inode *inode = NULL; + struct file *fp = NULL; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 7, 19) + mm_segment_t fs; +#endif + off_t size; + fp = filp_open(AF_FEATURE_CONFIG_FILE, O_RDONLY, 0); + + + if (IS_ERR(fp)) + { + return; + } + + inode = fp->f_inode; + size = inode->i_size; + if (size == 0) + { + return; + } + *config_buf = (char *)kzalloc(sizeof(char) * size, GFP_KERNEL); + if (NULL == *config_buf) + { + AF_ERROR("alloc buf fail\n"); + filp_close(fp, NULL); + return; + } + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 7, 19) + fs = get_fs(); + set_fs(KERNEL_DS); +#endif +// 4.14rc3 vfs_read-->kernel_read +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + kernel_read(fp, *config_buf, size, &(fp->f_pos)); +#else + vfs_read(fp, *config_buf, size, &(fp->f_pos)); +#endif + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 7, 19) + set_fs(fs); +#endif + filp_close(fp, NULL); +} + +int load_feature_config(void) +{ + char *feature_buf = NULL; + char *p; + char *begin; + char line[MAX_FEATURE_LINE_LEN] = {0}; + + load_feature_buf_from_file(&feature_buf); + if (!feature_buf) + { + return -1; + } + p = begin = feature_buf; + while (*p++) + { + if (*p == '\n') + { + if (p - begin < MIN_FEATURE_LINE_LEN || p - begin > MAX_FEATURE_LINE_LEN) + { + begin = p + 1; + continue; + } + memset(line, 0x0, sizeof(line)); + strncpy(line, begin, p - begin); + af_init_feature(line); + begin = p + 1; + } + } + + if (p != begin) + { + if (p - begin < MIN_FEATURE_LINE_LEN || p - begin > MAX_FEATURE_LINE_LEN) + return 0; + memset(line, 0x0, sizeof(line)); + strncpy(line, begin, p - begin); + af_init_feature(line); + begin = p + 1; + } + if (feature_buf) + kfree(feature_buf); + return 0; +} + +static void af_clean_feature_list(void) +{ + af_feature_node_t *node; + feature_list_write_lock(); + while (!list_empty(&af_feature_head)) + { + node = list_first_entry(&af_feature_head, af_feature_node_t, head); + list_del(&(node->head)); + kfree(node); + } + feature_list_write_unlock(); +} + +void af_add_feature_msg_handle(char *data, int len) +{ + char feature[MAX_FEATURE_LINE_LEN] = {0}; + if (len <= 0 || len >= MAX_FEATURE_LINE_LEN){ + printk("warn, feature data len = %d\n", len); + return; + } + strncpy(feature, data, len); + AF_INFO("add feature %s\n", feature); + af_init_feature(feature); +} +// free by caller +static unsigned char *read_skb(struct sk_buff *skb, unsigned int from, unsigned int len) +{ + struct skb_seq_state state; + unsigned char *msg_buf = NULL; + unsigned int consumed = 0; +#if 0 + if (from <= 0 || from > 1500) + return NULL; + + if (len <= 0 || from+len > 1500) + return NULL; +#endif + + msg_buf = kmalloc(len, GFP_KERNEL); + if (!msg_buf) + return NULL; + + skb_prepare_seq_read(skb, from, from + len, &state); + while (1) + { + unsigned int avail; + const u8 *ptr; + avail = skb_seq_read(consumed, &ptr, &state); + if (avail == 0) + { + break; + } + memcpy(msg_buf + consumed, ptr, avail); + consumed += avail; + if (consumed >= len) + { + skb_abort_seq_read(&state); + break; + } + } + return msg_buf; +} + +int parse_flow_proto(struct sk_buff *skb, flow_info_t *flow) +{ + unsigned char *ipp; + int ipp_len; + struct tcphdr *tcph = NULL; + struct udphdr *udph = NULL; + struct nf_conn *ct = NULL; + struct iphdr *iph = NULL; + struct ipv6hdr *ip6h = NULL; + if (!skb) + return -1; + switch (skb->protocol) + { + case htons(ETH_P_IP): + iph = ip_hdr(skb); + flow->src = iph->saddr; + flow->dst = iph->daddr; + flow->l4_protocol = iph->protocol; + ipp = ((unsigned char *)iph) + iph->ihl * 4; + ipp_len = ((unsigned char *)iph) + ntohs(iph->tot_len) - ipp; + break; + case htons(ETH_P_IPV6): + ip6h = ipv6_hdr(skb); + flow->src6 = ip6h->saddr.s6_addr; + flow->dst6 = ip6h->daddr.s6_addr; + flow->l4_protocol = ip6h->nexthdr; + ipp = ((unsigned char *)ip6h) + sizeof(struct ipv6hdr); + ipp_len = ntohs(ip6h->payload_len); + break; + default: + return -1; + } + + switch (flow->l4_protocol) + { + case IPPROTO_TCP: + tcph = (struct tcphdr *)ipp; + flow->l4_len = ipp_len - tcph->doff * 4; + flow->l4_data = ipp + tcph->doff * 4; + flow->dport = ntohs(tcph->dest); + flow->sport = ntohs(tcph->source); + return 0; + case IPPROTO_UDP: + udph = (struct udphdr *)ipp; + flow->l4_len = ntohs(udph->len) - 8; + flow->l4_data = ipp + 8; + flow->dport = ntohs(udph->dest); + flow->sport = ntohs(udph->source); + return 0; + case IPPROTO_ICMP: + break; + default: + return -1; + } + return -1; +} + +int check_domain(char *h, int len) +{ + int i; + for (i = 0; i < len; i++) + { + if ((h[i] >= 'a' && h[i] <= 'z') || (h[i] >= 'A' && h[i] <= 'Z') || + (h[i] >= '0' && h[i] <= '9') || h[i] == '.' || h[i] == '-') + { + continue; + } + else + return 0; + } + return 1; +} + +int dpi_https_proto(flow_info_t *flow) +{ + int i; + short url_len = 0; + char *p = flow->l4_data; + int data_len = flow->l4_len; + + if (NULL == flow) + { + AF_ERROR("flow is NULL\n"); + return -1; + } + if (NULL == p || data_len == 0) + { + return -1; + } + if (!((p[0] == 0x16 && p[1] == 0x03 && p[2] == 0x01) || flow->client_hello)) + return -1; + + for (i = 0; i < data_len; i++) + { + if (i + HTTPS_URL_OFFSET >= data_len) + { + AF_LMT_INFO("match https host failed, data_len = %d, sport:%d, dport:%d\n", data_len, flow->sport,flow->dport); + if ((TEST_MODE())){ + print_hex_ascii(flow->l4_data, flow->l4_len); + } + flow->client_hello = 1; + return -1; + } + + if (p[i] == 0x0 && p[i + 1] == 0x0 && p[i + 2] == 0x0 && p[i + 3] != 0x0) + { + // 2 bytes + memcpy(&url_len, p + i + HTTPS_LEN_OFFSET, 2); + + if (ntohs(url_len) <= MIN_HOST_LEN || ntohs(url_len) > data_len || ntohs(url_len) > MAX_HOST_LEN) + { + continue; + } + + if (i + HTTPS_URL_OFFSET + ntohs(url_len) < data_len) + { + // may invalid + if (!check_domain( p + i + HTTPS_URL_OFFSET, ntohs(url_len))) + continue; + flow->https.match = AF_TRUE; + flow->https.url_pos = p + i + HTTPS_URL_OFFSET; + flow->https.url_len = ntohs(url_len); + AF_LMT_INFO("match https host ok, data_len = %d, client hello = %d\n", data_len, flow->client_hello); + flow->client_hello = 0; + return 0; + } + } + } + return -1; +} + +void dpi_http_proto(flow_info_t *flow) +{ + int i = 0; + int start = 0; + char *data = NULL; + int data_len = 0; + if (!flow) + { + AF_ERROR("flow is null\n"); + return; + } + if (flow->l4_protocol != IPPROTO_TCP) + { + return; + } + + data = flow->l4_data; + data_len = flow->l4_len; + if (data_len < MIN_HTTP_DATA_LEN) + { + return; + } + + for (i = 0; i < data_len; i++) + { + if (data[i] == 0x0d && data[i + 1] == 0x0a) + { + if (0 == memcmp(&data[start], "POST ", 5)) + { + flow->http.match = AF_TRUE; + flow->http.method = HTTP_METHOD_POST; + flow->http.url_pos = data + start + 5; + flow->http.url_len = i - start - 5; + } + else if (0 == memcmp(&data[start], "GET ", 4)) + { + flow->http.match = AF_TRUE; + flow->http.method = HTTP_METHOD_GET; + flow->http.url_pos = data + start + 4; + flow->http.url_len = i - start - 4; + } + else if (0 == memcmp(&data[start], "Host:", 5)) + { + flow->http.host_pos = data + start + 6; + flow->http.host_len = i - start - 6; + } + if (data[i + 2] == 0x0d && data[i + 3] == 0x0a) + { + flow->http.data_pos = data + i + 4; + flow->http.data_len = data_len - i - 4; + break; + } + // 0x0d 0x0a + start = i + 2; + } + } +} + +static void dump_http_flow_info(http_proto_t *http) +{ + if (!http) + { + AF_ERROR("http ptr is NULL\n"); + return; + } + if (!http->match) + return; + if (http->method == HTTP_METHOD_GET) + { + printk("Http method: " HTTP_GET_METHOD_STR "\n"); + } + else if (http->method == HTTP_METHOD_POST) + { + printk("Http method: " HTTP_POST_METHOD_STR "\n"); + } + if (http->url_len > 0 && http->url_pos) + { + dump_str("Request url", http->url_pos, http->url_len); + } + + if (http->host_len > 0 && http->host_pos) + { + dump_str("Host", http->host_pos, http->host_len); + } + + printk("--------------------------------------------------------\n\n\n"); +} + +static void dump_https_flow_info(https_proto_t *https) +{ + if (!https) + { + AF_ERROR("https ptr is NULL\n"); + return; + } + if (!https->match) + return; + + if (https->url_len > 0 && https->url_pos) + { + dump_str("https server name", https->url_pos, https->url_len); + } + + printk("--------------------------------------------------------\n\n\n"); +} +static void dump_flow_info(flow_info_t *flow) +{ + if (!flow) + { + AF_ERROR("flow is null\n"); + return; + } + if (flow->l4_len > 0) + { + AF_LMT_INFO("src=" NIPQUAD_FMT ",dst=" NIPQUAD_FMT ",sport: %d, dport: %d, data_len: %d\n", + NIPQUAD(flow->src), NIPQUAD(flow->dst), flow->sport, flow->dport, flow->l4_len); + } + + if (flow->l4_protocol == IPPROTO_TCP) + { + if (AF_TRUE == flow->http.match) + { + printk("-------------------http protocol-------------------------\n"); + printk("protocol:TCP , sport: %-8d, dport: %-8d, data_len: %-8d\n", + flow->sport, flow->dport, flow->l4_len); + dump_http_flow_info(&flow->http); + } + if (AF_TRUE == flow->https.match) + { + printk("-------------------https protocol-------------------------\n"); + dump_https_flow_info(&flow->https); + } + } +} + + +char *k_memstr(char *data, char *str, int size) +{ + char *p; + char len = strlen(str); + for (p = data; p <= (data - len + size); p++) + { + if (memcmp(p, str, len) == 0) + return p; + } + return NULL; +} + +int af_match_by_pos(flow_info_t *flow, af_feature_node_t *node) +{ + int i; + unsigned int pos = 0; + + if (!flow || !node) + return AF_FALSE; + if (node->pos_num > 0) + { + + for (i = 0; i < node->pos_num && i < MAX_POS_INFO_PER_FEATURE; i++) + { + // -1 + if (node->pos_info[i].pos < 0) + { + pos = flow->l4_len + node->pos_info[i].pos; + } + else + { + pos = node->pos_info[i].pos; + } + if (pos >= flow->l4_len) + { + return AF_FALSE; + } + if (flow->l4_data[pos] != node->pos_info[i].value) + { + return AF_FALSE; + } + else{ + AF_DEBUG("match pos[%d] = %x\n", pos, node->pos_info[i].value); + } + } + if (strlen(node->search_str) > 0){ + if (k_memstr(flow->l4_data, node->search_str, flow->l4_len)){ + AF_DEBUG("match by search str, appid=%d, search_str=%s\n", node->app_id, node->search_str); + return AF_TRUE; + } + else{ + return AF_FALSE; + } + } + return AF_TRUE; + } + return AF_FALSE; +} + +int af_match_by_url(flow_info_t *flow, af_feature_node_t *node) +{ + char reg_url_buf[MAX_URL_MATCH_LEN] = {0}; + + if (!flow || !node) + return AF_FALSE; + // match host or https url + if (flow->https.match == AF_TRUE && flow->https.url_pos) + { + if (flow->https.url_len >= MAX_URL_MATCH_LEN) + strncpy(reg_url_buf, flow->https.url_pos, MAX_URL_MATCH_LEN - 1); + else + strncpy(reg_url_buf, flow->https.url_pos, flow->https.url_len); + } + else if (flow->http.match == AF_TRUE && flow->http.host_pos) + { + if (flow->http.host_len >= MAX_URL_MATCH_LEN) + strncpy(reg_url_buf, flow->http.host_pos, MAX_URL_MATCH_LEN - 1); + else + strncpy(reg_url_buf, flow->http.host_pos, flow->http.host_len); + } + if (strlen(reg_url_buf) > 0 && strlen(node->host_url) > 0 && regexp_match(node->host_url, reg_url_buf)) + { + AF_DEBUG("match url:%s reg = %s, appid=%d\n", + reg_url_buf, node->host_url, node->app_id); + return AF_TRUE; + } + + // match request url + if (flow->http.match == AF_TRUE && flow->http.url_pos) + { + memset(reg_url_buf, 0x0, sizeof(reg_url_buf)); + if (flow->http.url_len >= MAX_URL_MATCH_LEN) + strncpy(reg_url_buf, flow->http.url_pos, MAX_URL_MATCH_LEN - 1); + else + strncpy(reg_url_buf, flow->http.url_pos, flow->http.url_len); + if (strlen(reg_url_buf) > 0 && strlen(node->request_url) && regexp_match(node->request_url, reg_url_buf)) + { + AF_DEBUG("match request:%s reg:%s appid=%d\n", + reg_url_buf, node->request_url, node->app_id); + return AF_TRUE; + } + } + return AF_FALSE; +} + +int af_match_one(flow_info_t *flow, af_feature_node_t *node) +{ + int ret = AF_FALSE; + if (!flow || !node) + { + AF_ERROR("node or flow is NULL\n"); + return AF_FALSE; + } + if (node->proto > 0 && flow->l4_protocol != node->proto) + return AF_FALSE; + if (flow->l4_len == 0) + return AF_FALSE; + + if (node->sport != 0 && flow->sport != node->sport) + { + return AF_FALSE; + } + + if (!af_match_port(&node->dport_info, flow->dport)) + { + return AF_FALSE; + } + + if (strlen(node->request_url) > 0 || + strlen(node->host_url) > 0) + { + ret = af_match_by_url(flow, node); + } + else if (node->pos_num > 0) + { + + ret = af_match_by_pos(flow, node); + } + else + { + AF_DEBUG("node is empty, match sport:%d,dport:%d, appid = %d\n", + node->sport, node->dport, node->app_id); + return AF_TRUE; + } + + return ret; +} + +int match_feature(flow_info_t *flow) +{ + af_feature_node_t *n, *node; + feature_list_read_lock(); + if (!list_empty(&af_feature_head)) + { + list_for_each_entry_safe(node, n, &af_feature_head, head) + { + if (af_match_one(flow, node)) + { + AF_LMT_INFO("match feature, appid=%d, feature = %s\n", node->app_id, node->feature); + flow->app_id = node->app_id; + flow->feature = node; + strncpy(flow->app_name, node->app_name, sizeof(flow->app_name) - 1); + feature_list_read_unlock(); + return AF_TRUE; + } + } + } + feature_list_read_unlock(); + return AF_FALSE; +} + +int match_app_filter_rule(int appid, af_client_info_t *client) +{ + if (g_user_mode && !find_af_mac(client->mac)) + { + return AF_FALSE; + } + if (af_get_app_status(appid)) + { + AF_LMT_INFO("drop appid = %d\n", appid); + return AF_TRUE; + } + return AF_FALSE; +} + + +#define NF_DROP_BIT 0x80000000 +#define NF_CLIENT_HELLO_BIT 0x40000000 + + +static int af_get_visit_index(af_client_info_t *node, int app_id) +{ + int i; + for (i = 0; i < MAX_RECORD_APP_NUM; i++) + { + if (node->visit_info[i].app_id == app_id || node->visit_info[i].app_id == 0) + { + return i; + } + } + // default 0 + return 0; +} + +int af_update_client_app_info(af_client_info_t *node, int app_id, int drop) +{ + int index = -1; + if (!node) + return -1; + + index = af_get_visit_index(node, app_id); + if (index < 0 || index >= MAX_RECORD_APP_NUM) + return 0; + node->visit_info[index].total_num++; + if (drop) + node->visit_info[index].drop_num++; + node->visit_info[index].app_id = app_id; + node->visit_info[index].latest_time = af_get_timestamp_sec(); + node->visit_info[index].latest_action = drop; + return 0; +} + +int af_send_msg_to_user(char *pbuf, uint16_t len); +int af_match_bcast_packet(flow_info_t *f) +{ + if (!f) + return 0; + if (0 == f->src || 0 == f->dst || 0xffffffff == f->dst || 0 == f->dst) + return 1; + return 0; +} + +int af_match_local_packet(flow_info_t *f) +{ + if (!f) + return 0; + if (0x0100007f == f->src || 0x0100007f == f->dst) + { + return 1; + } + return 0; +} + +int dpi_main(struct sk_buff *skb, flow_info_t *flow) +{ + dpi_http_proto(flow); + dpi_https_proto(flow); + if (TEST_MODE()) + dump_flow_info(flow); + return 0; +} + +void af_get_smac(struct sk_buff *skb, u_int8_t *smac) +{ + struct ethhdr *ethhdr = NULL; + ethhdr = eth_hdr(skb); + if (ethhdr) + memcpy(smac, ethhdr->h_source, ETH_ALEN); + else + memcpy(smac, &skb->cb[40], ETH_ALEN); +} +int is_ipv4_broadcast(uint32_t ip) +{ + return (ip & 0x00FFFFFF) == 0x00FFFFFF; +} + +int is_ipv4_multicast(uint32_t ip) +{ + return (ip & 0xF0000000) == 0xE0000000; +} +int af_check_bcast_ip(flow_info_t *f) +{ + + if (0 == f->src || 0 == f->dst) + return 1; + if (is_ipv4_broadcast(ntohl(f->src)) || is_ipv4_broadcast(ntohl(f->dst))) + { + return 1; + } + if (is_ipv4_multicast(ntohl(f->src)) || is_ipv4_multicast(ntohl(f->dst))) + { + return 1; + } + + return 0; +} +u_int32_t app_filter_hook_bypass_handle(struct sk_buff *skb, struct net_device *dev) +{ + flow_info_t flow; + af_conn_t *conn; + u_int8_t smac[ETH_ALEN]; + af_client_info_t *client = NULL; + u_int32_t ret = NF_ACCEPT; + u_int8_t malloc_data = 0; + + if (!skb || !dev) + return NF_ACCEPT; + if (0 == af_lan_ip || 0 == af_lan_mask) + return NF_ACCEPT; + if (strstr(dev->name, "docker")) + return NF_ACCEPT; + + memset((char *)&flow, 0x0, sizeof(flow_info_t)); + if (parse_flow_proto(skb, &flow) < 0) + return NF_ACCEPT; + if (flow.src || flow.dst) + { + if (af_lan_ip == flow.src || af_lan_ip == flow.dst) + { + return NF_ACCEPT; + } + if (af_check_bcast_ip(&flow) || af_match_local_packet(&flow)) + return NF_ACCEPT; + + if ((flow.src & af_lan_mask) != (af_lan_ip & af_lan_mask)) + { + return NF_ACCEPT; + } + } + else if (flow.src6 && flow.dst6) + { + if (flow.src6[0] == 0xff || flow.dst6[0] == 0xff) + { + return NF_ACCEPT; + } + return NF_DROP; + } + else + { + return NF_ACCEPT; + } + af_get_smac(skb, smac); + + AF_CLIENT_LOCK_W(); + client = find_and_add_af_client(smac); + if (!client) + { + AF_CLIENT_UNLOCK_W(); + return NF_ACCEPT; + } + client->update_jiffies = jiffies; + if (flow.src) + client->ip = flow.src; + AF_CLIENT_UNLOCK_W(); + + + spin_lock(&af_conn_lock); + conn = af_conn_find_and_add(flow.src, flow.dst, flow.sport, flow.dport, flow.l4_protocol); + if (!conn){ + return NF_ACCEPT; + } + + conn->last_jiffies = jiffies; + conn->total_pkts++; + spin_unlock(&af_conn_lock); + #if 1 + if (g_by_pass_accl) { + if (conn->total_pkts > MAX_DPI_PKT_NUM) { + return NF_ACCEPT; + } + } + #endif + + + if (skb_is_nonlinear(skb) && flow.l4_len < MAX_AF_SUPPORT_DATA_LEN) + { + flow.l4_data = read_skb(skb, flow.l4_data - skb->data, flow.l4_len); + if (!flow.l4_data) + return NF_ACCEPT; + AF_LMT_DEBUG("##match nonlinear skb, len = %d\n", flow.l4_len); + malloc_data = 1; + } + flow.client_hello = conn->client_hello; + + if (conn->app_id != 0) + { + flow.app_id = conn->app_id; + flow.drop = conn->drop; + } + else{ + dpi_main(skb, &flow); + conn->client_hello = flow.client_hello; + + if (!match_feature(&flow)) + goto EXIT; + + + if (g_oaf_filter_enable){ + if (match_app_filter_rule(flow.app_id, client)){ + flow.drop = 1; + AF_LMT_INFO("##Drop appid %d\n",flow.app_id); + if (skb->protocol == htons(ETH_P_IP) && g_tcp_rst){ + #if LINUX_VERSION_CODE > KERNEL_VERSION(5,10,197) + nf_send_reset(&init_net, skb->sk, skb, NF_INET_PRE_ROUTING); + #elif LINUX_VERSION_CODE > KERNEL_VERSION(4,4,1) + nf_send_reset(&init_net, skb, NF_INET_PRE_ROUTING); + #else + nf_send_reset(skb, NF_INET_PRE_ROUTING); + #endif + } + + } + } + conn->drop = flow.drop; + conn->app_id = flow.app_id; + conn->state = AF_CONN_DPI_FINISHED; + } + + if (g_oaf_record_enable ){ + af_update_client_app_info(client, flow.app_id, flow.drop); + } + + if (flow.drop) + { + AF_LMT_INFO("drop appid = %d\n", flow.app_id); + ret = NF_DROP; + } + +EXIT: + if (malloc_data) + { + if (flow.l4_data) + { + kfree(flow.l4_data); + } + } + return ret; +} + +u_int32_t app_filter_hook_gateway_handle(struct sk_buff *skb, struct net_device *dev) +{ + unsigned long long total_packets = 0; + flow_info_t flow; + u_int8_t smac[ETH_ALEN]; + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = NULL; + struct nf_conn_acct *acct; + af_client_info_t *client = NULL; + u_int32_t ret = NF_ACCEPT; + u_int32_t app_id = 0; + u_int8_t drop = 0; + u_int8_t malloc_data = 0; + + if (!strstr(dev->name, g_lan_ifname)) + return NF_ACCEPT; + + memset((char *)&flow, 0x0, sizeof(flow_info_t)); + if (parse_flow_proto(skb, &flow) < 0) + return NF_ACCEPT; + + ct = nf_ct_get(skb, &ctinfo); + if (ct == NULL) + return NF_ACCEPT; + + if (flow.l4_protocol == IPPROTO_TCP && !nf_ct_is_confirmed(ct)){ + return NF_ACCEPT; + } + + if (!flow.src) + af_get_smac(skb, smac); + + AF_CLIENT_LOCK_R(); + client = flow.src ? find_af_client_by_ip(flow.src) : find_af_client(smac); + if (!client) + { + AF_CLIENT_UNLOCK_R(); + return NF_ACCEPT; + } + client->update_jiffies = jiffies; + AF_CLIENT_UNLOCK_R(); + + if (ct->mark != 0) + { + app_id = ct->mark & 0xffff; + if (app_id > 1000 && app_id < 9999) + { + if (g_oaf_filter_enable) { + if (NF_DROP_BIT == (ct->mark & NF_DROP_BIT)) + drop = 1; + } + if (g_oaf_record_enable){ + AF_CLIENT_LOCK_W(); + af_update_client_app_info(client, app_id, drop); + AF_CLIENT_UNLOCK_W(); + } + + if (drop) + { + return NF_DROP; + } + } + else { + AF_LMT_DEBUG("ct->mark = %x\n", ct->mark); + if (ct->mark & NF_CLIENT_HELLO_BIT) { + AF_LMT_INFO("match ct client hello...\n"); + flow.client_hello = 1; + } + } + } + acct = nf_conn_acct_find(ct); + if (!acct) + return NF_ACCEPT; + total_packets = (unsigned long long)atomic64_read(&acct->counter[IP_CT_DIR_ORIGINAL].packets) + (unsigned long long)atomic64_read(&acct->counter[IP_CT_DIR_REPLY].packets); + + if (total_packets > MAX_DPI_PKT_NUM) + return NF_ACCEPT; + + if (skb_is_nonlinear(skb) && flow.l4_len < MAX_AF_SUPPORT_DATA_LEN) + { + flow.l4_data = read_skb(skb, flow.l4_data - skb->data, flow.l4_len); + if (!flow.l4_data) + return NF_ACCEPT; + malloc_data = 1; + } + dpi_main(skb, &flow); + + if (flow.client_hello) { + ct->mark |= NF_CLIENT_HELLO_BIT; + } + else { + ct->mark &= ~NF_CLIENT_HELLO_BIT; + } + + if (!match_feature(&flow)) + goto EXIT; + + + if (TEST_MODE()){ + if (flow.l4_protocol == IPPROTO_UDP){ + if (flow.dport == 53 || flow.dport == 443){ + printk(" %s %pI4(%d)--> %pI4(%d) len = %d, %d ,pkt num = %llu \n ", IPPROTO_TCP == flow.l4_protocol ? "tcp" : "udp", + &flow.src, flow.sport, &flow.dst, flow.dport, skb->len, flow.app_id, total_packets); + print_hex_ascii(flow.l4_data, flow.l4_len > 64 ? 64 : flow.l4_len); + } + } + } + ct->mark = (ct->mark & 0xFFFF0000) | (flow.app_id & 0xFFFF); + + + if (g_oaf_filter_enable){ + if (match_app_filter_rule(flow.app_id, client)) + { + ct->mark |= NF_DROP_BIT; + flow.drop = 1; + AF_LMT_INFO("##Drop app %s flow, appid is %d\n", flow.app_name, flow.app_id); + if (skb->protocol == htons(ETH_P_IP) && g_tcp_rst){ + #if LINUX_VERSION_CODE > KERNEL_VERSION(5,10,197) + nf_send_reset(&init_net, skb->sk, skb, NF_INET_PRE_ROUTING); + #elif LINUX_VERSION_CODE > KERNEL_VERSION(4,4,1) + nf_send_reset(&init_net, skb, NF_INET_PRE_ROUTING); + #else + nf_send_reset(skb, NF_INET_PRE_ROUTING); + #endif + } + ret = NF_DROP; + } + } + + + if (g_oaf_record_enable){ + AF_CLIENT_LOCK_W(); + af_update_client_app_info(client, flow.app_id, flow.drop); + AF_CLIENT_UNLOCK_W(); + AF_LMT_INFO("match %s %pI4(%d)--> %pI4(%d) len = %d, %d\n ", IPPROTO_TCP == flow.l4_protocol ? "tcp" : "udp", + &flow.src, flow.sport, &flow.dst, flow.dport, skb->len, flow.app_id); + } + +EXIT: + if (malloc_data) + { + if (flow.l4_data) + { + kfree(flow.l4_data); + } + } + + return ret; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) +static u_int32_t app_filter_hook(void *priv, + struct sk_buff *skb, + const struct nf_hook_state *state) +{ +#else +static u_int32_t app_filter_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ +#endif + + if (AF_MODE_BYPASS == af_work_mode) + return NF_ACCEPT; + return app_filter_hook_gateway_handle(skb, skb->dev); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) +static u_int32_t app_filter_by_pass_hook(void *priv, + struct sk_buff *skb, + const struct nf_hook_state *state) +{ +#else +static u_int32_t app_filter_by_pass_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ +#endif + if (AF_MODE_GATEWAY == af_work_mode) + return NF_ACCEPT; + return app_filter_hook_bypass_handle(skb, skb->dev); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) +static struct nf_hook_ops app_filter_ops[] __read_mostly = { + { + .hook = app_filter_hook, + .pf = NFPROTO_INET, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP_PRI_MANGLE + 1, + + }, + { + .hook = app_filter_by_pass_hook, + .pf = NFPROTO_INET, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP_PRI_MANGLE + 1, + }, +}; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) +static struct nf_hook_ops app_filter_ops[] __read_mostly = { + { + .hook = app_filter_hook, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP_PRI_MANGLE + 1, + }, + { + .hook = app_filter_by_pass_hook, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP_PRI_MANGLE + 1, + }, + { + .hook = app_filter_hook, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP_PRI_MANGLE + 1, + + }, + { + .hook = app_filter_by_pass_hook, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_PRE_ROUTING, + .priority = NF_IP_PRI_MANGLE + 1, + }, +}; +#else +static struct nf_hook_ops app_filter_ops[] __read_mostly = { + { + .hook = app_filter_hook, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP_PRI_MANGLE + 1, + }, + { + .hook = app_filter_hook, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP_PRI_MANGLE + 1, + }, +}; +#endif + +struct timer_list oaf_timer; +int report_flag = 0; +#define OAF_TIMER_INTERVAL 1 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) +static void oaf_timer_func(struct timer_list *t) +#else +static void oaf_timer_func(unsigned long ptr) +#endif +{ + static int count = 0; + if (count % 60 == 0) + check_client_expire(); + if (count % 60 == 0 || report_flag) + { + report_flag = 0; + af_visit_info_report(); + } + count++; + af_conn_clean_timeout(); + + mod_timer(&oaf_timer, jiffies + OAF_TIMER_INTERVAL * HZ); +} + +void init_oaf_timer(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + timer_setup(&oaf_timer, oaf_timer_func, 0); +#else + setup_timer(&oaf_timer, oaf_timer_func, OAF_TIMER_INTERVAL * HZ); +#endif + mod_timer(&oaf_timer, jiffies + OAF_TIMER_INTERVAL * HZ); + AF_INFO("init oaf timer...ok"); +} + +void fini_oaf_timer(void) +{ + del_timer_sync(&oaf_timer); + AF_INFO("del oaf timer...ok"); +} + +static struct sock *oaf_sock = NULL; + +#define OAF_EXTRA_MSG_BUF_LEN 128 +int af_send_msg_to_user(char *pbuf, uint16_t len) +{ + struct sk_buff *nl_skb; + struct nlmsghdr *nlh; + int buf_len = OAF_EXTRA_MSG_BUF_LEN + len; + char *msg_buf = NULL; + struct af_msg_hdr *hdr = NULL; + char *p_data = NULL; + int ret; + if (len >= MAX_OAF_NL_MSG_LEN) + return -1; + + msg_buf = kmalloc(buf_len, GFP_ATOMIC); + if (!msg_buf) + return -1; + + memset(msg_buf, 0x0, buf_len); + nl_skb = nlmsg_new(len + sizeof(struct af_msg_hdr), GFP_ATOMIC); + if (!nl_skb) + { + ret = -1; + goto fail; + } + + nlh = nlmsg_put(nl_skb, 0, 0, OAF_NETLINK_ID, len + sizeof(struct af_msg_hdr), 0); + if (nlh == NULL) + { + nlmsg_free(nl_skb); + ret = -1; + goto fail; + } + + hdr = (struct af_msg_hdr *)msg_buf; + hdr->magic = 0xa0b0c0d0; + hdr->len = len; + p_data = msg_buf + sizeof(struct af_msg_hdr); + memcpy(p_data, pbuf, len); + memcpy(nlmsg_data(nlh), msg_buf, len + sizeof(struct af_msg_hdr)); + ret = netlink_unicast(oaf_sock, nl_skb, 999, MSG_DONTWAIT); + +fail: + kfree(msg_buf); + return ret; +} + +static void oaf_user_msg_handle(char *data, int len) +{ + char *msg_data = data + sizeof(af_msg_t); + if (len < sizeof(af_msg_t)) + return; + af_msg_t *msg = (af_msg_t *)data; + AF_INFO("msg action = %d\n", msg->action); + switch (msg->action) + { + case AF_MSG_INIT: + af_client_list_reset_report_num(); + report_flag = 1; + break; + case AF_MSG_ADD_FEATURE: + af_add_feature_msg_handle(msg_data, len - sizeof(af_msg_t)); + break; + case AF_MSG_CLEAN_FEATURE: + AF_INFO("clean feature\n"); + af_clean_feature_list(); + break; + default: + break; + } +} +static void oaf_msg_rcv(struct sk_buff *skb) +{ + struct nlmsghdr *nlh = NULL; + char *umsg = NULL; + void *udata = NULL; + struct af_msg_hdr *af_hdr = NULL; + if (skb->len >= nlmsg_total_size(0)) + { + nlh = nlmsg_hdr(skb); + umsg = NLMSG_DATA(nlh); + af_hdr = (struct af_msg_hdr *)umsg; + if (af_hdr->magic != 0xa0b0c0d0) + return; + if (af_hdr->len <= 0 || af_hdr->len >= MAX_OAF_NETLINK_MSG_LEN) + return; + udata = umsg + sizeof(struct af_msg_hdr); + + if (udata) + oaf_user_msg_handle(udata, af_hdr->len); + } +} + +int netlink_oaf_init(void) +{ + struct netlink_kernel_cfg nl_cfg = {0}; + nl_cfg.input = oaf_msg_rcv; + oaf_sock = netlink_kernel_create(&init_net, OAF_NETLINK_ID, &nl_cfg); + + if (NULL == oaf_sock) + { + AF_ERROR("init oaf netlink failed, id=%d\n", OAF_NETLINK_ID); + return -1; + } + AF_INFO("init oaf netlink ok, id = %d\n", OAF_NETLINK_ID); + return 0; +} + +static int __init app_filter_init(void) +{ + int err; + af_conn_init(); + netlink_oaf_init(); + af_log_init(); + af_register_dev(); + af_mac_list_init(); + af_init_app_status(); + init_af_client_procfs(); + af_client_init(); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) + err = nf_register_net_hooks(&init_net, app_filter_ops, ARRAY_SIZE(app_filter_ops)); +#else + err = nf_register_hooks(app_filter_ops, ARRAY_SIZE(app_filter_ops)); +#endif + if (err) + { + AF_ERROR("oaf register filter hooks failed!\n"); + } + init_oaf_timer(); + AF_INFO("init app filter ........ok\n"); + return 0; +} + +static void app_filter_fini(void) +{ + AF_INFO("app filter module exit\n"); + fini_oaf_timer(); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) + nf_unregister_net_hooks(&init_net, app_filter_ops, ARRAY_SIZE(app_filter_ops)); +#else + nf_unregister_hooks(app_filter_ops, ARRAY_SIZE(app_filter_ops)); +#endif + finit_af_client_procfs(); + af_clean_feature_list(); + af_mac_list_clear(); + af_unregister_dev(); + af_log_exit(); + af_client_exit(); + if (oaf_sock) + netlink_kernel_release(oaf_sock); + af_conn_exit(); + return; +} + +module_init(app_filter_init); +module_exit(app_filter_fini); + diff --git a/oaf/src/app_filter.h b/oaf/src/app_filter.h new file mode 100755 index 0000000..7cc4d5a --- /dev/null +++ b/oaf/src/app_filter.h @@ -0,0 +1,183 @@ +#ifndef APP_FILTER_H +#define APP_FILTER_H + +#define AF_VERSION "5.1" +#define AF_FEATURE_CONFIG_FILE "/tmp/feature.cfg" + +#define MAX_DPI_PKT_NUM 64 +#define MIN_HTTP_DATA_LEN 16 +#define MAX_APP_NAME_LEN 64 +#define MAX_FEATURE_NUM_PER_APP 16 +#define MIN_FEATURE_STR_LEN 16 +#define MAX_FEATURE_STR_LEN 128 +#define MAX_HOST_URL_LEN 128 +#define MAX_REQUEST_URL_LEN 128 +#define MAX_FEATURE_BITS 16 +#define MAX_POS_INFO_PER_FEATURE 16 +#define MAX_FEATURE_LINE_LEN 600 +#define MIN_FEATURE_LINE_LEN 16 +#define MAX_URL_MATCH_LEN 64 +#define MAX_BYPASS_DPI_PKT_LEN 600 + +//#define CONFIG_KERNEL_FUNC_TEST 1 + +#define HTTP_GET_METHOD_STR "GET" +#define HTTP_POST_METHOD_STR "POST" +#define HTTP_HEADER "HTTP" +#define NIPQUAD(addr) \ + ((unsigned char *)&addr)[0], \ + ((unsigned char *)&addr)[1], \ + ((unsigned char *)&addr)[2], \ + ((unsigned char *)&addr)[3] +#define NIPQUAD_FMT "%u.%u.%u.%u" +#define MAC_ARRAY(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" + +#define AF_TRUE 1 +#define AF_FALSE 0 + +#define AF_APP_TYPE(a) (a) / 1000 +#define AF_APP_ID(a) (a) % 1000 +#define MAC_ADDR_LEN 6 + +#define HTTPS_URL_OFFSET 9 +#define HTTPS_LEN_OFFSET 7 + +#define MAX_SEARCH_STR_LEN 32 + +enum AF_FEATURE_PARAM_INDEX{ + AF_PROTO_PARAM_INDEX, + AF_SRC_PORT_PARAM_INDEX, + AF_DST_PORT_PARAM_INDEX, + AF_HOST_URL_PARAM_INDEX, + AF_REQUEST_URL_PARAM_INDEX, + AF_DICT_PARAM_INDEX, + AF_STR_PARAM_INDEX, + AF_IGNORE_PARAM_INDEX, +}; + + +#define OAF_NETLINK_ID 29 +#define MAX_OAF_NL_MSG_LEN 1024 + +enum E_MSG_TYPE{ + AF_MSG_INIT, + AF_MSG_ADD_FEATURE, + AF_MSG_CLEAN_FEATURE, + AF_MSG_MAX +}; +enum AF_WORK_MODE { + AF_MODE_GATEWAY, + AF_MODE_BYPASS, + AF_MODE_BRIDGE, +}; +#define MAX_AF_MSG_DATA_LEN 800 +typedef struct af_msg{ + int action; +}af_msg_t; + +struct af_msg_hdr{ + int magic; + int len; +}; + +enum e_http_method{ + HTTP_METHOD_GET = 1, + HTTP_METHOD_POST, +}; +typedef struct http_proto{ + int match; + int method; + char *url_pos; + int url_len; + char *host_pos; + int host_len; + char *data_pos; + int data_len; +}http_proto_t; + +typedef struct https_proto{ + int match; + char *url_pos; + int url_len; +}https_proto_t; + + + + +typedef struct af_pos_info{ + int pos; + unsigned char value; +}af_pos_info_t; + +#define MAX_PORT_RANGE_NUM 5 + +typedef struct range_value +{ + int not ; + int start; + int end; +} range_value_t; + +typedef struct port_info +{ + u_int8_t mode; // 0: match, 1: not match + int num; + range_value_t range_list[MAX_PORT_RANGE_NUM]; +} port_info_t; + +typedef struct af_feature_node{ + struct list_head head; + u_int32_t app_id; + char app_name[MAX_APP_NAME_LEN]; + char feature[MAX_FEATURE_STR_LEN]; + u_int32_t proto; + u_int32_t sport; + u_int32_t dport; + port_info_t dport_info; + char host_url[MAX_HOST_URL_LEN]; + char request_url[MAX_REQUEST_URL_LEN]; + int pos_num; + char search_str[MAX_SEARCH_STR_LEN]; + int ignore; + af_pos_info_t pos_info[MAX_POS_INFO_PER_FEATURE]; +}af_feature_node_t; + +typedef struct af_mac_info { + struct list_head hlist; + unsigned char mac[MAC_ADDR_LEN]; +}af_mac_info_t; + +typedef struct flow_info{ + struct nf_conn *ct; + u_int32_t src; + u_int32_t dst; + u_int8_t *src6; + u_int8_t *dst6; + int l4_protocol; + u_int16_t sport; + u_int16_t dport; + unsigned char *l4_data; + int l4_len; + http_proto_t http; + https_proto_t https; + u_int32_t app_id; + u_int8_t app_name[MAX_APP_NAME_LEN]; + u_int8_t drop; + u_int8_t dir; + u_int16_t total_len; + u_int8_t client_hello; + af_feature_node_t *feature; +}flow_info_t; + +int af_register_dev(void); +void af_unregister_dev(void); +void af_init_app_status(void); +int af_get_app_status(int appid); +int regexp_match(char *reg, char *text); +void af_mac_list_init(void); +void af_mac_list_clear(void); +af_mac_info_t * find_af_mac(unsigned char *mac); +int is_user_match_enable(void); + +#endif diff --git a/oaf/src/app_filter_config.c b/oaf/src/app_filter_config.c new file mode 100755 index 0000000..6ba133f --- /dev/null +++ b/oaf/src/app_filter_config.c @@ -0,0 +1,458 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cJSON.h" +#include "app_filter.h" +#include "af_utils.h" +#include "af_log.h" +#define AF_MAX_APP_TYPE_NUM 16 +#define AF_MAX_APP_NUM 256 +#define AF_DEV_NAME "appfilter" + +DEFINE_RWLOCK(af_rule_lock); + +#define af_rule_read_lock() read_lock_bh(&af_rule_lock); +#define af_rule_read_unlock() read_unlock_bh(&af_rule_lock); +#define af_rule_write_lock() write_lock_bh(&af_rule_lock); +#define af_rule_write_unlock() write_unlock_bh(&af_rule_lock); + +static struct mutex af_cdev_mutex; +struct af_config_dev +{ + dev_t id; + struct cdev char_dev; + struct class *c; +}; +struct af_config_dev g_af_dev; + +struct af_cdev_file +{ + size_t size; + char buf[256 << 10]; +}; + +enum AF_CONFIG_CMD +{ + AF_CMD_ADD_APPID = 1, + AF_CMD_DEL_APPID, + AF_CMD_CLEAN_APPID, + AF_CMD_SET_MAC_LIST, +}; + +char g_app_id_array[AF_MAX_APP_TYPE_NUM][AF_MAX_APP_NUM] = {0}; + +void af_show_app_status(void) +{ + int i, j; + AF_DEBUG("#########show app status##########\n"); + for (i = 0; i < AF_MAX_APP_TYPE_NUM; i++) + { + for (j = 0; j < AF_MAX_APP_NUM; j++) + { + + af_rule_read_lock(); + if (g_app_id_array[i][j] == AF_TRUE) + { + AF_DEBUG("%d, %d\n", i, j); + } + af_rule_read_unlock(); + } + } + + AF_DEBUG("\n\n\n"); +} + +int af_change_app_status(cJSON *data_obj, int status) +{ + int i; + int id; + int type; + cJSON *appid_arr = NULL; + if (!data_obj) + { + AF_ERROR("data obj is null\n"); + return -1; + } + appid_arr = cJSON_GetObjectItem(data_obj, "apps"); + if (!appid_arr) + { + AF_ERROR("apps obj is null\n"); + return -1; + } + for (i = 0; i < cJSON_GetArraySize(appid_arr); i++) + { + cJSON *appid_obj = cJSON_GetArrayItem(appid_arr, i); + if (!appid_obj) + return -1; + id = AF_APP_ID(appid_obj->valueint); + type = AF_APP_TYPE(appid_obj->valueint); + af_rule_write_lock(); + g_app_id_array[type][id] = status; + af_rule_write_unlock(); + } + + return 0; +} +DEFINE_RWLOCK(af_mac_lock); +#define MAX_AF_MAC_HASH_SIZE 64 +#define AF_MAC_LOCK_R() read_lock_bh(&af_mac_lock); +#define AF_MAC_UNLOCK_R() read_unlock_bh(&af_mac_lock); +#define AF_MAC_LOCK_W() write_lock_bh(&af_mac_lock); +#define AF_MAC_UNLOCK_W() write_unlock_bh(&af_mac_lock); + +u32 total_mac = 0; +struct list_head af_mac_list_table[MAX_AF_MAC_HASH_SIZE]; + +void af_mac_list_init(void) +{ + int i; + AF_MAC_LOCK_W(); + for (i = 0; i < MAX_AF_MAC_HASH_SIZE; i++) + { + INIT_LIST_HEAD(&af_mac_list_table[i]); + } + AF_MAC_UNLOCK_W(); + AF_INFO("client list init......ok\n"); +} + +void af_mac_list_clear(void) +{ + int i; + af_mac_info_t *p = NULL; + char mac_str[32] = {0}; + + AF_DEBUG("clean list\n"); + AF_MAC_LOCK_W(); + for (i = 0; i < MAX_AF_MAC_HASH_SIZE; i++) + { + while (!list_empty(&af_mac_list_table[i])) + { + p = list_first_entry(&af_mac_list_table[i], af_mac_info_t, hlist); + memset(mac_str, 0x0, sizeof(mac_str)); + sprintf(mac_str, MAC_FMT, MAC_ARRAY(p->mac)); + AF_DEBUG("clean mac:%s\n", mac_str); + list_del(&(p->hlist)); + kfree(p); + } + } + total_mac = 0; + AF_MAC_UNLOCK_W(); +} + +int hash_mac(unsigned char *mac) +{ + if (!mac) + return 0; + return ((mac[0] ^ mac[1]) + (mac[2] ^ mac[3]) + (mac[4] ^ mac[5])) % MAX_AF_MAC_HASH_SIZE; +} + +af_mac_info_t *find_af_mac(unsigned char *mac) +{ + af_mac_info_t *node; + unsigned int index; + + index = hash_mac(mac); + list_for_each_entry(node, &af_mac_list_table[index], hlist) + { + if (0 == memcmp(node->mac, mac, 6)) + { + AF_DEBUG("match mac:" MAC_FMT "\n", MAC_ARRAY(node->mac)); + return node; + } + } + return NULL; +} + +static af_mac_info_t * +af_mac_add(unsigned char *mac) +{ + af_mac_info_t *node; + int index = 0; + + node = (af_mac_info_t *)kmalloc(sizeof(af_mac_info_t), GFP_ATOMIC); + if (node == NULL) + { + AF_ERROR("kmalloc failed\n"); + return NULL; + } + + memset(node, 0, sizeof(af_mac_info_t)); + memcpy(node->mac, mac, MAC_ADDR_LEN); + + index = hash_mac(mac); + + AF_LMT_INFO("new client mac=" MAC_FMT "\n", MAC_ARRAY(node->mac)); + total_mac++; + list_add(&(node->hlist), &af_mac_list_table[index]); + return node; +} + +int is_user_match_enable(void) +{ + return total_mac > 0; +} +int mac_to_hex(u8 *mac, u8 *mac_hex) +{ + u32 mac_tmp[MAC_ADDR_LEN]; + int ret = 0, i = 0; + ret = sscanf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", + (unsigned int *)&mac_tmp[0], + (unsigned int *)&mac_tmp[1], + (unsigned int *)&mac_tmp[2], + (unsigned int *)&mac_tmp[3], + (unsigned int *)&mac_tmp[4], + (unsigned int *)&mac_tmp[5]); + if (MAC_ADDR_LEN != ret) + return -1; + for (i = 0; i < MAC_ADDR_LEN; i++) + { + mac_hex[i] = mac_tmp[i]; + } + return 0; +} +int af_set_mac_list(cJSON *data_obj) +{ + int i; + cJSON *mac_arr = NULL; + u8 mac_hex[MAC_ADDR_LEN] = {0}; + if (!data_obj) + { + AF_ERROR("data obj is null\n"); + return -1; + } + mac_arr = cJSON_GetObjectItem(data_obj, "mac_list"); + if (!mac_arr) + { + AF_ERROR("apps obj is null\n"); + return -1; + } + af_mac_list_clear(); + for (i = 0; i < cJSON_GetArraySize(mac_arr); i++) + { + cJSON *mac_obj = cJSON_GetArrayItem(mac_arr, i); + if (!mac_obj) + { + AF_ERROR("appid obj is null\n"); + return -1; + } + if (-1 == mac_to_hex(mac_obj->valuestring, mac_hex)) + { + continue; + } + af_mac_add(mac_hex); + } + AF_DEBUG("## mac num = %d\n", total_mac); + return 0; +} + +void af_init_app_status(void) +{ + int i, j; + + for (i = 0; i < AF_MAX_APP_TYPE_NUM; i++) + { + for (j = 0; j < AF_MAX_APP_NUM; j++) + { + af_rule_write_lock(); + g_app_id_array[i][j] = AF_FALSE; + af_rule_write_unlock(); + } + } +} +int af_get_app_status(int appid) +{ + int status = 0; + int id = AF_APP_ID(appid); + int type = AF_APP_TYPE(appid); + af_rule_read_lock(); + status = g_app_id_array[type][id]; + af_rule_read_unlock(); + return status; +} +/* +add: +{ + "op":1, + "data"{ + "apps":[] + } +} +clean +{ + "op":3, +} + +*/ +int af_config_handle(char *config, unsigned int len) +{ + cJSON *config_obj = NULL; + cJSON *cmd_obj = NULL; + cJSON *data_obj = NULL; + if (!config || len == 0) + { + AF_ERROR("config or len is invalid\n"); + return -1; + } + config_obj = cJSON_Parse(config); + if (!config_obj) + { + AF_ERROR("config_obj is NULL\n"); + return -1; + } + cmd_obj = cJSON_GetObjectItem(config_obj, "op"); + if (!cmd_obj) + { + AF_ERROR("not find op object\n"); + return -1; + } + data_obj = cJSON_GetObjectItem(config_obj, "data"); + + switch (cmd_obj->valueint) + { + case AF_CMD_ADD_APPID: + if (!data_obj) + break; + af_change_app_status(data_obj, AF_TRUE); + break; + case AF_CMD_DEL_APPID: + if (!data_obj) + break; + af_change_app_status(data_obj, AF_FALSE); + break; + case AF_CMD_CLEAN_APPID: + af_init_app_status(); + break; + case AF_CMD_SET_MAC_LIST: + af_set_mac_list(data_obj); + break; + default: + AF_ERROR("invalid cmd %d\n", cmd_obj->valueint); + return -1; + } + af_show_app_status(); + return 0; +} + +static int af_cdev_open(struct inode *inode, struct file *filp) +{ + struct af_cdev_file *file; + file = vzalloc(sizeof(*file)); + if (!file) + return -EINVAL; + + mutex_lock(&af_cdev_mutex); + filp->private_data = file; + return 0; +} + +static ssize_t af_cdev_read(struct file *filp, char *buf, size_t count, loff_t *off) +{ + return 0; +} + +static int af_cdev_release(struct inode *inode, struct file *filp) +{ + struct af_cdev_file *file = filp->private_data; + AF_DEBUG("config size: %d,data = %s\n", (int)file->size, file->buf); + af_config_handle(file->buf, file->size); + filp->private_data = NULL; + mutex_unlock(&af_cdev_mutex); + vfree(file); + return 0; +} + +static ssize_t af_cdev_write(struct file *filp, const char *buffer, size_t count, loff_t *off) +{ + struct af_cdev_file *file = filp->private_data; + int ret; + if (file->size + count > sizeof(file->buf)) + { + AF_ERROR("config overflow, cur_size: %d, block_size: %d, max_size: %d", + (int)file->size, (int)count, (int)sizeof(file->buf)); + return -EINVAL; + } + + ret = copy_from_user(file->buf + file->size, buffer, count); + if (ret != 0) + return -EINVAL; + + file->size += count; + return count; +} + +static struct file_operations af_cdev_ops = { + owner : THIS_MODULE, + release : af_cdev_release, + open : af_cdev_open, + write : af_cdev_write, + read : af_cdev_read, +}; + +int af_register_dev(void) +{ + struct device *dev; + int res; + mutex_init(&af_cdev_mutex); + + res = alloc_chrdev_region(&g_af_dev.id, 0, 1, AF_DEV_NAME); + if (res != 0) + { + return -EINVAL; + } + + cdev_init(&g_af_dev.char_dev, &af_cdev_ops); + res = cdev_add(&g_af_dev.char_dev, g_af_dev.id, 1); + if (res < 0) + { + goto REGION_OUT; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) + g_af_dev.c = class_create(THIS_MODULE, AF_DEV_NAME); +#else + g_af_dev.c = class_create(AF_DEV_NAME); +#endif + if (IS_ERR_OR_NULL(g_af_dev.c)) + { + goto CDEV_OUT; + } + + dev = device_create(g_af_dev.c, NULL, g_af_dev.id, NULL, AF_DEV_NAME); + if (IS_ERR_OR_NULL(dev)) + { + goto CLASS_OUT; + } + AF_INFO("register char dev....ok\n"); + + return 0; + +CLASS_OUT: + class_destroy(g_af_dev.c); +CDEV_OUT: + cdev_del(&g_af_dev.char_dev); +REGION_OUT: + unregister_chrdev_region(g_af_dev.id, 1); + + AF_ERROR("register char dev....fail\n"); + return -EINVAL; +} + +void af_unregister_dev(void) +{ + device_destroy(g_af_dev.c, g_af_dev.id); + class_destroy(g_af_dev.c); + cdev_del(&g_af_dev.char_dev); + unregister_chrdev_region(g_af_dev.id, 1); + AF_INFO("unregister char dev....ok\n"); +} diff --git a/oaf/src/cJSON.c b/oaf/src/cJSON.c new file mode 100755 index 0000000..7dcb2e5 --- /dev/null +++ b/oaf/src/cJSON.c @@ -0,0 +1,519 @@ +/* + Copyright (c) 2009 Dave Gamble + + 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. +*/ + +// cJSON +// JSON parser in C. + +#if 0 +#include +#include +#include +#include +#include +#endif + +#include "cJSON.h" + +#include +#include + +#if 0 +#if defined(WINDOWS) || defined(__WIN32__) || defined(WIN32) || defined(_WIN32) +#define strcasecmp stricmp +#define strdup _strdup +#endif +#endif + + +#if 0 +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void *(*cJSON_realloc)(void *ptr, size_t sz) = realloc; +static void (*cJSON_free)(void *ptr) = free; +#endif + +static void *cJSON_malloc(size_t sz) { + return kmalloc(sz, GFP_KERNEL); +} + +static void *cJSON_realloc(void *ptr, size_t sz) +{ + return krealloc(ptr, sz, GFP_KERNEL); +} + +static void cJSON_free(void *ptr) +{ + kfree(ptr); +} + +static char* cJSON_strdup(const char* str) +{ + size_t len; + char* copy; + + len = strlen(str) + 1; + if (!(copy = (char*)cJSON_malloc(len))) return 0; + memcpy(copy,str,len); + return copy; +} + +#if 0 +void cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (!hooks) { /* Reset hooks */ + cJSON_malloc = malloc; + cJSON_realloc = realloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; + cJSON_realloc= (hooks->realloc_fn)?hooks->realloc_fn:realloc; + cJSON_free = (hooks->free_fn)?hooks->free_fn:free; +} +#endif + +// Internal constructor. +static cJSON *cJSON_New_Item(void) +{ + cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); + if (node) memset(node,0,sizeof(cJSON)); + return node; +} + +// Delete a cJSON structure. +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next=c->next; + if (c->child) cJSON_Delete(c->child); + if (c->valuestring) cJSON_free(c->valuestring); + if (c->string) cJSON_free(c->string); + cJSON_free(c); + c=next; + } +} + + +/* Parse the input text to generate a number, and populate the result into item. */ +static const char *parse_number(cJSON *item,const char *num) +{ + int n=0,sign=1; + + if (*num=='-') sign=-1,num++; /* Has sign? */ + if (*num=='0') num++; /* is zero */ + if (*num>='1' && *num<='9') do n=(n*10)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ + item->valueint=(int)n; + item->type=cJSON_Number; + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char *print_number(cJSON *item) +{ + char *str; + str=(char*)cJSON_malloc(21); + if (str) + sprintf(str,"%d",item->valueint); + return str; +} + + +// Parse the input text into an unescaped cstring, and populate item. +static const char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char *parse_string(cJSON *item,const char *str) +{ + const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc; + if (*str!='\"') return 0; // not a string! + + while (*ptr!='\"' && *ptr>31 && ++len) if (*ptr++ == '\\') ptr++; // Skip escaped quotes. + + out=(char*)cJSON_malloc(len+1); // This is how long we need for the string, roughly. + if (!out) return 0; + + ptr=str+1;ptr2=out; + while (*ptr!='\"' && *ptr>31) + { + if (*ptr!='\\') *ptr2++=*ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': *ptr2++='\b'; break; + case 'f': *ptr2++='\f'; break; + case 'n': *ptr2++='\n'; break; + case 'r': *ptr2++='\r'; break; + case 't': *ptr2++='\t'; break; + case 'u': // transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY. + sscanf(ptr+1,"%4x",&uc); // get the unicode char. + len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len; + + switch (len) { + case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; + case 1: *--ptr2 =(uc | firstByteMark[len]); + } + ptr2+=len;ptr+=4; + break; + default: *ptr2++=*ptr; break; + } + ptr++; + } + } + *ptr2=0; + if (*ptr=='\"') ptr++; + item->valuestring=out; + item->type=cJSON_String; + return ptr; +} + +// Render the cstring provided to an escaped version that can be printed. +static char *print_string_ptr(const char *str) +{ + const char *ptr;char *ptr2,*out;int len=0; + + ptr=str;while (*ptr && ++len) {if (*ptr<32 || *ptr=='\"' || *ptr=='\\') len++;ptr++;} + + out=(char*)cJSON_malloc(len+3); + ptr2=out;ptr=str; + *ptr2++='\"'; + while (*ptr) + { + if (*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; + else + { + *ptr2++='\\'; + switch (*ptr++) + { + case '\\': *ptr2++='\\'; break; + case '\"': *ptr2++='\"'; break; + case '\b': *ptr2++='b'; break; + case '\f': *ptr2++='f'; break; + case '\n': *ptr2++='n'; break; + case '\r': *ptr2++='r'; break; + case '\t': *ptr2++='t'; break; + default: ptr2--; break; // eviscerate with prejudice. + } + } + } + *ptr2++='\"';*ptr2++=0; + return out; +} +// Invote print_string_ptr (which is useful) on an item. +static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} + +// Predeclare these prototypes. +static const char *parse_value(cJSON *item,const char *value); +static char *print_value(cJSON *item,int depth); +static const char *parse_array(cJSON *item,const char *value); +static char *print_array(cJSON *item,int depth); +static const char *parse_object(cJSON *item,const char *value); +static char *print_object(cJSON *item,int depth); + +// Utility to jump whitespace and cr/lf +static const char *skip(const char *in) {while (in && *in<=32) in++; return in;} + +// Parse an object - create a new root, and populate. +cJSON *cJSON_Parse(const char *value) +{ + cJSON *c=cJSON_New_Item(); + if (!c) return 0; /* memory fail */ + + if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;} + return c; +} + +// Render a cJSON item/entity/structure to text. +char *cJSON_Print(cJSON *item) {return print_value(item,0);} + +// Parser core - when encountering text, process appropriately. +static const char *parse_value(cJSON *item,const char *value) +{ + if (!value) return 0; // Fail on null. + if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } + if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } + if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } + if (*value=='\"') { return parse_string(item,value); } + if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } + if (*value=='[') { return parse_array(item,value); } + if (*value=='{') { return parse_object(item,value); } + + return 0; // failure. +} + +// Render a value to text. +static char *print_value(cJSON *item,int depth) +{ + char *out=0; + switch (item->type) + { + case cJSON_NULL: out=cJSON_strdup("null"); break; + case cJSON_False: out=cJSON_strdup("false");break; + case cJSON_True: out=cJSON_strdup("true"); break; + case cJSON_Number: out=print_number(item);break; + case cJSON_String: out=print_string(item);break; + case cJSON_Array: out=print_array(item,depth);break; + case cJSON_Object: out=print_object(item,depth);break; + } + return out; +} + +// Build an array from input text. +static const char *parse_array(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='[') return 0; // not an array! + + item->type=cJSON_Array; + value=skip(value+1); + if (*value==']') return value+1; // empty array. + + item->child=child=cJSON_New_Item(); + if (!item->child) return 0; // memory fail + value=skip(parse_value(child,skip(value))); // skip any spacing, get the value. + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; // memory fail + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_value(child,skip(value+1))); + if (!value) return 0; // memory fail + } + + if (*value==']') return value+1; // end of array + return 0; // malformed. +} + +// Render an array to text +static char *print_array(cJSON *item,int depth) +{ + char *out,*ptr,*ret;int len=5; + cJSON *child=item->child; + + out=(char*)cJSON_malloc(len);*out='['; + ptr=out+1;*ptr=0; + while (child) + { + ret=print_value(child,depth+1); + if (!ret) {cJSON_free(out);return 0;} // Check for failure! + len+=strlen(ret)+3; + out=(char*)cJSON_realloc(out,len); + ptr=out+strlen(out); + ptr+=sprintf(ptr,ret); + if (child->next) {*ptr++=',';*ptr++=' ';*ptr=0;} + child=child->next; + cJSON_free(ret); + } + *ptr++=']';*ptr++=0; + return out; +} + +// Build an object from the text. +static const char *parse_object(cJSON *item,const char *value) +{ + cJSON *child; + if (*value!='{') return 0; // not an object! + + item->type=cJSON_Object; + value=skip(value+1); + if (*value=='}') return value+1; // empty array. + + item->child=child=cJSON_New_Item(); + value=skip(parse_string(child,skip(value))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') return 0; // fail! + value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value. + if (!value) return 0; + + while (*value==',') + { + cJSON *new_item; + if (!(new_item=cJSON_New_Item())) return 0; // memory fail + child->next=new_item;new_item->prev=child;child=new_item; + value=skip(parse_string(child,skip(value+1))); + if (!value) return 0; + child->string=child->valuestring;child->valuestring=0; + if (*value!=':') return 0; // fail! + value=skip(parse_value(child,skip(value+1))); // skip any spacing, get the value. + if (!value) return 0; + } + + if (*value=='}') return value+1; // end of array + return 0; // malformed. +} + +// Render an object to text. +static char *print_object(cJSON *item,int depth) +{ + char *out,*ptr,*ret,*str;int len=7,i; + cJSON *child=item->child; + + depth++;len+=depth;out=(char*)cJSON_malloc(len);*out='{'; + ptr=out+1;*ptr++='\n';*ptr=0; + while (child) + { + str=print_string_ptr(child->string); + if (!str) {cJSON_free(out);return 0;} + ret=print_value(child,depth); + if (!ret) {cJSON_free(str);cJSON_free(out);return 0;} // Check for failure! + len+=strlen(ret)+strlen(str)+4+depth; + out=(char*)cJSON_realloc(out,len); + ptr=out+strlen(out); + for (i=0;inext) *ptr++=','; + *ptr++='\n';*ptr=0; + child=child->next; + cJSON_free(str);cJSON_free(ret); + } + for (i=0;ichild;int i=0;while(c)i++,c=c->next;return i;} +cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item) item--,c=c->next; return c;} +cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && strcasecmp(c->string,string)) c=c->next; return c;} + +// Utility for array list handling. +static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} + +// Add item to array/object. +void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} +void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} + +// Create basic types: +cJSON *cJSON_CreateNull() {cJSON *item=cJSON_New_Item();item->type=cJSON_NULL;return item;} +cJSON *cJSON_CreateTrue() {cJSON *item=cJSON_New_Item();item->type=cJSON_True;return item;} +cJSON *cJSON_CreateFalse() {cJSON *item=cJSON_New_Item();item->type=cJSON_False;return item;} +cJSON *cJSON_CreateNumber(int num) {cJSON *item=cJSON_New_Item();item->type=cJSON_Number;item->valueint=(int)num;return item;} +cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();item->type=cJSON_String;item->valuestring=cJSON_strdup(string);return item;} +cJSON *cJSON_CreateArray() {cJSON *item=cJSON_New_Item();item->type=cJSON_Array;return item;} +cJSON *cJSON_CreateObject() {cJSON *item=cJSON_New_Item();item->type=cJSON_Object;return item;} + +// Create Arrays: +cJSON *cJSON_CreateIntArray(int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;ichild=n;else suffix_object(p,n);p=n;}return a;} +cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;ichild=n;else suffix_object(p,n);p=n;}return a;} diff --git a/oaf/src/cJSON.h b/oaf/src/cJSON.h new file mode 100755 index 0000000..97b701c --- /dev/null +++ b/oaf/src/cJSON.h @@ -0,0 +1,94 @@ +/* + Copyright (c) 2009 Dave Gamble + + 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. +*/ + +#ifndef cJSON__h +#define cJSON__h +#include + +// cJSON Types: +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +// The cJSON structure: +typedef struct cJSON { + struct cJSON *next,*prev; // next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem + struct cJSON *child; // An array or object item will have a child pointer pointing to a chain of the items in the array/object. + + int type; // The type of the item, as above. + + char *valuestring; // The item's string, if type==cJSON_String + int valueint; // The item's number, if type==cJSON_Number + char *string; // The item's name string, if this item is the child of, or is in the list of subitems of an object. +} cJSON; + +typedef struct cJSON_Hooks { + void *(*malloc_fn)(size_t sz); + void *(*realloc_fn)(void *ptr, size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +// Supply malloc, realloc and free functions to cJSON +extern void cJSON_InitHooks(cJSON_Hooks* hooks); + + +// Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. +extern cJSON *cJSON_Parse(const char *value); +// Render a cJSON entity to text for transfer/storage. Free the char* when finished. +extern char *cJSON_Print(cJSON *item); +// Delete a cJSON entity and all subentities. +extern void cJSON_Delete(cJSON *c); + +// Returns the number of items in an array (or object). +extern int cJSON_GetArraySize(cJSON *array); +// Retrieve item number "item" from array "array". Returns NULL if unsuccessful. +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); +// Get item "string" from object. Case insensitive. +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); + +// These calls create a cJSON item of the appropriate type. +extern cJSON *cJSON_CreateNull(void); +extern cJSON *cJSON_CreateTrue(void); +extern cJSON *cJSON_CreateFalse(void); +extern cJSON *cJSON_CreateNumber(int num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateArray(void); +extern cJSON *cJSON_CreateObject(void); +extern void cJSON_Minify(char *json); +// These utilities create an Array of count items. +extern cJSON *cJSON_CreateIntArray(int *numbers,int count); + +// Append item to the specified array/object. +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item); + +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +#endif diff --git a/oaf/src/regexp.c b/oaf/src/regexp.c new file mode 100755 index 0000000..6d42d53 --- /dev/null +++ b/oaf/src/regexp.c @@ -0,0 +1,302 @@ +#include +#include +#include +#include +#include +//#include "regexp.h" + +typedef enum{CHAR, DOT, BEGIN, END, STAR, PLUS, QUES, LIST, TYPENUM}TYPE; + +typedef struct RE{ + TYPE type; + int ch; + char *ccl; + int nccl; + struct RE *next; +}RE; + +int match_longest = 0; +char *match_first = NULL; + + +static void * getmem(size_t size) +{ + void *tmp; + if((tmp = kmalloc(size, GFP_ATOMIC))==NULL) + { + printk("malloc failed"); + return NULL; + } + return tmp; +} + +static size_t creat_list(char *str, int start, int end) +{ + size_t cnt = end - start + 1; + for(; start <= end ;start++) + *str++ = start; + return (cnt > 0)?cnt:0; +} + +static int in_list(char ch, RE *regexp) +{ + char *str = regexp->ccl; + if(regexp->type != LIST) + return 0; + for(; *str && ch != *str; str++) + ; + return (*str != '\0') ^ regexp->nccl; +} + +static void regexp_free(RE *regexp) +{ + RE *tmp; + for(; regexp; regexp = tmp) + { + tmp = regexp->next; + kfree(regexp); + } +} + +static RE* compile(char *regexp) +{ + RE head, *tail, *tmp; + char *pstr; + int err_flag = 0; + + for(tail = &head; *regexp != '\0' && err_flag == 0; regexp++) + { + tmp = getmem(sizeof(RE)); + switch(*regexp){ + case '\\': + regexp++; + if(*regexp == 'd') + { + tmp->type = LIST; + tmp->nccl = 0; + tmp->ccl = getmem(11); + creat_list(tmp->ccl, '0','9'); + tmp->ccl[11] = '\0'; + }else if(*regexp == 'D') + { + tmp->type = LIST; + tmp->nccl = 1; + tmp->ccl = getmem(11); + creat_list(tmp->ccl, '0','9'); + tmp->ccl[11] = '\0'; + }else + { + tmp->type = CHAR; + tmp->ch = *regexp; + } + break; + case '.': + tmp->type = DOT; + break; + case '^': + tmp->type = BEGIN; + tmp->ch = '^'; + break; + case '$': + tmp->type = END; + tmp->ch = '$'; + break; + case '*': + tmp->type = STAR; + break; + case '+': + tmp->type = PLUS; + break; + case '?': + tmp->type = QUES; + break; + case '[': + pstr = tmp->ccl = getmem(256); + tmp->nccl = 0; + if(*++regexp == '^') + { + tmp->nccl = 1; + regexp++; + } + while(*regexp != '\0' && *regexp != ']') + { + if(*regexp != '-') + { + *pstr++ = *regexp++; + continue; + } + if(pstr == tmp->ccl || *(regexp + 1) == ']') + { + err_flag = 1; + break; + } + pstr += creat_list(pstr, *(regexp - 1) + 1, *(regexp + 1)); + regexp += 2; + } + *pstr = '\0'; + if(*regexp == '\0') + err_flag = 1; + tmp->type = LIST; + break; + default: + tmp->type = CHAR; + tmp->ch = *regexp; + } + + tail->next = tmp; + tail = tmp; + } + + tail->next = NULL; + if(err_flag) + { + regexp_free(head.next); + return NULL; + } + return head.next; +} + +#define MATCH_ONE(reg, text) \ + (reg->type == DOT || in_list(*text, reg) || *text == reg->ch) +#define MATCH_ONE_P(reg, text) \ + (in_list(*text++, reg) || *(text - 1) == reg->ch || reg->type == DOT) + +static int matchhere(RE *regexp, char *text); + +static int matchstar(RE *cur, RE *regexp, char *text) +{ + do{ + if(matchhere(regexp, text)) + return 1; + }while(*text != '\0' && MATCH_ONE_P(cur, text)); + return 0; +} + +static int matchstar_l(RE *cur, RE *regexp, char *text) +{ + char *t; + for(t = text; *t != '\0' && MATCH_ONE(cur, t); t++) + ; + do{ + if(matchhere(regexp, t)) + return 1; + }while(t-- > text); + return 0; +} + +static int matchplus(RE *cur, RE *regexp, char *text) +{ + while(*text != '\0' && MATCH_ONE_P(cur, text)) + { + if(matchhere(regexp, text)) + return 1; + } + return 0; +} + +static int matchplus_l(RE *cur, RE *regexp, char *text) +{ + char *t; + for(t = text; *t != '\0' && MATCH_ONE(cur, t); t++) + ; + for(; t > text; t--) + { + if(matchhere(regexp, t)) + return 1; + } + return 0; +} + +static int matchques(RE *cur, RE *regexp, char *text) +{ + int cnt = 1; + char *t = text; + if(*t != '\0' && cnt-- && MATCH_ONE(cur, t)) + t++; + do{ + if(matchhere(regexp, t)) + return 1; + }while(t-- > text); + return 0; +} + +static int (*matchfun[TYPENUM][2])(RE *, RE *, char *) = { + 0, 0, 0, 0, 0, 0, 0, 0, + matchstar, matchstar_l, + matchplus, matchplus_l, + matchques, matchques, +}; + +static int matchhere(RE *regexp, char *text) +{ + if(regexp == NULL) + return 1; + if(regexp->type == END && regexp->next == NULL) + return *text == '\0'; + if(regexp->next && matchfun[regexp->next->type][match_longest]) + return matchfun[regexp->next->type][match_longest](regexp, regexp->next->next, text); + + if(*text != '\0' && MATCH_ONE(regexp, text)) + return matchhere(regexp->next, text + 1); + return 0; +} + +/* + * return value: + * -1 error + * 0 not match + * 1 matched + */ +int regexp_match(char *reg, char *text) +{ + int ret; + RE *regexp = compile(reg); + if(regexp == NULL) + return -1; + + if(regexp->type == BEGIN) + { + ret = matchhere(regexp->next, text); + goto out; + } + + do{ + if(ret = matchhere(regexp, text)) + { + goto out; + } + }while(*text++ != '\0'); + +out: + regexp_free(regexp); + return ret; +} + + +void TEST_reg_func(char *reg, char * str, int ret) +{ + + if (ret != regexp_match(reg, str)) { + if (reg) + printk("reg = %s,", reg); + else + printk("reg = null"); + if (str) + printk("str = %s ", str); + else + printk("str= null"); + printk("error, unit test.... failed, ret = %d\n",ret); + } + else { + if (reg && str) + printk("[unit test] %s %s......ok,ret = %d\n", reg, str, ret); + } +} + +void TEST_regexp(void) +{ + TEST_reg_func(".*baidu.com$", "www.baidu.com", 1); + TEST_reg_func("^sina.com", "www.sina.com.cn", 0); + TEST_reg_func("^sina.com", "sina.com.cn", 1); + TEST_reg_func(".*baidu.com$", "www.baidu.com223", 0); +} diff --git a/open-app-filter/Makefile b/open-app-filter/Makefile new file mode 100755 index 0000000..e31058c --- /dev/null +++ b/open-app-filter/Makefile @@ -0,0 +1,58 @@ + +include $(TOPDIR)/rules.mk + +PKG_NAME:=appfilter +PKG_VERSION:=6.1.3 +PKG_RELEASE:=1 + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) +include $(INCLUDE_DIR)/package.mk + +TARGET_CFLAGS +=-Werror=implicit-function-declaration +define Package/appfilter + SECTION:=TT Apps + CATEGORY:=TT Apps + DEPENDS:=+libubox +libubus +libuci +libpthread +libjson-c +libblobmsg-json + TITLE:=App filter userspace module +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + cp -rf ./src/* $(PKG_BUILD_DIR) +endef + +define Build/Compile + $(MAKE) -C $(PKG_BUILD_DIR)/ \ + CC="$(TARGET_CROSS)gcc" \ + CFLAGS="$(TARGET_CFLAGS)" \ + LIBS="$(TARGET_LDFLAGS) -lm -lpthread -lubox -luci -lubus -ljson-c -lblobmsg_json" \ + all +endef + + +define Build/Compile/Default + +endef + +define Package/appfilter/description + openappfilter app +endef + + +define Package/appfilter/install + $(INSTALL_DIR) $(1)/usr/bin $(1)/etc/init.d + $(INSTALL_DIR) $(1)/etc/appfilter + $(INSTALL_DIR) $(1)/etc/config + $(CP) ./files/*.cfg $(1)/etc/appfilter/ + $(INSTALL_BIN) ./files/appfilter.init $(1)/etc/init.d/appfilter + $(INSTALL_BIN) ./files/oaf_rule $(1)/usr/bin + $(INSTALL_BIN) ./files/gen_class.sh $(1)/usr/bin + $(INSTALL_BIN) ./files/appfilter.config $(1)/etc/config/appfilter + $(INSTALL_BIN) ./files/user_info.config $(1)/etc/config/user_info + $(INSTALL_BIN) $(PKG_BUILD_DIR)/oafd $(1)/usr/bin + $(INSTALL_BIN) ./files/hnat.sh $(1)/usr/bin +endef + + +$(eval $(call BuildPackage,appfilter)) + diff --git a/open-app-filter/files/appfilter.config b/open-app-filter/files/appfilter.config new file mode 100755 index 0000000..efbca3a --- /dev/null +++ b/open-app-filter/files/appfilter.config @@ -0,0 +1,28 @@ +config global global + option enable '0' + option work_mode '0' + option record_enable '1' + option disable_hnat '1' + option tcp_rst '1' + option lan_ifname 'br-lan' + option auto_load_engine '1' + +config appfilter appfilter + +config feature feature + option update 0 + option format 'v3.0' + +config time 'time' + option deny_time '60' + option start_time '00:00' + option end_time '23:00' + option allow_time '20' + option time_mode '0' + option days '1 2 3 4 5 6 0' + list time '00:00-23:59' + +config user user + + +config rule 'rule' \ No newline at end of file diff --git a/open-app-filter/files/appfilter.init b/open-app-filter/files/appfilter.init new file mode 100755 index 0000000..81f55c8 --- /dev/null +++ b/open-app-filter/files/appfilter.init @@ -0,0 +1,24 @@ +#!/bin/sh /etc/rc.common +. /usr/share/libubox/jshn.sh +. /lib/functions.sh + +START=96 +USE_PROCD=1 +OAFD_BIN="/usr/bin/oafd" +FEATURE_FILE="/tmp/feature.cfg" + +stop_service(){ + killall -9 oafd +} + +start_service(){ + test -f $FEATURE_FILE &&{ + rm $FEATURE_FILE + } + ln -s /etc/appfilter/feature.cfg $FEATURE_FILE + procd_open_instance + procd_set_param respawn 60 5 5 + procd_set_param stderr 1 + procd_set_param command "$OAFD_BIN" + procd_close_instance +} diff --git a/open-app-filter/files/feature.cfg b/open-app-filter/files/feature.cfg new file mode 100755 index 0000000..bd3a9c2 --- /dev/null +++ b/open-app-filter/files/feature.cfg @@ -0,0 +1,252 @@ +#version v22.3.24 +#format v3.0 +#id name:[proto;sport;dport;host url;request;dict] +#class chat 1 聊天 +1003 微博:[tcp;;443;weibo;;] +1004 陌陌:[tcp;;;momo;;,tcp;;;;;04:2f|05:66|06:65|07:65,tcp;;;;;00:03|01:03|02:00] +1005 支付宝:[tcp;;;alipay.com;;] +1006 钉钉:[tcp;;;dingtalk;;,tcp;;;;d?host=;,tcp;;;;/man/api;,tcp;;;;/beacon;] +1007 Soul:[tcp;;;soulapp;;] +1008 伊对:[tcp;;;520yidui;;] +1009 探探:[tcp;;;tancdn;;,tcp;;;tantanapp;;] +1010 多闪:[tcp;;;ppkankan;;] + +#class game 2 游戏 +2001 王者荣耀:[tcp;;;;;00:33|1:66|02:00|03:0b] +2003 英雄联盟手游:[tcp;;;;;00:33|01:66|02:00|03:0b,tcp;;443;;;00:01|01:00|10:86|11:47] +2015 我的世界:[tcp;;443;g79mclobt.nie.netease;;] +2005 欢乐斗地主:[tcp;;8000;;;00:74|01:67|02:77|03:5f] +2006 梦幻西游:[tcp;;;;;00:0e|01:00|02:fe|03:ff] +2007 明日之后:[udp;;;;;00:05|01:09|02:00,tcp;;;;;00:02|01:00|02:00|03:00|04:00|05:00] +2008 QQ飞车:[udp;;;;;00:28|01:28,tcp;;10000;;;00:33|01:66|02:00|03:08] +2009 跑跑卡丁车:[tcp;;8888;;;00:33|01:66|02:00|03:08] +2010 开心消消乐:[tcp;;80;happyelements;;] +2011 狂野飙车:[tcp;;;asphalt9;;] +2012 率土之滨:[tcp;;10001;;;00:00|01:00,tcp;;8001;;;00:00|01:00] +2013 一刀传世:[tcp;;8040;;;00:47|01:45] +2014 第五人格:[tcp;;4010;;;,tcp;;4010;;;,tcp;;4020;;;,tcp;;4030;;;,tcp;;4040;;;,tcp;;4050;;;,tcp;;4060;;;,tcp;;4070;;;,tcp;;4080;;;,tcp;;4090;;;] +2016 皇室战争:[udp;;9339;;;] +2017 炉石传说:[tcp;;3724;;;00:73:01:00:02:00] +2023 原神:[tcp;;443;yuanshen.com;;] +2025 天涯明月刀:[tcp;;10000;;;00:43|01:66|02:aa] +2026 微信小游戏:[tcp;;443;mmgame;;,tcp;;443;game.weixin.qq;;] +2033 我叫MT4:[tcp;;21248;;;,tcp;;;dir.mt4.qq.com;;] +2034 神都夜行录:[udp;;;;;00:00|01:00|02:00|03:00|04:56|05:40] +2041 光遇:[udp;;10000-15000;;;00:8f|01:ff,tcp;;;ma75.update.netease.com;;,tcp;;;ma75.proxima.nie.netease;;] +2042 保卫萝卜4:[tcp;;;s4.luobo.cn;;] +2040 哈利波特:[tcp;;10021-12000;;;00:02|01:00|02:00|03:00|04:00|05:00,tcp;;443;g92.proxima;;] +2067 9377游戏:[tcp;;;www.9377.com;;] +2068 4399游戏:[tcp;;;4399.com;;] +2069 7k7k游戏:[tcp;;;7k7k.com;;] +2070 17173游戏:[tcp;;;17173.com;;] +2071 37网游:[tcp;;;37.com;;] +2074 hao123游戏:[tcp;;;game.hao123.com;;] +2075 51游戏:[tcp;;;www.51.com;;] +2050 uu加速器:[tcp;;;mg.uu.163.com;;] +2051 腾讯加速器:[tcp;;;m.acc.qq.com;;] +2080 乐逗游戏:[tcp;;;.uu.cc;;] + +#class video 3 视频 +3001 抖音:[tcp;;;-dy-;;,tcp;;;-dy.;;,tcp;;;douyin;;,tcp;;;amemv.com;;,tcp;;;apstatp.com;;,tcp;;;ecombdapi.com;;] +3006 斗鱼:[tcp;;;douyu;;,tcp;;;douyu;;-2:2f|-1:00] +3004 爱奇艺:[tcp;;;iqiyi;;,tcp;;;qy.net;;] +3008 虎牙直播:[tcp;;;huya;;,udp;;;;;01:00|02:00|03:00|04:23,udp;;;;;01:00|02:00|03:00|04:24] +3010 小红书:[tcp;;;xiaohongshu;;,tcp;;;xhscdn;;] +3011 花椒直播:[tcp;;;huajiao;;] +3012 映客直播:[tcp;;;;.inke.cn;] +3016 芒果tv:[tcp;;443;mgtv;;,tcp;;80;mgtv;;,tcp;;443;hitv;;] +3017 西瓜视频:[tcp;;;ixigua;;,tcp;;443;snsdk;;,tcp;;;xg-p.ixigua;;,tcp;;;bdxigua;;] +3018 搜狐视频:[tcp;;;aty.sohu.com;;,tcp;;;tv.itc.cn;;] +3020 咪咕视频:[tcp;;;miguvideo;;,tcp;;;migu.cn;;] +3022 人人视频:[tcp;;;rr.tv;;] +3023 央视影音:[tcp;;;cntv;;] +3024 优酷:[tcp;;;youku;;,tcp;;;ykimg;;,tcp;;;;/youku;] +3025 最右:[tcp;;;izuiyou;;] +3026 风行视频:[tcp;;;funshion;;] +3019 播聊:[tcp;;80;randlove.cn;;,tcp;;;yueliao;;,tcp;;;5glive;;] +3021 韩剧TV:[tcp;;;hanju.koudaibaobao;;] +3027 企鹅电竞:[tcp;;;egame.qq;;,tcp;;;liveplay;;,tcp;;;;pggame;] +3028 波波视频:[tcp;;;miaopai;;] +3029 酷狗短酷:[tcp;;;bssdl.kugou;;] +3030 酷狗直播:[tcp;;;rt-m.kugou;;,tcp;;;kgimg.com;;] +3023 央视影音:[tcp;;;cntv;;] +3026 风行视频:[tcp;;;funshion;;] +3089 华数TV:[tcp;;;wasu.cn;;] +3121 梨视频:[tcp;;;pearvideo.com;;] +3094 南瓜电影:[tcp;;;vcinema.cn;;] +3084 六间房:[tcp;;;v.6.cn;;] +3085 百度直播:[tcp;;;live.baidu.com;;] +3086 度小视:[tcp;;;quanmin.baidu.com;;] + +#class shopping 4 购物 +4001 淘宝:[tcp;;;taobao;;,tcp;;;alicdn.com;;,tcp;;;tmall.com;;,tcp;;;;;00:d3|01:00,,tcp;;;;;00:d4|01:00,,tcp;;;;;00:d3|01:00] +4002 京东:[tcp;;;360buyimg;;,tcp;;;jd.com;;,tcp;;;jdcdn.com;;,tcp;;;;;00:d5|01:00] +4003 唯品会:[tcp;;;vips-mobile;;,tcp;;;vipshop;;,tcp;;;vip.com;;,tcp;;;vipstatic.com;;,tcp;;;appsimg.com;;] +4004 拼多多:[tcp;;;pinduoduo;;,tcp;;;yangkeduo.com;;,tcp;;;s1p.cdntip.com;;] +4010 饿了么:[tcp;;;eleme;;] +4012 闲鱼:[tcp;;;xianyu;;] +4021 转转:[tcp;;;zhuanzhuan;;,tcp;;;zhuanstatic;;] +4005 蘑菇街:[tcp;;;mogujie;;,tcp;;;mogucdn;;,tcp;;;;;00:73|01:ea|02:68|03:fb|04:3f] +4006 苏宁易购:[tcp;;;.suning.;;] +4007 当当网:[tcp;;;.dangdang.com;;] +4008 1号店:[tcp;;;.yhd.com;;] +4009 朴朴超市:[tcp;;;pupumall;;,tcp;;;pupuapi;;] +4013 叮咚买菜:[tcp;;;ddxq.mobi;;] +4014 小米有品:[tcp;;;youpin;;,tcp;;;shopapi.io.mi.com;;] +4015 微店:[tcp;;;weidian;;] +4016 折800:[tcp;;;zhe800.com;;] +4017 HM:[tcp;;;www.hm.com;;,tcp;;;measurement.com;;] +4018 好省:[tcp;;;hzhstb.com;;] +4019 什么值得买:[tcp;;;smzdm.com;;] +4022 网易严选:[tcp;;;yanxuan;;] +4023 识货:[tcp;;;shihuo;;] +4024 考拉海购:[tcp;;;kaola;;] +4025 宜家家居:[tcp;;;ikea.cn;;] +4026 小象优品:[tcp;;;xiaoxiangyoupin;;] +4040 国美:[tcp;;;gome.com;;] +4041 酒仙网:[tcp;;;jiuxian.com;;] +4052 1688:[tcp;;;1688.com;;] +4053 亚马逊:[tcp;;;amazon.cn;;] +4054 Lazada:[tcp;;;lazada.com;;] + +#class music 5 音乐 +5001 网易云音乐:[tcp;;;music.163;;,tcp;;;music.126;;] +5002 QQ音乐:[tcp;;;;^/amobile.music.tc.qq.com;,tcp;;;qqmusic;;] +5003 酷狗音乐:[tcp;;;kugou;;,tcp;;;kgimg;;,tcp;;;fanxing;;] +5004 酷我音乐:[tcp;;;.kuwo.cn;;] +5005 喜马拉雅:[tcp;;;.ximalaya.com;;] +5006 千千音乐:[tcp;;;music.taihe.com;;] +5007 虾米音乐:[tcp;;;xiami;;] +5008 音悦台:[tcp;;;yinyuetai.com;;] +5009 豆瓣FM:[tcp;;;douban.fm;;] +5010 唱吧:[tcp;;;changba.com;;] +5011 音乐随心听:[tcp;;;fm.taihe.com;;] +5012 懒人听书:[tcp;;;lrts.me;;] + +#class employee 6 招聘 +6001 前程无忧:[tcp;;;51job;;] +6002 智联招聘:[tcp;;;zhaopin;;] +6003 猎聘:[tcp;;;liepin;;] +6004 赶集网:[tcp;;;58.com;;,tcp;;;58cdn;;] +6005 同城急聘:[tcp;;;xiaomei;;] +6006 领英:[tcp;;;linkedin;;] +6007 斗米:[tcp;;;doumi;;] +6008 看准:[tcp;;;kanzhun.com;;] +6009 应届生求职:[tcp;;;yingjiesheng.com;;] +6010 中华英才网:[tcp;;;chinahr.com;;] +6011 拉勾网:[tcp;;;lagou.com;;] +6012 大街网:[tcp;;;dajie.com;;] +6013 boss直聘:[tcp;;;zhipin.com;;] +6014 实习僧:[tcp;;;shixiseng.com;;] + +#class download 7 下载 +7002 AppStore:[tcp;;;itunes.apple.com;;] +7004 ftp文件传输:[tcp;;21;;;] +7005 vivo应用商店:[tcp;;443;appstore.vivo;;,tcp;;443;apkappdefwsdl.vivo;;] +7006 王者荣耀更新:[tcp;;80;;/sgame/;] +7007 天翼云盘:[tcp;;;ctyunapi;;] +7008 腾讯微云:[tcp;;;weiyun.com;;,tcp;;;aegis.qq.com;;,tcp;;;pingtas.qq.com;;,tcp;;443;;;00:77|01:6e|02:73] +7009 坚果云:[tcp;;;jianguoyun;;] +7010 蓝奏云:[tcp;;;pan.lanzou.com;;] +7011 华为云:[tcp;;;cloud.huawei.com;;,tcp;;;hicloud.com;;,tcp;;;myhuaweicloud.cn;;] +7020 windows更新:[tcp;;80;update.microsoft.com;;,tcp;;;windowsupdate.com;;] +7030 向日葵:[tcp;;;oray.com;;,tcp;;;oray.net;;] +7031 TeamViewer:[tcp;;;teamviewer;;] +7032 阿里云盘:[tcp;;;aliyundrive;;] + + +#class website 8 常用网站 +8001 百度:[tcp;;;www.baidu.com;;,tcp;;;m.baidu.com;;] +8079 谷歌:[tcp;;;google.com;;] +8002 新浪:[tcp;;;www.sina.com;;,tcp;;;m.sina.com;;] +8003 搜狐:[tcp;;;www.sohu.com;;,tcp;;;m.sohu.com;;] +8004 网易:[tcp;;;www.163.com;;,tcp;;443;www.126.com;;] +8005 凤凰网:[tcp;;;ifeng.com;;] +8009 hao123:[tcp;;;www.hao123.com;;,tcp;;;m.hao123.com;;] +8010 2345:[tcp;;;www.2345.com;;,tcp;;;m.2345.com;;] +8006 人民网:[tcp;;;people.com.cn;;] +8008 中华网:[tcp;;;www.china.com;;] +8020 天涯社区:[tcp;;;tianya.cn;;] +8026 穷游网:[tcp;;;qyer.com;;] +8027 驴妈妈:[tcp;;;lvmama.com;;] +8029 太平洋汽车:[tcp;;;pcauto.com.cn;;] +8030 易车网:[tcp;;;bitauto.com;;] +8031 爱卡汽车:[tcp;;;xcar.com.cn;;] +8035 和讯:[tcp;;;hexun.com;;] +8036 第一财经:[tcp;;;yicai.com;;] +8037 全景网:[tcp;;;p5w.net;;] +8038 中彩网:[tcp;;;zhcw.com;;] +8039 体育彩票:[tcp;;;lottery.gov.cn;;] +8041 豆丁:[tcp;;;docin.com;;] +8044 缤客:[tcp;;;booking.com;;] +8046 猫扑:[tcp;;;mop.com;;] +8064 潇湘书院:[tcp;;;xxsy.net;;] +8065 cctv5:[tcp;;;sports.cctv.com;;] +8066 虎扑体育:[tcp;;;hupu.com;;] +8077 知网:[tcp;;;www.cnki.net;;] +8087 github:[tcp;;;github.com;;] +8089 gitee:[tcp;;;gitee.com;;] +8090 必应:[tcp;;;bing.com;;] +8092 中国福利彩:[tcp;;;www.cwl.gov.cn;;] +8093 新浪彩票:[tcp;;;lottery.sina.com.cn;;] +8094 竞彩网:[tcp;;;www.sporttery.cn;;] +8096 新浪体育:[tcp;;;sports.sina.com.cn;;] +8098 小米官网:[tcp;;;www.mi.com;;] +8099 BBC:[tcp;;;www.bbc.com;;] +8100 腾讯智影:[tcp;;;zenvideo.qq.com;;] +8103 IT之家:[tcp;;;www.ithome.com;;] +8104 太平洋电脑:[tcp;;;www.pconline.com.cn;;] +8105 中国移动:[tcp;;;www.10086.cn;;] +8106 中国联通:[tcp;;;www.10010.com;;] +8107 中国电信:[tcp;;;www.189.cn;;] +8108 华为商城:[tcp;;;www.vmall.com;;] +8110 vivo官网:[tcp;;;www.vivo.com.cn;;] +8111 华为官网:[tcp;;;www.huawei.com;;] +8112 苹果官网:[tcp;;;www.apple.com;;] + + +#class life 10 生活 +10003 京东钱包:[tcp;;;jdpay.com;;] +10034 饿了么:[tcp;;;eleme.com;;] +10035 美团:[tcp;;;meituan;;] +10004 豆瓣:[tcp;;;douban.com;;] +10005 知乎:[tcp;;;zhihu.com;;] +10006 链家:[tcp;;;lianjia.com;;] +10007 天眼查:[tcp;;;tianyancha.com;;] +10008 有道词典:[tcp;;;dict.youdao.com;;] +10010 萤石云:[tcp;;;ys7.com;;,udp;;;;;00:e2|01:62|02:0c,tcp;;11001;;;00:24|01:0a] +10011 掌阅:[tcp;;;ireader.com;;,tcp;;;zhangyue;;] +10012 安居客:[tcp;;;anjuke.com;;] +10013 房天下:[tcp;;;fang.com;;] +10014 58同城:[tcp;;;58.com;;] +10015 动漫之家:[tcp;;;dmzj.com;;] +10016 汽车之家:[tcp;;;autohome.com.cn;;] +10017 飞猪:[tcp;;;fliggy.com;;] +10018 12306:[tcp;;;12306.cn;;] +10019 马蜂窝:[tcp;;;mafengwo.cn;;] +10020 途牛:[tcp;;;tuniu.com;;] +10021 小爱音箱:[tcp;;;ai.xiaomi.com;;,tcp;;;mina.mi.com;;] +10022 搜狗拼音:[tcp;;;pinyin.sogou.com;;]:5:1 + + +#class finance 14 金融 +14001 建设银行:[tcp;;;ccb.com;;] +14002 农业银行:[tcp;;;abchina.com;;] +14003 中国银行:[tcp;;;boc.cn;;] +14004 交通银行:[tcp;;;bankcomm.com;;] +14005 招商银行:[tcp;;;cmbchina.com;;] +14006 邮政储蓄:[tcp;;;psbc.com;;] +14007 兴业银行:[tcp;;;cib.com.cn;;] +14008 浦发银行:[tcp;;;spdb.com.cn;;] +14009 中信银行:[tcp;;;citicbank.com;;] +14010 上海银行:[tcp;;;bosc.cn;;] +14011 平安银行:[tcp;;;pingan.com.cn;;] +14012 人民银行:[tcp;;;pbc.gov.cn;;] +14013 北京银行:[tcp;;;bankofbeijing;;] +14014 银联在线:[tcp;;;95516.com;;] + + +#class tools 11 工具 +11001 samba共享:[tcp;;445;;;] +11002 ftp文件传输:[tcp;;21;;;] +11003 SSH:[tcp;;;;;00:53|01:53|02:48] \ No newline at end of file diff --git a/open-app-filter/files/feature_cn.cfg b/open-app-filter/files/feature_cn.cfg new file mode 100755 index 0000000..a9c4ac5 --- /dev/null +++ b/open-app-filter/files/feature_cn.cfg @@ -0,0 +1,234 @@ +#version v22.3.24 +#format v2.0 +#id name:[proto;sport;dport;host url;request;dict] +#class chat 1 聊天 +1003 微博:[tcp;;443;weibo;;] +1004 陌陌:[tcp;;;momo;;,tcp;;;;;04:2f|05:66|06:65|07:65,tcp;;;;;00:03|01:03|02:00] +1005 支付宝:[tcp;;443;alipay.com;;] +1006 钉钉:[tcp;;;dingtalk;;,tcp;;;;d?host=;,tcp;;;;/man/api;,tcp;;;;/beacon;] +1007 Soul:[tcp;;;soulapp;;] +1008 伊对:[tcp;;;520yidui;;] +1009 探探:[tcp;;;tancdn;;,tcp;;;tantanapp;;] +1010 多闪:[tcp;;;ppkankan;;] + +#class game 2 游戏 +2001 王者荣耀:[tcp;;;;;00:33|1:66|02:00|03:0b] +2002 和平精英:[tcp;;17500;;;00:33|1:66|03:0a|05:0a] +2003 英雄联盟手游:[tcp;;;;;00:33|01:66|02:00|03:0b,tcp;;443;;;00:01|01:00|10:86|11:47] +2015 我的世界:[tcp;;443;g79mclobt.nie.netease;;] +2005 欢乐斗地主:[tcp;;8000;;;00:74|01:67|02:77|03:5f] +2006 梦幻西游:[tcp;;;;;00:0e|01:00|02:fe|03:ff] +2007 明日之后:[udp;;;;;00:05|01:09|02:00,tcp;;;;;00:02|01:00|02:00|03:00|04:00|05:00] +2008 QQ飞车:[udp;;;;;00:28|01:28,tcp;;10000;;;00:33|01:66|02:00|03:08] +2009 跑跑卡丁车:[tcp;;8888;;;00:33|01:66|02:00|03:08] +2010 开心消消乐:[tcp;;80;happyelements;;] +2011 狂野飙车:[tcp;;;asphalt9;;] +2012 率土之滨:[tcp;;10001;;;00:00|01:00,tcp;;8001;;;00:00|01:00] +2013 一刀传世:[tcp;;8040;;;00:47|01:45] +2014 第五人格:[tcp;;4010;;;,tcp;;4010;;;,tcp;;4020;;;,tcp;;4030;;;,tcp;;4040;;;,tcp;;4050;;;,tcp;;4060;;;,tcp;;4070;;;,tcp;;4080;;;,tcp;;4090;;;] +2016 皇室战争:[udp;;9339;;;] +2017 炉石传说:[tcp;;3724;;;00:73:01:00:02:00] +2023 原神:[tcp;;443;yuanshen.com;;] +2025 天涯明月刀:[tcp;;10000;;;00:43|01:66|02:aa] +2026 微信小游戏:[tcp;;443;mmgame;;,tcp;;443;game.weixin.qq;;] +2033 我叫MT4:[tcp;;21248;;;,tcp;;;dir.mt4.qq.com;;] +2034 神都夜行录:[udp;;;;;00:00|01:00|02:00|03:00|04:56|05:40] +2041 光遇:[udp;;10000-15000;;;00:8f|01:ff,tcp;;;ma75.update.netease.com;;,tcp;;;ma75.proxima.nie.netease;;] +2042 保卫萝卜4:[tcp;;;s4.luobo.cn;;] +2040 哈利波特:[tcp;;10021-12000;;;00:02|01:00|02:00|03:00|04:00|05:00,tcp;;443;g92.proxima;;] +2067 9377游戏:[tcp;;;www.9377.com;;] +2068 4399游戏:[tcp;;;4399.com;;] +2069 7k7k游戏:[tcp;;;7k7k.com;;] +2070 17173游戏:[tcp;;;17173.com;;] +2071 37网游:[tcp;;;37.com;;] +2072 游民星空:[tcp;;;gamersky.com;;] +2073 游侠网:[tcp;;;ali213.net;;] +2074 hao123游戏:[tcp;;;game.hao123.com;;] +2075 51游戏:[tcp;;;www.51.com;;] +2050 uu加速器:[tcp;;;mg.uu.163.com;;] +2051 腾讯加速器:[tcp;;;m.acc.qq.com;;] +2080 乐逗游戏:[tcp;;;.uu.cc;;] + +#class video 3 视频 +3001 抖音短视频:[tcp;;;-dy-;;,tcp;;;-dy.;;,tcp;;;douyin;;] +3002 火山小视频:[tcp;;;.huoshan.com;;,tcp;;;hs.pstatp.com;;,tcp;;;hs.ixigua.com;;] +3003 腾讯视频:[tcp;;443;v.qq.com;;,tcp;;443;video.qq.com;;,tcp;;443;btrace.qq.com;;] +3004 爱奇艺:[tcp;;;iqiyi;;,tcp;;;qy.net;;] +3005 微视:[tcp;;80;;;00:34|01:16|02:75,tcp;;80;weishi.qq.com;;] +3006 斗鱼直播:[tcp;;;douyu;;,tcp;;;douyu;;-2:2f|-1:00] +3008 虎牙直播:[tcp;;;huya;;,udp;;;;;01:00|02:00|03:00|04:23,udp;;;;;01:00|02:00|03:00|04:24] +3009 快手:[tcp;;;kuaishou;;,tcp;;;ksyuncdn.com;;,tcp;;;.gifshow.com;;,tcp;;;yximgs.com;;,tcp;;80;;/ksc;,tcp;;;kwaicdn;;,tcp;;;kwimgs;;] +3010 小红书:[tcp;;;xiaohongshu;;,tcp;;;xhscdn;;] +3011 花椒直播:[tcp;;;huajiao;;] +3012 映客直播:[tcp;;;;.inke.cn;] +3013 YY:[udp;;;;;02:00|03:00|04:08,udp;;;;;00:4f|01:00|02:00] +3014 哔哩哔哩:[tcp;;;bilivideo;;,tcp;;;bilibili.com;;,tcp;;;;;00:47|05:75|06:70|07:67,tcp;;;;/bfs/emote/;,,tcp;;;hdslb.com;;] +3016 芒果tv:[tcp;;443;mgtv;;,tcp;;80;mgtv;;,tcp;;443;hitv;;] +3017 西瓜视频:[tcp;;;ixigua;;,tcp;;443;snsdk;;,tcp;;;xg-p.ixigua;;,tcp;;;bdxigua;;] +3018 搜狐视频:[tcp;;;aty.sohu.com;;,tcp;;;tv.itc.cn;;] +3019 播聊:[tcp;;80;randlove.cn;;,tcp;;;yueliao;;,tcp;;;5glive;;] +3020 咪咕视频:[tcp;;;miguvideo;;,tcp;;;migu.cn;;] +3021 韩剧TV:[tcp;;;hanju.koudaibaobao;;] +3022 人人视频:[tcp;;;rr.tv;;] +3023 央视影音:[tcp;;;cntv;;] +3024 土豆视频:[tcp;;;youku;;,tcp;;;ykimg;;] +3025 最右:[tcp;;;izuiyou;;] +3026 风行视频:[tcp;;;funshion;;] +3027 企鹅电竞:[tcp;;;egame.qq;;,tcp;;;liveplay;;,tcp;;;;pggame;] +3028 波波视频:[tcp;;;miaopai;;] +3029 酷狗短酷:[tcp;;;bssdl.kugou;;] +3030 酷狗直播:[tcp;;;rt-m.kugou;;,tcp;;;kgimg.com;;] + +#class shopping 4 购物 +4001 淘宝:[tcp;;;taobao;;,tcp;;;alicdn.com;;,tcp;;;tmall.com;;,tcp;;;;;00:d3|01:00,,tcp;;;;;00:d4|01:00,,tcp;;;;;00:d3|01:00] +4002 京东:[tcp;;;360buyimg;;,tcp;;;jd.com;;,tcp;;;jdcdn.com;;,tcp;;;;;00:d5|01:00] +4003 唯品会:[tcp;;;vips-mobile;;,tcp;;;vipshop;;,tcp;;;vip.com;;,tcp;;;vipstatic.com;;,tcp;;;appsimg.com;;] +4004 拼多多:[tcp;;;pinduoduo;;,tcp;;;yangkeduo.com;;,tcp;;;s1p.cdntip.com;;] +4010 饿了么:[tcp;;;eleme;;] +4011 美团:[tcp;;;meituan;;] +4012 闲鱼:[tcp;;;xianyu;;] +4021 转转:[tcp;;;zhuanzhuan;;,tcp;;;zhuanstatic;;] +4005 蘑菇街:[tcp;;;mogujie;;,tcp;;;mogucdn;;,tcp;;;;;00:73|01:ea|02:68|03:fb|04:3f] +4006 苏宁易购:[tcp;;;.suning.;;] +4007 当当网:[tcp;;;.dangdang.com;;] +4008 1号店:[tcp;;;.yhd.com;;] +4009 朴朴超市:[tcp;;;pupumall;;,tcp;;;pupuapi;;] +4013 叮咚买菜:[tcp;;;ddxq.mobi;;] +4014 小米有品:[tcp;;;youpin;;,tcp;;;shopapi.io.mi.com;;] +4015 微店:[tcp;;;weidian;;] +4016 折800:[tcp;;;zhe800.com;;] +4017 HM:[tcp;;;www.hm.com;;,tcp;;;measurement.com;;] +4018 好省:[tcp;;;hzhstb.com;;] +4019 什么值得买:[tcp;;;smzdm.com;;] +4020 大众点评:[tcp;;;dianping.com;;] +4022 网易严选:[tcp;;;yanxuan;;] +4023 识货:[tcp;;;shihuo;;] +4024 考拉海购:[tcp;;;kaola;;] +4025 宜家家居:[tcp;;;ikea.cn;;] +4026 小象优品:[tcp;;;xiaoxiangyoupin;;] + +#class music 5 音乐 +5001 网易云音乐:[tcp;;;music.163;;,tcp;;;music.126;;] +5002 QQ音乐:[tcp;;;;^/amobile.music.tc.qq.com;,tcp;;;qqmusic;;] +5003 酷狗音乐:[tcp;;;kugou;;,tcp;;;kgimg;;,tcp;;;fanxing;;] +5004 酷我音乐:[tcp;;;.kuwo.cn;;] +5005 喜马拉雅:[tcp;;;.ximalaya.com;;] +5006 千千音乐:[tcp;;;music.taihe.com;;] +5007 虾米音乐:[tcp;;;xiami;;] +5008 音悦台:[tcp;;;yinyuetai.com;;] +5009 豆瓣FM:[tcp;;;douban.fm;;] +5010 唱吧:[tcp;;;changba.com;;] +5011 音乐随心听:[tcp;;;fm.taihe.com;;] +5012 懒人听书:[tcp;;;lrts.me;;] + +#class employee 6 招聘 +6001 前程无忧:[tcp;;;51job;;] +6002 智联招聘:[tcp;;;zhaopin;;] +6003 猎聘:[tcp;;;liepin;;] +6004 赶集网:[tcp;;;58.com;;,tcp;;;58cdn;;] +6005 同城急聘:[tcp;;;xiaomei;;] +6006 领英:[tcp;;;linkedin;;] +6007 斗米:[tcp;;;doumi;;] +6008 看准:[tcp;;;kanzhun.com;;] +6009 应届生求职:[tcp;;;yingjiesheng.com;;] +6010 中华英才网:[tcp;;;chinahr.com;;] +6011 拉勾网:[tcp;;;lagou.com;;] +6012 大街网:[tcp;;;dajie.com;;] +6013 boss直聘:[tcp;;;zhipin.com;;] +6014 实习僧:[tcp;;;shixiseng.com;;] + +#class download 7 下载 +7001 迅雷:[udp;12345;;;;,udp;15000;;;;,tcp;;54321;;;,tcp;;12345;;;,udp;6881;;;;,udp;;12346;;;,udp;12346;;;;] +7002 AppStore:[tcp;;;itunes.apple.com;;] HIDE:0 +7003 samba共享:[tcp;;445;;;] HIDE:0 +7004 ftp文件传输:[tcp;;21;;;] HIDE:0 +7005 vivo应用商店:[tcp;;443;appstore.vivo;;,tcp;;443;apkappdefwsdl.vivo;;] HIDE:0 +7006 王者荣耀更新:[tcp;;80;;/sgame/;] +7007 天翼云盘:[tcp;;;ctyunapi;;] +7008 腾讯微云:[tcp;;;weiyun.com;;,tcp;;;aegis.qq.com;;,tcp;;;pingtas.qq.com;;,tcp;;443;;;00:77|01:6e|02:73] +7009 坚果云:[tcp;;;jianguoyun;;] +7010 蓝奏云:[tcp;;;pan.lanzou.com;;] +7011 华为云:[tcp;;;cloud.huawei.com;;,tcp;;;hicloud.com;;,tcp;;;myhuaweicloud.cn;;] +7020 windows更新:[tcp;;80;update.microsoft.com;;,tcp;;;windowsupdate.com;;] +7030 向日葵:[tcp;;;oray.com;;,tcp;;;oray.net;;] +7031 TeamViewer:[tcp;;;teamviewer;;] +7032 阿里云盘:[tcp;;;aliyundrive;;] +7035 SSH:[tcp;;;;;00:53|01:53|02:48] + +#class website 8 常用网站 +8001 百度:[tcp;;;baidu.com;;] +8002 新浪:[tcp;;;sina.com;;] +8003 搜狐:[tcp;;;sohu.com;;] +8004 网易:[tcp;;;163.com;;,tcp;;443;126.com;;] +8005 凤凰网:[tcp;;;ifeng.com;;] +8006 人民网:[tcp;;;people.com.cn;;] +8007 凤凰网:[tcp;;;ifeng.com;;] +8008 中华网:[tcp;;;china.com;;] +8009 hao123:[tcp;;;hao123.com;;,] +8010 2345:[tcp;;;2345.com;;,] +8011 4399游戏:[tcp;;;4399.com;;] +8012 7k7k游戏:[tcp;;;7k7k.com;;] +8013 17173游戏:[tcp;;;17173.com;;] +8014 37网游:[tcp;;;37.com;;] +8015 游民星空:[tcp;;;gamersky.com;;] +8016 游侠网:[tcp;;;ali213.net;;] +8017 世纪佳缘:[tcp;;;jiayuan.com;;] +8018 珍爱网:[tcp;;;zhenai.com;;] +8019 百合网:[tcp;;;baihe.com;;] +8020 天涯社区:[tcp;;;tianya.cn;;] +8021 携程网:[tcp;;;ctrip.com;;] +8022 飞猪:[tcp;;;fliggy.com;;] +8023 12306:[tcp;;;12306.cn;;] +8024 马蜂窝:[tcp;;;mafengwo.cn;;] +8025 途牛:[tcp;;;tuniu.com;;] +8026 穷游网:[tcp;;;qyer.com;;] +8027 驴妈妈:[tcp;;;lvmama.com;;] +8028 同程旅游:[tcp;;;ly.com;;] +8029 太平洋汽车:[tcp;;;pcauto.com.cn;;] +8030 易车网:[tcp;;;bitauto.com;;] +8031 爱卡汽车:[tcp;;;xcar.com.cn;;] +8032 雪球:[tcp;;;xueqiu.com;;] +8033 东方财富:[tcp;;;eastmoney.com;;] +8034 证券之星:[tcp;;;stockstar.com;;] +8035 和讯:[tcp;;;hexun.com;;] +8036 第一财经:[tcp;;;yicai.com;;] +8037 全景网:[tcp;;;p5w.net;;] +8038 中彩网:[tcp;;;zhcw.com;;] +8039 中国体育彩票:[tcp;;;lottery.gov.cn;;] +8040 竞彩网:[tcp;;;sporttery.cn;;] +8041 豆丁:[tcp;;;docin.com;;] +8042 豆瓣:[tcp;;;douban.com;;] +8043 知乎:[tcp;;;zhihu.com;;] +8044 缤客:[tcp;;;booking.com;;] +8046 猫扑:[tcp;;;mop.com;;] +8047 赶集网:[tcp;;;ganji.com;;] +8048 安居客:[tcp;;;anjuke.com;;] +8049 房天下:[tcp;;;fang.com;;] +8050 链家:[tcp;;;lianjia.com;;] +8051 百姓网:[tcp;;;baixing.com;;] +8052 下厨房:[tcp;;;xiachufang.com;;] +8053 大众点评:[tcp;;;dianping.com;;] +8054 58同城:[tcp;;;58.com;;] +8055 天眼查:[tcp;;;tianyancha.com;;] +8056 千图网:[tcp;;;58pic.com;;] +8057 csdn社区:[tcp;;;csdn.net;;] +8058 有道词典:[tcp;;;dict.youdao.com;;] +8059 动漫之家:[tcp;;;dmzj.com;;] +8060 汽车之家:[tcp;;;autohome.com.cn;;] +8061 纵横中文网:[tcp;;;zongheng.com;;] +8062 起点中文网:[tcp;;;qidian.com;;] +8063 飞卢:[tcp;;;faloo.com;;] +8064 潇湘书院:[tcp;;;xxsy.net;;] +8065 cctv5:[tcp;;;sports.cctv.com;;] +8066 虎扑体育:[tcp;;;www.hupu.com;;] +8067 建设银行:[tcp;;;ccb.com;;] +8068 农业银行:[tcp;;;abchina.com;;] +8069 中国银行:[tcp;;;boc.cn;;] +8070 交通银行:[tcp;;;bankcomm.com;;] +8071 招商银行:[tcp;;;cmbchina.com;;] +8072 邮政储蓄:[tcp;;;psbc.com;;] +8073 兴业银行:[tcp;;;cib.com.cn;;] +8074 浦发银行:[tcp;;;spdb.com.cn;;] +8075 中信银行:[tcp;;;citicbank.com;;] +8076 上海银行:[tcp;;;bosc.cn;;] + diff --git a/open-app-filter/files/feature_en.cfg b/open-app-filter/files/feature_en.cfg new file mode 100755 index 0000000..3537874 --- /dev/null +++ b/open-app-filter/files/feature_en.cfg @@ -0,0 +1,99 @@ +#version v22.11.11 +#format v2.0 +#id name:[proto;sport;dport;host url;request;dict] +#class chat 1 Chat +1001 Facebook:[tcp;;;facebook.com;;] +1002 Whatsapp:[tcp;;;whatsapp;;] +1003 Twitter:[tcp;;;twitter.com;;] +1004 Instagram:[tcp;;;instagram.com;;] +1005 VK:[tcp;;;vk.com;;] +1006 Line:[tcp;;;line;;] +1007 Snapchat:[tcp;;;snapchat.com;;] +1008 Tinder:[tcp;;;tinder.com;;] + +#class video 3 Video +3001 YouTube:[tcp;;;youtube;;] +3002 Tiktok:[tcp;;;tiktok;;] +3003 NetFlix:[tcp;;;netflix;;] +3004 Vimeo:[tcp;;;vimeo;;] +3005 DailyMotion:[tcp;;;dailymotion;;] +3006 Hulu:[tcp;;;hulu;;] +3007 Vube:[tcp;;;vube;;] +3008 Twitch:[tcp;;;twitch;;] +3009 LiveLeak:[tcp;;;itemfix;;] +3010 Spotify:[tcp;;;spotify.com;;] +3050 Xvideos:[tcp;;;xvideos.com;;] +3051 Pornhub:[tcp;;;pornhub.com;;] +3052 Xnxx:[tcp;;;xnxx.com;;] + +#class shopping 4 Shopping +4001 Amazon:[tcp;;;amazon.com;;] +4002 eBay:[tcp;;;ebay.com;;] +4003 Etsy:[tcp;;;etsy.com;;] +4004 Wish:[tcp;;;wish.com;;] +4005 Alibaba:[tcp;;;alibaba;;] +4006 Aliexpress:[tcp;;;aliexpress.com;;] +4007 Walmart:[tcp;;;walmart.com;;] +4008 Sears:[tcp;;;sears.com;;] +4009 Kohls:[tcp;;;kohls.com;;] +4010 Costco:[tcp;;;costco.com;;] +4011 Asos:[tcp;;;asos.com;;] +4012 Cuyana:[tcp;;;cuyana.com;;] + +#class download 7 Download +7001 GooglePlay:[tcp;;;play.google.com;;] +7002 AppStore:[tcp;;;iosapps.itunes.apple.com;;] +7003 WindowsUpdate:[tcp;;80;update.microsoft.com;;,tcp;;;windowsupdate.com;;] +7050 Speedtest:[tcp;;;speedtest.net;;] +7060 samba:[tcp;;445;;;] +7061 ftp:[tcp;;21;;;] +7062 ssh:[tcp;;22;;;] + +#class website 8 Website +8001 Google:[tcp;;;www.google.com;;] +8002 Wiki:[tcp;;;wikipedia.com;;] +8003 Yahoo:[tcp;;;yahoo;;] +8004 Apple:[tcp;;;www.apple.com;;] +8010 Reddit:[tcp;;;reddit.com;;] +8011 Outlook:[tcp;;;outlook.live.com;;] +8012 Naver:[tcp;;;naver.com;;] +8013 Fandom:[tcp;;;fandom.com;;] +8015 Globo:[tcp;;;globo.com;;] +8016 Yelp:[tcp;;;yelp.com;;] +8017 Pinterest:[tcp;;;www.pinterest.com;;] +8018 BBC:[tcp;;;www.bbc.com;;] +8020 Linkedin:[tcp;;;linkedin.com;;] +8022 Merriam-webster:[tcp;;;merriam-webster.com;;] +8027 Dictionary:[tcp;;;dictionary.com;;] +8028 Tripadvisor:[tcp;;;tripadvisor.com;;] +8029 Britannica:[tcp;;;britannica.com;;] +8030 Cambridge:[tcp;;;cambridge.org;;] +8032 Weather:[tcp;;;weather.com;;] +8033 Wiktionary:[tcp;;;wiktionary.org;;] +8034 Espn:[tcp;;;espn.com;;] +8035 Microsoft:[tcp;;;microsoft.com;;] +8038 Gsmarena:[tcp;;;gsmarena.com;;] +8039 Webmd:[tcp;;;webmd.com;;] +8040 Craigslist:[tcp;;;craigslist.org;;] +8041 Cricbuzz:[tcp;;;cricbuzz.com;;] +8042 Mayoclinic:[tcp;;;mayoclinic.org;;] +8043 Timeanddate:[tcp;;;timeanddate.com;;] +8044 Espncricinfo:[tcp;;;espncricinfo.com;;] +8045 Healthline:[tcp;;;healthline.com;;] +8047 Rottentomatoes:[tcp;;;rottentomatoes.com;;] +8049 Thefreedictionary:[tcp;;;thefreedictionary.com;;] +8052 Bestbuy:[tcp;;;bestbuy.com;;] +8053 Indeed:[tcp;;;indeed.com;;] +8058 Samsung:[tcp;;;samsung.com;;] +8059 Investopedia:[tcp;;;investopedia.com;;] +8060 Flashscore:[tcp;;;flashscore.com;;] +8061 Steampowered:[tcp;;;steampowered.com;;] +8064 Roblox:[tcp;;;roblox.com;;] +8065 Nordstrom:[tcp;;;nordstrom.com;;] +8066 Thepiratebay:[tcp;;;thepiratebay.org;;] +8067 Indiatimes:[tcp;;;indiatimes.com;;] +8068 Cnbc:[tcp;;;cnbc.com;;] +8069 Ssyoutube:[tcp;;;ssyoutube.com;;] +8070 Adobe:[tcp;;;adobe.com;;] +8071 Speedtest:[tcp;;;speedtest.net;;] +8072 Lowes:[tcp;;;lowes.com;;] diff --git a/open-app-filter/files/gen_class.sh b/open-app-filter/files/gen_class.sh new file mode 100755 index 0000000..2127df3 --- /dev/null +++ b/open-app-filter/files/gen_class.sh @@ -0,0 +1,10 @@ +#!/bin/sh +CLASS_NAME_FILE="/tmp/app_class.txt" +f_file=$1 +test -z "$f_file" && return + +test -f $CLASS_NAME_FILE &&{ + rm $CLASS_NAME_FILE +} +cat $f_file |grep "#class" | awk '{print $3 " " $2 " " $4}' >$CLASS_NAME_FILE + diff --git a/open-app-filter/files/hnat.sh b/open-app-filter/files/hnat.sh new file mode 100644 index 0000000..286c816 --- /dev/null +++ b/open-app-filter/files/hnat.sh @@ -0,0 +1,33 @@ +. /usr/share/libubox/jshn.sh +. /lib/functions.sh + +disable_hnat=`uci get appfilter.global.disable_hnat` + +if [ x"1" != x"$disable_hnat" ];then + return +fi + +# mt798x +test -d /sys/kernel/debug/hnat && { + echo 0 >/sys/kernel/debug/hnat/hook_toggle +} +# qca ecm +test -d /sys/kernel/debug/ecm/ && { + echo "1000000" > /sys/kernel/debug/ecm/ecm_classifier_default/accel_delay_pkts +} + +# turbo acc +test -f /etc/config/turboacc && { + uci -q set "turboacc.config.fastpath_fo_hw"="0" + uci -q set "turboacc.config.fastpath_fc_ipv6"="0" + uci -q set "turboacc.config.fastpath"="none" + uci -q set "turboacc.config.fullcone"="0" + /etc/init.d/turboacc restart & +} + +uci -q set "firewall.@defaults[0].flow_offloading_hw"='0' +uci -q set "firewall.@defaults[0].flow_offloading"='0' +uci -q set "firewall.@defaults[0].fullcone"='0' + +fw3 reload & + diff --git a/open-app-filter/files/oaf_rule b/open-app-filter/files/oaf_rule new file mode 100755 index 0000000..a98af7e --- /dev/null +++ b/open-app-filter/files/oaf_rule @@ -0,0 +1,99 @@ +. /usr/share/libubox/jshn.sh +. /lib/functions.sh + + +config_apply() +{ + test -z "$1" && return 1 + if [ -e "/dev/appfilter" ];then + echo "$1" >/dev/appfilter + fi +} + +clean_rule() +{ + json_init + json_add_int "op" 3 + json_add_object "data" + json_str=`json_dump` + config_apply "$json_str" + json_cleanup +} + +load_rule() +{ + json_init + json_add_int "op" 1 + json_add_object "data" + json_add_array "apps" + config_get appid_list rule app_list + if ! test -z "$appid_list";then + for appid in $appid_list: + do + json_add_int "" $appid + done + fi + json_str=`json_dump` + config_apply "$json_str" + json_cleanup +} + + + +add_mac_to_array_callback() { + local section="$1" + local mac + config_get mac "$section" "mac" + if [ -n "$mac" ]; then + json_add_string "" "$mac" + fi +} + +load_mac_list() +{ + json_init + config_load appfilter + json_add_int "op" 4 + json_add_object "data" + json_add_array "mac_list" + + local user_mode=`uci get appfilter.global.user_mode` + if [ x"1" == x"$user_mode" ];then + config_foreach add_mac_to_array_callback af_user + fi + + json_str=`json_dump` + config_apply "$json_str" + json_cleanup +} + + + +reload_rule(){ + config_load appfilter + clean_rule + load_rule + load_mac_list +} + +reload_base_config(){ + ! test -d /proc/sys/oaf && return + config_load appfilter + config_get work_mode "global" "work_mode" + config_get lan_ifname "global" "lan_ifname" + config_get user_mode "global" "user_mode" + + echo "$work_mode" >/proc/sys/oaf/work_mode + echo "$user_mode" >/proc/sys/oaf/user_mode + + if [ x"" != x"$lan_ifname" ];then + echo "$lan_ifname" >/proc/sys/oaf/lan_ifname + fi +} + +case $1 in +"reload") + reload_base_config + reload_rule +;; +esac diff --git a/open-app-filter/files/user_info.config b/open-app-filter/files/user_info.config new file mode 100755 index 0000000..e69de29 diff --git a/open-app-filter/src/Makefile b/open-app-filter/src/Makefile new file mode 100755 index 0000000..2236bab --- /dev/null +++ b/open-app-filter/src/Makefile @@ -0,0 +1,6 @@ +OBJS:=appfilter_user.o appfilter_netlink.o appfilter_ubus.o appfilter_config.o utils.o main.o +EXEC:=oafd +all: $(OBJS) + $(CC) -o $(EXEC) $(OBJS) $(LIBS) +clean: + rm $(EXEC) *.o diff --git a/open-app-filter/src/appfilter.h b/open-app-filter/src/appfilter.h new file mode 100755 index 0000000..a93fac5 --- /dev/null +++ b/open-app-filter/src/appfilter.h @@ -0,0 +1,109 @@ +#ifndef __APPFILTER_H__ +#define __APPFILTER_H__ +#define MIN_INET_ADDR_LEN 7 + +#include +#include +#include +#include + +#define LOG_FILE_PATH "/tmp/log/appfilter.log" + +typedef enum { + LOG_LEVEL_DEBUG, + LOG_LEVEL_INFO, + LOG_LEVEL_WARN, + LOG_LEVEL_ERROR +} LogLevel; + +extern int current_log_level; + + static void af_log(LogLevel level, const char *format, ...){ + if (level < current_log_level) + return; + + FILE *log_file = fopen(LOG_FILE_PATH, "a"); + if (!log_file) { + perror("Failed to open log file"); + return; + } + + time_t now = time(NULL); + struct tm *t = localtime(&now); + char time_str[20]; + strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", t); + + const char *level_str; + switch (level) { + case LOG_LEVEL_DEBUG: level_str = "DEBUG"; break; + case LOG_LEVEL_INFO: level_str = "INFO"; break; + case LOG_LEVEL_WARN: level_str = "WARN"; break; + case LOG_LEVEL_ERROR: level_str = "ERROR"; break; + default: level_str = "UNKNOWN"; break; + } + + fprintf(log_file, "[%s] [%s] ", time_str, level_str); + + va_list args; + va_start(args, format); + vfprintf(log_file, format, args); + va_end(args); + fclose(log_file); +} + +#define LOG_DEBUG(format, ...) af_log(LOG_LEVEL_DEBUG, format, ##__VA_ARGS__) +#define LOG_INFO(format, ...) af_log(LOG_LEVEL_INFO, format, ##__VA_ARGS__) +#define LOG_WARN(format, ...) af_log(LOG_LEVEL_WARN, format, ##__VA_ARGS__) +#define LOG_ERROR(format, ...) af_log(LOG_LEVEL_ERROR, format, ##__VA_ARGS__) + + + +#define MAX_TIME_LIST_LEN 1024 +#define MAX_TIME_LIST 64 +typedef struct af_time +{ + int hour; + int min; +} af_time_t; + +typedef struct af_global_config_t{ + int enable; + int user_mode; + int work_mode; + int record_enable; + int disable_hnat; + int auto_load_engine; + int tcp_rst; + char lan_ifname[16]; +}af_global_config_t; + +typedef struct time_config{ + af_time_t start_time; + af_time_t end_time; +}time_config_t; + +typedef struct af_time_config_t{ + int time_mode; + time_config_t seg_time; + int deny_time; + int allow_time; + int days[7]; + int time_num; + time_config_t time_list[MAX_TIME_LIST]; +}af_time_config_t; + +typedef struct af_config_t{ + af_global_config_t global; + af_time_config_t time; +}af_config_t; + +typedef struct af_run_time_status{ + int deny_time; + int allow_time; + int filter; + int match_time; +}af_run_time_status_t; + + +extern af_config_t g_af_config; +#endif diff --git a/open-app-filter/src/appfilter_config.c b/open-app-filter/src/appfilter_config.c new file mode 100755 index 0000000..140a84f --- /dev/null +++ b/open-app-filter/src/appfilter_config.c @@ -0,0 +1,552 @@ +/* +Copyright (C) 2020 Derry + +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 +#include +#include +#include "appfilter_config.h" +#include "appfilter.h" +#include + +app_name_info_t app_name_table[MAX_SUPPORT_APP_NUM]; +int g_app_count = 0; +int g_cur_class_num = 0; +char CLASS_NAME_TABLE[MAX_APP_TYPE][MAX_CLASS_NAME_LEN]; + +const char *config_path = "./config"; +static struct uci_context *uci_ctx = NULL; +static struct uci_package *uci_appfilter; + + +int af_uci_get_int_value(struct uci_context *ctx, char *key) +{ + struct uci_element *e; + struct uci_ptr ptr; + int ret = -1; + int dummy; + char *parameters ; + char param_tmp[128] = {0}; + strcpy(param_tmp, key); + if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) { + return ret; + } + + if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) { + ctx->err = UCI_ERR_NOTFOUND; + goto done; + } + + e = ptr.last; + switch(e->type) { + case UCI_TYPE_SECTION: + ret = -1; + goto done; + case UCI_TYPE_OPTION: + ret = atoi(ptr.o->v.string); + goto done; + default: + break; + } +done: + + if (ptr.p) + uci_unload(ctx, ptr.p); + return ret; +} + + +int af_uci_get_value(struct uci_context *ctx, char *key, char *output, int out_len) +{ + struct uci_element *e; + struct uci_ptr ptr; + int ret = UCI_OK; + int dummy; + char *parameters ; + char param_tmp[128] = {0}; + strcpy(param_tmp, key); + if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) { + ret = 1; + return ret; + } + + if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) { + ctx->err = UCI_ERR_NOTFOUND; + ret = 1; + goto done; + } + + e = ptr.last; + switch(e->type) { + case UCI_TYPE_SECTION: + snprintf(output, out_len, "%s", ptr.s->type); + break; + case UCI_TYPE_OPTION: + snprintf(output, out_len, "%s", ptr.o->v.string); + break; + default: + ret = 1; + break; + } +done: + if (ptr.p) + uci_unload(ctx, ptr.p); + return ret; +} + + +int af_uci_delete(struct uci_context *ctx, char *key) +{ + struct uci_element *e; + struct uci_ptr ptr; + int ret = UCI_OK; + int dummy; + char *parameters ; + char param_tmp[128] = {0}; + strcpy(param_tmp, key); + if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) { + ret = 1; + return ret; + } + ret = uci_delete(ctx, &ptr); + if (ret == UCI_OK) + ret = uci_save(ctx, ptr.p); + + if (ptr.p) + uci_unload(ctx, ptr.p); + return ret; +} + + + +int af_uci_add_list(struct uci_context *ctx, char *key, char *value) +{ + struct uci_element *e; + struct uci_ptr ptr; + int ret = UCI_OK; + int dummy; + char *parameters; + if (strlen(value) + strlen(key) >= MAX_PARAM_LIST_LEN - 1) { + printf("value too long\n"); + return -1; + } + char param_tmp[MAX_PARAM_LIST_LEN] = {0}; + sprintf(param_tmp, "%s=%s", key, value); + if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) { + ret = 1; + return ret; + } + ret = uci_add_list(ctx, &ptr); + if (ret == UCI_OK) + ret = uci_save(ctx, ptr.p); + + if (ptr.p) + uci_unload(ctx, ptr.p); + return ret; +} + + +int af_uci_get_list_value(struct uci_context *ctx, char *key, char *output, int out_len, char *delimt) +{ + struct uci_element *e; + struct uci_ptr ptr; + int ret = -1; + int dummy; + char *parameters ; + char param_tmp[128] = {0}; + strcpy(param_tmp, key); + if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) { + return ret; + } + + if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) { + ctx->err = UCI_ERR_NOTFOUND; + goto done; + } + int sep = 0; + e = ptr.last; + int len = 0; + switch(e->type) { + case UCI_TYPE_SECTION: + ret = -1; + goto done; + case UCI_TYPE_OPTION: + if (UCI_TYPE_LIST == ptr.o->type){ + memset(output, 0x0, out_len); + uci_foreach_element(&ptr.o->v.list, e) { + len = strlen(output); + if (sep){ + strncat(output + len, delimt, out_len); + } + len = strlen(output); + sprintf(output + len, "%s", e->name); + sep = 1; + } + ret = 0; + } + goto done; + default: + break; + } +done: + if (ptr.p) + uci_unload(ctx, ptr.p); + return ret; +} + + +int af_uci_add_int_list(struct uci_context *ctx, char *key, int value) +{ + struct uci_element *e; + struct uci_ptr ptr; + int ret = UCI_OK; + int dummy; + char *parameters ; + char param_tmp[128] = {0}; + sprintf(param_tmp, "%s=%d", key, value); + if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) { + ret = 1; + return ret; + } + ret = uci_add_list(ctx, &ptr); + if (ret == UCI_OK) + ret = uci_save(ctx, ptr.p); + + if (ptr.p) + uci_unload(ctx, ptr.p); + return ret; +} + +int af_uci_del_list(struct uci_context *ctx, char *key, char *value) +{ + struct uci_element *e; + struct uci_ptr ptr; + int ret = UCI_OK; + int dummy; + char *parameters ; + char param_tmp[128] = {0}; + sprintf(param_tmp, "%s=%s", key, value); + if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) { + ret = 1; + return ret; + } + ret = uci_del_list(ctx, &ptr); + if (ret == UCI_OK) + ret = uci_save(ctx, ptr.p); + + if (ptr.p) + uci_unload(ctx, ptr.p); + return ret; +} + + +int af_uci_set_value(struct uci_context *ctx, char *key, char *value) +{ + struct uci_element *e; + struct uci_ptr ptr; + int ret = UCI_OK; + int dummy; + char *parameters ; + char param_tmp[2048] = {0}; + sprintf(param_tmp, "%s=%s", key, value); + if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) { + ret = 1; + return ret; + } + + e = ptr.last; + ret = uci_set(ctx, &ptr); + if (ret == UCI_OK) + ret = uci_save(ctx, ptr.p); + + if (ptr.p) + uci_unload(ctx, ptr.p); + return ret; +} + +int af_uci_set_int_value(struct uci_context *ctx, char *key, int value) +{ + struct uci_element *e; + struct uci_ptr ptr; + int ret = UCI_OK; + int dummy; + char *parameters ; + char param_tmp[128] = {0}; + sprintf(param_tmp, "%s=%d", key, value); + if (uci_lookup_ptr(ctx, &ptr, param_tmp, true) != UCI_OK) { + ret = 1; + return ret; + } + e = ptr.last; + ret = uci_set(ctx, &ptr); + if (ret == UCI_OK) + ret = uci_save(ctx, ptr.p); + + if (ptr.p) + uci_unload(ctx, ptr.p); + return ret; +} + +int af_uci_del_array_value(struct uci_context *ctx, char *key_fmt, int index){ + char key[128] = {0}; + sprintf(key, key_fmt, index); + return af_uci_delete(ctx, key); +} + +int af_uci_set_array_value(struct uci_context *ctx, char *key_fmt, int index, char *value){ + char key[128] = {0}; + sprintf(key, key_fmt, index); + return af_uci_set_value(ctx, key, value); +} + +int af_uci_commit(struct uci_context *ctx, const char * package) { + struct uci_ptr ptr; + int ret = UCI_OK; + if (!package){ + return -1; + } + if (uci_lookup_ptr(ctx, &ptr, package, true) != UCI_OK) { + return -1; + } + + if (uci_commit(ctx, &ptr.p, false) != UCI_OK) { + ret = -1; + goto done; + } +done: + if (ptr.p) + uci_unload(ctx, ptr.p); + + return UCI_OK; +} + +int af_get_uci_list_num(struct uci_context * ctx, char *package, char *section){ + int count = 0; + struct uci_ptr p; + struct uci_element *e; + struct uci_package *pkg = NULL; + + if (UCI_OK != uci_load(ctx, package, &pkg)){ + return -1; + } + uci_foreach_element(&pkg->sections, e){ + struct uci_section *s = uci_to_section(e); + if (strcmp(s->type, section)){ + continue; + } + count++; + } + uci_unload(ctx, pkg); + return count; +} +int af_uci_get_array_value(struct uci_context *ctx, char *key_fmt, int index, char *output, int out_len) +{ + char key[128] = {0}; + sprintf(key, key_fmt, index); + return af_uci_get_value(ctx, key, output, out_len); +} + +int af_uci_add_section(struct uci_context * ctx, char *package_name, char *section) +{ + struct uci_section *s = NULL; + struct uci_package *p = NULL; + int ret; + ret = uci_load(ctx, package_name , &p); + if (ret != UCI_OK) + goto done; + + ret = uci_add_section(ctx, p, section, &s); + if (ret != UCI_OK) + goto done; + ret = uci_save(ctx, p); +done: + if (s) + fprintf(stdout, "%s\n", s->e.name); + return ret; +} +// +static struct uci_package * +config_init_package(const char *config) +{ + struct uci_context *ctx = uci_ctx; + struct uci_package *p = NULL; + + if (!ctx) + { + ctx = uci_alloc_context(); + uci_ctx = ctx; + ctx->flags &= ~UCI_FLAG_STRICT; + //if (config_path) + // uci_set_confdir(ctx, config_path); + } + else + { + p = uci_lookup_package(ctx, config); + if (p) + uci_unload(ctx, p); + } + + if (uci_load(ctx, config, &p)) + return NULL; + + return p; +} +char *get_app_name_by_id(int id) +{ + int i; + for (i = 0; i < g_app_count; i++) + { + if (id == app_name_table[i].id) + return app_name_table[i].name; + } + return ""; +} + +void init_app_name_table(void) +{ + int count = 0; + char line_buf[2048] = {0}; + + FILE *fp = fopen("/tmp/feature.cfg", "r"); + if (!fp) + { + printf("open file failed\n"); + return; + } + g_app_count = 0; + while (fgets(line_buf, sizeof(line_buf), fp)) + { + if (strstr(line_buf, "#")) + continue; + if (strlen(line_buf) < 10) + continue; + if (!strstr(line_buf, ":")) + continue; + char *pos1 = strstr(line_buf, ":"); + char app_info_buf[128] = {0}; + int app_id; + char app_name[64] = {0}; + memset(app_name, 0x0, sizeof(app_name)); + strncpy(app_info_buf, line_buf, pos1 - line_buf); + sscanf(app_info_buf, "%d %s", &app_id, app_name); + app_name_table[g_app_count].id = app_id; + strcpy(app_name_table[g_app_count].name, app_name); + g_app_count++; + } + fclose(fp); +} + +void init_app_class_name_table(void) +{ + char line_buf[2048] = {0}; + int class_id; + char class_name[64] = {0}; + FILE *fp = fopen("/tmp/app_class.txt", "r"); + if (!fp) + { + printf("open file failed\n"); + return; + } + g_cur_class_num = 0; + while (fgets(line_buf, sizeof(line_buf), fp)) + { + sscanf(line_buf, "%d %*s %s", &class_id, class_name); + strcpy(CLASS_NAME_TABLE[class_id - 1], class_name); + g_cur_class_num++; + } + fclose(fp); +} +//00:00 9:1 +int check_time_valid(char *t) +{ + if (!t) + return 0; + if (strlen(t) < 3 || strlen(t) > 5 || (!strstr(t, ":"))) + return 0; + else + return 1; +} + + +int config_get_appfilter_enable(void) +{ + int enable = 0; + struct uci_context *ctx = uci_alloc_context(); + if (!ctx) + return -1; + enable = af_uci_get_int_value(ctx, "appfilter.global.enable"); + if (enable < 0) + enable = 0; + + uci_free_context(ctx); + return enable; +} + +int config_get_lan_ip(char *lan_ip, int len) +{ + int ret = 0; + struct uci_context *ctx = uci_alloc_context(); + if (!ctx) + return -1; + ret = af_uci_get_value(ctx, "network.lan.ipaddr", lan_ip, len); + uci_free_context(ctx); + return ret; +} + +int config_get_lan_mask(char *lan_mask, int len) +{ + int ret = 0; + struct uci_context *ctx = uci_alloc_context(); + if (!ctx) + return -1; + ret = af_uci_get_value(ctx, "network.lan.netmask", lan_mask, len); + uci_free_context(ctx); + return ret; +} + + +int appfilter_config_alloc(void) +{ + char *err; + uci_appfilter = config_init_package("appfilter"); + if (!uci_appfilter) + { + uci_get_errorstr(uci_ctx, &err, NULL); + printf("Failed to load appfilter config (%s)\n", err); + free(err); + return -1; + } + + return 0; +} + +int appfilter_config_free(void) +{ + if (uci_appfilter) + { + uci_unload(uci_ctx, uci_appfilter); + uci_appfilter = NULL; + } + if (uci_ctx) + { + uci_free_context(uci_ctx); + uci_ctx = NULL; + } +} diff --git a/open-app-filter/src/appfilter_config.h b/open-app-filter/src/appfilter_config.h new file mode 100755 index 0000000..b55a7fc --- /dev/null +++ b/open-app-filter/src/appfilter_config.h @@ -0,0 +1,66 @@ +/* +Copyright (C) 2020 Derry + +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. +*/ +#ifndef __APPFILTER_CONFIG_H__ +#define __APPFILTER_CONFIG_H__ +#include + +#define MAX_SUPPORT_APP_NUM 1024 +#define MAX_CLASS_NAME_LEN 32 +#define MAX_PARAM_LIST_LEN 1024 + +#include "appfilter_user.h" +extern int g_cur_class_num; +extern int g_app_count; +extern char CLASS_NAME_TABLE[MAX_APP_TYPE][MAX_CLASS_NAME_LEN]; + +typedef struct app_name_info +{ + int id; + char name[64]; +} app_name_info_t; +void init_app_name_table(void); +void init_app_class_name_table(void); +char *get_app_name_by_id(int id); + +int appfilter_config_alloc(void); + +int appfilter_config_free(void); +int config_get_appfilter_enable(void); +int config_get_lan_ip(char *lan_ip, int len); +int config_get_lan_mask(char *lan_mask, int len); +int af_uci_delete(struct uci_context *ctx, char *key); +int af_uci_add_list(struct uci_context *ctx, char *key, char *value); +int af_uci_add_int_list(struct uci_context *ctx, char *key, int value); +int af_uci_del_list(struct uci_context *ctx, char *key, char *value); +int af_uci_get_list_value(struct uci_context *ctx, char *key, char *output, int out_len, char *delimt); +int af_uci_set_value(struct uci_context *ctx, char *key, char *value); +int af_uci_set_int_value(struct uci_context *ctx, char *key, int value); +int af_uci_del_array_value(struct uci_context *ctx, char *key_fmt, int index); +int af_uci_set_array_value(struct uci_context *ctx, char *key_fmt, int index, char *value); +int af_get_uci_list_num(struct uci_context * ctx, char *package, char *section); +int af_uci_get_array_value(struct uci_context *ctx, char *key_fmt, int index, char *output, int out_len); +int af_uci_get_int_value(struct uci_context *ctx, char *key); +int af_uci_get_value(struct uci_context *ctx, char *key, char *output, int out_len); +int af_uci_add_section(struct uci_context * ctx, char *package_name, char *section); +int af_uci_commit(struct uci_context *ctx, const char * package); +#endif + diff --git a/open-app-filter/src/appfilter_netlink.c b/open-app-filter/src/appfilter_netlink.c new file mode 100755 index 0000000..79b0dca --- /dev/null +++ b/open-app-filter/src/appfilter_netlink.c @@ -0,0 +1,238 @@ +/* +Copyright (C) 2020 Derry + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "appfilter_user.h" +#include "appfilter_netlink.h" +#include "appfilter.h" +#define MAX_NL_RCV_BUF_SIZE 4096 + +#define REPORT_INTERVAL_SECS 60 +extern int hash_appid(int appid); +void appfilter_nl_handler(struct uloop_fd *u, unsigned int ev) +{ + int ret; + int i; + char buf[MAX_NL_RCV_BUF_SIZE]; + struct sockaddr_nl nladdr; + struct iovec iov = {buf, sizeof(buf)}; + struct nlmsghdr *h; + int type; + int id; + char *mac = NULL; + + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + do + { + ret = recvmsg(u->fd, &msg, 0); + } while ((-1 == ret) && (EINTR == errno)); + + if (ret < 0) + { + printf("recv msg error\n"); + return; + } + else if (0 == ret) + { + return; + } + + h = (struct nlmsghdr *)buf; + char *kmsg = (char *)NLMSG_DATA(h); + struct af_msg_hdr *af_hdr = (struct af_msg_hdr *)kmsg; + if (af_hdr->magic != 0xa0b0c0d0) + { + printf("magic error %x\n", af_hdr->magic); + return; + } + + if (af_hdr->len <= 0 || af_hdr->len >= MAX_OAF_NETLINK_MSG_LEN) + { + printf("data len error\n"); + return; + } + + char *kdata = kmsg + sizeof(struct af_msg_hdr); + struct json_object *root = json_tokener_parse(kdata); + if (!root) + { + printf("parse json failed:%s", kdata); + return; + } + + struct json_object *mac_obj = json_object_object_get(root, "mac"); + + if (!mac_obj) + { + printf("parse mac obj failed\n"); + json_object_put(root); + return; + } + + mac = json_object_get_string(mac_obj); + + dev_node_t *node = find_dev_node(mac); + + if (!node) + { + node = add_dev_node(mac); + if (!node) + { + printf("add dev node failed\n"); + json_object_put(root); + return; + } + } + + struct json_object *ip_obj = json_object_object_get(root, "ip"); + if (ip_obj) + strncpy(node->ip, json_object_get_string(ip_obj), sizeof(node->ip)); + struct json_object *visit_array = json_object_object_get(root, "visit_info"); + if (!visit_array) + { + json_object_put(root); + return; + } + + for (i = 0; i < json_object_array_length(visit_array); i++) + { + struct json_object *visit_obj = json_object_array_get_idx(visit_array, i); + struct json_object *appid_obj = json_object_object_get(visit_obj, "appid"); + struct json_object *action_obj = json_object_object_get(visit_obj, "latest_action"); + struct json_object *up_obj = json_object_object_get(visit_obj, "up_bytes"); + struct json_object *down_obj = json_object_object_get(visit_obj, "down_bytes"); + struct timeval cur_time; + + gettimeofday(&cur_time, NULL); + int appid = json_object_get_int(appid_obj); + int action = json_object_get_int(action_obj); + + type = appid / 1000; + id = appid % 1000; + if (id <= 0 || type <= 0) + continue; + node->stat[type - 1][id - 1].total_time += REPORT_INTERVAL_SECS; + + // node->stat[type - 1][id - 1].total_down_bytes += json_object_get_int(down_obj); + // node->stat[type - 1][id - 1].total_up_bytes += json_object_get_int(up_obj); + + int hash = hash_appid(appid); + visit_info_t *head = node->visit_htable[hash]; + visit_info_t *p = head; + while(p){ + LOG_DEBUG("appid = %d, p->appid = %d, p->latest_time = %d, cur_time.tv_sec = %d, cur_time.tv_sec - p->latest_time = %d\n", + appid, p->appid, p->latest_time, cur_time.tv_sec, cur_time.tv_sec - p->latest_time); + if((p->appid == appid) && ((cur_time.tv_sec - p->latest_time) < 300)){ + LOG_DEBUG("match appid = %d\n", appid, cur_time.tv_sec - p->latest_time); + break; + } + + p = p->next; + } + if (!p){ + p = (visit_info_t *)calloc(1, sizeof(visit_info_t)); + p->appid = appid; + p->next = NULL; + p->first_time = cur_time.tv_sec - MIN_VISIT_TIME; + add_visit_info_node(&node->visit_htable[hash], p); + } + p->action = action; + p->latest_time = cur_time.tv_sec; + } + json_object_put(root); +} + +#define MAX_NL_MSG_LEN 1024 +int send_msg_to_kernel(int fd, void *msg, int len) +{ + struct sockaddr_nl saddr, daddr; + memset(&daddr, 0, sizeof(daddr)); + daddr.nl_family = AF_NETLINK; + daddr.nl_pid = 0; // to kernel + daddr.nl_groups = 0; + int ret = 0; + struct nlmsghdr *nlh = NULL; + nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_NL_MSG_LEN)); + nlh->nlmsg_len = NLMSG_SPACE(MAX_NL_MSG_LEN); + nlh->nlmsg_flags = 0; + nlh->nlmsg_type = 0; + nlh->nlmsg_seq = 0; + nlh->nlmsg_pid = DEFAULT_USR_NL_PID; + + char msg_buf[MAX_NL_MSG_LEN] = {0}; + struct af_msg_hdr *hdr = (struct af_msg_hdr *)msg_buf; + hdr->magic = 0xa0b0c0d0; + hdr->len = len; + char *p_data = msg_buf + sizeof(struct af_msg_hdr); + memcpy(p_data, msg, len); + + memcpy(NLMSG_DATA(nlh), msg_buf, len + sizeof(struct af_msg_hdr)); + + ret = sendto(fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&daddr, sizeof(struct sockaddr_nl)); + if (!ret) + { + perror("sendto error\n"); + return -1; + } + + return 0; +} + +int appfilter_nl_init(void) +{ + int fd; + struct sockaddr_nl nls; + fd = socket(AF_NETLINK, SOCK_RAW, OAF_NETLINK_ID); + if (fd < 0) + { + LOG_DEBUG("Connect netlink %d failed %s\n", OAF_NETLINK_ID, strerror(errno)); + return -1; + } + memset(&nls, 0, sizeof(struct sockaddr_nl)); + nls.nl_pid = DEFAULT_USR_NL_PID; + nls.nl_groups = 0; + nls.nl_family = AF_NETLINK; + + if (bind(fd, (void *)&nls, sizeof(struct sockaddr_nl))) + { + LOG_DEBUG("Bind failed %s\n", strerror(errno)); + return -1; + } + + return fd; +} diff --git a/open-app-filter/src/appfilter_netlink.h b/open-app-filter/src/appfilter_netlink.h new file mode 100755 index 0000000..01e1212 --- /dev/null +++ b/open-app-filter/src/appfilter_netlink.h @@ -0,0 +1,57 @@ +/* +Copyright (C) 2020 Derry + +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. +*/ +#ifndef __APPFILTER_NETLINK_H__ +#define __APPFILTER_NETLINK_H__ +#define DEFAULT_USR_NL_PID 999 +#define OAF_NETLINK_ID 29 +#define MAX_OAF_NETLINK_MSG_LEN 1024 +#define MAX_AF_MSG_DATA_LEN 800 +#define MAX_FEATURE_LINE_LEN 600 + +struct af_msg_hdr +{ + int magic; + int len; +}; + +enum E_MSG_TYPE +{ + AF_MSG_INIT, + AF_MSG_ADD_FEATURE, + AF_MSG_CLEAN_FEATURE, + AF_MSG_MAX +}; + +typedef struct af_msg +{ + int action; +} af_msg_t; + +typedef struct af_feature_msg{ + af_msg_t hdr; + char feature[MAX_FEATURE_LINE_LEN]; +} af_feature_msg_t; + +int appfilter_nl_init(void); +void appfilter_nl_handler(struct uloop_fd *u, unsigned int ev); +int send_msg_to_kernel(int fd, void *msg, int len); +#endif \ No newline at end of file diff --git a/open-app-filter/src/appfilter_ubus.c b/open-app-filter/src/appfilter_ubus.c new file mode 100755 index 0000000..39c67d3 --- /dev/null +++ b/open-app-filter/src/appfilter_ubus.c @@ -0,0 +1,1626 @@ +/* +Copyright (C) 2020 Derry + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "appfilter_user.h" +#include "appfilter_config.h" +#include +#include "appfilter.h" +#include "utils.h" + +extern int g_oaf_config_change; + +struct ubus_context *ubus_ctx = NULL; +static struct blob_buf b; + +extern char *format_time(int timetamp); + +void reload_oaf_rule(){ + system("/usr/bin/oaf_rule reload"); +} + +void get_hostname_by_mac(char *mac, char *hostname) +{ + if (!mac || !hostname) + return; + FILE *fp = fopen("/tmp/dhcp.leases", "r"); + if (!fp) + { + printf("open dhcp lease file....failed\n"); + return; + } + char line_buf[256] = {0}; + while (fgets(line_buf, sizeof(line_buf), fp)) + { + char hostname_buf[128] = {0}; + char mac_buf[32] = {0}; + sscanf(line_buf, "%*s %s %*s %s", mac_buf, hostname_buf); + if (0 == strcmp(mac, mac_buf)) + { + strcpy(hostname, hostname_buf); + } + } + fclose(fp); +} + +void ubus_dump_visit_list(struct blob_buf *b, char *mac) +{ + int i, j; + void *c, *array; + void *t; + void *s; + + array = blobmsg_open_array(b, "dev_list"); + + for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) + { + dev_node_t *node = dev_hash_table[i]; + while (node) + { + if (mac && strcmp(mac, node->mac)) + { + node = node->next; + continue; + } + t = blobmsg_open_table(b, NULL); + blobmsg_add_string(b, "hostname", "unknown"); + blobmsg_add_string(b, "mac", node->mac); + blobmsg_add_string(b, "ip", node->ip); + void *visit_array; + + visit_array = blobmsg_open_array(b, "visit_info"); + for (j = 0; j < MAX_VISIT_HASH_SIZE; j++) + { + visit_info_t *p_info = node->visit_htable[j]; + while (p_info) + { + char *first_time_str = format_time(p_info->first_time); + char *latest_time_str = format_time(p_info->latest_time); + int total_time = p_info->latest_time - p_info->first_time; + s = blobmsg_open_table(b, NULL); + blobmsg_add_string(b, "appname", "unknown"); + blobmsg_add_u32(b, "appid", p_info->appid); + blobmsg_add_u32(b, "latest_action", p_info->action); + blobmsg_add_u32(b, "first_time", p_info->first_time); + blobmsg_add_u32(b, "latest_time", p_info->latest_time); + blobmsg_close_table(b, s); + if (first_time_str) + free(first_time_str); + if (latest_time_str) + free(latest_time_str); + p_info = p_info->next; + } + } + + blobmsg_close_array(b, visit_array); + blobmsg_close_table(b, t); + node = node->next; + } + } + blobmsg_close_array(b, array); +} + +// Function to compare JSON objects based on the "lt" field +int compare_lt(const void *a, const void *b) { + struct json_object *obj_a = *(struct json_object **)a; + struct json_object *obj_b = *(struct json_object **)b; + + struct json_object *lt_a, *lt_b; + json_object_object_get_ex(obj_a, "lt", <_a); + json_object_object_get_ex(obj_b, "lt", <_b); + + int lt_val_a = json_object_get_int(lt_a); + int lt_val_b = json_object_get_int(lt_b); + + return lt_val_b - lt_val_a; +} + + +static int +appfilter_handle_dev_visit_list(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + int i, j; + struct json_object *root_obj = json_object_new_object(); + struct json_object *visit_array = json_object_new_array(); + + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) + { + printf("format json failed\n"); + return 0; + } + + printf("msg_obj_str:%s\n", msg_obj_str); + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *mac_obj = json_object_object_get(req_obj, "mac"); + if (!mac_obj) + { + printf("mac is null\n"); + return 0; + } + + char *mac = json_object_get_string(mac_obj); + dev_node_t *node = find_dev_node(mac); + + if (!node) + { + printf("not found mac:%s\n", mac); + return 0; + } + + json_object_object_add(root_obj, "hostname", json_object_new_string(node->hostname)); + json_object_object_add(root_obj, "mac", json_object_new_string(node->mac)); + json_object_object_add(root_obj, "ip", json_object_new_string(node->ip)); + + for (j = 0; j < MAX_VISIT_HASH_SIZE; j++) + { + visit_info_t *p_info = node->visit_htable[j]; + while (p_info) + { + char *first_time_str = format_time(p_info->first_time); + char *latest_time_str = format_time(p_info->latest_time); + int total_time = p_info->latest_time - p_info->first_time; + struct json_object *visit_obj = json_object_new_object(); + json_object_object_add(visit_obj, "name", json_object_new_string(get_app_name_by_id(p_info->appid))); + json_object_object_add(visit_obj, "id", json_object_new_int(p_info->appid)); + json_object_object_add(visit_obj, "act", json_object_new_int(p_info->action)); + json_object_object_add(visit_obj, "ft", json_object_new_int(p_info->first_time)); + json_object_object_add(visit_obj, "lt", json_object_new_int(p_info->latest_time)); + json_object_object_add(visit_obj, "tt", json_object_new_int(total_time)); + json_object_array_add(visit_array, visit_obj); + + if (first_time_str) + free(first_time_str); + if (latest_time_str) + free(latest_time_str); + p_info = p_info->next; + } + } + + // Sort the visit_array based on the "lt" field + json_object_array_sort(visit_array, compare_lt); + + json_object_object_add(root_obj, "total_num", json_object_new_int(json_object_array_length(visit_array))); + json_object_object_add(root_obj, "list", visit_array); + blob_buf_init(&b, 0); + blobmsg_add_object(&b, root_obj); + ubus_send_reply(ctx, req, b.head); + json_object_put(root_obj); + return 0; +} + + +void update_app_visit_time_list(char *mac, struct app_visit_stat_info *visit_info) +{ + int i, j, s; + int num = 0; + + dev_node_t *node = find_dev_node(mac); + if (!node) + { + printf("not found mac:%s\n", mac); + return; + } + for (i = 0; i < MAX_APP_TYPE; i++) + { + for (j = 0; j < MAX_APP_ID_NUM; j++) + { + unsigned long long min = visit_info->visit_list[0].total_time; + int min_index = 0; + if (node->stat[i][j].total_time == 0) + continue; + if (num < MAX_APP_STAT_NUM) + { + min_index = num; + } + else + { + for (s = 0; s < MAX_APP_STAT_NUM; s++) + { + if (visit_info->visit_list[s].total_time < min) + { + min_index = s; + break; + } + } + } + num++; + if (node->stat[i][j].total_time > visit_info->visit_list[min_index].total_time) + { + visit_info->visit_list[min_index].total_time = node->stat[i][j].total_time; + visit_info->visit_list[min_index].app_id = (i + 1) * 1000 + j + 1; + } + } + } + if (num < MAX_APP_STAT_NUM) + visit_info->num = num; + else + visit_info->num = MAX_APP_STAT_NUM; +} + +void update_app_class_visit_time_list(char *mac, int *visit_time) +{ + int i, j, s; + int num = 0; + + dev_node_t *node = find_dev_node(mac); + if (!node) + { + printf("not found mac:%s\n", mac); + return; + } + for (i = 0; i < MAX_APP_TYPE; i++) + { + for (j = 0; j < MAX_APP_ID_NUM; j++) + { + if (node->stat[i][j].total_time == 0) + continue; + visit_time[i] += node->stat[i][j].total_time; + } + } +} + +void ubus_get_dev_visit_time_info(char *mac, struct blob_buf *b) +{ + int i, j; + void *c, *array; + void *t; + void *s; + struct app_visit_stat_info info; + memset((char *)&info, 0x0, sizeof(info)); + update_app_visit_time_list(mac, &info); +} + +static int +appfilter_handle_visit_list(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + int ret; + blob_buf_init(&b, 0); + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) + { + printf("format json failed\n"); + return 0; + } + + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *mac_obj = json_object_object_get(req_obj, "mac"); + + if (!mac_obj) + { + ubus_dump_visit_list(&b, NULL); + } + else + ubus_dump_visit_list(&b, json_object_get_string(mac_obj)); + ubus_send_reply(ctx, req, b.head); + return 0; +} +static int handle_debug(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + int ret; + blob_buf_init(&b, 0); + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) + { + printf("format json failed\n"); + return 0; + } + + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *debug_obj = json_object_object_get(req_obj, "debug"); + + if (debug_obj) + { + current_log_level = json_object_get_int(debug_obj); + LOG_WARN("debug level set to %d\n", current_log_level); + } + + ubus_send_reply(ctx, req, b.head); + return 0; +} + + + +typedef struct app_visit_time_info +{ + int app_id; + unsigned long long total_time; +} app_visit_time_info_t; + +int visit_time_compare(const void *a, const void *b) +{ + app_visit_time_info_t *p1 = (app_visit_time_info_t *)a; + app_visit_time_info_t *p2 = (app_visit_time_info_t *)b; + return p1->total_time < p2->total_time ? 1 : -1; +} + +#define MAX_STAT_APP_NUM 128 +void update_top5_app(dev_node_t *node, app_visit_time_info_t top5_app_list[]) +{ + int i, j; + //memset(app_array, 0x0, sizeof(int) *size); + app_visit_time_info_t app_visit_array[MAX_STAT_APP_NUM]; + memset(app_visit_array, 0x0, sizeof(app_visit_array)); + int app_visit_num = 0; + + for (i = 0; i < MAX_APP_TYPE; i++) + { + for (j = 0; j < MAX_APP_ID_NUM; j++) + { + if (node->stat[i][j].total_time == 0) + continue; + app_visit_array[app_visit_num].app_id = (i + 1) * 1000 + j + 1; + app_visit_array[app_visit_num].total_time = node->stat[i][j].total_time; + app_visit_num++; + } + } + + qsort((void *)app_visit_array, app_visit_num, sizeof(app_visit_time_info_t), visit_time_compare); +#if 0 +for (i = 0; i < app_visit_num; i++){ +printf("appid %d-----------total time %llu\n", app_visit_array[i].app_id, +app_visit_array[i].total_time); +} +#endif + for (i = 0; i < 5; i++) + { + top5_app_list[i] = app_visit_array[i]; + //printf("appid %d-----------total time %llu\n", app_visit_array[i].app_id, + // app_visit_array[i].total_time); + } +} + +static int +appfilter_handle_dev_list(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + int i, j; + struct json_object *root_obj = json_object_new_object(); + + struct json_object *dev_array = json_object_new_array(); + int count = 0; + for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) + { + + dev_node_t *node = dev_hash_table[i]; + while (node) + { + struct json_object *dev_obj = json_object_new_object(); + struct json_object *app_array = json_object_new_array(); + app_visit_time_info_t top5_app_list[5]; + memset(top5_app_list, 0x0, sizeof(top5_app_list)); + update_top5_app(node, top5_app_list); + + for (j = 0; j < 5; j++) + { + if (top5_app_list[j].app_id == 0) + break; + struct json_object *app_obj = json_object_new_object(); + json_object_object_add(app_obj, "id", json_object_new_int(top5_app_list[j].app_id)); + json_object_object_add(app_obj, "name", json_object_new_string(get_app_name_by_id(top5_app_list[j].app_id))); + json_object_array_add(app_array, app_obj); + } + + json_object_object_add(dev_obj, "applist", app_array); + json_object_object_add(dev_obj, "mac", json_object_new_string(node->mac)); + char hostname[128] = {0}; + get_hostname_by_mac(node->mac, hostname); + json_object_object_add(dev_obj, "ip", json_object_new_string(node->ip)); + + json_object_object_add(dev_obj, "online", json_object_new_int(1)); + json_object_object_add(dev_obj, "hostname", json_object_new_string(hostname)); + json_object_object_add(dev_obj, "nickname", json_object_new_string("")); + + + json_object_array_add(dev_array, dev_obj); + + node = node->next; + count++; + if (count >= MAX_SUPPORT_DEV_NUM) + goto END; + } + } + +END: + + json_object_object_add(root_obj, "devlist", dev_array); + blob_buf_init(&b, 0); + blobmsg_add_object(&b, root_obj); + ubus_send_reply(ctx, req, b.head); + json_object_put(root_obj); + return 0; +} + + +static int appfilter_handle_visit_time(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + int ret; + struct app_visit_stat_info info; + blob_buf_init(&b, 0); + memset((char *)&info, 0x0, sizeof(info)); + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) + { + printf("format json failed\n"); + return 0; + } + + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *mac_obj = json_object_object_get(req_obj, "mac"); + if (!mac_obj) + { + printf("mac is NULL\n"); + return 0; + } + update_app_visit_time_list(json_object_get_string(mac_obj), &info); + + struct json_object *resp_obj = json_object_new_object(); + struct json_object *app_info_array = json_object_new_array(); + json_object_object_add(resp_obj, "list", app_info_array); + json_object_object_add(resp_obj, "total_num", json_object_new_int(info.num)); + int i; + for (i = 0; i < info.num; i++) + { + struct json_object *app_info_obj = json_object_new_object(); + json_object_object_add(app_info_obj, "id", json_object_new_int(info.visit_list[i].app_id)); + json_object_object_add(app_info_obj, "name", json_object_new_string(get_app_name_by_id(info.visit_list[i].app_id))); + json_object_object_add(app_info_obj, "t", json_object_new_int(info.visit_list[i].total_time)); + json_object_array_add(app_info_array, app_info_obj); + } + + blobmsg_add_object(&b, resp_obj); + ubus_send_reply(ctx, req, b.head); + json_object_put(resp_obj); + json_object_put(req_obj); + return 0; +} + +static int +handle_app_class_visit_time(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + int ret; + int i; + blob_buf_init(&b, 0); + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) + { + printf("format json failed\n"); + return 0; + } + + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *mac_obj = json_object_object_get(req_obj, "mac"); + if (!mac_obj) + { + printf("mac is NULL\n"); + return 0; + } + int app_class_visit_time[MAX_APP_TYPE]; + memset(app_class_visit_time, 0x0, sizeof(app_class_visit_time)); + update_app_class_visit_time_list(json_object_get_string(mac_obj), app_class_visit_time); + + struct json_object *resp_obj = json_object_new_object(); + struct json_object *app_class_array = json_object_new_array(); + json_object_object_add(resp_obj, "class_list", app_class_array); + for (i = 0; i < MAX_APP_TYPE; i++) + { + if (i >= g_cur_class_num) + break; + struct json_object *app_class_obj = json_object_new_object(); + json_object_object_add(app_class_obj, "type", json_object_new_int(i)); + json_object_object_add(app_class_obj, "name", json_object_new_string(CLASS_NAME_TABLE[i])); + json_object_object_add(app_class_obj, "visit_time", json_object_new_int(app_class_visit_time[i])); + json_object_array_add(app_class_array, app_class_obj); + } + + blobmsg_add_object(&b, resp_obj); + ubus_send_reply(ctx, req, b.head); + json_object_put(resp_obj); + json_object_put(req_obj); + return 0; +} + + +static int parse_feature_cfg(struct json_object *class_list) { + FILE *file = fopen("/tmp/feature.cfg", "r"); + if (!file) { + perror("Failed to open /tmp/feature.cfg"); + return -1; + } + + char line[1024]; + char app_buf[128]; + struct json_object *current_class = NULL; + struct json_object *app_list = NULL; + + while (fgets(line, sizeof(line), file)) { + // Remove newline character + line[strcspn(line, "\n")] = 0; + + if (strncmp(line, "#class", 6) == 0) { + // New class definition + if (current_class) { + // Add the previous class to the class list + json_object_object_add(current_class, "app_list", app_list); + json_object_array_add(class_list, current_class); + } + + // Parse class name + char *name = strtok(line + 7, " "); + char *class_name = NULL; + while (name != NULL) { + class_name = name; // Keep updating class_name until the last token + name = strtok(NULL, " "); + } + current_class = json_object_new_object(); + json_object_object_add(current_class, "name", json_object_new_string(class_name)); + app_list = json_object_new_array(); + } else if (current_class) { + // Parse app definition + char *p_end = strstr(line, ":"); + if (!p_end) { + continue; + } + strncpy(app_buf, line, p_end - line); + app_buf[p_end - line] = '\0'; + char *appid_str = strtok(app_buf, " "); + char *name = strtok(NULL, " "); + if (appid_str && name) { + char combined[256]; + char icon_path[512]; + snprintf(icon_path, sizeof(icon_path), "/www/luci-static/resources/app_icons/%s.png", appid_str); + int with_icon = access(icon_path, F_OK) == 0 ? 1 : 0; // 检查文件是否存在 + snprintf(combined, sizeof(combined), "%s,%s,%d", appid_str, name, with_icon); + json_object_array_add(app_list, json_object_new_string(combined)); + } + } + } + + // Add the last class to the class list + if (current_class) { + json_object_object_add(current_class, "app_list", app_list); + json_object_array_add(class_list, current_class); + } + + fclose(file); + return 0; +} + +static int handle_get_class_list(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + struct json_object *response = json_object_new_object(); + struct json_object *class_list = json_object_new_array(); + + if (parse_feature_cfg(class_list) != 0) { + json_object_put(response); + return UBUS_STATUS_UNKNOWN_ERROR; + } + + json_object_object_add(response, "class_list", class_list); + + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + + return 0; +} +#define MAX_APPFILTER_STR_LEN 8192 +static int handle_get_app_filter(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + + int i; + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return 0; + } + char *appfilter_buf = (char *)malloc(MAX_APPFILTER_STR_LEN); + if (!appfilter_buf){ + return 0; + } + appfilter_buf[0] = '\0'; + struct json_object *response = json_object_new_object(); + struct json_object *app_list = json_object_new_array(); + af_uci_get_list_value(uci_ctx, "appfilter.rule.app_list", appfilter_buf, MAX_APPFILTER_STR_LEN - 1, " "); + char *app_id_str = strtok(appfilter_buf, " "); + while (app_id_str) { + json_object_array_add(app_list, json_object_new_int(atoi(app_id_str))); + app_id_str = strtok(NULL, " "); + } + json_object_object_add(response, "app_list", app_list); + uci_free_context(uci_ctx); + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + free(appfilter_buf); + json_object_put(response); + return 0; +} + +static int handle_set_app_filter(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + printf("handle_set_app_filter\n"); + struct json_object *response = json_object_new_object(); + int i; + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) { + printf("format json failed\n"); + return 0; + } + printf("msg_obj_str: %s\n", msg_obj_str); + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *app_list = json_object_object_get(req_obj, "app_list"); + if (!app_list) { + printf("app_list is NULL\n"); + return 0; + } + printf("app_list: %s\n", json_object_get_string(app_list)); + + // 新增代码:将 app_list_str 存储到 UCI 配置中 + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return 0; + } + + af_uci_delete(uci_ctx, "appfilter.rule.app_list"); + + int len = json_object_array_length(app_list); + for (i = 0; i < json_object_array_length(app_list); i++) { + struct json_object *app_id_obj = json_object_array_get_idx(app_list, i); + af_uci_add_int_list(uci_ctx, "appfilter.rule.app_list", json_object_get_int(app_id_obj)); + } + af_uci_commit(uci_ctx, "appfilter"); + reload_oaf_rule(); + g_oaf_config_change = 1; + + uci_free_context(uci_ctx); + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; +} + + + + +static int handle_get_app_filter_base(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + struct json_object *response = json_object_new_object(); + struct json_object *data_obj = json_object_new_object(); + int i; + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return 0; + } + int enable = 0; + int work_mode = 0; + int record_enable = 0; + enable = af_uci_get_int_value(uci_ctx, "appfilter.global.enable"); + work_mode = af_uci_get_int_value(uci_ctx, "appfilter.global.work_mode"); + record_enable = af_uci_get_int_value(uci_ctx, "appfilter.global.record_enable"); + + + json_object_object_add(data_obj, "enable", json_object_new_int(enable)); + json_object_object_add(data_obj, "work_mode", json_object_new_int(work_mode)); + json_object_object_add(data_obj, "record_enable", json_object_new_int(record_enable)); + + + json_object_object_add(response, "data", data_obj); + uci_free_context(uci_ctx); + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; +} + +static int handle_set_app_filter_base(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + printf("handle_set_app_filter_base\n"); + struct json_object *response = json_object_new_object(); + int i; + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) { + printf("format json failed\n"); + return 0; + } + printf("msg_obj_str: %s\n", msg_obj_str); + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *enable_obj = json_object_object_get(req_obj, "enable"); + struct json_object *record_enable_obj = json_object_object_get(req_obj, "record_enable"); + struct json_object *work_mode_obj = json_object_object_get(req_obj, "work_mode"); + if (!enable_obj || !work_mode_obj) { + printf("enable_obj or work_mode_obj is NULL\n"); + return 0; + } + printf("enable_obj: %d\n", json_object_get_int(enable_obj)); + printf("work_mode_obj: %d\n", json_object_get_int(work_mode_obj)); + + + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return 0; + } + + af_uci_set_int_value(uci_ctx, "appfilter.global.enable", json_object_get_int(enable_obj)); + af_uci_set_int_value(uci_ctx, "appfilter.global.work_mode", json_object_get_int(work_mode_obj)); + + if (record_enable_obj) + af_uci_set_int_value(uci_ctx, "appfilter.global.record_enable", json_object_get_int(record_enable_obj)); + else + af_uci_set_int_value(uci_ctx, "appfilter.global.record_enable", 0); + + + af_uci_commit(uci_ctx, "appfilter"); + reload_oaf_rule(); + g_oaf_config_change = 1; + uci_free_context(uci_ctx); + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; +} + + +static int handle_get_app_filter_adv(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + struct json_object *response = json_object_new_object(); + struct json_object *data_obj = json_object_new_object(); + + int i; + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return 0; + } + char lan_ifname[16]; + + int tcp_rst = af_uci_get_int_value(uci_ctx, "appfilter.global.tcp_rst"); + af_uci_get_value(uci_ctx, "appfilter.global.lan_ifname", lan_ifname, sizeof(lan_ifname)); + int disable_hnat = af_uci_get_int_value(uci_ctx, "appfilter.global.disable_hnat"); + int auto_load_engine = af_uci_get_int_value(uci_ctx, "appfilter.global.auto_load_engine"); + + json_object_object_add(data_obj, "tcp_rst", json_object_new_int(tcp_rst)); + json_object_object_add(data_obj, "lan_ifname", json_object_new_string(lan_ifname)); + json_object_object_add(data_obj, "disable_hnat", json_object_new_int(disable_hnat)); + json_object_object_add(data_obj, "auto_load_engine", json_object_new_int(auto_load_engine)); + + json_object_object_add(response, "data", data_obj); + uci_free_context(uci_ctx); + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; +} +static int handle_set_app_filter_adv(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + struct json_object *response = json_object_new_object(); + int i; + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) { + printf("format json failed\n"); + return 0; + } + printf("msg_obj_str: %s\n", msg_obj_str); + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *tcp_rst_obj = json_object_object_get(req_obj, "tcp_rst"); + struct json_object *lan_ifname_obj = json_object_object_get(req_obj, "lan_ifname"); + struct json_object *disable_hnat_obj = json_object_object_get(req_obj, "disable_hnat"); + struct json_object *auto_load_engine_obj = json_object_object_get(req_obj, "auto_load_engine"); + + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return 0; + } + + if (tcp_rst_obj) + af_uci_set_int_value(uci_ctx, "appfilter.global.tcp_rst", json_object_get_int(tcp_rst_obj)); + if (lan_ifname_obj) + af_uci_set_value(uci_ctx, "appfilter.global.lan_ifname", json_object_get_string(lan_ifname_obj)); + if (disable_hnat_obj) + af_uci_set_int_value(uci_ctx, "appfilter.global.disable_hnat", json_object_get_int(disable_hnat_obj)); + if (auto_load_engine_obj){ + af_uci_set_int_value(uci_ctx, "appfilter.global.auto_load_engine", json_object_get_int(auto_load_engine_obj)); + if (json_object_get_int(auto_load_engine_obj) == 0){ + system("rm /etc/modules.d/oaf"); + } + } + + + af_uci_commit(uci_ctx, "appfilter"); + g_oaf_config_change = 1; + reload_oaf_rule(); + system("/usr/bin/hnat.sh &"); + uci_free_context(uci_ctx); + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; +} + + +static int handle_get_app_filter_time(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + struct json_object *response = json_object_new_object(); + struct json_object *data_obj = json_object_new_object(); + + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return 0; + } + + // Get time_mode + int time_mode = af_uci_get_int_value(uci_ctx, "appfilter.time.time_mode"); + json_object_object_add(data_obj, "mode", json_object_new_int(time_mode)); + + // Get days + char days_str[128] = {0}; + af_uci_get_value(uci_ctx, "appfilter.time.days", days_str, sizeof(days_str)); + printf("days_str: %s\n", days_str); + struct json_object *days_array = json_object_new_array(); + char *day = strtok(days_str, " "); + while (day) { + json_object_array_add(days_array, json_object_new_int(atoi(day))); + day = strtok(NULL, " "); + } + json_object_object_add(data_obj, "weekday_list", days_array); + + // Get start_time and end_time + char start_time[32] = {0}; + char end_time[32] = {0}; + af_uci_get_value(uci_ctx, "appfilter.time.start_time", start_time, sizeof(start_time)); + af_uci_get_value(uci_ctx, "appfilter.time.end_time", end_time, sizeof(end_time)); + json_object_object_add(data_obj, "start_time", json_object_new_string(start_time)); + json_object_object_add(data_obj, "end_time", json_object_new_string(end_time)); + + // Get deny_time and allow_time + int deny_time = af_uci_get_int_value(uci_ctx, "appfilter.time.deny_time"); + int allow_time = af_uci_get_int_value(uci_ctx, "appfilter.time.allow_time"); + json_object_object_add(data_obj, "deny_time", json_object_new_int(deny_time)); + json_object_object_add(data_obj, "allow_time", json_object_new_int(allow_time)); + + // Get time list and parse into objects with start_time and end_time + char time_str[512] = {0}; + af_uci_get_list_value(uci_ctx, "appfilter.time.time", time_str, sizeof(time_str), " "); + struct json_object *time_array = json_object_new_array(); + char *time_period = strtok(time_str, " "); + while (time_period) { + char start[16] = {0}; + char end[16] = {0}; + char *delimiter = strchr(time_period, '-'); + if (delimiter) { + // Copy start_time (characters before '-') + strncpy(start, time_period, delimiter - time_period); + start[delimiter - time_period] = '\0'; + + // Copy end_time (characters after '-') + strcpy(end, delimiter + 1); + + // Create time period object + struct json_object *period_obj = json_object_new_object(); + json_object_object_add(period_obj, "start", json_object_new_string(start)); + json_object_object_add(period_obj, "end", json_object_new_string(end)); + json_object_array_add(time_array, period_obj); + } + time_period = strtok(NULL, " "); + } + json_object_object_add(data_obj, "time_list", time_array); + + json_object_object_add(response, "data", data_obj); + + uci_free_context(uci_ctx); + + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; +} + + +// {"end_time":"12:00","weekday_list":[1,2,3,4,5,6,0],"deny_time":5,"start_time":"22:22","allow_time":30,"mode":1} +// {"mode":0,"weekday_list":[1,2],"time_list":[{"start":"00:11","end":"00:12"},{"start":"12:00","end":"14:00"}]} +static int handle_set_app_filter_time(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + printf("set appfilter time\n"); + int mode = 0; + struct json_object *response = json_object_new_object(); + int i; + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) { + printf("format json failed\n"); + return 0; + } + printf("msg_obj_str: %s\n", msg_obj_str); + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *mode_obj = json_object_object_get(req_obj, "mode"); + struct json_object *weekday_list_obj = json_object_object_get(req_obj, "weekday_list"); + if (!mode_obj || !weekday_list_obj) { + printf("mode_obj or weekday_list_obj is NULL\n"); + return 0; + } + printf("mode_obj: %d\n", json_object_get_int(mode_obj)); + + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return 0; + } + mode = json_object_get_int(mode_obj); + af_uci_set_int_value(uci_ctx, "appfilter.time.time_mode", mode); + + // Build days string from weekday array + char days_str[128] = {0}; + for (i = 0; i < json_object_array_length(weekday_list_obj); i++) { + struct json_object *weekday_obj = json_object_array_get_idx(weekday_list_obj, i); + char tmp[8]; + snprintf(tmp, sizeof(tmp), "%d", json_object_get_int(weekday_obj)); + if (i > 0) strcat(days_str, " "); + strcat(days_str, tmp); + } + af_uci_set_value(uci_ctx, "appfilter.time.days", days_str); + + if (mode == 0) { + struct json_object *time_list_obj = json_object_object_get(req_obj, "time_list"); + if (!time_list_obj) { + printf("time_list_obj is NULL\n"); + goto EXIT; + } + af_uci_delete(uci_ctx, "appfilter.time.time"); + int time_list_len = json_object_array_length(time_list_obj); + for (i = 0; i < time_list_len; i++) { + struct json_object *time_obj = json_object_array_get_idx(time_list_obj, i); + struct json_object *start_time_obj = json_object_object_get(time_obj, "start"); + struct json_object *end_time_obj = json_object_object_get(time_obj, "end"); + if (!start_time_obj || !end_time_obj) { + printf("start_time_obj or end_time_obj is NULL\n"); + goto EXIT; + } + char time_str[128] = {0}; + sprintf(time_str, "%s-%s", json_object_get_string(start_time_obj), json_object_get_string(end_time_obj)); + printf("time_str: %s\n", time_str); + af_uci_add_list(uci_ctx, "appfilter.time.time", time_str); + } + } + else { + struct json_object *deny_time_obj = json_object_object_get(req_obj, "deny_time"); + struct json_object *allow_time_obj = json_object_object_get(req_obj, "allow_time"); + struct json_object *start_time_obj = json_object_object_get(req_obj, "start_time"); + struct json_object *end_time_obj = json_object_object_get(req_obj, "end_time"); + if (!deny_time_obj || !allow_time_obj || !start_time_obj || !end_time_obj) { + printf("deny_time_obj or allow_time_obj or start_time_obj or end_time_obj is NULL\n"); + goto EXIT; + } + af_uci_set_int_value(uci_ctx, "appfilter.time.deny_time", json_object_get_int(deny_time_obj)); + af_uci_set_int_value(uci_ctx, "appfilter.time.allow_time", json_object_get_int(allow_time_obj)); + af_uci_set_value(uci_ctx, "appfilter.time.start_time", json_object_get_string(start_time_obj)); + af_uci_set_value(uci_ctx, "appfilter.time.end_time", json_object_get_string(end_time_obj)); + } + af_uci_commit(uci_ctx, "appfilter"); + g_oaf_config_change = 1; + printf("uci commit ok\n"); + // reload_oaf_rule(); +EXIT: + uci_free_context(uci_ctx); + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; +} + +typedef struct all_users_info { + int flag; + struct json_object *users_array; +} all_users_info_t; + +void all_users_callback(void *arg, dev_node_t *dev) +{ + int flag = 0; + int i; + all_users_info_t *au_info = (all_users_info_t *)arg; + flag = au_info->flag; + struct json_object *users_array = au_info->users_array; + + if (json_object_array_length(users_array) >= MAX_SUPPORT_DEV_NUM) + { + printf("users_array length >= MAX_SUPPORT_DEV_NUM\n"); + return; + } + + struct json_object *user_obj = json_object_new_object(); + json_object_object_add(user_obj, "mac", json_object_new_string(dev->mac)); + json_object_object_add(user_obj, "online", json_object_new_int(dev->online)); + json_object_object_add(user_obj, "online_time", json_object_new_int(dev->online_time)); + json_object_object_add(user_obj, "offline_time", json_object_new_int(dev->offline_time)); + + if (flag > 0) { + json_object_object_add(user_obj, "ip", json_object_new_string(dev->ip)); + + } + + if (flag > 1){ + json_object_object_add(user_obj, "hostname", json_object_new_string(dev->hostname)); + json_object_object_add(user_obj, "nickname", json_object_new_string(dev->nickname)); + } + + if (flag > 2){ + struct json_object *app_array = json_object_new_array(); + app_visit_time_info_t top5_app_list[5]; + memset(top5_app_list, 0x0, sizeof(top5_app_list)); + update_top5_app(dev, top5_app_list); + for (i = 0; i < 5; i++) + { + if (top5_app_list[i].app_id == 0) + break; + + struct json_object *app_obj = json_object_new_object(); + json_object_object_add(app_obj, "id", json_object_new_int(top5_app_list[i].app_id)); + json_object_object_add(app_obj, "name", json_object_new_string(get_app_name_by_id(top5_app_list[i].app_id))); + + json_object_array_add(app_array, app_obj); + } + json_object_object_add(user_obj, "applist", app_array); + } + json_object_array_add(users_array, user_obj); +} + +int compare_users(const void *a, const void *b) +{ + struct json_object *user_a = *(struct json_object **)a; + struct json_object *user_b = *(struct json_object **)b; + + struct json_object *online_a, *online_b; + json_object_object_get_ex(user_a, "online", &online_a); + json_object_object_get_ex(user_b, "online", &online_b); + + int online_val_a = json_object_get_int(online_a); + int online_val_b = json_object_get_int(online_b); + + if (online_val_a != online_val_b) + return online_val_b - online_val_a; + + struct json_object *online_time_a, *online_time_b; + json_object_object_get_ex(user_a, "online_time", &online_time_a); + json_object_object_get_ex(user_b, "online_time", &online_time_b); + + int online_time_val_a = json_object_get_int(online_time_a); + int online_time_val_b = json_object_get_int(online_time_b); + + if (online_val_a == 1 && online_val_b == 1) { + // Both are online, sort by online_time + return online_time_val_a - online_time_val_b; + } else { + // Both are offline, sort by offline_time + struct json_object *offline_time_a, *offline_time_b; + json_object_object_get_ex(user_a, "offline_time", &offline_time_a); + json_object_object_get_ex(user_b, "offline_time", &offline_time_b); + + int offline_time_val_a = json_object_get_int(offline_time_a); + int offline_time_val_b = json_object_get_int(offline_time_b); + + return offline_time_val_a - offline_time_val_b; + } +} + +static int handle_get_all_users(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + struct json_object *response = json_object_new_object(); + struct json_object *data_obj = json_object_new_object(); + int flag = 0; + int page = 0; + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return 0; + } + + char *msg_obj_str = blobmsg_format_json(msg, true); + if (msg_obj_str) + { + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *flag_obj = json_object_object_get(req_obj, "flag"); + struct json_object *page_obj = json_object_object_get(req_obj, "page"); + if (flag_obj) { + flag = json_object_get_int(flag_obj); + } + if (page_obj) { + page = json_object_get_int(page_obj); + } + } + + printf("flag: %d, page: %d\n", flag, page); + all_users_info_t au_info; + au_info.flag = flag; + au_info.users_array = json_object_new_array(); + + struct json_object *users_array = json_object_new_array(); + + update_dev_nickname(); + + dev_foreach(&au_info, all_users_callback); + + // 对 users_array 进行排序 + json_object_array_sort(au_info.users_array, compare_users); + + json_object_object_add(data_obj, "list", au_info.users_array); + + json_object_object_add(response, "data", data_obj); + + uci_free_context(uci_ctx); + + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; +} + + +static int handle_get_app_filter_user(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + struct json_object *response = json_object_new_object(); + struct json_object *data_obj = json_object_new_object(); + + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return 0; + } + + int mode = af_uci_get_int_value(uci_ctx, "appfilter.global.user_mode"); + if (mode < 0) + mode = 0; + json_object_object_add(data_obj, "mode", json_object_new_int(mode)); + + struct json_object *user_array = json_object_new_array(); + char mac_str[128] = {0}; + int num = af_get_uci_list_num(uci_ctx, "appfilter", "af_user"); + for (int i = 0; i < num; i++) { + af_uci_get_array_value(uci_ctx, "appfilter.@af_user[%d].mac", i, mac_str, sizeof(mac_str)); + + struct json_object *user_obj = json_object_new_object(); + json_object_object_add(user_obj, "mac", json_object_new_string(mac_str)); + dev_node_t *dev = find_dev_node(mac_str); + if (dev){ + json_object_object_add(user_obj, "nickname", json_object_new_string(dev->nickname)); + json_object_object_add(user_obj, "hostname", json_object_new_string(dev->hostname)); + }else{ + json_object_object_add(user_obj, "nickname", json_object_new_string("")); + json_object_object_add(user_obj, "hostname", json_object_new_string("")); + } + json_object_array_add(user_array, user_obj); + } + + json_object_object_add(data_obj, "list", user_array); + json_object_object_add(response, "data", data_obj); + + uci_free_context(uci_ctx); + + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; + +} + + +static int handle_set_app_filter_user(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + struct json_object *response = json_object_new_object(); + int i; + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) { + printf("format json failed\n"); + return 0; + } + printf("msg_obj_str: %s\n", msg_obj_str); + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *mode_object = json_object_object_get(req_obj, "mode"); + if (!mode_object) { + printf("mode_object is NULL\n"); + return 0; + } + printf("mode_object: %d\n", json_object_get_int(mode_object)); + + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return 0; + } + + af_uci_set_int_value(uci_ctx, "appfilter.global.user_mode", json_object_get_int(mode_object)); + af_uci_commit(uci_ctx, "appfilter"); + reload_oaf_rule(); + + uci_free_context(uci_ctx); + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; +} + + + + +static int handle_del_app_filter_user(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + printf("handle_del_app_filter_user\n"); + struct json_object *response = json_object_new_object(); + int i; + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) { + printf("format json failed\n"); + return 0; + } + printf("msg_obj_str: %s\n", msg_obj_str); + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *mac_obj = json_object_object_get(req_obj, "mac"); + if (!mac_obj) { + printf("mac_obj is NULL\n"); + return 0; + } + printf("mac: %s\n", json_object_get_string(mac_obj)); + + + + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return 0; + } + char mac_str[128] = {0}; + int num = af_get_uci_list_num(uci_ctx, "appfilter", "af_user"); + for (int i = 0; i < num; i++) { + af_uci_get_array_value(uci_ctx, "appfilter.@af_user[%d].mac", i, mac_str, sizeof(mac_str)); + if (strcmp(mac_str, json_object_get_string(mac_obj)) == 0) { + printf("delete af_user[%d]\n", i); + + char buf[128] = {0}; + sprintf(buf, "appfilter.@af_user[%d]", i); + af_uci_delete(uci_ctx, buf); + break; + } + } + + af_uci_commit(uci_ctx, "appfilter"); + reload_oaf_rule(); + + uci_free_context(uci_ctx); + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; +} + + + + +static int handle_add_app_filter_user(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + printf("handle_add_app_filter_user\n"); + struct json_object *response = json_object_new_object(); + int i; + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) { + printf("format json failed\n"); + return -1; + } + printf("msg_obj_str: %s\n", msg_obj_str); + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *mac_array = json_object_object_get(req_obj, "mac_list"); + if (!mac_array) + return -1; + + + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return -1; + } + + int len = json_object_array_length(mac_array); + printf("len: %d\n", len); + for (int i = 0; i < len; i++) { + struct json_object *mac_obj = json_object_array_get_idx(mac_array, i); + af_uci_add_section(uci_ctx, "appfilter", "af_user"); + af_uci_set_value(uci_ctx, "appfilter.@af_user[-1].mac", json_object_get_string(mac_obj)); + } + printf("add af_user ok\n"); + af_uci_commit(uci_ctx, "appfilter"); + reload_oaf_rule(); + + uci_free_context(uci_ctx); + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; +} + +static int handle_set_nickname(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + + struct json_object *response = json_object_new_object(); + int i; + char *msg_obj_str = blobmsg_format_json(msg, true); + if (!msg_obj_str) { + printf("format json failed\n"); + return -1; + } + printf("msg_obj_str: %s\n", msg_obj_str); + struct json_object *req_obj = json_tokener_parse(msg_obj_str); + struct json_object *mac_obj = json_object_object_get(req_obj, "mac"); + + struct json_object *nickname_obj = json_object_object_get(req_obj, "nickname"); + if (!nickname_obj || !mac_obj) + return -1; + + + struct uci_context *uci_ctx = uci_alloc_context(); + if (!uci_ctx) { + printf("Failed to allocate UCI context\n"); + return -1; + } + int num = af_get_uci_list_num(uci_ctx, "user_info", "user_info"); + char mac_str[128] = {0}; + int index = -1; + for (i = 0; i < num; i++) { + af_uci_get_array_value(uci_ctx, "user_info.@user_info[%d].mac", i, mac_str, sizeof(mac_str)); + if (strcmp(mac_str, json_object_get_string(mac_obj)) == 0) { + index = i; + printf("found nickname index: %d\n", index); + break; + } + } + + if (strlen(json_object_get_string(nickname_obj)) > 0) { + if (index == -1) { + af_uci_add_section(uci_ctx, "user_info", "user_info"); + } + af_uci_set_array_value(uci_ctx, "user_info.@user_info[%d].mac", index, json_object_get_string(mac_obj)); + af_uci_set_array_value(uci_ctx, "user_info.@user_info[%d].nickname", index, json_object_get_string(nickname_obj)); + } + else{ + char uci_option[128] = {0}; + sprintf(uci_option, "user_info.@user_info[%d]", index); + af_uci_delete(uci_ctx, uci_option); + printf("delete nickname mac = %s\n", json_object_get_string(mac_obj)); + } + + + af_uci_commit(uci_ctx, "user_info"); + reload_oaf_rule(); + + uci_free_context(uci_ctx); + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; +} + +extern af_run_time_status_t g_af_status; + + +static int handle_get_oaf_status(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) { + struct json_object *response = json_object_new_object(); + struct json_object *data_obj = json_object_new_object(); + char result[128] = {0}; + char kernel_version[128] = {0}; + int enable = 0; + int ret = 0; + int engine_status = 0; + + ret = exec_with_result_line("cat /proc/sys/oaf/enable", result, sizeof(result)); + if (strlen(result) == 0){ + engine_status = 0; + enable = 0; + } + else{ + enable = atoi(result); + engine_status = 1; + } + + json_object_object_add(data_obj, "enable", json_object_new_int(enable)); + json_object_object_add(data_obj, "engine_status", json_object_new_int(engine_status)); + ret = exec_with_result_line("uname -r", kernel_version, sizeof(kernel_version)); + if (ret >= 0){ + json_object_object_add(data_obj, "kernel_version", json_object_new_string(kernel_version)); + } + else{ + json_object_object_add(data_obj, "kernel_version", json_object_new_string("")); + } + + json_object_object_add(data_obj, "config_enable", json_object_new_int(g_af_config.global.enable)); + json_object_object_add(data_obj, "time_mode", json_object_new_int(g_af_config.time.time_mode)); + json_object_object_add(data_obj, "match_time", json_object_new_int(g_af_status.match_time)); + + if (g_af_config.time.time_mode == 1) { + json_object_object_add(data_obj, "filter", json_object_new_int(g_af_status.filter)); + if (g_af_status.filter == 1) { // 过滤中 + json_object_object_add(data_obj, "remain_time", json_object_new_int(g_af_config.time.deny_time - g_af_status.deny_time)); + } + else { + json_object_object_add(data_obj, "remain_time", json_object_new_int(g_af_config.time.allow_time - g_af_status.allow_time)); + } + } + + + json_object_object_add(response, "data", data_obj); + + struct blob_buf b = {}; + blob_buf_init(&b, 0); + blobmsg_add_object(&b, response); + ubus_send_reply(ctx, req, b.head); + blob_buf_free(&b); + json_object_put(response); + return 0; + +} + +static const struct blobmsg_policy empty_policy[1] = { + //[DEV_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, +}; + +static struct ubus_method appfilter_object_methods[] = { + UBUS_METHOD("dev_visit_list", appfilter_handle_dev_visit_list, empty_policy), + UBUS_METHOD("dev_visit_time", appfilter_handle_visit_time, empty_policy), + UBUS_METHOD("app_class_visit_time", handle_app_class_visit_time, empty_policy), + UBUS_METHOD("dev_list", appfilter_handle_dev_list, empty_policy), + UBUS_METHOD("class_list", handle_get_class_list, empty_policy), + UBUS_METHOD("set_app_filter", handle_set_app_filter, empty_policy), + UBUS_METHOD("get_app_filter", handle_get_app_filter, empty_policy), + UBUS_METHOD("set_app_filter_base", handle_set_app_filter_base, empty_policy), + UBUS_METHOD("get_app_filter_base", handle_get_app_filter_base, empty_policy), + UBUS_METHOD("set_app_filter_adv", handle_set_app_filter_adv, empty_policy), + UBUS_METHOD("get_app_filter_adv", handle_get_app_filter_adv, empty_policy), + UBUS_METHOD("set_app_filter_time", handle_set_app_filter_time, empty_policy), + UBUS_METHOD("get_app_filter_time", handle_get_app_filter_time, empty_policy), + UBUS_METHOD("get_all_users", handle_get_all_users, empty_policy), + UBUS_METHOD("get_app_filter_user", handle_get_app_filter_user, empty_policy), + UBUS_METHOD("set_app_filter_user", handle_set_app_filter_user, empty_policy), + UBUS_METHOD("del_app_filter_user", handle_del_app_filter_user, empty_policy), + UBUS_METHOD("add_app_filter_user", handle_add_app_filter_user, empty_policy), + UBUS_METHOD("set_nickname", handle_set_nickname, empty_policy), + UBUS_METHOD("get_oaf_status", handle_get_oaf_status, empty_policy), + UBUS_METHOD("debug", handle_debug, empty_policy), +}; + + + + +static struct ubus_object_type main_object_type = + UBUS_OBJECT_TYPE("appfilter", appfilter_object_methods); + +static struct ubus_object main_object = { + .name = "appfilter", + .type = &main_object_type, + .methods = appfilter_object_methods, + .n_methods = ARRAY_SIZE(appfilter_object_methods), +}; + +static void appfilter_add_object(struct ubus_object *obj) +{ + int ret = ubus_add_object(ubus_ctx, obj); + + if (ret != 0) + fprintf(stderr, "Failed to publish object '%s': %s\n", obj->name, ubus_strerror(ret)); +} + +int appfilter_ubus_init(void) +{ + ubus_ctx = ubus_connect("/var/run/ubus/ubus.sock"); + if (!ubus_ctx){ + ubus_ctx = ubus_connect("/var/run/ubus.sock"); + } + if (!ubus_ctx){ + return -EIO; + } + + appfilter_add_object(&main_object); + ubus_add_uloop(ubus_ctx); + return 0; +} diff --git a/open-app-filter/src/appfilter_ubus.h b/open-app-filter/src/appfilter_ubus.h new file mode 100755 index 0000000..c53dde5 --- /dev/null +++ b/open-app-filter/src/appfilter_ubus.h @@ -0,0 +1,25 @@ +/* + Copyright (C) 2020 Derry + + 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. +*/ +#ifndef __APPFILTER_UBUS_H__ +#define __APPFILTER_UBUS_H__ +int appfilter_ubus_init(void); +#endif \ No newline at end of file diff --git a/open-app-filter/src/appfilter_user.c b/open-app-filter/src/appfilter_user.c new file mode 100755 index 0000000..15647b6 --- /dev/null +++ b/open-app-filter/src/appfilter_user.c @@ -0,0 +1,577 @@ +/* +Copyright (C) 2020 Derry + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "appfilter_config.h" +#include "appfilter.h" +#include "appfilter_user.h" + +dev_node_t *dev_hash_table[MAX_DEV_NODE_HASH_SIZE]; +int g_cur_user_num = 0; +unsigned int hash_mac(unsigned char *mac) +{ + if (!mac) + return 0; + else + return mac[0] & (MAX_DEV_NODE_HASH_SIZE - 1); +} +int get_timestamp(void) +{ + struct timeval cur_time; + gettimeofday(&cur_time, NULL); + return cur_time.tv_sec; +} + +int hash_appid(int appid) +{ + return appid % (MAX_VISIT_HASH_SIZE - 1); +} + +void add_visit_info_node(visit_info_t **head, visit_info_t *node) +{ + if (*head == NULL) + { + *head = node; + } + else + { + node->next = *head; + *head = node; + } +} + +void init_dev_node_htable() +{ + int i; + for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) + { + dev_hash_table[i] = NULL; + } + printf("init dev node htable ok...\n"); +} + +dev_node_t *add_dev_node(char *mac) +{ + unsigned int hash = 0; + if (g_cur_user_num >= MAX_SUPPORT_USER_NUM) + { + printf("error, user num reach max %d\n", g_cur_user_num); + return NULL; + } + hash = hash_mac(mac); + if (hash >= MAX_DEV_NODE_HASH_SIZE) + { + printf("hash code error %d\n", hash); + return NULL; + } + dev_node_t *node = (dev_node_t *)calloc(1, sizeof(dev_node_t)); + if (!node) + return NULL; + strncpy(node->mac, mac, sizeof(node->mac)); + node->online = 1; + node->online_time = get_timestamp(); + if (dev_hash_table[hash] == NULL) + dev_hash_table[hash] = node; + else + { + node->next = dev_hash_table[hash]; + dev_hash_table[hash] = node; + } + g_cur_user_num++; + printf("add mac:%s to htable[%d]....success\n", mac, hash); + return node; +} + +dev_node_t *find_dev_node(char *mac) +{ + unsigned int hash = 0; + dev_node_t *p = NULL; + hash = hash_mac(mac); + if (hash >= MAX_DEV_NODE_HASH_SIZE) + { + printf("hash code error %d\n", hash); + return NULL; + } + p = dev_hash_table[hash]; + while (p) + { + if (0 == strncmp(p->mac, mac, sizeof(p->mac))) + { + return p; + } + p = p->next; + } + return NULL; +} + +void dev_foreach(void *arg, iter_func iter) +{ + int i, j; + dev_node_t *node = NULL; + + for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) + { + dev_node_t *node = dev_hash_table[i]; + while (node) + { + iter(arg, node); + node = node->next; + } + } +} + +char *format_time(int timetamp) +{ + char time_buf[64] = {0}; + time_t seconds = timetamp; + struct tm *auth_tm = localtime(&seconds); + strftime(time_buf, sizeof(time_buf), "%Y %m %d %H:%M:%S", auth_tm); + return strdup(time_buf); +} + +void update_dev_hostname(void) +{ + char line_buf[256] = {0}; + char hostname_buf[128] = {0}; + char mac_buf[32] = {0}; + char ip_buf[32] = {0}; + + FILE *fp = fopen("/tmp/dhcp.leases", "r"); + if (!fp) + { + printf("open dhcp lease file....failed\n"); + return; + } + while (fgets(line_buf, sizeof(line_buf), fp)) + { + if (strlen(line_buf) <= 16) + continue; + sscanf(line_buf, "%*s %s %s %s", mac_buf, ip_buf, hostname_buf); + dev_node_t *node = find_dev_node(mac_buf); + if (!node) + { + node = add_dev_node(mac_buf); + strncpy(node->ip, ip_buf, sizeof(node->ip)); + node->online = 0; + node->offline_time = get_timestamp(); + } + + if (strlen(hostname_buf) > 0 && hostname_buf[0] != '*') + { + strncpy(node->hostname, hostname_buf, sizeof(node->hostname)); + } + } + fclose(fp); +} + +void clean_dev_nickname_iter(void *arg, dev_node_t *node) +{ + node->nickname[0] = '\0'; +} + +void clean_dev_nickname(void) +{ + dev_foreach(NULL, clean_dev_nickname_iter); +} + +void update_dev_nickname(void) +{ + char nickname_buf[128] = {0}; + char mac_str[128] = {0}; + struct uci_context *uci_ctx = uci_alloc_context(); + clean_dev_nickname(); + int num = af_get_uci_list_num(uci_ctx, "user_info", "user_info"); + + for (int i = 0; i < num; i++) { + af_uci_get_array_value(uci_ctx, "user_info.@user_info[%d].mac", i, mac_str, sizeof(mac_str)); + dev_node_t *node = find_dev_node(mac_str); + if (!node) + continue; + + af_uci_get_array_value(uci_ctx, "user_info.@user_info[%d].nickname", i, nickname_buf, sizeof(nickname_buf)); + printf("update dev nickname: %s\n", nickname_buf); + strncpy(node->nickname, nickname_buf, sizeof(node->nickname)); + } + printf("update dev nickname ok\n"); + uci_free_context(uci_ctx); +} + + + +void clean_dev_online_status(void) +{ + int i; + for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) + { + dev_node_t *node = dev_hash_table[i]; + while (node) + { + + if (node->online) + { + node->offline_time = get_timestamp(); + node->online = 0; + } + node = node->next; + } + } + +} + +/* +Id Mac Ip +1 10:bf:48:37:0c:94 192.168.66.244 +*/ +void update_dev_from_oaf(void) +{ + char line_buf[256] = {0}; + char mac_buf[32] = {0}; + char ip_buf[32] = {0}; + + FILE *fp = fopen("/proc/net/af_client", "r"); + if (!fp) + { + printf("open dev file....failed\n"); + return; + } + fgets(line_buf, sizeof(line_buf), fp); // title + while (fgets(line_buf, sizeof(line_buf), fp)) + { + sscanf(line_buf, "%*s %s %s", mac_buf, ip_buf); + if (strlen(mac_buf) < 17) + { + printf("invalid mac:%s\n", mac_buf); + continue; + } + dev_node_t *node = find_dev_node(mac_buf); + if (!node) + { + node = add_dev_node(mac_buf); + if (!node) + continue; + strncpy(node->ip, ip_buf, sizeof(node->ip)); + } + node->online = 1; + } + fclose(fp); +} + +void update_dev_online_status(void) +{ + update_dev_from_oaf(); +} + +#define DEV_OFFLINE_TIME (SECONDS_PER_DAY * 3) + +int check_dev_expire(void) +{ + int i, j; + int count = 0; + int cur_time = get_timestamp(); + int offline_time = 0; + int expire_count = 0; + int visit_count = 0; + for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) + { + dev_node_t *node = dev_hash_table[i]; + while (node) + { + if (node->online) + goto NEXT; + visit_count = 0; + offline_time = cur_time - node->offline_time; + if (offline_time > DEV_OFFLINE_TIME) + { + node->expire = 1; + for (j = 0; j < MAX_VISIT_HASH_SIZE; j++) + { + visit_info_t *p_info = node->visit_htable[j]; + while (p_info) + { + p_info->expire = 1; + visit_count++; + p_info = p_info->next; + } + } + expire_count++; + LOG_WARN("dev:%s expired, offline time = %ds, count=%d, visit_count=%d\n", + node->mac, offline_time, expire_count, visit_count); + } + NEXT: + node = node->next; + } + } + return expire_count; +} + +void flush_dev_expire_node(void) +{ + int i, j; + int count = 0; + dev_node_t *node = NULL; + dev_node_t *prev = NULL; + for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) + { + dev_node_t *node = dev_hash_table[i]; + prev = NULL; + while (node) + { + if (node->expire) + { + if (NULL == prev) + { + dev_hash_table[i] = node->next; + free(node); + node = dev_hash_table[i]; + prev = NULL; + } + else + { + prev->next = node->next; + free(node); + node = prev->next; + } + } + else + { + prev = node; + node = node->next; + } + } + } +} + + +void update_dev_list(void) +{ + clean_dev_online_status(); + update_dev_hostname(); + update_dev_nickname(); + update_dev_online_status(); +} + + +void dump_dev_list(void) + +{ + int i, j; + int count = 0; + char hostname_buf[MAX_HOSTNAME_SIZE] = {0}; + char ip_buf[MAX_IP_LEN] = {0}; + + FILE *fp = fopen(OAF_DEV_LIST_FILE, "w"); + if (!fp) + { + return; + } + fprintf(fp, "%-4s %-20s %-20s %-32s %-8s\n", "Id", "Mac Addr", "Ip Addr", "Hostname", "Online"); + for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) + { + dev_node_t *node = dev_hash_table[i]; + while (node) + { + if (node->online != 0) + { + if (strlen(node->hostname) == 0) + strcpy(hostname_buf, "*"); + else + strcpy(hostname_buf, node->hostname); + if (strlen(node->ip) == 0) + strcpy(ip_buf, "*"); + else + strcpy(ip_buf, node->ip); + fprintf(fp, "%-4d %-20s %-20s %-32s %-8d\n", + i + 1, node->mac, ip_buf, hostname_buf, node->online); + count++; + } + if (count >= MAX_SUPPORT_DEV_NUM) + { + goto EXIT; + } + node = node->next; + } + } + for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) + { + dev_node_t *node = dev_hash_table[i]; + while (node) + { + if (node->online == 0) + { + if (strlen(node->hostname) == 0) + strcpy(hostname_buf, "*"); + else + strcpy(hostname_buf, node->hostname); + + if (strlen(node->ip) == 0) + strcpy(ip_buf, "*"); + else + strcpy(ip_buf, node->ip); + + fprintf(fp, "%-4d %-20s %-20s %-32s %-8d\n", + i + 1, node->mac, ip_buf, hostname_buf, node->online); + } + if (count >= MAX_SUPPORT_DEV_NUM) + goto EXIT; + node = node->next; + } + } +EXIT: + fclose(fp); +} +// 记录最大保存时间 todo: support config +#define MAX_RECORD_TIME (3 * 24 * 60 * 60) // 7day +// 超过1天后清除短时间的记录 +#define RECORD_REMAIN_TIME (24 * 60 * 60) // 1day +#define INVALID_RECORD_TIME (5 * 60) // 5min + +void check_dev_visit_info_expire(void) +{ + int i, j; + int count = 0; + int cur_time = get_timestamp(); + for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) + { + dev_node_t *node = dev_hash_table[i]; + while (node) + { + for (j = 0; j < MAX_VISIT_HASH_SIZE; j++) + { + visit_info_t *p_info = node->visit_htable[j]; + while (p_info) + { + int total_time = p_info->latest_time - p_info->first_time; + int interval_time = cur_time - p_info->first_time; + if (interval_time > MAX_RECORD_TIME || interval_time < 0) + { + p_info->expire = 1; + } + else if (interval_time > RECORD_REMAIN_TIME) + { + if (total_time < INVALID_RECORD_TIME) + p_info->expire = 1; + } + p_info = p_info->next; + } + } + node = node->next; + } + } +} + +void flush_expire_visit_info(void) +{ + int i, j; + int count = 0; + visit_info_t *prev = NULL; + for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) + { + dev_node_t *node = dev_hash_table[i]; + while (node) + { + for (j = 0; j < MAX_VISIT_HASH_SIZE; j++) + { + visit_info_t *p_info = node->visit_htable[j]; + prev = NULL; + while (p_info) + { + if (p_info->expire) + { + if (NULL == prev) + { + node->visit_htable[j] = p_info->next; + free(p_info); + p_info = node->visit_htable[j]; + prev = NULL; + } + else + { + prev->next = p_info->next; + free(p_info); + p_info = prev->next; + } + } + else + { + prev = p_info; + p_info = p_info->next; + } + } + } + node = node->next; + } + } +} + +void dump_dev_visit_list(void) +{ + int i, j; + int count = 0; + FILE *fp = fopen(OAF_VISIT_LIST_FILE, "w"); + if (!fp) + { + return; + } + + fprintf(fp, "%-4s %-20s %-20s %-8s %-32s %-32s %-32s %-8s\n", "Id", "Mac Addr", + "Ip Addr", "Appid", "First Time", "Latest Time", "Total Time(s)", "Expire"); + for (i = 0; i < MAX_DEV_NODE_HASH_SIZE; i++) + { + dev_node_t *node = dev_hash_table[i]; + while (node) + { + for (j = 0; j < MAX_VISIT_HASH_SIZE; j++) + { + visit_info_t *p_info = node->visit_htable[j]; + while (p_info) + { + char *first_time_str = format_time(p_info->first_time); + char *latest_time_str = format_time(p_info->latest_time); + int total_time = p_info->latest_time - p_info->first_time; + fprintf(fp, "%-4d %-20s %-20s %-8d %-32s %-32s %-32d %-4d\n", + count, node->mac, node->ip, p_info->appid, first_time_str, + latest_time_str, total_time, p_info->expire); + if (first_time_str) + free(first_time_str); + if (latest_time_str) + free(latest_time_str); + p_info = p_info->next; + count++; + if (count > 50) + goto EXIT; + } + } + node = node->next; + } + } +EXIT: + fclose(fp); +} diff --git a/open-app-filter/src/appfilter_user.h b/open-app-filter/src/appfilter_user.h new file mode 100755 index 0000000..aa1540b --- /dev/null +++ b/open-app-filter/src/appfilter_user.h @@ -0,0 +1,126 @@ +/* +Copyright (C) 2020 Derry + +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 +#ifndef __FILTER_USER_H__ +#define __FILTER_USER_H__ +#define MAX_IP_LEN 32 +#define MAX_MAC_LEN 32 +#define MAX_VISIT_HASH_SIZE 64 +#define MAX_DEV_NODE_HASH_SIZE 64 +#define MAX_HOSTNAME_SIZE 64 +#define MAX_SUPPORT_USER_NUM 64 +#define OAF_VISIT_LIST_FILE "/tmp/visit_list" +#define OAF_DEV_LIST_FILE "/tmp/dev_list" +#define MIN_VISIT_TIME 5 // default 5s +#define MAX_APP_STAT_NUM 8 +#define MAX_VISITLIST_DUMP_NUM 16 +#define MAX_APP_TYPE 16 +#define MAX_APP_ID_NUM 128 +#define MAX_SUPPORT_DEV_NUM 64 +#define SECONDS_PER_DAY (24 * 3600) +#define MAX_NICKNAME_SIZE 64 + + +//extern dev_node_t *dev_hash_table[MAX_DEV_NODE_HASH_SIZE]; + +/* +{ +"mac": "10:bf:48:37:0c:94", +"ip": "192.168.100.244", +"app_num": 0, +"visit_info": [{ +"appid": 8002, +"latest_action": 1, +"latest_time": 1602604293, +"total_num": 4, +"drop_num": 4, +"history_info": [] +}] +} +*/ +/* 单个访问记录结构 */ +typedef struct visit_info +{ + int appid; + u_int32_t first_time; + u_int32_t latest_time; + int action; + int expire; /*定期清除无效数据*/ + struct visit_info *next; + +} visit_info_t; + +/* 用于记录某个app总时间和总流量 */ +typedef struct visit_stat +{ + unsigned long long total_time; + unsigned long long total_down_bytes; + unsigned long long total_up_bytes; +} visit_stat_t; + +typedef struct dev_node +{ + char mac[MAX_MAC_LEN]; + char ip[MAX_IP_LEN]; + char hostname[MAX_HOSTNAME_SIZE]; + char nickname[MAX_NICKNAME_SIZE]; + int online; + int expire; + u_int32_t offline_time; + u_int32_t online_time; + visit_info_t *visit_htable[MAX_VISIT_HASH_SIZE]; + visit_stat_t stat[MAX_APP_TYPE][MAX_APP_ID_NUM]; + struct dev_node *next; + +} dev_node_t; + +struct app_visit_info +{ + int app_id; + char app_name[32]; + int total_time; +}; + +struct app_visit_stat_info +{ + int num; + struct app_visit_info visit_list[MAX_APP_STAT_NUM]; +}; +typedef void (*iter_func)(void *arg, dev_node_t *dev); +//todo:dev for each +extern dev_node_t *dev_hash_table[MAX_DEV_NODE_HASH_SIZE]; + +dev_node_t *add_dev_node(char *mac); +void init_dev_node_htable(); +void dump_dev_list(void); +void dump_dev_visit_list(void); +dev_node_t *find_dev_node(char *mac); +void dev_foreach(void *arg, iter_func iter); +void add_visit_info_node(visit_info_t **head, visit_info_t *node); +void check_dev_visit_info_expire(void); +void flush_expire_visit_info(); +int check_dev_expire(void); +void flush_dev_expire_node(void); +void flush_expire_visit_info(void); +void update_dev_list(void); +void update_dev_nickname(void); +#endif diff --git a/open-app-filter/src/main.c b/open-app-filter/src/main.c new file mode 100755 index 0000000..57a1882 --- /dev/null +++ b/open-app-filter/src/main.c @@ -0,0 +1,547 @@ +/* +Copyright (C) 2020 Derry + +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 +#include +#include +#include +#include +#include +#include "appfilter_user.h" +#include "appfilter_netlink.h" +#include "appfilter_ubus.h" +#include "appfilter_config.h" +#include +#include +#include +#include +#include "appfilter.h" +#include +#include "utils.h" + +#define CMD_GET_LAN_IP_FMT "ifconfig %s | grep 'inet addr' | awk '{print $2}' | awk -F: '{print $2}'" +#define CMD_GET_LAN_MASK_FMT "ifconfig %s | grep 'inet addr' | awk '{print $4}' | awk -F: '{print $2}'" + + +int current_log_level = LOG_LEVEL_INFO; +af_run_time_status_t g_af_status; +int g_oaf_config_change = 1; +af_config_t g_af_config; +int g_hnat_init = 0; +int g_feature_update = 0; +void dev_list_timeout_handler(struct uloop_timeout *t); + +void af_init_time_status(void){ + g_af_status.filter = 0; + g_af_status.deny_time = 0; + g_af_status.allow_time = 0; + g_af_status.match_time = 0; +} + + +void af_init_status(void){ + af_init_time_status(); +} +struct uloop_timeout dev_tm = { + .cb = dev_list_timeout_handler}; + +static struct uloop_fd appfilter_nl_fd = { + .cb = appfilter_nl_handler, +}; + + + +/** +config time 'time' + option time_mode '0' + option start_time '00:00' + option end_time '23:59' + option days '1 2 3 4 5' + list time '12:00-13:00' + list time '15:00-16:00' + list time '18:00-19:00' + list time '21:00-21:30' + list time '22:00-23:00' + list time '23:01-23:30' + list time '23:50-23:40' +*/ + +int af_load_time_config(af_time_config_t *t_config) +{ + char time_list_buf[MAX_TIME_LIST_LEN] = {0}; + char days_buf[128] = {0}; + char start_time_buf[128] = {0}; + char end_time_buf[128] = {0}; + struct uci_context *ctx = uci_alloc_context(); + if (!ctx) + return -1; + memset(t_config, 0, sizeof(af_time_config_t)); + t_config->time_mode = af_uci_get_int_value(ctx, "appfilter.time.time_mode"); + t_config->deny_time = af_uci_get_int_value(ctx, "appfilter.time.deny_time"); + t_config->allow_time = af_uci_get_int_value(ctx, "appfilter.time.allow_time"); + + af_uci_get_value(ctx, "appfilter.time.start_time", start_time_buf, sizeof(start_time_buf)); + af_uci_get_value(ctx, "appfilter.time.end_time", end_time_buf, sizeof(end_time_buf)); + af_uci_get_value(ctx, "appfilter.time.days", days_buf, sizeof(days_buf)); + LOG_DEBUG("mode = %d, start_time: %s, end_time: %s, days: %s\n", t_config->time_mode, start_time_buf, end_time_buf, days_buf); + sscanf(start_time_buf, "%d:%d", &t_config->seg_time.start_time.hour, &t_config->seg_time.start_time.min); + sscanf(end_time_buf, "%d:%d", &t_config->seg_time.end_time.hour, &t_config->seg_time.end_time.min); + + t_config->time_num = 0; + char *p = strtok(days_buf, " "); + if (!p) + goto EXIT; + do + { + t_config->days[atoi(p)] = 1; + } while (p = strtok(NULL, " ")); + + af_uci_get_list_value(ctx, "appfilter.time.time", time_list_buf, sizeof(time_list_buf), " "); + p = strtok(time_list_buf, " "); + if (!p) + goto EXIT; + do + { + sscanf(p, "%d:%d-%d:%d", &t_config->time_list[t_config->time_num].start_time.hour, + &t_config->time_list[t_config->time_num].start_time.min, &t_config->time_list[t_config->time_num].end_time.hour, &t_config->time_list[t_config->time_num].end_time.min); + LOG_DEBUG("time[%d] %d:%d-%d:%d\n", t_config->time_num, t_config->time_list[t_config->time_num].start_time.hour, t_config->time_list[t_config->time_num].start_time.min, + t_config->time_list[t_config->time_num].end_time.hour, t_config->time_list[t_config->time_num].end_time.min); + t_config->time_num++; + } while (p = strtok(NULL, " ")); +EXIT: + uci_free_context(ctx); + return 0; +} + + +void af_load_global_config(af_global_config_t *config){ + int ret = 0; + char lan_ifname[32] = {0}; + struct uci_context *ctx = uci_alloc_context(); + if (!ctx) + return; + ret = af_uci_get_int_value(ctx, "appfilter.global.enable"); + if (ret < 0) + config->enable = 0; + else + config->enable = ret; + + ret = af_uci_get_int_value(ctx, "appfilter.global.record_enable"); + if (ret < 0) + config->record_enable = 0; + else + config->record_enable = ret; + + ret = af_uci_get_int_value(ctx, "appfilter.global.user_mode"); + if (ret < 0) + config->user_mode = 0; + else + config->user_mode = ret; + + ret = af_uci_get_int_value(ctx, "appfilter.global.work_mode"); + if (ret < 0) + config->work_mode = 0; + else + config->work_mode = ret; + ret = af_uci_get_int_value(ctx, "appfilter.global.tcp_rst"); + if (ret < 0) + config->tcp_rst = 1; + else + config->tcp_rst = ret; + + ret = af_uci_get_int_value(ctx, "appfilter.global.disable_hnat"); + if (ret < 0) + config->disable_hnat = 1; + else + config->disable_hnat = ret; + + ret = af_uci_get_int_value(ctx, "appfilter.global.auto_load_engine"); + if (ret < 0) + config->auto_load_engine = 0; + else + config->auto_load_engine = ret; + + + ret = af_uci_get_value(ctx, "appfilter.global.disable_hnat", lan_ifname, sizeof(lan_ifname)); + if (ret < 0) + strncpy(config->lan_ifname, "br-lan", sizeof(config->lan_ifname) - 1); + else + strncpy(config->lan_ifname, lan_ifname, sizeof(config->lan_ifname) - 1); + + uci_free_context(ctx); + LOG_INFO("enable=%d, user_mode=%d, work_mode=%d", config->enable, config->user_mode, config->work_mode); +} + +void af_load_config(af_config_t *config){ + memset(config, 0, sizeof(af_config_t)); + af_load_global_config(&config->global); + af_load_time_config(&config->time); +} + + +void update_oaf_proc_value(char *key, char *value){ + char cmd_buf[128] = {0}; + char file_path[128] = {0}; + char old_value[128] = {0}; + sprintf(file_path, "/proc/sys/oaf/%s", key); + + af_read_file_value(file_path, old_value, sizeof(old_value)); + if (strcmp(old_value, value) != 0){ + sprintf(cmd_buf, "echo %s >/proc/sys/oaf/%s", value, key); + system(cmd_buf); + LOG_INFO("update %s %s-->%s\n", key, old_value, value); + } +} + +void update_oaf_proc_u32_value(char *key, u_int32_t value){ + char buf[32] = {0}; + sprintf(buf, "%u", value); + update_oaf_proc_value(key, buf); +} + +void update_lan_ip(void){ + char ip_str[32] = {0}; + char mask_str[32] = {0}; + struct in_addr addr; + struct in_addr mask_addr; + char cmd_buf[128] = {0}; + u_int32_t lan_ip = 0; + u_int32_t lan_mask = 0; + char lan_ifname[32] = {0}; + char ip_cmd_buf[128] = {0}; + char mask_cmd_buf[128] = {0}; + struct uci_context *ctx = uci_alloc_context(); + if (!ctx) + return; + + int ret = af_uci_get_value(ctx, "appfilter.global.lan_ifname", lan_ifname, sizeof(lan_ifname) - 1); + if (ret != 0){ + strcpy(lan_ifname, "br-lan"); + } + sprintf(ip_cmd_buf, CMD_GET_LAN_IP_FMT , lan_ifname); + sprintf(mask_cmd_buf, CMD_GET_LAN_MASK_FMT , lan_ifname); + + exec_with_result_line(ip_cmd_buf, ip_str, sizeof(ip_str)); + if (strlen(ip_str) < MIN_INET_ADDR_LEN){ + update_oaf_proc_u32_value("lan_ip", 0); + } + else{ + inet_aton(ip_str, &addr); + lan_ip = addr.s_addr; + update_oaf_proc_u32_value("lan_ip", lan_ip); + } + + exec_with_result_line(mask_cmd_buf, mask_str, sizeof(mask_str)); + + if (strlen(mask_str) < MIN_INET_ADDR_LEN){ + update_oaf_proc_u32_value("lan_mask", 0); + } + else{ + inet_aton(mask_str, &mask_addr); + lan_mask = mask_addr.s_addr; + update_oaf_proc_u32_value("lan_mask", lan_mask); + } +} + + + + +int af_check_time_manual(af_time_config_t *t_config) { + time_t now = time(NULL); + struct tm *current_time = localtime(&now); + int current_minutes = current_time->tm_hour * 60 + current_time->tm_min; + + LOG_DEBUG("current time: %02d:%02d\n", current_time->tm_hour, current_time->tm_min); + + for (int i = 0; i < t_config->time_num; i++) { + int start_minutes = t_config->time_list[i].start_time.hour * 60 + t_config->time_list[i].start_time.min; + int end_minutes = t_config->time_list[i].end_time.hour * 60 + t_config->time_list[i].end_time.min; + LOG_DEBUG("check time: %02d:%02d-%02d:%02d\n", + t_config->time_list[i].start_time.hour, t_config->time_list[i].start_time.min, + t_config->time_list[i].end_time.hour, t_config->time_list[i].end_time.min); + + if (current_minutes >= start_minutes && current_minutes <= end_minutes) { + LOG_DEBUG("current time in time list\n"); + g_af_status.match_time = 1; + return 1; + } + } + g_af_status.match_time = 0; + return 0; +} + +int af_check_time_dynamic(af_time_config_t *t_config) { + time_t now = time(NULL); + struct tm *current_time = localtime(&now); + int current_minutes = current_time->tm_hour * 60 + current_time->tm_min; + + int start_minutes = t_config->seg_time.start_time.hour * 60 + t_config->seg_time.start_time.min; + int end_minutes = t_config->seg_time.end_time.hour * 60 + t_config->seg_time.end_time.min; + LOG_DEBUG("check seg_time: %02d:%02d-%02d:%02d\n", + t_config->seg_time.start_time.hour, t_config->seg_time.start_time.min, + t_config->seg_time.end_time.hour, t_config->seg_time.end_time.min); + if (!(current_minutes >= start_minutes && current_minutes <= end_minutes)) { + LOG_DEBUG("current time not in seg_time\n"); + af_init_time_status(); + return 0; + } + + g_af_status.match_time = 1; + if (g_af_status.filter == 1) { + g_af_status.deny_time++; + if (g_af_status.deny_time >= t_config->deny_time) { + g_af_status.filter = 0; + g_af_status.deny_time = 0; + LOG_DEBUG("deny time over, filter = 0"); + } + LOG_DEBUG("deny_time: %d\n", g_af_status.deny_time); + } else { + g_af_status.allow_time++; + if (g_af_status.allow_time >= t_config->allow_time) { + g_af_status.filter = 1; + g_af_status.allow_time = 0; + LOG_DEBUG("allow time over, filter = 1"); + } + LOG_DEBUG("allow_time: %d\n", g_af_status.allow_time); + } + return g_af_status.filter; +} + +int af_check_time(af_time_config_t *t_config) { + time_t now = time(NULL); + struct tm *current_time = localtime(&now); + LOG_DEBUG("current day: %d\n", current_time->tm_wday); + if (!t_config->days[current_time->tm_wday]) { + LOG_DEBUG("current day not in configured days\n"); + af_init_time_status(); + return 0; + } + if (t_config->time_mode == 0) { + LOG_DEBUG("manual mode\n"); + return af_check_time_manual(t_config); + } else { + LOG_DEBUG("dynamic mode\n"); + return af_check_time_dynamic(t_config); + } +} + + +void update_oaf_status(void){ + int ret = 0; + int cur_enable = 0; + if(g_af_config.global.enable == 1){ + ret = af_check_time(&g_af_config.time); + if (ret == 1){ + system("echo 1 >/proc/sys/oaf/enable"); + } + else{ + system("echo 0 >/proc/sys/oaf/enable"); + } + } + else{ + system("echo 0 >/proc/sys/oaf/enable"); + } +} + +void update_oaf_record_status(void){ + if(g_af_config.global.record_enable == 1){ + system("echo 1 >/proc/sys/oaf/record_enable"); + } + else{ + system("echo 0 >/proc/sys/oaf/record_enable"); + } +} + +void af_hnat_init(void){ + if (g_af_config.global.enable == 0){ + return; + } + if (g_hnat_init == 0){ + LOG_INFO("disable hnat...\n"); + system("/usr/bin/hnat.sh"); + g_hnat_init = 1; + } +} + + +int af_nl_clean_feature(void){ + af_msg_t msg; + if (appfilter_nl_fd.fd < 0){ + return -1; + } + msg.action = AF_MSG_CLEAN_FEATURE; + + send_msg_to_kernel(appfilter_nl_fd.fd,(void *)&msg, sizeof(msg)); + return 0; +} + +int af_nl_add_feature(char *feature){ + char msg_buf[1024] = {0}; + if (appfilter_nl_fd.fd < 0){ + return -1; + } + char *p_data = msg_buf + sizeof(af_msg_t); + memset(msg_buf, 0, sizeof(msg_buf)); + + af_msg_t *hdr = (af_msg_t *)msg_buf; + hdr->action = AF_MSG_ADD_FEATURE; + strncpy(p_data, feature, strlen(feature)); + send_msg_to_kernel(appfilter_nl_fd.fd,(void *)msg_buf, sizeof(af_msg_t) + strlen(feature) + 1); + return 0; +} + + + +int af_load_feature_to_kernel(void){ + char line_buf[MAX_FEATURE_LINE_LEN] = {0}; + FILE *fp = fopen("/tmp/feature.cfg", "r"); + if (!fp) + { + printf("open file failed\n"); + return -1; + } + if (af_nl_clean_feature() < 0){ + return -1; + } + while (fgets(line_buf, sizeof(line_buf), fp)) + { + str_trim(line_buf); + if (strlen(line_buf) < 8) + continue; + if (strstr(line_buf, "#")) + continue; + + if (strlen(line_buf) >= MAX_FEATURE_LINE_LEN - 1){ + continue; + } + af_nl_add_feature(line_buf); + } + fclose(fp); + return 0; +} + +int reload_feature(void){ + system("gen_class.sh /tmp/feature.cfg"); + init_app_name_table(); + init_app_class_name_table(); + if (af_load_feature_to_kernel() < 0){ + LOG_ERROR("Failed to load feature to kernel\n"); + return -1; + } + LOG_WARN("reload feature success\n"); + return 0; +} + +void dev_list_timeout_handler(struct uloop_timeout *t) +{ + static int count = 0; + count++; + if (count % 10 == 0){ + update_dev_list(); + } + if (count % 60 == 0){ + check_dev_visit_info_expire(); + update_lan_ip(); + if (check_dev_expire()){ + flush_dev_expire_node(); + } + flush_expire_visit_info(); + update_oaf_status(); + dump_dev_list(); + } + if (g_oaf_config_change == 1){ + update_lan_ip(); + af_load_config(&g_af_config); + update_oaf_status(); + update_oaf_record_status(); + g_oaf_config_change = 0; + } + if (count > 10){ // delay init + af_hnat_init(); + } + + + if (appfilter_nl_fd.fd < 0){ + appfilter_nl_fd.fd = appfilter_nl_init(); + if (appfilter_nl_fd.fd > 0){ + uloop_fd_add(&appfilter_nl_fd, ULOOP_READ); + system("oaf_rule reload &"); + // /etc/init.d/appfilter reload + LOG_INFO("netlink connect success\n"); + } + } + + if (g_feature_update == 1 && appfilter_nl_fd.fd > 0){ + if (0 == reload_feature()){ + g_feature_update = 0; + } + } + + uloop_timeout_set(t, 1000); +} + +void af_load_engine(void){ + if (g_af_config.global.auto_load_engine == 1){ + if (access("/lib/modules/oaf.ko", F_OK) == 0) { + system("insmod /lib/modules/oaf.ko"); + LOG_WARN("insmod /lib/modules/oaf.ko"); + } else { + system("modprobe oaf"); + LOG_WARN("modprobe oaf"); + } + } + else{ + LOG_WARN("auto load disabled, not load oaf.ko\n"); + } +} + + +void handle_sigusr1(int sig) { + LOG_INFO("Received SIGUSR1 signal\n"); + g_feature_update = 1; +} + + + +int main(int argc, char **argv) +{ + int ret = 0; + LOG_INFO("appfilter start"); + g_feature_update = 1; + af_load_config(&g_af_config); + af_load_engine(); + af_init_status(); + uloop_init(); + signal(SIGUSR1, handle_sigusr1); + signal(SIGCHLD, SIG_IGN); + init_dev_node_htable(); + if (appfilter_ubus_init() < 0) + { + LOG_ERROR("Failed to connect to ubus\n"); + return 1; + } + appfilter_nl_fd.fd = -1; + uloop_timeout_set(&dev_tm, 5000); + uloop_timeout_add(&dev_tm); + uloop_run(); + uloop_done(); + return 0; +} diff --git a/open-app-filter/src/utils.c b/open-app-filter/src/utils.c new file mode 100755 index 0000000..6fc136f --- /dev/null +++ b/open-app-filter/src/utils.c @@ -0,0 +1,102 @@ + +#include +#include +#include +#include +#include + + +char *str_trim(char *s) { + char *start, *last, *bk; + int len; + + start = s; + while (isspace(*start)) + start++; + + bk = last = s + strlen(s) - 1; + while (last > start && isspace(*last)) + last--; + + if ((s != start) || (bk != last)) { + len = last - start + 1; + strncpy(s, start, len); + s[len] = '\0'; + } + return s; +} + +int exec_with_result_line(char *cmd, char *result, int len) +{ + FILE *fp = NULL; + if (!cmd || !result || !len) + return -1; + fp = popen(cmd, "r"); + if (!fp) + return -1; + fgets(result, len, fp); + str_trim(result); + pclose(fp); + return 0; +} + + +#include +#include +#include +#include + +int check_same_network(char *ip1, char *netmask, char *ip2) { + struct in_addr addr1, addr2, mask; + + if (inet_pton(AF_INET, ip1, &addr1) != 1) { + printf("Invalid IP address: %s\n", ip1); + return -1; + } + if (inet_pton(AF_INET, netmask, &mask) != 1) { + printf("Invalid netmask: %s\n", netmask); + return -1; + } + if (inet_pton(AF_INET, ip2, &addr2) != 1) { + printf("Invalid IP address: %s\n", ip2); + return -1; + } + + if ((addr1.s_addr & mask.s_addr) == (addr2.s_addr & mask.s_addr)) { + return 1; + } else { + return 0; + } +} + + +int af_read_file_value(const char *file_path, char *value, int value_len) { + FILE *file = fopen(file_path, "r"); + if (!file) { + perror("Failed to open file"); + return -1; + } + + if (fgets(value, value_len, file) == NULL) { + perror("Failed to read line from file"); + fclose(file); + return -1; + } + + size_t len = strlen(value); + if (len > 0 && value[len - 1] == '\n') { + value[len - 1] = '\0'; + } + + fclose(file); + return 0; +} + +int af_read_file_int_value(const char *file_path, int *value) { + char line_buf[128] = {0}; + if (af_read_file_value(file_path, line_buf, sizeof(line_buf)) < 0){ + return -1; + } + *value = atoi(line_buf); + return 0; +} diff --git a/open-app-filter/src/utils.h b/open-app-filter/src/utils.h new file mode 100755 index 0000000..7974028 --- /dev/null +++ b/open-app-filter/src/utils.h @@ -0,0 +1,8 @@ +#ifndef __UTILS_H__ +#define __UTILS_H__ +char *str_trim(char *s); +int exec_with_result_line(char *cmd, char *result, int len); +int check_same_network(char *ip1, char *netmask, char *ip2); +int af_read_file_value(const char *file_path, char *value, int value_len); +int af_read_file_int_value(const char *file_path, int *value); +#endif \ No newline at end of file