mirror of
https://github.com/onionshare/onionshare.git
synced 2025-04-28 03:06:07 -04:00
Support handling events by monitoring an events folder for changes
This commit is contained in:
parent
d78d67adf6
commit
e7c683528d
4
BUILD.md
4
BUILD.md
@ -14,13 +14,13 @@ Install the needed dependencies:
|
|||||||
For Debian-like distros:
|
For Debian-like distros:
|
||||||
|
|
||||||
```
|
```
|
||||||
apt install -y python3-flask python3-stem python3-pyqt5 python3-crypto python3-socks python-nautilus tor obfs4proxy python3-pytest build-essential fakeroot python3-all python3-stdeb dh-python python3-flask-httpauth python3-distutils python3-psutil
|
apt install -y python3-flask python3-stem python3-pyqt5 python3-crypto python3-socks python-nautilus tor obfs4proxy python3-pytest build-essential fakeroot python3-all python3-stdeb dh-python python3-flask-httpauth python3-distutils python3-psutil python3-watchdog
|
||||||
```
|
```
|
||||||
|
|
||||||
For Fedora-like distros:
|
For Fedora-like distros:
|
||||||
|
|
||||||
```
|
```
|
||||||
dnf install -y python3-flask python3-flask-httpauth python3-stem python3-qt5 python3-crypto python3-pysocks nautilus-python tor obfs4 python3-pytest rpm-build python3-psutil
|
dnf install -y python3-flask python3-flask-httpauth python3-stem python3-qt5 python3-crypto python3-pysocks nautilus-python tor obfs4 python3-pytest rpm-build python3-psutil python3-watchdog
|
||||||
```
|
```
|
||||||
|
|
||||||
After that you can try both the CLI and the GUI version of OnionShare:
|
After that you can try both the CLI and the GUI version of OnionShare:
|
||||||
|
@ -21,3 +21,4 @@ requests==2.22.0
|
|||||||
stem==1.7.1
|
stem==1.7.1
|
||||||
urllib3==1.25.3
|
urllib3==1.25.3
|
||||||
Werkzeug==0.15.6
|
Werkzeug==0.15.6
|
||||||
|
watchdog==0.9.0
|
@ -23,9 +23,11 @@ import sys
|
|||||||
import platform
|
import platform
|
||||||
import argparse
|
import argparse
|
||||||
import signal
|
import signal
|
||||||
import psutil
|
|
||||||
from PyQt5 import QtCore, QtWidgets
|
from PyQt5 import QtCore, QtWidgets
|
||||||
|
|
||||||
|
if platform.system() == "Linux":
|
||||||
|
import psutil
|
||||||
|
|
||||||
from onionshare.common import Common
|
from onionshare.common import Common
|
||||||
|
|
||||||
from .gui_common import GuiCommon
|
from .gui_common import GuiCommon
|
||||||
@ -122,28 +124,30 @@ def main():
|
|||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
# Is there another onionshare-gui running?
|
# Is there another onionshare-gui running?
|
||||||
existing_pid = None
|
if common.platform == "Linux":
|
||||||
for proc in psutil.process_iter(attrs=["pid", "name", "cmdline"]):
|
existing_pid = None
|
||||||
if proc.info["pid"] == os.getpid():
|
for proc in psutil.process_iter(attrs=["pid", "name", "cmdline"]):
|
||||||
continue
|
if proc.info["pid"] == os.getpid():
|
||||||
|
continue
|
||||||
|
|
||||||
if proc.info["name"] == "onionshare-gui":
|
if proc.info["name"] == "onionshare-gui":
|
||||||
existing_pid = proc.info["pid"]
|
existing_pid = proc.info["pid"]
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
# Dev mode onionshare?
|
# Dev mode onionshare?
|
||||||
if proc.info["cmdline"] and len(proc.info["cmdline"]) >= 2:
|
if proc.info["cmdline"] and len(proc.info["cmdline"]) >= 2:
|
||||||
if (
|
if (
|
||||||
os.path.basename(proc.info["cmdline"][0]).lower() == "python"
|
os.path.basename(proc.info["cmdline"][0]).lower() == "python"
|
||||||
and os.path.basename(proc.info["cmdline"][1]) == "onionshare-gui"
|
and os.path.basename(proc.info["cmdline"][1])
|
||||||
):
|
== "onionshare-gui"
|
||||||
existing_pid = proc.info["pid"]
|
):
|
||||||
break
|
existing_pid = proc.info["pid"]
|
||||||
|
break
|
||||||
|
|
||||||
if existing_pid:
|
if existing_pid:
|
||||||
print(f"Opening tab in existing OnionShare window (pid {proc.info['pid']})")
|
print(f"Opening tab in existing OnionShare window (pid {proc.info['pid']})")
|
||||||
# TODO: open tab
|
# TODO: open tab
|
||||||
return
|
return
|
||||||
|
|
||||||
# Attach the GUI common parts to the common object
|
# Attach the GUI common parts to the common object
|
||||||
common.gui = GuiCommon(common, qtapp, local_only)
|
common.gui = GuiCommon(common, qtapp, local_only)
|
||||||
|
89
onionshare_gui/event_handler.py
Normal file
89
onionshare_gui/event_handler.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
OnionShare | https://onionshare.org/
|
||||||
|
|
||||||
|
Copyright (C) 2014-2018 Micah Lee <micah@micahflee.com>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from watchdog.observers import Observer
|
||||||
|
from watchdog.events import FileSystemEventHandler, FileModifiedEvent
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
|
||||||
|
|
||||||
|
class EventHandler(FileSystemEventHandler, QtCore.QObject):
|
||||||
|
"""
|
||||||
|
To trigger an event, write a JSON line to the events file. When that file changes,
|
||||||
|
each line will be handled as an event. Valid events are:
|
||||||
|
{"type": "new_tab"}
|
||||||
|
{"type": "new_share_tab", "filenames": ["file1", "file2"]}
|
||||||
|
"""
|
||||||
|
|
||||||
|
new_tab = QtCore.pyqtSignal()
|
||||||
|
new_share_tab = QtCore.pyqtSignal(list)
|
||||||
|
|
||||||
|
def __init__(self, common):
|
||||||
|
super(EventHandler, self).__init__()
|
||||||
|
self.common = common
|
||||||
|
|
||||||
|
def on_modified(self, event):
|
||||||
|
if (
|
||||||
|
type(event) == FileModifiedEvent
|
||||||
|
and event.src_path == self.common.gui.events_filename
|
||||||
|
):
|
||||||
|
# Read all the lines in the events, then delete it
|
||||||
|
with open(self.common.gui.events_filename, "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
os.remove(self.common.gui.events_filename)
|
||||||
|
|
||||||
|
self.common.log(
|
||||||
|
"EventHandler", "on_modified", f"processing {len(lines)} lines"
|
||||||
|
)
|
||||||
|
for line in lines:
|
||||||
|
try:
|
||||||
|
obj = json.loads(line)
|
||||||
|
if "type" not in obj:
|
||||||
|
self.common.log(
|
||||||
|
"EventHandler",
|
||||||
|
"on_modified",
|
||||||
|
f"event does not have a type: {obj}",
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
except json.decoder.JSONDecodeError:
|
||||||
|
self.common.log(
|
||||||
|
"EventHandler", "on_modified", f"ignoring invalid line: {line}"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if obj["type"] == "new_tab":
|
||||||
|
self.common.log("EventHandler", "on_modified", "new_tab event")
|
||||||
|
self.new_tab.emit()
|
||||||
|
|
||||||
|
elif obj["type"] == "new_share_tab":
|
||||||
|
if "filenames" in obj and type(obj["filenames"]) is list:
|
||||||
|
self.new_share_tab.emit(obj["filenames"])
|
||||||
|
else:
|
||||||
|
self.common.log(
|
||||||
|
"EventHandler",
|
||||||
|
"on_modified",
|
||||||
|
f"invalid new_share_tab event: {obj}",
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.common.log(
|
||||||
|
"EventHandler", "on_modified", f"invalid event type: {obj}"
|
||||||
|
)
|
||||||
|
|
@ -17,6 +17,8 @@ GNU General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
from onionshare import strings
|
from onionshare import strings
|
||||||
from onionshare.onion import Onion
|
from onionshare.onion import Onion
|
||||||
|
|
||||||
@ -44,6 +46,12 @@ class GuiCommon:
|
|||||||
# Start the Onion
|
# Start the Onion
|
||||||
self.onion = Onion(common)
|
self.onion = Onion(common)
|
||||||
|
|
||||||
|
# Directory to watch for events
|
||||||
|
self.events_dir = os.path.join(self.common.build_data_dir(), "events")
|
||||||
|
if not os.path.exists(self.events_dir):
|
||||||
|
os.makedirs(self.events_dir, 0o700, True)
|
||||||
|
self.events_filename = os.path.join(self.events_dir, "events")
|
||||||
|
|
||||||
self.css = {
|
self.css = {
|
||||||
# OnionShareGui styles
|
# OnionShareGui styles
|
||||||
"tab_widget_new_tab_button": """
|
"tab_widget_new_tab_button": """
|
||||||
|
@ -119,6 +119,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
|
|
||||||
# Tabs
|
# Tabs
|
||||||
self.tabs = TabWidget(self.common, self.system_tray, self.status_bar)
|
self.tabs = TabWidget(self.common, self.system_tray, self.status_bar)
|
||||||
|
self.tabs.bring_to_front.connect(self.bring_to_front)
|
||||||
|
|
||||||
# If we have saved persistent tabs, try opening those
|
# If we have saved persistent tabs, try opening those
|
||||||
if len(self.common.settings.get("persistent_tabs")) > 0:
|
if len(self.common.settings.get("persistent_tabs")) > 0:
|
||||||
@ -281,6 +282,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.update_thread.update_available.connect(update_available)
|
self.update_thread.update_available.connect(update_available)
|
||||||
self.update_thread.start()
|
self.update_thread.start()
|
||||||
|
|
||||||
|
def bring_to_front(self):
|
||||||
|
self.common.log("MainWindow", "bring_to_front")
|
||||||
|
self.raise_()
|
||||||
|
self.activateWindow()
|
||||||
|
|
||||||
def closeEvent(self, e):
|
def closeEvent(self, e):
|
||||||
self.common.log("MainWindow", "closeEvent")
|
self.common.log("MainWindow", "closeEvent")
|
||||||
|
|
||||||
|
@ -18,11 +18,13 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
from PyQt5 import QtCore, QtWidgets, QtGui
|
from PyQt5 import QtCore, QtWidgets, QtGui
|
||||||
|
from watchdog.observers import Observer
|
||||||
|
|
||||||
from onionshare import strings
|
from onionshare import strings
|
||||||
from onionshare.mode_settings import ModeSettings
|
from onionshare.mode_settings import ModeSettings
|
||||||
|
|
||||||
from .tab import Tab
|
from .tab import Tab
|
||||||
|
from .event_handler import EventHandler
|
||||||
|
|
||||||
|
|
||||||
class TabWidget(QtWidgets.QTabWidget):
|
class TabWidget(QtWidgets.QTabWidget):
|
||||||
@ -30,6 +32,8 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||||||
A custom tab widget, that has a "+" button for adding new tabs
|
A custom tab widget, that has a "+" button for adding new tabs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
bring_to_front = QtCore.pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, common, system_tray, status_bar):
|
def __init__(self, common, system_tray, status_bar):
|
||||||
super(TabWidget, self).__init__()
|
super(TabWidget, self).__init__()
|
||||||
self.common = common
|
self.common = common
|
||||||
@ -67,6 +71,14 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||||||
|
|
||||||
self.move_new_tab_button()
|
self.move_new_tab_button()
|
||||||
|
|
||||||
|
# Watch the events file for changes
|
||||||
|
self.event_handler = EventHandler(common)
|
||||||
|
self.event_handler.new_tab.connect(self.add_tab)
|
||||||
|
self.event_handler.new_share_tab.connect(self.new_share_tab)
|
||||||
|
self.observer = Observer()
|
||||||
|
self.observer.schedule(self.event_handler, self.common.gui.events_dir)
|
||||||
|
self.observer.start()
|
||||||
|
|
||||||
def move_new_tab_button(self):
|
def move_new_tab_button(self):
|
||||||
# Find the width of all tabs
|
# Find the width of all tabs
|
||||||
tabs_width = sum(
|
tabs_width = sum(
|
||||||
@ -95,6 +107,12 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||||||
mode_settings = ModeSettings(self.common, id=mode_settings_id)
|
mode_settings = ModeSettings(self.common, id=mode_settings_id)
|
||||||
self.add_tab(mode_settings)
|
self.add_tab(mode_settings)
|
||||||
|
|
||||||
|
def new_share_tab(self, filenames):
|
||||||
|
mode_settings = ModeSettings(self.common)
|
||||||
|
mode_settings.set("persistent", "mode", "share")
|
||||||
|
mode_settings.set("share", "filenames", filenames)
|
||||||
|
self.add_tab(mode_settings)
|
||||||
|
|
||||||
def add_tab(self, mode_settings=None):
|
def add_tab(self, mode_settings=None):
|
||||||
tab = Tab(self.common, self.current_tab_id, self.system_tray, self.status_bar)
|
tab = Tab(self.common, self.current_tab_id, self.system_tray, self.status_bar)
|
||||||
tab.change_title.connect(self.change_title)
|
tab.change_title.connect(self.change_title)
|
||||||
@ -111,6 +129,9 @@ class TabWidget(QtWidgets.QTabWidget):
|
|||||||
# If it's persistent, set the persistent image in the tab
|
# If it's persistent, set the persistent image in the tab
|
||||||
self.change_persistent(tab.tab_id, tab.settings.get("persistent", "enabled"))
|
self.change_persistent(tab.tab_id, tab.settings.get("persistent", "enabled"))
|
||||||
|
|
||||||
|
# Bring the window to front, in case this is being added by an event
|
||||||
|
self.bring_to_front.emit()
|
||||||
|
|
||||||
def change_title(self, tab_id, title):
|
def change_title(self, tab_id, title):
|
||||||
index = self.indexOf(self.tabs[tab_id])
|
index = self.indexOf(self.tabs[tab_id])
|
||||||
self.setTabText(index, title)
|
self.setTabText(index, title)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user