common.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. // Package common defines code shared among file transfer packages and protocols
  2. package common
  3. import (
  4. "context"
  5. "errors"
  6. "fmt"
  7. "net"
  8. "net/http"
  9. "net/url"
  10. "os"
  11. "os/exec"
  12. "path/filepath"
  13. "strings"
  14. "sync"
  15. "sync/atomic"
  16. "time"
  17. "github.com/pires/go-proxyproto"
  18. "github.com/drakkan/sftpgo/dataprovider"
  19. "github.com/drakkan/sftpgo/httpclient"
  20. "github.com/drakkan/sftpgo/logger"
  21. "github.com/drakkan/sftpgo/metrics"
  22. "github.com/drakkan/sftpgo/utils"
  23. )
  24. // constants
  25. const (
  26. logSender = "common"
  27. uploadLogSender = "Upload"
  28. downloadLogSender = "Download"
  29. renameLogSender = "Rename"
  30. rmdirLogSender = "Rmdir"
  31. mkdirLogSender = "Mkdir"
  32. symlinkLogSender = "Symlink"
  33. removeLogSender = "Remove"
  34. chownLogSender = "Chown"
  35. chmodLogSender = "Chmod"
  36. chtimesLogSender = "Chtimes"
  37. truncateLogSender = "Truncate"
  38. operationDownload = "download"
  39. operationUpload = "upload"
  40. operationDelete = "delete"
  41. operationPreDelete = "pre-delete"
  42. operationRename = "rename"
  43. operationSSHCmd = "ssh_cmd"
  44. chtimesFormat = "2006-01-02T15:04:05" // YYYY-MM-DDTHH:MM:SS
  45. idleTimeoutCheckInterval = 3 * time.Minute
  46. )
  47. // Stat flags
  48. const (
  49. StatAttrUIDGID = 1
  50. StatAttrPerms = 2
  51. StatAttrTimes = 4
  52. StatAttrSize = 8
  53. )
  54. // Transfer types
  55. const (
  56. TransferUpload = iota
  57. TransferDownload
  58. )
  59. // Supported protocols
  60. const (
  61. ProtocolSFTP = "SFTP"
  62. ProtocolSCP = "SCP"
  63. ProtocolSSH = "SSH"
  64. ProtocolFTP = "FTP"
  65. ProtocolWebDAV = "DAV"
  66. )
  67. // Upload modes
  68. const (
  69. UploadModeStandard = iota
  70. UploadModeAtomic
  71. UploadModeAtomicWithResume
  72. )
  73. // errors definitions
  74. var (
  75. ErrPermissionDenied = errors.New("permission denied")
  76. ErrNotExist = errors.New("no such file or directory")
  77. ErrOpUnsupported = errors.New("operation unsupported")
  78. ErrGenericFailure = errors.New("failure")
  79. ErrQuotaExceeded = errors.New("denying write due to space limit")
  80. ErrSkipPermissionsCheck = errors.New("permission check skipped")
  81. ErrConnectionDenied = errors.New("You are not allowed to connect")
  82. errNoTransfer = errors.New("requested transfer not found")
  83. errTransferMismatch = errors.New("transfer mismatch")
  84. )
  85. var (
  86. // Config is the configuration for the supported protocols
  87. Config Configuration
  88. // Connections is the list of active connections
  89. Connections ActiveConnections
  90. // QuotaScans is the list of active quota scans
  91. QuotaScans ActiveScans
  92. idleTimeoutTicker *time.Ticker
  93. idleTimeoutTickerDone chan bool
  94. supportedProtocols = []string{ProtocolSFTP, ProtocolSCP, ProtocolSSH, ProtocolFTP, ProtocolWebDAV}
  95. )
  96. // Initialize sets the common configuration
  97. func Initialize(c Configuration) {
  98. Config = c
  99. Config.idleLoginTimeout = 2 * time.Minute
  100. Config.idleTimeoutAsDuration = time.Duration(Config.IdleTimeout) * time.Minute
  101. if Config.IdleTimeout > 0 {
  102. startIdleTimeoutTicker(idleTimeoutCheckInterval)
  103. }
  104. }
  105. func startIdleTimeoutTicker(duration time.Duration) {
  106. stopIdleTimeoutTicker()
  107. idleTimeoutTicker = time.NewTicker(duration)
  108. idleTimeoutTickerDone = make(chan bool)
  109. go func() {
  110. for {
  111. select {
  112. case <-idleTimeoutTickerDone:
  113. return
  114. case <-idleTimeoutTicker.C:
  115. Connections.checkIdles()
  116. }
  117. }
  118. }()
  119. }
  120. func stopIdleTimeoutTicker() {
  121. if idleTimeoutTicker != nil {
  122. idleTimeoutTicker.Stop()
  123. idleTimeoutTickerDone <- true
  124. idleTimeoutTicker = nil
  125. }
  126. }
  127. // ActiveTransfer defines the interface for the current active transfers
  128. type ActiveTransfer interface {
  129. GetID() uint64
  130. GetType() int
  131. GetSize() int64
  132. GetVirtualPath() string
  133. GetStartTime() time.Time
  134. SignalClose()
  135. Truncate(fsPath string, size int64) (int64, error)
  136. GetRealFsPath(fsPath string) string
  137. }
  138. // ActiveConnection defines the interface for the current active connections
  139. type ActiveConnection interface {
  140. GetID() string
  141. GetUsername() string
  142. GetRemoteAddress() string
  143. GetClientVersion() string
  144. GetProtocol() string
  145. GetConnectionTime() time.Time
  146. GetLastActivity() time.Time
  147. GetCommand() string
  148. Disconnect() error
  149. AddTransfer(t ActiveTransfer)
  150. RemoveTransfer(t ActiveTransfer)
  151. GetTransfers() []ConnectionTransfer
  152. }
  153. // StatAttributes defines the attributes for set stat commands
  154. type StatAttributes struct {
  155. Mode os.FileMode
  156. Atime time.Time
  157. Mtime time.Time
  158. UID int
  159. GID int
  160. Flags int
  161. Size int64
  162. }
  163. // ConnectionTransfer defines the trasfer details to expose
  164. type ConnectionTransfer struct {
  165. ID uint64 `json:"-"`
  166. OperationType string `json:"operation_type"`
  167. StartTime int64 `json:"start_time"`
  168. Size int64 `json:"size"`
  169. VirtualPath string `json:"path"`
  170. }
  171. func (t *ConnectionTransfer) getConnectionTransferAsString() string {
  172. result := ""
  173. switch t.OperationType {
  174. case operationUpload:
  175. result += "UL "
  176. case operationDownload:
  177. result += "DL "
  178. }
  179. result += fmt.Sprintf("%#v ", t.VirtualPath)
  180. if t.Size > 0 {
  181. elapsed := time.Since(utils.GetTimeFromMsecSinceEpoch(t.StartTime))
  182. speed := float64(t.Size) / float64(utils.GetTimeAsMsSinceEpoch(time.Now())-t.StartTime)
  183. result += fmt.Sprintf("Size: %#v Elapsed: %#v Speed: \"%.1f KB/s\"", utils.ByteCountSI(t.Size),
  184. utils.GetDurationAsString(elapsed), speed)
  185. }
  186. return result
  187. }
  188. // Configuration defines configuration parameters common to all supported protocols
  189. type Configuration struct {
  190. // Maximum idle timeout as minutes. If a client is idle for a time that exceeds this setting it will be disconnected.
  191. // 0 means disabled
  192. IdleTimeout int `json:"idle_timeout" mapstructure:"idle_timeout"`
  193. // UploadMode 0 means standard, the files are uploaded directly to the requested path.
  194. // 1 means atomic: the files are uploaded to a temporary path and renamed to the requested path
  195. // when the client ends the upload. Atomic mode avoid problems such as a web server that
  196. // serves partial files when the files are being uploaded.
  197. // In atomic mode if there is an upload error the temporary file is deleted and so the requested
  198. // upload path will not contain a partial file.
  199. // 2 means atomic with resume support: as atomic but if there is an upload error the temporary
  200. // file is renamed to the requested path and not deleted, this way a client can reconnect and resume
  201. // the upload.
  202. UploadMode int `json:"upload_mode" mapstructure:"upload_mode"`
  203. // Actions to execute for SFTP file operations and SSH commands
  204. Actions ProtocolActions `json:"actions" mapstructure:"actions"`
  205. // SetstatMode 0 means "normal mode": requests for changing permissions and owner/group are executed.
  206. // 1 means "ignore mode": requests for changing permissions and owner/group are silently ignored.
  207. SetstatMode int `json:"setstat_mode" mapstructure:"setstat_mode"`
  208. // Support for HAProxy PROXY protocol.
  209. // If you are running SFTPGo behind a proxy server such as HAProxy, AWS ELB or NGNIX, you can enable
  210. // the proxy protocol. It provides a convenient way to safely transport connection information
  211. // such as a client's address across multiple layers of NAT or TCP proxies to get the real
  212. // client IP address instead of the proxy IP. Both protocol versions 1 and 2 are supported.
  213. // - 0 means disabled
  214. // - 1 means proxy protocol enabled. Proxy header will be used and requests without proxy header will be accepted.
  215. // - 2 means proxy protocol required. Proxy header will be used and requests without proxy header will be rejected.
  216. // If the proxy protocol is enabled in SFTPGo then you have to enable the protocol in your proxy configuration too,
  217. // for example for HAProxy add "send-proxy" or "send-proxy-v2" to each server configuration line.
  218. ProxyProtocol int `json:"proxy_protocol" mapstructure:"proxy_protocol"`
  219. // List of IP addresses and IP ranges allowed to send the proxy header.
  220. // If proxy protocol is set to 1 and we receive a proxy header from an IP that is not in the list then the
  221. // connection will be accepted and the header will be ignored.
  222. // If proxy protocol is set to 2 and we receive a proxy header from an IP that is not in the list then the
  223. // connection will be rejected.
  224. ProxyAllowed []string `json:"proxy_allowed" mapstructure:"proxy_allowed"`
  225. // Absolute path to an external program or an HTTP URL to invoke after a user connects
  226. // and before he tries to login. It allows you to reject the connection based on the source
  227. // ip address. Leave empty do disable.
  228. PostConnectHook string `json:"post_connect_hook" mapstructure:"post_connect_hook"`
  229. idleTimeoutAsDuration time.Duration
  230. idleLoginTimeout time.Duration
  231. }
  232. // IsAtomicUploadEnabled returns true if atomic upload is enabled
  233. func (c *Configuration) IsAtomicUploadEnabled() bool {
  234. return c.UploadMode == UploadModeAtomic || c.UploadMode == UploadModeAtomicWithResume
  235. }
  236. // GetProxyListener returns a wrapper for the given listener that supports the
  237. // HAProxy Proxy Protocol or nil if the proxy protocol is not configured
  238. func (c *Configuration) GetProxyListener(listener net.Listener) (*proxyproto.Listener, error) {
  239. var proxyListener *proxyproto.Listener
  240. var err error
  241. if c.ProxyProtocol > 0 {
  242. var policyFunc func(upstream net.Addr) (proxyproto.Policy, error)
  243. if c.ProxyProtocol == 1 && len(c.ProxyAllowed) > 0 {
  244. policyFunc, err = proxyproto.LaxWhiteListPolicy(c.ProxyAllowed)
  245. if err != nil {
  246. return nil, err
  247. }
  248. }
  249. if c.ProxyProtocol == 2 {
  250. if len(c.ProxyAllowed) == 0 {
  251. policyFunc = func(upstream net.Addr) (proxyproto.Policy, error) {
  252. return proxyproto.REQUIRE, nil
  253. }
  254. } else {
  255. policyFunc, err = proxyproto.StrictWhiteListPolicy(c.ProxyAllowed)
  256. if err != nil {
  257. return nil, err
  258. }
  259. }
  260. }
  261. proxyListener = &proxyproto.Listener{
  262. Listener: listener,
  263. Policy: policyFunc,
  264. }
  265. }
  266. return proxyListener, nil
  267. }
  268. // ExecutePostConnectHook executes the post connect hook if defined
  269. func (c *Configuration) ExecutePostConnectHook(remoteAddr, protocol string) error {
  270. if len(c.PostConnectHook) == 0 {
  271. return nil
  272. }
  273. ip := utils.GetIPFromRemoteAddress(remoteAddr)
  274. if strings.HasPrefix(c.PostConnectHook, "http") {
  275. var url *url.URL
  276. url, err := url.Parse(c.PostConnectHook)
  277. if err != nil {
  278. logger.Warn(protocol, "", "Login from ip %#v denied, invalid post connect hook %#v: %v",
  279. ip, c.PostConnectHook, err)
  280. return err
  281. }
  282. httpClient := httpclient.GetHTTPClient()
  283. q := url.Query()
  284. q.Add("ip", ip)
  285. q.Add("protocol", protocol)
  286. url.RawQuery = q.Encode()
  287. resp, err := httpClient.Get(url.String())
  288. if err != nil {
  289. logger.Warn(protocol, "", "Login from ip %#v denied, error executing post connect hook: %v", ip, err)
  290. return err
  291. }
  292. defer resp.Body.Close()
  293. if resp.StatusCode != http.StatusOK {
  294. logger.Warn(protocol, "", "Login from ip %#v denied, post connect hook response code: %v", ip, resp.StatusCode)
  295. return errUnexpectedHTTResponse
  296. }
  297. return nil
  298. }
  299. if !filepath.IsAbs(c.PostConnectHook) {
  300. err := fmt.Errorf("invalid post connect hook %#v", c.PostConnectHook)
  301. logger.Warn(protocol, "", "Login from ip %#v denied: %v", ip, err)
  302. return err
  303. }
  304. ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
  305. defer cancel()
  306. cmd := exec.CommandContext(ctx, c.PostConnectHook)
  307. cmd.Env = append(os.Environ(),
  308. fmt.Sprintf("SFTPGO_CONNECTION_IP=%v", ip),
  309. fmt.Sprintf("SFTPGO_CONNECTION_PROTOCOL=%v", protocol))
  310. err := cmd.Run()
  311. if err != nil {
  312. logger.Warn(protocol, "", "Login from ip %#v denied, connect hook error: %v", ip, err)
  313. }
  314. return err
  315. }
  316. // SSHConnection defines an ssh connection.
  317. // Each SSH connection can open several channels for SFTP or SSH commands
  318. type SSHConnection struct {
  319. id string
  320. conn net.Conn
  321. lastActivity int64
  322. }
  323. // NewSSHConnection returns a new SSHConnection
  324. func NewSSHConnection(id string, conn net.Conn) *SSHConnection {
  325. return &SSHConnection{
  326. id: id,
  327. conn: conn,
  328. lastActivity: time.Now().UnixNano(),
  329. }
  330. }
  331. // GetID returns the ID for this SSHConnection
  332. func (c *SSHConnection) GetID() string {
  333. return c.id
  334. }
  335. // UpdateLastActivity updates last activity for this connection
  336. func (c *SSHConnection) UpdateLastActivity() {
  337. atomic.StoreInt64(&c.lastActivity, time.Now().UnixNano())
  338. }
  339. // GetLastActivity returns the last connection activity
  340. func (c *SSHConnection) GetLastActivity() time.Time {
  341. return time.Unix(0, atomic.LoadInt64(&c.lastActivity))
  342. }
  343. // Close closes the underlying network connection
  344. func (c *SSHConnection) Close() error {
  345. return c.conn.Close()
  346. }
  347. // ActiveConnections holds the currect active connections with the associated transfers
  348. type ActiveConnections struct {
  349. sync.RWMutex
  350. connections []ActiveConnection
  351. sshConnections []*SSHConnection
  352. }
  353. // GetActiveSessions returns the number of active sessions for the given username.
  354. // We return the open sessions for any protocol
  355. func (conns *ActiveConnections) GetActiveSessions(username string) int {
  356. conns.RLock()
  357. defer conns.RUnlock()
  358. numSessions := 0
  359. for _, c := range conns.connections {
  360. if c.GetUsername() == username {
  361. numSessions++
  362. }
  363. }
  364. return numSessions
  365. }
  366. // Add adds a new connection to the active ones
  367. func (conns *ActiveConnections) Add(c ActiveConnection) {
  368. conns.Lock()
  369. defer conns.Unlock()
  370. conns.connections = append(conns.connections, c)
  371. metrics.UpdateActiveConnectionsSize(len(conns.connections))
  372. logger.Debug(c.GetProtocol(), c.GetID(), "connection added, num open connections: %v", len(conns.connections))
  373. }
  374. // Swap replaces an existing connection with the given one.
  375. // This method is useful if you have to change some connection details
  376. // for example for FTP is used to update the connection once the user
  377. // authenticates
  378. func (conns *ActiveConnections) Swap(c ActiveConnection) error {
  379. conns.Lock()
  380. defer conns.Unlock()
  381. for idx, conn := range conns.connections {
  382. if conn.GetID() == c.GetID() {
  383. conn = nil
  384. conns.connections[idx] = c
  385. return nil
  386. }
  387. }
  388. return errors.New("connection to swap not found")
  389. }
  390. // Remove removes a connection from the active ones
  391. func (conns *ActiveConnections) Remove(connectionID string) {
  392. conns.Lock()
  393. defer conns.Unlock()
  394. for idx, conn := range conns.connections {
  395. if conn.GetID() == connectionID {
  396. lastIdx := len(conns.connections) - 1
  397. conns.connections[idx] = conns.connections[lastIdx]
  398. conns.connections[lastIdx] = nil
  399. conns.connections = conns.connections[:lastIdx]
  400. metrics.UpdateActiveConnectionsSize(lastIdx)
  401. logger.Debug(conn.GetProtocol(), conn.GetID(), "connection removed, num open connections: %v", lastIdx)
  402. return
  403. }
  404. }
  405. logger.Warn(logSender, "", "connection id %#v to remove not found!", connectionID)
  406. }
  407. // Close closes an active connection.
  408. // It returns true on success
  409. func (conns *ActiveConnections) Close(connectionID string) bool {
  410. conns.RLock()
  411. result := false
  412. for _, c := range conns.connections {
  413. if c.GetID() == connectionID {
  414. defer func(conn ActiveConnection) {
  415. err := conn.Disconnect()
  416. logger.Debug(conn.GetProtocol(), conn.GetID(), "close connection requested, close err: %v", err)
  417. }(c)
  418. result = true
  419. break
  420. }
  421. }
  422. conns.RUnlock()
  423. return result
  424. }
  425. // AddSSHConnection adds a new ssh connection to the active ones
  426. func (conns *ActiveConnections) AddSSHConnection(c *SSHConnection) {
  427. conns.Lock()
  428. defer conns.Unlock()
  429. conns.sshConnections = append(conns.sshConnections, c)
  430. logger.Debug(logSender, c.GetID(), "ssh connection added, num open connections: %v", len(conns.sshConnections))
  431. }
  432. // RemoveSSHConnection removes a connection from the active ones
  433. func (conns *ActiveConnections) RemoveSSHConnection(connectionID string) {
  434. conns.Lock()
  435. defer conns.Unlock()
  436. for idx, conn := range conns.sshConnections {
  437. if conn.GetID() == connectionID {
  438. lastIdx := len(conns.sshConnections) - 1
  439. conns.sshConnections[idx] = conns.sshConnections[lastIdx]
  440. conns.sshConnections[lastIdx] = nil
  441. conns.sshConnections = conns.sshConnections[:lastIdx]
  442. logger.Debug(logSender, conn.GetID(), "ssh connection removed, num open ssh connections: %v", lastIdx)
  443. return
  444. }
  445. }
  446. logger.Warn(logSender, "", "ssh connection to remove with id %#v not found!", connectionID)
  447. }
  448. func (conns *ActiveConnections) checkIdles() {
  449. conns.RLock()
  450. for _, sshConn := range conns.sshConnections {
  451. idleTime := time.Since(sshConn.GetLastActivity())
  452. if idleTime > Config.idleTimeoutAsDuration {
  453. // we close the an ssh connection if it has no active connections associated
  454. idToMatch := fmt.Sprintf("_%v_", sshConn.GetID())
  455. toClose := true
  456. for _, conn := range conns.connections {
  457. if strings.Contains(conn.GetID(), idToMatch) {
  458. toClose = false
  459. break
  460. }
  461. }
  462. if toClose {
  463. defer func(c *SSHConnection) {
  464. err := c.Close()
  465. logger.Debug(logSender, c.GetID(), "close idle SSH connection, idle time: %v, close err: %v",
  466. time.Since(c.GetLastActivity()), err)
  467. }(sshConn)
  468. }
  469. }
  470. }
  471. for _, c := range conns.connections {
  472. idleTime := time.Since(c.GetLastActivity())
  473. isUnauthenticatedFTPUser := (c.GetProtocol() == ProtocolFTP && len(c.GetUsername()) == 0)
  474. if idleTime > Config.idleTimeoutAsDuration || (isUnauthenticatedFTPUser && idleTime > Config.idleLoginTimeout) {
  475. defer func(conn ActiveConnection, isFTPNoAuth bool) {
  476. err := conn.Disconnect()
  477. logger.Debug(conn.GetProtocol(), conn.GetID(), "close idle connection, idle time: %v, username: %#v close err: %v",
  478. time.Since(conn.GetLastActivity()), conn.GetUsername(), err)
  479. if isFTPNoAuth {
  480. ip := utils.GetIPFromRemoteAddress(c.GetRemoteAddress())
  481. logger.ConnectionFailedLog("", ip, dataprovider.LoginMethodNoAuthTryed, c.GetProtocol(), "client idle")
  482. metrics.AddNoAuthTryed()
  483. dataprovider.ExecutePostLoginHook("", dataprovider.LoginMethodNoAuthTryed, ip, c.GetProtocol(),
  484. dataprovider.ErrNoAuthTryed)
  485. }
  486. }(c, isUnauthenticatedFTPUser)
  487. }
  488. }
  489. conns.RUnlock()
  490. }
  491. // GetStats returns stats for active connections
  492. func (conns *ActiveConnections) GetStats() []ConnectionStatus {
  493. conns.RLock()
  494. defer conns.RUnlock()
  495. stats := make([]ConnectionStatus, 0, len(conns.connections))
  496. for _, c := range conns.connections {
  497. stat := ConnectionStatus{
  498. Username: c.GetUsername(),
  499. ConnectionID: c.GetID(),
  500. ClientVersion: c.GetClientVersion(),
  501. RemoteAddress: c.GetRemoteAddress(),
  502. ConnectionTime: utils.GetTimeAsMsSinceEpoch(c.GetConnectionTime()),
  503. LastActivity: utils.GetTimeAsMsSinceEpoch(c.GetLastActivity()),
  504. Protocol: c.GetProtocol(),
  505. Command: c.GetCommand(),
  506. Transfers: c.GetTransfers(),
  507. }
  508. stats = append(stats, stat)
  509. }
  510. return stats
  511. }
  512. // ConnectionStatus returns the status for an active connection
  513. type ConnectionStatus struct {
  514. // Logged in username
  515. Username string `json:"username"`
  516. // Unique identifier for the connection
  517. ConnectionID string `json:"connection_id"`
  518. // client's version string
  519. ClientVersion string `json:"client_version,omitempty"`
  520. // Remote address for this connection
  521. RemoteAddress string `json:"remote_address"`
  522. // Connection time as unix timestamp in milliseconds
  523. ConnectionTime int64 `json:"connection_time"`
  524. // Last activity as unix timestamp in milliseconds
  525. LastActivity int64 `json:"last_activity"`
  526. // Protocol for this connection
  527. Protocol string `json:"protocol"`
  528. // active uploads/downloads
  529. Transfers []ConnectionTransfer `json:"active_transfers,omitempty"`
  530. // SSH command or WevDAV method
  531. Command string `json:"command,omitempty"`
  532. }
  533. // GetConnectionDuration returns the connection duration as string
  534. func (c ConnectionStatus) GetConnectionDuration() string {
  535. elapsed := time.Since(utils.GetTimeFromMsecSinceEpoch(c.ConnectionTime))
  536. return utils.GetDurationAsString(elapsed)
  537. }
  538. // GetConnectionInfo returns connection info.
  539. // Protocol,Client Version and RemoteAddress are returned.
  540. // For SSH commands the issued command is returned too.
  541. func (c ConnectionStatus) GetConnectionInfo() string {
  542. result := fmt.Sprintf("%v. Client: %#v From: %#v", c.Protocol, c.ClientVersion, c.RemoteAddress)
  543. if c.Protocol == ProtocolSSH && len(c.Command) > 0 {
  544. result += fmt.Sprintf(". Command: %#v", c.Command)
  545. }
  546. if c.Protocol == ProtocolWebDAV && len(c.Command) > 0 {
  547. result += fmt.Sprintf(". Method: %#v", c.Command)
  548. }
  549. return result
  550. }
  551. // GetTransfersAsString returns the active transfers as string
  552. func (c ConnectionStatus) GetTransfersAsString() string {
  553. result := ""
  554. for _, t := range c.Transfers {
  555. if len(result) > 0 {
  556. result += ". "
  557. }
  558. result += t.getConnectionTransferAsString()
  559. }
  560. return result
  561. }
  562. // ActiveQuotaScan defines an active quota scan for a user home dir
  563. type ActiveQuotaScan struct {
  564. // Username to which the quota scan refers
  565. Username string `json:"username"`
  566. // quota scan start time as unix timestamp in milliseconds
  567. StartTime int64 `json:"start_time"`
  568. }
  569. // ActiveVirtualFolderQuotaScan defines an active quota scan for a virtual folder
  570. type ActiveVirtualFolderQuotaScan struct {
  571. // folder path to which the quota scan refers
  572. MappedPath string `json:"mapped_path"`
  573. // quota scan start time as unix timestamp in milliseconds
  574. StartTime int64 `json:"start_time"`
  575. }
  576. // ActiveScans holds the active quota scans
  577. type ActiveScans struct {
  578. sync.RWMutex
  579. UserHomeScans []ActiveQuotaScan
  580. FolderScans []ActiveVirtualFolderQuotaScan
  581. }
  582. // GetUsersQuotaScans returns the active quota scans for users home directories
  583. func (s *ActiveScans) GetUsersQuotaScans() []ActiveQuotaScan {
  584. s.RLock()
  585. defer s.RUnlock()
  586. scans := make([]ActiveQuotaScan, len(s.UserHomeScans))
  587. copy(scans, s.UserHomeScans)
  588. return scans
  589. }
  590. // AddUserQuotaScan adds a user to the ones with active quota scans.
  591. // Returns false if the user has a quota scan already running
  592. func (s *ActiveScans) AddUserQuotaScan(username string) bool {
  593. s.Lock()
  594. defer s.Unlock()
  595. for _, scan := range s.UserHomeScans {
  596. if scan.Username == username {
  597. return false
  598. }
  599. }
  600. s.UserHomeScans = append(s.UserHomeScans, ActiveQuotaScan{
  601. Username: username,
  602. StartTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
  603. })
  604. return true
  605. }
  606. // RemoveUserQuotaScan removes a user from the ones with active quota scans.
  607. // Returns false if the user has no active quota scans
  608. func (s *ActiveScans) RemoveUserQuotaScan(username string) bool {
  609. s.Lock()
  610. defer s.Unlock()
  611. indexToRemove := -1
  612. for i, scan := range s.UserHomeScans {
  613. if scan.Username == username {
  614. indexToRemove = i
  615. break
  616. }
  617. }
  618. if indexToRemove >= 0 {
  619. s.UserHomeScans[indexToRemove] = s.UserHomeScans[len(s.UserHomeScans)-1]
  620. s.UserHomeScans = s.UserHomeScans[:len(s.UserHomeScans)-1]
  621. return true
  622. }
  623. return false
  624. }
  625. // GetVFoldersQuotaScans returns the active quota scans for virtual folders
  626. func (s *ActiveScans) GetVFoldersQuotaScans() []ActiveVirtualFolderQuotaScan {
  627. s.RLock()
  628. defer s.RUnlock()
  629. scans := make([]ActiveVirtualFolderQuotaScan, len(s.FolderScans))
  630. copy(scans, s.FolderScans)
  631. return scans
  632. }
  633. // AddVFolderQuotaScan adds a virtual folder to the ones with active quota scans.
  634. // Returns false if the folder has a quota scan already running
  635. func (s *ActiveScans) AddVFolderQuotaScan(folderPath string) bool {
  636. s.Lock()
  637. defer s.Unlock()
  638. for _, scan := range s.FolderScans {
  639. if scan.MappedPath == folderPath {
  640. return false
  641. }
  642. }
  643. s.FolderScans = append(s.FolderScans, ActiveVirtualFolderQuotaScan{
  644. MappedPath: folderPath,
  645. StartTime: utils.GetTimeAsMsSinceEpoch(time.Now()),
  646. })
  647. return true
  648. }
  649. // RemoveVFolderQuotaScan removes a folder from the ones with active quota scans.
  650. // Returns false if the folder has no active quota scans
  651. func (s *ActiveScans) RemoveVFolderQuotaScan(folderPath string) bool {
  652. s.Lock()
  653. defer s.Unlock()
  654. indexToRemove := -1
  655. for i, scan := range s.FolderScans {
  656. if scan.MappedPath == folderPath {
  657. indexToRemove = i
  658. break
  659. }
  660. }
  661. if indexToRemove >= 0 {
  662. s.FolderScans[indexToRemove] = s.FolderScans[len(s.FolderScans)-1]
  663. s.FolderScans = s.FolderScans[:len(s.FolderScans)-1]
  664. return true
  665. }
  666. return false
  667. }