file.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. package api
  2. import (
  3. "fmt"
  4. "github.com/ente-io/museum/pkg/controller/file_copy"
  5. "net/http"
  6. "os"
  7. "strconv"
  8. "strings"
  9. "github.com/ente-io/stacktrace"
  10. "github.com/gin-contrib/requestid"
  11. log "github.com/sirupsen/logrus"
  12. "github.com/ente-io/museum/ente"
  13. "github.com/ente-io/museum/pkg/controller"
  14. "github.com/ente-io/museum/pkg/utils/auth"
  15. "github.com/ente-io/museum/pkg/utils/handler"
  16. "github.com/ente-io/museum/pkg/utils/time"
  17. "github.com/gin-gonic/gin"
  18. )
  19. // FileHandler exposes request handlers for all encrypted file related requests
  20. type FileHandler struct {
  21. Controller *controller.FileController
  22. FileCopyCtrl *file_copy.FileCopyController
  23. }
  24. // DefaultMaxBatchSize is the default maximum API batch size unless specified otherwise
  25. const DefaultMaxBatchSize = 1000
  26. const DefaultCopyBatchSize = 100
  27. // CreateOrUpdate creates an entry for a file
  28. func (h *FileHandler) CreateOrUpdate(c *gin.Context) {
  29. userID := auth.GetUserID(c.Request.Header)
  30. var file ente.File
  31. if err := c.ShouldBindJSON(&file); err != nil {
  32. handler.Error(c, stacktrace.Propagate(err, ""))
  33. return
  34. }
  35. file.UpdationTime = time.Microseconds()
  36. // get an ente.App from the ?app= query parameter with a default of photos
  37. enteApp := auth.GetApp(c)
  38. if file.ID == 0 {
  39. file.OwnerID = userID
  40. file.IsDeleted = false
  41. file, err := h.Controller.Create(c, userID, file, c.Request.UserAgent(), enteApp)
  42. if err != nil {
  43. handler.Error(c, stacktrace.Propagate(err, ""))
  44. return
  45. }
  46. c.JSON(http.StatusOK, file)
  47. return
  48. }
  49. response, err := h.Controller.Update(c, userID, file, enteApp)
  50. if err != nil {
  51. handler.Error(c, stacktrace.Propagate(err, ""))
  52. return
  53. }
  54. c.JSON(http.StatusOK, response)
  55. }
  56. // CopyFiles copies files that are owned by another user
  57. func (h *FileHandler) CopyFiles(c *gin.Context) {
  58. var req ente.CopyFileSyncRequest
  59. if err := c.ShouldBindJSON(&req); err != nil {
  60. handler.Error(c, stacktrace.Propagate(err, ""))
  61. return
  62. }
  63. if len(req.CollectionFileItems) > DefaultCopyBatchSize {
  64. handler.Error(c, stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("more than %d items", DefaultCopyBatchSize)), ""))
  65. return
  66. }
  67. response, err := h.FileCopyCtrl.CopyFiles(c, req)
  68. if err != nil {
  69. handler.Error(c, stacktrace.Propagate(err, ""))
  70. return
  71. }
  72. c.JSON(http.StatusOK, response)
  73. }
  74. // Update updates already existing file
  75. func (h *FileHandler) Update(c *gin.Context) {
  76. enteApp := auth.GetApp(c)
  77. userID := auth.GetUserID(c.Request.Header)
  78. var file ente.File
  79. if err := c.ShouldBindJSON(&file); err != nil {
  80. handler.Error(c, stacktrace.Propagate(err, ""))
  81. return
  82. }
  83. file.UpdationTime = time.Microseconds()
  84. if file.ID <= 0 {
  85. handler.Error(c, stacktrace.Propagate(ente.ErrBadRequest, "fileID should be >0"))
  86. return
  87. }
  88. response, err := h.Controller.Update(c, userID, file, enteApp)
  89. if err != nil {
  90. handler.Error(c, stacktrace.Propagate(err, ""))
  91. return
  92. }
  93. c.JSON(http.StatusOK, response)
  94. }
  95. // GetUploadURLs returns a bunch of urls where in the user can upload objects
  96. func (h *FileHandler) GetUploadURLs(c *gin.Context) {
  97. enteApp := auth.GetApp(c)
  98. userID := auth.GetUserID(c.Request.Header)
  99. count, _ := strconv.Atoi(c.Query("count"))
  100. urls, err := h.Controller.GetUploadURLs(c, userID, count, enteApp)
  101. if err != nil {
  102. handler.Error(c, stacktrace.Propagate(err, ""))
  103. return
  104. }
  105. c.JSON(http.StatusOK, gin.H{
  106. "urls": urls,
  107. })
  108. }
  109. // GetMultipartUploadURLs returns an array of PartUpload PresignedURLs
  110. func (h *FileHandler) GetMultipartUploadURLs(c *gin.Context) {
  111. enteApp := auth.GetApp(c)
  112. userID := auth.GetUserID(c.Request.Header)
  113. count, _ := strconv.Atoi(c.Query("count"))
  114. urls, err := h.Controller.GetMultipartUploadURLs(c, userID, count, enteApp)
  115. if err != nil {
  116. handler.Error(c, stacktrace.Propagate(err, ""))
  117. return
  118. }
  119. c.JSON(http.StatusOK, gin.H{
  120. "urls": urls,
  121. })
  122. }
  123. // Get redirects the request to the file location
  124. func (h *FileHandler) Get(c *gin.Context) {
  125. userID, fileID := getUserAndFileIDs(c)
  126. url, err := h.Controller.GetFileURL(userID, fileID)
  127. if err != nil {
  128. handler.Error(c, stacktrace.Propagate(err, ""))
  129. return
  130. }
  131. h.logBadRedirect(c)
  132. c.Redirect(http.StatusTemporaryRedirect, url)
  133. }
  134. // GetV2 returns the URL of the file to client
  135. func (h *FileHandler) GetV2(c *gin.Context) {
  136. userID, fileID := getUserAndFileIDs(c)
  137. url, err := h.Controller.GetFileURL(userID, fileID)
  138. if err != nil {
  139. handler.Error(c, stacktrace.Propagate(err, ""))
  140. return
  141. }
  142. c.JSON(http.StatusOK, gin.H{
  143. "url": url,
  144. })
  145. }
  146. // GetThumbnail redirects the request to the file's thumbnail location
  147. func (h *FileHandler) GetThumbnail(c *gin.Context) {
  148. userID, fileID := getUserAndFileIDs(c)
  149. url, err := h.Controller.GetThumbnailURL(userID, fileID)
  150. if err != nil {
  151. handler.Error(c, stacktrace.Propagate(err, ""))
  152. return
  153. }
  154. h.logBadRedirect(c)
  155. c.Redirect(http.StatusTemporaryRedirect, url)
  156. }
  157. // GetThumbnailV2 returns the URL of the thumbnail to the client
  158. func (h *FileHandler) GetThumbnailV2(c *gin.Context) {
  159. userID, fileID := getUserAndFileIDs(c)
  160. url, err := h.Controller.GetThumbnailURL(userID, fileID)
  161. if err != nil {
  162. handler.Error(c, stacktrace.Propagate(err, ""))
  163. return
  164. }
  165. c.JSON(http.StatusOK, gin.H{
  166. "url": url,
  167. })
  168. }
  169. // Trash moves the given files to the trash bin
  170. func (h *FileHandler) Trash(c *gin.Context) {
  171. var request ente.TrashRequest
  172. if err := c.ShouldBindJSON(&request); err != nil {
  173. handler.Error(c, stacktrace.Propagate(err, "failed to bind"))
  174. return
  175. }
  176. if len(request.TrashItems) > DefaultMaxBatchSize {
  177. handler.Error(c, stacktrace.Propagate(ente.ErrBatchSizeTooLarge, ""))
  178. return
  179. }
  180. userID := auth.GetUserID(c.Request.Header)
  181. request.OwnerID = userID
  182. err := h.Controller.Trash(c, userID, request)
  183. if err != nil {
  184. handler.Error(c, stacktrace.Propagate(err, ""))
  185. } else {
  186. c.Status(http.StatusOK)
  187. }
  188. }
  189. // GetSize returns the size of files indicated by fileIDs
  190. func (h *FileHandler) GetSize(c *gin.Context) {
  191. var request ente.FileIDsRequest
  192. if err := c.ShouldBindJSON(&request); err != nil {
  193. handler.Error(c, stacktrace.Propagate(err, ""))
  194. return
  195. }
  196. userID := auth.GetUserID(c.Request.Header)
  197. shouldReject, err := shouldRejectRequest(c)
  198. if err != nil {
  199. handler.Error(c, stacktrace.Propagate(err, ""))
  200. return
  201. }
  202. if shouldReject {
  203. c.Status(http.StatusUpgradeRequired)
  204. return
  205. }
  206. size, err := h.Controller.GetSize(userID, request.FileIDs)
  207. if err != nil {
  208. handler.Error(c, stacktrace.Propagate(err, ""))
  209. } else {
  210. c.JSON(http.StatusOK, gin.H{
  211. "size": size,
  212. })
  213. }
  214. }
  215. // GetInfo returns the FileInfo of files indicated by fileIDs
  216. func (h *FileHandler) GetInfo(c *gin.Context) {
  217. var request ente.FileIDsRequest
  218. if err := c.ShouldBindJSON(&request); err != nil {
  219. handler.Error(c, stacktrace.Propagate(err, "failed to bind request"))
  220. return
  221. }
  222. userID := auth.GetUserID(c.Request.Header)
  223. response, err := h.Controller.GetFileInfo(c, userID, request.FileIDs)
  224. if err != nil {
  225. handler.Error(c, stacktrace.Propagate(err, ""))
  226. } else {
  227. c.JSON(http.StatusOK, response)
  228. }
  229. }
  230. // shouldRejectRequest return true if the client which is making the request
  231. // is Android client with version less than 0.5.36
  232. func shouldRejectRequest(c *gin.Context) (bool, error) {
  233. userAgent := c.GetHeader("User-Agent")
  234. clientVersion := c.GetHeader("X-Client-Version")
  235. clientPkg := c.GetHeader("X-Client-Package")
  236. if !strings.Contains(strings.ToLower(userAgent), "android") {
  237. return false, nil
  238. }
  239. if clientPkg == "io.ente.photos.fdroid" {
  240. return false, nil
  241. }
  242. versionSplit := strings.Split(clientVersion, ".")
  243. if len(versionSplit) != 3 {
  244. return false, nil
  245. }
  246. if versionSplit[0] != "0" {
  247. return false, nil
  248. }
  249. minorVersion, err := strconv.Atoi(versionSplit[1])
  250. if err != nil {
  251. // avoid reject when parsing fails
  252. return false, nil
  253. }
  254. patchVersion, err := strconv.Atoi(versionSplit[2])
  255. if err != nil {
  256. // avoid reject when parsing fails
  257. return false, nil
  258. }
  259. shouldReject := minorVersion <= 5 && patchVersion <= 35
  260. if shouldReject {
  261. log.Warnf("request rejected from older client with version %s", clientVersion)
  262. }
  263. return shouldReject, nil
  264. }
  265. // GetDuplicates returns the list of files of the same size
  266. func (h *FileHandler) GetDuplicates(c *gin.Context) {
  267. userID := auth.GetUserID(c.Request.Header)
  268. dupes, err := h.Controller.GetDuplicates(userID)
  269. if err != nil {
  270. handler.Error(c, stacktrace.Propagate(err, ""))
  271. return
  272. }
  273. c.JSON(http.StatusOK, gin.H{
  274. "duplicates": dupes,
  275. })
  276. }
  277. // GetLargeThumbnail returns the list of files whose thumbnail size is larger than threshold size
  278. func (h *FileHandler) GetLargeThumbnailFiles(c *gin.Context) {
  279. userID := auth.GetUserID(c.Request.Header)
  280. threshold, _ := strconv.ParseInt(c.Query("threshold"), 10, 64)
  281. largeThumbnailFiles, err := h.Controller.GetLargeThumbnailFiles(userID, threshold)
  282. if err != nil {
  283. handler.Error(c, stacktrace.Propagate(err, ""))
  284. return
  285. }
  286. c.JSON(http.StatusOK, gin.H{
  287. "largeThumbnailFiles": largeThumbnailFiles,
  288. })
  289. }
  290. // UpdateMagicMetadata updates magic metadata for a list of files.
  291. func (h *FileHandler) UpdateMagicMetadata(c *gin.Context) {
  292. var request ente.UpdateMultipleMagicMetadataRequest
  293. if err := c.ShouldBindJSON(&request); err != nil {
  294. handler.Error(c, stacktrace.Propagate(err, ""))
  295. return
  296. }
  297. if len(request.MetadataList) > DefaultMaxBatchSize {
  298. handler.Error(c, stacktrace.Propagate(ente.ErrBatchSizeTooLarge, ""))
  299. return
  300. }
  301. err := h.Controller.UpdateMagicMetadata(c, request, false)
  302. if err != nil {
  303. handler.Error(c, stacktrace.Propagate(err, ""))
  304. return
  305. }
  306. c.Status(http.StatusOK)
  307. }
  308. // UpdatePublicMagicMetadata updates public magic metadata for a list of files.
  309. func (h *FileHandler) UpdatePublicMagicMetadata(c *gin.Context) {
  310. var request ente.UpdateMultipleMagicMetadataRequest
  311. if err := c.ShouldBindJSON(&request); err != nil {
  312. handler.Error(c, stacktrace.Propagate(err, ""))
  313. return
  314. }
  315. err := h.Controller.UpdateMagicMetadata(c, request, true)
  316. if err != nil {
  317. handler.Error(c, stacktrace.Propagate(err, ""))
  318. return
  319. }
  320. c.Status(http.StatusOK)
  321. }
  322. // UpdateThumbnail updates thumbnail of a file
  323. func (h *FileHandler) UpdateThumbnail(c *gin.Context) {
  324. enteApp := auth.GetApp(c)
  325. var request ente.UpdateThumbnailRequest
  326. if err := c.ShouldBindJSON(&request); err != nil {
  327. handler.Error(c, stacktrace.Propagate(err, ""))
  328. return
  329. }
  330. err := h.Controller.UpdateThumbnail(c, request.FileID, request.Thumbnail, enteApp)
  331. if err != nil {
  332. handler.Error(c, stacktrace.Propagate(err, ""))
  333. return
  334. }
  335. c.Status(http.StatusOK)
  336. }
  337. func (h *FileHandler) GetTotalFileCount(c *gin.Context) {
  338. count, err := h.Controller.GetTotalFileCount()
  339. if err != nil {
  340. handler.Error(c, stacktrace.Propagate(err, ""))
  341. return
  342. }
  343. c.JSON(http.StatusOK, gin.H{
  344. "count": count,
  345. })
  346. }
  347. func getUserAndFileIDs(c *gin.Context) (int64, int64) {
  348. fileID, _ := strconv.ParseInt(c.Param("fileID"), 10, 64)
  349. userID := auth.GetUserID(c.Request.Header)
  350. return userID, fileID
  351. }
  352. // logBadRedirect will log the request id if we are redirecting to another url with the auth-token in header
  353. func (h *FileHandler) logBadRedirect(c *gin.Context) {
  354. if len(c.GetHeader("X-Auth-Token")) != 0 && os.Getenv("ENVIRONMENT") != "" {
  355. log.WithField("req_id", requestid.Get(c)).Error("critical: sending token to another service")
  356. }
  357. }