Files
Proton-API-Bridge/file.go
Mark Ferrell 04a533aec9 feat: leverage go's mod replace sanely
The Proton-API-Bridge should not be attempting to take over, nor
re-define, the existing proton-go-api interfaces. This is known has an
"unfriendly fork" and increases the overall complexity of maintaining
the Proton-API-Bridge.

All changes to the proton-go-api should be done as "friendly" as
possible, and should be submitted back upstream for inclusion. This
improves the functionality of the proton-go-api for more than just 1
project, and it reduces the long-term maintenance required for
Proton-API-Bridge.

To this end, Proton-API-Bridge should be written to always assume it is
using github.com/ProtonMail/proton-go-api and leverage the `replace`
operation in go.mod in order to leverage our "friendly fork" of
proton-go-api. I.e. the long-term goal should be to _not_ maintain a
fork of proton-go-api.

> Anything else would be uncivilized.
2025-01-16 05:38:50 -08:00

145 lines
3.9 KiB
Go

package proton_api_bridge
import (
"context"
"strings"
"time"
"github.com/ProtonMail/go-proton-api"
"github.com/relvacode/iso8601"
)
type FileSystemAttrs struct {
ModificationTime time.Time
Size int64
BlockSizes []int64
Digests string // sha1 string
}
func (protonDrive *ProtonDrive) GetRevisions(ctx context.Context, link *proton.Link, revisionType proton.RevisionState) ([]*proton.RevisionMetadata, error) {
revisions, err := protonDrive.c.ListRevisions(ctx, protonDrive.MainShare.ShareID, link.LinkID)
if err != nil {
return nil, err
}
ret := make([]*proton.RevisionMetadata, 0)
// Revisions are only for files, they represent “versions” of files.
// Each file can have 1 active/draft revision and n obsolete revisions.
for i := range revisions {
if revisions[i].State == revisionType {
ret = append(ret, &revisions[i])
}
}
return ret, nil
}
func (protonDrive *ProtonDrive) GetActiveRevisionAttrsByID(ctx context.Context, linkID string) (*FileSystemAttrs, error) {
link, err := protonDrive.getLink(ctx, linkID)
if err != nil {
return nil, err
}
return protonDrive.GetActiveRevisionAttrs(ctx, link)
}
// Might return nil when xattr is missing
func (protonDrive *ProtonDrive) GetActiveRevisionAttrs(ctx context.Context, link *proton.Link) (*FileSystemAttrs, error) {
if link == nil {
return nil, ErrLinkMustNotBeNil
}
revisionMetadata := link.FileProperties.ActiveRevision
revisionMetadata.XAttr = link.XAttr
nodeKR, err := protonDrive.getLinkKR(ctx, link)
if err != nil {
return nil, err
}
signatureVerificationKR, err := protonDrive.getSignatureVerificationKeyring([]string{revisionMetadata.SignatureEmail})
if err != nil {
return nil, err
}
revisionXAttrCommon, err := revisionMetadata.GetDecXAttrString(signatureVerificationKR, nodeKR)
if err != nil {
return nil, err
}
if revisionXAttrCommon == nil {
return nil, nil
}
modificationTime, err := iso8601.ParseString(revisionXAttrCommon.ModificationTime)
if err != nil {
return nil, err
}
var sha1Hash string
if val, ok := revisionXAttrCommon.Digests["SHA1"]; ok {
sha1Hash = strings.ToLower(val) // https://github.com/henrybear327/Proton-API-Bridge/issues/21 and https://github.com/rclone/rclone/issues/7345#issuecomment-1821463100
} else {
sha1Hash = ""
}
return &FileSystemAttrs{
ModificationTime: modificationTime,
Size: revisionXAttrCommon.Size,
BlockSizes: revisionXAttrCommon.BlockSizes,
Digests: sha1Hash,
}, nil
}
func (protonDrive *ProtonDrive) GetActiveRevisionWithAttrs(ctx context.Context, link *proton.Link) (*proton.Revision, *FileSystemAttrs, error) {
if link == nil {
return nil, nil, ErrLinkMustNotBeNil
}
revisionsMetadata, err := protonDrive.GetRevisions(ctx, link, proton.RevisionStateActive)
if err != nil {
return nil, nil, err
}
if len(revisionsMetadata) != 1 {
return nil, nil, ErrCantFindActiveRevision
}
revision, err := protonDrive.c.GetRevisionAllBlocks(ctx, protonDrive.MainShare.ShareID, link.LinkID, revisionsMetadata[0].ID)
if err != nil {
return nil, nil, err
}
nodeKR, err := protonDrive.getLinkKR(ctx, link)
if err != nil {
return nil, nil, err
}
signatureVerificationKR, err := protonDrive.getSignatureVerificationKeyring([]string{link.FileProperties.ActiveRevision.SignatureEmail})
if err != nil {
return nil, nil, err
}
revisionXAttrCommon, err := revision.GetDecXAttrString(signatureVerificationKR, nodeKR)
if err != nil {
return nil, nil, err
}
modificationTime, err := iso8601.ParseString(revisionXAttrCommon.ModificationTime)
if err != nil {
return nil, nil, err
}
var sha1Hash string
if val, ok := revisionXAttrCommon.Digests["SHA1"]; ok {
sha1Hash = val
} else {
sha1Hash = ""
}
return &revision, &FileSystemAttrs{
ModificationTime: modificationTime,
Size: revisionXAttrCommon.Size,
BlockSizes: revisionXAttrCommon.BlockSizes,
Digests: sha1Hash,
}, nil
}