A Python port of the Invisible Internet Project (I2P)
1"""Addressbook manager for SusiDNS.
2
3Manages hostname-to-destination mappings with search, file I/O,
4and integration with the addressbook storage.
5
6Ported from i2p.susi.dns.AddressbookBean.
7"""
8
9from __future__ import annotations
10
11from dataclasses import dataclass
12from pathlib import Path
13
14
15@dataclass
16class AddressbookEntry:
17 """A single hostname→destination mapping."""
18 hostname: str
19 destination: str
20
21 def __str__(self) -> str:
22 return f"{self.hostname}={self.destination}"
23
24
25class AddressbookManager:
26 """Manages .i2p hostname-to-destination mappings."""
27
28 def __init__(self) -> None:
29 self._entries: dict[str, str] = {}
30
31 def add(self, hostname: str, destination: str) -> None:
32 """Add or update an entry."""
33 self._entries[hostname] = destination
34
35 def remove(self, hostname: str) -> None:
36 """Remove an entry."""
37 self._entries.pop(hostname, None)
38
39 def lookup(self, hostname: str) -> str | None:
40 """Look up a destination by hostname."""
41 return self._entries.get(hostname)
42
43 def list_entries(self) -> list[AddressbookEntry]:
44 """List all entries sorted by hostname."""
45 return [
46 AddressbookEntry(h, d)
47 for h, d in sorted(self._entries.items())
48 ]
49
50 def search(self, query: str) -> list[AddressbookEntry]:
51 """Search entries by hostname prefix."""
52 query_lower = query.lower()
53 return [
54 AddressbookEntry(h, d)
55 for h, d in sorted(self._entries.items())
56 if query_lower in h.lower()
57 ]
58
59 def save(self, path: Path) -> None:
60 """Save entries to a hosts.txt file."""
61 lines = [f"{h}={d}" for h, d in sorted(self._entries.items())]
62 path.write_text("\n".join(lines) + "\n")
63
64 @classmethod
65 def from_file(cls, path: Path) -> AddressbookManager:
66 """Load entries from a hosts.txt file."""
67 mgr = cls()
68 if not path.exists():
69 return mgr
70 for line in path.read_text().splitlines():
71 line = line.strip()
72 if not line or line.startswith("#"):
73 continue
74 if "=" in line:
75 hostname, destination = line.split("=", 1)
76 mgr.add(hostname.strip(), destination.strip())
77 return mgr