handler.go 6.2 KB

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