I am trying to automate the login and PPP credential update process
for a ZTE ZXHN H298A modem (firmware TTN.26_240416) using Selenium and
Python.
My goal is to perform these steps programmatically and reliably with Selenium, without manual interaction.
Even though I provide the correct credentials, Selenium fails to
complete the login step. The modem’s admin panel remains at the login
page.
Additional requirement:
When running in headful mode, I pause after Selenium fills in the
username and password so I can see the login screen with the credentials
filled in inside the browser. In addition to this, I also want to be
able to see the interface after a successful login — including the admin
menu and subsequent navigation steps — inside the browser while the
script continues executing.
#!/usr/bin/env python3
"""
ZTE ZXHN H298A – PPP dump/update using Selenium (UI flow mirroring the C# service)
Usage examples:
Dump PPP only:
py "Modem Refurbishment App/scripts/zte_ppp_selenium.py" --ip 192.168.5.1 --user admin --pass admin
Update PPP and verify:
py "Modem Refurbishment App/scripts/zte_ppp_selenium.py" --ip 192.168.5.1 --user admin --pass admin --ppp-user NEW --ppp-pass NEWPASS
Notes:
- Uses Chrome; requires a compatible chromedriver on PATH
- Follows the modem UI: navigate → login → WAN → expand INTERNET → read/update PPP → apply
"""
from __future__ import annotations
import argparse
import sys
import time
from datetime import datetime
from pathlib import Path
from typing import Optional, Tuple
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException, WebDriverException, NoSuchWindowException
def save_debug(prefix: str, html: str) -> None:
try:
p = Path(f"debug_{prefix}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html")
p.write_text(html or "", encoding="utf-8", errors="ignore")
print(f"Saved {p}")
except Exception:
pass
def wait_find(driver, by, selector, timeout=8):
return WebDriverWait(driver, timeout).until(EC.presence_of_element_located((by, selector)))
def any_present(driver, selectors, timeout=6) -> Optional[Tuple[By, str]]:
end = time.time() + timeout
while time.time() < end:
for by, sel in selectors:
try:
els = driver.find_elements(by, sel)
if els:
return by, sel
except Exception:
continue
time.sleep(0.2)
return None
def switch_to_admin_frame(driver) -> bool:
"""Ensure Selenium context is inside the admin UI frame that hosts the ZTE shell.
Strategy:
- Always reset to top-level document first
- Look for admin markers in the top document
- If not found, scan all top-level iframes; if still not found, scan one nested level
"""
try:
driver.switch_to.default_content()
except Exception:
pass
def has_admin_markers() -> bool:
try:
return bool(driver.find_elements(By.CSS_SELECTOR, "#page_container, #class2MenuItem, #mainNavigator"))
except Exception:
return False
# Top-level check
if has_admin_markers():
return True
# BFS over frames up to depth 2
try:
top_frames = driver.find_elements(By.TAG_NAME, 'iframe')
except Exception:
top_frames = []
for f in top_frames:
try:
driver.switch_to.frame(f)
if has_admin_markers():
return True
# search one nested level
try:
inner_frames = driver.find_elements(By.TAG_NAME, 'iframe')
except Exception:
inner_frames = []
for inner in inner_frames:
try:
driver.switch_to.frame(inner)
if has_admin_markers():
return True
finally:
try:
driver.switch_to.parent_frame()
except Exception:
pass
finally:
try:
driver.switch_to.default_content()
except Exception:
pass
return False
def is_internet_ui_present(driver) -> bool:
"""Detect markers of the INTERNET WAN template being present in the DOM."""
try:
if driver.find_elements(By.CSS_SELECTOR, "div[id^='template_Internet'], [id^='instName_Internet'], div[id^='changeArea_Internet']"):
return True
# Fallback: quick page_source scan to catch server-side rendered variants
html = driver.page_source or ""
return ('instName_Internet' in html) or ('template_Internet' in html) or ('Internet_Eth_t.lp' in html)
except Exception:
return False
def click_login_button(driver) -> bool:
"""Click the login button, preferring labels like 'Oturum Aç' and common variants.
Returns True if a click was attempted, else False.
"""
candidates: list[Tuple[By, str]] = [
(By.CSS_SELECTOR, "#LoginId"),
# Turkish labels
(By.XPATH, "//input[(translate(@type,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='submit' or translate(@type,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='button') and (contains(@value,'Oturum Aç') or contains(@value,'Oturum') or contains(@value,'Giriş'))]"),
(By.XPATH, "//button[contains(.,'Oturum Aç') or contains(.,'Oturum') or contains(.,'Giriş')]") ,
# English fallbacks
(By.XPATH, "//input[(translate(@type,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='submit' or translate(@type,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='button') and (contains(@value,'Login') or contains(@value,'Sign In') or contains(@value,'Log In'))]"),
(By.XPATH, "//button[contains(.,'Login') or contains(.,'Sign In') or contains(.,'Log In')]") ,
# Generic
(By.CSS_SELECTOR, "input[type='submit'], input[type='button'], button[type='submit']"),
]
for by, sel in candidates:
try:
btns = driver.find_elements(by, sel)
if not btns:
continue
btn = btns[0]
try:
driver.execute_script("arguments[0].scrollIntoView({block:'center'});", btn)
except Exception:
pass
try:
btn.click()
except Exception:
try:
driver.execute_script("arguments[0].click();", btn)
except Exception:
continue
# brief settle
time.sleep(0.2)
return True
except Exception:
continue
return False
def _select_main_content_window(driver, main_handle: Optional[str], base_url: Optional[str]) -> None:
"""Switch to the main content window (not DevTools), preferring main_handle, else matching base_url.
DevTools windows use the devtools:// scheme. We avoid those.
"""
try:
handles = driver.window_handles
except Exception:
return
# Prefer known main handle if still present
if main_handle and main_handle in handles:
try:
driver.switch_to.window(main_handle)
return
except Exception:
pass
# Else, pick a handle whose URL matches base_url and is not devtools
for h in handles:
try:
driver.switch_to.window(h)
url = ""
try:
url = driver.current_url or ""
except Exception:
url = ""
if url.startswith("devtools://"):
continue
if base_url and url.startswith(base_url):
return
except Exception:
continue
# Fallback: first non-devtools window
for h in handles:
try:
driver.switch_to.window(h)
url = driver.current_url or ""
if not url.startswith("devtools://"):
return
except Exception:
continue
def assert_window_alive(driver, context: str = "", main_handle: Optional[str] = None, base_url: Optional[str] = None) -> None:
"""Ensure the browser window/session is still alive; exit cleanly if closed/crashed."""
try:
handles = driver.window_handles
if not handles:
save_debug("window_closed_" + (context or "unknown"), "")
sys.exit("Browser window is closed; aborting")
_select_main_content_window(driver, main_handle, base_url)
except (NoSuchWindowException, WebDriverException):
save_debug("window_closed_" + (context or "unknown"), "")
sys.exit("Browser window is closed; aborting")
def enforce_wan_page(driver, base_url: str, wait_seconds: float = 6.0) -> bool:
"""Force-load the WAN INTERNET page using modem JS handlers, then fallback to direct URL.
Returns True if INTERNET UI markers appear, else False.
"""
# Ensure we are in the admin frame first
switch_to_admin_frame(driver)
# Ensure cookie support in this context
try:
driver.execute_script("document.cookie='_TESTCOOKIESUPPORT=1; path=/'")
except Exception:
pass
# Read token from current context
try:
tok = driver.execute_script("return window._sessionTmpToken || window._sessionTOKEN || window.sessionToken || window.g_loginToken || '';") or ''
except Exception:
tok = ''
# Attempt via firmware AJAX handler(s) with token if available (avoid template_csrf to prevent 404s)
ajax_scripts = [
"""
try{
var tok = arguments[0] || (window._sessionTmpToken || window._sessionTOKEN || window.sessionToken || '');
try{ if (tok){ window._sessionTmpToken = tok; } }catch(e){}
if (typeof AjaxPageGet==='function'){
var url = 'getpage.lua?pid=123&nextpage=Internet_Eth_t.lp&Menu3Location=0' + (tok?('&_sessionTOKEN='+tok):'') + '&_=' + Date.now();
AjaxPageGet(url, '2');
}
}catch(e){}
""",
"""
try{
var tok = arguments[0] || (window._sessionTmpToken || window._sessionTOKEN || window.sessionToken || '');
if (typeof AjaxPageGet==='function'){
var url = 'getpage.lua?pid=1002&nextpage=Internet_Eth_t.lp' + (tok?('&_sessionTOKEN='+tok):'') + '&_=' + Date.now();
AjaxPageGet(url, '2');
}
}catch(e){}
""",
]
for js in ajax_scripts:
try:
driver.execute_script(js, tok)
except Exception:
pass
# brief wait to allow AJAX render
end = time.time() + 2.0
while time.time() < end:
if is_internet_ui_present(driver):
return True
time.sleep(0.2)
switch_to_admin_frame(driver)
# Fallback: direct navigation within current frame
try:
driver.execute_script("var tok=arguments[1]||''; window.location.href = arguments[0] + '/getpage.lua?pid=123&nextpage=Internet_Eth_t.lp&Menu3Location=0' + (tok?('&_sessionTOKEN='+tok):'') + '&_=' + Date.now();", base_url, tok)
except Exception:
try:
url = base_url + "/getpage.lua?pid=123&nextpage=Internet_Eth_t.lp&Menu3Location=0"
if tok:
url += "&_sessionTOKEN=" + tok
url += "&_=" + str(int(time.time()*1000))
driver.get(url)
except Exception:
pass
# Wait for presence
deadline = time.time() + wait_seconds
while time.time() < deadline:
switch_to_admin_frame(driver)
if is_internet_ui_present(driver):
return True
time.sleep(0.3)
return False
def main() -> None:
ap = argparse.ArgumentParser(description="Dump/Update PPP via Selenium (ZTE H298A)")
ap.add_argument("--ip", required=True)
ap.add_argument("--user", required=True)
ap.add_argument("--pass", dest="password", required=True)
ap.add_argument("--ppp-user", dest="ppp_user")
ap.add_argument("--ppp-pass", dest="ppp_pass")
ap.add_argument("--headful", action="store_true")
ap.add_argument("--devtools", action="store_true", help="Open Chrome DevTools for tabs when headful")
ap.add_argument("--debug-delays", action="store_true", help="Insert 6-7 second pauses at key steps for manual inspection")
ap.add_argument("--debug-delay-sec", type=int, default=7, help="Pause duration in seconds when --debug-delays is set")
ap.add_argument("--prelogin-delay-sec", type=int, default=7, help="Pause before entering credentials (seconds). Set 0 to skip")
ap.add_argument("--no-auto-login", action="store_true", help="Do not auto-fill/submit login; hold on login page for manual testing")
ap.add_argument("--show-typing", action="store_true", help="Use Selenium typing to show credentials being entered")
ap.add_argument("--type-slow-ms", type=int, default=0, help="Delay in ms between keystrokes when --show-typing is used")
ap.add_argument("--maximize", action="store_true", help="Maximize window on start (headful only)")
ap.add_argument("--detach", action="store_true", help="Keep browser window open after script exits (headful only)")
ap.add_argument("--hold-open-end", action="store_true", help="Keep browser open at end until manually closed")
args = ap.parse_args()
base = f"http://{args.ip}"
# Chrome options
opts = webdriver.ChromeOptions()
if not args.headful:
opts.add_argument("--headless=new")
opts.add_argument("--disable-gpu")
opts.add_argument("--no-sandbox")
opts.add_argument("--window-size=1400,900")
if args.headful and args.devtools:
opts.add_argument("--auto-open-devtools-for-tabs")
if args.headful and args.detach:
try:
opts.add_experimental_option("detach", True)
except Exception:
pass
try:
driver = webdriver.Chrome(options=opts)
except WebDriverException as e:
print(f"Failed to start ChromeDriver: {e}")
sys.exit(1)
driver.set_page_load_timeout(15)
if args.headful and args.maximize:
try:
driver.maximize_window()
except Exception:
pass
try:
# helper for conditional debug sleep
def dbg_sleep():
if args.debug_delays:
try:
time.sleep(max(1, int(args.debug_delay_sec)))
except Exception:
time.sleep(7)
# 1) Navigate to modem and set cookie support
driver.get(base + "/")
main_handle = None
try:
main_handle = driver.current_window_handle
except Exception:
main_handle = None
assert_window_alive(driver, "after_get", main_handle, base)
try:
print(f"[Info] Landed URL: {driver.current_url}")
except Exception:
pass
save_debug("at_landing", driver.page_source)
dbg_sleep()
try:
driver.execute_script("document.cookie = '_TESTCOOKIESUPPORT=1; path=/';")
except Exception:
pass
# 2) Login
assert_window_alive(driver, "before_login_fields", main_handle, base)
# Optional fixed pause before interacting with login form
try:
if args.prelogin_delay_sec and args.prelogin_delay_sec > 0:
time.sleep(args.prelogin_delay_sec)
except Exception:
pass
try:
usr = wait_find(driver, By.CSS_SELECTOR, "#Frm_Username, input[name='Frm_Username'], input[name='username']", timeout=10)
pwd = wait_find(driver, By.CSS_SELECTOR, "#Frm_Password, input[name='Frm_Password'], input[type='password']", timeout=10)
except TimeoutException:
# Ensure we're not on DevTools window while saving
_select_main_content_window(driver, main_handle, base)
save_debug("login_fields_missing", driver.page_source)
sys.exit("Login fields not found")
# Optional: hold at login page for manual inspection without auto-filling
if args.no_auto_login:
save_debug("at_login_hold", driver.page_source)
# Keep the browser open for inspection, then exit
try:
time.sleep(max(1, int(args.debug_delay_sec)))
except Exception:
time.sleep(7)
sys.exit("Held at login as requested (--no-auto-login)")
# Helper for slow typing
def _type_slow(el, text: str, delay_ms: int = 0):
try:
el.clear()
except Exception:
pass
if delay_ms and delay_ms > 0:
for ch in text:
el.send_keys(ch)
time.sleep(delay_ms / 1000.0)
else:
el.send_keys(text)
# Fill via JS (ensures disabled/readonly removed), then invoke firmware login handler if present
dbg_sleep()
assert_window_alive(driver, "before_login_submit", main_handle, base)
did_submit = False
if not args.show_typing:
try:
driver.execute_script(
"""
try{
var u = arguments[0], p = arguments[1], usr=arguments[2], pwd=arguments[3];
if(u){ u.removeAttribute('disabled'); u.value=''; if(u.type==='text'||!u.type){ u.value = usr; } else { try{ u.setAttribute('type','text'); u.value=usr; }catch(e){} } }
if(p){ p.removeAttribute('disabled'); p.value=''; p.setAttribute('type','password'); p.value = pwd; }
// prefer calling modem's login function to ensure tokenized submit
if (typeof doLogin === 'function'){
doLogin();
} else if (typeof LoginFormObj !== 'undefined' && LoginFormObj && typeof LoginFormObj.doLogin === 'function'){
try{ if (window._sessionTmpToken){ template_csrf && template_csrf(window._sessionTmpToken); } }catch(e){}
LoginFormObj.doLogin();
} else {
var btn = document.getElementById('LoginId') || document.querySelector("input#LoginId, input[name='LoginId'], input[type='submit'], input[type='button'], button[type='submit']");
if (btn) btn.click();
else if (p && p.form) p.form.submit();
}
}catch(e){}
""",
usr, pwd, args.user, args.password
)
did_submit = True
except Exception:
did_submit = False
if args.show_typing or not did_submit:
try:
driver.execute_script("arguments[0].removeAttribute('disabled');", usr)
except Exception:
pass
try:
driver.execute_script("arguments[0].removeAttribute('disabled'); arguments[0].setAttribute('type','password');", pwd)
except Exception:
pass
_type_slow(usr, args.user, args.type_slow_ms)
_type_slow(pwd, args.password, args.type_slow_ms)
# Click login button with robust finder
if not click_login_button(driver):
try:
if hasattr(pwd, 'submit'):
pwd.submit()
except Exception:
pass
# allow spinner/login processing to be visible
dbg_sleep()
assert_window_alive(driver, "after_login_submit", main_handle, base)
# If no navigation or request seems to fire, force final submit paths
try:
driver.execute_script(
"""
try{
// set cookie support again
try{ document.cookie='_TESTCOOKIESUPPORT=1; path=/'; }catch(e){}
var tok = window._sessionTmpToken || window._sessionTOKEN || window.sessionToken || '';
// ensure hidden token input is present if form needs it
try{
var f = (document.querySelector('#Frm_Username, input[name=Frm_Username], input[name=username]')||{}).form || (document.querySelector('#Frm_Password, input[name=Frm_Password], input[type=password]')||{}).form || document.querySelector('form');
if (f){
var hid = f.querySelector('input[name=\"_sessionTOKEN\"]');
if (!hid){ hid = document.createElement('input'); hid.type='hidden'; hid.name='_sessionTOKEN'; f.appendChild(hid); } if (tok){ hid.value = tok; }
// fire typical events to satisfy validators
var u = document.querySelector('#Frm_Username, input[name=Frm_Username], input[name=username]');
var p = document.querySelector('#Frm_Password, input[name=Frm_Password], input[type=password]');
['input','change','keyup','keydown','blur'].forEach(function(ev){ try{ u && u.dispatchEvent(new Event(ev,{bubbles:true})); }catch(e){} try{ p && p.dispatchEvent(new Event(ev,{bubbles:true})); }catch(e){} });
// set IsAllowSubmit gate
try{ window.IsAllowSubmit = true; }catch(e){}
// Firmware observed pre-login token GET: invoke via button context
try{
if (typeof $ !== 'undefined' && $.fn && $("#LoginId").length && $("#LoginId").dataTransfer){
$("#LoginId").dataTransfer("/function_module/login_module/login_page/logintoken_lua.lua", "GET", (window.g_loginToken||tok||''), undefined, false);
}
}catch(e){}
// attempt known login functions again
if (typeof doLogin==='function'){ doLogin(); }
else if (typeof Login==='function'){ Login(); }
else if (typeof loginSubmit==='function'){ loginSubmit(); }
else if (typeof doSubmit==='function'){ doSubmit(); }
else {
try{ f.dispatchEvent(new Event('submit',{bubbles:true,cancelable:true})); }catch(e){}
try{ f.submit(); }catch(e){}
}
}
}catch(e){}
}catch(e){}
"""
)
except Exception:
pass
time.sleep(0.5)
save_debug("after_login_trigger", driver.page_source)
# Wait for admin UI
logged_in = False
for _ in range(20):
try:
if driver.find_elements(By.CSS_SELECTOR, "#mainNavigator, #Menu, #page_container"):
logged_in = True
break
except Exception:
pass
time.sleep(0.5)
if not logged_in:
# Detect lockout or error messages
html = driver.page_source or ''
if ('300' in html and 'saniye' in html.lower()):
save_debug("login_locked_wait_300", html)
sys.exit("Login locked by modem: wait 300 seconds before retrying")
save_debug("login_failed", html)
sys.exit("Login did not complete")
# 3) Ensure we are in admin UI context (handle frames)
_select_main_content_window(driver, main_handle, base)
switch_to_admin_frame(driver)
save_debug("after_login", driver.page_source)
dbg_sleep()
# 4) Click top WAN/Genel Ağ
top_candidates = [
(By.CSS_SELECTOR, "#mmInternet"),
(By.XPATH, "//a[contains(., 'Genel Ağ') or contains(., 'WAN')]")
]
found = any_present(driver, top_candidates, timeout=6)
assert_window_alive(driver, "before_top_wan_click", main_handle, base)
if found:
try:
driver.find_element(*found).click()
except Exception:
try:
driver.execute_script("try{ var e=document.getElementById('mmInternet'); if(e&&typeof AjaxQuery_ClassMenuClick==='function'){ try{ AjaxQuery_ClassMenuClick($(e)); }catch(err){ AjaxQuery_ClassMenuClick(e); } } }catch(err){}")
except Exception:
pass
save_debug("after_top_wan", driver.page_source)
dbg_sleep()
# 5) Click left WAN Bağlantıları
try:
WebDriverWait(driver, 6).until(EC.presence_of_element_located((By.CSS_SELECTOR, "#class2MenuItem, #Class2Menu")))
except TimeoutException:
pass
left_candidates = [
(By.CSS_SELECTOR, "#smInternetCof"),
(By.XPATH, "//a[contains(., 'WAN Bağlantıları')]")
]
left = any_present(driver, left_candidates, timeout=6)
assert_window_alive(driver, "before_left_wan_click", main_handle, base)
if left:
try:
driver.find_element(*left).click()
except Exception:
try:
driver.execute_script("try{ var a=document.getElementById('smInternetCof'); if(a) a.click(); }catch(err){}")
except Exception:
pass
else:
# Generate the submenu if missing
try:
driver.execute_script("try{ if (typeof Class2MenuShow==='function'){ Class2MenuShow('', 'mmInternet', 'smInternetCof'); } }catch(err){}")
except Exception:
pass
# Ensure we are still in the admin frame after submenu click
switch_to_admin_frame(driver)
save_debug("after_left_wan", driver.page_source)
dbg_sleep()
# Force-load WAN page if INTERNET markers not present yet
if not is_internet_ui_present(driver):
dbg_sleep()
assert_window_alive(driver, "before_enforce_wan", main_handle, base)
enforced = enforce_wan_page(driver, base)
save_debug("after_enforce_wan", driver.page_source)
dbg_sleep()
if not enforced:
# Try a second enforcement pass after small delay
time.sleep(0.6)
enforced = enforce_wan_page(driver, base)
save_debug("after_enforce_wan_retry", driver.page_source)
dbg_sleep()
# 6) Wait for center INTERNET template and expand INTERNET
try:
WebDriverWait(driver, 8).until(EC.presence_of_element_located((By.CSS_SELECTOR, "div[id^='template_Internet'], [id^='instName_Internet']")))
except TimeoutException:
# If still missing, attempt enforcement once more
if not is_internet_ui_present(driver):
enforce_wan_page(driver, base)
# Expand INTERNET header and show ChangeArea
try:
driver.execute_script(
"""
try{
var inst = document.querySelector("[id^='instName_Internet']");
if (inst){ inst.click(); inst.classList.add('instNameExp'); }
var t = document.querySelector("div[id^='template_Internet']") || document.querySelector("div[id^='template_Internet_']") || document.querySelector("div[id^='template_Internet:']");
if (t){ var ca=t.querySelector('.ChangeArea'); if (ca){ ca.style.display='block'; ca.style.visibility='visible'; } var h=t.querySelector('.collapsibleInst, .instName'); if (h){ h.classList.add('instNameExp'); } }
}catch(e){}
"""
)
except Exception:
pass
time.sleep(0.6)
# Derive INTERNET suffix if present (e.g., instName_Internet:3)
suffix = None
try:
suffix = driver.execute_script(
"""
try{
var s = document.querySelector("span[id^='instName_Internet']");
if (s && s.id && s.id.indexOf(':')>0) return s.id.split(':')[1]; var c = document.querySelector("div[id^='changeArea_Internet']");
if (c && c.id && c.id.indexOf(':')>0) return c.id.split(':')[1]; }catch(e){}
return null;
"""
)
except Exception:
suffix = None
# 7) Locate PPP inputs
def find_ppp_fields(suf: Optional[str]):
user = None
pwd = None
# Try dynamic ids first
if suf:
try:
user = driver.find_element(By.CSS_SELECTOR, f"#UserName\\:{suf}")
except NoSuchElementException:
user = None
try:
pwd = driver.find_element(By.CSS_SELECTOR, f"#Password\\:{suf}")
except NoSuchElementException:
pwd = None
if user is None:
try:
user = driver.find_element(By.CSS_SELECTOR, "[id^='UserName:']")
except NoSuchElementException:
try:
user = driver.find_element(By.CSS_SELECTOR, "#Frm_PPPUserName, input[name='Frm_PPPUserName']")
except NoSuchElementException:
user = None
if pwd is None:
try:
pwd = driver.find_element(By.CSS_SELECTOR, "[id^='Password:']")
except NoSuchElementException:
try:
pwd = driver.find_element(By.CSS_SELECTOR, "#Frm_PPPPassword, input[name='Frm_PPPPassword']")
except NoSuchElementException:
pwd = None
return user, pwd
# Also try to identify inputs via label 'for'
def find_via_label():
try:
lbl = driver.find_elements(By.CSS_SELECTOR, "label[for^='UserName:'], label[for='Frm_PPPUserName']")
if lbl:
tgt = lbl[0].get_attribute('for') or ''
if tgt:
try:
return driver.find_element(By.ID, tgt)
except Exception:
return None
except Exception:
return None
return None
u_el, p_el = find_ppp_fields(suffix)
if u_el is None:
via = find_via_label()
if via is not None:
u_el = via
if u_el is None and p_el is None:
# Try clicking Modify inside template to reveal edit fields
try:
driver.execute_script(
"""
try{
var el = document.querySelector("[id^='UserName:']") || document.querySelector('#Frm_PPPUserName');
if (el){ var t=el.closest("div[id^='template_Internet']")||el.closest("div[id^='template_Internet_']")||el.closest("div[id^='template_Internet:']");
if (t){ var m=t.querySelector("input[id^='Btn_modify'], .Btn_modify, input[type='button'][value*='Düzenle']"); if (m) m.click();
var ca=t.querySelector('.ChangeArea'); if (ca){ ca.style.display='block'; ca.style.visibility='visible'; }
}
}
}catch(e){}
"""
)
except Exception:
pass
# Also try explicit modify id with suffix
if suffix:
try:
btnm = driver.find_elements(By.CSS_SELECTOR, f"#Btn_modify_internet\\:{suffix}")
if btnm:
btnm[0].click()
except Exception:
pass
time.sleep(0.4)
u_el, p_el = find_ppp_fields(suffix)
if u_el is None:
via = find_via_label()
if via is not None:
u_el = via
# Dump current values
dbg_sleep()
cur_user = ''
cur_pass = ''
try:
if u_el:
cur_user = u_el.get_attribute('value') or driver.execute_script("return arguments[0].value || arguments[0].defaultValue || '';", u_el) or ''
if p_el:
cur_pass = p_el.get_attribute('value') or driver.execute_script("return arguments[0].value || arguments[0].defaultValue || '';", p_el) or ''
if not cur_user and suffix:
cur_user = driver.execute_script("try{ var u=document.getElementById('UserName:'+arguments[0]); return u? (u.value||u.defaultValue||'') : ''; }catch(e){return ''}", suffix) or ''
except Exception:
pass
# Fallback: parse page_source for PPP user if inputs returned empty
if not cur_user:
try:
html = driver.page_source or ''
import re as _re
# Try dynamic id UserName:<n>
m = _re.search(r'id=["\']UserName:(\d+)["\'][^>]*?value=["\']([^"\']*)["\']', html, flags=_re.IGNORECASE)
if m:
cur_user = m.group(2) or ''
if not cur_user:
# Try Frm_PPPUserName
m2 = _re.search(r'(?:id|name)=["\']Frm_PPPUserName["\'][^>]*?value=["\']([^"\']*)["\']', html, flags=_re.IGNORECASE)
if m2:
cur_user = m2.group(1) or ''
if not cur_user:
# Read-only display fields (common ZTE patterns)
m3 = _re.search(r'id=["\']UserNameR:(\d+)["\'][^>]*?>\s*([^<\s][^<]*)<', html, flags=_re.IGNORECASE)
if m3:
cur_user = (m3.group(2) or '').strip()
if not cur_user:
# Label-near value (Turkish/English)
m4 = _re.search(r'(Kullanıcı\s*Adı|User\s*Name)[^<]*</[^>]+>\s*<[^>]+>\s*([^<\s][^<]*)<', html, flags=_re.IGNORECASE)
if m4:
cur_user = (m4.group(2) or '').strip()
except Exception:
pass
print(f"PPP username (current): {cur_user}")
# If update requested
if args.ppp_user and args.ppp_pass and (u_el or p_el):
try:
if u_el:
driver.execute_script("arguments[0].value='';", u_el)
u_el.clear(); u_el.send_keys(args.ppp_user)
if p_el:
driver.execute_script("arguments[0].value='';", p_el)
p_el.clear(); p_el.send_keys(args.ppp_pass)
except Exception:
pass
# Attempt to click Apply within template
applied = False
apply_selectors = [
(By.CSS_SELECTOR, "input[id^='Btn_apply_internet:']"),
(By.CSS_SELECTOR, ".Btn_apply"),
(By.XPATH, "//input[@type='button' or @type='submit'][contains(@value,'Uygula')]")
]
for by, sel in apply_selectors:
try:
btns = driver.find_elements(by, sel)
if btns:
try:
driver.execute_script("arguments[0].scrollIntoView({block:'center'});", btns[0])
except Exception:
pass
btns[0].click()
applied = True
break
except Exception:
continue
if not applied:
save_debug("wan_apply_missing", driver.page_source)
sys.exit("Could not find Apply button")
time.sleep(1.0)
# Optional: accept confirm if shown
try:
confirm = driver.find_elements(By.CSS_SELECTOR, "#confirmOK, .confirmOK")
if confirm:
confirm[0].click()
except Exception:
pass
print("Apply clicked (check device UI for success)")
save_debug("wan_ppp_final", driver.page_source)
if args.hold_open_end and args.headful:
print("[Info] Holding browser open at end (--hold-open-end). Close the window to exit.")
try:
while True:
time.sleep(1.0)
except KeyboardInterrupt:
pass
finally:
try:
driver.quit()
except Exception:
pass
if __name__ == "__main__":
main()