First commit

This commit is contained in:
Martchus 2015-04-22 19:30:09 +02:00
commit bda20da10c
65 changed files with 10536 additions and 0 deletions

View File

@ -0,0 +1,48 @@
<?xml version="1.0"?>
<manifest package="org.martchus.passwordmanager" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="@string/app_name" android:icon="@drawable/icon">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="@string/app_name" android:screenOrientation="unspecified" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="passwordmanager"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
<!-- Splash screen -->
<!--
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/>
-->
<!-- Splash screen -->
</activity>
</application>
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="20"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<!-- %%INSERT_PERMISSIONS -->
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Password Manager</string>
</resources>

768
cli/cli.cpp Normal file
View File

@ -0,0 +1,768 @@
#include "cli.h"
#include <passwordfile/io/passwordfile.h>
#include <passwordfile/io/cryptoexception.h>
#include <passwordfile/io/parsingexception.h>
#include <passwordfile/io/entry.h>
#include <passwordfile/io/field.h>
#include <c++utilities/conversion/stringconversion.h>
#include <c++utilities/application/global.h>
#if defined(PLATFORM_UNIX)
#include <unistd.h>
#include <c++utilities/io/ansiescapecodes.h>
#endif
#include <algorithm>
#include <functional>
using namespace std;
using namespace std::placeholders;
using namespace ConversionUtilities;
using namespace Io;
namespace Cli {
InputMuter::InputMuter()
{
#if defined(PLATFORM_UNIX)
tcgetattr(STDIN_FILENO, &m_attr);
termios newAttr = m_attr;
newAttr.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &newAttr);
#elif defined(PLATFORM_WINDOWS)
m_cinHandle = GetStdHandle(STD_INPUT_HANDLE);
m_mode = 0;
GetConsoleMode(m_cinHandle, &m_mode);
SetConsoleMode(m_cinHandle, m_mode & (~ENABLE_ECHO_INPUT));
#endif
}
InputMuter::~InputMuter()
{
#if defined(PLATFORM_UNIX)
tcsetattr(STDIN_FILENO, TCSANOW, &m_attr);
#elif defined(PLATFORM_WINDOWS)
SetConsoleMode(m_cinHandle, m_mode);
#endif
}
void clearConsole()
{
#if defined(PLATFORM_WINDOWS)
HANDLE hStdOut;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD count;
DWORD cellCount;
COORD homeCoords = {0, 0};
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if(hStdOut == INVALID_HANDLE_VALUE) {
return;
}
// get the number of cells in the current buffer
if(!GetConsoleScreenBufferInfo(hStdOut, &csbi)) {
return;
}
cellCount = csbi.dwSize.X * csbi.dwSize.Y;
// fill the entire buffer with spaces
if(!FillConsoleOutputCharacter(hStdOut, (TCHAR) ' ', cellCount, homeCoords, &count)) {
return;
}
// fill the entire buffer with the current colors and attributes
if(!FillConsoleOutputAttribute(hStdOut, csbi.wAttributes, cellCount, homeCoords, &count)) {
return;
}
// move the cursor home
SetConsoleCursorPosition(hStdOut, homeCoords);
#else
EscapeCodes::setCursor(cout);
EscapeCodes::eraseDisplay(cout);
#endif
}
InteractiveCli::InteractiveCli() :
m_o(cout),
m_i(cin),
m_currentEntry(nullptr),
m_modified(false),
m_quit(false)
{}
void InteractiveCli::run(const string &file)
{
if(!file.empty()) {
openFile(file, false);
}
string input;
while(!m_quit) {
getline(m_i, input);
if(!input.empty()) {
processCommand(input);
}
}
}
void InteractiveCli::processCommand(const string &cmd)
{
#define CMD(value) !paramMissing && cmd == value
#define CMD2(value1, value2) !paramMissing && (cmd == value1 || cmd == value2)
#define CMD_P(value) !paramMissing && checkCommand(cmd, value, param, paramMissing)
#define CMD2_P(value1, value2) !paramMissing && (checkCommand(cmd, value1, param, paramMissing) || checkCommand(cmd, value2, param, paramMissing))
string param;
bool paramMissing = false;
if(CMD2("quit", "q")) {
quit();
} else if(CMD("q!")) {
m_quit = true;
} else if(CMD("wq")) {
saveFile();
m_quit = true;
} else if(CMD2("clear", "c")) {
clearConsole();
} else if(CMD2_P("openreadonly", "or")) {
openFile(param, true);
} else if(CMD2_P("open", "o")) {
openFile(param, false);
} else if(CMD2("close", "c")) {
closeFile();
} else if(CMD2("save", "w")) {
saveFile();
} else if(CMD2_P("create", "cr")) {
createFile(param);
} else if(CMD("chpassphrase")) {
changePassphrase();
} else if(CMD("rmpassphrase")) {
removePassphrase();
} else if(CMD("pwd")) {
pwd();
} else if(CMD_P("cd")) {
cd(param);
} else if(CMD("ls")) {
ls();
} else if(CMD2("tree", "t")) {
tree();
} else if(CMD2_P("mknode", "mkn")) {
makeEntry(EntryType::Node, param);
} else if(CMD2_P("mkaccount", "mka")) {
makeEntry(EntryType::Account, param);
} else if(CMD2("rmentry", "rme")) {
removeEntry(".");
} else if(CMD2_P("rmentry", "rme")) {
removeEntry(param);
} else if(CMD2_P("rnentry", "rne")) {
renameEntry(param);
} else if(CMD2_P("mventry", "me")) {
moveEntry(param);
} else if(CMD2_P("readfield", "rf")) {
readField(param);
} else if(CMD2_P("setfield", "sf")) {
setField(false, param);
} else if(CMD2_P("setfieldpw", "sp")) {
setField(true, param);
} else if(CMD2_P("rmfield", "rf")) {
removeField(param);
} else if(CMD2("help", "?")) {
printHelp();
} else if(paramMissing) {
m_o << "parameter is missing" << endl;
} else {
m_o << "command is unknown" << endl;
}
}
Entry *InteractiveCli::resolvePath(const string &path)
{
auto parts = splitString<vector<string> >(path, "/", EmptyPartsTreat::Merge);
bool fromRoot = path.at(0) == '/';
if(fromRoot && parts.empty()) {
return m_file.rootEntry();
} else {
Entry *entry = fromRoot ? m_file.rootEntry() : m_currentEntry;
for(const string &part : parts) {
if(part == "..") {
if(entry->parent()) {
entry = entry->parent();
} else {
m_o << "can not resolve path; entry \"" << entry->label() << "\" is root" << endl;
return nullptr;
}
} else if(part != ".") {
switch(entry->type()) {
case EntryType::Account:
m_o << "can not resolve path; entry \"" << entry->label() << "\" is not a node entry" << endl;
return nullptr;
case EntryType::Node:
for(Entry *child : (static_cast<NodeEntry *>(entry)->children())) {
if(child->label() == part) {
entry = child;
goto next;
}
}
m_o << "can not resolve path; entry \"" << entry->label() << "\" has no child \"" << part << "\"" << endl;
return nullptr;
}
}
next:;
}
return entry;
}
}
bool InteractiveCli::checkCommand(const string &str, const char *phrase, std::string &param, bool &paramMissing)
{
for(auto i = str.cbegin(), end = str.cend(); i != end; ++i, ++phrase) {
if(*phrase == 0) {
if(*i == ' ') {
if(++i != end) {
param.assign(i, end);
return true;
} else {
paramMissing = true;
}
}
return false;
} else if(*i != *phrase) {
return false;
}
}
paramMissing = *phrase == 0;
return false;
}
void InteractiveCli::openFile(const string &file, bool readOnly)
{
if(m_file.isOpen()) {
m_o << "file \"" << m_file.path() << "\" currently open; close first" << endl;
} else {
m_file.setPath(file);
try {
try {
m_file.open(readOnly);
if(m_file.isEncryptionUsed()) {
m_file.setPassword(askForPassphrase());
}
m_file.load();
m_currentEntry = m_file.rootEntry();
m_o << "file \"" << file << "\" opened" << endl;
} catch (ParsingException &) {
m_o << "error occured when parsing file \"" << file << "\"" << endl;
throw;
} catch (CryptoException &) {
m_o << "error occured when decrypting file \"" << file << "\"" << endl;
throw;
} catch (ios_base::failure &) {
m_o << "IO error occured when opening file \"" << file << "\"" << endl;
throw;
} catch (exception &) {
m_o << "an unexpected exception occured when opening file \"" << file << "\"" << endl;
terminate();
}
} catch(exception &e) {
if(*e.what() != 0) {
m_o << e.what() << endl;
}
m_file.clear();
m_currentEntry = nullptr;
}
m_modified = false;
}
}
void InteractiveCli::closeFile()
{
if(m_file.isOpen()) {
m_file.clear();
m_currentEntry = nullptr;
m_o << "file closed" << endl;
} else {
m_o << "no file was opened" << endl;
}
}
void InteractiveCli::saveFile()
{
if(m_file.isOpen()) {
try {
try {
m_file.save(*m_file.password());
m_o << "file \"" << m_file.path() << "\" saved" << endl;
} catch (ParsingException &) {
m_o << "error occured when parsing file \"" << m_file.path() << "\"" << endl;
throw;
} catch (CryptoException &) {
m_o << "error occured when encrypting file \"" << m_file.path() << "\"" << endl;
throw;
} catch (ios_base::failure &) {
m_o << "IO error occured when saving file \"" << m_file.path() << "\"" << endl;
throw;
} catch (exception &) {
m_o << "an unexpected exception occured when saving file \"" << m_file.path() << "\"" << endl;
terminate();
}
} catch(exception &e) {
if(*e.what() != 0) {
m_o << e.what() << endl;
}
m_o << "file has been closed; try reopening the file" << endl;
m_file.clear();
}
m_modified = false;
} else {
m_o << "nothing to save; no file opened or created" << endl;
}
}
void InteractiveCli::createFile(const string &file)
{
if(m_file.isOpen()) {
m_o << "file \"" << m_file.path() << "\" currently open; close first" << endl;
} else {
m_file.setPath(file);
try {
try {
m_file.create();
m_file.generateRootEntry();
m_currentEntry = m_file.rootEntry();
m_o << "file \"" << file << "\" created and opened" << endl;
} catch (ios_base::failure &) {
m_o << "IO error occured when creating file \"" << file << "\"" << endl;
throw;
} catch (exception &) {
m_o << "an unexpected exception occured when creating file \"" << file << "\"" << endl;
terminate();
}
} catch(exception &e) {
if(*e.what() != 0) {
m_o << e.what() << endl;
}
m_file.clear();
m_currentEntry = nullptr;
}
m_modified = false;
}
}
void InteractiveCli::changePassphrase()
{
if(m_file.isOpen()) {
try {
m_file.setPassword(askForPassphrase(true));
m_modified = true;
m_o << "passphrase changed; use save to apply" << endl;
} catch(runtime_error &) {
m_o << "passphrase has not changed" << endl;
}
} else {
m_o << "can not set passphrase; no file opened or created" << endl;
}
}
void InteractiveCli::removePassphrase()
{
if(m_file.isOpen()) {
if(*m_file.password()) {
m_file.clearPassword();
m_o << "passphrase removed; use save to apply" << endl;
m_modified = true;
} else {
m_o << "nothing to remove; no passphrase present on current file" << endl;
}
} else {
m_o << "nothing to remove; no file opened or created" << endl;
}
}
void InteractiveCli::pwd()
{
if(m_file.isOpen()) {
auto path = m_currentEntry->path();
m_o << path.front() << ": /";
path.pop_front();
m_o << joinStrings(path, "/") << endl;
} else {
m_o << "no file open" << endl;
}
}
void InteractiveCli::cd(const string &path)
{
if(m_file.isOpen()) {
if(Entry *entry = resolvePath(path)) {
m_currentEntry = entry;
m_o << "changed to \"" << entry->label() << "\"" << endl;
}
} else {
m_o << "can not change directory; no file open" << endl;
}
}
void InteractiveCli::ls()
{
if(m_file.isOpen()) {
switch(m_currentEntry->type()) {
case EntryType::Account: {
m_o << "fields:";
for(const Field &field : static_cast<AccountEntry *>(m_currentEntry)->fields()) {
m_o << " " << field.name();
}
break;
} case EntryType::Node: {
m_o << "entries:";
for(const Entry *entry : static_cast<NodeEntry *>(m_currentEntry)->children()) {
m_o << " " << entry->label();
}
break;
}
}
m_o << endl;
} else {
m_o << "can not list any entires; no file open" << endl;
}
}
void InteractiveCli::tree()
{
if(m_file.isOpen()) {
function<void(const Entry *entry, unsigned char level)> printEntries;
printEntries = [&printEntries, this] (const Entry *entry, unsigned char level) {
for(unsigned char i = 0; i < level; ++i) {
m_o << " ";
}
m_o << entry->label() << endl;
if(entry->type() == EntryType::Node) {
for(const Entry *child : (static_cast<const NodeEntry *>(entry)->children())) {
printEntries(child, level + 2);
}
}
};
printEntries(m_currentEntry, 0);
} else {
m_o << "can not print tree; no file open" << endl;
}
}
void InteractiveCli::makeEntry(EntryType entryType, const string &label)
{
if(m_file.isOpen()) {
switch(m_currentEntry->type()) {
case EntryType::Node:
switch(entryType) {
case EntryType::Node:
m_o << "node entry \"" << (new NodeEntry(label, static_cast<NodeEntry *>(m_currentEntry)))->label() << "\" created" << endl;
break;
case EntryType::Account:
m_o << "account entry \"" << (new AccountEntry(label, static_cast<NodeEntry *>(m_currentEntry)))->label() << "\" created" << endl;
break;
}
m_modified = true;
break;
case EntryType::Account:
m_o << "can not make entry; current entry is no node entry" << endl;
}
} else {
m_o << "can not make entry; no file open" << endl;
}
}
void InteractiveCli::removeEntry(const string &path)
{
if(m_file.isOpen()) {
if(Entry *entry = resolvePath(path)) {
if(entry == m_file.rootEntry()) {
m_o << "can not remove root entry" << endl;
} else {
if(entry == m_currentEntry) {
m_currentEntry = entry->parent();
}
m_o << "removed entry \"" << entry->label() << "\"" << endl;
delete entry;
m_modified = true;
}
}
} else {
m_o << "can not remove entry; no file open" << endl;
}
}
void InteractiveCli::renameEntry(const string &path)
{
if(m_file.isOpen()) {
if(Entry *entry = resolvePath(path)) {
string label;
m_o << "enter new name: " << endl;
getline(m_i, label);
if(label.empty()) {
m_o << "can not rename; new name is empty" << endl;
} else {
entry->setLabel(label);
m_o << "entry renamed to \"" << entry->label() << "\"" << endl;
m_modified = true;
}
}
} else {
m_o << "can not rename entry; no file open" << endl;
}
}
void InteractiveCli::moveEntry(const string &path)
{
if(m_file.isOpen()) {
if(Entry *entry = resolvePath(path)) {
string newParentPath;
m_o << "enter path of new parent: " << endl;
getline(m_i, newParentPath);
if(newParentPath.empty()) {
m_o << "can not move; path of new parent is empty" << endl;
} else {
if(Entry *newParent = resolvePath(newParentPath)) {
switch(newParent->type()) {
case EntryType::Account:
m_o << "can not move; new parent must be a node entry" << endl;
break;
case EntryType::Node:
if(entry->parent() == entry) {
m_o << "element not moved; parent doesn't change" << endl;
} else if(entry->type() == EntryType::Node && newParent->isIndirectChildOf(static_cast<NodeEntry *>(entry))) {
m_o << "can not move; new parent mustn't be child of the entry to move" << endl;
} else {
entry->setParent(static_cast<NodeEntry *>(newParent));
m_o << "entry moved to \"" << newParent->label() << "\"" << endl;
m_modified = true;
}
}
}
}
}
} else {
m_o << "can not rename entry; no file open" << endl;
}
}
void InteractiveCli::readField(const string &fieldName)
{
if(m_file.isOpen()) {
if(m_currentEntry->type() == EntryType::Account) {
const vector<Field> &fields = static_cast<AccountEntry *>(m_currentEntry)->fields();
bool valuesFound = false;
for(const Field &field : fields) {
if(field.name() == fieldName) {
m_o << field.value() << endl;
valuesFound = true;
}
}
if(!valuesFound) {
m_o << "field \"" << fieldName << "\" does not exist" << endl;
}
} else {
m_o << "can not read field; current entry is no account entry" << endl;
}
} else {
m_o << "can not read field; no file open" << endl;
}
}
void InteractiveCli::setField(bool useMuter, const string &fieldName)
{
if(m_file.isOpen()) {
if(m_currentEntry->type() == EntryType::Account) {
vector<Field> &fields = static_cast<AccountEntry *>(m_currentEntry)->fields();
unsigned int valuesFound = 0;
string value;
m_o << "enter new value: ";
if(useMuter) {
InputMuter m;
getline(m_i, value);
m_o << endl << "repeat: ";
string repeat;
getline(m_i, repeat);
if(value != repeat) {
m_o << "values do not match; field has not been altered" << endl;
return;
}
} else {
getline(m_i, value);
}
for(Field &field : fields) {
if(field.name() == fieldName) {
++valuesFound;
if(valuesFound == 1) {
field.setValue(value);
} else {
m_o << "enter new value for " << valuesFound << ". field (with the specified field): ";
value.clear();
if(useMuter) {
InputMuter m;
getline(m_i, value);
m_o << endl << "repeat: ";
string repeat;
getline(m_i, repeat);
if(value == repeat) {
field.setValue(value);
} else {
m_o << "values do not match; field has not been altered" << endl;
}
} else {
getline(m_i, value);
field.setValue(value);
}
}
field.setType(useMuter ? FieldType::Password : FieldType::Normal);
}
}
switch(valuesFound) {
case 0:
fields.emplace_back(static_cast<AccountEntry *>(m_currentEntry), fieldName, value);
if(useMuter) {
fields.back().setType(FieldType::Password);
}
m_o << "new field with value inserted" << endl;
break;
case 1:
m_o << "value updated" << endl;
m_modified = true;
break;
default:
m_o << valuesFound << " values updated" << endl;
m_modified = true;
}
} else {
m_o << "can not set field; current entry is no account entry" << endl;
}
} else {
m_o << "can not set field; no file open" << endl;
}
}
void InteractiveCli::removeField(const string &fieldName)
{
if(m_file.isOpen()) {
if(m_currentEntry->type() == EntryType::Account) {
vector<Field> &fields = static_cast<AccountEntry *>(m_currentEntry)->fields();
unsigned int valuesFound = 0;
for(Field &field : fields) {
if(field.name() == fieldName) {
++valuesFound;
}
}
switch(valuesFound) {
valuesFound = 0;
case 0:
break;
case 1:
fields.erase(remove_if(fields.begin(), fields.end(), [&fieldName] (Field &field) {
return field.name() == fieldName;
}));
break;
default:
fields.erase(remove_if(fields.begin(), fields.end(), [this, &fieldName, &valuesFound] (Field &field) {
if(field.name() == fieldName) {
m_o << "remove " << ++valuesFound << ". occurrence? [y]=yes, different key=no " << endl;
string res;
getline(m_i, res);
return !(res == "y" || res == "yes");
} else {
return false;
}
}));
}
switch(valuesFound) {
case 0:
m_o << "can not remove field; specified field \"" << fieldName << "\" not found" << endl;
break;
case 1:
m_o << "field removed" << endl;
m_modified = true;
break;
default:
m_o << valuesFound << " fields removed" << endl;
m_modified = true;
}
} else {
m_o << "can not remove field; current entry is no account entry" << endl;
}
} else {
m_o << "can not remove field; no file open" << endl;
}
}
void InteractiveCli::printHelp()
{
m_o << "Available commands: \n"
"quit,q quits the application\n"
"q! forces the application to quit\n"
"wq saves the current file and quits the application\n"
"clear,c clears the console\n"
"\n"
"create,cr creates a new file at the specified path\n"
"openreadonly opens the specified file (read-only)\n"
"open,o opens the specified file\n"
"close,cl closes the currently opened file\n"
"\n"
"save,w saves the currently opened file\n"
"chpassphrase changes the passphrase\n"
"rmpassphrase removes the passphrase\n"
"\n"
"pwd prints the path of the current entry\n"
"cd changes the current entry\n"
"ls lists the entries/fields of the current entry\n"
"tree,t shows all child entries of the current entry\n"
"mknode,mkn creates a node entry with the specified label in the current entry\n"
"mkaccount,mka creates an account entry with the specified label in the current entry\n"
"rmentry,rme removes the entry specified by its path\n"
"rnentry,rne renames the entry specified by its path\n"
"mventry,me moves the entry specified by its path\n"
"readfield,rf reads the specified field of the current account\n"
"setfield,sf sets the specified field of the current account\n"
"setfieldpw,sp sets the specified password field of the current account\n"
"rmfield,rf removes the specified field of the current account\n" << endl;
}
void InteractiveCli::quit()
{
if(m_file.isOpen() && m_modified) {
m_o << "file modified; use q! or wq" << endl;
} else {
m_quit = true;
}
}
string InteractiveCli::askForPassphrase(bool confirm)
{
if(confirm) {
m_o << "enter new passphrase: ";
} else {
m_o << "enter passphrase: ";
}
m_o.flush();
string input1;
{
InputMuter m;
getline(m_i, input1);
}
m_o << endl;
if(input1.empty()) {
m_o << "you did not enter a passphrase" << endl;
} else {
if(confirm) {
m_o << "confirm new passphrase: ";
m_o.flush();
string input2;
{
InputMuter m;
getline(m_i, input2);
}
m_o << endl;
if(input1 != input2) {
m_o << "phrases do not match" << endl;
throw runtime_error("confirmation failed");
}
}
}
return input1;
}
}

90
cli/cli.h Normal file
View File

@ -0,0 +1,90 @@
#ifndef CLI_CLI_H
#define CLI_CLI_H
#include <passwordfile/io/passwordfile.h>
#include <c++utilities/application/global.h>
#if defined(PLATFORM_UNIX)
#include <termios.h>
#elif defined(PLATFORM_WINDOWS)
#include <windows.h>
#endif
#include <string>
#include <vector>
#include <istream>
#include <ostream>
namespace ApplicationUtilities {
typedef std::vector<std::string> StringVector;
}
namespace Io {
class Entry;
enum class EntryType : int;
}
namespace Cli {
class InputMuter
{
public:
InputMuter();
~InputMuter();
private:
#if defined(PLATFORM_UNIX)
termios m_attr;
#elif defined(PLATFORM_WINDOWS)
HANDLE m_cinHandle;
DWORD m_mode;
#endif
};
void clearConsole();
class InteractiveCli
{
public:
InteractiveCli();
void run(const std::string &file = std::string());
void openFile(const std::string &file, bool readOnly);
void closeFile();
void saveFile();
void createFile(const std::string &file);
void changePassphrase();
void removePassphrase();
void pwd();
void cd(const std::string &path);
void ls();
void tree();
void makeEntry(Io::EntryType entryType, const std::string &label);
void removeEntry(const std::string &path);
void renameEntry(const std::string &path);
void moveEntry(const std::string &path);
void readField(const std::string &fieldName);
void setField(bool useMuter, const std::string &fieldName);
void removeField(const std::string &fieldName);
void printHelp();
void quit();
private:
void processCommand(const std::string &cmd);
Io::Entry *resolvePath(const std::string &path);
static bool checkCommand(const std::string &str, const char *phrase, std::string &param, bool &paramMissing);
std::string askForPassphrase(bool confirm = false);
std::ostream &m_o;
std::istream &m_i;
Io::PasswordFile m_file;
Io::Entry *m_currentEntry;
bool m_modified;
bool m_quit;
};
}
#endif // CLI_CLI_H

View File

@ -0,0 +1,81 @@
#include "draganddroptablewidget.h"
#include <QDropEvent>
#include <QMimeData>
#include <QMessageBox>
namespace QtGui {
DragAndDropTableWidget::DragAndDropTableWidget(QWidget *parent) :
QTableWidget(parent),
m_insertEmptyRowAtEnd(false)
{
connect(this, &DragAndDropTableWidget::cellChanged,
this, &DragAndDropTableWidget::processCellChanged);
}
void DragAndDropTableWidget::insertEmptyRowAtEndAutomatically(bool value)
{
m_insertEmptyRowAtEnd = value;
if(value) {
processCellChanged(0,0);
}
}
bool DragAndDropTableWidget::hasEmptyRowAtEnd() const
{
bool hasEmptyRow = false;
int rowCount = this->rowCount();
if(rowCount > 0) {
if(QTableWidgetItem *item = this->item(rowCount - 1, 0)) {
hasEmptyRow = item->text().isEmpty();
} else {
hasEmptyRow = true;
}
}
return hasEmptyRow;
}
bool DragAndDropTableWidget::dropMimeData(int row, int column, const QMimeData *data, Qt::DropAction action)
{
if(data->hasText()) {
if(QTableWidgetItem *item = this->item(row, column))
item->setText(data->text());
return false;
} else {
return QTableWidget::dropMimeData(row, column, data, action);
}
}
QStringList DragAndDropTableWidget::mimeTypes() const
{
return QTableWidget::mimeTypes() << QStringLiteral("text/plain");
}
QMimeData *DragAndDropTableWidget::mimeData(const QList<QTableWidgetItem *> items) const
{
QString text;
if(items.count() > 0) {
QTableWidgetItem *lastItem = items.last();
foreach(QTableWidgetItem *item, items) {
if(!item->text().isEmpty()) {
text.append(item->text());
if(item != lastItem) {
text.append(QStringLiteral("\n"));
}
}
}
}
QMimeData *data = QTableWidget::mimeData(items);
data->setText(text);
return data;
}
void DragAndDropTableWidget::processCellChanged(int, int)
{
if(m_insertEmptyRowAtEnd && !hasEmptyRowAtEnd()) {
insertRow(rowCount());
}
}
}

View File

@ -0,0 +1,33 @@
#ifndef DRAGANDDROPTABLEWIDGET_H
#define DRAGANDDROPTABLEWIDGET_H
#include <QTableWidget>
namespace QtGui {
class DragAndDropTableWidget : public QTableWidget
{
Q_OBJECT
public:
explicit DragAndDropTableWidget(QWidget *parent = nullptr);
void insertEmptyRowAtEndAutomatically(bool value);
bool hasEmptyRowAtEnd() const;
protected:
virtual bool dropMimeData(int row, int column, const QMimeData * data, Qt::DropAction action);
virtual QStringList mimeTypes() const;
virtual QMimeData *mimeData(const QList<QTableWidgetItem *> items) const;
private slots:
void processCellChanged(int, int);
private:
bool m_insertEmptyRowAtEnd;
};
}
#endif // DRAGANDDROPTABLEWIDGET_H

71
general.pri Normal file
View File

@ -0,0 +1,71 @@
# template
TEMPLATE = lib
#dirs
UI_DIR = ./gui
MOC_DIR = ./moc
OBJECTS_DIR = ./obj
RCC_DIR = ./res
# compiler flags
QMAKE_CXXFLAGS += -std=c++11
QMAKE_LFLAGS += -std=c++11
unix {
QMAKE_LFLAGS += "-Wl,--rpath=./"
}
# prefix
targetprefix = .
# target
CONFIG(debug, debug|release) {
TARGET = $$targetprefix/$${projectname}d
} else {
TARGET = $$targetprefix/$$projectname
}
# variables to check target architecture
win32-g++:QMAKE_TARGET.arch = $$QMAKE_HOST.arch
win32-g++-32:QMAKE_TARGET.arch = x86
win32-g++-64:QMAKE_TARGET.arch = x86_64
linux-g++:QMAKE_TARGET.arch = $$QMAKE_HOST.arch
linux-g++-32:QMAKE_TARGET.arch = x86
linux-g++-64:QMAKE_TARGET.arch = x86_64
# configuration
mobile {
DEFINES += CONFIG_MOBILE
} else:desktop {
DEFINES += CONFIG_DESKTOP
} else:android {
CONFIG += mobile
DEFINES += CONFIG_MOBILE
} else {
CONFIG += desktop
DEFINES += CONFIG_DESKTOP
}
no-gui {
QT -= gui
DEFINES += GUI_NONE
guiqtquick || guiqtwidgets {
error("Can not use no-gui with guiqtquick or guiqtwidgets.")
} else {
message("Configured for no GUI support.")
}
} else {
QT += gui
mobile {
CONFIG += guiqtquick
}
desktop {
CONFIG += guiqtwidgets
}
}
guiqtquick {
message("Configured for Qt Quick GUI support.")
greaterThan(QT_MAJOR_VERSION, 4): QT += quick
CONFIG(debug, debug|release) {
CONFIG += qml_debug
}
DEFINES += GUI_QTQUICK
}
guiqtwidgets {
message("Configured for Qt widgets GUI support.")
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
DEFINES += GUI_QTWIDGETS
DEFINES += MODEL_UNDO_SUPPORT
}

16
gui/initiate.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef INITIATE_H
#define INITIATE_H
#include <QtGlobal>
QT_BEGIN_NAMESPACE
class QString;
QT_END_NAMESPACE
namespace QtGui {
int runWidgetsGui(int argc, char *argv[], const QString &file);
}
#endif // INITIATE_H

42
gui/initiatequi.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "initiate.h"
# include "gui/mainwindow.h"
#include <qtutilities/resources/resources.h>
#include <QTextCodec>
#include <QApplication>
#include <QFile>
namespace QtGui {
int runWidgetsGui(int argc, char *argv[], const QString &file)
{
// init application
QApplication a(argc, argv);
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
QGuiApplication::setOrganizationName(QStringLiteral("Martchus"));
QGuiApplication::setOrganizationDomain(QStringLiteral("http://martchus.netai.net/"));
QGuiApplication::setApplicationName(QStringLiteral("Password Manager"));
QGuiApplication::setApplicationVersion(QStringLiteral("2.0.5"));
// load translation files
TranslationFiles::loadQtTranslationFile();
TranslationFiles::loadApplicationTranslationFile(QStringLiteral("passwordmanager"));
// load the other resources
QtUtilitiesResources::init();
Theme::setup();
// init widgets GUI
QtGui::MainWindow w;
w.show();
if(!file.isEmpty()) {
w.openFile(file);
}
// start event loop
int res = a.exec();
// cleanup resources
QtUtilitiesResources::cleanup();
return res;
}
}

1171
gui/mainwindow.cpp Normal file

File diff suppressed because it is too large Load Diff

129
gui/mainwindow.h Normal file
View File

@ -0,0 +1,129 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include "passwordgeneratordialog.h"
#include <passwordfile/io/passwordfile.h>
#include <qtutilities/aboutdialog/aboutdialog.h>
#include <c++utilities/io/binaryreader.h>
#include <c++utilities/io/binarywriter.h>
#include <QMainWindow>
#include <QMap>
#include <memory>
#include <iostream>
#include <fstream>
QT_BEGIN_NAMESPACE
class QCloseEvent;
class QTreeWidgetItem;
class QUndoStack;
class QUndoView;
QT_END_NAMESPACE
namespace Io {
DECLARE_ENUM(EntryType, int)
DECLARE_ENUM(FieldType, int)
}
namespace QtGui {
class FieldModel;
class EntryModel;
class EntryFilterModel;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
// file management
bool openFile(const QString &path);
void createFile(const QString &path, const QString &password);
void createFile(const QString &path);
public slots:
// file management
bool createFile();
void changePassword();
bool saveFile();
void exportFile();
bool closeFile();
protected:
bool eventFilter(QObject *obj, QEvent *event);
void closeEvent(QCloseEvent *event);
void timerEvent(QTimerEvent *event);
private slots:
// showing dialogs
void showAboutDialog();
void showPassowrdGeneratorDialog();
void showOpenFileDialog();
void showSaveFileDialog();
void showUndoView();
// file management
bool showFile();
// account/categories management
void addAccount();
void addCategory();
void addEntry(Io::EntryType type);
void removeEntry();
void applyFilter(const QString &filterText);
// row management
void accountSelected(const QModelIndex &selected, const QModelIndex &);
void insertRow();
void removeRows();
void markAsPasswordField();
void markAsNormalField();
void setFieldType(Io::FieldType fieldType);
QString selectedFieldsString() const;
void insertFields(const QString &fieldsString);
void copyFieldsForXMilliSeconds(int x = 5000);
void copyFields();
void insertFieldsFromClipboard();
// showing context menus
void showTreeViewContextMenu();
void showTableViewContextMenu();
// recent entries menu
void addRecentEntry(const QString &path);
void openRecentFile();
void clearRecent();
// other
void showContainingDirectory();
void clearClipboard();
void setSomethingChanged();
private:
// showing conditional messages/prompts
bool askForCreatingFile();
bool showNoFileOpened();
bool showNoAccount();
// other
void updateUiStatus();
void applyDefaultExpanding(const QModelIndex &parent);
std::unique_ptr<Ui::MainWindow> m_ui;
Io::PasswordFile m_file;
FieldModel *m_fieldModel;
EntryModel *m_entryModel;
EntryFilterModel *m_entryFilterModel;
QUndoStack *m_undoStack;
QUndoView *m_undoView;
bool m_somethingChanged;
bool m_dontUpdateSelection;
int m_clearClipboardTimer;
};
}
#endif // MAINWINDOW_H

516
gui/mainwindow.ui Normal file
View File

@ -0,0 +1,516 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QtGui::MainWindow</class>
<widget class="QMainWindow" name="QtGui::MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>848</width>
<height>444</height>
</rect>
</property>
<property name="windowTitle">
<string>Password Manager</string>
</property>
<property name="windowIcon">
<iconset resource="../resources/icons.qrc">
<normaloff>:/icons/hicolor/128x128/apps/passwordmanager.png</normaloff>:/icons/hicolor/128x128/apps/passwordmanager.png</iconset>
</property>
<property name="locale">
<locale language="English" country="UnitedStates"/>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="mainWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="handleWidth">
<number>1</number>
</property>
<widget class="QWidget" name="leftWidget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="Widgets::ClearLineEdit" name="accountFilterLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>225</width>
<height>0</height>
</size>
</property>
<property name="placeholderText">
<string>filter</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="treeView">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="animated">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="rightWidget" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTableView" name="tableView">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::CopyAction</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>848</width>
<height>29</height>
</rect>
</property>
<widget class="QMenu" name="menuProgramm">
<property name="title">
<string>Fi&amp;le</string>
</property>
<widget class="QMenu" name="menuRecent">
<property name="title">
<string>&amp;Recent</string>
</property>
<property name="icon">
<iconset theme="document-open-recent">
<normaloff/>
</iconset>
</property>
<addaction name="actionSepRecent"/>
<addaction name="actionClearRecent"/>
</widget>
<addaction name="actionCreate"/>
<addaction name="actionOpen"/>
<addaction name="actionSave"/>
<addaction name="actionSaveAs"/>
<addaction name="actionExport"/>
<addaction name="actionShowContainingDirectory"/>
<addaction name="actionAlwaysCreateBackup"/>
<addaction name="separator"/>
<addaction name="actionClose"/>
<addaction name="actionChangepassword"/>
<addaction name="separator"/>
<addaction name="menuRecent"/>
<addaction name="actionQuit"/>
</widget>
<widget class="QMenu" name="menu">
<property name="title">
<string>?</string>
</property>
<addaction name="actionAbout"/>
</widget>
<widget class="QMenu" name="menuEdit">
<property name="title">
<string>Edit</string>
</property>
<addaction name="actionUndo"/>
<addaction name="actionRedo"/>
<addaction name="separator"/>
<addaction name="actionAddAccount"/>
<addaction name="actionAddCategory"/>
<addaction name="actionRemoveAccount"/>
<addaction name="separator"/>
<addaction name="actionInsertRow"/>
<addaction name="actionRemoveRows"/>
</widget>
<widget class="QMenu" name="menuTools">
<property name="title">
<string>&amp;Tools</string>
</property>
<addaction name="actionPasswordGenerator"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
<string>&amp;View</string>
</property>
<addaction name="actionHidePasswords"/>
<addaction name="actionShowUndoStack"/>
</widget>
<addaction name="menuProgramm"/>
<addaction name="menuEdit"/>
<addaction name="menuView"/>
<addaction name="menuTools"/>
<addaction name="menu"/>
</widget>
<widget class="QStatusBar" name="statusBar">
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
</widget>
<action name="actionOpen">
<property name="icon">
<iconset theme="document-open">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;Open ...</string>
</property>
<property name="shortcut">
<string>Ctrl+O</string>
</property>
</action>
<action name="actionQuit">
<property name="icon">
<iconset theme="application-exit">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;Quit</string>
</property>
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
</action>
<action name="actionAbout">
<property name="icon">
<iconset theme="help-about">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;About</string>
</property>
</action>
<action name="actionSave">
<property name="icon">
<iconset theme="document-save">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;Save</string>
</property>
<property name="shortcut">
<string>Ctrl+S</string>
</property>
</action>
<action name="actionCreate">
<property name="icon">
<iconset theme="document-new">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;New ...</string>
</property>
<property name="shortcut">
<string>Ctrl+N</string>
</property>
</action>
<action name="actionClose">
<property name="icon">
<iconset theme="window-close">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;Close</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+Q</string>
</property>
</action>
<action name="actionChangepassword">
<property name="icon">
<iconset theme="dialog-password">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Change &amp;password ...</string>
</property>
</action>
<action name="actionAddAccount">
<property name="icon">
<iconset theme="list-add">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;Add new account</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+A</string>
</property>
</action>
<action name="actionSaveAs">
<property name="icon">
<iconset theme="document-save-as">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Sa&amp;ve as ...</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+S</string>
</property>
</action>
<action name="actionRemoveAccount">
<property name="icon">
<iconset theme="list-remove">
<normaloff/>
</iconset>
</property>
<property name="iconText">
<string>Remove selected entry</string>
</property>
<property name="toolTip">
<string>Remove selected entry</string>
</property>
</action>
<action name="actionInsertRow">
<property name="icon">
<iconset theme="insert-text">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;Insert field</string>
</property>
</action>
<action name="actionRemoveRows">
<property name="icon">
<iconset theme="list-remove">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Remove selected &amp;field(s)</string>
</property>
</action>
<action name="actionPasswordGenerator">
<property name="text">
<string>&amp;Password generator</string>
</property>
</action>
<action name="actionClearRecent">
<property name="icon">
<iconset theme="edit-clear">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;Clear list</string>
</property>
</action>
<action name="actionAlwaysCreateBackup">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Always create backup</string>
</property>
</action>
<action name="actionExport">
<property name="icon">
<iconset theme="document-export">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;Export ...</string>
</property>
</action>
<action name="actionShowContainingDirectory">
<property name="icon">
<iconset theme="folder">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Show containing &amp;directory</string>
</property>
</action>
<action name="actionUndo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset theme="edit-undo">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;Undo</string>
</property>
</action>
<action name="actionRedo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset theme="edit-redo">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>&amp;Redo</string>
</property>
</action>
<action name="actionAddCategory">
<property name="icon">
<iconset theme="list-add">
<normaloff/>
</iconset>
</property>
<property name="text">
<string>Add &amp;new category</string>
</property>
</action>
<action name="actionShowUndoStack">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Show undo stack</string>
</property>
</action>
<action name="actionHidePasswords">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Hide passwords</string>
</property>
</action>
<action name="actionSepRecent"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>Widgets::ClearLineEdit</class>
<extends>QLineEdit</extends>
<header location="global">qtutilities/widgets/clearlineedit.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../resources/icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,169 @@
#include "passwordgeneratordialog.h"
#include "ui_passwordgeneratordialog.h"
#include <passwordfile/io/cryptoexception.h>
#include <c++utilities/conversion/binaryconversion.h>
#include <openssl/rand.h>
#include <QMessageBox>
#include <QClipboard>
#include <string>
#include <sstream>
#include <algorithm>
#include <random>
using namespace std;
using namespace Io;
using namespace Util;
namespace QtGui {
const char smallLetters[] = {'a','b','c','d','e','f',
'g','h','i','j','k',
'l','m','n','o','p',
'q','r','s','t','u',
'v','w','x','y','z'};
const char capitalLetters[] = {'A','B','C','D','E','F',
'G','H','I','J','K',
'L','M','N','O','P',
'Q','R','S','T','U',
'V','W','X','Y','Z'};
const char digits[] = {'0','1','2','3','4',
'5','6','7','8','9'};
/*!
* \class PasswordGeneratorDialog
* \brief The PasswordGeneratorDialog class provides a password generation dialog.
*/
/*!
* \brief Constructs a new password generator dialog.
*/
PasswordGeneratorDialog::PasswordGeneratorDialog(QWidget *parent) :
QDialog(parent),
m_ui(new Ui::PasswordGeneratorDialog)
{
m_ui->setupUi(this);
#ifdef Q_OS_WIN32
setStyleSheet(QStringLiteral("* { font: 9pt \"Segoe UI\", \"Sans\"; } #mainFrame { border: none; background: white; } #bottomFrame { background-color: #F0F0F0; border-top: 1px solid #DFDFDF; } #mainFrame #captionNameLabel, QMessageBox QLabel, QCommandLinkButton { font-size: 12pt; color: #003399; font-weight: normal; } #atLeastOneOfEachCategoryFrame { border-top: 1px solid #eee; }"));
#else
setStyleSheet(QStringLiteral("#mainFrame #captionNameLabel { font-weight: bold; }"));
#endif
setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
connect(m_ui->copyPasswordCommandLinkButton, SIGNAL(clicked()), this, SLOT(copyPassword()));
connect(m_ui->generatePassowordCommandLinkButton, SIGNAL(clicked()), this, SLOT(generateNewPassword()));
connect(m_ui->useCapitalLettersCheckBox, SIGNAL(stateChanged(int)), this, SLOT(handleCheckedCategoriesChanged()));
connect(m_ui->useDigitsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(handleCheckedCategoriesChanged()));
connect(m_ui->otherCharsLineEdit, SIGNAL(editingFinished()), this, SLOT(handleCheckedCategoriesChanged()));
connect(m_ui->useSmallLettersCheckBox, SIGNAL(stateChanged(int)), this, SLOT(handleCheckedCategoriesChanged()));
connect(m_ui->passwordLineEdit, SIGNAL(textChanged(QString)), this, SLOT(handlePasswordChanged()));
handlePasswordChanged();
}
/*!
* \brief Destroys the dialog.
*/
PasswordGeneratorDialog::~PasswordGeneratorDialog()
{
delete m_ui;
}
/*!
* \brief Generates and shows a new password.
*/
void PasswordGeneratorDialog::generateNewPassword()
{
int length = m_ui->LengthSpinBox->value();
if(length > 0) {
if(m_charset.empty()) {
bool useSmallLetters = m_ui->useSmallLettersCheckBox->isChecked();
bool useCapitalLetters = m_ui->useCapitalLettersCheckBox->isChecked();
bool useDigits = m_ui->useDigitsCheckBox->isChecked();
QString otherChars = m_ui->otherCharsLineEdit->text();
int charsetSize = otherChars.length();
if(useSmallLetters) {
charsetSize += sizeof(smallLetters);
}
if(useCapitalLetters) {
charsetSize += sizeof(capitalLetters);
}
if(useDigits) {
charsetSize += sizeof(digits);
}
m_charset.reserve(charsetSize);
if(useSmallLetters) {
m_charset.insert(m_charset.end(), std::begin(smallLetters), std::end(smallLetters));
}
if(useCapitalLetters) {
m_charset.insert(m_charset.end(), std::begin(capitalLetters), std::end(capitalLetters));
}
if(useDigits) {
m_charset.insert(m_charset.end(), std::begin(digits), std::end(digits));
}
char charval;
foreach(QChar qchar, otherChars) {
charval = qchar.toLatin1();
if(charval != '\x00' && charval != ' ' && std::find(m_charset.begin(), m_charset.end(), charval) == m_charset.end()) {
m_charset.push_back(charval);
}
}
}
if(!m_charset.empty()) {
try {
default_random_engine rng(m_random());
uniform_int_distribution<> dist(0, m_charset.size() - 1);
auto randchar = [this, &dist, &rng]() {
return m_charset[dist(rng)];
};
string res(length, 0);
generate_n(res.begin(), length, randchar);
m_ui->passwordLineEdit->setText(QString::fromLatin1(res.c_str()));
} catch(const CryptoException &ex) {
QMessageBox::warning(this, QApplication::applicationName(), tr("Failed to generate password.\nOpenSSL error: %1").arg(QString::fromLocal8Bit(ex.what())));
}
} else {
QMessageBox::warning(this, QApplication::applicationName(), tr("You have to select at least one checkbox."));
}
} else {
QMessageBox::warning(this, QApplication::applicationName(), tr("The length has to be at least one."));
}
}
/*!
* \brief Handles when the user checked or unchecked a category.
*/
void PasswordGeneratorDialog::handleCheckedCategoriesChanged()
{
m_ui->generatePassowordCommandLinkButton->setEnabled(m_ui->useCapitalLettersCheckBox->isChecked()
|| m_ui->useDigitsCheckBox->isChecked()
|| m_ui->useSmallLettersCheckBox->isChecked()
|| !m_ui->otherCharsLineEdit->text().isEmpty());
m_charset.clear();
}
/*!
* \brief Handles when the password changed.
*/
void PasswordGeneratorDialog::handlePasswordChanged()
{
m_ui->copyPasswordCommandLinkButton->setEnabled(m_ui->passwordLineEdit->text().count() > 0);
}
/*!
* \brief Copies the current password to the clipboard.
*/
void PasswordGeneratorDialog::copyPassword()
{
QClipboard *cb = QApplication::clipboard();
cb->setText(m_ui->passwordLineEdit->text());
}
}

View File

@ -0,0 +1,38 @@
#ifndef PASSWORDGENERATORDIALOG_H
#define PASSWORDGENERATORDIALOG_H
#include <passwordfile/util/opensslrandomdevice.h>
#include <QDialog>
#include <vector>
namespace QtGui {
namespace Ui {
class PasswordGeneratorDialog;
}
class PasswordGeneratorDialog : public QDialog
{
Q_OBJECT
public:
explicit PasswordGeneratorDialog(QWidget *parent = 0);
~PasswordGeneratorDialog();
private slots:
void generateNewPassword();
void handleCheckedCategoriesChanged();
void handlePasswordChanged();
void copyPassword();
private:
Ui::PasswordGeneratorDialog *m_ui;
std::vector<char> m_charset;
Util::OpenSslRandomDevice m_random;
};
}
#endif // PASSWORDGENERATORDIALOG_H

View File

@ -0,0 +1,349 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QtGui::PasswordGeneratorDialog</class>
<widget class="QDialog" name="QtGui::PasswordGeneratorDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>512</width>
<height>350</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>43</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>512</width>
<height>350</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>512</width>
<height>350</height>
</size>
</property>
<property name="windowTitle">
<string>Password Generator</string>
</property>
<property name="windowIcon">
<iconset resource="../resources/icons.qrc">
<normaloff>:/icons/hicolor/128x128/apps/passwordmanager.png</normaloff>:/icons/hicolor/128x128/apps/passwordmanager.png</iconset>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="locale">
<locale language="English" country="UnitedStates"/>
</property>
<property name="modal">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="mainFrame">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="informationFrame">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QFrame" name="checkboxFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="useSmallLettersCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Use small letters</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="useCapitalLettersCheckBox">
<property name="text">
<string>Use capital letters</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="useDigitsCheckBox">
<property name="text">
<string>Use digits</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="lengthFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="horizontalSpacing">
<number>9</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="lengtLabel">
<property name="text">
<string>Length</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Other characters to be used</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="otherCharsLineEdit">
<property name="text">
<string>!&quot;§$%&amp;/()=?;:_'*~#,.-+&lt;&gt;|</string>
</property>
<property name="maxLength">
<number>50</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="LengthSpinBox">
<property name="minimum">
<number>4</number>
</property>
<property name="maximum">
<number>256</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="passwordFrame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QFormLayout" name="formLayout_2">
<property name="topMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="passwordLabel">
<property name="text">
<string>&lt;b&gt;Password:&lt;/b&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="passwordLineEdit"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCommandLinkButton" name="generatePassowordCommandLinkButton">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>40</height>
</size>
</property>
<property name="text">
<string>Generate new password</string>
</property>
<property name="description">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QCommandLinkButton" name="copyPasswordCommandLinkButton">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>40</height>
</size>
</property>
<property name="text">
<string>Copy password</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="bottomFrame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>216</width>
<height>14</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="closePushbutton">
<property name="font">
<font>
<family>Segoe UI,Sans</family>
<pointsize>9</pointsize>
<weight>50</weight>
<italic>false</italic>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>Close</string>
</property>
<property name="icon">
<iconset theme="window-close">
<normaloff/>
</iconset>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../resources/icons.qrc"/>
</resources>
<connections>
<connection>
<sender>closePushbutton</sender>
<signal>clicked()</signal>
<receiver>QtGui::PasswordGeneratorDialog</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>464</x>
<y>278</y>
</hint>
<hint type="destinationlabel">
<x>255</x>
<y>149</y>
</hint>
</hints>
</connection>
</connections>
</ui>

17
gui/stacksupport.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "stacksupport.h"
namespace QtGui {
/*!
* \class StackSupport
* \brief The StackSupport class is used as base class for models supporting undoing of changes via QUndoStack.
*/
/*!
* \brief Constructs a new stack support with the specified \a undoStack.
*/
StackSupport::StackSupport(QUndoStack *undoStack) :
m_undoStack(undoStack)
{}
}

104
gui/stacksupport.h Normal file
View File

@ -0,0 +1,104 @@
#ifndef QTGUI_STACKSUPPORT_H
#define QTGUI_STACKSUPPORT_H
#include "undocommands.h"
#include <QUndoStack>
namespace QtGui {
class StackAbsorper;
class StackSupport
{
friend class StackAbsorper;
public:
StackSupport(QUndoStack *undoStack = nullptr);
protected:
QUndoStack *undoStack();
bool push(CustomUndoCommand *command);
void clearUndoStack();
private:
QUndoStack *m_undoStack;
};
/*!
* \brief Returns the undo stack for the current instance.
*/
inline QUndoStack *StackSupport::undoStack()
{
return m_undoStack;
}
/*!
* \brief Pushes the specified custom undo \a command to the undo stack and returns whether the redo action was successful.
*/
inline bool StackSupport::push(CustomUndoCommand *command)
{
if(m_undoStack) {
if(command->isNoop()) {
return true; // doing nothing can never fail
} else {
m_undoStack->push(command);
return command->redoResult();
}
}
return false;
}
/*!
* \brief Clears the undo stack.
*/
inline void StackSupport::clearUndoStack()
{
if(m_undoStack) {
m_undoStack->clear();
}
}
/*!
* \brief The StackAbsorper class is used by the CustomUndoCommand class to prevent infinite recursion when pushing
* a new command to the stack.
*/
class StackAbsorper
{
public:
StackAbsorper(StackSupport *supported);
~StackAbsorper();
QUndoStack *stack();
private:
StackSupport *m_supported;
QUndoStack *m_stack;
};
/*!
* \brief Detaches the undo stack from the specified stack support temporary.
*/
inline StackAbsorper::StackAbsorper(StackSupport *supported) :
m_supported(supported),
m_stack(supported->m_undoStack)
{
m_supported->m_undoStack = nullptr;
}
/*!
* \brief Restores the undo stack of the stack support.
*/
inline StackAbsorper::~StackAbsorper()
{
m_supported->m_undoStack = m_stack;
}
/*!
* \brief Returns the stack for the current instance.
*/
inline QUndoStack *StackAbsorper::stack()
{
return m_stack;
}
}
#endif // QTGUI_STACKSUPPORT_H

458
gui/undocommands.cpp Normal file
View File

@ -0,0 +1,458 @@
#include "undocommands.h"
#include "stacksupport.h"
#include "model/fieldmodel.h"
#include "model/entrymodel.h"
#include <passwordfile/io/entry.h>
#include <QApplication>
using namespace std;
using namespace Io;
namespace QtGui {
/*!
* \class CustomUndoCommand
* \brief The CustomUndoCommand class acts as base class for undo commands used within the
* models of the application.
* \sa http://qt-project.org/doc/qt-5/qundocommand.html
*/
/*!
* \brief Constructs a new custom undo command with the specified \a stackSupport.
*/
CustomUndoCommand::CustomUndoCommand(StackSupport *stackSupport) :
m_stackSupport(stackSupport),
m_redoResult(false),
m_undoResult(true),
m_noop(false)
{}
void CustomUndoCommand::redo()
{
if(m_undoResult) {
StackAbsorper stackAbsorper(m_stackSupport);
m_redoResult = internalRedo();
}
}
void CustomUndoCommand::undo()
{
if(m_redoResult) {
StackAbsorper stackAbsorper(m_stackSupport);
m_undoResult = internalUndo();
}
}
/*!
* \fn CustomUndoCommand::internalRedo()
* \brief This method is internally called to perform the redo action.
*/
/*!
* \fn CustomUndoCommand::internalUndo()
* \brief This method is internally called to perform the undo action.
*/
/*!
* \class FieldModelSetValueCommand
* \brief Sets the value for the specified index and role in the specified field model.
*/
/*!
* \brief Constructs a new command.
*/
FieldModelSetValueCommand::FieldModelSetValueCommand(FieldModel *model, const QModelIndex &index, const QVariant &value, int role) :
CustomUndoCommand(model),
m_account(model->accountEntry()),
m_model(model),
m_row(index.row()),
m_col(index.column()),
m_newValue(value),
m_oldValue(model->data(index, role)),
m_role(role)
{
QString fieldName = model->index(m_row, 0, index.parent()).data().toString();
switch(role) {
case Qt::DisplayRole:
case Qt::EditRole:
switch(m_col) {
case 0:
if(m_oldValue.toString().isEmpty()) {
setText(QApplication::translate("undocommands", "setting field name to »%1«").arg(m_newValue.toString()));
} else {
setText(QApplication::translate("undocommands", "setting field name »%1« to »%2«").arg(m_oldValue.toString(), m_newValue.toString()));
}
break;
case 1:
if(fieldName.isEmpty()) {
setText(QApplication::translate("undocommands", "setting value of empty field"));
} else {
setText(QApplication::translate("undocommands", "setting value of »%1« field").arg(fieldName));
}
break;
}
break;
case FieldTypeRole:
setText(QApplication::translate("undocommands", "setting type of »%1« field").arg(fieldName));
break;
default:
setText(QApplication::translate("undocommands", "setting field property in row »%1«").arg(m_row + 1));
}
setNoop(m_oldValue == m_newValue);
}
bool FieldModelSetValueCommand::internalRedo()
{
m_model->setAccountEntry(m_account);
return m_model->setData(m_model->index(m_row, m_col), m_newValue, m_role);
}
bool FieldModelSetValueCommand::internalUndo()
{
m_model->setAccountEntry(m_account);
return m_model->setData(m_model->index(m_row, m_col), m_oldValue, m_role);
}
/*!
* \class FieldModelInsertRowsCommand
* \brief Inserts the specified number of rows before the specified row in the specified field model.
*/
/*!
* \brief Constructs a new command.
*/
FieldModelInsertRowsCommand::FieldModelInsertRowsCommand(FieldModel *model, int row, int count) :
CustomUndoCommand(model),
m_account(model->accountEntry()),
m_model(model),
m_row(row),
m_count(count)
{
setText(QApplication::translate("undocommands", "insertion of %1 row(s) before row %2", 0, count).arg(count).arg(row + 1));
}
bool FieldModelInsertRowsCommand::internalRedo()
{
m_model->setAccountEntry(m_account);
return m_model->insertRows(m_row, m_count, QModelIndex());
}
bool FieldModelInsertRowsCommand::internalUndo()
{
m_model->setAccountEntry(m_account);
return m_model->removeRows(m_row, m_count, QModelIndex());
}
/*!
* \class FieldModelRemoveRowsCommand
* \brief Removes the specified number of rows at the specified row in the specified field model.
*/
/*!
* \brief Constructs a new command.
*/
FieldModelRemoveRowsCommand::FieldModelRemoveRowsCommand(FieldModel *model, int row, int count) :
CustomUndoCommand(model),
m_account(model->accountEntry()),
m_model(model),
m_row(row),
m_count(count)
{
if(count == 1) {
setText(QApplication::translate("undocommands", "removal of row %1", 0, count).arg(row + 1));
} else {
setText(QApplication::translate("undocommands", "removal of the rows %1 to %2", 0, count).arg(row + 1).arg(row + count));
}
}
bool FieldModelRemoveRowsCommand::internalRedo()
{
m_model->setAccountEntry(m_account);
if(m_values.isEmpty()) {
for(int row = m_row, end = m_row + m_count; row < end; ++row) {
if(const Field *field = m_model->field(row)) {
m_values << Field(*field);
}
}
}
return m_model->removeRows(m_row, m_count, QModelIndex());
}
bool FieldModelRemoveRowsCommand::internalUndo()
{
m_model->setAccountEntry(m_account);
bool res = m_model->insertRows(m_row, m_count, QModelIndex());
for(int row = m_row, end = m_row + m_count, value = 0, values = m_values.size(); row < end && value < values; ++row, ++value) {
m_model->setData(m_model->index(row, 0), QString::fromStdString(m_values.at(value).name()), Qt::EditRole);
m_model->setData(m_model->index(row, 1), QString::fromStdString(m_values.at(value).value()), Qt::EditRole);
m_model->setData(m_model->index(row, 0), static_cast<int>(m_values.at(value).type()), FieldTypeRole);
}
return res;
}
/*!
* \brief Stores the entry path for the specified \a model and \a index in \a res.
*/
void indexToPath(EntryModel *model, const QModelIndex &index, list<string> &res)
{
res.clear();
if(Entry *entry = model->entry(index)) {
entry->path(res);
}
}
/*!
* \brief Fetches the entry for the specified \a model and \a path.
* \remarks The \a path will be modified. To prevent this use entryFromPathCpy().
*/
Entry *entryFromPath(EntryModel *model, list<string> &path)
{
if(NodeEntry *rootEntry = model->rootEntry()) {
return rootEntry->entryByPath(path);
}
return nullptr;
}
/*!
* \brief Fetches the entry for the specified \a model and \a path.
*/
Entry *entryFromPathCpy(EntryModel *model, list<string> path)
{
return entryFromPath(model, path);
}
/*!
* \class EntryModelSetValueCommand
* \brief Sets the value for the specified index and role in the specified entry model.
*/
/*!
* \brief Constructs a new command.
*/
EntryModelSetValueCommand::EntryModelSetValueCommand(EntryModel *model, const QModelIndex &index, const QVariant &value, int role) :
CustomUndoCommand(model),
m_model(model),
m_newValue(value),
m_oldValue(model->data(index, role)),
m_role(role)
{
indexToPath(model, index, m_path);
switch(role) {
case Qt::DisplayRole:
case Qt::EditRole:
if(m_oldValue.toString().isEmpty()) {
setText(QApplication::translate("undocommands", "setting entry name to »%1«").arg(m_newValue.toString()));
} else {
setText(QApplication::translate("undocommands", "setting entry name from »%1« to »%2«").arg(m_oldValue.toString(), m_newValue.toString()));
}
break;
default:
QString name = model->data(model->index(index.row(), 0, index.parent()), Qt::DisplayRole).toString();
if(name.isEmpty()) {
setText(QApplication::translate("undocommands", "setting property of an entry"));
} else {
setText(QApplication::translate("undocommands", "setting property of entry »%1«").arg(name));
}
}
setNoop(m_oldValue == m_newValue);
}
bool EntryModelSetValueCommand::internalRedo()
{
if(Entry *entry = entryFromPath(m_model, m_path)) {
bool res = m_model->setData(m_model->index(entry), m_newValue, m_role);
m_path.clear();
entry->path(m_path);
return res;
}
return false;
}
bool EntryModelSetValueCommand::internalUndo()
{
if(Entry *entry = entryFromPath(m_model, m_path)) {
bool res = m_model->setData(m_model->index(entry), m_oldValue, m_role);
m_path.clear();
entry->path(m_path);
return res;
}
return false;
}
/*!
* \class EntryModelInsertRowsCommand
* \brief Modifies the specified number of rows before the specified row in the specified entry model under the specified parent.
*/
/*!
* \brief Constructs a new command.
*/
EntryModelModifyRowsCommand::EntryModelModifyRowsCommand(EntryModel *model, int row, int count, const QModelIndex &parent) :
CustomUndoCommand(model),
m_model(model),
m_row(row),
m_count(count)
{
indexToPath(model, parent, m_parentPath);
}
/*!
* \brief Destroys the command.
*
* Removed entries will be deleted finally.
*/
EntryModelModifyRowsCommand::~EntryModelModifyRowsCommand()
{
qDeleteAll(m_values);
}
/*!
* \brief Inserts the buffered entries to the model.
*/
bool EntryModelModifyRowsCommand::insert()
{
if(Entry *parentEntry = entryFromPathCpy(m_model, m_parentPath)) {
if(m_model->insertEntries(m_row, m_model->index(parentEntry), m_values)) {
m_values.clear();
return true;
}
}
return false;
}
/*!
* \brief Removes the entries from the model.
*
* The entries and the model have been specified when constructing the class.
*
* The removed entries are buffered.
*/
bool EntryModelModifyRowsCommand::remove()
{
if(Entry *parentEntry = entryFromPathCpy(m_model, m_parentPath)) {
m_values = m_model->takeEntries(m_row, m_count, m_model->index(parentEntry));
return !m_values.isEmpty();
}
return false;
}
/*!
* \class EntryModelInsertRowsCommand
* \brief Inserts the specified number of rows before the specified row in the specified entry model under the specified parent.
*/
/*!
* \brief Constructs a new command.
*/
EntryModelInsertRowsCommand::EntryModelInsertRowsCommand(EntryModel *model, int row, int count, const QModelIndex &parent) :
EntryModelModifyRowsCommand(model, row, count, parent)
{
setText(QApplication::translate("undocommands", "insertion of %1 entry/entries", 0, count).arg(count));
switch(m_model->insertType()) {
case EntryType::Account:
m_values << new AccountEntry;
break;
case EntryType::Node:
m_values << new NodeEntry;
break;
}
}
bool EntryModelInsertRowsCommand::internalRedo()
{
return insert();
}
bool EntryModelInsertRowsCommand::internalUndo()
{
return remove();
}
/*!
* \class EntryModelRemoveRowsCommand
* \brief Removes the specified number of rows at the specified row in the specified entry model under the specified parent.
*/
/*!
* \brief Constructs a new command.
*/
EntryModelRemoveRowsCommand::EntryModelRemoveRowsCommand(EntryModel *model, int row, int count, const QModelIndex &parent) :
EntryModelModifyRowsCommand(model, row, count, parent)
{
setText(QApplication::translate("undocommands", "removal of %1 entry/entries", 0, count).arg(count));
}
bool EntryModelRemoveRowsCommand::internalRedo()
{
return remove();
}
bool EntryModelRemoveRowsCommand::internalUndo()
{
return insert();
}
/*!
* \class EntryModelMoveRowsCommand
* \brief Moves the specified rows to the specified destination within the specified entry model.
*/
/*!
* \brief Constructs a new command.
*/
EntryModelMoveRowsCommand::EntryModelMoveRowsCommand(EntryModel *model, const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) :
CustomUndoCommand(model),
m_model(model),
m_sourceRow(sourceRow),
m_count(count),
m_destChild(destinationChild)
{
indexToPath(model, sourceParent, m_sourceParentPath);
indexToPath(model, destinationParent, m_destParentPath);
setText(QApplication::translate("undocommands", "move of %1 entry/entries", 0, count).arg(count));
}
bool EntryModelMoveRowsCommand::internalRedo()
{
if(m_count) {
Entry *sourceParentEntry = entryFromPathCpy(m_model, m_sourceParentPath);
Entry *destParentEntry = entryFromPathCpy(m_model, m_destParentPath);
if(sourceParentEntry && destParentEntry) {
return m_model->moveRows(m_model->index(sourceParentEntry), m_sourceRow, m_count, m_model->index(destParentEntry), m_destChild);
}
return false;
}
return true;
}
bool EntryModelMoveRowsCommand::internalUndo()
{
if(m_count) {
Entry *sourceParentEntry = entryFromPathCpy(m_model, m_sourceParentPath);
Entry *destParentEntry = entryFromPathCpy(m_model, m_destParentPath);
if(sourceParentEntry && destParentEntry) {
int sourceRow = m_destChild;
int destChild = m_sourceRow;
// moves whithin the same parent needs special consideration
if(sourceParentEntry == destParentEntry) {
// move entry down
if(m_sourceRow < m_destChild) {
sourceRow -= m_count;
// move entry up
} else if(m_sourceRow > m_destChild) {
destChild += m_count;
// keep entry were it is
} else {
return true;
}
}
return m_model->moveRows(m_model->index(destParentEntry), sourceRow, m_count, m_model->index(sourceParentEntry), destChild);
}
return false;
}
return true;
}
}

194
gui/undocommands.h Normal file
View File

@ -0,0 +1,194 @@
#ifndef UNDOCOMMANDS_H
#define UNDOCOMMANDS_H
#include <passwordfile/io/field.h>
#include <QUndoCommand>
#include <QUndoStack>
#include <QList>
#include <QVariant>
QT_BEGIN_NAMESPACE
class QModelIndex;
QT_END_NAMESPACE
namespace Io {
class Entry;
class AccountEntry;
}
namespace QtGui {
class FieldModel;
class EntryModel;
class StackSupport;
class CustomUndoCommand : public QUndoCommand
{
public:
explicit CustomUndoCommand(StackSupport *stackSupport);
bool redoResult() const;
bool undoResult() const;
bool isNoop() const;
void redo();
void undo();
protected:
void setNoop(bool noop);
virtual bool internalRedo() = 0;
virtual bool internalUndo() = 0;
private:
StackSupport *m_stackSupport;
bool m_redoResult;
bool m_undoResult;
bool m_noop;
};
/*!
* \brief Returns whether the redo action was successful.
*/
inline bool CustomUndoCommand::redoResult() const
{
return m_redoResult;
}
/*!
* \brief Returns whether the undo action was successful.
*/
inline bool CustomUndoCommand::undoResult() const
{
return m_undoResult;
}
/*!
* \brief Returns whether this command does nothing, eg. the new value equals the old value.
*/
inline bool CustomUndoCommand::isNoop() const
{
return m_noop;
}
/*!
* \brief Sets whether this command does nothing.
*
* Meant to be called in the constructor when subclassing and - for example - the new value equals the old value.
*/
inline void CustomUndoCommand::setNoop(bool noop)
{
m_noop = noop;
}
class FieldModelSetValueCommand : public CustomUndoCommand
{
public:
explicit FieldModelSetValueCommand(FieldModel *model, const QModelIndex &index, const QVariant &value, int role);
protected:
bool internalRedo();
bool internalUndo();
private:
Io::AccountEntry *m_account;
FieldModel *m_model;
int m_row;
int m_col;
QVariant m_newValue;
QVariant m_oldValue;
int m_role;
};
class FieldModelInsertRowsCommand : public CustomUndoCommand
{
public:
explicit FieldModelInsertRowsCommand(FieldModel *model, int row, int count);
protected:
bool internalRedo();
bool internalUndo();
private:
Io::AccountEntry *m_account;
FieldModel *m_model;
int m_row;
int m_count;
};
class FieldModelRemoveRowsCommand : public CustomUndoCommand
{
public:
explicit FieldModelRemoveRowsCommand(FieldModel *model, int row, int count);
protected:
bool internalRedo();
bool internalUndo();
private:
Io::AccountEntry *m_account;
FieldModel *m_model;
int m_row;
int m_count;
QList<Io::Field> m_values;
};
class EntryModelSetValueCommand : public CustomUndoCommand
{
public:
explicit EntryModelSetValueCommand(EntryModel *model, const QModelIndex &index, const QVariant &value, int role);
protected:
bool internalRedo();
bool internalUndo();
private:
EntryModel *m_model;
std::list<std::string> m_path;
QVariant m_newValue;
QVariant m_oldValue;
int m_role;
};
class EntryModelModifyRowsCommand : public CustomUndoCommand
{
public:
~EntryModelModifyRowsCommand();
protected:
explicit EntryModelModifyRowsCommand(EntryModel *model, int row, int count, const QModelIndex &parent);
bool internalRedo() = 0;
bool internalUndo() = 0;
bool insert();
bool remove();
EntryModel *m_model;
std::list<std::string> m_parentPath;
int m_row;
int m_count;
QList<Io::Entry *> m_values;
};
class EntryModelInsertRowsCommand : public EntryModelModifyRowsCommand
{
public:
explicit EntryModelInsertRowsCommand(EntryModel *model, int row, int count, const QModelIndex &parent);
protected:
bool internalRedo();
bool internalUndo();
};
class EntryModelRemoveRowsCommand : public EntryModelModifyRowsCommand
{
public:
explicit EntryModelRemoveRowsCommand(EntryModel *model, int row, int count, const QModelIndex &parent);
protected:
bool internalRedo();
bool internalUndo();
};
class EntryModelMoveRowsCommand : public CustomUndoCommand
{
public:
explicit EntryModelMoveRowsCommand(EntryModel *model, const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild);
protected:
bool internalRedo();
bool internalUndo();
private:
EntryModel *m_model;
std::list<std::string> m_sourceParentPath;
int m_sourceRow;
int m_count;
std::list<std::string> m_destParentPath;
int m_destChild;
};
}
#endif // UNDOCOMMANDS_H

95
main.cpp Normal file
View File

@ -0,0 +1,95 @@
#include "cli/cli.h"
#ifdef GUI_QTWIDGETS
# include "gui/initiate.h"
#endif
#ifdef GUI_QTQUICK
# include "quickgui/initiate.h"
#endif
#include <passwordfile/util/openssl.h>
#include <c++utilities/application/argumentparser.h>
#include <c++utilities/application/failure.h>
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
# include <qtutilities/resources/qtconfigarguments.h>
#else
# include <c++utilities/application/fakeqtconfigarguments.h>
#endif
#include <QString>
#include <iostream>
using namespace std;
using namespace ApplicationUtilities;
using namespace Util;
int main(int argc, char *argv[])
{
// init open ssl
OpenSsl::init();
// setup argument parser
ArgumentParser parser;
parser.setIgnoreUnknownArguments(true);
// Qt configuration arguments
QT_CONFIG_ARGUMENTS qtConfigArgs;
// file argument
Argument fileArg("file", "f", "specifies the file to be opened (or created when using --modify)");
fileArg.setValueNames({"path"});
fileArg.setRequiredValueCount(1);
fileArg.setCombinable(true);
fileArg.setRequired(false);
fileArg.setImplicit(true);
// cli argument
Argument cliArg("interactive-cli", "i", "starts the interactive command line interface");
// help argument
HelpArgument helpArg(parser);
parser.setMainArguments({&fileArg, &helpArg, &(qtConfigArgs.qtWidgetsGuiArg()), &(qtConfigArgs.qtQuickGuiArg()), &cliArg});
// holds the application's return code
int res = 0;
// parse the specified arguments
try {
parser.parseArgs(argc, argv);
if(cliArg.isPresent()) {
Cli::InteractiveCli cli;
if(fileArg.isPresent() && fileArg.valueCount() == 1) {
cli.run(fileArg.value(0));
} else {
cli.run();
}
} else if(qtConfigArgs.areQtGuiArgsPresent()) {
// run Qt gui if no arguments, --qt-gui or --qt-quick-gui specified, a file might be specified
QString file;
if(fileArg.valueCount() > 0) {
file = QString::fromLocal8Bit(fileArg.value(0).c_str());
}
if(qtConfigArgs.qtWidgetsGuiArg().isPresent()) {
#ifdef GUI_QTWIDGETS
res = QtGui::runWidgetsGui(argc, argv, file);
#else
cout << "The application has not been built with Qt widgets support." << endl;
#endif
} else if(qtConfigArgs.qtQuickGuiArg().isPresent()) {
#ifdef GUI_QTQUICK
res = QtGui::runQuickGui(argc, argv);
#else
cout << "The application has not been built with Qt quick support." << endl;
#endif
} else {
#if defined(GUI_QTQUICK)
res = QtGui::runQuickGui(argc, argv);
#elif defined(GUI_QTWIDGETS)
res = QtGui::runWidgetsGui(argc, argv, file);
#else
cout << "See --help for usage." << endl;
#endif
}
}
} catch(Failure &ex) {
cout << "Unable to parse arguments. " << ex.what() << "\nSee --help for available commands." << endl;
}
// clean open ssl
OpenSsl::clean();
return res;
}

View File

@ -0,0 +1,32 @@
#include "entryfiltermodel.h"
#include "entrymodel.h"
namespace QtGui {
/*!
* \class EntryFilterModel
* \brief The EntryFilterModel class provides a QSortFilterProxyModel specialization for the EntryModel class.
*
* \sa http://qt-project.org/doc/qt-5/qsortfilterproxymodel.html
*/
/*!
* \brief Constructs a new filter entry model.
*/
EntryFilterModel::EntryFilterModel(QObject *parent) :
QSortFilterProxyModel(parent)
{}
bool EntryFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
if(QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)) {
return true;
}
QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent);
if(sourceModel()->hasChildren(sourceIndex)) {
return true;
}
return false;
}
}

20
model/entryfiltermodel.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef ENTRYFILTERMODEL_H
#define ENTRYFILTERMODEL_H
#include <QSortFilterProxyModel>
namespace QtGui {
class EntryFilterModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit EntryFilterModel(QObject *parent = nullptr);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
};
}
#endif // ENTRYFILTERMODEL_H

563
model/entrymodel.cpp Normal file
View File

@ -0,0 +1,563 @@
#include "entrymodel.h"
#ifdef MODEL_UNDO_SUPPORT
#include "gui/undocommands.h"
#endif
#include <passwordfile/io/entry.h>
#include <QIcon>
#include <QBuffer>
#include <QMimeData>
#include <QDebug>
#include <sstream>
using namespace std;
using namespace Io;
namespace QtGui {
/*!
* \class EntryModel
* \brief The EntryModel class provides a model interface for a hierarchy of Entry instances.
*
* If MODEL_UNDO_SUPPORT the model supports Qt's undo framework.
* \sa http://qt-project.org/doc/qt-5/qabstractitemmodel.html
* \sa http://qt-project.org/doc/qt-5/qundo.html
*/
/*!
* \brief Constructs a new entry model.
*/
EntryModel::EntryModel(QObject *parent) :
QAbstractItemModel(parent),
m_rootEntry(nullptr),
m_insertType(EntryType::Node)
{}
#ifdef MODEL_UNDO_SUPPORT
/*!
* \brief Constructs a new entry model with the specified \a undoStack.
*
* This constructor is only available when MODEL_UNDO_SUPPORT is defined.
*/
EntryModel::EntryModel(QUndoStack *undoStack, QObject *parent) :
QAbstractItemModel(parent),
StackSupport(undoStack),
m_rootEntry(nullptr),
m_insertType(EntryType::Node)
{}
#endif
/*!
* \brief Returns the Entry for the specified \a index of nullptr if the \a index is invalid.
*
* Modifications should be done using the common methods defined by the QAbstractItemModel class.
* This method is intended to be used only internally to implement the QAbstractItemModel interface
* or when implementing a QUndoCommand to support Qt's undo framework.
*/
Entry *EntryModel::entry(const QModelIndex &index)
{
if(index.isValid()) {
return static_cast<Entry *>(index.internalPointer());
} else {
return nullptr;
}
}
/*!
* \brief Removes \a count number of entries at the specified \a row within the specified \a parent.
* \returns Returns the removed entries (rather then deleting them).
*
* Modifications should be done using the common methods defined by the QAbstractItemModel class.
* This method is intended to be used only internally to implement the QAbstractItemModel interface
* or when implementing a QUndoCommand to support Qt's undo framework.
*/
QList<Entry *> EntryModel::takeEntries(int row, int count, const QModelIndex &parent)
{
QList<Entry *> res;
if(Entry *parentEntry = entry(parent)) {
if(parentEntry->type() == EntryType::Node) {
NodeEntry *parentNodeEntry = static_cast<NodeEntry *>(parentEntry);
int lastIndex = row + count - 1;
const vector<Entry *> &children = parentNodeEntry->children();
if(lastIndex < 0 || static_cast<size_t>(lastIndex) >= children.size()) {
lastIndex = children.size() - 1;
}
beginRemoveRows(parent, row, lastIndex);
for(int index = lastIndex; index >= row; --index) {
Entry *child = children.at(index);
child->setParent(nullptr);
res << child;
}
endRemoveRows();
}
}
return res;
}
/*!
* \brief Inserts the specified \a entries before the specified \a row within the specified \a parent.
*
* Modifications should be done using the common methods defined by the QAbstractItemModel class.
* This method is intended to be used only internally to implement the QAbstractItemModel interface
* or when implementing a QUndoCommand to support Qt's undo framework.
*/
bool EntryModel::insertEntries(int row, const QModelIndex &parent, const QList<Entry *> &entries)
{
if(entries.isEmpty()) {
return true;
}
if(Entry *parentEntry = entry(parent)) {
if(parentEntry->type() == EntryType::Node) {
NodeEntry *parentNodeEntry = static_cast<NodeEntry *>(parentEntry);
const vector<Entry *> &children = parentNodeEntry->children();
if(row < 0 || static_cast<size_t>(row) > children.size()) {
row = children.size();
}
beginInsertRows(parent, row, row + entries.size() - 1);
foreach(Entry *entry, entries) {
entry->setParent(parentNodeEntry, row);
++row;
}
endInsertRows();
return true;
}
}
return false;
}
QModelIndex EntryModel::index(int row, int column, const QModelIndex &parent) const
{
if(parent.isValid()) {
if(Entry *parentEntry = static_cast<Entry *>(parent.internalPointer())) {
switch(parentEntry->type()) {
case EntryType::Node: {
const std::vector<Entry *> &children = static_cast<NodeEntry *>(parentEntry)->children();
if(row >= 0 && static_cast<size_t>(row) < children.size()) {
return createIndex(row, column, children.at(row));
}
break;
} case EntryType::Account:
;
}
}
} else if(m_rootEntry && row == 0) {
return createIndex(row, column, m_rootEntry);
}
return QModelIndex();
}
/*!
* \brief Returns the index of the specified \a entry.
* \remarks It is up to the caller to ensure that \a entry is a child of the root element.
*/
QModelIndex EntryModel::index(Entry *entry) const
{
if(entry->parent()) {
return createIndex(entry->index(), 0, entry);
} else {
return createIndex(0, 0, m_rootEntry);
}
}
QModelIndex EntryModel::parent(const QModelIndex &child) const
{
if(child.isValid()) {
if(Entry *entry = static_cast<Entry *>(child.internalPointer())) {
NodeEntry *parent = entry->parent();
if(parent && (child.row() >= 0 && static_cast<size_t>(child.row()) < parent->children().size())) {
return createIndex(parent->index() > 0 ? parent->index() : 0, 0, parent);
}
}
}
return QModelIndex();
}
bool EntryModel::hasChildren(const QModelIndex &parent) const
{
if(parent.isValid()) {
if(Entry *entry = static_cast<Entry *>(parent.internalPointer())) {
if(entry->type() == EntryType::Node) {
return static_cast<NodeEntry *>(entry)->children().size();
}
}
} else {
return true;
}
return false;
}
/*!
* \brief Returns an indication whether the specified \a parent might have children.
* \remarks Only node entries might have childs.
*/
bool EntryModel::isNode(const QModelIndex &parent) const
{
if(parent.isValid()) {
if(Entry *entry = static_cast<Entry *>(parent.internalPointer())) {
return entry->type() == EntryType::Node;
}
}
return false;
}
QVariant EntryModel::data(const QModelIndex &index, int role) const
{
if(index.isValid()) {
if(Entry *entry = static_cast<Entry *>(index.internalPointer())) {
switch(role) {
case Qt::DisplayRole:
case Qt::EditRole:
switch(index.column()) {
case 0:
return QString::fromStdString(entry->label());
default:
;
}
break;
case Qt::DecorationRole:
if(index.column() == 0 && entry->type() == EntryType::Node) {
static const QVariant folderIcon = QIcon::fromTheme(QStringLiteral("folder"));
return folderIcon;
}
break;
case SerializedRole: {
stringstream ss(stringstream::in | stringstream::out | stringstream::binary);
ss.exceptions(std::stringstream::failbit | std::stringstream::badbit);
try {
entry->make(ss);
string string = ss.str();
return QByteArray(string.data(), string.size());
} catch(const ios_base::failure &) {
return false;
}
}
break;
case DefaultExpandedRole:
return entry->type() == EntryType::Node && static_cast<NodeEntry *>(entry)->isExpandedByDefault();
default:
;
}
}
}
return QVariant();
}
QMap<int, QVariant> EntryModel::itemData(const QModelIndex &index) const
{
QMap<int, QVariant> roles;
roles.insert(Qt::DisplayRole, data(index, Qt::DisplayRole));
roles.insert(SerializedRole, data(index, SerializedRole));
return roles;
}
bool EntryModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
#ifdef MODEL_UNDO_SUPPORT
if(undoStack()) {
return push(new EntryModelSetValueCommand(this, index, value, role));
}
#endif
if(index.isValid()) {
if(Entry *entry = static_cast<Entry *>(index.internalPointer())) {
switch(role) {
case Qt::DisplayRole:
case Qt::EditRole:
switch(index.column()) {
case 0:
entry->setLabel(value.toString().toStdString());
emit dataChanged(index, index, QVector<int>() << role);
return true;
default:
;
}
break;
case SerializedRole: {
NodeEntry *parent = entry->parent();
QModelIndex parentIndex = index.parent();
if(parent && parentIndex.isValid()) {
stringstream ss(stringstream::in | stringstream::out | stringstream::binary);
ss.exceptions(std::stringstream::failbit | std::stringstream::badbit);
QByteArray array = value.toByteArray();
if(array.size()) {
try {
ss.write(array.data(), array.size());
Entry *newEntry = Entry::parse(ss);
int row = entry->index();
beginRemoveRows(parentIndex, row, row);
delete entry;
endRemoveRows();
beginInsertRows(parentIndex, row, row);
newEntry->setParent(parent, row);
endInsertRows();
return true;
} catch(const ios_base::failure &) {}
}
}
}
break;
case DefaultExpandedRole:
switch(entry->type()) {
case EntryType::Account:
return false;
case EntryType::Node:
static_cast<NodeEntry *>(entry)->setExpandedByDefault(value.toBool());
emit dataChanged(index, index, QVector<int>() << role);
return true;
}
break;
default:
;
}
}
}
return false;
}
bool EntryModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)
{
for(QMap<int, QVariant>::ConstIterator it = roles.constBegin(); it != roles.constEnd(); ++it) {
setData(index, it.value(), it.key());
}
return true;
}
Qt::ItemFlags EntryModel::flags(const QModelIndex &index) const
{
if(isNode(index)) {
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
} else {
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
}
}
QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int role) const
{
switch(orientation) {
case Qt::Horizontal:
switch(role) {
case Qt::DisplayRole:
switch(section) {
case 0:
return tr("Name");
default:
;
}
break;
default:
;
}
break;
default:
;
}
return QVariant();
return QVariant();
}
int EntryModel::rowCount(const QModelIndex &parent) const
{
if(parent.isValid()) {
if(Entry *parentEntry = static_cast<Entry *>(parent.internalPointer())) {
switch(parentEntry->type()) {
case EntryType::Node:
return static_cast<NodeEntry *>(parentEntry)->children().size();
case EntryType::Account:
;
}
}
} else if(m_rootEntry) {
return 1;
}
return 0;
}
int EntryModel::columnCount(const QModelIndex &) const
{
return 1;
}
bool EntryModel::insertRows(int row, int count, const QModelIndex &parent)
{
#ifdef MODEL_UNDO_SUPPORT
if(undoStack()) {
return push(new EntryModelInsertRowsCommand(this, row, count, parent));
}
#endif
if(parent.isValid()) {
if(Entry *parentEntry = static_cast<Entry *>(parent.internalPointer())) {
if(parentEntry->type() == EntryType::Node) {
beginInsertRows(parent, row, row + count - 1);
for(int end = row + count; row < end; ++row) {
Entry *newEntry;
switch(m_insertType) {
case EntryType::Node:
newEntry = new NodeEntry;
break;
case EntryType::Account:
newEntry = new AccountEntry;
break;
default:
return false; // should never be reached, just to suppress compiler warning
}
newEntry->setParent(static_cast<NodeEntry *>(parentEntry), row);
}
endInsertRows();
return true;
}
}
}
return false;
}
bool EntryModel::removeRows(int row, int count, const QModelIndex &parent)
{
#ifdef MODEL_UNDO_SUPPORT
if(undoStack()) {
return push(new EntryModelRemoveRowsCommand(this, row, count, parent));
}
#endif
if(parent.isValid() && count > 0) {
if(Entry *parentEntry = static_cast<Entry *>(parent.internalPointer())) {
if(parentEntry->type() == EntryType::Node) {
beginRemoveRows(parent, row, row + count - 1);
static_cast<NodeEntry *>(parentEntry)->deleteChildren(row, row + count);
endRemoveRows();
return true;
}
}
}
return false;
}
bool EntryModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
{
#ifdef MODEL_UNDO_SUPPORT
if(undoStack()) {
return push(new EntryModelMoveRowsCommand(this, sourceParent, sourceRow, count, destinationParent, destinationChild));
}
#endif
// check validation of specified arguments
if(sourceParent.isValid() && destinationParent.isValid()
&& sourceRow >= 0 && count > 0
&& entry(sourceParent)->type() == EntryType::Node // source and destination parent entries
&& entry(destinationParent)->type() == EntryType::Node) { // need to be node entries
// determine the source parent entry and dest parent entry as node entries
NodeEntry *srcParentEntry = static_cast<NodeEntry *>(sourceParent.internalPointer());
NodeEntry *destParentEntry = static_cast<NodeEntry *>(destinationParent.internalPointer());
// source rows must be within the valid range
if(static_cast<size_t>(sourceRow + count) <= srcParentEntry->children().size()
// if source and destination parent are the same the destination child mustn't be in the source range
&& (srcParentEntry != destParentEntry || (destinationChild < sourceRow || (sourceRow + count) < destinationChild))) {
// do not move a row to one of its own children! -> check before
for(int index = 0; index < count; ++index) {
Entry *toMove = srcParentEntry->children().at(sourceRow + index);
if(toMove->type() == EntryType::Node) {
if(destParentEntry->isIndirectChildOf(static_cast<NodeEntry *>(toMove))) {
return false;
}
}
}
// actually perform the move operation
beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destinationParent, destinationChild);
for(int index = 0; index < count; ++index) {
Entry *toMove = srcParentEntry->children().at(sourceRow + index);
if(srcParentEntry == destParentEntry && sourceRow < destinationChild) {
toMove->setParent(destParentEntry, destinationChild + index - 1);
} else {
toMove->setParent(destParentEntry, destinationChild + index);
}
}
endMoveRows();
return true;
}
}
return false;
}
QStringList EntryModel::mimeTypes() const
{
return QStringList() << QStringLiteral("application/x-entrymodelpathlistmove") << QStringLiteral("text/plain");
}
QMimeData *EntryModel::mimeData(const QModelIndexList &indexes) const
{
if(indexes.count() <= 0) {
return nullptr;
}
QStringList types = mimeTypes();
if(types.isEmpty()) {
return nullptr;
}
QMimeData *data = new QMimeData();
QStringList plainTextParts;
QByteArray encoded;
QDataStream dataStream(&encoded, QIODevice::WriteOnly);
foreach(const QModelIndex &index, indexes) {
if(index.isValid()) {
Entry *entry = static_cast<Entry *>(index.internalPointer());
list<string> path = entry->path();
dataStream << static_cast<quint32>(path.size());
for(const string &part : path) {
dataStream << QString::fromStdString(part);
}
plainTextParts << QString::fromStdString(entry->label());
}
}
data->setData(types.at(0), encoded);
data->setText(plainTextParts.join(QStringLiteral(", ")));
return data;
}
bool EntryModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if(!m_rootEntry || !data || action != Qt::MoveAction) {
return false;
}
QStringList types = mimeTypes();
if(types.isEmpty()) {
return false;
}
QString format = types.at(0);
if(!data->hasFormat(format)) {
return false;
}
if(row > rowCount(parent) || row < 0) {
row = rowCount(parent);
}
if(column > columnCount(parent) || column < 0) {
column = 0;
}
// decode and insert
QByteArray encoded = data->data(format);
QDataStream stream(&encoded, QIODevice::ReadOnly);
int moved = 0;
while(!stream.atEnd()) {
quint32 size;
stream >> size;
list<string> path;
for(quint32 i = 0; i < size; ++i) {
QString part;
stream >> part;
path.push_back(part.toStdString());
}
if(Entry *entry = m_rootEntry->entryByPath(path, true)) {
if(NodeEntry *srcParentEntry = entry->parent()) {
if(moveRows(index(srcParentEntry), entry->index(), 1, parent, row)) {
++moved;
}
}
}
}
return false;
}
Qt::DropActions EntryModel::supportedDropActions() const
{
return Qt::MoveAction;
}
}

127
model/entrymodel.h Normal file
View File

@ -0,0 +1,127 @@
#ifndef ENTRYMODEL_H
#define ENTRYMODEL_H
#ifdef MODEL_UNDO_SUPPORT
#include "gui/stacksupport.h"
#endif
#include <c++utilities/application/global.h>
#include <QAbstractItemModel>
namespace Io {
class Entry;
class NodeEntry;
DECLARE_ENUM(EntryType, int)
}
namespace QtGui {
/*!
* \brief The EntryModelRoles enum defines custom roles for the EntryModel class.
*/
enum EntryModelRoles
{
SerializedRole = Qt::UserRole + 1, /**< the entry (including descendants) in serialized from (QByteArray) */
DefaultExpandedRole = Qt::UserRole + 2 /**< whether the entry should be expanded by default */
};
class EntryModel : public QAbstractItemModel
#ifdef MODEL_UNDO_SUPPORT
, public StackSupport
#endif
{
Q_OBJECT
public:
explicit EntryModel(QObject *parent = nullptr);
#ifdef MODEL_UNDO_SUPPORT
explicit EntryModel(QUndoStack *undoStack, QObject *parent = nullptr);
#endif
Io::NodeEntry *rootEntry();
void setRootEntry(Io::NodeEntry *entry);
Io::Entry *entry(const QModelIndex &index);
QList<Io::Entry *> takeEntries(int row, int count, const QModelIndex &parent);
bool insertEntries(int row, const QModelIndex &parent, const QList<Io::Entry *> &entries);
Io::EntryType insertType() const;
void setInsertType(Io::EntryType type);
bool hidePasswords() const;
QModelIndex index(int row, int column, const QModelIndex &parent) const;
QModelIndex index(Io::Entry *entry) const;
QModelIndex parent(const QModelIndex &child) const;
bool hasChildren(const QModelIndex &parent) const;
bool isNode(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
QMap<int, QVariant> itemData(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles);
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
bool insertRows(int row, int count, const QModelIndex &parent);
bool removeRows(int row, int count, const QModelIndex &parent);
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild);
QStringList mimeTypes() const;
QMimeData *mimeData(const QModelIndexList &indexes) const;
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
Qt::DropActions supportedDropActions() const;
public slots:
void reset();
private:
Io::NodeEntry *m_rootEntry;
Io::EntryType m_insertType;
};
/*!
* \brief Returns the root entry.
*/
inline Io::NodeEntry *EntryModel::rootEntry()
{
return m_rootEntry;
}
/*!
* \brief Sets the root entry. Causes a model reset. The undo stack will be cleard if MODEL_UNDO_SUPPORT is defined.
*/
inline void EntryModel::setRootEntry(Io::NodeEntry *entry)
{
if(m_rootEntry != entry) {
#ifdef MODEL_UNDO_SUPPORT
clearUndoStack();
#endif
beginResetModel();
m_rootEntry = entry;
endResetModel();
}
}
/*!
* \brief Resets the model. The root entry will be unset.
*/
inline void EntryModel::reset()
{
setRootEntry(nullptr);
}
/*!
* \brief Returns the entry type used when inserting new rows.
*/
inline Io::EntryType EntryModel::insertType() const
{
return m_insertType;
}
/*!
* \brief Sets the entry type used when inserting new rows.
*/
inline void EntryModel::setInsertType(Io::EntryType type)
{
m_insertType = type;
}
}
#endif // ENTRYMODEL_H

339
model/fieldmodel.cpp Normal file
View File

@ -0,0 +1,339 @@
#include "fieldmodel.h"
#ifdef MODEL_UNDO_SUPPORT
#include "gui/undocommands.h"
#endif
#include <passwordfile/io/field.h>
#include <QMimeData>
#include <QStringList>
using namespace std;
using namespace Io;
namespace QtGui {
/*!
* \class FieldModel
* \brief The FieldModel class provides a model interface for the fields of an AccountEntry.
*
* If MODEL_UNDO_SUPPORT the model supports Qt's undo framework.
* \sa http://qt-project.org/doc/qt-5/qabstracttablemodel.html
* \sa http://qt-project.org/doc/qt-5/qundo.html
*/
/*!
* \brief Constructs a new field model.
*/
FieldModel::FieldModel(QObject *parent) :
QAbstractTableModel(parent),
m_accountEntry(nullptr),
m_fields(nullptr),
m_hidePasswords(false)
{}
#ifdef MODEL_UNDO_SUPPORT
/*!
* \brief Constructs a new field model with the specified \a undoStack.
*
* This constructor is only available when MODEL_UNDO_SUPPORT is defined.
*/
FieldModel::FieldModel(QUndoStack *undoStack, QObject *parent) :
QAbstractTableModel(parent),
StackSupport(undoStack),
m_accountEntry(nullptr),
m_fields(nullptr)
{}
#endif
/*!
* \brief Sets the account entry. Causes a model reset.
*
* The \a entry might be nullptr.
* The caller keeps the ownership and should not destroy the \a entry as long it is assigned.
*/
void FieldModel::setAccountEntry(AccountEntry *entry)
{
if(entry != m_accountEntry) {
beginResetModel();
if((m_accountEntry = entry)) {
m_fields = &entry->fields();
} else {
m_fields = nullptr;
}
endResetModel();
}
}
QVariant FieldModel::data(const QModelIndex &index, int role) const
{
if(index.isValid() && m_fields && index.row() >= 0) {
// return data for existent field
if(static_cast<size_t>(index.row()) < m_fields->size()) {
switch(role) {
case Qt::DisplayRole:
case Qt::EditRole:
switch(index.column()) {
case 0:
return QString::fromStdString(m_fields->at(index.row()).name());
case 1:
if(role == Qt::DisplayRole
&& m_hidePasswords
&& m_fields->at(index.row()).type() == FieldType::Password) {
return QString(m_fields->at(index.row()).value().size(), QChar(0x2022));
} else {
return QString::fromStdString(m_fields->at(index.row()).value());
}
default:
;
}
break;
case FieldTypeRole:
return static_cast<int>(m_fields->at(index.row()).type());
default:
;
}
// return data for empty field at the end which enables the user to append fields
} else if(static_cast<size_t>(index.row()) == m_fields->size()) {
switch(role) {
case Qt::DisplayRole:
case Qt::EditRole:
switch(index.column()) {
case 0:
return QString();
case 1:
return QString();
default:
;
}
break;
default:
;
}
}
}
return QVariant();
}
QMap<int, QVariant> FieldModel::itemData(const QModelIndex &index) const
{
static int roles[] = {Qt::EditRole, FieldTypeRole};
QMap<int, QVariant> roleMap;
for(int role : roles) {
QVariant variantData = data(index, role);
if (variantData.isValid()) {
roleMap.insert(role, variantData);
}
}
return roleMap;
}
bool FieldModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
#if MODEL_UNDO_SUPPORT
if(undoStack()) {
return push(new FieldModelSetValueCommand(this, index, value, role));
}
#endif
QVector<int> roles;
if(index.isValid() && m_fields && index.row() >= 0) {
// set data for existing field
if(static_cast<size_t>(index.row()) < m_fields->size()) {
switch(role) {
case Qt::EditRole:
switch(index.column()) {
case 0:
m_fields->at(index.row()).setName(value.toString().toStdString());
roles << role;
break;
case 1:
m_fields->at(index.row()).setValue(value.toString().toStdString());
roles << role;
break;
default:
;
}
break;
case FieldTypeRole: {
bool ok;
int fieldType = value.toInt(&ok);
if(ok && Field::isValidType(fieldType)) {
roles << role;
m_fields->at(index.row()).setType(static_cast<FieldType>(fieldType));
}
break;
} default:
;
}
// remove last field if empty, showing an empty field at the end to enabled appending new rows is provided by the data method
if(!roles.isEmpty() && static_cast<size_t>(index.row()) == m_fields->size() - 1 && m_fields->at(index.row()).isEmpty()) {
beginRemoveRows(index.parent(), index.row(), index.row());
m_fields->pop_back();
endRemoveRows();
}
// set data for a new field emplaced at the end of the field list
} else if(static_cast<size_t>(index.row()) == m_fields->size() && !value.toString().isEmpty()) {
switch(role) {
case Qt::DisplayRole:
case Qt::EditRole:
switch(index.column()) {
case 0:
beginInsertRows(index.parent(), rowCount(), rowCount());
m_fields->emplace_back(m_accountEntry);
m_fields->back().setName(value.toString().toStdString());
endInsertRows();
roles << role;
break;
case 1:
beginInsertRows(index.parent(), rowCount(), rowCount());
m_fields->emplace_back(m_accountEntry);
m_fields->back().setValue(value.toString().toStdString());
endInsertRows();
roles << role;
break;
default:
;
}
break;
default:
;
}
}
}
// return false if nothing could be changed
if(roles.isEmpty()) {
return false;
} else {
// some roles affect other roles
switch(role) {
case Qt::EditRole:
roles << Qt::DisplayRole;
break;
case FieldTypeRole:
roles << Qt::DisplayRole << Qt::EditRole;
break;
default:
;
}
}
// emit data changed signal on sucess
emit dataChanged(index, index, roles);
return true;
}
Qt::ItemFlags FieldModel::flags(const QModelIndex &index) const
{
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}
QVariant FieldModel::headerData(int section, Qt::Orientation orientation, int role) const
{
switch(orientation) {
case Qt::Horizontal:
switch(role) {
case Qt::DisplayRole:
switch(section) {
case 0:
return tr("Name");
case 1:
return tr("Value");
default:
;
}
break;
default:
;
}
break;
default:
;
}
return QVariant();
}
int FieldModel::rowCount(const QModelIndex &parent) const
{
return (!parent.isValid() && m_fields) ? m_fields->size() + 1 : 0;
}
int FieldModel::columnCount(const QModelIndex &parent) const
{
return !parent.isValid() ? 2 : 0;
}
bool FieldModel::insertRows(int row, int count, const QModelIndex &parent)
{
#ifdef MODEL_UNDO_SUPPORT
if(undoStack()) {
return push(new FieldModelInsertRowsCommand(this, row, count));
}
#endif
if(!parent.isValid() && row >= 0 && count > 0 && static_cast<size_t>(row) <= m_fields->size()) {
beginInsertRows(parent, row, row + count - 1);
m_fields->insert(m_fields->begin() + row, count, Field(m_accountEntry));
endInsertRows();
return true;
}
return false;
}
bool FieldModel::removeRows(int row, int count, const QModelIndex &parent)
{
#ifdef MODEL_UNDO_SUPPORT
if(undoStack()) {
return push(new FieldModelRemoveRowsCommand(this, row, count));
}
#endif
if(!parent.isValid() && row >= 0 && count > 0 && static_cast<size_t>(row + count) <= m_fields->size()) {
beginRemoveRows(parent, row, row + count - 1);
m_fields->erase(m_fields->begin() + row, m_fields->begin() + row + count);
endRemoveRows();
return true;
}
return false;
}
bool FieldModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if(!QAbstractTableModel::dropMimeData(data, action, row, column, parent)) {
if(data->hasText()) {
return setData(parent, data->text(), Qt::EditRole);
}
}
return false;
}
QStringList FieldModel::mimeTypes() const
{
return QAbstractTableModel::mimeTypes() << QStringLiteral("text/plain");
}
QMimeData *FieldModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *data = QAbstractTableModel::mimeData(indexes);
if(!indexes.isEmpty()) {
QStringList result;
foreach(const QModelIndex &index, indexes) {
result << index.data(Qt::EditRole).toString();
}
data->setText(result.join(QChar('\n')));
}
return data;
}
/*!
* \brief Returns the field for the specified row.
*
* Might be nullptr if no account entry is assigned or if the row is out of range.
*/
const Field *FieldModel::field(size_t row) const
{
if(m_fields && row < m_fields->size()) {
return &m_fields->at(row);
}
return nullptr;
}
}

121
model/fieldmodel.h Normal file
View File

@ -0,0 +1,121 @@
#ifndef FIELDMODEL_H
#define FIELDMODEL_H
#include <passwordfile/io/entry.h>
#ifdef MODEL_UNDO_SUPPORT
#include "gui/stacksupport.h"
#endif
#include <QAbstractTableModel>
#include <vector>
namespace Io {
class Field;
}
namespace QtGui {
/*!
* \brief The FieldModelRoles enum defines custom roles for the FieldModel class.
*/
enum FieldModelRoles
{
FieldTypeRole = Qt::UserRole + 1 /**< the field type */
};
class FieldModel : public QAbstractTableModel
#ifdef MODEL_UNDO_SUPPORT
, public StackSupport
#endif
{
Q_OBJECT
public:
explicit FieldModel(QObject *parent = nullptr);
#ifdef MODEL_UNDO_SUPPORT
explicit FieldModel(QUndoStack *undoStack, QObject *parent = nullptr);
#endif
Io::AccountEntry *accountEntry();
const Io::AccountEntry *accountEntry() const;
void setAccountEntry(Io::AccountEntry *entry);
std::vector<Io::Field> *fields();
bool hidePasswords() const;
QVariant data(const QModelIndex &index, int role) const;
QMap<int, QVariant> itemData(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role);
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
bool insertRows(int row, int count, const QModelIndex &parent);
bool removeRows(int row, int count, const QModelIndex &parent);
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
QStringList mimeTypes() const;
QMimeData *mimeData(const QModelIndexList &indexes) const;
const Io::Field *field(std::size_t row) const;
public slots:
void setHidePasswords(bool hidePasswords);
void reset();
private:
Io::AccountEntry *m_accountEntry;
std::vector<Io::Field> *m_fields;
bool m_hidePasswords;
};
/*!
* \brief Returns the account entry. Might be nullptr if no account entry has been set.
*
* The ownership remains unaltered.
*/
inline Io::AccountEntry *FieldModel::accountEntry()
{
return m_accountEntry;
}
/*!
* \brief Returns the account entry. Might be nullptr if no account entry has been set.
*
* The ownership remains unaltered.
*/
inline const Io::AccountEntry *FieldModel::accountEntry() const
{
return m_accountEntry;
}
/*!
* \brief Returns the fields of the account entry. Might be nullptr if no account entry is assigned.
*
* The ownership remains unaltered.
*/
inline std::vector<Io::Field> *FieldModel::fields()
{
return m_fields;
}
/*!
* \brief Resets the model. The account entry will be unset.
*/
inline void FieldModel::reset()
{
setAccountEntry(nullptr);
}
inline bool FieldModel::hidePasswords() const
{
return m_hidePasswords;
}
inline void FieldModel::setHidePasswords(bool hidePasswords)
{
m_hidePasswords = hidePasswords;
if(m_fields) {
emit dataChanged(index(0, 1), index(m_fields->size() - 1, 1), QVector<int>() << Qt::DisplayRole);
}
}
}
#endif // FIELDMODEL_H

2310
passwordmanager.doxygen Normal file

File diff suppressed because it is too large Load Diff

116
passwordmanager.pro Normal file
View File

@ -0,0 +1,116 @@
projectname = passwordmanager
# include ../../common.pri when building as part of a subdirs project; otherwise include general.pri
!include(../../common.pri) {
!include(./general.pri) {
error("Couldn't find the common.pri or the general.pri file!")
}
}
TEMPLATE = app
no-gui {
CONFIG -= qt # Qt is only required for GUI
} else {
QT += core gui
android {
OTHER_FILES += android/AndroidManifest.xml
include(../../deployment.pri)
}
}
guiqtquick {
QML_IMPORT_PATH += ./quick ./qml ./qml/pages ./qml/touch
}
# files
SOURCES += main.cpp\
model/entrymodel.cpp \
model/fieldmodel.cpp \
model/entryfiltermodel.cpp \
util/testroutines.cpp \
cli/cli.cpp
guiqtwidgets {
SOURCES += gui/mainwindow.cpp \
gui/passwordgeneratordialog.cpp \
gui/undocommands.cpp \
gui/stacksupport.cpp \
gui/initiatequi.cpp
FORMS += gui/mainwindow.ui \
gui/passwordgeneratordialog.ui
}
guiqtquick {
SOURCES += quickgui/applicationinfo.cpp \
quickgui/initiatequick.cpp
}
HEADERS += model/entrymodel.h \
model/fieldmodel.h \
model/entryfiltermodel.h \
util/testroutines.h \
cli/cli.h
guiqtwidgets {
HEADERS += gui/mainwindow.h \
gui/passwordgeneratordialog.h \
gui/undocommands.h \
gui/stacksupport.h \
gui/initiate.h
}
guiqtquick {
HEADERS += quickgui/applicationinfo.h \
quickgui/applicationpaths.h
quickgui/initiate.h
}
# resources and translations
!no-gui {
RESOURCES += resources/icons.qrc
contains(DEFINES, GUI_QTQUICK) {
RESOURCES += resources/qml.qrc
}
TRANSLATIONS = translations/passwordmanager_en_US.ts \
translations/passwordmanager_de_DE.ts
}
updateqm.commands = lrelease $${projectname}.pro
updateqm.target = updateqm
QMAKE_EXTRA_TARGETS += updateqm
OTHER_FILES += \
pkgbuild/default/PKGBUILD \
pkgbuild/mingw-w64/PKGBUILD
# libs and includepath
CONFIG(debug, debug|release) {
LIBS += -L../../ -lc++utilitiesd -lpasswordfiled
!no-gui {
LIBS += -lqtutilitiesd
}
} else {
LIBS += -L../../ -lc++utilities -lpasswordfile
!no-gui {
LIBS += -lqtutilities
}
}
# requried when building in subdirs project
INCLUDEPATH += ../
# installs
target.path = $$(INSTALL_ROOT)/bin
INSTALLS += target
icon.path = $$(INSTALL_ROOT)/share/icons/hicolor/scalable/apps/
icon.files = ./resources/icons/hicolor/scalable/apps/$${projectname}.svg
INSTALLS += icon
menu.path = $$(INSTALL_ROOT)/share/applications/
menu.files = ./resources/desktop/applications/$${projectname}.desktop
INSTALLS += menu
translations.path = $$(INSTALL_ROOT)/share/$${projectname}/translations/
translations.files = ./translations/*.qm
INSTALLS += translations

23
pkgbuild/default/PKGBUILD Normal file
View File

@ -0,0 +1,23 @@
pkgname=passwordmanager
pkgver=2.0.5
pkgrel=2
arch=('i686' 'x86_64')
pkgdesc="A simple password store using AES-256-CBC encryption via OpenSSL."
license=('GPL')
depends=('qt5-base' 'qtutilities' 'passwordfile' 'openssl' 'libxkbcommon-x11')
makedepends=('qt5-tools')
install=${pkgname}.install
url="http://martchus.netai.net/"
source=("$pkgname.tar.gz")
md5sums=('d359927292464fcf41c8a11315ff79e2')
# head end
build() {
cd $srcdir/$pkgname
INSTALL_ROOT=$pkgdir/usr/ qmake-qt5 "$pkgname.pro" -r -spec linux-g++
make && make updateqm
}
package() {
cd $srcdir/$pkgname
make install
}

View File

@ -0,0 +1,11 @@
post_install() {
gtk-update-icon-cache -q -t -f /usr/share/icons/hicolor
}
post_upgrade() {
post_install
}
post_remove() {
post_install
}

View File

@ -0,0 +1,35 @@
_name=passwordmanager
pkgname=mingw-w64-$_name
pkgver=2.0.5
pkgrel=2
arch=('any')
pkgdesc="A simple password store using AES-256-CBC encryption via OpenSSL (mingw-w64)."
license=('GPL')
depends=('mingw-w64-crt' 'mingw-w64-qt5-base' 'mingw-w64-qtutilities' 'mingw-w64-passwordfile' 'mingw-w64-openssl')
makedepends=('mingw-w64-gcc')
url="http://martchus.netai.net/"
source=("${_name}.tar.gz")
md5sums=('4e94aff9225d8873f752995c1bcc5f15')
_architectures="i686-w64-mingw32 x86_64-w64-mingw32"
# head end
build() {
cd $srcdir/$_name
# build utilities for each architecture
for _arch in ${_architectures}; do
mkdir -p build-${_arch} && pushd build-${_arch}
INSTALL_ROOT=$pkgdir/usr/ ${_arch}-qmake-qt5 -r ../${_name}.pro
make
popd
done
}
package() {
cd $srcdir/$_name
for _arch in ${_architectures}; do
# bin stuff
pushd build-${_arch}
strip --strip-unneeded ./release/${_name}.exe
install -m755 -D ./release/${_name}.exe $pkgdir/usr/${_arch}/bin/${_name}.exe
popd
done
}

BIN
qml/images/backarrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

BIN
qml/images/clear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
qml/images/close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
qml/images/magnifier.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

175
qml/main.qml Normal file
View File

@ -0,0 +1,175 @@
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import martchus.passwordmanager 2.0
import "./pages" as Pages
import "./touch" as Touch
ApplicationWindow {
id: root
width: 700
height: 800
title: qsTr("Password Manager")
visible: true
property string statusBarMessage
property Component startPage: Pages.StartPage {
onUpdateStatusBar: statusBarMessage = message
onNextPage: if (!isLocked) {
pageView.push(accountsPage)
clearSearchBox()
}
}
property Component accountsPage: Pages.AccountsPage {
onUpdateStatusBar: statusBarMessage = message
onNextPage: if (!isLocked) {
pageView.push(fieldsPage)
}
onPreviousPage: {
pageView.pop()
}
}
property Component fieldsPage: Pages.FieldsPage {
onUpdateStatusBar: statusBarMessage = message
onNextPage: if (!isLocked) {
pageView.pop()
}
onPreviousPage: {
pageView.pop()
}
}
StackView {
id: pageView
anchors.fill: parent
focus: true
Keys.onReleased: {
if (event.key === Qt.Key_Back ||
(event.key === Qt.Key_Left && (event.modifiers & Qt.AltModifier))) {
if (pageView.depth > 1) {
event.accepted = true
if (!currentItem.isLocked)
currentItem.previousPage()
} else {
if (!currentItem.hasNoSearchText) {
event.accepted = true
currentItem.clearSearchBox()
}
}
}
}
initialItem: startPage
delegate: StackViewDelegate {
pushTransition: StackViewTransition {
function transitionFinished(properties)
{
properties.exitItem.opacity = 1
}
PropertyAnimation {
target: enterItem
property: "x"
from: target.width
to: 0
duration: 500
easing.type: Easing.OutSine
}
PropertyAnimation {
target: exitItem
property: "x"
from: 0
to: -target.width
duration: 500
easing.type: Easing.OutSine
}
}
popTransition: StackViewTransition {
function transitionFinished(properties)
{
properties.exitItem.opacity = 1
}
PropertyAnimation {
target: enterItem
property: "x"
from: -target.width
to: 0
duration: 500
easing.type: Easing.OutSine
}
PropertyAnimation {
target: exitItem
property: "x"
from: 0
to: target.width
duration: 500
easing.type: Easing.OutSine
}
}
property Component replaceTransition: pushTransition
}
}
statusBar: StatusBar {
id: statusbar
width: parent.width
opacity: label.text !== "" ? 1 : 0
property real statusBarHeight: 65 * ApplicationInfo.ratio
height: label.text !== "" ? statusBarHeight : 0
Behavior on height { NumberAnimation {easing.type: Easing.OutSine}}
Behavior on opacity { NumberAnimation {}}
style: StatusBarStyle {
padding { left: 0; right: 0 ; top: 0 ; bottom: 0}
property Component background: Rectangle {
implicitHeight: 65 * ApplicationInfo.ratio
implicitWidth: root.width
color: ApplicationInfo.colors.smokeGray
Rectangle {
width: parent.width
height: 1
color: Qt.darker(parent.color, 1.5)
}
Rectangle {
y: 1
width: parent.width
height: 1
color: "white"
}
}
}
Touch.TouchLabel {
id: label
y: 32 * ApplicationInfo.ratio - height/2
width: parent.width // The text will only wrap if an explicit width has been set
text: statusBarMessage
textFormat: Text.RichText
onLinkActivated: Qt.openUrlExternally(link)
wrapMode: Text.Wrap
pixelSize: 18
letterSpacing: -0.15
color: ApplicationInfo.colors.mediumGray
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
function decreaseFontSizeOnNarrowScreen() {
if (label.implicitHeight > statusbar.statusBarHeight)
pixelSize = Math.floor(pixelSize * statusbar.statusBarHeight/label.implicitHeight)
}
onTextChanged: {
if (text === "")
pixelSize = 18
else
decreaseFontSizeOnNarrowScreen()
}
onWidthChanged: decreaseFontSizeOnNarrowScreen()
}
}
menuBar: MenuBar {
Menu {
title: qsTr("Application")
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
}
}
}
}

View File

@ -0,0 +1,13 @@
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import martchus.passwordmanager 2.0
BasicPage {
id: accountsPage
title1: qsTr("Accounts")
pageComponent: Item {
}
}

182
qml/pages/BasicPage.qml Normal file
View File

@ -0,0 +1,182 @@
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Layouts 1.1
import martchus.passwordmanager 2.0
import "../touch" as Touch
Item {
id: page
signal updateStatusBar(string message)
signal nextPage
signal previousPage
signal clearSearchBox
property Component pageComponent
property bool isLocked: Stack.status !== Stack.Active
property string title1
property string title2
property string title3
property alias searchText: searchField.text
property alias hasNoSearchText: searchField.isEmpty
signal searchBoxReturn
property string statusBarMessageDefault
property alias topRect: topRect
property color topRectColor: ApplicationInfo.colors.yetAnotherBlue
property bool searchFieldVisisble: false
Binding {
target: ApplicationInfo
property: "isPortraitMode"
value: page.height > page.width
when: !ApplicationInfo.isMobile
}
Binding {
target: ApplicationInfo
property: "applicationWidth"
value: page.width
}
Rectangle {
id: topRect
z: 2 // so flickable doesn't draw on top
anchors.top: parent.top
height: 80 * ApplicationInfo.ratio
width: parent.width
color: page.Stack.index !== 0 && mouseBack.pressed ?
Qt.lighter(topRectColor, 1.2) : topRectColor
Rectangle {
color: Qt.lighter(parent.color, 1.2)
height: 1
anchors.bottom: parent.bottom
anchors.bottomMargin: 1
width: parent.width
}
Rectangle {
z: 2 // so flickable doesn't draw on top
height: 1
width: parent.width
color: Qt.darker(ApplicationInfo.colors.blue, 1.6)
anchors.bottom: parent.bottom
}
RowLayout {
id: titleRow
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
anchors.verticalCenter: parent.verticalCenter
Separator {
Layout.fillWidth: false
Layout.preferredWidth: ApplicationInfo.hMargin
}
Image {
source: ApplicationInfo.imagePath("backarrow.png")
Layout.preferredWidth: 22 * ApplicationInfo.ratio
Layout.preferredHeight: 35 * ApplicationInfo.ratio
visible: page.Stack.index > 0
Accessible.role: Accessible.Button
Accessible.name: qsTr("Back")
function accessiblePressAction () { backPressed() }
}
Rectangle {
opacity: 0
Layout.preferredWidth: 20 * ApplicationInfo.ratio
Layout.fillHeight: true
visible: page.Stack.index > 0
}
Touch.TouchLabel {
id: t1
text: title1 + " "
color: ApplicationInfo.colors.white
pixelSize: 30
font.weight: Font.Bold
Layout.maximumWidth: ApplicationInfo.applicationWidth - t3.implicitWidth - 2 * ApplicationInfo.hMargin - 5 * ApplicationInfo.ratio - 42 * ApplicationInfo.ratio
Layout.alignment: Qt.AlignBaseline
}
Touch.TouchLabel {
text: "- " + title2
color: ApplicationInfo.colors.white
visible: title2 !== ""
pixelSize: 22
letterSpacing: -0.15
Layout.alignment: Qt.AlignBaseline
Layout.maximumWidth: freeSpace > implicitWidth ? freeSpace : 0
property real freeSpace: ApplicationInfo.applicationWidth - t1.width - t3.implicitWidth - 2 * ApplicationInfo.hMargin - 5 * ApplicationInfo.ratio - 42 * ApplicationInfo.ratio
}
Item {
Layout.fillWidth: true
height: 0
}
Touch.TouchLabel {
id: t3
text: title3
color: ApplicationInfo.colors.white
visible: title3 !== ""
pixelSize: 22
letterSpacing: -0.15
Layout.alignment: Qt.AlignBaseline
}
Separator {
Layout.fillWidth: false
Layout.preferredWidth: ApplicationInfo.hMargin
}
}
Rectangle {
width: parent.width
height: 5
anchors.top: parent.bottom
gradient: Gradient {
GradientStop {position: 0 ; color: "#40000000"}
GradientStop {position: 1 ; color: "#00000000"}
}
}
MouseArea {
id: mouseBack
anchors.fill: parent
onClicked: backPressed()
}
}
function backPressed() {
if (!isLocked) page.previousPage()
}
Touch.TouchTextField {
id: searchField
z: 2
visible: searchFieldVisisble
anchors.right: topRect.right
anchors.top: topRect.top
anchors.bottom: topRect.bottom
width: ApplicationInfo.isPortraitMode ?
parent.width - t1.implicitWidth - ApplicationInfo.ratio * 50 :
parent.width/2.5
anchors.leftMargin: topRect.width/2
anchors.rightMargin: 20 * ApplicationInfo.ratio
anchors.margins: 12 * ApplicationInfo.ratio
placeholderText: qsTr("filter")
Layout.fillWidth: true
Keys.onReturnPressed: page.searchBoxReturn()
Keys.onEnterPressed: page.searchBoxReturn()
onClearButtonClicked: page.clearSearchBox()
}
Loader {
sourceComponent: pageComponent
anchors.top: topRect.bottom
anchors.bottom: parent.bottom
width: parent.width
Rectangle {
z: -1
anchors.fill: parent
color: ApplicationInfo.colors.white
}
}
}

14
qml/pages/FieldsPage.qml Normal file
View File

@ -0,0 +1,14 @@
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import martchus.passwordmanager 2.0
BasicPage {
id : fieldsPage
title1: ApplicationInfo.currentAccountName
title3: qsTr("Fields")
pageComponent: Item {
}
}

12
qml/pages/Separator.qml Normal file
View File

@ -0,0 +1,12 @@
import QtQuick 2.1
import QtQuick.Layouts 1.0
import martchus.passwordmanager 2.0
Item {
implicitHeight: ApplicationInfo.hMargin
implicitWidth: ApplicationInfo.hMargin
Layout.minimumHeight: 0
Layout.minimumWidth: 0
Layout.fillHeight: true
Layout.fillWidth: true
}

66
qml/pages/StartPage.qml Normal file
View File

@ -0,0 +1,66 @@
import QtQuick 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import QtQuick.Dialogs 1.2
import martchus.passwordmanager 2.0
import "../touch" as Touch
BasicPage {
id: startPage
title1: qsTr("Password Manager")
searchFieldVisisble: true
FileDialog {
id: fileDialog
title: qsTr("Select a password file")
selectExisting: true
onAccepted: {
ApplicationInfo.currentFile = fileUrl
nextPage()
}
}
ColumnLayout {
anchors.top: topRect.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 5
Touch.TouchButton {
text: qsTr("Create a new password file")
Layout.fillWidth: true
}
Touch.TouchButton {
text: qsTr("Open an existing password file")
onClicked: {
ApplicationInfo.currentFile = "./test"
nextPage() // fileDialog.visible = true
}
Layout.fillWidth: true
}
Touch.TouchLabel {
text: qsTr("Recently opened files")
}
Touch.TouchLabel {
text: qsTr("Test 1")
//Layout.fillWidth: true
color: ApplicationInfo.colors.lightGray
}
Touch.TouchLabel {
text: qsTr("Test 2")
//Layout.fillWidth: true
color: ApplicationInfo.colors.lightGray
}
Touch.TouchLabel {
text: qsTr("Test 3")
//Layout.fillWidth: true
color: ApplicationInfo.colors.lightGray
}
}
}

View File

@ -0,0 +1,52 @@
import QtQuick 2.1
import QtQuick.Layouts 1.0
import martchus.passwordmanager 2.0
Rectangle {
id: rect
height: ApplicationInfo.constants.rowDelegateHeight
width: parent.width
signal clicked
signal deleteEntry
color: mouseNext.pressed ? ApplicationInfo.colors.smokeGray : ApplicationInfo.colors.white
GridLayout {
id: _grid
anchors.fill: parent
flow: Qt.LeftToRight
rowSpacing: 4 * ApplicationInfo.ratio
columnSpacing: 0
columns: 2
Rectangle {
Layout.preferredWidth: ApplicationInfo.hMargin
Layout.fillHeight: true
opacity: 0
}
Loader {
// sourceComponent:
Layout.fillHeight: true
Layout.fillWidth: true
}
Rectangle {
id: separator
Layout.fillWidth: true
Layout.fillHeight: true
Layout.columnSpan: 2
}
}
Rectangle {
z: 1
height: 1
anchors.bottom: parent.bottom
width: parent.width
color: ApplicationInfo.colors.paleGray
}
MouseArea {
id: mouseNext
anchors.left: parent.left
width: parent.width - 80 * ApplicationInfo.ratio - ApplicationInfo.hMargin
height: parent.height
onClicked: rect.clicked()
}
}

43
qml/touch/TouchButton.qml Normal file
View File

@ -0,0 +1,43 @@
import QtQuick 2.1
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.0
import martchus.passwordmanager 2.0
Button {
id: button
property int pixelSize: 34
property real letterSpacing: -0.25
property string text: ""
style: ButtonStyle {
label: Label {
id: label
font.family: "Open Sans"
font.pixelSize: pixelSize * ApplicationInfo.ratio * 1.1 // increasing fonts
font.letterSpacing: letterSpacing * ApplicationInfo.ratio
color: ApplicationInfo.colors.doubleDarkGray
verticalAlignment: Text.AlignBottom
horizontalAlignment: Text.AlignLeft
elide: Text.ElideRight
linkColor: ApplicationInfo.colors.blue
renderType: ApplicationInfo.isMobile ? Text.QtRendering : Text.NativeRendering
text: button.text
Text {
id: text
visible: false
font.family: label.font.family
font.pixelSize: label.font.pixelSize
font.letterSpacing: label.font.letterSpacing
wrapMode: label.wrapMode
elide: label.elide
text: label.text
}
}
}
property int maximumWidth: (ApplicationInfo.constants.isMobile ? ApplicationInfo.applicationWidth : 1120) / 4
Layout.minimumWidth: Math.min(Layout.maximumWidth, implicitWidth + 1)
}

35
qml/touch/TouchLabel.qml Normal file
View File

@ -0,0 +1,35 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
import martchus.passwordmanager 2.0
Label {
id: label
property int pixelSize: 34
property real letterSpacing: -0.25
font.family: "Open Sans"
font.pixelSize: pixelSize * ApplicationInfo.ratio * 1.1 // increasing fonts
font.letterSpacing: letterSpacing * ApplicationInfo.ratio
color: ApplicationInfo.colors.doubleDarkGray
verticalAlignment: Text.AlignBottom
horizontalAlignment: Text.AlignLeft
elide: Text.ElideRight
linkColor: ApplicationInfo.colors.blue
renderType: ApplicationInfo.isMobile ? Text.QtRendering : Text.NativeRendering
function expectedTextWidth(value)
{
text.text = value
return text.width
}
property int maximumWidth: (ApplicationInfo.constants.isMobile ? ApplicationInfo.applicationWidth : 1120) / 4
Layout.minimumWidth: Math.min(Layout.maximumWidth, implicitWidth + 1)
Text {
id: text
visible: false
font.family: label.font.family
font.pixelSize: label.font.pixelSize
font.letterSpacing: label.font.letterSpacing
wrapMode: label.wrapMode
elide: label.elide
}
}

View File

@ -0,0 +1,18 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.0
import martchus.passwordmanager 2.0
ScrollView {
frameVisible: false
style: ScrollViewStyle {
property int handleWidth: 20 * ApplicationInfo.ratio
transientScrollBars: true
padding{ top: 4 ; bottom: 4 ; right: 4}
property bool hovered: false
}
Rectangle {
anchors.fill: parent
color: ApplicationInfo.colors.white
}
}

42
qml/touch/TouchSlider.qml Normal file
View File

@ -0,0 +1,42 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.0
import martchus.passwordmanager 2.0
Slider {
id: slider
property real sliderHandleHeight: 0.
property real sliderHandleWidth: 0.
implicitHeight: sliderHandleHeight + ApplicationInfo.ratio * 25
style: SliderStyle {
groove: Rectangle {
Rectangle {
id: beforeHandle
width: styleData.handlePosition
height: 20 * ApplicationInfo.ratio
color: ApplicationInfo.colors.blue
radius: 90
z: -1
}
Rectangle {
id: afterHandle
anchors.left: beforeHandle.right
anchors.right: parent.right
height: 20 * ApplicationInfo.ratio
color: ApplicationInfo.colors.darkGray
radius: 90
z: -1
}
}
handle: Item {
width: sliderHandleWidth
height: sliderHandleHeight
Image {
anchors.centerIn: parent
source: ApplicationInfo.imagePath(control.pressed ? "Pointer_pressed.png" : "Pointer.png")
width: sliderHandleWidth + 16 * ApplicationInfo.ratio
height: sliderHandleHeight + 16 * ApplicationInfo.ratio
}
}
}
}

View File

@ -0,0 +1,75 @@
import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.0
import martchus.passwordmanager 2.0
TextField {
id: textfield
signal clearButtonClicked
implicitWidth: parent.width
property bool isEmpty: true
style: TextFieldStyle {
renderType: ApplicationInfo.isMobile ? Text.QtRendering : Text.NativeRendering
background: Rectangle {
radius: 8
border.width: 1
border.color: Qt.darker(ApplicationInfo.colors.blue, 1.6)
color: ApplicationInfo.colors.white
gradient: Gradient {
GradientStop { position: 0 ; color: "#ddd"}
GradientStop { position: 0.05 ; color: "#fff"}
}
implicitHeight: 60 * ApplicationInfo.ratio
opacity: 1
}
padding.left : (12 + 50) * ApplicationInfo.ratio
padding.right: (12 + 50) * ApplicationInfo.ratio
font.pixelSize: 28 * ApplicationInfo.ratio
font.family: "Open Sans"
font.letterSpacing: -0.25 * ApplicationInfo.ratio
selectedTextColor : ApplicationInfo.colors.lightGray
selectionColor : ApplicationInfo.colors.darkBlue
textColor : ApplicationInfo.colors.mediumGray
}
onClearButtonClicked: text = ""
Item {
id: item
anchors.left: parent.left
anchors.top: parent.top
height: parent.height
width: parent.height
Image {
opacity: 0.9
anchors.centerIn: item
height: iconSize
width: iconSize
source: ApplicationInfo.imagePath("magnifier.png")
property int iconSize: 50 * ApplicationInfo.ratio
}
}
onTextChanged: isEmpty = (text === "")
inputMethodHints: Qt.ImhNoPredictiveText
MouseArea {
z: 2
opacity: !textfield.isEmpty ? 1 : 0
Behavior on opacity {NumberAnimation{}}
anchors.right: parent.right
anchors.rightMargin: 4 * ApplicationInfo.ratio
anchors.top: parent.top
height: parent.height
width: parent.height
Image {
anchors.centerIn: parent
source: ApplicationInfo.imagePath("clear.png")
property int iconSize: 40 * ApplicationInfo.ratio
opacity: parent.pressed ? 1 : 0.9
width: iconSize
height: iconSize
}
onClicked: textfield.clearButtonClicked()
}
}

View File

@ -0,0 +1,94 @@
#include "applicationinfo.h"
#include <qmath.h>
#include <QFile>
#include <QRegExp>
#include <QUrl>
#include <QUrlQuery>
#include <QGuiApplication>
#include <QScreen>
#include <QDebug>
using namespace Io;
namespace QtGui {
ApplicationInfo::ApplicationInfo()
{
m_colors = new QQmlPropertyMap(this);
m_colors->insert(QLatin1String("white"), QVariant("#ffffff"));
m_colors->insert(QLatin1String("smokeGray"), QVariant("#eeeeee"));
m_colors->insert(QLatin1String("paleGray"), QVariant("#d7d6d5"));
m_colors->insert(QLatin1String("lightGray"), QVariant("#aeadac"));
m_colors->insert(QLatin1String("darkGray"), QVariant("#35322f"));
m_colors->insert(QLatin1String("mediumGray"), QVariant("#5d5b59"));
m_colors->insert(QLatin1String("doubleDarkGray"), QVariant("#1e1b18"));
m_colors->insert(QLatin1String("blue"), QVariant("#14aaff"));
m_colors->insert(QLatin1String("yetAnotherBlue"), QVariant("#428bca"));
m_colors->insert(QLatin1String("darkBlue"), QVariant("#14148c"));
m_colors->insert(QLatin1String("darkYellow"), QVariant("#dfdc00"));
m_colors->insert(QLatin1String("darkYellow"), QVariant("#eb881c"));
m_colors->insert(QLatin1String("almostBlack"), QVariant("#222222"));
m_constants = new QQmlPropertyMap(this);
m_constants->insert(QLatin1String("isMobile"), QVariant(isMobile()));
QRect rect = QGuiApplication::primaryScreen()->geometry();
m_ratio = isMobile() ? qMin(qMax(rect.width(), rect.height()) / 1136., qMin(rect.width(), rect.height()) / 640.) : .5;
m_sliderHandleWidth = sizeWithRatio(70);
m_sliderHandleHeight = sizeWithRatio(87);
m_sliderGapWidth = sizeWithRatio(100);
m_isPortraitMode = isMobile() ? rect.height() > rect.width() : false;
m_hMargin = m_isPortraitMode ? 20 * ratio() : 50 * ratio();
m_applicationWidth = isMobile() ? rect.width() : 1120;
m_constants->insert(QLatin1String("rowDelegateHeight"), QVariant(sizeWithRatio(118)));
m_fieldModel = new FieldModel(this);
if(isMobile()) {
connect(QGuiApplication::primaryScreen(), &QScreen::orientationChanged, this, &ApplicationInfo::notifyPortraitMode);
}
}
void ApplicationInfo::setApplicationWidth(const int newWidth)
{
if (newWidth != m_applicationWidth) {
m_applicationWidth = newWidth;
emit applicationWidthChanged();
}
}
QString ApplicationInfo::imagePath(const QString image)
{
return QStringLiteral("qrc:/qml/images/%1").arg(image);
}
void ApplicationInfo::notifyPortraitMode(Qt::ScreenOrientation orientation)
{
switch (orientation) {
case Qt::LandscapeOrientation:
case Qt::InvertedLandscapeOrientation:
setIsPortraitMode(false);
break;
case Qt::PortraitOrientation:
case Qt::InvertedPortraitOrientation:
setIsPortraitMode(true);
break;
default:
break;
}
}
void ApplicationInfo::setIsPortraitMode(const bool newMode)
{
if (m_isPortraitMode != newMode) {
m_isPortraitMode = newMode;
m_hMargin = m_isPortraitMode ? 20 * ratio() : 50 * ratio();
emit portraitModeChanged();
emit hMarginChanged();
}
}
}

178
quickgui/applicationinfo.h Normal file
View File

@ -0,0 +1,178 @@
#ifndef APPLICATIONINFO_H
#define APPLICATIONINFO_H
#include "model/fieldmodel.h"
#include <QObject>
#include <QQmlPropertyMap>
namespace QtGui {
class ApplicationInfo : public QObject
{
Q_OBJECT
Q_PROPERTY(int applicationWidth READ applicationWidth WRITE setApplicationWidth NOTIFY applicationWidthChanged)
Q_PROPERTY(bool isMobile READ isMobile CONSTANT)
Q_PROPERTY(QObject *colors READ colors CONSTANT)
Q_PROPERTY(QObject *constants READ constants CONSTANT)
Q_PROPERTY(bool isPortraitMode READ isPortraitMode WRITE setIsPortraitMode NOTIFY portraitModeChanged)
Q_PROPERTY(const QString &currenfFile READ currentFile WRITE setCurrentFile NOTIFY currentFileChanged)
Q_PROPERTY(FieldModel *fieldModel READ fieldModel)
Q_PROPERTY(Io::AccountEntry *currentAccountEntry READ currentAccountEntry WRITE setCurrentAccountEntry)
Q_PROPERTY(QString currentAccountName READ currentAccountName)
Q_PROPERTY(qreal ratio READ ratio CONSTANT)
Q_PROPERTY(qreal hMargin READ hMargin NOTIFY hMarginChanged)
Q_PROPERTY(qreal sliderHandleWidth READ sliderHandleWidth CONSTANT)
Q_PROPERTY(qreal sliderHandleHeight READ sliderHandleHeight CONSTANT)
Q_PROPERTY(qreal sliderGapWidth READ sliderGapWidth CONSTANT)
public:
ApplicationInfo();
static constexpr bool isMobile();
QQmlPropertyMap *colors() const;
QQmlPropertyMap *constants() const;
int applicationWidth() const;
void setApplicationWidth(const int newWidth);
bool isPortraitMode() const;
void setIsPortraitMode(const bool newMode);
const QString &currentFile() const;
void setCurrentFile(const QString &currentFile);
FieldModel *fieldModel();
Io::AccountEntry *currentAccountEntry();
QString currentAccountName() const;
void setCurrentAccountEntry(Io::AccountEntry *entry);
qreal hMargin() const;
qreal ratio() const;
qreal sliderHandleHeight();
qreal sliderGapWidth();
qreal sliderHandleWidth();
Q_INVOKABLE QString imagePath(const QString image);
protected slots:
void notifyPortraitMode(Qt::ScreenOrientation);
protected:
qreal sizeWithRatio(const qreal height);
signals:
void currentFileChanged();
void applicationWidthChanged();
void portraitModeChanged();
void hMarginChanged();
private:
int m_applicationWidth;
QQmlPropertyMap *m_colors;
QQmlPropertyMap *m_constants;
bool m_isPortraitMode;
QString m_currentFile;
FieldModel *m_fieldModel;
qreal m_ratio;
qreal m_hMargin;
qreal m_sliderHandleHeight, m_sliderHandleWidth, m_sliderGapWidth;
};
constexpr bool ApplicationInfo::isMobile()
{
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_BLACKBERRY)
return true;
#else
return false;
#endif
}
inline QQmlPropertyMap *ApplicationInfo::colors() const
{
return m_colors;
}
inline QQmlPropertyMap *ApplicationInfo::constants() const
{
return m_constants;
}
inline int ApplicationInfo::applicationWidth() const
{
return m_applicationWidth;
}
inline bool ApplicationInfo::isPortraitMode() const
{
return m_isPortraitMode;
}
inline const QString &ApplicationInfo::currentFile() const
{
return m_currentFile;
}
inline void ApplicationInfo::setCurrentFile(const QString &currentFile)
{
if(m_currentFile != currentFile) {
m_currentFile = currentFile;
emit currentFileChanged();
}
}
inline FieldModel *ApplicationInfo::fieldModel()
{
return m_fieldModel;
}
inline Io::AccountEntry *ApplicationInfo::currentAccountEntry()
{
return m_fieldModel->accountEntry();
}
inline QString ApplicationInfo::currentAccountName() const
{
if(m_fieldModel->accountEntry()) {
return QString::fromStdString(m_fieldModel->accountEntry()->label());
}
return QString();
}
inline void ApplicationInfo::setCurrentAccountEntry(Io::AccountEntry *entry)
{
m_fieldModel->setAccountEntry(entry);
}
inline qreal ApplicationInfo::hMargin() const
{
return m_hMargin;
}
inline qreal ApplicationInfo::ratio() const
{
return m_ratio;
}
inline qreal ApplicationInfo::sliderHandleHeight()
{
return m_sliderHandleHeight;
}
inline qreal ApplicationInfo::sliderGapWidth()
{
return m_sliderGapWidth;
}
inline qreal ApplicationInfo::sliderHandleWidth()
{
return m_sliderHandleWidth;
}
inline qreal ApplicationInfo::sizeWithRatio(const qreal height)
{
return ratio() * height;
}
}
#endif // APPLICATIONINFO_H

View File

@ -0,0 +1,42 @@
#ifndef APPLICATIONPATHS_H
#define APPLICATIONPATHS_H
#include <QDir>
#include <QStandardPaths>
#include <QStringList>
namespace QtGui {
class ApplicationPaths
{
public:
static QString settingsPath();
static QString dowloadedFilesPath();
protected:
static QString path(QStandardPaths::StandardLocation location);
};
inline QString ApplicationPaths::settingsPath()
{
return path(QStandardPaths::DataLocation);
}
inline QString ApplicationPaths::dowloadedFilesPath()
{
return path(QStandardPaths::CacheLocation);
}
inline QString ApplicationPaths::path(QStandardPaths::StandardLocation location)
{
QString path = QStandardPaths::standardLocations(location).value(0);
QDir dir(path);
if(!dir.exists())
dir.mkpath(path);
if(!path.isEmpty() && !path.endsWith("/"))
path += "/";
return path;
}
}
#endif // APPLICATIONPATHS_H

16
quickgui/initiate.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef QT_QUICK_GUI_INITIATE_H
#define QT_QUICK_GUI_INITIATE_H
#include <QtGlobal>
QT_BEGIN_NAMESPACE
class QString;
QT_END_NAMESPACE
namespace QtGui {
int runQuickGui(int argc, char *argv[]);
}
#endif // QT_QUICK_GUI_INITIATE_H

View File

@ -0,0 +1,66 @@
#include "initiate.h"
# include "model/entryfiltermodel.h"
# include "model/entrymodel.h"
# include "model/fieldmodel.h"
# include "quickgui/applicationinfo.h"
#include <qtutilities/resources/resources.h>
#if defined(GUI_QTWIDGETS)
# include <QApplication>
#else
# include <QGuiApplication>
#endif
#include <QGuiApplication>
#include <QTextCodec>
#include <QtQml>
#include <QQmlApplicationEngine>
#include <QDebug>
namespace QtGui {
#if defined(GUI_QTQUICK)
static QObject *applicationInfo(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
return new ApplicationInfo();
}
#endif
int runQuickGui(int argc, char *argv[])
{
// init application
#if defined(GUI_QTWIDGETS)
QApplication a(argc, argv);
#else
QGuiApplication a(argc, argv);
#endif
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
QGuiApplication::setOrganizationName(QStringLiteral("Martchus"));
QGuiApplication::setOrganizationDomain(QStringLiteral("http://martchus.netai.net/"));
QGuiApplication::setApplicationName(QStringLiteral("Password Manager (mobile)"));
QGuiApplication::setApplicationVersion(QStringLiteral("2.0.5"));
// load translation files
TranslationFiles::loadQtTranslationFile();
TranslationFiles::loadApplicationTranslationFile(QStringLiteral("passwordmanager"));
// load the other resources
QtUtilitiesResources::init();
Theme::setup();
qDebug() << "test 2";
// init quick GUI
qmlRegisterSingletonType<QtGui::ApplicationInfo>("martchus.passwordmanager", 2, 0, "ApplicationInfo", applicationInfo);
qmlRegisterType<QtGui::EntryFilterModel>("martchus.passwordmanager", 2, 0, "EntryFilterModel");
qmlRegisterType<QtGui::EntryModel>("martchus.passwordmanager", 2, 0, "EntryModel");
qmlRegisterType<QtGui::FieldModel>("martchus.passwordmanager", 2, 0, "FieldModel");
QQmlApplicationEngine engine(QUrl("qrc:/qml/main.qml"));
// start event loop
int res = a.exec();
// cleanup resources
QtUtilitiesResources::cleanup();
return res;
}
}

View File

@ -0,0 +1,9 @@
[Desktop Entry]
Name=Password manager
GenericName=Password manager
Comment=A simple password store using AES-256-CBC encryption via OpenSSL.
Exec=passwordmanager
Icon=passwordmanager
Terminal=false
Type=Application
Categories=Utility

5
resources/icons.qrc Normal file
View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>icons/hicolor/128x128/apps/passwordmanager.png</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" ?><svg height="24" version="1.1" width="24" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><g transform="translate(0 -1028.4)"><path d="m12 1031.4c-2.7614 0-5 2.6-5 6v1c0 0.5 0.3731 1 0.8333 1 0.4603 0 0.8334-0.5 0.8334-1v-1c0-2.4 1.4923-4.3 3.3333-4.3s3.333 1.9 3.333 4.3v1c0 0.5 0.373 1 0.834 1 0.46 0 0.833-0.5 0.833-1v-1c0-3.4-2.239-6-5-6z" fill="#7f8c8d"/><path d="m6 1037.4c-1.1046 0-2 0.9-2 2v7c0 1.1 0.8954 2 2 2h12c1.105 0 2-0.9 2-2v-7c0-1.1-0.895-2-2-2h-12z" fill="#f1c40f"/><path d="m12 1029.9c-3.3137 0-6 2.7-6 6v1c0 0.6 0.4477 1 1 1s1-0.4 1-1v-1c0-2.3 1.7909-4.2 4-4.2 2.209 0 4 1.9 4 4.2v1c0 0.6 0.448 1 1 1s1-0.4 1-1v-1c0-3.3-2.686-6-6-6z" fill="#bdc3c7"/><path d="m6 1039.4c-1.1046 0-2 0.9-2 2v7c0 1.1 0.8954 2 2 2h12c1.105 0 2-0.9 2-2v-7c0-1.1-0.895-2-2-2h-12z" fill="#f39c12"/><path d="m6.327 1037.4c-0.3403 0-0.6569 0.1-0.9375 0.2 0.222 0.9 1.0056 1.5 1.9375 1.5 1.0193 0 1.845-0.8 1.9688-1.7h-2.9688z" fill="#7f8c8d"/><path d="m17.792 1037.3c0.34 0 0.657 0.1 0.937 0.3-0.222 0.8-1.005 1.5-1.937 1.5-1.02 0-1.845-0.8-1.969-1.8h2.969z" fill="#7f8c8d"/><path d="m12 1030.4c-3.3137 0-6 2.6-6 6v1c0 0.5 0.4477 1 1 1s1-0.5 1-1v-1c0-2.4 1.7909-4.3 4-4.3 2.209 0 4 1.9 4 4.3v1c0 0.5 0.448 1 1 1s1-0.5 1-1v-1c0-3.4-2.686-6-6-6z" fill="#95a5a6"/><g fill="#e67e22"><rect height="1" width="12" x="6" y="1041.4"/><rect height="1" width="12" x="6" y="1043.4"/><rect height="1" width="12" x="6" y="1045.4"/><rect height="1" width="12" x="6" y="1047.4"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

20
resources/qml.qrc Normal file
View File

@ -0,0 +1,20 @@
<RCC>
<qresource prefix="/qml">
<file alias="main.qml">../qml/main.qml</file>
<file alias="pages/AccountsPage.qml">../qml/pages/AccountsPage.qml</file>
<file alias="pages/BasicPage.qml">../qml/pages/BasicPage.qml</file>
<file alias="pages/FieldsPage.qml">../qml/pages/FieldsPage.qml</file>
<file alias="pages/StartPage.qml">../qml/pages/StartPage.qml</file>
<file alias="pages/Separator.qml">../qml/pages/Separator.qml</file>
<file alias="touch/TouchButton.qml">../qml/touch/TouchButton.qml</file>
<file alias="touch/TouchLabel.qml">../qml/touch/TouchLabel.qml</file>
<file alias="touch/TouchTextField.qml">../qml/touch/TouchTextField.qml</file>
<file alias="touch/TouchSlider.qml">../qml/touch/TouchSlider.qml</file>
<file alias="touch/TouchScrollView.qml">../qml/touch/TouchScrollView.qml</file>
<file alias="touch/ListViewDelegate.qml">../qml/touch/ListViewDelegate.qml</file>
<file alias="images/clear.png">../qml/images/clear.png</file>
<file alias="images/magnifier.png">../qml/images/magnifier.png</file>
<file alias="images/backarrow.png">../qml/images/backarrow.png</file>
<file alias="images/close.png">../qml/images/close.png</file>
</qresource>
</RCC>

1
resources/windowsicon.rc Normal file
View File

@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "PasswordManager\resources\icon.ico"

Binary file not shown.

View File

@ -0,0 +1,620 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE">
<context>
<name>QtGui::EntryModel</name>
<message>
<location filename="../model/entrymodel.cpp" line="329"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtGui::FieldModel</name>
<message>
<location filename="../model/fieldmodel.cpp" line="197"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../model/fieldmodel.cpp" line="199"/>
<source>Value</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtGui::MainWindow</name>
<message>
<location filename="../gui/mainwindow.ui" line="14"/>
<source>Password Manager</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="98"/>
<source>filter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="188"/>
<source>File</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="195"/>
<source>Recent</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="221"/>
<source>?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="227"/>
<source>Edit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="242"/>
<source>Tools</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="263"/>
<source>Open ...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="266"/>
<source>Ctrl+O</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="276"/>
<source>Quit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="279"/>
<source>Ctrl+Q</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="289"/>
<source>About</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="299"/>
<source>Save</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="302"/>
<source>Ctrl+S</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="312"/>
<source>New ...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="315"/>
<source>Ctrl+N</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="325"/>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="328"/>
<source>Ctrl+Shift+Q</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="338"/>
<source>Change password ...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="348"/>
<source>Add new account</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="351"/>
<source>Ctrl+Shift+A</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="361"/>
<source>Save as ...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="364"/>
<source>Ctrl+Shift+S</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="374"/>
<location filename="../gui/mainwindow.ui" line="377"/>
<source>Remove selected entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="387"/>
<location filename="../gui/mainwindow.cpp" line="871"/>
<source>Insert field</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="397"/>
<source>Remove selected field(s)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="402"/>
<source>Password generator</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="412"/>
<source>Clear list</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="423"/>
<source>Always create backup</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="433"/>
<source>Export ...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="443"/>
<source>Show containing directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="456"/>
<source>Undo</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="469"/>
<source>Redo</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="479"/>
<source>Add new category</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="484"/>
<source>Show stack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="195"/>
<source>A simple password store using AES-256-CBC encryption via OpenSSL.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="216"/>
<source>Select a password list</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="236"/>
<source>The selected file can&apos;t be found anymore. Do you want to delete the obsolete entry from the list?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="238"/>
<source>keep entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="239"/>
<source>delete entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="270"/>
<source>Undo stack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="301"/>
<source>The file you want to load seems to be very big. Do you really want to open it?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="310"/>
<source>Enter the password to open the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="315"/>
<location filename="../gui/mainwindow.cpp" line="316"/>
<source>A password is needed to open the file.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="335"/>
<source>The file couldn&apos;t be decrypted.
OpenSSL error queue: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="339"/>
<source>Unable to parse the file. %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="397"/>
<source>The file &lt;i&gt;%1&lt;/i&gt; couldn&apos;t be created.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="414"/>
<source>A new password list has been created.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="418"/>
<source>The password list &quot;%1&quot; has been load.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="481"/>
<source>Select where you want to save the password list</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="483"/>
<source>All files (*.*)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="485"/>
<source>The file was not be saved.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="506"/>
<source>There is no password list opened.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="519"/>
<source>There&apos;s no account selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="536"/>
<source>The password file has been modified.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="537"/>
<source>Do you want to save the changes before closing?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="557"/>
<source>The password list has been closed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="580"/>
<source>The backup file couldn&apos;t be created. %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="595"/>
<source>Enter a password to save the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="603"/>
<source>The file hasn&apos;t been saved.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="612"/>
<source>The password list couldn&apos;t be saved due to encryption failure.
OpenSSL error queue: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="624"/>
<source>The password list has been saved.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="643"/>
<source>The password list couldn&apos;t be exported. %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="646"/>
<source>The password list has been exported.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="662"/>
<source>The currently opened file hasn&apos;t been saved yet.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="676"/>
<location filename="../gui/mainwindow.cpp" line="856"/>
<source>Add account</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="684"/>
<location filename="../gui/mainwindow.cpp" line="857"/>
<source>Add category</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="702"/>
<source>Enter the entry name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="702"/>
<source>new entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="711"/>
<source>Unable to create new entry.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="714"/>
<source>You didn&apos;t enter text.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="720"/>
<source>No node element selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="735"/>
<source>Unable to remove the entry.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="738"/>
<source>No entry selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="776"/>
<source>A field has to be selected since new fields are always inserted before the currently selected field.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="800"/>
<source>No fields have been removed since there are currently no fields selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="821"/>
<source>You didn&apos;t enter a password. &lt;strong&gt;No encryption&lt;/strong&gt; will be used when saving the file next time.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="825"/>
<source>The new password will be used next time you save the file.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="829"/>
<source>You aborted. The old password will still be used when saving the file next time.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="858"/>
<source>Remove entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="872"/>
<source>Remove field</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="874"/>
<source>Copy for 5 seconds</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="893"/>
<source>The selected cell is empty.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="896"/>
<source>Exactly one cell needs to be selected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtGui::PasswordGeneratorDialog</name>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="32"/>
<source>Password Generator</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="117"/>
<source>Use small letters</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="127"/>
<source>Use capital letters</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="137"/>
<source>Use digits</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="174"/>
<source>Length</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="181"/>
<source>Other characters to be used</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="188"/>
<source>!&quot;§$%&amp;/()=?;:_&apos;*~#,.-+&lt;&gt;|</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="229"/>
<source>&lt;b&gt;Password:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="248"/>
<source>Generate new password</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="264"/>
<source>Copy password</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="311"/>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.cpp" line="130"/>
<source>Failed to generate password.
OpenSSL error: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.cpp" line="133"/>
<source>You have to select at least one checkbox.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.cpp" line="136"/>
<source>The length has to be at least one.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>undocommands</name>
<message>
<location filename="../gui/undocommands.cpp" line="76"/>
<source>setting field name to »%1«</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="78"/>
<source>setting field name »%1« to »%2«</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="84"/>
<source>setting value of empty field to »%1«</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="86"/>
<source>setting value of »%1« field to »%2«</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<location filename="../gui/undocommands.cpp" line="117"/>
<source>insertion of %1 row(s) before row %2</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="../gui/undocommands.cpp" line="145"/>
<source>removal of row %1</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="../gui/undocommands.cpp" line="147"/>
<source>removal of the rows %1 to %2</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="188"/>
<source>selection of account »%1«</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="190"/>
<source>account selection repealed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="254"/>
<source>setting entry name to »%1«</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="256"/>
<source>setting entry name from »%1« to »%2«</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<location filename="../gui/undocommands.cpp" line="297"/>
<source>insertion of %1 entry/entries</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="../gui/undocommands.cpp" line="332"/>
<source>removal of %1 entry/entries</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="../gui/undocommands.cpp" line="372"/>
<source>move of %1 entry/entries</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
</context>
</TS>

Binary file not shown.

View File

@ -0,0 +1,620 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US">
<context>
<name>QtGui::EntryModel</name>
<message>
<location filename="../model/entrymodel.cpp" line="329"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtGui::FieldModel</name>
<message>
<location filename="../model/fieldmodel.cpp" line="197"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../model/fieldmodel.cpp" line="199"/>
<source>Value</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtGui::MainWindow</name>
<message>
<location filename="../gui/mainwindow.ui" line="14"/>
<source>Password Manager</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="98"/>
<source>filter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="188"/>
<source>File</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="195"/>
<source>Recent</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="221"/>
<source>?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="227"/>
<source>Edit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="242"/>
<source>Tools</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="263"/>
<source>Open ...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="266"/>
<source>Ctrl+O</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="276"/>
<source>Quit</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="279"/>
<source>Ctrl+Q</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="289"/>
<source>About</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="299"/>
<source>Save</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="302"/>
<source>Ctrl+S</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="312"/>
<source>New ...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="315"/>
<source>Ctrl+N</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="325"/>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="328"/>
<source>Ctrl+Shift+Q</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="338"/>
<source>Change password ...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="348"/>
<source>Add new account</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="351"/>
<source>Ctrl+Shift+A</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="361"/>
<source>Save as ...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="364"/>
<source>Ctrl+Shift+S</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="374"/>
<location filename="../gui/mainwindow.ui" line="377"/>
<source>Remove selected entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="387"/>
<location filename="../gui/mainwindow.cpp" line="871"/>
<source>Insert field</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="397"/>
<source>Remove selected field(s)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="402"/>
<source>Password generator</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="412"/>
<source>Clear list</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="423"/>
<source>Always create backup</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="433"/>
<source>Export ...</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="443"/>
<source>Show containing directory</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="456"/>
<source>Undo</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="469"/>
<source>Redo</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="479"/>
<source>Add new category</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.ui" line="484"/>
<source>Show stack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="195"/>
<source>A simple password store using AES-256-CBC encryption via OpenSSL.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="216"/>
<source>Select a password list</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="236"/>
<source>The selected file can&apos;t be found anymore. Do you want to delete the obsolete entry from the list?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="238"/>
<source>keep entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="239"/>
<source>delete entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="270"/>
<source>Undo stack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="301"/>
<source>The file you want to load seems to be very big. Do you really want to open it?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="310"/>
<source>Enter the password to open the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="315"/>
<location filename="../gui/mainwindow.cpp" line="316"/>
<source>A password is needed to open the file.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="335"/>
<source>The file couldn&apos;t be decrypted.
OpenSSL error queue: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="339"/>
<source>Unable to parse the file. %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="397"/>
<source>The file &lt;i&gt;%1&lt;/i&gt; couldn&apos;t be created.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="414"/>
<source>A new password list has been created.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="418"/>
<source>The password list &quot;%1&quot; has been load.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="481"/>
<source>Select where you want to save the password list</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="483"/>
<source>All files (*.*)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="485"/>
<source>The file was not be saved.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="506"/>
<source>There is no password list opened.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="519"/>
<source>There&apos;s no account selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="536"/>
<source>The password file has been modified.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="537"/>
<source>Do you want to save the changes before closing?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="557"/>
<source>The password list has been closed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="580"/>
<source>The backup file couldn&apos;t be created. %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="595"/>
<source>Enter a password to save the file</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="603"/>
<source>The file hasn&apos;t been saved.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="612"/>
<source>The password list couldn&apos;t be saved due to encryption failure.
OpenSSL error queue: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="624"/>
<source>The password list has been saved.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="643"/>
<source>The password list couldn&apos;t be exported. %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="646"/>
<source>The password list has been exported.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="662"/>
<source>The currently opened file hasn&apos;t been saved yet.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="676"/>
<location filename="../gui/mainwindow.cpp" line="856"/>
<source>Add account</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="684"/>
<location filename="../gui/mainwindow.cpp" line="857"/>
<source>Add category</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="702"/>
<source>Enter the entry name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="702"/>
<source>new entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="711"/>
<source>Unable to create new entry.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="714"/>
<source>You didn&apos;t enter text.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="720"/>
<source>No node element selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="735"/>
<source>Unable to remove the entry.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="738"/>
<source>No entry selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="776"/>
<source>A field has to be selected since new fields are always inserted before the currently selected field.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="800"/>
<source>No fields have been removed since there are currently no fields selected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="821"/>
<source>You didn&apos;t enter a password. &lt;strong&gt;No encryption&lt;/strong&gt; will be used when saving the file next time.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="825"/>
<source>The new password will be used next time you save the file.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="829"/>
<source>You aborted. The old password will still be used when saving the file next time.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="858"/>
<source>Remove entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="872"/>
<source>Remove field</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="874"/>
<source>Copy for 5 seconds</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="893"/>
<source>The selected cell is empty.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/mainwindow.cpp" line="896"/>
<source>Exactly one cell needs to be selected.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtGui::PasswordGeneratorDialog</name>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="32"/>
<source>Password Generator</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="117"/>
<source>Use small letters</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="127"/>
<source>Use capital letters</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="137"/>
<source>Use digits</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="174"/>
<source>Length</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="181"/>
<source>Other characters to be used</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="188"/>
<source>!&quot;§$%&amp;/()=?;:_&apos;*~#,.-+&lt;&gt;|</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="229"/>
<source>&lt;b&gt;Password:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="248"/>
<source>Generate new password</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="264"/>
<source>Copy password</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.ui" line="311"/>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.cpp" line="130"/>
<source>Failed to generate password.
OpenSSL error: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.cpp" line="133"/>
<source>You have to select at least one checkbox.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/passwordgeneratordialog.cpp" line="136"/>
<source>The length has to be at least one.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>undocommands</name>
<message>
<location filename="../gui/undocommands.cpp" line="76"/>
<source>setting field name to »%1«</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="78"/>
<source>setting field name »%1« to »%2«</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="84"/>
<source>setting value of empty field to »%1«</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="86"/>
<source>setting value of »%1« field to »%2«</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<location filename="../gui/undocommands.cpp" line="117"/>
<source>insertion of %1 row(s) before row %2</source>
<translation type="unfinished">
<numerusform>insertion of %1 row before row %2</numerusform>
<numerusform>insertion of %1 rows before row %2</numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="../gui/undocommands.cpp" line="145"/>
<source>removal of row %1</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="../gui/undocommands.cpp" line="147"/>
<source>removal of the rows %1 to %2</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="188"/>
<source>selection of account »%1«</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="190"/>
<source>account selection repealed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="254"/>
<source>setting entry name to »%1«</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/undocommands.cpp" line="256"/>
<source>setting entry name from »%1« to »%2«</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<location filename="../gui/undocommands.cpp" line="297"/>
<source>insertion of %1 entry/entries</source>
<translation type="unfinished">
<numerusform>insertion of %1 entry</numerusform>
<numerusform>insertion of %1 entries</numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="../gui/undocommands.cpp" line="332"/>
<source>removal of %1 entry/entries</source>
<translation type="unfinished">
<numerusform>removal of %1 entry</numerusform>
<numerusform>removal of %1 entries</numerusform>
</translation>
</message>
<message numerus="yes">
<location filename="../gui/undocommands.cpp" line="372"/>
<source>move of %1 entry/entries</source>
<translation type="unfinished">
<numerusform>move of %1 entry</numerusform>
<numerusform>move of %1 entries</numerusform>
</translation>
</message>
</context>
</TS>

37
util/testroutines.cpp Normal file
View File

@ -0,0 +1,37 @@
#include "testroutines.h"
#include <c++utilities/io/binaryreader.h>
#include <c++utilities/io/binarywriter.h>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
using namespace IoUtilities;
namespace Testroutines {
void lengthPrefixedString()
{
stringstream stream;
BinaryReader reader(&stream);
BinaryWriter writer(&stream);
string string1("jöalfj32öl4fj34 f234ölf3je frasdölkajwe fqwöejkfwöfklja sdefölasje fasef jasöefjas efajs eflasje faöslefj asöflej asefölajsefl öasejföaslefja söef jaseö flajseflas jeföaslefj aslefjaweöflja4 rfq34jqlök4jfq ljase öfaje4fqp 34f89uj <pfj apefjawepfoi jaefoaje föasdjfaösefj a4jfase9fau sejfpas");
string string2;
string string3("asdfalsjd23öl4j3");
writer.writeLengthPrefixedString(string1);
writer.writeLengthPrefixedString(string2);
writer.writeLengthPrefixedString(string3);
if(reader.readLengthPrefixedString() == string1
&& reader.readLengthPrefixedString() == string2
&& reader.readLengthPrefixedString() == string3) {
cout << "test sucessfull" << endl;
} else {
cout << "test failed" << endl;
}
}
}

10
util/testroutines.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef TESTROUTINES_H
#define TESTROUTINES_H
namespace Testroutines {
void lengthPrefixedString();
}
#endif // TESTROUTINES_H