Browse Source

Added toggle-able SFTP server

Toby Chui 2 years ago
parent
commit
5b70ed8bd4

+ 10 - 2
desktop.go

@@ -363,6 +363,8 @@ func desktop_handleUserInfo(w http.ResponseWriter, r *http.Request) {
 		common.SendErrorResponse(w, err.Error())
 		return
 	}
+	nic, _ := common.Mv(r, "noicon", true)
+	noicon := (nic == "true")
 
 	type returnStruct struct {
 		Username          string
@@ -385,14 +387,20 @@ func desktop_handleUserInfo(w http.ResponseWriter, r *http.Request) {
 		pgs = append(pgs, pg.Name)
 	}
 
-	jsonString, _ := json.Marshal(returnStruct{
+	rs := returnStruct{
 		Username:          userinfo.Username,
 		UserIcon:          userinfo.GetUserIcon(),
 		IsAdmin:           userinfo.IsAdmin(),
 		UserGroups:        pgs,
 		StorageQuotaTotal: userinfo.StorageQuota.GetUserStorageQuota(),
 		StorageQuotaLeft:  remainingQuota,
-	})
+	}
+
+	if noicon {
+		rs.UserIcon = ""
+	}
+
+	jsonString, _ := json.Marshal(rs)
 	common.SendJSONResponse(w, string(jsonString))
 }
 

+ 73 - 73
error.go

@@ -1,73 +1,73 @@
-package main
-
-/*
-	This page mainly used to handle error interfaces
-
-*/
-
-import (
-	"io/ioutil"
-	"net/http"
-	"strings"
-
-	fs "imuslab.com/arozos/mod/filesystem"
-)
-
-func errorHandleNotFound(w http.ResponseWriter, r *http.Request) {
-	notFoundPage := "./web/SystemAO/notfound.html"
-	if fs.FileExists(notFoundPage) {
-
-		notFoundTemplateBytes, err := ioutil.ReadFile(notFoundPage)
-		notFoundTemplate := string(notFoundTemplateBytes)
-		if err != nil {
-			http.NotFound(w, r)
-		} else {
-			//Replace the request URL inside the page
-			notFoundTemplate = strings.ReplaceAll(notFoundTemplate, "{{request_url}}", r.RequestURI)
-			rel := getRootEscapeFromCurrentPath(r.RequestURI)
-			notFoundTemplate = strings.ReplaceAll(notFoundTemplate, "{{root_escape}}", rel)
-			w.WriteHeader(http.StatusNotFound)
-			w.Write([]byte(notFoundTemplate))
-		}
-	} else {
-		http.NotFound(w, r)
-	}
-
-}
-
-func errorHandlePermissionDenied(w http.ResponseWriter, r *http.Request) {
-	unauthorizedPage := "./web/SystemAO/unauthorized.html"
-	if fs.FileExists(unauthorizedPage) {
-		notFoundTemplateBytes, err := ioutil.ReadFile(unauthorizedPage)
-		notFoundTemplate := string(notFoundTemplateBytes)
-		if err != nil {
-			http.NotFound(w, r)
-		} else {
-			//Replace the request URL inside the page
-			notFoundTemplate = strings.ReplaceAll(notFoundTemplate, "{{request_url}}", r.RequestURI)
-			rel := getRootEscapeFromCurrentPath(r.RequestURI)
-			notFoundTemplate = strings.ReplaceAll(notFoundTemplate, "{{root_escape}}", rel)
-			w.WriteHeader(http.StatusNotFound)
-			w.Write([]byte(notFoundTemplate))
-		}
-	} else {
-		http.Error(w, "Not authorized", http.StatusUnauthorized)
-	}
-}
-
-//Get escape root path, example /asd/asd => ../../
-func getRootEscapeFromCurrentPath(requestURL string) string {
-	rel := ""
-	if !strings.Contains(requestURL, "/") {
-		return ""
-	}
-	splitter := requestURL
-	if splitter[len(splitter)-1:] != "/" {
-		splitter = splitter + "/"
-	}
-	for i := 0; i < len(strings.Split(splitter, "/"))-2; i++ {
-		rel += "../"
-	}
-
-	return rel
-}
+package main
+
+/*
+	This page mainly used to handle error interfaces
+
+*/
+
+import (
+	"io/ioutil"
+	"net/http"
+	"strings"
+
+	fs "imuslab.com/arozos/mod/filesystem"
+)
+
+func errorHandleNotFound(w http.ResponseWriter, r *http.Request) {
+	notFoundPage := "./web/SystemAO/notfound.html"
+	if fs.FileExists(notFoundPage) {
+
+		notFoundTemplateBytes, err := ioutil.ReadFile(notFoundPage)
+		notFoundTemplate := string(notFoundTemplateBytes)
+		if err != nil {
+			http.NotFound(w, r)
+		} else {
+			//Replace the request URL inside the page
+			notFoundTemplate = strings.ReplaceAll(notFoundTemplate, "{{request_url}}", r.RequestURI)
+			rel := getRootEscapeFromCurrentPath(r.RequestURI)
+			notFoundTemplate = strings.ReplaceAll(notFoundTemplate, "{{root_escape}}", rel)
+			w.WriteHeader(http.StatusNotFound)
+			w.Write([]byte(notFoundTemplate))
+		}
+	} else {
+		http.NotFound(w, r)
+	}
+
+}
+
+func errorHandlePermissionDenied(w http.ResponseWriter, r *http.Request) {
+	unauthorizedPage := "./web/SystemAO/unauthorized.html"
+	if fs.FileExists(unauthorizedPage) {
+		notFoundTemplateBytes, err := ioutil.ReadFile(unauthorizedPage)
+		notFoundTemplate := string(notFoundTemplateBytes)
+		if err != nil {
+			http.NotFound(w, r)
+		} else {
+			//Replace the request URL inside the page
+			notFoundTemplate = strings.ReplaceAll(notFoundTemplate, "{{request_url}}", r.RequestURI)
+			rel := getRootEscapeFromCurrentPath(r.RequestURI)
+			notFoundTemplate = strings.ReplaceAll(notFoundTemplate, "{{root_escape}}", rel)
+			w.WriteHeader(http.StatusUnauthorized)
+			w.Write([]byte(notFoundTemplate))
+		}
+	} else {
+		http.Error(w, "Not authorized", http.StatusUnauthorized)
+	}
+}
+
+//Get escape root path, example /asd/asd => ../../
+func getRootEscapeFromCurrentPath(requestURL string) string {
+	rel := ""
+	if !strings.Contains(requestURL, "/") {
+		return ""
+	}
+	splitter := requestURL
+	if splitter[len(splitter)-1:] != "/" {
+		splitter = splitter + "/"
+	}
+	for i := 0; i < len(strings.Split(splitter, "/"))-2; i++ {
+		rel += "../"
+	}
+
+	return rel
+}

+ 11 - 0
mod/fileservers/fileservers.go

@@ -5,3 +5,14 @@ package fileservers
 
 	This module handle the functions related to file server managements
 */
+
+//Utilities
+func GetFileServerById(servers []*Server, id string) *Server {
+	for _, server := range servers {
+		if server.ID == id {
+			return server
+		}
+	}
+
+	return nil
+}

+ 5 - 1
mod/fileservers/servers/ftpserv/ftpserv.go

