diff --git a/CHANGELOG.md b/CHANGELOG.md index bc5c5474..f66ea4ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,26 +8,34 @@ the changes in the CHANGELOG formatting. ## [Unreleased] -## [0.4.41] - 2020-12-02 +## [0.4.41] - 2020-12-04 ### Added - Tor support - - Automatic use of Tor when bootstrapping succeeds - - Tor proxying for all ipfs-search and cyber requests + - Tor proxying on all platforms (enabled manually from the + status bar for now, very soon there'll be finer control + of the relays via [stem](https://stem.torproject.org/)) + - Proxying of ipfs-search and cyber requests via Tor - Add a new *anonymous* web profile - Automatically fetch favicons when hashmarking an http(s) website - Handle SSL certificate errors +- New python dependencies + - [aiohttp-socks](https://pypi.org/project/aiohttp-socks/) >=0.5.5 + - validators >= 0.18.1 + ### Changed -- Bookmarking of any type of URLs -- Browser UI +- Browser tab UI - Use a block-style cursor for the address bar - Typing an .eth domain name automatically loads it through *ens://* - Run IPFS searches or searches with popular engines (duckduckgo, ..) from the address bar - - Nicer history lookup interface + - Change the history lookup interface -- The @Earth workspace is now the default workspace in the stack +- The @Earth workspace is now the first/default workspace in the WS stack +- Workspace APIs + - Changed wsRegisterTab(): accept a *position* argument to insert tabs + at the end of the tabs list or after the current tab ### Fixed - Bookmarking of clearnet URLs diff --git a/README.rst b/README.rst index d432e73e..19f055f7 100644 --- a/README.rst +++ b/README.rst @@ -122,6 +122,7 @@ for more information). of files in the MFS) - File sharing - BitTorrent to IPFS bridge +- Tor support - Search content with the ipfs-search_ search engine as well as with cyber_ - Built-in blog with Atom feeds - Webcam to IPFS capture (image and videos) diff --git a/galacteek/application.py b/galacteek/application.py index 4c1e8a12..981e4ec2 100644 --- a/galacteek/application.py +++ b/galacteek/application.py @@ -511,7 +511,8 @@ def initMisc(self): self.tempDirWeb = self.tempDirCreate( self.tempDir.path(), 'webdownloads') - self.tor = TorLauncher(self._torConfigLocation) + self.tor = TorLauncher(self._torConfigLocation, + self._torDataDirLocation) self._goIpfsBinPath = self.suitableGoIpfsBinary() def tempDirCreate(self, basedir, name=None): @@ -1008,6 +1009,7 @@ def setupPaths(self): self._mHashDbLocation = os.path.join(self.dataLocation, 'mhashmetadb') self._sqliteDbLocation = os.path.join(self.dataLocation, 'db.sqlite') self._torConfigLocation = os.path.join(self.dataLocation, 'torrc') + self._torDataDirLocation = os.path.join(self.dataLocation, 'tor-data') self._pLockLocation = os.path.join(self.dataLocation, 'profile.lock') self._mainDbLocation = os.path.join( self.dataLocation, 'db_main.sqlite3') @@ -1041,6 +1043,7 @@ def setupPaths(self): for dir in [self._mHashDbLocation, self._logsLocation, self.ipfsBinLocation, + self._torDataDirLocation, self.marksDataLocation, self.cryptoDataLocation, self.eccDataLocation, diff --git a/galacteek/core/glogger.py b/galacteek/core/glogger.py index 306318ba..ccba239f 100644 --- a/galacteek/core/glogger.py +++ b/galacteek/core/glogger.py @@ -50,6 +50,9 @@ class LogRecordStyler: 'basecolor': 'darkred', 'red': -0.1 }, + 'galacteek.core.tor': { + 'basecolor': 'darkred' + }, # crypto modules 'galacteek.crypto': { diff --git a/galacteek/core/tor.py b/galacteek/core/tor.py index 99dd6525..967a7eb3 100644 --- a/galacteek/core/tor.py +++ b/galacteek/core/tor.py @@ -1,16 +1,56 @@ import asyncio import re -import signal import psutil import platform import subprocess -import tempfile +import secrets +from os import urandom +from binascii import b2a_hex +from hashlib import sha1 from galacteek import log from galacteek import ensure from galacteek import AsyncSignal from galacteek.core.asynclib import asyncWriteFile -from galacteek.core import unusedTcpPort + + +def getTorHashedPassword(secret): + ''' + https://gist.github.com/jamesacampbell/2f170fc17a328a638322078f42e04cbc + ''' + # static 'count' value later referenced as "c" + indicator = chr(96) + # generate salt and append indicator value so that it + salt = "%s%s" % (urandom(8), indicator) + c = ord(salt[8]) + # generate an even number that can be divided in subsequent sections. + # (Thanks Roman) + EXPBIAS = 6 + count = (16 + (c & 15)) << ((c >> 4) + EXPBIAS) + d = sha1() + # take the salt and append the password + tmp = salt[:8] + secret + # hash the salty password + slen = len(tmp) + while count: + if count > slen: + d.update(tmp.encode()) + count -= slen + else: + d.update(tmp[:count].encode()) + count = 0 + hashed = d.digest() + + # Put it all together into the proprietary Tor format. + salt = b2a_hex(salt[:8].encode()).decode().upper() + ind = b2a_hex(indicator.encode()).decode() + h = b2a_hex(hashed).decode().upper() + + return '16:{salt}{i}{h}'.format( + salt=salt, + i=ind, + h=h + ) class TorProtocol(asyncio.SubprocessProtocol): @@ -65,16 +105,18 @@ def process_exited(self): AutomapHostsOnResolve 1 AutomapHostsSuffixes .exit,.onion DataDirectory {dataDir} +HashedControlPassword {hashedControlPass} ''' class TorConfigBuilder: - def __init__(self): + def __init__(self, dataDir): self._socksPort = None self._controlPort = None self._dnsPort = None self._hostname = '127.0.0.1' - self._dataDir = tempfile.mkdtemp(prefix='gtor') + self._dataDir = dataDir + self.__controlPass = secrets.token_hex(8) @property def socksPort(self): @@ -103,12 +145,14 @@ def __str__(self): socksPort=self.socksPort, controlPort=self.controlPort, dnsPort=self.dnsPort, - dataDir=self._dataDir + dataDir=self._dataDir, + hashedControlPass=getTorHashedPassword(self.__controlPass) ) class TorLauncher: - def __init__(self, configPath, torPath='tor', debug=True, loop=None): + def __init__(self, configPath, dataDirPath, + torPath='tor', debug=True, loop=None): self.loop = loop if loop else asyncio.get_event_loop() self.exitFuture = asyncio.Future(loop=self.loop) @@ -118,9 +162,10 @@ def __init__(self, configPath, torPath='tor', debug=True, loop=None): self._process = None self.torPath = torPath self.configPath = configPath + self.dataDirPath = dataDirPath self.debug = debug self.transport, self.proto = None, None - self.torCfg = TorConfigBuilder() + self.torCfg = TorConfigBuilder(self.dataDirPath) self.torProto = TorProtocol(self.loop, self.exitFuture, self.startedFuture, debug=self.debug) @@ -158,9 +203,9 @@ async def start(self): startupInfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW startupInfo.wShowWindow = subprocess.SW_HIDE - # for socksPort in range(9052, 9080): - for x in range(0, 12): - socksPort = unusedTcpPort() + # for x in range(0, 12): + for socksPort in range(9050, 9080): + # socksPort = unusedTcpPort() self.torCfg.socksPort = socksPort await asyncWriteFile(self.configPath, str(self.torCfg), 'w+t') @@ -184,8 +229,20 @@ async def start(self): self._procPid = self.transport.get_pid() self.process = psutil.Process(self._procPid) - except Exception: - log.debug(f'Starting TOR failed on port {socksPort}') + + # Wait a bit, if there are port binding issues + # tor will exit immediately + await asyncio.sleep(3) + + status = self.process.status() + assert status in [ + psutil.STATUS_RUNNING, + psutil.STATUS_SLEEPING + ] + except Exception as err: + log.debug(f'Starting TOR failed on port {socksPort} : ' + f'error {err}') + self.transport.close() continue else: log.debug(f'Starting TOR OK on port {socksPort}') @@ -197,12 +254,7 @@ def stop(self): if not self.process: raise Exception('Process not found') - if platform.system() == 'Windows': - self.process.kill() - else: - self.process.send_signal(signal.SIGINT) - self.process.send_signal(signal.SIGHUP) - + self.transport.terminate() self._procPid = None return True except Exception as err: diff --git a/galacteek/core/webprofiles.py b/galacteek/core/webprofiles.py index 2726d34f..dca6c3f9 100644 --- a/galacteek/core/webprofiles.py +++ b/galacteek/core/webprofiles.py @@ -70,6 +70,7 @@ def setSettings(self): True) self.webSettings.setAttribute(QWebEngineSettings.LocalStorageEnabled, True) + self.setHttpCacheType(QWebEngineProfile.NoCache) def installHandler(self, scheme, handler): sch = scheme if isinstance(scheme, bytes) else scheme.encode() diff --git a/galacteek/docs/manual/__init__.py b/galacteek/docs/manual/__init__.py index 9cd32d66..f91247df 100644 --- a/galacteek/docs/manual/__init__.py +++ b/galacteek/docs/manual/__init__.py @@ -1 +1 @@ -__manual_en_version__ = '20201022' +__manual_en_version__ = '20201204' diff --git a/galacteek/docs/manual/en/browsing.rst b/galacteek/docs/manual/en/browsing.rst index 05a6bc51..d8b408a2 100644 --- a/galacteek/docs/manual/en/browsing.rst +++ b/galacteek/docs/manual/en/browsing.rst @@ -19,10 +19,9 @@ will hide the results. You can also use specific syntax to search with certain search engines: -- Use the **d** command to search with the - [DuckDuckGo](https://duckduckgo.com/) web search engine. +- Use the **d** prefix to search with the DuckDuckGo_ web search engine. Example: **d distributed web** -- Use the **i** or **ip** command to run a search on the IPFS +- Use the **i** or **ip** prefix to run a search on the IPFS network. Example: **i distributed web** CID status icon @@ -170,7 +169,7 @@ and the result is cached. Web profiles ------------ -There are 3 distinct web profiles that can be used when accessing a +There are 4 distinct web profiles that can be used when accessing a webpage. The current profile can be changed from a browser tab by opening the IPFS menu and selecting a profile from the *Web profile* submenu. @@ -208,3 +207,4 @@ a *Web3* instance (from the *web3.js* JS library) available as *window.web3* in the main Javascript world .. _ENS: https://ens.domains/ +.. _DuckDuckGo: https://duckduckgo.com diff --git a/galacteek/ui/browser.py b/galacteek/ui/browser.py index e8f5e5cc..f19e42b2 100644 --- a/galacteek/ui/browser.py +++ b/galacteek/ui/browser.py @@ -27,6 +27,10 @@ from PyQt5.QtCore import QTimer from PyQt5.QtCore import QPoint from PyQt5.QtCore import QSize +from PyQt5.QtCore import QEvent + +from PyQt5.QtGui import QPalette +from PyQt5.QtGui import QColor from PyQt5 import QtWebEngineWidgets @@ -68,6 +72,7 @@ from galacteek.core.schemes import DAGProxySchemeHandler from galacteek.core.schemes import MultiDAGProxySchemeHandler from galacteek.core.schemes import IPFSObjectProxyScheme +from galacteek.core.schemes import isUrlSupported from galacteek.core.webprofiles import WP_NAME_IPFS from galacteek.core.webprofiles import WP_NAME_MINIMAL @@ -246,6 +251,11 @@ def iInvalidUrl(text): 'Invalid URL: {0}').format(text) +def iUnsupportedUrl(): + return QCoreApplication.translate('BrowserTabForm', + 'Unsupported URL type') + + def iInvalidObjectPath(text): return QCoreApplication.translate( 'BrowserTabForm', @@ -341,13 +351,13 @@ def __init__(self, webProfile, parent): super(DefaultBrowserWebPage, self).__init__(webProfile, parent) self.app = QCoreApplication.instance() self.fullScreenRequested.connect(self.onFullScreenRequest) - self.setBackgroundColor(desertStrike1) + self.setBackgroundColor(desertStrikeColor) def certificateError(self, error): - return not questionBox( - error.url.toString(), + return questionBox( + error.url().toString(), f'Certificate error: {error.errorDescription()}.' - 'Continue ?') + 'Continue ?') def onFullScreenRequest(self, req): # Accept fullscreen requests unconditionally @@ -438,6 +448,7 @@ def __init__(self, browserTab, webProfile, enablePlugins=False, self.browserTab = browserTab self.linkInfoTimer = QTimer() self.linkInfoTimer.timeout.connect(self.onLinkInfoTimeout) + # self.setMouseTracking(True) self.webPage = None self.altSearchPage = None @@ -466,7 +477,10 @@ def createWindow(self, wintype): # Disabled for now if wintype == QWebEnginePage.WebBrowserTab: - tab = self.app.mainWindow.addBrowserTab(current=True) + tab = self.app.mainWindow.addBrowserTab( + current=True, + position='nextcurrent' + ) return tab.webEngineView def onViewSource(self): @@ -756,11 +770,11 @@ def hashmarkPath(self, path): iUnknown())) def openInTab(self, path): - tab = self.browserTab.gWindow.addBrowserTab() + tab = self.browserTab.gWindow.addBrowserTab(position='nextcurrent') tab.browseFsPath(path) def openUrlInTab(self, url): - tab = self.browserTab.gWindow.addBrowserTab() + tab = self.browserTab.gWindow.addBrowserTab(position='nextcurrent') tab.enterUrl(url) def downloadLink(self, menudata): @@ -875,8 +889,16 @@ def __init__(self, history, historyView, parent): @property def editTimeoutMs(self): return 400 - timeout = self.app.settingsMgr.urlHistoryEditTimeout - return timeout if isinstance(timeout, int) else 200 + + @property + def matchesVisible(self): + return self.historyMatches.isVisible() + + def setupPalette(self): + palette = self.palette() + palette.setColor(QPalette.HighlightedText, ipfsColor1) + palette.setColor(QPalette.Highlight, QColor('transparent')) + self.setPalette(palette) def keyPressEvent(self, ev): if ev.key() == Qt.Key_Up: @@ -928,10 +950,13 @@ def focusOutEvent(self, event): super(URLInputWidget, self).focusOutEvent(event) + def onUrlTextChanged(self, text): + pass + def onUrlUserEdit(self, text): self.urlInput = text - if not self.urlEditing: + if not self.hasFocus(): return self.startEditTimer() @@ -1729,14 +1754,12 @@ def onToggledPinAll(self, checked): def onFocusUrl(self): self.focusUrlZone(True, reason=Qt.ShortcutFocusReason) - def focusUrlZone(self, select=False, reason=Qt.OtherFocusReason): + def focusUrlZone(self, select=False, reason=Qt.ActiveWindowFocusReason): self.urlZone.setFocus(reason) if select: self.urlZone.setSelection(0, len(self.urlZone.text())) - self.urlZone.urlEditing = True - def onReloadPage(self): self.reloadPage() @@ -1781,8 +1804,6 @@ def onSavePageContained(self): page.save(path, QWebEngineDownloadItem.CompleteHtmlSaveFormat) def onHashmarkPage(self): - # if self.currentUrl and isEnsUrl(self.currentUrl): - if self.currentIpfsObject and self.currentIpfsObject.valid: scheme = self.currentUrl.scheme() ensure(addHashmarkAsync( @@ -1790,13 +1811,13 @@ def onHashmarkPage(self): title=self.currentPageTitle, schemePreferred=scheme )) - elif self.currentUrl: + elif self.currentUrl and isUrlSupported(self.currentUrl): ensure(addHashmarkAsync( self.currentUrl.toString(), title=self.currentPageTitle )) else: - messageBox(iNotAnIpfsResource()) + messageBox(iUnsupportedUrl()) def onPinResult(self, f): try: @@ -2004,14 +2025,11 @@ async def onUrlChanged(self, url): background-color: #C3D7DF; }''') - # self.urlZone.clear() - # self.urlZone.insert(url.toString()) self.urlZoneInsert(url.toString()) self._currentIpfsObject = IPFSPath(url.toString()) self._currentUrl = url self.ipfsObjectVisited.emit(self.currentIpfsObject) - # self.ui.hashmarkThisPage.setEnabled(True) self.ui.pinToolButton.setEnabled(True) self.followIpnsAction.setEnabled( diff --git a/galacteek/ui/colors.py b/galacteek/ui/colors.py index 9821d653..c96ec29a 100644 --- a/galacteek/ui/colors.py +++ b/galacteek/ui/colors.py @@ -10,4 +10,4 @@ ipfsColor1 = QColor('#4b9fa2') ipfsColor2 = QColor('#4a9ea1') brownColor1 = QColor('#CFCC94') -desertStrike1 = QColor('#e0dbc8') +desertStrikeColor = QColor('#e0dbc8') diff --git a/galacteek/ui/dialogs.py b/galacteek/ui/dialogs.py index da7f6a09..30b34d01 100644 --- a/galacteek/ui/dialogs.py +++ b/galacteek/ui/dialogs.py @@ -180,7 +180,6 @@ async def initDialog(self, ipfsop): @ipfsOp async def fetchFavIcon(self, ipfsop, qurl): qurl.setPath('/favicon.ico') - print('fetching', qurl.toString()) try: async with self.app.webClientSession() as session: @@ -189,13 +188,12 @@ async def fetchFavIcon(self, ipfsop, qurl): async with session.get(qurl.toString()) as resp: while True: b = await resp.content.read(1024) - print('read', len(b)) if not b: break _data.extend(b) - if len(_data) > 1024 * 1024: - raise Exception('bb') + if len(_data) > 512 * 1024: + raise Exception('Too large, get lost') icon = getIconFromImageData(_data) if not icon: @@ -206,13 +204,6 @@ async def fetchFavIcon(self, ipfsop, qurl): self.iconSelector.injectCustomIcon( icon, entry['Hash'], qurl.toString()) - - if 0: - entry = await ipfsop.addBytes(_data) - icoPath = IPFSPath(entry['Hash']) - print(icoPath) - if not icoPath.valid: - raise Exception('Invalid icon') except Exception as err: log.debug(f'Could not load favicon: {err}') @@ -1416,6 +1407,7 @@ def onTimerOut(self): class DefaultProgressDialog(QWidget): def __init__(self, parent=None): super().__init__(parent) + self.setObjectName('statusProgressDialog') self.vl = QVBoxLayout(self) self.cube = AnimatedLabel(RotatingCubeClipSimple()) self.pBar = QProgressBar() @@ -1431,6 +1423,8 @@ def __init__(self, parent=None): QSpacerItem(10, 50, QSizePolicy.Expanding, QSizePolicy.Expanding)) self.cube.clip.setScaledSize(QSize(128, 128)) + self.setContentsMargins(0, 0, 0, 0) + self.vl.setContentsMargins(0, 0, 0, 0) def spin(self): self.cube.startClip() @@ -1451,6 +1445,10 @@ def paintEvent(self, event): w, h = 420, 420 # it's a coincidence painter = QPainter(self) + b = QBrush(desertStrikeColor, Qt.SolidPattern) + painter.setBrush(b) + painter.fillRect(self.rect(), b) + painter.setBrush(QBrush(brownColor1, Qt.SolidPattern)) painter.setPen(QPen(ipfsColor1, 2, Qt.SolidLine)) painter.drawEllipse(center.x() - w / 2, center.y() - h / 2, w, h) diff --git a/galacteek/ui/dwebspace/__init__.py b/galacteek/ui/dwebspace/__init__.py index 52a95bdd..0a66997e 100644 --- a/galacteek/ui/dwebspace/__init__.py +++ b/galacteek/ui/dwebspace/__init__.py @@ -361,11 +361,17 @@ def nextWorkspace(self): return self.stack.nextWorkspace(self) def wsRegisterTab(self, tab, name, icon=None, current=False, - tooltip=None): + tooltip=None, + position='append'): + if position == 'append': + atIdx = self.tabWidget.count() + elif position == 'nextcurrent': + atIdx = self.tabWidget.currentIndex() + 1 + if icon: - idx = self.tabWidget.addTab(tab, icon, name) + idx = self.tabWidget.insertTab(atIdx, tab, icon, name) else: - idx = self.tabWidget.addTab(tab, name) + idx = self.tabWidget.insertTab(atIdx, tab, name) tab.workspaceAttach(self) @@ -478,6 +484,9 @@ def planet(self): def wsToolTip(self): return f'Planet workspace: {self.planet}' + def onHelpBrowsing(self): + self.app.manuals.browseManualPage('browsing.html') + async def loadDapps(self): if self.app.cmdArgs.enablequest and 0: await self.loadQuestService() @@ -486,6 +495,12 @@ async def loadQuestService(self): from galacteek.dweb.quest import loadQuestService self.qView, self.qPage = await loadQuestService() + def setupWorkspace(self): + super().setupWorkspace() + self.wsAddCustomAction( + 'help-browsing', getIcon('help.png'), + iHelp(), self.onHelpBrowsing) + class WorkspaceCore(TabbedWorkspace): def __init__(self, stack): diff --git a/galacteek/ui/ipfssearch.py b/galacteek/ui/ipfssearch.py index 0baaaa63..50a5c8c2 100644 --- a/galacteek/ui/ipfssearch.py +++ b/galacteek/ui/ipfssearch.py @@ -182,7 +182,7 @@ def __init__( self.handler = IPFSSearchHandler(self) self.channel.registerObject('ipfssearch', self.handler) self.setWebChannel(self.channel) - self.setBackgroundColor(desertStrike1) + self.setBackgroundColor(desertStrikeColor) self.app = QApplication.instance() diff --git a/galacteek/ui/mainui.py b/galacteek/ui/mainui.py index 0cb3b81e..023bdfba 100644 --- a/galacteek/ui/mainui.py +++ b/galacteek/ui/mainui.py @@ -630,9 +630,9 @@ def wsAddGlobalCustomAction(self, *args, **kw): for widx, ws in self.workspaces(): ws.wsAddCustomAction(*args, **kw) - def wsAddGlobalAction(self, action: QAction): + def wsAddGlobalAction(self, action: QAction, default=False): for widx, ws in self.workspaces(): - ws.wsAddAction(action) + ws.wsAddAction(action, default=default) def wsActivityNotify(self, workspace): widx, curWorkspace = self.currentWorkspace() @@ -1181,7 +1181,7 @@ def setupWorkspaces(self): self.stack.addWorkspace(self.wspaceMultimedia) self.stack.addWorkspace(self.wspaceMisc) - self.stack.wsAddGlobalAction(self.browseAction) + self.stack.wsAddGlobalAction(self.browseAction, default=True) self.stack.activateWorkspaces(False) def onSeedAppImage(self): @@ -1428,7 +1428,8 @@ def statusMessage(self, msg): None, QRect(0, 0, 0, 0), 2400) def registerTab(self, tab, name, icon=None, current=True, - tooltip=None, workspace=None): + tooltip=None, workspace=None, + position='append'): if workspace is None: sidx, wspace = self.stack.currentWorkspace() elif isinstance(workspace, str): @@ -1442,7 +1443,8 @@ def registerTab(self, tab, name, icon=None, current=True, sidx, wspace = self.stack.currentWorkspace() wspace.wsRegisterTab(tab, name, icon=icon, current=current, - tooltip=tooltip) + tooltip=tooltip, + position=position) if self.stack.currentWorkspace() is not wspace: wspace.wsSwitch() @@ -1587,7 +1589,7 @@ def onOpenMediaPlayer(self): self.getMediaPlayer() def onOpenBrowserTabClicked(self, pinBrowsed=False): - self.addBrowserTab(pinBrowsed=pinBrowsed) + self.addBrowserTab(pinBrowsed=pinBrowsed, urlFocus=True) def onWriteNewDocumentClicked(self): w = textedit.AddDocumentWidget(self, parent=self.tabWidget) @@ -1615,15 +1617,20 @@ def onHelpDonatePatreon(self): def addBrowserTab(self, label='No page loaded', pinBrowsed=False, minProfile=None, current=True, - workspace=None): + workspace=None, + urlFocus=False, + position='append'): icon = getIconIpfsIce() tab = browser.BrowserTab(self, minProfile=minProfile, pinBrowsed=pinBrowsed) self.registerTab(tab, label, icon=icon, current=current, - workspace=workspace) + workspace=workspace, + position=position) + + if urlFocus: + tab.focusUrlZone() - tab.focusUrlZone() return tab def addEventLogTab(self, current=False): diff --git a/galacteek/ui/widgets.py b/galacteek/ui/widgets.py index 0290fb12..4c3d73f7 100644 --- a/galacteek/ui/widgets.py +++ b/galacteek/ui/widgets.py @@ -1707,6 +1707,12 @@ def __init__(self, torInstance, *args, **kw): self.toggled.connect(self.onToggled) + def sysTrayMessage(self, msg): + self.app.systemTrayMessage( + 'Tor', + msg + ) + def useTorProxy(self, use=True): if use is True: proxy = TorNetworkProxy(self.tor.torCfg) @@ -1716,8 +1722,7 @@ def useTorProxy(self, use=True): f'TOR is used as proxy ' f'(socks port: {proxy.port()})')) - self.app.systemTrayMessage( - 'Tor', + self.sysTrayMessage( 'Tor is now used as proxy (click on the onion to disable it)' ) else: @@ -1731,6 +1736,10 @@ def useTorProxy(self, use=True): QWebEngineSettings.XSSAuditingEnabled, use ) + self.app.allWebProfilesSetAttribute( + QWebEngineSettings.DnsPrefetchEnabled, + not use + ) def onToggled(self, checked): self.useTorProxy(checked) @@ -1750,7 +1759,7 @@ async def onTorBootstrapStatus(self, pct, status): self.setToolTip(self.tt(f'TOR bootstrap: {pct}% complete')) if pct == 100: - self.useTorProxy() - - self.setChecked(True) self.setEnabled(True) + self.sysTrayMessage( + 'Tor is ready, click on the onion to enable it' + ) diff --git a/packaging/windows/galacteek-installer.nsi b/packaging/windows/galacteek-installer.nsi index b6365d6b..bb055ad1 100644 --- a/packaging/windows/galacteek-installer.nsi +++ b/packaging/windows/galacteek-installer.nsi @@ -14,7 +14,7 @@ RequestExecutionLevel admin !define VERSIONMAJOR 0 !define VERSIONMINOR 4 -!define VERSIONBUILD 39 +!define VERSIONBUILD 41 Name "${APPNAME}" Icon "share/icons/galacteek.ico" diff --git a/share/static/qss/default/galacteek.qss b/share/static/qss/default/galacteek.qss index 8f8c8b91..89b17fca 100644 --- a/share/static/qss/default/galacteek.qss +++ b/share/static/qss/default/galacteek.qss @@ -568,10 +568,8 @@ QGroupBox::title[niceBox="true"] { } -/* Other QToolButtons +/* Other QToolButtons */ -QToolButton::pressed { - background-color: #eec146; +QToolButton#torControlButton::checked { + background-color: #4b9fa2; } - -*/ diff --git a/share/translations/galacteek_en.ts b/share/translations/galacteek_en.ts index 366a4b4f..500036f2 100644 --- a/share/translations/galacteek_en.ts +++ b/share/translations/galacteek_en.ts @@ -148,27 +148,27 @@ - + Download - + PIN - + PIN (this page) - + PIN (recursive) - + Load IPNS key dialog @@ -183,27 +183,27 @@ - + Enter an IPFS CID - + Load IPFS CID dialog - + Browse IPNS resource from hash/name - + Enter an IPNS hash/name - + {0} is an invalid IPFS CID (Content IDentifier) @@ -213,52 +213,52 @@ - + Go to home page - + Follow IPNS resource - + Browse IPFS resource (CID) - + Browse multiple IPFS resources (CID) - + IPNS add feed dialog - + Open with - + Hashmarked {0} - + Hashmark title - + Invalid URL: {0} - + Not an IPFS resource @@ -283,12 +283,12 @@ - + Open link in new tab - + Open http/https link in new tab @@ -298,47 +298,47 @@ - + IPFS profile - + Web3 profile - + Minimal profile - + Javascript console - + Browse current clipboard item - + Create quick-access mapping - + Invalid IPFS object path: {0} - + Save full webpage to the "Web Pages" folder - + <p> <img src='{0}' width='32' height='32'/> @@ -357,7 +357,7 @@ - + <p> <img src='{0}' width='32' height='32'/> @@ -374,7 +374,7 @@ - + Link to Quick Access toolbar @@ -394,7 +394,7 @@ - + Save selected text to IPFS @@ -404,22 +404,22 @@ - + Save webpage to PDF - + Error saving webpage to PDF file - + Saved to PDF file: {0} - + Print (text) diff --git a/share/translations/galacteek_fr.ts b/share/translations/galacteek_fr.ts index d40430e9..5757df83 100644 --- a/share/translations/galacteek_fr.ts +++ b/share/translations/galacteek_fr.ts @@ -233,87 +233,87 @@ Ouvrir le lien dans une nouvelle tab - + Open with Ouvrir avec - + Download Télécharger - + PIN PIN - + PIN (this page) Pin (clouer) (cette page) - + PIN (recursive) Pin (clouer) (récursif) - + Follow IPNS resource Suivre la clé IPNS - + Enter an IPFS CID Entrez un CID IPFS - + Go to home page Allez a la page d'accueil - + Browse IPFS resource (CID) Accéder a un CID - + Browse multiple IPFS resources (CID) Accéder a plusieurs CIDs - + Load IPFS CID dialog Dialogue de chargement de CID - + IPNS add feed dialog Dialogue d'ajout de flux IPNS - + Browse IPNS resource from hash/name Accéder a une ressource IPNS (hash/nom) - + Enter an IPNS hash/name Entrez un nom/hash IPNS - + Load IPNS key dialog Dialogue de chargement IPNS - + Hashmarked {0} Hash-marqué: {0} - + Hashmark title Titre du hashmark @@ -323,12 +323,12 @@ URL invalide - + {0} is an invalid IPFS CID (Content IDentifier) {0} est un CID invalide - + Invalid URL: {0} URL invalide: {0} @@ -343,7 +343,7 @@ <html><head/><body><p>Dézoomer</p></body></html> - + Not an IPFS resource @@ -353,57 +353,57 @@ <html><head/><body><p>Arreter</p></body></html> - + Open link in new tab - + Open http/https link in new tab - + IPFS profile - + Web3 profile - + Minimal profile - + Javascript console - + Browse current clipboard item - + Create quick-access mapping - + Invalid IPFS object path: {0} - + Save full webpage to the "Web Pages" folder - + <p> <img src='{0}' width='32' height='32'/> @@ -422,7 +422,7 @@ - + <p> <img src='{0}' width='32' height='32'/> @@ -439,7 +439,7 @@ - + Link to Quick Access toolbar @@ -459,7 +459,7 @@ - + Save selected text to IPFS @@ -469,22 +469,22 @@ - + Save webpage to PDF - + Error saving webpage to PDF file - + Saved to PDF file: {0} - + Print (text) diff --git a/tests/core/test_multisearch.py b/tests/core/test_multisearch.py index 4fe522b9..0d33d4de 100644 --- a/tests/core/test_multisearch.py +++ b/tests/core/test_multisearch.py @@ -1,4 +1,3 @@ -import asyncio import pytest from galacteek.ipfs.search import multiSearch diff --git a/travis/Info.plist b/travis/Info.plist index 250c7dcf..e67fe7c8 100644 --- a/travis/Info.plist +++ b/travis/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleVersion - 0.4.30 + 0.4.41 CFBundleShortVersionString - 0.4.30 + 0.4.41 LSMinimumSystemVersion 10.7.0 LSUIElement @@ -27,7 +27,7 @@ NSHighResolutionCapable NSHumanReadableCopyright - © 2019 galacteek + © 2020 galacteek NSPrincipalClass NSApplication LSEnvironment