gtk2passwordapp 5.2.1 → 6.0.210202

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.rdoc DELETED
@@ -1,64 +0,0 @@
1
- = Gtk2PasswordApp
2
-
3
- {<img src="https://badge.fury.io/rb/gtk2passwordapp.svg" alt="Gem Version" />}[http://badge.fury.io/rb/gtk2passwordapp]
4
-
5
- == Description:
6
-
7
- Ruby-Gnome Password Manager.
8
-
9
- Uses Blowfish to encrypt the datafile.
10
- Features random password generator, clipboard use, and TOTP.
11
-
12
- == Install:
13
-
14
- sudo gem install gtk2passwordapp
15
-
16
- == Help:
17
-
18
- Usage:
19
- gtk2pwdV [:options]
20
- gtk2pwdV --nogui [<pattern>]
21
- Options:
22
- -v --version Show version and exit
23
- -h --help Show help and exit
24
-
25
- == More:
26
-
27
- "Mouse Left Click" on window to get the application menu.
28
-
29
-
30
- == Configuration:
31
-
32
- ~/.config/gtk3app/gtk2passwordapp/config-?.?.yml
33
-
34
- [Salt] If your master-password length is under 14(MinPwdLen), it'll append this Salt.
35
- [TOTP] If password matches this pattern, it will show the time based one time code.
36
- [BackupFile] Although the GUI allows you to specify the file, this gives a default.
37
- [TooOld] I have this set for a year (in seconds).
38
- [CustomDigits] I have this as "3479ACEFHJKLMNPRTUVWXYabcdefghijkmnopqrstuvwxyz".
39
- [Custom] I have this as "Custom".
40
-
41
- == LICENSE:
42
-
43
- (The MIT License)
44
-
45
- Copyright (c) 2017
46
-
47
- Permission is hereby granted, free of charge, to any person obtaining
48
- a copy of this software and associated documentation files (the
49
- 'Software'), to deal in the Software without restriction, including
50
- without limitation the rights to use, copy, modify, merge, publish,
51
- distribute, sublicense, and/or sell copies of the Software, and to
52
- permit persons to whom the Software is furnished to do so, subject to
53
- the following conditions:
54
-
55
- The above copyright notice and this permission notice shall be
56
- included in all copies or substantial portions of the Software.
57
-
58
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
59
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
60
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
61
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
62
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
63
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
64
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/bin/gtk2pwdV DELETED
@@ -1,68 +0,0 @@
1
- #!/usr/bin/env ruby
2
- module Gtk2PasswordApp
3
- VERSION = '5.2.1'
4
- end
5
-
6
- begin
7
- if a1 = ARGV.shift
8
- if ['-v', '--version'].include?(a1)
9
- puts Gtk2PasswordApp::VERSION
10
- exit 0
11
- end
12
-
13
- if ['-h', '--help'].include?(a1)
14
- puts <<-HELP
15
- Usage:
16
- gtk2pwdV [:options]
17
- gtk2pwdV --nogui [<pattern>]
18
- Options:
19
- -v --version \tShow version and exit
20
- -h --help \tShow help and exit
21
- Notes:
22
- With the --nogui option,
23
- one can give a pattern to filter by account names.
24
- Expected passwords data file is:
25
- ~/.cache/gtk3app/gtk2passwordapp/gtk2pwdV.dat
26
- HELP
27
- exit 0
28
- end
29
-
30
- if a1 == '--nogui'
31
- dat = File.expand_path('~/.cache/gtk3app/gtk2passwordapp/gtk2pwdV.dat')
32
- raise "Passwords data file missing: #{dat}" unless File.exist? dat
33
- require 'pp'
34
- require 'yaml_zlib_blowfish'
35
- require 'base_convert'
36
- system('clear; clear')
37
- print "Enter password: "
38
- pwd = $stdin.gets.strip
39
- system('clear; clear')
40
- print "Enter salt: "
41
- pwd << $stdin.gets.strip
42
- system('clear; clear')
43
- pwd = BaseConvert::FromTo.new(:hex, :qgraph).convert Digest::SHA256.hexdigest pwd
44
- begin
45
- lst = YamlZlibBlowfish.new(pwd).load(dat)
46
- rescue OpenSSL::Cipher::CipherError
47
- $stderr.puts "Bad password"
48
- exit 65
49
- end
50
- if pattern = ARGV.shift
51
- pattern = Regexp.new(pattern, Regexp::IGNORECASE)
52
- end
53
- if ARGV.empty? # if ARGV is not empty, it's bad usage.
54
- pp (pattern)? lst.select{|k,v|pattern.match(k)} : lst
55
- exit 0
56
- end
57
- end
58
-
59
- $stderr.puts "Please match usage."
60
- exit 64
61
- end
62
- rescue RuntimeError
63
- $stderr.puts $!
64
- exit 66
65
- end
66
-
67
- require 'gtk2passwordapp'
68
- Gtk3App.main(Gtk2PasswordApp)
data/data/VERSION DELETED
@@ -1 +0,0 @@
1
- 5.2.1
@@ -1,449 +0,0 @@
1
- module Gtk2PasswordApp
2
- using Rafini::Exception
3
- using Rafini::Array
4
-
5
- RND = SuperRandom.new
6
- H2Q = BaseConvert::FromTo.new(:hex, :qgraph)
7
- H2W = BaseConvert::FromTo.new(:hex, :word)
8
-
9
- def self.run(program)
10
- Gtk2PwdV.new(program)
11
- end
12
-
13
- class DeleteDialog < Gtk3App::Dialog::CancelOk
14
- def initialize(parent)
15
- super([parent: parent], :delete_dialog)
16
- label :delete_label!
17
- end
18
- end
19
-
20
- class BackupDialog < Such::FileChooserDialog
21
- def initialize(parent)
22
- super([parent: parent], :backup_dialog)
23
- set_action Gtk::FileChooserAction::SAVE
24
- add_button(Gtk::Stock::CANCEL, Gtk::ResponseType::CANCEL)
25
- add_button(Gtk::Stock::OPEN, Gtk::ResponseType::ACCEPT)
26
- if CONFIG[:BackupFile]
27
- set_filename CONFIG[:BackupFile]
28
- set_current_name File.basename CONFIG[:BackupFile]
29
- end
30
- end
31
-
32
- def runs
33
- show_all
34
- value = nil
35
- if run == Gtk::ResponseType::ACCEPT
36
- value = filename
37
- end
38
- destroy
39
- return value
40
- end
41
- end
42
-
43
- class ErrorDialog < Such::MessageDialog
44
- def initialize(parent)
45
- super([parent: parent, flags: :modal, type: :error, buttons_type: :close], :error_dialog)
46
- end
47
-
48
- def runs
49
- set_secondary_text $!.message
50
- run
51
- destroy
52
- end
53
- end
54
-
55
- class Gtk2PwdV
56
-
57
- def initialize(program)
58
- @program = program
59
- @names = @combo = nil
60
-
61
- @blue = Gdk::RGBA.parse(CONFIG[:Blue])
62
- @red = Gdk::RGBA.parse(CONFIG[:Red])
63
- @black = Gdk::RGBA.parse(CONFIG[:Black])
64
-
65
- _ = CONFIG[:CustomDigits]
66
- @h2c = BaseConvert::FromTo.new(:hex, _.length)
67
- @h2c.to_digits = _
68
-
69
- if CONFIG[:SwitchClipboard]
70
- @clipboard = Gtk::Clipboard.get(Gdk::Selection::PRIMARY)
71
- @primary = Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD)
72
- else
73
- @primary = Gtk::Clipboard.get(Gdk::Selection::PRIMARY)
74
- @clipboard = Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD)
75
- end
76
-
77
- @current, @previous = [], []
78
-
79
- window = program.window
80
- @page = Such::Box.new window, :vbox!
81
- @accounts = Accounts.new(CONFIG[:PwdFile])
82
- password_page((@accounts.exist?)? :load : :init)
83
- window.show
84
-
85
- # Because accounts are editable from the main window,
86
- # minime's menu needs to be updated each time.
87
- destroy_menu_items
88
- program.mini.signal_connect('show'){generate_menu_items}
89
- program.mini.signal_connect('hide'){destroy_menu_items}
90
- end
91
-
92
- def copy2clipboard(pwd, user)
93
- @primary.text = pwd
94
- @clipboard.text = user
95
- GLib::Timeout.add_seconds(CONFIG[:ClipboardTimeout]) do
96
- if @primary.wait_for_text == pwd
97
- @primary.text = ''
98
- if @clipboard.wait_for_text == user
99
- @clipboard.text = ''
100
- end
101
- end
102
- false
103
- end
104
- end
105
-
106
- def color_code(selected)
107
- @current.unshift selected; @current.uniq!
108
- if @current.length > CONFIG[:Recent]
109
- popped = @current.pop
110
- popped.override_color :normal, @black
111
- end
112
- selected.override_color :normal, @blue
113
- end
114
-
115
- def generate_menu_items
116
- now = Time.now.to_i
117
- @accounts.names.sort{|a,b|a.upcase<=>b.upcase}.each do |name|
118
- account = @accounts.get name
119
- pwd, user, updated = account.password, account.username, account.updated
120
- selected = Such::MenuItem.new([label: name], 'activate') do
121
- color_code selected
122
- @combo.set_active @names.index name if @combo
123
- copy2clipboard pwd, user
124
- @program.mini_menu.reorder_child(selected,0)
125
- end
126
- if @previous.include? name
127
- @current[@previous.index(name)] = selected
128
- selected.override_color :normal, @blue
129
- elsif ((now - updated) > CONFIG[:TooOld])
130
- selected.override_color :normal, @red
131
- end
132
- @program.mini_menu.append selected
133
- selected.show
134
- end
135
- @current.delete_if{|a|a.nil?}
136
- @previous.clear
137
- end
138
-
139
- def destroy_menu_items
140
- @current.each{|item| @previous.push item.label}
141
- @current.clear
142
- @program.mini_menu.each{|item|item.destroy}
143
- end
144
-
145
- def clear_page
146
- @page.each do |w|
147
- w.text = '' if w.class == Such::Entry # Else gives warning.... :-??
148
- w.destroy
149
- end
150
- end
151
-
152
- def rehash(pwd)
153
- pwd << CONFIG[:Salt] if pwd.length < CONFIG[:MinPwdLen]
154
- BaseConvert::FromTo.new(:hex, :qgraph).convert Digest::SHA256.hexdigest(pwd)
155
- end
156
-
157
- def process_pwd_entries(entry1, entry2)
158
- begin
159
- pwd = entry1.text.strip
160
- raise 'No password given.' if pwd == ''
161
- if entry2
162
- raise 'Passwords did not match' unless entry2.text.strip==pwd
163
- @accounts.save rehash pwd
164
- else
165
- @accounts.load rehash pwd
166
- end
167
- true
168
- rescue StandardError
169
- $!.puts
170
- entry1.text = ''
171
- entry2.text = '' if entry2
172
- false
173
- end
174
- end
175
-
176
- def backup
177
- if filename = BackupDialog.new(@program.window).runs
178
- begin
179
- FileUtils.cp CONFIG[:PwdFile], filename
180
- rescue
181
- $!.puts
182
- ErrorDialog.new(@program.window).runs
183
- end
184
- end
185
- end
186
-
187
- # mode can be :init, :load, or :reset
188
- def password_page(mode)
189
- clear_page
190
-
191
- password_label = Such::Label.new @page, :password_label!
192
- password_entry1 = Such::Entry.new @page, :password_entry!
193
- password_entry2 = (mode==:load)? nil : Such::Entry.new(@page, :password_entry!)
194
-
195
- action = Such::AbButtons.new(@page, :hbox!) do |button, *_|
196
- case button
197
- when action.a_Button # Cancel
198
- (mode==:reset)? view_page : @program.quit!
199
- when action.b_Button # Go
200
- if process_pwd_entries password_entry1, password_entry2
201
- unless mode==:reset
202
- @program.app_menu.append_menu_item(:reset!){password_page(:reset)}
203
- @program.app_menu.append_menu_item(:backup!){backup}
204
- end
205
- view_page
206
- else
207
- password_label.text = CONFIG[:ReTry]
208
- end
209
- end
210
- end
211
- action.labels :Cancel, :Go
212
-
213
- password_entry1.signal_connect('activate') do
214
- if password_entry2
215
- password_entry2.grab_focus
216
- else
217
- action.b_Button.clicked
218
- end
219
- end
220
- if password_entry2
221
- password_entry2.signal_connect('activate') do
222
- action.b_Button.clicked
223
- end
224
- end
225
-
226
- @page.show_all
227
- end
228
-
229
- def create_combo
230
- combo = Such::PromptedCombo.new @page, :hbox!
231
- combo.prompt_Label.text = CONFIG[:Name]
232
- @combo= combo.prompted_ComboBoxText
233
- @names = @accounts.names.sort{|a,b|a.upcase<=>b.upcase}
234
- @names.each{|name|@combo.append_text name}
235
- @combo.set_active @names.index(@account.name)
236
- @combo.signal_connect('destroy'){@names = @combo = nil}
237
- end
238
-
239
- def create_entries
240
- entries = {}
241
- CONFIG[:FIELDS].each do |field, text|
242
- entry = Such::PromptedLabel.new @page, :hbox!
243
- entry.prompt_Label.text = text
244
- (_=entry.prompted_Label).text = @account.method(field).call
245
- _.set_alignment(*CONFIG[:FIELD_ALIGNMENT])
246
- _.selectable = true
247
- entries[field] = entry
248
- end
249
- return entries
250
- end
251
-
252
- def any_name
253
- names = @accounts.names
254
- if name = ARGV.shift
255
- unless names.include? name
256
- like = Regexp.new name
257
- name = names.which{|nm|nm=~like}
258
- end
259
- end
260
- name = names.sample unless name
261
- return name
262
- end
263
-
264
- def view_page
265
- if @accounts.data.length == 0
266
- edit_page(:add)
267
- return
268
- end
269
- @account ||= @accounts.get any_name
270
-
271
- clear_page
272
-
273
- Such::Label.new @page, :view_label!
274
- create_combo
275
- entries = create_entries
276
-
277
- label, hidden = entries[:password].prompted_Label, CONFIG[:HiddenPwd]
278
- label.text = hidden
279
-
280
- @combo.signal_connect('changed') do
281
- @account = @accounts.get @combo.active_text
282
- CONFIG[:FIELDS].each do |field, _|
283
- entries[field].prompted_Label.text = @account.method(field).call
284
- end
285
- label.text = hidden
286
- end
287
-
288
- clip_box = Such::AbcButtons.new(@page, :hbox!) do |button, *_|
289
- case button
290
- when clip_box.a_Button # Current
291
- copy2clipboard @account.password, @account.username
292
- when clip_box.b_Button # Previous
293
- copy2clipboard @account.previous, @account.password
294
- when clip_box.c_Button # Show
295
- case label.text
296
- when hidden
297
- pwd = @account.password
298
- if pwd=~/^[A-Z2-7]+$/
299
- pwd = TOTP.passwords(pwd)[1].to_s
300
- end
301
- label.text = pwd
302
- when @account.password
303
- label.text = hidden
304
- else
305
- label.text = @account.password
306
- end
307
- end
308
- end
309
- clip_box.labels :Current, :Previous, :Show
310
-
311
- edit_box = Such::AbcButtons.new(@page, :hbox!) do |button, *_|
312
- case button
313
- when edit_box.a_Button then edit_page
314
- when edit_box.b_Button then edit_page(:add)
315
- when edit_box.c_Button
316
- system("#{Gtk3App::CONFIG[:Open]} '#{@account.url}'") if @account.url.length > 0
317
- end
318
- end
319
- edit_box.labels :Edit, :Add, :Goto
320
-
321
- @page.show_all
322
- end
323
-
324
- def edit_page(mode=:edit)
325
- clear_page
326
-
327
- edited = false
328
- previous = @account ? @account.name : nil
329
- name = nil
330
-
331
- case mode
332
- when :add
333
- Such::Label.new @page, :add_label!
334
- name = Such::PromptedEntryLabel.new @page, :hbox!
335
- name.prompt_Label.text = CONFIG[:Name]
336
- when :edit
337
- Such::Label.new @page, :edit_label!
338
- name = Such::PromptedLabel.new @page, :hbox!
339
- name.prompt_Label.text = CONFIG[:Name]
340
- name.prompted_Label.text = @account.name
341
- end
342
- name.prompted_Label.set_alignment(*CONFIG[:FIELD_ALIGNMENT])
343
-
344
- entries = {}
345
- CONFIG[:FIELDS].each do |field, text|
346
- entry = Such::PromptedEntry.new @page, :hbox!
347
- entry.prompt_Label.text = text
348
- entry.prompted_Entry.text = @account.method(field).call if mode==:edit
349
- entries[field] = entry
350
- end
351
-
352
- # cb and sb will be a CheckButton and SpinButton respectively.
353
- cb = sb = nil
354
- password = (mode==:edit)? @account.password : ''
355
- truncate = Proc.new do |p|
356
- password = p
357
- if cb.active?
358
- n = sb.value.to_i
359
- p = p[-n..-1] if p.length > n
360
- end
361
- p
362
- end
363
-
364
- pwd = entries[:password].prompted_Entry
365
- pwd.set_visibility false
366
- pwd.signal_connect('focus-in-event' ){pwd.set_visibility true}
367
- pwd.signal_connect('focus-out-event'){pwd.set_visibility false}
368
-
369
- generators = Such::AbcButtons.new(@page, :hbox!) do |button,*e,s|
370
- hex = RND.hexadecimal
371
- case button
372
- when generators.a_Button
373
- pwd.text = truncate.call H2Q.convert hex
374
- when generators.b_Button
375
- pwd.text = truncate.call H2W.convert hex
376
- when generators.c_Button
377
- pwd.text = truncate.call @h2c.convert hex
378
- end
379
- end
380
- generators.labels :Random, :AlphaNumeric, :Custom
381
-
382
- cb = Such::CheckButton.new(generators, :pwd_size_check!, 'toggled') do
383
- pwd.text = (cb.active?) ? truncate.call(password) : password
384
- end
385
- sb = Such::SpinButton.new(generators, :pwd_size_spin!, 'value-changed') do
386
- pwd.text = truncate.call password if cb.active?
387
- end
388
-
389
- action = Such::AbcButtons.new(@page, :hbox!) do |button, *_|
390
- case button
391
- when action.a_Button # Cancel
392
- if edited
393
- @accounts.load
394
- @account = previous ? @accounts.get(previous) : nil
395
- end
396
- view_page
397
- when action.b_Button # Delete
398
- dialog = DeleteDialog.new(@program.window)
399
- dialog.set_title @account.name
400
- if dialog.runs
401
- @accounts.delete @account.name
402
- @accounts.save
403
- @account = nil
404
- view_page
405
- end
406
- when action.c_Button # Save
407
- edited = true
408
- begin
409
- if mode==:add
410
- @account = @accounts.add(name.prompted_Entry.text.strip)
411
- name.prompted_Label.text = @account.name
412
- name.prompted_Entry.hide
413
- name.prompted_Label.show
414
- name.prompt_Label.override_color :normal, @blue
415
- mode = :edit
416
- end
417
- errors = false
418
- entries.each do |field, entry|
419
- begin
420
- @account.method("#{field}=".to_sym).call(entry.prompted_Entry.text.strip)
421
- entry.prompt_Label.override_color :normal, @blue
422
- rescue RuntimeError
423
- $!.puts
424
- errors ||= true
425
- entry.prompt_Label.override_color :normal, @red
426
- end
427
- end
428
- unless errors
429
- @accounts.save
430
- view_page
431
- end
432
- rescue RuntimeError
433
- $!.puts
434
- name.prompt_Label.override_color :normal, @red
435
- end
436
- end
437
- end
438
- action.labels :Cancel, :Delete, :Save
439
-
440
- @page.show_all
441
- if mode==:add
442
- name.prompted_Label.hide
443
- action.b_Button.hide
444
- end
445
- end
446
-
447
- end
448
-
449
- end