gtk2passwordapp 0.0.8 → 1.0.0

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.txt CHANGED
@@ -1,7 +1,50 @@
1
- Just run:
2
- gtk2passwordapp
1
+ Ruby-Gnome Password Manager
3
2
 
4
- For some documentation on usage, see
3
+ A Ruby-Gnome password manager.
4
+ Uses crypt-tea's Tiny Encryption Algorithm to encrypt the datafile.
5
+ Features random password generator and clipboard use.
6
+
7
+
8
+ To add an account, enter the new account name in the "Account:" entry/combo box.
9
+ For the account, write the associated url, a note about the accout, and the username
10
+ in the appropriate entry boxes.
11
+
12
+ To set a new password, either enter the password in the "New:" entry box, or
13
+ generate it by pressing "Random", "Alpha-Numeric", "Numeric", "Letters", or
14
+ "All-Caps". One can set the password length generated with the spin-box.
15
+ To make the password generated visible, press the "Visible" button.
16
+ The "Current" button copies the current password to the primary clipboard.
17
+ The "Previous" button copies the previous password to the primary clipboard.
18
+
19
+ To delete an account, select the account in the entry/combo box, and
20
+ the press the "Delete Account" button.
21
+
22
+ Once one has edited the account, clicking the "Update" button finalizes the record.
23
+ Note, however, that the change is not yet permanent and saved on disk.
24
+ Once one is done with all updates, one then needs to press "Save".
25
+ "Cancel" or closing the window without "Save" will ignore all of the sessions updates.
26
+
27
+ The "Change Data File Password" button will allow one the change
28
+ 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".
32
+
33
+ Left click on the docked icon to bring back the editor window.
34
+
35
+ Right click on the docked icon to select one of the accounts to load
36
+ the password and username to the clipboard.
37
+ The password is copied the the primary clipboard and will paste on
38
+ middle mouse button click.
39
+ Right click on an entry box to paste the username (via the clipboard's menu).
40
+
41
+ Lastly, do not edit
42
+ ~/.gtk2passwordapp-1/passphrase.txt
43
+ It's used to "salt" the password... without it,
44
+ one will not be able to decrypt the datafile.
45
+
46
+
47
+ For full documentation and comments, see
5
48
 
6
49
  http://ruby-gnome-apps.blogspot.com/search/label/Passwords
7
50
 
data/bin/gtk2passwordapp CHANGED
@@ -1,40 +1,29 @@
1
1
  #!/usr/bin/env ruby
2
- # $Date: 2009/02/27 23:51:40 $
3
- ##########################################################
4
- require 'lib/global_options_variables'
5
- GlobalOptionsVariables.set('0.0.8',
6
- <<EOT
7
- Usage: #{$0.sub(/^.*\//,'')} [options]
2
+ require 'rubygems'
3
+ gem 'gtk2applib', '~> 3.1.0'
4
+ require 'gtk2applib/gtk2_app'
8
5
 
9
- Options:
10
- -h, --help print this help text and exit
11
- -v, --version print program version and exit
12
- -t, --test test
13
- -T, --trace trace
14
- EOT
15
- )
16
- require 'lib/setup_user_space'
17
- UserSpace.setup
18
- UserSpace.copy('/README.txt')
19
- require USER_CONF_DIR+CONF_FILE
20
- ##########################################################
21
- require 'lib/gtk2passwordapp'
6
+ application = {
7
+ :name => 'Ruby-Gnome Password Manager',
8
+ :tooltip => 'Password Manager',
9
+ :FILE => __FILE__,
10
+ }
11
+ Gtk2App.init(application)
12
+ Gtk2App.icon.set_icon_name(Gtk::Stock::DIALOG_AUTHENTICATION)
22
13
 
23
- lock = USER_CONF_DIR+'/lock'
24
- if File.exist?(lock) then
25
- $stderr.puts "process already running?"
26
- # user should then notice it's already running, but
27
- # let's remove the lock in case it's not and user
28
- # tries again....
29
- File.unlink(lock)
30
- else
31
- begin
32
- File.open(lock,'w'){|fh| fh.puts $$ }
33
- gpa = Gtk2PasswordApp.new
34
- gpa.status_icon
35
- rescue Exception
36
- puts_bang!
37
- ensure
38
- File.unlink(lock) if File.exist?(lock)
39
- end
14
+ require 'gtk2passwordapp/edit_box.rb'
15
+ require 'gtk2passwordapp/passwords.rb'
16
+ passwords = Gtk2PasswordApp::Passwords.new
17
+ Gtk2PasswordApp.build_menu(passwords)
18
+
19
+ about = {
20
+ :authors => ['carlosjhr64@gmail.com'],
21
+ :comments => 'Ruby-Gtk2 Passsword Manager',
22
+ :website => 'http://ruby-gnome-apps.blogspot.com/search/label/Passwords',
23
+ :website_label => 'Ruby-Gnome Password Manager',
24
+ :license => 'GPL',
25
+ :copyright => '2009-Dec-14',
26
+ }
27
+ Gtk2App.main_window(about) do |window|
28
+ Gtk2PasswordApp.edit(window,passwords)
40
29
  end
@@ -0,0 +1,29 @@
1
+ # Note: you'll see in ~/.gtk2passwordapp-* a file called passphrase.txt.
2
+ # Do not edit or delete passphrase, or you'll loose your passwords data.
3
+
4
+ module Configuration
5
+ # Note that the passwords data file name is auto generated, but...
6
+ # You can place your passwords data file in a directory other than ~/gtk2passwordapp-*
7
+ PASSWORDS_DATA_DIR = UserSpace::DIRECTORY
8
+
9
+ ENTRY_WIDTH = (Gtk2App::HILDON)? 600: 300
10
+ LABEL_WIDTH = 75
11
+ GO_BUTTON_LENGTH = 50
12
+ SPIN_BUTTON_LENGTH = 60
13
+ PAD = 2 # cell padding
14
+
15
+ MAX_PASSWORD_LENGTH = 20
16
+ DEFAULT_PASSWORD_LENGTH = 7
17
+ MIN_PASSWORD_LENGTH = 3
18
+
19
+ # Switches the roles of PRIMARY and CLIPBOARD when true
20
+ SWITCH_CLIPBOARDS = Gtk2App::HILDON
21
+
22
+ PASSWORD_EXPIRED = 60*60*24*30*3 # 3 months
23
+
24
+ URL_PATTERN = Regexp.new('^https?:\/\/[^\s\']+$')
25
+
26
+ FONT[:normal] = FONT[:large] = Pango::FontDescription.new( 'Arial 18' ) if Gtk2App::HILDON
27
+ GUI[:window_size] = [100,100]
28
+ MENU[:close] = '_Close'
29
+ end
@@ -0,0 +1,328 @@
1
+ module Gtk2PasswordApp
2
+ include Configuration
3
+ PRIMARY = Gtk::Clipboard.get((SWITCH_CLIPBOARDS)? Gdk::Selection::CLIPBOARD: Gdk::Selection::PRIMARY)
4
+ CLIPBOARD = Gtk::Clipboard.get((SWITCH_CLIPBOARDS)? Gdk::Selection::PRIMARY: Gdk::Selection::CLIPBOARD)
5
+
6
+ @@index = nil
7
+ def self.build_menu(passwords)
8
+ passwords.accounts.each {|account|
9
+ item = Gtk2App.dock_menu.append_menu_item(account){
10
+ @@index = passwords.accounts.index(account)
11
+ PRIMARY.text = passwords.password_of(account)
12
+ CLIPBOARD.text = passwords.username_of(account)
13
+ }
14
+ item.child.modify_fg(Gtk::STATE_NORMAL, RED) if passwords.expired?(account)
15
+ }
16
+ Gtk2App.dock_menu.show_all
17
+ end
18
+ def self.rebuild_menu(passwords)
19
+ items = Gtk2App.dock_menu.children
20
+ 3.times{ items.shift } # shift out Quit, Run, and Spacer
21
+ while item = items.shift do
22
+ Gtk2App.dock_menu.remove(item)
23
+ item.destroy
24
+ end
25
+ Gtk2PasswordApp.build_menu(passwords)
26
+ end
27
+
28
+ def self.get_salt(title='Short Password')
29
+ dialog = Gtk::Dialog.new(
30
+ title,
31
+ nil, nil,
32
+ [ Gtk::Stock::QUIT, 0 ],
33
+ [ Gtk::Stock::OK, 1 ])
34
+
35
+ label = Gtk::Label.new(title)
36
+ label.justify = Gtk::JUSTIFY_LEFT
37
+ label.wrap = true
38
+ label.modify_font(Configuration::FONT[:normal])
39
+ dialog.vbox.add(label)
40
+ entry = Gtk::Entry.new
41
+ entry.visibility = false
42
+ entry.modify_font(Configuration::FONT[:normal])
43
+ dialog.vbox.add(entry)
44
+ dialog.show_all
45
+
46
+ entry.signal_connect('activate'){
47
+ dialog.response(1)
48
+ }
49
+
50
+ ret = nil
51
+ dialog.run {|response|
52
+ ret = entry.text.strip if response == 1
53
+ }
54
+ dialog.destroy
55
+
56
+ return ret
57
+ end
58
+
59
+ DIALOGS = Gtk2App::Dialogs.new
60
+
61
+ EDITOR_LABELS = [
62
+ :account,
63
+ :url,
64
+ :note,
65
+ :username,
66
+ :password,
67
+ ]
68
+
69
+
70
+ EDITOR_BUTTONS = [
71
+ [ :random, :alphanum, :num, :alpha, :caps, ],
72
+ [ :visibility, :current, :previous, :cancel, :save, :update, ],
73
+ [ :delete, :cpwd ],
74
+ ]
75
+
76
+ TEXT = {
77
+ # Labels
78
+ :account => 'Account',
79
+ :note => 'Note',
80
+ :password => 'New',
81
+ # Buttons
82
+ :username => 'Username',
83
+ :current => 'Current',
84
+ :url => 'Url',
85
+ :note => 'Note',
86
+ :edit => 'Edit',
87
+ :update => 'Update',
88
+ :visibility => 'Visible',
89
+ :alphanum => 'Alpha-Numeric',
90
+ :num => 'Numeric',
91
+ :alpha => 'Letters',
92
+ :caps => 'All-Caps',
93
+ :random => 'Random',
94
+ :previous => 'Previous',
95
+ :quit => 'Quit',
96
+ :cancel => 'Cancel',
97
+ :save => 'Save',
98
+ :cpwd => 'Change Data File Password',
99
+ :delete => 'Delete Account',
100
+ }
101
+
102
+ def self.edit(window, passwords)
103
+ begin
104
+ dialog_options = {:window=>window}
105
+ updated = false # only saves data if data updated
106
+
107
+ vbox = Gtk::VBox.new
108
+ window.add(vbox)
109
+
110
+ pwdlength = Gtk::SpinButton.new(MIN_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH, 1)
111
+ pwdlength.value = DEFAULT_PASSWORD_LENGTH
112
+ pwdlength.width_request = SPIN_BUTTON_LENGTH
113
+ pwdlength.modify_font(FONT[:normal])
114
+ goto_url = Gtk::Button.new('Go')
115
+ goto_url.child.modify_font(FONT[:normal])
116
+ goto_url.width_request = GO_BUTTON_LENGTH
117
+
118
+ widget = {}
119
+ EDITOR_LABELS.each {|s|
120
+ hbox = Gtk::HBox.new
121
+ #label = Gtk::Label.new(TEXT[s]+':',hbox)
122
+ label = Gtk2App::Label.new(TEXT[s]+':',hbox) # Gtk2App's Label
123
+ #label.modify_font(FONT[:normal])
124
+ label.width_request = LABEL_WIDTH
125
+ label.justify = Gtk::JUSTIFY_RIGHT
126
+ #label.wrap = true
127
+ widget[s] = (s==:account)? Gtk::ComboBoxEntry.new : Gtk::Entry.new
128
+ widget[s].width_request = ENTRY_WIDTH -
129
+ ((s == :password)? (SPIN_BUTTON_LENGTH+2*GUI[:padding]):
130
+ ((s == :url)? (GO_BUTTON_LENGTH+2*GUI[:padding]): 0))
131
+ widget[s].modify_font(FONT[:normal])
132
+ #hbox.pack_start(label, false, false, GUI[:padding])
133
+ hbox.pack_start(widget[s], false, false, GUI[:padding])
134
+ vbox.pack_start(hbox, false, false, GUI[:padding])
135
+ hbox.pack_start(pwdlength, false, false, GUI[:padding]) if s == :password
136
+ hbox.pack_start(goto_url, false, false, GUI[:padding]) if s == :url
137
+ }
138
+
139
+ # The go button opens the url in a browser
140
+ goto_url.signal_connect('clicked'){
141
+ system("#{APP[:browser]} #{widget[:url].text} > /dev/null 2> /dev/null &")
142
+ }
143
+
144
+ EDITOR_BUTTONS.each{|row|
145
+ hbox = Gtk::HBox.new
146
+ row.each {|s|
147
+ widget[s] = Gtk::Button.new(TEXT[s])
148
+ widget[s].child.modify_font(FONT[:normal])
149
+ hbox.pack_start(widget[s], false, false, GUI[:padding])
150
+ }
151
+ vbox.pack_start(hbox, false, false, GUI[:padding])
152
+ }
153
+
154
+ # Account
155
+ passwords.accounts.each { |account|
156
+ widget[:account].append_text( account )
157
+ }
158
+ widget[:account].active = @@index if @@index
159
+ account_changed = proc {
160
+ account = (widget[:account].active_text)? widget[:account].active_text.strip: ''
161
+ if account.length > 0 then
162
+ widget[:password].text = ''
163
+ if passwords.include?(account) then
164
+ widget[:url].text = passwords.url_of(account)
165
+ widget[:note].text = passwords.note_of(account)
166
+ widget[:username].text = passwords.username_of(account)
167
+ else
168
+ widget[:url].text = ''
169
+ widget[:note].text = ''
170
+ widget[:username].text = ''
171
+ end
172
+ @@index = widget[:account].active
173
+ end
174
+ }
175
+ account_changed.call
176
+ widget[:account].signal_connect('changed'){
177
+ account_changed.call
178
+ }
179
+
180
+ # New Password
181
+ widget[:password].visibility = false
182
+
183
+ # Update
184
+ widget[:update].signal_connect('clicked'){
185
+ url = widget[:url].text.strip
186
+ if url.length == 0 || url =~ URL_PATTERN then
187
+ account = (widget[:account].active_text)? widget[:account].active_text.strip: ''
188
+ if account.length > 0 then
189
+ updated = true if !updated
190
+ if !passwords.include?(account) then
191
+ passwords.add(account)
192
+ @@index = i = passwords.accounts.index(account)
193
+ widget[:account].insert_text(i,account)
194
+ end
195
+ passwords.url_of(account, url)
196
+ passwords.note_of(account, widget[:note].text.strip)
197
+ passwords.username_of(account, widget[:username].text.strip)
198
+ password = widget[:password].text.strip
199
+ if password.length > 0 then
200
+ passwords.password_of(account, password) if !passwords.verify?(account, password)
201
+ widget[:password].text = ''
202
+ end
203
+ end
204
+ else
205
+ DIALOGS.quick_message('Need url like http://www.site.com/page.html', dialog_options)
206
+ end
207
+ }
208
+
209
+ # Random
210
+ widget[:random].signal_connect('clicked'){
211
+ suggestion = ''
212
+ pwdlength.value.to_i.times do
213
+ suggestion += (rand(94)+33).chr
214
+ end
215
+ widget[:password].text = suggestion
216
+ }
217
+ # Alpha-Numeric
218
+ widget[:alphanum].signal_connect('clicked'){
219
+ suggestion = ''
220
+ while suggestion.length < pwdlength.value.to_i do
221
+ chr = (rand(75)+48).chr
222
+ suggestion += chr if chr =~/\w/
223
+ end
224
+ widget[:password].text = suggestion
225
+ }
226
+ # Numeric
227
+ widget[:num].signal_connect('clicked'){
228
+ suggestion = ''
229
+ pwdlength.value.to_i.times do
230
+ chr = (rand(10)+48).chr
231
+ suggestion += chr
232
+ end
233
+ widget[:password].text = suggestion
234
+ }
235
+ # Letters
236
+ widget[:alpha].signal_connect('clicked'){
237
+ suggestion = ''
238
+ while suggestion.length < pwdlength.value.to_i do
239
+ chr = (rand(58)+65).chr
240
+ suggestion += chr if chr =~/[A-Z]/i
241
+ end
242
+ widget[:password].text = suggestion
243
+ }
244
+ # Caps
245
+ widget[:caps].signal_connect('clicked'){
246
+ suggestion = ''
247
+ pwdlength.value.to_i.times do
248
+ chr = (rand(26)+65).chr
249
+ suggestion += chr
250
+ end
251
+ widget[:password].text = suggestion
252
+ }
253
+
254
+ # Visibility
255
+ widget[:visibility].signal_connect('clicked'){
256
+ widget[:password].visibility = !widget[:password].visibility?
257
+ }
258
+
259
+ # Current
260
+ widget[:current].signal_connect('clicked'){
261
+ account = (widget[:account].active_text)? widget[:account].active_text.strip: ''
262
+ PRIMARY.text = passwords.password_of(account)
263
+ CLIPBOARD.text = passwords.username_of(account)
264
+ }
265
+
266
+ # Previous
267
+ widget[:previous].signal_connect('clicked'){
268
+ account = (widget[:account].active_text)? widget[:account].active_text.strip: ''
269
+ PRIMARY.text = passwords.previous_password_of(account)
270
+ CLIPBOARD.text = passwords.username_of(account)
271
+ }
272
+
273
+ # Change Password
274
+ widget[:cpwd].signal_connect('clicked'){
275
+ if pwd1 = Gtk2PasswordApp.get_salt('New Password') then
276
+ if pwd2 = Gtk2PasswordApp.get_salt('Verify') then
277
+ while !(pwd1==pwd2) do
278
+ pwd1 = Gtk2PasswordApp.get_salt('Try again!')
279
+ return if !pwd1
280
+ pwd2 = Gtk2PasswordApp.get_salt('Verify')
281
+ return if !pwd2
282
+ end
283
+ #@pwd = pwd1
284
+ passwords.save(pwd1)
285
+ end
286
+ end
287
+ }
288
+
289
+ # Save
290
+ widget[:save].signal_connect('clicked'){
291
+ if updated then
292
+ passwords.save
293
+ updated = false
294
+ Gtk2PasswordApp.rebuild_menu(passwords)
295
+ end
296
+ Gtk2App.close
297
+ }
298
+
299
+ # Delete
300
+ widget[:delete].signal_connect('clicked'){
301
+ account = (widget[:account].active_text)? widget[:account].active_text.strip: nil
302
+ if account then
303
+ i = passwords.accounts.index(account)
304
+ if i then
305
+ passwords.delete(account)
306
+ widget[:account].remove_text(i)
307
+ @@index = (widget[:account].active = (i > 0)? i - 1: 0)
308
+ updated = true
309
+ DIALOGS.quick_message("#{account} deleted.", dialog_options)
310
+ end
311
+ end
312
+ }
313
+
314
+ # Cancel
315
+ widget[:cancel].signal_connect('clicked'){ Gtk2App.close }
316
+ window.signal_connect('destroy'){
317
+ if updated then
318
+ passwords.load # revert
319
+ updated = false
320
+ end
321
+ }
322
+
323
+ window.show_all
324
+ rescue Exception
325
+ puts_bang!
326
+ end
327
+ end
328
+ end
@@ -0,0 +1,30 @@
1
+ require 'yaml'
2
+ require 'rubygems'
3
+ begin
4
+ require 'crypt_tea'
5
+ rescue Exception
6
+ # above is what works, but documentation shows this...
7
+ require 'crypt-tea'
8
+ end
9
+
10
+ module Gtk2PasswordApp
11
+ class IOCrypt
12
+ LENGTH = 15
13
+
14
+ def initialize(passphrase)
15
+ @key = Crypt::XXTEA.new(passphrase[0..LENGTH])
16
+ end
17
+
18
+ def load(dumpfile)
19
+ data = nil
20
+ File.open(dumpfile,'r'){|fh| data = YAML.load( @key.decrypt( fh.read ) ) }
21
+ return data
22
+ end
23
+
24
+ def dump(dumpfile, data)
25
+ count = nil
26
+ File.open(dumpfile,'w') { |fh| count = fh.write( @key.encrypt( YAML.dump( data ) ) ) }
27
+ return count
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,93 @@
1
+ require 'gtk2passwordapp/passwords_data.rb'
2
+ module Gtk2PasswordApp
3
+ class Passwords < PasswordsData
4
+
5
+ def _create_passphrase
6
+ passphrase = ''
7
+
8
+ IOCrypt::LENGTH.times do
9
+ passphrase += (rand(94)+33).chr
10
+ end
11
+ File.open(@pfile,'w'){|fh| fh.write passphrase }
12
+ File.chmod(0600, @pfile)
13
+
14
+ return passphrase
15
+ end
16
+
17
+ def get_passphrase(mv=false)
18
+ passphrase = ''
19
+
20
+ @pfile = UserSpace::DIRECTORY+'/passphrase.txt'
21
+ if mv then
22
+ File.rename(@pfile, @pfile+'.bak') if File.exist?(@pfile)
23
+ passphrase = _create_passphrase
24
+ else
25
+ if File.exist?(@pfile) then
26
+ File.open(@pfile,'r'){|fh| passphrase = fh.read }
27
+ else
28
+ passphrase = _create_passphrase(@pfile)
29
+ end
30
+ end
31
+
32
+ return passphrase
33
+ end
34
+
35
+ def has_datafile?
36
+ Find.find(UserSpace::DIRECTORY){|fn|
37
+ Find.prune if !(fn==UserSpace::DIRECTORY) && File.directory?(fn)
38
+ if fn =~/[0123456789abcdef]{32}\.dat$/ then
39
+ return true
40
+ end
41
+ }
42
+ return false
43
+ end
44
+
45
+ attr_reader :pfile
46
+ def initialize
47
+ @pwd = Gtk2PasswordApp.get_salt || exit
48
+ @pfile = nil
49
+ @pph = get_passphrase
50
+ super(@pwd+@pph)
51
+ # Password file exist?
52
+ if self.exist? # then
53
+ # Yes, load passwords file.
54
+ self.load
55
+ else
56
+ # No, check if there is a file....
57
+ if has_datafile? # then
58
+ # Yes, it's got a datafile. Ask for password again.
59
+ while !self.exist? do
60
+ @pwd = Gtk2PasswordApp.get_salt('Try again!') || exit
61
+ super(@pwd+@pph)
62
+ end
63
+ self.load
64
+ else
65
+ # Else, must be a new install.
66
+ pwd = @pwd
67
+ @pwd = Gtk2PasswordApp.get_salt('Verify New Password') || exit
68
+ while !(pwd == @pwd) do
69
+ pwd = Gtk2PasswordApp.get_salt('Try again!') || exit
70
+ @pwd = Gtk2PasswordApp.get_salt('Verify New Password') || exit
71
+ end
72
+ end
73
+ end
74
+ # Off to the races...
75
+ end
76
+
77
+ def save(pwd=nil)
78
+ if pwd then
79
+ pfbak = self.pfile + '.bak'
80
+ pph = get_passphrase(true) # new passphrase
81
+ dfbak = self.dumpfile + '.bak'
82
+ super(pwd+pph)
83
+ @pwd = pwd
84
+ @pph = pph
85
+ File.unlink(pfbak) if File.exist?(pfbak)
86
+ File.unlink(dfbak) if File.exist?(dfbak)
87
+ else
88
+ super()
89
+ end
90
+ end
91
+
92
+ end
93
+ end
@@ -1,11 +1,11 @@
1
- # $Date: 2009/02/26 00:27:18 $
2
- require 'lib/iocrypt'
1
+ require 'gtk2passwordapp/iocrypt'
3
2
  require 'digest/md5'
4
3
 
4
+ module Gtk2PasswordApp
5
5
  class PasswordsData
6
6
  include Configuration
7
7
  attr_accessor :account
8
- attr_reader :data
8
+ attr_reader :data, :dumpfile
9
9
 
10
10
  PASSWORD = 0
11
11
  PREVIOUS = 1
@@ -16,13 +16,8 @@ class PasswordsData
16
16
 
17
17
  def _reset(passphrase)
18
18
  raise "Need a good passphrase" if !passphrase || passphrase.length < 7
19
- @passphrase = passphrase[0..55]
19
+ @passphrase = passphrase[0..IOCrypt::LENGTH]
20
20
  @dumpfile = PASSWORDS_DATA_DIR + '/' + Digest::MD5.hexdigest(@passphrase) + '.dat'
21
- @online = (@dumpfile =~ /^http:\/\//)? true: false
22
- end
23
-
24
- def online?
25
- @online
26
21
  end
27
22
 
28
23
  def initialize(passphrase)
@@ -31,19 +26,16 @@ class PasswordsData
31
26
  end
32
27
 
33
28
  def exist?
34
- raise "n/a online" if @online
35
29
  File.exist?(@dumpfile)
36
30
  end
37
31
 
38
32
  def load(passphrase = nil)
39
33
  _reset(passphrase) if passphrase
40
- raise "Wrong passphrase" if !@online && !exist?
41
34
  iocrypt = IOCrypt.new(@passphrase)
42
35
  @data = iocrypt.load(@dumpfile)
43
36
  end
44
37
 
45
38
  def save(passphrase = nil)
46
- raise "n/a online" if @online
47
39
  # just in case, keep a backup
48
40
  File.rename(@dumpfile, @dumpfile+'.bak') if File.exist?(@dumpfile)
49
41
  _reset(passphrase) if passphrase
@@ -53,7 +45,6 @@ class PasswordsData
53
45
  end
54
46
 
55
47
  def add(account)
56
- raise "n/a online" if @online
57
48
  raise "Pre-existing" if @data[account]
58
49
  raise "Can't have nil account" if !account
59
50
  @data[account] = ['','','','','']
@@ -72,7 +63,6 @@ class PasswordsData
72
63
  end
73
64
 
74
65
  def delete(account)
75
- raise "n/a online" if @online
76
66
  raise "#{account} not found" if !@data[account]
77
67
  @data.delete(account)
78
68
  end
@@ -117,3 +107,4 @@ class PasswordsData
117
107
  return @data[account][USERNAME] || ''
118
108
  end
119
109
  end
110
+ end
data/pngs/logo.png ADDED
Binary file