|
4 | 4 |
|
5 | 5 | import argparse |
6 | 6 | import configparser |
| 7 | +import contextlib |
7 | 8 | import logging |
8 | 9 | import os |
9 | 10 | import stat |
@@ -311,36 +312,60 @@ def fetch_zuliprc(zuliprc_path: str) -> None: |
311 | 312 | print(in_color("red", "\nIncorrect Email(or Username) or Password!\n")) |
312 | 313 | login_data = get_api_key(realm_url) |
313 | 314 |
|
| 315 | + zulip_key_path = os.path.join( |
| 316 | + os.path.dirname(os.path.abspath(zuliprc_path)), "zulip_key" |
| 317 | + ) |
| 318 | + |
314 | 319 | preferred_realm_url, login_id, api_key = login_data |
315 | 320 | save_zuliprc_failure = _write_zuliprc( |
316 | | - zuliprc_path, |
317 | | - login_id=login_id, |
| 321 | + to_path=zuliprc_path, |
| 322 | + key_path=zulip_key_path, |
318 | 323 | api_key=api_key, |
| 324 | + login_id=login_id, |
319 | 325 | server_url=preferred_realm_url, |
320 | 326 | ) |
321 | 327 | if not save_zuliprc_failure: |
322 | | - print(f"Generated API key saved at {zuliprc_path}") |
| 328 | + print(f"Generated config file saved at {zuliprc_path}") |
323 | 329 | else: |
324 | 330 | exit_with_error(save_zuliprc_failure) |
325 | 331 |
|
326 | 332 |
|
327 | 333 | def _write_zuliprc( |
328 | | - to_path: str, *, login_id: str, api_key: str, server_url: str |
| 334 | + to_path: str, *, key_path: str, login_id: str, api_key: str, server_url: str |
329 | 335 | ) -> str: |
330 | 336 | """ |
331 | | - Writes a zuliprc file, returning a non-empty error string on failure |
332 | | - Only creates new private files; errors if file already exists |
| 337 | + Writes both zuliprc and zulip_key files securely. |
| 338 | + Ensures atomicity: if one file fails to write, cleans up the other. |
| 339 | + Returns an empty string on success, or a descriptive error message on failure. |
333 | 340 | """ |
| 341 | + zuliprc_created = False |
| 342 | + |
334 | 343 | try: |
| 344 | + # Write zuliprc |
335 | 345 | with open( |
336 | 346 | os.open(to_path, os.O_CREAT | os.O_WRONLY | os.O_EXCL, 0o600), "w" |
337 | 347 | ) as f: |
338 | | - f.write(f"[api]\nemail={login_id}\nkey={api_key}\nsite={server_url}") |
| 348 | + f.write( |
| 349 | + f"[api]\nemail={login_id}\npasscmd=cat zulip_key\nsite={server_url}" |
| 350 | + ) |
| 351 | + zuliprc_created = True |
| 352 | + # Write zulip_key |
| 353 | + with open( |
| 354 | + os.open(key_path, os.O_CREAT | os.O_WRONLY | os.O_EXCL, 0o600), "w" |
| 355 | + ) as f: |
| 356 | + f.write(api_key) |
| 357 | + |
339 | 358 | return "" |
| 359 | + |
340 | 360 | except FileExistsError: |
341 | | - return f"zuliprc already exists at {to_path}" |
| 361 | + filename = to_path if not zuliprc_created else key_path |
| 362 | + return f"FileExistsError: {filename} already exists" |
342 | 363 | except OSError as ex: |
343 | | - return f"{ex.__class__.__name__}: zuliprc could not be created at {to_path}" |
| 364 | + if zuliprc_created: |
| 365 | + with contextlib.suppress(Exception): |
| 366 | + os.remove(to_path) |
| 367 | + filename = key_path if zuliprc_created else to_path |
| 368 | + return f"{ex.__class__.__name__}: could not create {filename} ({ex})" |
344 | 369 |
|
345 | 370 |
|
346 | 371 | def parse_zuliprc(zuliprc_str: str) -> Dict[str, SettingData]: |
|
0 commit comments