#!/usr/bin/perl -w --
# generated by wxGlade 0.4.1cvs on Fri Jan 26 18:24:52 2007
# To get wxPerl visit http://wxPerl.sourceforge.net/
$VERSION = 0.92;
use Wx 0.15 qw[:allclasses];
use strict;
package MainFrame;
use Wx qw[:everything];
use base qw(Wx::Frame);
use strict;
our $cb;
sub new {
my( $self, $parent, $id, $title, $pos, $size, $style, $name ) = @_;
$parent = undef unless defined $parent;
$id = -1 unless defined $id;
$title = "" unless defined $title;
$pos = wxDefaultPosition unless defined $pos;
$size = wxDefaultSize unless defined $size;
$name = "" unless defined $name;
# begin wxGlade: MainFrame::new
$style = wxDEFAULT_FRAME_STYLE
unless defined $style;
$self = $self->SUPER::new( $parent, $id, $title, $pos, $size, $style, $name );
$self->{sizer_account_staticbox} = Wx::StaticBox->new($self, -1, "Account" );
$self->{sizer_passwd_staticbox} = Wx::StaticBox->new($self, -1, "Password" );
$self->{sizer_note_staticbox} = Wx::StaticBox->new($self, -1, "Note" );
$self->{sizer_mod_staticbox} = Wx::StaticBox->new($self, -1, "Last update" );
$self->{sizer_category_staticbox} = Wx::StaticBox->new($self, -1, "Category" );
# Menu Bar
$self->{menubar} = Wx::MenuBar->new();
$self->SetMenuBar($self->{menubar});
my $wxglade_tmp_menu;
$wxglade_tmp_menu = Wx::Menu->new();
$wxglade_tmp_menu->Append(wxID_OPEN, "&Open...\tCtrl-O", "Select a file");
$wxglade_tmp_menu->AppendSeparator();
$wxglade_tmp_menu->Append(wxID_EXIT, "&Quit\tCtrl-Q", "Exit program");
$self->{menubar}->Append($wxglade_tmp_menu, "&File");
$wxglade_tmp_menu = Wx::Menu->new();
$wxglade_tmp_menu->AppendSeparator();
$wxglade_tmp_menu->Append(6001, "Select all", "Display all categories");
$wxglade_tmp_menu->Append(6002, "Deselect all", "Deselect all");
$self->{menubar}->Append($wxglade_tmp_menu, "&Categories");
$wxglade_tmp_menu = Wx::Menu->new();
$wxglade_tmp_menu->Append(wxID_ABOUT, "&About...", "About...");
$self->{menubar}->Append($wxglade_tmp_menu, "&Help");
# Menu Bar end
$self->{statusbar} = $self->CreateStatusBar(1, wxST_SIZEGRIP);
$self->{list_box_topic} = Wx::ListBox->new($self, -1, wxDefaultPosition, wxDefaultSize, [], wxLB_SINGLE|wxLB_ALWAYS_SB);
$self->{button_fetch} = Wx::Button->new($self, -1, "Fetch");
$self->{text_ctrl_category} = Wx::TextCtrl->new($self, -1, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY);
$self->{text_ctrl_account} = Wx::TextCtrl->new($self, -1, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY);
$self->{text_ctrl_passwd} = Wx::TextCtrl->new($self, -1, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY);
$self->{checkbox_pw_veil} = Wx::CheckBox->new($self, -1, "Veil", wxDefaultPosition, wxDefaultSize, );
$self->{checkbox_pw_clip} = Wx::CheckBox->new($self, -1, "Clip", wxDefaultPosition, wxDefaultSize, );
$self->{text_ctrl_note} = Wx::TextCtrl->new($self, -1, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY);
$self->{text_ctrl_mod} = Wx::TextCtrl->new($self, -1, "", wxDefaultPosition, wxDefaultSize, wxTE_READONLY);
$self->__set_properties();
$self->__do_layout();
Wx::Event::EVT_MENU($self, wxID_OPEN, \&OnOpen);
Wx::Event::EVT_MENU($self, wxID_EXIT, \&OnQuit);
Wx::Event::EVT_MENU($self, 6001, \&OnSelectAllCategories);
Wx::Event::EVT_MENU($self, 6002, \&OnDeselectAllCategories);
Wx::Event::EVT_MENU($self, wxID_ABOUT, \&OnAbout);
Wx::Event::EVT_LISTBOX_DCLICK($self, $self->{list_box_topic}->GetId, \&OnTopicActivate);
Wx::Event::EVT_LISTBOX($self, $self->{list_box_topic}->GetId, \&OnTopicSelect);
Wx::Event::EVT_BUTTON($self, $self->{button_fetch}->GetId, \&OnFetch);
Wx::Event::EVT_CHECKBOX($self, $self->{checkbox_pw_veil}->GetId, \&OnPwVeil);
Wx::Event::EVT_CHECKBOX($self, $self->{checkbox_pw_clip}->GetId, \&OnPwClip);
# end wxGlade
$self->SetSize($size);
return $self;
}
sub __set_properties {
my $self = shift;
# begin wxGlade: MainFrame::__set_properties
$self->SetTitle("GNU Keyring");
$self->{statusbar}->SetStatusWidths(-1);
my( @statusbar_fields ) = (
""
);
if( @statusbar_fields ) {
$self->{statusbar}->SetStatusText($statusbar_fields[$_], $_)
for 0 .. $#statusbar_fields ;
}
$self->{list_box_topic}->SetSelection(0);
$self->{button_fetch}->Enable(0);
$self->{checkbox_pw_veil}->SetValue(1);
# end wxGlade
}
sub __do_layout {
my $self = shift;
# begin wxGlade: MainFrame::__do_layout
$self->{outer_sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
$self->{info_sizer} = Wx::BoxSizer->new(wxVERTICAL);
$self->{sizer_mod}= Wx::StaticBoxSizer->new($self->{sizer_mod_staticbox}, wxHORIZONTAL);
$self->{sizer_note}= Wx::StaticBoxSizer->new($self->{sizer_note_staticbox}, wxHORIZONTAL);
$self->{sizer_passwd}= Wx::StaticBoxSizer->new($self->{sizer_passwd_staticbox}, wxVERTICAL);
$self->{sizer_pwprefs} = Wx::BoxSizer->new(wxHORIZONTAL);
$self->{sizer_account}= Wx::StaticBoxSizer->new($self->{sizer_account_staticbox}, wxHORIZONTAL);
$self->{sizer_category}= Wx::StaticBoxSizer->new($self->{sizer_category_staticbox}, wxHORIZONTAL);
$self->{sizer_list_box} = Wx::BoxSizer->new(wxVERTICAL);
$self->{sizer_list_box}->Add($self->{list_box_topic}, 1, wxEXPAND|wxFIXED_MINSIZE, 0);
$self->{sizer_list_box}->Add(1, 3, 0, wxADJUST_MINSIZE, 0);
$self->{sizer_list_box}->Add($self->{button_fetch}, 0, wxEXPAND|wxADJUST_MINSIZE, 0);
$self->{outer_sizer}->Add($self->{sizer_list_box}, 1, wxLEFT|wxTOP|wxBOTTOM|wxEXPAND, 5);
$self->{sizer_category}->Add($self->{text_ctrl_category}, 1, wxEXPAND|wxADJUST_MINSIZE, 0);
$self->{info_sizer}->Add($self->{sizer_category}, 0, wxEXPAND, 0);
$self->{sizer_account}->Add($self->{text_ctrl_account}, 1, wxADJUST_MINSIZE, 0);
$self->{info_sizer}->Add($self->{sizer_account}, 0, wxEXPAND, 0);
$self->{sizer_passwd}->Add($self->{text_ctrl_passwd}, 1, wxEXPAND|wxADJUST_MINSIZE, 0);
$self->{sizer_pwprefs}->Add($self->{checkbox_pw_veil}, 1, wxADJUST_MINSIZE, 0);
$self->{sizer_pwprefs}->Add($self->{checkbox_pw_clip}, 1, wxADJUST_MINSIZE, 0);
$self->{sizer_passwd}->Add($self->{sizer_pwprefs}, 0, wxEXPAND, 0);
$self->{info_sizer}->Add($self->{sizer_passwd}, 0, wxEXPAND, 0);
$self->{sizer_note}->Add($self->{text_ctrl_note}, 1, wxEXPAND|wxADJUST_MINSIZE, 0);
$self->{info_sizer}->Add($self->{sizer_note}, 1, wxEXPAND, 0);
$self->{sizer_mod}->Add($self->{text_ctrl_mod}, 1, wxADJUST_MINSIZE, 0);
$self->{info_sizer}->Add($self->{sizer_mod}, 0, wxEXPAND, 0);
$self->{outer_sizer}->Add($self->{info_sizer}, 1, wxALL|wxEXPAND, 5);
$self->SetAutoLayout(1);
$self->SetSizer($self->{outer_sizer});
$self->{outer_sizer}->Fit($self);
$self->{outer_sizer}->SetSizeHints($self);
$self->Layout();
# end wxGlade
}
sub OnFetch {
my ($self, $event) = @_;
# wxGlade: MainFrame::OnFetch <event_handler>
# Get the desired topic. Note it can be undefined.
my $topic = ($self->{list_box_topic}->GetSelections)[0];
unless ( defined($topic) ) {
$self->{button_fetch}->Enable(0);
return;
}
# Get the topic name from the list.
my $rec = $self->{_keyringsortedrecs}->[$topic];
# Create the password dialog, if needed.
my $d_passwd;
if ( !$self->{_keyringdecryptor} ) {
$d_passwd = Wx::TextEntryDialog->new
($self,
"Enter password for $self->{_keyringfile}",
"Enter Keyring password",
"",
wxOK|wxCANCEL|wxTE_PASSWORD,
wxDefaultPosition);
}
# Forever...
while ( 1 ) {
# If there's no decryptor, start the password dialog.
if ( !$self->{_keyringdecryptor} ) {
my $ret = $d_passwd->ShowModal;
if ( $ret != wxID_OK ) {
# Cancelled.
$d_passwd->Destroy;
return;
}
$ret = $d_passwd->GetValue;
$self->{_keyringdecryptor} = $self->{_keyringdb}->Password($ret);
# Check for a valid decryptor.
if ( !$self->{_keyringdecryptor} ) {
my $md = Wx::MessageDialog->new
($self, "Incorrect password",
"Password error",
wxOK|wxICON_ERROR,
wxDefaultPosition);
$md->ShowModal;
$md->Destroy;
next;
}
# It's valid, get rid of the dialog.
$d_passwd->Destroy;
}
# Get the record.
if ( !$rec ) {
my $md = Wx::MessageDialog->new
($self, "Topic \"$topic\" not found",
"Database error",
wxOK|wxICON_ERROR,
wxDefaultPosition);
$md->ShowModal;
$md->Destroy;
next;
}
# Decrypt it.
my ($acct) = $self->{_keyringdb}->Decrypt($rec);
# Show values.
$self->{text_ctrl_category}->SetValue(
$self->{_keyringcategories}->[$rec->{category}]);
$self->{text_ctrl_account}->SetValue($acct->{account});
if ( $self->{checkbox_pw_veil}->IsChecked ) {
$self->{text_ctrl_passwd}->SetValue("\x{2022}" x 8);
}
else {
$self->{text_ctrl_passwd}->SetValue($acct->{password});
}
if ( $self->{checkbox_pw_clip}->IsChecked ) {
$self->PutOnClipboard($acct->{password});
}
else {
$self->PutOnClipboard(undef);
}
$acct->{notes} =~ s/\n+$//;
$self->{text_ctrl_note}->SetValue($acct->{notes});
if ( $acct->{lastchange} ) {
$self->{text_ctrl_mod}->SetValue
(sprintf("%d-%02d-%02d",
1900 + $acct->{lastchange}->{year},
1 + $acct->{lastchange}->{month},
$acct->{lastchange}->{day}));
}
# Exit.
last;
}
# end wxGlade
}
sub OnQuit {
my ($self, $event) = @_;
# wxGlade: MainFrame::OnQuit <event_handler>
$self->Destroy;
# end wxGlade
}
sub OnTopicSelect {
my ($self, $event) = @_;
# wxGlade: MainFrame::OnTopicSelect <event_handler>
# We can fetch now.
$self->{button_fetch}->Enable(1);
# Clear values.
foreach ( qw(category account passwd note mod) ) {
$self->{"text_ctrl_$_"}->SetValue("");
}
# Display the current topic category.
my $topic = ($self->{list_box_topic}->GetSelections)[0];
unless ( defined($topic) ) {
$self->{button_fetch}->Enable(0);
return;
}
my $rec = $self->{_keyringsortedrecs}->[$topic];
$self->{text_ctrl_category}->SetValue($self->{_keyringcategories}->[ $rec->{category} ]);
# end wxGlade
}
sub OnTopicActivate {
my ($self, $event) = @_;
# wxGlade: MainFrame::OnTopicActivate <event_handler>
$self->OnFetch($event);
# end wxGlade
}
sub LoadKeyring {
my ($self, $file) = @_;
# Load the database.
eval {
$self->{_keyringdb} = ::loadKeyring($file);
};
if ( $@ ) {
my $msg = $@;
$msg =~ s/\n.*//;
my $md = Wx::MessageDialog->new
($self, $msg,
"Database error",
wxOK|wxICON_ERROR,
wxDefaultPosition);
$md->ShowModal;
$md->Destroy;
return;
}
$self->{_keyringfile} = $file;
my @cats;
foreach my $i (0..$#{ $self->{_keyringdb}->{appinfo}->{categories} }) {
push @cats, {
id => $i,
name => $self->{_keyringdb}->{appinfo}->{categories}->[$i]->{name},
}
}
$self->{_keyringcategories} = [ map { $_->{name} } @cats ];
$self->{_keyringsortedcategoryids} = [
map { $_->{id} } sort { $a->{name} cmp $b->{name} } @cats ];
undef($self->{_keyringdecryptor});
# It's no use to remember the cat mask, since codes will differ
# from file to file.
$self->{_catmask} = ~0;
# From the Categories menu, remove all but the last 3 (separator + ena/dis all).
my $catmenux = $self->{menubar}->FindMenu("Categories");
my Wx::Menu $catmenu = $self->{menubar}->GetMenu($catmenux);
my $count = $catmenu->GetMenuItemCount;
my $i;
for ( $i = 0; $i < $count-3; $i++ ) {
#### MEMORY LEAK ####
$catmenu->Remove($catmenu->FindItemByPosition(0));
}
# Prepend new choices to the Categories menu.
my $did = 0;
$self->{_catmaskids} = [];
foreach my $i (reverse 0..$#{ $self->{_keyringsortedcategoryids} }) {
my $cat_id = $self->{_keyringsortedcategoryids}->[$i];
my $name = $self->{_keyringcategories}->[$cat_id];
next if !$name;
$name ||= "<Unknown $i>";
my $id = ::next_id();
my $m = $catmenu->PrependCheckItem($id, $name, $name);
# Remember for toggle access.
$self->{_catmaskids}->[$i] = $m;
# Set check status.
$m->Check($self->{_catmask} & (1 << $i));
# Handler.
my $maskno = $i; # lexical copy
Wx::Event::EVT_MENU($self, $id, sub {
OnToggleCategory($self, $_[1], $maskno) });
# Yes.
$did++;
}
$self->UpdateCatMask;
$self->{statusbar}->SetStatusText("File: $file, ".
(@{ $self->{_keyringdb}->{records} })." entries.",
0);
}
sub OnOpen {
my ($self, $event) = @_;
# wxGlade: MainFrame::OnOpen <event_handler>
my $fd = Wx::FileDialog->new
($self,
"Choose Keyring",
"", "",
"*.pdb",
0,
wxDefaultPosition);
my $ret = $fd->ShowModal;
if ( $ret == wxID_OK ) {
$self->LoadKeyring($fd->GetPath);
}
$fd->Destroy;
# end wxGlade
}
sub OnAbout {
my ($self, $event) = @_;
# wxGlade: MainFrame::OnAbout <event_handler>
my $md = Wx::MessageDialog->new
($self,
"wxKeyring version $::VERSION\n".
::COPYRIGHT() . "\n\n".
"Written by Johan Vromans\n".
"<jvromans\@squirrel.nl>\n".
"http://www.squirrel.nl\n\n".
"GUI design with wxGlade, http://www.wxglade.org\n\n".
"Perl version ".sprintf("%vd",$^V)."\n".
"WxPerl version $Wx::VERSION\n".
"wxWidgets version ".Wx::wxVERSION."\n".
"Palm::Keyring version $Palm::Keyring::VERSION\n",
"About wxKeyring",
wxOK|wxICON_INFORMATION,
wxDefaultPosition);
$md->ShowModal;
$md->Destroy;
# end wxGlade
}
sub UpdateCatMask {
my ($self) = @_;
# Update check status on the category menu.
for ( my $i = 0; $i < 16; $i++ ) {
my $m = $self->{_catmaskids}->[$i];
next unless $m;
$m->Check($self->{_catmask} & (1 << $i));
}
# Update the list of displayed items.
my %id_by_cat;
foreach (0..$#{ $self->{_keyringsortedcategoryids} }) {
$id_by_cat{ $self->{_keyringsortedcategoryids}->[$_]} = $_;
}
$self->{_keyringsortedrecs} = [];
foreach my $rec ( sort { lc($a->{name}) cmp lc($b->{name}) }
@{ $self->{_keyringdb}->{records} } ) {
next unless $rec->{name};
if ($self->{_catmask} != ~0) {
next unless $self->{_catmask} & (1 <<
$id_by_cat{ $rec->{category} });
}
push @{$self->{_keyringsortedrecs}}, $rec;
}
$self->{list_box_topic}->Set( [ map { $_->{name} }
@{ $self->{_keyringsortedrecs} } ]);
}
sub OnSelectAllCategories {
my ($self, $event) = @_;
# wxGlade: MainFrame::OnSelectAllCategories <event_handler>
$self->{_catmask} = ~0;
$self->UpdateCatMask;
# end wxGlade
}
sub OnToggleCategory {
my ($self, $event, $cat) = @_;
# wxGlade: MainFrame::OnToggleCategory <event_handler>
$self->{_catmask} ^= (1 << $cat);
$self->UpdateCatMask;
# end wxGlade
}
sub OnDeselectAllCategories {
my ($self, $event) = @_;
# wxGlade: MainFrame::OnDeselectAllCategories <event_handler>
$self->{_catmask} = 0;
$self->UpdateCatMask;
# end wxGlade
}
sub OnPwVeil {
my ($self, $event) = @_;
# wxGlade: MainFrame::OnPwVeil <event_handler>
if ( $self->{checkbox_pw_veil}->IsChecked ) {
$self->{text_ctrl_passwd}->SetValue("\x{2022}" x 8);
}
else {
$self->OnFetch($event);
}
# end wxGlade
}
sub PutOnClipboard {
my ($self, $data) = @_;
for ( 0, 1 ) {
# Put it on the Clipboard as well as the Primary Selection.
wxTheClipboard->UsePrimarySelection($_);
if ( $data ) {
my $cb = Wx::TextDataObject->new($data);
wxTheClipboard->Open;
wxTheClipboard->SetData($cb);
wxTheClipboard->Close;
$self->{statusbar}->SetStatusText
("Password stored on Clipboard", 0);
$self->{_pwstored}++;
}
elsif ( $self->{_pwstored} ) {
wxTheClipboard->Open;
wxTheClipboard->Clear;
wxTheClipboard->Close;
$self->{statusbar}->SetStatusText
("Clipboard cleared", 0);
$self->{_pwstored} = 0 if $_;
}
}
}
sub OnPwClip {
my ($self, $event) = @_;
# wxGlade: MainFrame::OnPwClip <event_handler>
if ( $self->{checkbox_pw_clip}->IsChecked ) {
if ( $self->{checkbox_pw_veil}->IsChecked ) {
$self->OnFetch($event);
}
else {
$self->PutOnClipboard($self->{text_ctrl_passwd}->GetValue);
}
}
else {
$self->PutOnClipboard(undef);
}
# end wxGlade
}
# end of class MainFrame
1;
package main;
use Palm::Keyring;
use Wx qw(wxID_HIGHEST);
my $next_id;
sub next_id {
$next_id ||= wxID_HIGHEST;
++$next_id;
}
sub loadKeyring {
my $file = shift;
my $pdb = new Palm::Keyring;
$pdb->Load($file);
}
sub COPYRIGHT() {
"Copyright 2007 Squirrel Consultancy";
}
use Getopt::Long;
my $keyringfile;
my $geo = "450x300";
my $veil = 1;
my $clip = 0;
my $passwd;
# Note: Using --password is dangerous, and meant for testing only!
GetOptions("geometry=s" => \$geo,
"veil!" => \$veil,
"clip!" => \$clip,
"password=s" => \$passwd,
) && @ARGV <= 1
or die("Usage: $0 [ -geometry WxH+X+Y ] [ Keyring ]\n");
my ($w, $h, $x, $y) = (450, 300, -1, -1);
if ( $geo =~ /^(?:(\d+)x(\d+))?(?:\+(\d+)\+(\d+))?$/ ) {
$w = $1 if defined $1;
$h = $2 if defined $2;
$x = $3 if defined $3;
$y = $4 if defined $4;
}
else {
die("Invalid geometry: $geo\n");
}
$keyringfile = shift if @ARGV;
no warnings 'redefine';
local *Wx::App::OnInit = sub{1};
use warnings 'redefine';
my $app = Wx::App->new();
Wx::InitAllImageHandlers();
my $frame = MainFrame->new(undef,undef,undef,[$x,$y],[$w,$h]);
$app->SetTopWindow($frame);
$frame->Show(1);
$frame->LoadKeyring($keyringfile) if $keyringfile;
$frame->{statusbar}->SetStatusText(COPYRIGHT, 0);
# Presets.
$frame->{checkbox_pw_clip}->SetValue($clip);
$frame->{checkbox_pw_veil}->SetValue($veil);
if ( $passwd ) {
$frame->{_keyringdecryptor} = $frame->{_keyringdb}->Password($passwd);
$frame->{list_box_topic}->SetSelection(0);
$frame->OnFetch;
}
# And go...
$app->MainLoop();