#!/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 # 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 $self->Destroy; # end wxGlade } sub OnTopicSelect { my ($self, $event) = @_; # wxGlade: MainFrame::OnTopicSelect # 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 $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 ||= ""; 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 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 my $md = Wx::MessageDialog->new ($self, "wxKeyring version $::VERSION\n". ::COPYRIGHT() . "\n\n". "Written by Johan Vromans\n". "\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 $self->{_catmask} = ~0; $self->UpdateCatMask; # end wxGlade } sub OnToggleCategory { my ($self, $event, $cat) = @_; # wxGlade: MainFrame::OnToggleCategory $self->{_catmask} ^= (1 << $cat); $self->UpdateCatMask; # end wxGlade } sub OnDeselectAllCategories { my ($self, $event) = @_; # wxGlade: MainFrame::OnDeselectAllCategories $self->{_catmask} = 0; $self->UpdateCatMask; # end wxGlade } sub OnPwVeil { my ($self, $event) = @_; # wxGlade: MainFrame::OnPwVeil 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 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); return $pdb; } 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();