fjueic / gsconnect-mount-manager

MIT License
23 stars 1 forks source link

Is it ready for Ubuntu 24.04 ? #3

Open Coeur-Noir opened 3 months ago

Coeur-Noir commented 3 months ago

Hello,

I can't find a package named dbus-python though there is a python3-dbus package https://packages.ubuntu.com/search?keywords=dbus-python&searchon=names&suite=noble&section=all [ installed ] and a python-dbus-dev one https://packages.ubuntu.com/search?suite=noble&section=all&arch=any&keywords=python-dbus&searchon=names [ not installed ]

dconf-editor is already installed, as gsconnect too.

No problem for « installing » gsconnect-mount-manager

Phone ( Motorola G84, android 14 ) is not in « hot-spot » neither « shared connection ».

But in the end, if clicking on « mount » in gsconnect popup, nothing happens.

Other features of gsconnect work ( I can send files from Phone to computer… )

…I don't say it's a bug, I am a bug ;-)

fjueic commented 3 months ago

about Python package, seems right just a different name across distros

I did try it in Ubuntu and it worked

can you access your device like this

if no, out of scope

if yes, let me know I will help

Coeur-Noir commented 3 months ago

Thanks for your attention ;-)

Yes I do access to /192.168.1.252/storage/emulated/0 that's how I use to do. IP addresses of phone and computer are static - both at home and work place ( but of course not the same in each place ).

But I had to « remove » gsconnect-mount-manager for being anew able to mount the phone. So now is only the « vanilla » gsconnect installed, without gsconnect-mount-manager.

As I have many users accounts on my phone and also computers, I'd like to « ease » a bit accessing files on phone(s) from user(s) session(s) on computer(s). Each user ( on computers ) having a specific account on phone(s).

Well at least if I can achieve that with one phone ( with 2 users ) I'll be happy.

fjueic commented 3 months ago

after running install script, does /192.168.1.252/storage/emulated/0 method fails to work too?

systemctl status gsconnect-mount-manager.service any errors? is it active?

Coeur-Noir commented 3 months ago

Hi, sorry for late answer.

That might explain alot :

django@ASGARD:~$ systemctl status gsconnect-mount-manager.service
Unit gsconnect-mount-manager.service could not be found.
django@ASGARD:~$ 

Running the script as normal user ended by a « DONE ! » message, after asking for an admin' password.

django@ASGARD:~/Logiciels/gsconnect-mount-manager$ ./install.sh 
Checking if GS-Connect extension if installed
Checking if the patch has been applied
Patch is already applied
To Update, Reinstall gsconnect extension and Try Again
django@ASGARD:~/Logiciels/gsconnect-mount-manager$ 

Now clicking on « mount » in gsconnect quick menu seems to do nothing. I can't find any mention of « 192.168.1.252 » in Nautilus. So yes, /192.168.1.252/storage/emulated/0 method fails.

Coeur-Noir commented 3 months ago

If of any interest, modes end permissions are not « default » ones in my system but they do NOT interfere with other gnome-shell extensions :

django@ASGARD:~/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io$ ls -la *
-rw-rw----  1 django users   879 avril 17 08:34 config.js
-rw-rw----  1 django users 13502 avril 17 08:31 extension.js
-rwxrwx--x  1 django users  2955 avril 17 08:31 gsconnect-preferences
-rw-rw----  1 django users   770 avril 17 06:45 metadata.json
-rw-rw----  1 django users  6699 avril 17 08:31 nautilus-gsconnect.py
-rw-rw----  1 django users 60052 avril 17 08:33 org.gnome.Shell.Extensions.GSConnect.gresource
-rw-rw----  1 django users   902 avril 17 08:31 prefs.js
-rw-rw----  1 django users  2473 avril 17 08:31 stylesheet.css
-rw-rw----  1 django users  8595 avril 17 08:31 wl_clipboard.js

locale:
total 144
drwxrws--- 36 django users 4096 avril 17 08:36 .
drwxrws---  8 django users 4096 mai    9 17:29 ..
drwxrws---  3 django users 4096 avril 17 08:36 ar
drwxrws---  3 django users 4096 avril 17 08:36 be
drwxrws---  3 django users 4096 avril 17 08:36 ca
drwxrws---  3 django users 4096 avril 17 08:36 cs
drwxrws---  3 django users 4096 avril 17 08:36 da
drwxrws---  3 django users 4096 avril 17 08:36 de
drwxrws---  3 django users 4096 avril 17 08:36 el
drwxrws---  3 django users 4096 avril 17 08:36 es
drwxrws---  3 django users 4096 avril 17 08:36 et
drwxrws---  3 django users 4096 avril 17 08:36 fa
drwxrws---  3 django users 4096 avril 17 08:36 fi
drwxrws---  3 django users 4096 avril 17 08:36 fr
drwxrws---  3 django users 4096 avril 17 08:36 fy
drwxrws---  3 django users 4096 avril 17 08:36 gl
drwxrws---  3 django users 4096 avril 17 08:36 he
drwxrws---  3 django users 4096 avril 17 08:36 hu
drwxrws---  3 django users 4096 avril 17 08:36 id
drwxrws---  3 django users 4096 avril 17 08:36 it
drwxrws---  3 django users 4096 avril 17 08:36 ko
drwxrws---  3 django users 4096 avril 17 08:36 lt
drwxrws---  3 django users 4096 avril 17 08:36 nl
drwxrws---  3 django users 4096 avril 17 08:36 nl_BE
drwxrws---  3 django users 4096 avril 17 08:36 pl
drwxrws---  3 django users 4096 avril 17 08:36 pt
drwxrws---  3 django users 4096 avril 17 08:36 pt_BR
drwxrws---  3 django users 4096 avril 17 08:36 ru
drwxrws---  3 django users 4096 avril 17 08:36 sk
drwxrws---  3 django users 4096 avril 17 08:36 sr
drwxrws---  3 django users 4096 avril 17 08:36 sr@latin
drwxrws---  3 django users 4096 avril 17 08:36 sv
drwxrws---  3 django users 4096 avril 17 08:36 tr
drwxrws---  3 django users 4096 avril 17 08:36 uk
drwxrws---  3 django users 4096 avril 17 08:36 zh_CN
drwxrws---  3 django users 4096 avril 17 08:36 zh_TW

