RemoteRF 1.1.4__tar.gz → 1.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {remoterf-1.1.4/src/remoteRF.egg-info → remoterf-1.1.5}/PKG-INFO +1 -1
  2. {remoterf-1.1.4 → remoterf-1.1.5}/setup.py +1 -1
  3. remoterf-1.1.5/src/remoteRF/config/cert_fetcher.py +170 -0
  4. {remoterf-1.1.4 → remoterf-1.1.5/src/remoteRF.egg-info}/PKG-INFO +1 -1
  5. remoterf-1.1.4/src/remoteRF/config/cert_fetcher.py +0 -118
  6. {remoterf-1.1.4 → remoterf-1.1.5}/LICENSE +0 -0
  7. {remoterf-1.1.4 → remoterf-1.1.5}/MANIFEST.in +0 -0
  8. {remoterf-1.1.4 → remoterf-1.1.5}/README.md +0 -0
  9. {remoterf-1.1.4 → remoterf-1.1.5}/setup.cfg +0 -0
  10. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/__init__.py +0 -0
  11. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/common/__init__.py +0 -0
  12. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/common/grpc/__init__.py +0 -0
  13. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/common/grpc/grpc_pb2.py +0 -0
  14. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/common/grpc/grpc_pb2_grpc.py +0 -0
  15. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/common/utils/__init__.py +0 -0
  16. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/common/utils/ansi_codes.py +0 -0
  17. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/common/utils/api_token.py +0 -0
  18. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/common/utils/list_string.py +0 -0
  19. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/common/utils/process_arg.py +0 -0
  20. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/config/__init__.py +0 -0
  21. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/config/config.py +0 -0
  22. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/core/__init__.py +0 -0
  23. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/core/acc_login.py +0 -0
  24. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/core/app.py +0 -0
  25. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/core/grpc_acc.py +0 -0
  26. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/core/grpc_client.py +0 -0
  27. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/drivers/__init__.py +0 -0
  28. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/drivers/adalm_pluto/__init__.py +0 -0
  29. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/drivers/adalm_pluto/pluto_remote.py +0 -0
  30. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/remoterf_cli.py +0 -0
  31. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF/version.py +0 -0
  32. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF.egg-info/SOURCES.txt +0 -0
  33. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF.egg-info/dependency_links.txt +0 -0
  34. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF.egg-info/entry_points.txt +0 -0
  35. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF.egg-info/requires.txt +0 -0
  36. {remoterf-1.1.4 → remoterf-1.1.5}/src/remoteRF.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoterf
3
- Version: 1.1.4
3
+ Version: 1.1.5
4
4
  Summary: A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.
5
5
  Author: Ethan Ge
6
6
  Author-email: ethoGalaxy@gmail.com
@@ -6,7 +6,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
6
6
 
