From 8da497849889833bfeecce93db6edbe53b6f59f8 Mon Sep 17 00:00:00 2001 From: SimplifiedPrivacy Date: Wed, 5 Aug 2026 12:03:11 -0400 Subject: [PATCH] Linter bug fixes. Mostly undeclared, unused, or missing imports. One or two outright typos. There's still issues with undeclared types that need to be fixed after api connection refactor is fully implemented. --- core/controllers/ClientController.py | 126 +++++++++--------- core/controllers/ConnectionController.py | 6 +- core/controllers/SyncController.py | 11 +- .../tickets/TicketPayController.py | 5 +- .../tickets/UseTicketController.py | 5 +- core/models/Result.py | 4 +- core/services/WebServiceApiService.py | 4 +- .../is_the_key_to_blame.py | 19 ++- .../networking/api_requests/step2_execute.py | 2 +- .../api_requests/subtools/direct_dns_tools.py | 1 + .../connection_enable.py | 3 +- .../networking/httpx/async_request.py | 2 +- core/services/networking/httpx/connect.py | 11 +- .../services/networking/httpx/make_request.py | 2 +- .../networking/legacy/get_data_from_server.py | 1 + .../dns_tools/process_dns_result.py | 4 +- .../systemwide/encrypted_proxy/singbox.py | 2 +- .../encrypted_proxy/singbox_runner.py | 6 +- .../systemwide/systemwide_wireguard.py | 1 + .../systemwide/wireguard/nmcli_tools.py | 2 + .../networking/tor_tools/bootstrap.py | 3 +- .../networking/tor_tools/install_tor.py | 2 +- core/services/networking/tor_tools/ports.py | 4 +- .../networking/tor_tools/pre_bootstrap.py | 2 +- .../networking/tor_tools/tor_orchestrator.py | 2 +- .../save_and_send_intitial_billing.py | 36 +++-- .../payment_phase/save_billing_choices.py | 2 +- core/services/prepare_tickets/get_pub_key.py | 26 ++-- .../get_public_key_by_config.py | 4 +- .../prepare_tickets/setup_ticket_tracker.py | 2 +- .../ticket_prep_orchestrator.py | 4 +- .../prepare_tickets/ticket_tracker.py | 9 +- .../write_or_read_from_json.py | 43 ++++-- core/utils/run_commands.py | 22 ++- 34 files changed, 228 insertions(+), 150 deletions(-) diff --git a/core/controllers/ClientController.py b/core/controllers/ClientController.py index 5e8289d..7be4a62 100644 --- a/core/controllers/ClientController.py +++ b/core/controllers/ClientController.py @@ -59,86 +59,86 @@ class ClientController: return not ClientVersionController.is_latest(version) - @staticmethod - def legacy_sync(client_observer: ClientObserver = None, connection_observer: ConnectionObserver = None): - if client_observer is not None: - client_observer.notify('synchronizing', "Fetching list of new data ..") + # @staticmethod + # def legacy_sync(client_observer: ClientObserver = None, connection_observer: ConnectionObserver = None): + # if client_observer is not None: + # client_observer.notify('synchronizing', "Fetching list of new data ..") - result = coordinate_cache_sync(client_observer, connection_observer) + # result = coordinate_cache_sync(client_observer, connection_observer) - # Outright Error: - if not result["success"]: - error_msg = result["error"] - if client_observer is not None: - client_observer.notify('synchronizing', f'Error! {error_msg}') - return + # # Outright Error: + # if not result["success"]: + # error_msg = result["error"] + # if client_observer is not None: + # client_observer.notify('synchronizing', f'Error! {error_msg}') + # return - # Same: - changed_tables = result["changed_tables"] - if not changed_tables: - if client_observer is not None: - client_observer.notify('synchronized') - return + # # Same: + # changed_tables = result["changed_tables"] + # if not changed_tables: + # if client_observer is not None: + # client_observer.notify('synchronized') + # return - # We only make it past this point if there's New Data + # # We only make it past this point if there's New Data - # flag for after the save, - data_was_saved = False + # # flag for after the save, + # data_was_saved = False - # Fetch and update the real data (no longer metadata)... + # # Fetch and update the real data (no longer metadata)... - # =================== ORM BASED MODELS ================== - """ - Note: for the new ORM based models, - it does the Tor/system check in the API call itself. - """ + # # =================== ORM BASED MODELS ================== + # """ + # Note: for the new ORM based models, + # it does the Tor/system check in the API call itself. + # """ - if "locations" in changed_tables: - logger.info("Sync of Locations") - if client_observer is not None: - client_observer.notify('synchronizing', 'Fetching Locations List..') + # if "locations" in changed_tables: + # logger.info("Sync of Locations") + # if client_observer is not None: + # client_observer.notify('synchronizing', 'Fetching Locations List..') - final_result = sync_one_orm_model(Location, "locations") - evaluate_errors(final_result) + # final_result = sync_one_orm_model(Location, "locations") + # evaluate_errors(final_result) - if "operators" in changed_tables: - logger.info("Sync of Operators") - if client_observer is not None: - client_observer.notify('synchronizing', 'Fetching Operators List..') + # if "operators" in changed_tables: + # logger.info("Sync of Operators") + # if client_observer is not None: + # client_observer.notify('synchronizing', 'Fetching Operators List..') - final_result_two = sync_one_orm_model(Operator, "operators") - evaluate_errors(final_result_two) + # final_result_two = sync_one_orm_model(Operator, "operators") + # evaluate_errors(final_result_two) - # =================== MANUAL-SQL BASED MODELS ================== - try: - from core.controllers.ConnectionController import ConnectionController - ConnectionController.with_preferred_connection(task=ClientController.__sync, changed_tables=changed_tables, client_observer=client_observer, connection_observer=connection_observer) + # # =================== MANUAL-SQL BASED MODELS ================== + # try: + # from core.controllers.ConnectionController import ConnectionController + # ConnectionController.with_preferred_connection(task=ClientController.__sync, changed_tables=changed_tables, client_observer=client_observer, connection_observer=connection_observer) - # We set the flag to true, - # the reason we use a flag, and don't just save it right here, - # is because we want to isolate the success (or failure) of the real data, - # from the potential failure of the ORM session metadata. - data_was_saved = True + # # We set the flag to true, + # # the reason we use a flag, and don't just save it right here, + # # is because we want to isolate the success (or failure) of the real data, + # # from the potential failure of the ORM session metadata. + # data_was_saved = True - except: - # sync failed here, - if client_observer is not None: - client_observer.notify('synchronizing', 'Fetch Failed, but you can use old data.') - finally: - if data_was_saved: - filtered_metadata = result["filtered_metadata"] # from the top of the function - save_successful = save_metadata(filtered_metadata) # the "save_data" function is inside sync_service + # except: + # # sync failed here, + # if client_observer is not None: + # client_observer.notify('synchronizing', 'Fetch Failed, but you can use old data.') + # finally: + # if data_was_saved: + # filtered_metadata = result["filtered_metadata"] # from the top of the function + # save_successful = save_metadata(filtered_metadata) # the "save_data" function is inside sync_service - if client_observer is None: - logger.error("Error: No client_observer to update the UI, the final part of the sync function skipped") - return # can't update their UI + # if client_observer is None: + # logger.error("Error: No client_observer to update the UI, the final part of the sync function skipped") + # return # can't update their UI - if save_successful: - logger.info("Metadata Saved Successfully") - client_observer.notify('synchronized', "Fetch & Save Complete!") - else: - client_observer.notify('synchronizing', "Saving List of Metadata Failed.") + # if save_successful: + # logger.info("Metadata Saved Successfully") + # client_observer.notify('synchronized', "Fetch & Save Complete!") + # else: + # client_observer.notify('synchronizing', "Saving List of Metadata Failed.") @staticmethod diff --git a/core/controllers/ConnectionController.py b/core/controllers/ConnectionController.py index 300d311..149c843 100644 --- a/core/controllers/ConnectionController.py +++ b/core/controllers/ConnectionController.py @@ -136,12 +136,10 @@ class ConnectionController: try: tor_module = TorModule(Constants.HV_TOR_STATE_HOME) tor_module.create_session(port_number, connection_observer) - except TorServiceInitializationError as e: - logger.error(f"TorServiceInitializationError. Tor Can't Start: {e}") - if connection_observer is not None: - connection_observer.notify('custom_message', "Tor Can't Initialize") except Exception as e: logger.error(f"Tor Can't Start: {e}") + if connection_observer is not None: + connection_observer.notify('custom_message', "Tor Can't Initialize") @staticmethod def terminate_tor_session_connection(port_number: int): diff --git a/core/controllers/SyncController.py b/core/controllers/SyncController.py index 56838f8..63ae333 100644 --- a/core/controllers/SyncController.py +++ b/core/controllers/SyncController.py @@ -165,12 +165,19 @@ def new_sync(client_observer: ClientObserver, connection_observer: ConnectionObs if total_skipped == 0: client_observer.notify('synchronized', "Fetch & Save Complete!") - save_successful = save_metadata(filtered_metadata) # the "save_data" function is inside sync_service - return Result(valid=True, message="Finshed sync.") + save_successful = save_metadata(filtered_metadata) + if save_successful: + return Result(valid=True, message="Finshed sync.") + else: + error_msg = "Finshed sync, but had issues with the saving of metadata for next time." + logger.error(error_msg) + return Result(valid=True, message=error_msg) + elif total_skipped < quantity_of_entries: error_msg = f"Partial Success. {total_skipped} skipped." client_observer.notify('synchronized', error_msg) return Result(valid=True, data=skipped, message=error_msg) + else: error_msg = f"Sync Failed. All {total_skipped} entries were skipped!" client_observer.notify('synchronized', error_msg) diff --git a/core/controllers/tickets/TicketPayController.py b/core/controllers/tickets/TicketPayController.py index 05423aa..d1088ed 100644 --- a/core/controllers/tickets/TicketPayController.py +++ b/core/controllers/tickets/TicketPayController.py @@ -11,7 +11,8 @@ from core.observers.BaseObserver import BaseObserver from core.services.payment_phase.save_and_send_intitial_billing import save_and_send_intitial_billing from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType from core.services.networking.api_requests.step5_solve_api_problems import solve_api_problems - +from core.errors.logger import logger +from core.errors.exceptions import ServerSideError, NetworkingError from core.services.prepare_tickets.ticket_tracker import does_ticket_tracker_exist @@ -132,7 +133,7 @@ def initiate_payment( return invoice_data_object - except InvalidData as e: + except ValueError as e: error_msg = "Invalid Data." ticket_observer.notify("failed_input", subject=error_msg) invoice_data_object.add_error_code("invalid_data") diff --git a/core/controllers/tickets/UseTicketController.py b/core/controllers/tickets/UseTicketController.py index 0341528..37510f9 100644 --- a/core/controllers/tickets/UseTicketController.py +++ b/core/controllers/tickets/UseTicketController.py @@ -19,6 +19,7 @@ from core.services.helpers.get_value_from_config import get_value_from_config from core.utils.basic_operations.does_file_exist import does_file_exist from core.utils.basic_operations.write_or_read_from_json import update_json from core.services.prepare_tickets.ticket_tracker import get_all_unused_tickets +from core.errors.logger import logger import random @@ -162,8 +163,10 @@ def use_ticket( error_msg = f"Ticket is already tied to {location} with the subscription {subscription}" ticket_observer.notify("failed_input", subject=error_msg) return {"valid": False, "message": error_msg} - except: + except ValueError as e: error_msg = f"Your local ticket tracker has no value for ticket {which_ticket}" + logger.error(error_msg) + logger.error(str(e)) return {"valid": False, "message": error_msg} # the actual work here, everything else is just handling: diff --git a/core/models/Result.py b/core/models/Result.py index 5c3ca13..3178e27 100644 --- a/core/models/Result.py +++ b/core/models/Result.py @@ -37,7 +37,7 @@ class Result(): def user_message(self) -> str: """Human-readable error for the UI.""" messages = { - DBErrorType.SUCCESS: "Operation completed successfully.", - DBErrorType.UNKNOWN: f"Error: {self.message}", + ResultError.SUCCESS: "Operation completed successfully.", + ResultError.UNKNOWN: f"Error: {self.message}", } return messages.get(self.error_type, "Unknown error") diff --git a/core/services/WebServiceApiService.py b/core/services/WebServiceApiService.py index 3cfd0a0..57217f0 100644 --- a/core/services/WebServiceApiService.py +++ b/core/services/WebServiceApiService.py @@ -1,7 +1,7 @@ from core.Constants import Constants from core.models.ClientVersion import ClientVersion -# from core.models.Location import Location -# from core.models.Operator import Operator +from core.models.orm_models.Location import Location +from core.models.orm_models.Operator import Operator from core.models.Subscription import Subscription from core.models.SubscriptionPlan import SubscriptionPlan from core.models.invoice.Invoice import Invoice diff --git a/core/services/failed_verification/is_the_key_to_blame.py b/core/services/failed_verification/is_the_key_to_blame.py index 7c0a2b8..58639b5 100644 --- a/core/services/failed_verification/is_the_key_to_blame.py +++ b/core/services/failed_verification/is_the_key_to_blame.py @@ -13,6 +13,8 @@ from core.services.failed_verification.test_if_new_key_works import test_if_new_ from core.services.networking.make_url import make_url from core.services.networking.api_requests.step1_get_or_post import get_data_from_api +from core.services.networking.httpx import connect +from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType # utils from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file @@ -72,12 +74,21 @@ def get_new_pubkey_from_api(connection_observer: ConnectionObserver) -> dict | N url = make_url(which_key_plan) # the result of this is a python dictionary with single ' - api_results = get_data_from_server(url, None, connection_observer) + # api_results = get_data_from_server(url, None, connection_observer) + api_results = connect.single_endpoint( + method="get", + url=url, + observer=connection_observer, + payload=None + ) - if "data" in api_results: - new_public_key = api_results["data"] + if api_results.valid: + new_public_key = api_results.data + return new_public_key + else: + logger.error(f"API Results returned were invalid for that key.") + return None - return new_public_key def are_keys_different(old_public_key, new_public_key) -> bool: diff --git a/core/services/networking/api_requests/step2_execute.py b/core/services/networking/api_requests/step2_execute.py index 6ca5e1a..24eabd5 100644 --- a/core/services/networking/api_requests/step2_execute.py +++ b/core/services/networking/api_requests/step2_execute.py @@ -24,7 +24,7 @@ from core.errors.logger import logger # generic import json, os -from typing import Any +from typing import Any, Optional import time diff --git a/core/services/networking/api_requests/subtools/direct_dns_tools.py b/core/services/networking/api_requests/subtools/direct_dns_tools.py index 0c79723..cd8891a 100644 --- a/core/services/networking/api_requests/subtools/direct_dns_tools.py +++ b/core/services/networking/api_requests/subtools/direct_dns_tools.py @@ -174,6 +174,7 @@ def quad9_proxy_dns_lookup( domain: str, custom_proxy: str, timeout: int = 10, + client_observer: ClientObserver = None ) -> str: logger.debug("Doing a Proxy Quad9 DNS lookup") diff --git a/core/services/networking/general_connection_tools/connection_enable.py b/core/services/networking/general_connection_tools/connection_enable.py index 3449202..51e094f 100644 --- a/core/services/networking/general_connection_tools/connection_enable.py +++ b/core/services/networking/general_connection_tools/connection_enable.py @@ -11,10 +11,11 @@ from core.services.subscriptions.subscriptions import activate_subscription from core.errors.logger import logger from core.errors.exceptions import * +from core.errors.exceptions import FirewallError from typing import Union, Optional, Callable from core.models.session.SessionProfile import SessionProfile from core.models.system.SystemProfile import SystemProfile -from core.Errors import ConnectionTerminationError, InvalidSubscriptionError +from core.Errors import ConnectionTerminationError, InvalidSubscriptionError, MissingSubscriptionError from core.services.WebServiceApiService import WebServiceApiService from core.controllers.ConnectionController import ConnectionController from core.models.BaseProfile import ProfileType diff --git a/core/services/networking/httpx/async_request.py b/core/services/networking/httpx/async_request.py index fd63b11..a9df43a 100644 --- a/core/services/networking/httpx/async_request.py +++ b/core/services/networking/httpx/async_request.py @@ -182,4 +182,4 @@ async def switch_get_and_post(method: str, url: str, client: httpx.Client, paylo else: # nevermind, logger.info("Our strategy of switching GET/POST did NOT work.") - return initial_result + return second_result diff --git a/core/services/networking/httpx/connect.py b/core/services/networking/httpx/connect.py index 1e2ef1f..ddc3ea7 100644 --- a/core/services/networking/httpx/connect.py +++ b/core/services/networking/httpx/connect.py @@ -34,9 +34,7 @@ def single_endpoint(method: str, url: str, observer: ConnectionObserver, payload Purpose: Try a request to a single endpoint using either an existing client, or creating a new one, then error handling. """ - - global _port_used - + connection_type = ConfigurationController.get_connection_enum() client = httpx_client.get_http_session() @@ -62,9 +60,6 @@ def single_endpoint(method: str, url: str, observer: ConnectionObserver, payload ######################################################## # FROM THIS POINT ON, WE HAVE A CLIENT ######################################################## - # #### SPOOF - # _port_used = 9050 - # initial_result = ApiResponse(valid=False, error_type=ErrorType.DNS_TEMPORARY) initial_result = make_request( method=method, @@ -367,3 +362,7 @@ def custom_dns_resolver_for_SINGLE_ENDPOINT(method: str, url: str, observer: Con # """Merge two dictionaries. dict1 values take precedence on key collision.""" # return {**dict2, **dict1} +def replace_http_with_https(url): + if url.startswith("http://"): + return url.replace("http://", "https://", 1) + return url diff --git a/core/services/networking/httpx/make_request.py b/core/services/networking/httpx/make_request.py index fd37e11..6695c6d 100644 --- a/core/services/networking/httpx/make_request.py +++ b/core/services/networking/httpx/make_request.py @@ -176,4 +176,4 @@ def switch_get_and_post(method: str, url: str, client: httpx.Client, payload: di else: # nevermind, logger.info("Our strategy of switching GET/POST did NOT work.") - return initial_result + return second_result diff --git a/core/services/networking/legacy/get_data_from_server.py b/core/services/networking/legacy/get_data_from_server.py index 6b781ab..2a1992f 100644 --- a/core/services/networking/legacy/get_data_from_server.py +++ b/core/services/networking/legacy/get_data_from_server.py @@ -16,6 +16,7 @@ from core.services.networking.regular_get_request import regular_get_request from core.errors.exceptions import * from core.errors.logger import logger import traceback +from typing import Optional # Generic GET request to an endpoint, filtered by the user's preference of connection type (Tor or Not) diff --git a/core/services/networking/systemwide/dns_tools/process_dns_result.py b/core/services/networking/systemwide/dns_tools/process_dns_result.py index 176b4cb..2d3f357 100644 --- a/core/services/networking/systemwide/dns_tools/process_dns_result.py +++ b/core/services/networking/systemwide/dns_tools/process_dns_result.py @@ -84,13 +84,13 @@ def orchestrate_dns_check(target_interface: str) -> Result: else: logger.info(f"[DNS Orchestrator] Some of the DNS checks did NOT work. But this might be because the tunnel is down.") - targets_not_found = 0 + target_not_found = 0 for each_object in list_of_results_as_objects: if each_object.error_type == SearchError.TARGET_MISSING: target_not_found = target_not_found + 1 - return Result(valid=False, data=targets_not_found) + return Result(valid=False, data=target_not_found) diff --git a/core/services/networking/systemwide/encrypted_proxy/singbox.py b/core/services/networking/systemwide/encrypted_proxy/singbox.py index 4d91656..ad64147 100644 --- a/core/services/networking/systemwide/encrypted_proxy/singbox.py +++ b/core/services/networking/systemwide/encrypted_proxy/singbox.py @@ -1,6 +1,6 @@ from core.services.networking.systemwide.systemwide_errors import systemwide_hell_raiser from core.utils.basic_operations.wrap_with import wrap_with -from core.utils.run_commands import run_generic_command import run_generic_command +from core.utils.run_commands import run_generic_command from core.models.Result import Result, ResultError from core.errors.logger import logger diff --git a/core/services/networking/systemwide/encrypted_proxy/singbox_runner.py b/core/services/networking/systemwide/encrypted_proxy/singbox_runner.py index 27784da..0188898 100644 --- a/core/services/networking/systemwide/encrypted_proxy/singbox_runner.py +++ b/core/services/networking/systemwide/encrypted_proxy/singbox_runner.py @@ -10,6 +10,8 @@ from core.services.networking.systemwide.systemwide_utils import get_firewall_se from core.services.networking.systemwide import dns from core.services.networking.systemwide.general_connection_tools.general_firewall_dns_tools import generic_enable_firewall_w_retry from core.errors.exceptions import FirewallError, DNSError +from essentials.observers.ConnectionObserver import ConnectionObserver + # generic import subprocess @@ -106,7 +108,7 @@ def start_singbox( if not activation_result.valid: error_msg = f"Singbox failed to start after {QUANTITY_OF_ATTEMPTS} attempts" logger.error(f"[{function_name}] {error_msg}") - return Result(valid=False, error_type=ResultError=PROCESS_WONT_START, message=error_msg) + return Result(valid=False, error_type=ResultError.PROCESS_WONT_START, message=error_msg) process_id = activation_result.data time.sleep(2) @@ -129,7 +131,7 @@ def start_singbox( ) # ============= FIREWALL ============= - logger.info(f"[{function_name}] Attempting to enable the Firewall for {SINGBOX_TUN_IF} and {Constants.SINGBOX_INTERNAL_SUBNET}...") + logger.info(f"[{function_name}] Attempting to enable the Firewall for {Constants.SINGBOX_TUN_IF} and {Constants.SINGBOX_INTERNAL_SUBNET}...") # this is labeled "generic" for being protocol neutral firewall_result = generic_enable_firewall_w_retry( interface_name=Constants.SINGBOX_TUN_IF, diff --git a/core/services/networking/systemwide/systemwide_wireguard.py b/core/services/networking/systemwide/systemwide_wireguard.py index d934432..549f64f 100644 --- a/core/services/networking/systemwide/systemwide_wireguard.py +++ b/core/services/networking/systemwide/systemwide_wireguard.py @@ -230,6 +230,7 @@ def _establish_connection_with_retry( except ConnectionError as e: # Unrecoverable error, fail immediately + logger.error(str(e)) raise except CalledProcessError as e: diff --git a/core/services/networking/systemwide/wireguard/nmcli_tools.py b/core/services/networking/systemwide/wireguard/nmcli_tools.py index 0ceebc6..abd1a9d 100644 --- a/core/services/networking/systemwide/wireguard/nmcli_tools.py +++ b/core/services/networking/systemwide/wireguard/nmcli_tools.py @@ -1,5 +1,7 @@ from core.utils.run_commands import run_generic_command from core.models.Result import Result, ResultError +from core.errors.logger import logger + from subprocess import CalledProcessError import os import re diff --git a/core/services/networking/tor_tools/bootstrap.py b/core/services/networking/tor_tools/bootstrap.py index 0832804..ad953f4 100644 --- a/core/services/networking/tor_tools/bootstrap.py +++ b/core/services/networking/tor_tools/bootstrap.py @@ -1,4 +1,5 @@ from core.services.networking.tor_tools.monitor_bootstrap import monitor_bootstrap_progress +from core.services.networking.tor_tools.install_tor import install_tor from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType from core.services.networking.tor_tools import ports from core.Constants import Constants @@ -89,7 +90,7 @@ def bootstrap( current_port = ports.get_random_available_port() use_new_folder = True elif strategy == BootstrapStrategy.REINSTALL_AND_RETRY: - if not install_tor().valid: + if not install_tor(observer).valid: return ApiResponse(valid=False, error_type=ErrorType.INSTALL_FAILED) use_new_folder = True diff --git a/core/services/networking/tor_tools/install_tor.py b/core/services/networking/tor_tools/install_tor.py index 46fd388..ec7021a 100644 --- a/core/services/networking/tor_tools/install_tor.py +++ b/core/services/networking/tor_tools/install_tor.py @@ -27,7 +27,7 @@ def get_distro_package_manager() -> Optional[str]: return None -async def install_tor() -> ApiResponse: +async def install_tor(observer: ConnectionObserver) -> ApiResponse: """ Prompt user to install Tor with pkexec, detecting distro for correct package manager. """ diff --git a/core/services/networking/tor_tools/ports.py b/core/services/networking/tor_tools/ports.py index e21abe2..647fa44 100644 --- a/core/services/networking/tor_tools/ports.py +++ b/core/services/networking/tor_tools/ports.py @@ -7,13 +7,13 @@ from core.Constants import Constants from typing import Optional import subprocess import asyncio -import subprocess import socket import httpx from httpx_socks import AsyncProxyTransport import json import re - +import os +import signal def is_port_in_use(port: int) -> bool: """ diff --git a/core/services/networking/tor_tools/pre_bootstrap.py b/core/services/networking/tor_tools/pre_bootstrap.py index 0f49dc7..e564ded 100644 --- a/core/services/networking/tor_tools/pre_bootstrap.py +++ b/core/services/networking/tor_tools/pre_bootstrap.py @@ -71,7 +71,7 @@ def get_bootstrap_port(port_tried: int, observer: ConnectionObserver): def diagnose_tor_port(port_tried: int, observer: ConnectionObserver) -> ApiResponse: # Step 1) Is Tor Installed? if not is_installed('tor'): - installed = install_tor() + installed = install_tor(observer) if installed: return ApiResponse(valid=True, error_type=ErrorType.TOR_NOT_INSTALLED, port=Constants.DEFAULT_TOR_PORT) else: diff --git a/core/services/networking/tor_tools/tor_orchestrator.py b/core/services/networking/tor_tools/tor_orchestrator.py index fdd759c..17fcdbc 100644 --- a/core/services/networking/tor_tools/tor_orchestrator.py +++ b/core/services/networking/tor_tools/tor_orchestrator.py @@ -35,7 +35,7 @@ def establish_tor_connection(observer: ConnectionObserver) -> ApiResponse: # Step 2) Install Tor if needed. if result.error_type == ErrorType.TOR_NOT_INSTALLED: - installed = install_tor() + installed = install_tor(observer) if not installed: return ApiResponse(valid=False, error_type=ErrorType.REFUSAL_TO_INSTALL_TOR) diff --git a/core/services/payment_phase/save_and_send_intitial_billing.py b/core/services/payment_phase/save_and_send_intitial_billing.py index 2ccde47..358802a 100644 --- a/core/services/payment_phase/save_and_send_intitial_billing.py +++ b/core/services/payment_phase/save_and_send_intitial_billing.py @@ -11,6 +11,7 @@ from core.services.networking.api_requests.step1_get_or_post import send_data_to from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType from core.services.networking.api_requests.step5_solve_api_problems import solve_api_problems +from core.services.networking.httpx import connect from core.services.networking.make_url import make_url from core.services.payment_phase.extract_payment_details import extract_payment_details @@ -42,24 +43,35 @@ def save_and_send_intitial_billing( """ # Save choices: - save_billing_choices(payload) + saved_choices = save_billing_choices(payload) + if not saved_choices: + error_msg = "Unable to save billing choices locally. Check file permissions and storage space." + logger.error(error_msg) + return TicketInvoice(is_valid=False, final_error_msg=error_msg) # send them: which_endpoint = "start_payment" url = make_url(which_endpoint) - api_reply_object = send_data_to_server(payload, url, connection_observer) + # api_reply_object = send_data_to_server(payload, url, connection_observer) + api_reply_object = connect.single_endpoint( + method="post", + url=url, + observer=connection_observer, + payload=payload + ) + + # if not api_reply_object.valid: + # api_reply_object = solve_api_problems( + # api_reply_object=api_reply_object, + # get_or_post="post", + # url=url, + # payload=payload, + # connection_observer=connection_observer, + # client_observer=None + # ) if not api_reply_object.valid: - api_reply_object = solve_api_problems( - api_reply_object=api_reply_object, - get_or_post="post", - url=url, - payload=payload, - connection_observer=connection_observer, - client_observer=None - ) - if not api_reply_object.valid: - return api_reply_object + return api_reply_object reply_dict_data = api_reply_object.data diff --git a/core/services/payment_phase/save_billing_choices.py b/core/services/payment_phase/save_billing_choices.py index afbf469..40afa8d 100644 --- a/core/services/payment_phase/save_billing_choices.py +++ b/core/services/payment_phase/save_billing_choices.py @@ -8,4 +8,4 @@ def save_billing_choices(payload: dict) -> None: billing_folder = Constants.HV_TICKETING_CONFIG_HOME filepath = f"{billing_folder}/billing_choices.json" - write_json_to_file(payload, filepath) + return write_json_to_file(payload, filepath) diff --git a/core/services/prepare_tickets/get_pub_key.py b/core/services/prepare_tickets/get_pub_key.py index 73861eb..c009ccc 100644 --- a/core/services/prepare_tickets/get_pub_key.py +++ b/core/services/prepare_tickets/get_pub_key.py @@ -7,6 +7,8 @@ from typing import Any # services from core.services.networking.make_url import make_url from core.services.networking.api_requests.step1_get_or_post import get_data_from_api +from core.services.networking.httpx import connect +from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType # utils from core.utils.basic_operations.does_file_exist import does_file_exist @@ -30,18 +32,20 @@ def get_from_server_and_save( url = make_url(string_form_of_key_name) - # the result of this is a python dictionary with single ' - public_key_results = get_data_from_server(url, connection_observer) + # public_key_results = get_data_from_server(url, connection_observer) + public_key_results = connect.single_endpoint( + method="get", + url=url, + observer=connection_observer, + payload=None + ) + + if public_key_results.valid: + # extract: + public_key = public_key_results.data - if isinstance(public_key_results, dict) and "valid" in public_key_results: - status = public_key_results.get("valid", False) - - if status == True: - # extract: - public_key = public_key_results.get("data", False) - - # save it: - did_it_save = write_string_to_text_file(public_key, file_path) + # save it: + did_it_save = write_string_to_text_file(public_key, file_path) return public_key_results diff --git a/core/services/prepare_tickets/get_public_key_by_config.py b/core/services/prepare_tickets/get_public_key_by_config.py index 401a3db..46eea03 100644 --- a/core/services/prepare_tickets/get_public_key_by_config.py +++ b/core/services/prepare_tickets/get_public_key_by_config.py @@ -71,7 +71,7 @@ def get_public_key_by_config(connection_observer: ConnectionObserver) -> dict: if reply in list_of_failures: return complete_failure_msg - if not instance(reply, dict): + if not isinstance(reply, dict): return { "status": False, "message": f"Server returned an invalid format, and even accessing via local files. Please check {filepath}", @@ -141,7 +141,7 @@ def get_public_key_from_LOCAL_files_only( if reply in list_of_failures: return complete_failure_msg - if not instance(reply, dict): + if not isinstance(reply, dict): return { "status": False, "message": f"Server returned an invalid format, and even accessing via local files. Please check {filepath}", diff --git a/core/services/prepare_tickets/setup_ticket_tracker.py b/core/services/prepare_tickets/setup_ticket_tracker.py index 6b9efdd..88a86ce 100644 --- a/core/services/prepare_tickets/setup_ticket_tracker.py +++ b/core/services/prepare_tickets/setup_ticket_tracker.py @@ -21,4 +21,4 @@ def setup_ticket_tracker(how_many_profiles: int) -> None: } counter += 1 - write_json_to_file(ticket_data, ticket_tracker_path) + return write_json_to_file(ticket_data, ticket_tracker_path) diff --git a/core/services/prepare_tickets/ticket_prep_orchestrator.py b/core/services/prepare_tickets/ticket_prep_orchestrator.py index 8e4b59e..7461ec6 100644 --- a/core/services/prepare_tickets/ticket_prep_orchestrator.py +++ b/core/services/prepare_tickets/ticket_prep_orchestrator.py @@ -94,9 +94,9 @@ def ticket_prep_orchestrator( ) # make a json to keep track of which tickets are used: - setup_ticket_tracker(how_many_profiles) + setup_tracker = setup_ticket_tracker(how_many_profiles) - if did_prep_work: + if did_prep_work and setup_tracker: # this means it unblinded, & setup the tracker without erroring out, # but it does NOT mean that verification of the blind sigs worked. return {"valid": True, "message": "worked"} diff --git a/core/services/prepare_tickets/ticket_tracker.py b/core/services/prepare_tickets/ticket_tracker.py index 411100b..b3519fb 100644 --- a/core/services/prepare_tickets/ticket_tracker.py +++ b/core/services/prepare_tickets/ticket_tracker.py @@ -1,7 +1,4 @@ -from core.utils.basic_operations.write_or_read_from_json import ( - write_json_to_file, - read_entire_json, -) +from core.utils.basic_operations.write_or_read_from_json import read_entire_json from core.errors.exceptions import * from core.errors.logger import logger from core.Constants import Constants @@ -62,7 +59,7 @@ def get_data_for_a_single_ticket(which_ticket_as_int: int) -> tuple: error_msg = f"Entire file of tickets does not exist. Check the location {ticket_tracker_path}" logger.error(error_msg, exc_info=True) print(error_msg) - raise InvalidData(error_msg) + raise ValueError(error_msg) # the data is a string for lookups: which_ticket = str(which_ticket_as_int) @@ -74,6 +71,6 @@ def get_data_for_a_single_ticket(which_ticket_as_int: int) -> tuple: subscription = data.get(which_ticket, {}).get("subscription") return status, location, subscription else: - raise InvalidData( + raise ValueError( f"Key '{which_ticket}' does not exist in JSON file {ticket_tracker_path}" ) diff --git a/core/utils/basic_operations/write_or_read_from_json.py b/core/utils/basic_operations/write_or_read_from_json.py index aec49dc..c3ed6a3 100644 --- a/core/utils/basic_operations/write_or_read_from_json.py +++ b/core/utils/basic_operations/write_or_read_from_json.py @@ -1,4 +1,3 @@ -from core.errors.exceptions import * from core.errors.logger import logger import json @@ -6,7 +5,7 @@ import os from pathlib import Path -def write_json_to_file(data: dict, filepath: str) -> None: +def write_json_to_file(data: dict, filepath: str) -> bool: try: # Create directory if it doesn't exist directory = os.path.dirname(filepath) @@ -16,10 +15,20 @@ def write_json_to_file(data: dict, filepath: str) -> None: with open(filepath, "w") as f: json.dump(data, f, indent=4) + return True + except TypeError as e: - raise TypeError(f"Data is not JSON serializable: {e}") + logger.error(f"Data is not JSON serializable: {e}") + return False + except FileNotFoundError: + logger.error(f"File not found: {filepath}") + return False + except IsADirectoryError: + logger.error(f"Path is a directory, not a file: {filepath}") + return False except IOError as e: - raise IOError(f"Error writing to file {filepath}: {e}") + logger.error(f"Error reading file {filepath}: {e}") + return False def read_entire_json(filepath: str) -> dict: @@ -43,13 +52,13 @@ def read_entire_json(filepath: str) -> dict: f"Invalid JSON in file {filepath}: {e.msg}", e.doc, e.pos ) except FileNotFoundError: - raise FileNotFoundError(f"File not found: {filepath}") + logger.error(f"File not found: {filepath}") return False except IsADirectoryError: - raise IsADirectoryError(f"Path is a directory, not a file: {filepath}") + logger.error(f"Path is a directory, not a file: {filepath}") return False except IOError as e: - raise IOError(f"Error reading file {filepath}: {e}") + logger.error(f"Error reading file {filepath}: {e}") return False @@ -103,8 +112,14 @@ def update_value_in_json_with_two_values( return True - except: - raise InvalidData(f"Error reading file {filepath}") + except FileNotFoundError: + logger.error(f"File not found: {filepath}") + return False + except IsADirectoryError: + logger.error(f"Path is a directory, not a file: {filepath}") + return False + except IOError as e: + logger.error(f"Error reading file {filepath}: {e}") return False @@ -122,6 +137,12 @@ def update_json(filepath, key_to_add, value_to_update): write_json_to_file(data, filepath) return True - except: - raise InvalidData(f"Error reading file {filepath}") + except FileNotFoundError: + logger.error(f"File not found: {filepath}") + return False + except IsADirectoryError: + logger.error(f"Path is a directory, not a file: {filepath}") + return False + except IOError as e: + logger.error(f"Error reading file {filepath}: {e}") return False diff --git a/core/utils/run_commands.py b/core/utils/run_commands.py index 6d2a473..78eec59 100644 --- a/core/utils/run_commands.py +++ b/core/utils/run_commands.py @@ -131,14 +131,14 @@ def run_generic_command( try: returncode, stdout = _run_command_via_terminal(command, timeout) - output_data = stdout + # output_data = stdout if returncode == 0: logger.info(f"{human_readable_goal} was successful") - return Result(valid=True, data=output_data) + return Result(valid=True, data=stdout) else: # Try stderr first, fallback to stdout for error parsing - error_output = (stderr or stdout).strip() + error_output = stdout.strip() error_enum = parse_errors(error_output) logger.error(f"{human_readable_goal} Failed, error type is {error_enum}.") return Result(valid=False, error_type=error_enum, goal=human_readable_goal, message=error_output) @@ -147,3 +147,19 @@ def run_generic_command( return Result(valid=False, error_type=ResultError.TIMEOUT, goal=human_readable_goal, data=output_data, message=f"Command timed out {e}") except Exception as e: return Result(valid=False, error_type=ResultError.UNKNOWN, goal=human_readable_goal, data=output_data, message=str(e)) + + +def parse_errors(error_output: str) -> ResultError: + # Interface missing + if "No such device" in error_output: + error_enum = ResultError.INTERFACE + # Permission error + elif "No permissions" in error_output: + error_enum = ResultError.PERMISSION + elif "sudo: a password is required" or "password is required" in error_output: + error_enum = ResultError.PERMISSION + elif "command not found" in error_output: + error_enum = ResultError.MISSING_DEPENDENCY + else: + error_enum = ResultError.UNKNOWN + return error_enum