diff --git a/CHANGELOG.md b/CHANGELOG.md index af827b21..adc6335e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,13 +57,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - It is now possible to `addPeer` and `removePeer` using the admin socket again - The `getSessions` admin socket call reports number of bytes received and transmitted again - The link setup code has been refactored, making it easier to support new peering types in the future -- Yggdrasil now maintains configuration internally, rather than relying on a shared and potentially mutable structure +- RiV-mesh now maintains configuration internally, rather than relying on a shared and potentially mutable structure ### Fixed - Tracking information about expired root nodes has been fixed, which should hopefully resolve issues with reparenting and connection failures when the root node disappears - A bug in the mobile framework code which caused a crash on Android when multicast failed to set up has been fixed -- Yggdrasil should now shut down gracefully and clean up correctly when running as a Windows service +- RiV-mesh should now shut down gracefully and clean up correctly when running as a Windows service ## [0.4.4] - 2022-07-07 @@ -71,11 +71,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - ICMPv6 "Packet Too Big" payload size has been increased, which should fix Path MTU Discovery (PMTUD) when two nodes have different `IfMTU` values configured - A crash has been fixed when handling debug packet responses -- `yggdrasilctl getSelf` should now report coordinates correctly again +- `meshctl getSelf` should now report coordinates correctly again ### Changed -- Go 1.17 is now required to build Yggdrasil +- Go 1.17 is now required to build RiV-mesh ## [0.4.3] - 2022-02-06 diff --git a/LICENSE.rtf b/LICENSE.rtf new file mode 100644 index 00000000..c5ad8d48 --- /dev/null +++ b/LICENSE.rtf @@ -0,0 +1,189 @@ +{\rtf1\ansi\ansicpg1251\deff0\nouicompat\deflang1049{\fonttbl{\f0\fnil\fcharset0 Roboto;}} +{\colortbl ;\red0\green0\blue255;} +{\*\generator Riched20 10.0.19041}\viewkind4\uc1 +\pard\sl240\slmult1\f0\fs16\lang9 This software is licensed under the LGPLv3, included below.\par +\par +As a special exception to the GNU Lesser General Public License version 3\par +("LGPL3"), the copyright holders of this Library give you permission to\par +convey to a third party a Combined Work that links statically or dynamically\par +to this Library without providing any Minimal Corresponding Source or\par +Minimal Application Code as set out in 4d or providing the installation\par +information set out in section 4e, provided that you comply with the other\par +provisions of LGPL3 and provided that you meet, for the Application the\par +terms and conditions of the license(s) which apply to the Application.\par +\par +Except as stated in this special exception, the provisions of LGPL3 will\par +continue to comply in full to this Library. If you modify this Library, you\par +may apply this exception to your version of this Library, but you are not\par +obliged to do so. If you do not wish to do so, delete this exception\par +statement from your version. This exception does not (and cannot) modify any\par +license terms which apply to the Application, with which you must still\par +comply.\par +\par + GNU LESSER GENERAL PUBLIC LICENSE\par + Version 3, 29 June 2007\par +\par + Copyright (C) 2007 Free Software Foundation, Inc. <{{\field{\*\fldinst{HYPERLINK "https://fsf.org/"}}{\fldrslt{https://fsf.org/\ul0\cf0}}}}\f0\fs16 >\par + Everyone is permitted to copy and distribute verbatim copies\par + of this license document, but changing it is not allowed.\par +\par +\par + This version of the GNU Lesser General Public License incorporates\par +the terms and conditions of version 3 of the GNU General Public\par +License, supplemented by the additional permissions listed below.\par +\par + 0. Additional Definitions.\par +\par + As used herein, "this License" refers to version 3 of the GNU Lesser\par +General Public License, and the "GNU GPL" refers to version 3 of the GNU\par +General Public License.\par +\par + "The Library" refers to a covered work governed by this License,\par +other than an Application or a Combined Work as defined below.\par +\par + An "Application" is any work that makes use of an interface provided\par +by the Library, but which is not otherwise based on the Library.\par +Defining a subclass of a class defined by the Library is deemed a mode\par +of using an interface provided by the Library.\par +\par + A "Combined Work" is a work produced by combining or linking an\par +Application with the Library. The particular version of the Library\par +with which the Combined Work was made is also called the "Linked\par +Version".\par +\par + The "Minimal Corresponding Source" for a Combined Work means the\par +Corresponding Source for the Combined Work, excluding any source code\par +for portions of the Combined Work that, considered in isolation, are\par +based on the Application, and not on the Linked Version.\par +\par + The "Corresponding Application Code" for a Combined Work means the\par +object code and/or source code for the Application, including any data\par +and utility programs needed for reproducing the Combined Work from the\par +Application, but excluding the System Libraries of the Combined Work.\par +\par + 1. Exception to Section 3 of the GNU GPL.\par +\par + You may convey a covered work under sections 3 and 4 of this License\par +without being bound by section 3 of the GNU GPL.\par +\par + 2. Conveying Modified Versions.\par +\par + If you modify a copy of the Library, and, in your modifications, a\par +facility refers to a function or data to be supplied by an Application\par +that uses the facility (other than as an argument passed when the\par +facility is invoked), then you may convey a copy of the modified\par +version:\par +\par + a) under this License, provided that you make a good faith effort to\par + ensure that, in the event an Application does not supply the\par + function or data, the facility still operates, and performs\par + whatever part of its purpose remains meaningful, or\par +\par + b) under the GNU GPL, with none of the additional permissions of\par + this License applicable to that copy.\par +\par + 3. Object Code Incorporating Material from Library Header Files.\par +\par + The object code form of an Application may incorporate material from\par +a header file that is part of the Library. You may convey such object\par +code under terms of your choice, provided that, if the incorporated\par +material is not limited to numerical parameters, data structure\par +layouts and accessors, or small macros, inline functions and templates\par +(ten or fewer lines in length), you do both of the following:\par +\par + a) Give prominent notice with each copy of the object code that the\par + Library is used in it and that the Library and its use are\par + covered by this License.\par +\par + b) Accompany the object code with a copy of the GNU GPL and this license\par + document.\par +\par + 4. Combined Works.\par +\par + You may convey a Combined Work under terms of your choice that,\par +taken together, effectively do not restrict modification of the\par +portions of the Library contained in the Combined Work and reverse\par +engineering for debugging such modifications, if you also do each of\par +the following:\par +\par + a) Give prominent notice with each copy of the Combined Work that\par + the Library is used in it and that the Library and its use are\par + covered by this License.\par +\par + b) Accompany the Combined Work with a copy of the GNU GPL and this license\par + document.\par +\par + c) For a Combined Work that displays copyright notices during\par + execution, include the copyright notice for the Library among\par + these notices, as well as a reference directing the user to the\par + copies of the GNU GPL and this license document.\par +\par + d) Do one of the following:\par +\par + 0) Convey the Minimal Corresponding Source under the terms of this\par + License, and the Corresponding Application Code in a form\par + suitable for, and under terms that permit, the user to\par + recombine or relink the Application with a modified version of\par + the Linked Version to produce a modified Combined Work, in the\par + manner specified by section 6 of the GNU GPL for conveying\par + Corresponding Source.\par +\par + 1) Use a suitable shared library mechanism for linking with the\par + Library. A suitable mechanism is one that (a) uses at run time\par + a copy of the Library already present on the user's computer\par + system, and (b) will operate properly with a modified version\par + of the Library that is interface-compatible with the Linked\par + Version.\par +\par + e) Provide Installation Information, but only if you would otherwise\par + be required to provide such information under section 6 of the\par + GNU GPL, and only to the extent that such information is\par + necessary to install and execute a modified version of the\par + Combined Work produced by recombining or relinking the\par + Application with a modified version of the Linked Version. (If\par + you use option 4d0, the Installation Information must accompany\par + the Minimal Corresponding Source and Corresponding Application\par + Code. If you use option 4d1, you must provide the Installation\par + Information in the manner specified by section 6 of the GNU GPL\par + for conveying Corresponding Source.)\par +\par + 5. Combined Libraries.\par +\par + You may place library facilities that are a work based on the\par +Library side by side in a single library together with other library\par +facilities that are not Applications and are not covered by this\par +License, and convey such a combined library under terms of your\par +choice, if you do both of the following:\par +\par + a) Accompany the combined library with a copy of the same work based\par + on the Library, uncombined with any other library facilities,\par + conveyed under the terms of this License.\par +\par + b) Give prominent notice with the combined library that part of it\par + is a work based on the Library, and explaining where to find the\par + accompanying uncombined form of the same work.\par +\par + 6. Revised Versions of the GNU Lesser General Public License.\par +\par + The Free Software Foundation may publish revised and/or new versions\par +of the GNU Lesser General Public License from time to time. Such new\par +versions will be similar in spirit to the present version, but may\par +differ in detail to address new problems or concerns.\par +\par + Each version is given a distinguishing version number. If the\par +Library as you received it specifies that a certain numbered version\par +of the GNU Lesser General Public License "or any later version"\par +applies to it, you have the option of following the terms and\par +conditions either of that published version or of any later version\par +published by the Free Software Foundation. If the Library as you\par +received it does not specify a version number of the GNU Lesser\par +General Public License, you may choose any version of the GNU Lesser\par +General Public License ever published by the Free Software Foundation.\par +\par + If the Library as you received it specifies that a proxy can decide\par +whether future versions of the GNU Lesser General Public License shall\par +apply, that proxy's public statement of acceptance of any version is\par +permanent authorization for you to choose that version for the\par +Library.\par +} + \ No newline at end of file diff --git a/README.md b/README.md index d0afae47..79e13e06 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,28 @@ -# Yggdrasil +# RiV-mesh first self arranging mesh network with link aggregation. -[![Build status](https://github.com/yggdrasil-network/yggdrasil-go/actions/workflows/ci.yml/badge.svg)](https://github.com/yggdrasil-network/yggdrasil-go/actions/workflows/ci.yml) +[![CircleCI](https://circleci.com/gh/RiV-chain/RiV-mesh.svg?style=shield&circle-token=:circle-token +)](https://circleci.com/gh/RiV-chain/RiV-mesh) + +## Why fork? +RiV-mesh is fork of Yggdrasil which is great project. Starting from Yggdrasil 0.4 dev team removed CKR feature which is a core for secure tunneling like VPN does. RiV-mesh gets back CKR feature. Second reason: Yggdrasil uses deprecated 200::/7 IPv6 address pool which can be assigned for some network in future, unlike this fc00::/7 is safe and has been taken for RiV-mesh. ## Introduction -Yggdrasil is an early-stage implementation of a fully end-to-end encrypted IPv6 -network. It is lightweight, self-arranging, supported on multiple platforms and -allows pretty much any IPv6-capable application to communicate securely with -other Yggdrasil nodes. Yggdrasil does not require you to have IPv6 Internet -connectivity - it also works over IPv4. +RiV-mesh is an implementation of a fully end-to-end encrypted IPv6 +network, created in the scope to produce the Transport Layer for RiV Chain Blockchain, +also to facilitate secure conectivity between a wide spectrum of endpoint devices like IoT devices, +desktop computers or even routers. +It is lightweight, self-arranging, supported on multiple +platforms and allows pretty much any IPv6-capable application +to communicate securely with other RiV-mesh nodes. +RiV-mesh does not require you to have IPv6 Internet connectivity - it also works over IPv4. ## Supported Platforms -Yggdrasil works on a number of platforms, including Linux, macOS, Ubiquiti +RiV-mesh works on a number of platforms, including Linux, macOS, Ubiquiti EdgeRouter, VyOS, Windows, FreeBSD, OpenBSD and OpenWrt. -Please see our [Installation](https://yggdrasil-network.github.io/installation.html) +Please see our [Installation](https://RiV-chain.github.io/installation.html) page for more information. You may also find other platform-specific wrappers, scripts or tools in the `contrib` folder. @@ -40,52 +47,85 @@ To generate static configuration, either generate a HJSON file (human-friendly, complete with comments): ``` -./yggdrasil -genconf > /path/to/yggdrasil.conf +./mesh -genconf > /path/to/mesh.conf ``` ... or generate a plain JSON file (which is easy to manipulate programmatically): ``` -./yggdrasil -genconf -json > /path/to/yggdrasil.conf +./mesh -genconf -json > /path/to/mesh.conf ``` -You will need to edit the `yggdrasil.conf` file to add or remove peers, modify +You will need to edit the `mesh.conf` file to add or remove peers, modify other configuration such as listen addresses or multicast addresses, etc. -### Run Yggdrasil +### Run RiV-mesh To run with the generated static configuration: ``` -./yggdrasil -useconffile /path/to/yggdrasil.conf +./mesh -useconffile /path/to/mesh.conf ``` To run in auto-configuration mode (which will use sane defaults and random keys at each startup, instead of using a static configuration file): ``` -./yggdrasil -autoconf +./mesh -autoconf ``` -You will likely need to run Yggdrasil as a privileged user or under `sudo`, +You will likely need to run RiV-mesh as a privileged user or under `sudo`, unless you have permission to create TUN/TAP adapters. On Linux this can be done -by giving the Yggdrasil binary the `CAP_NET_ADMIN` capability. +by giving the RiV-mesh binary the `CAP_NET_ADMIN` capability. ## Documentation -Documentation is available [on our website](https://yggdrasil-network.github.io). +Documentation is available [on our website](https://riv-chain.github.io/RiV-mesh/). -- [Installing Yggdrasil](https://yggdrasil-network.github.io/installation.html) -- [Configuring Yggdrasil](https://yggdrasil-network.github.io/configuration.html) -- [Frequently asked questions](https://yggdrasil-network.github.io/faq.html) +- [Installing RiV-mesh](https://riv-chain.github.io/RiV-mesh/) +- [Configuring RiV-mesh](https://riv-chain.github.io/RiV-mesh/) +- [Frequently asked questions](https://riv-chain.github.io/RiV-mesh/) - [Version changelog](CHANGELOG.md) ## Community -Feel free to join us on our [Matrix -channel](https://matrix.to/#/#yggdrasil:matrix.org) at `#yggdrasil:matrix.org` -or in the `#yggdrasil` IRC channel on [libera.chat](https://libera.chat). +Feel free to join us on our [Telegram +channel](https://t.me/rivchain). + +## Public peers +If you are operating RiV-mesh peer and may create your pool request with your new per or use existing one https://github.com/RiV-chain/public-peers + +## Known issues + +### 1. Log message: +``` +An error occurred starting multicast: listen udp6 [::]:9001: socket: address family not supported by protocol +``` +and +``` +An error occurred starting TUN/TAP: operation not supported +``` + +### Caused by: +The device has no IPv6 support + + +### 2. Log message: +``` +An error occurred starting TUN/TAP: permission denied +``` + +### Caused by: +IPv6 support is not enabled. See the solution: https://github.com/yggdrasil-network/yggdrasil-go/issues/479#issuecomment-519512395 + +### 3. Mesh infinite output in log: + Connected SCTP ... + + Disconnected SCTP ... + +### Caused by: +Docker interface docker0 is conflicting with SCTP bind process. The issue can be resolved by removing docker. ## License diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..034e8480 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,21 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 5.1.x | :white_check_mark: | +| 5.0.x | :x: | +| 4.0.x | :white_check_mark: | +| < 4.0 | :x: | + +## Reporting a Vulnerability + +Use this section to tell people how to report a vulnerability. + +Tell them where to go, how often they can expect to get an update on a +reported vulnerability, what to expect if the vulnerability is accepted or +declined, etc. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..d5ec8dad --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,31 @@ +version: '{build}' +pull_requests: + do_not_increment_build_number: true +os: Visual Studio 2022 +shallow_clone: false + +environment: + MSYSTEM: MINGW64 + MSYS2_PATH_TYPE: inherit + CHERE_INVOKING: enabled_from_arguments + GO111MODULE: on + GOPATH: c:\gopath + +stack: go 1.19 + +build_script: +- cmd: >- + cd %APPVEYOR_BUILD_FOLDER% + ##### MinGW build +- set OPT_PATH=C:\msys64\mingw32\bin;C:\msys64\mingw64\bin; +- set PATH=%GOPATH%\bin;%OPT_PATH%%PATH% +- go install github.com/tc-hib/go-winres@latest +- c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi.sh x64" +- c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi.sh x86" +- c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi-gui.sh x64" +- c:\msys64\usr\bin\bash -lc "./contrib/msi/build-msi-gui.sh x86" + +test: off + +artifacts: +- path: '*.msi' diff --git a/build b/build index c7214438..708c431b 100755 --- a/build +++ b/build @@ -2,11 +2,14 @@ set -ef -PKGSRC=${PKGSRC:-github.com/yggdrasil-network/yggdrasil-go/src/version} +PKGSRC=${PKGSRC:-github.com/RiV-chain/RiV-mesh/src/version} PKGNAME=${PKGNAME:-$(sh contrib/semver/name.sh)} PKGVER=${PKGVER:-$(sh contrib/semver/version.sh --bare)} - -LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" +if [ "$LDFLAGS" ]; then + LDFLAGS="$LDFLAGS -X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" +else + LDFLAGS="-X $PKGSRC.buildName=$PKGNAME -X $PKGSRC.buildVersion=$PKGVER" +fi ARGS="-v" while getopts "utc:l:dro:p" option @@ -28,9 +31,9 @@ if [ -z $TABLES ] && [ -z $DEBUG ]; then LDFLAGS="$LDFLAGS -s -w" fi -for CMD in yggdrasil yggdrasilctl ; do +for CMD in ./cmd/mesh ./cmd/meshctl ./contrib/ui/mesh-ui ; do echo "Building: $CMD" - go build $ARGS -ldflags="$LDFLAGS" -gcflags="$GCFLAGS" ./cmd/$CMD + go build $ARGS -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" $CMD if [ $UPX ]; then upx --brute $CMD diff --git a/cmd/genkeys/main.go b/cmd/genkeys/main.go index 81942446..30b7d604 100644 --- a/cmd/genkeys/main.go +++ b/cmd/genkeys/main.go @@ -19,7 +19,7 @@ import ( "net" "runtime" - "github.com/yggdrasil-network/yggdrasil-go/src/address" + "github.com/RiV-chain/RiV-mesh/src/address" ) type keySet struct { diff --git a/cmd/yggdrasil/main.go b/cmd/mesh/main.go similarity index 91% rename from cmd/yggdrasil/main.go rename to cmd/mesh/main.go index 8185dee0..adf0e467 100644 --- a/cmd/yggdrasil/main.go +++ b/cmd/mesh/main.go @@ -25,16 +25,16 @@ import ( "github.com/kardianos/minwinsvc" "github.com/mitchellh/mapstructure" - "github.com/yggdrasil-network/yggdrasil-go/src/address" - "github.com/yggdrasil-network/yggdrasil-go/src/admin" - "github.com/yggdrasil-network/yggdrasil-go/src/config" - "github.com/yggdrasil-network/yggdrasil-go/src/defaults" - "github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc" + "github.com/RiV-chain/RiV-mesh/src/address" + "github.com/RiV-chain/RiV-mesh/src/admin" + "github.com/RiV-chain/RiV-mesh/src/config" + "github.com/RiV-chain/RiV-mesh/src/defaults" - "github.com/yggdrasil-network/yggdrasil-go/src/core" - "github.com/yggdrasil-network/yggdrasil-go/src/multicast" - "github.com/yggdrasil-network/yggdrasil-go/src/tun" - "github.com/yggdrasil-network/yggdrasil-go/src/version" + "github.com/RiV-chain/RiV-mesh/src/core" + "github.com/RiV-chain/RiV-mesh/src/ipv6rwc" + "github.com/RiV-chain/RiV-mesh/src/multicast" + "github.com/RiV-chain/RiV-mesh/src/tun" + "github.com/RiV-chain/RiV-mesh/src/version" ) type node struct { @@ -153,6 +153,8 @@ type yggArgs struct { useconffile string logto string loglevel string + httpaddress string + wwwroot string } func getArgs() yggArgs { @@ -167,6 +169,9 @@ func getArgs() yggArgs { getaddr := flag.Bool("address", false, "returns the IPv6 address as derived from the supplied configuration") getsnet := flag.Bool("subnet", false, "returns the IPv6 subnet as derived from the supplied configuration") loglevel := flag.String("loglevel", "info", "loglevel to enable") + httpaddress := flag.String("httpaddress", "", "httpaddress to enable") + wwwroot := flag.String("wwwroot", "", "wwwroot to enable") + flag.Parse() return yggArgs{ genconf: *genconf, @@ -180,10 +185,11 @@ func getArgs() yggArgs { getaddr: *getaddr, getsnet: *getsnet, loglevel: *loglevel, + httpaddress: *httpaddress, + wwwroot: *wwwroot, } } -// The main function is responsible for configuring and starting Yggdrasil. func run(args yggArgs, ctx context.Context) { // Create a new logger that logs output to stdout. var logger *log.Logger @@ -281,10 +287,12 @@ func run(args yggArgs, ctx context.Context) { } return } + //override httpaddress and wwwroot parameters in cfg + cfg.HttpAddress = args.httpaddress + cfg.WwwRoot = args.wwwroot n := &node{} - - // Setup the Yggdrasil node itself. + // Setup the RiV-mesh node itself. { sk, err := hex.DecodeString(cfg.PrivateKey) if err != nil { @@ -335,10 +343,10 @@ func run(args yggArgs, ctx context.Context) { options := []multicast.SetupOption{} for _, intf := range cfg.MulticastInterfaces { options = append(options, multicast.MulticastInterface{ - Regex: regexp.MustCompile(intf.Regex), - Beacon: intf.Beacon, - Listen: intf.Listen, - Port: intf.Port, + Regex: regexp.MustCompile(intf.Regex), + Beacon: intf.Beacon, + Listen: intf.Listen, + Port: intf.Port, Priority: intf.Priority, }) } @@ -372,7 +380,8 @@ func run(args yggArgs, ctx context.Context) { logger.Infof("Your public key is %s", hex.EncodeToString(public[:])) logger.Infof("Your IPv6 address is %s", address.String()) logger.Infof("Your IPv6 subnet is %s", subnet.String()) - + // Start HTTP server + n.admin.StartHttpServer(cfg) // Block until we are told to shut down. <-ctx.Done() diff --git a/cmd/yggdrasilctl/cmd_line_env.go b/cmd/meshctl/cmd_line_env.go similarity index 98% rename from cmd/yggdrasilctl/cmd_line_env.go rename to cmd/meshctl/cmd_line_env.go index 9fcabad9..b2883b84 100644 --- a/cmd/yggdrasilctl/cmd_line_env.go +++ b/cmd/meshctl/cmd_line_env.go @@ -10,7 +10,7 @@ import ( "github.com/hjson/hjson-go" "golang.org/x/text/encoding/unicode" - "github.com/yggdrasil-network/yggdrasil-go/src/defaults" + "github.com/RiV-chain/RiV-mesh/src/defaults" ) type CmdLineEnv struct { diff --git a/cmd/yggdrasilctl/main.go b/cmd/meshctl/main.go similarity index 93% rename from cmd/yggdrasilctl/main.go rename to cmd/meshctl/main.go index c9b1522a..0aba84ea 100644 --- a/cmd/yggdrasilctl/main.go +++ b/cmd/meshctl/main.go @@ -14,11 +14,11 @@ import ( "time" "github.com/olekukonko/tablewriter" - "github.com/yggdrasil-network/yggdrasil-go/src/admin" - "github.com/yggdrasil-network/yggdrasil-go/src/core" - "github.com/yggdrasil-network/yggdrasil-go/src/multicast" - "github.com/yggdrasil-network/yggdrasil-go/src/tun" - "github.com/yggdrasil-network/yggdrasil-go/src/version" + "github.com/RiV-chain/RiV-mesh/src/admin" + "github.com/RiV-chain/RiV-mesh/src/core" + "github.com/RiV-chain/RiV-mesh/src/multicast" + "github.com/RiV-chain/RiV-mesh/src/tun" + "github.com/RiV-chain/RiV-mesh/src/version" ) func main() { @@ -45,7 +45,7 @@ func run() int { if cmdLineEnv.ver { fmt.Println("Build name:", version.BuildName()) fmt.Println("Build version:", version.BuildVersion()) - fmt.Println("To get the version number of the running Yggdrasil node, run", os.Args[0], "getSelf") + fmt.Println("To get the version number of the running Mesh node, run", os.Args[0], "getSelf") return 0 } @@ -111,6 +111,8 @@ func run() int { panic(err) } logger.Printf("Request sent") + //js, _ := json.Marshal(send) + //fmt.Println("sent:", string(js)) if err := decoder.Decode(&recv); err != nil { panic(err) } @@ -180,11 +182,10 @@ func run() int { fmt.Sprintf("%d", peer.Port), peer.PublicKey, peer.IPAddress, + peer.Remote, (time.Duration(peer.Uptime) * time.Second).String(), peer.RXBytes.String(), peer.TXBytes.String(), - fmt.Sprintf("%d", peer.Priority), - peer.Remote, }) } table.Render() @@ -270,8 +271,6 @@ func run() int { } table.Render() - case "addpeer", "removepeer": - default: fmt.Println(string(recv.Response)) } diff --git a/contrib/ansible/genkeys.go b/contrib/ansible/genkeys.go index 4a02b9bd..5d941ad1 100644 --- a/contrib/ansible/genkeys.go +++ b/contrib/ansible/genkeys.go @@ -1,6 +1,6 @@ /* -This file generates crypto keys for [ansible-yggdrasil](https://github.com/jcgruenhage/ansible-yggdrasil/) +This file generates crypto keys for [ansible-mesh](https://github.com/jcgruenhage/ansible-mesh/) */ package main @@ -14,7 +14,7 @@ import ( "os" "github.com/cheggaaa/pb/v3" - "github.com/yggdrasil-network/yggdrasil-go/src/address" + "github.com/RiV-chain/RiV-mesh/src/address" ) var numHosts = flag.Int("hosts", 1, "number of host vars to generate") @@ -57,8 +57,8 @@ func main() { return } defer file.Close() - file.WriteString(fmt.Sprintf("yggdrasil_public_key: %v\n", hex.EncodeToString(keys[i].pub))) - file.WriteString("yggdrasil_private_key: \"{{ vault_yggdrasil_private_key }}\"\n") + file.WriteString(fmt.Sprintf("mesh_public_key: %v\n", hex.EncodeToString(keys[i].pub))) + file.WriteString("mesh_private_key: \"{{ vault_mesh_private_key }}\"\n") file.WriteString(fmt.Sprintf("ansible_host: %v\n", keys[i].ip)) file, err = os.Create(fmt.Sprintf("host_vars/%x/vault", i)) @@ -66,7 +66,7 @@ func main() { return } defer file.Close() - file.WriteString(fmt.Sprintf("vault_yggdrasil_private_key: %v\n", hex.EncodeToString(keys[i].priv))) + file.WriteString(fmt.Sprintf("vault_mesh_private_key: %v\n", hex.EncodeToString(keys[i].priv))) bar.Increment() } bar.Finish() diff --git a/contrib/apparmor/usr.bin.yggdrasil b/contrib/apparmor/usr.bin.mesh similarity index 80% rename from contrib/apparmor/usr.bin.yggdrasil rename to contrib/apparmor/usr.bin.mesh index b305199f..cecd410b 100644 --- a/contrib/apparmor/usr.bin.yggdrasil +++ b/contrib/apparmor/usr.bin.mesh @@ -1,7 +1,7 @@ # Last Modified: Fri Oct 30 11:33:31 2020 #include -/usr/bin/yggdrasil { +/usr/bin/mesh { #include #include @@ -12,6 +12,6 @@ /proc/sys/net/core/somaxconn r, /sys/kernel/mm/transparent_hugepage/hpage_pmd_size r, - /etc/yggdrasil.conf rw, - /run/yggdrasil.sock rw, + /etc/mesh.conf rw, + /run/mesh.sock rw, } diff --git a/contrib/busybox-init/S42yggdrasil b/contrib/busybox-init/S42mesh similarity index 68% rename from contrib/busybox-init/S42yggdrasil rename to contrib/busybox-init/S42mesh index 862efc25..9d6b0e39 100755 --- a/contrib/busybox-init/S42yggdrasil +++ b/contrib/busybox-init/S42mesh @@ -1,9 +1,9 @@ #!/bin/sh -CONFFILE="/etc/yggdrasil.conf" +CONFFILE="/etc/mesh.conf" genconf() { - /usr/bin/yggdrasil -genconf > "$1" + /usr/bin/mesh -genconf > "$1" return $? } @@ -33,8 +33,8 @@ start() { fi fi - printf 'Starting yggdrasil: ' - if start-stop-daemon -S -q -b -x /usr/bin/yggdrasil \ + printf 'Starting mesh: ' + if start-stop-daemon -S -q -b -x /usr/bin/mesh \ -- -useconffile "$CONFFILE"; then echo "OK" else @@ -43,8 +43,8 @@ start() { } stop() { - printf "Stopping yggdrasil: " - if start-stop-daemon -K -q -x /usr/bin/yggdrasil; then + printf "Stopping mesh: " + if start-stop-daemon -K -q -x /usr/bin/mesh; then echo "OK" else echo "FAIL" @@ -52,8 +52,8 @@ stop() { } reload() { - printf "Reloading yggdrasil: " - if start-stop-daemon -K -q -s HUP -x /usr/bin/yggdrasil; then + printf "Reloading mesh: " + if start-stop-daemon -K -q -s HUP -x /usr/bin/mesh; then echo "OK" else echo "FAIL" diff --git a/contrib/deb/generate-gui.sh b/contrib/deb/generate-gui.sh new file mode 100755 index 00000000..42905376 --- /dev/null +++ b/contrib/deb/generate-gui.sh @@ -0,0 +1,165 @@ +#!/bin/sh + +# This is a lazy script to create a .deb for Debian/Ubuntu. It installs +# mesh and enables it in systemd. You can give it the PKGARCH= argument +# i.e. PKGARCH=i386 sh contrib/deb/generate.sh + +if [ `pwd` != `git rev-parse --show-toplevel` ] +then + echo "You should run this script from the top-level directory of the git repo" + exit 1 +fi + +PKGBRANCH=$(basename `git name-rev --name-only HEAD`) +PKG=$(sh contrib/semver/name.sh) +PKGVERSION=$(sh contrib/semver/version.sh --bare) +PKGARCH=${PKGARCH-amd64} +PKGNAME=$PKG-$PKGVERSION-$PKGARCH +PKGFILE=$PKGNAME.deb +PKGREPLACES=mesh + +if [ $PKGBRANCH = "master" ]; then + PKGREPLACES=mesh-develop +fi + +if [ $PKGARCH = "amd64" ]; then GOARCH=amd64 GOOS=linux ./build +elif [ $PKGARCH = "i386" ]; then GOARCH=386 GOOS=linux ./build +else + echo "Specify PKGARCH=amd64,i386,mips,mipsel,armhf,arm64,armel" + exit 1 +fi + +echo "Building $PKGFILE" + +mkdir -p /tmp/$PKGNAME/ +mkdir -p /tmp/$PKGNAME/debian/ +mkdir -p /tmp/$PKGNAME/DEBIAN/ +mkdir -p /tmp/$PKGNAME/usr/bin/ +mkdir -p /tmp/$PKGNAME/usr/local/bin/ +mkdir -p /tmp/$PKGNAME/etc/systemd/system/ +mkdir -p /tmp/$PKGNAME/usr/share/applications/ +mkdir -p /tmp/$PKGNAME/etc/ +mkdir -p /tmp/$PKGNAME/etc/xdg/autostart +chmod 0775 /tmp/$PKGNAME/ -R + +for resolution in 16x16 24x24 32x32 48x48 64x64 192x192 256x256 512x512; do + echo "Converting icon for: $resolution" + mkdir -p /tmp/$PKGNAME/usr/share/icons/hicolor/$resolution/apps && \ + convert -colorspace sRGB ./riv.png -resize $resolution PNG8:/tmp/$PKGNAME/usr/share/icons/hicolor/$resolution/apps/riv.png && \ + chmod 644 /tmp/$PKGNAME/usr/share/icons/hicolor/$resolution/apps/riv.png +done + +cp contrib/ui/mesh-ui/index.html /tmp/$PKGNAME/etc/ + +cat > /tmp/$PKGNAME/usr/share/applications/riv.desktop << EOF +[Desktop Entry] +Name=RiV mesh +GenericName=Mesh network +Comment=RiV-mesh is an early-stage implementation of a fully end-to-end encrypted IPv6 network +Exec=sh -c "/usr/bin/mesh-ui /etc/index.html" +Terminal=false +Type=Application +Icon=riv +Categories=Network;FileTransfer; +StartupNotify=false +EOF + +cat > /tmp/$PKGNAME/debian/changelog << EOF +Please see https://github.com/RiV-chain/RiV-mesh/ +EOF +echo 9 > /tmp/$PKGNAME/debian/compat +cat > /tmp/$PKGNAME/DEBIAN/control << EOF +Package: mesh +Version: $PKGVERSION +Section: contrib/net +Priority: extra +Architecture: $PKGARCH +Replaces: $PKGREPLACES +Conflicts: $PKGREPLACES +Maintainer: Vadym Vikulin +Description: RiV-mesh is an implementation of a fully end-to-end encrypted IPv6 network. + It is lightweight, self-arranging, supported on multiple platforms and + allows pretty much any IPv6-capable application to communicate securely with + other RiV-mesh nodes. +EOF +cat > /tmp/$PKGNAME/debian/copyright << EOF +Please see https://github.com/RiV-chain/RiV-mesh/ +EOF +cat > /tmp/$PKGNAME/debian/docs << EOF +Please see https://github.com/RiV-chain/RiV-mesh/ +EOF +cat > /tmp/$PKGNAME/debian/install << EOF +usr/bin/mesh usr/bin +usr/bin/meshctl usr/bin +usr/bin/mesh-ui usr/bin +usr/local/bin/meshctl usr/local/bin +etc/index.html etc +etc/xdg/autostart/riv.desktop etc/xdg/autostart +etc/systemd/system/*.service etc/systemd/system +usr/share/applications/riv.desktop usr/share/applications +usr/share/icons/hicolor/16x16/apps/riv.png usr/share/icons/hicolor/16x16/apps +usr/share/icons/hicolor/24x24/apps/riv.png usr/share/icons/hicolor/24x24/apps +usr/share/icons/hicolor/32x32/apps/riv.png usr/share/icons/hicolor/32x32/apps +usr/share/icons/hicolor/48x48/apps/riv.png usr/share/icons/hicolor/48x48/apps +usr/share/icons/hicolor/64x64/apps/riv.png usr/share/icons/hicolor/64x64/apps +usr/share/icons/hicolor/192x192/apps/riv.png usr/share/icons/hicolor/192x192/apps +usr/share/icons/hicolor/256x256/apps/riv.png usr/share/icons/hicolor/256x256/apps +usr/share/icons/hicolor/512x512/apps/riv.png usr/share/icons/hicolor/512x512/apps +EOF + +cat > /tmp/$PKGNAME/DEBIAN/postinst << EOF +#!/bin/sh + +if ! getent group mesh 2>&1 > /dev/null; then + groupadd --system --force mesh || echo "Failed to create group 'mesh' - please create it manually and reinstall" +fi + +if [ -f /etc/mesh.conf ]; then + mkdir -p /var/backups + echo "Backing up configuration file to /var/backups/mesh.conf.`date +%Y%m%d`" + cp /etc/mesh.conf /var/backups/mesh.conf.`date +%Y%m%d` + echo "Normalising and updating /etc/mesh.conf" + /usr/bin/mesh -useconf -normaliseconf < /var/backups/mesh.conf.`date +%Y%m%d` > /etc/mesh.conf +else + echo "Generating initial configuration file /etc/mesh.conf" + echo "Please familiarise yourself with this file before starting Mesh" + sh -c 'umask 0027 && /usr/bin/mesh -genconf > /etc/mesh.conf' +fi +chgrp mesh /etc/mesh.conf +chmod 755 /etc/mesh.conf +if command -v systemctl >/dev/null; then + systemctl daemon-reload || echo -n "daemon not reloaded!" + systemctl enable mesh || echo -n "systemctl enable failed!" + systemctl restart mesh || echo -n "systemctl restart failed!" +fi +update-icon-caches /usr/share/icons/* +update-desktop-database /usr/share/applications +EOF + +cat > /tmp/$PKGNAME/DEBIAN/prerm << EOF +#!/bin/sh +if command -v systemctl >/dev/null; then + if systemctl is-active --quiet mesh; then + systemctl stop mesh || true + fi + systemctl disable mesh || true +fi +EOF + +cp mesh /tmp/$PKGNAME/usr/bin/ +cp meshctl /tmp/$PKGNAME/usr/bin/ +cp mesh-ui /tmp/$PKGNAME/usr/bin/ +ln -s /usr/bin/meshctl /tmp/$PKGNAME/usr/local/bin/meshctl +cp contrib/systemd/*.service /tmp/$PKGNAME/etc/systemd/system/ +cp /tmp/$PKGNAME/usr/share/applications/riv.desktop /tmp/$PKGNAME/etc/xdg/autostart +chmod 0775 /tmp/$PKGNAME/DEBIAN/* +chmod 644 /tmp/$PKGNAME/etc/systemd/system/* +chmod 644 /tmp/$PKGNAME/usr/share/applications/riv.desktop +chmod 644 /tmp/$PKGNAME/etc/xdg/autostart/* +chmod 755 /tmp/$PKGNAME/usr/bin/* +chmod 755 /tmp/$PKGNAME/etc/index.html + +dpkg-deb --build --root-owner-group /tmp/$PKGNAME +cp /tmp/$PKGFILE . + +rm -rf /tmp/$PKGNAME diff --git a/contrib/deb/generate.sh b/contrib/deb/generate.sh index ebe2753a..30622c19 100644 --- a/contrib/deb/generate.sh +++ b/contrib/deb/generate.sh @@ -1,7 +1,7 @@ #!/bin/sh # This is a lazy script to create a .deb for Debian/Ubuntu. It installs -# yggdrasil and enables it in systemd. You can give it the PKGARCH= argument +# mesh and enables it in systemd. You can give it the PKGARCH= argument # i.e. PKGARCH=i386 sh contrib/deb/generate.sh if [ `pwd` != `git rev-parse --show-toplevel` ] @@ -11,14 +11,15 @@ then fi PKGBRANCH=$(basename `git name-rev --name-only HEAD`) -PKGNAME=$(sh contrib/semver/name.sh) +PKG=$(sh contrib/semver/name.sh) PKGVERSION=$(sh contrib/semver/version.sh --bare) PKGARCH=${PKGARCH-amd64} -PKGFILE=$PKGNAME-$PKGVERSION-$PKGARCH.deb -PKGREPLACES=yggdrasil +PKGNAME=$PKG-$PKGVERSION-$PKGARCH-nogui +PKGFILE=$PKGNAME.deb +PKGREPLACES=mesh if [ $PKGBRANCH = "master" ]; then - PKGREPLACES=yggdrasil-develop + PKGREPLACES=mesh-develop fi if [ $PKGARCH = "amd64" ]; then GOARCH=amd64 GOOS=linux ./build @@ -37,91 +38,92 @@ echo "Building $PKGFILE" mkdir -p /tmp/$PKGNAME/ mkdir -p /tmp/$PKGNAME/debian/ +mkdir -p /tmp/$PKGNAME/DEBIAN/ mkdir -p /tmp/$PKGNAME/usr/bin/ +mkdir -p /tmp/$PKGNAME/usr/local/bin/ mkdir -p /tmp/$PKGNAME/etc/systemd/system/ +chmod 0775 /tmp/$PKGNAME/ -R cat > /tmp/$PKGNAME/debian/changelog << EOF -Please see https://github.com/yggdrasil-network/yggdrasil-go/ +Please see https://github.com/RiV-chain/RiV-mesh/ EOF echo 9 > /tmp/$PKGNAME/debian/compat -cat > /tmp/$PKGNAME/debian/control << EOF -Package: $PKGNAME +cat > /tmp/$PKGNAME/DEBIAN/control << EOF +Package: mesh Version: $PKGVERSION Section: contrib/net Priority: extra Architecture: $PKGARCH Replaces: $PKGREPLACES Conflicts: $PKGREPLACES -Maintainer: Neil Alexander -Description: Yggdrasil Network - Yggdrasil is an early-stage implementation of a fully end-to-end encrypted IPv6 - network. It is lightweight, self-arranging, supported on multiple platforms and +Maintainer: Vadym Vikulin +Description: RiV-mesh is an implementation of a fully end-to-end encrypted IPv6 network. + It is lightweight, self-arranging, supported on multiple platforms and allows pretty much any IPv6-capable application to communicate securely with - other Yggdrasil nodes. + other RiV-mesh nodes. EOF cat > /tmp/$PKGNAME/debian/copyright << EOF -Please see https://github.com/yggdrasil-network/yggdrasil-go/ +Please see https://github.com/RiV-chain/RiV-mesh/ EOF cat > /tmp/$PKGNAME/debian/docs << EOF -Please see https://github.com/yggdrasil-network/yggdrasil-go/ +Please see https://github.com/RiV-chain/RiV-mesh/ EOF cat > /tmp/$PKGNAME/debian/install << EOF -usr/bin/yggdrasil usr/bin -usr/bin/yggdrasilctl usr/bin +usr/bin/mesh usr/bin +usr/bin/meshctl usr/bin +usr/local/bin/meshctl usr/local/bin etc/systemd/system/*.service etc/systemd/system EOF -cat > /tmp/$PKGNAME/debian/postinst << EOF +cat > /tmp/$PKGNAME/DEBIAN/postinst << EOF #!/bin/sh -if ! getent group yggdrasil 2>&1 > /dev/null; then - groupadd --system --force yggdrasil || echo "Failed to create group 'yggdrasil' - please create it manually and reinstall" +if ! getent group mesh 2>&1 > /dev/null; then + groupadd --system --force mesh || echo "Failed to create group 'mesh' - please create it manually and reinstall" fi -if [ -f /etc/yggdrasil.conf ]; -then +if [ -f /etc/mesh.conf ]; then mkdir -p /var/backups - echo "Backing up configuration file to /var/backups/yggdrasil.conf.`date +%Y%m%d`" - cp /etc/yggdrasil.conf /var/backups/yggdrasil.conf.`date +%Y%m%d` - echo "Normalising and updating /etc/yggdrasil.conf" - /usr/bin/yggdrasil -useconf -normaliseconf < /var/backups/yggdrasil.conf.`date +%Y%m%d` > /etc/yggdrasil.conf - chgrp yggdrasil /etc/yggdrasil.conf - - if command -v systemctl >/dev/null; then - systemctl daemon-reload >/dev/null || true - systemctl enable yggdrasil || true - systemctl start yggdrasil || true - fi + echo "Backing up configuration file to /var/backups/mesh.conf.`date +%Y%m%d`" + cp /etc/mesh.conf /var/backups/mesh.conf.`date +%Y%m%d` + echo "Normalising and updating /etc/mesh.conf" + /usr/bin/mesh -useconf -normaliseconf < /var/backups/mesh.conf.`date +%Y%m%d` > /etc/mesh.conf else - echo "Generating initial configuration file /etc/yggdrasil.conf" - echo "Please familiarise yourself with this file before starting Yggdrasil" - sh -c 'umask 0027 && /usr/bin/yggdrasil -genconf > /etc/yggdrasil.conf' - chgrp yggdrasil /etc/yggdrasil.conf + echo "Generating initial configuration file /etc/mesh.conf" + echo "Please familiarise yourself with this file before starting RiV-mesh" + sh -c 'umask 0027 && /usr/bin/mesh -genconf > /etc/mesh.conf' +fi +chgrp mesh /etc/mesh.conf +chmod 755 /etc/mesh.conf +if command -v systemctl >/dev/null; then + systemctl daemon-reload || echo -n "daemon not reloaded!" + systemctl enable mesh || echo -n "systemctl enable failed!" + systemctl restart mesh || echo -n "systemctl restart failed!" fi EOF -cat > /tmp/$PKGNAME/debian/prerm << EOF +cat > /tmp/$PKGNAME/DEBIAN/prerm << EOF #!/bin/sh if command -v systemctl >/dev/null; then - if systemctl is-active --quiet yggdrasil; then - systemctl stop yggdrasil || true + if systemctl is-active --quiet mesh; then + systemctl stop mesh || true fi - systemctl disable yggdrasil || true + systemctl disable mesh || true fi EOF -cp yggdrasil /tmp/$PKGNAME/usr/bin/ -cp yggdrasilctl /tmp/$PKGNAME/usr/bin/ -cp contrib/systemd/*.service /tmp/$PKGNAME/etc/systemd/system/ +cp mesh /tmp/$PKGNAME/usr/bin/ +cp meshctl /tmp/$PKGNAME/usr/bin/ +ln -s /usr/bin/meshctl /tmp/$PKGNAME/usr/local/bin/meshctl +if [ $LOGLEVEL = "DEBUG" ]; then cp contrib/systemd/mesh-debug.service /tmp/$PKGNAME/etc/systemd/system/mesh.service +else + cp contrib/systemd/mesh.service /tmp/$PKGNAME/etc/systemd/system/ +fi -tar -czvf /tmp/$PKGNAME/data.tar.gz -C /tmp/$PKGNAME/ \ - usr/bin/yggdrasil usr/bin/yggdrasilctl \ - etc/systemd/system/yggdrasil.service \ - etc/systemd/system/yggdrasil-default-config.service -tar -czvf /tmp/$PKGNAME/control.tar.gz -C /tmp/$PKGNAME/debian . -echo 2.0 > /tmp/$PKGNAME/debian-binary +cp contrib/systemd/mesh-default-config.service /tmp/$PKGNAME/etc/systemd/system/ +chmod 0775 /tmp/$PKGNAME/DEBIAN/* +chmod 644 /tmp/$PKGNAME/etc/systemd/system/* +chmod 755 /tmp/$PKGNAME/usr/bin/* -ar -r $PKGFILE \ - /tmp/$PKGNAME/debian-binary \ - /tmp/$PKGNAME/control.tar.gz \ - /tmp/$PKGNAME/data.tar.gz +dpkg-deb --build --root-owner-group /tmp/$PKGNAME +cp /tmp/$PKGFILE . rm -rf /tmp/$PKGNAME diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index 15129b16..bdce88d7 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -9,17 +9,17 @@ RUN apk add git && ./build && go build -o /src/genkeys cmd/genkeys/main.go FROM docker.io/alpine -COPY --from=builder /src/yggdrasil /usr/bin/yggdrasil -COPY --from=builder /src/yggdrasilctl /usr/bin/yggdrasilctl +COPY --from=builder /src/mesh /usr/bin/mesh +COPY --from=builder /src/meshctl /usr/bin/meshctl COPY --from=builder /src/genkeys /usr/bin/genkeys COPY contrib/docker/entrypoint.sh /usr/bin/entrypoint.sh -# RUN addgroup -g 1000 -S yggdrasil-network \ -# && adduser -u 1000 -S -g 1000 --home /etc/yggdrasil-network yggdrasil-network +# RUN addgroup -g 1000 -S RiV-chain \ +# && adduser -u 1000 -S -g 1000 --home /etc/RiV-chain RiV-chain # -# USER yggdrasil-network +# USER RiV-chain # TODO: Make running unprivileged work -VOLUME [ "/etc/yggdrasil-network" ] +VOLUME [ "/etc/RiV-chain" ] ENTRYPOINT [ "/usr/bin/entrypoint.sh" ] diff --git a/contrib/docker/entrypoint.sh b/contrib/docker/entrypoint.sh index 26c685a8..167eead1 100755 --- a/contrib/docker/entrypoint.sh +++ b/contrib/docker/entrypoint.sh @@ -2,12 +2,12 @@ set -e -CONF_DIR="/etc/yggdrasil-network" +CONF_DIR="/etc/RiV-chain" if [ ! -f "$CONF_DIR/config.conf" ]; then echo "generate $CONF_DIR/config.conf" - yggdrasil --genconf > "$CONF_DIR/config.conf" + mesh --genconf > "$CONF_DIR/config.conf" fi -yggdrasil --useconf < "$CONF_DIR/config.conf" +mesh --useconf < "$CONF_DIR/config.conf" exit $? diff --git a/contrib/freebsd/mesh b/contrib/freebsd/mesh new file mode 100644 index 00000000..30fb8471 --- /dev/null +++ b/contrib/freebsd/mesh @@ -0,0 +1,72 @@ +#!/bin/sh +# +# Put the mesh and meshctl binaries into /usr/local/bin +# Then copy this script into /etc/rc.d/mesh +# Finally, run: +# 1. chmod +x /etc/rc.d/mesh /usr/local/bin/{mesh,meshctl} +# 2. echo "mesh_enable=yes" >> /etc/rc.d +# 3. service mesh start +# +# PROVIDE: mesh +# REQUIRE: networking +# KEYWORD: + +. /etc/rc.subr + +name="mesh" +rcvar="mesh_enable" + +start_cmd="${name}_start" +stop_cmd="${name}_stop" + +pidfile="/var/run/mesh/${name}.pid" +command="/usr/sbin/daemon" +command_args="-P ${pidfile} -r -f ${mesh_command}" + +mesh_start() +{ + test ! -x /usr/local/bin/mesh && ( + logger -s -t mesh "Warning: /usr/local/bin/mesh is missing or not executable" + logger -s -t mesh "Copy the mesh binary into /usr/local/bin and then chmod +x /usr/local/bin/mesh" + return 1 + ) + + test ! -f /etc/mesh.conf && ( + logger -s -t mesh "Generating new configuration file into /etc/mesh.conf" + /usr/local/bin/mesh -genconf > /etc/mesh.conf + ) + + tap_path="$(cat /etc/mesh.conf | egrep -o '/dev/tap[0-9]{1,2}$')" + tap_name="$(echo -n ${tap_path} | tr -d '/dev/')" + + /sbin/ifconfig ${tap_name} >/dev/null 2>&1 || ( + logger -s -t mesh "Creating ${tap_name} adapter" + /sbin/ifconfig ${tap_name} create || logger -s -t mesh "Failed to create ${tap_name} adapter" + ) + + test ! -d /var/run/mesh && mkdir -p /var/run/mesh + + logger -s -t mesh "Starting mesh" + ${command} ${command_args} /usr/local/bin/mesh -useconffile /etc/mesh.conf \ + 1>/var/log/mesh.stdout.log \ + 2>/var/log/mesh.stderr.log & +} + +mesh_stop() +{ + logger -s -t mesh "Stopping mesh" + test -f /var/run/mesh/${name}.pid && kill -TERM $(cat /var/run/mesh/${name}.pid) + + tap_path="$(cat /etc/mesh.conf | grep /dev/tap | egrep -o '/dev/.*$')" + tap_name="$(echo -n ${tap_path} | tr -d '/dev/')" + + /sbin/ifconfig ${tap_name} >/dev/null 2>&1 && ( + logger -s -t mesh "Destroying ${tap_name} adapter" + /sbin/ifconfig ${tap_name} destroy || logger -s -t mesh "Failed to destroy ${tap_name} adapter" + ) +} + +load_rc_config $name +: ${mesh_enable:=no} + +run_rc_command "$1" diff --git a/contrib/freebsd/yggdrasil b/contrib/freebsd/yggdrasil deleted file mode 100644 index 58482fc9..00000000 --- a/contrib/freebsd/yggdrasil +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/sh -# -# Put the yggdrasil and yggdrasilctl binaries into /usr/local/bin -# Then copy this script into /etc/rc.d/yggdrasil -# Finally, run: -# 1. chmod +x /etc/rc.d/yggdrasil /usr/local/bin/{yggdrasil,yggdrasilctl} -# 2. echo "yggdrasil_enable=yes" >> /etc/rc.d -# 3. service yggdrasil start -# -# PROVIDE: yggdrasil -# REQUIRE: networking -# KEYWORD: - -. /etc/rc.subr - -name="yggdrasil" -rcvar="yggdrasil_enable" - -start_cmd="${name}_start" -stop_cmd="${name}_stop" - -pidfile="/var/run/yggdrasil/${name}.pid" -command="/usr/sbin/daemon" -command_args="-P ${pidfile} -r -f ${yggdrasil_command}" - -yggdrasil_start() -{ - test ! -x /usr/local/bin/yggdrasil && ( - logger -s -t yggdrasil "Warning: /usr/local/bin/yggdrasil is missing or not executable" - logger -s -t yggdrasil "Copy the yggdrasil binary into /usr/local/bin and then chmod +x /usr/local/bin/yggdrasil" - return 1 - ) - - test ! -f /etc/yggdrasil.conf && ( - logger -s -t yggdrasil "Generating new configuration file into /etc/yggdrasil.conf" - /usr/local/bin/yggdrasil -genconf > /etc/yggdrasil.conf - ) - - tap_path="$(cat /etc/yggdrasil.conf | egrep -o '/dev/tap[0-9]{1,2}$')" - tap_name="$(echo -n ${tap_path} | tr -d '/dev/')" - - /sbin/ifconfig ${tap_name} >/dev/null 2>&1 || ( - logger -s -t yggdrasil "Creating ${tap_name} adapter" - /sbin/ifconfig ${tap_name} create || logger -s -t yggdrasil "Failed to create ${tap_name} adapter" - ) - - test ! -d /var/run/yggdrasil && mkdir -p /var/run/yggdrasil - - logger -s -t yggdrasil "Starting yggdrasil" - ${command} ${command_args} /usr/local/bin/yggdrasil -useconffile /etc/yggdrasil.conf \ - 1>/var/log/yggdrasil.stdout.log \ - 2>/var/log/yggdrasil.stderr.log & -} - -yggdrasil_stop() -{ - logger -s -t yggdrasil "Stopping yggdrasil" - test -f /var/run/yggdrasil/${name}.pid && kill -TERM $(cat /var/run/yggdrasil/${name}.pid) - - tap_path="$(cat /etc/yggdrasil.conf | grep /dev/tap | egrep -o '/dev/.*$')" - tap_name="$(echo -n ${tap_path} | tr -d '/dev/')" - - /sbin/ifconfig ${tap_name} >/dev/null 2>&1 && ( - logger -s -t yggdrasil "Destroying ${tap_name} adapter" - /sbin/ifconfig ${tap_name} destroy || logger -s -t yggdrasil "Failed to destroy ${tap_name} adapter" - ) -} - -load_rc_config $name -: ${yggdrasil_enable:=no} - -run_rc_command "$1" diff --git a/contrib/macos/create-pkg-gui.sh b/contrib/macos/create-pkg-gui.sh new file mode 100644 index 00000000..35a617e6 --- /dev/null +++ b/contrib/macos/create-pkg-gui.sh @@ -0,0 +1,176 @@ +#!/bin/sh + +# Check if xar and mkbom are available +command -v xar >/dev/null 2>&1 || ( + echo "Building xar" + sudo apt-get install libxml2-dev libssl1.0-dev zlib1g-dev autoconf -y + rm -rf /tmp/xar && mkdir -p /tmp/xar && cd /tmp/xar + #git clone https://github.com/mackyle/xar && cd xar/xar + git clone https://github.com/RiV-chain/xar.git && cd xar/xar + (sh autogen.sh && make && sudo make install) || (echo "Failed to build xar"; exit 1) +) +command -v mkbom >/dev/null 2>&1 || ( + echo "Building mkbom" + mkdir -p /tmp/mkbom && cd /tmp/mkbom + git clone https://github.com/hogliux/bomutils && cd bomutils + sudo make install || (echo "Failed to build mkbom"; exit 1) +) + +# Build RiV-mesh +echo "running GO111MODULE=on GOOS=darwin GOARCH=${PKGARCH-amd64} ./build" +GO111MODULE=on GOOS=darwin GOARCH=${PKGARCH-amd64} ./build + +# Check if we can find the files we need - they should +# exist if you are running this script from the root of +# the RiV-mesh repo and you have ran ./build +test -f mesh || (echo "mesh binary not found"; exit 1) +test -f meshctl || (echo "meshctl binary not found"; exit 1) +test -f mesh-ui || (echo "mesh-ui binary not found"; exit 1) +test -f contrib/macos/mesh.plist || (echo "contrib/macos/mesh.plist not found"; exit 1) +test -f contrib/semver/version.sh || (echo "contrib/semver/version.sh not found"; exit 1) + +# Delete the pkgbuild folder if it already exists +test -d pkgbuild && rm -rf pkgbuild + +# Create our folder structure + +mkdir -p pkgbuild/scripts +mkdir -p pkgbuild/flat/base.pkg +mkdir -p pkgbuild/flat/Resources/en.lproj +mkdir -p pkgbuild/root/Applications/RiV-mesh.app/Contents/MacOS +mkdir -p pkgbuild/root/Applications/RiV-mesh.app/Contents/Resources +mkdir -p pkgbuild/root/usr/local/bin +mkdir -p pkgbuild/root/Library/LaunchDaemons + +# Copy package contents into the pkgbuild root +cp meshctl pkgbuild/root/usr/local/bin +cp mesh pkgbuild/root/Applications/RiV-mesh.app/Contents/MacOS +cp mesh-ui pkgbuild/root/Applications/RiV-mesh.app/Contents/MacOS +cp riv.icns pkgbuild/root/Applications/RiV-mesh.app/Contents/Resources +cp contrib/ui/mesh-ui/index.html pkgbuild/root/Applications/RiV-mesh.app/Contents/MacOS +cp contrib/macos/mesh.plist pkgbuild/root/Library/LaunchDaemons + +# Create the postinstall script +cat > pkgbuild/scripts/postinstall << EOF +#!/bin/sh + +# Normalise the config if it exists, generate it if it doesn't +if [ -f /etc/mesh.conf ]; +then + mkdir -p /Library/Preferences/RiV-mesh + echo "Backing up configuration file to /Library/Preferences/RiV-mesh/mesh.conf.`date +%Y%m%d`" + cp /etc/mesh.conf /Library/Preferences/RiV-mesh/mesh.conf.`date +%Y%m%d` + echo "Normalising /etc/mesh.conf" + /Applications/RiV-mesh.app/Contents/MacOS/mesh -useconffile /Library/Preferences/RiV-mesh/mesh.conf.`date +%Y%m%d` -normaliseconf > /etc/mesh.conf +else + /Applications/RiV-mesh.app/Contents/MacOS/mesh -genconf > /etc/mesh.conf +fi + +chmod 755 /etc/mesh.conf + +# Unload existing RiV-mesh launchd service, if possible +test -f /Library/LaunchDaemons/mesh.plist && (launchctl unload /Library/LaunchDaemons/mesh.plist || true) + +# Load RiV-mesh launchd service and start RiV-mesh +launchctl load /Library/LaunchDaemons/mesh.plist +EOF + +# Set execution permissions +chmod 755 pkgbuild/scripts/postinstall +chmod 755 pkgbuild/root/usr/local/bin/meshctl +chmod 755 pkgbuild/root/Applications/RiV-mesh.app/Contents/MacOS/mesh +chmod 755 pkgbuild/root/Applications/RiV-mesh.app/Contents/MacOS/mesh-ui +chmod 755 pkgbuild/root/Applications/RiV-mesh.app/Contents/MacOS/index.html + +# Work out metadata for the package info +PKGNAME=$(sh contrib/semver/name.sh) +PKGVERSION=$(sh contrib/semver/version.sh --bare) +PKGARCH=${PKGARCH-amd64} + +# Create the Info.plist file +cat > pkgbuild/root/Applications/RiV-mesh.app/Contents/Info.plist << EOF + + + + Label + org.riv-mesh.ui + NSPrincipalClass + NSApplication + CFBundleName + RiV-mesh + NSHighResolutionCapable + True + CFBundleIconFile + riv.icns + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleGetInfoString + ${PKGVERSION} + CFBundleVersion + ${PKGVERSION} + CFBundleShortVersionString + ${PKGVERSION} + CFBundleExecutable + mesh-ui + CFBundleIdentifier + io.github.RiV-mesh.pkg + StandardOutPath + /tmp/mesh-ui.stdout.log + StandardErrorPath + /tmp/mesh-ui.stderr.log + + +EOF + +# Pack payload and scripts +( cd pkgbuild/scripts && find . | cpio -o --format odc --owner 0:80 | gzip -c ) > pkgbuild/flat/base.pkg/Scripts +( cd pkgbuild/root && find . | cpio -o --format odc --owner 0:80 | gzip -c ) > pkgbuild/flat/base.pkg/Payload + +PAYLOADSIZE=$(( $(wc -c pkgbuild/flat/base.pkg/Payload | awk '{ print $1 }') / 1024 )) + +# Create the PackageInfo file +cat > pkgbuild/flat/base.pkg/PackageInfo << EOF + + + + + + +EOF + +# Create the BOM +( cd pkgbuild && mkbom root flat/base.pkg/Bom ) + +# Create the Distribution file +cat > pkgbuild/flat/Distribution << EOF + + + RiV-mesh (${PKGNAME}-${PKGVERSION}) + + + + + + + + + + + #base.pkg + +EOF + +# Finally pack the .pkg +( cd pkgbuild/flat && xar --compression none -cf "../../${PKGNAME}-${PKGVERSION}-macos-${PKGARCH}.pkg" * ) diff --git a/contrib/macos/create-pkg.sh b/contrib/macos/create-pkg.sh index 773f01ee..960ee691 100755 --- a/contrib/macos/create-pkg.sh +++ b/contrib/macos/create-pkg.sh @@ -15,16 +15,12 @@ command -v mkbom >/dev/null 2>&1 || ( sudo make install || (echo "Failed to build mkbom"; exit 1) ) -# Build Yggdrasil -echo "running GO111MODULE=on GOOS=darwin GOARCH=${PKGARCH-amd64} ./build" -GO111MODULE=on GOOS=darwin GOARCH=${PKGARCH-amd64} ./build - # Check if we can find the files we need - they should # exist if you are running this script from the root of -# the yggdrasil-go repo and you have ran ./build -test -f yggdrasil || (echo "yggdrasil binary not found"; exit 1) -test -f yggdrasilctl || (echo "yggdrasilctl binary not found"; exit 1) -test -f contrib/macos/yggdrasil.plist || (echo "contrib/macos/yggdrasil.plist not found"; exit 1) +# the RiV-mesh repo and you have ran ./build +test -f mesh || (echo "mesh binary not found"; exit 1) +test -f meshctl || (echo "meshctl binary not found"; exit 1) +test -f contrib/macos/mesh.plist || (echo "contrib/macos/mesh.plist not found"; exit 1) test -f contrib/semver/version.sh || (echo "contrib/semver/version.sh not found"; exit 1) # Delete the pkgbuild folder if it already exists @@ -35,40 +31,43 @@ mkdir -p pkgbuild/scripts mkdir -p pkgbuild/flat/base.pkg mkdir -p pkgbuild/flat/Resources/en.lproj mkdir -p pkgbuild/root/usr/local/bin +mkdir -p pkgbuild/root/Applications/RiV-mesh.app/Contents/MacOS mkdir -p pkgbuild/root/Library/LaunchDaemons # Copy package contents into the pkgbuild root -cp yggdrasil pkgbuild/root/usr/local/bin -cp yggdrasilctl pkgbuild/root/usr/local/bin -cp contrib/macos/yggdrasil.plist pkgbuild/root/Library/LaunchDaemons +cp mesh pkgbuild/root/Applications/RiV-mesh.app/Contents/MacOS +cp meshctl pkgbuild/root/usr/local/bin +cp contrib/macos/mesh.plist pkgbuild/root/Library/LaunchDaemons # Create the postinstall script cat > pkgbuild/scripts/postinstall << EOF #!/bin/sh # Normalise the config if it exists, generate it if it doesn't -if [ -f /etc/yggdrasil.conf ]; +if [ -f /etc/mesh.conf ]; then - mkdir -p /Library/Preferences/Yggdrasil - echo "Backing up configuration file to /Library/Preferences/Yggdrasil/yggdrasil.conf.`date +%Y%m%d`" - cp /etc/yggdrasil.conf /Library/Preferences/Yggdrasil/yggdrasil.conf.`date +%Y%m%d` - echo "Normalising /etc/yggdrasil.conf" - /usr/local/bin/yggdrasil -useconffile /Library/Preferences/Yggdrasil/yggdrasil.conf.`date +%Y%m%d` -normaliseconf > /etc/yggdrasil.conf + mkdir -p /Library/Preferences/RiV-mesh + echo "Backing up configuration file to /Library/Preferences/RiV-mesh/mesh.conf.`date +%Y%m%d`" + cp /etc/mesh.conf /Library/Preferences/RiV-mesh/mesh.conf.`date +%Y%m%d` + echo "Normalising /etc/mesh.conf" + /Applications/RiV-mesh.app/Contents/MacOS/mesh -useconffile /Library/Preferences/RiV-mesh/mesh.conf.`date +%Y%m%d` -normaliseconf > /etc/mesh.conf else - /usr/local/bin/yggdrasil -genconf > /etc/yggdrasil.conf + /Applications/RiV-mesh.app/Contents/MacOS/mesh -genconf > /etc/mesh.conf fi -# Unload existing Yggdrasil launchd service, if possible -test -f /Library/LaunchDaemons/yggdrasil.plist && (launchctl unload /Library/LaunchDaemons/yggdrasil.plist || true) +chmod 755 /etc/mesh.conf -# Load Yggdrasil launchd service and start Yggdrasil -launchctl load /Library/LaunchDaemons/yggdrasil.plist +# Unload existing RiV-mesh launchd service, if possible +test -f /Library/LaunchDaemons/mesh.plist && (launchctl unload /Library/LaunchDaemons/mesh.plist || true) + +# Load RiV-mesh launchd service and start RiV-mesh +launchctl load /Library/LaunchDaemons/mesh.plist EOF # Set execution permissions -chmod +x pkgbuild/scripts/postinstall -chmod +x pkgbuild/root/usr/local/bin/yggdrasil -chmod +x pkgbuild/root/usr/local/bin/yggdrasilctl +chmod 755 pkgbuild/scripts/postinstall +chmod 755 pkgbuild/root/Applications/RiV-mesh.app/Contents/MacOS/mesh +chmod 755 pkgbuild/root/usr/local/bin/meshctl # Pack payload and scripts ( cd pkgbuild/scripts && find . | cpio -o --format odc --owner 0:80 | gzip -c ) > pkgbuild/flat/base.pkg/Scripts @@ -79,11 +78,10 @@ PKGNAME=$(sh contrib/semver/name.sh) PKGVERSION=$(sh contrib/semver/version.sh --bare) PKGARCH=${PKGARCH-amd64} PAYLOADSIZE=$(( $(wc -c pkgbuild/flat/base.pkg/Payload | awk '{ print $1 }') / 1024 )) -[ "$PKGARCH" = "amd64" ] && PKGHOSTARCH="x86_64" || PKGHOSTARCH=${PKGARCH} # Create the PackageInfo file cat > pkgbuild/flat/base.pkg/PackageInfo << EOF - + @@ -98,15 +96,15 @@ EOF cat > pkgbuild/flat/Distribution << EOF - Yggdrasil (${PKGNAME}-${PKGVERSION}) - + RiV-mesh (${PKGNAME}-${PKGVERSION}) + + + + + +
+
+ +
+ + + + +
+ +
+
+
IPv6
+
N/A
+
+ +
+
+ +
+
Subnet
+
N/A
+
+ +
+
+ +
+ +
Peers
+
+ +
+
+ +
+ +
+ +
+
+ + + + +
+ + +
+ +
+ + + + + diff --git a/contrib/ui/mesh-ui/webview.go b/contrib/ui/mesh-ui/webview.go new file mode 100755 index 00000000..80744d75 --- /dev/null +++ b/contrib/ui/mesh-ui/webview.go @@ -0,0 +1,266 @@ +package main + +import ( + "github.com/webview/webview" + "github.com/hjson/hjson-go" + "encoding/json" + "path/filepath" + "io/ioutil" + "os/exec" + "net/url" + "runtime" + "strings" + "strconv" + "time" + "net" + "log" + "fmt" + "os" + + "github.com/RiV-chain/RiV-mesh/src/admin" +) + +var riv_ctrl_path string + +func main() { + debug := true + w := webview.New(debug) + defer w.Destroy() + w.SetTitle("RiV-mesh") + w.SetSize(690, 920, webview.HintFixed) + /*1. Create ~/.riv-mesh folder if not existing + *2. Create ~/.riv-mesh/mesh.conf if not existing + *3. If the file exists read Peers. + *3.1 Invoke add peers for each record + */ + mesh_folder := ".riv-mesh" + mesh_conf := "mesh.conf" + user_home := get_user_home_path() + mesh_settings_folder := filepath.Join(user_home, mesh_folder) + err := os.MkdirAll(mesh_settings_folder, os.ModePerm) + if err != nil { + fmt.Printf("Unable to create folder: %v", err) + } + mesh_settings_path := filepath.Join(user_home, mesh_folder, mesh_conf) + riv_ctrl_path = get_ctl_path() + if _, err := os.Stat(mesh_settings_path); os.IsNotExist(err) { + err := ioutil.WriteFile(mesh_settings_path, []byte(""), 0750) + if err != nil { + fmt.Printf("Unable to write file: %v", err) + } + } else { + //read peers from mesh.conf + conf, _ := ioutil.ReadFile(mesh_settings_path) + var dat map[string]interface {} + if err := hjson.Unmarshal(conf, &dat); err != nil { + fmt.Printf("Unable to parse mesh.conf file: %v", err) + } else { + if dat["Peers"]!=nil { + peers := dat["Peers"].([]interface{}) + remove_peers() + for _, u := range peers { + log.Printf("Unmarshaled: %v", u.(string)) + add_peers(u.(string)) + } + } else { + fmt.Printf("Warning: Peers array not loaded from mesh.conf file") + } + } + } + var path string + + if len(os.Args)>1 { + path, err = filepath.Abs(filepath.Dir(os.Args[1])) + } else { + path, err = filepath.Abs(filepath.Dir(os.Args[0])) + } + if err != nil { + log.Fatal(err) + } + + log.Println(path) + w.Bind("onLoad", func() { + log.Println("page loaded") + go run(w) + }) + w.Bind("savePeers", func(peer_list string) { + //log.Println("peers saved ", peer_list) + var peers []string + _ = json.Unmarshal([]byte(peer_list), &peers) + log.Printf("Unmarshaled: %v", peers) + remove_peers() + for _, u := range peers { + log.Printf("Unmarshaled: %v", u) + add_peers(u) + } + //add peers to ~/mesh.conf + dat := make(map[string]interface{}) + dat["Peers"] = peers + bs, _ := hjson.Marshal(dat) + e := ioutil.WriteFile(mesh_settings_path, bs, 0750) + if e != nil { + fmt.Printf("Unable to write file: %v", e) + } + }) + w.Bind("ping", func(peer_list string) { + go ping(w, peer_list) + }) + dat, err := ioutil.ReadFile(path+"/index.html") + w.Navigate("data:text/html,"+url.QueryEscape(string(dat))) + //w.Navigate("data:text/html,"+""+path+"") + w.Run() +} + +func ping(w webview.WebView, peer_list string){ + var peers []string + _ = json.Unmarshal([]byte(peer_list), &peers) + log.Printf("Unmarshaled: %v", peers) + for _, u := range peers { + log.Printf("Unmarshaled: %v", u) + ping_time := check(u); + log.Printf("ping: %d", ping_time) + setPingValue(w, u, strconv.FormatInt(ping_time, 10)); + } +} + +func check(peer string) int64 { + u, e := url.Parse(peer) + if e!=nil { + return -1 + } + t := time.Now() + _, err := net.DialTimeout("tcp", u.Host, 5*time.Second) + if err!=nil { + return -1 + } + d := time.Since(t) + return d.Milliseconds() +} + +func get_user_home_path() string { + if runtime.GOOS == "windows" { + path, exists := os.LookupEnv("USERPROFILE") + if exists { + return path + } else { + return "" + } + } else { + path, exists := os.LookupEnv("HOME") + if exists { + return path + } else { + return "" + } + } +} + +func get_ctl_path() string{ + if runtime.GOOS == "windows" { + program_path := "programfiles" + path, exists := os.LookupEnv(program_path) + if exists { + fmt.Println("Program path: %s", path) + ctl_path := fmt.Sprintf("%s\\RiV-mesh\\meshctl.exe", path) + return ctl_path + } else { + fmt.Println("could not find Program Files path") + return "" + } + } else { + ctl_path := fmt.Sprintf("/usr/local/bin/meshctl") + return ctl_path + } +} + +func run(w webview.WebView){ + if len(riv_ctrl_path) > 0 { + get_self(w) + get_peers(w) + } + _ = time.AfterFunc(10*time.Second, func() { + run(w) + }) +} + +func run_command(command string) []byte{ + args := []string{"-json", command} + cmd := exec.Command(riv_ctrl_path, args...) + out, err := cmd.CombinedOutput() + if err != nil { + //log.Fatalf("cmd.Run() failed with %s\n", err) + return []byte(err.Error()) + } + return out +} + +func run_command_with_arg(command string, arg string) []byte{ + args := []string{"-json", command, arg} + cmd := exec.Command(riv_ctrl_path, args...) + out, err := cmd.CombinedOutput() + if err != nil { + //log.Fatalf("command failed: %s\n", riv_ctrl_path+" "+strings.Join(args, " ")) + return []byte(err.Error()) + } + return out +} + +func add_peers(uri string){ + run_command_with_arg("addpeers", "uri="+uri) +} + +func remove_peers(){ + run_command("removepeers") +} + +func get_self(w webview.WebView){ + + res := &admin.GetSelfResponse{} + out := run_command("getSelf") + if err := json.Unmarshal(out, &res); err != nil { + go setFieldValue(w, "ipv6", err.Error()) + return + } + //found ipv6 + fmt.Printf("IPv6: %s\n", res.IPAddress) + go setFieldValue(w, "ipv6", res.IPAddress) + //found subnet + fmt.Printf("Subnet: %s\n", res.Subnet) + go setFieldValue(w, "subnet", res.Subnet) + out = run_command("getPeers") + //go setFieldValue(w, "peers", string(out)) +} + +func get_peers(w webview.WebView){ + + res := &admin.GetPeersResponse{} + out := run_command("getPeers") + if err := json.Unmarshal(out, &res); err != nil { + go setFieldValue(w, "peers", err.Error()) + return + } + + var m []string + for _, s := range res.Peers { + m=append(m, s.Remote) + } + for k := range m { + // Loop + fmt.Println(k) + } + inner_html := strings.Join(m[:], "
") + strings.Join(m[:], "
") + go setFieldValue(w, "peers", inner_html) +} + +func setFieldValue(p webview.WebView, id string, value string) { + p.Dispatch(func() { + p.Eval("setFieldValue('"+id+"','"+value+"');") + }) +} + +func setPingValue(p webview.WebView, peer string, value string) { + p.Dispatch(func() { + p.Eval("setPingValue('"+peer+"','"+value+"');") + }) +} diff --git a/contrib/ui/nas-asustor/CONTROL/description.txt b/contrib/ui/nas-asustor/CONTROL/description.txt new file mode 100644 index 00000000..71124ae0 --- /dev/null +++ b/contrib/ui/nas-asustor/CONTROL/description.txt @@ -0,0 +1,4 @@ +RiV-mesh is an implementation of a fully end-to-end encrypted IPv6 network. +It is lightweight, self-arranging, supported on multiple platforms and +allows pretty much any IPv6-capable application to communicate securely with +other RiV-mesh nodes. diff --git a/contrib/ui/nas-asustor/CONTROL/start-stop.sh b/contrib/ui/nas-asustor/CONTROL/start-stop.sh new file mode 100644 index 00000000..4f883d27 --- /dev/null +++ b/contrib/ui/nas-asustor/CONTROL/start-stop.sh @@ -0,0 +1,87 @@ +#!/bin/sh + +BASE="/usr/local/AppCentral/mesh-nas-asustor" +CONFIG_DIR="/usr/local/etc" + +MESH_PACKAGE_LOG=/tmp/mesh.log +echo "start-stop called" >> "$MESH_PACKAGE_LOG" + +exec 2>>$MESH_PACKAGE_LOG +set -x + +whoami + +init () +{ + config_file=${CONFIG_DIR}/mesh.conf + if [ ! -f "$CONFIG_DIR" ]; then + mkdir -p ${CONFIG_DIR} + fi + + if [ -f $config_file ]; then + mkdir -p /var/backups + echo "Backing up configuration file to /var/backups/mesh.conf.`date +%Y%m%d`" + cp $config_file /var/backups/mesh.conf.`date +%Y%m%d` + echo "Normalising and updating /etc/mesh.conf" + ${BASE}/bin/mesh -useconf -normaliseconf < /var/backups/mesh.conf.`date +%Y%m%d` > $config_file + else + echo "Generating initial configuration file $config_file" + echo "Please familiarise yourself with this file before starting RiV-mesh" + sh -c "umask 0027 && ${BASE}/bin/mesh -genconf > '$config_file'" + fi + + #chown -R admin:administrators $config_file + #chmod -R 664 $config_file + #sudo insmod /lib/modules/5.4.x/tun.ko + # Create the necessary file structure for /dev/net/tun + if ( [ ! -c /dev/net/tun ] ); then + if ( [ ! -d /dev/net ] ); then + mkdir -m 755 /dev/net + fi + mknod /dev/net/tun c 10 200 + chmod 0755 /dev/net/tun + fi + + # Load the tun module if not already loaded + if ( !(lsmod | grep -q "^tun\s") ); then + insmod /lib/modules/5.4.x/tun.ko + fi +} + +start_service () +{ + init + + # Launch the mesh in the background. + ${BASE}/bin/mesh -useconffile "$config_file" \ + -httpaddress "http://0.0.0.0:19019" \ + -wwwroot "$BASE/www" \ + -logto "$BASE/var/log/mesh.log" & + return $? +} + +stop_service () +{ + pid=`pidof -s mesh` + if [ -z "$pid" ]; then + echo "mesh was not running" + exit 0 + fi + kill "$pid" +} + +case $1 in + start) + start_service + echo "Running RiV Mesh" + exit 0 + ;; + stop) + stop_service + echo "Stopped RiV Mesh" + exit 0 + ;; + *) + exit 1 + ;; +esac diff --git a/contrib/ui/nas-asustor/var/lib/mesh/deviceinfo b/contrib/ui/nas-asustor/var/lib/mesh/deviceinfo new file mode 100644 index 00000000..81f5b3cd --- /dev/null +++ b/contrib/ui/nas-asustor/var/lib/mesh/deviceinfo @@ -0,0 +1,6 @@ +#!/bin/sh +echo vendor=Asustor +echo vendorOperatingSystemName=ADM +eval $(cat /etc/default/nas.conf | grep '\\|\' | sed 's/ //g') +echo firmwareVersion="$Version" +echo model=$Model diff --git a/contrib/ui/nas-asustor/var/lib/mesh/webauth b/contrib/ui/nas-asustor/var/lib/mesh/webauth new file mode 100644 index 00000000..1d1bc249 --- /dev/null +++ b/contrib/ui/nas-asustor/var/lib/mesh/webauth @@ -0,0 +1,76 @@ +#!/bin/sh +#exit with zero status on auth success and 1 on error +# +#You can use our cgi to restrict access to RiV Mesh configuration page only for authenticated NAS OS users. +#========================== +#1. use login to verify authenticated NAS OS user +# +#for example: +# +#root@AS6208T-RD:/ # REMOTE_ADDR="127.0.0.1" QUERY_STRING="act=login&apptag=mesh&account=admin&password=admin888" /usr/webman/portal/apis/appCentral/applogin.cgi +#Content-type: text/plain; charset=utf-8 +# +#result: +#{ "success": true, "account": "admin", "sid": "yPgoWu95eXxCxZJr", "isAdminGroup": 1, "model": "AS6208T", "hostid": "20-16-01-21-14-01" } +# +#explanation: +#apptag: application name +#account&password: which you want to verify +# +#2. When you finish verifying authenticated NAS OS user, you must logout from NAS. +# +#for example: +# +#root@AS6208T-RD:/ # QUERY_STRING="act=logout&sid=yPgoWu95eXxCxZJr" /usr/webman/portal/apis/login.cgi +#Content-type: text/plain; charset=utf-8 +# +#result +#{ "success": true } +# +#explanation: +#sid: same as above (login result) + + + +CACHE="/usr/local/AppCentral/mesh/var/lib/mesh/access_key" +if [ -f $CACHE ] && [ "$(expr $(date +%s) - $(date -r $CACHE +%s))" -lt 3600 ]; then + exit 0 +fi + +#we want read e.g.: +#HTTP_COOKIE='access_key=sdu45KJFDHksadulf=' +IFS=';' +for x in $HTTP_COOKIE +do + eval $x +done +#we want get e.g.: +#access_key='user=admin;pwd=L4edNyoCC15.kDBLIN05480' +access_key=$(echo $access_key | base64 -d) + +CACHE="/usr/local/AppCentral/mesh/var/lib/mesh/$access_key" +if [ -f $CACHE ] && [ "$(expr $(date +%s) - $(date -r $CACHE +%s))" -lt 3600 ]; then + exit 0 +fi + +IFS=';' +for x in $access_key +do + eval $x +done + +if [ -z "${user}" ] || [ -z "${pwd}" ]; then + exit 1 +fi + +export REMOTE_ADDR="127.0.0.1" +export QUERY_STRING="act=login&apptag=mesh&account=${user}&password=${pwd}" +S=$(/usr/webman/portal/apis/appCentral/applogin.cgi | sed '/"sid"/!d; s/\s\+//g; s/.*"sid":"\([^"]*\)".*/\1/') +if [ -z $S ]; then + exit 1 +else + export QUERY_STRING="act=logout&sid=$S" + /usr/webman/portal/apis/login.cgi >/dev/null + touch $CACHE + exit 0 +fi diff --git a/contrib/ui/nas-asustor/www/assets/partner-logo.png b/contrib/ui/nas-asustor/www/assets/partner-logo.png new file mode 100644 index 00000000..eb478e11 Binary files /dev/null and b/contrib/ui/nas-asustor/www/assets/partner-logo.png differ diff --git a/contrib/ui/nas-asustor/www/assets/properties.js b/contrib/ui/nas-asustor/www/assets/properties.js new file mode 100644 index 00000000..d7af016c --- /dev/null +++ b/contrib/ui/nas-asustor/www/assets/properties.js @@ -0,0 +1,35 @@ +var ed = { + partnerId: 1422, + applicationName: 'RiV Mesh Asustor ADM App', + nasOSName: 'Asustor ADM', + useAuthNASRichScreen: true, + nasVisitEDWebsiteLogin: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteSignup: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteLoggedin: "https://github.com/RiV-chain/RiV-mesh", + getNasAuthUrl: function () { + return "/"; + }, + nasLoginCall: function (nasLoginSuccess, nasLoginFailure) { + var d = new Date(); + d.setTime(d.getTime() + (10 * 60 * 1000)); + document.cookie = "access_key=" + btoa( "user=" + encodeURIComponent($('#nasInputUser').val()) + ";pwd=" + encodeURIComponent($('#nasInputPassword').val()))+ "; expires=" + d.toUTCString() + "; path=/"; + $.ajax({url: "api/getself"}).done(function () { + window.location.reload(); + }).fail(function () { + ed.nasLogoutCall(); + nasLoginFailure(); + }); + }, + nasLogoutCall: function () { + document.cookie = "access_key=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/"; + }, + getNasUser: function() { + function getCookie(name) { + var matches = document.cookie.match(new RegExp( + "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)" + )); + return matches ? decodeURIComponent(matches[1]) : undefined; + } + return decodeURIComponent(atob(getCookie('access_key')).split(';')[0].split('=')[1]); + } +}; diff --git a/contrib/ui/nas-drobo/Content/install.sh b/contrib/ui/nas-drobo/Content/install.sh new file mode 100644 index 00000000..31c1fde3 --- /dev/null +++ b/contrib/ui/nas-drobo/Content/install.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +. $(dirname $0)/scripts.sh +_install diff --git a/contrib/ui/nas-drobo/Content/mesh.conf b/contrib/ui/nas-drobo/Content/mesh.conf new file mode 100644 index 00000000..8b2ed5c6 --- /dev/null +++ b/contrib/ui/nas-drobo/Content/mesh.conf @@ -0,0 +1,20 @@ +RedirectMatch 301 ^/mesh$ /mesh/ +LoadModule proxy_http_module modules/mod_proxy_http.so + + + + AuthType Form + AuthName "DroboApps" + AuthUserFile /tmp/DroboApps/apache/htpasswd + AuthFormProvider file + ErrorDocument 401 /login/index.html + Session On + SessionCookieName session path=/ + SessionCryptoPassphrase "exec:/mnt/DroboFS/Shares/DroboApps/apache/libexec/cookie.sh" + Require valid-user + + ProxyPreserveHost On + + ProxyPass "http://127.0.0.1:19019/" + ProxyPassReverse "http://127.0.0.1:19019/" + diff --git a/contrib/ui/nas-drobo/Content/scripts.sh b/contrib/ui/nas-drobo/Content/scripts.sh new file mode 100644 index 00000000..6ae96e13 --- /dev/null +++ b/contrib/ui/nas-drobo/Content/scripts.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env sh + +tmpdir="/tmp/DroboApps/mesh" +errorfile="${tmpdir}/error.txt" +base_dir="/mnt/DroboFS/Shares/DroboApps/mesh" +config_dir="$base_dir/config" +config_file="$config_dir/mesh.conf" +prog_dir="$(dirname "$(realpath "${0}")")" + + +_install() { + if [ ! -f "${errorfile}" ] + then + mkdir -p "${tmpdir}" + if [ ! -f "$config_file" ]; then + echo 3 > "${errorfile}" + fi + fi + ln -s $base_dir/var/log/mesh.log $base_dir/www/log + # install apache 2.x + /usr/bin/DroboApps.sh install_version apache 2 +} + +_uninstall() { + pid=`pidof -s mesh` + if [ -z "$pid" ]; then + echo "mesh was not running" + else + kill "$pid" + fi +} + +_update() { + /bin/sh "${prog_dir}/service.sh" stop + + cd "$base_dir" + rm -rf $(ls | grep -v 'host_uid.txt\|var\|config') + + echo 'update successful' > "${prog_dir}/update.log" +} diff --git a/contrib/ui/nas-drobo/Content/service.sh b/contrib/ui/nas-drobo/Content/service.sh new file mode 100644 index 00000000..74b90520 --- /dev/null +++ b/contrib/ui/nas-drobo/Content/service.sh @@ -0,0 +1,121 @@ +#!/bin/sh +# + +##!! +. /etc/service.subr + +prog_dir=`dirname \`realpath $0\`` +base_dir=/mnt/DroboFS/Shares/DroboApps/mesh +config_dir="$base_dir/config" +config_file="$config_dir/mesh.conf" + +name="mesh" +framework_version="2.1" +description="RiV-mesh is an implementation of a fully end-to-end encrypted IPv6 network" +depends="" +webui="WebUI" + +errorfile=/tmp/DroboApps/mesh/error.txt +pidfile=/tmp/DroboApps/mesh/pid.txt +statusfile=/tmp/DroboApps/mesh/status.txt +edstatusfile=$base_dir/var/lib/mesh/status + +start() +{ + mkdir -p /tmp/DroboApps/mesh + # delete edstatufile before starting daemon to delete previous status + rm -f $edstatusfile + rm -f $errorfile + + if [ -f $config_file ]; then + mkdir -p /var/backups + echo "Backing up configuration file to /var/backups/mesh.conf.`date +%Y%m%d`" + cp $config_file /var/backups/mesh.conf.`date +%Y%m%d` + echo "Normalising and updating /etc/mesh.conf" + $base_dir/bin/mesh -useconf -normaliseconf < /var/backups/mesh.conf.`date +%Y%m%d` > $config_file + else + mkdir -p $config_dir + echo "Generating initial configuration file $config_file" + echo "Please familiarise yourself with this file before starting RiV-mesh" + sh -c "umask 0027 && $base_dir/bin/mesh -genconf > '$config_file'" + fi + + # Create the necessary file structure for /dev/net/tun + if ( [ ! -c /dev/net/tun ] ); then + if ( [ ! -d /dev/net ] ); then + mkdir -m 755 /dev/net + fi + mknod /dev/net/tun c 10 200 + chmod 0755 /dev/net/tun + fi + + # Load the tun module if not already loaded + if ( !(lsmod | grep -q "^tun\s") ); then + KERNEL_VERSION=$(/bin/uname -r) + insmod $base_dir/lib/modules/$KERNEL_VERSION/tun.ko + fi + + # Launch the mesh in the background. + ${base_dir}/bin/mesh -useconffile "$config_file" \ + -httpaddress "http://localhost:19019" \ + -wwwroot "$base_dir/www" \ + -logto "$base_dir/var/log/mesh.log" & + + sleep 1 + update_status +} + +update_status() +{ + + # wait until file appears + i=30 + + while [ -z $(pidof -s mesh) ] + do + sleep 1 + i=$((i-1)) + + if [ $i -eq 0 ] + then + break + fi + done + + # if we don't have file here. throw error into status and return + if [ -z $(pidof -s mesh) ] + then + echo "" > "$pidfile" + echo 1 > "${errorfile}" + echo "Configuration required" > $statusfile + else + echo $(pidof -s mesh) > "$pidfile" + echo 0 > "${errorfile}" + echo "Application is running" > $statusfile + fi + +} + +stop() +{ + pid=`pidof -s mesh` + if [ -z "$pid" ]; then + echo 1 > "${errorfile}" + echo "mesh was not running" > $statusfile + else + kill "$pid" + echo 0 > "${errorfile}" + echo "Application is stopped" > $statusfile + fi + echo "" > "$pidfile" + +} + +case "$1" in + update_status) + update_status + exit $? + ;; +esac + +main "$@" diff --git a/contrib/ui/nas-drobo/Content/uninstall.sh b/contrib/ui/nas-drobo/Content/uninstall.sh new file mode 100644 index 00000000..a0889ee2 --- /dev/null +++ b/contrib/ui/nas-drobo/Content/uninstall.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +# +. $(dirname $0)/scripts.sh +_uninstall diff --git a/contrib/ui/nas-drobo/Content/update.sh b/contrib/ui/nas-drobo/Content/update.sh new file mode 100644 index 00000000..5b4a3d43 --- /dev/null +++ b/contrib/ui/nas-drobo/Content/update.sh @@ -0,0 +1,3 @@ +#!/bin/sh +. $(dirname $0)/scripts.sh +_update diff --git a/contrib/ui/nas-drobo/Content/var/lib/mesh/hooks/deviceinfo b/contrib/ui/nas-drobo/Content/var/lib/mesh/hooks/deviceinfo new file mode 100644 index 00000000..bf591753 --- /dev/null +++ b/contrib/ui/nas-drobo/Content/var/lib/mesh/hooks/deviceinfo @@ -0,0 +1,16 @@ +#!/bin/sh +echo vendor=Drobo + +# get device model, e.g., "5N" +_get_device_model() { + for f in /sys/devices/dri_dnas_primary/dnas_adp_1/host*/target*/*:*:*:*/model; do + if [ -e "$f" ]; then + cat "$f" + return + fi + done +} + +echo serial=$(cat /mnt/DroboFS/Shares/DroboApps/mesh/host_uid.txt) +echo firmwareVersion=$(/usr/bin/esa vxver) +echo model=$(_get_device_model) diff --git a/contrib/ui/nas-drobo/Content/www/assets/partner-logo.png b/contrib/ui/nas-drobo/Content/www/assets/partner-logo.png new file mode 100644 index 00000000..4df18638 Binary files /dev/null and b/contrib/ui/nas-drobo/Content/www/assets/partner-logo.png differ diff --git a/contrib/ui/nas-drobo/Content/www/assets/partner.css b/contrib/ui/nas-drobo/Content/www/assets/partner.css new file mode 100644 index 00000000..a58b72b5 --- /dev/null +++ b/contrib/ui/nas-drobo/Content/www/assets/partner.css @@ -0,0 +1,25 @@ +body { + color: gray; + background: black; +} +.nas-apps-config-form-message.nas-apps-config-form-message-error { + color: white; +} + +/* bottom */ +.nas-apps-config-form-partner-logo { + width: 200px; + height: 82px; + margin: 0 auto; +} +.nas-apps-config-form-auth-id, +.nas-apps-config-form-location-name { + color: white; +} +.nas-apps-config-form-field input, +.nas-apps-config-form-field select { + color: white; +} +.nas-apps-config-form-field select:focus option { + background: white; +} diff --git a/contrib/ui/nas-drobo/Content/www/assets/properties.js b/contrib/ui/nas-drobo/Content/www/assets/properties.js new file mode 100644 index 00000000..6bb89526 --- /dev/null +++ b/contrib/ui/nas-drobo/Content/www/assets/properties.js @@ -0,0 +1,9 @@ +var ed = { + partnerId: 1046, + brand: 'RiV Mesh', + applicationName: "RiV Mesh Drobo App", + nasOSName: "Drobo", + nasVisitEDWebsiteLogin: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteSignup: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteLoggedin: "https://github.com/RiV-chain/RiV-mesh" +}; diff --git a/contrib/ui/nas-netgear-os6/package/DEBIAN/control b/contrib/ui/nas-netgear-os6/package/DEBIAN/control new file mode 100644 index 00000000..d5065ae4 --- /dev/null +++ b/contrib/ui/nas-netgear-os6/package/DEBIAN/control @@ -0,0 +1,11 @@ +Source: mesh +Package: mesh +Version: 0.4.4 +Architecture: amd64 +Maintainer: Vadym Vikulin +Depends: lsb-base, debconf, readynasos (>= 6.0.5~T1271) +Section: net +Priority: optional +Installed-Size: 20000 +Homepage: https://github.com/RiV-chain/RiV-mesh +Description: RiV-mesh is an implementation of a fully end-to-end encrypted IPv6 network diff --git a/contrib/ui/nas-netgear-os6/package/DEBIAN/postinst b/contrib/ui/nas-netgear-os6/package/DEBIAN/postinst new file mode 100755 index 00000000..37ab51c3 --- /dev/null +++ b/contrib/ui/nas-netgear-os6/package/DEBIAN/postinst @@ -0,0 +1,61 @@ +#!/bin/sh +# postinst script for mesh +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see http://www.debian.org/doc/debian-policy/ or +# the debian-policy package + + +case "$1" in + configure) + chown -R admin:admin /apps/mesh + if systemctl restart apache2.service; then + # success + event_push app meshd '' 0 0 + else + # error + event_push app meshd '' 0 0 + fi + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +if [ -f /etc/mesh.conf ]; then + mkdir -p /var/backups + echo "Backing up configuration file to /var/backups/mesh.conf.`date +%Y%m%d`" + cp /etc/mesh.conf /var/backups/mesh.conf.`date +%Y%m%d` + echo "Normalising and updating /etc/mesh.conf" + /apps/mesh/bin/mesh -useconf -normaliseconf < /var/backups/mesh.conf.`date +%Y%m%d` > /etc/mesh.conf +else + echo "Generating initial configuration file /etc/mesh.conf" + echo "Please familiarise yourself with this file before starting RiV-mesh" + sh -c 'umask 0027 && /apps/mesh/bin/mesh -genconf > /etc/mesh.conf' +fi + +chmod 755 /etc/mesh.conf +if command -v systemctl >/dev/null; then + systemctl daemon-reload || echo -n "daemon not reloaded!" + systemctl enable mesh || echo -n "systemctl enable failed!" + systemctl restart mesh || echo -n "systemctl restart failed!" +fi + +exit 0 diff --git a/contrib/ui/nas-netgear-os6/package/DEBIAN/postrm b/contrib/ui/nas-netgear-os6/package/DEBIAN/postrm new file mode 100755 index 00000000..44d7e218 --- /dev/null +++ b/contrib/ui/nas-netgear-os6/package/DEBIAN/postrm @@ -0,0 +1,32 @@ +#! /bin/sh +# postrm script for mesh +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `purge' +# * `upgrade' +# * `failed-upgrade' +# * `abort-install' +# * `abort-install' +# * `abort-upgrade' +# * `disappear' overwrit>r> +# for details, see /usr/share/doc/packaging-manual/ + +case "$1" in + purge) + rm -rf /apps/mesh + systemctl restart apache2.service + event_push app meshd '' 0 0 + ;; + remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) + ;; + + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 0 + +esac diff --git a/contrib/ui/nas-netgear-os6/package/DEBIAN/prerm b/contrib/ui/nas-netgear-os6/package/DEBIAN/prerm new file mode 100755 index 00000000..64acb80d --- /dev/null +++ b/contrib/ui/nas-netgear-os6/package/DEBIAN/prerm @@ -0,0 +1,32 @@ +#! /bin/sh +# prerm script for mesh +# +# see: dh_installdeb(1) + +set -e + +# summary of how this script can be called: +# * `remove' +# * `upgrade' +# * `failed-upgrade' +# * `remove' `in-favour' +# * `deconfigure' `in-favour' +# `removing' +# +# for details, see /usr/share/doc/packaging-manual/ + +case "$1" in + remove|upgrade|deconfigure) + systemctl stop fvapp-mesh.service || true + systemctl disable fvapp-mesh.service || true + ;; + failed-upgrade) + ;; + *) + echo "prerm called with unknown argument \`$1'" >&2 + exit 0 + ;; +esac + + +exit 0 diff --git a/contrib/ui/nas-netgear-os6/package/apps/mesh/fvapp-mesh.service b/contrib/ui/nas-netgear-os6/package/apps/mesh/fvapp-mesh.service new file mode 100755 index 00000000..08c3bb9b --- /dev/null +++ b/contrib/ui/nas-netgear-os6/package/apps/mesh/fvapp-mesh.service @@ -0,0 +1,19 @@ +[Unit] +Description=RiV-mesh is an early-stage implementation of a fully end-to-end encrypted IPv6 network + +[Service] +Type=idle +Group=admin +ProtectHome=true +ProtectSystem=true +SyslogIdentifier=mesh +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE +ExecStartPre=+-/sbin/modprobe tun +ExecStart=/apps/mesh/bin/mesh -useconffile /etc/mesh.conf -httpaddress http://localhost:19019 -wwwroot /apps/mesh/www -logto /apps/mesh/var/log/mesh.log +ExecStop=/bin/kill -HUP $MAINPID +KillMode=process +Restart=on-failure +TimeoutStopSec=10 + +[Install] +WantedBy=multi-user.target diff --git a/contrib/ui/nas-netgear-os6/package/apps/mesh/https.conf b/contrib/ui/nas-netgear-os6/package/apps/mesh/https.conf new file mode 100755 index 00000000..5538c07e --- /dev/null +++ b/contrib/ui/nas-netgear-os6/package/apps/mesh/https.conf @@ -0,0 +1,12 @@ +RewriteEngine on +RewriteRule "(.*)/apps/mesh$" "$1/apps/mesh/" [R=301,L] +LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so +LoadModule proxy_http_module /usr/lib/apache2/modules/mod_proxy_http.so + + + Include "/etc/frontview/apache/Admin_Auth.conf" + SSLRequireSSL + ProxyPreserveHost On + ProxyPass "http://127.0.0.1:19019/" + ProxyPassReverse "http://127.0.0.1:19019/" + diff --git a/contrib/ui/nas-netgear-os6/package/apps/mesh/var/lib/mesh/hooks/deviceinfo b/contrib/ui/nas-netgear-os6/package/apps/mesh/var/lib/mesh/hooks/deviceinfo new file mode 100755 index 00000000..93d2bcac --- /dev/null +++ b/contrib/ui/nas-netgear-os6/package/apps/mesh/var/lib/mesh/hooks/deviceinfo @@ -0,0 +1,10 @@ +#!/bin/sh + +get_info() { + rn_nml -g systeminfo | tr '\n' ' ' | sed -r "s|.*<$1>(.*).*|\1|" +} +echo vendor=Netgear +echo vendorOperatingSystemName="$(get_info Firmware_Name)" +echo firmwareVersion="$(get_info Firmware_Version)" +echo model="$(get_info Model)" +echo serial="$(get_info Serial)" diff --git a/contrib/ui/nas-netgear-os6/package/apps/mesh/www/assets/partner-logo.png b/contrib/ui/nas-netgear-os6/package/apps/mesh/www/assets/partner-logo.png new file mode 100755 index 00000000..e64c371b Binary files /dev/null and b/contrib/ui/nas-netgear-os6/package/apps/mesh/www/assets/partner-logo.png differ diff --git a/contrib/ui/nas-netgear-os6/package/apps/mesh/www/assets/partner.css b/contrib/ui/nas-netgear-os6/package/apps/mesh/www/assets/partner.css new file mode 100755 index 00000000..9166c5e0 --- /dev/null +++ b/contrib/ui/nas-netgear-os6/package/apps/mesh/www/assets/partner.css @@ -0,0 +1,9 @@ +.nas-apps-config-form-app-logo { + height: 110px; + background: url(logo.png) left center no-repeat; + background-size: 262px 56px; +} + +a { + color: #692782!important; +} diff --git a/contrib/ui/nas-netgear-os6/package/apps/mesh/www/assets/properties.js b/contrib/ui/nas-netgear-os6/package/apps/mesh/www/assets/properties.js new file mode 100755 index 00000000..ee7a884b --- /dev/null +++ b/contrib/ui/nas-netgear-os6/package/apps/mesh/www/assets/properties.js @@ -0,0 +1,10 @@ +var ed = { + partnerId: 252, + applicationName: 'RiV-Mesh App', + nasOSName: 'Ready NAS OS 6', + vaultUrl: "https://github.com/RiV-chain/RiV-mesh", + basicEDWebsite: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteLogin: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteSignup: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteLoggedin: "https://github.com/RiV-chain/RiV-mesh" +}; diff --git a/contrib/ui/nas-qnap/au/apache-mesh.conf b/contrib/ui/nas-qnap/au/apache-mesh.conf new file mode 100644 index 00000000..c7a70f39 --- /dev/null +++ b/contrib/ui/nas-qnap/au/apache-mesh.conf @@ -0,0 +1,8 @@ +RedirectMatch 301 ^/mesh$ /mesh/ +LoadModule proxy_http_module modules/mod_proxy_http.so + + + ProxyPreserveHost On + ProxyPass "http://127.0.0.1:19019/" + ProxyPassReverse "http://127.0.0.1:19019/" + diff --git a/contrib/ui/nas-qnap/au/var/lib/mesh/hooks/deviceinfo b/contrib/ui/nas-qnap/au/var/lib/mesh/hooks/deviceinfo new file mode 100644 index 00000000..b4e73251 --- /dev/null +++ b/contrib/ui/nas-qnap/au/var/lib/mesh/hooks/deviceinfo @@ -0,0 +1,7 @@ +#!/bin/sh +CFG=/etc/default_config/uLinux.conf +echo vendor=QNAP +echo vendorOperatingSystemName=$(/sbin/getcfg System OS -f $CFG) +echo firmwareVersion=$(/sbin/getcfg System Version -f $CFG) +echo model=$(/sbin/getcfg System Model -f $CFG) +echo serial=$(/sbin/get_hwsn) diff --git a/contrib/ui/nas-qnap/au/var/lib/mesh/hooks/webauth b/contrib/ui/nas-qnap/au/var/lib/mesh/hooks/webauth new file mode 100644 index 00000000..8948b523 --- /dev/null +++ b/contrib/ui/nas-qnap/au/var/lib/mesh/hooks/webauth @@ -0,0 +1,31 @@ +#!/bin/sh +#we want read: +#export HTTP_COOKIE='qnapuser=admin; qnappwd=L4edNyoCC15.kDBLIN05480' + +IFS=';' +for x in $HTTP_COOKIE +do + eval $x +done +[ -z ${qnapuser} ] && exit 1 +[ -z ${qnappwd} ] && exit 1 + +#exit with zero status on auth success +#2-pass auth +S=$(curl -s -k -L "http://127.0.0.1:58080/cgi-bin/authLogin.cgi?user=${qnapuser}&pwd=${qnappwd}" | tr -d '\040\011\012\015' | grep -F '') +[ ! 0 -eq ${#S} ] && exit 0 + +AUTH_PORT=$(cat /etc/apache-sys-proxy.conf | grep Listen | awk '{print $2}') +if [ -n $AUTH_PORT ] ; then + AUTH_PORT=8080 +fi + +S=$(curl -s -k -L "http://127.0.0.1:${AUTH_PORT}/cgi-bin/authLogin.cgi?user=${qnapuser}&pwd=${qnappwd}" | tr -d '\040\011\012\015' | grep -F '') +[ ! 0 -eq ${#S} ] && exit 0 + +#fallback to plain password auth +#decode password first +plain_pwd=$(openssl enc -base64 -d <<< ${qnappwd}) +[ -z $plain_pwd ] && exit 1 +S=$(curl -s -k -L "http://127.0.0.1:${AUTH_PORT}/cgi-bin/authLogin.cgi?user=${qnapuser}&plain_pwd=${plain_pwd}" | tr -d '\040\011\012\015' | grep -F '') +[ 0 -eq ${#S} ] && exit 1 || exit 0 diff --git a/contrib/ui/nas-qnap/au/var/lib/mesh/pre_upgrade.sh b/contrib/ui/nas-qnap/au/var/lib/mesh/pre_upgrade.sh new file mode 100644 index 00000000..3d71230e --- /dev/null +++ b/contrib/ui/nas-qnap/au/var/lib/mesh/pre_upgrade.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +log_exit(){ + echo $2 + exit $1 +} + +[ -z "$MESH_USER_NAME" ] && log_exit 1 "Credentials are not set. Remove aborted" + +rm "$MESH_APP_ROOT/bin/mesh +rm -rf "$MESH_APP_ROOT/www" + diff --git a/contrib/ui/nas-qnap/au/www/assets/get_sid.js b/contrib/ui/nas-qnap/au/www/assets/get_sid.js new file mode 100644 index 00000000..d57b0dc7 --- /dev/null +++ b/contrib/ui/nas-qnap/au/www/assets/get_sid.js @@ -0,0 +1,102 @@ +/* encode function start */ +var ezEncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +var ezDecodeChars = new Array( + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1); + +function utf16to8(str) +{ + var out, i, len, c; + out = ""; + len = str.length; + for (i=0; i= 0x0001) && (c <= 0x007F)) { + out += str.charAt(i); + } + else if (c > 0x07FF) { + out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F)); + out += String.fromCharCode(0x80 | ((c >>6) & 0x3F)); + out += String.fromCharCode(0x80 | ((c >>0) & 0x3F)); + + } + else { + out += String.fromCharCode(0xC0 | ((c >>6) & 0x1F)); + out += String.fromCharCode(0x80 | ((c >>0) & 0x3F)); + } + } + return out; +} +function utf8to16(str) { + var out, i, len, c; + var char2, char3; + + out = ""; + len = str.length; + i = 0; + while(i < len) { + c = str.charCodeAt(i++); + switch(c >> 4) + { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + // 0xxxxxxx + out += str.charAt(i-1); + break; + case 12: case 13: + // 110x xxxx 10xx xxxx + char2 = str.charCodeAt(i++); + out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); + break; + case 14: + // 1110 xxxx10xx xxxx10xx xxxx + char2 = str.charCodeAt(i++); + char3 = str.charCodeAt(i++); + out += String.fromCharCode(((c & 0x0F) << 12) | + ((char2 & 0x3F) << 6) | + ((char3 & 0x3F) << 0)); + } + } + return out; +} + +function ezEncode(str) +{ + var out, i, len; + var c1, c2, c3; + + len = str.length; + i = 0; + out = ""; + while(i < len) + { + c1 = str.charCodeAt(i++) & 0xff; + if(i == len) + { + out += ezEncodeChars.charAt(c1 >> 2); + out += ezEncodeChars.charAt((c1 & 0x3) << 4); + out += "=="; + break; + } + c2 = str.charCodeAt(i++); + if(i == len) + { + out += ezEncodeChars.charAt(c1 >> 2); + out += ezEncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)); + out += ezEncodeChars.charAt((c2 & 0xF) << 2); + out += "="; + break; + } + c3 = str.charCodeAt(i++); + out += ezEncodeChars.charAt(c1 >> 2); + out += ezEncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)); + out += ezEncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); + out += ezEncodeChars.charAt(c3 & 0x3F); + } + return out; +} diff --git a/contrib/ui/nas-qnap/au/www/assets/partner-logo.png b/contrib/ui/nas-qnap/au/www/assets/partner-logo.png new file mode 100644 index 00000000..0d89fb31 Binary files /dev/null and b/contrib/ui/nas-qnap/au/www/assets/partner-logo.png differ diff --git a/contrib/ui/nas-qnap/au/www/assets/properties.js b/contrib/ui/nas-qnap/au/www/assets/properties.js new file mode 100644 index 00000000..f68f4596 --- /dev/null +++ b/contrib/ui/nas-qnap/au/www/assets/properties.js @@ -0,0 +1,104 @@ +var ed = { + partnerId: 1222, + brand: 'RiV Mesh', + applicationName: "RiV Mesh QNAP NAS OS App", + nasOSName: "QNAP NAS device", + useAuthNASRichScreen: true, + nasVisitEDWebsiteLogin: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteSignup: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteLoggedin: "https://github.com/RiV-chain/RiV-mesh", + getNasAuthUrl: function () { + return "/"; + } +}; + +$(function () { + + ed.nasLoginCall = function (nasLoginSuccess, nasLoginFailure) { + /* encode function start */ + var ezEncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + function utf16to8(str) + { + var out, i, len, c; + out = ""; + len = str.length; + for (i = 0; i < len; i++) { + c = str.charCodeAt(i); + if ((c >= 0x0001) && (c <= 0x007F)) { + out += str.charAt(i); + } else if (c > 0x07FF) { + out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F)); + out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F)); + out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F)); + + } else { + out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F)); + out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F)); + } + } + return out; + } + + function ezEncode(str) + { + var out, i, len; + var c1, c2, c3; + + len = str.length; + i = 0; + out = ""; + while (i < len) + { + c1 = str.charCodeAt(i++) & 0xff; + if (i == len) + { + out += ezEncodeChars.charAt(c1 >> 2); + out += ezEncodeChars.charAt((c1 & 0x3) << 4); + out += "=="; + break; + } + c2 = str.charCodeAt(i++); + if (i == len) + { + out += ezEncodeChars.charAt(c1 >> 2); + out += ezEncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); + out += ezEncodeChars.charAt((c2 & 0xF) << 2); + out += "="; + break; + } + c3 = str.charCodeAt(i++); + out += ezEncodeChars.charAt(c1 >> 2); + out += ezEncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); + out += ezEncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); + out += ezEncodeChars.charAt(c3 & 0x3F); + } + return out; + } + + var d = new Date(); + d.setTime(d.getTime() + (30 * 60 * 1000)); + document.cookie = "qnapuser=" + encodeURIComponent($('#nasInputUser').val()) + "; expires=" + d.toUTCString() + "; path=/"; + document.cookie = "qnappwd=" + encodeURIComponent(ezEncode(utf16to8($('#nasInputPassword').val()))) + "; expires=" + d.toUTCString() + "; path=/"; + $.ajax({url: "rest/info"}).done(function (response) { + window.location.reload(); + checkError(response); + }).fail(function () { + ed.nasLogoutCall(); + nasLoginFailure(); + }); + }; + ed.nasLogoutCall = function() { + document.cookie = "qnapuser=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/"; + document.cookie = "qnappwd=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/"; + }; + function getCookie(name) { + var matches = document.cookie.match(new RegExp( + "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)" + )); + return matches ? decodeURIComponent(matches[1]) : undefined; + } + ed.getNasUser = function() { + return getCookie('qnapuser'); + }; +}); diff --git a/contrib/ui/nas-qnap/package/package_routines b/contrib/ui/nas-qnap/package/package_routines new file mode 100644 index 00000000..6d767310 --- /dev/null +++ b/contrib/ui/nas-qnap/package/package_routines @@ -0,0 +1,42 @@ +#script called from qinstall.sh + +#SYS_QPKG_SERVICE_ENABLED="FALSE" +QPKG_NAME="mesh" +CONF=/etc/config/mesh.conf + +PKG_PRE_REMOVE="{ + killall -q mesh + rm -rf /share/Web/$QPKG_NAME + [ -L /var/log/mesh.log ] && rm -f /var/log/mesh.log +}" + +PKG_MAIN_REMOVE="{ + $CMD_RM -f $CONF +}" + +pkg_pre_install(){ + killall -q mesh + rm -rf /share/Web/$QPKG_NAME +} + +pkg_install(){ + exec 2>/tmp/mesh.log + set -x + if [ -f $CONF ]; then + mkdir -p /var/backups + echo "Backing up configuration file to /var/backups/mesh.conf.`date +%Y%m%d`" + cp $CONF /var/backups/mesh.conf.`date +%Y%m%d` + echo "Normalising and updating $CONF" + ${SYS_QPKG_DIR}/bin/mesh -useconf -normaliseconf < /var/backups/mesh.conf.`date +%Y%m%d` > $CONF + else + echo "Generating initial configuration file $config_file" + echo "Please familiarise yourself with this file before starting RiV-mesh" + sh -c "umask 0027 && ${SYS_QPKG_DIR}/bin/mesh -genconf > '$CONF'" + fi + chmod a+w $CONF + +} + +pkg_post_install(){ + ln -sf $SYS_QPKG_DIR/var/log/mesh.log $SYS_QPKG_DIR/www/log +} diff --git a/contrib/ui/nas-qnap/package/shared/mesh.sh b/contrib/ui/nas-qnap/package/shared/mesh.sh new file mode 100644 index 00000000..92c8c788 --- /dev/null +++ b/contrib/ui/nas-qnap/package/shared/mesh.sh @@ -0,0 +1,120 @@ +#!/bin/sh +QPKG_CONF="/etc/config/qpkg.conf" +CONF="/etc/config/mesh.conf" +QPKG_NAME="mesh" +QPKG_DIR=$(/sbin/getcfg $QPKG_NAME Install_Path -f $QPKG_CONF) +KERNEL_MODULES+=" tun" + +load_kernel_modules(){ + local KERNEL_VERSION=$(/bin/uname -r) + local KERNEL_MODULES_PATH="/lib/modules" + for M in ${KERNEL_MODULES}; do + if [ -f ${KERNEL_MODULES_PATH}/vpn/${M}.ko ]; then + /sbin/insmod ${KERNEL_MODULES_PATH}/vpn/${M}.ko + continue + fi + if [ -f ${KERNEL_MODULES_PATH}/qvpn/${M}.ko ]; then + /sbin/insmod ${KERNEL_MODULES_PATH}/qvpn/${M}.ko + continue + fi + if [ -f ${KERNEL_MODULES_PATH}/misc/${M}.ko ]; then + /sbin/insmod ${KERNEL_MODULES_PATH}/misc/${M}.ko + continue + fi + if [ -f ${KERNEL_MODULES_PATH}/others/${M}.ko ]; then + /sbin/insmod ${KERNEL_MODULES_PATH}/others/${M}.ko + continue + fi + if [ -f ${KERNEL_MODULES_PATH}/${KERNEL_VERSION}/${M}.ko ]; then + /sbin/insmod ${KERNEL_MODULES_PATH}/${KERNEL_VERSION}/${M}.ko + continue + fi + done +} + +create_tun(){ + if ( [ ! -c /dev/net/tun ] ); then + if ( [ ! -d /dev/net ] ); then + mkdir -m 755 /dev/net + fi + mknod /dev/net/tun c 10 200 + chmod 0755 /dev/net/tun + fi + + # Load the tun module if not already loaded + if ( !(lsmod | grep -q "^tun\s") ); then + insmod /lib/modules/tun.ko + fi +} + +start_service () +{ + exec 2>>/tmp/mesh.log + set -x + + #enable ipv6 + sysctl -w net.ipv6.conf.all.disable_ipv6=0 + sysctl -w net.ipv6.conf.default.disable_ipv6=0 + + # Create the necessary file structure for /dev/net/tun + create_tun + load_kernel_modules + + #. /etc/init.d/vpn_common.sh && load_kernel_modules + + if [ ! -f '/etc/config/apache/extra/apache-mesh.conf' ] ; then + ln -sf $QPKG_DIR/apache-mesh.conf /etc/config/apache/extra/ + apache_reload=1 + fi + + if ! grep '/etc/config/apache/extra/apache-mesh.conf' /etc/config/apache/apache.conf ; then + echo 'Include /etc/config/apache/extra/apache-mesh.conf' >> /etc/config/apache/apache.conf + apache_reload=1 + fi + + if [ -n "$apache_reload" ] ; then + /usr/local/apache/bin/apachectl -k graceful + fi + + # Launch the mesh in the background. + ${QPKG_DIR}/bin/mesh -useconffile "$CONF" \ + -httpaddress "http://127.0.0.1:19019" \ + -wwwroot "$QPKG_DIR/www" \ + -logto "$QPKG_DIR/var/log/mesh.log" & + if [ $? -ne 0 ]; then + echo "Starting $QPKG_NAME failed" + exit 1 + fi +} + +stop_service () +{ + # Kill mesh + pid=`pidof -s mesh` + if [ -z "$pid" ]; then + echo "mesh was not running" + exit 0 + fi + kill "$pid" +} + +case "$1" in + start) + start_service + ;; + + stop) + stop_service + ;; + + restart) + $0 stop + $0 start + ;; + + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 +esac + +exit 0 diff --git a/contrib/ui/nas-synology-dsm6.0/package/dsm.mesh.conf b/contrib/ui/nas-synology-dsm6.0/package/dsm.mesh.conf new file mode 100644 index 00000000..c22b015b --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/package/dsm.mesh.conf @@ -0,0 +1,14 @@ +location = /mesh { + return 301 /mesh/; +} + +location ~ ^/mesh/ { + rewrite ^/mesh/(.*) /$1 break; + proxy_pass http://127.0.0.1:19019; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-For-Addr $server_addr; + proxy_set_header X-Forwarded-For-Port $server_port; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Original-URI $request_uri; +} diff --git a/contrib/ui/nas-synology-dsm6.0/package/mesh.sc b/contrib/ui/nas-synology-dsm6.0/package/mesh.sc new file mode 100644 index 00000000..fd9eb76b --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/package/mesh.sc @@ -0,0 +1,5 @@ +[mesh] +title="HTTP" +desc="RiV Mesh Web interface" +port_forward="no" +dst.ports="19019/tcp" diff --git a/contrib/ui/nas-synology-dsm6.0/package/ui/config b/contrib/ui/nas-synology-dsm6.0/package/ui/config new file mode 100644 index 00000000..f84eb833 --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/package/ui/config @@ -0,0 +1,11 @@ +{ + ".url": { + "org.mesh": { + "type": "url", + "title": "RiV Mesh", + "desc": "ipv6 mesh network", + "icon": "mesh-{0}.png", + "url": "/webman/3rdparty/mesh/login.html" + } + } +} diff --git a/contrib/ui/nas-synology-dsm6.0/package/ui/login.html b/contrib/ui/nas-synology-dsm6.0/package/ui/login.html new file mode 100644 index 00000000..2befc70b --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/package/ui/login.html @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/contrib/ui/nas-synology-dsm6.0/package/var/lib/mesh/hooks/deviceinfo b/contrib/ui/nas-synology-dsm6.0/package/var/lib/mesh/hooks/deviceinfo new file mode 100644 index 00000000..1dcdd82c --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/package/var/lib/mesh/hooks/deviceinfo @@ -0,0 +1,7 @@ +#!/bin/sh +echo vendor=Synology +echo vendorOperatingSystemName=DSM +. /etc.defaults/VERSION +echo firmwareVersion="${productversion}-${buildnumber}" +echo $(cat /etc/avahi/services/dsminfo.service | grep model | sed -e 's/<[^>]*>//g;s/^ *//') +echo $(cat /etc/avahi/services/dsminfo.service | grep serial | sed -e 's/<[^>]*>//g;s/^ *//') diff --git a/contrib/ui/nas-synology-dsm6.0/package/var/lib/mesh/hooks/webauth b/contrib/ui/nas-synology-dsm6.0/package/var/lib/mesh/hooks/webauth new file mode 100644 index 00000000..cd61f3c5 --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/package/var/lib/mesh/hooks/webauth @@ -0,0 +1,22 @@ +#!/bin/sh +export CONTENT_LENGTH=$HTTP_CONTENT_LENGTH +export CONTENT_TYPE=$HTTP_CONTENT_TYPE + +IFS=';' +for x in $HTTP_COOKIE +do + case $x in + *EdSynoToken*) + eval $x + ;; + esac +done +[ -z $QUERY_STRING ] && export QUERY_STRING=SynoToken=$EdSynoToken || export QUERY_STRING="$QUERY_STRING&SynoToken=$EdSynoToken" + +[ ! -z $HTTP_X_REAL_IP ] && export REMOTE_ADDR=$HTTP_X_REAL_IP +[ ! -z $HTTP_X_FORWARDED_FOR_ADDR ] && export SERVER_ADDR=$HTTP_X_FORWARDED_FOR_ADDR +[ ! -z $HTTP_X_FORWARDED_FOR_PORT ] && export SERVER_PORT=$HTTP_X_FORWARDED_FOR_PORT + +#exit with zero status on auth success and 1 on error +[ ! -z $(/usr/syno/synoman/webman/modules/authenticate.cgi) ] +exit $? diff --git a/contrib/ui/nas-synology-dsm6.0/package/var/lib/mesh/pre_upgrade.sh b/contrib/ui/nas-synology-dsm6.0/package/var/lib/mesh/pre_upgrade.sh new file mode 100644 index 00000000..1faceb4b --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/package/var/lib/mesh/pre_upgrade.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +log_exit(){ + echo $2 + exit $1 +} + +rm "$ED_APP_ROOT/bin/mesh +rm -rf "$ED_APP_ROOT/www" +rm -rf "$ED_APP_ROOT/ui" diff --git a/contrib/ui/nas-synology-dsm6.0/package/www/assets/partner-logo.png b/contrib/ui/nas-synology-dsm6.0/package/www/assets/partner-logo.png new file mode 100644 index 00000000..887975a1 Binary files /dev/null and b/contrib/ui/nas-synology-dsm6.0/package/www/assets/partner-logo.png differ diff --git a/contrib/ui/nas-synology-dsm6.0/package/www/assets/properties.js b/contrib/ui/nas-synology-dsm6.0/package/www/assets/properties.js new file mode 100644 index 00000000..505d3f53 --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/package/www/assets/properties.js @@ -0,0 +1,81 @@ +var ed = { + partnerId: 1126, + brand: 'RiV Mesh', + applicationName: 'RiV Mesh Synology DSM App', + nasOSName: 'Synology DSM', + nasVisitEDWebsiteLogin: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteSignup: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteLoggedin: "https://github.com/RiV-chain/RiV-mesh", + getCookie: function (name) { + var matches = document.cookie.match(new RegExp( + "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)" + )); + return matches ? decodeURIComponent(matches[1]) : undefined; + }, + setCookie: function (key, value, expires) { + if(expires) { + var d = new Date(); + d.setTime(d.getTime() + (30 * 60 * 1000)); + expires = "; expires=" + d.toUTCString(); + } else { + expires = ""; + } + document.cookie = key + "=" + value + expires + "; path=/"; + }, + getNasAuthUrl: function () { + ed.setCookie('EdSynoToken', ""); + var url = ed.getCookie('origin'); + if (url === undefined) { + url = window.location.protocol + "//" + window.location.hostname + ":" + 5000; + } + return url; + } +}; +$(function () { + function params(obj) { + if (typeof obj === 'string') { + if (obj[0] === '?') { + obj = obj.substring(1); + } + var result = {}; + obj = obj.split("&"); + obj.forEach(function (pair) { + pair = pair.split("="); + var key = decodeURIComponent(pair[0]); + var value = decodeURIComponent(pair[1]); + // If first entry with this name + if (typeof result[key] === "undefined") { + result[key] = value; + // If second entry with this name + } else if (typeof result[key] === "string") { + result[key] = [result[key], value]; + // If third or later entry with this name + } else { + result[key].push(value); + } + }); + return result; + } else { + return Object.keys(obj).map(function (key) { + return encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]); + }).join('&'); + } + }; + //Hide URL parameters + var query = params(window.location.search.substring(1)); + var refresh = false; + for (var k in query) { + if (k === 'SynoToken') { + ed.setCookie('EdSynoToken', query[k]); + refresh = true; + delete query[k]; + } else if (k === 'origin') { + ed.setCookie('origin', query[k]); + refresh = true; + delete query[k]; + } + } + query = $.param(query); + if (refresh) + window.location.replace(window.location.origin + window.location.pathname + window.location.hash + ((query === "") ? "" : ("?" + query))); +}); diff --git a/contrib/ui/nas-synology-dsm6.0/spk/conf/resource b/contrib/ui/nas-synology-dsm6.0/spk/conf/resource new file mode 100644 index 00000000..37c611c5 --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/spk/conf/resource @@ -0,0 +1,5 @@ +{ + "port-config": { + "protocol-file": "mesh.sc" + } +} diff --git a/contrib/ui/nas-synology-dsm6.0/spk/scripts/postinst b/contrib/ui/nas-synology-dsm6.0/spk/scripts/postinst new file mode 100644 index 00000000..824040ca --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/spk/scripts/postinst @@ -0,0 +1,17 @@ +#!/bin/bash +DEBUG=true +SH="$(readlink /proc/$$/exe)" +if $DEBUG ; then + echo "" > /tmp/mesh.log + exec 2>>/tmp/mesh.log + SH="$SH -x" + set -x +fi + +BASE="/var/packages/mesh/target" +LOG_FILE="$BASE/var/log/mesh.log" + +ln -sf $LOG_FILE $BASE/www/log +ln -sf $BASE/dsm.mesh.conf /etc/nginx/conf.d/dsm.mesh.conf +nginx -s reload +exit 0 diff --git a/contrib/ui/nas-synology-dsm6.0/spk/scripts/postuninst b/contrib/ui/nas-synology-dsm6.0/spk/scripts/postuninst new file mode 100644 index 00000000..19fa823c --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/spk/scripts/postuninst @@ -0,0 +1,8 @@ +#!/bin/sh + +rm -f /var/run/mesh.pid +LOG_SYMLINK=/var/log/mesh.log +[ -L $LOG_SYMLINK ] && rm -f $LOG_SYMLINK +rm -f /etc/nginx/conf.d/dsm.mesh.conf + +exit 0 diff --git a/contrib/ui/nas-synology-dsm6.0/spk/scripts/postupgrade b/contrib/ui/nas-synology-dsm6.0/spk/scripts/postupgrade new file mode 100644 index 00000000..c52d3c26 --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/spk/scripts/postupgrade @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 diff --git a/contrib/ui/nas-synology-dsm6.0/spk/scripts/preinst b/contrib/ui/nas-synology-dsm6.0/spk/scripts/preinst new file mode 100644 index 00000000..c52d3c26 --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/spk/scripts/preinst @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 diff --git a/contrib/ui/nas-synology-dsm6.0/spk/scripts/preuninst b/contrib/ui/nas-synology-dsm6.0/spk/scripts/preuninst new file mode 100644 index 00000000..c52d3c26 --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/spk/scripts/preuninst @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 diff --git a/contrib/ui/nas-synology-dsm6.0/spk/scripts/preupgrade b/contrib/ui/nas-synology-dsm6.0/spk/scripts/preupgrade new file mode 100644 index 00000000..c52d3c26 --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/spk/scripts/preupgrade @@ -0,0 +1,3 @@ +#!/bin/sh + +exit 0 diff --git a/contrib/ui/nas-synology-dsm6.0/spk/scripts/start-stop-status b/contrib/ui/nas-synology-dsm6.0/spk/scripts/start-stop-status new file mode 100644 index 00000000..70d3afd1 --- /dev/null +++ b/contrib/ui/nas-synology-dsm6.0/spk/scripts/start-stop-status @@ -0,0 +1,120 @@ +#!/bin/sh + +BASE="/var/packages/mesh/target" +CONFIG_DIR="/var/packages/mesh/etc" +config_file="$CONFIG_DIR/mesh.conf" +LOG_FILE="$BASE/var/log/mesh.log" +export LD_LIBRARY_PATH="$BASE/lib" +cd / + +set_proxy() +{ + if [ -f /etc/proxy.conf ]; then + . /etc/proxy.conf + if [ "$proxy_enabled" = "yes" ]; then + export http_proxy="$https_host":"$https_port" + fi + fi +} + +start_service () +{ + + set_proxy + + if [ -f $config_file ]; then + mkdir -p /var/backups + echo "Backing up configuration file to /var/backups/mesh.conf.`date +%Y%m%d`" + cp $config_file /var/backups/mesh.conf.`date +%Y%m%d` + echo "Normalising and updating /etc/mesh.conf" + ${BASE}/bin/mesh -useconf -normaliseconf < /var/backups/mesh.conf.`date +%Y%m%d` > $config_file + else + echo "Generating initial configuration file $config_file" + echo "Please familiarise yourself with this file before starting RiV-mesh" + sh -c "umask 0027 && ${BASE}/bin/mesh -genconf > '$config_file'" + fi + + # Create the necessary file structure for /dev/net/tun + if ( [ ! -c /dev/net/tun ] ); then + if ( [ ! -d /dev/net ] ); then + mkdir -m 755 /dev/net + fi + mknod /dev/net/tun c 10 200 + chmod 0755 /dev/net/tun + fi + + # Load the tun module if not already loaded + if ( !(lsmod | grep -q "^tun\s") ); then + insmod /lib/modules/tun.ko + fi + + #workaround for bin error: + #Not multicasting on eth0 due to error: listen tcp [xxxx]:0: bind: cannot assign requested address + sysctl net.ipv6.ip_nonlocal_bind=1 + + # Launch the mesh in the background. + ${BASE}/bin/mesh -useconffile "$config_file" \ + -httpaddress "http://localhost:19019" \ + -wwwroot "$BASE/www" \ + -logto "$BASE/var/log/mesh.log" & + + return $? +} + +stop_service () +{ + pid=`pidof -s mesh` + if [ -z "$pid" ]; then + echo "mesh was not running" + exit 0 + fi + kill "$pid" +} + +status_service () +{ + + pid=`pidof -s mesh` + + if [[ ! -z "${pid}" ]] && [ -d /proc/${pid} ]; then + return 0 + fi + if [[ ! -z "${pid}" ]]; then + return 1 + fi + return 3 +} + +case $1 in + start) + start_service + echo "Running RiV Mesh" + exit 0 + ;; + stop) + stop_service + echo "Stopped RiV Mesh" + exit 0 + ;; + status) + status_service + sts=$? + if [ $sts -eq 0 ]; then + echo "RiV Mesh is running" + else + echo "RiV Mesh is not running" + fi + exit $sts + ;; + + log) + if [ -f $LOG_FILE ]; + then + echo $LOG_FILE + fi + exit 0 + ;; + *) + exit 1 + ;; +esac diff --git a/contrib/ui/nas-synology-dsm7.0/package/ui/login.html b/contrib/ui/nas-synology-dsm7.0/package/ui/login.html new file mode 100644 index 00000000..87f26c1f --- /dev/null +++ b/contrib/ui/nas-synology-dsm7.0/package/ui/login.html @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/contrib/ui/nas-synology-dsm7.0/spk/conf/privilege b/contrib/ui/nas-synology-dsm7.0/spk/conf/privilege new file mode 100644 index 00000000..a6d06a81 --- /dev/null +++ b/contrib/ui/nas-synology-dsm7.0/spk/conf/privilege @@ -0,0 +1,21 @@ +{ + "defaults":{ + "run-as": "package" + }, + "ctrl-script":[{ + "action": "postinst", + "run-as": "root" + }, { + "action": "postuninst", + "run-as": "root" + }, { + "action": "preuninst", + "run-as": "root" + }, { + "action": "start", + "run-as": "root" + }, { + "action": "stop", + "run-as": "root" + }] +} diff --git a/contrib/ui/nas-synology-dsm7.0/spk/scripts/start-stop-status b/contrib/ui/nas-synology-dsm7.0/spk/scripts/start-stop-status new file mode 100644 index 00000000..b4ad92d7 --- /dev/null +++ b/contrib/ui/nas-synology-dsm7.0/spk/scripts/start-stop-status @@ -0,0 +1,120 @@ +#!/bin/sh + +BASE="/var/packages/mesh/target" +CONFIG_DIR="/var/packages/mesh/etc" +config_file="$CONFIG_DIR/mesh.conf" +LOG_FILE="$BASE/var/log/mesh.log" +export LD_LIBRARY_PATH="$BASE/lib" +cd / + +set_proxy() +{ + if [ -f /etc/proxy.conf ]; then + . /etc/proxy.conf + if [ "$proxy_enabled" = "yes" ]; then + export http_proxy="$https_host":"$https_port" + fi + fi +} + +start_service () +{ + + set_proxy + + if [ -f $config_file ]; then + mkdir -p /var/backups + echo "Backing up configuration file to /var/backups/mesh.conf.`date +%Y%m%d`" + cp $config_file /var/backups/mesh.conf.`date +%Y%m%d` + echo "Normalising and updating /etc/mesh.conf" + ${BASE}/bin/mesh -useconf -normaliseconf < /var/backups/mesh.conf.`date +%Y%m%d` > $config_file + else + echo "Generating initial configuration file $config_file" + echo "Please familiarise yourself with this file before starting RiV-mesh" + sh -c "umask 0027 && ${BASE}/bin/mesh -genconf > '$config_file'" + fi + + # Create the necessary file structure for /dev/net/tun + if ( [ ! -c /dev/net/tun ] ); then + if ( [ ! -d /dev/net ] ); then + mkdir -m 755 /dev/net + fi + mknod /dev/net/tun c 10 200 + chmod 0755 /dev/net/tun + fi + + # Load the tun module if not already loaded + if ( !(lsmod | grep -q "^tun\s") ); then + insmod /lib/modules/tun.ko + fi + + #workaround for bin error: + #Not multicasting on eth0 due to error: listen tcp [xxxx]:0: bind: cannot assign requested address + sysctl net.ipv6.ip_nonlocal_bind=1 + + # Launch the server in the background. + ${BASE}/bin/mesh -useconffile "$config_file" \ + -httpaddress "http://[::]:19019" \ + -wwwroot "$BASE/www" \ + -logto "$BASE/var/log/mesh.log" & + + return $? +} + +stop_service () +{ + pid=`pidof -s mesh` + if [ -z "$pid" ]; then + echo "mesh was not running" + exit 0 + fi + kill "$pid" +} + +status_service () +{ + + pid=`pidof -s mesh` + + if [[ ! -z "${pid}" ]] && [ -d /proc/${pid} ]; then + return 0 + fi + if [[ ! -z "${pid}" ]]; then + return 1 + fi + return 3 +} + +case $1 in + start) + start_service + echo "Running RiV Mesh" + exit 0 + ;; + stop) + stop_service + echo "Stopped RiV Mesh" + exit 0 + ;; + status) + status_service + sts=$? + if [ $sts -eq 0 ]; then + echo "RiV Mesh is running" + else + echo "RiV Mesh is not running" + fi + exit $sts + ;; + + log) + if [ -f $LOG_FILE ]; + then + echo $LOG_FILE + fi + exit 0 + ;; + *) + exit 1 + ;; +esac diff --git a/contrib/ui/nas-terramaster/mesh/config.ini b/contrib/ui/nas-terramaster/mesh/config.ini new file mode 100644 index 00000000..b70eff7b --- /dev/null +++ b/contrib/ui/nas-terramaster/mesh/config.ini @@ -0,0 +1,10 @@ +{ +"path": "/module/?mod=4.Application/21.mesh.php&t=0", +"id": "mesh", +"exec": 1, +"hideTitle": 1, +"width": 900, +"height": 580, +"icon": "/images/icons/mesh.svg", +"content": "core.appNormal();" +} diff --git a/contrib/ui/nas-terramaster/mesh/etc/init.d/mesh b/contrib/ui/nas-terramaster/mesh/etc/init.d/mesh new file mode 100755 index 00000000..25259422 --- /dev/null +++ b/contrib/ui/nas-terramaster/mesh/etc/init.d/mesh @@ -0,0 +1,74 @@ +#!/bin/sh + +BASE="/usr" +CONFIG_DIR="/etc" +config_file="$CONFIG_DIR/mesh.conf" +LOG_FILE="$BASE/var/log/mesh.log" + +service_status() +{ + + pid=`pidof -s mesh` + + if [[ ! -z "${pid}" ]] && [ -d /proc/${pid} ]; then + echo "mesh running" + exit 1 + fi + echo "mesh stopped" +} + +service_start(){ + + if [ -f $config_file ]; then + mkdir -p /var/backups + echo "Backing up configuration file to /var/backups/mesh.conf.`date +%Y%m%d`" + cp $config_file /var/backups/mesh.conf.`date +%Y%m%d` + echo "Normalising and updating /etc/mesh.conf" + ${BASE}/bin/mesh -useconf -normaliseconf < /var/backups/mesh.conf.`date +%Y%m%d` > $config_file + else + echo "Generating initial configuration file $config_file" + echo "Please familiarise yourself with this file before starting RiV-mesh" + sh -c "umask 0027 && ${BASE}/bin/mesh -genconf > '$config_file'" + fi + + # Launch the server in the background. + ${BASE}/bin/mesh -useconffile "$config_file" \ + -httpaddress "http://127.0.0.1:19019" \ + -wwwroot "/usr/local/mesh/www" \ + -logto "$BASE/var/log/mesh.log" & + + service nginx reload + sleep 1 +} + +service_stop(){ + pid=`pidof -s mesh` + if [ -z "$pid" ]; then + echo "mesh was not running" + exit 0 + fi + kill "$pid" +} + +service_restart(){ + service_stop + service_start +} + +case $1 in + start) + service_start + exit 0 ;; + stop) + service_stop + exit 0 ;; + restart) + service_restart + exit 0 ;; + status) + service_status + exit 0 ;; + *) + echo "Usage: `basename $0` {start|stop|restart|status}" + exit 1 ;; +esac diff --git a/contrib/ui/nas-terramaster/mesh/etc/nginx/conf.d/mesh.conf b/contrib/ui/nas-terramaster/mesh/etc/nginx/conf.d/mesh.conf new file mode 100644 index 00000000..294000ba --- /dev/null +++ b/contrib/ui/nas-terramaster/mesh/etc/nginx/conf.d/mesh.conf @@ -0,0 +1,4 @@ +location ~ ^/mesh/ { + rewrite ^/mesh/(.*) /$1 break; + proxy_pass http://127.0.0.1:19019; +} diff --git a/contrib/ui/nas-terramaster/mesh/mesh.lang b/contrib/ui/nas-terramaster/mesh/mesh.lang new file mode 100644 index 00000000..2a5bcab3 --- /dev/null +++ b/contrib/ui/nas-terramaster/mesh/mesh.lang @@ -0,0 +1,11 @@ +[zh-cn] +name = "RiV Mesh" +auth = "mesh" +version = "%%VERSION%%" +descript = "RiV-mesh is an implementation of a fully end-to-end encrypted IPv6 network." + +[en-us] +name = "RiV Mesh" +auth = "mesh" +version = "%%VERSION%%" +descript = "RiV-mesh is an implementation of a fully end-to-end encrypted IPv6 network." diff --git a/contrib/ui/nas-terramaster/mesh/remove_list b/contrib/ui/nas-terramaster/mesh/remove_list new file mode 100644 index 00000000..45543188 --- /dev/null +++ b/contrib/ui/nas-terramaster/mesh/remove_list @@ -0,0 +1 @@ +/usr/www/mesh diff --git a/contrib/ui/nas-terramaster/mesh/usr/local/mesh/var/lib/mesh/hooks/deviceinfo b/contrib/ui/nas-terramaster/mesh/usr/local/mesh/var/lib/mesh/hooks/deviceinfo new file mode 100644 index 00000000..8af339e2 --- /dev/null +++ b/contrib/ui/nas-terramaster/mesh/usr/local/mesh/var/lib/mesh/hooks/deviceinfo @@ -0,0 +1,6 @@ +#!/bin/sh +echo vendor=TerraMaster +echo vendorOperatingSystemName=TerraMaster TOS +echo firmwareVersion=$(awk -F= '$1 ~ /version/ {print $2}' /etc/tos.conf) +#echo model= +#echo serial= diff --git a/contrib/ui/nas-terramaster/mesh/usr/local/mesh/www/assets/partner-logo.png b/contrib/ui/nas-terramaster/mesh/usr/local/mesh/www/assets/partner-logo.png new file mode 100644 index 00000000..40efc970 Binary files /dev/null and b/contrib/ui/nas-terramaster/mesh/usr/local/mesh/www/assets/partner-logo.png differ diff --git a/contrib/ui/nas-terramaster/mesh/usr/local/mesh/www/assets/properties.js b/contrib/ui/nas-terramaster/mesh/usr/local/mesh/www/assets/properties.js new file mode 100644 index 00000000..659a34b6 --- /dev/null +++ b/contrib/ui/nas-terramaster/mesh/usr/local/mesh/www/assets/properties.js @@ -0,0 +1,11 @@ +var ed = { + partnerId: 1599, + brand: 'RiV Mesh', + applicationName: 'RiV Mesh TerraMaster App', + nasOSName: 'TerraMaster NAS', + nasVisitEDWebsiteLogin: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteSignup: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteLoggedin: "https://github.com/RiV-chain/RiV-mesh", + signingUpMessageHtml: "", + signingUpPropositionMessageHtml: "" +}; diff --git a/contrib/ui/nas-terramaster/mesh/usr/www/css/style-mesh.css b/contrib/ui/nas-terramaster/mesh/usr/www/css/style-mesh.css new file mode 100644 index 00000000..7eb76373 --- /dev/null +++ b/contrib/ui/nas-terramaster/mesh/usr/www/css/style-mesh.css @@ -0,0 +1,17 @@ +.plex{} +.plex .header{border:none;height:150px;padding:20px 0 30px;box-sizing:border-box;} +.plex .header .logo{margin:0px auto;width:300px;height:100px;display:block;outline:none;background: url(../images/ext/plex-logo.svg) #8B6161 center center no-repeat;background-size:90%;} +.plex .introduce{width:520px;margin:0 auto;line-height:24px;font-size:16px;} +.plex .introduce *{line-height:24px;font-size:16px;} +.plex .introduce i{display: table-row;font-size:24px;font-style: normal;line-height:40px;} +.heder-info{display: table;width: 85%;margin: 0 auto;} +.logo-info{display: table-cell;width: 70%;vertical-align: middle;} +.logo-info img{width: 10%;float: left;} +.logo-info figcaption{height: 45px;float: left;line-height: 45px;font-size: 30px;margin-left: 10px;} +.v-info{display: table-cell;} +.v-info p{margin: 0;padding: 0;} +.intr{display: table;width: 85%;margin: 0 auto;margin-top: 8px;margin-bottom: 50px;} +.btn-info-no{line-height: 1;height: 30px;padding: 0 15px;background-color: rgb(255,255,255);border: 1px solid rgb(210,210,210); color: rgb(120,120,120);position: relative;z-index: 200;} +.btn-info-no:active{color: rgb(60,60,60);border-color: rgb(138,138,138);} +.btn-info-no:hover{color: rgb(60,60,60);border-color: rgb(138,138,138);} +.fieldset:first-child{padding-top: 0 !important;} \ No newline at end of file diff --git a/contrib/ui/nas-terramaster/mesh/usr/www/include/class/mesh.class.php b/contrib/ui/nas-terramaster/mesh/usr/www/include/class/mesh.class.php new file mode 100644 index 00000000..a50b7f5e --- /dev/null +++ b/contrib/ui/nas-terramaster/mesh/usr/www/include/class/mesh.class.php @@ -0,0 +1,33 @@ +fun->_exec(self::$execute." status"); + if(strstr($ret, "running")){ + return 1; + }else{ + return 0; + } + }else{ + return 0; + } + } + + function _switch($stat) + { + if($stat == 1){ + $this->_sysAutoEvent(basename(self::$execute), true); + $this->fun->_backexec(self::$execute." start"); + }else{ + $this->_sysAutoEvent(basename(self::$execute), false); + $this->fun->_exec(self::$execute." stop"); + } + sleep(1); + $ret = $this->_status(); + return $ret; + } +} diff --git a/contrib/ui/nas-terramaster/mesh/usr/www/lang/ext/mesh.lang b/contrib/ui/nas-terramaster/mesh/usr/www/lang/ext/mesh.lang new file mode 100644 index 00000000..a5a59360 --- /dev/null +++ b/contrib/ui/nas-terramaster/mesh/usr/www/lang/ext/mesh.lang @@ -0,0 +1,11 @@ +[zh-cn] +mesh = "RiV Mesh" +auth = "Riv Chain ltd" +tos_mesh_note = "RiV-mesh 是完全端到端加密 IPv6 網絡的實現" +enter = "进入" + +[en-us] +mesh = "RiV Mesh" +auth = "Riv Chain ltd" +tos_mesh_note = "RiV-mesh is an implementation of a fully end-to-end encrypted IPv6 network." +enter = "Enter" diff --git a/contrib/ui/nas-terramaster/mesh/usr/www/mod/4.Application/21.mesh.php b/contrib/ui/nas-terramaster/mesh/usr/www/mod/4.Application/21.mesh.php new file mode 100644 index 00000000..9f743d26 --- /dev/null +++ b/contrib/ui/nas-terramaster/mesh/usr/www/mod/4.Application/21.mesh.php @@ -0,0 +1,93 @@ +_status(); +$app_info = $mesh->get_app_info('mesh'); +$L = $La->extlanguage("mesh"); +$url = "http://".$_SERVER['SERVER_ADDR'].":8181/mesh/"; +?> + + + + + + + diff --git a/contrib/ui/nas-westerndigital/package/mesh/apache-mesh.conf b/contrib/ui/nas-westerndigital/package/mesh/apache-mesh.conf new file mode 100644 index 00000000..c7a70f39 --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/apache-mesh.conf @@ -0,0 +1,8 @@ +RedirectMatch 301 ^/mesh$ /mesh/ +LoadModule proxy_http_module modules/mod_proxy_http.so + + + ProxyPreserveHost On + ProxyPass "http://127.0.0.1:19019/" + ProxyPassReverse "http://127.0.0.1:19019/" + diff --git a/contrib/ui/nas-westerndigital/package/mesh/clean.sh b/contrib/ui/nas-westerndigital/package/mesh/clean.sh new file mode 100755 index 00000000..c3c9cf02 --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/clean.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +MESH_PACKAGE_LOG=/var/log/mesh.log +echo "clean.sh called" >> "$MESH_PACKAGE_LOG" + +rm -f /usr/bin/mesh +rm -f /usr/bin/meshctl + + +rm -rf /var/www/mesh +rm -f /usr/local/apache2/conf/extra/apache-mesh.conf diff --git a/contrib/ui/nas-westerndigital/package/mesh/init.sh b/contrib/ui/nas-westerndigital/package/mesh/init.sh new file mode 100755 index 00000000..c3590b6b --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/init.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +install_path="$1" +config_file=$install_path/mesh.conf +backup_config_file=/var/backups/mesh.conf + +MESH_PACKAGE_LOG=/var/log/mesh.log +echo "init.sh called" >> "$MESH_PACKAGE_LOG" + +exec 2>>/var/log/mesh.log +set -x + +mkdir -p /var/www/mesh + +# Binaries +ln -s "$install_path/bin/mesh" /usr/bin +ln -s "$install_path/bin/meshctl" /usr/bin + +# Web, probably, the app wil serve it by embedded server +ln -s "$install_path/www/mesh.png" /var/www/mesh +ln -s "$install_path/www/index.html" /var/www/mesh + +if [ -f $backup_config_file ]; then + echo "Backing up configuration file to /var/backups/mesh.conf" + echo "Normalising and updating /etc/mesh.conf" + mesh -useconf -normaliseconf < $backup_config_file > $config_file +else + echo "Generating initial configuration file $config_file" + echo "Please familiarise yourself with this file before starting RiV-mesh" + sh -c "umask 0027 && mesh -genconf > '$config_file'" + mkdir -p /var/backups +fi + +cp $config_file $backup_config_file diff --git a/contrib/ui/nas-westerndigital/package/mesh/install.sh b/contrib/ui/nas-westerndigital/package/mesh/install.sh new file mode 100755 index 00000000..b1172653 --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/install.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +INST_PATH=/mnt/HD/HD_a2/Nas_Prog/mesh +MESH_PACKAGE_LOG=/var/log/mesh.log +echo "install.sh called" >> "$MESH_PACKAGE_LOG" + +path_src="$1" +path_dst="$2" + +rm -rf "$INST_PATH" +mv "$path_src" "$path_dst" +#LOG_SYMLINK +ln -s "$INST_PATH"/var/log/mesh.log "$INST_PATH"/www/log +#already installed in start +#ln -s "$INST_PATH"/apache-mesh.conf /usr/local/apache2/conf/extra + +echo "install.sh: setting permissions to '$path_dst/mesh'" >> "$MESH_PACKAGE_LOG" +chown -R nobody:share "$path_dst/mesh" + +#( sleep 2 ; /usr/sbin/apache restart web ) & diff --git a/contrib/ui/nas-westerndigital/package/mesh/preinst.sh b/contrib/ui/nas-westerndigital/package/mesh/preinst.sh new file mode 100755 index 00000000..0d2e15f4 --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/preinst.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Write to a different log, because main log will be overwritten by install.sh +MESH_PACKAGE_LOG=/var/log/mesh-preinst.log +echo "preinst.sh called" >> "$MESH_PACKAGE_LOG" + +path_dst="$1" +echo "path_dst=$path_dst" >> "$MESH_PACKAGE_LOG" + + +chown -R nobody:share "$path_dst" diff --git a/contrib/ui/nas-westerndigital/package/mesh/remove.sh b/contrib/ui/nas-westerndigital/package/mesh/remove.sh new file mode 100755 index 00000000..6272ba92 --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/remove.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +MESH_PACKAGE_LOG=/var/log/mesh.log +echo "remove.sh called" >> "$MESH_PACKAGE_LOG" +inst_path="$1" + +rm -f /usr/bin/mesh +rm -f /usr/bin/meshctl +rm -fr /var/www/mesh +rm -fr "$inst_path" diff --git a/contrib/ui/nas-westerndigital/package/mesh/start.sh b/contrib/ui/nas-westerndigital/package/mesh/start.sh new file mode 100755 index 00000000..8f3aa8f8 --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/start.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +MESH_PACKAGE_LOG=/var/log/mesh.log +echo "start.sh called" >> "$MESH_PACKAGE_LOG" + +pid=`pidof -s mesh` +if [ -n "$pid" ]; then + echo "start.sh: mesh already running" >> "$MESH_PACKAGE_LOG" + exit 0 +fi + +EXE_PATH=`readlink -f /usr/bin/mesh` +BIN_PATH=`dirname $EXE_PATH` +INSTALL_PATH=`dirname $BIN_PATH` +config_file=$INSTALL_PATH/mesh.conf + +ln -fs "$INSTALL_PATH"/apache-mesh.conf /usr/local/apache2/conf/extra +(/usr/sbin/apache restart web ) & + +START_COMMAND="/usr/bin/mesh -useconffile '$config_file' -httpaddress 'http://localhost:19019' -wwwroot '${INSTALL_PATH}/www' -logto '${INSTALL_PATH}/var/log/mesh.log'" +echo "start.sh: starting (START_COMMAND=$START_COMMAND)" >> "$MESH_PACKAGE_LOG" + + +(/usr/bin/mesh -useconffile "$config_file" -httpaddress "http://localhost:19019" -wwwroot "${INSTALL_PATH}/www" -logto "${INSTALL_PATH}/var/log/mesh.log") & diff --git a/contrib/ui/nas-westerndigital/package/mesh/stop.sh b/contrib/ui/nas-westerndigital/package/mesh/stop.sh new file mode 100755 index 00000000..6efa40c4 --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/stop.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +MESH_PACKAGE_LOG=/var/log/mesh.log +echo "stop.sh called" >> "$MESH_PACKAGE_LOG" + +rm -f /usr/local/apache2/conf/extra/apache-mesh.conf +(/usr/sbin/apache restart web ) & + +# ash is VERY limited, so use only basic ops +pid=`pidof -s mesh` +if [ -z "$pid" ]; then + echo "stop.sh: mesh was not running" >> "$MESH_PACKAGE_LOG" + exit 0 +fi + +echo "stop.sh: stop attempt #1" >> "$MESH_PACKAGE_LOG" +kill "$pid" +sleep 2 + +pid=`pidof -s mesh` +if [ -z "$pid" ]; then + echo "stop.sh: stopped" >> "$MESH_PACKAGE_LOG" + exit 0 +fi + +echo "stop.sh: stop attempt #2" >> "$MESH_PACKAGE_LOG" +kill "$pid" +sleep 4 + +pid=`pidof -s mesh` +if [ -z "$pid" ]; then + echo "stop.sh: stopped" >> "$MESH_PACKAGE_LOG" + exit 0 +fi + +echo "stop.sh: stop attempt #3" >> "$MESH_PACKAGE_LOG" +kill "$pid" +sleep 4 + +pid=`pidof -s mesh` +if [ -z "$pid" ]; then + echo "stop.sh: stopped" >> "$MESH_PACKAGE_LOG" + exit 0 +fi + +echo "stop.sh: hard kill" >> "$MESH_PACKAGE_LOG" +pkill -9 mesh diff --git a/contrib/ui/nas-westerndigital/package/mesh/ui/desc.xml b/contrib/ui/nas-westerndigital/package/mesh/ui/desc.xml new file mode 100644 index 00000000..fd9268ec --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/ui/desc.xml @@ -0,0 +1,7 @@ + + + Learn more here...]]> + diff --git a/contrib/ui/nas-westerndigital/package/mesh/var/lib/mesh/hooks/deviceinfo b/contrib/ui/nas-westerndigital/package/mesh/var/lib/mesh/hooks/deviceinfo new file mode 100644 index 00000000..a6c4860a --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/var/lib/mesh/hooks/deviceinfo @@ -0,0 +1,7 @@ +#!/bin/sh +echo vendor=westerndigital +echo vendorOperatingSystemName=WDMyCloud +echo firmwareVersion=$(cat /etc/version) +echo model=$(cat /etc/nas/nasdevice.xml | grep modelName | sed -e 's/<[^>]*>//g;s/^[ \t]*//') +echo serial=$(cat /etc/nas/nasdevice.xml | grep serialNumber | sed -e 's/<[^>]*>//g;s/^[ \t]*//') + diff --git a/contrib/ui/nas-westerndigital/package/mesh/var/lib/pre_upgrade.sh b/contrib/ui/nas-westerndigital/package/mesh/var/lib/pre_upgrade.sh new file mode 100644 index 00000000..1faceb4b --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/var/lib/pre_upgrade.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +log_exit(){ + echo $2 + exit $1 +} + +rm "$ED_APP_ROOT/bin/mesh +rm -rf "$ED_APP_ROOT/www" +rm -rf "$ED_APP_ROOT/ui" diff --git a/contrib/ui/nas-westerndigital/package/mesh/www/assets/partner-logo.png b/contrib/ui/nas-westerndigital/package/mesh/www/assets/partner-logo.png new file mode 100644 index 00000000..7d0ae31a Binary files /dev/null and b/contrib/ui/nas-westerndigital/package/mesh/www/assets/partner-logo.png differ diff --git a/contrib/ui/nas-westerndigital/package/mesh/www/assets/partner.css b/contrib/ui/nas-westerndigital/package/mesh/www/assets/partner.css new file mode 100755 index 00000000..9166c5e0 --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/www/assets/partner.css @@ -0,0 +1,9 @@ +.nas-apps-config-form-app-logo { + height: 110px; + background: url(logo.png) left center no-repeat; + background-size: 262px 56px; +} + +a { + color: #692782!important; +} diff --git a/contrib/ui/nas-westerndigital/package/mesh/www/assets/properties.js b/contrib/ui/nas-westerndigital/package/mesh/www/assets/properties.js new file mode 100644 index 00000000..663f0902 --- /dev/null +++ b/contrib/ui/nas-westerndigital/package/mesh/www/assets/properties.js @@ -0,0 +1,9 @@ +var ed = { + partnerId: 1950, + applicationName: 'RiV-Mesh App', + nasOSName: 'My Cloud OS', + basicEDWebsite: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteLogin: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteSignup: "https://github.com/RiV-chain/RiV-mesh", + nasVisitEDWebsiteLoggedin: "https://github.com/RiV-chain/RiV-mesh" +}; diff --git a/contrib/ui/www/assets/ajax-loader.gif b/contrib/ui/www/assets/ajax-loader.gif new file mode 100644 index 00000000..d84f6537 Binary files /dev/null and b/contrib/ui/www/assets/ajax-loader.gif differ diff --git a/contrib/ui/www/assets/edrive.css b/contrib/ui/www/assets/edrive.css new file mode 100644 index 00000000..edc992f5 --- /dev/null +++ b/contrib/ui/www/assets/edrive.css @@ -0,0 +1,160 @@ +.hide{display:none;} + +body { + font: 12px Verdana; + padding: 0; + margin: 0; +} +hr { + background: none; + border: none; + border-bottom: 2px solid gray; +} +a { color: #02b7ff; } + +::-webkit-input-placeholder { color: #666 !important; } +:-moz-placeholder { color: #666 !important; } +::-moz-placeholder { color: #666 !important; } +:-ms-input-placeholder { color: #666 !important; } + +.nas-apps-config-form-message { + background: #ccc; + color: black; + padding: 6px 12px; + margin: 0 8px 8px 8px; + border-radius: 6px; + font-weight: bold; + text-align: center; + background-color: #00c81e; +} + +.nas-apps-config-form-message-error { + background-color: #fa1c1ce8; +} + +.nas-apps-config-form-middle { + width: 500px; + margin: 0 auto; + padding: 20px; + overflow: hidden; +} + +.nas-apps-config-form-top { + padding: 10px 20px; + width: 500px; + margin: 0 auto; +} +.nas-apps-config-form-bottom { + text-align: right; + width: 500px; + margin: 0 auto; + padding: 10px 20px; +} +.nas-apps-config-form-left { float: left; } +.nas-apps-config-form-right { + float: right; + width: 200px; + padding: 20px 0; +} + +.nas-apps-config-form-link-info { + margin: 5px 0 20px 0; +} + +/* top */ +.nas-apps-config-form-app-logo { + height: 110px; + background: center center no-repeat; + background-size: 439px 90px; + background-image: url(logo.png),url(logo.svg); +} +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + .nas-apps-config-form-app-logo { background-image: url(copy_logo@2x.png); } +} +.nas-apps-config-form-app-version { + font-size: 10px; + text-align: right; +} + +/* bottom */ +.nas-apps-config-form-partner-logo { + height: 110px; + background: center center no-repeat; + background-image: url(partner-logo.png),url(partner-logo.svg); +} + +.nas-apps-config-form-auth { + margin-bottom: 16px; +} +.nas-apps-config-form-auth-id, +.nas-apps-config-form-location-name { + margin: 8px; + font-weight: bold; + font-size: 14px; +} +.nas-apps-config-form-auth-link { + margin: 8px; +} + +.nas-apps-config-form-field-label { + display: block; + margin: 4px 8px -4px 8px; + font-size: 10px; + color: #999; +} +.nas-apps-config-form-field input, +.nas-apps-config-form-field select { + background: transparent; + -webkit-appearance: none; + border-radius: 10px; + padding: 6px 12px; + font: 12px Verdana; + border: 1px solid #666; + margin: 8px; + width: 200px; + box-sizing: border-box; + -o-box-sizing: border-box; + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} +.nas-apps-config-form-field select:focus option { + color: black; +} + +.nas-apps-config-form-field input:focus, +.nas-apps-config-form-field select:focus { + border: 2px solid #02b7ff; + outline: none; + margin: 7px; +} + +.nas-apps-config-form-button { + height: 30px; + width: 75px; + color: white; + background: black; + font: 12px Verdana; + border-radius: 8px; + border: 1px solid #666; + margin: 8px; + padding: 0 16px; + cursor: pointer; + background: -webkit-linear-gradient(#3f3f3f, #000); +} + +.nas-apps-config-form-button.nas-apps-feedback-button { + width: auto !important; + margin: 0; + margin-top: 8px; +} + +.nas-apps-config-form-button[type=submit] { + border-color: #c1d3dc; + background: -webkit-linear-gradient(#405f70, #000); + box-shadow: inset 0 0 6px #405f70; +} + +.nas-apps-config-nas-logout-block { + text-align: right; +} diff --git a/contrib/ui/www/assets/edrive.js b/contrib/ui/www/assets/edrive.js new file mode 100644 index 00000000..2bb63512 --- /dev/null +++ b/contrib/ui/www/assets/edrive.js @@ -0,0 +1,416 @@ +$(function () { + var ed = 'ed' in window ? window.ed : {}; + var info = { + devicename : null, + vaultUrl : "https://github.com/RiV-chain/RiV-mesh" + }; + var basicEDWebsite = "https://github.com/RiV-chain/RiV-mesh"; + var authNASScreen = '#authNASScreen'; + var isLoggedInMode = false; + + $.ajaxSetup({cache: false}); + + var fillAjax = function (url, ajax) { + ajax.url = "api/" + url; + ajax.contentType = 'application/json'; + if ('fillAjax' in ed) { + ed.fillAjax(ajax); + } + return ajax; + }; + + var loadingMode = function (message) { + $('#signupScreen').hide(); + $('#statusScreen').hide(); + $('#loginScreen').hide(); + $(authNASScreen).hide(); + $('#loadingMessage').text(message); + $('#loadingScreen').show(); + }; + + var signupMode = function () { + $('#signupScreen').show(); + $('#loadingScreen').hide(); + $('#statusScreen').hide(); + $('#loginScreen').hide(); + $('#signupAlert').hide(); + $('#inputPeerIPS').val(''); + $('#inputPeerPortS').val(''); + $('#inputPortConfirm').val(''); + }; + + var statusMode = function () { + $('#signupScreen').hide(); + $('#loadingScreen').hide(); + $('#statusScreen').show(); + $('#loginScreen').hide(); + }; + + var errorHandler = function (httpObj) { + if (httpObj.status > 300) { + $('#loadingScreen').hide(); + if (httpObj.status === 401) { + $('#signupScreen').hide(); + $('#statusScreen').hide(); + $('#loginScreen').hide(); + $('#loginToYourNAS').prop("href", ed.getNasAuthUrl()); + $(authNASScreen).show(); + $("#nasLogoutBlock").hide(); + } else { + console.error(httpObj); + } + } + }; + + var loginMode = function () { + $('#loadingScreen').hide(); + $('#statusScreen').hide(); + $('#signupScreen').hide(); + $('#loginScreen').show(); + }; + + var infoErrorCheckerScheduled = false; + + var updateInfo = function (response) { + info = {}; + for (var key in response) { + info[key] = response[key]; + } + for (var key in ed) { + info[key] = ed[key]; + } + }; + + var checkInfoError = function (response, forceSchedule) { + forceSchedule = typeof (forceSchedule) !== "undefined" ? forceSchedule : true; + if(isLoggedInMode) { + if (response.isAuthenticated === false) { + $('#statusAlert').html('Login failed: ' + response.authStatus); + $('#statusAlert').addClass("nas-apps-config-form-message-error"); + $('#statusAlert').show(); + } else { + $('#statusAlert').hide(); + } + } + if (response.appStatus === 'error') { + $('#applicationAlert').html(response.errorMessage + '

Please resolve error before continue'); + $('#applicationAlert').show(); + $('#loginBtn').prop('disabled', true); + $('#signupBtn').prop('disabled', true); + $('#inputPeerIP').prop('disabled', true); + $('#inputPeerPort').prop('disabled', true); + $('#inputPeerIPS').prop('disabled', true); + $('#inputPeerPortS').prop('disabled', true); + $('#inputPortConfirm').prop('disabled', true); + } else { + $('#applicationAlert').hide(); + $('#loginBtn').prop('disabled', false); + $('#signupBtn').prop('disabled', false); + $('#inputPeerIP').prop('disabled', false); + $('#inputPeerPort').prop('disabled', false); + $('#inputPeerIPS').prop('disabled', false); + $('#inputPeerPortS').prop('disabled', false); + $('#inputPortConfirm').prop('disabled', false); + } + if(infoErrorCheckerScheduled !== true || forceSchedule === true) { + $.ajax(fillAjax('getself', {})).done(function (response) { + updateInfo(response); + setTimeout(function(){checkInfoError(response, true);}, 1000); + }); + infoErrorCheckerScheduled = true; + } + }; + + var startMode = function () { + $('#loginAlert').hide(); + $('#inputPeerIP').val(''); + $('#inputPeerPort').val(''); + $.ajax(fillAjax('getself', {})).done(function (response) { + if (ed.useAuthNASRichScreen) { + $("#nasLogoutBlock").show(); + $('.nas-user-name').text(ed.getNasUser()); + } + loginMode(); + if (response.size !== 0) { + isLoggedInMode = true; + statusMode(); + } + var self = response; + checkInfoError(response); + $('#version').html((self.build_version !== null ? self.build_version : ' ') + (self.build_name !== null ? (' (' + self.build_name + ')') : '')); + $('.nas-apps-config-form-app-version').show(); + $('#username').text(self.address); + updateInfo(response); + }).fail(errorHandler); + $('#nasLoginAlert').hide(); + $('#nasInputPassword').val(''); + }; + + var waitForEvent = function (eventType, onSuccess, onError, timeout, startTime) { + startTime = typeof (startTime) === "undefined" ? new Date() : startTime; + timeout = typeof (timeout) === "undefined" ? Infinity : timeout; + setTimeout(function () { + if (((new Date()) - startTime) > timeout) { + onError("timeout"); + } else { + $.ajax(fillAjax('events', { + method: 'GET' + })).done(function (response) { + for (var k in response) { + if (response[k].sourceType === eventType) { + $.ajax(fillAjax('event/' + k, { + method: 'DELETE' + })); + if (response[k].type === 'success') + onSuccess(); + else if (response[k].type === 'error') + onError(response[k].errorMessage); + return; + } + } + waitForEvent(eventType, onSuccess, onError, timeout, startTime); + }).fail(function (httpObj) { + if (httpObj.status === 0) { + waitForEvent(eventType, onSuccess, onError, timeout, startTime); + } else { + errorHandler(httpObj); + } + }); + } + }, 500); + }; + + var loginDoneHandler = function () { + function onSuccess() { + //Show logged in screen + $('#username').text(($('#inputPeerIPS').val() === "") ? $('#inputPeerIP').val() : $('#inputPeerIPS').val()); + statusMode(); + $('#statusAlert').html($('#loginSuccess').html()); + $('#statusAlert').removeClass("nas-apps-config-form-message-error"); + $('#statusAlert').show(); + } + function onError(message) { + if (message === "timeout") { + $.ajax(fillAjax('info', {})).done(function (response) { + if (response.isAuthenticated === false) { + waitForEvent("login", onSuccess, onError, 30000); + } else { + onSuccess(); + } + }).fail(errorHandler); + } else { + if (message === "PEER_UNAVAILABLE" || message === "DN_UNRESOLVED") { + message = "Unable to connect to a peer"; + } + $('#loginAlert').html(message); + $('#loginAlert').show(); + loginMode(); + } + } + waitForEvent("login", onSuccess, onError, 30000); + }; + + $('#loginBtn').click(function (e) { + e.preventDefault(); + if ($('#loadingScreen').is(":visible")) return; + $('#inputPeerIP').val($('#inputPeerIP').val().trim()); + if ($('#inputPeerIP').val().length === 0 || $('#inputPeerPort').val().trim().length === 0) { + return; + } + $('#loginAlert').hide(); + loadingMode($('#loginLoadingMessage').text()); + + $.ajax(fillAjax('user', { + method: 'POST', + data: JSON.stringify({user: $('#inputPeerIP').val(), pass: $('#inputPeerPort').val()}) + })).done(loginDoneHandler).statusCode({202: loginDoneHandler}).fail(errorHandler); + }); + + + $("#inputPeerIP, #inputPeerPort").keypress(function (e) { + if (e.which === 13) { + $("#loginBtn").trigger("click"); + } + }); + + $('#logoutBtn').click(function (e) { + e.preventDefault(); + if ($('#loadingScreen').is(":visible")) return; + + loadingMode($('#logoutLoadingMessage').text()); + var doneHandler = function () { + $('#inputPeerIP').val(''); + $('#inputPeerPort').val(''); + setTimeout(function () { + location.reload(); + }, 5000); + }; + $.ajax(fillAjax('user', { + method: 'DELETE' + })).done(doneHandler).statusCode({202: doneHandler}).fail(errorHandler); + }); + + $('#manageBackups').click(function (e) { + e.preventDefault(); + $.ajax(fillAjax('autologin', { + async: false + })).done(function (response) { + var params = response.autologinToken; + params["tab"] = "naswizard"; + params["dname"] = info.devicename; + window.open(info.vaultUrl + "/account/login.aspx?" + $.param(params), "_blank"); + }).fail(errorHandler); + }); + + var currentLocation = function () { + var url = location.href; + // http://xxx// -> http://xxx// + if (url.slice(-1) === "/") { + return url; + // http://xxx//index.html -> http://xxx// + } else if (url.lastIndexOf("/") < url.lastIndexOf(".")) { + return url.substring(0, url.lastIndexOf("/") + 1); + // http://xxx/ -> http://xxx// + } else { + return url + "/"; + } + }; + + $('#logFileBtn').click(function (e) { + e.preventDefault(); + window.open(currentLocation().split("#")[0] + 'log', "_blank"); + }); + + $('#getDiagFileBtn').click(function (e) { + e.preventDefault(); + window.open(currentLocation().split("#")[0] + 'diagnostics.zip'); + }); + + $('#checkPeerBtn').click(function (e) { + e.preventDefault(); + window.open(info.vaultUrl + "/account/check_peer?UName=" + encodeURIComponent($('#inputPeerIP').val().trim()), "_blank"); + }); + + $('#goToSignupBtn').click(function (e) { + e.preventDefault(); + signupMode(); + }); + + $('#signupBackToLoginBtn').click(function (e) { + e.preventDefault(); + startMode(); + }); + + var signupDoneHandler = function () { + waitForEvent("register", loginDoneHandler, function (message) { + $('#loadingScreen').hide(); + $('#signupScreen').show(); + $('#signupAlert').html(message); + $('#signupAlert').show(); + }); + }; + + $('#signupBtn').click(function (e) { + e.preventDefault(); + if ($('#loadingScreen').is(":visible")) return; + + if ($('#inputPeerPortS').val() !== $('#inputPortConfirm').val()) { + $('#signupAlert').text($('#hostFormatError').text()); + $('#signupAlert').show(); + return; + } + + $('#inputPeerIPS').val($('#inputPeerIPS').val().trim()); + loadingMode($('#signupLoadingMessage').text()); + + $.ajax(fillAjax('user', { + method: 'PUT', + data: JSON.stringify({user: $('#inputPeerIPS').val(), pass: $('#inputPeerPortS').val(), c: ed.partnerId}) + })).done(signupDoneHandler).statusCode({202: signupDoneHandler}).fail(errorHandler); + }); //signupBtn.click + + $("#inputPeerIPS, #inputPeerPortS, #inputPortConfirm").keypress(function (e) { + if (e.which === 13) { + $("#signupBtn").trigger("click"); + } + }); + + var nasLoginSuccess = function () { + $('.nas-user-name').text(ed.getNasUser()); + }; + + var nasLoginFailure = function (message) { + //Show notification: IP/DN errors + if (typeof message !== 'undefined') { + $('#nasLoginAlert').html(message); + } else { + $('#nasLoginAlert').text($('#ipOrDNError').text()); + } + $(authNASScreen).show(); + $('#nasInputPassword').val(''); + $('#nasLoginAlert').show(); + $('#loadingScreen').hide(); + }; + + $('#nasLoginBtn').click(function (e) { + e.preventDefault(); + $('#nasInputUser').val($('#nasInputUser').val().trim()); + if ($('#nasInputUser').val().length === 0 || $('#nasInputPassword').val().trim().length === 0) { + return; + } + $('#nasLoginAlert').hide(); + loadingMode($('#loginLoadingMessage').text()); + ed.nasLoginCall(nasLoginSuccess, nasLoginFailure); + }); + + + $("#nasInputUser, #nasInputPassword").keypress(function (e) { + if (e.which === 13) { + $("#nasLoginBtn").trigger("click"); + return false; + } + }); + + $('#nasLogoutBtn').click(function (e) { + e.preventDefault(); + ed.nasLogoutCall(); + $("#nasLogoutBlock").hide(); + window.location.reload(); + }); + + var main = function () { + document.getElementById("dynamic_body").style.display = "block"; + $('title').text(ed.applicationName); + $('.nas-os-name').text(ed.nasOSName); + if ('nasVisitEDWebsiteLogin' in ed) { + $('.nas-visit-ed-website-login').prop("href", ed.nasVisitEDWebsiteLogin); + } else { + $('.nas-visit-ed-website-login').prop("href", basicEDWebsite); + } + if ('nasVisitEDWebsiteSignup' in ed) { + $('.nas-visit-ed-website-signup').prop("href", ed.nasVisitEDWebsiteSignup); + } else { + $('.nas-visit-ed-website-signup').prop("href", basicEDWebsite); + } + if ('nasVisitEDWebsiteLoggedin' in ed) { + $('.nas-visit-ed-website-loggedin').prop("href", ed.nasVisitEDWebsiteLoggedin); + } else { + $('.nas-visit-ed-website-loggedin').prop("href", basicEDWebsite); + } + if ('signingUpMessageHtml' in ed) { + $('#signingUpMessage').html(ed.signingUpMessageHtml); + } + if ('signingUpPropositionMessageHtml' in ed) { + $('#signingUpPropositionMessage').html(ed.signingUpPropositionMessageHtml); + } + if ('useAuthNASRichScreen' in ed && ed.useAuthNASRichScreen) { + authNASScreen = '#authNASRichScreen'; + } else { + ed.useAuthNASRichScreen = false; + } + startMode(); + }; + + main(); + +}); diff --git a/contrib/ui/www/assets/favicon.png b/contrib/ui/www/assets/favicon.png new file mode 100644 index 00000000..1579e261 Binary files /dev/null and b/contrib/ui/www/assets/favicon.png differ diff --git a/contrib/ui/www/assets/jquery.min.js b/contrib/ui/www/assets/jquery.min.js new file mode 100644 index 00000000..006e9531 --- /dev/null +++ b/contrib/ui/www/assets/jquery.min.js @@ -0,0 +1,5 @@ +/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery.min.map +*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEvent("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}catch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
a",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compatMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="
t
",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"removeClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:function(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i||o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj; +return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cacheLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}function at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelectorAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="
",f.insertBefore(e,f.firstChild);var t=n.getElementsByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).length;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatchesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T.detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsupported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]}),even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.pseudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:b.support.htmlSerialize?[0,"",""]:[1,"X
","
"]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(this[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l) +}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];return n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("