preferences:
total 80
drwxrws--- 2 django users  4096 avril 17 08:36 .
drwxrws--- 8 django users  4096 mai    9 17:29 ..
-rw-rw---- 1 django users 34841 avril 17 08:31 device.js
-rw-rw---- 1 django users   340 avril 17 08:31 init.js
-rw-rw---- 1 django users  9183 avril 17 08:31 keybindings.js
-rw-rw---- 1 django users 19086 avril 17 08:31 service.js

schemas:
total 24
drwxrws--- 2 django users 4096 mai    9 17:29 .
drwxrws--- 8 django users 4096 mai    9 17:29 ..
-rw-rw---- 1 django users 5316 mai    9 17:29 gschemas.compiled
-rw-rw---- 1 django users 6113 avril 17 08:31 org.gnome.Shell.Extensions.GSConnect.gschema.xml

service:
total 152
drwxrws--- 7 django users  4096 avril 17 08:36 .
drwxrws--- 8 django users  4096 mai    9 17:29 ..
drwxrws--- 2 django users  4096 avril 17 08:36 backends
drwxrws--- 2 django users  4096 avril 17 08:36 components
-rw-rw---- 1 django users 17914 avril 17 08:31 core.js
-rwxrwx--x 1 django users 19944 avril 17 08:31 daemon.js
-rw-rw---- 1 django users 33295 avril 17 08:31 device.js
-rw-rw---- 1 django users 14115 avril 17 08:31 init.js
-rw-rw---- 1 django users 14066 avril 17 08:31 manager.js
-rwxrwx--x 1 django users  6571 avril 17 08:31 nativeMessagingHost.js
-rw-rw---- 1 django users  7209 avril 17 08:31 plugin.js
drwxrws--- 2 django users  4096 avril 17 08:36 plugins
drwxrws--- 2 django users  4096 avril 17 08:36 ui
drwxrws--- 2 django users  4096 mai   13 13:01 utils

shell:
total 100
drwxrws--- 2 django users  4096 avril 17 08:36 .
drwxrws--- 8 django users  4096 mai    9 17:29 ..
-rw-rw---- 1 django users 10801 avril 17 08:31 clipboard.js
-rw-rw---- 1 django users 11265 avril 17 08:31 device.js
-rw-rw---- 1 django users 18766 avril 17 08:31 gmenu.js
-rw-rw---- 1 django users  1108 avril 17 08:31 input.js
-rw-rw---- 1 django users  3239 avril 17 08:31 keybindings.js
-rw-rw---- 1 django users 15019 avril 17 08:31 notification.js
-rw-rw---- 1 django users  8196 avril 17 08:31 tooltip.js
-rw-rw---- 1 django users  9705 avril 17 08:31 utils.js

utils:
total 28
drwxrws--- 2 django users  4096 avril 17 08:36 .
drwxrws--- 8 django users  4096 mai    9 17:29 ..
-rw-rw---- 1 django users 14421 avril 17 08:31 remote.js
-rw-rw---- 1 django users  1548 avril 17 08:31 setup.js
django@ASGARD:~/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io$ 

and

django@ASGARD:~/.local/share/gnome-shell$ ls -la
total 16
drwx------  2 django django 4096 mai   13 13:19 .
drwx------ 34 django django 4096 mai   13 02:43 ..
-rw-rw-r--  1 django django 2646 mai   13 13:19 application_state
lrwxrwxrwx  1 django django   28 mai    8 16:16 extensions -> '/home/Extensions Gnome-Shell'
-rw-rw-r--  1 django django 2797 mai   13 13:04 notifications
-rw-rw-r--  1 django django    0 avril 26 00:55 update-check-46
django@ASGARD:/home$ ls -la Extensions\ Gnome-Shell/
total 36
drwxrws--T  8 root   users 4096 mai    9 17:29 .
drwxr-xr-x  6 root   root  4096 mai    9 02:37 ..
drwxrws---  6 django users 4096 avril 27 04:15 azclock@azclock.gitlab.com
drwxrws--- 11 django users 4096 mai    8 16:00 blur-my-shell@aunetx
drwxrws---  8 django users 4096 mai    9 17:29 gsconnect@andyholmes.github.io
drwxrws---  5 django users 4096 mai    8 16:00 lockkeys@vaina.lt
drwxrws---  3 django users 4096 mai    8 16:00 transparent-top-bar@ftpix.com
drwxrws---  5 django users 4096 mai    8 16:00 unite@hardpixel.eu
django@ASGARD:/home$ 
fjueic commented 3 months ago

