Skip to content

Commit

Permalink
ProtonDrive API implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
henrybear327 committed Jan 27, 2025
1 parent 67bd01a commit 34a951a
Show file tree
Hide file tree
Showing 49 changed files with 564 additions and 74 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Go Proton API

<a href="https://github.com/ProtonMail/go-proton-api/actions/workflows/check.yml"><img src="https://github.com/ProtonMail/go-proton-api/actions/workflows/check.yml/badge.svg?branch=master" alt="CI Status"></a>
<a href="https://pkg.go.dev/github.com/ProtonMail/go-proton-api"><img src="https://pkg.go.dev/badge/github.com/ProtonMail/go-proton-api" alt="GoDoc"></a>
<a href="https://goreportcard.com/report/github.com/ProtonMail/go-proton-api"><img src="https://goreportcard.com/badge/github.com/ProtonMail/go-proton-api" alt="Go Report Card"></a>
<a href="https://github.com/henrybear327/go-proton-api/actions/workflows/check.yml"><img src="https://github.com/henrybear327/go-proton-api/actions/workflows/check.yml/badge.svg?branch=master" alt="CI Status"></a>
<a href="https://pkg.go.dev/github.com/henrybear327/go-proton-api"><img src="https://pkg.go.dev/badge/github.com/henrybear327/go-proton-api" alt="GoDoc"></a>
<a href="https://goreportcard.com/report/github.com/henrybear327/go-proton-api"><img src="https://goreportcard.com/badge/github.com/henrybear327/go-proton-api" alt="Go Report Card"></a>
<a href="LICENSE"><img src="https://img.shields.io/github/license/ProtonMail/go-proton-api.svg" alt="License"></a>

This repository holds Go Proton API, a Go library implementing a client and development server for (a subset of) the Proton REST API.
Expand All @@ -20,4 +20,8 @@ Most of the integration tests run locally. The ones that interact with Proton se

## Contribution

The library is maintained by Proton AG, and is not actively looking for contributors.
This library is forked from [go-proton-api](https://github.com/ProtonMail/go-proton-api) in order to support the [Proton API Bridge](https://github.com/henrybear327/Proton-API-Bridge) project.

Contribution is welcomed!

The intention to upstream the changes are planned, once the changes to the codebase has stabalized.
2 changes: 1 addition & 1 deletion auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"testing"
"time"

"github.com/bradenaw/juniper/parallel"
"github.com/ProtonMail/go-proton-api"
"github.com/ProtonMail/go-proton-api/server"
"github.com/bradenaw/juniper/parallel"
"github.com/stretchr/testify/require"
)

Expand Down
5 changes: 3 additions & 2 deletions contact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package proton_test

import (
"bytes"
"testing"

"github.com/ProtonMail/gluon/rfc822"
"github.com/ProtonMail/go-proton-api"
"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/emersion/go-vcard"
"github.com/ProtonMail/go-proton-api"
"github.com/stretchr/testify/require"
"testing"
)