@@ -246,9 +246,13 @@ func (m *Manager) FTPServerToggle(enabled bool) error {
 
 func (m *Manager) FTPGetEndpoints(userinfo *user.User) []*fileservers.Endpoint {
 	ftpEndpoints := []*fileservers.Endpoint{}
+	port := 21
+	if m.option.FtpServer != nil {
+		port = m.option.FtpServer.Port
+	}
 	ftpEndpoints = append(ftpEndpoints, &fileservers.Endpoint{
 		ProtocolName: "ftp://",
-		Port:         m.option.FtpServer.Port,
+		Port:         port,
 		Subpath:      "",
 	})
 	return ftpEndpoints

+ 1 - 1
mod/fileservers/servers/ftpserv/handler.go

@@ -135,7 +135,7 @@ func (m *Manager) HandleFTPPassiveModeSettings(w http.ResponseWriter, r *http.Re
 			return
 		}
 
-		m.option.Logger.PrintAndLog("FTP", "Updating FTP Server PassiveMode to"+passive, nil)
+		m.option.Logger.PrintAndLog("FTP", "Updating FTP Server PassiveMode to "+passive, nil)
 		if passive == "true" {
 			m.option.Sysdb.Write("ftp", "passive", true)
 		} else {

+ 99 - 0
mod/fileservers/servers/sftpserv/sftpserv.go

@@ -0,0 +1,99 @@
+package sftpserv
+
+import (
+	"strconv"
+
+	"imuslab.com/arozos/mod/database"
+	"imuslab.com/arozos/mod/fileservers"
+	"imuslab.com/arozos/mod/info/logger"
+	"imuslab.com/arozos/mod/storage/sftpserver"
+	user "imuslab.com/arozos/mod/user"
+)
+
+type ManagerOption struct {
+	UserManager *user.UserHandler
+	KeyFile     string
+	Logger      *logger.Logger
+	Sysdb       *database.Database
+}
+
+type Manager struct {
+	Enabled       bool
+	listeningPort int
+	instance      *sftpserver.Instance
+	option        *ManagerOption
+	config        *sftpserver.SFTPConfig
+}
+
+func NewSFTPServer(option *ManagerOption) *Manager {
+	option.Sysdb.NewTable("sftp")
+
+	i, lp, err := newSFTPServerInstance(option.KeyFile, option.Sysdb, option.UserManager)
+	return &Manager{
+		Enabled:       err != nil,
+		listeningPort: lp,
+		instance:      i,
+		option:        option,
+	}
+}
+
+func newSFTPServerInstance(keyfile string, sysdb *database.Database, userManager *user.UserHandler) (*sftpserver.Instance, int, error) {
+	//Load default port from database
+	defaultListeningPort := 2022
+	if sysdb.KeyExists("sftp", "port") {
+		sysdb.Read("sftp", "port", &defaultListeningPort)
+	}
+
+	//Create an SFTP Server
+	var currentConfig = sftpserver.SFTPConfig{
+		ListeningIP: "0.0.0.0:" + strconv.Itoa(defaultListeningPort),
+		KeyFile:     keyfile,
+		UserManager: userManager,
+	}
+
+	i, err := sftpserver.NewSFTPServer(&currentConfig)
+	return i, defaultListeningPort, err
+}
+
+/*
+	Handlers for handling config change
+*/
+
+//Get or Set listening port for SFTP
+func (m *Manager) HandleListeningPort() {
+
+}
+
+/*
+	Functions requested by the file server service router
+*/
+func (m *Manager) ServerToggle(enabled bool) error {
+	if m.instance != nil && !enabled {
+		//Shutdown the running instances
+		m.instance.Close()
+		m.instance = nil
+	} else if m.instance == nil && enabled {
+		//Startup a new instance
+		i, lp, err := newSFTPServerInstance(m.option.KeyFile, m.option.Sysdb, m.option.UserManager)
+		if err != nil {
+			return err
+		}
+		m.listeningPort = lp
+		m.instance = i
+	}
+	return nil
+}
+
+func (m *Manager) IsEnabled() bool {
+	return m.instance != nil && !m.instance.Closed
+}
+
+func (m *Manager) GetEndpoints(userinfo *user.User) []*fileservers.Endpoint {
+	eps := []*fileservers.Endpoint{}
+	eps = append(eps, &fileservers.Endpoint{
+		ProtocolName: "sftp://",
+		Port:         m.listeningPort,
+		Subpath:      "",
+	})
+	return eps
+}

+ 4 - 0
mod/fileservers/servers/webdavserv/webdavserv.go

@@ -98,6 +98,10 @@ func (m *Manager) WebDavGetEndpoints(userinfo *user.User) []*fileservers.Endpoin
 	return eps
 }
 
+func (m *Manager) GetWebDavEnabled() bool {
+	return m.WebDavHandler.Enabled
+}
+
 //Mapper of the original Connection related features
 func (m *Manager) HandleConnectionList(w http.ResponseWriter, r *http.Request) {
 	m.WebDavHandler.HandleConnectionList(w, r)

+ 1 - 1
mod/fileservers/typedef.go

@@ -19,12 +19,12 @@ type Server struct {
 	IconPath          string //Path for the protocol Icon, if any
 	DefaultPorts      []int  //Default ports aquire by the Server. Override by Ports if set
 	Ports             []int  //Ports required by the File Server Type that might need port forward. e.g. 21, 22
-	Enabled           bool   //If the server is enabled
 	ForwardPortIfUpnp bool   //Forward the port if UPnP is enabled
 	ConnInstrPage     string //Connection instruction page, visable by all users
 	ConfigPage        string //Config page for changing settings of this File Server Type, admin only
 
 	//Generic operation endpoints
+	EnableCheck  func() bool                  `json:"-"` //Return the status of if the server is currently runnign
 	ToggleFunc   func(bool) error             `json:"-"` //Toggle on/off of this service
 	GetEndpoints func(*user.User) []*Endpoint `json:"-"` //Get the accessible endpoints for this user
 }

+ 47 - 35
mod/storage/sftpserver/sftpserver.go

@@ -16,11 +16,13 @@ import (
 type SFTPConfig struct {
 	ListeningIP string
 	KeyFile     string
-	ReadOnly    bool
 	UserManager *user.UserHandler
 }
 
 type Instance struct {
+	Closed           bool
+	closer           chan bool
+	ConnectedClients []*chan bool
 }
 
 //Create a new SFTP Server
@@ -32,11 +34,11 @@ func NewSFTPServer(sftpConfig *SFTPConfig) (*Instance, error) {
 		PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
 			// Should use constant-time compare (or better, salt+hash) in
 			// a production setting.
-			fmt.Printf("Login: %s\n", c.User())
+			fmt.Printf("[SFTP] %s Logged in\n", c.User())
 
 			ok := sftpConfig.UserManager.GetAuthAgent().ValidateUsernameAndPassword(c.User(), string(pass))
 			if !ok {
-				return nil, errors.New("password rejected for " + c.User())
+				return nil, errors.New("[SFTP] Password rejected for " + c.User())
 			}
 			return nil, nil
 		},
@@ -59,7 +61,25 @@ func NewSFTPServer(sftpConfig *SFTPConfig) (*Instance, error) {
 	if err != nil {
 		return nil, err
 	}
-	fmt.Printf("Listening on %v\n", listener.Addr())
+	log.Printf("[SFTP] Listening on %v\n", listener.Addr())
+
+	//Setup a closer handler for this instance
+	closeChan := make(chan bool)
+	thisServerInstance := Instance{
+		closer:           closeChan,
+		Closed:           false,
+		ConnectedClients: []*chan bool{},
+	}
+
+	go func() {
+		<-closeChan
+		//Kick all the client off
+		for _, kickchan := range thisServerInstance.ConnectedClients {
+			*kickchan <- true
+		}
+		//Close the listener
+		listener.Close()
+	}()
 
 	//Start the ssh server listener in go routine
 	go func() error {
@@ -76,9 +96,7 @@ func NewSFTPServer(sftpConfig *SFTPConfig) (*Instance, error) {
 				if err != nil {
 					return err
 				}
-				fmt.Println("SSH server established\n")
-
-				fmt.Println("Connected username", cx.User())
+				log.Println("[SFTP] User Connected: ", cx.User())
 
 				userinfo, err := sftpConfig.UserManager.GetUserInfoFromUsername(cx.User())
 				if err != nil {
@@ -93,74 +111,68 @@ func NewSFTPServer(sftpConfig *SFTPConfig) (*Instance, error) {
 					// Channels have a type, depending on the application level
 					// protocol intended. In the case of an SFTP session, this is "subsystem"
 					// with a payload string of "<length=4>sftp"
-					fmt.Println("Incoming channel: %s\n", newChannel.ChannelType())
+					//fmt.Println("Incoming channel: %s\n", newChannel.ChannelType())
 					if newChannel.ChannelType() != "session" {
 						newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
-						fmt.Println("Unknown channel type: %s\n", newChannel.ChannelType())
+						//fmt.Println("Unknown channel type: %s\n", newChannel.ChannelType())
 						continue
 					}
 					channel, requests, err := newChannel.Accept()
 					if err != nil {
 						return err
 					}
-					fmt.Println("Channel accepted\n")
+					//fmt.Println("Channel accepted\n")
 
 					// Sessions have out-of-band requests such as "shell",
 					// "pty-req" and "env".  Here we handle only the
 					// "subsystem" request.
 					go func(in <-chan *ssh.Request) {
 						for req := range in {
-							fmt.Println("Request: %v\n", req.Type)
+							//fmt.Println("Request: %v\n", req.Type)
 							ok := false
 							switch req.Type {
 							case "subsystem":
-								fmt.Println("Subsystem: %s\n", req.Payload[4:])
+								//fmt.Println("Subsystem: %s\n", req.Payload[4:])
 								if string(req.Payload[4:]) == "sftp" {
 									ok = true
 								}
 							}
-							fmt.Println(" - accepted: %v\n", ok)
+							//fmt.Println(" - accepted: %v\n", ok)
 							req.Reply(ok, nil)
 						}
 					}(requests)
 
-					serverOptions := []sftp.ServerOption{}
-
-					if sftpConfig.ReadOnly {
-						serverOptions = append(serverOptions, sftp.ReadOnly())
-						fmt.Println("Read-only server\n")
-					} else {
-						fmt.Println("Read write server\n")
-					}
-
-					/*
-						server, err := sftp.NewServer(
-							channel,
-							serverOptions...,
-						)
-						if err != nil {
-							return err
-						}
-					*/
-
 					//Create a virtual SSH Server that contains all this user's fsh
 					root := GetNewSFTPRoot(userinfo.Username, userinfo.GetAllFileSystemHandler())
 					server := sftp.NewRequestServer(channel, root)
 
+					//Create a channel for kicking the user off
+					kickChan := make(chan bool)
+					thisServerInstance.ConnectedClients = append(thisServerInstance.ConnectedClients, &kickChan)
+					go func() {
+						<-kickChan
+						server.Close()
+					}()
+
 					if err := server.Serve(); err == io.EOF {
 						server.Close()
 						log.Print("sftp client exited session.")
 					} else if err != nil {
-						log.Fatal("sftp server completed with error:", err)
+						log.Println("sftp server completed with error:", err)
 					}
 
 				}
 
 				return nil
 			}(nConn)
-
 		}
 	}()
 
-	return &Instance{}, nil
+	return &thisServerInstance, nil
+}
+
+func (i *Instance) Close() {
+	i.closer <- true
+	i.Closed = true
+	log.Println("[SFTP] SFTP Server Closed")
 }

+ 128 - 36
network.go

@@ -9,6 +9,7 @@ import (
 	"imuslab.com/arozos/mod/common"
 	"imuslab.com/arozos/mod/fileservers"
 	"imuslab.com/arozos/mod/fileservers/servers/ftpserv"
+	"imuslab.com/arozos/mod/fileservers/servers/sftpserv"
 	"imuslab.com/arozos/mod/fileservers/servers/webdavserv"
 	network "imuslab.com/arozos/mod/network"
 	mdns "imuslab.com/arozos/mod/network/mdns"
@@ -30,6 +31,7 @@ var (
 	//File Server Managers
 	FTPManager    *ftpserv.Manager
 	WebDAVManager *webdavserv.Manager
+	SFTPManager   *sftpserv.Manager
 )
 
 func NetworkServiceInit() {
@@ -238,7 +240,7 @@ func StopNetworkServices() {
 
 */
 
-var networkFileServerDaemon []fileservers.Server = []fileservers.Server{}
+var networkFileServerDaemon []*fileservers.Server = []*fileservers.Server{}
 
 //Initiate all File Server services
 func FileServerInit() {
@@ -272,6 +274,19 @@ func FileServerInit() {
 	})
 
 	//Create File Server Managers
+	webdavPort := *listen_port
+	if *use_tls {
+		webdavPort = *tls_listen_port
+	}
+	WebDAVManager = webdavserv.NewWebDAVManager(&webdavserv.ManagerOption{
+		Sysdb:       sysdb,
+		Hostname:    *host_name,
+		TmpDir:      *tmp_directory,
+		Port:        webdavPort,
+		UseTls:      *use_tls,
+		UserHandler: userHandler,
+	})
+
 	FTPManager = ftpserv.NewFTPManager(&ftpserv.ManagerOption{
 		Hostname:    *host_name,
 		TmpFolder:   *tmp_directory,
@@ -283,67 +298,118 @@ func FileServerInit() {
 		AllowUpnp:   *allow_upnp,
 	})
 
-	webdavPort := *listen_port
-	if *use_tls {
-		webdavPort = *tls_listen_port
-	}
-	WebDAVManager = webdavserv.NewWebDAVManager(&webdavserv.ManagerOption{
+	SFTPManager = sftpserv.NewSFTPServer(&sftpserv.ManagerOption{
+		UserManager: userHandler,
+		KeyFile:     "system/auth/id_rsa.key",
+		Logger:      systemWideLogger,
 		Sysdb:       sysdb,
-		Hostname:    *host_name,
-		TmpDir:      *tmp_directory,
-		Port:        webdavPort,
-		UseTls:      *use_tls,
-		UserHandler: userHandler,
 	})
 
 	//Register Endpoints
+	//WebDAV
+	http.HandleFunc("/system/network/webdav/list", WebDAVManager.HandleConnectionList)
+	router.HandleFunc("/system/network/webdav/edit", WebDAVManager.HandlePermissionEdit)
+	router.HandleFunc("/system/network/webdav/clear", WebDAVManager.HandleClearAllPending)
+	router.HandleFunc("/system/network/webdav/status", WebDAVManager.HandleStatusChange)
+
 	//FTP
-	adminRouter.HandleFunc("/system/storage/ftp/start", FTPManager.HandleFTPServerStart)
-	adminRouter.HandleFunc("/system/storage/ftp/stop", FTPManager.HandleFTPServerStop)
+	//adminRouter.HandleFunc("/system/storage/ftp/start", FTPManager.HandleFTPServerStart)
+	//adminRouter.HandleFunc("/system/storage/ftp/stop", FTPManager.HandleFTPServerStop)
 	adminRouter.HandleFunc("/system/storage/ftp/upnp", FTPManager.HandleFTPUPnP)
 	adminRouter.HandleFunc("/system/storage/ftp/status", FTPManager.HandleFTPServerStatus)
 	adminRouter.HandleFunc("/system/storage/ftp/updateGroups", FTPManager.HandleFTPAccessUpdate)
 	adminRouter.HandleFunc("/system/storage/ftp/setPort", FTPManager.HandleFTPSetPort)
 	adminRouter.HandleFunc("/system/storage/ftp/passivemode", FTPManager.HandleFTPPassiveModeSettings)
 
-	//WebDAV
-	http.HandleFunc("/system/network/webdav/list", WebDAVManager.HandleConnectionList)
-	router.HandleFunc("/system/network/webdav/edit", WebDAVManager.HandlePermissionEdit)
-	router.HandleFunc("/system/network/webdav/clear", WebDAVManager.HandleClearAllPending)
-	router.HandleFunc("/system/network/webdav/status", WebDAVManager.HandleStatusChange)
+	networkFileServerDaemon = append(networkFileServerDaemon, &fileservers.Server{
+		ID:                "webdav",
+		Name:              "WebDAV",
+		Desc:              "WebDAV Server",
+		IconPath:          "img/system/network-folder-blue.svg",
+		DefaultPorts:      []int{},
+		Ports:             []int{},
+		ForwardPortIfUpnp: false,
+		ConnInstrPage:     "SystemAO/disk/instr/webdav.html",
+		ConfigPage:        "SystemAO/disk/webdav.html",
+		EnableCheck:       WebDAVManager.GetWebDavEnabled,
+		ToggleFunc:        WebDAVManager.WebDavToogle,
+		GetEndpoints:      WebDAVManager.WebDavGetEndpoints,
+	})
 
-	networkFileServerDaemon = append(networkFileServerDaemon, fileservers.Server{
+	networkFileServerDaemon = append(networkFileServerDaemon, &fileservers.Server{
+		ID:                "sftp",
+		Name:              "SFTP",
+		Desc:              "SSH File Transfer Protocol Server",
+		IconPath:          "img/system/network-folder-sftp.svg",
+		DefaultPorts:      []int{2022},
+		Ports:             []int{},
+		ForwardPortIfUpnp: true,
+		ConnInstrPage:     "SystemAO/disk/instr/sftp.html",
+		ConfigPage:        "SystemAO/disk/sftp.html",
+		EnableCheck:       SFTPManager.IsEnabled,
+		ToggleFunc:        SFTPManager.ServerToggle,
+		GetEndpoints:      SFTPManager.GetEndpoints,
+	})
+
+	networkFileServerDaemon = append(networkFileServerDaemon, &fileservers.Server{
 		ID:                "ftp",
 		Name:              "FTP",
 		Desc:              "File Transfer Protocol Server",
 		IconPath:          "img/system/network-folder.svg",
 		DefaultPorts:      []int{21, 22, 23},
 		Ports:             []int{},
-		Enabled:           false,
 		ForwardPortIfUpnp: true,
-		ConnInstrPage:     "SystemAO/disk/ftp.html",
+		ConnInstrPage:     "SystemAO/disk/instr/ftp.html",
 		ConfigPage:        "SystemAO/disk/ftp.html",
+		EnableCheck:       FTPManager.IsFtpServerEnabled,
 		ToggleFunc:        FTPManager.FTPServerToggle,
 		GetEndpoints:      FTPManager.FTPGetEndpoints,
 	})
 
-	networkFileServerDaemon = append(networkFileServerDaemon, fileservers.Server{
-		ID:                "webdav",
-		Name:              "WebDAV",
-		Desc:              "WebDAV Server",
-		IconPath:          "img/system/network-folder-blue.svg",
-		DefaultPorts:      []int{},
-		Ports:             []int{},
-		Enabled:           false,
-		ForwardPortIfUpnp: false,
-		ConnInstrPage:     "SystemAO/disk/webdav.html",
-		ConfigPage:        "SystemAO/disk/webdav.html",
-		ToggleFunc:        WebDAVManager.WebDavToogle,
-		GetEndpoints:      WebDAVManager.WebDavGetEndpoints,
-	})
-
 	router.HandleFunc("/system/network/server/list", NetworkHandleGetFileServerServiceList)
 	router.HandleFunc("/system/network/server/endpoints", NetworkHandleGetFileServerEndpoints)
+	router.HandleFunc("/system/network/server/status", NetworkHandleGetFileServerStatus)
+	adminRouter.HandleFunc("/system/network/server/toggle", NetworkHandleFileServerToggle)
+}
+
+//Toggle the target File Server Services
+func NetworkHandleFileServerToggle(w http.ResponseWriter, r *http.Request) {
+	servid, err := common.Mv(r, "id", true)
+	if err != nil {
+		common.SendErrorResponse(w, "invalid service id given")
+		return
+	}
+
+	newState, err := common.Mv(r, "enable", true)
+	if err != nil {
+		common.SendErrorResponse(w, "undefined enable state")
+		return
+	}
+
+	targetfserv := fileservers.GetFileServerById(networkFileServerDaemon, servid)
+	if targetfserv == nil {
+		common.SendErrorResponse(w, "target service not exists")
+		return
+	}
+
+	if newState == "true" {
+		//Start up the target service
+		err = targetfserv.ToggleFunc(true)
+		if err != nil {
+			common.SendErrorResponse(w, "startup failed: "+err.Error())
+			return
+		}
+	} else if newState == "false" {
+		err = targetfserv.ToggleFunc(false)
+		if err != nil {
+			common.SendErrorResponse(w, "shutdown failed: "+err.Error())
+			return
+		}
+	} else {
+		common.SendErrorResponse(w, "unknown state keyword")
+		return
+	}
+
 }
 
 //Return a list of supported File Server Services
@@ -352,6 +418,32 @@ func NetworkHandleGetFileServerServiceList(w http.ResponseWriter, r *http.Reques
 	common.SendJSONResponse(w, string(js))
 }
 
+//Get the status of a file server type.
+func NetworkHandleGetFileServerStatus(w http.ResponseWriter, r *http.Request) {
+	servid, _ := common.Mv(r, "id", false)
+	if servid == "" {
+		//List all state in map
+		result := map[string]bool{}
+		for _, fserv := range networkFileServerDaemon {
+			result[fserv.ID] = fserv.EnableCheck()
+		}
+
+		js, _ := json.Marshal(result)
+		common.SendJSONResponse(w, string(js))
+	} else {
+		//ID is defined. Get the target server and return its status
+		targetfserv := fileservers.GetFileServerById(networkFileServerDaemon, servid)
+		if targetfserv == nil {
+			common.SendErrorResponse(w, "target file server type not found")
+			return
+		}
+
+		js, _ := json.Marshal(targetfserv.EnableCheck())
+		common.SendJSONResponse(w, string(js))
+	}
+}
+
+//Get a list of endpoint usable by this service
 func NetworkHandleGetFileServerEndpoints(w http.ResponseWriter, r *http.Request) {
 	userinfo, err := userHandler.GetUserInfoFromRequest(w, r)
 	if err != nil {

+ 0 - 9
startup.go

@@ -14,7 +14,6 @@ import (
 	"imuslab.com/arozos/mod/filesystem"
 	fs "imuslab.com/arozos/mod/filesystem"
 	"imuslab.com/arozos/mod/info/logger"
-	"imuslab.com/arozos/mod/storage/sftpserver"
 )
 
 func RunStartup() {
@@ -129,12 +128,4 @@ func RunStartup() {
 	//Finally
 	moduleHandler.ModuleSortList() //Sort the system module list
 
-	_, err = sftpserver.NewSFTPServer(&sftpserver.SFTPConfig{
-		ListeningIP: "0.0.0.0:2022",
-		KeyFile:     "legacy/id_rsa",
-		ReadOnly:    false,
-		UserManager: userHandler,
-	})
-	fmt.Println(err)
-
 }

+ 15 - 0
system/auth/id_rsa.key

@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDLXcAWHwJ2XJ9tqMELQVszLKfaMkpJGCD4Q6iPBBs24ENzAf7Y
+J+8hRFKRVEQPZlIsWZPOXBdG27nXW/q6Z68JI6dyEvj5bg4rlde6nQcNcKd3EtQc
+eB6N/nmhLZf1q6DsHvFMol7yN/Zu42bUFupyNaBDjN9H2sCyc6okebLvUQIDAQAB
+AoGAR17ngtvvKUroSLvow+Jz90m8vr7Xgz+MkpRsG4T9aAzcnwgcQBADxFEOCSLh
+n+XxAM+PJ+T55kxGtGX7YF/y9Uln39wWoy3Vm0sFyxI/6YYre/xbMHM7pCM5OkEL
+3bfPv/twXENz3y/1s5rrgICkar3x2IbZk4uC5krqW246cAECQQD4mDbX2iDAagmc
+ezhMGo9Le5P87jSQriqkmpmy0HEtIngog8O/clKcp5pE5Ckc3jVYLNfGfkyVngwS
+FddRQqPxAkEA0Wyft4ftBPvqwpZolQJEMwGoDS5VcfH0DzFsKZe3B6YrLbhVpNwI
+MKshAvYeglGQoPB0flG6epQpCELvHxzhYQJAKsaeYUQScKmOX9PAGzBSye1IyLQA
+bYjao5pKqj89ykNtI6OQskesuXIJlKMiA+qkiTimJGylJvWcJByIAV6TMQJAYlhY
+SJ+UNpr2i5qGUjNWQ32rpUT06yVsLxZObNnKIdVuwXGnBlwtnG1Ae1uIyDn1aR6C
+Fi/bGmUpP6/vCvVNAQJAJnBvPs/C1gDMrjDwqWv6E0PX9QN5tHBQbJBhdD436XIk
+WqWaOF7Rnz8aDsyX3RhBbhyTzTDmSgXTipzy8vfE9g==
+-----END RSA PRIVATE KEY-----

+ 14 - 38
web/SystemAO/disk/ftp.html

@@ -1,12 +1,14 @@
 <!DOCTYPE html>
 <html>
 <head>
+    <!-- 
 	<meta name="mobile-web-app-capable" content="yes">
 	<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
 	<meta charset="UTF-8">
     <link rel="stylesheet" href="../../script/semantic/semantic.min.css">
     <script src="../../script/jquery.min.js"></script>
 	<script src="../../script/semantic/semantic.min.js"></script>
+    -->
     <style>
         .hidden{
             display:none;
@@ -19,41 +21,15 @@
     </style>
 </head>
 <body>
-    <div id="ok" class="ui secondary inverted green segment" style="display:none;">
-        <i class="checkmark icon"></i> Setting Applied
-    </div>
-    <div id="error" class="ui secondary inverted red segment" style="display:none;">
-        <i class="remove icon"></i> <span class="msg">Something went wrong</span>
-    </div>
-    <div class="ui blue message windowsonly" style="display:none; margin-top: 0;">
-        <h4 class="ui header">
-            <i class="windows icon"></i>
-            <div class="content">
-                FTP Server Endpoint
-                <div class="sub header">ftp://<span class="hostname"></span>:<span class="port"></span></div>
-            </div>
-        </h4>
-        <p>To connect your File Explorer to the ArozOS FTP server, enter the link above into the address bar of your file explorer window.</p>
-        <p>If you <b><a href="https://superuser.com/questions/1309756/not-able-to-access-ftp-server-from-other-machines-in-same-lan-when-windows-firew">cannot access the FTP without turning off the Windows Firewall</a></b>, execute this command in cmd as administrator<br>
-        <code>netsh advfirewall set global StatefulFTP disable</code></p>
-    </div>
-    <div class="ui basic message maconly" style="display:none;">
-        <h4 class="ui header">
-            <i class="apple icon"></i>
-            <div class="content">
-                FTP Server Endpoint
-                <div class="sub header">ftp://<span class="hostname"></span>:<span class="port"></span></div>
-            </div>
-        </h4>
-        <p>To connect your Finder to the ArozOS FTP server, select <code>Go</code> <i class="arrow right icon"></i> <code>Connect to Server</code> and enter the link above into the address bar</p>
-    </div>
     <div class="ui form">
+        <!-- 
         <div class="field">
             <div class="ui toggle checkbox">
                 <input id="enabled" type="checkbox" name="enable" onchange="toggleFTPServer(this.checked);">
                 <label>Enable FTP Server</label>
             </div>
         </div>
+        -->
         <div class="field">
             <div class="ui toggle checkbox">
                 <input id="useUPNP" type="checkbox" name="upnp" onchange="toggleUPNP(this.checked);">
@@ -80,18 +56,20 @@
             <button onclick="updateGroupAccess();" class="ui secondary right floated button">Update Access Policy</button>
         </div>
         <br><br>
+        <div id="ok" class="ui secondary inverted green segment" style="display:none;">
+            <i class="checkmark icon"></i> Setting Applied
+        </div>
+        <div id="error" class="ui secondary inverted red segment" style="display:none;">
+            <i class="remove icon"></i> <span class="msg">Something went wrong</span>
+        </div>
         <div class="ui divider"></div>
         <h4>Advance Options</h4>
-        <div class="ui message">
-            <h4><i class="info circle icon"></i>FTP Usage under UPnP Mode</h4>
-            <p>When UPnP Mode is enabled, the FTP Server must be run under force passive mode and it is only accessible outside of your NAT network (aka access from the internet).</p>
-        </div>
         <br>
         <div class="field">
             <div class="ui toggle checkbox">
                 <input id="passiveMode" type="checkbox" name="passivemode" onchange="handlePassiveModeChange(this.checked);">
                 <label>Force Passive Mode</label>
-                <small>Enable this option if your server is hosted behind a NAT router, require manual port forwarding in router.</small>
+                <small>Required under UPnP mode. Optional in default mode with manual port forwarding in gateway router</small>
             </div>
         </div>
         <div class="field">
@@ -104,6 +82,7 @@
         <div class="field">
             <button onclick="updatePublicIPSetting();" class="ui secondary right floated button">Update Public IP Setting</button>
         </div>
+       
     </div>
     <br><br>
     <script>
@@ -130,6 +109,7 @@
                 $(".ui.dropdown").dropdown();
 
                 //Init server status
+                //Update done by service.html
                 initFTPServerStatus();
             });
 
@@ -143,7 +123,7 @@
                     console.log(data.error);
                 }else{
                     if (data.Enabled == true){
-                        $("#enabled")[0].checked = true;
+                        //$("#enabled")[0].checked = true;
                         $("#useUPNP").parent().removeClass("disabled");
                     }else{
                         $("#listeningPort").parent().addClass("disabled");
@@ -343,10 +323,6 @@
             $("#error").find(".msg").text(msg);
             $("#error").stop().finish().slideDown("fast").delay(3000).slideUp("fast");
         }
-
-        //Update examples
-        $(".thisAddr").text(window.location.hostname.replace('www.',''));
-
        
     </script>
 </body>

+ 30 - 0
web/SystemAO/disk/instr/ftp.html

@@ -0,0 +1,30 @@
+<div class="ui blue message windowsonly" style="display:none; margin-top: 0;">
+    <h4 class="ui header">
+        <i class="windows icon"></i>
+        <div class="content">
+            Mount FTP on Windows
+        </div>
+    </h4>
+    <p>To connect your File Explorer to the ArozOS FTP server, enter the link above into the address bar of your file explorer window.</p>
+    <p>If you <b><a href="https://superuser.com/questions/1309756/not-able-to-access-ftp-server-from-other-machines-in-same-lan-when-windows-firew">cannot access the FTP without turning off the Windows Firewall</a></b>, execute this command in cmd as administrator<br>
+    <code>netsh advfirewall set global StatefulFTP disable</code></p>
+</div>
+<div class="ui basic message maconly" style="display:none;">
+    <h4 class="ui header">
+        <i class="apple icon"></i>
+        <div class="content">
+            Mount FTP on your Mac
+        </div>
+    </h4>
+    <p>To connect your Finder to the ArozOS FTP server, select <code>Go</code> <i class="arrow right icon"></i> <code>Connect to Server</code> and enter the link above into the address bar</p>
+</div>
+<script>
+    var isMac = navigator.platform.indexOf('Mac') > -1;
+    var isWindows = navigator.platform.indexOf('Win') > -1;
+
+    if (isMac){
+        $(".maconly").show();
+    }else if (isWindows){
+        $(".windowsonly").show();
+    }
+</script>

+ 5 - 0
web/SystemAO/disk/instr/noaccess.html

@@ -0,0 +1,5 @@
+<div class="ui container">
+    <div style="padding-left: 0.6em; padding-right: 0.6em; font-size: 0.9em; color: grey; text-align: center;">
+        <p><i class="grey info circle icon"></i> If you are unable to access your network drive with the link provided on the left, <br>please contact your system administrator.</p>
+    </div>
+</div>

+ 45 - 0
web/SystemAO/disk/instr/webdav.html

@@ -0,0 +1,45 @@
+<div class="ui message maconly" style="display:none; margin-top: 0;">
+    <h4><i class="apple icon"></i> Login Method for MacOS</h4>
+    <p>If you are using MacOS, you can select "Connect to Network Server", enter the endpoint of the arozos WebDAV service (<span class="protocol"></span>//<span class="hostname"></span>:<span class="port"></span>/webdav/{vroot_name}) and login with your arozos username and password.</p>
+</div>
+<div class="ui blue message windowonly nontls" style="display:none; margin-top: 0;">
+    <h4><i class="windows icon"></i> Login Method for Windows (Non SSL / TLS Mode)</h4>
+    <p>If you are using Windows File Explorer as your WebDAV Client without TLS enabled on arozos server, <b>WebDAV will run in compatibility mode and Basic Auth is not usable. You must authenticate through this Web UI. </b> Please follow the steps below to authenticate your Windows WebDAV Client.</p>
+    <ol class="ui list">
+        <li>Mount the WebDAV endpoint in your File Explorer (Endpoint: <span class="protocol"></span>//<span class="hostname"></span>:<span class="port"></span>/webdav/{vroot_name})</li>
+        <li>Connect with no username and password</li>
+        <li>You will be directed to a READONLY directory. Refresh this page and see if there is a new client pop up in the "Access Pending Clients" list.</li>
+        <li>Click "Allow Access" to assign your user storage pool to be accessiable by this Windows WebDAV Client(s). (Sometime more than 1 conncetions will be used by the same File Explorer)</li>
+        <li>Refresh your file list in File Explorer and your root folder (user:/) should be listed</li>
+    </ol>
+</div>
+<div class="ui blue message windowonly tls" style="display:none">
+    <h4><i class="windows icon"></i> Login Method for Windows (With SSL / TLS MODE)</h4>
+    <p>If you are using Windows File Explorer as your WebDAV Client, go to "My Computer" and add a network drive with the following endpoint: <span class="protocol"></span>//<span class="hostname"></span>:<span class="port"></span>/webdav/{vroot_name}</p>
+</div>
+
+<script>
+    var isMac = navigator.platform.indexOf('Mac') > -1;
+    var isWindows = navigator.platform.indexOf('Win') > -1;
+    
+    //Update tutorial information
+    $(".hostname").text(window.location.hostname);
+    $(".port").text(window.location.port);
+    if (window.location.port == ""){
+        $(".port").text("80");
+    }
+    $(".protocol").text(location.protocol);
+    if (isMac){
+        $(".maconly").show();
+    }else if (isWindows){
+        $(".windowonly").show();
+    }
+
+    //Check if running in HTTPS mode. If yes, hide this functions
+    if (location.protocol == 'https:'){
+        $("#nontlsWindowsSettings").hide();
+        $(".nontls").hide();
+    }else{
+        $(".tls").hide();
+    }
+</script>

+ 85 - 16
web/SystemAO/disk/services.html

@@ -36,6 +36,7 @@
 
         .servertype.active{
             background-color: #f7f7f7  !important;
+            border-right: 3px solid #6bc2ec !important;
         }
 
         .clickable{
@@ -46,6 +47,11 @@
             margin-bottom: 1em;
         }
 
+        .serviceConnEndpoints{
+            margin-top: 0 !important;
+            display: inline-block;
+            vertical-align:top;
+        }
         
         .ui.toggle.checkbox input[type=checkbox]:checked~label::before,
         .ui.toggle.checkbox input[type=checkbox]:checked:focus~label::before {
@@ -65,20 +71,38 @@
         <br>
         <div class="ui stackable grid">
             <div class="six wide column" style="border-right: 1px solid #e0e0e0;">
-                <div class="ui clickable servertype segment" style="margin-bottom: 1em;">
-                    
-                </div>
                 <div id="serviceList"></div>
+                <div class="ui divider"></div>
+                <!-- 
+                <div style="width: 100%;" align="center">
+                    <button title="Start All Services" onclick="" class="circular basic green large ui icon button">
+                        <i class="green play icon"></i>
+                    </button>
+                    <button title="Stop All Services" onclick="" class="circular basic red large ui icon button">
+                        <i class="red stop icon"></i>
+                    </button>
+                </div>
+                -->
             </div>
-            <div class="ten wide column" id="serviceSettings">
-                
+            <div class="ten wide column">
+                <div id="serviceInstruction"></div>
+                <div class="ui divider"></div>
+                <div id="serviceSettings"></div>
             </div>
         </div>
         <br><br>
     </div>
     <script>
+        var isAdmin = false;
         var fileServerServices = [];
-        initServiceList();
+        
+        function isAdminCheck(){
+            $.get("../../system/desktop/user?noicon=true", function(data){
+                isAdmin = data.IsAdmin;
+                initServiceList();
+            });
+        }
+        isAdminCheck();
 
         function initServiceList(){
             $("#serviceList").html("");
@@ -95,7 +119,7 @@
                     }
                     $("#serviceList").append(`<div class="fileservice">
                         <div class="ui clickable servertype top attached segment" uuid="${server.ID}" onclick="openServerConfig('${server.ID}', event, this);">
-                            <h4 class="ui header storagepool">
+                            <h4 class="ui header">
                                 <img src="../../${server.IconPath}">
                                 <div class="content">
                                     ${server.Name}
@@ -109,14 +133,20 @@
                             </a>
                         </div>
                         <div class="ui bottom attached ${messageDefaultColor} mini message">
-                            <div class="ui toggle mini checkbox">
+                            <div class="ui toggle mini checkbox enableState" fserv="${server.ID}" onclick="handleServiceStateChange(this, event);">
                                 <input type="checkbox" ${checkboxDefaultState} onchange="updateCheckboxBackground(this, event);">
-                                <label>Toggle Service </label>
+                                <label></label>
                             </div>
                             <p fserv="${server.ID}" class="serviceConnEndpoints"></p>
                     </div>`);
                 });
 
+                if (data.length > 0){
+                    openServerConfig(data[0].ID);
+                    $($(".servertype")[0]).addClass("active");
+                }
+
+                //Get a list of endpoints that is dedicated for this file server
                 $.get("../../system/network/server/endpoints", function(data){
                     for (let [id, endpoints] of Object.entries(data)){
                         console.log(id, endpoints)
@@ -152,13 +182,45 @@
                             }, 3000);
                             e.clearSelection();
                         });
-                        
-
+                        $(".ui.checkbox").checkbox();
+                        updateAllServiceState();
                     }
+                });
+            });
+       }
 
-                    
+        function updateAllServiceState(){
+            $.get("../../system/network/server/status", function(data){
+                $(".enableState").each(function(){
+                    var fservid = $(this).attr("fserv");
+                    var enableState = data[fservid];
+                    if (enableState){
+                        $(this).checkbox("set checked");
+                    }else{
+                        $(this).checkbox("set unchecked");
+                    }
                 });
             });
+        }
+
+       function handleServiceStateChange(object, event){
+            event.preventDefault();
+            let targetElement = object;
+            setTimeout(function(){
+                //This delay is to make sure the checkbox event is executed first
+                //Before the state is checked
+                let isChecked = $(targetElement).checkbox("is checked");
+                let isCheckedString = isChecked?"true":"false";
+                let serviceId = $(targetElement).attr("fserv");
+                $.ajax({
+                    url: "../../system/network/server/toggle",
+                    method: "POST",
+                    data: {id: serviceId, enable: isCheckedString},
+                    success: function(){
+                        updateAllServiceState();
+                    }
+                })
+            }, 100);
        }
 
        function getServiceById(serviceid){
@@ -169,17 +231,24 @@
             }
        }
 
-       function openServerConfig(serviceid, evt, object){
-            evt.preventDefault();
+       function openServerConfig(serviceid, evt=undefined, object=undefined){
+            if (evt != undefined){
+                evt.preventDefault();
+            }
             $(".servertype.active").removeClass("active");
             $(object).addClass("active");
-            console.log(object);
             let targetService = getServiceById(serviceid);
             let targetServiceConfigPage = targetService.ConfigPage;
             if (targetServiceConfigPage == "" || targetServiceConfigPage == undefined){
                 $("#serviceSettings").html(`No Configuration Avabile`);
             }else{
-                $("#serviceSettings").load("../../" + targetService.ConfigPage);
+                $("#serviceInstruction").load("../../" + targetService.ConnInstrPage);
+                if (isAdmin){
+                    $("#serviceSettings").load("../../" + targetService.ConfigPage);
+                }else{
+                    $("#serviceSettings").load("../disk/instr/noaccess.html");
+                }
+               
             }
        }    
 

+ 82 - 0
web/SystemAO/disk/sftp.html

@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+    
+	<meta name="mobile-web-app-capable" content="yes">
+	<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
+	<meta charset="UTF-8">
+    <link rel="stylesheet" href="../../script/semantic/semantic.min.css">
+    <script src="../../script/jquery.min.js"></script>
+	<script src="../../script/semantic/semantic.min.js"></script>
+    
+    <style>
+        .hidden{
+            display:none;
+        }
+
+        .disabled{
+            opacity: 0.5;
+            pointer-events: none;
+        }
+    </style>
+</head>
+<body>
+    <div class="ui form">
+        <div class="field">
+            <div class="ui toggle checkbox">
+                <input id="useUPNP" type="checkbox" name="upnp" onchange="toggleUPNP(this.checked);">
+                <label>Enable UPnP on SFTP Server Port</label>
+                <small>Aka Auto Port Forwarding. Disable this option if you are connecting within Local Area Network</small>
+            </div>
+        </div>
+        <div class="field">
+            <label>Listening Port</label>
+            <div class="ui labeled input">
+                <input id="listeningPort" type="number" placeholder="2022" min="22" onchange="updateFTPPort(this.value)">
+            </div>
+        </div>
+        
+
+        <div id="ok" class="ui secondary inverted green segment" style="display:none;">
+            <i class="checkmark icon"></i> Setting Applied
+        </div>
+        <div id="error" class="ui secondary inverted red segment" style="display:none;">
+            <i class="remove icon"></i> <span class="msg">Something went wrong</span>
+        </div>
+        <div class="ui divider"></div>
+       
+    </div>
+    <br><br>
+    <script>
+        var serverAllowUPNP = false;
+
+        function updateFTPPort(portNumber){
+            if (portNumber < 21){
+                showError("Port number must be > 21")
+                return
+            }
+            $.ajax({
+                url: "../../system/storage/ftp/setPort",
+                data: {port: portNumber},
+                success: function(data){
+                    if (data.error !== undefined){
+                        showError(data.error);
+                    }else{
+                        showOK();
+                    }
+                }
+            })
+        }
+
+        function showOK(){
+            $("#ok").stop().finish().slideDown("fast").delay(3000).slideUp("fast");
+        }
+
+        function showError(msg){
+            $("#error").find(".msg").text(msg);
+            $("#error").stop().finish().slideDown("fast").delay(3000).slideUp("fast");
+        }
+       
+    </script>
+</body>
+</html>

+ 82 - 112
web/SystemAO/disk/webdav.html

@@ -1,12 +1,14 @@
 <!DOCTYPE html>
 <html>
 <head>
+    <!-- 
 	<meta name="mobile-web-app-capable" content="yes">
 	<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
 	<meta charset="UTF-8">
     <link rel="stylesheet" href="../../script/semantic/semantic.min.css">
     <script src="../../script/jquery.min.js"></script>
 	<script src="../../script/semantic/semantic.min.js"></script>
+    -->
     <style>
         .hidden{
             display:none;
@@ -19,111 +21,90 @@
     </style>
 </head>
 <body>
-    <br>
-   <div class="ui container">
-        <div class="ui message maconly" style="display:none">
-            <h4><i class="apple icon"></i> Login Method for MacOS</h4>
-            <p>If you are using MacOS, you can select "Connect to Network Server", enter the endpoint of the arozos WebDAV service (<span class="protocol"></span>//<span class="hostname"></span>:<span class="port"></span>/webdav/{vroot_name}) and login with your arozos username and password.</p>
-        </div>
-        <div class="ui blue message windowonly nontls" style="display:none">
-            <h4><i class="windows icon"></i> Login Method for Windows (Non SSL / TLS Mode)</h4>
-            <p>If you are using Windows File Explorer as your WebDAV Client without TLS enabled on arozos server, <b>WebDAV will run in compatibility mode and Basic Auth is not usable. You must authenticate through this Web UI. </b> Please follow the steps below to authenticate your Windows WebDAV Client.</p>
-            <ol class="ui list">
-                <li>Mount the WebDAV endpoint in your File Explorer (Endpoint: <span class="protocol"></span>//<span class="hostname"></span>:<span class="port"></span>/webdav/{vroot_name})</li>
-                <li>Connect with no username and password</li>
-                <li>You will be directed to a READONLY directory. Refresh this page and see if there is a new client pop up in the "Access Pending Clients" list.</li>
-                <li>Click "Allow Access" to assign your user storage pool to be accessiable by this Windows WebDAV Client(s). (Sometime more than 1 conncetions will be used by the same File Explorer)</li>
-                <li>Refresh your file list in File Explorer and your root folder (user:/) should be listed</li>
-            </ol>
-        </div>
-        <div class="ui blue message windowonly tls" style="display:none">
-            <h4><i class="windows icon"></i> Login Method for Windows (With SSL / TLS MODE)</h4>
-            <p>If you are using Windows File Explorer as your WebDAV Client, go to "My Computer" and add a network drive with the following endpoint: <span class="protocol"></span>//<span class="hostname"></span>:<span class="port"></span>/webdav/{vroot_name}</p>
- 
-        </div>
-        <!-- General Settings -->
-        <h3>Basic Settings</h3>
-        <form class="ui form">
-            <div class="field">
-                <div class="ui toggle checkbox">
-                    <input type="checkbox" id="serverToggle" tabindex="0" class="hidden" onchange="toggleWebDAVServer(this.checked);">
-                    <label>Enable WebDAV Server</label>
-                </div>
-            </div>
-        </form>
-        <div id="status" class="ui icon message">
-            <i class="exchange icon"></i>
-            <div class="content">
-                <div class="header">
-                <span class="onlinestatus">Status: Offline</span>
-                </div>
-                <p>Server might take a few seconds to startup / shutdown after switching on and off.</p>
+    <!-- General Settings -->
+    <!-- 
+    <h3>Basic Settings</h3>
+    <form class="ui form">
+        <div class="field">
+            <div class="ui toggle checkbox">
+                <input type="checkbox" id="serverToggle" tabindex="0" class="hidden" onchange="toggleWebDAVServer(this.checked);">
+                <label>Enable WebDAV Server</label>
             </div>
         </div>
-       
-        <!-- Connection Endpoint List -->
-        <div class="ui segment">
-            <p>The following are the endpoints that you can connect via WebDAV with your user account.</p>
-            <div class="ui list" id="vrootList">
-                
+    </form>
+    <div id="status" class="ui icon message">
+        <i class="exchange icon"></i>
+        <div class="content">
+            <div class="header">
+            <span class="onlinestatus">Status: Offline</span>
             </div>
+            <p>Server might take a few seconds to startup / shutdown after switching on and off.</p>
+        </div>
+    </div>
+    -->
+    <!-- Connection Endpoint List -->
+    <!-- 
+    <div class="ui segment">
+        <p>The following are the endpoints that you can connect via WebDAV with your user account.</p>
+        <div class="ui list" id="vrootList">
+            
         </div>
-        <!-- NON TLS Windows Only Mode-->
-        <div id="nontlsWindowsSettings">
-            <br>
-            <h3>Non-TLS Windows Client Connection Settings</h3>
-            <h4>Access Pending Clients</h4>
-            <table class="ui very basic collapsing celled table">
-                <thead>
-                <tr>
-                    <th>Client IP</th>
-                    <th>Last Request</th>
-                    <th>Connection UUID</th>
-                    <th>Action</th>
-                    </tr>
-                </thead>
-                <tbody id="accessPendingClientList">
-                <tr><td>
-                        <h4 class="ui header">
-                            <div class="content">
-                                192.168.0.1
-                            </div>
-                        </h4>
-                    </td>
-                    <td>
-                        1 Jan 1970 08:00:00
-                    </td>
-                    <td>
-                        5bfba525-00ba-4d00-b8cc-7938fd8a0175
-                    </td>
-                    <td>
-                        <button class="ui tiny primary button">Allow Access</button>
-                    </td>
+    </div>
+    -->
+    <!-- NON TLS Windows Only Mode-->
+    <div id="nontlsWindowsSettings">
+        <h4><b>Non-TLS Windows Client Connection Settings</b><br>
+        <small>Connect with TLS to bypass manual client access check</h4>
+        <table class="ui very basic celled table">
+            <thead>
+            <tr>
+                <th>Client IP</th>
+                <th>Last Request</th>
+                <th>Connection UUID</th>
+                <th>Action</th>
+                </tr>
+            </thead>
+            <tbody id="accessPendingClientList">
+            <tr><td>
+                    <h4 class="ui header">
+                        <div class="content">
+                            192.168.0.1
+                        </div>
+                    </h4>
+                </td>
+                <td>
+                    1 Jan 1970 08:00:00
+                </td>
+                <td>
+                    5bfba525-00ba-4d00-b8cc-7938fd8a0175
+                </td>
+                <td>
+                    <button class="ui tiny primary button">Allow Access</button>
+                </td>
+            </tr>
+            </tbody>
+        </table>
+        <div class="ui divider"></div>
+        <h4>Access Allowed Clients</h4>
+        <table class="ui very basic celled table">
+            <thead>
+            <tr>
+                <th>Client IP</th>
+                <th>Last Request</th>
+                <th>Connection UUID</th>
+                <th>Owner</th>
+                <th>Action</th>
                 </tr>
-                </tbody>
-            </table>
-            <div class="ui divider"></div>
-            <h4>Access Allowed Clients</h4>
-            <table class="ui very basic collapsing celled table">
-                <thead>
-                <tr>
-                    <th>Client IP</th>
-                    <th>Last Request</th>
-                    <th>Connection UUID</th>
-                    <th>Owner</th>
-                    <th>Action</th>
-                    </tr>
-                </thead>
-                <tbody id="permittedClientList">
+            </thead>
+            <tbody id="permittedClientList">
 
-                </tbody>
-            </table>
-            <div class="ui divider"></div>
-            <button class="ui blue button" onclick="refreshList();"><i class="refresh icon"></i> Refresh List</button>
-            <button class="ui button" onclick="clearPendings();"><i class="remove icon"></i> Clear All Pending Request</button>
-        </div>
-        <br><br>
+            </tbody>
+        </table>
+        <div class="ui divider"></div>
+        <button class="ui blue button" onclick="refreshList();"><i class="refresh icon"></i> Refresh List</button>
+        <button class="ui button" onclick="clearPendings();"><i class="remove icon"></i> Clear All Pending Request</button>
     </div>
+    <br><br>
     <script>
     var isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0;
     var isWindows = navigator.platform.indexOf('Win') > -1;
@@ -132,7 +113,7 @@
     $(".ui.checkbox").checkbox();
 
     //Generate endpoint list
-    generateConnectionEndpointList();
+    //generateConnectionEndpointList();
 
     function toggleWebDAVServer(value){
         if (value == true){
@@ -181,7 +162,7 @@
     }
 
     //Check server toggle state
-    initServerStatus();
+    //initServerStatus();
     function initServerStatus(){
         $.get("../../system/network/webdav/status", function(data){
             if (data[0] == true){
@@ -202,18 +183,7 @@
     }
    
 
-    //Update tutorial information
-    $(".hostname").text(window.location.hostname);
-    $(".port").text(window.location.port);
-    if (window.location.port == ""){
-        $(".port").text("80");
-    }
-    $(".protocol").text(location.protocol);
-    if (isMac){
-        $(".maconly").show();
-    }else if (isWindows){
-        $(".windowonly").show();
-    }
+ 
 
     //Check if running in HTTPS mode. If yes, hide this functions
     if (location.protocol == 'https:'){

File diff suppressed because it is too large
+ 27 - 0
web/img/system/network-folder-sftp.ai


BIN
web/img/system/network-folder-sftp.png


+ 28 - 0
web/img/system/network-folder-sftp.svg

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="128px"
+	 height="128px" viewBox="0 0 128 128" enable-background="new 0 0 128 128" xml:space="preserve">
+<g id="圖層_2">
+	<polygon fill="#DBAC50" points="110.998,23.424 110.998,84.064 17.001,84.064 17.001,13.652 48.449,13.469 55.315,23.669 	"/>
+</g>
+<g id="圖層_3">
+	<polygon fill="#E5BD64" points="110.998,84.064 17.001,84.064 17.087,31.401 110.57,31.401 	"/>
+</g>
+<g id="圖層_4">
+	<rect x="17.001" y="103.51" fill="#443F5D" width="93.997" height="4.691"/>
+	<rect x="60.985" y="84.064" fill="#443F5D" width="6.029" height="19.445"/>
+	<path fill="#55516E" d="M72.935,110.512c0,2.221-1.8,4.02-4.021,4.02h-9.827c-2.221,0-4.021-1.799-4.021-4.02v-9.828
+		c0-2.221,1.8-4.021,4.021-4.021h9.827c2.221,0,4.021,1.801,4.021,4.021V110.512z"/>
+</g>
+<g id="圖層_5">
+	<path fill="#F7CB5D" d="M54.316,96.26c0,1.657-1.343,3-3,3H16.149c-1.657,0-3-1.343-3-3V70.927c0-1.657,1.343-3,3-3h35.167
+		c1.657,0,3,1.343,3,3V96.26z"/>
+	<path fill="none" stroke="#DCDDDD" stroke-width="4" stroke-miterlimit="10" d="M23.566,72.684V56.35
+		c0-4.928,4.547-8.917,10.167-8.917c5.527,0,10,3.988,10,8.917v16.333"/>
+	<path fill="#F7DB78" d="M49.066,96.257c0,1.657-1.536,3-3.43,3H15.913c-1.894,0-3.43-1.343-3.43-3v-25.33c0-1.657,1.536-3,3.43-3
+		h29.724c1.894,0,3.43,1.343,3.43,3V96.257z"/>
+	<path fill="#55516E" d="M35.399,88.049c0,0.897-0.728,1.625-1.625,1.625l0,0c-0.897,0-1.625-0.728-1.625-1.625v-7
+		c0-0.897,0.728-1.625,1.625-1.625l0,0c0.897,0,1.625,0.728,1.625,1.625V88.049z"/>
+</g>
+</svg>

Some files were not shown because too many files changed in this diff