1package config
2
3import (
4 "os"
5 "path/filepath"
6)
7
8// signatureFile returns the full path to the global signature file.
9func signatureFile() (string, error) {
10 dir, err := configDir()
11 if err != nil {
12 return "", err
13 }
14 return filepath.Join(dir, "signature.txt"), nil
15}
16
17// accountSignatureFile returns the path to the per-account signature file.
18func accountSignatureFile(accountID string) (string, error) {
19 dir, err := configDir()
20 if err != nil {
21 return "", err
22 }
23 return filepath.Join(dir, "signatures", accountID+".txt"), nil
24}
25
26// LoadSignature loads the signature from the global signature file.
27func LoadSignature() (string, error) {
28 path, err := signatureFile()
29 if err != nil {
30 return "", err
31 }
32 data, err := SecureReadFile(path)
33 if err != nil {
34 if os.IsNotExist(err) {
35 return "", nil
36 }
37 return "", err
38 }
39 return string(data), nil
40}
41
42// LoadRawAccountSignature loads the per-account signature if one exists,
43// without falling back to the global signature.
44func LoadRawAccountSignature(account *Account) (string, error) {
45 if account == nil || account.ID == "" {
46 return "", nil
47 }
48
49 // Check for per-account signature file first
50 path, err := accountSignatureFile(account.ID)
51 if err != nil {
52 return "", err
53 }
54 data, err := SecureReadFile(path)
55 if err == nil && len(data) > 0 {
56 return string(data), nil
57 }
58
59 // Fall back to inline account signature
60 if account.Signature != "" {
61 return account.Signature, nil
62 }
63
64 return "", nil
65}
66
67// LoadSignatureForAccount loads the per-account signature if one exists,
68// otherwise falls back to the global signature.
69func LoadSignatureForAccount(account *Account) (string, error) {
70 sig, err := LoadRawAccountSignature(account)
71 if err == nil && sig != "" {
72 return sig, nil
73 }
74 // Fall back to global signature
75 return LoadSignature()
76}
77
78// SaveSignature saves the signature to the global signature file.
79func SaveSignature(signature string) error {
80 path, err := signatureFile()
81 if err != nil {
82 return err
83 }
84 if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
85 return err
86 }
87 return SecureWriteFile(path, []byte(signature), 0600)
88}
89
90// SaveSignatureForAccount saves a per-account signature file.
91func SaveSignatureForAccount(accountID, signature string) error {
92 path, err := accountSignatureFile(accountID)
93 if err != nil {
94 return err
95 }
96 if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
97 return err
98 }
99 if signature == "" {
100 // Remove the file to fall back to global
101 os.Remove(path) //nolint:errcheck,gosec
102 return nil
103 }
104 return SecureWriteFile(path, []byte(signature), 0600)
105}
106
107// HasSignature checks if a global signature file exists and is non-empty.
108func HasSignature() bool {
109 sig, err := LoadSignature()
110 if err != nil {
111 return false
112 }
113 return sig != ""
114}
115
116// HasAccountSignature checks if an account has its own signature (file or inline).
117func HasAccountSignature(account *Account) bool {
118 sig, err := LoadRawAccountSignature(account)
119 if err != nil {
120 return false
121 }
122 return sig != ""
123}