metadata.go 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983
  1. package onedrive
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "net/http"
  8. "strings"
  9. "time"
  10. "github.com/rclone/rclone/backend/onedrive/api"
  11. "github.com/rclone/rclone/fs"
  12. "github.com/rclone/rclone/fs/fserrors"
  13. "github.com/rclone/rclone/lib/dircache"
  14. "github.com/rclone/rclone/lib/errcount"
  15. "golang.org/x/exp/slices" // replace with slices after go1.21 is the minimum version
  16. )
  17. const (
  18. dirMimeType = "inode/directory"
  19. timeFormatIn = time.RFC3339
  20. timeFormatOut = "2006-01-02T15:04:05.999Z" // mS for OneDrive Personal, otherwise only S
  21. )
  22. // system metadata keys which this backend owns
  23. var systemMetadataInfo = map[string]fs.MetadataHelp{
  24. "content-type": {
  25. Help: "The MIME type of the file.",
  26. Type: "string",
  27. Example: "text/plain",
  28. ReadOnly: true,
  29. },
  30. "mtime": {
  31. Help: "Time of last modification with S accuracy (mS for OneDrive Personal).",
  32. Type: "RFC 3339",
  33. Example: "2006-01-02T15:04:05Z",
  34. },
  35. "btime": {
  36. Help: "Time of file birth (creation) with S accuracy (mS for OneDrive Personal).",
  37. Type: "RFC 3339",
  38. Example: "2006-01-02T15:04:05Z",
  39. },
  40. "utime": {
  41. Help: "Time of upload with S accuracy (mS for OneDrive Personal).",
  42. Type: "RFC 3339",
  43. Example: "2006-01-02T15:04:05Z",
  44. ReadOnly: true,
  45. },
  46. "created-by-display-name": {
  47. Help: "Display name of the user that created the item.",
  48. Type: "string",
  49. Example: "John Doe",
  50. ReadOnly: true,
  51. },
  52. "created-by-id": {
  53. Help: "ID of the user that created the item.",
  54. Type: "string",
  55. Example: "48d31887-5fad-4d73-a9f5-3c356e68a038",
  56. ReadOnly: true,
  57. },
  58. "description": {
  59. Help: "A short description of the file. Max 1024 characters. Only supported for OneDrive Personal.",
  60. Type: "string",
  61. Example: "Contract for signing",
  62. },
  63. "id": {
  64. Help: "The unique identifier of the item within OneDrive.",
  65. Type: "string",
  66. Example: "01BYE5RZ6QN3ZWBTUFOFD3GSPGOHDJD36K",
  67. ReadOnly: true,
  68. },
  69. "last-modified-by-display-name": {
  70. Help: "Display name of the user that last modified the item.",
  71. Type: "string",
  72. Example: "John Doe",
  73. ReadOnly: true,
  74. },
  75. "last-modified-by-id": {
  76. Help: "ID of the user that last modified the item.",
  77. Type: "string",
  78. Example: "48d31887-5fad-4d73-a9f5-3c356e68a038",
  79. ReadOnly: true,
  80. },
  81. "malware-detected": {
  82. Help: "Whether OneDrive has detected that the item contains malware.",
  83. Type: "boolean",
  84. Example: "true",
  85. ReadOnly: true,
  86. },
  87. "package-type": {
  88. Help: "If present, indicates that this item is a package instead of a folder or file. Packages are treated like files in some contexts and folders in others.",
  89. Type: "string",
  90. Example: "oneNote",
  91. ReadOnly: true,
  92. },
  93. "shared-owner-id": {
  94. Help: "ID of the owner of the shared item (if shared).",
  95. Type: "string",
  96. Example: "48d31887-5fad-4d73-a9f5-3c356e68a038",
  97. ReadOnly: true,
  98. },
  99. "shared-by-id": {
  100. Help: "ID of the user that shared the item (if shared).",
  101. Type: "string",
  102. Example: "48d31887-5fad-4d73-a9f5-3c356e68a038",
  103. ReadOnly: true,
  104. },
  105. "shared-scope": {
  106. Help: "If shared, indicates the scope of how the item is shared: anonymous, organization, or users.",
  107. Type: "string",
  108. Example: "users",
  109. ReadOnly: true,
  110. },
  111. "shared-time": {
  112. Help: "Time when the item was shared, with S accuracy (mS for OneDrive Personal).",
  113. Type: "RFC 3339",
  114. Example: "2006-01-02T15:04:05Z",
  115. ReadOnly: true,
  116. },
  117. "permissions": {
  118. Help: "Permissions in a JSON dump of OneDrive format. Enable with --onedrive-metadata-permissions. Properties: id, grantedTo, grantedToIdentities, invitation, inheritedFrom, link, roles, shareId",
  119. Type: "JSON",
  120. Example: "{}",
  121. },
  122. }
  123. // rwChoices type for fs.Bits
  124. type rwChoices struct{}
  125. func (rwChoices) Choices() []fs.BitsChoicesInfo {
  126. return []fs.BitsChoicesInfo{
  127. {Bit: uint64(rwOff), Name: "off"},
  128. {Bit: uint64(rwRead), Name: "read"},
  129. {Bit: uint64(rwWrite), Name: "write"},
  130. {Bit: uint64(rwFailOK), Name: "failok"},
  131. }
  132. }
  133. // rwChoice type alias
  134. type rwChoice = fs.Bits[rwChoices]
  135. const (
  136. rwRead rwChoice = 1 << iota
  137. rwWrite
  138. rwFailOK
  139. rwOff rwChoice = 0
  140. )
  141. // Examples for the options
  142. var rwExamples = fs.OptionExamples{{
  143. Value: rwOff.String(),
  144. Help: "Do not read or write the value",
  145. }, {
  146. Value: rwRead.String(),
  147. Help: "Read the value only",
  148. }, {
  149. Value: rwWrite.String(),
  150. Help: "Write the value only",
  151. }, {
  152. Value: (rwRead | rwWrite).String(),
  153. Help: "Read and Write the value.",
  154. }, {
  155. Value: rwFailOK.String(),
  156. Help: "If writing fails log errors only, don't fail the transfer",
  157. }}
  158. // Metadata describes metadata properties shared by both Objects and Directories
  159. type Metadata struct {
  160. fs *Fs // what this object/dir is part of
  161. remote string // remote, for convenience when obj/dir not in scope
  162. mimeType string // Content-Type of object from server (may not be as uploaded)
  163. description string // Provides a user-visible description of the item. Read-write. Only on OneDrive Personal
  164. mtime time.Time // Time of last modification with S accuracy.
  165. btime time.Time // Time of file birth (creation) with S accuracy.
  166. utime time.Time // Time of upload with S accuracy.
  167. createdBy api.IdentitySet // user that created the item
  168. lastModifiedBy api.IdentitySet // user that last modified the item
  169. malwareDetected bool // Whether OneDrive has detected that the item contains malware.
  170. packageType string // If present, indicates that this item is a package instead of a folder or file.
  171. shared *api.SharedType // information about the shared state of the item, if shared
  172. normalizedID string // the normalized ID of the object or dir
  173. permissions []*api.PermissionsType // The current set of permissions for the item. Note that to save API calls, this is not guaranteed to be cached on the object. Use m.Get() to refresh.
  174. queuedPermissions []*api.PermissionsType // The set of permissions queued to be updated.
  175. permsAddOnly bool // Whether to disable "update" and "remove" (for example, during server-side copy when the dst will have new IDs)
  176. }
  177. // Get retrieves the cached metadata and converts it to fs.Metadata.
  178. // This is most typically used when OneDrive is the source (as opposed to the dest).
  179. // If m.fs.opt.MetadataPermissions includes "read" then this will also include permissions, which requires an API call.
  180. // Get does not use an API call otherwise.
  181. func (m *Metadata) Get(ctx context.Context) (metadata fs.Metadata, err error) {
  182. metadata = make(fs.Metadata, 17)
  183. metadata["content-type"] = m.mimeType
  184. metadata["mtime"] = m.mtime.Format(timeFormatOut)
  185. metadata["btime"] = m.btime.Format(timeFormatOut)
  186. metadata["utime"] = m.utime.Format(timeFormatOut)
  187. metadata["created-by-display-name"] = m.createdBy.User.DisplayName
  188. metadata["created-by-id"] = m.createdBy.User.ID
  189. if m.description != "" {
  190. metadata["description"] = m.description
  191. }
  192. metadata["id"] = m.normalizedID
  193. metadata["last-modified-by-display-name"] = m.lastModifiedBy.User.DisplayName
  194. metadata["last-modified-by-id"] = m.lastModifiedBy.User.ID
  195. metadata["malware-detected"] = fmt.Sprint(m.malwareDetected)
  196. if m.packageType != "" {
  197. metadata["package-type"] = m.packageType
  198. }
  199. if m.shared != nil {
  200. metadata["shared-owner-id"] = m.shared.Owner.User.ID
  201. metadata["shared-by-id"] = m.shared.SharedBy.User.ID
  202. metadata["shared-scope"] = m.shared.Scope
  203. metadata["shared-time"] = time.Time(m.shared.SharedDateTime).Format(timeFormatOut)
  204. }
  205. if m.fs.opt.MetadataPermissions.IsSet(rwRead) {
  206. p, _, err := m.fs.getPermissions(ctx, m.normalizedID)
  207. if err != nil {
  208. return nil, fmt.Errorf("failed to get permissions: %w", err)
  209. }
  210. m.permissions = p
  211. if len(p) > 0 {
  212. fs.PrettyPrint(m.permissions, "perms", fs.LogLevelDebug)
  213. buf, err := json.Marshal(m.permissions)
  214. if err != nil {
  215. return nil, fmt.Errorf("failed to marshal permissions: %w", err)
  216. }
  217. metadata["permissions"] = string(buf)
  218. }
  219. }
  220. return metadata, nil
  221. }
  222. // Set takes fs.Metadata and parses/converts it to cached Metadata.
  223. // This is most typically used when OneDrive is the destination (as opposed to the source).
  224. // It does not actually update the remote (use Write for that.)
  225. // It sets only the writeable metadata properties (i.e. read-only properties are skipped.)
  226. // Permissions are included if m.fs.opt.MetadataPermissions includes "write".
  227. // It returns errors if writeable properties can't be parsed.
  228. // It does not return errors for unsupported properties that may be passed in.
  229. // It returns the number of writeable properties set (if it is 0, we can skip the Write API call.)
  230. func (m *Metadata) Set(ctx context.Context, metadata fs.Metadata) (numSet int, err error) {
  231. numSet = 0
  232. for k, v := range metadata {
  233. k, v := k, v
  234. switch k {
  235. case "mtime":
  236. t, err := time.Parse(timeFormatIn, v)
  237. if err != nil {
  238. return numSet, fmt.Errorf("failed to parse metadata %q = %q: %w", k, v, err)
  239. }
  240. m.mtime = t
  241. numSet++
  242. case "btime":
  243. t, err := time.Parse(timeFormatIn, v)
  244. if err != nil {
  245. return numSet, fmt.Errorf("failed to parse metadata %q = %q: %w", k, v, err)
  246. }
  247. m.btime = t
  248. numSet++
  249. case "description":
  250. if m.fs.driveType != driveTypePersonal {
  251. fs.Debugf(m.remote, "metadata description is only supported for OneDrive Personal -- skipping: %s", v)
  252. continue
  253. }
  254. m.description = v
  255. numSet++
  256. case "permissions":
  257. if !m.fs.opt.MetadataPermissions.IsSet(rwWrite) {
  258. continue
  259. }
  260. var perms []*api.PermissionsType
  261. err := json.Unmarshal([]byte(v), &perms)
  262. if err != nil {
  263. return numSet, fmt.Errorf("failed to unmarshal permissions: %w", err)
  264. }
  265. m.queuedPermissions = perms
  266. numSet++
  267. default:
  268. fs.Debugf(m.remote, "skipping unsupported metadata item: %s: %s", k, v)
  269. }
  270. }
  271. if numSet == 0 {
  272. fs.Infof(m.remote, "no writeable metadata found: %v", metadata)
  273. }
  274. return numSet, nil
  275. }
  276. // toAPIMetadata converts object/dir Metadata to api.Metadata for API calls.
  277. // If btime is missing but mtime is present, mtime is also used as the btime, as otherwise it would get overwritten.
  278. func (m *Metadata) toAPIMetadata() api.Metadata {
  279. update := api.Metadata{
  280. FileSystemInfo: &api.FileSystemInfoFacet{},
  281. }
  282. if m.description != "" && m.fs.driveType == driveTypePersonal {
  283. update.Description = m.description
  284. }
  285. if !m.mtime.IsZero() {
  286. update.FileSystemInfo.LastModifiedDateTime = api.Timestamp(m.mtime)
  287. }
  288. if !m.btime.IsZero() {
  289. update.FileSystemInfo.CreatedDateTime = api.Timestamp(m.btime)
  290. }
  291. if m.btime.IsZero() && !m.mtime.IsZero() { // use mtime as btime if missing
  292. m.btime = m.mtime
  293. update.FileSystemInfo.CreatedDateTime = api.Timestamp(m.btime)
  294. }
  295. return update
  296. }
  297. // Write takes the cached Metadata and sets it on the remote, using API calls.
  298. // If m.fs.opt.MetadataPermissions includes "write" and updatePermissions == true, permissions are also set.
  299. // Calling Write without any writeable metadata will result in an error.
  300. func (m *Metadata) Write(ctx context.Context, updatePermissions bool) (*api.Item, error) {
  301. update := m.toAPIMetadata()
  302. if update.IsEmpty() {
  303. return nil, fmt.Errorf("%v: no writeable metadata found: %v", m.remote, m)
  304. }
  305. opts := m.fs.newOptsCallWithPath(ctx, m.remote, "PATCH", "")
  306. var info *api.Item
  307. err := m.fs.pacer.Call(func() (bool, error) {
  308. resp, err := m.fs.srv.CallJSON(ctx, &opts, &update, &info)
  309. return shouldRetry(ctx, resp, err)
  310. })
  311. if err != nil {
  312. fs.Debugf(m.remote, "errored metadata: %v", m)
  313. return nil, fmt.Errorf("%v: error updating metadata: %v", m.remote, err)
  314. }
  315. if m.fs.opt.MetadataPermissions.IsSet(rwWrite) && updatePermissions {
  316. m.normalizedID = info.GetID()
  317. err = m.WritePermissions(ctx)
  318. if err != nil {
  319. fs.Errorf(m.remote, "error writing permissions: %v", err)
  320. return info, err
  321. }
  322. }
  323. // update the struct since we have fresh info
  324. m.fs.setSystemMetadata(info, m, m.remote, m.mimeType)
  325. return info, err
  326. }
  327. // RefreshPermissions fetches the current permissions from the remote and caches them as Metadata
  328. func (m *Metadata) RefreshPermissions(ctx context.Context) (err error) {
  329. if m.normalizedID == "" {
  330. return errors.New("internal error: normalizedID is missing")
  331. }
  332. p, _, err := m.fs.getPermissions(ctx, m.normalizedID)
  333. if err != nil {
  334. return fmt.Errorf("failed to refresh permissions: %w", err)
  335. }
  336. m.permissions = p
  337. return nil
  338. }
  339. // WritePermissions sets the permissions (and no other metadata) on the remote.
  340. // m.permissions (the existing perms) and m.queuedPermissions (the new perms to be set) must be set correctly before calling this.
  341. // m.permissions == nil will not error, as it is valid to add permissions when there were previously none.
  342. // If successful, m.permissions will be set with the new current permissions and m.queuedPermissions will be nil.
  343. func (m *Metadata) WritePermissions(ctx context.Context) (err error) {
  344. if !m.fs.opt.MetadataPermissions.IsSet(rwWrite) {
  345. return errors.New("can't write permissions without --onedrive-metadata-permissions write")
  346. }
  347. if m.normalizedID == "" {
  348. return errors.New("internal error: normalizedID is missing")
  349. }
  350. if m.fs.opt.MetadataPermissions.IsSet(rwFailOK) {
  351. // If failok is set, allow the permissions setting to fail and only log an ERROR
  352. defer func() {
  353. if err != nil {
  354. fs.Errorf(m.fs, "Ignoring error as failok is set: %v", err)
  355. err = nil
  356. }
  357. }()
  358. }
  359. // compare current to queued and sort into add/update/remove queues
  360. add, update, remove := m.sortPermissions()
  361. fs.Debugf(m.remote, "metadata permissions: to add: %d to update: %d to remove: %d", len(add), len(update), len(remove))
  362. _, err = m.processPermissions(ctx, add, update, remove)
  363. if err != nil {
  364. return fmt.Errorf("failed to process permissions: %w", err)
  365. }
  366. err = m.RefreshPermissions(ctx)
  367. fs.Debugf(m.remote, "updated permissions (now has %d permissions)", len(m.permissions))
  368. if err != nil {
  369. return fmt.Errorf("failed to get permissions: %w", err)
  370. }
  371. m.queuedPermissions = nil
  372. return nil
  373. }
  374. // sortPermissions sorts the permissions (to be written) into add, update, and remove queues
  375. func (m *Metadata) sortPermissions() (add, update, remove []*api.PermissionsType) {
  376. new, old := m.queuedPermissions, m.permissions
  377. if len(old) == 0 || m.permsAddOnly {
  378. return new, nil, nil // they must all be "add"
  379. }
  380. for _, n := range new {
  381. if n == nil {
  382. continue
  383. }
  384. if n.ID != "" {
  385. // sanity check: ensure there's a matching "old" id with a non-matching role
  386. if !slices.ContainsFunc(old, func(o *api.PermissionsType) bool {
  387. return o.ID == n.ID && slices.Compare(o.Roles, n.Roles) != 0 && len(o.Roles) > 0 && len(n.Roles) > 0 && !slices.Contains(o.Roles, api.OwnerRole)
  388. }) {
  389. fs.Debugf(m.remote, "skipping update for invalid roles: %v (perm ID: %v)", n.Roles, n.ID)
  390. continue
  391. }
  392. if m.fs.driveType != driveTypePersonal && n.Link != nil && n.Link.WebURL != "" {
  393. // special case to work around API limitation -- can't update a sharing link perm so need to remove + add instead
  394. // https://learn.microsoft.com/en-us/answers/questions/986279/why-is-update-permission-graph-api-for-files-not-w
  395. // https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/1135
  396. fs.Debugf(m.remote, "sortPermissions: can't update due to API limitation, will remove + add instead: %v", n.Roles)
  397. remove = append(remove, n)
  398. add = append(add, n)
  399. continue
  400. }
  401. fs.Debugf(m.remote, "sortPermissions: will update role to %v", n.Roles)
  402. update = append(update, n)
  403. } else {
  404. fs.Debugf(m.remote, "sortPermissions: will add permission: %v %v", n, n.Roles)
  405. add = append(add, n)
  406. }
  407. }
  408. for _, o := range old {
  409. if slices.Contains(o.Roles, api.OwnerRole) {
  410. fs.Debugf(m.remote, "skipping remove permission -- can't remove 'owner' role")
  411. continue
  412. }
  413. newHasOld := slices.ContainsFunc(new, func(n *api.PermissionsType) bool {
  414. if n == nil || n.ID == "" {
  415. return false // can't remove perms without an ID
  416. }
  417. return n.ID == o.ID
  418. })
  419. if !newHasOld && o.ID != "" && !slices.Contains(add, o) && !slices.Contains(update, o) {
  420. fs.Debugf(m.remote, "sortPermissions: will remove permission: %v %v (perm ID: %v)", o, o.Roles, o.ID)
  421. remove = append(remove, o)
  422. }
  423. }
  424. return add, update, remove
  425. }
  426. // processPermissions executes the add, update, and remove queues for writing permissions
  427. func (m *Metadata) processPermissions(ctx context.Context, add, update, remove []*api.PermissionsType) (newPermissions []*api.PermissionsType, err error) {
  428. errs := errcount.New()
  429. for _, p := range remove { // remove (need to do these first because of remove + add workaround)
  430. _, err := m.removePermission(ctx, p)
  431. if err != nil {
  432. fs.Errorf(m.remote, "Failed to remove permission: %v", err)
  433. errs.Add(err)
  434. }
  435. }
  436. for _, p := range add { // add
  437. newPs, _, err := m.addPermission(ctx, p)
  438. if err != nil {
  439. fs.Errorf(m.remote, "Failed to add permission: %v", err)
  440. errs.Add(err)
  441. continue
  442. }
  443. newPermissions = append(newPermissions, newPs...)
  444. }
  445. for _, p := range update { // update
  446. newP, _, err := m.updatePermission(ctx, p)
  447. if err != nil {
  448. fs.Errorf(m.remote, "Failed to update permission: %v", err)
  449. errs.Add(err)
  450. continue
  451. }
  452. newPermissions = append(newPermissions, newP)
  453. }
  454. err = errs.Err("failed to set permissions")
  455. if err != nil {
  456. err = fserrors.NoRetryError(err)
  457. }
  458. return newPermissions, err
  459. }
  460. // fillRecipients looks for recipients to add from the permission passed in.
  461. // It looks for an email address in identity.User.Email, ID, and DisplayName, otherwise it uses the identity.User.ID as r.ObjectID.
  462. // It considers both "GrantedTo" and "GrantedToIdentities".
  463. func fillRecipients(p *api.PermissionsType, driveType string) (recipients []api.DriveRecipient) {
  464. if p == nil {
  465. return recipients
  466. }
  467. ids := make(map[string]struct{}, len(p.GetGrantedToIdentities(driveType))+1)
  468. isUnique := func(s string) bool {
  469. _, ok := ids[s]
  470. return !ok && s != ""
  471. }
  472. addRecipient := func(identity *api.IdentitySet) {
  473. r := api.DriveRecipient{}
  474. id := ""
  475. if strings.ContainsRune(identity.User.Email, '@') {
  476. id = identity.User.Email
  477. r.Email = id
  478. } else if strings.ContainsRune(identity.User.ID, '@') {
  479. id = identity.User.ID
  480. r.Email = id
  481. } else if strings.ContainsRune(identity.User.DisplayName, '@') {
  482. id = identity.User.DisplayName
  483. r.Email = id
  484. } else {
  485. id = identity.User.ID
  486. r.ObjectID = id
  487. }
  488. if !isUnique(id) {
  489. return
  490. }
  491. ids[id] = struct{}{}
  492. recipients = append(recipients, r)
  493. }
  494. forIdentitySet := func(iSet *api.IdentitySet) {
  495. if iSet == nil {
  496. return
  497. }
  498. iS := *iSet
  499. forIdentity := func(i api.Identity) {
  500. if i != (api.Identity{}) {
  501. iS.User = i
  502. addRecipient(&iS)
  503. }
  504. }
  505. forIdentity(iS.User)
  506. forIdentity(iS.SiteUser)
  507. forIdentity(iS.Group)
  508. forIdentity(iS.SiteGroup)
  509. forIdentity(iS.Application)
  510. forIdentity(iS.Device)
  511. }
  512. for _, identitySet := range p.GetGrantedToIdentities(driveType) {
  513. forIdentitySet(identitySet)
  514. }
  515. forIdentitySet(p.GetGrantedTo(driveType))
  516. return recipients
  517. }
  518. // addPermission adds new permissions to an object or dir.
  519. // if p.Link.Scope == "anonymous" then it will also create a Public Link.
  520. func (m *Metadata) addPermission(ctx context.Context, p *api.PermissionsType) (newPs []*api.PermissionsType, resp *http.Response, err error) {
  521. opts := m.fs.newOptsCall(m.normalizedID, "POST", "/invite")
  522. req := &api.AddPermissionsRequest{
  523. Recipients: fillRecipients(p, m.fs.driveType),
  524. RequireSignIn: m.fs.driveType != driveTypePersonal, // personal and business have conflicting requirements
  525. Roles: p.Roles,
  526. }
  527. if m.fs.driveType != driveTypePersonal {
  528. req.RetainInheritedPermissions = false // not supported for personal
  529. }
  530. if p.Link != nil && p.Link.Scope == api.AnonymousScope {
  531. link, err := m.fs.PublicLink(ctx, m.remote, fs.DurationOff, false)
  532. if err != nil {
  533. return nil, nil, err
  534. }
  535. p.Link.WebURL = link
  536. newPs = append(newPs, p)
  537. if len(req.Recipients) == 0 {
  538. return newPs, nil, nil
  539. }
  540. }
  541. if len(req.Recipients) == 0 {
  542. fs.Debugf(m.remote, "skipping add permission -- at least one valid recipient is required")
  543. return nil, nil, nil
  544. }
  545. if len(req.Roles) == 0 {
  546. return nil, nil, errors.New("at least one role is required to add a permission (choices: read, write, owner, member)")
  547. }
  548. if slices.Contains(req.Roles, api.OwnerRole) {
  549. fs.Debugf(m.remote, "skipping add permission -- can't invite a user with 'owner' role")
  550. return nil, nil, nil
  551. }
  552. newP := &api.PermissionsResponse{}
  553. err = m.fs.pacer.Call(func() (bool, error) {
  554. resp, err = m.fs.srv.CallJSON(ctx, &opts, &req, &newP)
  555. return shouldRetry(ctx, resp, err)
  556. })
  557. return newP.Value, resp, err
  558. }
  559. // updatePermission updates an existing permission on an object or dir.
  560. // This requires the permission ID and a role to update (which will error if it is the same as the existing role.)
  561. // Role is the only property that can be updated.
  562. func (m *Metadata) updatePermission(ctx context.Context, p *api.PermissionsType) (newP *api.PermissionsType, resp *http.Response, err error) {
  563. opts := m.fs.newOptsCall(m.normalizedID, "PATCH", "/permissions/"+p.ID)
  564. req := api.UpdatePermissionsRequest{Roles: p.Roles} // roles is the only property that can be updated
  565. if len(req.Roles) == 0 {
  566. return nil, nil, errors.New("at least one role is required to update a permission (choices: read, write, owner, member)")
  567. }
  568. newP = &api.PermissionsType{}
  569. err = m.fs.pacer.Call(func() (bool, error) {
  570. resp, err = m.fs.srv.CallJSON(ctx, &opts, &req, &newP)
  571. return shouldRetry(ctx, resp, err)
  572. })
  573. return newP, resp, err
  574. }
  575. // removePermission removes an existing permission on an object or dir.
  576. // This requires the permission ID.
  577. func (m *Metadata) removePermission(ctx context.Context, p *api.PermissionsType) (resp *http.Response, err error) {
  578. opts := m.fs.newOptsCall(m.normalizedID, "DELETE", "/permissions/"+p.ID)
  579. opts.NoResponse = true
  580. err = m.fs.pacer.Call(func() (bool, error) {
  581. resp, err = m.fs.srv.CallJSON(ctx, &opts, nil, nil)
  582. return shouldRetry(ctx, resp, err)
  583. })
  584. return resp, err
  585. }
  586. // getPermissions gets the current permissions for an object or dir, from the API.
  587. func (f *Fs) getPermissions(ctx context.Context, normalizedID string) (p []*api.PermissionsType, resp *http.Response, err error) {
  588. opts := f.newOptsCall(normalizedID, "GET", "/permissions")
  589. permResp := &api.PermissionsResponse{}
  590. err = f.pacer.Call(func() (bool, error) {
  591. resp, err = f.srv.CallJSON(ctx, &opts, nil, &permResp)
  592. return shouldRetry(ctx, resp, err)
  593. })
  594. return permResp.Value, resp, err
  595. }
  596. func (f *Fs) newMetadata(remote string) *Metadata {
  597. return &Metadata{fs: f, remote: remote}
  598. }
  599. // returns true if metadata includes a "permissions" key and f.opt.MetadataPermissions includes "write".
  600. func (f *Fs) needsUpdatePermissions(metadata fs.Metadata) bool {
  601. _, ok := metadata["permissions"]
  602. return ok && f.opt.MetadataPermissions.IsSet(rwWrite)
  603. }
  604. // returns a non-zero btime if we have one
  605. // otherwise falls back to mtime
  606. func (o *Object) tryGetBtime(modTime time.Time) time.Time {
  607. if o.meta != nil && !o.meta.btime.IsZero() {
  608. return o.meta.btime
  609. }
  610. return modTime
  611. }
  612. // adds metadata (except permissions) if --metadata is in use
  613. func (o *Object) fetchMetadataForCreate(ctx context.Context, src fs.ObjectInfo, options []fs.OpenOption, modTime time.Time) (createRequest api.CreateUploadRequest, metadata fs.Metadata, err error) {
  614. createRequest = api.CreateUploadRequest{ // we set mtime no matter what
  615. Item: api.Metadata{
  616. FileSystemInfo: &api.FileSystemInfoFacet{
  617. CreatedDateTime: api.Timestamp(o.tryGetBtime(modTime)),
  618. LastModifiedDateTime: api.Timestamp(modTime),
  619. },
  620. },
  621. }
  622. meta, err := fs.GetMetadataOptions(ctx, o.fs, src, options)
  623. if err != nil {
  624. return createRequest, nil, fmt.Errorf("failed to read metadata from source object: %w", err)
  625. }
  626. if meta == nil {
  627. return createRequest, nil, nil // no metadata or --metadata not in use, so just return mtime
  628. }
  629. if o.meta == nil {
  630. o.meta = o.fs.newMetadata(o.Remote())
  631. }
  632. o.meta.mtime = modTime
  633. numSet, err := o.meta.Set(ctx, meta)
  634. if err != nil {
  635. return createRequest, meta, err
  636. }
  637. if numSet == 0 {
  638. return createRequest, meta, nil
  639. }
  640. createRequest.Item = o.meta.toAPIMetadata()
  641. return createRequest, meta, nil
  642. }
  643. // Fetch metadata and update updateInfo if --metadata is in use
  644. // modtime will still be set when there is no metadata to set
  645. func (f *Fs) fetchAndUpdateMetadata(ctx context.Context, src fs.ObjectInfo, options []fs.OpenOption, updateInfo *Object) (info *api.Item, err error) {
  646. meta, err := fs.GetMetadataOptions(ctx, f, src, options)
  647. if err != nil {
  648. return nil, fmt.Errorf("failed to read metadata from source object: %w", err)
  649. }
  650. if meta == nil {
  651. return updateInfo.setModTime(ctx, src.ModTime(ctx)) // no metadata or --metadata not in use, so just set modtime
  652. }
  653. if updateInfo.meta == nil {
  654. updateInfo.meta = f.newMetadata(updateInfo.Remote())
  655. }
  656. newInfo, err := updateInfo.updateMetadata(ctx, meta)
  657. if newInfo == nil {
  658. return info, err
  659. }
  660. return newInfo, err
  661. }
  662. // updateMetadata calls Get, Set, and Write
  663. func (o *Object) updateMetadata(ctx context.Context, meta fs.Metadata) (info *api.Item, err error) {
  664. _, err = o.meta.Get(ctx) // refresh permissions
  665. if err != nil {
  666. return nil, err
  667. }
  668. numSet, err := o.meta.Set(ctx, meta)
  669. if err != nil {
  670. return nil, err
  671. }
  672. if numSet == 0 {
  673. return nil, nil
  674. }
  675. info, err = o.meta.Write(ctx, o.fs.needsUpdatePermissions(meta))
  676. if err != nil {
  677. return info, err
  678. }
  679. err = o.setMetaData(info)
  680. if err != nil {
  681. return info, err
  682. }
  683. // Remove versions if required
  684. if o.fs.opt.NoVersions {
  685. err := o.deleteVersions(ctx)
  686. if err != nil {
  687. return info, fmt.Errorf("%v: Failed to remove versions: %v", o, err)
  688. }
  689. }
  690. return info, nil
  691. }
  692. // MkdirMetadata makes the directory passed in as dir.
  693. //
  694. // It shouldn't return an error if it already exists.
  695. //
  696. // If the metadata is not nil it is set.
  697. //
  698. // It returns the directory that was created.
  699. func (f *Fs) MkdirMetadata(ctx context.Context, dir string, metadata fs.Metadata) (fs.Directory, error) {
  700. var info *api.Item
  701. var meta *Metadata
  702. dirID, err := f.dirCache.FindDir(ctx, dir, false)
  703. if err == fs.ErrorDirNotFound {
  704. // Directory does not exist so create it
  705. var leaf, parentID string
  706. leaf, parentID, err = f.dirCache.FindPath(ctx, dir, true)
  707. if err != nil {
  708. return nil, err
  709. }
  710. info, meta, err = f.createDir(ctx, parentID, dir, leaf, metadata)
  711. if err != nil {
  712. return nil, err
  713. }
  714. if f.driveType != driveTypePersonal {
  715. // for some reason, OneDrive Business needs this extra step to set modtime, while Personal does not. Seems like a bug...
  716. fs.Debugf(dir, "setting time %v", meta.mtime)
  717. info, err = meta.Write(ctx, false)
  718. }
  719. } else if err == nil {
  720. // Directory exists and needs updating
  721. info, meta, err = f.updateDir(ctx, dirID, dir, metadata)
  722. }
  723. if err != nil {
  724. return nil, err
  725. }
  726. // Convert the info into a directory entry
  727. parent, _ := dircache.SplitPath(dir)
  728. entry, err := f.itemToDirEntry(ctx, parent, info)
  729. if err != nil {
  730. return nil, err
  731. }
  732. directory, ok := entry.(*Directory)
  733. if !ok {
  734. return nil, fmt.Errorf("internal error: expecting %T to be a *Directory", entry)
  735. }
  736. directory.meta = meta
  737. f.setSystemMetadata(info, directory.meta, entry.Remote(), dirMimeType)
  738. dirEntry, ok := entry.(fs.Directory)
  739. if !ok {
  740. return nil, fmt.Errorf("internal error: expecting %T to be an fs.Directory", entry)
  741. }
  742. return dirEntry, nil
  743. }
  744. // createDir makes a directory with pathID as parent and name leaf with optional metadata
  745. func (f *Fs) createDir(ctx context.Context, pathID, dirWithLeaf, leaf string, metadata fs.Metadata) (info *api.Item, meta *Metadata, err error) {
  746. // fs.Debugf(f, "CreateDir(%q, %q)\n", dirID, leaf)
  747. var resp *http.Response
  748. opts := f.newOptsCall(pathID, "POST", "/children")
  749. mkdir := api.CreateItemWithMetadataRequest{
  750. CreateItemRequest: api.CreateItemRequest{
  751. Name: f.opt.Enc.FromStandardName(leaf),
  752. ConflictBehavior: "fail",
  753. },
  754. }
  755. m := f.newMetadata(dirWithLeaf)
  756. m.mimeType = dirMimeType
  757. numSet := 0
  758. if len(metadata) > 0 {
  759. numSet, err = m.Set(ctx, metadata)
  760. if err != nil {
  761. return nil, m, err
  762. }
  763. if numSet > 0 {
  764. mkdir.Metadata = m.toAPIMetadata()
  765. }
  766. }
  767. err = f.pacer.Call(func() (bool, error) {
  768. resp, err = f.srv.CallJSON(ctx, &opts, &mkdir, &info)
  769. return shouldRetry(ctx, resp, err)
  770. })
  771. if err != nil {
  772. return nil, m, err
  773. }
  774. if f.needsUpdatePermissions(metadata) && numSet > 0 { // permissions must be done as a separate step
  775. m.normalizedID = info.GetID()
  776. err = m.RefreshPermissions(ctx)
  777. if err != nil {
  778. return info, m, err
  779. }
  780. err = m.WritePermissions(ctx)
  781. if err != nil {
  782. fs.Errorf(m.remote, "error writing permissions: %v", err)
  783. return info, m, err
  784. }
  785. }
  786. return info, m, nil
  787. }
  788. // updateDir updates an existing a directory with the metadata passed in
  789. func (f *Fs) updateDir(ctx context.Context, dirID, remote string, metadata fs.Metadata) (info *api.Item, meta *Metadata, err error) {
  790. d := f.newDir(dirID, remote)
  791. _, err = d.meta.Set(ctx, metadata)
  792. if err != nil {
  793. return nil, nil, err
  794. }
  795. info, err = d.meta.Write(ctx, f.needsUpdatePermissions(metadata))
  796. return info, d.meta, err
  797. }
  798. func (f *Fs) newDir(dirID, remote string) (d *Directory) {
  799. d = &Directory{
  800. fs: f,
  801. remote: remote,
  802. size: -1,
  803. items: -1,
  804. id: dirID,
  805. meta: f.newMetadata(remote),
  806. }
  807. d.meta.normalizedID = dirID
  808. return d
  809. }
  810. // Metadata returns metadata for a DirEntry
  811. //
  812. // It should return nil if there is no Metadata
  813. func (o *Object) Metadata(ctx context.Context) (metadata fs.Metadata, err error) {
  814. err = o.readMetaData(ctx)
  815. if err != nil {
  816. fs.Logf(o, "Failed to read metadata: %v", err)
  817. return nil, err
  818. }
  819. return o.meta.Get(ctx)
  820. }
  821. // DirSetModTime sets the directory modtime for dir
  822. func (f *Fs) DirSetModTime(ctx context.Context, dir string, modTime time.Time) error {
  823. dirID, err := f.dirCache.FindDir(ctx, dir, false)
  824. if err != nil {
  825. return err
  826. }
  827. d := f.newDir(dirID, dir)
  828. return d.SetModTime(ctx, modTime)
  829. }
  830. // SetModTime sets the metadata on the DirEntry to set the modification date
  831. //
  832. // If there is any other metadata it does not overwrite it.
  833. func (d *Directory) SetModTime(ctx context.Context, t time.Time) error {
  834. btime := t
  835. if d.meta != nil && !d.meta.btime.IsZero() {
  836. btime = d.meta.btime // if we already have a non-zero btime, preserve it
  837. }
  838. d.meta = d.fs.newMetadata(d.remote) // set only the mtime and btime
  839. d.meta.mtime = t
  840. d.meta.btime = btime
  841. _, err := d.meta.Write(ctx, false)
  842. return err
  843. }
  844. // Metadata returns metadata for a DirEntry
  845. //
  846. // It should return nil if there is no Metadata
  847. func (d *Directory) Metadata(ctx context.Context) (metadata fs.Metadata, err error) {
  848. return d.meta.Get(ctx)
  849. }
  850. // SetMetadata sets metadata for a Directory
  851. //
  852. // It should return fs.ErrorNotImplemented if it can't set metadata
  853. func (d *Directory) SetMetadata(ctx context.Context, metadata fs.Metadata) error {
  854. _, meta, err := d.fs.updateDir(ctx, d.id, d.remote, metadata)
  855. d.meta = meta
  856. return err
  857. }
  858. // Fs returns read only access to the Fs that this object is part of
  859. func (d *Directory) Fs() fs.Info {
  860. return d.fs
  861. }
  862. // String returns the name
  863. func (d *Directory) String() string {
  864. return d.remote
  865. }
  866. // Remote returns the remote path
  867. func (d *Directory) Remote() string {
  868. return d.remote
  869. }
  870. // ModTime returns the modification date of the file
  871. //
  872. // If one isn't available it returns the configured --default-dir-time
  873. func (d *Directory) ModTime(ctx context.Context) time.Time {
  874. if !d.meta.mtime.IsZero() {
  875. return d.meta.mtime
  876. }
  877. ci := fs.GetConfig(ctx)
  878. return time.Time(ci.DefaultTime)
  879. }
  880. // Size returns the size of the file
  881. func (d *Directory) Size() int64 {
  882. return d.size
  883. }
  884. // Items returns the count of items in this directory or this
  885. // directory and subdirectories if known, -1 for unknown
  886. func (d *Directory) Items() int64 {
  887. return d.items
  888. }
  889. // ID gets the optional ID
  890. func (d *Directory) ID() string {
  891. return d.id
  892. }
  893. // MimeType returns the content type of the Object if
  894. // known, or "" if not
  895. func (d *Directory) MimeType(ctx context.Context) string {
  896. return dirMimeType
  897. }