7
7
  setup(
8
8
  name="remoterf",
9
- version="1.1.4",
9
+ version="1.1.5",
10
10
  author="Ethan Ge",
11
11
  author_email="ethoGalaxy@gmail.com",
12
12
  description="A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.",
@@ -0,0 +1,170 @@
1
+ # cert_fetcher.py
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import os
7
+ import socket
8
+ import urllib.request
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+ CERT_FILENAME_DEFAULT = "ca.crt"
13
+
14
+ def _default_config_dir() -> Path:
15
+ return Path(os.path.expanduser("~")) / ".config" / "remoterf"
16
+
17
+ def _default_env_path() -> Path:
18
+ return _default_config_dir() / ".env"
19
+
20
+ def _ensure_parent_dir(p: Path) -> None:
21
+ p.parent.mkdir(parents=True, exist_ok=True)
22
+
23
+ def _looks_like_pem_cert(data: bytes) -> bool:
24
+ return b"BEGIN CERTIFICATE" in data and b"END CERTIFICATE" in data
25
+
26
+ def sha256_fingerprint_pem(pem_bytes: bytes) -> str:
27
+ h = hashlib.sha256(pem_bytes).hexdigest()
28
+ return ":".join(h[i:i+2] for i in range(0, len(h), 2))
29
+
30
+ def _fetch_http(host: str, port: int, timeout_sec: float) -> bytes:
31
+ url = f"http://{host}:{port}/ca.crt"
32
+ req = urllib.request.Request(url, method="GET")
33
+ with urllib.request.urlopen(req, timeout=timeout_sec) as resp:
34
+ return resp.read()
35
+
36
+ def _fetch_raw_tcp(host: str, port: int, timeout_sec: float) -> bytes:
37
+ chunks: list[bytes] = []
38
+ with socket.create_connection((host, port), timeout=timeout_sec) as s:
39
+ s.settimeout(timeout_sec)
40
+ while True:
41
+ try:
42
+ b = s.recv(4096)
43
+ except socket.timeout:
44
+ break
45
+ if not b:
46
+ break
47
+ chunks.append(b)
48
+ return b"".join(chunks)
49
+
50
+ def _dotenv_escape_value_no_quotes(value: str) -> str:
51
+ """
52
+ Returns a .env-safe value WITHOUT surrounding quotes.
53
+ We use forward slashes to avoid backslash-escape weirdness.
54
+ """
55
+ # Trim outer whitespace; keep internal spaces.
56
+ v = value.strip()
57
+
58
+ # Convert Windows backslashes to forward slashes.
59
+ v = v.replace("\\", "/")
60
+
61
+ # Remove any accidental surrounding quotes.
62
+ if (len(v) >= 2) and ((v[0] == v[-1]) and v[0] in ("'", '"')):
63
+ v = v[1:-1]
64
+
65
+ # Disallow newlines (dotenv lines are single-line)
66
+ v = v.replace("\r", "").replace("\n", "")
67
+
68
+ return v
69
+
70
+ def _dotenv_set_key(env_path: Path, key: str, value: str) -> None:
71
+ """
72
+ Create/update KEY=value in env_path. Writes value WITHOUT quotes.
73
+ Preserves unrelated lines. Comments are kept.
74
+ """
75
+ _ensure_parent_dir(env_path)
76
+
77
+ new_line = f"{key}={_dotenv_escape_value_no_quotes(value)}\n"
78
+
79
+ if not env_path.exists():
80
+ env_path.write_text(new_line, encoding="utf-8")
81
+ return
82
+
83
+ lines = env_path.read_text(encoding="utf-8", errors="ignore").splitlines(keepends=True)
84
+
85
+ # Replace existing KEY=... (ignores leading whitespace); doesn't touch commented-out keys.
86
+ replaced = False
87
+ out: list[str] = []
88
+ for line in lines:
89
+ stripped = line.lstrip()
90
+ if stripped.startswith("#"):
91
+ out.append(line)
92
+ continue
93
+
94
+ # Match "KEY=" at start (after lstrip). Keep any leading whitespace.
95
+ if stripped.startswith(key + "="):
96
+ prefix_ws = line[: len(line) - len(stripped)]
97
+ out.append(prefix_ws + new_line)
98
+ replaced = True
99
+ else:
100
+ out.append(line)
101
+
102
+ if not replaced:
103
+ # Append with a newline boundary
104
+ if out and not out[-1].endswith("\n"):
105
+ out[-1] += "\n"
106
+ out.append(new_line)
107
+
108
+ env_path.write_text("".join(out), encoding="utf-8")
109
+
110
+ def fetch_and_save_ca_cert(
111
+ host: str,
112
+ port: int,
113
+ *,
114
+ out_path: Optional[str | Path] = None,
115
+ profile: Optional[str] = None,
116
+ timeout_sec: float = 3.0,
117
+ overwrite: bool = True,
118
+ write_env: bool = True,
119
+ env_path: Optional[str | Path] = None,
120
+ ) -> bool:
121
+ """
122
+ Fetch CA cert and save. Optionally writes REMOTERF_CA_CERT into ~/.config/remoterf/.env
123
+ WITHOUT quotes.
124
+
125
+ Args:
126
+ write_env: if True, updates REMOTERF_CA_CERT in env file to point at saved cert
127
+ env_path: override env file location; default ~/.config/remoterf/.env
128
+ """
129
+ try:
130
+ if not isinstance(port, int):
131
+ port = int(port)
132
+
133
+ # Determine destination path
134
+ if out_path is not None:
135
+ dest = Path(out_path).expanduser().resolve()
136
+ else:
137
+ cfg = _default_config_dir()
138
+ certs_dir = cfg / "certs"
139
+ name = f"{profile}.crt" if profile else CERT_FILENAME_DEFAULT
140
+ dest = certs_dir / name
141
+
142
+ _ensure_parent_dir(dest)
143
+
144
+ if dest.exists() and not overwrite:
145
+ # Still ensure env points to it (helpful for idempotency)
146
+ if write_env:
147
+ ep = Path(env_path).expanduser().resolve() if env_path else _default_env_path()
148
+ _dotenv_set_key(ep, "REMOTERF_CA_CERT", dest.as_posix())
149
+ return True
150
+
151
+ # Fetch (HTTP first, then raw TCP fallback)
152
+ try:
153
+ data = _fetch_http(host, port, timeout_sec)
154
+ except Exception:
155
+ data = _fetch_raw_tcp(host, port, timeout_sec)
156
+
157
+ if not data or not _looks_like_pem_cert(data):
158
+ return False
159
+
160
+ dest.write_bytes(data)
161
+
162
+ # Update .env without quotes
163
+ if write_env:
164
+ ep = Path(env_path).expanduser().resolve() if env_path else _default_env_path()
165
+ _dotenv_set_key(ep, "REMOTERF_CA_CERT", dest.as_posix())
166
+
167
+ return True
168
+
169
+ except Exception:
170
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoterf
3
- Version: 1.1.4
3
+ Version: 1.1.5
4
4
  Summary: A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.
5
5
  Author: Ethan Ge
6
6
  Author-email: ethoGalaxy@gmail.com
@@ -1,118 +0,0 @@
1
- # cert_fetcher.py
2
-
3
- from __future__ import annotations
4
-
5
- import hashlib
6
- import os
7
- import socket
8
- import urllib.request
9
- from pathlib import Path
10
- from typing import Optional
11
-
12
-
13
- CERT_FILENAME_DEFAULT = "ca.crt"
14
-
15
-
16
- def _default_config_dir() -> Path:
17
- # Cross-platform-ish default; adjust if you already have a standard in your project.
18
- # Linux: ~/.config/remoterf
19
- # macOS: also acceptable; if you want Apple-standard, use ~/Library/Application Support/remoterf
20
- base = Path(os.path.expanduser("~")) / ".config" / "remoterf"
21
- return base
22
-
23
-
24
- def _ensure_parent_dir(p: Path) -> None:
25
- p.parent.mkdir(parents=True, exist_ok=True)
26
-
27
-
28
- def _looks_like_pem_cert(data: bytes) -> bool:
29
- return b"BEGIN CERTIFICATE" in data and b"END CERTIFICATE" in data
30
-
31
-
32
- def sha256_fingerprint_pem(pem_bytes: bytes) -> str:
33
- h = hashlib.sha256(pem_bytes).hexdigest()
34
- return ":".join(h[i:i+2] for i in range(0, len(h), 2))
35
-
36
-
37
- def _fetch_http(host: str, port: int, timeout_sec: float) -> bytes:
38
- url = f"http://{host}:{port}/ca.crt"
39
- req = urllib.request.Request(url, method="GET")
40
- with urllib.request.urlopen(req, timeout=timeout_sec) as resp:
41
- data = resp.read()
42
- return data
43
-
44
-
45
- def _fetch_raw_tcp(host: str, port: int, timeout_sec: float) -> bytes:
46
- chunks: list[bytes] = []
47
- with socket.create_connection((host, port), timeout=timeout_sec) as s:
48
- s.settimeout(timeout_sec)
49
- while True:
50
- try:
51
- b = s.recv(4096)
52
- except socket.timeout:
53
- break
54
- if not b:
55
- break
56
- chunks.append(b)
57
- return b"".join(chunks)
58
-
59
-
60
- def fetch_and_save_ca_cert(
61
- host: str,
62
- port: int,
63
- *,
64
- out_path: Optional[str | Path] = None,
65
- profile: Optional[str] = None,
66
- timeout_sec: float = 3.0,
67
- overwrite: bool = True,
68
- ) -> bool:
69
- """
70
- Fetch CA cert from the server bootstrap endpoint and save it to disk.
71
-
72
- Args:
73
- host: server host/ip running cert_provider
74
- port: cert_provider port (NOT the TLS gRPC port)
75
- out_path: explicit output path (overrides profile/default location)
76
- profile: if provided, saves as ~/.config/remoterf/certs/<profile>.crt
77
- timeout_sec: network timeout
78
- overwrite: whether to overwrite existing file
79
-
80
- Returns:
81
- True on success, False on any failure.
82
- """
83
- try:
84
- if not isinstance(port, int):
85
- port = int(port)
86
-
87
- # Determine destination path
88
- if out_path is not None:
89
- dest = Path(out_path).expanduser().resolve()
90
- else:
91
- cfg = _default_config_dir()
92
- certs_dir = cfg / "certs"
93
- name = f"{profile}.crt" if profile else CERT_FILENAME_DEFAULT
94
- dest = certs_dir / name
95
-
96
- _ensure_parent_dir(dest)
97
-
98
- if dest.exists() and not overwrite:
99
- return True # already present, treat as success
100
-
101
- # Fetch (HTTP first, then raw TCP fallback)
102
- data = b""
103
- try:
104
- data = _fetch_http(host, port, timeout_sec)
105
- except Exception:
106
- data = _fetch_raw_tcp(host, port, timeout_sec)
107
-
108
- if not data or not _looks_like_pem_cert(data):
109
- return False
110
-
111
- # Save
112
- dest.write_bytes(data)
113
-
114
- # Optional: you may want to return/print fingerprint, but requested API is bool.
115
- return True
116
-
117
- except Exception:
118
- return False
File without changes
File without changes
File without changes
File without changes