Files
Proton-API-Bridge/folder.go
Chun-Hung Tseng cce9f787a8 Add MoveFile code
2023-06-26 21:56:16 +02:00

319 lines
8.5 KiB
Go

package proton_api_bridge
import (
"context"
"log"
"os"
"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/henrybear327/go-proton-api"
)
type ProtonDirectoryData struct {
Link *proton.Link
Name string
IsFolder bool
}
func (protonDrive *ProtonDrive) ListDirectory(
ctx context.Context,
folderLinkID string) ([]*ProtonDirectoryData, error) {
ret := make([]*ProtonDirectoryData, 0)
folderLink, err := protonDrive.c.GetLink(ctx, protonDrive.MainShare.ShareID, folderLinkID)
if err != nil {
return nil, err
}
if folderLink.State == proton.LinkStateActive {
childrenLinks, err := protonDrive.c.ListChildren(ctx, protonDrive.MainShare.ShareID, folderLink.LinkID, true)
if err != nil {
return nil, err
}
if childrenLinks != nil {
folderParentKR, err := protonDrive.getNodeKRByID(ctx, folderLink.ParentLinkID)
if err != nil {
return nil, err
}
defer folderParentKR.ClearPrivateParams()
folderLinkKR, err := folderLink.GetKeyRing(folderParentKR, protonDrive.AddrKR)
if err != nil {
return nil, err
}
defer folderLinkKR.ClearPrivateParams()
for i := range childrenLinks {
if childrenLinks[i].State != proton.LinkStateActive {
continue
}
name, err := childrenLinks[i].GetName(folderLinkKR, protonDrive.AddrKR)
if err != nil {
return nil, err
}
ret = append(ret, &ProtonDirectoryData{
Link: &childrenLinks[i],
Name: name,
IsFolder: childrenLinks[i].Type == proton.LinkTypeFolder,
})
}
}
}
return ret, nil
}
func (protonDrive *ProtonDrive) ListDirectoriesRecursively(
ctx context.Context,
parentNodeKR *crypto.KeyRing,
link *proton.Link,
download bool,
maxDepth, curDepth /* 0-based */ int,
excludeRoot bool,
pathSoFar string,
paths *[]string) error {
/*
Assumptions:
- we only care about the active ones
*/
if link.State != proton.LinkStateActive {
return nil
}
// log.Println("curDepth", curDepth, "pathSoFar", pathSoFar)
var currentPath = ""
if !(excludeRoot && curDepth == 0) {
name, err := link.GetName(parentNodeKR, protonDrive.AddrKR)
if err != nil {
return err
}
currentPath = pathSoFar + "/" + name
// log.Println("currentPath", currentPath)
if paths != nil {
*paths = append(*paths, currentPath)
}
}
if download {
if protonDrive.Config.DataFolderName == "" {
return ErrDataFolderNameIsEmpty
}
if link.Type == proton.LinkTypeFile {
log.Println("Downloading", currentPath)
defer log.Println("Completes downloading", currentPath)
byteArray, _, err := protonDrive.DownloadFile(ctx, link)
if err != nil {
return err
}
err = os.WriteFile("./"+protonDrive.Config.DataFolderName+"/"+currentPath, byteArray, 0777)
if err != nil {
return err
}
} else /* folder */ {
if !(excludeRoot && curDepth == 0) {
// log.Println("Creating folder", currentPath)
// defer log.Println("Completes creating folder", currentPath)
err := os.Mkdir("./"+protonDrive.Config.DataFolderName+"/"+currentPath, 0777)
if err != nil {
return err
}
}
}
}
if maxDepth == -1 || curDepth < maxDepth {
if link.Type == proton.LinkTypeFolder {
childrenLinks, err := protonDrive.c.ListChildren(ctx, protonDrive.MainShare.ShareID, link.LinkID, true)
if err != nil {
return err
}
// log.Printf("childrenLinks len = %v, %#v", len(childrenLinks), childrenLinks)
if childrenLinks != nil {
// get current node's keyring
linkKR, err := link.GetKeyRing(parentNodeKR, protonDrive.AddrKR)
if err != nil {
return err
}
defer linkKR.ClearPrivateParams()
for _, childLink := range childrenLinks {
err = protonDrive.ListDirectoriesRecursively(ctx, linkKR, &childLink, download, maxDepth, curDepth+1, excludeRoot, currentPath, paths)
if err != nil {
return err
}
}
}
}
}
return nil
}
func (protonDrive *ProtonDrive) CreateNewFolderByID(ctx context.Context, parentLinkID string, folderName string) (string, error) {
parentLink, err := protonDrive.c.GetLink(ctx, protonDrive.MainShare.ShareID, parentLinkID)
if err != nil {
return "", err
}
return protonDrive.CreateNewFolder(ctx, &parentLink, folderName)
}
func (protonDrive *ProtonDrive) CreateNewFolder(ctx context.Context, parentLink *proton.Link, folderName string) (string, error) {
// TODO: check for duplicated folder name
parentNodeKR, err := protonDrive.getNodeKR(ctx, parentLink)
if err != nil {
return "", err
}
newNodeKey, newNodePassphraseEnc, newNodePassphraseSignature, err := generateNodeKeys(parentNodeKR, protonDrive.AddrKR)
if err != nil {
return "", err
}
createFolderReq := proton.CreateFolderReq{
ParentLinkID: parentLink.LinkID,
// Name string
// Hash string
NodeKey: newNodeKey,
// NodeHashKey string
NodePassphrase: newNodePassphraseEnc,
NodePassphraseSignature: newNodePassphraseSignature,
SignatureAddress: protonDrive.signatureAddress,
}
/* Name is encrypted using the parent's keyring, and signed with address key */
err = createFolderReq.SetName(folderName, protonDrive.AddrKR, parentNodeKR)
if err != nil {
return "", err
}
parentHashKey, err := parentLink.GetHashKey(parentNodeKR)
if err != nil {
return "", err
}
err = createFolderReq.SetHash(folderName, parentHashKey)
if err != nil {
return "", err
}
newNodeKR, err := getKeyRing(parentNodeKR, protonDrive.AddrKR, newNodeKey, newNodePassphraseEnc, newNodePassphraseSignature)
if err != nil {
return "", err
}
err = createFolderReq.SetNodeHashKey(newNodeKR)
if err != nil {
return "", err
}
createFolderResp, err := protonDrive.c.CreateFolder(ctx, protonDrive.MainShare.ShareID, createFolderReq)
if err != nil {
return "", err
}
// log.Printf("createFolderResp %#v", createFolderResp)
return createFolderResp.ID, nil
}
func (protonDrive *ProtonDrive) MoveFileByID(ctx context.Context, srcLinkID, dstParentLinkID string, dstName string) error {
srcLink, err := protonDrive.c.GetLink(ctx, protonDrive.MainShare.ShareID, srcLinkID)
if err != nil {
return err
}
if srcLink.State != proton.LinkStateActive {
return ErrLinkMustBeActive
}
dstParentLink, err := protonDrive.c.GetLink(ctx, protonDrive.MainShare.ShareID, dstParentLinkID)
if err != nil {
return err
}
if dstParentLink.State != proton.LinkStateActive {
return ErrLinkMustBeActive
}
return protonDrive.MoveFile(ctx, &srcLink, &dstParentLink, dstName)
}
func (protonDrive *ProtonDrive) MoveFile(ctx context.Context, srcLink *proton.Link, dstParentLink *proton.Link, dstName string) error {
return protonDrive.MoveFolder(ctx, srcLink, dstParentLink, dstName)
}
func (protonDrive *ProtonDrive) MoveFolderByID(ctx context.Context, srcLinkID, dstParentLinkID, dstName string) error {
srcLink, err := protonDrive.c.GetLink(ctx, protonDrive.MainShare.ShareID, srcLinkID)
if err != nil {
return err
}
if srcLink.State != proton.LinkStateActive {
return ErrLinkMustBeActive
}
dstParentLink, err := protonDrive.c.GetLink(ctx, protonDrive.MainShare.ShareID, dstParentLinkID)
if err != nil {
return err
}
if dstParentLink.State != proton.LinkStateActive {
return ErrLinkMustBeActive
}
return protonDrive.MoveFolder(ctx, &srcLink, &dstParentLink, dstName)
}
func (protonDrive *ProtonDrive) MoveFolder(ctx context.Context, srcLink *proton.Link, dstParentLink *proton.Link, dstName string) error {
return protonDrive.moveLink(ctx, srcLink, dstParentLink, dstName)
}
func (protonDrive *ProtonDrive) moveLink(ctx context.Context, srcLink *proton.Link, dstParentLink *proton.Link, dstName string) error {
// we are moving the srcLink to under dstParentLink, with name dstName
req := proton.MoveLinkReq{
ParentLinkID: dstParentLink.LinkID,
OriginalHash: srcLink.Hash,
SignatureAddress: protonDrive.signatureAddress,
}
dstParentKR, err := protonDrive.getNodeKR(ctx, dstParentLink)
if err != nil {
return err
}
err = req.SetName(dstName, protonDrive.AddrKR, dstParentKR)
if err != nil {
return err
}
dstParentHashKey, err := dstParentLink.GetHashKey(dstParentKR)
if err != nil {
return err
}
err = req.SetHash(dstName, dstParentHashKey)
if err != nil {
return err
}
srcParentKR, err := protonDrive.getNodeKRByID(ctx, srcLink.ParentLinkID)
if err != nil {
return err
}
nodePassphrase, err := reencryptKeyPacket(srcParentKR, dstParentKR, protonDrive.AddrKR, srcLink.NodePassphrase)
if err != nil {
return err
}
req.NodePassphrase = nodePassphrase
req.NodePassphraseSignature = srcLink.NodePassphraseSignature
return protonDrive.c.MoveLink(ctx, protonDrive.MainShare.ShareID, srcLink.LinkID, req)
}