gtk2passwordapp 0.0.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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