const message = `From: Nathaniel Borenstein <[email protected]>
Expand Down
2 changes: 1 addition & 1 deletion event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import (
"testing"
"time"

"github.com/google/uuid"
"github.com/ProtonMail/go-proton-api"
"github.com/ProtonMail/go-proton-api/server"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
)

Expand Down
12 changes: 8 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module github.com/ProtonMail/go-proton-api

go 1.23
go 1.22.0

toolchain go1.23.5

require (
github.com/Masterminds/semver/v3 v3.2.0
Expand All @@ -9,13 +11,13 @@ require (
github.com/ProtonMail/go-srp v0.0.7
github.com/ProtonMail/gopenpgp/v2 v2.8.2-proton
github.com/PuerkitoBio/goquery v1.8.1
github.com/bradenaw/juniper v0.12.0
github.com/bradenaw/juniper v0.13.1
github.com/emersion/go-message v0.16.0
github.com/emersion/go-vcard v0.0.0-20230331202150-f3d26859ccd3
github.com/emersion/go-vcard v0.0.0-20230626131229-38c18b295bbd
github.com/gin-gonic/gin v1.9.1
github.com/go-resty/resty/v2 v2.7.0
github.com/google/uuid v1.3.0
github.com/sirupsen/logrus v1.9.2
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.3
github.com/urfave/cli/v2 v2.24.4
gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a
Expand Down Expand Up @@ -69,3 +71,5 @@ require (
)

replace github.com/go-resty/resty/v2 => github.com/LBeernaertProton/resty/v2 v2.0.0-20231129100320-dddf8030d93a

replace github.com/ProtonMail/go-proton-api => github.com/henrybear327/go-proton-api v0.0.0-20250127182155-ab0ad0b85cddd6757b087c8ebf8e67d7bb490e6a
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJs
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/bradenaw/juniper v0.12.0 h1:Q/7icpPQD1nH/La5DobQfNEtwyrBSiSu47jOQx7lJEM=
github.com/bradenaw/juniper v0.12.0/go.mod h1:Z2B7aJlQ7xbfWsnMLROj5t/5FQ94/MkIdKC30J4WvzI=
github.com/bradenaw/juniper v0.13.1 h1:9P7/xeaYuEyqPuJHSHCJoisWyPvZH4FAi59BxJLh7F8=
github.com/bradenaw/juniper v0.13.1/go.mod h1:Z2B7aJlQ7xbfWsnMLROj5t/5FQ94/MkIdKC30J4WvzI=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
Expand All @@ -45,8 +45,8 @@ github.com/emersion/go-message v0.16.0 h1:uZLz8ClLv3V5fSFF/fFdW9jXjrZkXIpE1Fn8fK
github.com/emersion/go-message v0.16.0/go.mod h1:pDJDgf/xeUIF+eicT6B/hPX/ZbEorKkUMPOxrPVG2eQ=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/emersion/go-vcard v0.0.0-20230331202150-f3d26859ccd3 h1:hQ1wTMaKcGfobYRT88RM8NFNyX+IQHvagkm/tqViU98=
github.com/emersion/go-vcard v0.0.0-20230331202150-f3d26859ccd3/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM=
github.com/emersion/go-vcard v0.0.0-20230626131229-38c18b295bbd h1:n1kH4lDJLDgO8sqkt0QgeQXKims1L8khdgilk9G5lm8=
github.com/emersion/go-vcard v0.0.0-20230626131229-38c18b295bbd/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
Expand Down Expand Up @@ -100,8 +100,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y=
github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
Expand Down
2 changes: 1 addition & 1 deletion helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import (
"testing"

"github.com/ProtonMail/gluon/async"
"github.com/ProtonMail/go-proton-api"
"github.com/bradenaw/juniper/iterator"
"github.com/bradenaw/juniper/stream"
"github.com/google/uuid"
"github.com/ProtonMail/go-proton-api"
"github.com/stretchr/testify/require"
)

Expand Down
2 changes: 1 addition & 1 deletion keyring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package proton_test
import (
"testing"

"github.com/ProtonMail/go-proton-api"
"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/ProtonMail/gopenpgp/v2/helper"
"github.com/ProtonMail/go-proton-api"
"github.com/stretchr/testify/require"
)

Expand Down
84 changes: 80 additions & 4 deletions link.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package proton

import (
"context"
"encoding/json"
"net/http"

"github.com/go-resty/resty/v2"
)
Expand All @@ -20,14 +22,55 @@ func (c *Client) GetLink(ctx context.Context, shareID, linkID string) (Link, err
return res.Link, nil
}

func (c *Client) MoveLink(ctx context.Context, shareID, linkID string, req MoveLinkReq) error {
var res struct {
Code int
}

if err := c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
return r.SetResult(&res).SetBody(req).Put("/drive/shares/" + shareID + "/links/" + linkID + "/move")
}); err != nil {
return err
}

return nil
}

func (c *Client) CreateFile(ctx context.Context, shareID string, req CreateFileReq) (CreateFileRes, error) {
var res struct {
Code int
File CreateFileRes
}

if err := c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
resp, err := c.doRes(ctx, func(r *resty.Request) (*resty.Response, error) {
return r.SetResult(&res).SetBody(req).Post("/drive/shares/" + shareID + "/files")
}); err != nil {
})
if err != nil { // if the status code is not 200~299, it's considered an error

// handle the file or folder name exists error
if resp.StatusCode() == http.StatusUnprocessableEntity /* 422 */ {
var apiError APIError
err := json.Unmarshal(resp.Body(), &apiError)
if err != nil {
return CreateFileRes{}, err
}
if apiError.Code == AFileOrFolderNameExist {
return CreateFileRes{}, ErrFileNameExist // since we are in CreateFile, so we return this error
}
}

// handle draft exists error
if resp.StatusCode() == http.StatusConflict /* 409 */ {
var apiError APIError
err := json.Unmarshal(resp.Body(), &apiError)
if err != nil {
return CreateFileRes{}, err
}
if apiError.Code == ADraftExist {
return CreateFileRes{}, ErrADraftExist
}
}

return CreateFileRes{}, err
}

Expand All @@ -39,11 +82,44 @@ func (c *Client) CreateFolder(ctx context.Context, shareID string, req CreateFol
Folder CreateFolderRes
}

if err := c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
resp, err := c.doRes(ctx, func(r *resty.Request) (*resty.Response, error) {
return r.SetResult(&res).SetBody(req).Post("/drive/shares/" + shareID + "/folders")
}); err != nil {
})
if err != nil { // if the status code is not 200~299, it's considered an error

// handle the file or folder name exists error
if resp.StatusCode() == http.StatusUnprocessableEntity /* 422 */ {
// log.Println(resp.String())
var apiError APIError
err := json.Unmarshal(resp.Body(), &apiError)
if err != nil {
return CreateFolderRes{}, err
}
if apiError.Code == AFileOrFolderNameExist {
return CreateFolderRes{}, ErrFolderNameExist // since we are in CreateFolder, so we return this error
}
}

return CreateFolderRes{}, err
}

return res.Folder, nil
}

func (c *Client) CheckAvailableHashes(ctx context.Context, shareID, linkID string, req CheckAvailableHashesReq) (CheckAvailableHashesRes, error) {
var res struct {
AvailableHashes []string
PendingHashesData []PendingHashData
}

if err := c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
return r.SetResult(&res).SetBody(req).Post("/drive/shares/" + shareID + "/links/" + linkID + "/checkAvailableHashes")
}); err != nil {
return CheckAvailableHashesRes{}, err
}

return CheckAvailableHashesRes{
AvailableHashes: res.AvailableHashes,
PendingHashesData: res.PendingHashesData,
}, nil
}
38 changes: 37 additions & 1 deletion link_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ func (c *Client) ListRevisions(ctx context.Context, shareID, linkID string) ([]R
return res.Revisions, nil
}

func (c *Client) GetRevisionAllBlocks(ctx context.Context, shareID, linkID, revisionID string) (Revision, error) {
var res struct {
Revision Revision
}

if err := c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
return r.
SetResult(&res).
Get("/drive/shares/" + shareID + "/files/" + linkID + "/revisions/" + revisionID)
}); err != nil {
return Revision{}, err
}

return res.Revision, nil
}

func (c *Client) GetRevision(ctx context.Context, shareID, linkID, revisionID string, fromBlock, pageSize int) (Revision, error) {
if fromBlock < 1 {
return Revision{}, fmt.Errorf("fromBlock must be greater than 0")
Expand All @@ -48,8 +64,28 @@ func (c *Client) GetRevision(ctx context.Context, shareID, linkID, revisionID st
return res.Revision, nil
}

func (c *Client) UpdateRevision(ctx context.Context, shareID, linkID, revisionID string, req UpdateRevisionReq) error {
func (c *Client) CommitRevision(ctx context.Context, shareID, linkID, revisionID string, req CommitRevisionReq) error {
return c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
return r.SetBody(req).Put("/drive/shares/" + shareID + "/files/" + linkID + "/revisions/" + revisionID)
})
}

func (c *Client) DeleteRevision(ctx context.Context, shareID, linkID, revisionID string) error {
return c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
return r.Delete("/drive/shares/" + shareID + "/files/" + linkID + "/revisions/" + revisionID)
})
}

func (c *Client) CreateRevision(ctx context.Context, shareID, linkID string) (CreateRevisionRes, error) {
var res struct {
Revision CreateRevisionRes
}

if err := c.do(ctx, func(r *resty.Request) (*resty.Response, error) {
return r.SetResult(&res).Post("/drive/shares/" + shareID + "/files/" + linkID + "/revisions")
}); err != nil {
return CreateRevisionRes{}, err
}

return res.Revision, nil
}
29 changes: 29 additions & 0 deletions link_file_type_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package proton_test

import (
"testing"

"github.com/ProtonMail/go-proton-api"
)

func Test_HMAC(t *testing.T) {
hashKey := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")

check := func(t *testing.T, str, ans string) {
ret, err := proton.GetNameHash(str, hashKey)
if err != nil {
t.Fatal(err)
}
if ret != ans {
t.Fatal("Mismatching HMAC", ans, ret)
}
}

check(t, "garçon", "02ef4861a4b9f833aa104a8210f5eb338e231c9532d9c2551aaf76bafb511208")
check(t, "apă", "fd80de16c11bdcea2783274f6b7f334093ef95d58c7381c615005614ed77dc94")
check(t, "bala", "35733f41071d4997876b5bb54acc1d587646bdf1251f9b9c49ee9dc023a69962")
check(t, "țânțar", "4f4dee0cd87928027982c6ca280d2c7661073082ed46a82c111880126b0c3e14")
check(t, "întuneric", "6bed2bff136e165ad54d0a2a9a549481c88aca55d567c93123e0e5b876c291b2")
check(t, "mädchen", "2b112b1b7ac4fd9dae5a2acd8fcf2e905bd92a06a95dc4495fa012bda93e8607")
check(t, "integrationTestImage.png", "2e700ef3b52379a9277ac48bcfc5dff56e6927274267a0df4673f4f21e5d04d6")
}
Loading

0 comments on commit 34a951a

Please sign in to comment.