Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions file_upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"io"
"mime"
"os"
Expand All @@ -24,6 +25,17 @@ func (protonDrive *ProtonDrive) handleRevisionConflict(ctx context.Context, link

draftRevision, err := protonDrive.GetRevisions(ctx, link, proton.RevisionStateDraft)
if err != nil {
// If we can't list revisions but the link is already in draft state
// (e.g. a broken/incomplete upload from a previous failed attempt)
// and the user wants to replace existing drafts, delete the link and
// let the caller retry from scratch rather than failing outright.
if protonDrive.Config.ReplaceExistingDraft && link.State == proton.LinkStateDraft {
err = protonDrive.c.DeleteChildren(ctx, protonDrive.MainShare.ShareID, link.ParentLinkID, linkID)
if err != nil {
return "", false, err
}
return "", true, nil
}
return "", false, err
}

Expand Down Expand Up @@ -250,6 +262,18 @@ func (protonDrive *ProtonDrive) uploadAndCollectBlockData(ctx context.Context, n
return nil, 0, nil, "", ErrMissingInputUploadAndCollectBlockData
}

// Fetch the per-revision verification code required by Proton's storage backend.
// Each block's Verifier.Token is produced by XOR-ing this code with the first
// bytes of that block's ciphertext (per the Proton Drive JS SDK spec).
revVerification, err := protonDrive.c.GetRevisionVerification(ctx, protonDrive.MainShare.VolumeID, linkID, revisionID)
if err != nil {
return nil, 0, nil, "", fmt.Errorf("uploadAndCollectBlockData: get revision verification: %w", err)
}
verificationCode, err := base64.StdEncoding.DecodeString(revVerification.VerificationCode)
if err != nil {
return nil, 0, nil, "", fmt.Errorf("uploadAndCollectBlockData: decode verification code: %w", err)
}

totalFileSize := int64(0)

pendingUploadBlocks := make([]PendingUploadBlocks, 0)
Expand Down Expand Up @@ -309,7 +333,7 @@ func (protonDrive *ProtonDrive) uploadAndCollectBlockData(ctx context.Context, n
blockSizes := make([]int64, 0)
for i := 1; shouldContinue; i++ {
if (i-1) > 0 && (i-1)%UPLOAD_BATCH_BLOCK_SIZE == 0 {
err := uploadPendingBlocks()
err = uploadPendingBlocks()
if err != nil {
return nil, 0, nil, "", err
}
Expand Down Expand Up @@ -365,17 +389,31 @@ func (protonDrive *ProtonDrive) uploadAndCollectBlockData(ctx context.Context, n
}
manifestSignatureData = append(manifestSignatureData, hash...)

// Compute per-block verifier token: XOR verificationCode with the
// leading bytes of the encrypted block (zero-padded if block is shorter).
verificationToken := make([]byte, len(verificationCode))
for j, v := range verificationCode {
var b byte
if j < len(encData) {
b = encData[j]
}
verificationToken[j] = v ^ b
}

pendingUploadBlocks = append(pendingUploadBlocks, PendingUploadBlocks{
blockUploadInfo: proton.BlockUploadInfo{
Index: i, // iOS drive: BE starts with 1
Size: int64(len(encData)),
EncSignature: encSignatureStr,
Hash: base64Hash,
Verifier: proton.BlockUploadVerifier{
Token: base64.StdEncoding.EncodeToString(verificationToken),
},
},
encData: encData,
})
}
err := uploadPendingBlocks()
err = uploadPendingBlocks()
if err != nil {
return nil, 0, nil, "", err
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ toolchain go1.23.5
require (
github.com/ProtonMail/gluon v0.17.1-0.20230724134000-308be39be96e
github.com/ProtonMail/gopenpgp/v2 v2.8.2
github.com/rclone/go-proton-api v1.0.1-0.20260127173028-eb465cac3b18
github.com/rclone/go-proton-api v1.0.1-0.20260218123427-1a63a293e3a2
github.com/relvacode/iso8601 v1.6.0
golang.org/x/sync v0.10.0
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rclone/go-proton-api v1.0.1-0.20260127173028-eb465cac3b18 h1:Lc+d3ISfQaMJKWZOE7z4ZSY4RVmdzbn1B0IM8xN18qM=
github.com/rclone/go-proton-api v1.0.1-0.20260127173028-eb465cac3b18/go.mod h1:LB2kCEaZMzNn3ocdz+qYfxXmuLxxN0ka62KJd2x53Bc=
github.com/rclone/go-proton-api v1.0.1-0.20260218123427-1a63a293e3a2 h1:QR87vlRq+z0JwJsUteEhsXcSrXGJ2yte5MocMSfajM4=
github.com/rclone/go-proton-api v1.0.1-0.20260218123427-1a63a293e3a2/go.mod h1:LB2kCEaZMzNn3ocdz+qYfxXmuLxxN0ka62KJd2x53Bc=
github.com/relvacode/iso8601 v1.6.0 h1:eFXUhMJN3Gz8Rcq82f9DTMW0svjtAVuIEULglM7QHTU=
github.com/relvacode/iso8601 v1.6.0/go.mod h1:FlNp+jz+TXpyRqgmM7tnzHHzBnz776kmAH2h3sZCn0I=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
Expand Down