Hi, sorry for late answer.

No problem.

most probably issue with script i will look in this later.

For now how about i help you with this manually?

Coeur-Noir commented 3 months ago

I guess it's about « creating » gsconnect-mount-manager.service unit in systemd ?

Yes help needed if you will ;-)

Coeur-Noir commented 3 months ago

Units containing « mount » here :

django@ASGARD:~$ systemctl list-unit-files | grep mount
proc-sys-fs-binfmt_misc.automount              static          -
-.mount                                        generated       -
boot-efi.mount                                 generated       -
boot.mount                                     generated       -
dev-hugepages.mount                            static          -
dev-mqueue.mount                               static          -
media-DATA\x2dUSERS.mount                      generated       -
mnt-DATA\x2dSYSTEM.mount                       generated       -
proc-sys-fs-binfmt_misc.mount                  disabled        disabled
snap-bare-5.mount                              enabled         enabled
snap-core22-1380.mount                         enabled         enabled
snap-cups-1047.mount                           enabled         enabled
snap-firmware\x2dupdater-127.mount             enabled         enabled
snap-gnome\x2d42\x2d2204-176.mount             enabled         enabled
snap-gtk\x2dcommon\x2dthemes-1535.mount        enabled         enabled
snap-onlyoffice\x2ddesktopeditors-191.mount    enabled         enabled
snap-snapd-21465.mount                         enabled         enabled
snap-snapd\x2ddesktop\x2dintegration-157.mount enabled         enabled
sys-fs-fuse-connections.mount                  static          -
sys-kernel-config.mount                        static          -
sys-kernel-debug.mount                         static          -
sys-kernel-tracing.mount                       static          -
systemd-remount-fs.service                     enabled-runtime enabled
snapd.mounts-pre.target                        static          -
snapd.mounts.target                            static          -
umount.target                                  static          -
django@ASGARD:~$ 
fjueic commented 3 months ago

I guess it's about « creating » gsconnect-mount-manager.service unit in systems ?

yes

fjueic commented 3 months ago

/etc/systemd/user/gsconnect-mount-manager.service does this exists?

Coeur-Noir commented 3 months ago

Yes :

django@ASGARD:~$ ls -l /etc/systemd/user/
total 44
lrwxrwxrwx 1 root root   34 avril 24 12:49 dbus-org.bluez.obex.service -> /usr/lib/systemd/user/obex.service
drwxr-xr-x 2 root root 4096 avril 26 00:55 default.target.wants
drwxr-xr-x 2 root root 4096 avril 24 12:50 gnome-session.target.wants
drwxr-xr-x 2 root root 4096 avril 24 12:50 graphical-session-pre.target.wants
-rw-r--r-- 1 root root  262 mai   13 13:01 gsconnect-mount-manager.service               ### here ###
drwxr-xr-x 2 root root 4096 avril 24 12:50 pipewire.service.wants
-rw-r--r-- 1 root root  407 avril 24 12:51 snap.firmware-updater.firmware-notifier.service
-rw-r--r-- 1 root root  422 avril 24 12:51 snap.firmware-updater.firmware-notifier.timer
-rw-r--r-- 1 root root  429 avril 24 12:51 snap.firmware-updater.firmware-updater-app.service
-rw-r--r-- 1 root root  465 avril 24 12:51 snap.snapd-desktop-integration.snapd-desktop-integration.service
drwxr-xr-x 2 root root 4096 avril 24 12:50 sockets.target.wants
drwxr-xr-x 2 root root 4096 avril 26 00:55 timers.target.wants
django@ASGARD:~$ 

from today.

fjueic commented 3 months ago

place this in path commented and also replace {HOME} with absolute path for your home for that user

# /etc/systemd/user/gsconnect-mount-manager.service

[Unit]
Description=GSConnect Mount Manager
After=network.target

[Service]
ExecStart=python3 {HOME}/.config/gsconnect-mount-manager/run.py
Restart=always
RestartSec=5

[Install]
WantedBy=default.target
fjueic commented 3 months ago

place this in ~/.config/gsconnect-mount-manager/run.py create gsconnect-mount-manager if not already there

fjueic commented 3 months ago

Yes :

django@ASGARD:~$ ls -l /etc/systemd/user/
total 44
lrwxrwxrwx 1 root root   34 avril 24 12:49 dbus-org.bluez.obex.service -> /usr/lib/systemd/user/obex.service
drwxr-xr-x 2 root root 4096 avril 26 00:55 default.target.wants
drwxr-xr-x 2 root root 4096 avril 24 12:50 gnome-session.target.wants
drwxr-xr-x 2 root root 4096 avril 24 12:50 graphical-session-pre.target.wants
-rw-r--r-- 1 root root  262 mai   13 13:01 gsconnect-mount-manager.service               ### here ###
drwxr-xr-x 2 root root 4096 avril 24 12:50 pipewire.service.wants
-rw-r--r-- 1 root root  407 avril 24 12:51 snap.firmware-updater.firmware-notifier.service
-rw-r--r-- 1 root root  422 avril 24 12:51 snap.firmware-updater.firmware-notifier.timer
-rw-r--r-- 1 root root  429 avril 24 12:51 snap.firmware-updater.firmware-updater-app.service
-rw-r--r-- 1 root root  465 avril 24 12:51 snap.snapd-desktop-integration.snapd-desktop-integration.service
drwxr-xr-x 2 root root 4096 avril 24 12:50 sockets.target.wants
drwxr-xr-x 2 root root 4096 avril 26 00:55 timers.target.wants
django@ASGARD:~$ 

