gtk2passwordapp 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +13 -35
- data/bin/gtk2passwordapp +52 -21
- data/lib/gtk2passwordapp.rb +241 -266
- data/lib/gtk2passwordapp/appconfig.rb +94 -26
- data/lib/gtk2passwordapp/passwords.rb +5 -3
- metadata +13 -13
data/README.txt
CHANGED
@@ -5,45 +5,23 @@ Uses crypt-tea's Tiny Encryption Algorithm to encrypt the datafile.
|
|
5
5
|
Features random password generator and clipboard use.
|
6
6
|
|
7
7
|
|
8
|
-
To add an account, enter the new account name in the "Account:" entry/combo box.
|
9
|
-
|
10
|
-
in the
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
"
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
To delete an account, select the account in the entry/combo box, and
|
20
|
-
the press the "Delete Account" button.
|
21
|
-
Once one is done with all updates, one then needs to press "Save To Disk".
|
22
|
-
"Cancel All Changes" or closing the window without "Save To Disk" will ignore all of the sessions updates.
|
23
|
-
|
24
|
-
The "Clip Current Password" button copies the current password to the primary clipboard.
|
25
|
-
The "Clip Previous Password" button copies the previous (old) password to the primary clipboard.
|
26
|
-
|
27
|
-
The "Change Data File Password" button will allow one the change
|
28
|
-
the master password. Do not forget the master password!
|
29
|
-
|
30
|
-
Right click most anywhere on the app's window for the main menu.
|
31
|
-
"Close" will dock the app and has the same effect as "Cancel All Changes".
|
32
|
-
|
33
|
-
Left click on the docked icon to bring back the editor window.
|
34
|
-
Right click on the docked icon to select one of the accounts to load
|
35
|
-
the password and username to the clipboard.
|
36
|
-
The password is copied to the primary clipboard and will paste on
|
37
|
-
middle mouse button click.
|
38
|
-
Right click on an entry box to paste the username (via the clipboard's menu).
|
8
|
+
To add an account, enter the new account name in the "Account:" entry/combo box. For the account, write the associated url, a note about the account, and the username in the appropriate entry boxes.
|
9
|
+
|
10
|
+
To set a new password, either enter the password in the "New:" entry box, or generate it by pressing "Random", "Alpha-Numeric", "Numeric", "Letters", or "All-Caps". One can set the password length generated with the spin-box. To make the password generated visible, un-check the check button.
|
11
|
+
|
12
|
+
Once one has edited the account, clicking the "Update Account" button finalizes the record. Note, however, that the change is not yet permanent and saved on disk. To delete an account, select the account in the entry/combo box, and the press the "Delete Account" button. Once one is done with all updates, one then needs to press "Save To Disk". Clicking the "Close" button or closing the window without "Save To Disk" will ignore all of the sessions updates.
|
13
|
+
|
14
|
+
The "Clip Current Password" button copies the current password to the primary clipboard. The "Clip Previous Password" button copies the previous (old) password to the primary clipboard. The "Change Data File Password" button will allow one the change the master password. Do not forget the master password!
|
15
|
+
|
16
|
+
Right click most anywhere on the app's window for the main menu. "Close" will dock the app and has the same effect as the "Close" button.
|
17
|
+
|
18
|
+
Left click on the docked icon to bring back the editor window. Right click on the docked icon to select one of the accounts to load the password and username to the clipboard. The password is copied to the primary clipboard and will paste on middle mouse button click. Right click on an entry box to paste the username (via the clipboard's menu).
|
39
19
|
|
40
20
|
Lastly, do not edit
|
41
21
|
~/.gtk2passwordapp-1/passphrase.txt
|
42
|
-
It's used to "salt" the password... without it,
|
43
|
-
one will not be able to decrypt the datafile.
|
22
|
+
It's used to "salt" the password... without it, one will not be able to decrypt the datafile.
|
44
23
|
|
45
24
|
For full documentation and comments, see
|
46
|
-
|
47
|
-
http://ruby-gnome-apps.blogspot.com/search/label/Passwords
|
25
|
+
https://sites.google.com/site/gtk2applib/home/gtk2applib-applications/gtk2passwordapp
|
48
26
|
|
49
27
|
carlosjhr64@gmail.com
|
data/bin/gtk2passwordapp
CHANGED
@@ -1,38 +1,69 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
2
|
require 'rubygems'
|
4
|
-
gem 'gtk2applib', '~>
|
3
|
+
gem 'gtk2applib', '~> 13.0'
|
5
4
|
require 'gtk2applib'
|
6
5
|
|
7
6
|
include Gtk2AppLib
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
program = Program.new( {
|
8
|
+
'name' => 'Ruby-Gnome Password Manager',
|
9
|
+
'authors' => ['carlosjhr64@gmail.com'],
|
10
|
+
'website' => 'https://sites.google.com/site/gtk2applib/home/gtk2applib-applications/gtk2passwordapp',
|
11
|
+
'website-label' => 'Ruby-Gnome Password Manager',
|
12
|
+
'license' => 'GPL',
|
13
|
+
'copyright' => '2011-03-07 17:05:39',
|
14
14
|
} )
|
15
15
|
|
16
16
|
begin
|
17
17
|
require 'gtk2passwordapp'
|
18
|
-
passwords = Gtk2PasswordApp::Passwords.new
|
19
|
-
Gtk2PasswordApp.build_menu(passwords)
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
:website => 'http://ruby-gnome-apps.blogspot.com/search/label/Passwords',
|
24
|
-
:website_label => 'Ruby-Gnome Password Manager',
|
25
|
-
:license => 'GPL',
|
26
|
-
:copyright => '2010-08-17 19:00:27',
|
27
|
-
}
|
19
|
+
passwords = Gtk2PasswordApp::Passwords.new
|
20
|
+
Gtk2PasswordApp.build_menu(program,passwords)
|
28
21
|
|
29
|
-
|
30
|
-
|
22
|
+
modified = false
|
23
|
+
program.window do |window|
|
24
|
+
Gtk2AppLib::Configuration::PARAMETERS[:Account_ComboBoxEntry][0] = passwords.accounts
|
25
|
+
Gtk2AppLib::Dialogs::DIALOG[:Window] = window
|
26
|
+
gui = Gtk2PasswordApp::Component::Gui.new(window) do |is,signal,*emits|
|
27
|
+
$stderr.puts "#{is},#{signal}:\t#{emits}" if $trace
|
28
|
+
Gtk2PasswordApp.method(signal).call(is,passwords) do |action|
|
29
|
+
case action
|
30
|
+
when :modified then modified ||= true
|
31
|
+
when :save
|
32
|
+
if modified then
|
33
|
+
Gtk2PasswordApp.save(program,passwords)
|
34
|
+
modified = false
|
35
|
+
else
|
36
|
+
Gtk2AppLib::DIALOGS.quick_message(*Gtk2PasswordApp::Configuration::NO_UPDATES)
|
37
|
+
end
|
38
|
+
when :close
|
39
|
+
if !modified || Gtk2AppLib::DIALOGS.question?(*Gtk2PasswordApp::Configuration::ARE_YOU_SURE) then
|
40
|
+
passwords.load # revert
|
41
|
+
modified = false
|
42
|
+
program.close
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
Gtk2PasswordApp::Component::SHARED[:Password_SpinButton].value = Gtk2PasswordApp::Configuration::DEFAULT_PASSWORD_LENGTH
|
48
|
+
Gtk2PasswordApp::Component::SHARED[:Account_ComboBoxEntry].active = Gtk2PasswordApp.index
|
49
|
+
window.signal_connect('destroy') do
|
50
|
+
if modified then
|
51
|
+
if Gtk2AppLib::DIALOGS.question?(*Gtk2PasswordApp::Configuration::WANT_TO_SAVE) then
|
52
|
+
Gtk2PasswordApp.save(program,passwords)
|
53
|
+
modified = false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
if modified then
|
57
|
+
passwords.load # revert
|
58
|
+
modified = false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
window.show_all
|
31
62
|
end
|
32
63
|
rescue Exception
|
33
64
|
$verbose = true
|
34
|
-
|
65
|
+
$!.puts_bang!
|
35
66
|
ensure
|
36
|
-
|
67
|
+
program.finalize
|
37
68
|
end
|
38
69
|
|
data/lib/gtk2passwordapp.rb
CHANGED
@@ -1,275 +1,250 @@
|
|
1
1
|
require 'gtk2passwordapp/passwords'
|
2
2
|
module Gtk2PasswordApp
|
3
|
-
include Configuration
|
4
|
-
PRIMARY = Gtk::Clipboard.get((SWITCH_CLIPBOARDS)? Gdk::Selection::CLIPBOARD: Gdk::Selection::PRIMARY)
|
5
|
-
CLIPBOARD = Gtk::Clipboard.get((SWITCH_CLIPBOARDS)? Gdk::Selection::PRIMARY: Gdk::Selection::CLIPBOARD)
|
6
3
|
|
7
|
-
|
8
|
-
|
9
|
-
passwords.accounts.each {|account|
|
10
|
-
item = PROGRAM.dock_menu.append_menu_item(account){
|
11
|
-
@@index = passwords.accounts.index(account)
|
12
|
-
PRIMARY.text = passwords.password_of(account)
|
13
|
-
CLIPBOARD.text = passwords.username_of(account)
|
14
|
-
}
|
15
|
-
item.child.modify_fg(Gtk::STATE_NORMAL, COLOR[:red]) if passwords.expired?(account)
|
16
|
-
}
|
17
|
-
PROGRAM.dock_menu.show_all
|
18
|
-
end
|
19
|
-
def self.rebuild_menu(passwords)
|
20
|
-
items = PROGRAM.dock_menu.children
|
21
|
-
3.times{ items.shift } # shift out Quit, Run, and Spacer
|
22
|
-
while item = items.shift do
|
23
|
-
PROGRAM.dock_menu.remove(item)
|
24
|
-
item.destroy
|
25
|
-
end
|
26
|
-
Gtk2PasswordApp.build_menu(passwords)
|
27
|
-
end
|
4
|
+
PRIMARY = Gtk::Clipboard.get((Configuration::SWITCH_CLIPBOARDS)? Gdk::Selection::CLIPBOARD: Gdk::Selection::PRIMARY)
|
5
|
+
CLIPBOARD = Gtk::Clipboard.get((Configuration::SWITCH_CLIPBOARDS)? Gdk::Selection::PRIMARY: Gdk::Selection::CLIPBOARD)
|
28
6
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
]
|
48
|
-
|
49
|
-
TEXT = {
|
50
|
-
# Labels
|
51
|
-
:account => 'Account',
|
52
|
-
:note => 'Note',
|
53
|
-
:password => 'New',
|
54
|
-
# Buttons
|
55
|
-
:username => 'Username',
|
56
|
-
:current => 'Clip Current Password',
|
57
|
-
:url => 'Url',
|
58
|
-
:note => 'Note',
|
59
|
-
:edit => 'Edit',
|
60
|
-
:update => 'Update Account',
|
61
|
-
:alphanum => 'Alpha-Numeric',
|
62
|
-
:num => 'Numeric',
|
63
|
-
:alpha => 'Letters',
|
64
|
-
:caps => 'All-Caps',
|
65
|
-
:random => 'Random',
|
66
|
-
:previous => 'Clip Previous Password',
|
67
|
-
:quit => 'Quit',
|
68
|
-
:cancel => 'Cancel All Changes',
|
69
|
-
:save => 'Save To Disk',
|
70
|
-
:cpwd => 'Change Data File Password',
|
71
|
-
:delete => 'Delete Account',
|
72
|
-
}
|
73
|
-
|
74
|
-
def self.edit(window, passwords)
|
75
|
-
dialog_options = {:window=>window}
|
76
|
-
updated = false # only saves data if data updated
|
7
|
+
@@index = 0
|
8
|
+
def self.index
|
9
|
+
@@index
|
10
|
+
end
|
11
|
+
def self.build_menu(program,passwords)
|
12
|
+
if program.icon? then
|
13
|
+
program.clear_dock_menu
|
14
|
+
program.append_dock_menu(Gtk::SeparatorMenuItem.new)
|
15
|
+
passwords.accounts.each do |account|
|
16
|
+
item = program.append_dock_menu(account) do
|
17
|
+
@@index = passwords.accounts.index(account)
|
18
|
+
PRIMARY.text = passwords.password_of(account)
|
19
|
+
CLIPBOARD.text = passwords.username_of(account)
|
20
|
+
end
|
21
|
+
item.child.modify_fg(Gtk::STATE_NORMAL, Configuration::EXPIRED_COLOR) if passwords.expired?(account)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
77
25
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
EDITOR_LABELS.each {|s|
|
84
|
-
hbox = Gtk2AppLib::HBox.new(vbox)
|
85
|
-
label = Gtk2AppLib::Label.new(TEXT[s]+':',hbox,{:label_width=>LABEL_WIDTH})
|
86
|
-
dx = ((s == :password)? (SPIN_BUTTON_LENGTH+2*PADDING+30): ((s == :url)? (GO_BUTTON_LENGTH+2*PADDING): 0))
|
87
|
-
width = ENTRY_WIDTH - dx
|
88
|
-
widget[s] = (s==:account)?
|
89
|
-
Gtk2AppLib::ComboBoxEntry.new(passwords.accounts,hbox,{:comboboxentry_width=>width}) :
|
90
|
-
Gtk2AppLib::Entry.new('',hbox,{:entry_width=>width})
|
26
|
+
def self.save(program,passwords)
|
27
|
+
dumpfile = passwords.save
|
28
|
+
Gtk2PasswordApp.passwords_updated(dumpfile)
|
29
|
+
Gtk2PasswordApp.build_menu(program,passwords)
|
30
|
+
end
|
91
31
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
pwdlength.value = DEFAULT_PASSWORD_LENGTH
|
96
|
-
elsif s == :url then
|
97
|
-
goto_url = Gtk2AppLib::Button.new('Go',hbox,{:button_width=>GO_BUTTON_LENGTH}){
|
98
|
-
# The go button opens the url in a browser
|
99
|
-
system("#{APP[:browser]} #{widget[:url].text} > /dev/null 2> /dev/null &")
|
100
|
-
}
|
101
|
-
end
|
102
|
-
}
|
32
|
+
def self.get_salt(prompt,title=prompt)
|
33
|
+
Gtk2AppLib::DIALOGS.entry( prompt, {:Title=>title, :Entry => [{:visibility= => false},'activate']} )
|
34
|
+
end
|
103
35
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
widget[:account].active = @@index if @@index
|
110
|
-
account_changed = proc {
|
111
|
-
account = (widget[:account].active_text)? widget[:account].active_text.strip: ''
|
112
|
-
if account.length > 0 then
|
113
|
-
widget[:password].text = ''
|
114
|
-
if passwords.include?(account) then
|
115
|
-
widget[:url].text = passwords.url_of(account)
|
116
|
-
widget[:note].text = passwords.note_of(account)
|
117
|
-
widget[:username].text = passwords.username_of(account)
|
118
|
-
else
|
119
|
-
widget[:url].text = ''
|
120
|
-
widget[:note].text = ''
|
121
|
-
widget[:username].text = ''
|
122
|
-
end
|
123
|
-
@@index = widget[:account].active
|
124
|
-
end
|
125
|
-
}
|
126
|
-
account_changed.call
|
127
|
-
widget[:account].signal_connect('changed'){
|
128
|
-
account_changed.call
|
129
|
-
}
|
130
|
-
|
131
|
-
# New Password
|
132
|
-
widget[:password].visibility = false
|
133
|
-
|
134
|
-
# Update
|
135
|
-
widget[:update].signal_connect('clicked'){
|
136
|
-
url = widget[:url].text.strip
|
137
|
-
if url.length == 0 || url =~ URL_PATTERN then
|
138
|
-
account = (widget[:account].active_text)? widget[:account].active_text.strip: ''
|
139
|
-
if account.length > 0 then
|
140
|
-
updated = true if !updated
|
141
|
-
if !passwords.include?(account) then
|
142
|
-
passwords.add(account)
|
143
|
-
@@index = i = passwords.accounts.index(account)
|
144
|
-
widget[:account].insert_text(i,account)
|
145
|
-
end
|
146
|
-
passwords.url_of(account, url)
|
147
|
-
passwords.note_of(account, widget[:note].text.strip)
|
148
|
-
passwords.username_of(account, widget[:username].text.strip)
|
149
|
-
password = widget[:password].text.strip
|
150
|
-
if password.length > 0 then
|
151
|
-
passwords.password_of(account, password) if !passwords.verify?(account, password)
|
152
|
-
widget[:password].text = ''
|
153
|
-
end
|
154
|
-
end
|
155
|
-
else
|
156
|
-
Gtk2AppLib::DIALOGS.quick_message('Need url like http://www.site.com/page.html', dialog_options)
|
157
|
-
end
|
158
|
-
}
|
159
|
-
|
160
|
-
# Random
|
161
|
-
widget[:random].signal_connect('clicked'){
|
162
|
-
suggestion = ''
|
163
|
-
pwdlength.value.to_i.times do
|
164
|
-
suggestion += (rand(94)+33).chr
|
165
|
-
end
|
166
|
-
widget[:password].text = suggestion
|
167
|
-
}
|
168
|
-
# Alpha-Numeric
|
169
|
-
widget[:alphanum].signal_connect('clicked'){
|
170
|
-
suggestion = ''
|
171
|
-
while suggestion.length < pwdlength.value.to_i do
|
172
|
-
chr = (rand(75)+48).chr
|
173
|
-
suggestion += chr if chr =~/\w/
|
174
|
-
end
|
175
|
-
widget[:password].text = suggestion
|
176
|
-
}
|
177
|
-
# Numeric
|
178
|
-
widget[:num].signal_connect('clicked'){
|
179
|
-
suggestion = ''
|
180
|
-
pwdlength.value.to_i.times do
|
181
|
-
chr = (rand(10)+48).chr
|
182
|
-
suggestion += chr
|
183
|
-
end
|
184
|
-
widget[:password].text = suggestion
|
185
|
-
}
|
186
|
-
# Letters
|
187
|
-
widget[:alpha].signal_connect('clicked'){
|
188
|
-
suggestion = ''
|
189
|
-
while suggestion.length < pwdlength.value.to_i do
|
190
|
-
chr = (rand(58)+65).chr
|
191
|
-
suggestion += chr if chr =~/[A-Z]/i
|
192
|
-
end
|
193
|
-
widget[:password].text = suggestion
|
194
|
-
}
|
195
|
-
# Caps
|
196
|
-
widget[:caps].signal_connect('clicked'){
|
197
|
-
suggestion = ''
|
198
|
-
pwdlength.value.to_i.times do
|
199
|
-
chr = (rand(26)+65).chr
|
200
|
-
suggestion += chr
|
201
|
-
end
|
202
|
-
widget[:password].text = suggestion
|
203
|
-
}
|
204
|
-
|
205
|
-
# Visibility
|
206
|
-
visibility.signal_connect('clicked'){
|
207
|
-
widget[:password].visibility = !widget[:password].visibility?
|
208
|
-
}
|
209
|
-
|
210
|
-
# Current
|
211
|
-
widget[:current].signal_connect('clicked'){
|
212
|
-
account = (widget[:account].active_text)? widget[:account].active_text.strip: ''
|
213
|
-
PRIMARY.text = passwords.password_of(account)
|
214
|
-
CLIPBOARD.text = passwords.username_of(account)
|
215
|
-
}
|
216
|
-
|
217
|
-
# Previous
|
218
|
-
widget[:previous].signal_connect('clicked'){
|
219
|
-
account = (widget[:account].active_text)? widget[:account].active_text.strip: ''
|
220
|
-
PRIMARY.text = passwords.previous_password_of(account)
|
221
|
-
CLIPBOARD.text = passwords.username_of(account)
|
222
|
-
}
|
223
|
-
|
224
|
-
# Change Password
|
225
|
-
widget[:cpwd].signal_connect('clicked'){
|
226
|
-
if pwd1 = Gtk2PasswordApp.get_salt('New Password') then
|
227
|
-
if pwd2 = Gtk2PasswordApp.get_salt('Verify') then
|
228
|
-
while !(pwd1==pwd2) do
|
229
|
-
pwd1 = Gtk2PasswordApp.get_salt('Try again!')
|
230
|
-
return if !pwd1
|
231
|
-
pwd2 = Gtk2PasswordApp.get_salt('Verify')
|
232
|
-
return if !pwd2
|
233
|
-
end
|
234
|
-
Gtk2AppLib.passwords_updated( passwords.save(pwd1) )
|
235
|
-
end
|
236
|
-
end
|
237
|
-
}
|
238
|
-
|
239
|
-
# Save
|
240
|
-
widget[:save].signal_connect('clicked'){
|
241
|
-
if updated then
|
242
|
-
Gtk2AppLib.passwords_updated( passwords.save )
|
243
|
-
updated = false
|
244
|
-
Gtk2PasswordApp.rebuild_menu(passwords)
|
245
|
-
end
|
246
|
-
PROGRAM.close
|
247
|
-
}
|
248
|
-
|
249
|
-
# Delete
|
250
|
-
widget[:delete].signal_connect('clicked'){
|
251
|
-
account = (widget[:account].active_text)? widget[:account].active_text.strip: nil
|
252
|
-
if account then
|
253
|
-
i = passwords.accounts.index(account)
|
254
|
-
if i then
|
255
|
-
passwords.delete(account)
|
256
|
-
widget[:account].remove_text(i)
|
257
|
-
@@index = (widget[:account].active = (i > 0)? i - 1: 0)
|
258
|
-
updated = true
|
259
|
-
Gtk2AppLib::DIALOGS.quick_message("#{account} deleted.", dialog_options)
|
260
|
-
end
|
261
|
-
end
|
262
|
-
}
|
36
|
+
def self.account( shared = Component::SHARED )
|
37
|
+
account = (shared[:Account_ComboBoxEntry].active_text)? shared[:Account_ComboBoxEntry].active_text.strip: ''
|
38
|
+
(account.length>0)? account: nil
|
39
|
+
end
|
263
40
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
41
|
+
def self.clicked(is,passwords)
|
42
|
+
|
43
|
+
shared = Component::SHARED
|
44
|
+
pwdlength = shared[:Password_SpinButton]
|
45
|
+
account = Gtk2PasswordApp.account
|
46
|
+
|
47
|
+
case is
|
48
|
+
|
49
|
+
when shared[:Url_Button]
|
50
|
+
url = shared[:Url_Entry].text.strip
|
51
|
+
Gtk2AppLib.run(url) if url =~ Configuration::URL_PATTERN
|
52
|
+
|
53
|
+
when shared[:Random_Button]
|
54
|
+
suggestion = ''
|
55
|
+
pwdlength.value.to_i.times do
|
56
|
+
suggestion += (rand(94)+33).chr
|
57
|
+
end
|
58
|
+
shared[:Password_Entry].text = suggestion
|
59
|
+
|
60
|
+
when shared[:Alpha_Button]
|
61
|
+
suggestion = ''
|
62
|
+
while suggestion.length < pwdlength.value.to_i do
|
63
|
+
chr = (rand(75)+48).chr
|
64
|
+
suggestion += chr if chr =~/\w/
|
65
|
+
end
|
66
|
+
shared[:Password_Entry].text = suggestion
|
67
|
+
|
68
|
+
when shared[:Numeric_Button]
|
69
|
+
suggestion = ''
|
70
|
+
pwdlength.value.to_i.times do
|
71
|
+
chr = (rand(10)+48).chr
|
72
|
+
suggestion += chr
|
73
|
+
end
|
74
|
+
shared[:Password_Entry].text = suggestion
|
75
|
+
|
76
|
+
when shared[:Letters_Button]
|
77
|
+
suggestion = ''
|
78
|
+
while suggestion.length < pwdlength.value.to_i do
|
79
|
+
chr = (rand(58)+65).chr
|
80
|
+
suggestion += chr if chr =~/[A-Z]/i
|
81
|
+
end
|
82
|
+
shared[:Password_Entry].text = suggestion
|
83
|
+
|
84
|
+
when shared[:Caps_Button]
|
85
|
+
suggestion = ''
|
86
|
+
pwdlength.value.to_i.times do
|
87
|
+
chr = (rand(26)+65).chr
|
88
|
+
suggestion += chr
|
89
|
+
end
|
90
|
+
shared[:Password_Entry].text = suggestion
|
91
|
+
|
92
|
+
when shared[:Delete_Button]
|
93
|
+
# MODIFIES!!!
|
94
|
+
if account then
|
95
|
+
i = passwords.accounts.index(account)
|
96
|
+
if i then
|
97
|
+
passwords.delete(account)
|
98
|
+
shared[:Account_ComboBoxEntry].remove_text(i)
|
99
|
+
@@index = (shared[:Account_ComboBoxEntry].active = (i > 0)? i - 1: 0)
|
100
|
+
yield(:modified)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
when shared[:Update_Button]
|
105
|
+
# MODIFIES!!!
|
106
|
+
if account then
|
107
|
+
url = shared[:Url_Entry].text.strip
|
108
|
+
if url.length == 0 || url =~ Configuration::URL_PATTERN then
|
109
|
+
yield(:modified)
|
110
|
+
if !passwords.include?(account) then
|
111
|
+
passwords.add(account)
|
112
|
+
@@index = i = passwords.accounts.index(account)
|
113
|
+
shared[:Account_ComboBoxEntry].insert_text(i,account)
|
114
|
+
end
|
115
|
+
passwords.url_of(account, url)
|
116
|
+
passwords.note_of(account, shared[:Note_Entry].text.strip)
|
117
|
+
passwords.username_of(account, shared[:Username_Entry].text.strip)
|
118
|
+
password = shared[:Password_Entry].text.strip
|
119
|
+
passwords.password_of(account, password) if !passwords.verify?(account, password)
|
120
|
+
Gtk2AppLib::DIALOGS.quick_message(*Configuration::UPDATED)
|
121
|
+
else
|
122
|
+
Gtk2AppLib::DIALOGS.quick_message(*Configuration::BAD_URL)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
when shared[:Datafile_Button]
|
127
|
+
if pwd1 = Gtk2PasswordApp.get_salt('New Password') then
|
128
|
+
if pwd2 = Gtk2PasswordApp.get_salt('Verify') then
|
129
|
+
while !(pwd1==pwd2) do
|
130
|
+
pwd1 = Gtk2PasswordApp.get_salt('Try again!')
|
131
|
+
return if !pwd1
|
132
|
+
pwd2 = Gtk2PasswordApp.get_salt('Verify')
|
133
|
+
return if !pwd2
|
134
|
+
end
|
135
|
+
dumpfile = passwords.save(pwd1)
|
136
|
+
Gtk2PasswordApp.passwords_updated(dumpfile)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
when shared[:Current_Button]
|
141
|
+
if account then
|
142
|
+
PRIMARY.text = passwords.password_of(account)
|
143
|
+
CLIPBOARD.text = passwords.username_of(account)
|
144
|
+
end
|
145
|
+
|
146
|
+
when shared[:Previous_Button]
|
147
|
+
if account then
|
148
|
+
PRIMARY.text = passwords.previous_password_of(account)
|
149
|
+
CLIPBOARD.text = passwords.username_of(account)
|
150
|
+
end
|
151
|
+
|
152
|
+
when shared[:Save_Button] then yield(:save)
|
153
|
+
when shared[:Cancel_Button] then yield(:close)
|
154
|
+
else $stderr.puts "What? #{is} in #{is.parent.class}."
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.changed(is,passwords)
|
160
|
+
shared = Component::SHARED
|
161
|
+
account = Gtk2PasswordApp.account
|
162
|
+
case is
|
163
|
+
when shared[:Account_ComboBoxEntry]
|
164
|
+
if account then
|
165
|
+
shared[:Password_Entry].text = ''
|
166
|
+
if passwords.include?(account) then
|
167
|
+
shared[:Url_Entry].text = passwords.url_of(account)
|
168
|
+
shared[:Note_Entry].text = passwords.note_of(account)
|
169
|
+
shared[:Username_Entry].text = passwords.username_of(account)
|
170
|
+
shared[:Password_Entry].text = passwords.password_of(account)
|
171
|
+
else
|
172
|
+
shared[:Url_Entry].text = ''
|
173
|
+
shared[:Note_Entry].text = ''
|
174
|
+
shared[:Username_Entry].text = ''
|
175
|
+
end
|
176
|
+
@@index = is.active
|
177
|
+
$stderr.puts "Index: #{@@index}" if $trace
|
178
|
+
end
|
179
|
+
else
|
180
|
+
$stderr.puts "What? Expected #{shared[:Account_ComboBoxEntry]}, got #{is}."
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def self.toggled(is,passwords)
|
185
|
+
pwd = Component::SHARED[:Password_Entry]
|
186
|
+
pwd.visibility = !pwd.visibility?
|
187
|
+
end
|
188
|
+
|
189
|
+
module Component
|
190
|
+
SHARED = {}
|
191
|
+
|
192
|
+
updates = [:Delete_Button,:Update_Button,:Save_Button]
|
193
|
+
updates.unshift(:Cancel_Button) if Gtk2AppLib::Configuration::MENU[:close]
|
194
|
+
|
195
|
+
vbox = 'Gtk2AppLib::Widgets::VBox'
|
196
|
+
hbox = 'Gtk2AppLib::Widgets::HBox'
|
197
|
+
classes = [
|
198
|
+
['Gui', vbox, [:Account_Component,:Url_Component,:Note_Component,:Username_Component,:Password_Component,:Buttons_Component]],
|
199
|
+
['Account', hbox, [:Account_Label,:Account_ComboBoxEntry]],
|
200
|
+
['Url', hbox, [:Url_Label,:Url_Entry,:Url_Button]],
|
201
|
+
['Note', hbox, [:Note_Label,:Note_Entry]],
|
202
|
+
['Username', hbox, [:Username_Label,:Username_Entry]],
|
203
|
+
['Password', hbox, [:Password_Label,:Password_Entry,:Password_CheckButton,:Password_SpinButton]],
|
204
|
+
['Buttons', vbox, [:Generators_Component,:Updates_Component,:Datafile_Component,:Clip_Component]],
|
205
|
+
['Generators', hbox, [:Random_Button,:Alpha_Button,:Numeric_Button,:Letters_Button,:Caps_Button]],
|
206
|
+
['Updates', hbox, updates],
|
207
|
+
['Datafile', hbox, [:Datafile_Button]],
|
208
|
+
['Clip', hbox, [:Current_Button,:Previous_Button]],
|
209
|
+
]
|
210
|
+
classes.each do |clss,spr,keys|
|
211
|
+
code = Gtk2AppLib::Component.define(clss,spr,keys)
|
212
|
+
$stderr.puts "<<<START CODE EVAL>>>\n#{code}\n<<<END CODE EVAL>>>" if $trace && $verbose
|
213
|
+
eval( code )
|
214
|
+
end
|
215
|
+
|
216
|
+
def self.init_code(clss,keys)
|
217
|
+
code = <<-EOT
|
218
|
+
class #{clss}
|
219
|
+
def _init(block)
|
220
|
+
EOT
|
221
|
+
keys.each do |key|
|
222
|
+
code += <<-EOT
|
223
|
+
SHARED[:#{key}] = self.#{key.to_s.downcase}
|
224
|
+
EOT
|
225
|
+
end
|
226
|
+
code += <<-EOT
|
227
|
+
end
|
228
|
+
end
|
229
|
+
EOT
|
230
|
+
code
|
231
|
+
end
|
232
|
+
|
233
|
+
# Regardless of where in the gui they are...
|
234
|
+
[ ['Account', [:Account_ComboBoxEntry]],
|
235
|
+
['Url', [:Url_Entry,:Url_Button]],
|
236
|
+
['Note', [:Note_Entry]],
|
237
|
+
['Username', [:Username_Entry]],
|
238
|
+
['Password', [:Password_Entry,:Password_SpinButton]],
|
239
|
+
['Generators', [:Random_Button,:Alpha_Button,:Numeric_Button,:Letters_Button,:Caps_Button]],
|
240
|
+
['Updates', updates],
|
241
|
+
['Datafile', [:Datafile_Button]],
|
242
|
+
['Clip', [:Current_Button,:Previous_Button]],
|
243
|
+
].each do |clss,keys|
|
244
|
+
code = Component.init_code(clss,keys)
|
245
|
+
$stderr.puts "<<<START CODE EVAL>>>\n#{code}\n<<<END CODE EVAL>>>" if $trace && $verbose
|
246
|
+
eval( code )
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
275
250
|
end
|
@@ -2,47 +2,115 @@
|
|
2
2
|
# Do not edit or delete passphrase, or you'll loose your passwords data.
|
3
3
|
|
4
4
|
module Gtk2AppLib
|
5
|
+
# Only one gtk2passwordapp will be allowed to run.
|
6
|
+
# Will kill a duplicate proccess...
|
7
|
+
Lock.lock_mode
|
5
8
|
module Configuration
|
6
|
-
# Note that the passwords data file name is auto generated, but...
|
7
|
-
# You can place your passwords data file in a directory other than ~/gtk2passwordapp-*
|
8
|
-
PASSWORDS_DATA_DIR = UserSpace::DIRECTORY
|
9
9
|
|
10
|
-
|
10
|
+
padding = Widgets::WIDGET[:Widgets][:pack_start].last
|
11
|
+
go = 50
|
12
|
+
label = 75
|
13
|
+
entry = 300
|
14
|
+
spin = 60
|
15
|
+
check = 20
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
+
if HILDON then
|
18
|
+
# WIDGET_OPTIONS[:font] =
|
19
|
+
FONT[:Normal] = FONT[:Large] = Pango::FontDescription.new( 'Arial 18' )
|
20
|
+
go = 75
|
21
|
+
label = 150
|
22
|
+
entry = 500
|
23
|
+
spin = 75
|
24
|
+
check = 25
|
25
|
+
if Gtk2AppLib::Configuration::OSTYPE == 'Internet Tablet OS: maemo Linux based OS2008' then
|
26
|
+
# Icon works on N800, but not N800 (Maemo 5)
|
27
|
+
MENU[:close] = '_Close'
|
28
|
+
end
|
29
|
+
else
|
30
|
+
WINDOW_DEFAULT_SIZE[0],WINDOW_DEFAULT_SIZE[1] = 100,100
|
31
|
+
MENU[:close] = '_Close'
|
32
|
+
end
|
17
33
|
|
34
|
+
wrap = {:wrap= => true}
|
35
|
+
label_width = {:width_request= => label, :wrap= => true}
|
36
|
+
entry_width = {:width_request= => entry}
|
37
|
+
entry_shorten = {:width_request= => entry - go - 2*padding}
|
38
|
+
entry_shorten2 = {:width_request= => entry - spin - check - 4*padding, :visibility= => false}
|
39
|
+
go_width = {:width_request= => go}
|
40
|
+
check_width = {:width_request= => check, :active= => true}
|
41
|
+
spin_width = {:width_request= => spin, :set_range => [3,30]}
|
42
|
+
clicked = 'clicked'
|
18
43
|
|
19
|
-
|
20
|
-
|
44
|
+
PARAMETERS[:Account_Label] = ['Account:',label_width]
|
45
|
+
# nil needs to be set to the accounts list in bin/gtk2passwordapp
|
46
|
+
PARAMETERS[:Account_ComboBoxEntry] = [nil,entry_width,'changed'] # catching changed signal
|
21
47
|
|
22
|
-
|
48
|
+
PARAMETERS[:Url_Label] = ['Url:',label_width]
|
49
|
+
PARAMETERS[:Url_Entry] = [entry_shorten]
|
50
|
+
PARAMETERS[:Url_Button] = ['Go!',go_width,clicked]
|
23
51
|
|
24
|
-
|
52
|
+
PARAMETERS[:Note_Label] = ['Note:',label_width]
|
53
|
+
PARAMETERS[:Note_Entry] = [entry_width]
|
25
54
|
|
26
|
-
|
27
|
-
|
28
|
-
|
55
|
+
PARAMETERS[:Username_Label] = ['Username:',label_width]
|
56
|
+
PARAMETERS[:Username_Entry] = [entry_width]
|
57
|
+
|
58
|
+
PARAMETERS[:Password_Label] = ['Password:',label_width]
|
59
|
+
PARAMETERS[:Password_Entry] = [entry_shorten2]
|
60
|
+
PARAMETERS[:Password_CheckButton] = [check_width,'toggled']
|
61
|
+
PARAMETERS[:Password_SpinButton] = [spin_width]
|
29
62
|
|
30
|
-
|
63
|
+
PARAMETERS[:Random_Button] = ['Random',clicked]
|
64
|
+
PARAMETERS[:Alpha_Button] = ['Alpha-Numeric',clicked]
|
65
|
+
PARAMETERS[:Numeric_Button] = ['Numeric',clicked]
|
66
|
+
PARAMETERS[:Letters_Button] = ['Letters',clicked]
|
67
|
+
PARAMETERS[:Caps_Button] = ['All-Caps',clicked]
|
31
68
|
|
32
|
-
|
69
|
+
PARAMETERS[:Cancel_Button] = ['Close',clicked]
|
70
|
+
PARAMETERS[:Delete_Button] = ['Delete Account',clicked]
|
71
|
+
PARAMETERS[:Update_Button] = ['Update Account',clicked]
|
72
|
+
PARAMETERS[:Save_Button] = ['Save To Disk',clicked]
|
33
73
|
|
34
|
-
|
35
|
-
|
74
|
+
PARAMETERS[:Datafile_Button] = ['Change Data File Password',clicked]
|
75
|
+
|
76
|
+
PARAMETERS[:Current_Button] = ['Clip Current Password',clicked]
|
77
|
+
PARAMETERS[:Previous_Button] = ['Clip Previous Password',clicked]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module Gtk2PasswordApp
|
82
|
+
module Configuration
|
83
|
+
# Note that the passwords data file name is auto generated, but...
|
84
|
+
# You can place your passwords data file in a directory other than ~/gtk2passwordapp-*
|
85
|
+
PASSWORDS_DATA_DIR = Gtk2AppLib::USERDIR
|
86
|
+
# Switches the roles of PRIMARY and CLIPBOARD when true
|
87
|
+
SWITCH_CLIPBOARDS = (Gtk2AppLib::HILDON || !Gtk2AppLib::Configuration::X)? true: false
|
88
|
+
PASSWORD_EXPIRED = 60*60*24*30*3 # 3 months
|
89
|
+
URL_PATTERN = Regexp.new('^https?:\/\/[^\s\']+$')
|
36
90
|
DEFAULT_PASSWORD_LENGTH = 7
|
91
|
+
EXPIRED_COLOR = Gtk2AppLib::Color[:Red]
|
37
92
|
|
38
|
-
|
39
|
-
|
93
|
+
BAD_URL = ['Need url like http://www.site.com/page.html',{:Title => 'Error: Bad Url',:Scrolled_Window => false}]
|
94
|
+
ARE_YOU_SURE = ['Changes will lost, are you sure you want to close?',{:Title => 'Are you sure?'}]
|
95
|
+
WANT_TO_SAVE = ['Would you like to save your changes?',{:Title => 'Save?'}]
|
96
|
+
NO_UPDATES = ["You've not updated any accounts yet.",{:Title => 'Not Modified',:Scrolled_Window => false}]
|
97
|
+
UPDATED = ["Updated!",{:Title => 'Updated!',:Scrolled_Window => false}]
|
40
98
|
end
|
41
99
|
|
42
100
|
def self.passwords_updated(dumpfile)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
101
|
+
## After the password files are saved, you have the option here to backup or mirror the files elsewhere.
|
102
|
+
## Here's an example:
|
103
|
+
# system( "ssh user@192.168.1.123 mv .gtk2passwordapp-1/*.dat .gtk2passwordapp-1/bak/")
|
104
|
+
# system( "ssh user@192.168.1.123 cp .gtk2passwordapp-1/passphrase.txt .gtk2passwordapp-1/bak/passphrase.txt") # you might choose not too.
|
105
|
+
# fn = File.basename(dumpfile)
|
106
|
+
# if system( "scp #{dumpfile} user@192.168.1.123:.gtk2passwordapp-1/#{fn}") then
|
107
|
+
# # again, you might choose not to distribute passphrase (reduces cracking to your short password)
|
108
|
+
# if system( "scp #{Configuration::PASSWORDS_DATA_DIR}/passphrase.txt user@192.168.1.123:.gtk2passwordapp-1/passphrase.txt") then
|
109
|
+
# Gtk2AppLib::DIALOGS.quick_message("Passwords saved on 192.168.1.101 and 102")
|
110
|
+
# return
|
111
|
+
# end
|
112
|
+
# end
|
113
|
+
# Gtk2AppLib::DIALOGS.quick_message("Warning: Could not create backup on 192.168.1.123")
|
114
|
+
Gtk2AppLib::DIALOGS.quick_message("Passwords Data Saved.",{:Title => 'Saved',:Scrolled_Window => false})
|
47
115
|
end
|
48
116
|
end
|
@@ -17,7 +17,7 @@ class Passwords < PasswordsData
|
|
17
17
|
def get_passphrase(mv=false)
|
18
18
|
passphrase = ''
|
19
19
|
|
20
|
-
@pfile =
|
20
|
+
@pfile = Gtk2AppLib::USERDIR+'/passphrase.txt'
|
21
21
|
if mv then
|
22
22
|
File.rename(@pfile, @pfile+'.bak') if File.exist?(@pfile)
|
23
23
|
passphrase = _create_passphrase
|
@@ -33,8 +33,8 @@ class Passwords < PasswordsData
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def has_datafile?
|
36
|
-
Find.find(
|
37
|
-
Find.prune if !(fn==
|
36
|
+
Find.find(Gtk2AppLib::USERDIR){|fn|
|
37
|
+
Find.prune if !(fn==Gtk2AppLib::USERDIR) && File.directory?(fn)
|
38
38
|
if fn =~/[0123456789abcdef]{32}\.dat$/ then
|
39
39
|
return true
|
40
40
|
end
|
@@ -74,6 +74,8 @@ class Passwords < PasswordsData
|
|
74
74
|
pwd = Passwords._get_salt('Try again!')
|
75
75
|
@pwd = Passwords._get_salt('Verify New Password')
|
76
76
|
end
|
77
|
+
super(@pwd+@pph)
|
78
|
+
self.save
|
77
79
|
end
|
78
80
|
end
|
79
81
|
# Off to the races...
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gtk2passwordapp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 3
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
8
|
+
- 5
|
9
9
|
- 0
|
10
|
-
version: 1.
|
10
|
+
version: 1.5.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- carlosjhr64@gmail.com
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-03-07 00:00:00 -08:00
|
19
19
|
default_executable: gtk2passwordapp
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -26,12 +26,11 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
29
|
+
hash: 9
|
30
30
|
segments:
|
31
31
|
- 1
|
32
32
|
- 3
|
33
|
-
|
34
|
-
version: 1.3.0
|
33
|
+
version: "1.3"
|
35
34
|
type: :runtime
|
36
35
|
version_requirements: *id001
|
37
36
|
- !ruby/object:Gem::Dependency
|
@@ -42,10 +41,11 @@ dependencies:
|
|
42
41
|
requirements:
|
43
42
|
- - ~>
|
44
43
|
- !ruby/object:Gem::Version
|
45
|
-
hash:
|
44
|
+
hash: 63
|
46
45
|
segments:
|
47
|
-
-
|
48
|
-
|
46
|
+
- 13
|
47
|
+
- 0
|
48
|
+
version: "13.0"
|
49
49
|
type: :runtime
|
50
50
|
version_requirements: *id002
|
51
51
|
description: |
|
@@ -71,7 +71,7 @@ files:
|
|
71
71
|
- README.txt
|
72
72
|
- bin/gtk2passwordapp
|
73
73
|
has_rdoc: true
|
74
|
-
homepage:
|
74
|
+
homepage: https://sites.google.com/site/gtk2applib/home/gtk2applib-applications/gtk2passwordapp
|
75
75
|
licenses: []
|
76
76
|
|
77
77
|
post_install_message:
|
@@ -100,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
101
101
|
- gtk2
|
102
102
|
rubyforge_project: gtk2passwordapp
|
103
|
-
rubygems_version: 1.
|
103
|
+
rubygems_version: 1.5.2
|
104
104
|
signing_key:
|
105
105
|
specification_version: 3
|
106
106
|
summary: Ruby-Gnome Password Manager
|