handler.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. package httpd
  2. import (
  3. "io"
  4. "net/http"
  5. "os"
  6. "path"
  7. "strings"
  8. "github.com/drakkan/sftpgo/v2/common"
  9. "github.com/drakkan/sftpgo/v2/dataprovider"
  10. "github.com/drakkan/sftpgo/v2/logger"
  11. "github.com/drakkan/sftpgo/v2/util"
  12. "github.com/drakkan/sftpgo/v2/vfs"
  13. )
  14. // Connection details for a HTTP connection used to inteact with an SFTPGo filesystem
  15. type Connection struct {
  16. *common.BaseConnection
  17. request *http.Request
  18. }
  19. // GetClientVersion returns the connected client's version.
  20. func (c *Connection) GetClientVersion() string {
  21. if c.request != nil {
  22. return c.request.UserAgent()
  23. }
  24. return ""
  25. }
  26. // GetLocalAddress returns local connection address
  27. func (c *Connection) GetLocalAddress() string {
  28. return util.GetHTTPLocalAddress(c.request)
  29. }
  30. // GetRemoteAddress returns the connected client's address
  31. func (c *Connection) GetRemoteAddress() string {
  32. if c.request != nil {
  33. return c.request.RemoteAddr
  34. }
  35. return ""
  36. }
  37. // Disconnect closes the active transfer
  38. func (c *Connection) Disconnect() (err error) {
  39. return c.SignalTransfersAbort()
  40. }
  41. // GetCommand returns the request method
  42. func (c *Connection) GetCommand() string {
  43. if c.request != nil {
  44. return strings.ToUpper(c.request.Method)
  45. }
  46. return ""
  47. }
  48. // Stat returns a FileInfo describing the named file/directory, or an error,
  49. // if any happens
  50. func (c *Connection) Stat(name string, mode int) (os.FileInfo, error) {
  51. c.UpdateLastActivity()
  52. name = util.CleanPath(name)
  53. if !c.User.HasPerm(dataprovider.PermListItems, path.Dir(name)) {
  54. return nil, c.GetPermissionDeniedError()
  55. }
  56. fi, err := c.DoStat(name, mode)
  57. if err != nil {
  58. c.Log(logger.LevelDebug, "error running stat on path %#v: %+v", name, err)
  59. return nil, err
  60. }
  61. return fi, err
  62. }
  63. // ReadDir returns a list of directory entries
  64. func (c *Connection) ReadDir(name string) ([]os.FileInfo, error) {
  65. c.UpdateLastActivity()
  66. name = util.CleanPath(name)
  67. return c.ListDir(name)
  68. }
  69. func (c *Connection) getFileReader(name string, offset int64, method string) (io.ReadCloser, error) {
  70. c.UpdateLastActivity()
  71. name = util.CleanPath(name)
  72. if !c.User.HasPerm(dataprovider.PermDownload, path.Dir(name)) {
  73. return nil, c.GetPermissionDeniedError()
  74. }
  75. if !c.User.IsFileAllowed(name) {
  76. c.Log(logger.LevelWarn, "reading file %#v is not allowed", name)
  77. return nil, c.GetPermissionDeniedError()
  78. }
  79. fs, p, err := c.GetFsAndResolvedPath(name)
  80. if err != nil {
  81. return nil, err
  82. }
  83. if method != http.MethodHead {
  84. if err := common.ExecutePreAction(&c.User, common.OperationPreDownload, p, name, c.GetProtocol(), 0, 0); err != nil {
  85. c.Log(logger.LevelDebug, "download for file %#v denied by pre action: %v", name, err)
  86. return nil, c.GetPermissionDeniedError()
  87. }
  88. }
  89. file, r, cancelFn, err := fs.Open(p, offset)
  90. if err != nil {
  91. c.Log(logger.LevelWarn, "could not open file %#v for reading: %+v", p, err)
  92. return nil, c.GetFsError(fs, err)
  93. }
  94. baseTransfer := common.NewBaseTransfer(file, c.BaseConnection, cancelFn, p, p, name, common.TransferDownload,
  95. 0, 0, 0, false, fs)
  96. return newHTTPDFile(baseTransfer, nil, r), nil
  97. }
  98. func (c *Connection) getFileWriter(name string) (io.WriteCloser, error) {
  99. c.UpdateLastActivity()
  100. if !c.User.IsFileAllowed(name) {
  101. c.Log(logger.LevelWarn, "writing file %#v is not allowed", name)
  102. return nil, c.GetPermissionDeniedError()
  103. }
  104. fs, p, err := c.GetFsAndResolvedPath(name)
  105. if err != nil {
  106. return nil, err
  107. }
  108. filePath := p
  109. if common.Config.IsAtomicUploadEnabled() && fs.IsAtomicUploadSupported() {
  110. filePath = fs.GetAtomicUploadPath(p)
  111. }
  112. stat, statErr := fs.Lstat(p)
  113. if (statErr == nil && stat.Mode()&os.ModeSymlink != 0) || fs.IsNotExist(statErr) {
  114. if !c.User.HasPerm(dataprovider.PermUpload, path.Dir(name)) {
  115. return nil, c.GetPermissionDeniedError()
  116. }
  117. return c.handleUploadFile(fs, p, filePath, name, true, 0)
  118. }
  119. if statErr != nil {
  120. c.Log(logger.LevelError, "error performing file stat %#v: %+v", p, statErr)
  121. return nil, c.GetFsError(fs, statErr)
  122. }
  123. // This happen if we upload a file that has the same name of an existing directory
  124. if stat.IsDir() {
  125. c.Log(logger.LevelWarn, "attempted to open a directory for writing to: %#v", p)
  126. return nil, c.GetOpUnsupportedError()
  127. }
  128. if !c.User.HasPerm(dataprovider.PermOverwrite, path.Dir(name)) {
  129. return nil, c.GetPermissionDeniedError()
  130. }
  131. if common.Config.IsAtomicUploadEnabled() && fs.IsAtomicUploadSupported() {
  132. err = fs.Rename(p, filePath)
  133. if err != nil {
  134. c.Log(logger.LevelWarn, "error renaming existing file for atomic upload, source: %#v, dest: %#v, err: %+v",
  135. p, filePath, err)
  136. return nil, c.GetFsError(fs, err)
  137. }
  138. }
  139. return c.handleUploadFile(fs, p, filePath, name, false, stat.Size())
  140. }
  141. func (c *Connection) handleUploadFile(fs vfs.Fs, resolvedPath, filePath, requestPath string, isNewFile bool, fileSize int64) (io.WriteCloser, error) {
  142. quotaResult := c.HasSpace(isNewFile, false, requestPath)
  143. if !quotaResult.HasSpace {
  144. c.Log(logger.LevelInfo, "denying file write due to quota limits")
  145. return nil, common.ErrQuotaExceeded
  146. }
  147. err := common.ExecutePreAction(&c.User, common.OperationPreUpload, resolvedPath, requestPath, c.GetProtocol(), fileSize, os.O_TRUNC)
  148. if err != nil {
  149. c.Log(logger.LevelDebug, "upload for file %#v denied by pre action: %v", requestPath, err)
  150. return nil, c.GetPermissionDeniedError()
  151. }
  152. maxWriteSize, _ := c.GetMaxWriteSize(quotaResult, false, fileSize, fs.IsUploadResumeSupported())
  153. file, w, cancelFn, err := fs.Create(filePath, 0)
  154. if err != nil {
  155. c.Log(logger.LevelWarn, "error opening existing file, source: %#v, err: %+v", filePath, err)
  156. return nil, c.GetFsError(fs, err)
  157. }
  158. initialSize := int64(0)
  159. if !isNewFile {
  160. if vfs.IsLocalOrSFTPFs(fs) {
  161. vfolder, err := c.User.GetVirtualFolderForPath(path.Dir(requestPath))
  162. if err == nil {
  163. dataprovider.UpdateVirtualFolderQuota(&vfolder.BaseVirtualFolder, 0, -fileSize, false) //nolint:errcheck
  164. if vfolder.IsIncludedInUserQuota() {
  165. dataprovider.UpdateUserQuota(&c.User, 0, -fileSize, false) //nolint:errcheck
  166. }
  167. } else {
  168. dataprovider.UpdateUserQuota(&c.User, 0, -fileSize, false) //nolint:errcheck
  169. }
  170. } else {
  171. initialSize = fileSize
  172. }
  173. if maxWriteSize > 0 {
  174. maxWriteSize += fileSize
  175. }
  176. }
  177. vfs.SetPathPermissions(fs, filePath, c.User.GetUID(), c.User.GetGID())
  178. baseTransfer := common.NewBaseTransfer(file, c.BaseConnection, cancelFn, resolvedPath, filePath, requestPath,
  179. common.TransferUpload, 0, initialSize, maxWriteSize, isNewFile, fs)
  180. return newHTTPDFile(baseTransfer, w, nil), nil
  181. }