from today.

nice

fjueic commented 3 months ago

"$USER_HOME/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io/service/plugins/sftp.js this exists?

Coeur-Noir commented 3 months ago

It was already good :

django@ASGARD:~$ cat /etc/systemd/user/gsconnect-mount-manager.service 
# /etc/systemd/user/gsconnect-mount-manager.service

[Unit]
Description=GSConnect Mount Manager
After=network.target

[Service]
ExecStart=python3 /home/django/.config/gsconnect-mount-manager/run.py
Restart=always
RestartSec=5

[Install]
WantedBy=default.target

django@ASGARD:~$ 

and

django@ASGARD:~$ ls -l $HOME/.config/gsconnect*
/home/django/.config/gsconnect:
total 8
-rw-rw-r-- 1 django django 2017 mai    3 00:38 certificate.pem
-rw------- 1 django django 3272 mai    3 00:38 private.pem

/home/django/.config/gsconnect-mount-manager:
total 4
-rw-rw-r-- 1 django django 3609 mai   13 13:01 run.py
django@ASGARD:~$ 
Coeur-Noir commented 3 months ago

sftp.js exists :

django@ASGARD:~$ ls -l .local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io/service/plugins/
total 204
-rw-rw---- 1 django users 12427 avril 17 08:31 battery.js
-rw-rw---- 1 django users  5171 avril 17 08:31 clipboard.js
-rw-rw---- 1 django users  5076 avril 17 08:31 connectivity_report.js
-rw-rw---- 1 django users 14003 avril 17 08:31 contacts.js
-rw-rw---- 1 django users  6475 avril 17 08:31 findmyphone.js
-rw-rw---- 1 django users  1080 avril 17 08:31 index.js
-rw-rw---- 1 django users 10242 avril 17 08:31 mousepad.js
-rw-rw---- 1 django users 26558 avril 17 08:31 mpris.js
-rw-rw---- 1 django users 22737 avril 17 08:31 notification.js
-rw-rw---- 1 django users  1844 avril 17 08:31 ping.js
-rw-rw---- 1 django users  1825 avril 17 08:31 presenter.js
-rw-rw---- 1 django users  7488 avril 17 08:31 runcommand.js
-rw-rw---- 1 django users 15544 mai   13 13:01 sftp.js               ### here ###
-rw-rw---- 1 django users 16148 avril 17 08:31 share.js
-rw-rw---- 1 django users 15557 avril 17 08:31 sms.js
-rw-rw---- 1 django users  5804 avril 17 08:31 systemvolume.js
-rw-rw---- 1 django users  7218 avril 17 08:31 telephony.js
django@ASGARD:~$ 
fjueic commented 3 months ago

So yes, /192.168.1.252/storage/emulated/0 method fails

shit! missed this, it should not be happening. is this the classic works on my machine(i tried it in many different env PC and os)? or something change in gsconnect

i moved away from gnome and dont use it(installing rn)

i will try it myself and back to you

try systemctl --user status gsconnect-mount-manager.service instead. (user added)

Coeur-Noir commented 3 months ago
django@ASGARD:~$ systemctl --user status gsconnect-mount-manager.service | cat
● gsconnect-mount-manager.service - GSConnect Mount Manager
     Loaded: loaded (/etc/xdg/systemd/user/gsconnect-mount-manager.service; enabled; preset: enabled)
     Active: active (running) since Mon 2024-05-13 13:02:39 CEST; 45min ago
   Main PID: 2341 (python3)
      Tasks: 1 (limit: 18953)
     Memory: 8.9M (peak: 9.1M)
        CPU: 72ms
     CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/gsconnect-mount-manager.service
             └─2341 python3 /home/django/.config/gsconnect-mount-manager/run.py

mai 13 13:02:39 ASGARD systemd[2311]: Started gsconnect-mount-manager.service - GSConnect Mount Manager.
django@ASGARD:~$ 
Coeur-Noir commented 3 months ago

And thanks alot already ! Take your time, I may not be back before tomorrow ;-)

fjueic commented 3 months ago

Same thing as your

Something broke I will let you know after fixing it

fjueic commented 3 months ago

@Coeur-Noir hillo

fixed just reinstall gsconnect and follow the installation step

let me know you face issue

if it works close this issue with a confirmation comment

Coeur-Noir commented 3 months ago

Better but not yet…

django@ASGARD:~$ systemctl --user status gsconnect-mount-manager.service | cat
● gsconnect-mount-manager.service - GSConnect Mount Manager
     Loaded: loaded (/etc/xdg/systemd/user/gsconnect-mount-manager.service; enabled; preset: enabled)
     Active: active (running) since Tue 2024-05-14 02:44:37 CEST; 5min ago
   Main PID: 2397 (python3)
      Tasks: 1 (limit: 18953)
     Memory: 8.9M (peak: 9.1M)
        CPU: 79ms
     CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/gsconnect-mount-manager.service
             └─2397 python3 /home/django/.config/gsconnect-mount-manager/run.py

mai 14 02:44:37 ASGARD systemd[2378]: Started gsconnect-mount-manager.service - GSConnect Mount Manager.
django@ASGARD:~$ 

and

gsconnect_mount

I wish I did not need to manually add the missing part of path /storage/emulated/0 or storage/emulated/10

fjueic commented 3 months ago

I wish I did not need to manually add the missing part of path /storage/emulated/0 or storage/emulated/10

That's the point of this repo

Dumb question, did you log out and log in?

fjueic commented 3 months ago

ignore the service file, it's good its working and was not changed in the recent commit

it worked on my environment and definitely should work on yours too

can you give me ~/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io/service/plugins/sftp.js

and what is in the ~/.config/gsconnect-mount-manager/ directory? there should be three files

/home/minoru/.config/gsconnect-mount-manager/mount.py
/home/minoru/.config/gsconnect-mount-manager/run.py
/home/minoru/.config/gsconnect-mount-manager/temp.json
Coeur-Noir commented 3 months ago

Yes, logged in and out, even rebooted…

django@ASGARD:~$ cat ~/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io/service/plugins/sftp.js

// SPDX-FileCopyrightText: GSConnect Developers https://github.com/GSConnect
//
// SPDX-License-Identifier: GPL-2.0-or-later

import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';

import Config from '../../config.js';
import Plugin from '../plugin.js';

export const Metadata = {
    label: _('SFTP'),
    id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.SFTP',
    description: _('Browse the paired device filesystem'),
    incomingCapabilities: ['kdeconnect.sftp'],
    outgoingCapabilities: ['kdeconnect.sftp.request'],
    actions: {
        mount: {
            label: _('Mount'),
            icon_name: 'folder-remote-symbolic',

            parameter_type: null,
            incoming: ['kdeconnect.sftp'],
            outgoing: ['kdeconnect.sftp.request'],
        },
        unmount: {
            label: _('Unmount'),
            icon_name: 'media-eject-symbolic',

            parameter_type: null,
            incoming: ['kdeconnect.sftp'],
            outgoing: ['kdeconnect.sftp.request'],
        },
    },
};

const MAX_MOUNT_DIRS = 12;

/**
 * SFTP Plugin
 * https://github.com/KDE/kdeconnect-kde/tree/master/plugins/sftp
 * https://github.com/KDE/kdeconnect-android/tree/master/src/org/kde/kdeconnect/Plugins/SftpPlugin
 */
const SFTPPlugin = GObject.registerClass({
    GTypeName: 'GSConnectSFTPPlugin',
}, class SFTPPlugin extends Plugin {

    _init(device) {
        super._init(device, 'sftp');

        this._gmount = null;
        this._mounting = false;

        // A reusable launcher for ssh processes
        this._launcher = new Gio.SubprocessLauncher({
            flags: (Gio.SubprocessFlags.STDOUT_PIPE |
                    Gio.SubprocessFlags.STDERR_MERGE),
        });

        // Watch the volume monitor
        this._volumeMonitor = Gio.VolumeMonitor.get();

        this._mountAddedId = this._volumeMonitor.connect(
            'mount-added',
            this._onMountAdded.bind(this)
        );

        this._mountRemovedId = this._volumeMonitor.connect(
            'mount-removed',
            this._onMountRemoved.bind(this)
        );
    }

    get gmount() {
        if (this._gmount === null && this.device.connected) {
            const host = this.device.channel.host;

            const regex = new RegExp(
                `sftp://(${host}):(1739|17[4-5][0-9]|176[0-4])`
            );

            for (const mount of this._volumeMonitor.get_mounts()) {
                const uri = mount.get_root().get_uri();

                if (regex.test(uri)) {
                    this._gmount = mount;
                    this._addSubmenu(mount);
                    this._addSymlink(mount);

                    break;
                }
            }
        }

        return this._gmount;
    }

    connected() {
        super.connected();

        // Only enable for Lan connections
        if (this.device.channel.constructor.name === 'LanChannel') { // FIXME: Circular import workaround
            if (this.settings.get_boolean('automount'))
                this.mount();
        } else {
            this.device.lookup_action('mount').enabled = false;
            this.device.lookup_action('unmount').enabled = false;
        }
    }

    handlePacket(packet) {
        switch (packet.type) {
            case 'kdeconnect.sftp':
                if (packet.body.hasOwnProperty('errorMessage'))
                    this._handleError(packet);
              else{ // if script is editing the file, don't forget to place {} around else
                        this._handleMount(packet);
                        let temp = JSON.stringify(packet);
                        let open = Gio.File.new_for_path(`${GLib.get_home_dir()}/.config/gsconnect-mount-manager/temp.json`);
                        let out = open.replace(null, false, Gio.FileCreateFlags.NONE, null, null);
                        out.write(temp, null);
                        out.close(null);
                        GLib.spawn_command_line_sync(`python ${GLib.get_home_dir()}/.config/gsconnect-mount-manager/mount.py add`)
                    }

                break;
        }
    }

    _onMountAdded(monitor, mount) {
        if (this._gmount !== null || !this.device.connected)
            return;

        const host = this.device.channel.host;
        const regex = new RegExp(`sftp://(${host}):(1739|17[4-5][0-9]|176[0-4])`);
        const uri = mount.get_root().get_uri();

        if (!regex.test(uri))
            return;

        this._gmount = mount;
        this._addSubmenu(mount);
        this._addSymlink(mount);
    }

    _onMountRemoved(monitor, mount) {
        // this block of code is not part of gsconnect connect extension
        try{
            GLib.spawn_command_line_sync(`python ${GLib.get_home_dir()}/.config/gsconnect-mount-manager/mount.py remove ${this._device._id}/`)

        }catch(e){
            pass
        }
        // end of block
        if (this.gmount !== mount)
            return;

        this._gmount = null;
        this._removeSubmenu();
    }

    async _listDirectories(mount) {
        const file = mount.get_root();

        const iter = await file.enumerate_children_async(
            Gio.FILE_ATTRIBUTE_STANDARD_NAME,
            Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
            GLib.PRIORITY_DEFAULT,
            this.cancellable);

        const infos = await iter.next_files_async(MAX_MOUNT_DIRS,
            GLib.PRIORITY_DEFAULT, this.cancellable);
        iter.close_async(GLib.PRIORITY_DEFAULT, null, null);

        const directories = {};

        for (const info of infos) {
            const name = info.get_name();
            directories[name] = `${file.get_uri()}${name}/`;
        }

        return directories;
    }

    _onAskQuestion(op, message, choices) {
        op.reply(Gio.MountOperationResult.HANDLED);
    }

    _onAskPassword(op, message, user, domain, flags) {
        op.reply(Gio.MountOperationResult.HANDLED);
    }

    /**
     * Handle an error reported by the remote device.
     *
     * @param {Core.Packet} packet - a `kdeconnect.sftp`
     */
    _handleError(packet) {
        this.device.showNotification({
            id: 'sftp-error',
            title: _('%s reported an error').format(this.device.name),
            body: packet.body.errorMessage,
            icon: new Gio.ThemedIcon({name: 'dialog-error-symbolic'}),
            priority: Gio.NotificationPriority.HIGH,
        });
    }

    /**
     * Mount the remote device using the provided information.
     *
     * @param {Core.Packet} packet - a `kdeconnect.sftp`
     */
    async _handleMount(packet) {
        try {
            // Already mounted or mounting
            if (this.gmount !== null || this._mounting)
                return;

            this._mounting = true;

            // Ensure the private key is in the keyring
            await this._addPrivateKey();

            // Create a new mount operation
            const op = new Gio.MountOperation({
                username: packet.body.user || null,
                password: packet.body.password || null,
                password_save: Gio.PasswordSave.NEVER,
            });

            op.connect('ask-question', this._onAskQuestion);
            op.connect('ask-password', this._onAskPassword);

            // This is the actual call to mount the device
            const host = this.device.channel.host;
            const uri = `sftp://${host}:${packet.body.port}/`;
            const file = Gio.File.new_for_uri(uri);

            await file.mount_enclosing_volume(GLib.PRIORITY_DEFAULT, op,
                this.cancellable);
        } catch (e) {
            // Special case when the GMount didn't unmount properly but is still
            // on the same port and can be reused.
            if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.ALREADY_MOUNTED))
                return;

            // There's a good chance this is a host key verification error;
            // regardless we'll remove the key for security.
            this._removeHostKey(this.device.channel.host);
            logError(e, this.device.name);
        } finally {
            this._mounting = false;
        }
    }

    /**
     * Add GSConnect's private key identity to the authentication agent so our
     * identity can be verified by Android during private key authentication.
     *
     * @return {Promise} A promise for the operation
     */
    async _addPrivateKey() {
        const ssh_add = this._launcher.spawnv([
            Config.SSHADD_PATH,
            GLib.build_filenamev([Config.CONFIGDIR, 'private.pem']),
        ]);

        const [stdout] = await ssh_add.communicate_utf8_async(null,
            this.cancellable);

        if (ssh_add.get_exit_status() !== 0)
            debug(stdout.trim(), this.device.name);
    }

    /**
     * Remove all host keys from ~/.ssh/known_hosts for @host in the port range
     * used by KDE Connect (1739-1764).
     *
     * @param {string} host - A hostname or IP address
     */
    async _removeHostKey(host) {
        for (let port = 1739; port <= 1764; port++) {
            try {
                const ssh_keygen = this._launcher.spawnv([
                    Config.SSHKEYGEN_PATH,
                    '-R',
                    `[${host}]:${port}`,
                ]);

                const [stdout] = await ssh_keygen.communicate_utf8_async(null,
                    this.cancellable);

                const status = ssh_keygen.get_exit_status();

                if (status !== 0) {
                    throw new Gio.IOErrorEnum({
                        code: Gio.io_error_from_errno(status),
                        message: `${GLib.strerror(status)}\n${stdout}`.trim(),
                    });
                }
            } catch (e) {
                logError(e, this.device.name);
            }
        }
    }

    /*
     * Mount menu helpers
     */
    _getUnmountSection() {
        if (this._unmountSection === undefined) {
            this._unmountSection = new Gio.Menu();

            const unmountItem = new Gio.MenuItem();
            unmountItem.set_label(Metadata.actions.unmount.label);
            unmountItem.set_icon(new Gio.ThemedIcon({
                name: Metadata.actions.unmount.icon_name,
            }));
            unmountItem.set_detailed_action('device.unmount');
            this._unmountSection.append_item(unmountItem);
        }

        return this._unmountSection;
    }

    _getFilesMenuItem() {
        if (this._filesMenuItem === undefined) {
            // Files menu icon
            const emblem = new Gio.Emblem({
                icon: new Gio.ThemedIcon({name: 'emblem-default'}),
            });

            const mountedIcon = new Gio.EmblemedIcon({
                gicon: new Gio.ThemedIcon({name: 'folder-remote-symbolic'}),
            });
            mountedIcon.add_emblem(emblem);

            // Files menu item
            this._filesMenuItem = new Gio.MenuItem();
            this._filesMenuItem.set_detailed_action('device.mount');
            this._filesMenuItem.set_icon(mountedIcon);
            this._filesMenuItem.set_label(_('Files'));
        }

        return this._filesMenuItem;
    }

    async _addSubmenu(mount) {
        try {
            const directories = await this._listDirectories(mount);

            // Submenu sections
            const dirSection = new Gio.Menu();
            const unmountSection = this._getUnmountSection();

            for (const [name, uri] of Object.entries(directories))
                dirSection.append(name, `device.openPath::${uri}`);

            // Files submenu
            const filesSubmenu = new Gio.Menu();
            filesSubmenu.append_section(null, dirSection);
            filesSubmenu.append_section(null, unmountSection);

            // Files menu item
            const filesMenuItem = this._getFilesMenuItem();
            filesMenuItem.set_submenu(filesSubmenu);

            // Replace the existing menu item
            const index = this.device.removeMenuAction('device.mount');
            this.device.addMenuItem(filesMenuItem, index);
        } catch (e) {
            if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
                debug(e, this.device.name);

            // Reset to allow retrying
            this._gmount = null;
        }
    }

    _removeSubmenu() {
        try {
            const index = this.device.removeMenuAction('device.mount');
            const action = this.device.lookup_action('mount');

            if (action !== null) {
                this.device.addMenuAction(
                    action,
                    index,
                    Metadata.actions.mount.label,
                    Metadata.actions.mount.icon_name
                );
            }
        } catch (e) {
            logError(e, this.device.name);
        }
    }

    /**
     * Create a symbolic link referring to the device by name
     *
     * @param {Gio.Mount} mount - A GMount to link to
     */
    async _addSymlink(mount) {
        try {
            const by_name_dir = Gio.File.new_for_path(
                `${Config.RUNTIMEDIR}/by-name/`
            );

            try {
                by_name_dir.make_directory_with_parents(null);
            } catch (e) {
                if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
                    throw e;
            }

            // Replace path separator with a Unicode lookalike:
            let safe_device_name = this.device.name.replace('/', '∕');

            if (safe_device_name === '.')
                safe_device_name = '·';
            else if (safe_device_name === '..')
                safe_device_name = '··';

            const link_target = mount.get_root().get_path();
            const link = Gio.File.new_for_path(
                `${by_name_dir.get_path()}/${safe_device_name}`);

            // Check for and remove any existing stale link
            try {
                const link_stat = await link.query_info_async(
                    'standard::symlink-target',
                    Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
                    GLib.PRIORITY_DEFAULT,
                    this.cancellable);

                if (link_stat.get_symlink_target() === link_target)
                    return;

                await link.delete_async(GLib.PRIORITY_DEFAULT,
                    this.cancellable);
            } catch (e) {
                if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
                    throw e;
            }

            link.make_symbolic_link(link_target, this.cancellable);
        } catch (e) {
            debug(e, this.device.name);
        }
    }

    /**
     * Send a request to mount the remote device
     */
    mount() {
        if (this.gmount !== null)
            return;

        this.device.sendPacket({
            type: 'kdeconnect.sftp.request',
            body: {
                startBrowsing: true,
            },
        });
    }

    /**
     * Remove the menu items, unmount the filesystem, replace the mount item
     */
    async unmount() {
        try {
            if (this.gmount === null)
                return;

            this._removeSubmenu();
            this._mounting = false;

            await this.gmount.unmount_with_operation(
                Gio.MountUnmountFlags.FORCE,
                new Gio.MountOperation(),
                this.cancellable);
        } catch (e) {
            debug(e, this.device.name);
        }
    }

    destroy() {
        if (this._volumeMonitor) {
            this._volumeMonitor.disconnect(this._mountAddedId);
            this._volumeMonitor.disconnect(this._mountRemovedId);
            this._volumeMonitor = null;
        }

        super.destroy();
    }
});

export default SFTPPlugin;

and the three files seat there :

django@ASGARD:~$ ls -l ~/.config/gsconnect-mount-manager/

total 12
-rw-rw-r-- 1 django django 4024 mai   14 02:43 mount.py
-rw-rw-r-- 1 django django 3609 mai   14 02:43 run.py
-rw-rw-r-- 1 django django  267 mai   14 02:45 temp.json
django@ASGARD:~$ 
Coeur-Noir commented 3 months ago

Trying to understand how it works…

/home/django/.config/gsconnect-mount-manager/temp.json

contains

{"id":1231231231231,"type":"kdeconnect.sftp","body":{"ip":"192.168.1.252","port":1739,"user":"kdeconnect","password":"@01xyZ@01xyZ@01xyZ@01xyZ@01x","path":"/storage/emulated/0","multiPaths":["/storage/emulated/0"],"pathNames":["Espace de stockage interne partagé"]}}

…so the final path is correctly seen I think.

But here

/home/django/.config/gtk-3.0/bookmarks

contains

file:///home/django/Documents
file:///home/django/Musique
file:///home/django/Images
file:///home/django/Vid%C3%A9os
file:///home/django/T%C3%A9l%C3%A9chargements
file:///home/django/Bureau Bureau
file:///media/django/Ubuntu-2204/home/django Django $HOME [22.04]
file:///home /home [24.04]

