GUI: open functionality is barely functional

pytomb: adds undertaker, better parser (updated for new tomb messages)
undertaker: fix some things to make it work with new tomb
the rest is ui stuff :)

the ui does:
- allow auto-searching for the key near the tomb itself
- allow manually choosing a key location
- mount the tomb ;)
the ui DOESN'T:
- support usb nor anything else (shouldn't be that difficult)
- support periodical polling
- anything else
This commit is contained in:
boyska 2012-01-28 04:15:23 +01:00
parent 9318377aca
commit a0eb6ba9c3
12 changed files with 599 additions and 35 deletions

View File

@ -3,7 +3,13 @@ Utilities to analyze tomb output
'''
import re
_err_regex = re.compile(r'\[!\][^ ]* +(.+)$')
#found: [m] followed by some ID (usually "found") inside square brackets, then
#something else, then a space, then the content
_found_regex = re.compile(r'^\[m\]\[([^]]+)\] +(([^:]+)://(.+))$')
#generic: programname, then some identifiers in square (or round) brackets,
#then maybe something else, then a space, then the context
_generic_regex = re.compile(r'^[a-z-]+ [[(]([^]]+)[\])] +(.+)$')
types = {'E':'error', 'W':'warning', 'D':'debug', '*':'success'}
def parse_line(line):
'''Analyze a single line.
Return None if no standard format is detected, a dict otherwise.
@ -12,9 +18,17 @@ def parse_line(line):
'type' can be 'error', 'progress'
'''
match = _err_regex.search(line)
match = _found_regex.match(line)
if match:
return { 'type': 'error', 'content': match.group(1) }
return { 'type': types.get(match.group(1)) or match.group(1),
'content': match.group(2), 'scheme': match.group(3),
'path': match.group(4) }
match = _generic_regex.search(line)
if match:
return { 'type': types.get(match.group(1)) or match.group(1),
'content': match.group(2) }
return None

View File

@ -1,29 +1,52 @@
from tomblib.parser import *
class TestWrong:
def test_wrong_tag(self):
assert parse_line(' [a] foo') is None
def test_no_space(self):
assert parse_line(' [!]foo') is None
def test_short(self):
'''short format is not supported anymore'''
assert parse_line('[!] foo') is None
def test_colors(self):
'''parsing while using colors should fail'''
parse = parse_line('\033[32mundertaker [W] url protocol not recognized: nonscheme')
assert parse is None
def test_no_spaces_in_programname(self):
parse = parse_line('tomb open [W] url protocol not recognized: nonscheme')
assert parse is None
class TestError:
class TestFound:
def test_simple(self):
parse = parse_line('[!] foo')
parse = parse_line('[m][found] scheme:///and/path')
assert parse is not None
assert parse['type'] == 'error'
assert parse['content'] == 'foo'
def test_preceding(self):
parse = parse_line(' [!] foo')
assert parse['type'] == 'found'
assert parse['content'] == 'scheme:///and/path'
assert 'scheme' in parse
assert parse['scheme'] == 'scheme'
assert 'path' in parse
assert parse['path'] == '/and/path'
class TestGeneric:
def test_simple(self):
parse = parse_line('undertaker [W] url protocol not recognized: nonscheme')
assert parse is not None
assert parse['type'] == 'error'
assert parse['content'] == 'foo'
def test_following(self):
parse = parse_line('[!]shdad foo')
assert parse['type'] == 'warning'
assert parse['content'] == 'url protocol not recognized: nonscheme'
def test_debug(self):
parse = parse_line('undertaker [D] url protocol not recognized: nonscheme')
assert parse is not None
assert parse['type'] == 'error'
assert parse['content'] == 'foo'
def test_mul_words(self):
parse = parse_line('[!] shdad foo')
assert parse['type'] == 'debug'
assert parse['content'] == 'url protocol not recognized: nonscheme'
def test_success(self):
parse = parse_line('undertaker (*) url protocol not recognized: nonscheme')
assert parse is not None
assert parse['type'] == 'error'
assert parse['content'] == 'shdad foo'
assert parse['type'] == 'success'
assert parse['content'] == 'url protocol not recognized: nonscheme'
def test_dash(self):
parse = parse_line('tomb-open [W] url protocol not recognized: nonscheme')
assert parse is not None
assert parse['type'] == 'warning'
assert parse['content'] == 'url protocol not recognized: nonscheme'

View File

@ -0,0 +1,79 @@
import subprocess
from tempfile import NamedTemporaryFile
import parser
class Undertaker(object):
'''
This is similar to Tomb class, and provides a wrapper on undertaker.
TODO:
* methods for automagical scan
* output parsing, giving meaningful output
Due to the non-interactive nature of undertaker, it's simpler than Tomb
'''
undertakerexec = 'undertaker'
@classmethod
def check(cls, paths):
'''Will check if there are keys available there, as in --path
paths can be a string (one address), or a list of
'''
#TODO: more solid check: something like
if type(paths) is not str:
out = []
for p in paths:
try:
res = cls.check(p)
except:
continue
else:
if res:
out.extend(res)
return out
buf = NamedTemporaryFile()
try:
subprocess.check_call([cls.undertakerexec, paths, '--batch',
'--path'], stdout=buf)
except subprocess.CalledProcessError as exc:
return False
out = []
buf.seek(0)
for line in buf:
ret = parser.parse_line(line)
if ret and ret['type'] == 'found':
out.append(ret['content'])
return out
@classmethod
def get(cls, paths):
'''
Similar to check, but truly get the key content.
If paths is iterable, stop at the first successful path
'''
if type(paths) is not str:
for p in paths:
try:
res = cls.get(p)
except:
continue
else:
if res:
return res
buf = NamedTemporaryFile()
try:
subprocess.check_call([cls.undertakerexec, paths, '--batch'],
stdout=buf)
except subprocess.CalledProcessError:
return False
buf.seek(0)
return buf.read()
if __name__ == '__main__':
Undertaker.undertakerexec = '/home/davide/coding/projects/tomb/src/undertaker'
print Undertaker.get('near:///home/davide/Desktop/testing.tomb')

2
src/qt/.gitignore vendored
View File

@ -2,3 +2,5 @@
*.pyc
TombQt.egg-info
build
dist
disk

109
src/qt/tombqt/open.py Normal file
View File

@ -0,0 +1,109 @@
import sys
from PyQt4 import QtCore, QtGui
from ui_open_tombfile import Ui_tombfile
from ui_open_keymethod import Ui_keymethod
from ui_open_success import Ui_success
from tomblib.tomb import Tomb
from tomblib.undertaker import Undertaker
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class TombfilePage(QtGui.QWizardPage):
def __init__(self, *args, **kwargs):
QtGui.QWizardPage.__init__(self, *args)
self.ui = Ui_tombfile()
self.ui.setupUi(self)
if 'tombfile' in kwargs and kwargs['tombfile'] is not None:
self.ui.tomb_line.setText(kwargs['tombfile'])
self.ui.tomb_browse.clicked.connect(self.on_tomb_location_clicked)
def on_tomb_location_clicked(self, *args, **kwargs):
filename = QtGui.QFileDialog.getOpenFileName(self, 'Select Tomb',
filter="Tomb (*.tomb)")
self.ui.tomb_line.setText(filename)
class MethodPage(QtGui.QWizardPage):
def __init__(self, *args, **kwargs):
QtGui.QWizardPage.__init__(self, *args, **kwargs)
self.ui = Ui_keymethod()
self.ui.setupUi(self)
self.group = group = QtGui.QButtonGroup()
for radio in self.children():
if type(radio) == QtGui.QRadioButton:
group.addButton(radio)
def initializePage(self):
self.found = Undertaker.check( str('near://' + self.wizard().get_tombfile()) ) or []
box = self.ui.radio_layout
for key in self.found:
radio = QtGui.QRadioButton('Automatically found: ' + key, parent=self)
radio.setChecked(True)
radio.setProperty('path', key)
box.insertWidget(0, radio)
self.group.addButton(radio)
def nextId(self):
'''Virtual method reimplemented to decide next page'''
if self.ui.fs.isChecked():
keyfile = QtGui.QFileDialog.getOpenFileName(self.wizard(), 'Key file',
filter="Tomb keys (*.tomb.key);;Buried keys (*.jpeg)")
if keyfile:
#TODO: check if this really is a success :)
if Tomb.open(self.wizard().get_tombfile(), keyfile): #bugs when wrong password
return TombOpenWizard.SUCCESS_PAGE
#else: #TODO: should alert the user that we failed
return TombOpenWizard.METHOD_PAGE
if self.ui.usb.isChecked():
return TombOpenWizard.USB_PAGE
print self.group.checkedButton().property('path').toPyObject()
return TombOpenWizard.SUCCESS_PAGE
class SuccessPage(QtGui.QWizardPage):
def __init__(self, *args, **kwargs):
QtGui.QWizardPage.__init__(self, *args, **kwargs)
self.ui = Ui_success()
self.ui.setupUi(self)
class TombOpenWizard(QtGui.QWizard):
TOMBFILE_PAGE=1
METHOD_PAGE=2
SUCCESS_PAGE=99
USB_PAGE=20
def __init__(self, *args, **kwargs):
QtGui.QWizard.__init__(self, *args)
self.setPage(TombOpenWizard.TOMBFILE_PAGE,
TombfilePage(self, tombfile = kwargs['tombfile']
if 'tombfile' in kwargs else None))
self.setPage(TombOpenWizard.METHOD_PAGE, MethodPage(self))
self.setPage(TombOpenWizard.SUCCESS_PAGE, SuccessPage(self))
if 'tombfile' in kwargs and kwargs['tombfile'] is not None:
self.setStartId(TombOpenWizard.METHOD_PAGE)
def get_tombfile(self):
page = self.page(TombOpenWizard.TOMBFILE_PAGE)
return page.ui.tomb_line.text()
def run_open_wizard():
app = QtGui.QApplication(sys.argv)
window = TombOpenWizard(tombfile=sys.argv[1] if len(sys.argv) > 1 else None)
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
Undertaker.undertakerexec = '/home/davide/coding/projects/tomb/src/undertaker'
run_open_wizard()

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>keymethod</class>
<widget class="QWizardPage" name="keymethod">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>480</width>
<height>640</height>
</rect>
</property>
<property name="windowTitle">
<string>WizardPage</string>
</property>
<property name="title">
<string>Choose key</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>265</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="radio_layout">
<item>
<widget class="QRadioButton" name="fs">
<property name="text">
<string>Filesystem</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="usb">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>USB drive</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="bluetooth">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Retrieve via bluetooth (advanced)</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>265</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>success</class>
<widget class="QWizardPage" name="success">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>480</width>
<height>640</height>
</rect>
</property>
<property name="windowTitle">
<string>WizardPage</string>
</property>
<property name="title">
<string>Tomb opened</string>
</property>
<property name="subTitle">
<string>success</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>You successfully opened the tomb</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>tombfile</class>
<widget class="QWizardPage" name="tombfile">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>480</width>
<height>640</height>
</rect>
</property>
<property name="windowTitle">
<string>WizardPage</string>
</property>
<property name="title">
<string>Choose tomb</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>276</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Choose a tomb file on your filesystem</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="tomb_line">
<property name="placeholderText">
<string>/path/to/your.tomb</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tomb_browse">
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>276</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'tombqt/open_keymethod.ui'
#
# Created: Sat Jan 28 03:36:11 2012
# by: PyQt4 UI code generator 4.9
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_keymethod(object):
def setupUi(self, keymethod):
keymethod.setObjectName(_fromUtf8("keymethod"))
keymethod.resize(480, 640)
self.verticalLayout = QtGui.QVBoxLayout(keymethod)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
spacerItem = QtGui.QSpacerItem(20, 265, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.radio_layout = QtGui.QVBoxLayout()
self.radio_layout.setObjectName(_fromUtf8("radio_layout"))
self.fs = QtGui.QRadioButton(keymethod)
self.fs.setObjectName(_fromUtf8("fs"))
self.radio_layout.addWidget(self.fs)
self.usb = QtGui.QRadioButton(keymethod)
self.usb.setEnabled(False)
self.usb.setObjectName(_fromUtf8("usb"))
self.radio_layout.addWidget(self.usb)
self.bluetooth = QtGui.QRadioButton(keymethod)
self.bluetooth.setEnabled(False)
self.bluetooth.setObjectName(_fromUtf8("bluetooth"))
self.radio_layout.addWidget(self.bluetooth)
self.verticalLayout.addLayout(self.radio_layout)
spacerItem1 = QtGui.QSpacerItem(20, 265, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem1)
self.retranslateUi(keymethod)
QtCore.QMetaObject.connectSlotsByName(keymethod)
def retranslateUi(self, keymethod):
keymethod.setWindowTitle(QtGui.QApplication.translate("keymethod", "WizardPage", None, QtGui.QApplication.UnicodeUTF8))
keymethod.setTitle(QtGui.QApplication.translate("keymethod", "Choose key", None, QtGui.QApplication.UnicodeUTF8))
self.fs.setText(QtGui.QApplication.translate("keymethod", "Filesystem", None, QtGui.QApplication.UnicodeUTF8))
self.usb.setText(QtGui.QApplication.translate("keymethod", "USB drive", None, QtGui.QApplication.UnicodeUTF8))
self.bluetooth.setText(QtGui.QApplication.translate("keymethod", "Retrieve via bluetooth (advanced)", None, QtGui.QApplication.UnicodeUTF8))

View File

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'tombqt/open_success.ui'
#
# Created: Mon Jan 23 23:06:38 2012
# by: PyQt4 UI code generator 4.9
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_success(object):
def setupUi(self, success):
success.setObjectName(_fromUtf8("success"))
success.resize(480, 640)
self.verticalLayout = QtGui.QVBoxLayout(success)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.label = QtGui.QLabel(success)
self.label.setObjectName(_fromUtf8("label"))
self.verticalLayout.addWidget(self.label)
self.retranslateUi(success)
QtCore.QMetaObject.connectSlotsByName(success)
def retranslateUi(self, success):
success.setWindowTitle(QtGui.QApplication.translate("success", "WizardPage", None, QtGui.QApplication.UnicodeUTF8))
success.setTitle(QtGui.QApplication.translate("success", "Tomb opened", None, QtGui.QApplication.UnicodeUTF8))
success.setSubTitle(QtGui.QApplication.translate("success", "success", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("success", "You successfully opened the tomb", None, QtGui.QApplication.UnicodeUTF8))

View File

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'tombqt/open_tombfile.ui'
#
# Created: Tue Jan 24 00:49:10 2012
# by: PyQt4 UI code generator 4.9
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_tombfile(object):
def setupUi(self, tombfile):
tombfile.setObjectName(_fromUtf8("tombfile"))
tombfile.resize(480, 640)
self.verticalLayout_2 = QtGui.QVBoxLayout(tombfile)
self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
spacerItem = QtGui.QSpacerItem(20, 276, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem)
self.verticalLayout = QtGui.QVBoxLayout()
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.label = QtGui.QLabel(tombfile)
self.label.setObjectName(_fromUtf8("label"))
self.verticalLayout.addWidget(self.label)
self.horizontalLayout = QtGui.QHBoxLayout()
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
self.tomb_line = QtGui.QLineEdit(tombfile)
self.tomb_line.setObjectName(_fromUtf8("tomb_line"))
self.horizontalLayout.addWidget(self.tomb_line)
self.tomb_browse = QtGui.QPushButton(tombfile)
self.tomb_browse.setObjectName(_fromUtf8("tomb_browse"))
self.horizontalLayout.addWidget(self.tomb_browse)
self.verticalLayout.addLayout(self.horizontalLayout)
self.verticalLayout_2.addLayout(self.verticalLayout)
spacerItem1 = QtGui.QSpacerItem(20, 276, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem1)
self.retranslateUi(tombfile)
QtCore.QMetaObject.connectSlotsByName(tombfile)
def retranslateUi(self, tombfile):
tombfile.setWindowTitle(QtGui.QApplication.translate("tombfile", "WizardPage", None, QtGui.QApplication.UnicodeUTF8))
tombfile.setTitle(QtGui.QApplication.translate("tombfile", "Choose tomb", None, QtGui.QApplication.UnicodeUTF8))
self.label.setText(QtGui.QApplication.translate("tombfile", "Choose a tomb file on your filesystem", None, QtGui.QApplication.UnicodeUTF8))
self.tomb_line.setPlaceholderText(QtGui.QApplication.translate("tombfile", "/path/to/your.tomb", None, QtGui.QApplication.UnicodeUTF8))
self.tomb_browse.setText(QtGui.QApplication.translate("tombfile", "Browse", None, QtGui.QApplication.UnicodeUTF8))

View File

@ -37,7 +37,7 @@ fi
key_found() {
# $1 is "url"
if option_is_set --machine-parseable; then
if option_is_set --batch; then
print -n '[m]'
fi
print "$fg[white][found] $1"
@ -46,7 +46,7 @@ key_found() {
function undertaker_scheme() {
zparseopts -D -print-path=print_path
zparseopts -D -path=print_path
local scheme
scheme=$1
@ -60,7 +60,7 @@ function undertaker_scheme() {
act "access to bluetooth protocol requested"
which obexftp &> /dev/null
if [[ $? != 0 ]]; then
error "obexftp not found, needed for bluetooth: operation aborted."
_warning "obexftp not found, needed for bluetooth: operation aborted."
return 64
fi
keytmp=`safe_dir undertaker`
@ -95,10 +95,10 @@ function undertaker_scheme() {
file)
if ! [[ -f $keypath ]]; then
error "Invalid path $keypath"
_warning "Invalid path $keypath"
return 1
fi
if [[ -n $print_path ]]; then
if option_is_set --path; then
key_found $scheme://$keypath;
else
< $keypath
@ -122,7 +122,7 @@ function undertaker_scheme() {
#It implements automounting using udisks; udisks is a (recently)
#new technology, so we can't rely on it being present
if ! which udisks &> /dev/null; then
error 'udisks not found'
_warning 'udisks not found'
exit 64
fi
while true; do
@ -149,7 +149,7 @@ function undertaker_scheme() {
*)
if ! which undertaker-$scheme &> /dev/null; then
error "url protocol not recognized: $scheme"
_warning "url protocol not recognized: $scheme"
return 64
fi
undertaker-$scheme ${print_path[@]} ${scheme}://$keypath
@ -162,13 +162,14 @@ function main() {
typeset -A opts
zparseopts -M -E -D -Aopts -poll -path -batch
if ! [ $1 ] ; then
error "an argument is missing, the undertaker is confused"
act "usage: undertaker [options] url://host:path/to/tomb.key"
echo "[W] an argument is missing, the undertaker is confused"
echo "usage: undertaker [options] url://host:path/to/tomb.key"
exit 1;
fi
local -a tomb_opts
if [[ -n ${(k)opts[--batch]} ]]; then
tomb_opts+='--batch'
tomb_opts+='--no-color'
tomb_opts+='--quiet'
fi
local -a under_opts
if [[ -n ${(k)opts[--path]} ]]; then
@ -178,13 +179,14 @@ function main() {
for a in ${(k)opts}; do
backupopts[$a]=${opts[$a]}
done
source tomb ${tomb_opts[@]} source
source tomb ${tomb_opts[@]} source
TOMBEXEC=undertaker
for a in ${(k)backupopts}; do
opts[$a]=${backupopts[$a]}
done
check_bin
notice "Undertaker will look for $1"
_success "Undertaker will look for $1"
ARG1=${1}
scheme=${ARG1%://*}