diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 21:38:38 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 21:38:38 +0000 |
commit | 2e2851dc13d73352530dd4495c7e05603b2e520d (patch) | |
tree | 622b9cd8e5d32091c9aa9e4937b533975a40356c /deluge/ui/gtk3/ipcinterface.py | |
parent | Initial commit. (diff) | |
download | deluge-2e2851dc13d73352530dd4495c7e05603b2e520d.tar.xz deluge-2e2851dc13d73352530dd4495c7e05603b2e520d.zip |
Adding upstream version 2.1.2~dev0+20240219.upstream/2.1.2_dev0+20240219upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'deluge/ui/gtk3/ipcinterface.py')
-rw-r--r-- | deluge/ui/gtk3/ipcinterface.py | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/deluge/ui/gtk3/ipcinterface.py b/deluge/ui/gtk3/ipcinterface.py new file mode 100644 index 0000000..0ef28d8 --- /dev/null +++ b/deluge/ui/gtk3/ipcinterface.py @@ -0,0 +1,221 @@ +# +# Copyright (C) 2008-2009 Andrew Resch <andrewresch@gmail.com> +# +# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with +# the additional special exception to link portions of this program with the OpenSSL library. +# See LICENSE for more details. +# + +import logging +import os +import sys +from base64 import b64encode +from glob import glob +from tempfile import mkstemp +from urllib.parse import urlparse +from urllib.request import url2pathname + +import rencode +import twisted.internet.error +from twisted.internet import reactor +from twisted.internet.protocol import ClientFactory, Factory, Protocol, connectionDone + +import deluge.component as component +from deluge.common import decode_bytes, is_magnet, is_url, windows_check +from deluge.configmanager import ConfigManager, get_config_dir +from deluge.ui.client import client + +log = logging.getLogger(__name__) + + +class IPCProtocolServer(Protocol): + def __init__(self): + pass + + def dataReceived(self, data): # NOQA: N802 + config = ConfigManager('gtk3ui.conf') + data = rencode.loads(data, decode_utf8=True) + if not data or config['focus_main_window_on_add']: + component.get('MainWindow').present() + process_args(data) + + +class IPCProtocolClient(Protocol): + def __init__(self): + pass + + def connectionMade(self): # NOQA: N802 + self.transport.write(rencode.dumps(self.factory.args)) + self.transport.loseConnection() + + def connectionLost(self, reason=connectionDone): # NOQA: N802 + reactor.stop() + self.factory.stop = True + + +class IPCClientFactory(ClientFactory): + protocol = IPCProtocolClient + + def __init__(self): + self.stop = False + + def clientConnectionFailed(self, connector, reason): # NOQA: N802 + log.warning('Connection to running instance failed.') + reactor.stop() + + +class IPCInterface(component.Component): + def __init__(self, args): + component.Component.__init__(self, 'IPCInterface') + self.listener = None + ipc_dir = get_config_dir('ipc') + if not os.path.exists(ipc_dir): + os.makedirs(ipc_dir) + socket = os.path.join(ipc_dir, 'deluge-gtk') + if windows_check(): + # If we're on windows we need to check the global mutex to see if deluge is + # already running. + import win32api + import win32event + import winerror + + self.mutex = win32event.CreateMutex(None, False, 'deluge') + if win32api.GetLastError() != winerror.ERROR_ALREADY_EXISTS: + # Create listen socket + self.factory = Factory() + self.factory.protocol = IPCProtocolServer + import random + + port = random.randrange(20000, 65535) + self.listener = reactor.listenTCP(port, self.factory) + # Store the port number in the socket file + with open(socket, 'w') as _file: + _file.write(str(port)) + # We need to process any args when starting this process + process_args(args) + else: + # Send to existing deluge process + with open(socket) as _file: + port = int(_file.readline()) + self.factory = ClientFactory() + self.factory.args = args + self.factory.protocol = IPCProtocolClient + reactor.connectTCP('127.0.0.1', port, self.factory) + reactor.run() + sys.exit(0) + else: + # Find and remove any restart tempfiles + restart_tempfile = glob(os.path.join(ipc_dir, 'restart.*')) + for f in restart_tempfile: + os.remove(f) + lockfile = socket + '.lock' + log.debug('Checking if lockfile exists: %s', lockfile) + if os.path.lexists(lockfile): + + def delete_lockfile(): + log.debug('Delete stale lockfile.') + try: + os.remove(lockfile) + os.remove(socket) + except OSError as ex: + log.error('Failed to delete lockfile: %s', ex) + + try: + os.kill(int(os.readlink(lockfile)), 0) + except OSError: + delete_lockfile() + else: + if restart_tempfile: + log.warning( + 'Found running PID but it is not a Deluge process, removing lockfile...' + ) + delete_lockfile() + try: + self.factory = Factory() + self.factory.protocol = IPCProtocolServer + self.listener = reactor.listenUNIX(socket, self.factory, wantPID=True) + except twisted.internet.error.CannotListenError as ex: + log.info( + 'Deluge is already running! Sending arguments to running instance...' + ) + self.factory = IPCClientFactory() + self.factory.args = args + reactor.connectUNIX(socket, self.factory, checkPID=True) + reactor.run() + if self.factory.stop: + log.info('Success sending arguments to running Deluge.') + from gi.repository.Gdk import notify_startup_complete + + notify_startup_complete() + sys.exit(0) + else: + if restart_tempfile: + log.error('Deluge restart failed: %s', ex) + sys.exit(1) + else: + log.warning('Restarting Deluge... (%s)', ex) + # Create a tempfile to keep track of restart + mkstemp(prefix='restart.', dir=ipc_dir) + os.execv(sys.argv[0], sys.argv) + else: + process_args(args) + + def shutdown(self): + if windows_check(): + import win32api + + win32api.CloseHandle(self.mutex) + if self.listener: + return self.listener.stopListening() + + +def process_args(args): + """Process arguments sent to already running Deluge""" + # Make sure args is a list + args = list(args) + log.debug('Processing args from other process: %s', args) + if not client.connected(): + # We're not connected so add these to the queue + log.debug('Not connected to host.. Adding to queue.') + component.get('QueuedTorrents').add_to_queue(args) + return + config = ConfigManager('gtk3ui.conf') + + for arg in args: + if not arg.strip(): + continue + log.debug('arg: %s', arg) + + if is_url(arg): + log.debug('Attempting to add url (%s) from external source...', arg) + if config['interactive_add']: + component.get('AddTorrentDialog').add_from_url(arg) + component.get('AddTorrentDialog').show(config['focus_add_dialog']) + else: + client.core.add_torrent_url(arg, None) + + elif is_magnet(arg): + log.debug('Attempting to add magnet (%s) from external source...', arg) + if config['interactive_add']: + component.get('AddTorrentDialog').add_from_magnets([arg]) + component.get('AddTorrentDialog').show(config['focus_add_dialog']) + else: + client.core.add_torrent_magnet(arg, {}) + + else: + log.debug('Attempting to add file (%s) from external source...', arg) + if urlparse(arg).scheme == 'file': + arg = url2pathname(urlparse(arg).path) + path = os.path.abspath(decode_bytes(arg)) + + if not os.path.exists(path): + log.error('No such file: %s', path) + continue + + if config['interactive_add']: + component.get('AddTorrentDialog').add_from_files([path]) + component.get('AddTorrentDialog').show(config['focus_add_dialog']) + else: + with open(path, 'rb') as _file: + filedump = b64encode(_file.read()) + client.core.add_torrent_file(os.path.split(path)[-1], filedump, None) |