mirror of
https://github.com/vwxyzjn/portwarden
synced 2025-12-29 05:13:14 +00:00
133 lines
3.4 KiB
Go
133 lines
3.4 KiB
Go
package portwarden
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/mholt/archiver"
|
|
"github.com/tidwall/pretty"
|
|
)
|
|
|
|
const (
|
|
BackupFolderName = "./portwarden_backup/"
|
|
ErrVaultIsLocked = "vault is locked"
|
|
ErrNoPhassPhraseProvided = "no passphrase provided"
|
|
ErrNoFilenameProvided = "no filename provided"
|
|
ErrSessionKeyExtractionFailed = "session key extraction failed"
|
|
|
|
BWErrNotLoggedIn = "You are not logged in."
|
|
BWErrInvalidMasterPassword = "Invalid master password."
|
|
BWEnterEmailAddress = "? Email address:"
|
|
BWEnterMasterPassword = "? Master password:"
|
|
)
|
|
|
|
func EncryptBackup(fileName, passphrase, sessionKey string, sleepMilliseconds int) error {
|
|
pwes := []PortWardenElement{}
|
|
|
|
// save formmated json to "main.json"
|
|
rawByte := BWListItemsRawBytes(sessionKey)
|
|
if err := json.Unmarshal(rawByte, &pwes); err != nil {
|
|
return err
|
|
}
|
|
err := BWGetAllAttachments(BackupFolderName, sessionKey, pwes, sleepMilliseconds)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
formattedByte := pretty.Pretty(rawByte)
|
|
if err := ioutil.WriteFile(BackupFolderName+"main.json", formattedByte, 0644); err != nil {
|
|
return err
|
|
}
|
|
|
|
var b bytes.Buffer
|
|
writer := bufio.NewWriter(&b)
|
|
err = archiver.Zip.Write(writer, []string{BackupFolderName})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// derive a key from the master password
|
|
err = EncryptFile(fileName, b.Bytes(), passphrase)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// cleanup: delete temporary files
|
|
err = os.RemoveAll(BackupFolderName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DecryptBackup(fileName, passphrase string) error {
|
|
tb, err := DecryptFile(fileName, passphrase)
|
|
if err != nil {
|
|
fmt.Println("decryption failed: " + err.Error())
|
|
return err
|
|
}
|
|
if err := ioutil.WriteFile(fileName+".decrypted"+".zip", tb, 0644); err != nil {
|
|
fmt.Println("decryption failed: " + err.Error())
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ExtractSessionKey(stdout string) (string, error) {
|
|
r := regexp.MustCompile(`BW_SESSION=".+"`)
|
|
matches := r.FindAllString(stdout, 1)
|
|
if len(matches) == 0 {
|
|
return "", errors.New(ErrSessionKeyExtractionFailed)
|
|
}
|
|
sessionKeyRawString := r.FindAllString(stdout, 1)[0]
|
|
sessionKey := strings.TrimPrefix(sessionKeyRawString, `BW_SESSION="`)
|
|
sessionKey = sessionKey[:len(sessionKey)-1]
|
|
return sessionKey, nil
|
|
}
|
|
|
|
func BWListItemsRawBytes(sessionKey string) []byte {
|
|
var stdout, stderr bytes.Buffer
|
|
cmd := exec.Command("bw", "list", "items", "--session", sessionKey)
|
|
cmd.Stdin = os.Stdin
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return stdout.Bytes()
|
|
}
|
|
|
|
func BWGetAttachment(outputDir, itemID, attachmentID, sessionKey string) error {
|
|
cmd := exec.Command("bw", "get", "attachment", attachmentID, "--itemid", itemID,
|
|
"--session", sessionKey, "--output", outputDir)
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func BWGetAllAttachments(outputDir, sessionKey string, pws []PortWardenElement, sleepMilliseconds int) error {
|
|
for _, item := range pws {
|
|
if len(item.Attachments) > 0 {
|
|
for _, innerItem := range item.Attachments {
|
|
err := BWGetAttachment(outputDir+item.Name+"/", item.ID, innerItem.ID, sessionKey)
|
|
time.Sleep(time.Millisecond * time.Duration(sleepMilliseconds))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|