Squashed commit of the following:
commit c8126262de851ef2b9bfed5ca3b07b9dc927f203
Author: riomoo <alister@kamikishi.net>
Date: Tue Jan 6 01:58:02 2026 -0500
feat: planned additions
- Watch folder
- CBT support with AES-256-CFB encryption (script provided to make it
easier)
This commit is contained in:
parent
c06c716d23
commit
9752725855
12 changed files with 1427 additions and 189 deletions
231
scripts-go/cbt.go
Normal file
231
scripts-go/cbt.go
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
inputDir := flag.String("input", "", "Input directory containing comic files")
|
||||
outputFile := flag.String("output", "", "Output .cbt file")
|
||||
password := flag.String("password", "", "Password for encryption (leave empty for no encryption)")
|
||||
flag.Parse()
|
||||
|
||||
if *inputDir == "" || *outputFile == "" {
|
||||
fmt.Println("Usage: cbt-creator -input <directory> -output <file.cbt> [-password <password>]")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Ensure output has .cbt extension
|
||||
if !strings.HasSuffix(strings.ToLower(*outputFile), ".cbt") {
|
||||
*outputFile += ".cbt"
|
||||
}
|
||||
|
||||
var err error
|
||||
if *password != "" {
|
||||
err = createEncryptedCBT(*inputDir, *outputFile, *password)
|
||||
fmt.Printf("Created encrypted CBT: %s\n", *outputFile)
|
||||
} else {
|
||||
err = createUnencryptedCBT(*inputDir, *outputFile)
|
||||
fmt.Printf("Created unencrypted CBT: %s\n", *outputFile)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func createUnencryptedCBT(inputDir, outputPath string) error {
|
||||
outFile, err := os.Create(outputPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
tw := tar.NewWriter(outFile)
|
||||
defer tw.Close()
|
||||
|
||||
// Collect all files
|
||||
var files []string
|
||||
err = filepath.Walk(inputDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
files = append(files, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add files to tar
|
||||
for _, file := range files {
|
||||
relPath, err := filepath.Rel(inputDir, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read file
|
||||
data, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write tar header
|
||||
hdr := &tar.Header{
|
||||
Name: relPath,
|
||||
Mode: 0600,
|
||||
Size: int64(len(data)),
|
||||
}
|
||||
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write file data
|
||||
if _, err := tw.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Added: %s\n", relPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createEncryptedCBT(inputDir, outputPath, password string) error {
|
||||
// Create temporary unencrypted tar
|
||||
tmpTar := outputPath + ".tmp"
|
||||
tmpFile, err := os.Create(tmpTar)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tw := tar.NewWriter(tmpFile)
|
||||
|
||||
// Collect all files
|
||||
var files []string
|
||||
err = filepath.Walk(inputDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
files = append(files, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
tmpFile.Close()
|
||||
os.Remove(tmpTar)
|
||||
return err
|
||||
}
|
||||
|
||||
// Add files to tar
|
||||
for _, file := range files {
|
||||
relPath, err := filepath.Rel(inputDir, file)
|
||||
if err != nil {
|
||||
tw.Close()
|
||||
tmpFile.Close()
|
||||
os.Remove(tmpTar)
|
||||
return err
|
||||
}
|
||||
|
||||
// Read file
|
||||
data, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
tw.Close()
|
||||
tmpFile.Close()
|
||||
os.Remove(tmpTar)
|
||||
return err
|
||||
}
|
||||
|
||||
// Write tar header
|
||||
hdr := &tar.Header{
|
||||
Name: relPath,
|
||||
Mode: 0600,
|
||||
Size: int64(len(data)),
|
||||
}
|
||||
|
||||
if err := tw.WriteHeader(hdr); err != nil {
|
||||
tw.Close()
|
||||
tmpFile.Close()
|
||||
os.Remove(tmpTar)
|
||||
return err
|
||||
}
|
||||
|
||||
// Write file data
|
||||
if _, err := tw.Write(data); err != nil {
|
||||
tw.Close()
|
||||
tmpFile.Close()
|
||||
os.Remove(tmpTar)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Added: %s\n", relPath)
|
||||
}
|
||||
|
||||
tw.Close()
|
||||
tmpFile.Close()
|
||||
|
||||
// Read the tar file
|
||||
tarData, err := os.ReadFile(tmpTar)
|
||||
if err != nil {
|
||||
os.Remove(tmpTar)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Encrypting...")
|
||||
|
||||
// Encrypt
|
||||
key := deriveKey(password)
|
||||
encrypted, err := encryptAES(tarData, key)
|
||||
if err != nil {
|
||||
os.Remove(tmpTar)
|
||||
return err
|
||||
}
|
||||
|
||||
// Write encrypted file
|
||||
err = os.WriteFile(outputPath, encrypted, 0644)
|
||||
os.Remove(tmpTar)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func deriveKey(password string) []byte {
|
||||
hash := sha256.Sum256([]byte(password))
|
||||
return hash[:]
|
||||
}
|
||||
|
||||
func encryptAES(plaintext []byte, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate IV
|
||||
iv := make([]byte, aes.BlockSize)
|
||||
if _, err := rand.Read(iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
|
||||
ciphertext := make([]byte, len(plaintext))
|
||||
stream.XORKeyStream(ciphertext, plaintext)
|
||||
|
||||
// Prepend IV to ciphertext
|
||||
return append(iv, ciphertext...), nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue