Kennwortmanager KeePassX Weiterentwicklung der Version 1
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
 
 
 
 
 
keepassx1/src/lib/EntryView.cpp

764 lines
22 KiB

/***************************************************************************
* Copyright (C) 2005-2007 by Tarek Saidi *
* tarek.saidi@arcor.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <QHeaderView>
#include <QClipboard>
#include <QFileDialog>
#include <QProcess>
#include "lib/AutoType.h"
#include "lib/EntryView.h"
#include "dialogs/EditEntryDlg.h"
// just for the lessThan funtion
QList<EntryViewItem*>* pItems;
KeepassEntryView* pEntryView;
KeepassEntryView::KeepassEntryView(QWidget* parent):QTreeWidget(parent){
ViewMode=Normal;
AutoResizeColumns=true;
header()->setResizeMode(QHeaderView::Interactive);
header()->setStretchLastSection(false);
header()->setClickable(true);
header()->setCascadingSectionResizes(true);
ColumnSizes=config->columnSizes();
Columns=config->columns();
ColumnOrder=config->columnOrder();
updateColumns();
header()->setSortIndicator(config->columnSort(), config->columnSortOrder());
connect(header(),SIGNAL(sectionResized(int,int,int)),this,SLOT(OnColumnResized(int,int,int)));
connect(this,SIGNAL(itemSelectionChanged()),this,SLOT(OnItemsChanged()));
connect(&ClipboardTimer, SIGNAL(timeout()), this, SLOT(OnClipboardTimeOut()));
connect(header(),SIGNAL(sectionClicked(int)),this,SLOT(OnHeaderSectionClicked(int)));
connect(header(),SIGNAL(sectionMoved(int,int,int)),this,SLOT(OnColumnMoved(int,int,int)));
Clipboard=QApplication::clipboard();
ContextMenu=new QMenu(this);
setAlternatingRowColors(config->alternatingRowColors());
pItems=&Items;
pEntryView=this;
}
KeepassEntryView::~KeepassEntryView(){
config->setColumnSizes(ColumnSizes);
config->setColumns(Columns);
config->setColumnOrder(ColumnOrder);
config->setColumnSort(header()->sortIndicatorSection());
config->setColumnSortOrder(header()->sortIndicatorOrder());
}
void KeepassEntryView::OnGroupChanged(IGroupHandle* group){
CurrentGroup=group;
showGroup(group);
}
void KeepassEntryView::OnShowSearchResults(){
CurrentGroup=NULL;
showSearchResults();
}
void KeepassEntryView::OnItemsChanged(){
switch(selectedItems().size()){
case 0: emit selectionChanged(NONE);
break;
case 1: emit selectionChanged(SINGLE);
break;
default:emit selectionChanged(MULTIPLE);
}
}
bool sortSearchResultsLessThan(const IEntryHandle* a, const IEntryHandle* b){
int indexA, indexB;
for(indexA=0;indexA<pItems->size();indexA++){
if((*pItems)[indexA]->EntryHandle==a)break;
}
for(indexB=0;indexB<pItems->size();indexB++){
if((*pItems)[indexB]->EntryHandle==b)break;
}
return pEntryView->indexOfTopLevelItem((*pItems)[indexA])<pEntryView->indexOfTopLevelItem((*pItems)[indexB]);
}
void KeepassEntryView::OnHeaderSectionClicked(int index){
if(header()->sortIndicatorSection()==index){
header()->setSortIndicator(index,header()->sortIndicatorOrder() ? Qt::DescendingOrder : Qt::AscendingOrder);
}
else{
header()->setSortIndicator(index,Qt::AscendingOrder);
}
if(ViewMode==Normal){
for(int i=0;i<Items.size();i++){
Items[i]->EntryHandle->setVisualIndexDirectly(indexOfTopLevelItem(Items[i]));
}
}
else if(ViewMode==ShowSearchResults){
if(header()->sortIndicatorOrder()==Qt::AscendingOrder)
qSort(SearchResults.begin(),SearchResults.end(),sortSearchResultsLessThan);
else
qSort(SearchResults.end(),SearchResults.begin(),sortSearchResultsLessThan);
}
}
void KeepassEntryView::OnSaveAttachment(){
if (selectedItems().size() == 0) return;
CEditEntryDlg::saveAttachment(((EntryViewItem*)selectedItems().first())->EntryHandle,this);
}
void KeepassEntryView::OnCloneEntry(){
QList<QTreeWidgetItem*> entries=selectedItems();
for(int i=0; i<entries.size();i++){
Items.append(new EntryViewItem(this));
Items.back()->EntryHandle=
db->cloneEntry(((EntryViewItem*)entries[i])->EntryHandle);
updateEntry(Items.back());
}
emit fileModified();
}
void KeepassEntryView::OnDeleteEntry(){
QList<QTreeWidgetItem*> entries=selectedItems();
if(config->askBeforeDelete()){
QString text;
if(entries.size()==1)
text=tr("Are you sure you want to delete this entry?");
else
text=tr("Are you sure you want to delete these %1 entries?").arg(entries.size());
if(QMessageBox::question(this,tr("Delete?"),text,QMessageBox::Yes | QMessageBox::No,QMessageBox::No)==QMessageBox::No)
return;
}
bool backup = false;
IGroupHandle* bGroup;
if (config->backup() && ((EntryViewItem*)entries[0])->EntryHandle->group() != (bGroup=db->backupGroup()))
backup = true;
for(int i=0; i<entries.size();i++){
IEntryHandle* entryHandle = ((EntryViewItem*)entries[i])->EntryHandle;
if (backup){
db->moveEntry(entryHandle, bGroup);
QDateTime now = QDateTime::currentDateTime();
entryHandle->setLastAccess(now);
entryHandle->setLastMod(now);
}
else{
db->deleteEntry(entryHandle);
}
Items.removeAt(Items.indexOf((EntryViewItem*)entries[i]));
delete entries[i];
}
emit fileModified();
}
void KeepassEntryView::updateEntry(EntryViewItem* item){
IEntryHandle* entry = item->EntryHandle;
int j=0;
if(Columns.at(0)){
item->setText(j++,entry->title());
item->setIcon(0,db->icon(entry->image()));
}
if (Columns.at(1)){
if(config->hideUsernames())
item->setText(j++,"******");
else
item->setText(j++,entry->username());
}
if (Columns.at(2)){item->setText(j++,entry->url());}
if (Columns.at(3)){
if(config->hidePasswords())
item->setText(j++,"******");
else{
SecString password=entry->password();
password.unlock();
item->setText(j++,password.string());
}
}
if (Columns.at(4)){
item->setText(j++,entry->comment().section('\n',0,0));}
if (Columns.at(5)){
item->setText(j++,entry->expire().dateToString(Qt::SystemLocaleDate));}
if (Columns.at(6)){
item->setText(j++,entry->creation().dateToString(Qt::SystemLocaleDate));}
if (Columns.at(7)){
item->setText(j++,entry->lastMod().dateToString(Qt::SystemLocaleDate));}
if (Columns.at(8)){
item->setText(j++,entry->lastAccess().dateToString(Qt::SystemLocaleDate));}
if (Columns.at(9)){
item->setText(j++,entry->binaryDesc());}
if(Columns.at(10) && ViewMode==ShowSearchResults){
item->setText(j,entry->group()->title());
item->setIcon(j++,db->icon(entry->group()->image()));
}
}
void KeepassEntryView::editEntry(EntryViewItem* item){
CEntry old = item->EntryHandle->data();
CEditEntryDlg dlg(db,item->EntryHandle,this,true);
int result = dlg.exec();
switch(result){
case 0: //canceled or no changes
break;
case 1: //modifications but same group
updateEntry(item);
emit fileModified();
break;
//entry moved to another group
case 2: //modified
case 3: //not modified
delete item;
Items.removeAt(Items.indexOf(item));
emit fileModified();
break;
}
IGroupHandle* bGroup;
if ((result==1 || result==2) && config->backup() && item->EntryHandle->group() != (bGroup=db->backupGroup())){
old.LastAccess = QDateTime::currentDateTime();
old.LastMod = old.LastAccess;
if (bGroup==NULL)
emit requestCreateGroup("Backup", 4, NULL);
if ((bGroup = db->backupGroup())!=NULL)
db->addEntry(&old, bGroup);
}
}
void KeepassEntryView::OnNewEntry(){
IEntryHandle* NewEntry = NULL;
if (!CurrentGroup){// We must be viewing search results. Add the new entry to the first group.
if (db->groups().size() > 0)
NewEntry=db->newEntry(db->sortedGroups()[0]);
else{
QMessageBox::critical(NULL,tr("Error"),tr("At least one group must exist before adding an entry."),tr("OK"));
}
}
else
NewEntry=db->newEntry(CurrentGroup);
CEditEntryDlg dlg(db,NewEntry,this,true,true);
if(!dlg.exec()){
db->deleteLastEntry();
}
else{
Items.append(new EntryViewItem(this));
Items.back()->EntryHandle=NewEntry;
updateEntry(Items.back());
emit fileModified();
if (header()->isSortIndicatorShown())
sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
}
}
void KeepassEntryView::OnEntryActivated(QTreeWidgetItem* item,int Column){
switch (columnListIndex(Column)){
case 0:
editEntry((EntryViewItem*)item);
break;
case 1:
OnUsernameToClipboard();
break;
case 2:
OnEditOpenUrl();
break;
case 3:
OnPasswordToClipboard();
break;
}
}
void KeepassEntryView::OnEditEntry(){
if (selectedItems().size() == 0) return;
editEntry((EntryViewItem*)selectedItems().first());
}
void KeepassEntryView::OnEditOpenUrl(){
if (selectedItems().size() == 0) return;
openBrowser( ((EntryViewItem*)selectedItems().first())->EntryHandle );
}
void KeepassEntryView::OnEditCopyUrl(){
if (selectedItems().size() == 0) return;
QString url = ((EntryViewItem*)selectedItems().first())->EntryHandle->url();
if (url.trimmed().isEmpty()) return;
if (url.startsWith("cmd://") && url.length()>6)
url = url.right(url.length()-6);
Clipboard->setText(url, QClipboard::Clipboard);
if(Clipboard->supportsSelection()){
Clipboard->setText(url, QClipboard::Selection);
}
}
void KeepassEntryView::OnUsernameToClipboard(){
if (selectedItems().size() == 0) return;
QString username = ((EntryViewItem*)selectedItems().first())->EntryHandle->username();
if (username.trimmed().isEmpty()) return;
Clipboard->setText(username, QClipboard::Clipboard);
if(Clipboard->supportsSelection()){
Clipboard->setText(username, QClipboard::Selection);
}
if (config->clipboardTimeOut()!=0) {
ClipboardTimer.setSingleShot(true);
ClipboardTimer.start(config->clipboardTimeOut()*1000);
}
}
void KeepassEntryView::OnPasswordToClipboard(){
if (selectedItems().size() == 0) return;
SecString password;
password=((EntryViewItem*)selectedItems().first())->EntryHandle->password();
password.unlock();
if (password.string().isEmpty()) return;
Clipboard->setText(password.string(), QClipboard::Clipboard);
if(Clipboard->supportsSelection()){
Clipboard->setText(password.string(), QClipboard::Selection);
}
if (config->clipboardTimeOut()!=0) {
ClipboardTimer.setSingleShot(true);
ClipboardTimer.start(config->clipboardTimeOut()*1000);
}
}
void KeepassEntryView::OnClipboardTimeOut(){
Clipboard->clear(QClipboard::Clipboard);
if(Clipboard->supportsSelection()){
Clipboard->clear(QClipboard::Selection);
}
#ifdef Q_WS_X11
static bool clearKlipper3 = true;
static bool clearKlipper4 = true;
if (clearKlipper3){
if (QProcess::execute("dcop klipper klipper clearClipboardHistory")!=0)
clearKlipper3 = false;
}
if (clearKlipper4){
if (QProcess::execute("dbus-send --type=method_call --print-reply --dest=org.kde.klipper /klipper org.kde.klipper.klipper.clearClipboardHistory")!=0)
clearKlipper4 = false;
}
#endif
}
void KeepassEntryView::contextMenuEvent(QContextMenuEvent* e){
if(itemAt(e->pos())){
EntryViewItem* item=(EntryViewItem*)itemAt(e->pos());
if(!selectedItems().size()){
setItemSelected(item,true);
}
else{
if(!isItemSelected(item)){
while(selectedItems().size()){
setItemSelected(selectedItems().first(),false);
}
setItemSelected(item,true);
}
}
}
else{
while (selectedItems().size())
setItemSelected(selectedItems().first(),false);
}
e->accept();
ContextMenu->popup(e->globalPos());
}
void KeepassEntryView::resizeEvent(QResizeEvent* e){
resizeColumns();
e->accept();
}
void KeepassEntryView::showSearchResults(){
if(ViewMode==Normal){
ViewMode=ShowSearchResults;
if(Columns.at(10))ColumnOrder[10]--;
updateColumns();
}
clear();
Items.clear();
createItems(SearchResults);
}
void KeepassEntryView::showGroup(IGroupHandle* group){
if(ViewMode==ShowSearchResults){
ViewMode=Normal;
updateColumns();
}
clear();
Items.clear();
if(group==NULL)return;
QList<IEntryHandle*>entries=db->entries(group);
createItems(entries);
}
void KeepassEntryView::createItems(QList<IEntryHandle*>& entries){
for(int i=0;i<entries.size();i++){
if(!entries[i]->isValid())continue;
EntryViewItem* item=new EntryViewItem(this);
Items.push_back(item);
Items.back()->EntryHandle=entries[i];
int j=0;
if (Columns.at(0)){
item->setText(j++,entries[i]->title());
item->setIcon(0,db->icon(entries[i]->image()));}
if (Columns.at(1)){
if(config->hideUsernames())
item->setText(j++,"******");
else
item->setText(j++,entries[i]->username());}
if (Columns.at(2)){item->setText(j++,entries[i]->url());}
if (Columns.at(3)){
if(config->hidePasswords())
item->setText(j++,"******");
else{
SecString password=entries[i]->password();
password.unlock();
item->setText(j++,password.string());
}
}
if (Columns.at(4)){
item->setText(j++,entries[i]->comment().section('\n',0,0));}
if (Columns.at(5)){
item->setText(j++,entries[i]->expire().dateToString(Qt::SystemLocaleDate));}
if (Columns.at(6)){
item->setText(j++,entries[i]->creation().dateToString(Qt::SystemLocaleDate));}
if (Columns.at(7)){
item->setText(j++,entries[i]->lastMod().dateToString(Qt::SystemLocaleDate));}
if (Columns.at(8)){
item->setText(j++,entries[i]->lastAccess().dateToString(Qt::SystemLocaleDate));}
if (Columns.at(9)){
item->setText(j++,entries[i]->binaryDesc());}
if(Columns.at(10) && ViewMode==ShowSearchResults){
item->setText(j,entries[i]->group()->title());
item->setIcon(j++,db->icon(entries[i]->group()->image()));}
}
}
void KeepassEntryView::updateIcons(){
for(int i=0;i<Items.size();i++){
Items[i]->setIcon(0,db->icon(Items[i]->EntryHandle->image()));
}
}
void KeepassEntryView::setEntry(IEntryHandle* entry){
}
void KeepassEntryView::updateColumns(){
setColumnCount(0);
QStringList cols;
if (Columns.at(0)){
cols << tr("Title");}
if (Columns.at(1)){
cols << tr("Username");}
if (Columns.at(2)){
cols << tr("URL");}
if (Columns.at(3)){
cols << tr("Password");}
if (Columns.at(4)){
cols << tr("Comments");}
if (Columns.at(5)){
cols << tr("Expires");}
if (Columns.at(6)){
cols << tr("Creation");}
if (Columns.at(7)){
cols << tr("Last Change");}
if (Columns.at(8)){
cols << tr("Last Access");}
if (Columns.at(9)){
cols << tr("Attachment");}
if(Columns.at(10) && ViewMode==ShowSearchResults){
cols << tr("Group");}
setHeaderLabels(cols);
for(int i=0;i<NUM_COLUMNS;i++){
if(!Columns.at(i))
ColumnOrder[i]=100;
}
QMap<int,int> Order;
for(int i=NUM_COLUMNS-1;i>=0;i--)
Order.insertMulti(ColumnOrder.at(i),i);
QMapIterator<int, int> i(Order);
while (i.hasNext()) {
i.next();
int index=i.value();
if(!Columns.at(index))continue;
header()->moveSection(header()->visualIndex(logicalColIndex(index)),header()->count()-1);
}
resizeColumns();
}
void KeepassEntryView::refreshItems(){
for (int i=0;i<Items.size();i++)
updateEntry(Items.at(i));
}
void KeepassEntryView::OnColumnMoved(int LogIndex,int OldVisIndex,int NewVisIndex){
for(int i=0;i<header()->count();i++){
ColumnOrder[columnListIndex(header()->logicalIndex(i))]=i;
}
sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder());
}
int KeepassEntryView::logicalColIndex(int LstIndex){
int c=-1;
for(int i=0;i<NUM_COLUMNS;i++){
if(Columns.at(i))
c++;
if(i==10 && Columns.at(10) && ViewMode!=ShowSearchResults)
c--;
if(i==LstIndex)
return c;
}
Q_ASSERT(false);
return -1;
}
void KeepassEntryView::resizeColumns(){
AutoResizeColumns=false;
int w=viewport()->width();
int sum=0;
for(int i=0;i<NUM_COLUMNS;i++){
// if(i==10) continue; //skip "Group" column
if(!Columns.at(i))
ColumnSizes[i]=0;
if(Columns.at(i) && !ColumnSizes.at(i))
ColumnSizes[i]=w/10;
}
for(int i=0;i<header()->count();i++){
sum+=ColumnSizes.at(columnListIndex(i));
}
float stretch=((float)w)/((float)sum);
sum=0;
for(int i=0;i<header()->count();i++){
int lstIndex=columnListIndex(header()->logicalIndex(i));
int NewSize=qRound(stretch*(float)ColumnSizes.at(lstIndex));
sum+=NewSize;
if(i==header()->count()-1){
NewSize+=(w-sum); // add rounding difference to the last column
}
header()->resizeSection(header()->logicalIndex(i),NewSize);
ColumnSizes[lstIndex]=NewSize;
}
AutoResizeColumns=true;
}
int KeepassEntryView::columnListIndex(int LogicalIndex){
int c=-1; int i;
for(i=0;i<NUM_COLUMNS;i++){
if(Columns.at(i))
c++;
if(i==10 && Columns.at(10) && ViewMode!=ShowSearchResults)
c--;
if(c==LogicalIndex)
break;
}
return i;
}
void KeepassEntryView::OnColumnResized(int lindex, int Old, int New){
if(!AutoResizeColumns)return;
for(int i=0;i<header()->count();i++){
ColumnSizes[columnListIndex(i)]=header()->sectionSize(i);
}
resizeColumns();
}
void KeepassEntryView::mousePressEvent(QMouseEvent *event){
//save event position - maybe this is the start of a drag
if (event->button() == Qt::LeftButton)
DragStartPos = event->pos();
QTreeWidget::mousePressEvent(event);
}
void KeepassEntryView::mouseMoveEvent(QMouseEvent *event){
if (!(event->buttons() & Qt::LeftButton))
return;
if ((event->pos() - DragStartPos).manhattanLength() < QApplication::startDragDistance())
return;
DragItems.clear();
EntryViewItem* DragStartItem=(EntryViewItem*)itemAt(DragStartPos);
if(!DragStartItem){
while(selectedItems().size()){
setItemSelected(selectedItems().first(),false);
}
return;
}
if(selectedItems().isEmpty()){
setItemSelected(DragStartItem,true);
}
else{
bool AlreadySelected=false;
for(int i=0;i<selectedItems().size();i++){
if(selectedItems()[i]==DragStartItem){
AlreadySelected=true;
break;
}
}
if(!AlreadySelected){
while(selectedItems().size()){
setItemSelected(selectedItems().first(),false);
}
setItemSelected(DragStartItem,true);
}
}
DragItems=selectedItems();
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
void* pDragItems=&DragItems;
mimeData->setData("text/plain;charset=UTF-8",DragItems.first()->text(0).toUtf8());
mimeData->setData("application/x-keepassx-entry",QByteArray((char*)&pDragItems,sizeof(void*)));
drag->setMimeData(mimeData);
EventOccurredBlock = true;
drag->exec(Qt::MoveAction);
EventOccurredBlock = false;
}
void KeepassEntryView::removeDragItems(){
for(int i=0;i<DragItems.size();i++){
for(int j=0;j<Items.size();j++){
if(Items[j]==DragItems[i]){
Items.removeAt(j);
j--;
delete DragItems[i];
}
}
}
}
#ifdef AUTOTYPE
void KeepassEntryView::OnAutoType(){
if (selectedItems().size() == 0) return;
autoType->perform(((EntryViewItem*)selectedItems().first())->EntryHandle);
}
#endif
void KeepassEntryView::paintEvent(QPaintEvent * event){
QTreeWidget::paintEvent(event);
}
EntryViewItem::EntryViewItem(QTreeWidget *parent):QTreeWidgetItem(parent){
}
EntryViewItem::EntryViewItem(QTreeWidget *parent, QTreeWidgetItem *preceding):QTreeWidgetItem(parent,preceding){
}
EntryViewItem::EntryViewItem(QTreeWidgetItem *parent):QTreeWidgetItem(parent){
}
EntryViewItem::EntryViewItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding):QTreeWidgetItem(parent,preceding){
}
bool EntryViewItem::operator<(const QTreeWidgetItem& other) const{
int SortCol = treeWidget()->header()->sortIndicatorSection();
int ListIndex = ((KeepassEntryView*)treeWidget())->columnListIndex(SortCol);
int comp = compare(other, SortCol, ListIndex);
if (comp!=0)
return (comp<0);
else {
int visibleCols = treeWidget()->header()->count() - treeWidget()->header()->hiddenSectionCount();
int ListIndexOrg = ListIndex;
for (int i=0; i<visibleCols; i++){
SortCol = treeWidget()->header()->logicalIndex(i);
ListIndex = ((KeepassEntryView*)treeWidget())->columnListIndex(SortCol);
if (ListIndex==ListIndexOrg || ListIndex==3) // sort or password column
continue;
comp = compare(other, SortCol, ListIndex);
if (comp!=0)
return (comp<0);
}
return true; // entries are equal
}
}
int EntryViewItem::compare(const QTreeWidgetItem& other, int col, int index) const{
if (index < 5 || index > 8){ //columns with string values (Title, Username, Password, URL, Comment, Group)
return QString::localeAwareCompare(text(col),other.text(col));
}
KpxDateTime DateThis;
KpxDateTime DateOther;
switch (index){
case 5:
DateThis=EntryHandle->expire();
DateOther=((EntryViewItem&)other).EntryHandle->expire();
break;
case 6:
DateThis=EntryHandle->creation();
DateOther=((EntryViewItem&)other).EntryHandle->creation();
break;
case 7:
DateThis=EntryHandle->lastMod();
DateOther=((EntryViewItem&)other).EntryHandle->lastMod();
break;
case 8:
DateThis=EntryHandle->lastAccess();
DateOther=((EntryViewItem&)other).EntryHandle->lastAccess();
break;
default:
Q_ASSERT(false);
}
if (DateThis==DateOther)
return 0;
else if (DateThis < DateOther)
return -1;
else
return 1;
}
void KeepassEntryView::setCurrentEntry(IEntryHandle* entry){
bool found=false;
int i;
for(i=0;i<Items.size();i++)
if(Items.at(i)->EntryHandle==entry){found=true; break;}
if(!found)return;
setCurrentItem(Items.at(i));
}