add logic for dynamic IP lookup for deltachat links and install #31

This commit is contained in:
n8fr8 2026-07-01 13:31:24 -04:00
parent d4b4b5a9e0
commit 8bb7792ee3
2 changed files with 64 additions and 2 deletions

View file

@ -1,5 +1,6 @@
import io
import re
import socket
import subprocess
from app import app
@ -26,6 +27,39 @@ from install_madmail import run_madmail_installer
CHANGES_REQUIRING_RESTART = ['wifi_password', 'ssid', 'enable_access_point', 'enable_chat', 'enable_delta_chat', 'butterbox_hostname', 'ssh_access_settings', 'root_account_settings', 'root_password']
RASPAP_INSTALLED = os.path.exists("/var/www/html/raspap")
def resolve_butterbox_ip() -> str:
"""Best-effort lookup of the address clients should use to reach this box.
``BUTTERBOX_DEFAULT_IP`` is only correct while the box serves its own
access point. When it's joined to another network its address differs, so
try to discover the real one and only fall back to the configured default.
"""
hostname = get_setting('butterbox_hostname') or app.config['BUTTERBOX_HOSTNAME']
# 1. Resolve the box's own mDNS/.local name. This is the same name the
# DeltaChat link uses for the mail host, so avahi/nss-mdns is present.
try:
ip = socket.gethostbyname(f"{hostname}.local")
if ip and not ip.startswith("127."):
return ip
except OSError:
pass
# 2. Fall back to the address of the primary outbound interface. The
# connect() picks a route without sending any packets.
try:
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
if ip and not ip.startswith("127."):
return ip
except OSError:
pass
# 3. Give up and use the configured access-point default.
return app.config['BUTTERBOX_DEFAULT_IP']
def gen_username() -> str:
words = top_n_list("en", 5000)
prefix = random.randint(1000, 9999)
@ -370,7 +404,7 @@ def messaging():
@app.route("/deltachat_credentials", methods=["POST"])
def deltachat_credentials():
ip = app.config['BUTTERBOX_DEFAULT_IP']
ip = resolve_butterbox_ip()
username = gen_username()
password = gen_password()
hostname = f"{get_setting('butterbox_hostname')}.local"

View file

@ -1,8 +1,36 @@
import pexpect
import json
import socket
from app import app
from subprocess import run
def resolve_butterbox_ip(hostname):
"""Best-effort lookup of the box's current address, falling back to the
configured default. Mirrors app.routes.resolve_butterbox_ip but avoids a
circular import (routes imports this module) and reuses the hostname that
was already loaded from settings.txt."""
# 1. Resolve the box's own mDNS/.local name.
try:
ip = socket.gethostbyname(f"{hostname}.local")
if ip and not ip.startswith("127."):
return ip
except OSError:
pass
# 2. Fall back to the address of the primary outbound interface. The
# connect() picks a route without sending any packets.
try:
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
if ip and not ip.startswith("127."):
return ip
except OSError:
pass
# 3. Give up and use the configured access-point default.
return app.config['BUTTERBOX_DEFAULT_IP']
def run_madmail_installer():
with open("./settings.txt", "r") as f:
settings = json.load(f)
@ -18,7 +46,7 @@ def run_madmail_installer():
child.expect("MX record")
child.sendline(f"{butterbox_hostname}.local")
child.expect("Public IP address")
child.sendline(app.config['BUTTERBOX_DEFAULT_IP'])
child.sendline(resolve_butterbox_ip(butterbox_hostname))
child.expect("Additional domains")
child.sendline("")
child.expect("State directory")