1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-28 07:22:50 -05:00

clipboard: fix file transfer & export functionality

- grants read to all installed flatpak apps
This commit is contained in:
bbedward
2026-01-26 17:58:06 -05:00
parent 705d5b04dd
commit 8499033221
30 changed files with 3940 additions and 838 deletions

View File

@@ -45,6 +45,10 @@ func HandleRequest(conn net.Conn, req models.Request, m *Manager) {
handleGetPinnedEntries(conn, req, m)
case "clipboard.getPinnedCount":
handleGetPinnedCount(conn, req, m)
case "clipboard.startFileTransfer":
handleStartFileTransfer(conn, req, m)
case "clipboard.exportFile":
handleExportFile(conn, req, m)
default:
models.RespondError(conn, req.ID, "unknown method: "+req.Method)
}
@@ -281,3 +285,35 @@ func handleGetPinnedCount(conn net.Conn, req models.Request, m *Manager) {
count := m.GetPinnedCount()
models.Respond(conn, req.ID, map[string]int{"count": count})
}
func handleStartFileTransfer(conn net.Conn, req models.Request, m *Manager) {
filePath, err := params.String(req.Params, "filePath")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
key, err := m.StartFileTransfer(filePath)
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, map[string]string{"key": key})
}
func handleExportFile(conn net.Conn, req models.Request, m *Manager) {
filePath, err := params.String(req.Params, "filePath")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
exportedPath, err := m.ExportFileForFlatpak(filePath)
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, map[string]string{"path": exportedPath})
}

View File

@@ -10,6 +10,7 @@ import (
_ "image/png"
"io"
"os"
"os/exec"
"path/filepath"
"slices"
"strings"
@@ -19,6 +20,7 @@ import (
"hash/fnv"
"github.com/fsnotify/fsnotify"
"github.com/godbus/dbus/v5"
_ "golang.org/x/image/bmp"
_ "golang.org/x/image/tiff"
_ "golang.org/x/image/webp"
@@ -1529,3 +1531,133 @@ func (m *Manager) GetPinnedCount() int {
return count
}
func (m *Manager) StartFileTransfer(filePath string) (string, error) {
if _, err := os.Stat(filePath); err != nil {
return "", fmt.Errorf("file not found: %w", err)
}
if m.dbusConn == nil {
conn, err := dbus.ConnectSessionBus()
if err != nil {
return "", fmt.Errorf("connect session bus: %w", err)
}
if !conn.SupportsUnixFDs() {
conn.Close()
return "", fmt.Errorf("D-Bus connection does not support Unix FD passing")
}
m.dbusConn = conn
}
portal := m.dbusConn.Object("org.freedesktop.portal.Documents", "/org/freedesktop/portal/documents")
var key string
options := map[string]dbus.Variant{
"writable": dbus.MakeVariant(false),
"autostop": dbus.MakeVariant(false),
}
if err := portal.Call("org.freedesktop.portal.FileTransfer.StartTransfer", 0, options).Store(&key); err != nil {
return "", fmt.Errorf("start transfer: %w", err)
}
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("open file: %w", err)
}
if _, err := file.Seek(0, 0); err != nil {
file.Close()
return "", fmt.Errorf("seek file: %w", err)
}
fd := int(file.Fd())
addOptions := map[string]dbus.Variant{}
if err := portal.Call("org.freedesktop.portal.FileTransfer.AddFiles", 0, key, []dbus.UnixFD{dbus.UnixFD(fd)}, addOptions).Err; err != nil {
file.Close()
return "", fmt.Errorf("add files: %w", err)
}
m.transferFiles = append(m.transferFiles, file)
return key, nil
}
func (m *Manager) ExportFileForFlatpak(filePath string) (string, error) {
if _, err := os.Stat(filePath); err != nil {
return "", fmt.Errorf("file not found: %w", err)
}
if m.dbusConn == nil {
conn, err := dbus.ConnectSessionBus()
if err != nil {
return "", fmt.Errorf("connect session bus: %w", err)
}
if !conn.SupportsUnixFDs() {
conn.Close()
return "", fmt.Errorf("D-Bus connection does not support Unix FD passing")
}
m.dbusConn = conn
}
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("open file: %w", err)
}
fd := int(file.Fd())
portal := m.dbusConn.Object("org.freedesktop.portal.Documents", "/org/freedesktop/portal/documents")
var docIds []string
var extra map[string]dbus.Variant
flags := uint32(0)
err = portal.Call(
"org.freedesktop.portal.Documents.AddFull",
0,
[]dbus.UnixFD{dbus.UnixFD(fd)},
flags,
"",
[]string{},
).Store(&docIds, &extra)
file.Close()
if err != nil {
return "", fmt.Errorf("AddFull: %w", err)
}
if len(docIds) == 0 {
return "", fmt.Errorf("no doc IDs returned")
}
docId := docIds[0]
for _, app := range getInstalledFlatpaks() {
_ = portal.Call(
"org.freedesktop.portal.Documents.GrantPermissions",
0,
docId,
app,
[]string{"read"},
).Err
}
uid := os.Getuid()
basename := filepath.Base(filePath)
exportedPath := fmt.Sprintf("/run/user/%d/doc/%s/%s", uid, docId, basename)
return exportedPath, nil
}
func getInstalledFlatpaks() []string {
out, err := exec.Command("flatpak", "list", "--app", "--columns=application").Output()
if err != nil {
return nil
}
var apps []string
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
if app := strings.TrimSpace(line); app != "" {
apps = append(apps, app)
}
}
return apps
}

View File

@@ -7,6 +7,7 @@ import (
"sync"
"time"
"github.com/godbus/dbus/v5"
bolt "go.etcd.io/bbolt"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlcontext"
@@ -157,6 +158,9 @@ type Manager struct {
dirty chan struct{}
notifierWg sync.WaitGroup
lastState *State
dbusConn *dbus.Conn
transferFiles []*os.File // Keep files open for portal transfers
}
func (m *Manager) GetState() State {