mirror of
https://github.com/moonD4rk/HackBrowserData.git
synced 2026-05-19 18:58:03 +02:00
d75738b90f
* feat(safari): multi-profile support
69 lines
1.9 KiB
Go
69 lines
1.9 KiB
Go
package safari
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/moond4rk/plist"
|
|
|
|
"github.com/moond4rk/hackbrowserdata/types"
|
|
)
|
|
|
|
type safariDownloads struct {
|
|
DownloadHistory []safariDownloadEntry `plist:"DownloadHistory"`
|
|
}
|
|
|
|
type safariDownloadEntry struct {
|
|
URL string `plist:"DownloadEntryURL"`
|
|
Path string `plist:"DownloadEntryPath"`
|
|
TotalBytes int64 `plist:"DownloadEntryProgressTotalToLoad"`
|
|
ProfileUUID string `plist:"DownloadEntryProfileUUIDStringKey"`
|
|
RemoveWhenDone bool `plist:"DownloadEntryRemoveWhenDoneKey"`
|
|
DownloadIdentifier string `plist:"DownloadEntryIdentifier"`
|
|
}
|
|
|
|
// extractDownloads reads Downloads.plist (shared across Safari profiles) and returns only the entries
|
|
// owned by ownerUUID — either "DefaultProfile" or a named profile's uppercase UUID. Entries written by
|
|
// older Safari (no ProfileUUID field) are attributed to the default profile.
|
|
func extractDownloads(path, ownerUUID string) ([]types.DownloadEntry, error) {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open downloads: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
var dl safariDownloads
|
|
if err := plist.NewDecoder(f).Decode(&dl); err != nil {
|
|
return nil, fmt.Errorf("decode downloads: %w", err)
|
|
}
|
|
|
|
var downloads []types.DownloadEntry
|
|
for _, d := range dl.DownloadHistory {
|
|
if !ownsDownload(d.ProfileUUID, ownerUUID) {
|
|
continue
|
|
}
|
|
downloads = append(downloads, types.DownloadEntry{
|
|
URL: d.URL,
|
|
TargetPath: d.Path,
|
|
TotalBytes: d.TotalBytes,
|
|
})
|
|
}
|
|
return downloads, nil
|
|
}
|
|
|
|
func countDownloads(path, ownerUUID string) (int, error) {
|
|
downloads, err := extractDownloads(path, ownerUUID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return len(downloads), nil
|
|
}
|
|
|
|
// ownsDownload treats empty ProfileUUID as DefaultProfile for backward compat with pre-profile Safari.
|
|
func ownsDownload(entryUUID, ownerUUID string) bool {
|
|
if entryUUID == "" {
|
|
entryUUID = defaultProfileSentinel
|
|
}
|
|
return entryUUID == ownerUUID
|
|
}
|