Initial commit
This commit is contained in:
132
application/controller/base.go
Normal file
132
application/controller/base.go
Normal file
@@ -0,0 +1,132 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/niruix/sshwifty/application/log"
|
||||
)
|
||||
|
||||
// Error
|
||||
var (
|
||||
ErrControllerNotImplemented = NewError(
|
||||
http.StatusNotImplemented, "Server does not know how to handle the "+
|
||||
"request")
|
||||
)
|
||||
|
||||
type controller interface {
|
||||
Get(w http.ResponseWriter, r *http.Request, l log.Logger) error
|
||||
Head(w http.ResponseWriter, r *http.Request, l log.Logger) error
|
||||
Post(w http.ResponseWriter, r *http.Request, l log.Logger) error
|
||||
Put(w http.ResponseWriter, r *http.Request, l log.Logger) error
|
||||
Delete(w http.ResponseWriter, r *http.Request, l log.Logger) error
|
||||
Connect(w http.ResponseWriter, r *http.Request, l log.Logger) error
|
||||
Options(w http.ResponseWriter, r *http.Request, l log.Logger) error
|
||||
Trace(w http.ResponseWriter, r *http.Request, l log.Logger) error
|
||||
Patch(w http.ResponseWriter, r *http.Request, l log.Logger) error
|
||||
Other(
|
||||
method string,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
l log.Logger,
|
||||
) error
|
||||
}
|
||||
|
||||
type baseController struct{}
|
||||
|
||||
func (b baseController) Get(
|
||||
w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
return ErrControllerNotImplemented
|
||||
}
|
||||
|
||||
func (b baseController) Head(
|
||||
w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
return ErrControllerNotImplemented
|
||||
}
|
||||
|
||||
func (b baseController) Post(
|
||||
w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
return ErrControllerNotImplemented
|
||||
}
|
||||
|
||||
func (b baseController) Put(
|
||||
w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
return ErrControllerNotImplemented
|
||||
}
|
||||
|
||||
func (b baseController) Delete(
|
||||
w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
return ErrControllerNotImplemented
|
||||
}
|
||||
|
||||
func (b baseController) Connect(
|
||||
w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
return ErrControllerNotImplemented
|
||||
}
|
||||
|
||||
func (b baseController) Options(
|
||||
w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
return ErrControllerNotImplemented
|
||||
}
|
||||
|
||||
func (b baseController) Trace(
|
||||
w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
return ErrControllerNotImplemented
|
||||
}
|
||||
|
||||
func (b baseController) Patch(
|
||||
w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
return ErrControllerNotImplemented
|
||||
}
|
||||
|
||||
func (b baseController) Other(
|
||||
method string, w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
return ErrControllerNotImplemented
|
||||
}
|
||||
|
||||
func serveController(
|
||||
c controller,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
l log.Logger,
|
||||
) error {
|
||||
switch strings.ToUpper(r.Method) {
|
||||
case "GET":
|
||||
return c.Get(w, r, l)
|
||||
case "HEAD":
|
||||
return c.Head(w, r, l)
|
||||
case "POST":
|
||||
return c.Post(w, r, l)
|
||||
case "PUT":
|
||||
return c.Put(w, r, l)
|
||||
case "DELETE":
|
||||
return c.Delete(w, r, l)
|
||||
case "CONNECT":
|
||||
return c.Connect(w, r, l)
|
||||
case "OPTIONS":
|
||||
return c.Options(w, r, l)
|
||||
case "TRACE":
|
||||
return c.Trace(w, r, l)
|
||||
case "PATCH":
|
||||
return c.Patch(w, r, l)
|
||||
default:
|
||||
return c.Other(r.Method, w, r, l)
|
||||
}
|
||||
}
|
||||
75
application/controller/common.go
Normal file
75
application/controller/common.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func clientSupportGZIP(r *http.Request) bool {
|
||||
// Should be good enough
|
||||
return strings.Contains(r.Header.Get("Accept-Encoding"), "gzip")
|
||||
}
|
||||
|
||||
func clientContentEtagIsValid(r *http.Request, eTag string) bool {
|
||||
d := r.Header.Get("If-None-Match")
|
||||
|
||||
if len(d) < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
dStart := 0
|
||||
qETag := "\"" + eTag + "\""
|
||||
|
||||
for {
|
||||
dIdx := strings.Index(d[dStart:], ",")
|
||||
|
||||
if dIdx < 0 {
|
||||
return strings.Contains(d[dStart:], qETag) ||
|
||||
strings.Contains(d[dStart:], "*")
|
||||
}
|
||||
|
||||
if strings.Contains(d[dStart:dStart+dIdx], qETag) {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.Contains(d[dStart:dStart+dIdx], "*") {
|
||||
return true
|
||||
}
|
||||
|
||||
dStart += dIdx + 1
|
||||
}
|
||||
}
|
||||
|
||||
func clientContentModifiedSince(r *http.Request, mod time.Time) bool {
|
||||
d := r.Header.Get("If-Modified-Since")
|
||||
|
||||
if len(d) < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
dt, dtErr := time.Parse(time.RFC1123, d)
|
||||
|
||||
if dtErr != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return !mod.Before(dt)
|
||||
}
|
||||
47
application/controller/common_test.go
Normal file
47
application/controller/common_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestClientContentEtagIsValid(t *testing.T) {
|
||||
test := func(id int, hd []string, etag string, expected bool) {
|
||||
r := http.Request{
|
||||
Header: http.Header{
|
||||
"If-None-Match": hd,
|
||||
},
|
||||
}
|
||||
rr := clientContentEtagIsValid(&r, etag)
|
||||
|
||||
if rr != expected {
|
||||
t.Errorf("Test: %d: Expecting the result to be %v, got %v instead",
|
||||
id, expected, rr)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
test(0, []string{""}, "test", false)
|
||||
test(1, []string{"*"}, "test", true)
|
||||
test(2, []string{"W/\"67ab43\", \"54ed21\", \"7892dd\""}, "54ed21", true)
|
||||
test(3, []string{"\"bfc13a64729c4290ef5b2c2730249c88ca92d82d\""},
|
||||
"bfc13a64729c4290ef5b2c2730249c88ca92d82d", true)
|
||||
}
|
||||
134
application/controller/controller.go
Normal file
134
application/controller/controller.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/niruix/sshwifty/application/command"
|
||||
"github.com/niruix/sshwifty/application/configuration"
|
||||
"github.com/niruix/sshwifty/application/log"
|
||||
"github.com/niruix/sshwifty/application/server"
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrNotFound = NewError(
|
||||
http.StatusNotFound, "Page not found")
|
||||
)
|
||||
|
||||
// handler is the main service dispatcher
|
||||
type handler struct {
|
||||
hostNameChecker string
|
||||
commonCfg configuration.Common
|
||||
logger log.Logger
|
||||
homeCtl home
|
||||
socketCtl socket
|
||||
}
|
||||
|
||||
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
|
||||
clientLogger := h.logger.Context("Client (%s)", r.RemoteAddr)
|
||||
|
||||
if len(h.commonCfg.HostName) > 0 {
|
||||
hostPort := r.Host
|
||||
|
||||
if len(hostPort) <= 0 {
|
||||
hostPort = r.URL.Host
|
||||
}
|
||||
|
||||
if h.commonCfg.HostName != hostPort &&
|
||||
!strings.HasPrefix(hostPort, h.hostNameChecker) {
|
||||
clientLogger.Warning("Request invalid host \"%s\", deined",
|
||||
r.Host)
|
||||
|
||||
serveFailure(
|
||||
NewError(http.StatusForbidden, "Invalid host"), w, r, h.logger)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Add("Date", time.Now().UTC().Format(time.RFC1123))
|
||||
|
||||
switch r.URL.Path {
|
||||
case "/":
|
||||
err = serveController(h.homeCtl, w, r, clientLogger)
|
||||
|
||||
case "/socket":
|
||||
err = serveController(h.socketCtl, w, r, clientLogger)
|
||||
|
||||
case "/robots.txt":
|
||||
fallthrough
|
||||
case "/favicon.ico":
|
||||
fallthrough
|
||||
case "/README.md":
|
||||
fallthrough
|
||||
case "/LICENSE.md":
|
||||
fallthrough
|
||||
case "/DEPENDENCES.md":
|
||||
err = serveStaticData(r.URL.Path[1:], w, r, clientLogger)
|
||||
|
||||
default:
|
||||
if strings.HasPrefix(r.URL.Path, "/assets/") {
|
||||
err = serveStaticData(r.URL.Path[8:], w, r, clientLogger)
|
||||
} else {
|
||||
err = ErrNotFound
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
clientLogger.Info("Request completed: %s", r.URL.String())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
clientLogger.Warning("Request ended with error: %s: %s",
|
||||
r.URL.String(), err)
|
||||
|
||||
controllerErr, isControllerErr := err.(Error)
|
||||
|
||||
if isControllerErr {
|
||||
serveFailure(controllerErr, w, r, h.logger)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
serveFailure(
|
||||
NewError(http.StatusInternalServerError, err.Error()), w, r, h.logger)
|
||||
}
|
||||
|
||||
// Builder returns a http controller builder
|
||||
func Builder(cmds command.Commands) server.HandlerBuilder {
|
||||
return func(
|
||||
commonCfg configuration.Common,
|
||||
cfg configuration.Server,
|
||||
logger log.Logger,
|
||||
) http.Handler {
|
||||
return handler{
|
||||
hostNameChecker: commonCfg.HostName + ":",
|
||||
commonCfg: commonCfg,
|
||||
logger: logger,
|
||||
homeCtl: home{},
|
||||
socketCtl: newSocketCtl(commonCfg, cfg, cmds),
|
||||
}
|
||||
}
|
||||
}
|
||||
44
application/controller/error.go
Normal file
44
application/controller/error.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package controller
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Error Controller error
|
||||
type Error struct {
|
||||
code int
|
||||
message string
|
||||
}
|
||||
|
||||
// NewError creates a new Error
|
||||
func NewError(code int, message string) Error {
|
||||
return Error{
|
||||
code: code,
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// Code return the error code
|
||||
func (f Error) Code() int {
|
||||
return f.code
|
||||
}
|
||||
|
||||
// Error returns the error message
|
||||
func (f Error) Error() string {
|
||||
return fmt.Sprintf("HTTP Error (%d): %s", f.code, f.message)
|
||||
}
|
||||
35
application/controller/failure.go
Normal file
35
application/controller/failure.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/niruix/sshwifty/application/log"
|
||||
)
|
||||
|
||||
func serveFailure(
|
||||
err Error,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
l log.Logger,
|
||||
) error {
|
||||
w.WriteHeader(err.Code())
|
||||
|
||||
return serveStaticPage("error.html", w, r, l)
|
||||
}
|
||||
33
application/controller/home.go
Normal file
33
application/controller/home.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/niruix/sshwifty/application/log"
|
||||
)
|
||||
|
||||
// home controller
|
||||
type home struct {
|
||||
baseController
|
||||
}
|
||||
|
||||
func (h home) Get(w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
return serveStaticPage("index.html", w, r, l)
|
||||
}
|
||||
444
application/controller/socket.go
Normal file
444
application/controller/socket.go
Normal file
@@ -0,0 +1,444 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/niruix/sshwifty/application/command"
|
||||
"github.com/niruix/sshwifty/application/configuration"
|
||||
"github.com/niruix/sshwifty/application/log"
|
||||
"github.com/niruix/sshwifty/application/rw"
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrSocketAuthFailed = NewError(
|
||||
http.StatusForbidden,
|
||||
"To use Websocket interface, a valid Auth Key must be provided")
|
||||
|
||||
ErrSocketUnableToGenerateKey = NewError(
|
||||
http.StatusInternalServerError,
|
||||
"Unable to generate crypto key")
|
||||
|
||||
ErrSocketInvalidDataPackage = NewError(
|
||||
http.StatusBadRequest, "Invalid data package")
|
||||
)
|
||||
|
||||
const (
|
||||
socketGCMStandardNonceSize = 12
|
||||
)
|
||||
|
||||
type socket struct {
|
||||
baseController
|
||||
|
||||
commonCfg configuration.Common
|
||||
serverCfg configuration.Server
|
||||
randomKey string
|
||||
authKey []byte
|
||||
upgrader websocket.Upgrader
|
||||
commander command.Commander
|
||||
}
|
||||
|
||||
func getNewSocketCtlRandomSharedKey() string {
|
||||
b := [32]byte{}
|
||||
|
||||
io.ReadFull(rand.Reader, b[:])
|
||||
|
||||
return base64.StdEncoding.EncodeToString(b[:])
|
||||
}
|
||||
|
||||
func getSocketAuthKey(randomKey string, sharedKey string) []byte {
|
||||
var k []byte
|
||||
|
||||
if len(sharedKey) > 0 {
|
||||
k = []byte(sharedKey)
|
||||
} else {
|
||||
k = []byte(randomKey)
|
||||
}
|
||||
|
||||
h := hmac.New(sha512.New, k)
|
||||
|
||||
h.Write([]byte(randomKey))
|
||||
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func newSocketCtl(
|
||||
commonCfg configuration.Common,
|
||||
cfg configuration.Server,
|
||||
cmds command.Commands,
|
||||
) socket {
|
||||
randomKey := getNewSocketCtlRandomSharedKey()
|
||||
|
||||
return socket{
|
||||
commonCfg: commonCfg,
|
||||
serverCfg: cfg,
|
||||
randomKey: randomKey,
|
||||
authKey: getSocketAuthKey(randomKey, commonCfg.SharedKey)[:32],
|
||||
upgrader: buildWebsocketUpgrader(cfg),
|
||||
commander: command.New(cmds),
|
||||
}
|
||||
}
|
||||
|
||||
type websocketWriter struct {
|
||||
*websocket.Conn
|
||||
}
|
||||
|
||||
func (w websocketWriter) Write(b []byte) (int, error) {
|
||||
wErr := w.WriteMessage(websocket.BinaryMessage, b)
|
||||
|
||||
if wErr != nil {
|
||||
return 0, wErr
|
||||
}
|
||||
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
type socketPackageWriter struct {
|
||||
w websocketWriter
|
||||
packager func(w websocketWriter, b []byte) error
|
||||
}
|
||||
|
||||
func (s socketPackageWriter) Write(b []byte) (int, error) {
|
||||
packageWriteErr := s.packager(s.w, b)
|
||||
|
||||
if packageWriteErr != nil {
|
||||
return 0, packageWriteErr
|
||||
}
|
||||
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func buildWebsocketUpgrader(cfg configuration.Server) websocket.Upgrader {
|
||||
return websocket.Upgrader{
|
||||
HandshakeTimeout: cfg.InitialTimeout,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
Error: func(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
status int,
|
||||
reason error,
|
||||
) {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s socket) Options(
|
||||
w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
w.Header().Add("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Add("Access-Control-Allow-Headers", "X-Key")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s socket) setServerConfigHeader(hd *http.Header) {
|
||||
hd.Add("X-Heartbeat",
|
||||
strconv.FormatFloat(s.serverCfg.HeartbeatTimeout.Seconds(), 'g', 2, 64))
|
||||
hd.Add("X-Timeout",
|
||||
strconv.FormatFloat(s.serverCfg.ReadTimeout.Seconds(), 'g', 2, 64))
|
||||
}
|
||||
|
||||
func (s socket) Head(
|
||||
w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
key := r.Header.Get("X-Key")
|
||||
hd := w.Header()
|
||||
|
||||
if len(key) <= 0 {
|
||||
hd.Add("X-Key", s.randomKey)
|
||||
|
||||
if len(s.commonCfg.SharedKey) <= 0 {
|
||||
s.setServerConfigHeader(&hd)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return ErrSocketAuthFailed
|
||||
}
|
||||
|
||||
if len(key) > 64 {
|
||||
return ErrSocketAuthFailed
|
||||
}
|
||||
|
||||
decodedKey, decodedKeyErr := base64.StdEncoding.DecodeString(key)
|
||||
|
||||
if decodedKeyErr != nil {
|
||||
return NewError(http.StatusBadRequest, decodedKeyErr.Error())
|
||||
}
|
||||
|
||||
if !hmac.Equal(s.authKey, decodedKey) {
|
||||
return ErrSocketAuthFailed
|
||||
}
|
||||
|
||||
hd.Add("X-Key", s.randomKey)
|
||||
s.setServerConfigHeader(&hd)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s socket) buildWSFetcher(c *websocket.Conn) rw.FetchReaderFetcher {
|
||||
return func() ([]byte, error) {
|
||||
for {
|
||||
mt, message, err := c.ReadMessage()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if mt != websocket.BinaryMessage {
|
||||
return nil, NewError(
|
||||
http.StatusBadRequest,
|
||||
fmt.Sprintf("Received unknown type of data: %d", message))
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s socket) generateNonce(nonce []byte) error {
|
||||
_, rErr := io.ReadFull(rand.Reader, nonce[:socketGCMStandardNonceSize])
|
||||
|
||||
return rErr
|
||||
}
|
||||
|
||||
func (s socket) increaseNonce(nonce []byte) {
|
||||
for i := len(nonce); i > 0; i-- {
|
||||
nonce[i-1]++
|
||||
|
||||
if nonce[i-1] <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func (s socket) createCipher(key []byte) (cipher.AEAD, cipher.AEAD, error) {
|
||||
readCipher, readCipherErr := aes.NewCipher(key)
|
||||
|
||||
if readCipherErr != nil {
|
||||
return nil, nil, readCipherErr
|
||||
}
|
||||
|
||||
writeCipher, writeCipherErr := aes.NewCipher(key)
|
||||
|
||||
if writeCipherErr != nil {
|
||||
return nil, nil, writeCipherErr
|
||||
}
|
||||
|
||||
gcmRead, gcmReadErr := cipher.NewGCMWithNonceSize(
|
||||
readCipher, socketGCMStandardNonceSize)
|
||||
|
||||
if gcmReadErr != nil {
|
||||
return nil, nil, gcmReadErr
|
||||
}
|
||||
|
||||
gcmWrite, gcmWriteErr := cipher.NewGCMWithNonceSize(
|
||||
writeCipher, socketGCMStandardNonceSize)
|
||||
|
||||
if gcmWriteErr != nil {
|
||||
return nil, nil, gcmWriteErr
|
||||
}
|
||||
|
||||
return gcmRead, gcmWrite, nil
|
||||
}
|
||||
|
||||
func (s socket) privateKey() string {
|
||||
if len(s.commonCfg.SharedKey) > 0 {
|
||||
return s.commonCfg.SharedKey
|
||||
}
|
||||
|
||||
return s.randomKey
|
||||
}
|
||||
|
||||
func (s socket) buildCipherKey() [24]byte {
|
||||
key := [24]byte{}
|
||||
now := strconv.FormatInt(time.Now().Unix()/100, 10)
|
||||
|
||||
copy(key[:], getSocketAuthKey(now, s.privateKey()))
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
func (s socket) Get(
|
||||
w http.ResponseWriter, r *http.Request, l log.Logger) error {
|
||||
// Error will not be returned when Websocket already handled
|
||||
// (i.e. returned the error to client). We just log the error and that's it
|
||||
c, err := s.upgrader.Upgrade(w, r, nil)
|
||||
|
||||
if err != nil {
|
||||
return NewError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
defer c.Close()
|
||||
|
||||
wsReader := rw.NewFetchReader(s.buildWSFetcher(c))
|
||||
wsWriter := websocketWriter{Conn: c}
|
||||
|
||||
// Initialize ciphers
|
||||
readNonce := [socketGCMStandardNonceSize]byte{}
|
||||
_, nonceReadErr := io.ReadFull(&wsReader, readNonce[:])
|
||||
|
||||
if nonceReadErr != nil {
|
||||
return NewError(http.StatusBadRequest, fmt.Sprintf(
|
||||
"Unable to read initial client nonce: %s", nonceReadErr.Error()))
|
||||
}
|
||||
|
||||
writeNonce := [socketGCMStandardNonceSize]byte{}
|
||||
nonceReadErr = s.generateNonce(writeNonce[:])
|
||||
|
||||
if nonceReadErr != nil {
|
||||
return NewError(http.StatusBadRequest, fmt.Sprintf(
|
||||
"Unable to generate initial server nonce: %s",
|
||||
nonceReadErr.Error()))
|
||||
}
|
||||
|
||||
_, nonceSendErr := wsWriter.Write(writeNonce[:])
|
||||
|
||||
if nonceSendErr != nil {
|
||||
return NewError(http.StatusBadRequest, fmt.Sprintf(
|
||||
"Unable to send server nonce to client: %s", nonceSendErr.Error()))
|
||||
}
|
||||
|
||||
cipherKey := s.buildCipherKey()
|
||||
|
||||
readCipher, writeCipher, cipherCreationErr := s.createCipher(cipherKey[:])
|
||||
|
||||
if cipherCreationErr != nil {
|
||||
return NewError(http.StatusInternalServerError, fmt.Sprintf(
|
||||
"Unable to create cipher: %s", cipherCreationErr.Error()))
|
||||
}
|
||||
|
||||
// Start service
|
||||
const cipherReadBufSize = 1024
|
||||
|
||||
cipherReadBuf := [cipherReadBufSize]byte{}
|
||||
cipherWriteBuf := [cipherReadBufSize]byte{}
|
||||
|
||||
maxWriteLen := cipherReadBufSize - (writeCipher.Overhead() + 2)
|
||||
|
||||
senderLock := sync.Mutex{}
|
||||
cmdExec, cmdExecErr := s.commander.New(
|
||||
s.commonCfg.Dialer, rw.NewFetchReader(func() ([]byte, error) {
|
||||
defer s.increaseNonce(readNonce[:])
|
||||
|
||||
// Size is unencrypted
|
||||
_, rErr := io.ReadFull(&wsReader, cipherReadBuf[:2])
|
||||
|
||||
if rErr != nil {
|
||||
return nil, rErr
|
||||
}
|
||||
|
||||
// Read full size
|
||||
packageSize := uint16(cipherReadBuf[0])
|
||||
packageSize <<= 8
|
||||
packageSize |= uint16(cipherReadBuf[1])
|
||||
|
||||
if packageSize <= 0 {
|
||||
return nil, ErrSocketInvalidDataPackage
|
||||
}
|
||||
|
||||
if int(packageSize) <= wsReader.Remain() {
|
||||
rData, rErr := wsReader.Export(int(packageSize))
|
||||
|
||||
if rErr != nil {
|
||||
return nil, rErr
|
||||
}
|
||||
|
||||
return readCipher.Open(rData[:0], readNonce[:], rData, nil)
|
||||
}
|
||||
|
||||
if packageSize > cipherReadBufSize {
|
||||
return nil, ErrSocketInvalidDataPackage
|
||||
}
|
||||
|
||||
_, rErr = io.ReadFull(&wsReader, cipherReadBuf[:packageSize])
|
||||
|
||||
if rErr != nil {
|
||||
return nil, rErr
|
||||
}
|
||||
|
||||
return readCipher.Open(
|
||||
cipherReadBuf[:0],
|
||||
readNonce[:],
|
||||
cipherReadBuf[:packageSize],
|
||||
nil)
|
||||
}), socketPackageWriter{
|
||||
w: wsWriter,
|
||||
packager: func(w websocketWriter, b []byte) error {
|
||||
start := 0
|
||||
bLen := len(b)
|
||||
readLen := bLen
|
||||
|
||||
for start < bLen {
|
||||
if readLen > maxWriteLen {
|
||||
readLen = maxWriteLen
|
||||
}
|
||||
|
||||
encrypted := writeCipher.Seal(
|
||||
cipherWriteBuf[2:2],
|
||||
writeNonce[:],
|
||||
b[start:start+readLen],
|
||||
nil)
|
||||
|
||||
s.increaseNonce(writeNonce[:])
|
||||
|
||||
encryptedSize := uint16(len(encrypted))
|
||||
|
||||
if encryptedSize <= 0 {
|
||||
return ErrSocketInvalidDataPackage
|
||||
}
|
||||
|
||||
cipherWriteBuf[0] = byte(encryptedSize >> 8)
|
||||
cipherWriteBuf[1] = byte(encryptedSize)
|
||||
|
||||
_, wErr := w.Write(cipherWriteBuf[:encryptedSize+2])
|
||||
|
||||
if wErr != nil {
|
||||
return wErr
|
||||
}
|
||||
|
||||
start += readLen
|
||||
readLen = bLen - start
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}, &senderLock, s.serverCfg.ReadDelay, s.serverCfg.WriteDelay, l)
|
||||
|
||||
if cmdExecErr != nil {
|
||||
return NewError(http.StatusBadRequest, cmdExecErr.Error())
|
||||
}
|
||||
|
||||
return cmdExec.Handle()
|
||||
}
|
||||
154
application/controller/static.go
Normal file
154
application/controller/static.go
Normal file
@@ -0,0 +1,154 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"mime"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/niruix/sshwifty/application/log"
|
||||
)
|
||||
|
||||
type staticData struct {
|
||||
data []byte
|
||||
dataHash string
|
||||
compressd []byte
|
||||
compressdHash string
|
||||
created time.Time
|
||||
}
|
||||
|
||||
func (s staticData) hasCompressed() bool {
|
||||
return len(s.compressd) > 0
|
||||
}
|
||||
|
||||
func staticFileExt(fileName string) string {
|
||||
extIdx := strings.LastIndex(fileName, ".")
|
||||
|
||||
if extIdx < 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.ToLower(fileName[extIdx:])
|
||||
}
|
||||
|
||||
func serveStaticPage(
|
||||
dataName string,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
l log.Logger,
|
||||
) error {
|
||||
d, dFound := staticPages[dataName]
|
||||
|
||||
if !dFound {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
selectedData := d.data
|
||||
|
||||
if clientSupportGZIP(r) && d.hasCompressed() {
|
||||
selectedData = d.compressd
|
||||
|
||||
w.Header().Add("Vary", "Accept-Encoding")
|
||||
w.Header().Add("Content-Encoding", "gzip")
|
||||
}
|
||||
|
||||
mimeType := mime.TypeByExtension(staticFileExt(dataName))
|
||||
|
||||
if len(mimeType) > 0 {
|
||||
w.Header().Add("Content-Type", mimeType)
|
||||
} else {
|
||||
w.Header().Add("Content-Type", "application/binary")
|
||||
}
|
||||
|
||||
_, wErr := w.Write(selectedData)
|
||||
|
||||
return wErr
|
||||
}
|
||||
|
||||
func serveStaticData(
|
||||
dataName string,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
l log.Logger,
|
||||
) error {
|
||||
if strings.ToUpper(r.Method) != "GET" {
|
||||
return ErrControllerNotImplemented
|
||||
}
|
||||
|
||||
fileExt := staticFileExt(dataName)
|
||||
|
||||
if fileExt == ".html" || fileExt == ".htm" {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
d, dFound := staticPages[dataName]
|
||||
|
||||
if !dFound {
|
||||
return ErrNotFound
|
||||
}
|
||||
|
||||
selectedData := d.data
|
||||
selectedDataHash := d.dataHash
|
||||
compressEnabled := false
|
||||
|
||||
if clientSupportGZIP(r) && d.hasCompressed() {
|
||||
selectedData = d.compressd
|
||||
selectedDataHash = d.compressdHash
|
||||
|
||||
compressEnabled = true
|
||||
|
||||
w.Header().Add("Vary", "Accept-Encoding")
|
||||
}
|
||||
|
||||
canUseCache := true
|
||||
|
||||
if !clientContentEtagIsValid(r, selectedDataHash) {
|
||||
canUseCache = false
|
||||
}
|
||||
|
||||
if clientContentModifiedSince(r, d.created) {
|
||||
canUseCache = false
|
||||
}
|
||||
|
||||
if canUseCache {
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
w.Header().Add("Cache-Control", "public, max-age=31536000")
|
||||
w.Header().Add("ETag", "\""+selectedDataHash+"\"")
|
||||
|
||||
mimeType := mime.TypeByExtension(fileExt)
|
||||
|
||||
if len(mimeType) > 0 {
|
||||
w.Header().Add("Content-Type", mimeType)
|
||||
} else {
|
||||
w.Header().Add("Content-Type", "application/binary")
|
||||
}
|
||||
|
||||
if compressEnabled {
|
||||
w.Header().Add("Content-Encoding", "gzip")
|
||||
}
|
||||
|
||||
_, wErr := w.Write([]byte(selectedData))
|
||||
|
||||
return wErr
|
||||
}
|
||||
457
application/controller/static_page_generater/main.go
Normal file
457
application/controller/static_page_generater/main.go
Normal file
@@ -0,0 +1,457 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
parentPackage = "github.com/niruix/sshwifty/application/controller"
|
||||
)
|
||||
|
||||
const (
|
||||
staticListTemplate = `import "time"
|
||||
|
||||
var (
|
||||
staticPages = map[string]staticData{
|
||||
{{ range . }}"{{ .Name }}":
|
||||
parseStaticData({{ .GOPackage }}.{{ .GOVariableName }}()),
|
||||
{{ end }}
|
||||
}
|
||||
)
|
||||
|
||||
// parseStaticData parses result from a static file returner and generate
|
||||
// a new ` + "`" + `staticData` + "`" + ` item
|
||||
func parseStaticData(
|
||||
fileStart int,
|
||||
fileEnd int,
|
||||
compressedStart int,
|
||||
compressedEnd int,
|
||||
contentHash string,
|
||||
compressedHash string,
|
||||
creation time.Time,
|
||||
data []byte,
|
||||
) staticData {
|
||||
return staticData{
|
||||
data: data[fileStart:fileEnd],
|
||||
dataHash: contentHash,
|
||||
compressd: data[compressedStart:compressedEnd],
|
||||
compressdHash: compressedHash,
|
||||
created: creation,
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
staticListTemplateDev = `import "io/ioutil"
|
||||
import "bytes"
|
||||
import "fmt"
|
||||
import "compress/gzip"
|
||||
import "encoding/base64"
|
||||
import "time"
|
||||
import "crypto/sha256"
|
||||
|
||||
// WARNING: THIS GENERATION IS FOR DEBUG / DEVELOPMENT ONLY, DO NOT
|
||||
// USE IT IN PRODUCTION!
|
||||
|
||||
func staticFileGen(filePath string) staticData {
|
||||
content, readErr := ioutil.ReadFile(filePath)
|
||||
|
||||
if readErr != nil {
|
||||
panic(fmt.Sprintln("Cannot read file:", readErr))
|
||||
}
|
||||
|
||||
compressed := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
|
||||
compresser, compresserBuildErr := gzip.NewWriterLevel(
|
||||
compressed, gzip.BestCompression)
|
||||
|
||||
if compresserBuildErr != nil {
|
||||
panic(fmt.Sprintln("Cannot build data compresser:", compresserBuildErr))
|
||||
}
|
||||
|
||||
contentLen := len(content)
|
||||
|
||||
_, compressErr := compresser.Write(content)
|
||||
|
||||
if compressErr != nil {
|
||||
panic(fmt.Sprintln("Cannot write compressed data:", compressErr))
|
||||
}
|
||||
|
||||
compressErr = compresser.Flush()
|
||||
|
||||
if compressErr != nil {
|
||||
panic(fmt.Sprintln("Cannot write compressed data:", compressErr))
|
||||
}
|
||||
|
||||
content = append(content, compressed.Bytes()...)
|
||||
|
||||
getHash := func(b []byte) []byte {
|
||||
h := sha256.New()
|
||||
h.Write(b)
|
||||
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
return staticData{
|
||||
data: content[0:contentLen],
|
||||
dataHash: base64.StdEncoding.EncodeToString(
|
||||
getHash(content[0:contentLen])[:8]),
|
||||
compressd: content[contentLen:],
|
||||
compressdHash: base64.StdEncoding.EncodeToString(
|
||||
getHash(content[contentLen:])[:8]),
|
||||
created: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
staticPages = map[string]staticData{
|
||||
{{ range . }}"{{ .Name }}": staticFileGen(
|
||||
"{{ .Path }}",
|
||||
),
|
||||
{{ end }}
|
||||
}
|
||||
)`
|
||||
|
||||
staticPageTemplate = `package {{ .GOPackage }}
|
||||
|
||||
// This file is part of Sshwifty Project
|
||||
//
|
||||
// Copyright (C) {{ .Date.Year }} Rui NI (nirui@gmx.com)
|
||||
//
|
||||
// https://github.com/niruix/sshwifty
|
||||
//
|
||||
// This file is generated at {{ .Date.Format "Mon, 02 Jan 2006 15:04:05 MST" }}
|
||||
// by "go generate", DO NOT EDIT! Also, do not open this file, it maybe too large
|
||||
// for your editor. You've been warned.
|
||||
//
|
||||
// This file may contain third-party binaries. See DEPENDENCES for detail.
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
var raw{{ .GOVariableName }}Data = ` + "`" + `{{ .Data }}` + "`" + `
|
||||
|
||||
// {{ .GOVariableName }} returns static file
|
||||
func {{ .GOVariableName }}() (
|
||||
int, // FileStart
|
||||
int, // FileEnd
|
||||
int, // CompressedStart
|
||||
int, // CompressedEnd
|
||||
string, // ContentHash
|
||||
string, // CompressedHash
|
||||
time.Time, // Time of creation
|
||||
[]byte, // Data
|
||||
) {
|
||||
created, createErr := time.Parse(
|
||||
time.RFC1123, "{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 MST" }}")
|
||||
|
||||
if createErr != nil {
|
||||
panic(createErr)
|
||||
}
|
||||
|
||||
data, dataErr := hex.DecodeString(raw{{ .GOVariableName }}Data)
|
||||
|
||||
raw{{ .GOVariableName }}Data = ""
|
||||
|
||||
if dataErr != nil {
|
||||
panic(dataErr)
|
||||
}
|
||||
|
||||
return {{ .FileStart }}, {{ .FileEnd }},
|
||||
{{ .CompressedStart }}, {{ .CompressedEnd }},
|
||||
"{{ .ContentHash }}", "{{ .CompressedHash }}", created, data
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
const (
|
||||
templateStarts = "//go:generate"
|
||||
)
|
||||
|
||||
type parsedFile struct {
|
||||
Name string
|
||||
GOVariableName string
|
||||
GOFileName string
|
||||
GOPackage string
|
||||
Path string
|
||||
Data string
|
||||
FileStart int
|
||||
FileEnd int
|
||||
CompressedStart int
|
||||
CompressedEnd int
|
||||
ContentHash string
|
||||
CompressedHash string
|
||||
Date time.Time
|
||||
}
|
||||
|
||||
func getHash(b []byte) []byte {
|
||||
h := sha256.New()
|
||||
h.Write(b)
|
||||
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func buildListFile(w io.Writer, data interface{}) error {
|
||||
tpl := template.Must(template.New(
|
||||
"StaticPageList").Parse(staticListTemplate))
|
||||
|
||||
return tpl.Execute(w, data)
|
||||
}
|
||||
|
||||
func buildListFileDev(w io.Writer, data interface{}) error {
|
||||
tpl := template.Must(template.New(
|
||||
"StaticPageList").Parse(staticListTemplateDev))
|
||||
|
||||
return tpl.Execute(w, data)
|
||||
}
|
||||
|
||||
func buildDataFile(w io.Writer, data interface{}) error {
|
||||
tpl := template.Must(template.New(
|
||||
"StaticPageData").Parse(staticPageTemplate))
|
||||
|
||||
return tpl.Execute(w, data)
|
||||
}
|
||||
|
||||
func byteToArrayStr(b []byte) string {
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
func parseFile(
|
||||
id int, name string, filePath string, packageName string) parsedFile {
|
||||
content, readErr := ioutil.ReadFile(filePath)
|
||||
|
||||
if readErr != nil {
|
||||
panic(fmt.Sprintln("Cannot read file:", readErr))
|
||||
}
|
||||
|
||||
compressed := bytes.NewBuffer(make([]byte, 0, 1024))
|
||||
|
||||
compresser, compresserBuildErr := gzip.NewWriterLevel(
|
||||
compressed, gzip.BestCompression)
|
||||
|
||||
if compresserBuildErr != nil {
|
||||
panic(fmt.Sprintln("Cannot build data compresser:", compresserBuildErr))
|
||||
}
|
||||
|
||||
contentLen := len(content)
|
||||
|
||||
_, compressErr := compresser.Write(content)
|
||||
|
||||
if compressErr != nil {
|
||||
panic(fmt.Sprintln("Cannot write compressed data:", compressErr))
|
||||
}
|
||||
|
||||
compressErr = compresser.Flush()
|
||||
|
||||
if compressErr != nil {
|
||||
panic(fmt.Sprintln("Cannot write compressed data:", compressErr))
|
||||
}
|
||||
|
||||
content = append(content, compressed.Bytes()...)
|
||||
|
||||
goFileName := "Static" + strconv.FormatInt(int64(id), 10)
|
||||
|
||||
return parsedFile{
|
||||
Name: name,
|
||||
GOVariableName: strings.Title(goFileName),
|
||||
GOFileName: strings.ToLower(goFileName) + "_generated.go",
|
||||
GOPackage: packageName,
|
||||
Path: filePath,
|
||||
Data: byteToArrayStr(content),
|
||||
FileStart: 0,
|
||||
FileEnd: contentLen,
|
||||
CompressedStart: contentLen,
|
||||
CompressedEnd: len(content),
|
||||
ContentHash: base64.StdEncoding.EncodeToString(
|
||||
getHash(content[0:contentLen])[:8]),
|
||||
CompressedHash: base64.StdEncoding.EncodeToString(
|
||||
getHash(content[contentLen:len(content)])[:8]),
|
||||
Date: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 3 {
|
||||
panic("Usage: <Source Folder> <(Destination) List File>")
|
||||
}
|
||||
|
||||
sourcePath, sourcePathErr := filepath.Abs(os.Args[1])
|
||||
|
||||
if sourcePathErr != nil {
|
||||
panic(fmt.Sprintf("Invalid source folder path %s: %s",
|
||||
os.Args[1], sourcePathErr))
|
||||
}
|
||||
|
||||
listFilePath, listFilePathErr := filepath.Abs(os.Args[2])
|
||||
|
||||
if listFilePathErr != nil {
|
||||
panic(fmt.Sprintf("Invalid destination list file path %s: %s",
|
||||
os.Args[2], listFilePathErr))
|
||||
}
|
||||
|
||||
listFileName := filepath.Base(listFilePath)
|
||||
destFolderPackage := strings.TrimSuffix(
|
||||
listFileName, filepath.Ext(listFileName))
|
||||
destFolderPath := filepath.Join(
|
||||
filepath.Dir(listFilePath), destFolderPackage)
|
||||
|
||||
destFolderPathErr := os.RemoveAll(destFolderPath)
|
||||
|
||||
if destFolderPathErr != nil {
|
||||
panic(fmt.Sprintf("Unable to remove data destination folder %s: %s",
|
||||
destFolderPath, destFolderPathErr))
|
||||
}
|
||||
|
||||
destFolderPathErr = os.Mkdir(destFolderPath, 0777)
|
||||
|
||||
if destFolderPathErr != nil {
|
||||
panic(fmt.Sprintf("Unable to build data destination folder %s: %s",
|
||||
destFolderPath, destFolderPathErr))
|
||||
}
|
||||
|
||||
listFile, listFileErr := os.OpenFile(listFilePath, os.O_RDWR, 0666)
|
||||
|
||||
if listFileErr != nil {
|
||||
panic(fmt.Sprintf("Unable to open destination list file %s: %s",
|
||||
listFilePath, listFileErr))
|
||||
}
|
||||
|
||||
defer listFile.Close()
|
||||
|
||||
files, dirOpenErr := ioutil.ReadDir(sourcePath)
|
||||
|
||||
if dirOpenErr != nil {
|
||||
panic(fmt.Sprintf("Unable to open dir: %s", dirOpenErr))
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(listFile)
|
||||
destBytesByPass := int64(0)
|
||||
foundLastLine := false
|
||||
|
||||
for scanner.Scan() {
|
||||
text := scanner.Text()
|
||||
|
||||
if strings.Index(text, templateStarts) < 0 {
|
||||
if foundLastLine {
|
||||
break
|
||||
}
|
||||
|
||||
destBytesByPass += int64(len(text) + 1)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
destBytesByPass += int64(len(text) + 1)
|
||||
foundLastLine = true
|
||||
}
|
||||
|
||||
listFile.Seek(destBytesByPass, 0)
|
||||
listFile.Truncate(destBytesByPass)
|
||||
|
||||
listFile.WriteString("\n// This file is generated by `go generate` at " +
|
||||
time.Now().Format(time.RFC1123) + "\n// DO NOT EDIT!\n\n")
|
||||
|
||||
switch os.Getenv("NODE_ENV") {
|
||||
case "development":
|
||||
type sourceFiles struct {
|
||||
Name string
|
||||
Path string
|
||||
}
|
||||
|
||||
var sources []sourceFiles
|
||||
|
||||
for f := range files {
|
||||
if !files[f].Mode().IsRegular() {
|
||||
continue
|
||||
}
|
||||
|
||||
sources = append(sources, sourceFiles{
|
||||
Name: files[f].Name(),
|
||||
Path: filepath.Join(sourcePath, files[f].Name()),
|
||||
})
|
||||
}
|
||||
|
||||
tempBuildErr := buildListFileDev(listFile, sources)
|
||||
|
||||
if tempBuildErr != nil {
|
||||
panic(fmt.Sprintf(
|
||||
"Unable to build destination file due to error: %s",
|
||||
tempBuildErr))
|
||||
}
|
||||
|
||||
default:
|
||||
var parsedFiles []parsedFile
|
||||
|
||||
for f := range files {
|
||||
if !files[f].Mode().IsRegular() {
|
||||
continue
|
||||
}
|
||||
|
||||
currentFilePath := filepath.Join(sourcePath, files[f].Name())
|
||||
|
||||
parsedFiles = append(parsedFiles, parseFile(
|
||||
f, files[f].Name(), currentFilePath, destFolderPackage))
|
||||
}
|
||||
|
||||
for f := range parsedFiles {
|
||||
fn := filepath.Join(destFolderPath, parsedFiles[f].GOFileName)
|
||||
|
||||
ff, ffErr := os.Create(fn)
|
||||
|
||||
if ffErr != nil {
|
||||
panic(fmt.Sprintf("Unable to create static page file %s: %s",
|
||||
fn, ffErr))
|
||||
}
|
||||
|
||||
bErr := buildDataFile(ff, parsedFiles[f])
|
||||
|
||||
if bErr != nil {
|
||||
panic(fmt.Sprintf("Unable to build static page file %s: %s",
|
||||
fn, bErr))
|
||||
}
|
||||
}
|
||||
|
||||
listFile.WriteString(
|
||||
"\nimport \"" + parentPackage + "/" + destFolderPackage + "\"\n\n")
|
||||
|
||||
tempBuildErr := buildListFile(listFile, parsedFiles)
|
||||
|
||||
if tempBuildErr != nil {
|
||||
panic(fmt.Sprintf(
|
||||
"Unable to build destination file due to error: %s",
|
||||
tempBuildErr))
|
||||
}
|
||||
}
|
||||
}
|
||||
128
application/controller/static_pages.go
Normal file
128
application/controller/static_pages.go
Normal file
@@ -0,0 +1,128 @@
|
||||
// Sshwifty - A Web SSH client
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI <nirui@gmx.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package controller
|
||||
|
||||
//go:generate go run ./static_page_generater ../../.tmp/dist ./static_pages.go
|
||||
//go:generate go fmt ./static_pages.go
|
||||
|
||||
// This file is generated by `go generate` at Wed, 07 Aug 2019 15:54:10 CST
|
||||
// DO NOT EDIT!
|
||||
|
||||
import "github.com/niruix/sshwifty/application/controller/static_pages"
|
||||
|
||||
import "time"
|
||||
|
||||
var (
|
||||
staticPages = map[string]staticData{
|
||||
"13ec0eb5bdb821ff4930237d7c9f943f.woff2": parseStaticData(static_pages.Static0()),
|
||||
"13efe6cbc10b97144a28310ebdeda594.woff": parseStaticData(static_pages.Static1()),
|
||||
"1d6594826615607f6dc860bb49258acb.woff": parseStaticData(static_pages.Static2()),
|
||||
"313a65630d341645c13e4f2a0364381d.woff": parseStaticData(static_pages.Static3()),
|
||||
"35b07eb2f8711ae08d1f58c043880930.woff": parseStaticData(static_pages.Static4()),
|
||||
"4357beb823a5f8d65c260f045d9e019a.woff2": parseStaticData(static_pages.Static5()),
|
||||
"4fe0f73cc919ba2b7a3c36e4540d725c.woff": parseStaticData(static_pages.Static6()),
|
||||
"50d75e48e0a3ddab1dd15d6bfb9d3700.woff": parseStaticData(static_pages.Static7()),
|
||||
"59eb3601394dd87f30f82433fb39dd94.woff2": parseStaticData(static_pages.Static8()),
|
||||
"5b4a33e176ff736a74f0ca2dd9e6b396.woff2": parseStaticData(static_pages.Static9()),
|
||||
"6972f63e5242168cc5637e2771292ec0-android-chrome-144x144.png": parseStaticData(static_pages.Static10()),
|
||||
"6972f63e5242168cc5637e2771292ec0-android-chrome-192x192.png": parseStaticData(static_pages.Static11()),
|
||||
"6972f63e5242168cc5637e2771292ec0-android-chrome-256x256.png": parseStaticData(static_pages.Static12()),
|
||||
"6972f63e5242168cc5637e2771292ec0-android-chrome-36x36.png": parseStaticData(static_pages.Static13()),
|
||||
"6972f63e5242168cc5637e2771292ec0-android-chrome-384x384.png": parseStaticData(static_pages.Static14()),
|
||||
"6972f63e5242168cc5637e2771292ec0-android-chrome-48x48.png": parseStaticData(static_pages.Static15()),
|
||||
"6972f63e5242168cc5637e2771292ec0-android-chrome-512x512.png": parseStaticData(static_pages.Static16()),
|
||||
"6972f63e5242168cc5637e2771292ec0-android-chrome-72x72.png": parseStaticData(static_pages.Static17()),
|
||||
"6972f63e5242168cc5637e2771292ec0-android-chrome-96x96.png": parseStaticData(static_pages.Static18()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-icon-114x114.png": parseStaticData(static_pages.Static19()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-icon-120x120.png": parseStaticData(static_pages.Static20()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-icon-144x144.png": parseStaticData(static_pages.Static21()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-icon-152x152.png": parseStaticData(static_pages.Static22()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-icon-167x167.png": parseStaticData(static_pages.Static23()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-icon-180x180.png": parseStaticData(static_pages.Static24()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-icon-57x57.png": parseStaticData(static_pages.Static25()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-icon-60x60.png": parseStaticData(static_pages.Static26()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-icon-72x72.png": parseStaticData(static_pages.Static27()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-icon-76x76.png": parseStaticData(static_pages.Static28()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-icon-precomposed.png": parseStaticData(static_pages.Static29()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-icon.png": parseStaticData(static_pages.Static30()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-startup-image-1182x2208.png": parseStaticData(static_pages.Static31()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-startup-image-1242x2148.png": parseStaticData(static_pages.Static32()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-startup-image-1496x2048.png": parseStaticData(static_pages.Static33()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-startup-image-1536x2008.png": parseStaticData(static_pages.Static34()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-startup-image-320x460.png": parseStaticData(static_pages.Static35()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-startup-image-640x1096.png": parseStaticData(static_pages.Static36()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-startup-image-640x920.png": parseStaticData(static_pages.Static37()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-startup-image-748x1024.png": parseStaticData(static_pages.Static38()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-startup-image-750x1294.png": parseStaticData(static_pages.Static39()),
|
||||
"6972f63e5242168cc5637e2771292ec0-apple-touch-startup-image-768x1004.png": parseStaticData(static_pages.Static40()),
|
||||
"6972f63e5242168cc5637e2771292ec0-favicon-16x16.png": parseStaticData(static_pages.Static41()),
|
||||
"6972f63e5242168cc5637e2771292ec0-favicon-32x32.png": parseStaticData(static_pages.Static42()),
|
||||
"6972f63e5242168cc5637e2771292ec0-favicon.ico": parseStaticData(static_pages.Static43()),
|
||||
"6972f63e5242168cc5637e2771292ec0-firefox_app_128x128.png": parseStaticData(static_pages.Static44()),
|
||||
"6972f63e5242168cc5637e2771292ec0-firefox_app_512x512.png": parseStaticData(static_pages.Static45()),
|
||||
"6972f63e5242168cc5637e2771292ec0-firefox_app_60x60.png": parseStaticData(static_pages.Static46()),
|
||||
"6972f63e5242168cc5637e2771292ec0-manifest.json": parseStaticData(static_pages.Static47()),
|
||||
"6972f63e5242168cc5637e2771292ec0-manifest.webapp": parseStaticData(static_pages.Static48()),
|
||||
"7237b0744491024097d3d.css": parseStaticData(static_pages.Static49()),
|
||||
"7237b0744491024097d3d.css.map": parseStaticData(static_pages.Static50()),
|
||||
"7237b0744491024097d3d.js": parseStaticData(static_pages.Static51()),
|
||||
"73f0a88bbca1bec19fb1303c689d04c6.woff2": parseStaticData(static_pages.Static52()),
|
||||
"83e114c316fcc3f23f524ec3e1c65984.woff": parseStaticData(static_pages.Static53()),
|
||||
"8a96edbbcd9a6991d79371aed0b0288e.woff": parseStaticData(static_pages.Static54()),
|
||||
"90d1676003d9c28c04994c18bfd8b558.woff2": parseStaticData(static_pages.Static55()),
|
||||
"94008e69aaf05da75c0bbf8f8bb0db41.woff2": parseStaticData(static_pages.Static56()),
|
||||
"DEPENDENCES.md": parseStaticData(static_pages.Static57()),
|
||||
"LICENSE.md": parseStaticData(static_pages.Static58()),
|
||||
"README.md": parseStaticData(static_pages.Static59()),
|
||||
"ad538a69b0e8615ed0419c4529344ffc.woff2": parseStaticData(static_pages.Static60()),
|
||||
"b52fac2bb93c5858f3f2675e4b52e1de.woff2": parseStaticData(static_pages.Static61()),
|
||||
"c599374e350aa7225ee3e8a114e8d8d7.svg": parseStaticData(static_pages.Static62()),
|
||||
"c73eb1ceba3321a80a0aff13ad373cb4.woff": parseStaticData(static_pages.Static63()),
|
||||
"cc2fadc3928f2f223418887111947b40.woff": parseStaticData(static_pages.Static64()),
|
||||
"d26871e8149b5759f814fd3c7a4f784b.woff2": parseStaticData(static_pages.Static65()),
|
||||
"d3b47375afd904983d9be8d6e239a949.woff": parseStaticData(static_pages.Static66()),
|
||||
"e8eaae902c3a4dacb9a5062667e10576.woff2": parseStaticData(static_pages.Static67()),
|
||||
"ea4853ff509fe5f353d52586fcb94e20.svg": parseStaticData(static_pages.Static68()),
|
||||
"f5902d5ef961717ed263902fc429e6ae.woff": parseStaticData(static_pages.Static69()),
|
||||
"f75569f8a5fab0893fa712d8c0d9c3fe.woff2": parseStaticData(static_pages.Static70()),
|
||||
"index.html": parseStaticData(static_pages.Static71()),
|
||||
"manifest.json": parseStaticData(static_pages.Static72()),
|
||||
"robots.txt": parseStaticData(static_pages.Static73()),
|
||||
}
|
||||
)
|
||||
|
||||
// parseStaticData parses result from a static file returner and generate
|
||||
// a new `staticData` item
|
||||
func parseStaticData(
|
||||
fileStart int,
|
||||
fileEnd int,
|
||||
compressedStart int,
|
||||
compressedEnd int,
|
||||
contentHash string,
|
||||
compressedHash string,
|
||||
creation time.Time,
|
||||
data []byte,
|
||||
) staticData {
|
||||
return staticData{
|
||||
data: data[fileStart:fileEnd],
|
||||
dataHash: contentHash,
|
||||
compressd: data[compressedStart:compressedEnd],
|
||||
compressdHash: compressedHash,
|
||||
created: creation,
|
||||
}
|
||||
}
|
||||
51
application/controller/static_pages/static0_generated.go
Normal file
51
application/controller/static_pages/static0_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static10_generated.go
Normal file
51
application/controller/static_pages/static10_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static11_generated.go
Normal file
51
application/controller/static_pages/static11_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static12_generated.go
Normal file
51
application/controller/static_pages/static12_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static13_generated.go
Normal file
51
application/controller/static_pages/static13_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static14_generated.go
Normal file
51
application/controller/static_pages/static14_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static15_generated.go
Normal file
51
application/controller/static_pages/static15_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static16_generated.go
Normal file
51
application/controller/static_pages/static16_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static17_generated.go
Normal file
51
application/controller/static_pages/static17_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static18_generated.go
Normal file
51
application/controller/static_pages/static18_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static19_generated.go
Normal file
51
application/controller/static_pages/static19_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static1_generated.go
Normal file
51
application/controller/static_pages/static1_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static20_generated.go
Normal file
51
application/controller/static_pages/static20_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static21_generated.go
Normal file
51
application/controller/static_pages/static21_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static22_generated.go
Normal file
51
application/controller/static_pages/static22_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static23_generated.go
Normal file
51
application/controller/static_pages/static23_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static24_generated.go
Normal file
51
application/controller/static_pages/static24_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static25_generated.go
Normal file
51
application/controller/static_pages/static25_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static26_generated.go
Normal file
51
application/controller/static_pages/static26_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static27_generated.go
Normal file
51
application/controller/static_pages/static27_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static28_generated.go
Normal file
51
application/controller/static_pages/static28_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static29_generated.go
Normal file
51
application/controller/static_pages/static29_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static2_generated.go
Normal file
51
application/controller/static_pages/static2_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static30_generated.go
Normal file
51
application/controller/static_pages/static30_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static31_generated.go
Normal file
51
application/controller/static_pages/static31_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static32_generated.go
Normal file
51
application/controller/static_pages/static32_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static33_generated.go
Normal file
51
application/controller/static_pages/static33_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static34_generated.go
Normal file
51
application/controller/static_pages/static34_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static35_generated.go
Normal file
51
application/controller/static_pages/static35_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static36_generated.go
Normal file
51
application/controller/static_pages/static36_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static37_generated.go
Normal file
51
application/controller/static_pages/static37_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static38_generated.go
Normal file
51
application/controller/static_pages/static38_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static39_generated.go
Normal file
51
application/controller/static_pages/static39_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static3_generated.go
Normal file
51
application/controller/static_pages/static3_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static40_generated.go
Normal file
51
application/controller/static_pages/static40_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static41_generated.go
Normal file
51
application/controller/static_pages/static41_generated.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package static_pages
|
||||
|
||||
// This file is part of Sshwifty Project
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI (nirui@gmx.com)
|
||||
//
|
||||
// https://github.com/niruix/sshwifty
|
||||
//
|
||||
// This file is generated at Wed, 07 Aug 2019 15:54:10 CST
|
||||
// by "go generate", DO NOT EDIT! Also, do not open this file, it maybe too large
|
||||
// for your editor. You've been warned.
|
||||
//
|
||||
// This file may contain third-party binaries. See DEPENDENCES for detail.
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
var rawStatic41Data = `89504e470d0a1a0a0000000d49484452000000100000001008060000001ff3ff6100000002494441547801ec1a7ed2000002f5494441542dc14d689c551480e1f7def9ee4c7e6a930c8d9a1447512aad92fa035584d68516110a5310eaa62bbb510b8ae822e84610c1c69592aec42e8a0b372ed2080aa250248aa042a5d22ada1012a9689be94ce3cc7ce7dc7bbe6b167d1ec72d6b2f2dceda787f1e68032d5cf6bea870c1f0a1c21756b960eb3ed8e7b59dfd53b79f387d956d8e6dabafbf7f30bbea1cd03432d7b3a22e8137f046f6891032adc9827a23e383ddf085b5779df870c5fdf9c6a959071781e6956ac84fb1cb3045524a98252c19658cf4ca9280d1ded7e4d0fd13b8609d5a11e70a07f340f30f1bb0a21d528c9825524a5832cc127d514484ada87cf45d0ff3333cbd7faa49c1bc07da89cc0f650751a5b2c49b47eee6f89333dc360a4311066589882022880867ceaf925cc4d5aded81d6bf55c94d2d115506226c5e13e6a677f0d6b1bd3c776837356f880a228aa870bd3be0f2df5d6ac15ade15952f9d22aa94220c4ae1dd2f2ff1f1d757e85d130e3f30c3e22b4ff0ece3bb1115440451a1371ce2ebe6bd0f46e512a28a444554198af0cde5bf78f5ecf77cf2d5eff821bc7c648e50cb880a224276091f8cc2070349882a3146628aa4188931d17030966a84be27fb4c590a228aaa927dc207a3f0c1c83ea1aa688cc418b194786afa4e8e3fb88789a9069bff0cf9e0b31fd9ec6d1135a251c92ee18251b8ba55a38dec45158d919c12efdd7580fb9a13c48171f6b78b9cf9e502ddfe008d4a8c11556572d2e3eb56153ed8fabdd38d7b8233fe538154b18b11beddd86071ed67d66e76508d685454231a951da370e0e19df8a25af7ae6ecb8d91ccb147ef4054d92a071cbdb0c4fca5f3ac763729451011440451415579e7b57d8c8d032e2ffb50ab167c489dc3fb9b9c7ca645a356514a8988202a8828a28288323e9239fdf643bcf07c0b1c1d7c5e706cbbf1e9c983ae96cfb95035638efcbad1a13b2cc92e814b646f4c4d7a1e7b6492b171c7b60e70d4ed5d5a71dcd25b7a71d61736ef83b55d51b57c30ef82e143852b0c1faacad56c1ddc32355d707bbeb8cab6ff01f23ad711d68e17be0000000049454e44ae4260821f8b08000000000002ff003c03c3fc89504e470d0a1a0a0000000d49484452000000100000001008060000001ff3ff6100000002494441547801ec1a7ed2000002f5494441542dc14d689c551480e1f7def9ee4c7e6a930c8d9a1447512aad92fa035584d68516110a5310eaa62bbb510b8ae822e84610c1c69592aec42e8a0b372ed2080aa250248aa042a5d22ada1012a9689be94ce3cc7ce7dc7bbe6b167d1ec72d6b2f2dceda787f1e68032d5cf6bea870c1f0a1c21756b960eb3ed8e7b59dfd53b79f387d956d8e6dabafbf7f30bbea1cd03432d7b3a22e8137f046f6891032adc9827a23e383ddf085b5779df870c5fdf9c6a959071781e6956ac84fb1cb3045524a98252c19658cf4ca9280d1ded7e4d0fd13b8609d5a11e70a07f340f30f1bb0a21d528c9825524a5832cc127d514484ada87cf45d0ff3333cbd7faa49c1bc07da89cc0f650751a5b2c49b47eee6f89333dc360a4311066589882022880867ceaf925cc4d5aded81d6bf55c94d2d115506226c5e13e6a677f0d6b1bd3c776837356f880a228aa870bd3be0f2df5d6ac15ade15952f9d22aa94220c4ae1dd2f2ff1f1d757e85d130e3f30c3e22b4ff0ece3bb1115440451a1371ce2ebe6bd0f46e512a28a444554198af0cde5bf78f5ecf77cf2d5eff821bc7c648e50cb880a224276091f8cc2070349882a3146628aa4188931d17030966a84be27fb4c590a228aaa927dc207a3f0c1c83ea1aa688cc418b194786afa4e8e3fb88789a9069bff0cf9e0b31fd9ec6d1135a251c92ee18251b8ba55a38dec45158d919c12efdd7580fb9a13c48171f6b78b9cf9e502ddfe008d4a8c11556572d2e3eb56153ed8fabdd38d7b8233fe538154b18b11beddd86071ed67d66e76508d685454231a951da370e0e19df8a25af7ae6ecb8d91ccb147ef4054d92a071cbdb0c4fca5f3ac763729451011440451415579e7b57d8c8d032e2ffb50ab167c489dc3fb9b9c7ca645a356514a8988202a8828a28288323e9239fdf643bcf07c0b1c1d7c5e706cbbf1e9c983ae96cfb95035638efcbad1a13b2cc92e814b646f4c4d7a1e7b6492b171c7b60e70d4ed5d5a71dcd25b7a71d61736ef83b55d51b57c30ef82e143852b0c1faacad56c1ddc32355d707bbeb8cab6ff01f23ad711d68e17be0000000049454e44ae426082000000ffff`
|
||||
|
||||
// Static41 returns static file
|
||||
func Static41() (
|
||||
int, // FileStart
|
||||
int, // FileEnd
|
||||
int, // CompressedStart
|
||||
int, // CompressedEnd
|
||||
string, // ContentHash
|
||||
string, // CompressedHash
|
||||
time.Time, // Time of creation
|
||||
[]byte, // Data
|
||||
) {
|
||||
created, createErr := time.Parse(
|
||||
time.RFC1123, "Wed, 07 Aug 2019 15:54:10 CST")
|
||||
|
||||
if createErr != nil {
|
||||
panic(createErr)
|
||||
}
|
||||
|
||||
data, dataErr := hex.DecodeString(rawStatic41Data)
|
||||
|
||||
rawStatic41Data = ""
|
||||
|
||||
if dataErr != nil {
|
||||
panic(dataErr)
|
||||
}
|
||||
|
||||
return 0, 828,
|
||||
828, 1676,
|
||||
"DPkhEZBiqsE=", "H8/B3536RVw=", created, data
|
||||
}
|
||||
51
application/controller/static_pages/static42_generated.go
Normal file
51
application/controller/static_pages/static42_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static43_generated.go
Normal file
51
application/controller/static_pages/static43_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static44_generated.go
Normal file
51
application/controller/static_pages/static44_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static45_generated.go
Normal file
51
application/controller/static_pages/static45_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static46_generated.go
Normal file
51
application/controller/static_pages/static46_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static47_generated.go
Normal file
51
application/controller/static_pages/static47_generated.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package static_pages
|
||||
|
||||
// This file is part of Sshwifty Project
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI (nirui@gmx.com)
|
||||
//
|
||||
// https://github.com/niruix/sshwifty
|
||||
//
|
||||
// This file is generated at Wed, 07 Aug 2019 15:54:10 CST
|
||||
// by "go generate", DO NOT EDIT! Also, do not open this file, it maybe too large
|
||||
// for your editor. You've been warned.
|
||||
//
|
||||
// This file may contain third-party binaries. See DEPENDENCES for detail.
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
var rawStatic47Data = `7b0a2020226e616d65223a202253737769667479222c0a20202273686f72745f6e616d65223a202253737769667479222c0a2020226465736372697074696f6e223a206e756c6c2c0a202022646972223a20226175746f222c0a2020226c616e67223a2022656e2d5553222c0a202022646973706c6179223a20227374616e64616c6f6e65222c0a2020226f7269656e746174696f6e223a2022616e79222c0a20202273746172745f75726c223a20222f3f686f6d6573637265656e3d31222c0a2020226261636b67726f756e645f636f6c6f72223a202223666666222c0a20202269636f6e73223a205b0a202020207b0a20202020202022737263223a2022616e64726f69642d6368726f6d652d33367833362e706e67222c0a2020202020202273697a6573223a20223336783336222c0a2020202020202274797065223a2022696d6167652f706e67220a202020207d2c0a202020207b0a20202020202022737263223a2022616e64726f69642d6368726f6d652d34387834382e706e67222c0a2020202020202273697a6573223a20223438783438222c0a2020202020202274797065223a2022696d6167652f706e67220a202020207d2c0a202020207b0a20202020202022737263223a2022616e64726f69642d6368726f6d652d37327837322e706e67222c0a2020202020202273697a6573223a20223732783732222c0a2020202020202274797065223a2022696d6167652f706e67220a202020207d2c0a202020207b0a20202020202022737263223a2022616e64726f69642d6368726f6d652d39367839362e706e67222c0a2020202020202273697a6573223a20223936783936222c0a2020202020202274797065223a2022696d6167652f706e67220a202020207d2c0a202020207b0a20202020202022737263223a2022616e64726f69642d6368726f6d652d313434783134342e706e67222c0a2020202020202273697a6573223a202231343478313434222c0a2020202020202274797065223a2022696d6167652f706e67220a202020207d2c0a202020207b0a20202020202022737263223a2022616e64726f69642d6368726f6d652d313932783139322e706e67222c0a2020202020202273697a6573223a202231393278313932222c0a2020202020202274797065223a2022696d6167652f706e67220a202020207d2c0a202020207b0a20202020202022737263223a2022616e64726f69642d6368726f6d652d323536783235362e706e67222c0a2020202020202273697a6573223a202232353678323536222c0a2020202020202274797065223a2022696d6167652f706e67220a202020207d2c0a202020207b0a20202020202022737263223a2022616e64726f69642d6368726f6d652d333834783338342e706e67222c0a2020202020202273697a6573223a202233383478333834222c0a2020202020202274797065223a2022696d6167652f706e67220a202020207d2c0a202020207b0a20202020202022737263223a2022616e64726f69642d6368726f6d652d353132783531322e706e67222c0a2020202020202273697a6573223a202235313278353132222c0a2020202020202274797065223a2022696d6167652f706e67220a202020207d0a20205d0a7d1f8b08000000000002ffac90cd6eab301085f73c85e5bb4d6e04180291aa3e44d45555452e1862d58c9131aa699477affc537553ab5d78e3c5f966fc8dce2d4308039d183e217c5edef9a037bcb3e172954a5f7e443d5b3ac567cd25e0138255081f736547e9aaa59f1314469b30d83f9dc32a5f6641379b2e9a424f8504e691549c81a6e1574ce1eb104d95beac4ad8f4f0789593d533060fb91f78a5dddba8e40afda59342ba23fe0dc3e029ef242cf8849e338410bab9d7feaa3a6fe995e4fdbebb2a39b17d599bb2fe3fc3e876fd20ff60761f3bf69deb6d76cdf0898eec60571cb9effee2218d214dc4e35822cfb130c722e2712c91a7ad4d1bebcdb1449e9c1093131231059acad516266f63ed059ac85554b529aa5883812672950d316513eb30d044ae2a2f4c95c73a0cf4775786d04b76ff040000ffff`
|
||||
|
||||
// Static47 returns static file
|
||||
func Static47() (
|
||||
int, // FileStart
|
||||
int, // FileEnd
|
||||
int, // CompressedStart
|
||||
int, // CompressedEnd
|
||||
string, // ContentHash
|
||||
string, // CompressedHash
|
||||
time.Time, // Time of creation
|
||||
[]byte, // Data
|
||||
) {
|
||||
created, createErr := time.Parse(
|
||||
time.RFC1123, "Wed, 07 Aug 2019 15:54:10 CST")
|
||||
|
||||
if createErr != nil {
|
||||
panic(createErr)
|
||||
}
|
||||
|
||||
data, dataErr := hex.DecodeString(rawStatic47Data)
|
||||
|
||||
rawStatic47Data = ""
|
||||
|
||||
if dataErr != nil {
|
||||
panic(dataErr)
|
||||
}
|
||||
|
||||
return 0, 1196,
|
||||
1196, 1511,
|
||||
"WdtgnBG8Sks=", "EKpkCbFqOGQ=", created, data
|
||||
}
|
||||
51
application/controller/static_pages/static48_generated.go
Normal file
51
application/controller/static_pages/static48_generated.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package static_pages
|
||||
|
||||
// This file is part of Sshwifty Project
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI (nirui@gmx.com)
|
||||
//
|
||||
// https://github.com/niruix/sshwifty
|
||||
//
|
||||
// This file is generated at Wed, 07 Aug 2019 15:54:10 CST
|
||||
// by "go generate", DO NOT EDIT! Also, do not open this file, it maybe too large
|
||||
// for your editor. You've been warned.
|
||||
//
|
||||
// This file may contain third-party binaries. See DEPENDENCES for detail.
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
var rawStatic48Data = `7b0a20202276657273696f6e223a2022312e30222c0a2020226e616d65223a202253737769667479222c0a2020226465736372697074696f6e223a206e756c6c2c0a20202269636f6e73223a207b0a20202020223630223a202266697265666f785f6170705f36307836302e706e67222c0a2020202022313238223a202266697265666f785f6170705f313238783132382e706e67222c0a2020202022353132223a202266697265666f785f6170705f353132783531322e706e67220a20207d2c0a202022646576656c6f706572223a207b0a20202020226e616d65223a206e756c6c2c0a202020202275726c223a206e756c6c0a20207d0a7d1f8b08000000000002ff5c8e410a83301444f79e62f8eb22f90145bc460f20626309a44948d4a614ef5ea29616b78f37c37b17002d2a44ed2cb5202e055d32b3fd4365708d4f3d4eaf1dde541c82f6d32edbd9980debc1d9482df21940b5c8c3510735bad4f5de77b548b528bdbd6f3700b16cce0ecb26b16cfead8ae5d9aa58a68ae56615c07a642dca38afc2afe1c8ff260234077380bc2bd60f000000ffff`
|
||||
|
||||
// Static48 returns static file
|
||||
func Static48() (
|
||||
int, // FileStart
|
||||
int, // FileEnd
|
||||
int, // CompressedStart
|
||||
int, // CompressedEnd
|
||||
string, // ContentHash
|
||||
string, // CompressedHash
|
||||
time.Time, // Time of creation
|
||||
[]byte, // Data
|
||||
) {
|
||||
created, createErr := time.Parse(
|
||||
time.RFC1123, "Wed, 07 Aug 2019 15:54:10 CST")
|
||||
|
||||
if createErr != nil {
|
||||
panic(createErr)
|
||||
}
|
||||
|
||||
data, dataErr := hex.DecodeString(rawStatic48Data)
|
||||
|
||||
rawStatic48Data = ""
|
||||
|
||||
if dataErr != nil {
|
||||
panic(dataErr)
|
||||
}
|
||||
|
||||
return 0, 250,
|
||||
250, 410,
|
||||
"1dt/vqnONiQ=", "S7HxwGVBXUY=", created, data
|
||||
}
|
||||
51
application/controller/static_pages/static49_generated.go
Normal file
51
application/controller/static_pages/static49_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static4_generated.go
Normal file
51
application/controller/static_pages/static4_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static50_generated.go
Normal file
51
application/controller/static_pages/static50_generated.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package static_pages
|
||||
|
||||
// This file is part of Sshwifty Project
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI (nirui@gmx.com)
|
||||
//
|
||||
// https://github.com/niruix/sshwifty
|
||||
//
|
||||
// This file is generated at Wed, 07 Aug 2019 15:54:10 CST
|
||||
// by "go generate", DO NOT EDIT! Also, do not open this file, it maybe too large
|
||||
// for your editor. You've been warned.
|
||||
//
|
||||
// This file may contain third-party binaries. See DEPENDENCES for detail.
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
var rawStatic50Data = `7b2276657273696f6e223a332c22736f7572636573223a5b5d2c226e616d6573223a5b5d2c226d617070696e6773223a22222c2266696c65223a223732333762303734343439313032343039376433642e637373222c22736f75726365526f6f74223a22227d1f8b08000000000002ffaa562a4b2d2acecccf53b232d6512ace2f2d4a4e2d56b28a8ed551ca4bcc853173130b0a32f3d28b95ac94947494d232735295ac94cc8d8ccd930ccc4d4c4c2c0d0d8c4c0c2ccd538c53f4928b8b95600605e5e79780b4d402000000ffff`
|
||||
|
||||
// Static50 returns static file
|
||||
func Static50() (
|
||||
int, // FileStart
|
||||
int, // FileEnd
|
||||
int, // CompressedStart
|
||||
int, // CompressedEnd
|
||||
string, // ContentHash
|
||||
string, // CompressedHash
|
||||
time.Time, // Time of creation
|
||||
[]byte, // Data
|
||||
) {
|
||||
created, createErr := time.Parse(
|
||||
time.RFC1123, "Wed, 07 Aug 2019 15:54:10 CST")
|
||||
|
||||
if createErr != nil {
|
||||
panic(createErr)
|
||||
}
|
||||
|
||||
data, dataErr := hex.DecodeString(rawStatic50Data)
|
||||
|
||||
rawStatic50Data = ""
|
||||
|
||||
if dataErr != nil {
|
||||
panic(dataErr)
|
||||
}
|
||||
|
||||
return 0, 102,
|
||||
102, 206,
|
||||
"Pax14oQxroQ=", "aCysfJVxov4=", created, data
|
||||
}
|
||||
51
application/controller/static_pages/static51_generated.go
Normal file
51
application/controller/static_pages/static51_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static52_generated.go
Normal file
51
application/controller/static_pages/static52_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static53_generated.go
Normal file
51
application/controller/static_pages/static53_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static54_generated.go
Normal file
51
application/controller/static_pages/static54_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static55_generated.go
Normal file
51
application/controller/static_pages/static55_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static56_generated.go
Normal file
51
application/controller/static_pages/static56_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static57_generated.go
Normal file
51
application/controller/static_pages/static57_generated.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package static_pages
|
||||
|
||||
// This file is part of Sshwifty Project
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI (nirui@gmx.com)
|
||||
//
|
||||
// https://github.com/niruix/sshwifty
|
||||
//
|
||||
// This file is generated at Wed, 07 Aug 2019 15:54:10 CST
|
||||
// by "go generate", DO NOT EDIT! Also, do not open this file, it maybe too large
|
||||
// for your editor. You've been warned.
|
||||
//
|
||||
// This file may contain third-party binaries. See DEPENDENCES for detail.
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
var rawStatic57Data = `2320446570656e64656e636573204f662053736877696674790a0a53736877696674792075736573206d616e792074686972642d706172747920636f6d706f6e656e74732e2054686f736520636f6d706f6e656e747320697320726571756972656420696e6f726465720a666f7220537368776966747920746f2066756e6374696f6e2e0a0a41206c697374206f66207573656420636f6d706f6e656e74732063616e20626520666f756e6420696e7369646520607061636b6167652e6a736f6e6020616e642060676f2e6d6f64602066696c652e0a0a4d616a6f7220646570656e64656e63657320696e636c756465733a0a0a232320466f722066726f6e742d656e64206170706c69636174696f6e0a0a2d205b5675655d2868747470733a2f2f7675656a732e6f7267292c204c6963656e73656420756e646572204d4954206c6963656e73650a2d205b426162656c5d2868747470733a2f2f626162656c6a732e696f2f292c204c6963656e73656420756e646572204d4954206c6963656e73650a2d205b585465726d2e6a735d2868747470733a2f2f787465726d6a732e6f72672f292c204c6963656e73656420756e646572204d4954206c6963656e73650a2d205b6e6f726d616c697a652e6373735d2868747470733a2f2f6769746875622e636f6d2f6e65636f6c61732f6e6f726d616c697a652e637373292c204c6963656e73656420756e646572204d4954206c6963656e73650a2d205b526f626f746f20666f6e745d2868747470733a2f2f656e2e77696b6970656469612e6f72672f77696b692f526f626f746f292c204c6963656e73656420756e64657220417061636865206c6963656e73652e0a20205061636b61676564206279205b43687269737469616e20486f66666d6569737465725d2868747470733a2f2f6769746875622e636f6d2f63686f66666d6569737465722f726f626f746f2d666f6e74666163652d626f776572292c204c6963656e73656420756e6465722041706163686520322e300a0a232320466f72206261636b2d656e64206170706c69636174696f6e0a0a2d20606769746875622e636f6d2f676f72696c6c612f776562736f636b6574602c204c6963656e73656420756e646572204253442d322d4361757365206c6963656e73650a2d2060676f6c616e672e6f72672f782f6e65742f70726f787960205b56696577206c6963656e73655d2868747470733a2f2f6769746875622e636f6d2f676f6c616e672f6e65742f626c6f622f6d61737465722f4c4943454e5345290a2d2060676f6c616e672e6f72672f782f63727970746f602c205b56696577206c6963656e73655d2868747470733a2f2f6769746875622e636f6d2f676f6c616e672f63727970746f2f626c6f622f6d61737465722f4c4943454e5345291f8b08000000000002ff8c914f6bdc3c1087effa1403b9bc81587ec931b7fc2b0d246d699650580a96a5b13dbbf28c2bc9dd753f7df19acdbaec96ed4d12f33cbf19cd053c6087ec902d46f85cc16b6c3654a541a9fd09fa88115ac303a48682cb3a13d20056da4e1839450d8b4622ce5e802204fcd1534007c4121c06554978b74312a87ab68984b552b7e02926906acc727391350c2542253d8fa6480ea1e88c5d9b1af52a0a1760d841518b6ec515509147add48b594900371b8dd8fade61bc51eae2023e48802a08a70cd981e93a4fd68ccd2895c1f2adc7efff352975f126cf7ff6b88a5a427d7905cf6491c70e7b7618e0e569017e7a1ab13b53a23f80e5785d454d929f45bf2d30b47a150ff4366168a7e0f3384b688da75fa86d9c396a4a4d5f6a2b6dce68c59b98ff5179d6fb554a1937259c0e5664bda13575e8c8ecda1b6ff9547a6cbced8c6d702fd50ae0cbb43d07e500cbfb26504c64183e4a55b54831613839816d0e0579d8a565636395b19895b2c1f0d7f06bfdfffbd64b63d7a7965ecca26a09e4bdc9375846b16b4cc591f9eef521bbceee4d1f71f661452dde70bdfb956dce98f22ec8762860f946b8d9179e1c6f22774ce9a5cc5bb31bf4f9e9fef1d3ebe3e591dc86a14b525cfdbb7a224eda7f030000ffff`
|
||||
|
||||
// Static57 returns static file
|
||||
func Static57() (
|
||||
int, // FileStart
|
||||
int, // FileEnd
|
||||
int, // CompressedStart
|
||||
int, // CompressedEnd
|
||||
string, // ContentHash
|
||||
string, // CompressedHash
|
||||
time.Time, // Time of creation
|
||||
[]byte, // Data
|
||||
) {
|
||||
created, createErr := time.Parse(
|
||||
time.RFC1123, "Wed, 07 Aug 2019 15:54:10 CST")
|
||||
|
||||
if createErr != nil {
|
||||
panic(createErr)
|
||||
}
|
||||
|
||||
data, dataErr := hex.DecodeString(rawStatic57Data)
|
||||
|
||||
rawStatic57Data = ""
|
||||
|
||||
if dataErr != nil {
|
||||
panic(dataErr)
|
||||
}
|
||||
|
||||
return 0, 1030,
|
||||
1030, 1507,
|
||||
"ZkFdpk2P7P0=", "tqmXI4zYPC8=", created, data
|
||||
}
|
||||
51
application/controller/static_pages/static58_generated.go
Normal file
51
application/controller/static_pages/static58_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static59_generated.go
Normal file
51
application/controller/static_pages/static59_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static5_generated.go
Normal file
51
application/controller/static_pages/static5_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static60_generated.go
Normal file
51
application/controller/static_pages/static60_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static61_generated.go
Normal file
51
application/controller/static_pages/static61_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static62_generated.go
Normal file
51
application/controller/static_pages/static62_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static63_generated.go
Normal file
51
application/controller/static_pages/static63_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static64_generated.go
Normal file
51
application/controller/static_pages/static64_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static65_generated.go
Normal file
51
application/controller/static_pages/static65_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static66_generated.go
Normal file
51
application/controller/static_pages/static66_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static67_generated.go
Normal file
51
application/controller/static_pages/static67_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static68_generated.go
Normal file
51
application/controller/static_pages/static68_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static69_generated.go
Normal file
51
application/controller/static_pages/static69_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static6_generated.go
Normal file
51
application/controller/static_pages/static6_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static70_generated.go
Normal file
51
application/controller/static_pages/static70_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static71_generated.go
Normal file
51
application/controller/static_pages/static71_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static72_generated.go
Normal file
51
application/controller/static_pages/static72_generated.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package static_pages
|
||||
|
||||
// This file is part of Sshwifty Project
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI (nirui@gmx.com)
|
||||
//
|
||||
// https://github.com/niruix/sshwifty
|
||||
//
|
||||
// This file is generated at Wed, 07 Aug 2019 15:54:11 CST
|
||||
// by "go generate", DO NOT EDIT! Also, do not open this file, it maybe too large
|
||||
// for your editor. You've been warned.
|
||||
//
|
||||
// This file may contain third-party binaries. See DEPENDENCES for detail.
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
var rawStatic72Data = `7b0a2020226170702e637373223a20222f6173736574732f3732333762303734343439313032343039376433642e637373222c0a2020226170702e6a73223a20222f6173736574732f3732333762303734343439313032343039376433642e6a73222c0a2020226170702e6373732e6d6170223a20222f6173736574732f3732333762303734343439313032343039376433642e6373732e6d6170222c0a202022686f6d652e637373223a20222f6173736574732f66373535363966386135666162303839336661373132643863306439633366652e776f666632222c0a20202273736877696674792e706e67223a20222f6173736574732f36393732663633653532343231363863633536333765323737313239326563302d6d616e69666573742e776562617070222c0a202022444550454e44454e4345532e6d64223a20222f6173736574732f444550454e44454e4345532e6d64222c0a2020224c4943454e53452e6d64223a20222f6173736574732f4c4943454e53452e6d64222c0a202022524541444d452e6d64223a20222f6173736574732f524541444d452e6d64222c0a202022636f6e6e6563742e637373223a20222f6173736574732f63353939333734653335306161373232356565336538613131346538643864372e737667222c0a202022636f6e6e656374696e672e737667223a20222f6173736574732f65613438353366663530396665356633353364353235383666636239346532302e737667222c0a202022696e6465782e68746d6c223a20222f6173736574732f696e6465782e68746d6c222c0a202022726f626f74732e747874223a20222f6173736574732f726f626f74732e747874220a7d1f8b08000000000002ff8cd0cd4ee3301405e07d9fa2ea7ac6756cdfd89edda8cd02092a449fc0b1ef6d53911f618b1621de1dc9806ab2eaf6e43b27577e5f2c972b374dccc7b8fab75cad5d8c98e25a0ba95bae9552b6e24271ab830c19fdf9699c6e289c0aef6364bd9b6efa4b86b9791c7b9c1f471aa0b6641c906bb9b1929cae44309e07eb25213b8f44e2ab1fe3f1dc517a63d37028376aab05d512412851d5c67ba8a546a17525ac40cffff66ee8086362676cddf47dcdb6796c76db66b769f6ac0fe5deec4bd6f7779b66b76f66b248b37a6afe6f1fe6e81a66e3c761409fe60fe1c15aa9154ae0ce69210051a27155a5d00413348baf875f03dd70c859b1814e199044c02d219004194080a9c9b756a1e0d78d6e087861c7d43f97fd22cdea656cc71459baa45215e9e2e3130000ffff`
|
||||
|
||||
// Static72 returns static file
|
||||
func Static72() (
|
||||
int, // FileStart
|
||||
int, // FileEnd
|
||||
int, // CompressedStart
|
||||
int, // CompressedEnd
|
||||
string, // ContentHash
|
||||
string, // CompressedHash
|
||||
time.Time, // Time of creation
|
||||
[]byte, // Data
|
||||
) {
|
||||
created, createErr := time.Parse(
|
||||
time.RFC1123, "Wed, 07 Aug 2019 15:54:11 CST")
|
||||
|
||||
if createErr != nil {
|
||||
panic(createErr)
|
||||
}
|
||||
|
||||
data, dataErr := hex.DecodeString(rawStatic72Data)
|
||||
|
||||
rawStatic72Data = ""
|
||||
|
||||
if dataErr != nil {
|
||||
panic(dataErr)
|
||||
}
|
||||
|
||||
return 0, 629,
|
||||
629, 937,
|
||||
"EJKjdtxdaNE=", "4ebrZ4o5Olg=", created, data
|
||||
}
|
||||
51
application/controller/static_pages/static73_generated.go
Normal file
51
application/controller/static_pages/static73_generated.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package static_pages
|
||||
|
||||
// This file is part of Sshwifty Project
|
||||
//
|
||||
// Copyright (C) 2019 Rui NI (nirui@gmx.com)
|
||||
//
|
||||
// https://github.com/niruix/sshwifty
|
||||
//
|
||||
// This file is generated at Wed, 07 Aug 2019 15:54:11 CST
|
||||
// by "go generate", DO NOT EDIT! Also, do not open this file, it maybe too large
|
||||
// for your editor. You've been warned.
|
||||
//
|
||||
// This file may contain third-party binaries. See DEPENDENCES for detail.
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
var rawStatic73Data = `757365722d6167656e743a202a0a0a416c6c6f773a202f240a446973616c6c6f773a202f1f8b08000000000002ff2a2d4e2dd24d4c4fcd2bb152d0e2e272ccc9c92fb752d057e172c92c4e847200000000ffff`
|
||||
|
||||
// Static73 returns static file
|
||||
func Static73() (
|
||||
int, // FileStart
|
||||
int, // FileEnd
|
||||
int, // CompressedStart
|
||||
int, // CompressedEnd
|
||||
string, // ContentHash
|
||||
string, // CompressedHash
|
||||
time.Time, // Time of creation
|
||||
[]byte, // Data
|
||||
) {
|
||||
created, createErr := time.Parse(
|
||||
time.RFC1123, "Wed, 07 Aug 2019 15:54:11 CST")
|
||||
|
||||
if createErr != nil {
|
||||
panic(createErr)
|
||||
}
|
||||
|
||||
data, dataErr := hex.DecodeString(rawStatic73Data)
|
||||
|
||||
rawStatic73Data = ""
|
||||
|
||||
if dataErr != nil {
|
||||
panic(dataErr)
|
||||
}
|
||||
|
||||
return 0, 36,
|
||||
36, 83,
|
||||
"UqJ2rT+4q5w=", "Zxyoo2D9bZ0=", created, data
|
||||
}
|
||||
51
application/controller/static_pages/static7_generated.go
Normal file
51
application/controller/static_pages/static7_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static8_generated.go
Normal file
51
application/controller/static_pages/static8_generated.go
Normal file
File diff suppressed because one or more lines are too long
51
application/controller/static_pages/static9_generated.go
Normal file
51
application/controller/static_pages/static9_generated.go
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user