def get_form(self): data = current_state.backend.perform("data_collect", "get") if self.data and "enable" in self.data: data["enable"] = self.data["enable"] else: data["enable"] = data["agreed"] form = fapi.ForisForm("enable_collection", data) section = form.add_section( name="collection_toggle", title=_(self.userfriendly_title), ) section.add_field(Checkbox, name="enable", label=_("Enable data collection"), preproc=lambda val: bool(int(val))) def form_cb(data): data = current_state.backend.perform("data_collect", "set", {"agreed": data["enable"]}) return "save_result", data # store {"result": ...} to be used later... form.add_callback(form_cb) return form
def get_form(self): form = fapi.ForisForm("registration_check", self.data) main_section = form.add_section(name="check_email", title=_(self.userfriendly_title)) main_section.add_field(Email, name="email", label=_("Email")) def form_cb(data): data = current_state.backend.perform( "data_collect", "get_registered", { "email": data.get("email"), "language": current_state.language }) error = None registration_number = None url = None if data["status"] == "unknown": error = _("Failed to query the server.") elif data["status"] == "not_valid": error = _("Failed to verify the router's registration.") elif data["status"] in ["free", "foreign"]: url = data["url"] registration_number = data["registration_number"] return "save_result", { 'success': data["status"] not in ["unknown", "not_valid"], 'status': data["status"], 'error': error, 'url': url, 'registration_number': registration_number, } form.add_callback(form_cb) return form
def get_serial_form(self, data=None): generate_serial_form = fapi.ForisForm("serial_form", data) serial_section = generate_serial_form.add_section("serial_section", title=None) serial_section.add_field( Textbox, name="serial", label=" ", required=True, validators=[validators.MacAddress()] ) return generate_serial_form
def get_form(self): form = fapi.ForisForm("create_and_upload", self.data) section = form.add_section( name="passwords", title=self.userfriendly_title, ) section.add_field( Password, name="password", label=_("Password"), required=True, validators=validators.LenRange(6, 128) ) section.add_field( Password, name="password_validation", label=_("Password (repeat)"), required=True, validators=validators.EqualTo( "password", "password_validation", _("Passwords are not equal.") ) ) def form_cb(data): res = current_state.backend.perform( "ssbackups", "set_password", {"password": base64.b64encode(data["password"].encode()).decode()} ) return "save_result", res # store {"result": ...} to be used later... form.add_callback(form_cb) return form
def get_form(self): system_pw_form = fapi.ForisForm("system_password", self.data) system_pw_main = system_pw_form.add_section( name="set_password", title=_(self.userfriendly_title), description= _("In order to access the advanced configuration possibilities which are " "not present here, you must set the root user's password. The advanced " "configuration options can be managed either through the " "<a href=\"//%(host)s/%(path)s\">LuCI web interface</a> " "or over SSH.") % { 'host': bottle.request.get_header('host'), 'path': 'cgi-bin/luci' }) system_pw_main.add_field(Password, name="password", label=_("Password"), required=True, validators=validators.LenRange(6, 128)) system_pw_main.add_field(Password, name="password_validation", label=_("Password (repeat)"), required=True, validators=validators.EqualTo( "password", "password_validation", _("Passwords are not equal."))) def system_pw_form_cb(data): client.set_password("root", data["password"]) return "none", None system_pw_form.add_callback(system_pw_form_cb) return system_pw_form
def get_form(self): data = current_state.backend.perform("sample", "get_slices") if self.data: # Update from post (used when the form is updated via ajax) data.update(self.data) form = fapi.ForisForm("sample", data) section = form.add_section( name="main_section", title=self.userfriendly_title, ) # _() translates the string immediatelly # it is also used for detecting translations during foris_make_messages cmd section.add_field(Number, name="slices", label=_("Number of slices"), required=True, validators=validators.InRange(2, 15)) def form_cb(data): res = current_state.backend.perform( "sample", "set_slices", {"slices": int(data["slices"])}) return "save_result", res # store {"result": ...} to be used in SamplePluginPage save() method form.add_callback(form_cb) return form
def make_form(self, data: typing.Optional[dict]) -> fapi.ForisForm: backend_data = current_state.backend.perform( "wifi", "get_settings", controller_id=self.controller_id ) form_data = self.convert_data_from_backend_to_form(backend_data) if data: form_data.update(data) used_bands = [] for field_name, band in [(k, v) for k, v in form_data.items() if k.endswith("hwmode")]: # radioX-hwmode => X radio_number = field_name.split("-", 1)[0][len("radio"):] radio_enabled = form_data.get(f"radio{radio_number}-device_enabled", False) if (isinstance(radio_enabled, bool) and radio_enabled) or radio_enabled == "1": used_bands.append(band) wifi_form = fapi.ForisForm("wifi", form_data) # display conflict message when two wifis are using the same band wifi_form.band_conflict = not (len(used_bands) == len(set(used_bands))) wifi_form.add_section( name="wifi", title=_("Wi-Fi"), description=_( "If you want to use your router as a Wi-Fi access point, enable Wi-Fi " "here and fill in an SSID (the name of the access point) and a " "corresponding password. You can then set up your mobile devices, " "using the QR code available within the form." ), ) # Add wifi section wifi_section = wifi_form.add_section(name="wifi_settings", title=_("Wi-Fi settings")) for idx, device in enumerate(backend_data["devices"]): prefix = WifiEditForm.prefixed(device["id"], "") device_form_data = { k[len(prefix) :]: v for k, v in form_data.items() if k.startswith(prefix) } # prefix removed self._prepare_device_fields( wifi_section, device, device_form_data, len(backend_data["devices"]) - 1 == idx ) def form_cb(data): update_data = self.convert_data_from_form_to_backend( data, [e["id"] for e in backend_data["devices"]] ) res = current_state.backend.perform( "wifi", "update_settings", update_data, controller_id=self.controller_id ) return "save_result", res wifi_form.add_callback(form_cb) return wifi_form
def get_form(self): form = fapi.ForisForm("netboot", {}) form.add_section(name="main_section", title=self.userfriendly_title) def form_cb(data): return "save_result", {} form.add_callback(form_cb) return form
def get_form(self): finished_form = fapi.ForisForm("guide_finished", {}) finished_form.add_section(name="guide_finished", title=_(self.userfriendly_title)) def guide_finished_cb(data): res = current_state.backend.perform("web", "update_guide", {"enabled": False}) return "save_result", res # store {"result": ...} to be used later... finished_form.add_callback(guide_finished_cb) return finished_form
def get_form(self): data = { "enabled": self.backend_data["enabled"], "port": self.backend_data["port"], "wan_access": self.backend_data["wan_access"], } if self.data: # Update from post data.update(self.data) form = fapi.ForisForm("remote", data) config_section = form.add_section(name="set_remote", title=_(self.userfriendly_title)) config_section.add_field( Checkbox, name="enabled", label=_("Enable remote access"), ) config_section.add_field( Checkbox, name="wan_access", label=_("Accessible via WAN"), hint= _("If this option is check the device in the WAN network will be able to connect " "to the configuration interface. Otherwise only devices on LAN will be able to " "access the configuration interface."), ).requires("enabled", True) config_section.add_field( Number, name="port", label=_("Port"), hint=_("A port which will be opened for the remote configuration " "of this device."), validator=[InRange(1, 2**16 - 1)], default=11884, ).requires("enabled", True) def form_callback(data): msg = {"enabled": data['enabled']} if msg["enabled"]: msg["port"] = int(data["port"]) msg["wan_access"] = data['wan_access'] res = current_state.backend.perform("remote", "update_settings", msg) res['enabled'] = msg['enabled'] return "save_result", res # store {"result": ...} to be used later... form.add_callback(form_callback) return form
def get_form(self): data = current_state.backend.perform("netmetr", "get_settings") # init hours for i in range(24): data["hour_to_run_%d" % i] = False # update the enabled for e in data["hours_to_run"]: data["hour_to_run_%d" % e] = True if self.data: # Update from post data.update(self.data) form = fapi.ForisForm("netmetr", data) main = form.add_section(name="set_netmetr", title=_(self.userfriendly_title)) autostart_section = main.add_section(name="autostart", title=_("Autostart")) autostart_section.add_field( Checkbox, name="autostart_enabled", label=_("Autostart enabled"), preproc=lambda val: bool(int(val)), hint= _("Measuring will start about selected hour (time is no exact for load distribution)" )) hours = main.add_section(name="hours", title=_("Autostart times")) for i in range(24): hours.add_field( Checkbox, name="hour_to_run_%d" % i, label="%02d:00" % i, preproc=lambda val: bool(int(val)), ).requires("autostart_enabled", True) def form_cb(data): msg = {"autostart_enabled": data["autostart_enabled"]} if data["autostart_enabled"]: msg["hours_to_run"] = [] for i in range(24): name = "hour_to_run_%d" % i if name in data and data[name]: msg["hours_to_run"].append(i) current_state.backend.perform("netmetr", "update_settings", msg) messages.success(_("Netmetr settings were updated.")) return "none", None form.add_callback(form_cb) return form
def get_form(self): maintenance_form = fapi.ForisForm("maintenance", self.data) maintenance_main = maintenance_form.add_section( name="restore_backup", title=_(self.userfriendly_title)) maintenance_main.add_field(File, name="backup_file", label=_("Backup file"), required=True) def maintenance_form_cb(data): result = client.load_config_backup(data['backup_file'].file) return "save_result", {'new_ip': result} maintenance_form.add_callback(maintenance_form_cb) return maintenance_form
def get_form(self): maintenance_form = fapi.ForisForm("maintenance", self.data) maintenance_main = maintenance_form.add_section( name="restore_backup", title=_(self.userfriendly_title) ) maintenance_main.add_field(File, name="backup_file", label=_("Backup file"), required=True) def maintenance_form_cb(data): data = current_state.backend.perform( "maintain", "restore_backup", {"backup": base64.b64encode(data["backup_file"].file.read()).decode("utf-8")}, ) return "save_result", {"result": data["result"]} maintenance_form.add_callback(maintenance_form_cb) return maintenance_form
def get_form(self): data = copy.deepcopy(self.backend_data) if self.data: # Update from post data.update(self.data) networks_form = fapi.ForisForm("networks", self.data) ports_section = networks_form.add_section( name="set_ports", title=_(self.userfriendly_title)) checkboxes = [] for kind in ["wan", "lan", "guest", "none"]: checkboxes += [(e["id"], e["id"]) for e in self.backend_data["networks"][kind]] ports_section.add_field(MultiCheckbox, name="wan", args=checkboxes, multifield=True) ports_section.add_field(MultiCheckbox, name="lan", args=checkboxes, multifield=True) ports_section.add_field(MultiCheckbox, name="guest", args=checkboxes, multifield=True) ports_section.add_field(MultiCheckbox, name="none", args=checkboxes, multifield=True) ports_section.add_field(Checkbox, name="ssh_on_wan", default=False, required=False) ports_section.add_field(Checkbox, name="http_on_wan", default=False, required=False) ports_section.add_field(Checkbox, name="https_on_wan", default=False, required=False) def networks_form_cb(data): wan = data.get("wan", []) lan = data.get("lan", []) guest = data.get("guest", []) none = data.get("none", []) ssh_on_wan = bool(data.get("ssh_on_wan", "0")) http_on_wan = bool(data.get("http_on_wan", "0")) https_on_wan = bool(data.get("https_on_wan", "0")) result = current_state.backend.perform( "networks", "update_settings", { "firewall": { "ssh_on_wan": ssh_on_wan, "http_on_wan": http_on_wan, "https_on_wan": https_on_wan }, "networks": {"lan": lan, "wan": wan, "guest": guest, "none": none} } ) return "save_result", result networks_form.add_callback(networks_form_cb) return networks_form
def get_generate_token_form(self, data=None): generate_token_form = fapi.ForisForm("generate_remote_token", data) token_section = generate_token_form.add_section("generate_token", title=None) token_section.add_field( Textbox, name="name", label=_("Token name"), required=True, hint= _("The display name for the token. It must be shorter than 64 characters " "and must contain only alphanumeric characters, dots, dashes and " "underscores."), validators=[ RegExp(_("Token name is invalid."), r'[a-zA-Z0-9_.-]+'), LenRange(1, 63) ]) return generate_token_form
def get_form(self): form = fapi.ForisForm("registration_check", self.data, filter=create_config_filter("foris")) main_section = form.add_section(name="check_email", title=_(self.userfriendly_title)) main_section.add_field(Email, name="email", label=_("Email")) def form_cb(data): result = client.get_registration_status(data.get("email"), bottle.request.app.lang) return "save_result", { 'success': result[0], 'response': result[1], } form.add_callback(form_cb) return form
def get_form(self): form = fapi.ForisForm("suboridinates", self.data) section = form.add_section(name="main_section", title=_(self.userfriendly_title)) section.add_field(File, name="token_file", label=_("Token file"), required=True) def form_cb(data): res = current_state.backend.perform( "subordinates", "add_sub", {"token": base64.b64encode(data["token_file"].file.read()).decode("utf-8")}, ) return "save_result", res form.add_callback(form_cb) return form
def get_form(self): finished_form = fapi.ForisForm("guide_finished", {}) finished_form.add_section( name="guide_finished", title=_(self.userfriendly_title), description=_( "Congratulations you've successfully reached the end of this guide. " "Once you leave this guide you'll be granted access to the " "full configuration interface of this device.")) def guide_finished_cb(data): res = current_state.backend.perform("web", "update_guide", { "enabled": False, }) return "save_result", res # store {"result": ...} to be used later... finished_form.add_callback(guide_finished_cb) return finished_form
def make_form(self, data: typing.Optional[dict]): form_data = self.convert_data_from_backend_to_form( current_state.backend.perform("subordinates", "list") ) if data: form_data.update(data) sub_form = fapi.ForisForm("update_subsub", form_data) sub_section = sub_form.add_section( "subsubordinate_section", title="", description=_( "You can edit managed devices here. These devices are not " "not directly connected to this device but " "they are connected through another managed device." ), ) sub_section.add_field(Hidden, name="controller_id", required=True, title="") sub_section.add_field( Textbox, name="custom_name", label=_("Custom Name"), validators=[validators.LenRange(0, 30)], hint=_("Nicer name for your device with serial '%(controller_id)s'.") % dict(controller_id=self.subsubordinate_controller_id), ) def form_cb(data): res = current_state.backend.perform( "subordinates", "update_subsub", { "controller_id": data["controller_id"], "options": {"custom_name": data["custom_name"]}, }, ) return "save_result", res sub_form.add_callback(form_cb) return sub_form
def get_form(self): backend_data = current_state.backend.perform("wifi", "get_settings") form_data = self._backend_data_to_form_data(backend_data) if self.data: form_data.update(self.data) wifi_form = fapi.ForisForm("wifi", form_data) wifi_form.add_section( name="wifi", title=_(self.userfriendly_title), description= _("If you want to use your router as a Wi-Fi access point, enable Wi-Fi " "here and fill in an SSID (the name of the access point) and a " "corresponding password. You can then set up your mobile devices, " "using the QR code available within the form.")) # Add wifi section wifi_section = wifi_form.add_section( name="wifi_settings", title=_("Wi-Fi settings"), ) for idx, device in enumerate(backend_data["devices"]): prefix = WifiHandler.prefixed(device["id"], "") device_form_data = { k[len(prefix):]: v for k, v in form_data.items() if k.startswith(prefix) } # prefix removed self._prepare_device_fields( wifi_section, device, device_form_data, len(backend_data["devices"]) - 1 == idx) def form_cb(data): update_data = self._form_data_to_backend_data( data, [e["id"] for e in backend_data["devices"]]) res = current_state.backend.perform("wifi", "update_settings", update_data) return "save_result", res # store {"result": ...} to be used later... wifi_form.add_callback(form_cb) return wifi_form
def get_form(self): data = {"workflow": self.backend_data["current_workflow"]} if self.data: data.update(self.data) profile_form = fapi.ForisForm("profile", data) main = profile_form.add_section(name="set_profile", title=_(self.userfriendly_title)) main.add_field(Hidden, name="workflow", value=self.backend_data["current_workflow"]) def profile_form_cb(data): result = current_state.backend.perform( "web", "update_guide", { "enabled": True, "workflow": data["workflow"], } ) return "save_result", result profile_form.add_callback(profile_form_cb) return profile_form
def get_token_id_form(self, data=None): token_id_form = fapi.ForisForm("token_id_form", data) token_section = token_id_form.add_section("token_id_section", title=None) token_section.add_field(Textbox, name="token_id", label="", required=True, validators=[ RegExp(_("Token id is invalid."), r'([a-zA-Z0-9][a-zA-Z0-9])+') ]) token_section.add_field( Textbox, name="name", label=_("Token name"), required=False, validators=[ RegExp(_("Token name is invalid."), r'[a-zA-Z0-9_.-]+'), LenRange(1, 63) ], ) return token_id_form
def get_form(self): time_form = fapi.ForisForm("time", self.data, filter=filters.time) time_main = time_form.add_section( name="set_time", title=_(self.userfriendly_title), description= _("We could not synchronize the time with a timeserver, probably due to a " "loss of connection. It is necessary for the router to have correct time " "in order to function properly. Please, synchronize it with your " "computer's time, or set it manually.")) time_main.add_field(Textbox, name="time", label=_("Time"), nuci_path="time", nuci_preproc=lambda v: v.local) def time_form_cb(data): client.set_time(data['time']) return "none", None time_form.add_callback(time_form_cb) return time_form
def get_form(self): data = copy.deepcopy(self.backend_data) data["enabled"] = "1" if data["enabled"] else "0" data["approval_status"] = data["approval_settings"]["status"] if "delay" in data["approval_settings"]: data["approval_delay"] = data["approval_settings"]["delay"] for userlist in [e for e in data['user_lists'] if not e["hidden"]]: data["install_%s" % userlist["name"]] = userlist["enabled"] for lang in data["languages"]: data["language_%s" % lang["code"]] = lang["enabled"] if self.data: # Update from post data.update(self.data) self.updater_enabled = True if data["enabled"] == "1" else False self.approval_setting_status = data["approval_status"] self.approval_setting_delay = data.get("approval_delay", self.APPROVAL_DEFAULT_DELAY) form = fapi.ForisForm("updater", data) main_section = form.add_section( name="main", title=_(self.userfriendly_title), description=_("Updater is a service that keeps all TurrisOS " "software up to date. Apart from the standard " "installation, you can optionally select bundles of " "additional software that'd be installed on the " "router. This software can be selected from the " "following list. " "Please note that only software that is part of " "TurrisOS or that has been installed from a package " "list is maintained by Updater. Software that has " "been installed manually or using opkg is not " "affected.")) main_section.add_field( Radio, name="enabled", label=_("I agree"), default="1", args=(("1", _("Use automatic updates (recommended)")), ("0", _("Turn automatic updates off"))), ) approval_section = main_section.add_section( name="approvals", title=_("Update approvals")) approval_section.add_field( RadioSingle, name=UpdaterHandler.APPROVAL_NO, group="approval_status", label=_("Automatic installation"), hint=_("Updates will be installed without user's intervention."), default=data["approval_status"], ) approval_section.add_field( RadioSingle, name=UpdaterHandler.APPROVAL_TIMEOUT, group="approval_status", label=_("Delayed updates"), hint=_("Updates will be installed with an adjustable delay. " "You can also approve them manually."), default=data["approval_status"], ) approval_section.add_field( Number, name="approval_delay", validators=[validators.InRange(1, 24 * 7)], default=UpdaterHandler.APPROVAL_DEFAULT_DELAY, min=1, max=24 * 7, required=True, ).requires(UpdaterHandler.APPROVAL_TIMEOUT, UpdaterHandler.APPROVAL_TIMEOUT).requires( UpdaterHandler.APPROVAL_NO, UpdaterHandler.APPROVAL_TIMEOUT).requires( UpdaterHandler.APPROVAL_NEEDED, UpdaterHandler.APPROVAL_TIMEOUT) approval_section.add_field( RadioSingle, name=UpdaterHandler.APPROVAL_NEEDED, group="approval_status", label=_("Update approval needed"), hint= _("You have to approve the updates, otherwise they won't be installed." ), default=data["approval_status"], ) package_lists_main = main_section.add_section( name="select_package_lists", title=None, ) for userlist in [e for e in data['user_lists'] if not e["hidden"]]: package_lists_main.add_field(Checkbox, name="install_%s" % userlist["name"], label=userlist["title"], hint=userlist["msg"]).requires( "enabled", "1") language_lists_main = main_section.add_section( name="select_languages", title=_( "If you want to use other language than English you can select it from the " "following list:")) for lang in data["languages"]: language_lists_main.add_field(Checkbox, name="language_%s" % lang["code"], label=lang["code"].upper()) if self.backend_data["approval"]["present"]: # field for hidden approval current_approval_section = main_section.add_section( name="current_approval", title="") current_approval_section.add_field( Hidden, name="approval-id", default=self.backend_data["approval"]["hash"]) # this will be filled according to action main_section.add_field(Hidden, name="target") def form_cb(data): data["enabled"] = True if data["enabled"] == "1" else False if data["enabled"] and data["target"] == "save": if data[self.APPROVAL_NEEDED] == self.APPROVAL_NEEDED: data["approval_settings"] = { "status": self.APPROVAL_NEEDED } elif data[self.APPROVAL_TIMEOUT] == self.APPROVAL_TIMEOUT: data["approval_settings"] = { "status": self.APPROVAL_TIMEOUT } data["approval_settings"]["delay"] = int( data["approval_delay"]) elif data[self.APPROVAL_NO] == self.APPROVAL_NO: data["approval_settings"] = {"status": self.APPROVAL_NO} if self.agreed_collect: data["enabled"] = True languages = [ k[9:] for k, v in data.items() if v and k.startswith("language_") ] user_lists = [ k[8:] for k, v in data.items() if v and k.startswith("install_") ] # merge with enabled hidden user lists user_lists += [ e["name"] for e in self.backend_data["user_lists"] if e["hidden"] and e["enabled"] ] res = current_state.backend.perform( "updater", "update_settings", { "enabled": True, "approval_settings": data["approval_settings"], "user_lists": user_lists, "languages": languages, }) elif data["enabled"] and data["target"] in ["grant", "deny"]: res = current_state.backend.perform( "updater", "resolve_approval", { "hash": data["approval-id"], "solution": data["target"] }) else: res = current_state.backend.perform("updater", "update_settings", { "enabled": False, }) res["target"] = data["target"] return "save_result", res form.add_callback(form_cb) return form
def get_form(self): stats = client.get(filter=filters.stats).find_child("stats") cards = self._get_wireless_cards(stats) if not cards: return None wifi_form = fapi.ForisForm("wifi", self.data, filter=filters.wifi_filter()) # Create mapping of radio_name -> iface_index radio_to_iface = self._get_radio_to_iface(wifi_form) # Add header section (used for page title) wifi_form.add_section( name="wifi", title=_(self.userfriendly_title), description= _("If you want to use your router as a Wi-Fi access point, enable Wi-Fi " "here and fill in an SSID (the name of the access point) and a " "corresponding password. You can then set up your mobile devices, " "using the QR code available next to the form.")) # Add wifi section wifi_section = wifi_form.add_section( name="wifi_settings", title=_("Wi-Fi settings"), ) # Get list of available radios radios = self._get_radios(cards, wifi_section, radio_to_iface, self.data or {}, wifi_form.nuci_config()) def wifi_form_cb(data): uci = Uci() wireless = Config("wireless") uci.add(wireless) guest_wifi_enabled = False for radio in radios: if self._prepare_radio_cb(data, wireless, radio): guest_wifi_enabled = True guest_interfaces = ["guest_turris_%s" % e for e in sorted(radios)] # test whether it is required to pass update guest network current_data = client.get(filter=filters.wifi_filter()) current_enabled = preprocessors.guest_network_enabled(current_data) if guest_wifi_enabled and not current_enabled: # Guest network handling guest_network_subnet = data.get("guest_network_subnet") if guest_network_subnet: network, prefix = data.get("guest_network_subnet").split( "/") else: network, prefix = DEFAULT_GUEST_NETWORK, DEFAULT_GUEST_PREFIX LanHandler.prepare_guest_configs(uci, True, network, prefix, guest_interfaces) elif guest_wifi_enabled: # try to update guest interfaces if the differs stored = current_data.find_child( "uci.network.guest_turris.ifname") if not stored or set( stored.value.split(" ")) != set(guest_interfaces): network_conf = uci.add(Config("network")) interface_section = network_conf.add( Section("guest_turris", "interface")) interface_section.add( Option("ifname", " ".join(guest_interfaces))) return "edit_config", uci wifi_form.add_callback(wifi_form_cb) return wifi_form
def get_form(self): form = fapi.ForisForm("enable_collection", self.data, filter=filters.create_config_filter( "foris", "updater")) section = form.add_section( name="collection_toggle", title=_(self.userfriendly_title), ) section.add_field(Checkbox, name="enable", label=_("Enable data collection"), nuci_path="uci.foris.eula.agreed_collect", nuci_preproc=lambda val: bool(int(val.value))) def form_cb(data): uci = build_option_uci_tree("foris.eula.agreed_collect", "config", data.get("enable")) return "edit_config", uci def adjust_lists_cb(data): uci = Uci() # All enabled lists enabled_lists = map( lambda x: x.content, form.nuci_config.find_child( "uci.updater.pkglists.lists").children) # Lists that do not need agreement enabled_no_agree = filter(lambda x: not x.startswith("i_agree_"), enabled_lists) # Lists that need agreement enabled_i_agree = filter(lambda x: x.startswith("i_agree_"), enabled_lists) # Always install lists that do not need agreement - create a copy of the list installed_lists = enabled_no_agree[:] logger.warning("no agree: %s", enabled_no_agree) logger.warning("installed: %s", installed_lists) if data.get("enable", False): # Include i_agree lists if user agreed with EULA installed_lists.extend(enabled_i_agree) # Add main data collection list if it's not present logger.warning(installed_lists) logger.warning("i_agree_datacollect" not in installed_lists) logger.warning("i_agree_datacollect" in installed_lists) if "i_agree_datacollect" not in installed_lists: logger.warning("appending") installed_lists.append("i_agree_datacollect") logger.warning("saving %s", installed_lists) # Reconstruct list of package lists updater = uci.add(Config("updater")) pkglists = updater.add(Section("pkglists", "pkglists")) lists = List("lists") for i, name in enumerate(installed_lists): lists.add(Value(i, name)) # If there's anything to add, replace the list, otherwise remove it completely if len(installed_lists) > 0: pkglists.add_replace(lists) else: pkglists.add_removal(lists) return "edit_config", uci def run_updater_cb(data): logger.info("Checking for updates.") client.check_updates() return "none", None form.add_callback(form_cb) form.add_callback(adjust_lists_cb) form.add_callback(run_updater_cb) return form
def get_form(self): # WAN wan_form = fapi.ForisForm("wan", self.data, filter=create_config_filter( "network", "smrtd", "ucollect")) wan_main = wan_form.add_section( name="set_wan", title=_(self.userfriendly_title), description= _("Here you specify your WAN port settings. Usually, you can leave this " "options untouched unless instructed otherwise by your internet service " "provider. Also, in case there is a cable or DSL modem connecting your " "router to the network, it is usually not necessary to change this " "setting.")) WAN_DHCP = "dhcp" WAN_STATIC = "static" WAN_PPPOE = "pppoe" WAN_OPTIONS = ( (WAN_DHCP, _("DHCP (automatic configuration)")), (WAN_STATIC, _("Static IP address (manual configuration)")), (WAN_PPPOE, _("PPPoE (for DSL bridges, Modem Turris, etc.)")), ) WAN6_NONE = "none" WAN6_DHCP = "dhcpv6" WAN6_STATIC = "static" WAN6_OPTIONS = ( (WAN6_DHCP, _("DHCPv6 (automatic configuration)")), (WAN6_STATIC, _("Static IP address (manual configuration)")), ) if not self.hide_no_wan: WAN6_OPTIONS = ((WAN6_NONE, _("Disable IPv6")), ) + WAN6_OPTIONS # protocol wan_main.add_field(Dropdown, name="proto", label=_("IPv4 protocol"), nuci_path="uci.network.wan.proto", args=WAN_OPTIONS, default=WAN_DHCP) # static ipv4 wan_main.add_field(Textbox, name="ipaddr", label=_("IP address"), nuci_path="uci.network.wan.ipaddr", required=True, validators=validators.IPv4())\ .requires("proto", WAN_STATIC) wan_main.add_field(Textbox, name="netmask", label=_("Network mask"), nuci_path="uci.network.wan.netmask", required=True, validators=validators.IPv4Netmask())\ .requires("proto", WAN_STATIC) wan_main.add_field(Textbox, name="gateway", label=_("Gateway"), nuci_path="uci.network.wan.gateway", validators=validators.IPv4(), required=True)\ .requires("proto", WAN_STATIC) def extract_dns_item(dns_option, index, default=None): if isinstance(dns_option, List): dns_list = [e.content for e in dns_option.children] elif isinstance(dns_option, Option): dns_list = dns_option.value.split(" ") else: return default # Server with higher priority should be last dns_list.reverse() try: return dns_list[index] except IndexError: return default # DNS servers wan_main.add_field(Textbox, name="dns1", label=_("DNS server 1"), nuci_path="uci.network.wan.dns", nuci_preproc=lambda val: extract_dns_item(val, 0), validators=validators.AnyIP(), hint=_("DNS server address is not required as the built-in " "DNS resolver is capable of working without it."))\ .requires("proto", WAN_STATIC) wan_main.add_field(Textbox, name="dns2", label=_("DNS server 2"), nuci_path="uci.network.wan.dns", nuci_preproc=lambda val: extract_dns_item(val, 1), validators=validators.AnyIP(), hint=_("DNS server address is not required as the built-in " "DNS resolver is capable of working without it."))\ .requires("proto", WAN_STATIC) # xDSL settings wan_main.add_field(Textbox, name="username", label=_("PAP/CHAP username"), nuci_path="uci.network.wan.username")\ .requires("proto", WAN_PPPOE) wan_main.add_field(Textbox, name="password", label=_("PAP/CHAP password"), nuci_path="uci.network.wan.password")\ .requires("proto", WAN_PPPOE) # IPv6 configuration wan_main.add_field(Dropdown, name="wan6_proto", label=_("IPv6 protocol"), args=WAN6_OPTIONS, default=WAN6_NONE, nuci_path="uci.network.wan6.proto") wan_main.add_field(Textbox, name="ip6addr", label=_("IPv6 address"), nuci_path="uci.network.wan6.ip6addr", validators=validators.IPv6Prefix(), hint=_("IPv6 address and prefix length for WAN interface, " "e.g. 2001:db8:be13:37da::1/64"), required=True)\ .requires("wan6_proto", WAN6_STATIC) wan_main.add_field(Textbox, name="ip6gw", label=_("IPv6 gateway"), validators=validators.IPv6(), nuci_path="uci.network.wan6.ip6gw")\ .requires("wan6_proto", WAN6_STATIC) wan_main.add_field(Textbox, name="ip6prefix", label=_("IPv6 prefix"), validators=validators.IPv6Prefix(), nuci_path="uci.network.wan6.ip6prefix", hint=_("Address range for local network, " "e.g. 2001:db8:be13:37da::/64"))\ .requires("wan6_proto", WAN6_STATIC) # enable SMRT settings only if smrtd config is present has_smrtd = wan_form.nuci_config.find_child("uci.smrtd") is not None if has_smrtd: wan_main.add_field(Hidden, name="has_smrtd", default="1") wan_main.add_field(Checkbox, name="use_smrt", label=_("Use Modem Turris"), nuci_path="uci.smrtd.global.enabled", nuci_preproc=lambda val: bool(int(val.value)), hint=_("Modem Turris (aka SMRT - Small Modem for Router Turris), " "a simple ADSL/VDSL modem designed specially for router " "Turris. Enable this option if you have Modem Turris " "connected to your router."))\ .requires("proto", WAN_PPPOE) def get_smrtd_param(param_name): """Helper function for getting SMRTd params for "connections" list.""" def wrapped(conn_list): # internet connection must be always first list element vlan_id, vpi, vci = ( conn_list.children[0].content.split(" ") if conn_list else (None, None, None)) if param_name == "VPI": return vpi elif param_name == "VCI": return vci elif param_name == "VLAN": return vlan_id raise ValueError("Unknown SMRTd connection parameter.") return wrapped def get_smrtd_vlan(data): """Helper function for getting VLAN number from Uci data.""" ifname = data.find_child("uci.network.wan.ifname") if ifname: ifname = ifname.value matches = re.match("%s.(\d+)" % self.wan_ifname, ifname) if matches: return matches.group(1) connections = data.find_child("uci.smrtd.%s.connections" % self.wan_ifname) result = get_smrtd_param("VLAN")(connections) return result # 802.1Q VLAN number is 12-bit, 0x0 and 0xFFF reserved wan_main.add_field(Textbox, name="smrt_vlan", label=_("xDSL VLAN number"), nuci_preproc=get_smrtd_vlan, validators=[validators.PositiveInteger(), validators.InRange(1, 4095)], hint=_("VLAN number for your internet connection. Your ISP might " "have provided you this number. If you have VPI and VCI " "numbers instead, leave this field empty, a default value " "will be used automatically."))\ .requires("use_smrt", True) vpi_vci_validator = validators.RequiredWithOtherFields( ("smrt_vpi", "smrt_vci"), _("Both VPI and VCI must be filled or both must be empty.")) wan_main.add_field( Textbox, name="smrt_vpi", label=_("VPI"), nuci_path="uci.smrtd.%s.connections" % self.wan_ifname, nuci_preproc=get_smrtd_param("VPI"), validators=[validators.PositiveInteger(), validators.InRange(0, 255), vpi_vci_validator], hint=_("Virtual Path Identifier (VPI) is a parameter that you might have received " "from your ISP. If you have a VLAN number instead, leave this field empty. " "You need to fill in both VPI and VCI together.") ) \ .requires("use_smrt", True) wan_main.add_field( Textbox, name="smrt_vci", label=_("VCI"), nuci_path="uci.smrtd.%s.connections" % self.wan_ifname, nuci_preproc=get_smrtd_param("VCI"), validators=[validators.PositiveInteger(), validators.InRange(32, 65535), vpi_vci_validator], hint=_("Virtual Circuit Identifier (VCI) is a parameter that you might have " "received from your ISP. If you have a VLAN number instead, leave this " "field empty. You need to fill in both VPI and VCI together.") )\ .requires("use_smrt", True) # custom MAC wan_main.add_field( Checkbox, name="custom_mac", label=_("Custom MAC address"), nuci_path="uci.network.wan.macaddr", nuci_preproc=lambda val: bool(val.value), hint=_( "Useful in cases, when a specific MAC address is required by " "your internet service provider.")) wan_main.add_field(Textbox, name="macaddr", label=_("MAC address"), nuci_path="uci.network.wan.macaddr", validators=validators.MacAddress(), hint=_("Separator is a colon, for example 00:11:22:33:44:55"), required=True)\ .requires("custom_mac", True) def wan_form_cb(data): uci = Uci() network = Config("network") uci.add(network) wan = Section("wan", "interface") network.add(wan) wan.add(Option("proto", data['proto'])) if data['custom_mac'] is True: wan.add(Option("macaddr", data['macaddr'])) else: wan.add_removal(Option("macaddr", None)) ucollect_ifname = self.wan_ifname if data['proto'] == WAN_PPPOE: wan.add(Option("username", data['username'])) wan.add(Option("password", data['password'])) wan.add(Option("ipv6", data.get("wan6_proto") is not WAN6_NONE)) ucollect_ifname = "pppoe-wan" elif data['proto'] == WAN_STATIC: wan.add(Option("ipaddr", data['ipaddr'])) wan.add(Option("netmask", data['netmask'])) wan.add(Option("gateway", data['gateway'])) dns_list = List("dns") dns2 = data.get("dns2", None) if dns2: dns_list.add(Value(0, dns2)) dns1 = data.get("dns1", None) if dns1: dns_list.add(Value( 1, dns1)) # dns with higher priority should be added last if not dns_list.children: wan.add_removal(dns_list) else: wan.add_replace(dns_list) # IPv6 configuration wan6 = Section("wan6", "interface") network.add(wan6) wan6.add(Option("ifname", "@wan")) wan6.add(Option("proto", data['wan6_proto'])) if data.get("wan6_proto") == WAN6_STATIC: wan6.add(Option("ip6addr", data['ip6addr'])) wan6.add(Option("ip6gw", data['ip6gw'])) wan6.add(Option("ip6prefix", data['ip6prefix'])) else: wan6.add_removal(Option("ip6addr", None)) wan6.add_removal(Option("ip6gw", None)) wan6.add_removal(Option("ip6prefix", None)) if has_smrtd: smrtd = Config("smrtd") uci.add(smrtd) smrt_vlan = data.get("smrt_vlan") use_smrt = data.get("use_smrt", False) wan_if = Section(self.wan_ifname, "interface") smrtd.add(wan_if) wan_if.add(Option("name", self.wan_ifname)) if use_smrt: if not smrt_vlan: # "proprietary" number - and also a common VLAN ID in CZ smrt_vlan = "848" self.wan_ifname += ".%s" % smrt_vlan vpi, vci = data.get("smrt_vpi"), data.get("smrt_vci") connections = List("connections") if vpi and vci: wan_if.add(connections) connections.add( Value(1, "%s %s %s" % (smrt_vlan, vpi, vci))) elif use_smrt: wan_if.add_removal(connections) smrtd_global = Section("global", "global") smrtd.add(smrtd_global) smrtd_global.add(Option("enabled", use_smrt)) # set correct ifname for WAN - must be changed when disabling SMRT wan.add(Option("ifname", self.wan_ifname)) # set interface for ucollect to listen on interface_if_name = None ucollect_interface0 = wan_form.nuci_config.find_child( "uci.ucollect.@interface[0]") if ucollect_interface0: interface_if_name = ucollect_interface0.name ucollect = Config("ucollect") uci.add(ucollect) interface = Section(interface_if_name, "interface", True) ucollect.add(interface) interface.add(Option("ifname", ucollect_ifname)) return "edit_config", uci wan_form.add_callback(wan_form_cb) return wan_form
def get_form(self): ucollect_form = fapi.ForisForm( "ucollect", self.data, filter=filters.create_config_filter("ucollect")) fakes = ucollect_form.add_section( name="fakes", title=_("Emulated services"), description= _("One of uCollect's features is emulation of some commonly abused " "services. If this function is enabled, uCollect is listening for " "incoming connection attempts to these services. Enabling of the " "emulated services has no effect if another service is already " "listening on its default port (port numbers are listed below)." )) SERVICES_OPTIONS = ( ("23tcp", _("Telnet (23/TCP)")), ("2323tcp", _("Telnet - alternative port (2323/TCP)")), ("80tcp", _("HTTP (80/TCP)")), ("3128tcp", _("Squid HTTP proxy (3128/TCP)")), ("8123tcp", _("Polipo HTTP proxy (8123/TCP)")), ("8080tcp", _("HTTP proxy (8080/TCP)")), ) def get_enabled_services(disabled_list): disabled_services = map(lambda value: value.content, disabled_list.children) res = [ x[0] for x in SERVICES_OPTIONS if x[0] not in disabled_services ] return res fakes.add_field(MultiCheckbox, name="services", label=_("Emulated services"), args=SERVICES_OPTIONS, multifield=True, nuci_path="uci.ucollect.fakes.disable", nuci_preproc=get_enabled_services, default=[x[0] for x in SERVICES_OPTIONS]) fakes.add_field( Checkbox, name="log_credentials", label=_("Collect credentials"), hint= _("If this option is enabled, user names and passwords are collected " "and sent to server in addition to the IP address of the client." ), nuci_path="uci.ucollect.fakes.log_credentials", nuci_preproc=parse_uci_bool) def ucollect_form_cb(data): uci = Uci() ucollect = Config("ucollect") uci.add(ucollect) fakes = Section("fakes", "fakes") ucollect.add(fakes) disable = List("disable") disabled_services = [ x[0] for x in SERVICES_OPTIONS if x[0] not in data['services'] ] for i, service in enumerate(disabled_services): disable.add(Value(i, service)) if len(disabled_services): fakes.add_replace(disable) else: fakes.add_removal(disable) fakes.add(Option("log_credentials", data['log_credentials'])) return "edit_config", uci ucollect_form.add_callback(ucollect_form_cb) return ucollect_form
def get_form(self): region_form = fapi.ForisForm( "region", self.data, filter=filters.create_config_filter("system")) timezone = region_form.add_section( name="timezone", title=_(self.userfriendly_title), description=_( "Please select the timezone the router is being operated in. " "Correct setting is required to display the right time and for related functions." )) lang = bottle.request.app.lang def construct_args(items, translation_function=_, key_getter=lambda x: x): """ Helper function that builds args for country/timezone dropdowns. If there's only one item, dropdown should contain only that item. Otherwise the list of items should be prepended by an empty value. :param items: list of filtered TZ data :param translation_function: function that returns displayed choice from TZ data :param key_getter: :return: list of args """ args = localized_sorted( ((key_getter(x), translation_function(x)) for x in items), lang=lang, key=lambda x: x[1]) if len(args) > 1: return [(None, "-" * 16)] + args return args timezone.add_field(Hidden, name="system_name", nuci_path="uci.system.@system[0]", nuci_preproc=lambda val: val.name) regions = localized_sorted(((x, _(x)) for x in tzinfo.regions), lang=lang, key=lambda x: x[1]) timezone.add_field(Dropdown, name="region", label=_("Continent or ocean"), required=True, args=regions, nuci_path="uci.system.@system[0].zonename", nuci_preproc=lambda x: x.value.split("/")[0]) # Get region and offer available countries region = region_form.current_data.get('region') countries = construct_args(tzinfo.countries_in_region(region), lambda x: _(tzinfo.countries[x])) timezone.add_field(Dropdown, name="country", label=_("Country"), required=True, default=None, args=countries, nuci_path="uci.system.@system[0].zonename", nuci_preproc=lambda x: tzinfo.get_country_for_tz(x.value))\ .requires("region") # Get country and offer available timezones country = region_form.current_data.get("country", countries[0][0]) # It's possible that data contain country from the previous request, # in that case fall back to the first item in list of available countries if country not in (x[0] for x in countries): country = countries[0][0] timezones = construct_args(tzinfo.timezones_in_region_and_country( region, country), translation_function=lambda x: _(x[2]), key_getter=lambda x: x[0]) # Offer timezones - but only if a country is selected and is not None (ensured by the # requires() method) timezone.add_field(Dropdown, name="zonename", label=_("Timezone"), required=True, default=None, args=timezones, nuci_path="uci.system.@system[0].zonename")\ .requires("country", lambda x: country and x is not None) def region_form_cb(data): uci = Uci() system = Config("system") uci.add(system) system_section = Section(data['system_name'], "system") system.add(system_section) zonename = data['zonename'] system_section.add(Option("zonename", zonename)) system_section.add( Option("timezone", tzinfo.get_zoneinfo_for_tz(zonename))) return "edit_config", uci region_form.add_callback(region_form_cb) return region_form
def get_form(self): # form definitions pw_form = fapi.ForisForm("password", self.data) pw_main = pw_form.add_section( name="set_password", title=_(self.userfriendly_title), description=_("Set your password for this administration " "interface. The password must be at least 6 " "characters long.")) if self.change: pw_main.add_field(Password, name="old_password", label=_("Current password")) label_pass1 = _("New password") label_pass2 = _("New password (repeat)") else: label_pass1 = _("Password") label_pass2 = _("Password (repeat)") pw_main.add_field(Password, name="password", label=label_pass1, required=True, validators=validators.LenRange(6, 128)) pw_main.add_field(Password, name="password_validation", label=label_pass2, required=True, validators=validators.EqualTo( "password", "password_validation", _("Passwords are not equal."))) pw_main.add_field( Checkbox, name="set_system_pw", label=_("Use the same password for advanced configuration"), hint=_( "Same password would be used for accessing this administration " "interface, for root user in LuCI web interface and for SSH " "login. Use a strong password! (If you choose not to set the " "password for advanced configuration here, you will have the " "option to do so later. Until then, the root account will be " "blocked.)")) def pw_form_cb(data): from beaker.crypto import pbkdf2 if self.change: # if changing password, check the old pw is right first uci_data = client.get(filter=filters.foris_config) password_hash = uci_data.find_child("uci.foris.auth.password") # allow changing the password if password_hash is empty if password_hash: password_hash = password_hash.value # crypt automatically extracts salt and iterations from formatted pw hash if password_hash != pbkdf2.crypt(data['old_password'], salt=password_hash): return "save_result", {'wrong_old_password': True} uci = Uci() foris = Config("foris") uci.add(foris) auth = Section("auth", "config") foris.add(auth) # use 48bit pseudo-random salt internally generated by pbkdf2 new_password_hash = pbkdf2.crypt(data['password'], iterations=1000) auth.add(Option("password", new_password_hash)) if data['set_system_pw'] is True: client.set_password("root", data['password']) return "edit_config", uci pw_form.add_callback(pw_form_cb) return pw_form