→ should I expect there some bookmark named « Espace de stockage interne partagé » ? ? ?

as suggested in mount.py ( line 61 )

def addBookmark(id):
    file = os.path.expanduser('~') + "/.config/gtk-3.0/bookmarks"
    with open(file, 'r') as f:
        result = f.read()
    name = getDeviceName(id)
    line = f"file://{os.path.expanduser('~')}/.gsconnectMounts/{name}___{id.split('/')[0]}"
    line = line.replace(" ", "%20")
    line = line + " " + name
    if line in result:
        return
    result += line + "\n"
    with open(file, 'w') as f:
        f.write(result)

Here that part line = f"file://{os.path.expanduser('~')}/.gsconnectMounts/{name}___{id.split('/')[0]}" seems weird to me as there is no ~/.gsconnectMounts/ in my $HOME. And will that be able to handle a path like ://ip:port/storage/emulated/10 ?

[ I have no skill in coding so it's probably my wrong understanding ;-) ]

Capture d’écran du 2024-05-16 20-57-34

fjueic commented 3 months ago

contains

{"id":1231231231231,"type":"kdeconnect.sftp","body":{"ip":"192.168.1.252","port":1739,"user":"kdeconnect","password":"@01xyZ@01xyZ@01xyZ@01xyZ@01x","path":"/storage/emulated/0","multiPaths":["/storage/emulated/0"],"pathNames":["Espace de stockage interne partagé"]}}

…so the final path is correctly seen I think.

cool making progress

yes there should be bookmark and handled on its own

Here that part line = f"file://{os.path.expanduser('~')}/.gsconnectMounts/{name}___{id.split('/')[0]}" seems weird to me as there is no ~/.gsconnectMounts/ in my $HOME.

yes i know the code i wrote is not the best and can be made more readable that directory is created in this function

try this

python ~/.config/gsconnect-mount-manager/mount.py add

it will most probably give an error as you told bookmark was not added

Coeur-Noir commented 3 months ago

No error after

python3 ~/.config/gsconnect-mount-manager/mount.py add

then ~/.gsconnectMounts/ exists but if phone is not already mounted Capture d’écran du 2024-05-16 23-08-15 Once mounted ( by quick menu in gsconnect extension ) authentication no longer needed.

So it's almost ok. There's maybe an issue about when .gsconnectMounts folder is created or deleted ?

Coeur-Noir commented 3 months ago

I don't understand the need for symlink - which opens a new window of Nautilus. Capture d’écran du 2024-05-17 00-18-01


edit

This seems to work in bookmarks :

file:///home/django/Images
file:///home/django/Vid%C3%A9os
file:///home/django/T%C3%A9l%C3%A9chargements
file:///home/django/Bureau Bureau
file:///media/django/Ubuntu-2204/home/django Django $HOME [22.04]
file:///home /home [24.04]
sftp://192.168.1.252:1739/storage/emulated/0 moto g84 5G

removing the need for the symlink ? ? ?

fjueic commented 3 months ago

that is your case I have an sd card too which mean that I will need to add two bookmarks plus i was trying to kind of copy kde connect

why is symbolic link self targeting?

removing the need for the symlink ? ? ?

if you want to do it for yourself, that is possible. but i will stick to them. this is just the workaround i decided because of nautilus limitations.

i am able to pin point the error to this line of code in sftp.js that you provided

but it's working on my system and I just can't seem to understand why it's not working

Jones14021 commented 3 weeks ago

Can confirm that this is still an issue. I am running Ubuntu 24.04 with Gnome on Wayland.

I installed and setup the vanilla GSConnect GNOME extension. Works fine with "mount" and then manually appending "sdcard".

After running your ./install.sh (finishes successfully with "===DONE==="), there is absolutely no change in behavior. Nothing happens upon clicking on "mount" (still see the mount with the IP address and still need to edit it manually).

I tried to debug you script (run.py). And it seems that handle_signal() is not called when clicking on "mount" in GSConnect. It is however called whenever I open Nautilus.

Hope this helps in finding a solution. Cheers!

fjueic commented 3 weeks ago

@Jones14021 i am busy nowadays, I will download Ubuntu iso later

Jones14021 commented 3 weeks ago

@fjueic nevermind actually. I just did a few reboots and the problem solved itself. It is now fully working! Thanks for putting out this gem!