Module: Lich::Common::GUI::EncryptionModeChange

Defined in:
documented/common/gui/encryption_mode_change.rb

Class Method Summary collapse

Class Method Details

.show_change_mode_dialog(parent, data_dir, on_completion = nil) ⇒ Boolean

Displays a dialog for changing the encryption mode.

Examples:

Lich::Common::GUI::EncryptionModeChange.show_change_mode_dialog(parent_window, "/path/to/data")

Parameters:

  • parent (Gtk::Window)

    The parent window for the dialog.

  • data_dir (String)

    The directory containing the YAML data.

  • on_completion (Proc, nil) (defaults to: nil)

    Optional callback to be called upon completion.

Returns:

  • (Boolean)

    Returns true if the dialog was shown successfully.

Raises:

  • (StandardError)

    Raises an error if the YAML file cannot be loaded.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'documented/common/gui/encryption_mode_change.rb', line 20

def self.show_change_mode_dialog(parent, data_dir, on_completion = nil)
  yaml_file = YamlState.yaml_file_path(data_dir)

  # Load current mode
  begin
    yaml_data = YAML.load_file(yaml_file)
    current_mode = yaml_data['encryption_mode']&.to_sym || :plaintext
  rescue StandardError => e
    Lich.log "error: Failed to load YAML for mode change dialog: #{e.message}"
    show_error_dialog(parent, "Failed to load account data: #{e.message}")
    return false
  end

  # Create dialog
  dialog = Gtk::Dialog.new(
    title: "Change Encryption Mode",
    parent: parent,
    flags: :modal,
    buttons: [
      ["Change Mode", Gtk::ResponseType::APPLY],
      ["Cancel", Gtk::ResponseType::CANCEL]
    ]
  )

  dialog.set_default_size(450, 350)
  dialog.border_width = 10

  Accessibility.make_window_accessible(
    dialog,
    "Change Encryption Mode Dialog",
    "Dialog for changing the encryption mode"
  )

  # Create content area
  content_area = dialog.content_area
  content_area.spacing = 10

  # Add header
  header_label = Gtk::Label.new
  header_label.set_markup("<span weight='bold'>Change Encryption Mode</span>")
  header_label.set_xalign(0)

  Accessibility.make_accessible(
    header_label,
    "Change Encryption Mode Header",
    "Title for the encryption mode change dialog",
    :label
  )

  content_area.add(header_label)

  # Add current mode display
  current_mode_label = Gtk::Label.new
  current_mode_text = mode_display_text(current_mode)
  current_mode_label.set_markup("Current Mode: <b>#{current_mode_text}</b>")
  current_mode_label.set_xalign(0)

  Accessibility.make_accessible(
    current_mode_label,
    "Current Mode Display",
    "Shows the current encryption mode",
    :label
  )

  content_area.add(current_mode_label)

  # Add separator
  separator = Gtk::Separator.new(:horizontal)
  content_area.add(separator)

  # Add mode selection label
  selection_label = Gtk::Label.new("Select new encryption mode:")
  selection_label.set_xalign(0)

  Accessibility.make_accessible(
    selection_label,
    "Mode Selection Instruction",
    "Instruction to select a new encryption mode",
    :label
  )

  content_area.add(selection_label)

  # Create radio button group for mode selection
  plaintext_radio = Gtk::RadioButton.new(label: "Plaintext (No Encryption)")
  plaintext_radio.tooltip_text = "Passwords stored unencrypted (accessibility mode)"

  Accessibility.make_accessible(
    plaintext_radio,
    "Plaintext Mode Option",
    "Select plaintext encryption mode",
    :button
  )

  standard_radio = Gtk::RadioButton.new(member: plaintext_radio,
                                        label: "Standard Encryption (Account Name)")
  standard_radio.tooltip_text = "Encrypt with account name"

  Accessibility.make_accessible(
    standard_radio,
    "Standard Mode Option",
    "Select standard encryption mode",
    :button
  )

  enhanced_radio = Gtk::RadioButton.new(member: plaintext_radio,
                                        label: "Enhanced Encryption (Master Password)")
  enhanced_radio.tooltip_text = "Encrypt with master password (strongest security)"

  Accessibility.make_accessible(
    enhanced_radio,
    "Enhanced Mode Option",
    "Select enhanced encryption mode",
    :button
  )

  # Set currently selected mode
  case current_mode
  when :plaintext
    plaintext_radio.active = true
  when :standard
    standard_radio.active = true
  when :enhanced
    enhanced_radio.active = true
  end

  content_area.add(plaintext_radio)
  content_area.add(standard_radio)
  content_area.add(enhanced_radio)

  # Add warning note
  warning_label = Gtk::Label.new
  warning_label.set_markup("<i>All passwords will be decrypted and re-encrypted\nwith the new method.</i>")
  warning_label.set_xalign(0)
  warning_label.set_line_wrap(true)

  Accessibility.make_accessible(
    warning_label,
    "Re-encryption Warning",
    "Warning that all passwords will be re-encrypted",
    :label
  )

  content_area.add(warning_label)

  # Track mode change result
  mode_changed = false

  # Set up response handler
  dialog.signal_connect('response') do |dlg, response|
    Lich.log "debug: change_encryption_mode response handler, response=#{response}"
    if response == Gtk::ResponseType::APPLY
      # Determine selected mode
      selected_mode = if plaintext_radio.active?
                        :plaintext
                      elsif standard_radio.active?
                        :standard
                      elsif enhanced_radio.active?
                        :enhanced
                      end

      Lich.log "debug: selected_mode=#{selected_mode}, current_mode=#{current_mode}"

      # If mode didn't change, just close dialog
      if selected_mode == current_mode
        Lich.log "debug: mode unchanged, closing"
        dlg.destroy
        next
      end

      dlg.destroy
      Lich.log "debug: spawning thread for dialogs and mode change"

      # Spawn thread to run dialogs and mode change
      # Dialogs use Gtk.queue internally, so they need a non-GTK thread
      Thread.new do
        Lich.log "debug: thread starting"
        new_master_password = nil
        validation_passed = true

        # Validate leaving current mode
        if current_mode == :enhanced
          Lich.log "debug: showing password confirmation dialog for leaving enhanced mode"
          validation_result = MasterPasswordPromptUI.show_password_confirmation_for_mode_change(
            yaml_data['master_password_validation_test'],
            leaving_enhanced: true
          )
          Lich.log "debug: validation_result=#{validation_result.inspect}"
          unless validation_result && validation_result[:password]
            Lich.log "debug: password confirmation cancelled"
            validation_passed = false
          end
        end

        if validation_passed
          # Validate entering new mode
          if selected_mode == :enhanced
            Lich.log "debug: showing password dialog"
            new_master_password = MasterPasswordPromptUI.show_dialog
            Lich.log "debug: password_nil?=#{new_master_password.nil?}"
            unless new_master_password
              Lich.log "debug: password entry cancelled"
              validation_passed = false
            end
          elsif selected_mode == :plaintext
            Lich.log "debug: showing plaintext confirmation"
            unless confirm_plaintext_mode_dialog(parent)
              Lich.log "debug: plaintext confirmation cancelled"
              validation_passed = false
            end
          end
          # selected_mode == :standard needs nothing special
        end

        # All validations passed, queue the actual mode change
        if validation_passed
          Lich.log "debug: all validations passed, queuing mode change"
          Gtk.queue do
            Lich.log "debug: in Gtk.queue, calling YamlState.change_encryption_mode"
            success = YamlState.change_encryption_mode(
              data_dir,
              selected_mode,
              new_master_password
            )
            Lich.log "debug: change_encryption_mode returned #{success}"

            if success
              mode_changed = true
              success_dialog = Gtk::MessageDialog.new(
                parent: parent,
                flags: :modal,
                type: :info,
                buttons: :ok,
                message: "Encryption mode changed successfully."
              )

              Accessibility.make_window_accessible(
                success_dialog,
                "Success Message",
                "Encryption mode change success notification"
              )

              success_dialog.run
              success_dialog.destroy
              # Call completion callback after mode change succeeds
              on_completion.call if on_completion
            else
              error_dialog = Gtk::MessageDialog.new(
                parent: parent,
                flags: :modal,
                type: :error,
                buttons: :ok,
                message: "Failed to change encryption mode. Please check the logs."
              )

              Accessibility.make_window_accessible(
                error_dialog,
                "Error Message",
                "Encryption mode change failed notification"
              )

              error_dialog.run
              error_dialog.destroy
            end
          end
        end
        Lich.log "debug: thread complete"
      end
      Lich.log "debug: thread spawned, signal handler returning"
    elsif response == Gtk::ResponseType::CANCEL
      Lich.log "debug: mode change cancelled"
      dlg.destroy
    end
  end

  # Show dialog
  dialog.show_all

  # Return true to indicate dialog was shown (actual result will be async)
  true
end