diff --git a/icloud_gui.py b/icloud_gui.py index 2067218..41a53f1 100644 --- a/icloud_gui.py +++ b/icloud_gui.py @@ -1188,32 +1188,59 @@ class AuthPage(Gtk.Box): def _on_auth(self, btn): btn.set_sensitive(False) - btn.set_label("Signing in…") - args = ["auth"] + btn.set_label("Waiting for Apple…") + self._auth_btn = btn + cmd = [ICLOUDCTL, "auth"] if self.sms_check.get_active(): - args.append("--force-sms") + cmd.append("--force-sms") if self.debug_check.get_active(): - args.append("--debug") - self.status_label.set_markup('Connecting to Apple — a code will be sent to your device…') + cmd.append("--debug") + self.status_label.set_markup('Connecting to Apple — a code will appear on your device…') - def _worker(): - rc, out, err = icloudctl(*args) - GLib.idle_add(self._auth_result, rc, out + err, btn) - threading.Thread(target=_worker, daemon=True).start() - - def _auth_result(self, rc, output, btn): - btn.set_sensitive(True) - btn.set_label("Sign in with Apple ID") - if "two-factor" in output.lower() or "verification" in output.lower() or "2fa" in output.lower() or "enter" in output.lower(): - self.status_label.set_markup( - 'Code sent — enter it in the box below.' + try: + self._auth_proc = subprocess.Popen( + cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, text=True, bufsize=1 ) - self.tfa_entry.grab_focus() - elif rc == 0: + except Exception as e: + btn.set_sensitive(True) + btn.set_label("Sign in with Apple ID") + self.status_label.set_markup(f'Failed to start: {e}') + return + + proc = self._auth_proc + output_lines = [] + + def _reader(): + for line in proc.stdout: + output_lines.append(line) + low = line.lower() + if "code" in low or "enter" in low or "sent" in low or "2fa" in low: + GLib.idle_add(self._prompt_for_code) + proc.wait() + out = "".join(output_lines) + GLib.idle_add(self._auth_proc_done, proc.returncode, out) + + threading.Thread(target=_reader, daemon=True).start() + + def _prompt_for_code(self): + self.status_label.set_markup('Code sent — enter it below and click Verify.') + self.tfa_entry.grab_focus() + + def _auth_proc_done(self, rc, output): + if rc == 0 and "authenticated" in output.lower(): + self._unlock_auth_btn() self.status_label.set_markup('Authentication successful!') - else: - msg = GLib.markup_escape_text(output[:300]) - self.status_label.set_markup(f'{msg}') + elif rc != 0: + self._unlock_auth_btn() + msg = GLib.markup_escape_text(output[-300:].strip()) + self.status_label.set_markup(f'{msg or f"Exited {rc}"}') + # rc==0 but still waiting for code → leave button locked until _on_tfa completes + + def _unlock_auth_btn(self): + if hasattr(self, "_auth_btn"): + self._auth_btn.set_sensitive(True) + self._auth_btn.set_label("Sign in with Apple ID") def _on_tfa(self, _): code = self.tfa_entry.get_text().strip() @@ -1222,12 +1249,24 @@ class AuthPage(Gtk.Box): return self.tfa_status.set_markup('Verifying…') + proc = getattr(self, "_auth_proc", None) + if proc and proc.poll() is None: + # Feed the code into the waiting auth process + try: + proc.stdin.write(code + "\n") + proc.stdin.flush() + except Exception as e: + self.tfa_status.set_markup(f'Error: {e}') + return + + # auth process already finished — run a fresh auth with code piped in def _worker(): rc, out, err = icloudctl("auth", input_data=code + "\n") GLib.idle_add(self._tfa_result, rc, out + err) threading.Thread(target=_worker, daemon=True).start() def _tfa_result(self, rc, output): + self._unlock_auth_btn() if rc == 0 or "authenticated" in output.lower() or "success" in output.lower(): self.tfa_status.set_markup('Verified! You can now start the service.') self.tfa_entry.set_text("")