mirror of
https://github.com/billz/raspap-webgui.git
synced 2025-12-27 15:34:28 +01:00
Compare commits
154 Commits
3.4.5-test
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e95be333f6 | ||
|
|
ae106497f9 | ||
|
|
018561bdf7 | ||
|
|
5fadbc7ef4 | ||
|
|
ca6b445433 | ||
|
|
00f54d6d7e | ||
|
|
8bd98ed518 | ||
|
|
2e187d79d1 | ||
|
|
01caca9fad | ||
|
|
632f72a5a7 | ||
|
|
b4f9b3d5f4 | ||
|
|
e92329231f | ||
|
|
e7d18d3ad0 | ||
|
|
8cdbfe44fd | ||
|
|
bf822a239c | ||
|
|
dac7723441 | ||
|
|
3211735534 | ||
|
|
15a680934d | ||
|
|
83783ae056 | ||
|
|
1d2a22397e | ||
|
|
206e23213a | ||
|
|
23b964a172 | ||
|
|
c03f81719c | ||
|
|
0a3dbef531 | ||
|
|
3ab66d3e76 | ||
|
|
98b55ad10b | ||
|
|
a25e0f1956 | ||
|
|
ac8a6fe4a8 | ||
|
|
1dde68ff88 | ||
|
|
08d4443c02 | ||
|
|
d0b1b1c8f9 | ||
|
|
fc8508b260 | ||
|
|
8628c8d5b1 | ||
|
|
f0dbb6455b | ||
|
|
c29f45867c | ||
|
|
de9f7548d5 | ||
|
|
955add1b25 | ||
|
|
f34cb43882 | ||
|
|
c08095065e | ||
|
|
b21f9897e7 | ||
|
|
7cb3e62850 | ||
|
|
42a2a299c2 | ||
|
|
0f5768a7dd | ||
|
|
24fd53f8de | ||
|
|
c8047670a2 | ||
|
|
af74dc35f3 | ||
|
|
53e4019e08 | ||
|
|
1afb980ce5 | ||
|
|
1ca57260e1 | ||
|
|
a21ecbfc0e | ||
|
|
cca91dfeab | ||
|
|
681594b8d5 | ||
|
|
d226b70c92 | ||
|
|
a0a3a9ca52 | ||
|
|
f5ade0f444 | ||
|
|
9538bd3f95 | ||
|
|
93ef7c7430 | ||
|
|
94e197ddd7 | ||
|
|
8bf918ce28 | ||
|
|
0d9d1f3319 | ||
|
|
0a668475d8 | ||
|
|
346f774517 | ||
|
|
f9d115bbde | ||
|
|
789cc4f593 | ||
|
|
0938c3d9e9 | ||
|
|
2c63f84862 | ||
|
|
16e52c3b90 | ||
|
|
4c2138d419 | ||
|
|
302fc4f6dc | ||
|
|
bf86a6ef57 | ||
|
|
ce7032c99a | ||
|
|
d86ad11f0a | ||
|
|
2080b0d576 | ||
|
|
53af2ecc03 | ||
|
|
e81b74ab61 | ||
|
|
f547a35312 | ||
|
|
db9ca93bfb | ||
|
|
e120379602 | ||
|
|
4d8cf545af | ||
|
|
8310c26021 | ||
|
|
704b042082 | ||
|
|
79862e65c4 | ||
|
|
391f765beb | ||
|
|
a60ed2e9ba | ||
|
|
21f157778a | ||
|
|
7be38373d8 | ||
|
|
05371b3bb0 | ||
|
|
b76efe1ab7 | ||
|
|
d70158cbe3 | ||
|
|
60d58376f1 | ||
|
|
36e447cd4d | ||
|
|
ee776f2a2e | ||
|
|
49ee0751d0 | ||
|
|
963c533985 | ||
|
|
95229fd1b7 | ||
|
|
b451971c46 | ||
|
|
352008b966 | ||
|
|
efc2dbc136 | ||
|
|
e5c162ddcf | ||
|
|
de188cd8fc | ||
|
|
2e2438b045 | ||
|
|
836444105e | ||
|
|
c5fdff2413 | ||
|
|
e360211ae4 | ||
|
|
2c34fef61f | ||
|
|
4f0f4bb93c | ||
|
|
5b8ecff185 | ||
|
|
22c558a786 | ||
|
|
821aaf646c | ||
|
|
aecf8e6e94 | ||
|
|
cb7ece3d4c | ||
|
|
076c03311b | ||
|
|
4667ce53be | ||
|
|
e7df5691bd | ||
|
|
e1304c7dcb | ||
|
|
936cc15656 | ||
|
|
752d01c8ed | ||
|
|
34f3d59b2c | ||
|
|
a89d571c69 | ||
|
|
0c8e1a310c | ||
|
|
2b61f91ba5 | ||
|
|
3ae73a9728 | ||
|
|
4976d72683 | ||
|
|
58e27eff24 | ||
|
|
f0d5bde1e5 | ||
|
|
1522cfb7e7 | ||
|
|
3bbfcba539 | ||
|
|
504403a5c3 | ||
|
|
256277b85e | ||
|
|
b4de5fa8be | ||
|
|
3d31f7ec9b | ||
|
|
067d488617 | ||
|
|
f75d10bd0e | ||
|
|
9dd6bb8fd6 | ||
|
|
aae31f14ad | ||
|
|
6e4272f325 | ||
|
|
dcf1a8f8f4 | ||
|
|
6a9a2711e0 | ||
|
|
299981eac1 | ||
|
|
6ba5b9e7fd | ||
|
|
9fa2d5a9b1 | ||
|
|
2336d179ae | ||
|
|
9b1912b6d7 | ||
|
|
f6ce051076 | ||
|
|
28733668a2 | ||
|
|
066080643d | ||
|
|
7a2efb68cd | ||
|
|
07612999fa | ||
|
|
e1e7ae4f40 | ||
|
|
6a90070b0e | ||
|
|
de277e7588 | ||
|
|
ab3c4d90da | ||
|
|
d6bcbc7a3f | ||
|
|
988e75a564 |
6
.github/ISSUE_TEMPLATE/issue_form.yml
vendored
6
.github/ISSUE_TEMPLATE/issue_form.yml
vendored
@@ -57,8 +57,10 @@ body:
|
|||||||
- Raspberry Pi OS Lite 64-bit Debian 12 (bookworm)
|
- Raspberry Pi OS Lite 64-bit Debian 12 (bookworm)
|
||||||
- Raspberry Pi OS Lite 32-bit Debian 12 (bookworm)
|
- Raspberry Pi OS Lite 32-bit Debian 12 (bookworm)
|
||||||
- Raspberry Pi OS Desktop 64-bit Debian 12 (bookworm)
|
- Raspberry Pi OS Desktop 64-bit Debian 12 (bookworm)
|
||||||
- Raspberry Pi OS Lite 64-bit Debian 11 (bullseye)
|
- Raspberry Pi OS Lite 64-bit Debian 11 (bullseye)
|
||||||
- Raspberry Pi OS Lite 32-bit Debian 11 (bullseye)
|
- Raspberry Pi OS Lite 32-bit Debian 11 (bullseye)
|
||||||
|
- Kali Linux 2025.3 64-bit
|
||||||
|
- Kali Linux 2025.3 32-bit
|
||||||
- Armbian 23.11 (jammy)
|
- Armbian 23.11 (jammy)
|
||||||
- Debian 12 (bookworm)
|
- Debian 12 (bookworm)
|
||||||
validations:
|
validations:
|
||||||
|
|||||||
59
.github/workflows/update-os-list.yml
vendored
Normal file
59
.github/workflows/update-os-list.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
name: Update OS List for RPi Imager
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["release.yaml"]
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-os-list:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Get release information
|
||||||
|
id: release
|
||||||
|
run: |
|
||||||
|
# Fetch latest release info
|
||||||
|
RESPONSE=$(curl -s "https://api.github.com/repos/${{ github.repository }}/releases/latest")
|
||||||
|
VERSION=$(echo "$RESPONSE" | jq -r '.tag_name')
|
||||||
|
RELEASE_DATE=$(echo "$RESPONSE" | jq -r '.published_at' | cut -d'T' -f1)
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "release_date=$RELEASE_DATE" >> $GITHUB_OUTPUT
|
||||||
|
echo "Found release: $VERSION (published: $RELEASE_DATE)"
|
||||||
|
|
||||||
|
- name: Download release assets and generate OS list
|
||||||
|
env:
|
||||||
|
VERSION: ${{ steps.release.outputs.version }}
|
||||||
|
RELEASE_DATE: ${{ steps.release.outputs.release_date }}
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
cd installers
|
||||||
|
echo "Downloading release assets for $VERSION..."
|
||||||
|
gh release download "$VERSION" \
|
||||||
|
--pattern "raspap-trixie-armhf-lite-*.img.zip" \
|
||||||
|
--pattern "raspap-trixie-arm64-lite-*.img.zip"
|
||||||
|
chmod +x ./generate_os_list.sh
|
||||||
|
./generate_os_list.sh
|
||||||
|
|
||||||
|
- name: Commit and push changes
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add installers/os-sublist-raspap.json
|
||||||
|
if git diff --staged --quiet; then
|
||||||
|
echo "No changes to commit"
|
||||||
|
else
|
||||||
|
git commit -m "Update OS list for RPi Imager (${{ steps.release.outputs.version }})"
|
||||||
|
git push
|
||||||
|
fi
|
||||||
|
|
||||||
43
README.md
43
README.md
@@ -1,5 +1,5 @@
|
|||||||

|

|
||||||
[](https://github.com/raspap/raspap-webgui/releases) [](https://github.com/thibmaek/awesome-raspberry-pi) [](https://github.com/sponsors/RaspAP) [](https://app.travis-ci.com/RaspAP/raspap-webgui) [](https://crowdin.com/project/raspap) [](https://twitter.com/rasp_ap) [](https://reddit.com/r/RaspAP) [](https://discord.gg/KVAsaAR)
|
[](https://github.com/raspap/raspap-webgui/releases) [](https://github.com/thibmaek/awesome-raspberry-pi) [](https://github.com/sponsors/RaspAP) [](https://app.travis-ci.com/RaspAP/raspap-webgui) [](https://crowdin.com/project/raspap) [](https://twitter.com/rasp_ap) [](https://reddit.com/r/RaspAP) [](https://discord.gg/KVAsaAR)
|
||||||
|
|
||||||
RaspAP is feature-rich wireless router software that _just works_ on many popular [Debian-based devices](#supported-operating-systems), including the Raspberry Pi. Our [custom OS images](#pre-built-image), [Quick installer](#quick-installer) and [Docker container](#docker-support) create a known-good default configuration for all current Raspberry Pis with onboard wireless. A fully responsive, mobile-ready interface gives you control over the relevant services and networking options. Advanced DHCP settings, [WireGuard](https://docs.raspap.com/wireguard/), [Tailscale](https://docs.raspap.com/tailscale/) and [OpenVPN](https://docs.raspap.com/openvpn/) support, [SSL certificates](https://docs.raspap.com/ssl/), [ad blocking](#ad-blocking), security audits, [captive portal integration](https://docs.raspap.com/captive/), themes and [multilingual options](https://docs.raspap.com/translations/) are included.
|
RaspAP is feature-rich wireless router software that _just works_ on many popular [Debian-based devices](#supported-operating-systems), including the Raspberry Pi. Our [custom OS images](#pre-built-image), [Quick installer](#quick-installer) and [Docker container](#docker-support) create a known-good default configuration for all current Raspberry Pis with onboard wireless. A fully responsive, mobile-ready interface gives you control over the relevant services and networking options. Advanced DHCP settings, [WireGuard](https://docs.raspap.com/wireguard/), [Tailscale](https://docs.raspap.com/tailscale/) and [OpenVPN](https://docs.raspap.com/openvpn/) support, [SSL certificates](https://docs.raspap.com/ssl/), [ad blocking](#ad-blocking), security audits, [captive portal integration](https://docs.raspap.com/captive/), themes and [multilingual options](https://docs.raspap.com/translations/) are included.
|
||||||
|
|
||||||
@@ -72,12 +72,12 @@ The Quick installer will respond to several [command line arguments](https://doc
|
|||||||
### Initial settings
|
### Initial settings
|
||||||
After completing either of these setup options, the wireless AP network will be configured as follows:
|
After completing either of these setup options, the wireless AP network will be configured as follows:
|
||||||
|
|
||||||
* IP address: 10.3.141.1
|
* IP address: `10.3.141.1`
|
||||||
* Username: admin
|
* Username: `admin`
|
||||||
* Password: secret
|
* Password: `secret`
|
||||||
* DHCP range: 10.3.141.50 — 10.3.141.254
|
* DHCP range: `10.3.141.50` — `10.3.141.254`
|
||||||
* SSID: `raspi-webgui`
|
* SSID: `RaspAP`
|
||||||
* Password: ChangeMe
|
* Password: `ChangeMe`
|
||||||
|
|
||||||
It's _strongly recommended_ that your first post-install action is to change the default admin [authentication](https://docs.raspap.com/authentication/) settings. Thereafter, your AP's [basic settings](https://docs.raspap.com/ap-basics/) and many [advanced options](https://docs.raspap.com/ap-basics#advanced-options) are now ready to be modified by RaspAP.
|
It's _strongly recommended_ that your first post-install action is to change the default admin [authentication](https://docs.raspap.com/authentication/) settings. Thereafter, your AP's [basic settings](https://docs.raspap.com/ap-basics/) and many [advanced options](https://docs.raspap.com/ap-basics#advanced-options) are now ready to be modified by RaspAP.
|
||||||
|
|
||||||
@@ -95,13 +95,13 @@ A tangible side benefit of sponsorship is that **Insiders** are able to help _st
|
|||||||
|
|
||||||
WireGuard® is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be considerably more performant than OpenVPN, and is generally regarded as the most secure, easiest to use, and simplest VPN solution for modern Linux distributions.
|
WireGuard® is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be considerably more performant than OpenVPN, and is generally regarded as the most secure, easiest to use, and simplest VPN solution for modern Linux distributions.
|
||||||
|
|
||||||
WireGuard may be optionally installed by the [Quick Installer](https://docs.raspap.com/quick/). Once this is done, you can manage local (server) settings, create a peer configuration and control the `wg-quick` service with RaspAP.
|
WireGuard is included in the pre-built OS and may be optionally installed by the [Quick Installer](https://docs.raspap.com/quick/). Once this is done, you can manage local (server) settings, create a peer configuration and control the `wg-quick` service with RaspAP.
|
||||||
|
|
||||||
Details are [provided here](https://docs.raspap.com/wireguard/).
|
Details are [provided here](https://docs.raspap.com/wireguard/).
|
||||||
|
|
||||||
## OpenVPN support
|
## OpenVPN support
|
||||||
|
|
||||||
OpenVPN may be optionally installed by the Quick Installer. Once this is done, you can [manage client configurations](https://docs.raspap.com/openvpn/) and the `openvpn-client` service with RaspAP.
|
OpenVPN is included in the pre-built OS and may be optionally installed by the Quick Installer. Once this is done, you can [manage client configurations](https://docs.raspap.com/openvpn/) and the `openvpn-client` service with RaspAP.
|
||||||
|
|
||||||
To configure an OpenVPN client, upload a valid .ovpn file and, optionally, specify your login credentials. RaspAP will store your client configuration and add firewall rules to forward traffic from OpenVPN's `tun0` interface to your configured wireless interface.
|
To configure an OpenVPN client, upload a valid .ovpn file and, optionally, specify your login credentials. RaspAP will store your client configuration and add firewall rules to forward traffic from OpenVPN's `tun0` interface to your configured wireless interface.
|
||||||
|
|
||||||
@@ -114,22 +114,20 @@ Several popular VPN providers include a Linux Command Line Interface (CLI) for i
|
|||||||
See our [VPN provider documentation](https://docs.raspap.com/providers/) for more information.
|
See our [VPN provider documentation](https://docs.raspap.com/providers/) for more information.
|
||||||
|
|
||||||
## Ad Blocking
|
## Ad Blocking
|
||||||
This feature uses DNS blacklisting to block requests for ads, trackers and other undesirable hosts. To enable ad blocking, simply respond to the prompt during the installation. As a beta release, we encourage testing and feedback from users of RaspAP.
|
This feature uses DNS blacklisting to block requests for ads, trackers and other undesirable hosts. Ad blocking is included in the pre-built OS and may be optionally installed by the [Quick Installer](https://docs.raspap.com/quick/). Thereafter, you may choose between several of the best available [blocklist sources](https://docs.raspap.com/features-core/adblock/#blocklist-sources) to suit your needs.
|
||||||
|
|
||||||
Details are [provided here](https://docs.raspap.com/adblock/).
|
Details are [provided here](https://docs.raspap.com/adblock/).
|
||||||
|
|
||||||
## Bridged AP
|
## Bridged AP
|
||||||
By default RaspAP configures a routed AP for your clients to connect to. A bridged AP configuration is also possible. Slide the **Bridged AP mode** toggle under the **Advanced** tab of **Configure hotspot**, then save and restart the hotspot.
|
By default RaspAP configures a routed AP for your clients to connect to. A bridged AP configuration is also possible. Select the **Bridged AP mode** toggle under the **Advanced** tab of **Hotspot**, configure a static IP address for the bridge interface, then save and restart the AP.
|
||||||
|
|
||||||
**Note:** In bridged mode, all routing capabilities are handled by your upstream router. Because your router assigns IP addresses to your device's hotspot and its clients, you might not be able to reach the RaspAP web interface from the default `10.3.141.1` address. Instead use your RPi's hostname followed by `.local` to access the RaspAP web interface. With Raspbian default settings, this should look like `raspberrypi.local`. Alternate methods are [discussed here](https://www.raspberrypi.org/documentation/remote-access/ip-address.md).
|
Details on Bridged AP mode are [provided here](https://docs.raspap.com/bridged/).
|
||||||
|
|
||||||
More information on Bridged AP mode is provided [in our documentation](https://docs.raspap.com/bridged/).
|
|
||||||
|
|
||||||
## Manual installation
|
## Manual installation
|
||||||
Detailed manual setup instructions are provided [on our documentation site](https://docs.raspap.com/manual/).
|
Detailed manual setup instructions are [provided here](https://docs.raspap.com/manual/).
|
||||||
|
|
||||||
## 802.11ac 5GHz support
|
## 802.11ac 5GHz support
|
||||||
RaspAP provides an 802.11ac wireless mode option for supported hardware (currently the RPi 3B+/4 and compatible Orange Pi models) and wireless regulatory domains. See [this](https://docs.raspap.com/ap-basics/#80211ac-5-ghz) for more information.
|
RaspAP provides an 802.11ac wireless mode option for supported hardware (currently the RPi 3B+, 4, 5 and compatible Orange Pi models) and wireless regulatory domains. See [this](https://docs.raspap.com/ap-basics/#80211ac-5-ghz) for more information.
|
||||||
|
|
||||||
## Supported operating systems
|
## Supported operating systems
|
||||||
RaspAP was originally made for Raspbian, but now also installs on the following Debian-based distros.
|
RaspAP was originally made for Raspbian, but now also installs on the following Debian-based distros.
|
||||||
@@ -143,10 +141,13 @@ RaspAP was originally made for Raspbian, but now also installs on the following
|
|||||||
| Raspberry Pi OS Desktop | 64-bit Debian 12 (bookworm) | ARM | Official |
|
| Raspberry Pi OS Desktop | 64-bit Debian 12 (bookworm) | ARM | Official |
|
||||||
| Raspberry Pi OS Lite | 64-bit Debian 11 (bullseye) | ARM | Official |
|
| Raspberry Pi OS Lite | 64-bit Debian 11 (bullseye) | ARM | Official |
|
||||||
| Raspberry Pi OS Lite | 32-bit Debian 11 (bullseye) | ARM | Official |
|
| Raspberry Pi OS Lite | 32-bit Debian 11 (bullseye) | ARM | Official |
|
||||||
|
| Kali Linux | 2025.3 | [ARM 64-bit](https://www.kali.org/get-kali/#kali-arm) | Beta |
|
||||||
|
| Kali Linux | 2025.3 | [ARM 32-bit](https://www.kali.org/get-kali/#kali-arm) | Beta |
|
||||||
|
| Debian 13 | trixie | [ARM](https://raspi.debian.net/tested-images/) | Beta |
|
||||||
|
| Debian 12 | bookworm | [ARM](https://raspi.debian.net/tested-images/) | Beta |
|
||||||
| Armbian | 23.11 (jammy) | ARM | Beta |
|
| Armbian | 23.11 (jammy) | ARM | Beta |
|
||||||
| Debian | 12 (bookworm) | ARM / x86_64 | Beta |
|
|
||||||
|
|
||||||
<img src="https://i.imgur.com/XiAJNKb.png" style="width:480px;" />
|
<img src="https://i.imgur.com/L27nH8f.png" style="width:540px;" />
|
||||||
|
|
||||||
You are also encouraged to use RaspAP's community-led [Docker container](#docker-support). Please note that "supported" is not a guarantee. If you are able to improve support for your preferred distro, we encourage you to [actively contribute](#how-to-contribute) to the project.
|
You are also encouraged to use RaspAP's community-led [Docker container](#docker-support). Please note that "supported" is not a guarantee. If you are able to improve support for your preferred distro, we encourage you to [actively contribute](#how-to-contribute) to the project.
|
||||||
|
|
||||||
@@ -164,8 +165,6 @@ curl -sL https://install.raspap.com | bash -s -- --cert
|
|||||||
More information on SSL certificates and HTTPS support is available [in our documentation](https://docs.raspap.com/ssl/).
|
More information on SSL certificates and HTTPS support is available [in our documentation](https://docs.raspap.com/ssl/).
|
||||||
|
|
||||||
## Docker support
|
## Docker support
|
||||||
<img src="https://github.com/RaspAP/raspap-webgui/assets/229399/dc40dfc4-e9b8-405f-8ffb-6c5f88482b8e" width="450">
|
|
||||||
|
|
||||||
As an alternative to the [Quick installer](#quick-installer), RaspAP may be run in an isolated, portable [Docker container](https://docs.raspap.com/docker/).
|
As an alternative to the [Quick installer](#quick-installer), RaspAP may be run in an isolated, portable [Docker container](https://docs.raspap.com/docker/).
|
||||||
|
|
||||||
See the [RaspAP-docker repo](https://github.com/RaspAP/raspap-docker/) for more information.
|
See the [RaspAP-docker repo](https://github.com/RaspAP/raspap-docker/) for more information.
|
||||||
@@ -174,7 +173,9 @@ See the [RaspAP-docker repo](https://github.com/RaspAP/raspap-docker/) for more
|
|||||||
RaspAP's integrated `PluginManager` provides a framework for developers to create custom plugins. To facilitate this, a `SamplePlugin` [repository](https://github.com/RaspAP/SamplePlugin) is available to get developers started on the right track. If you'd like to develop your own plugin for RaspAP, see the [documentation](https://docs.raspap.com/custom-plugins/) or get started right away by forking the [SamplePlugin](https://github.com/RaspAP/SamplePlugin).
|
RaspAP's integrated `PluginManager` provides a framework for developers to create custom plugins. To facilitate this, a `SamplePlugin` [repository](https://github.com/RaspAP/SamplePlugin) is available to get developers started on the right track. If you'd like to develop your own plugin for RaspAP, see the [documentation](https://docs.raspap.com/custom-plugins/) or get started right away by forking the [SamplePlugin](https://github.com/RaspAP/SamplePlugin).
|
||||||
|
|
||||||
## Multilingual support
|
## Multilingual support
|
||||||
RaspAP uses [GNU Gettext](https://www.gnu.org/software/gettext/) to manage multilingual messages. In order to use RaspAP with one of our supported translations, you must configure a corresponding language package on your RPi. To list languages currently installed on your system, use `locale -a` at the shell prompt. To generate new locales, run `sudo dpkg-reconfigure locales` and select any other desired locales. Details are provided on our [documentation site](https://docs.raspap.com/translations/).
|
RaspAP uses [GNU Gettext](https://www.gnu.org/software/gettext/) to manage multilingual messages. Our pre-built OS includes the `locales-all` package, eliminating the need to manually generate locales.
|
||||||
|
|
||||||
|
If you're using the Quick Installer or Manual setup methods, you must configure a corresponding language package for your system. To list languages currently installed on your system, use `locale -a` at the shell prompt. To generate new locales, run `sudo dpkg-reconfigure locales` and select any other desired locales. Details are provided [here](https://docs.raspap.com/translations/).
|
||||||
|
|
||||||
See this list of [supported languages](https://docs.raspap.com/translations/#supported-languages) that are actively maintained by volunteer translators. If your language is not supported, why not [contribute a translation](https://docs.raspap.com/translations/#contributing-to-a-translation)? Contributors will receive credit as the original translators.
|
See this list of [supported languages](https://docs.raspap.com/translations/#supported-languages) that are actively maintained by volunteer translators. If your language is not supported, why not [contribute a translation](https://docs.raspap.com/translations/#contributing-to-a-translation)? Contributors will receive credit as the original translators.
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ foreach ($hostapdconfig as $hostapdconfigline) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$arrLine = explode("=", $hostapdconfigline);
|
$arrLine = explode("=", $hostapdconfigline);
|
||||||
$arrConfig[$arrLine[0]]=$arrLine[1];
|
if (count($arrLine) >= 2) {
|
||||||
|
$arrConfig[$arrLine[0]]=$arrLine[1];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
$channel = intval($arrConfig['channel']);
|
$channel = intval($arrConfig['channel']);
|
||||||
echo json_encode($channel);
|
echo json_encode($channel);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
header("Content-Type: image/svg+xml");
|
header("Content-Type: image/svg+xml");
|
||||||
|
|
||||||
|
require_once '../../includes/functions.php';
|
||||||
|
$color = getColorOpt();
|
||||||
$showDevice1 = isset($_GET['device-1']);
|
$showDevice1 = isset($_GET['device-1']);
|
||||||
$showOut = isset($_GET['out']);
|
$showOut = isset($_GET['out']);
|
||||||
$showDevice2 = isset($_GET['device-2']);
|
$showDevice2 = isset($_GET['device-2']);
|
||||||
@@ -12,33 +15,33 @@ $showDevice2 = isset($_GET['device-2']);
|
|||||||
<?php if ($showDevice2): ?>
|
<?php if ($showDevice2): ?>
|
||||||
<line id="joint-device-2" y1="-0.75" x2="154" y2="-0.75"
|
<line id="joint-device-2" y1="-0.75" x2="154" y2="-0.75"
|
||||||
transform="matrix(4.37114e-08 1 1 -4.37114e-08 114 297)"
|
transform="matrix(4.37114e-08 1 1 -4.37114e-08 114 297)"
|
||||||
stroke="#008281" stroke-width="4"/>
|
stroke="<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>" stroke-width="4"/>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ($showDevice1): ?>
|
<?php if ($showDevice1): ?>
|
||||||
<line id="joint-device-1" style="display: inline;"
|
<line id="joint-device-1" style="display: inline;"
|
||||||
y1="-0.75" x2="154" y2="-0.75"
|
y1="-0.75" x2="154" y2="-0.75"
|
||||||
transform="matrix(4.37114e-08 1 1 -4.37114e-08 114 144)"
|
transform="matrix(4.37114e-08 1 1 -4.37114e-08 114 144)"
|
||||||
stroke="#008281" stroke-width="4"/>
|
stroke="<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>" stroke-width="4"/>
|
||||||
|
|
||||||
<line id="device-1" style="display: inline;"
|
<line id="device-1" style="display: inline;"
|
||||||
y1="-0.75" x2="113.231" y2="-0.75"
|
y1="-0.75" x2="113.231" y2="-0.75"
|
||||||
transform="matrix(1 8.74228e-08 8.74228e-08 -1 114 144)"
|
transform="matrix(1 8.74228e-08 8.74228e-08 -1 114 144)"
|
||||||
stroke="#008281" stroke-width="4"/>
|
stroke="<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>" stroke-width="4"/>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ($showOut): ?>
|
<?php if ($showOut): ?>
|
||||||
<line id="out" style="display: inline;"
|
<line id="out" style="display: inline;"
|
||||||
y1="-0.75" x2="113.231" y2="-0.75"
|
y1="-0.75" x2="113.231" y2="-0.75"
|
||||||
transform="matrix(1 8.74228e-08 8.74228e-08 -1 -0.000305176 297)"
|
transform="matrix(1 8.74228e-08 8.74228e-08 -1 -0.000305176 297)"
|
||||||
stroke="#008281" stroke-width="4"/>
|
stroke="<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>" stroke-width="4"/>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ($showDevice2): ?>
|
<?php if ($showDevice2): ?>
|
||||||
<line id="device-2" style="display: inline;"
|
<line id="device-2" style="display: inline;"
|
||||||
y1="-0.75" x2="113.231" y2="-0.75"
|
y1="-0.75" x2="113.231" y2="-0.75"
|
||||||
transform="matrix(1 8.74228e-08 8.74228e-08 -1 113 450)"
|
transform="matrix(1 8.74228e-08 8.74228e-08 -1 113 450)"
|
||||||
stroke="#008281" stroke-width="4"/>
|
stroke="<?php echo htmlspecialchars($color, ENT_QUOTES, 'UTF-8'); ?>" stroke-width="4"/>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ if (!isset($_SERVER['HTTP_REFERER'])) {
|
|||||||
|
|
||||||
$hostapd = parse_ini_file(RASPI_HOSTAPD_CONFIG, false, INI_SCANNER_RAW);
|
$hostapd = parse_ini_file(RASPI_HOSTAPD_CONFIG, false, INI_SCANNER_RAW);
|
||||||
|
|
||||||
|
// handle parse failure
|
||||||
|
if ($hostapd === false) {
|
||||||
|
header('HTTP/1.0 500 Internal Server Error');
|
||||||
|
exit('Error: Unable to parse hostapd configuration');
|
||||||
|
}
|
||||||
|
|
||||||
// assume WPA encryption and get the passphrase
|
// assume WPA encryption and get the passphrase
|
||||||
$type = "WPA";
|
$type = "WPA";
|
||||||
$password = isset($hostapd['wpa_psk']) ? $hostapd['wpa_psk'] : $hostapd['wpa_passphrase'];
|
$password = isset($hostapd['wpa_psk']) ? $hostapd['wpa_psk'] : $hostapd['wpa_passphrase'];
|
||||||
|
|||||||
@@ -381,12 +381,15 @@ function loadChannelSelect(selected) {
|
|||||||
// Map selected hw_mode to available channels
|
// Map selected hw_mode to available channels
|
||||||
if (hw_mode === 'a') {
|
if (hw_mode === 'a') {
|
||||||
selectableChannels = data.filter(item => item.MHz.toString().startsWith('5'));
|
selectableChannels = data.filter(item => item.MHz.toString().startsWith('5'));
|
||||||
} else if (hw_mode !== 'ac') {
|
|
||||||
selectableChannels = data.filter(item => item.MHz.toString().startsWith('24'));
|
|
||||||
} else if (hw_mode === 'b') {
|
|
||||||
selectableChannels = data.filter(item => item.MHz.toString().startsWith('24'));
|
|
||||||
} else if (hw_mode === 'ac') {
|
} else if (hw_mode === 'ac') {
|
||||||
selectableChannels = data.filter(item => item.MHz.toString().startsWith('5'));
|
selectableChannels = data.filter(item => item.MHz.toString().startsWith('5'));
|
||||||
|
} else if (hw_mode === 'ax') {
|
||||||
|
selectableChannels = data.filter(item => item.MHz.toString().startsWith('5'));
|
||||||
|
} else if (hw_mode === 'be') {
|
||||||
|
selectableChannels = data.filter(item => item.MHz.toString().startsWith('5'));
|
||||||
|
} else {
|
||||||
|
// hw_mode 'b', 'g', or default to 2.4GHz
|
||||||
|
selectableChannels = data.filter(item => item.MHz.toString().startsWith('24'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If selected channel doeesn't exist in allowed channels, set default or null (unsupported)
|
// If selected channel doeesn't exist in allowed channels, set default or null (unsupported)
|
||||||
|
|||||||
@@ -457,6 +457,36 @@ function setDhcpFieldsDisabled() {
|
|||||||
$('#txtmetric').prop('disabled', true);
|
$('#txtmetric').prop('disabled', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const dhcpCheckbox = document.getElementById('dhcp-iface');
|
||||||
|
const rangeStart = document.getElementById('txtrangestart');
|
||||||
|
const rangeEnd = document.getElementById('txtrangeend');
|
||||||
|
const leaseTime = document.getElementById('txtrangeleasetime');
|
||||||
|
|
||||||
|
function updateRequiredFields() {
|
||||||
|
const isChecked = dhcpCheckbox.checked === true;
|
||||||
|
|
||||||
|
if (isChecked) {
|
||||||
|
rangeStart.setAttribute('required', 'required');
|
||||||
|
rangeEnd.setAttribute('required', 'required');
|
||||||
|
leaseTime.setAttribute('required', 'required');
|
||||||
|
} else {
|
||||||
|
rangeStart.removeAttribute('required');
|
||||||
|
rangeEnd.removeAttribute('required');
|
||||||
|
leaseTime.removeAttribute('required');
|
||||||
|
|
||||||
|
rangeStart.classList.remove('is-invalid', 'is-valid');
|
||||||
|
rangeEnd.classList.remove('is-invalid', 'is-valid');
|
||||||
|
leaseTime.classList.remove('is-invalid', 'is-valid');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set initial state
|
||||||
|
updateRequiredFields();
|
||||||
|
setTimeout(updateRequiredFields, 100);
|
||||||
|
dhcpCheckbox.addEventListener('change', updateRequiredFields);
|
||||||
|
});
|
||||||
|
|
||||||
// Static Array method
|
// Static Array method
|
||||||
Array.range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);
|
Array.range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ define('RASPI_ADBLOCK_CONFIG', RASPI_DNSMASQ_PREFIX.'adblock.conf');
|
|||||||
define('RASPI_HOSTAPD_CONFIG', '/etc/hostapd/hostapd.conf');
|
define('RASPI_HOSTAPD_CONFIG', '/etc/hostapd/hostapd.conf');
|
||||||
define('RASPI_DHCPCD_CONFIG', '/etc/dhcpcd.conf');
|
define('RASPI_DHCPCD_CONFIG', '/etc/dhcpcd.conf');
|
||||||
define('RASPI_DHCPCD_LOG', '/var/log/dnsmasq.log');
|
define('RASPI_DHCPCD_LOG', '/var/log/dnsmasq.log');
|
||||||
define('RASPI_HOSTAPD_LOG', '/tmp/hostapd.log');
|
|
||||||
define('RASPI_WPA_SUPPLICANT_CONFIG', '/etc/wpa_supplicant/wpa_supplicant.conf');
|
define('RASPI_WPA_SUPPLICANT_CONFIG', '/etc/wpa_supplicant/wpa_supplicant.conf');
|
||||||
define('RASPI_HOSTAPD_CTRL_INTERFACE', '/var/run/hostapd');
|
define('RASPI_HOSTAPD_CTRL_INTERFACE', '/var/run/hostapd');
|
||||||
define('RASPI_WPA_CTRL_INTERFACE', '/var/run/wpa_supplicant');
|
define('RASPI_WPA_CTRL_INTERFACE', '/var/run/wpa_supplicant');
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"# N",
|
"# N",
|
||||||
"ieee80211n=1",
|
"ieee80211n=1",
|
||||||
"require_ht=1",
|
"require_ht=1",
|
||||||
"ht_capab=[MAX-AMSDU-3839][HT40+][SHORT-GI-20][SHORT-GI-40][DSSS_CCK-40]",
|
"ht_capab=[MAX-AMSDU-3839][{HT40_DIR}][SHORT-GI-20][SHORT-GI-40][DSSS_CCK-40]",
|
||||||
"# AC",
|
"# AC",
|
||||||
"ieee80211ac=1",
|
"ieee80211ac=1",
|
||||||
"require_vht=1",
|
"require_vht=1",
|
||||||
@@ -25,6 +25,148 @@
|
|||||||
"vht_oper_centr_freq_seg0_idx={VHT_FREQ_IDX}"
|
"vht_oper_centr_freq_seg0_idx={VHT_FREQ_IDX}"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"ax": {
|
||||||
|
"settings": [
|
||||||
|
"# Basic settings",
|
||||||
|
"hw_mode=a",
|
||||||
|
"# Enable 802.11n/ac",
|
||||||
|
"ieee80211d=1",
|
||||||
|
"ieee80211n=1",
|
||||||
|
"ieee80211ac=1",
|
||||||
|
"# Enable 802.11ax",
|
||||||
|
"ieee80211ax=1",
|
||||||
|
"# HE 802.11ax capabilities",
|
||||||
|
"he_su_beamformer=1",
|
||||||
|
"he_su_beamformee=1",
|
||||||
|
"he_mu_beamformer=1",
|
||||||
|
"# BSS color for spatial reuse, value 1-63",
|
||||||
|
"he_bss_color=1",
|
||||||
|
"he_oper_chwidth=1",
|
||||||
|
"# HE/VHT channel widths",
|
||||||
|
"he_oper_chwidth=1",
|
||||||
|
"vht_oper_chwidth=1",
|
||||||
|
"he_oper_centr_freq_seg0_idx={HE_FREQ_IDX}",
|
||||||
|
"vht_oper_centr_freq_seg0_idx={VHT_FREQ_IDX}",
|
||||||
|
"# HT 802.11n capabilities",
|
||||||
|
"ht_capab=[{HT40_DIR}][LDPC][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][MAX-AMSDU-7935]",
|
||||||
|
"# VHT capabilities 802.11ac",
|
||||||
|
"vht_capab=[RXLDPC][SHORT-GI-80][TX-STBC-2BY1][RX-STBC-1][MAX-MPDU-11454][MAX-A-MPDU-LEN-EXP7]",
|
||||||
|
"# WMM/QoS",
|
||||||
|
"wmm_enabled=1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"be": {
|
||||||
|
"settings": [
|
||||||
|
"# Basic settings",
|
||||||
|
"hw_mode=a",
|
||||||
|
"# Enable 802.11n/ac/ax",
|
||||||
|
"ieee80211n=1",
|
||||||
|
"ieee80211ac=1",
|
||||||
|
"ieee80211ax=1",
|
||||||
|
"# Maximum MPDU Length of HE 6 GHz band capabilities.",
|
||||||
|
"# Indicates maximum MPDU length",
|
||||||
|
"# 0 = 3895 octets",
|
||||||
|
"# 1 = 7991 octets",
|
||||||
|
"# 2 = 11454 octets",
|
||||||
|
"he_6ghz_max_mpdu=2",
|
||||||
|
"# Maximum A-MPDU Length Exponent of HE 6 GHz band capabilities. Indicates",
|
||||||
|
"# the maximum length of A-MPDU pre-EOF padding that # the STA can receive.",
|
||||||
|
"# This field is an integer in the range of 0 to 7. The length defined by",
|
||||||
|
"# this field is equal to 2 pow -1",
|
||||||
|
"# octets",
|
||||||
|
"# 0 = AMPDU length of 8k",
|
||||||
|
"# 1 = AMPDU length of 16k",
|
||||||
|
"# 2 = AMPDU length of 32k",
|
||||||
|
"# 3 = AMPDU length of 65k",
|
||||||
|
"# 4 = AMPDU length of 131k",
|
||||||
|
"# 5 = AMPDU length of 262kv",
|
||||||
|
"# 6 = AMPDU length of 524k",
|
||||||
|
"# 7 = AMPDU length of 1048k",
|
||||||
|
"he_6ghz_max_ampdu_len_exp=7",
|
||||||
|
"# 0 = Indoor AP",
|
||||||
|
"# 1 = Standard power AP",
|
||||||
|
"# 2 = Very low power AP",
|
||||||
|
"# 3 = Indoor enabled AP",
|
||||||
|
"# 4 = Indoor standard power AP",
|
||||||
|
"he_6ghz_reg_pwr_type=0",
|
||||||
|
"# HE beamforming capabilities",
|
||||||
|
"he_su_beamformer=1",
|
||||||
|
"he_su_beamformee=1",
|
||||||
|
"he_mu_beamformer=1",
|
||||||
|
"he_mu_edca_qos_info_param_count=0",
|
||||||
|
"he_mu_edca_qos_info_q_ack=0",
|
||||||
|
"he_mu_edca_qos_info_queue_request=0",
|
||||||
|
"he_mu_edca_qos_info_txop_request=0",
|
||||||
|
"he_mu_edca_ac_be_aifsn=8",
|
||||||
|
"he_mu_edca_ac_be_aci=0",
|
||||||
|
"he_mu_edca_ac_be_ecwmin=9",
|
||||||
|
"he_mu_edca_ac_be_ecwmax=10",
|
||||||
|
"he_mu_edca_ac_be_timer=255",
|
||||||
|
"he_mu_edca_ac_bk_aifsn=15",
|
||||||
|
"he_mu_edca_ac_bk_aci=1",
|
||||||
|
"he_mu_edca_ac_bk_ecwmin=9",
|
||||||
|
"he_mu_edca_ac_bk_ecwmax=10",
|
||||||
|
"he_mu_edca_ac_bk_timer=255",
|
||||||
|
"he_mu_edca_ac_vi_ecwmin=5",
|
||||||
|
"he_mu_edca_ac_vi_ecwmax=7",
|
||||||
|
"he_mu_edca_ac_vi_aifsn=5",
|
||||||
|
"he_mu_edca_ac_vi_aci=2",
|
||||||
|
"he_mu_edca_ac_vi_timer=255",
|
||||||
|
"he_mu_edca_ac_vo_aifsn=5",
|
||||||
|
"he_mu_edca_ac_vo_aci=3",
|
||||||
|
"he_mu_edca_ac_vo_ecwmin=5",
|
||||||
|
"he_mu_edca_ac_vo_ecwmax=7",
|
||||||
|
"he_mu_edca_ac_vo_timer=255",
|
||||||
|
"# EHT beamforming capabilities",
|
||||||
|
"eht_su_beamformer=0",
|
||||||
|
"eht_su_beamformee=0",
|
||||||
|
"eht_mu_beamformer=0",
|
||||||
|
"# used by clients to discern the source of interference",
|
||||||
|
"# each AP in your area needs to use a different number",
|
||||||
|
"# allowed: 1-63",
|
||||||
|
"he_bss_color=37",
|
||||||
|
"# 160 MHz for HE",
|
||||||
|
"he_oper_chwidth=2",
|
||||||
|
"he_oper_centr_freq_seg0_idx={HE_FREQ_IDX}",
|
||||||
|
"# IEEE 802.11be WiFi 7 configuration",
|
||||||
|
"ieee80211be=1",
|
||||||
|
"# EHT configuration",
|
||||||
|
"eht_su_beamformer=1",
|
||||||
|
"eht_su_beamformee=1",
|
||||||
|
"eht_mu_beamformer=1",
|
||||||
|
"# EHT operating channel information; see matching he_* parameters for details.",
|
||||||
|
"# The field eht_oper_centr_freq_seg0_idx field is used to indicate center",
|
||||||
|
"# frequency of 40, 80, and 160 MHz bandwidth operation.",
|
||||||
|
"# In the 6 GHz band, eht_oper_chwidth is ignored and the channel width is",
|
||||||
|
"# derived from the configured operating class IEEE P802.11be/D1.5,",
|
||||||
|
"# Annex E.1 - Country information and operating classes.",
|
||||||
|
"# Channel width 0 = 40 MHz, 1 = 80 Mhz, 2 = 160 Mhz",
|
||||||
|
"eht_oper_chwidth=2",
|
||||||
|
"eht_oper_centr_freq_seg0_idx={VHT_FREQ_IDX}",
|
||||||
|
"# VHT operation parameters",
|
||||||
|
"vht_oper_chwidth=2",
|
||||||
|
"vht_oper_centr_freq_seg0_idx={VHT_FREQ_IDX}",
|
||||||
|
"vht_capab=[MAX-MPDU-11454][RXLDPC][SHORT-GI-80][SHORT-GI-160][TX-STBC-2BY1][RX-STBC-1][SU-BEAMFORMER][SU-BEAMFORMEE][MU-BEAMFORMER][MU-BEAMFORMEE]",
|
||||||
|
"# WMM configuration",
|
||||||
|
"wmm_enabled=1",
|
||||||
|
"wmm_ac_bk_cwmin=4",
|
||||||
|
"wmm_ac_bk_cwmax=10",
|
||||||
|
"wmm_ac_bk_aifs=7",
|
||||||
|
"wmm_ac_bk_txop_limit=0",
|
||||||
|
"wmm_ac_be_aifs=3",
|
||||||
|
"wmm_ac_be_cwmin=4",
|
||||||
|
"wmm_ac_be_cwmax=10",
|
||||||
|
"wmm_ac_be_txop_limit=0",
|
||||||
|
"wmm_ac_vi_aifs=2",
|
||||||
|
"wmm_ac_vi_cwmin=3",
|
||||||
|
"wmm_ac_vi_cwmax=4",
|
||||||
|
"wmm_ac_vi_txop_limit=94",
|
||||||
|
"wmm_ac_vo_aifs=2",
|
||||||
|
"wmm_ac_vo_cwmin=2",
|
||||||
|
"wmm_ac_vo_cwmax=3",
|
||||||
|
"wmm_ac_vo_txop_limit=47"
|
||||||
|
]
|
||||||
|
},
|
||||||
"g": {
|
"g": {
|
||||||
"settings": [
|
"settings": [
|
||||||
"hw_mode=g",
|
"hw_mode=g",
|
||||||
|
|||||||
152
includes/configure_client.php
Executable file → Normal file
152
includes/configure_client.php
Executable file → Normal file
@@ -3,7 +3,7 @@
|
|||||||
use RaspAP\Networking\Hotspot\WiFiManager;
|
use RaspAP\Networking\Hotspot\WiFiManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* WiFi client configuration page handler
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function DisplayWPAConfig()
|
function DisplayWPAConfig()
|
||||||
@@ -16,17 +16,15 @@ function DisplayWPAConfig()
|
|||||||
$wifi->knownWifiStations($networks);
|
$wifi->knownWifiStations($networks);
|
||||||
$wifi->setKnownStationsWPA($networks);
|
$wifi->setKnownStationsWPA($networks);
|
||||||
|
|
||||||
$iface = escapeshellarg($_SESSION['wifi_client_interface']);
|
$clientInterface = $_SESSION['wifi_client_interface'];
|
||||||
|
|
||||||
if (isset($_POST['connect'])) {
|
if (isset($_POST['connect'])) {
|
||||||
$netid = intval($_POST['connect']);
|
$netid = intval($_POST['connect']);
|
||||||
$cmd = "sudo wpa_cli -i $iface select_network $netid";
|
|
||||||
$return = shell_exec($cmd);
|
if ($wifi->connectToNetwork($clientInterface, $netid)) {
|
||||||
sleep(2);
|
|
||||||
if (trim($return) == "FAIL") {
|
|
||||||
$status->addMessage('WPA command line client returned failure. Check your adapter.', 'danger');
|
|
||||||
} else {
|
|
||||||
$status->addMessage('New network selected', 'success');
|
$status->addMessage('New network selected', 'success');
|
||||||
|
} else {
|
||||||
|
$status->addMessage('WPA command line client returned failure. Check your adapter.', 'danger');
|
||||||
}
|
}
|
||||||
} elseif (isset($_POST['wpa_reinit'])) {
|
} elseif (isset($_POST['wpa_reinit'])) {
|
||||||
$status->addMessage('Attempting to reinitialize wpa_supplicant', 'warning');
|
$status->addMessage('Attempting to reinitialize wpa_supplicant', 'warning');
|
||||||
@@ -34,120 +32,52 @@ function DisplayWPAConfig()
|
|||||||
$result = $wifi->reinitializeWPA($force_remove);
|
$result = $wifi->reinitializeWPA($force_remove);
|
||||||
} elseif (isset($_POST['client_settings'])) {
|
} elseif (isset($_POST['client_settings'])) {
|
||||||
$tmp_networks = $networks;
|
$tmp_networks = $networks;
|
||||||
if ($wpa_file = fopen('/tmp/wifidata', 'w')) {
|
|
||||||
fwrite($wpa_file, 'ctrl_interface=DIR=' . RASPI_WPA_CTRL_INTERFACE . ' GROUP=netdev' . PHP_EOL);
|
|
||||||
fwrite($wpa_file, 'update_config=1' . PHP_EOL);
|
|
||||||
|
|
||||||
foreach (array_keys($_POST) as $post) {
|
foreach (array_keys($_POST) as $post) {
|
||||||
|
|
||||||
if (preg_match('/delete(\d+)/', $post, $post_match)) {
|
if (preg_match('/delete(\d+)/', $post, $post_match)) {
|
||||||
$network = $tmp_networks[$_POST['ssid' . $post_match[1]]];
|
$network = $tmp_networks[$_POST['ssid' . $post_match[1]]];
|
||||||
$netid = $network['index'];
|
$netid = $network['index'];
|
||||||
exec('sudo wpa_cli -i ' . $iface . ' disconnect ' . $netid);
|
$wifi->deleteNetwork($clientInterface, $netid);
|
||||||
exec('sudo wpa_cli -i ' . $iface . ' remove_network ' . $netid);
|
unset($tmp_networks[$_POST['ssid' . $post_match[1]]]);
|
||||||
unset($tmp_networks[$_POST['ssid' . $post_match[1]]]);
|
} elseif (preg_match('/disconnect(\d+)/', $post, $post_match)) {
|
||||||
|
$network = $tmp_networks[$_POST['ssid' . $post_match[1]]];
|
||||||
|
$netid = $network['index'];
|
||||||
|
$wifi->disconnectNetwork($clientInterface, $netid);
|
||||||
|
} elseif (preg_match('/update(\d+)/', $post, $post_match)) {
|
||||||
|
// NB, multiple protocols are separated with a forward slash ('/')
|
||||||
|
$protocol = $_POST['protocol' . $post_match[1]] === $wifi::SECURITY_OPEN ? $wifi::SECURITY_OPEN : 'WPA';
|
||||||
|
$tmp_networks[$_POST['ssid' . $post_match[1]]] = array(
|
||||||
|
'protocol' => $protocol,
|
||||||
|
'passphrase' => $_POST['passphrase' . $post_match[1]] ?? '',
|
||||||
|
'configured' => true
|
||||||
|
);
|
||||||
|
if (array_key_exists('priority' . $post_match[1], $_POST)) {
|
||||||
|
$tmp_networks[$_POST['ssid' . $post_match[1]]]['priority'] = $_POST['priority' . $post_match[1]];
|
||||||
|
}
|
||||||
|
$network = $tmp_networks[$_POST['ssid' . $post_match[1]]];
|
||||||
|
|
||||||
} elseif (preg_match('/update(\d+)/', $post, $post_match)) {
|
$ssid = $_POST['ssid' . $post_match[1]];
|
||||||
// NB, multiple protocols are separated with a forward slash ('/')
|
$passphrase = $_POST['passphrase' . $post_match[1]] ?? '';
|
||||||
$tmp_networks[$_POST['ssid' . $post_match[1]]] = array(
|
|
||||||
'protocol' => ( $_POST['protocol' . $post_match[1]] === 'Open' ? 'Open' : 'WPA' ),
|
$netid = $wifi->updateNetwork($clientInterface, $ssid, $passphrase, $protocol);
|
||||||
'passphrase' => $_POST['passphrase' . $post_match[1]],
|
if ($netid === null) {
|
||||||
'configured' => true
|
$status->addMessage('Unable to add network with WPA command line client', 'warning');
|
||||||
);
|
|
||||||
if (array_key_exists('priority' . $post_match[1], $_POST)) {
|
|
||||||
$tmp_networks[$_POST['ssid' . $post_match[1]]]['priority'] = $_POST['priority' . $post_match[1]];
|
|
||||||
}
|
|
||||||
$network = $tmp_networks[$_POST['ssid' . $post_match[1]]];
|
|
||||||
$ssid = escapeshellarg('"'.$_POST['ssid' . $post_match[1]].'"');
|
|
||||||
$psk = escapeshellarg('"'.$_POST['passphrase' . $post_match[1]].'"');
|
|
||||||
$netid = trim(shell_exec("sudo wpa_cli -i $iface add_network"));
|
|
||||||
if (isset($netid)) {
|
|
||||||
$commands = [
|
|
||||||
"sudo wpa_cli -i $iface set_network $netid ssid $ssid",
|
|
||||||
"sudo wpa_cli -i $iface set_network $netid psk $psk",
|
|
||||||
"sudo wpa_cli -i $iface enable_network $netid"
|
|
||||||
];
|
|
||||||
foreach ($commands as $cmd) {
|
|
||||||
exec($cmd);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$status->addMessage('Unable to add network with WPA command line client', 'warning');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$ok = true;
|
$result = $wifi->writeWpaSupplicant($tmp_networks, $clientInterface);
|
||||||
foreach ($tmp_networks as $ssid => $network) {
|
|
||||||
if ($network['protocol'] === 'Open') {
|
|
||||||
fwrite($wpa_file, "network={".PHP_EOL);
|
|
||||||
fwrite($wpa_file, "\tssid=\"".$ssid."\"".PHP_EOL);
|
|
||||||
fwrite($wpa_file, "\tkey_mgmt=NONE".PHP_EOL);
|
|
||||||
fwrite($wpa_file, "\tscan_ssid=1".PHP_EOL);
|
|
||||||
if (array_key_exists('priority', $network)) {
|
|
||||||
fwrite($wpa_file, "\tpriority=".$network['priority'].PHP_EOL);
|
|
||||||
}
|
|
||||||
fwrite($wpa_file, "}".PHP_EOL);
|
|
||||||
} else {
|
|
||||||
if (strlen($network['passphrase']) >=8 && strlen($network['passphrase']) <= 63) {
|
|
||||||
unset($wpa_passphrase);
|
|
||||||
unset($line);
|
|
||||||
exec('wpa_passphrase '. $wifi->ssid2utf8( escapeshellarg($ssid) ) . ' ' . escapeshellarg($network['passphrase']), $wpa_passphrase);
|
|
||||||
foreach ($wpa_passphrase as $line) {
|
|
||||||
if (preg_match('/^\s*}\s*$/', $line)) {
|
|
||||||
if (array_key_exists('priority', $network)) {
|
|
||||||
fwrite($wpa_file, "\tpriority=".$network['priority'].PHP_EOL);
|
|
||||||
}
|
|
||||||
fwrite($wpa_file, $line.PHP_EOL);
|
|
||||||
} else {
|
|
||||||
if ( preg_match('/\\\\x[0-9A-Fa-f]{2}/',$ssid) && strpos($line, "ssid=\"") !== false ) {
|
|
||||||
fwrite($wpa_file, "\tssid=P\"".$ssid."\"".PHP_EOL);
|
|
||||||
} else {
|
|
||||||
fwrite($wpa_file, $line.PHP_EOL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} elseif (strlen($network['passphrase']) == 0 && strlen($network['passkey']) == 64) {
|
|
||||||
$line = "\tpsk=" . $network['passkey'];
|
|
||||||
fwrite($wpa_file, "network={".PHP_EOL);
|
|
||||||
fwrite($wpa_file, "\tssid=\"".$ssid."\"".PHP_EOL);
|
|
||||||
fwrite($wpa_file, $line.PHP_EOL);
|
|
||||||
if (array_key_exists('priority', $network)) {
|
|
||||||
fwrite($wpa_file, "\tpriority=".$network['priority'].PHP_EOL);
|
|
||||||
}
|
|
||||||
fwrite($wpa_file, "}".PHP_EOL);
|
|
||||||
} else {
|
|
||||||
$status->addMessage('WPA passphrase must be between 8 and 63 characters', 'danger');
|
|
||||||
$ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ok) {
|
if ($result['success']) {
|
||||||
system('sudo cp /tmp/wifidata ' . RASPI_WPA_SUPPLICANT_CONFIG, $returnval);
|
$status->addMessage($result['message'], 'success');
|
||||||
if ($returnval == 0) {
|
$networks = $tmp_networks;
|
||||||
exec('sudo wpa_cli -i ' . $_SESSION['wifi_client_interface'] . ' reconfigure', $reconfigure_out, $reconfigure_return);
|
|
||||||
if ($reconfigure_return == 0) {
|
|
||||||
$status->addMessage('Wifi settings updated successfully', 'success');
|
|
||||||
$networks = $tmp_networks;
|
|
||||||
} else {
|
|
||||||
$status->addMessage('Wifi settings updated but cannot restart (cannot execute "wpa_cli reconfigure")', 'danger');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$status->addMessage('Wifi settings failed to be updated', 'danger');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
$status->addMessage('Failed to update wifi settings', 'danger');
|
$status->addMessage($result['message'], 'danger');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$clientInterface = $_SESSION['wifi_client_interface'];
|
$ifaceStatus = $wifi->getInterfaceStatus($clientInterface);
|
||||||
|
|
||||||
exec('ip a show '.$clientInterface, $stdoutIp);
|
|
||||||
$stdoutIpAllLinesGlued = implode(" ", $stdoutIp);
|
|
||||||
$stdoutIpWRepeatedSpaces = preg_replace('/\s\s+/', ' ', $stdoutIpAllLinesGlued);
|
|
||||||
preg_match('/state (UP|DOWN)/i', $stdoutIpWRepeatedSpaces, $matchesState) || $matchesState[1] = 'unknown';
|
|
||||||
$ifaceStatus = strtolower($matchesState[1]) ? "up" : "down";
|
|
||||||
|
|
||||||
echo renderTemplate("configure_client", compact("status", "clientInterface", "ifaceStatus"));
|
echo renderTemplate("configure_client", compact("status", "clientInterface", "ifaceStatus"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,12 @@ function DisplayDashboard(&$extraFooterScripts): void
|
|||||||
$plugins = $pluginManager->getInstalledPlugins();
|
$plugins = $pluginManager->getInstalledPlugins();
|
||||||
$bridgedEnable = getBridgedState();
|
$bridgedEnable = getBridgedState();
|
||||||
|
|
||||||
|
if ($bridgedEnable) {
|
||||||
|
$interface = 'br0';
|
||||||
|
$details = $dashboard->getInterfaceDetails($interface);
|
||||||
|
$connectionType = 'ethernet';
|
||||||
|
}
|
||||||
|
|
||||||
// handle page actions
|
// handle page actions
|
||||||
if (!empty($_POST)) {
|
if (!empty($_POST)) {
|
||||||
$status = $dashboard->handlePageAction($state, $_POST, $status, $interface);
|
$status = $dashboard->handlePageAction($state, $_POST, $status, $interface);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ if (!defined('RASPI_CONFIG')) {
|
|||||||
$defaults = [
|
$defaults = [
|
||||||
'RASPI_BRAND_TEXT' => 'RaspAP',
|
'RASPI_BRAND_TEXT' => 'RaspAP',
|
||||||
'RASPI_BRAND_TITLE' => RASPI_BRAND_TEXT.' Admin Panel',
|
'RASPI_BRAND_TITLE' => RASPI_BRAND_TEXT.' Admin Panel',
|
||||||
'RASPI_VERSION' => '3.4.5',
|
'RASPI_VERSION' => '3.5.1',
|
||||||
'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json',
|
'RASPI_CONFIG_NETWORK' => RASPI_CONFIG.'/networking/defaults.json',
|
||||||
'RASPI_CONFIG_PROVIDERS' => 'config/vpn-providers.json',
|
'RASPI_CONFIG_PROVIDERS' => 'config/vpn-providers.json',
|
||||||
'RASPI_CONFIG_API' => RASPI_CONFIG.'/api',
|
'RASPI_CONFIG_API' => RASPI_CONFIG.'/api',
|
||||||
@@ -28,7 +28,6 @@ $defaults = [
|
|||||||
'RASPI_HOSTAPD_CONFIG' => '/etc/hostapd/hostapd.conf',
|
'RASPI_HOSTAPD_CONFIG' => '/etc/hostapd/hostapd.conf',
|
||||||
'RASPI_DHCPCD_CONFIG' => '/etc/dhcpcd.conf',
|
'RASPI_DHCPCD_CONFIG' => '/etc/dhcpcd.conf',
|
||||||
'RASPI_DHCPCD_LOG' => '/var/log/dnsmasq.log',
|
'RASPI_DHCPCD_LOG' => '/var/log/dnsmasq.log',
|
||||||
'RASPI_HOSTAPD_LOG' => '/tmp/hostapd.log',
|
|
||||||
'RASPI_WPA_SUPPLICANT_CONFIG' => '/etc/wpa_supplicant/wpa_supplicant.conf',
|
'RASPI_WPA_SUPPLICANT_CONFIG' => '/etc/wpa_supplicant/wpa_supplicant.conf',
|
||||||
'RASPI_HOSTAPD_CTRL_INTERFACE' => '/var/run/hostapd',
|
'RASPI_HOSTAPD_CTRL_INTERFACE' => '/var/run/hostapd',
|
||||||
'RASPI_WPA_CTRL_INTERFACE' => '/var/run/wpa_supplicant',
|
'RASPI_WPA_CTRL_INTERFACE' => '/var/run/wpa_supplicant',
|
||||||
|
|||||||
@@ -112,6 +112,9 @@ function getProviderValue($id, $key)
|
|||||||
if (!isset($obj['providers']) || !is_array($obj['providers'])) {
|
if (!isset($obj['providers']) || !is_array($obj['providers'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if ($id === null || !is_numeric($id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$id--;
|
$id--;
|
||||||
if (!isset($obj['providers'][$id]) || !is_array($obj['providers'][$id])) {
|
if (!isset($obj['providers'][$id]) || !is_array($obj['providers'][$id])) {
|
||||||
return false;
|
return false;
|
||||||
@@ -918,7 +921,7 @@ function renderStatus($hostapd_led, $hostapd_status, $memused_led, $memused, $cp
|
|||||||
<img src="app/img/raspAP-logo.php?static=1" class="navbar-logo" width="70" height="70">
|
<img src="app/img/raspAP-logo.php?static=1" class="navbar-logo" width="70" height="70">
|
||||||
</div>
|
</div>
|
||||||
<div class="col ml-2">
|
<div class="col ml-2">
|
||||||
<div class="ml-1 sb-status">Status</div>
|
<div class="ml-1 sb-status"><?php echo _("Status"); ?></div>
|
||||||
<div class="info-item-xs"><span class="icon">
|
<div class="info-item-xs"><span class="icon">
|
||||||
<i class="fas fa-circle hostapd-led <?php echo ($hostapd_led); ?>"></i></span> <?php echo _("Hotspot").' '. _($hostapd_status); ?>
|
<i class="fas fa-circle hostapd-led <?php echo ($hostapd_led); ?>"></i></span> <?php echo _("Hotspot").' '. _($hostapd_status); ?>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ $wifi->getWifiInterface();
|
|||||||
*/
|
*/
|
||||||
function DisplayHostAPDConfig()
|
function DisplayHostAPDConfig()
|
||||||
{
|
{
|
||||||
|
$reg_domain = 'GB';
|
||||||
$hostapd = new HostapdManager();
|
$hostapd = new HostapdManager();
|
||||||
$hotspot = new HotspotService();
|
$hotspot = new HotspotService();
|
||||||
$status = new StatusMessage();
|
$status = new StatusMessage();
|
||||||
@@ -30,18 +31,24 @@ function DisplayHostAPDConfig()
|
|||||||
$arr80211w = $hotspot->get80211wOptions();
|
$arr80211w = $hotspot->get80211wOptions();
|
||||||
$languageCode = strtok($_SESSION['locale'], '_');
|
$languageCode = strtok($_SESSION['locale'], '_');
|
||||||
$countryCodes = getCountryCodes($languageCode);
|
$countryCodes = getCountryCodes($languageCode);
|
||||||
$reg_domain = $hotspot->getRegDomain();
|
|
||||||
$interfaces = $hotspot->getInterfaces();
|
$interfaces = $hotspot->getInterfaces();
|
||||||
$arrTxPower = getDefaultNetOpts('txpower','dbm');
|
$arrTxPower = getDefaultNetOpts('txpower','dbm');
|
||||||
$managedModeEnabled = false;
|
$managedModeEnabled = false;
|
||||||
|
try {
|
||||||
|
$reg_domain = $hotspot->getRegDomain();
|
||||||
|
} catch (RuntimeException $e) {
|
||||||
|
error_log('Failed to get regulatory domain: ' . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($_POST['interface'])) {
|
if (isset($_POST['interface'])) {
|
||||||
$interface = $_POST['interface'];
|
$interface = $_POST['interface'];
|
||||||
} else {
|
} else {
|
||||||
$interface = $_SESSION['ap_interface'];
|
$interface = $_SESSION['ap_interface'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$txpower = $hotspot->getTxPower($interface);
|
$txpower = $hotspot->getTxPower($interface);
|
||||||
$arrHostapdConf = $hotspot->getHostapdIni();
|
$arrHostapdConf = $hotspot->getHostapdIni();
|
||||||
|
$logOutput = [];
|
||||||
|
|
||||||
if (!RASPI_MONITOR_ENABLED) {
|
if (!RASPI_MONITOR_ENABLED) {
|
||||||
if (isset($_POST['StartHotspot']) || isset($_POST['RestartHotspot'])) {
|
if (isset($_POST['StartHotspot']) || isset($_POST['RestartHotspot'])) {
|
||||||
@@ -72,6 +79,10 @@ function DisplayHostAPDConfig()
|
|||||||
$reg_domain,
|
$reg_domain,
|
||||||
$status
|
$status
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// reload hostapi.ini
|
||||||
|
$arrHostapdConf = $hotspot->getHostapdIni();
|
||||||
|
|
||||||
} elseif (isset($_POST['StopHotspot'])) {
|
} elseif (isset($_POST['StopHotspot'])) {
|
||||||
$status->addMessage('Attempting to stop hotspot', 'info');
|
$status->addMessage('Attempting to stop hotspot', 'info');
|
||||||
exec('sudo /bin/systemctl stop hostapd.service', $return);
|
exec('sudo /bin/systemctl stop hostapd.service', $return);
|
||||||
@@ -130,15 +141,21 @@ function DisplayHostAPDConfig()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fetch hostapd logs if enabled
|
||||||
|
if ((string)$arrHostapdConf['LogEnable'] === "1") {
|
||||||
|
$logResult = $hotspot->getHostapdLogs(5000);
|
||||||
|
if ($logResult['success']) {
|
||||||
|
$joined = implode("\n", $logResult['logs']);
|
||||||
|
$limited = getLogLimited('', $joined);
|
||||||
|
$logOutput = explode("\n", $limited);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// assign disassoc_low_ack boolean if value is set
|
// assign disassoc_low_ack boolean if value is set
|
||||||
$arrConfig['disassoc_low_ack_bool'] = isset($arrConfig['disassoc_low_ack']) ? 1 : 0;
|
$arrConfig['disassoc_low_ack_bool'] = isset($arrConfig['disassoc_low_ack']) ? 1 : 0;
|
||||||
$hostapdstatus = $system->hostapdStatus();
|
$hostapdstatus = $system->hostapdStatus();
|
||||||
$serviceStatus = $hostapdstatus[0] == 0 ? "down" : "up";
|
$serviceStatus = $hostapdstatus[0] == 0 ? "down" : "up";
|
||||||
|
|
||||||
// ensure log is writeable
|
|
||||||
exec('sudo /bin/chmod o+r '.RASPI_HOSTAPD_LOG);
|
|
||||||
$logdata = getLogLimited(RASPI_HOSTAPD_LOG);
|
|
||||||
|
|
||||||
echo renderTemplate(
|
echo renderTemplate(
|
||||||
"hostapd", compact(
|
"hostapd", compact(
|
||||||
"status",
|
"status",
|
||||||
@@ -156,7 +173,7 @@ function DisplayHostAPDConfig()
|
|||||||
"arrHostapdConf",
|
"arrHostapdConf",
|
||||||
"operatingSystem",
|
"operatingSystem",
|
||||||
"countryCodes",
|
"countryCodes",
|
||||||
"logdata"
|
"logOutput"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,133 +1,41 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Sets locale information for i18n support, with secure input validation.
|
* Sets locale information for i18n support, with validation
|
||||||
*
|
*
|
||||||
* Rudimentary language detection is performed via the browser.
|
* @see RaspAP\Localization\LocaleManager
|
||||||
* Accept-Language returns a list of weighted values with a quality (or 'q') parameter.
|
|
||||||
* A better method would parse the list of preferred languages and match this with
|
|
||||||
* the languages supported by our platform.
|
|
||||||
* @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
|
|
||||||
*
|
|
||||||
* Uses $validLocales to mitigate OS Command Injection (CWE-78)
|
|
||||||
* @see Vulnerability Report for JVN #27202136
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Set locale from POST, if provided and valid
|
use RaspAP\Localization\LocaleManager;
|
||||||
$validLocales = array_keys(getLocales());
|
|
||||||
if (!empty($_POST['locale']) && in_array($_POST['locale'], $validLocales, true)) {
|
|
||||||
$_SESSION['locale'] = $_POST['locale'];
|
|
||||||
setcookie('locale', $_POST['locale'], time() + (86400 * 30), '/', '', false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set locale from cookie or browser detection, if not already set in session
|
// Initialize locale manager
|
||||||
if (empty($_SESSION['locale'])) {
|
$localeManager = new LocaleManager();
|
||||||
if (isset($_COOKIE['locale']) && in_array($_COOKIE['locale'], $validLocales, true)) {
|
$localeManager->initializeAndApply();
|
||||||
$_SESSION['locale'] = $_COOKIE['locale'];
|
|
||||||
} else {
|
|
||||||
$_SESSION['locale'] = detectBrowserLocale();
|
|
||||||
setcookie('locale', $_SESSION['locale'], time() + (86400 * 30), '/', '', false, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enforce only valid locale values in session
|
|
||||||
if (!in_array($_SESSION['locale'], $validLocales, true)) {
|
|
||||||
$_SESSION['locale'] = 'en_GB.UTF-8';
|
|
||||||
// Update cookie with default locale
|
|
||||||
setcookie('locale', $_SESSION['locale'], time() + (86400 * 30), '/', '', false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply locale settings
|
|
||||||
putenv("LANG=" . escapeshellarg($_SESSION['locale']));
|
|
||||||
setlocale(LC_ALL, $_SESSION['locale']);
|
|
||||||
bindtextdomain(LOCALE_DOMAIN, LOCALE_ROOT);
|
|
||||||
bind_textdomain_codeset(LOCALE_DOMAIN, 'UTF-8');
|
|
||||||
textdomain(LOCALE_DOMAIN);
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all available locales
|
||||||
|
*
|
||||||
|
* @return array Associative array of locale codes to language names
|
||||||
|
*/
|
||||||
function getLocales(): array
|
function getLocales(): array
|
||||||
{
|
{
|
||||||
return [
|
static $localeManager = null;
|
||||||
'en_GB.UTF-8' => 'English',
|
if ($localeManager === null) {
|
||||||
'cs_CZ.UTF-8' => 'Čeština',
|
$localeManager = new LocaleManager();
|
||||||
'zh_TW.UTF-8' => '正體中文 (Chinese traditional)',
|
}
|
||||||
'zh_CN.UTF-8' => '简体中文 (Chinese simplified)',
|
return $localeManager->getLocales();
|
||||||
'da_DK.UTF-8' => 'Dansk',
|
|
||||||
'de_DE.UTF-8' => 'Deutsch',
|
|
||||||
'es_MX.UTF-8' => 'Español',
|
|
||||||
'fi_FI.UTF-8' => 'Finnish',
|
|
||||||
'fr_FR.UTF-8' => 'Français',
|
|
||||||
'el_GR.UTF-8' => 'Ελληνικά',
|
|
||||||
'id_ID.UTF-8' => 'Indonesian',
|
|
||||||
'it_IT.UTF-8' => 'Italiano',
|
|
||||||
'ja_JP.UTF-8' => '日本語 (Japanese)',
|
|
||||||
'ko_KR.UTF-8' => '한국어 (Korean)',
|
|
||||||
'nl_NL.UTF-8' => 'Nederlands',
|
|
||||||
'pl_PL.UTF-8' => 'Polskie',
|
|
||||||
'pt_BR.UTF-8' => 'Português',
|
|
||||||
'ru_RU.UTF-8' => 'Русский',
|
|
||||||
'ro_RO.UTF-8' => 'Română',
|
|
||||||
'sk_SK.UTF-8' => 'Slovenčina',
|
|
||||||
'sv_SE.UTF-8' => 'Svenska',
|
|
||||||
'tr_TR.UTF-8' => 'Türkçe',
|
|
||||||
'vi_VN.UTF-8' => 'Tiếng Việt (Vietnamese)'
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect browser locale from Accept-Language header
|
||||||
|
*
|
||||||
|
* @return string Detected locale code
|
||||||
|
*/
|
||||||
function detectBrowserLocale(): string
|
function detectBrowserLocale(): string
|
||||||
{
|
{
|
||||||
if (empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) || strlen($_SERVER['HTTP_ACCEPT_LANGUAGE']) < 2) {
|
static $localeManager = null;
|
||||||
return 'en_GB.UTF-8';
|
if ($localeManager === null) {
|
||||||
}
|
$localeManager = new LocaleManager();
|
||||||
|
|
||||||
$acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
|
||||||
$lang = strtolower(substr($acceptLang, 0, 2));
|
|
||||||
|
|
||||||
if ($lang === 'zh' && strpos($acceptLang, 'zh-TW') === 0) {
|
|
||||||
return 'zh_TW.UTF-8';
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($lang) {
|
|
||||||
case 'de':
|
|
||||||
return 'de_DE.UTF-8';
|
|
||||||
case 'fr':
|
|
||||||
return 'fr_FR.UTF-8';
|
|
||||||
case 'it':
|
|
||||||
return 'it_IT.UTF-8';
|
|
||||||
case 'pt':
|
|
||||||
return 'pt_BR.UTF-8';
|
|
||||||
case 'sv':
|
|
||||||
return 'sv_SE.UTF-8';
|
|
||||||
case 'nl':
|
|
||||||
return 'nl_NL.UTF-8';
|
|
||||||
case 'zh':
|
|
||||||
return 'zh_CN.UTF-8';
|
|
||||||
case 'cs':
|
|
||||||
return 'cs_CZ.UTF-8';
|
|
||||||
case 'ru':
|
|
||||||
return 'ru_RU.UTF-8';
|
|
||||||
case 'es':
|
|
||||||
return 'es_MX.UTF-8';
|
|
||||||
case 'fi':
|
|
||||||
return 'fi_FI.UTF-8';
|
|
||||||
case 'da':
|
|
||||||
return 'da_DK.UTF-8';
|
|
||||||
case 'tr':
|
|
||||||
return 'tr_TR.UTF-8';
|
|
||||||
case 'id':
|
|
||||||
return 'id_ID.UTF-8';
|
|
||||||
case 'ko':
|
|
||||||
return 'ko_KR.UTF-8';
|
|
||||||
case 'ja':
|
|
||||||
return 'ja_JP.UTF-8';
|
|
||||||
case 'vi':
|
|
||||||
return 'vi_VN.UTF-8';
|
|
||||||
case 'el':
|
|
||||||
return 'el_GR.UTF-8';
|
|
||||||
case 'pl':
|
|
||||||
return 'pl_PL.UTF-8';
|
|
||||||
case 'sk':
|
|
||||||
return 'sk_SK.UTF-8';
|
|
||||||
default:
|
|
||||||
return 'en_GB.UTF-8';
|
|
||||||
}
|
}
|
||||||
|
return $localeManager->detectBrowserLocale();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
* @author Lawrence Yau <sirlagz@gmail.com>
|
* @author Lawrence Yau <sirlagz@gmail.com>
|
||||||
* @author Bill Zimmerman <billzimmerman@gmail.com>
|
* @author Bill Zimmerman <billzimmerman@gmail.com>
|
||||||
* @license GNU General Public License, version 3 (GPL-3.0)
|
* @license GNU General Public License, version 3 (GPL-3.0)
|
||||||
* @version 3.4.5
|
* @version 3.5.1
|
||||||
* @link https://github.com/RaspAP/raspap-webgui/
|
* @link https://github.com/RaspAP/raspap-webgui/
|
||||||
* @link https://raspap.com/
|
* @link https://raspap.com/
|
||||||
* @see http://sirlagz.net/2013/02/08/raspap-webgui/
|
* @see http://sirlagz.net/2013/02/08/raspap-webgui/
|
||||||
|
|||||||
61
installers/common.sh
Executable file → Normal file
61
installers/common.sh
Executable file → Normal file
@@ -158,7 +158,7 @@ function _get_linux_distro() {
|
|||||||
# Sets php package option based on Linux version, abort if unsupported distro
|
# Sets php package option based on Linux version, abort if unsupported distro
|
||||||
function _set_php_package() {
|
function _set_php_package() {
|
||||||
case $RELEASE in
|
case $RELEASE in
|
||||||
13) # Debian 13 trixie
|
13|2025.*) # Debian 13 trixie, Kali Linux 2025
|
||||||
php_package="php8.4-fpm"
|
php_package="php8.4-fpm"
|
||||||
phpiniconf="/etc/php/8.4/fpm/php.ini" ;;
|
phpiniconf="/etc/php/8.4/fpm/php.ini" ;;
|
||||||
23.05|12*) # Debian 12 & Armbian 23.05
|
23.05|12*) # Debian 12 & Armbian 23.05
|
||||||
@@ -246,7 +246,7 @@ function _install_dependencies() {
|
|||||||
else
|
else
|
||||||
echo "${php_package} will be installed from the main deb sources list"
|
echo "${php_package} will be installed from the main deb sources list"
|
||||||
fi
|
fi
|
||||||
if [ ${OS,,} = "debian" ] || [ ${OS,,} = "ubuntu" ]; then
|
if [ ${OS,,} = "debian" ] || [ ${OS,,} = "ubuntu" ] || [ ${OS,,} = "kali" ]; then
|
||||||
dhcpcd_package="dhcpcd5"
|
dhcpcd_package="dhcpcd5"
|
||||||
iw_package="iw"
|
iw_package="iw"
|
||||||
rsync_package="rsync"
|
rsync_package="rsync"
|
||||||
@@ -287,9 +287,10 @@ function _install_dependencies() {
|
|||||||
|
|
||||||
if [[ "$php_package" == *"-fpm" ]]; then
|
if [[ "$php_package" == *"-fpm" ]]; then
|
||||||
_install_log "Enabling lighttpd fastcgi-php-fpm module for $php_package"
|
_install_log "Enabling lighttpd fastcgi-php-fpm module for $php_package"
|
||||||
sudo lighty-enable-mod fastcgi-php-fpm || _install_status 1 "Unable to enable fastcgi-php-fpm module"
|
sudo lighty-enable-mod fastcgi-php-fpm 2>&1 | grep -qE "already enabled" || \
|
||||||
|
_install_status 1 "Unable to enable fastcgi-php-fpm module"
|
||||||
|
sudo systemctl restart $php_package.service || _install_status 1 "Unable to restart $php_package.service"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_install_status 0
|
_install_status 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,9 +325,6 @@ function _create_hostapd_scripts() {
|
|||||||
_install_log "Creating hostapd logging & control scripts"
|
_install_log "Creating hostapd logging & control scripts"
|
||||||
sudo mkdir $raspap_dir/hostapd || _install_status 1 "Unable to create directory '$raspap_dir/hostapd'"
|
sudo mkdir $raspap_dir/hostapd || _install_status 1 "Unable to create directory '$raspap_dir/hostapd'"
|
||||||
|
|
||||||
# Copy logging shell scripts
|
|
||||||
sudo cp "$webroot_dir/installers/"enablelog.sh "$raspap_dir/hostapd" || _install_status 1 "Unable to move logging scripts"
|
|
||||||
sudo cp "$webroot_dir/installers/"disablelog.sh "$raspap_dir/hostapd" || _install_status 1 "Unable to move logging scripts"
|
|
||||||
# Copy service control shell scripts
|
# Copy service control shell scripts
|
||||||
sudo cp "$webroot_dir/installers/"servicestart.sh "$raspap_dir/hostapd" || _install_status 1 "Unable to move service control scripts"
|
sudo cp "$webroot_dir/installers/"servicestart.sh "$raspap_dir/hostapd" || _install_status 1 "Unable to move service control scripts"
|
||||||
# Change ownership and permissions of hostapd control scripts
|
# Change ownership and permissions of hostapd control scripts
|
||||||
@@ -426,7 +424,28 @@ function _prompt_install_feature() {
|
|||||||
else
|
else
|
||||||
$function
|
$function
|
||||||
fi
|
fi
|
||||||
|
elif [ "$opt" == "pv_option" ]; then
|
||||||
|
local opt_value=${!opt:-0}
|
||||||
|
if [ "$opt_value" == 0 ]; then
|
||||||
|
echo "(Skipped)"
|
||||||
|
else
|
||||||
|
local valid_ids=($(jq -r '.providers[].id' "$webroot_dir/config/vpn-providers.json"))
|
||||||
|
local found=0
|
||||||
|
for id in "${valid_ids[@]}"; do
|
||||||
|
if [ "$id" == "$opt_value" ]; then
|
||||||
|
found=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ $found == 1 ]; then
|
||||||
|
echo -e
|
||||||
|
$function
|
||||||
|
else
|
||||||
|
_install_status 1 "Invalid VPN provider ID $opt_value - (Skipped)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
elif [ "${!opt}" == 1 ]; then
|
elif [ "${!opt}" == 1 ]; then
|
||||||
|
echo -e
|
||||||
$function
|
$function
|
||||||
else
|
else
|
||||||
echo "(Skipped)"
|
echo "(Skipped)"
|
||||||
@@ -641,14 +660,32 @@ function _download_latest_files() {
|
|||||||
if [ -d "$webroot_dir" ] && [ "$update" == 0 ]; then
|
if [ -d "$webroot_dir" ] && [ "$update" == 0 ]; then
|
||||||
sudo mv $webroot_dir "$webroot_dir.`date +%F-%R`" || _install_status 1 "Unable to move existing webroot directory"
|
sudo mv $webroot_dir "$webroot_dir.`date +%F-%R`" || _install_status 1 "Unable to move existing webroot directory"
|
||||||
elif [ "$upgrade" == 1 ] || [ "$update" == 1 ]; then
|
elif [ "$upgrade" == 1 ] || [ "$update" == 1 ]; then
|
||||||
exclude='--exclude=ajax/system/sys_read_logfile.php'
|
# Preserve user plugins temporarily
|
||||||
shopt -s extglob
|
if [ -d "$webroot_dir/plugins" ]; then
|
||||||
sudo find "$webroot_dir" ! -path "${webroot_dir}/ajax/system/sys_read_logfile.php" -delete 2>/dev/null
|
sudo cp -r "$webroot_dir/plugins" "/tmp/raspap-user-plugins"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo find "$webroot_dir" -mindepth 1 \
|
||||||
|
! -path "${webroot_dir}/ajax/system/sys_read_logfile.php" \
|
||||||
|
! -path "${webroot_dir}/plugins" \
|
||||||
|
! -path "${webroot_dir}/plugins/*" \
|
||||||
|
-delete 2>/dev/null
|
||||||
|
|
||||||
|
# Remove plugins to permit clean rsync
|
||||||
|
sudo rm -rf "$webroot_dir/plugins"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_install_log "Installing application to $webroot_dir"
|
_install_log "Installing application to $webroot_dir"
|
||||||
sudo rsync -av $exclude "$source_dir"/ "$webroot_dir"/ >/dev/null 2>&1 || _install_status 1 "Unable to install files to $webroot_dir"
|
sudo rsync -av $exclude "$source_dir"/ "$webroot_dir"/ >/dev/null 2>&1 || _install_status 1 "Unable to install files to $webroot_dir"
|
||||||
|
|
||||||
|
# Restore user plugins after rsync
|
||||||
|
if [ "$upgrade" == 1 ] || [ "$update" == 1 ]; then
|
||||||
|
if [ -d "/tmp/raspap-user-plugins" ]; then
|
||||||
|
sudo find /tmp/raspap-user-plugins -mindepth 1 -maxdepth 1 -type d -exec cp -r {} "$webroot_dir/plugins/" \; 2>/dev/null
|
||||||
|
sudo rm -rf "/tmp/raspap-user-plugins"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$update" == 1 ]; then
|
if [ "$update" == 1 ]; then
|
||||||
_install_log "Applying existing configuration to ${webroot_dir}/includes"
|
_install_log "Applying existing configuration to ${webroot_dir}/includes"
|
||||||
sudo mv /tmp/config.php $webroot_dir/includes || _install_status 1 "Unable to move config.php to ${webroot_dir}/includes"
|
sudo mv /tmp/config.php $webroot_dir/includes || _install_status 1 "Unable to move config.php to ${webroot_dir}/includes"
|
||||||
@@ -820,8 +857,8 @@ function _configure_networking() {
|
|||||||
"-A POSTROUTING -s 192.168.50.0/24 ! -d 192.168.50.0/24 -j MASQUERADE"
|
"-A POSTROUTING -s 192.168.50.0/24 ! -d 192.168.50.0/24 -j MASQUERADE"
|
||||||
)
|
)
|
||||||
for rule in "${rules[@]}"; do
|
for rule in "${rules[@]}"; do
|
||||||
if grep -- "$rule" $rulesv4 > /dev/null; then
|
if sudo grep -- "$rule" $rulesv4 > /dev/null; then
|
||||||
echo "Rule already exits: ${rule}"
|
echo "Rule already exists: ${rule}"
|
||||||
else
|
else
|
||||||
rule=$(sed -e 's/^\(-A POSTROUTING\)/-t nat \1/' <<< $rule)
|
rule=$(sed -e 's/^\(-A POSTROUTING\)/-t nat \1/' <<< $rule)
|
||||||
echo "Adding rule: ${rule}"
|
echo "Adding rule: ${rule}"
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
/bin/sed -i 's|DAEMON_OPTS=" -f /tmp/hostapd.log"|#DAEMON_OPTS=""|' /etc/default/hostapd
|
|
||||||
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
/bin/sed -i 's|#DAEMON_OPTS=""|DAEMON_OPTS=" -f /tmp/hostapd.log"|' /etc/default/hostapd
|
|
||||||
touch /tmp/hostapd.log
|
|
||||||
173
installers/generate_os_list.sh
Executable file
173
installers/generate_os_list.sh
Executable file
@@ -0,0 +1,173 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# RaspAP OS Subitem Generator for RPi Imager 2.x
|
||||||
|
# Author: @billz <billzimmerman@gmail.com>
|
||||||
|
# Author URI: https://github.com/billz
|
||||||
|
# Project URI: https://github.com/RaspAP/
|
||||||
|
# License: GNU General Public License v3.0
|
||||||
|
# License URI: https://github.com/RaspAP/raspap-webgui/blob/master/LICENSE
|
||||||
|
|
||||||
|
# Exit on error
|
||||||
|
set -o errexit
|
||||||
|
|
||||||
|
# Exit on error inside functions
|
||||||
|
set -o errtrace
|
||||||
|
|
||||||
|
# Set defaults
|
||||||
|
NAME="RaspAP"
|
||||||
|
WEBSITE="https://raspap.com/"
|
||||||
|
ICON="https://raspap.com/assets/images/raspAP-logo.svg"
|
||||||
|
DESCRIPTION="The easiest, full-featured wireless router for Debian-based devices."
|
||||||
|
REPO="RaspAP/raspap-webgui"
|
||||||
|
|
||||||
|
# Fetch latest release from GitHub (adapted from RaspAP installer)
|
||||||
|
_get_release() {
|
||||||
|
local response
|
||||||
|
local host="api.github.com"
|
||||||
|
|
||||||
|
echo "Fetching latest release from GitHub..." >&2
|
||||||
|
response=$(curl -s "https://$host/repos/$REPO/releases/latest")
|
||||||
|
|
||||||
|
if echo "$response" | grep -q 'API rate limit exceeded'; then
|
||||||
|
echo "Error: GitHub API rate limit exceeded. Try again later or use a GitHub token." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION=$(echo "$response" | grep -o '"tag_name": *"[^"]*"' | sed 's/"tag_name": *"\(.*\)"/\1/')
|
||||||
|
|
||||||
|
if [ -z "$VERSION" ]; then
|
||||||
|
echo "Error: Failed to fetch latest release. Check network connectivity." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local published_at=$(echo "$response" | grep -o '"published_at": *"[^"]*"' | sed 's/"published_at": *"\(.*\)"/\1/')
|
||||||
|
RELEASE_DATE=$(echo "$published_at" | cut -d'T' -f1)
|
||||||
|
|
||||||
|
echo "Found release: $VERSION (published: $RELEASE_DATE)" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fetch latest release info (if not provided via environment variables)
|
||||||
|
if [ -z "$VERSION" ] || [ -z "$RELEASE_DATE" ]; then
|
||||||
|
_get_release
|
||||||
|
else
|
||||||
|
echo "Using VERSION=$VERSION and RELEASE_DATE=$RELEASE_DATE from environment" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
BASE_URL="https://github.com/RaspAP/raspap-webgui/releases/download/${VERSION}"
|
||||||
|
|
||||||
|
_get_size() {
|
||||||
|
if [ -f "$1" ]; then
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
stat -f %z "$1" 2>/dev/null
|
||||||
|
else
|
||||||
|
stat -c %s "$1" 2>/dev/null
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "0"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_sha256() {
|
||||||
|
if [ -f "$1" ]; then
|
||||||
|
if command -v sha256sum &> /dev/null; then
|
||||||
|
sha256sum "$1" | awk '{print $1}'
|
||||||
|
else
|
||||||
|
shasum -a 256 "$1" | awk '{print $1}'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Process armhf (32-bit)
|
||||||
|
ARMHF_ZIP="raspap-trixie-armhf-lite-${VERSION}.img.zip"
|
||||||
|
|
||||||
|
if [ -f "$ARMHF_ZIP" ]; then
|
||||||
|
ARMHF_IMG=$(ls *-raspap-trixie-armhf-lite-${VERSION}.img 2>/dev/null | head -1)
|
||||||
|
|
||||||
|
if [ -z "$ARMHF_IMG" ]; then
|
||||||
|
echo "Extracting $ARMHF_ZIP..."
|
||||||
|
unzip -o "$ARMHF_ZIP"
|
||||||
|
ARMHF_IMG=$(ls *-raspap-trixie-armhf-lite-${VERSION}.img 2>/dev/null | head -1)
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
ARMHF_IMG=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
ARMHF_EXTRACT_SIZE=$(_get_size "$ARMHF_IMG")
|
||||||
|
ARMHF_EXTRACT_SHA=$(_get_sha256 "$ARMHF_IMG")
|
||||||
|
ARMHF_DOWNLOAD_SIZE=$(_get_size "$ARMHF_ZIP")
|
||||||
|
ARMHF_DOWNLOAD_SHA=$(_get_sha256 "$ARMHF_ZIP")
|
||||||
|
|
||||||
|
# Process arm64 (64-bit)
|
||||||
|
ARM64_ZIP="raspap-trixie-arm64-lite-${VERSION}.img.zip"
|
||||||
|
|
||||||
|
if [ -f "$ARM64_ZIP" ]; then
|
||||||
|
ARM64_IMG=$(ls *-raspap-trixie-arm64-lite-${VERSION}.img 2>/dev/null | head -1)
|
||||||
|
|
||||||
|
if [ -z "$ARM64_IMG" ]; then
|
||||||
|
echo "Extracting $ARM64_ZIP..."
|
||||||
|
unzip -o "$ARM64_ZIP"
|
||||||
|
ARM64_IMG=$(ls *-raspap-trixie-arm64-lite-${VERSION}.img 2>/dev/null | head -1)
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
ARM64_IMG=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
ARM64_EXTRACT_SIZE=$(_get_size "$ARM64_IMG")
|
||||||
|
ARM64_EXTRACT_SHA=$(_get_sha256 "$ARM64_IMG")
|
||||||
|
ARM64_DOWNLOAD_SIZE=$(_get_size "$ARM64_ZIP")
|
||||||
|
ARM64_DOWNLOAD_SHA=$(_get_sha256 "$ARM64_ZIP")
|
||||||
|
|
||||||
|
# Generate JSON
|
||||||
|
cat > os-sublist-raspap.json << EOF
|
||||||
|
{
|
||||||
|
"name": "${NAME}",
|
||||||
|
"description": "${DESCRIPTION}",
|
||||||
|
"icon": "${ICON}",
|
||||||
|
"random": false,
|
||||||
|
"subitems": [
|
||||||
|
{
|
||||||
|
"name": "${NAME} 32-bit (armhf)",
|
||||||
|
"description": "${DESCRIPTION}",
|
||||||
|
"icon": "${ICON}",
|
||||||
|
"url": "${BASE_URL}/${ARMHF_ZIP}",
|
||||||
|
"extract_size": ${ARMHF_EXTRACT_SIZE},
|
||||||
|
"extract_sha256": "${ARMHF_EXTRACT_SHA}",
|
||||||
|
"image_download_size": ${ARMHF_DOWNLOAD_SIZE},
|
||||||
|
"image_download_sha256": "${ARMHF_DOWNLOAD_SHA}",
|
||||||
|
"release_date": "${RELEASE_DATE}",
|
||||||
|
"init_format": "systemd",
|
||||||
|
"devices": [
|
||||||
|
"pi5-32bit",
|
||||||
|
"pi4-32bit",
|
||||||
|
"pi3-32bit",
|
||||||
|
"pi2-32bit"
|
||||||
|
],
|
||||||
|
"capabilities": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "${NAME} 64-bit (arm64)",
|
||||||
|
"description": "${DESCRIPTION}",
|
||||||
|
"icon": "${ICON}",
|
||||||
|
"url": "${BASE_URL}/${ARM64_ZIP}",
|
||||||
|
"extract_size": ${ARM64_EXTRACT_SIZE},
|
||||||
|
"extract_sha256": "${ARM64_EXTRACT_SHA}",
|
||||||
|
"image_download_size": ${ARM64_DOWNLOAD_SIZE},
|
||||||
|
"image_download_sha256": "${ARM64_DOWNLOAD_SHA}",
|
||||||
|
"release_date": "${RELEASE_DATE}",
|
||||||
|
"init_format": "systemd",
|
||||||
|
"devices": [
|
||||||
|
"pi5-64bit",
|
||||||
|
"pi4-64bit",
|
||||||
|
"pi3-64bit"
|
||||||
|
],
|
||||||
|
"capabilities": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Generated os-sublist-raspap.json"
|
||||||
|
cat os-sublist-raspap.json
|
||||||
|
|
||||||
45
installers/os-sublist-raspap.json
Normal file
45
installers/os-sublist-raspap.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"name": "RaspAP",
|
||||||
|
"description": "The easiest, full-featured wireless router for Debian-based devices.",
|
||||||
|
"icon": "https://raspap.com/assets/images/raspAP-logo.svg",
|
||||||
|
"random": false,
|
||||||
|
"subitems": [
|
||||||
|
{
|
||||||
|
"name": "RaspAP 32-bit (armhf)",
|
||||||
|
"description": "The easiest, full-featured wireless router for Debian-based devices.",
|
||||||
|
"icon": "https://raspap.com/assets/images/raspAP-logo.svg",
|
||||||
|
"url": "https://github.com/RaspAP/raspap-webgui/releases/download/3.5.1-test/raspap-trixie-armhf-lite-3.5.1-test.img.zip",
|
||||||
|
"extract_size": 3145728000,
|
||||||
|
"extract_sha256": "c992ce2d375b36c0477e24e6d68004683341dd4e217eb4a074c45bd9984131f9",
|
||||||
|
"image_download_size": 901948510,
|
||||||
|
"image_download_sha256": "801c73645edae571a6e130f935e506d761f02aaaa39a5001cb0c49a88aba0713",
|
||||||
|
"release_date": "2025-12-26",
|
||||||
|
"init_format": "systemd",
|
||||||
|
"devices": [
|
||||||
|
"pi5-32bit",
|
||||||
|
"pi4-32bit",
|
||||||
|
"pi3-32bit",
|
||||||
|
"pi2-32bit"
|
||||||
|
],
|
||||||
|
"capabilities": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RaspAP 64-bit (arm64)",
|
||||||
|
"description": "The easiest, full-featured wireless router for Debian-based devices.",
|
||||||
|
"icon": "https://raspap.com/assets/images/raspAP-logo.svg",
|
||||||
|
"url": "https://github.com/RaspAP/raspap-webgui/releases/download/3.5.1-test/raspap-trixie-arm64-lite-3.5.1-test.img.zip",
|
||||||
|
"extract_size": 3481272320,
|
||||||
|
"extract_sha256": "8bdc5aca58ba93294ddf252b92d606551f2ebc0bc6202856c4671bf15fb09d13",
|
||||||
|
"image_download_size": 932458500,
|
||||||
|
"image_download_sha256": "488627448ac4f6a67b9040547f00f28e0e23808a7a1ad56d9c85719c044fec88",
|
||||||
|
"release_date": "2025-12-26",
|
||||||
|
"init_format": "systemd",
|
||||||
|
"devices": [
|
||||||
|
"pi5-64bit",
|
||||||
|
"pi4-64bit",
|
||||||
|
"pi3-64bit"
|
||||||
|
],
|
||||||
|
"capabilities": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -9,14 +9,16 @@ www-data ALL=(ALL) NOPASSWD:/sbin/wpa_supplicant -i [a-zA-Z0-9]* -c /etc/wpa_sup
|
|||||||
www-data ALL=(ALL) NOPASSWD:/bin/rm /var/run/wpa_supplicant/[a-zA-Z0-9]*
|
www-data ALL=(ALL) NOPASSWD:/bin/rm /var/run/wpa_supplicant/[a-zA-Z0-9]*
|
||||||
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* scan_results
|
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* scan_results
|
||||||
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* scan
|
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* scan
|
||||||
|
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* status
|
||||||
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* reconfigure
|
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* reconfigure
|
||||||
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* add_network
|
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* add_network
|
||||||
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* list_networks
|
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* list_networks
|
||||||
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i enable_network [0-9]
|
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* enable_network [0-9]
|
||||||
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i disconnect [0-9]
|
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* disconnect [0-9]
|
||||||
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* select_network [0-9]
|
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* select_network [0-9]
|
||||||
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* set_network [0-9] *
|
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* set_network [0-9] *
|
||||||
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* remove_network [0-9]
|
www-data ALL=(ALL) NOPASSWD:/sbin/wpa_cli -i [a-zA-Z0-9]* remove_network [0-9]
|
||||||
|
www-data ALL=(ALL) NOPASSWD:/bin/rm -f /var/run/wpa_supplicant/wlan[0-9]
|
||||||
www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/hostapddata /etc/hostapd/hostapd.conf
|
www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/hostapddata /etc/hostapd/hostapd.conf
|
||||||
www-data ALL=(ALL) NOPASSWD:/bin/systemctl start hostapd.service
|
www-data ALL=(ALL) NOPASSWD:/bin/systemctl start hostapd.service
|
||||||
www-data ALL=(ALL) NOPASSWD:/bin/systemctl stop hostapd.service
|
www-data ALL=(ALL) NOPASSWD:/bin/systemctl stop hostapd.service
|
||||||
@@ -47,6 +49,7 @@ www-data ALL=(ALL) NOPASSWD:/sbin/ip -s a f label wl*
|
|||||||
www-data ALL=(ALL) NOPASSWD:/sbin/ifup *
|
www-data ALL=(ALL) NOPASSWD:/sbin/ifup *
|
||||||
www-data ALL=(ALL) NOPASSWD:/sbin/ifdown *
|
www-data ALL=(ALL) NOPASSWD:/sbin/ifdown *
|
||||||
www-data ALL=(ALL) NOPASSWD:/sbin/iw dev*
|
www-data ALL=(ALL) NOPASSWD:/sbin/iw dev*
|
||||||
|
www-data ALL=(ALL) NOPASSWD:/sbin/iw reg set*
|
||||||
www-data ALL=(ALL) NOPASSWD:/bin/cp /etc/raspap/networking/dhcpcd.conf /etc/dhcpcd.conf
|
www-data ALL=(ALL) NOPASSWD:/bin/cp /etc/raspap/networking/dhcpcd.conf /etc/dhcpcd.conf
|
||||||
www-data ALL=(ALL) NOPASSWD:/etc/raspap/hostapd/enablelog.sh
|
www-data ALL=(ALL) NOPASSWD:/etc/raspap/hostapd/enablelog.sh
|
||||||
www-data ALL=(ALL) NOPASSWD:/etc/raspap/hostapd/disablelog.sh
|
www-data ALL=(ALL) NOPASSWD:/etc/raspap/hostapd/disablelog.sh
|
||||||
@@ -90,3 +93,4 @@ www-data ALL=(ALL) NOPASSWD:/etc/raspap/plugins/plugin_helper.sh
|
|||||||
www-data ALL=(ALL) NOPASSWD:/bin/systemctl start raspap-network-activity@*.service
|
www-data ALL=(ALL) NOPASSWD:/bin/systemctl start raspap-network-activity@*.service
|
||||||
www-data ALL=(ALL) NOPASSWD:/bin/systemctl stop raspap-network-activity@*.service
|
www-data ALL=(ALL) NOPASSWD:/bin/systemctl stop raspap-network-activity@*.service
|
||||||
www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/wpa_conf_* /etc/wpa_supplicant/wpa_supplicant.conf
|
www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/wpa_conf_* /etc/wpa_supplicant/wpa_supplicant.conf
|
||||||
|
www-data ALL=(ALL) NOPASSWD:/usr/bin/journalctl -u hostapd.service *
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ function _parse_params() {
|
|||||||
restapi_option=1
|
restapi_option=1
|
||||||
adblock_option=1
|
adblock_option=1
|
||||||
wg_option=1
|
wg_option=1
|
||||||
|
pv_option=0
|
||||||
insiders=0
|
insiders=0
|
||||||
ssh=0
|
ssh=0
|
||||||
minwrite=0
|
minwrite=0
|
||||||
|
|||||||
Binary file not shown.
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: raspap\n"
|
"Project-Id-Version: raspap\n"
|
||||||
"Report-Msgid-Bugs-To: Bill Zimmerman <billzimmerman@gmail.com>\n"
|
"Report-Msgid-Bugs-To: Bill Zimmerman <billzimmerman@gmail.com>\n"
|
||||||
"POT-Creation-Date: 2017-10-19 08:56+0000\n"
|
"POT-Creation-Date: 2017-10-19 08:56+0000\n"
|
||||||
"PO-Revision-Date: 2025-10-21 06:26\n"
|
"PO-Revision-Date: 2025-11-20 12:20\n"
|
||||||
"Last-Translator: Bill Zimmerman <billzimmerman@gmail.com>\n"
|
"Last-Translator: Bill Zimmerman <billzimmerman@gmail.com>\n"
|
||||||
"Language-Team: German\n"
|
"Language-Team: German\n"
|
||||||
"Language: de_DE\n"
|
"Language: de_DE\n"
|
||||||
@@ -33,9 +33,15 @@ msgstr "Übersicht"
|
|||||||
msgid "WiFi client"
|
msgid "WiFi client"
|
||||||
msgstr "WLAN Client"
|
msgstr "WLAN Client"
|
||||||
|
|
||||||
|
msgid "Status"
|
||||||
|
msgstr "Status"
|
||||||
|
|
||||||
msgid "Hotspot"
|
msgid "Hotspot"
|
||||||
msgstr "Hotspot"
|
msgstr "Hotspot"
|
||||||
|
|
||||||
|
msgid "Logging"
|
||||||
|
msgstr "Protokollierung"
|
||||||
|
|
||||||
msgid "Mem Use"
|
msgid "Mem Use"
|
||||||
msgstr "RAM Nutzung"
|
msgstr "RAM Nutzung"
|
||||||
|
|
||||||
@@ -116,7 +122,7 @@ msgid "Click or tap to upload a new user avatar."
|
|||||||
msgstr "Klicken oder tippen, um einen neuen Avatar hochzuladen."
|
msgstr "Klicken oder tippen, um einen neuen Avatar hochzuladen."
|
||||||
|
|
||||||
msgid "Image files of type <code>JPG, GIF or PNG</code> are accepted. Max file size: 2 MB."
|
msgid "Image files of type <code>JPG, GIF or PNG</code> are accepted. Max file size: 2 MB."
|
||||||
msgstr ""
|
msgstr "Bilddateien vom Typ <code>JPG, GIF oder PNG</code> werden akzeptiert. Maximale Dateigröße: 2 MB."
|
||||||
|
|
||||||
msgid "Reset avatar"
|
msgid "Reset avatar"
|
||||||
msgstr "Profilbild zurücksetzen"
|
msgstr "Profilbild zurücksetzen"
|
||||||
@@ -222,7 +228,7 @@ msgid "No Wifi stations found"
|
|||||||
msgstr "Keine WLAN-Sender gefunden"
|
msgstr "Keine WLAN-Sender gefunden"
|
||||||
|
|
||||||
msgid "Reinitialized wpa_supplicant. Choose <strong>Rescan</strong>."
|
msgid "Reinitialized wpa_supplicant. Choose <strong>Rescan</strong>."
|
||||||
msgstr ""
|
msgstr "wpa_supplicant neu initialisiert. Wählen Sie <strong>Aktualisieren</strong>."
|
||||||
|
|
||||||
msgid "Click 'Rescan' to search for nearby Wifi stations."
|
msgid "Click 'Rescan' to search for nearby Wifi stations."
|
||||||
msgstr "Auf „Rescan“ klicken, um nach WLAN-Sendern in der Nähe zu suchen."
|
msgstr "Auf „Rescan“ klicken, um nach WLAN-Sendern in der Nähe zu suchen."
|
||||||
@@ -355,7 +361,7 @@ msgid "Cellular"
|
|||||||
msgstr "Mobile Daten"
|
msgstr "Mobile Daten"
|
||||||
|
|
||||||
msgid "AP"
|
msgid "AP"
|
||||||
msgstr "Access Point"
|
msgstr "AP"
|
||||||
|
|
||||||
msgid "Bridged"
|
msgid "Bridged"
|
||||||
msgstr "Brücken"
|
msgstr "Brücken"
|
||||||
@@ -439,6 +445,24 @@ msgstr "Vielen Dank, dass Sie ein Insider sind"
|
|||||||
msgid "DHCP server settings"
|
msgid "DHCP server settings"
|
||||||
msgstr "DHCP-Servereinstellungen"
|
msgstr "DHCP-Servereinstellungen"
|
||||||
|
|
||||||
|
msgid "Default gateway"
|
||||||
|
msgstr "Standard-Gateway"
|
||||||
|
|
||||||
|
msgid "Install a default route for this interface"
|
||||||
|
msgstr "Eine Standardroute für diese Schnittstelle installieren"
|
||||||
|
|
||||||
|
msgid "Static IP options"
|
||||||
|
msgstr "Statische IP-Optionen"
|
||||||
|
|
||||||
|
msgid "DHCP options"
|
||||||
|
msgstr "DHCP-Optionen"
|
||||||
|
|
||||||
|
msgid "Enable this option if you want RaspAP to assign IP addresses to clients on the selected interface. A static IP address is required for this option."
|
||||||
|
msgstr "Aktivieren Sie diese Option, wenn Sie möchten, dass RaspAP den Clients an der ausgewählten Schnittstelle IP-Adressen zuweist. Eine statische IP-Adresse ist für diese Option erforderlich."
|
||||||
|
|
||||||
|
msgid "Enable this only if you want your device to use this interface as its primary route to the internet."
|
||||||
|
msgstr "Aktivieren Sie dies nur, wenn Ihr Gerät diese Schnittstelle als primäre Route zum Internet verwenden soll."
|
||||||
|
|
||||||
msgid "Client list"
|
msgid "Client list"
|
||||||
msgstr "Client Liste"
|
msgstr "Client Liste"
|
||||||
|
|
||||||
@@ -575,10 +599,10 @@ msgid "This option adds <code>dhcp-host</code> entries to the dnsmasq configurat
|
|||||||
msgstr "Diese Option fügt <code>dhcp-host</code>-Einträge zur dnsmasq-Konfiguration hinzu."
|
msgstr "Diese Option fügt <code>dhcp-host</code>-Einträge zur dnsmasq-Konfiguration hinzu."
|
||||||
|
|
||||||
msgid "This toggles the <code>gateway</code>/<code>nogateway</code> option for this interface in the dhcpcd.conf file."
|
msgid "This toggles the <code>gateway</code>/<code>nogateway</code> option for this interface in the dhcpcd.conf file."
|
||||||
msgstr ""
|
msgstr "Dies schaltet die Option <code>gateway</code>/<code>nogateway</code> für diese Schnittstelle in der dhcpcd.conf-Datei um."
|
||||||
|
|
||||||
msgid "This toggles the <code>nohook wpa_supplicant</code> option for this interface in the dhcpcd.conf file."
|
msgid "This toggles the <code>nohook wpa_supplicant</code> option for this interface in the dhcpcd.conf file."
|
||||||
msgstr ""
|
msgstr "Dies schaltet die Option <code>nohook wpa_supplicant</code> für diese Schnittstelle in der dhcpcd.conf-Datei um."
|
||||||
|
|
||||||
msgid "Disable wpa_supplicant dhcp hook for this interface"
|
msgid "Disable wpa_supplicant dhcp hook for this interface"
|
||||||
msgstr "Deaktiviere wpa_supplicant dhcp Hook für diese Schnittstelle"
|
msgstr "Deaktiviere wpa_supplicant dhcp Hook für diese Schnittstelle"
|
||||||
@@ -719,6 +743,27 @@ msgstr "Aktiviere Protokoll"
|
|||||||
msgid "Logfile output"
|
msgid "Logfile output"
|
||||||
msgstr "Logfile Ausgabe"
|
msgstr "Logfile Ausgabe"
|
||||||
|
|
||||||
|
msgid "Log level"
|
||||||
|
msgstr "Log-Stufe"
|
||||||
|
|
||||||
|
msgid "Higher levels reduce log verbosity. Informational is recommended."
|
||||||
|
msgstr "Höhere Stufen reduzieren die Ausführlichkeit des Protokolls. Informational wird empfohlen."
|
||||||
|
|
||||||
|
msgid "Verbose debugging"
|
||||||
|
msgstr "Ausführliche Fehlersuche"
|
||||||
|
|
||||||
|
msgid "Debugging"
|
||||||
|
msgstr "Fehlersuche"
|
||||||
|
|
||||||
|
msgid "Informational"
|
||||||
|
msgstr "Informativ"
|
||||||
|
|
||||||
|
msgid "Notification"
|
||||||
|
msgstr "Benachrichtigung"
|
||||||
|
|
||||||
|
msgid "Warning"
|
||||||
|
msgstr "Warnung"
|
||||||
|
|
||||||
msgid "WiFi client AP mode"
|
msgid "WiFi client AP mode"
|
||||||
msgstr "AP-Modus von WiFI-Client"
|
msgstr "AP-Modus von WiFI-Client"
|
||||||
|
|
||||||
@@ -729,7 +774,7 @@ msgid "WiFi repeater mode"
|
|||||||
msgstr "WLAN-Verstärker Modus"
|
msgstr "WLAN-Verstärker Modus"
|
||||||
|
|
||||||
msgid "Dual band AP mode"
|
msgid "Dual band AP mode"
|
||||||
msgstr ""
|
msgstr "Dual-Band-AP-Modus"
|
||||||
|
|
||||||
msgid "Hide SSID in broadcast"
|
msgid "Hide SSID in broadcast"
|
||||||
msgstr "SSID in der Übertragung ausblenden"
|
msgstr "SSID in der Übertragung ausblenden"
|
||||||
@@ -921,8 +966,11 @@ msgstr "DNS-Server wechseln"
|
|||||||
msgid "Adapter IP Address Settings"
|
msgid "Adapter IP Address Settings"
|
||||||
msgstr "IP-Adressen Einstellungen für Adapter"
|
msgstr "IP-Adressen Einstellungen für Adapter"
|
||||||
|
|
||||||
msgid "Enable Fallback to Static Option"
|
msgid "Enable fallback to static option"
|
||||||
msgstr "Aktiviere Fallback zur Statischen Option"
|
msgstr "Fallback auf statische Option aktivieren"
|
||||||
|
|
||||||
|
msgid "Enable this option to configure a static profile and fall back to it when DHCP lease fails."
|
||||||
|
msgstr "Aktivieren Sie dies nur, wenn Ihr Gerät diese Schnittstelle als primäre Route zum Internet verwenden soll."
|
||||||
|
|
||||||
msgid "Static IP"
|
msgid "Static IP"
|
||||||
msgstr "Statische IP"
|
msgstr "Statische IP"
|
||||||
@@ -1078,7 +1126,7 @@ msgid "Attempting to disable routing between %s and %s interfaces"
|
|||||||
msgstr "Versuche Routing zwischen den Schnittstellen %s und %s zu deaktivieren"
|
msgstr "Versuche Routing zwischen den Schnittstellen %s und %s zu deaktivieren"
|
||||||
|
|
||||||
msgid "No default DHCP configuration exists for the %s interface"
|
msgid "No default DHCP configuration exists for the %s interface"
|
||||||
msgstr "Keine Standard-DHCP-Konfiguration für die Schnittstelle %s vorhanden"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Configure a static IP and DHCP for this interface in DHCP Server settings"
|
msgid "Configure a static IP and DHCP for this interface in DHCP Server settings"
|
||||||
msgstr "Konfigurieren Sie eine statische IP und DHCP für diese Schnittstelle in den DHCP-Server-Einstellungen"
|
msgstr "Konfigurieren Sie eine statische IP und DHCP für diese Schnittstelle in den DHCP-Server-Einstellungen"
|
||||||
@@ -1135,6 +1183,9 @@ msgstr "Genutzter Speicher"
|
|||||||
msgid "CPU Load"
|
msgid "CPU Load"
|
||||||
msgstr "CPU-Auslastung"
|
msgstr "CPU-Auslastung"
|
||||||
|
|
||||||
|
msgid "CPU Temp"
|
||||||
|
msgstr "CPU Temp"
|
||||||
|
|
||||||
msgid "Reboot"
|
msgid "Reboot"
|
||||||
msgstr "Neustarten"
|
msgstr "Neustarten"
|
||||||
|
|
||||||
@@ -1223,7 +1274,7 @@ msgid "System tools"
|
|||||||
msgstr "Systemwerkzeuge"
|
msgstr "Systemwerkzeuge"
|
||||||
|
|
||||||
msgid "To generate a system <a href=\"%s\">debug log</a>, click or tap the button below."
|
msgid "To generate a system <a href=\"%s\">debug log</a>, click or tap the button below."
|
||||||
msgstr "Um ein System-<a href=\"%s\">Debug-Protokoll</a> zu erstellen, klicken oder tippen Sie auf die Schaltfläche unten."
|
msgstr "."
|
||||||
|
|
||||||
msgid "Debug log information contains the RaspAP version, current state and configuration of AP related services, installed system packages, Linux kernel version and networking details. No passwords or other sensitive data are included."
|
msgid "Debug log information contains the RaspAP version, current state and configuration of AP related services, installed system packages, Linux kernel version and networking details. No passwords or other sensitive data are included."
|
||||||
msgstr "Debug-Protokollinformationen enthalten die RaspAP-Version, den aktuellen Status und die Konfiguration von AP-bezogenen Diensten, installierte Systempakete, Linux-Kernel-Version und Netzwerkdetails. Keine Passwörter oder andere sensible Daten sind enthalten."
|
msgstr "Debug-Protokollinformationen enthalten die RaspAP-Version, den aktuellen Status und die Konfiguration von AP-bezogenen Diensten, installierte Systempakete, Linux-Kernel-Version und Netzwerkdetails. Keine Passwörter oder andere sensible Daten sind enthalten."
|
||||||
@@ -1240,6 +1291,9 @@ msgstr "Größenbeschränkung für Diagnoseprotokoll (KB)"
|
|||||||
msgid "Changing log limit size to %s KB"
|
msgid "Changing log limit size to %s KB"
|
||||||
msgstr "Protokollgrößenbeschränkung wird auf %s KB geändert"
|
msgstr "Protokollgrößenbeschränkung wird auf %s KB geändert"
|
||||||
|
|
||||||
|
msgid "Information provided by raspap.sysinfo"
|
||||||
|
msgstr "Informationen von raspap.sysinfo"
|
||||||
|
|
||||||
msgid "The following user plugins are available to extend RaspAP's functionality."
|
msgid "The following user plugins are available to extend RaspAP's functionality."
|
||||||
msgstr "Die folgenden Benutzer-Plugins sind verfügbar, um die Funktionalität von RaspAP zu erweitern."
|
msgstr "Die folgenden Benutzer-Plugins sind verfügbar, um die Funktionalität von RaspAP zu erweitern."
|
||||||
|
|
||||||
@@ -1291,14 +1345,17 @@ msgstr "Konfigurationsdateien"
|
|||||||
msgid "Dependencies"
|
msgid "Dependencies"
|
||||||
msgstr "Abhängigkeiten"
|
msgstr "Abhängigkeiten"
|
||||||
|
|
||||||
|
msgid "Permissions"
|
||||||
|
msgstr "Berechtigungen"
|
||||||
|
|
||||||
msgid "Non-privileged users"
|
msgid "Non-privileged users"
|
||||||
msgstr "Benutzer ohne Privilegien"
|
msgstr "Nicht-privilegierte Benutzer"
|
||||||
|
|
||||||
msgid "Install now"
|
msgid "Install now"
|
||||||
msgstr "Jetzt installieren"
|
msgstr "Installation beginnen"
|
||||||
|
|
||||||
msgid "Installing plugin"
|
msgid "Installing plugin"
|
||||||
msgstr "Plugin wird installiert"
|
msgstr "Plugin installieren"
|
||||||
|
|
||||||
msgid "Plugin installation in progress..."
|
msgid "Plugin installation in progress..."
|
||||||
msgstr "Plugin-Installation läuft..."
|
msgstr "Plugin-Installation läuft..."
|
||||||
@@ -1339,6 +1396,9 @@ msgstr "Adapter-Zustandsprüfung"
|
|||||||
msgid "Inspect adapters"
|
msgid "Inspect adapters"
|
||||||
msgstr "Adapter überprüfen"
|
msgstr "Adapter überprüfen"
|
||||||
|
|
||||||
|
msgid "Theme"
|
||||||
|
msgstr "Thema"
|
||||||
|
|
||||||
#: includes/data_usage.php
|
#: includes/data_usage.php
|
||||||
msgid "Data usage"
|
msgid "Data usage"
|
||||||
msgstr "Datennutzung"
|
msgstr "Datennutzung"
|
||||||
@@ -1825,7 +1885,7 @@ msgid "Enable Firewall"
|
|||||||
msgstr "Firewall aktivieren"
|
msgstr "Firewall aktivieren"
|
||||||
|
|
||||||
msgid "Changing the firewall status may disrupt or allow incoming traffic. Choose <strong>Proceed</strong> to continue."
|
msgid "Changing the firewall status may disrupt or allow incoming traffic. Choose <strong>Proceed</strong> to continue."
|
||||||
msgstr ""
|
msgstr "Das Ändern des Firewall-Status kann eingehenden Verkehr unterbrechen oder zulassen. Wählen Sie <strong>Fortfahren</strong>, um fortzufahren."
|
||||||
|
|
||||||
msgid "Proceed"
|
msgid "Proceed"
|
||||||
msgstr "Fortfahren"
|
msgstr "Fortfahren"
|
||||||
@@ -1840,7 +1900,7 @@ msgid "Service provider"
|
|||||||
msgstr "Dienstanbieter"
|
msgstr "Dienstanbieter"
|
||||||
|
|
||||||
msgid "Select a Dynamic DNS service supported by <strong>ddclient</strong> from the list below. Selecting a known service provider will populate the <code>protocol</code> and <code>server</code> fields. You may also configure the service manually."
|
msgid "Select a Dynamic DNS service supported by <strong>ddclient</strong> from the list below. Selecting a known service provider will populate the <code>protocol</code> and <code>server</code> fields. You may also configure the service manually."
|
||||||
msgstr ""
|
msgstr "Wählen Sie einen von <strong>ddclient</strong> unterstützten Dynamic-DNS-Dienst aus der Liste unten aus. Durch Auswahl eines bekannten Dienstanbieters werden die Felder <code>protocol</code> und <code>server</code> ausgefüllt. Sie können den Dienst auch manuell konfigurieren."
|
||||||
|
|
||||||
msgid "Method to obtain IP"
|
msgid "Method to obtain IP"
|
||||||
msgstr "Methode zum Abrufen der IP"
|
msgstr "Methode zum Abrufen der IP"
|
||||||
@@ -1908,6 +1968,15 @@ msgstr "Dynamisches DNS stoppen"
|
|||||||
msgid "Restart Dynamic DNS"
|
msgid "Restart Dynamic DNS"
|
||||||
msgstr "Dynamisches DNS neu starten"
|
msgstr "Dynamisches DNS neu starten"
|
||||||
|
|
||||||
|
msgid "Account details"
|
||||||
|
msgstr "Kontodetails"
|
||||||
|
|
||||||
|
msgid "My account"
|
||||||
|
msgstr "Mein Benutzerkonto"
|
||||||
|
|
||||||
|
msgid "Server location"
|
||||||
|
msgstr "Server-Standort"
|
||||||
|
|
||||||
msgid "Choosing <strong>Save settings</strong> will connect to the selected country."
|
msgid "Choosing <strong>Save settings</strong> will connect to the selected country."
|
||||||
msgstr "Durch Auswahl von <strong>Einstellungen speichern</strong> wird eine Verbindung zum ausgewählten Land hergestellt."
|
msgstr "Durch Auswahl von <strong>Einstellungen speichern</strong> wird eine Verbindung zum ausgewählten Land hergestellt."
|
||||||
|
|
||||||
@@ -1923,6 +1992,15 @@ msgstr "Wählen Sie ein Land..."
|
|||||||
msgid "Account information not available from %s's Linux CLI."
|
msgid "Account information not available from %s's Linux CLI."
|
||||||
msgstr "Kontoinformationen sind über die Linux-CLI von %s nicht verfügbar."
|
msgstr "Kontoinformationen sind über die Linux-CLI von %s nicht verfügbar."
|
||||||
|
|
||||||
|
msgid "Attempting to connect to %s"
|
||||||
|
msgstr "Versuche mit %s zu verbinden"
|
||||||
|
|
||||||
|
msgid "Attempting to connect VPN provider"
|
||||||
|
msgstr "Versuche VPN-Anbieter zu verbinden"
|
||||||
|
|
||||||
|
msgid "Attempting to disconnect VPN provider"
|
||||||
|
msgstr "Versuche VPN-Anbieter zu trennen"
|
||||||
|
|
||||||
msgid "Expected %s binary not found at: %s"
|
msgid "Expected %s binary not found at: %s"
|
||||||
msgstr "Erwartete %s-Binärdatei nicht gefunden unter: %s"
|
msgstr "Erwartete %s-Binärdatei nicht gefunden unter: %s"
|
||||||
|
|
||||||
@@ -2016,6 +2094,27 @@ msgstr "Update abgeschlossen"
|
|||||||
msgid "An error occurred. Check the log at <code>/tmp/raspap_install.log</code>"
|
msgid "An error occurred. Check the log at <code>/tmp/raspap_install.log</code>"
|
||||||
msgstr "Ein Fehler ist aufgetreten. Überprüfen Sie das Protokoll unter <code>/tmp/raspap_install.log</code>"
|
msgstr "Ein Fehler ist aufgetreten. Überprüfen Sie das Protokoll unter <code>/tmp/raspap_install.log</code>"
|
||||||
|
|
||||||
|
msgid "RaspAP is a co-creation of %1$s and %2$s with the contributions of our %3$s and %4$s. Learn more about joining the project as a %5$s, %6$s or %7$s with immediate access to %8$s available to %9$s."
|
||||||
|
msgstr "RaspAP ist eine gemeinsame Kreation von %1$s und %2$s mit den Beiträgen unserer %3$s und %4$s. Erfahren Sie mehr darüber, wie Sie dem Projekt als %5$s, %6$s oder %7$s beitreten können, und erhalten Sie sofortigen Zugang zu %8$s, der für %9$sverfügbar ist."
|
||||||
|
|
||||||
|
msgid "developer community"
|
||||||
|
msgstr "Entwickler Community"
|
||||||
|
|
||||||
|
msgid "language translators"
|
||||||
|
msgstr "Sprachübersetzer"
|
||||||
|
|
||||||
|
msgid "code contributor"
|
||||||
|
msgstr "Code-Beitragender"
|
||||||
|
|
||||||
|
msgid "translator"
|
||||||
|
msgstr "Übersetzer"
|
||||||
|
|
||||||
|
msgid "financial sponsor"
|
||||||
|
msgstr "Finanzieller Sponsor"
|
||||||
|
|
||||||
|
msgid "exclusive features"
|
||||||
|
msgstr "Exklusive Funktionen"
|
||||||
|
|
||||||
msgid "RaspAP Exception"
|
msgid "RaspAP Exception"
|
||||||
msgstr "RaspAP-Ausnahme"
|
msgstr "RaspAP-Ausnahme"
|
||||||
|
|
||||||
@@ -2112,8 +2211,8 @@ msgstr "Öffentliche NTP-Server, die Network Time Security (NTS) unterstützen,
|
|||||||
msgid "Examples of valid server entries include <code>%s</code>, <code>%s</code> and <code>%s</code>."
|
msgid "Examples of valid server entries include <code>%s</code>, <code>%s</code> and <code>%s</code>."
|
||||||
msgstr "Beispiele für gültige Servereinträge sind <code>%s</code>, <code>%s</code> und <code>%s</code>."
|
msgstr "Beispiele für gültige Servereinträge sind <code>%s</code>, <code>%s</code> und <code>%s</code>."
|
||||||
|
|
||||||
msgid "Current <code>ntpq peer</code> status is displayed below. An asterisk (<code></code>) indicates the preferred server."
|
msgid "Current <code>ntpq peer</code> status is displayed below. An asterisk (<code>*</code>) indicates the preferred server."
|
||||||
msgstr "Der aktuelle Status von <code>ntpq peer</code> wird unten angezeigt. Ein Sternchen (<code></code>) kennzeichnet den bevorzugten Server."
|
msgstr "Der aktuelle Status von <code>ntpq peer</code> wird unten angezeigt. Ein Sternchen (<code>*</code>) kennzeichnet den bevorzugten Server."
|
||||||
|
|
||||||
msgid "NTP configuration cannot be empty"
|
msgid "NTP configuration cannot be empty"
|
||||||
msgstr "NTP-Konfiguration darf nicht leer sein"
|
msgstr "NTP-Konfiguration darf nicht leer sein"
|
||||||
@@ -2517,8 +2616,8 @@ msgstr "Das Ändern des Portaldienstes wird den Client-Verkehr kurzzeitig unterb
|
|||||||
msgid "Interface to be managed by the portal"
|
msgid "Interface to be managed by the portal"
|
||||||
msgstr "Vom Portal zu verwaltende Schnittstelle"
|
msgstr "Vom Portal zu verwaltende Schnittstelle"
|
||||||
|
|
||||||
msgid "Name of your gateway (available as $gatewayname variable)"
|
msgid "Name of your gateway (available as \\$gatewayname variable)"
|
||||||
msgstr "Name Ihres Gateways (verfügbar als $gatewayname Variable)"
|
msgstr "Name Ihres Gateways (verfügbar als \\\\$gatewayname Variable)"
|
||||||
|
|
||||||
msgid "IP address of the router. Leave empty for auto-detection"
|
msgid "IP address of the router. Leave empty for auto-detection"
|
||||||
msgstr "IP-Adresse des Routers. Leer lassen für automatische Erkennung"
|
msgstr "IP-Adresse des Routers. Leer lassen für automatische Erkennung"
|
||||||
@@ -2636,3 +2735,4 @@ msgstr "Portal-Status"
|
|||||||
|
|
||||||
msgid "Current <code>nodogsplash</code> status is displayed below."
|
msgid "Current <code>nodogsplash</code> status is displayed below."
|
||||||
msgstr "Der aktuelle <code>nodogsplash</code>-Status wird unten angezeigt."
|
msgstr "Der aktuelle <code>nodogsplash</code>-Status wird unten angezeigt."
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -10,7 +10,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: 1.2.1\n"
|
"Project-Id-Version: 1.2.1\n"
|
||||||
"Report-Msgid-Bugs-To: Bill Zimmerman <billzimmerman@gmail.com>\n"
|
"Report-Msgid-Bugs-To: Bill Zimmerman <billzimmerman@gmail.com>\n"
|
||||||
"POT-Creation-Date: 2017-10-19 08:56+0000\n"
|
"POT-Creation-Date: 2017-10-19 08:56+0000\n"
|
||||||
"PO-Revision-Date: 2025-10-20 08:35+0000\n"
|
"PO-Revision-Date: 2025-10-25 08:35+0000\n"
|
||||||
"Last-Translator: Bill Zimmerman <billzimmerman@gmail.com>\n"
|
"Last-Translator: Bill Zimmerman <billzimmerman@gmail.com>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"Language: en_US\n"
|
"Language: en_US\n"
|
||||||
@@ -34,9 +34,15 @@ msgstr "Dashboard"
|
|||||||
msgid "WiFi client"
|
msgid "WiFi client"
|
||||||
msgstr "WiFi client"
|
msgstr "WiFi client"
|
||||||
|
|
||||||
|
msgid "Status"
|
||||||
|
msgstr "Status"
|
||||||
|
|
||||||
msgid "Hotspot"
|
msgid "Hotspot"
|
||||||
msgstr "Hotspot"
|
msgstr "Hotspot"
|
||||||
|
|
||||||
|
msgid "Logging"
|
||||||
|
msgstr "Logging"
|
||||||
|
|
||||||
msgid "Mem Use"
|
msgid "Mem Use"
|
||||||
msgstr "Mem Use"
|
msgstr "Mem Use"
|
||||||
|
|
||||||
@@ -440,6 +446,24 @@ msgstr "Thanks for being an Insider"
|
|||||||
msgid "DHCP server settings"
|
msgid "DHCP server settings"
|
||||||
msgstr "DHCP server settings"
|
msgstr "DHCP server settings"
|
||||||
|
|
||||||
|
msgid "Default gateway"
|
||||||
|
msgstr "Default gateway"
|
||||||
|
|
||||||
|
msgid "Install a default route for this interface"
|
||||||
|
msgstr "Install a default route for this interface"
|
||||||
|
|
||||||
|
msgid "Static IP options"
|
||||||
|
msgstr "Static IP options"
|
||||||
|
|
||||||
|
msgid "DHCP options"
|
||||||
|
msgstr "DHCP options"
|
||||||
|
|
||||||
|
msgid "Enable this option if you want RaspAP to assign IP addresses to clients on the selected interface. A static IP address is required for this option."
|
||||||
|
msgstr "Enable this option if you want RaspAP to assign IP addresses to clients on the selected interface. A static IP address is required for this option."
|
||||||
|
|
||||||
|
msgid "Enable this only if you want your device to use this interface as its primary route to the internet."
|
||||||
|
msgstr "Enable this only if you want your device to use this interface as its primary route to the internet."
|
||||||
|
|
||||||
msgid "Client list"
|
msgid "Client list"
|
||||||
msgstr "Client list"
|
msgstr "Client list"
|
||||||
|
|
||||||
@@ -720,6 +744,27 @@ msgstr "Enable logging"
|
|||||||
msgid "Logfile output"
|
msgid "Logfile output"
|
||||||
msgstr "Logfile output"
|
msgstr "Logfile output"
|
||||||
|
|
||||||
|
msgid "Log level"
|
||||||
|
msgstr "Log level"
|
||||||
|
|
||||||
|
msgid "Higher levels reduce log verbosity. Informational is recommended."
|
||||||
|
msgstr "Higher levels reduce log verbosity. Informational is recommended."
|
||||||
|
|
||||||
|
msgid "Verbose debugging"
|
||||||
|
msgstr "Verbose debugging"
|
||||||
|
|
||||||
|
msgid "Debugging"
|
||||||
|
msgstr "Debugging"
|
||||||
|
|
||||||
|
msgid "Informational"
|
||||||
|
msgstr "Informational"
|
||||||
|
|
||||||
|
msgid "Notification"
|
||||||
|
msgstr "Notification"
|
||||||
|
|
||||||
|
msgid "Warning"
|
||||||
|
msgstr "Warning"
|
||||||
|
|
||||||
msgid "WiFi client AP mode"
|
msgid "WiFi client AP mode"
|
||||||
msgstr "WiFi client AP mode"
|
msgstr "WiFi client AP mode"
|
||||||
|
|
||||||
@@ -922,8 +967,11 @@ msgstr "Alternate DNS Server"
|
|||||||
msgid "Adapter IP Address Settings"
|
msgid "Adapter IP Address Settings"
|
||||||
msgstr "Adapter IP Address Settings"
|
msgstr "Adapter IP Address Settings"
|
||||||
|
|
||||||
msgid "Enable Fallback to Static Option"
|
msgid "Enable fallback to static option"
|
||||||
msgstr "Enable Fallback to Static Option"
|
msgstr "Enable fallback to static option"
|
||||||
|
|
||||||
|
msgid "Enable this option to configure a static profile and fall back to it when DHCP lease fails."
|
||||||
|
msgstr "Enable this option to configure a static profile and fall back to it when DHCP lease fails."
|
||||||
|
|
||||||
msgid "Static IP"
|
msgid "Static IP"
|
||||||
msgstr "Static IP"
|
msgstr "Static IP"
|
||||||
@@ -1136,6 +1184,9 @@ msgstr "Storage Used"
|
|||||||
msgid "CPU Load"
|
msgid "CPU Load"
|
||||||
msgstr "CPU Load"
|
msgstr "CPU Load"
|
||||||
|
|
||||||
|
msgid "CPU Temp"
|
||||||
|
msgstr "CPU Temp"
|
||||||
|
|
||||||
msgid "Reboot"
|
msgid "Reboot"
|
||||||
msgstr "Reboot"
|
msgstr "Reboot"
|
||||||
|
|
||||||
@@ -1346,6 +1397,9 @@ msgstr "Adapter health check"
|
|||||||
msgid "Inspect adapters"
|
msgid "Inspect adapters"
|
||||||
msgstr "Inspect adapters"
|
msgstr "Inspect adapters"
|
||||||
|
|
||||||
|
msgid "Theme"
|
||||||
|
msgstr "Theme"
|
||||||
|
|
||||||
#: includes/data_usage.php
|
#: includes/data_usage.php
|
||||||
msgid "Data usage"
|
msgid "Data usage"
|
||||||
msgstr "Data usage"
|
msgstr "Data usage"
|
||||||
@@ -2053,6 +2107,27 @@ msgstr "Update complete"
|
|||||||
msgid "An error occurred. Check the log at <code>/tmp/raspap_install.log</code>"
|
msgid "An error occurred. Check the log at <code>/tmp/raspap_install.log</code>"
|
||||||
msgstr "An error occurred. Check the log at <code>/tmp/raspap_install.log</code>"
|
msgstr "An error occurred. Check the log at <code>/tmp/raspap_install.log</code>"
|
||||||
|
|
||||||
|
msgid "RaspAP is a co-creation of %1$s and %2$s with the contributions of our %3$s and %4$s. Learn more about joining the project as a %5$s, %6$s or %7$s with immediate access to %8$s available to %9$s."
|
||||||
|
msgstr "RaspAP is a co-creation of %1$s and %2$s with the contributions of our %3$s and %4$s. Learn more about joining the project as a %5$s, %6$s or %7$s with immediate access to %8$s available to %9$s."
|
||||||
|
|
||||||
|
msgid "developer community"
|
||||||
|
msgstr "developer community"
|
||||||
|
|
||||||
|
msgid "language translators"
|
||||||
|
msgstr "language translators"
|
||||||
|
|
||||||
|
msgid "code contributor"
|
||||||
|
msgstr "code contributor"
|
||||||
|
|
||||||
|
msgid "translator"
|
||||||
|
msgstr "translator"
|
||||||
|
|
||||||
|
msgid "financial sponsor"
|
||||||
|
msgstr "financial sponsor"
|
||||||
|
|
||||||
|
msgid "exclusive features"
|
||||||
|
msgstr "exclusive features"
|
||||||
|
|
||||||
#: includes/exceptions.php
|
#: includes/exceptions.php
|
||||||
|
|
||||||
msgid "RaspAP Exception"
|
msgid "RaspAP Exception"
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: raspap\n"
|
"Project-Id-Version: raspap\n"
|
||||||
"Report-Msgid-Bugs-To: Bill Zimmerman <billzimmerman@gmail.com>\n"
|
"Report-Msgid-Bugs-To: Bill Zimmerman <billzimmerman@gmail.com>\n"
|
||||||
"POT-Creation-Date: 2017-10-19 08:56+0000\n"
|
"POT-Creation-Date: 2017-10-19 08:56+0000\n"
|
||||||
"PO-Revision-Date: 2025-10-22 14:14\n"
|
"PO-Revision-Date: 2025-11-20 12:20\n"
|
||||||
"Last-Translator: Bill Zimmerman <billzimmerman@gmail.com>\n"
|
"Last-Translator: Bill Zimmerman <billzimmerman@gmail.com>\n"
|
||||||
"Language-Team: French\n"
|
"Language-Team: French\n"
|
||||||
"Language: fr_FR\n"
|
"Language: fr_FR\n"
|
||||||
@@ -33,9 +33,15 @@ msgstr "Tableau de bord"
|
|||||||
msgid "WiFi client"
|
msgid "WiFi client"
|
||||||
msgstr "Client WiFi"
|
msgstr "Client WiFi"
|
||||||
|
|
||||||
|
msgid "Status"
|
||||||
|
msgstr "État"
|
||||||
|
|
||||||
msgid "Hotspot"
|
msgid "Hotspot"
|
||||||
msgstr "Hotspot"
|
msgstr "Hotspot"
|
||||||
|
|
||||||
|
msgid "Logging"
|
||||||
|
msgstr "Journalisation"
|
||||||
|
|
||||||
msgid "Mem Use"
|
msgid "Mem Use"
|
||||||
msgstr "Mémoire"
|
msgstr "Mémoire"
|
||||||
|
|
||||||
@@ -439,6 +445,24 @@ msgstr "Merci d'être un Insider"
|
|||||||
msgid "DHCP server settings"
|
msgid "DHCP server settings"
|
||||||
msgstr "Paramètres du serveur DHCP"
|
msgstr "Paramètres du serveur DHCP"
|
||||||
|
|
||||||
|
msgid "Default gateway"
|
||||||
|
msgstr "Passerelle par défaut"
|
||||||
|
|
||||||
|
msgid "Install a default route for this interface"
|
||||||
|
msgstr "Installer une route par défaut pour cette interface"
|
||||||
|
|
||||||
|
msgid "Static IP options"
|
||||||
|
msgstr "Options d'IP statique"
|
||||||
|
|
||||||
|
msgid "DHCP options"
|
||||||
|
msgstr "Options DHCP"
|
||||||
|
|
||||||
|
msgid "Enable this option if you want RaspAP to assign IP addresses to clients on the selected interface. A static IP address is required for this option."
|
||||||
|
msgstr "Activez cette option si vous souhaitez que RaspAP attribue des adresses IP aux clients sur l'interface sélectionnée. Une adresse IP statique est requise pour cette option."
|
||||||
|
|
||||||
|
msgid "Enable this only if you want your device to use this interface as its primary route to the internet."
|
||||||
|
msgstr "Activez cette option uniquement si vous voulez que votre appareil utilise cette interface comme route principale vers Internet."
|
||||||
|
|
||||||
msgid "Client list"
|
msgid "Client list"
|
||||||
msgstr "Liste des clients"
|
msgstr "Liste des clients"
|
||||||
|
|
||||||
@@ -719,6 +743,27 @@ msgstr "Activer la journalisation"
|
|||||||
msgid "Logfile output"
|
msgid "Logfile output"
|
||||||
msgstr "Sortie de journal"
|
msgstr "Sortie de journal"
|
||||||
|
|
||||||
|
msgid "Log level"
|
||||||
|
msgstr "Niveau d'enregistrement"
|
||||||
|
|
||||||
|
msgid "Higher levels reduce log verbosity. Informational is recommended."
|
||||||
|
msgstr "Des niveaux plus élevés réduisent la verbosité des journaux. Il est recommandé d'opter pour un niveau d'information."
|
||||||
|
|
||||||
|
msgid "Verbose debugging"
|
||||||
|
msgstr "Débogage verbeux"
|
||||||
|
|
||||||
|
msgid "Debugging"
|
||||||
|
msgstr "Débogage"
|
||||||
|
|
||||||
|
msgid "Informational"
|
||||||
|
msgstr "Information"
|
||||||
|
|
||||||
|
msgid "Notification"
|
||||||
|
msgstr "Notification"
|
||||||
|
|
||||||
|
msgid "Warning"
|
||||||
|
msgstr "Avertissement"
|
||||||
|
|
||||||
msgid "WiFi client AP mode"
|
msgid "WiFi client AP mode"
|
||||||
msgstr "Mode AP client WiFi"
|
msgstr "Mode AP client WiFi"
|
||||||
|
|
||||||
@@ -921,9 +966,12 @@ msgstr "Serveur DNS alternatif"
|
|||||||
msgid "Adapter IP Address Settings"
|
msgid "Adapter IP Address Settings"
|
||||||
msgstr "Paramètres d'adresse IP de l'adaptateur"
|
msgstr "Paramètres d'adresse IP de l'adaptateur"
|
||||||
|
|
||||||
msgid "Enable Fallback to Static Option"
|
msgid "Enable fallback to static option"
|
||||||
msgstr "Activer la repli vers l'option statique"
|
msgstr "Activer la repli vers l'option statique"
|
||||||
|
|
||||||
|
msgid "Enable this option to configure a static profile and fall back to it when DHCP lease fails."
|
||||||
|
msgstr "Activer cette option pour configurer un profil statique et y revenir lorsque le bail DHCP échoue."
|
||||||
|
|
||||||
msgid "Static IP"
|
msgid "Static IP"
|
||||||
msgstr "IP statique"
|
msgstr "IP statique"
|
||||||
|
|
||||||
@@ -949,7 +997,7 @@ msgid "Devices"
|
|||||||
msgstr "Appareils"
|
msgstr "Appareils"
|
||||||
|
|
||||||
msgid "Diagnostics"
|
msgid "Diagnostics"
|
||||||
msgstr "Diagnostics"
|
msgstr "Diagnostiques"
|
||||||
|
|
||||||
msgid "Network devices"
|
msgid "Network devices"
|
||||||
msgstr "Appareils réseau"
|
msgstr "Appareils réseau"
|
||||||
@@ -1135,6 +1183,9 @@ msgstr "Stockage utilisé"
|
|||||||
msgid "CPU Load"
|
msgid "CPU Load"
|
||||||
msgstr "Charge du processeur"
|
msgstr "Charge du processeur"
|
||||||
|
|
||||||
|
msgid "CPU Temp"
|
||||||
|
msgstr "Temp. CPU"
|
||||||
|
|
||||||
msgid "Reboot"
|
msgid "Reboot"
|
||||||
msgstr "Redémarrer"
|
msgstr "Redémarrer"
|
||||||
|
|
||||||
@@ -1345,9 +1396,12 @@ msgstr "Vérification de l'état de l'adaptateur"
|
|||||||
msgid "Inspect adapters"
|
msgid "Inspect adapters"
|
||||||
msgstr "Inspecter les adaptateurs"
|
msgstr "Inspecter les adaptateurs"
|
||||||
|
|
||||||
|
msgid "Theme"
|
||||||
|
msgstr "Thème"
|
||||||
|
|
||||||
#: includes/data_usage.php
|
#: includes/data_usage.php
|
||||||
msgid "Data usage"
|
msgid "Data usage"
|
||||||
msgstr "Données utilisées"
|
msgstr "Utilisation des données"
|
||||||
|
|
||||||
msgid "Data usage monitoring"
|
msgid "Data usage monitoring"
|
||||||
msgstr "Surveillance de l'utilisation des données"
|
msgstr "Surveillance de l'utilisation des données"
|
||||||
@@ -2040,6 +2094,27 @@ msgstr "Mise à jour terminée"
|
|||||||
msgid "An error occurred. Check the log at <code>/tmp/raspap_install.log</code>"
|
msgid "An error occurred. Check the log at <code>/tmp/raspap_install.log</code>"
|
||||||
msgstr "Une erreur s'est produite. Vérifiez le journal à <code>/tmp/raspap_install.log</code>"
|
msgstr "Une erreur s'est produite. Vérifiez le journal à <code>/tmp/raspap_install.log</code>"
|
||||||
|
|
||||||
|
msgid "RaspAP is a co-creation of %1$s and %2$s with the contributions of our %3$s and %4$s. Learn more about joining the project as a %5$s, %6$s or %7$s with immediate access to %8$s available to %9$s."
|
||||||
|
msgstr "RaspAP est une co-création de %1$s et %2$s avec les contributions de nos %3$s et %4$s. Pour en savoir plus sur la façon de rejoindre le projet en tant que %5$s, %6$s ou %7$s avec un accès immédiat à %8$s disponible à %9$s."
|
||||||
|
|
||||||
|
msgid "developer community"
|
||||||
|
msgstr "communauté des développeurs"
|
||||||
|
|
||||||
|
msgid "language translators"
|
||||||
|
msgstr "traducteurs de langues"
|
||||||
|
|
||||||
|
msgid "code contributor"
|
||||||
|
msgstr "contributeur du code"
|
||||||
|
|
||||||
|
msgid "translator"
|
||||||
|
msgstr "traducteur"
|
||||||
|
|
||||||
|
msgid "financial sponsor"
|
||||||
|
msgstr "sponsor financier"
|
||||||
|
|
||||||
|
msgid "exclusive features"
|
||||||
|
msgstr "fonctionnalités exclusives"
|
||||||
|
|
||||||
msgid "RaspAP Exception"
|
msgid "RaspAP Exception"
|
||||||
msgstr "Exception RaspAP"
|
msgstr "Exception RaspAP"
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
2
plugins
2
plugins
Submodule plugins updated: deb689143e...9236df6139
258
src/RaspAP/Localization/LocaleManager.php
Executable file
258
src/RaspAP/Localization/LocaleManager.php
Executable file
@@ -0,0 +1,258 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locale Manager class
|
||||||
|
*
|
||||||
|
* @description Manages locale/i18n functionality for RaspAP
|
||||||
|
* @author Bill Zimmerman <billzimmerman@gmail.com>
|
||||||
|
* @license https://github.com/raspap/raspap-webgui/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace RaspAP\Localization;
|
||||||
|
|
||||||
|
class LocaleManager
|
||||||
|
{
|
||||||
|
private string $locale;
|
||||||
|
private const DEFAULT_LOCALE = 'en_GB.UTF-8';
|
||||||
|
private const COOKIE_LIFETIME = 86400 * 30; // 30 days
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->locale = self::DEFAULT_LOCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all available locales
|
||||||
|
*
|
||||||
|
* @return array Associative array of locale codes to language names
|
||||||
|
*/
|
||||||
|
public function getLocales(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'en_GB.UTF-8' => 'English',
|
||||||
|
'cs_CZ.UTF-8' => 'Čeština',
|
||||||
|
'zh_TW.UTF-8' => '正體中文 (Chinese traditional)',
|
||||||
|
'zh_CN.UTF-8' => '简体中文 (Chinese simplified)',
|
||||||
|
'da_DK.UTF-8' => 'Dansk',
|
||||||
|
'de_DE.UTF-8' => 'Deutsch',
|
||||||
|
'es_MX.UTF-8' => 'Español',
|
||||||
|
'fi_FI.UTF-8' => 'Finnish',
|
||||||
|
'fr_FR.UTF-8' => 'Français',
|
||||||
|
'el_GR.UTF-8' => 'Ελληνικά',
|
||||||
|
'id_ID.UTF-8' => 'Indonesian',
|
||||||
|
'it_IT.UTF-8' => 'Italiano',
|
||||||
|
'ja_JP.UTF-8' => '日本語 (Japanese)',
|
||||||
|
'ko_KR.UTF-8' => '한국어 (Korean)',
|
||||||
|
'nl_NL.UTF-8' => 'Nederlands',
|
||||||
|
'pl_PL.UTF-8' => 'Polskie',
|
||||||
|
'pt_BR.UTF-8' => 'Português',
|
||||||
|
'ru_RU.UTF-8' => 'Русский',
|
||||||
|
'ro_RO.UTF-8' => 'Română',
|
||||||
|
'sk_SK.UTF-8' => 'Slovenčina',
|
||||||
|
'sv_SE.UTF-8' => 'Svenska',
|
||||||
|
'tr_TR.UTF-8' => 'Türkçe',
|
||||||
|
'vi_VN.UTF-8' => 'Tiếng Việt (Vietnamese)'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get valid locale codes
|
||||||
|
*
|
||||||
|
* @return array Array of valid locale codes
|
||||||
|
*/
|
||||||
|
public function getValidLocales(): array
|
||||||
|
{
|
||||||
|
return array_keys($this->getLocales());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect browser locale from Accept-Language header
|
||||||
|
*
|
||||||
|
* Language detection is performed via the browser.
|
||||||
|
* Accept-Language returns a list of weighted values with a quality (or 'q') parameter.
|
||||||
|
* A better method would parse the list of preferred languages and match this with
|
||||||
|
* the languages supported by our platform.
|
||||||
|
*
|
||||||
|
* @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
|
||||||
|
* @return string Detected locale code
|
||||||
|
*/
|
||||||
|
public function detectBrowserLocale(): string
|
||||||
|
{
|
||||||
|
if (empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) || strlen($_SERVER['HTTP_ACCEPT_LANGUAGE']) < 2) {
|
||||||
|
return self::DEFAULT_LOCALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$acceptLang = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
||||||
|
$lang = strtolower(substr($acceptLang, 0, 2));
|
||||||
|
|
||||||
|
if ($lang === 'zh' && strpos($acceptLang, 'zh-TW') === 0) {
|
||||||
|
return 'zh_TW.UTF-8';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($lang) {
|
||||||
|
case 'de':
|
||||||
|
return 'de_DE.UTF-8';
|
||||||
|
case 'fr':
|
||||||
|
return 'fr_FR.UTF-8';
|
||||||
|
case 'it':
|
||||||
|
return 'it_IT.UTF-8';
|
||||||
|
case 'pt':
|
||||||
|
return 'pt_BR.UTF-8';
|
||||||
|
case 'sv':
|
||||||
|
return 'sv_SE.UTF-8';
|
||||||
|
case 'nl':
|
||||||
|
return 'nl_NL.UTF-8';
|
||||||
|
case 'zh':
|
||||||
|
return 'zh_CN.UTF-8';
|
||||||
|
case 'cs':
|
||||||
|
return 'cs_CZ.UTF-8';
|
||||||
|
case 'ru':
|
||||||
|
return 'ru_RU.UTF-8';
|
||||||
|
case 'es':
|
||||||
|
return 'es_MX.UTF-8';
|
||||||
|
case 'fi':
|
||||||
|
return 'fi_FI.UTF-8';
|
||||||
|
case 'da':
|
||||||
|
return 'da_DK.UTF-8';
|
||||||
|
case 'tr':
|
||||||
|
return 'tr_TR.UTF-8';
|
||||||
|
case 'id':
|
||||||
|
return 'id_ID.UTF-8';
|
||||||
|
case 'ko':
|
||||||
|
return 'ko_KR.UTF-8';
|
||||||
|
case 'ja':
|
||||||
|
return 'ja_JP.UTF-8';
|
||||||
|
case 'vi':
|
||||||
|
return 'vi_VN.UTF-8';
|
||||||
|
case 'el':
|
||||||
|
return 'el_GR.UTF-8';
|
||||||
|
case 'pl':
|
||||||
|
return 'pl_PL.UTF-8';
|
||||||
|
case 'sk':
|
||||||
|
return 'sk_SK.UTF-8';
|
||||||
|
default:
|
||||||
|
return self::DEFAULT_LOCALE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate if a locale code is valid
|
||||||
|
*
|
||||||
|
* Uses $validLocales to mitigate OS Command Injection (CWE-78)
|
||||||
|
* @see Vulnerability Report for JVN #27202136
|
||||||
|
*
|
||||||
|
* @param string $locale Locale code to validate
|
||||||
|
* @return bool True if valid, false otherwise
|
||||||
|
*/
|
||||||
|
public function isValidLocale(string $locale): bool
|
||||||
|
{
|
||||||
|
return in_array($locale, $this->getValidLocales(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current locale
|
||||||
|
*
|
||||||
|
* @param string $locale Locale code to set
|
||||||
|
* @return bool true if locale was set, false if invalid
|
||||||
|
*/
|
||||||
|
public function setLocale(string $locale): bool
|
||||||
|
{
|
||||||
|
if (!$this->isValidLocale($locale)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->locale = $locale;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current locale
|
||||||
|
*
|
||||||
|
* @return string Current locale code
|
||||||
|
*/
|
||||||
|
public function getCurrentLocale(): string
|
||||||
|
{
|
||||||
|
return $this->locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize locale
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function initialize(): void
|
||||||
|
{
|
||||||
|
$validLocales = $this->getValidLocales();
|
||||||
|
|
||||||
|
// Set locale from POST, if provided and valid
|
||||||
|
if (!empty($_POST['locale']) && in_array($_POST['locale'], $validLocales, true)) {
|
||||||
|
$_SESSION['locale'] = $_POST['locale'];
|
||||||
|
$this->setLocaleCookie($_POST['locale']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set locale from cookie or browser detection, if not already set in session
|
||||||
|
if (empty($_SESSION['locale'])) {
|
||||||
|
if (isset($_COOKIE['locale']) && in_array($_COOKIE['locale'], $validLocales, true)) {
|
||||||
|
$_SESSION['locale'] = $_COOKIE['locale'];
|
||||||
|
} else {
|
||||||
|
$_SESSION['locale'] = $this->detectBrowserLocale();
|
||||||
|
$this->setLocaleCookie($_SESSION['locale']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce only valid locale values in session
|
||||||
|
if (!in_array($_SESSION['locale'], $validLocales, true)) {
|
||||||
|
$_SESSION['locale'] = self::DEFAULT_LOCALE;
|
||||||
|
$this->setLocaleCookie($_SESSION['locale']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update internal state
|
||||||
|
$this->locale = $_SESSION['locale'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply locale settings to the system
|
||||||
|
*
|
||||||
|
* Sets environment variables and configures gettext
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function applyLocale(): void
|
||||||
|
{
|
||||||
|
putenv("LANG=" . escapeshellarg($this->locale));
|
||||||
|
setlocale(LC_ALL, $this->locale);
|
||||||
|
|
||||||
|
// Use global constants if defined, otherwise use defaults
|
||||||
|
$localeDomain = defined('LOCALE_DOMAIN') ? LOCALE_DOMAIN : 'messages';
|
||||||
|
$localeRoot = defined('LOCALE_ROOT') ? LOCALE_ROOT : 'locale';
|
||||||
|
|
||||||
|
bindtextdomain($localeDomain, $localeRoot);
|
||||||
|
bind_textdomain_codeset($localeDomain, 'UTF-8');
|
||||||
|
textdomain($localeDomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set locale cookie
|
||||||
|
*
|
||||||
|
* @param string $locale Locale code to store in cookie
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function setLocaleCookie(string $locale): void
|
||||||
|
{
|
||||||
|
setcookie('locale', $locale, time() + self::COOKIE_LIFETIME, '/', '', false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize and apply locale in one call
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function initializeAndApply(): void
|
||||||
|
{
|
||||||
|
$this->initialize();
|
||||||
|
$this->applyLocale();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,6 +111,8 @@ class DhcpcdManager
|
|||||||
$domain_name_server
|
$domain_name_server
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$dhcp_cfg = file_get_contents(SELF::CONF_DEFAULT);
|
$dhcp_cfg = file_get_contents(SELF::CONF_DEFAULT);
|
||||||
$skip_dhcp = false;
|
$skip_dhcp = false;
|
||||||
|
|
||||||
@@ -133,7 +135,11 @@ class DhcpcdManager
|
|||||||
$dhcp_cfg = $this->removeIface($dhcp_cfg,'br0');
|
$dhcp_cfg = $this->removeIface($dhcp_cfg,'br0');
|
||||||
$dhcp_cfg = $this->removeIface($dhcp_cfg,'uap0');
|
$dhcp_cfg = $this->removeIface($dhcp_cfg,'uap0');
|
||||||
if (!strpos($dhcp_cfg, 'metric')) {
|
if (!strpos($dhcp_cfg, 'metric')) {
|
||||||
$dhcp_cfg = preg_replace('/^#\sRaspAP\s'.$ap_iface.'\s.*?(?=(?:\s*^\s*$|\s*nogateway))/ms', $config, $dhcp_cfg, 1);
|
$pattern = '/^#\sRaspAP\s' . preg_quote($ap_iface, '/') . '\sconfiguration\n' .
|
||||||
|
'(?:.*\n)*?' .
|
||||||
|
'(?:\n)*' .
|
||||||
|
'(?=#\sRaspAP\s|\z)/m';
|
||||||
|
$dhcp_cfg = preg_replace($pattern, $config . "\n\n", $dhcp_cfg, 1);
|
||||||
} else {
|
} else {
|
||||||
$metrics = true;
|
$metrics = true;
|
||||||
}
|
}
|
||||||
@@ -193,7 +199,11 @@ class DhcpcdManager
|
|||||||
$status->addMessage('DHCP configuration for '.$iface.' added.', 'success');
|
$status->addMessage('DHCP configuration for '.$iface.' added.', 'success');
|
||||||
} else {
|
} else {
|
||||||
$cfg = join(PHP_EOL, $cfg);
|
$cfg = join(PHP_EOL, $cfg);
|
||||||
$dhcp_cfg = preg_replace('/^#\sRaspAP\s'.$iface.'\s.*?(?=\s*^\s*$)/ms', $cfg, $dhcp_cfg, 1);
|
$pattern = '/^#\sRaspAP\s' . preg_quote($iface, '/') . '\sconfiguration\n' .
|
||||||
|
'(?:.*\n)*?' .
|
||||||
|
'(?:\n)*' .
|
||||||
|
'(?=#\sRaspAP\s|\z)/m';
|
||||||
|
$dhcp_cfg = preg_replace($pattern, $cfg . "\n\n", $dhcp_cfg, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $dhcp_cfg;
|
return $dhcp_cfg;
|
||||||
@@ -352,18 +362,29 @@ class DhcpcdManager
|
|||||||
];
|
];
|
||||||
|
|
||||||
// merge existing settings with updates
|
// merge existing settings with updates
|
||||||
|
$processed_keys = [];
|
||||||
foreach ($existing_config as $line) {
|
foreach ($existing_config as $line) {
|
||||||
$matched = false;
|
$matched = false;
|
||||||
foreach ($static_settings as $key => $value) {
|
foreach ($static_settings as $key => $value) {
|
||||||
if (strpos($line, $key) === 0) {
|
if (strpos($line, $key) === 0) {
|
||||||
$config[] = "$key=$value";
|
$config[] = "$key=$value";
|
||||||
$matched = true;
|
$matched = true;
|
||||||
|
$processed_keys[] = $key;
|
||||||
unset($static_settings[$key]);
|
unset($static_settings[$key]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$matched && !preg_match('/^interface/', $line)) {
|
if (!$matched && !preg_match('/^interface/', $line)) {
|
||||||
$config[] = $line;
|
$is_duplicate = false;
|
||||||
|
foreach ($processed_keys as $processed_key) {
|
||||||
|
if (strpos($line, $processed_key) === 0) {
|
||||||
|
$is_duplicate = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$is_duplicate && !in_array($line, $config, true)) {
|
||||||
|
$config[] = $line;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,8 +102,11 @@ class HostapdManager
|
|||||||
if (!empty($config['ieee80211ac']) && strval($config['ieee80211ac']) === '1') {
|
if (!empty($config['ieee80211ac']) && strval($config['ieee80211ac']) === '1') {
|
||||||
$selected = 'ac';
|
$selected = 'ac';
|
||||||
}
|
}
|
||||||
if (!empty($config['ieee80211w']) && strval($config['ieee80211w']) === '2') {
|
if (!empty($config['ieee80211ax']) && strval($config['ieee80211ax']) === '1') {
|
||||||
$selected = 'w';
|
$selected = 'ax';
|
||||||
|
}
|
||||||
|
if (!empty($config['ieee80211be']) && strval($config['ieee80211be']) === '1') {
|
||||||
|
$selected = 'be';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $selected;
|
return $selected;
|
||||||
@@ -151,6 +154,7 @@ class HostapdManager
|
|||||||
$config[] = 'auth_algs=1';
|
$config[] = 'auth_algs=1';
|
||||||
|
|
||||||
$wpa = $params['wpa'];
|
$wpa = $params['wpa'];
|
||||||
|
$wpa_numeric = $wpa;
|
||||||
$wpa_key_mgmt = 'WPA-PSK';
|
$wpa_key_mgmt = 'WPA-PSK';
|
||||||
|
|
||||||
if ($wpa == 4) {
|
if ($wpa == 4) {
|
||||||
@@ -185,20 +189,42 @@ class HostapdManager
|
|||||||
$config[] = 'ssid=' . $params['ssid'];
|
$config[] = 'ssid=' . $params['ssid'];
|
||||||
$config[] = 'channel=' . $params['channel'];
|
$config[] = 'channel=' . $params['channel'];
|
||||||
|
|
||||||
// choose VHT segment index (fallback only if required)
|
|
||||||
$vht_freq_idx = ($params['channel'] < RASPI_5GHZ_CHANNEL_MIN) ? 42 : 155;
|
|
||||||
$hwMode = isset($params['hw_mode']) ? $params['hw_mode'] : '';
|
$hwMode = isset($params['hw_mode']) ? $params['hw_mode'] : '';
|
||||||
|
|
||||||
|
// validate channel width for 802.11ax/be
|
||||||
|
if (in_array($hwMode, ['ax', 'be'])) {
|
||||||
|
// for 6GHz band (channels 1-233) wider bandwidths are available
|
||||||
|
$is6GHz = ($params['channel'] >= 1 && $params['channel'] <= 233);
|
||||||
|
|
||||||
|
// for 802.11be, 320 MHz only available on 6GHz
|
||||||
|
if ($hwMode === 'be' && !$is6GHz && isset($params['eht_oper_chwidth']) && $params['eht_oper_chwidth'] == 4) {
|
||||||
|
// reset to 160 MHz if 320 MHz requested on non-6GHz
|
||||||
|
$params['eht_oper_chwidth'] = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fetch settings for selected mode
|
// fetch settings for selected mode
|
||||||
$modeSettings = getDefaultNetOpts('hostapd', 'modes', $hwMode);
|
$modeSettings = getDefaultNetOpts('hostapd', 'modes', $hwMode);
|
||||||
$settings = $modeSettings[$hwMode]['settings'] ?? [];
|
$settings = $modeSettings[$hwMode]['settings'] ?? [];
|
||||||
|
|
||||||
|
// extract channel width from settings to calculate center frequency
|
||||||
|
$chwidth = $this->extractChannelWidth($settings, $hwMode);
|
||||||
|
|
||||||
|
// calculate center frequency indices based on channel + width
|
||||||
|
$vht_freq_idx = $this->calculateCenterFreqIndex((int)$params['channel'], $chwidth);
|
||||||
|
$he_freq_idx = $vht_freq_idx; // For most cases, HE and VHT use the same center frequency
|
||||||
|
|
||||||
|
// calculate HT40 direction based on channel and width
|
||||||
|
$ht40_dir = $this->calculateHT40Direction((int)$params['channel'], $chwidth);
|
||||||
|
|
||||||
if (!empty($settings)) {
|
if (!empty($settings)) {
|
||||||
foreach ($settings as $line) {
|
foreach ($settings as $line) {
|
||||||
if (!is_string($line)) {
|
if (!is_string($line)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$replaced = str_replace('{VHT_FREQ_IDX}', (string) $vht_freq_idx ?? '',$line);
|
$replaced = str_replace('{VHT_FREQ_IDX}', (string) $vht_freq_idx ?? '', $line);
|
||||||
|
$replaced = str_replace('{HE_FREQ_IDX}', (string) $he_freq_idx ?? '', $replaced);
|
||||||
|
$replaced = str_replace('{HT40_DIR}', (string) $ht40_dir ?? '', $replaced);
|
||||||
$config[] = $replaced;
|
$config[] = $replaced;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,9 +235,9 @@ class HostapdManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
// bridge handling
|
// bridge handling
|
||||||
if (!empty($params['bridge'])) {
|
if (!empty($params['bridgeName'])) {
|
||||||
$config[] = 'interface=' . $params['interface'];
|
$config[] = 'interface=' . $params['interface'];
|
||||||
$config[] = 'bridge=' . $params['bridge'];
|
$config[] = 'bridge=' . $params['bridgeName'];
|
||||||
} else {
|
} else {
|
||||||
$config[] = 'interface=' . $params['interface'];
|
$config[] = 'interface=' . $params['interface'];
|
||||||
}
|
}
|
||||||
@@ -225,6 +251,12 @@ class HostapdManager
|
|||||||
$config[] = 'max_num_sta=' . (int)$params['max_num_sta'];
|
$config[] = 'max_num_sta=' . (int)$params['max_num_sta'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add logging configuration if enabled
|
||||||
|
if (!empty($params['log_enable'])) {
|
||||||
|
$config[] = 'logger_syslog=-1';
|
||||||
|
$config[] = 'logger_syslog_level=0';
|
||||||
|
}
|
||||||
|
|
||||||
// optional additional user config
|
// optional additional user config
|
||||||
$config[] = $this->parseUserHostapdCfg();
|
$config[] = $this->parseUserHostapdCfg();
|
||||||
|
|
||||||
@@ -325,17 +357,6 @@ class HostapdManager
|
|||||||
return [$apIface, $cliIface, $sessionIface];
|
return [$apIface, $cliIface, $sessionIface];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables or disables hostapd logging
|
|
||||||
*
|
|
||||||
* @param int $logEnable
|
|
||||||
*/
|
|
||||||
private function handleLogState(int $logEnable): void
|
|
||||||
{
|
|
||||||
$script = $logEnable === 1 ? 'enablelog.sh' : 'disablelog.sh';
|
|
||||||
exec('sudo ' . RASPI_CONFIG . '/hostapd/' . $script);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses optional /etc/hostapd/hostapd.conf.users file
|
* Parses optional /etc/hostapd/hostapd.conf.users file
|
||||||
*
|
*
|
||||||
@@ -415,8 +436,6 @@ class HostapdManager
|
|||||||
*/
|
*/
|
||||||
public function persistHostapdIni(array $states, string $apIface, string $cliIface, array $previousIni = []): bool
|
public function persistHostapdIni(array $states, string $apIface, string $cliIface, array $previousIni = []): bool
|
||||||
{
|
{
|
||||||
$this->applyLogState($states['LogEnable']);
|
|
||||||
|
|
||||||
// compose new ini payload
|
// compose new ini payload
|
||||||
$cfg = [
|
$cfg = [
|
||||||
'WifiInterface' => $apIface,
|
'WifiInterface' => $apIface,
|
||||||
@@ -435,17 +454,6 @@ class HostapdManager
|
|||||||
return write_php_ini($cfg, RASPI_CONFIG . '/hostapd.ini');
|
return write_php_ini($cfg, RASPI_CONFIG . '/hostapd.ini');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables or disables hostapd logging
|
|
||||||
*
|
|
||||||
* @param int $logEnable 1 = enable, 0 = disable
|
|
||||||
*/
|
|
||||||
private function applyLogState(int $logEnable): void
|
|
||||||
{
|
|
||||||
$script = $logEnable === 1 ? 'enablelog.sh' : 'disablelog.sh';
|
|
||||||
exec('sudo ' . RASPI_CONFIG . '/hostapd/' . $script);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a count of hostapd-<interface>.conf files
|
* Returns a count of hostapd-<interface>.conf files
|
||||||
*
|
*
|
||||||
@@ -457,5 +465,241 @@ class HostapdManager
|
|||||||
return is_array($configs) ? count($configs) : 0;
|
return is_array($configs) ? count($configs) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* Extracts channel width from mode settings
|
||||||
|
*
|
||||||
|
* @param array $settings mode settings array
|
||||||
|
* @param string $hwMode hardware mode (ac, ax, be)
|
||||||
|
* @return int channel width in MHz (20, 40, 80, 160, 320)
|
||||||
|
*/
|
||||||
|
private function extractChannelWidth(array $settings, string $hwMode): int
|
||||||
|
{
|
||||||
|
$chwidthParam = '';
|
||||||
|
|
||||||
|
// determine parameter based on mode
|
||||||
|
if ($hwMode === 'ac') {
|
||||||
|
$chwidthParam = 'vht_oper_chwidth';
|
||||||
|
} elseif ($hwMode === 'ax') {
|
||||||
|
$chwidthParam = 'he_oper_chwidth';
|
||||||
|
} elseif ($hwMode === 'be') {
|
||||||
|
$chwidthParam = 'eht_oper_chwidth';
|
||||||
|
} else {
|
||||||
|
return 20; // 20 MHz default for other modes
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse settings to find channel width
|
||||||
|
foreach ($settings as $line) {
|
||||||
|
if (!is_string($line)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip comments
|
||||||
|
if (strpos(trim($line), '#') === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($line, $chwidthParam . '=') !== false) {
|
||||||
|
$parts = explode('=', $line, 2);
|
||||||
|
if (count($parts) === 2) {
|
||||||
|
// extract numeric value
|
||||||
|
$value = trim($parts[1]);
|
||||||
|
// remove any inline comments
|
||||||
|
$value = preg_replace('/\s*#.*$/', '', $value);
|
||||||
|
$chwidthCode = (int) $value;
|
||||||
|
|
||||||
|
// convert hostapd encoding to MHz based on mode
|
||||||
|
if ($hwMode === 'be') {
|
||||||
|
// EHT uses: 0=20, 1=40, 2=80, 3=160, 4=320
|
||||||
|
switch ($chwidthCode) {
|
||||||
|
case 0: return 20;
|
||||||
|
case 1: return 40;
|
||||||
|
case 2: return 80;
|
||||||
|
case 3: return 160;
|
||||||
|
case 4: return 320;
|
||||||
|
default: return 20;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// VHT/HE uses: 0=20/40, 1=80, 2=160, 3=80+80
|
||||||
|
switch ($chwidthCode) {
|
||||||
|
case 0: return 40;
|
||||||
|
case 1: return 80;
|
||||||
|
case 2: return 160;
|
||||||
|
case 3: return 160; // 80+80 treated as 160
|
||||||
|
default: return 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 20; // default to 20 MHz channel if not found
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates center frequency segment 0 index for given channel and width
|
||||||
|
*
|
||||||
|
* @param int $channel primary channel number
|
||||||
|
* @param int $chwidthMHz channel width in MHz (20, 40, 80, 160, 320)
|
||||||
|
* @return int center frequency segment 0 index
|
||||||
|
*/
|
||||||
|
private function calculateCenterFreqIndex(int $channel, int $chwidthMHz): int
|
||||||
|
{
|
||||||
|
// determine band based on channel number
|
||||||
|
$is24GHz = ($channel >= 1 && $channel <= 14);
|
||||||
|
$is5GHz = ($channel >= 36 && $channel <= 177);
|
||||||
|
$is6GHz = ($channel >= 1 && $channel <= 233 && !$is24GHz); // 6 GHz uses 1-233
|
||||||
|
|
||||||
|
// 20 MHz - center is always primary channel
|
||||||
|
if ($chwidthMHz <= 20) {
|
||||||
|
return $channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.4 GHz band
|
||||||
|
if ($is24GHz) {
|
||||||
|
if ($chwidthMHz == 40) {
|
||||||
|
// for 2.4 GHz, typically use HT40+ (center is primary + 2)
|
||||||
|
// channels 1-7 use HT40+, channels 8-13 use HT40-
|
||||||
|
return ($channel <= 7) ? $channel + 2 : $channel - 2;
|
||||||
|
}
|
||||||
|
// wider bandwidths not supported on 2.4 GHz
|
||||||
|
return $channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5 GHz band
|
||||||
|
if ($is5GHz) {
|
||||||
|
if ($chwidthMHz == 40) {
|
||||||
|
// HT40+ configuration: center = primary + 2
|
||||||
|
// adjust for upper/lower position in 40 MHz pair
|
||||||
|
if (in_array($channel, [36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 149, 157, 165, 173])) {
|
||||||
|
return $channel + 2;
|
||||||
|
} else {
|
||||||
|
return $channel - 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($chwidthMHz == 80) {
|
||||||
|
// map channel to 80 MHz center frequency
|
||||||
|
if ($channel >= 36 && $channel <= 48) return 42;
|
||||||
|
if ($channel >= 52 && $channel <= 64) return 58;
|
||||||
|
if ($channel >= 100 && $channel <= 112) return 106;
|
||||||
|
if ($channel >= 116 && $channel <= 128) return 122;
|
||||||
|
if ($channel >= 132 && $channel <= 144) return 138;
|
||||||
|
if ($channel >= 149 && $channel <= 161) return 155;
|
||||||
|
if ($channel >= 165 && $channel <= 177) return 171;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($chwidthMHz == 160) {
|
||||||
|
// map channel to 160 MHz center frequency
|
||||||
|
if ($channel >= 36 && $channel <= 64) return 50;
|
||||||
|
if ($channel >= 100 && $channel <= 128) return 114;
|
||||||
|
// channels 149-177 don't support 160 MHz in most regions
|
||||||
|
if ($channel >= 149 && $channel <= 177) return 163;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6 GHz band (UNII-5 through UNII-8)
|
||||||
|
if ($is6GHz && !$is24GHz) {
|
||||||
|
// 6 GHz uses different channel numbering: 1, 5, 9, 13, ... (every 4)
|
||||||
|
if ($chwidthMHz == 40) {
|
||||||
|
// center is at the midpoint between two 20 MHz channels
|
||||||
|
return $channel + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($chwidthMHz == 80) {
|
||||||
|
// calculate 80 MHz center
|
||||||
|
$blockStart = (int)(($channel - 1) / 16) * 16 + 1;
|
||||||
|
return $blockStart + 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($chwidthMHz == 160) {
|
||||||
|
// calculate 160 MHz center
|
||||||
|
$blockStart = (int)(($channel - 1) / 32) * 32 + 1;
|
||||||
|
return $blockStart + 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($chwidthMHz == 320) {
|
||||||
|
// calculate 320 MHz center
|
||||||
|
$blockStart = (int)(($channel - 1) / 64) * 64 + 1;
|
||||||
|
return $blockStart + 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback: return primary channel
|
||||||
|
return $channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates HT40 direction (+ or -) based on channel and bandwidth
|
||||||
|
*
|
||||||
|
* @param int $channel primary channel number
|
||||||
|
* @param int $chwidthMHz channel width in MHz
|
||||||
|
* @return string HT40 direction: "HT40+" or "HT40-" or "" for 20MHz
|
||||||
|
*/
|
||||||
|
private function calculateHT40Direction(int $channel, int $chwidthMHz): string
|
||||||
|
{
|
||||||
|
// only applicable for 40 MHz and wider on 5 GHz
|
||||||
|
if ($chwidthMHz < 40) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$is24GHz = ($channel >= 1 && $channel <= 14);
|
||||||
|
$is5GHz = ($channel >= 36 && $channel <= 177);
|
||||||
|
|
||||||
|
// 2.4 GHz band
|
||||||
|
if ($is24GHz) {
|
||||||
|
// channels 1-7 use HT40+, channels 8-13 use HT40-
|
||||||
|
return ($channel <= 7) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5 GHz band
|
||||||
|
if ($is5GHz) {
|
||||||
|
if ($chwidthMHz == 40) {
|
||||||
|
// for pure 40 MHz mode
|
||||||
|
if (in_array($channel, [36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 149, 157, 165, 173])) {
|
||||||
|
return 'HT40+';
|
||||||
|
} else {
|
||||||
|
return 'HT40-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($chwidthMHz >= 80) {
|
||||||
|
// for 80 MHz and wider, determine based on position within the 80 MHz block
|
||||||
|
// lower half of 80 MHz block uses HT40+, upper half uses HT40-
|
||||||
|
|
||||||
|
// determine which 80 MHz block this channel belongs to
|
||||||
|
if ($channel >= 36 && $channel <= 48) {
|
||||||
|
// block: 36, 40, 44, 48 (center 42)
|
||||||
|
return ($channel <= 40) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
if ($channel >= 52 && $channel <= 64) {
|
||||||
|
// block: 52, 56, 60, 64 (center 58)
|
||||||
|
return ($channel <= 56) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
if ($channel >= 100 && $channel <= 112) {
|
||||||
|
// block: 100, 104, 108, 112 (center 106)
|
||||||
|
return ($channel <= 104) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
if ($channel >= 116 && $channel <= 128) {
|
||||||
|
// block: 116, 120, 124, 128 (center 122)
|
||||||
|
return ($channel <= 120) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
if ($channel >= 132 && $channel <= 144) {
|
||||||
|
// block: 132, 136, 140, 144 (center 138)
|
||||||
|
return ($channel <= 136) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
if ($channel >= 149 && $channel <= 161) {
|
||||||
|
// block: 149, 153, 157, 161 (center 155)
|
||||||
|
return ($channel <= 153) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
if ($channel >= 165 && $channel <= 177) {
|
||||||
|
// block: 165, 169, 173, 177 (center 171)
|
||||||
|
return ($channel <= 169) ? 'HT40+' : 'HT40-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// default fallback
|
||||||
|
return 'HT40+';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ class HotspotService
|
|||||||
'b' => '802.11b - 2.4 GHz',
|
'b' => '802.11b - 2.4 GHz',
|
||||||
'g' => '802.11g - 2.4 GHz',
|
'g' => '802.11g - 2.4 GHz',
|
||||||
'n' => '802.11n - 2.4/5 GHz',
|
'n' => '802.11n - 2.4/5 GHz',
|
||||||
'ac' => '802.11ac - 5 GHz'
|
'ac' => '802.11ac - 5 GHz',
|
||||||
|
'ax' => '802.11ax - 2.4/5/6 GHz',
|
||||||
|
'be' => '802.11be - 2.4/5/6 GHz'
|
||||||
];
|
];
|
||||||
|
|
||||||
// encryption types
|
// encryption types
|
||||||
@@ -42,6 +44,21 @@ class HotspotService
|
|||||||
'TKIP CCMP' => 'TKIP+CCMP'
|
'TKIP CCMP' => 'TKIP+CCMP'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 802.11ax (Wi-Fi 6) channel widths
|
||||||
|
private const HE_CHANNEL_WIDTHS = [
|
||||||
|
0 => '20/40 MHz',
|
||||||
|
1 => '80 MHz',
|
||||||
|
2 => '160 MHz'
|
||||||
|
];
|
||||||
|
|
||||||
|
// 802.11be (Wi-Fi 7) channel widths
|
||||||
|
private const EHT_CHANNEL_WIDTHS = [
|
||||||
|
0 => '20 MHz',
|
||||||
|
1 => '40 MHz',
|
||||||
|
2 => '80 MHz',
|
||||||
|
3 => '160 MHz',
|
||||||
|
4 => '320 MHz (6 GHz only)'
|
||||||
|
];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@@ -67,7 +84,23 @@ class HotspotService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns translated security modes.
|
* Returns 802.11ax (Wi-Fi 6) channel widths
|
||||||
|
*/
|
||||||
|
public static function getHeChannelWidths(): array
|
||||||
|
{
|
||||||
|
return self::HE_CHANNEL_WIDTHS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns 802.11be (Wi-Fi 7) channel widths
|
||||||
|
*/
|
||||||
|
public static function getEhtChannelWidths(): array
|
||||||
|
{
|
||||||
|
return self::EHT_CHANNEL_WIDTHS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns translated security modes
|
||||||
*/
|
*/
|
||||||
public static function getSecurityModes(): array
|
public static function getSecurityModes(): array
|
||||||
{
|
{
|
||||||
@@ -94,7 +127,6 @@ class HotspotService
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates user input + saves configs for hostapd, dnsmasq & dhcp
|
* Validates user input + saves configs for hostapd, dnsmasq & dhcp
|
||||||
*
|
*
|
||||||
@@ -138,19 +170,26 @@ class HotspotService
|
|||||||
|
|
||||||
if ($validated === false) {
|
if ($validated === false) {
|
||||||
$status->addMessage('Unable to save WiFi hotspot settings due to validation errors', 'danger');
|
$status->addMessage('Unable to save WiFi hotspot settings due to validation errors', 'danger');
|
||||||
error_log("HotspotService::validate() -> validated = false");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// normalize state flags
|
// normalize state flags
|
||||||
$validated['interface'] = $apIface;
|
$validated['interface'] = $apIface;
|
||||||
|
$validated["bridgeName"] = !empty($states["BridgedEnable"]) ? "br0" : null;
|
||||||
$validated['bridge'] = !empty($states['BridgedEnable']);
|
$validated['bridge'] = !empty($states['BridgedEnable']);
|
||||||
$validated['apsta'] = !empty($states['WifiAPEnable']);
|
$validated['apsta'] = !empty($states['WifiAPEnable']);
|
||||||
$validated['repeater'] = !empty($states['RepeaterEnable']);
|
$validated['repeater'] = !empty($states['RepeaterEnable']);
|
||||||
$validated['dualmode'] = !empty($states['DualAPEnable']);
|
$validated['dualmode'] = !empty($states['DualAPEnable']);
|
||||||
$validated['txpower'] = $post_data['txpower'];
|
$validated['txpower'] = $post_data['txpower'];
|
||||||
|
|
||||||
|
// add 802.11ax/be specific parameters if present
|
||||||
|
if (in_array($validated['hw_mode'], ['ax', 'be'])) {
|
||||||
|
if ($validated['wpa'] < 4 && $validated['hw_mode'] === 'be') {
|
||||||
|
$status->addMessage('Note: WiFi 7 works best with WPA3 security', 'info');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// hostapd
|
// hostapd
|
||||||
$config = $this->hostapd->buildConfig($validated, $status);
|
$config = $this->hostapd->buildConfig($validated, $status);
|
||||||
$this->hostapd->saveConfig($config, $dualAPEnable, $validated['interface']);
|
$this->hostapd->saveConfig($config, $dualAPEnable, $validated['interface']);
|
||||||
@@ -295,10 +334,22 @@ class HotspotService
|
|||||||
* Gets the current regulatory domain
|
* Gets the current regulatory domain
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
|
* @throws RuntimeException if unable to determine regulatory domain
|
||||||
*/
|
*/
|
||||||
public function getRegDomain(): string
|
public function getRegDomain(): string
|
||||||
{
|
{
|
||||||
$domain = shell_exec("iw reg get | grep -o 'country [A-Z]\{2\}' | awk 'NR==1{print $2}'");
|
$domain = shell_exec("iw reg get | grep -o 'country [A-Z]\{2\}' | awk 'NR==1{print $2}'");
|
||||||
|
|
||||||
|
if ($domain === null) {
|
||||||
|
throw new \RuntimeException('Failed to execute regulatory domain command');
|
||||||
|
}
|
||||||
|
|
||||||
|
$domain = trim($domain);
|
||||||
|
|
||||||
|
if (empty($domain)) {
|
||||||
|
throw new \RuntimeException('Unable to determine regulatory domain');
|
||||||
|
}
|
||||||
|
|
||||||
return $domain;
|
return $domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,6 +390,36 @@ class HotspotService
|
|||||||
return array_values($interfaces);
|
return array_values($interfaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves hostapd service logs from systemd journal
|
||||||
|
*
|
||||||
|
* @param int $lines number of log lines to retrieve (default: 100, max: 1000)
|
||||||
|
* @param bool $follow return command for real-time following (tbd)
|
||||||
|
* @return array ['success' => bool, 'logs' => array, 'command' => string]
|
||||||
|
*/
|
||||||
|
public function getHostapdLogs(int $lines = 100, bool $follow = false): array
|
||||||
|
{
|
||||||
|
// sanitize and limit line count
|
||||||
|
$lines = max(1, min(1000, $lines));
|
||||||
|
|
||||||
|
if ($follow) {
|
||||||
|
return [
|
||||||
|
'success' => true,
|
||||||
|
'logs' => [],
|
||||||
|
'command' => 'journalctl -u hostapd.service -f --no-pager'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$cmd = sprintf('sudo journalctl -u hostapd.service -n %d --no-pager 2>&1', $lines);
|
||||||
|
exec($cmd, $output, $status);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => $status === 0,
|
||||||
|
'logs' => $output,
|
||||||
|
'line_count' => count($output)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts services for given interface
|
* Starts services for given interface
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -16,6 +16,15 @@ use RaspAP\Messages\StatusMessage;
|
|||||||
|
|
||||||
class HostapdValidator
|
class HostapdValidator
|
||||||
{
|
{
|
||||||
|
// Valid channel widths for 802.11ax (HE)
|
||||||
|
private const HE_VALID_CHWIDTHS = [0, 1, 2]; // 20/40, 80, 160 MHz
|
||||||
|
|
||||||
|
// Valid channel widths for 802.11be (EHT)
|
||||||
|
private const EHT_VALID_CHWIDTHS = [0, 1, 2, 3, 4]; // 20, 40, 80, 160, 320 MHz
|
||||||
|
|
||||||
|
// 6 GHz channel range (US)
|
||||||
|
private const CHANNEL_6GHZ_MIN = 1;
|
||||||
|
private const CHANNEL_6GHZ_MAX = 233;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates full hostapd parameter set
|
* Validates full hostapd parameter set
|
||||||
@@ -64,6 +73,16 @@ class HostapdValidator
|
|||||||
$goodInput = false;
|
$goodInput = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate 802.11ax specific parameters
|
||||||
|
if ($post['hw_mode'] === 'ax' && !$this->validateAxParams($post, $status)) {
|
||||||
|
$goodInput = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate 802.11be specific parameters
|
||||||
|
if ($post['hw_mode'] === 'be' && !$this->validateBeParams($post, $status)) {
|
||||||
|
$goodInput = false;
|
||||||
|
}
|
||||||
|
|
||||||
// validate SSID
|
// validate SSID
|
||||||
if (empty($post['ssid']) || strlen($post['ssid']) > 32) {
|
if (empty($post['ssid']) || strlen($post['ssid']) > 32) {
|
||||||
$status->addMessage('SSID must be between 1 and 32 characters', 'danger');
|
$status->addMessage('SSID must be between 1 and 32 characters', 'danger');
|
||||||
@@ -200,9 +219,97 @@ class HostapdValidator
|
|||||||
'bridgeStaticIp' => ($post['bridgeStaticIp']),
|
'bridgeStaticIp' => ($post['bridgeStaticIp']),
|
||||||
'bridgeNetmask' => ($post['bridgeNetmask']),
|
'bridgeNetmask' => ($post['bridgeNetmask']),
|
||||||
'bridgeGateway' => ($post['bridgeGateway']),
|
'bridgeGateway' => ($post['bridgeGateway']),
|
||||||
'bridgeDNS' => ($post['bridgeDNS'])
|
'bridgeDNS' => ($post['bridgeDNS']),
|
||||||
|
'he_oper_chwidth' => $post['he_oper_chwidth'] ?? null, // 802.11ax parameters
|
||||||
|
'he_bss_color' => $post['he_bss_color'] ?? null, // 802.11be parameters
|
||||||
|
'eht_oper_chwidth' => $post['eht_oper_chwidth'] ?? null
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates 802.11ax (Wi-Fi 6) specific parameters
|
||||||
|
*
|
||||||
|
* @param array $post
|
||||||
|
* @param StatusMessage $status
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function validateAxParams(array $post, StatusMessage $status): bool
|
||||||
|
{
|
||||||
|
$valid = true;
|
||||||
|
|
||||||
|
// Validate HE channel width
|
||||||
|
if (isset($post['he_oper_chwidth'])) {
|
||||||
|
$chwidth = (int)$post['he_oper_chwidth'];
|
||||||
|
if (!in_array($chwidth, self::HE_VALID_CHWIDTHS, true)) {
|
||||||
|
$status->addMessage('Invalid 802.11ax channel width. Must be 0 (20/40 MHz), 1 (80 MHz), or 2 (160 MHz)', 'danger');
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate BSS color (1-63)
|
||||||
|
if (isset($post['he_bss_color'])) {
|
||||||
|
$bssColor = (int)$post['he_bss_color'];
|
||||||
|
if ($bssColor < 1 || $bssColor > 63) {
|
||||||
|
$status->addMessage('802.11ax BSS color must be between 1 and 63', 'danger');
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates 802.11be (Wi-Fi 7) specific parameters
|
||||||
|
*
|
||||||
|
* @param array $post
|
||||||
|
* @param StatusMessage $status
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function validateBeParams(array $post, StatusMessage $status): bool
|
||||||
|
{
|
||||||
|
$valid = true;
|
||||||
|
$channel = (int)$post['channel'];
|
||||||
|
|
||||||
|
// Validate EHT channel width
|
||||||
|
if (isset($post['eht_oper_chwidth'])) {
|
||||||
|
$chwidth = (int)$post['eht_oper_chwidth'];
|
||||||
|
|
||||||
|
if (!in_array($chwidth, self::EHT_VALID_CHWIDTHS, true)) {
|
||||||
|
$status->addMessage('Invalid 802.11be channel width. Must be 0-4 (20, 40, 80, 160, or 320 MHz)', 'danger');
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 320 MHz only valid on 6 GHz band
|
||||||
|
if ($chwidth === 4) {
|
||||||
|
if ($channel < self::CHANNEL_6GHZ_MIN || $channel > self::CHANNEL_6GHZ_MAX) {
|
||||||
|
$status->addMessage('802.11be 320 MHz channel width is only available on 6 GHz band (channels 1-233)', 'danger');
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate BSS color (same as 802.11ax, inherited)
|
||||||
|
if (isset($post['he_bss_color'])) {
|
||||||
|
$bssColor = (int)$post['he_bss_color'];
|
||||||
|
if ($bssColor < 1 || $bssColor > 63) {
|
||||||
|
$status->addMessage('BSS color must be between 1 and 63', 'danger');
|
||||||
|
$valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if channel is in 6GHz band
|
||||||
|
*
|
||||||
|
* @param int $channel
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function is6GHzChannel(int $channel): bool
|
||||||
|
{
|
||||||
|
return $channel >= self::CHANNEL_6GHZ_MIN && $channel <= self::CHANNEL_6GHZ_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,16 +16,15 @@ class WiFiManager
|
|||||||
|
|
||||||
private const MIN_RSSI = -100;
|
private const MIN_RSSI = -100;
|
||||||
private const MAX_RSSI = -55;
|
private const MAX_RSSI = -55;
|
||||||
|
const SECURITY_OPEN = 'OPEN';
|
||||||
|
|
||||||
public function knownWifiStations(&$networks)
|
public function knownWifiStations(&$networks)
|
||||||
{
|
{
|
||||||
// find currently configured networks
|
// find currently configured networks
|
||||||
exec(' sudo cat ' . RASPI_WPA_SUPPLICANT_CONFIG, $known_return);
|
exec(' sudo cat ' . RASPI_WPA_SUPPLICANT_CONFIG, $known_return);
|
||||||
$index = 0;
|
|
||||||
foreach ($known_return as $line) {
|
foreach ($known_return as $line) {
|
||||||
if (preg_match('/network\s*=/', $line)) {
|
if (preg_match('/network\s*=/', $line)) {
|
||||||
$network = array('visible' => false, 'configured' => true, 'connected' => false, 'index' => null);
|
$network = array('visible' => false, 'configured' => true, 'connected' => false, 'index' => null);
|
||||||
++$index;
|
|
||||||
} elseif (isset($network) && $network !== null) {
|
} elseif (isset($network) && $network !== null) {
|
||||||
if (preg_match('/^\s*}\s*$/', $line)) {
|
if (preg_match('/^\s*}\s*$/', $line)) {
|
||||||
$networks[$ssid] = $network;
|
$networks[$ssid] = $network;
|
||||||
@@ -37,8 +36,7 @@ class WiFiManager
|
|||||||
$ssid = trim($lineArr[1], '"');
|
$ssid = trim($lineArr[1], '"');
|
||||||
$ssid = str_replace('P"','',$ssid);
|
$ssid = str_replace('P"','',$ssid);
|
||||||
$network['ssid'] = $ssid;
|
$network['ssid'] = $ssid;
|
||||||
$index = $this->getNetworkIdBySSID($ssid);
|
$network['index'] = $this->getNetworkIdBySSID($ssid);
|
||||||
$network['index'] = $index;
|
|
||||||
break;
|
break;
|
||||||
case 'psk':
|
case 'psk':
|
||||||
$network['passkey'] = trim($lineArr[1]);
|
$network['passkey'] = trim($lineArr[1]);
|
||||||
@@ -51,7 +49,7 @@ class WiFiManager
|
|||||||
break;
|
break;
|
||||||
case 'key_mgmt':
|
case 'key_mgmt':
|
||||||
if (! array_key_exists('passphrase', $network) && $lineArr[1] === 'NONE') {
|
if (! array_key_exists('passphrase', $network) && $lineArr[1] === 'NONE') {
|
||||||
$network['protocol'] = 'Open';
|
$network['protocol'] = self::SECURITY_OPEN;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'priority':
|
case 'priority':
|
||||||
@@ -84,24 +82,27 @@ class WiFiManager
|
|||||||
$cacheKey,
|
$cacheKey,
|
||||||
function () use ($iface) {
|
function () use ($iface) {
|
||||||
$stdout = shell_exec("sudo iw dev $iface scan");
|
$stdout = shell_exec("sudo iw dev $iface scan");
|
||||||
|
sleep(1);
|
||||||
|
if ($stdout === null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
return preg_split("/\n/", $stdout);
|
return preg_split("/\n/", $stdout);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// exclude the AP from nearby networks
|
// determine the next index that follows the indexes of the known networks
|
||||||
exec('sed -rn "s/ssid=(.*)\s*$/\1/p" ' . escapeshellarg(RASPI_HOSTAPD_CONFIG), $ap_ssid);
|
|
||||||
$ap_ssid = $ap_ssid[0] ?? '';
|
|
||||||
|
|
||||||
$index = 0;
|
$index = 0;
|
||||||
if (!empty($networks)) {
|
if (!empty($networks)) {
|
||||||
$lastnet = end($networks);
|
foreach ($networks as $network) {
|
||||||
if (isset($lastnet['index'])) {
|
if (isset($network['index']) && is_numeric($network['index']) && ($network['index'] > $index)) {
|
||||||
$index = $lastnet['index'] + 1;
|
$index = (int)$network['index'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$index++;
|
||||||
|
|
||||||
$current = [];
|
$current = [];
|
||||||
$commitCurrent = function () use (&$current, &$networks, &$index, $ap_ssid) {
|
$commitCurrent = function () use (&$current, &$networks, &$index) {
|
||||||
if (empty($current['ssid'])) {
|
if (empty($current['ssid'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -109,7 +110,7 @@ class WiFiManager
|
|||||||
$ssid = $current['ssid'];
|
$ssid = $current['ssid'];
|
||||||
|
|
||||||
// unprintable 7bit ASCII control codes, delete or quotes -> ignore network
|
// unprintable 7bit ASCII control codes, delete or quotes -> ignore network
|
||||||
if ($ssid === $ap_ssid || preg_match('/[\x00-\x1f\x7f\'`\´"]/', $ssid)) {
|
if (preg_match('/[\x00-\x1f\x7f\'`\´"]/', $ssid)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +128,7 @@ class WiFiManager
|
|||||||
$networks[$ssid] = [
|
$networks[$ssid] = [
|
||||||
'ssid' => $ssid,
|
'ssid' => $ssid,
|
||||||
'configured' => false,
|
'configured' => false,
|
||||||
'protocol' => $current['security'] ?? 'OPEN',
|
'protocol' => $current['security'] ?? self::SECURITY_OPEN,
|
||||||
'channel' => $channel,
|
'channel' => $channel,
|
||||||
'passphrase' => '',
|
'passphrase' => '',
|
||||||
'visible' => true,
|
'visible' => true,
|
||||||
@@ -135,10 +136,14 @@ class WiFiManager
|
|||||||
'RSSI' => $rssi,
|
'RSSI' => $rssi,
|
||||||
'index' => $index
|
'index' => $index
|
||||||
];
|
];
|
||||||
++$index;
|
$index++; // increment for next new network
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (is_string($scan_results)) {
|
||||||
|
$scan_results = explode("\n", trim($scan_results));
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($scan_results as $line) {
|
foreach ($scan_results as $line) {
|
||||||
$line = trim($line);
|
$line = trim($line);
|
||||||
|
|
||||||
@@ -149,7 +154,7 @@ class WiFiManager
|
|||||||
'ssid' => '',
|
'ssid' => '',
|
||||||
'signal' => null,
|
'signal' => null,
|
||||||
'freq' => null,
|
'freq' => null,
|
||||||
'security' => 'OPEN'
|
'security' => self::SECURITY_OPEN
|
||||||
];
|
];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -172,20 +177,50 @@ class WiFiManager
|
|||||||
}
|
}
|
||||||
$commitCurrent();
|
$commitCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Check if networks are connected via wpa_cli status
|
||||||
|
* NB: iwconfig shows the last associated SSID even when connection is inactive
|
||||||
*/
|
*/
|
||||||
public function connectedWifiStations(&$networks)
|
public function connectedWifiStations(&$networks)
|
||||||
{
|
{
|
||||||
exec('iwconfig ' .$_SESSION['wifi_client_interface'], $iwconfig_return);
|
$wpa_state = null;
|
||||||
foreach ($iwconfig_return as $line) {
|
$connected_ssid = null;
|
||||||
if (preg_match('/ESSID:\"([^"]+)\"/i', $line, $iwconfig_ssid)) {
|
$iface = $_SESSION['wifi_client_interface'];
|
||||||
$ssid=hexSequence2lower($iwconfig_ssid[1]);
|
|
||||||
$networks[$ssid]['connected'] = true;
|
$cmd = "sudo wpa_cli -i $iface status";
|
||||||
//$check=detectCaptivePortal($_SESSION['wifi_client_interface']);
|
$status_output = shell_exec($cmd);
|
||||||
$networks[$ssid]["portal-url"]=$check["URL"];
|
|
||||||
|
if ($status_output === null || empty($status_output)) {
|
||||||
|
error_log("WiFiManager::connectedWifiStations: wpa_cli command failed or returned no output");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$lines = explode("\n", trim($status_output));
|
||||||
|
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$line = trim($line);
|
||||||
|
if (preg_match('/^wpa_state=(.+)$/', $line, $matches)) {
|
||||||
|
$wpa_state = trim($matches[1]);
|
||||||
}
|
}
|
||||||
|
if (preg_match('/^ssid=(.+)$/', $line, $matches)) {
|
||||||
|
$connected_ssid = trim($matches[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($wpa_state === 'COMPLETED' && !empty($connected_ssid)) {
|
||||||
|
$ssid = hexSequence2lower($connected_ssid);
|
||||||
|
|
||||||
|
// check if this SSID exists in networks array
|
||||||
|
if (array_key_exists($ssid, $networks)) {
|
||||||
|
$networks[$ssid]['connected'] = true;
|
||||||
|
} else {
|
||||||
|
error_log("WiFiManager::connectedWifiStations: SSID '$ssid' not found. SSIDs: " . implode(', ', array_keys($networks)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// captive portal detection
|
||||||
|
// $check = detectCaptivePortal($iface);
|
||||||
|
// if (isset($check["URL"])) {
|
||||||
|
// $networks[$ssid]["portal-url"] = $check["URL"];
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,8 +285,11 @@ class WiFiManager
|
|||||||
{
|
{
|
||||||
$iface = $_SESSION['wifi_client_interface'];
|
$iface = $_SESSION['wifi_client_interface'];
|
||||||
if ($force == true) {
|
if ($force == true) {
|
||||||
$cmd = "sudo /sbin/wpa_supplicant -i $unescapedIface -c /etc/wpa_supplicant/wpa_supplicant.conf -B 2>&1";
|
$cmd = "sudo rm -f /var/run/wpa_supplicant/" . $iface;
|
||||||
$result = shell_exec($cmd);
|
$result = shell_exec($cmd);
|
||||||
|
$cmd = "sudo /sbin/wpa_supplicant -i $iface -c /etc/wpa_supplicant/wpa_supplicant.conf -B 2>&1";
|
||||||
|
$result = shell_exec($cmd);
|
||||||
|
sleep(2);
|
||||||
}
|
}
|
||||||
$cmd = "sudo wpa_cli -i $iface reconfigure";
|
$cmd = "sudo wpa_cli -i $iface reconfigure";
|
||||||
$result = shell_exec($cmd);
|
$result = shell_exec($cmd);
|
||||||
@@ -420,7 +458,7 @@ class WiFiManager
|
|||||||
"sudo wpa_cli -i $iface set_network $netid ssid $ssid",
|
"sudo wpa_cli -i $iface set_network $netid ssid $ssid",
|
||||||
];
|
];
|
||||||
|
|
||||||
if (strtolower($protocol) === 'open') {
|
if ($protocol === self::SECURITY_OPEN) {
|
||||||
$commands[] = "sudo wpa_cli -i $iface set_network $netid key_mgmt NONE";
|
$commands[] = "sudo wpa_cli -i $iface set_network $netid key_mgmt NONE";
|
||||||
} else {
|
} else {
|
||||||
$commands[] = "sudo wpa_cli -i $iface set_network $netid psk $psk";
|
$commands[] = "sudo wpa_cli -i $iface set_network $netid psk $psk";
|
||||||
@@ -442,12 +480,12 @@ class WiFiManager
|
|||||||
error_log("Successfully added network: {$network['ssid']}");
|
error_log("Successfully added network: {$network['ssid']}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Parses wpa_cli list_networks output and returns the id
|
* Parses wpa_cli list_networks output and returns the id
|
||||||
* of a corresponding network SSID
|
* of a corresponding network SSID
|
||||||
*
|
*
|
||||||
* @param string $ssid
|
* @param string $ssid
|
||||||
* @return integer id
|
* @return integer id or null
|
||||||
*/
|
*/
|
||||||
public function getNetworkIdBySSID($ssid) {
|
public function getNetworkIdBySSID($ssid) {
|
||||||
$iface = escapeshellarg($_SESSION['wifi_client_interface']);
|
$iface = escapeshellarg($_SESSION['wifi_client_interface']);
|
||||||
@@ -455,10 +493,11 @@ class WiFiManager
|
|||||||
$output = [];
|
$output = [];
|
||||||
exec($cmd, $output);
|
exec($cmd, $output);
|
||||||
array_shift($output);
|
array_shift($output);
|
||||||
|
|
||||||
foreach ($output as $line) {
|
foreach ($output as $line) {
|
||||||
$columns = preg_split('/\t/', $line);
|
$columns = preg_split('/\t/', $line);
|
||||||
if (count($columns) >= 4 && trim($columns[1]) === trim($ssid)) {
|
if (count($columns) >= 2 && trim($columns[1]) === trim($ssid)) {
|
||||||
return $columns[0]; // return network ID
|
return (int)$columns[0]; // return network ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -513,5 +552,211 @@ CONF;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the operational status of a network interface
|
||||||
|
*
|
||||||
|
* @param string $interface network interface name
|
||||||
|
* @return string returns up, down, or unknown
|
||||||
|
*/
|
||||||
|
public function getInterfaceStatus(string $interface): string
|
||||||
|
{
|
||||||
|
exec('ip a show ' . escapeshellarg($interface), $output);
|
||||||
|
$outputGlued = implode(" ", $output);
|
||||||
|
$outputNormalized = preg_replace('/\s\s+/', ' ', $outputGlued);
|
||||||
|
|
||||||
|
if (preg_match('/state (UP|DOWN)/i', $outputNormalized, $matches)) {
|
||||||
|
return strtolower($matches[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connects to a network using wpa_cli
|
||||||
|
*
|
||||||
|
* @param string $interface network interface name
|
||||||
|
* @param int $netid network ID to connect to
|
||||||
|
* @return bool true on success, false on failure
|
||||||
|
*/
|
||||||
|
public function connectToNetwork(string $interface, int $netid): bool
|
||||||
|
{
|
||||||
|
$iface = escapeshellarg($interface);
|
||||||
|
|
||||||
|
$cmd = "sudo wpa_cli -i $iface select_network $netid";
|
||||||
|
$selectResult = shell_exec($cmd);
|
||||||
|
|
||||||
|
if ($selectResult === null || trim($selectResult) === "FAIL") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sleep(3);
|
||||||
|
|
||||||
|
$cmd = "sudo wpa_cli -i $iface reassociate";
|
||||||
|
$reassociateResult = shell_exec($cmd);
|
||||||
|
|
||||||
|
if ($reassociateResult !== null) {
|
||||||
|
$trimmed = trim($reassociateResult);
|
||||||
|
if ($trimmed === "FAIL") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a network from wpa_cli
|
||||||
|
*
|
||||||
|
* @param string $interface network interface name
|
||||||
|
* @param int $netid network ID to delete
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function deleteNetwork(string $interface, int $netid): void
|
||||||
|
{
|
||||||
|
$iface = escapeshellarg($interface);
|
||||||
|
|
||||||
|
exec("sudo wpa_cli -i $iface disconnect $netid");
|
||||||
|
exec("sudo wpa_cli -i $iface remove_network $netid");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects from a network using wpa_cli
|
||||||
|
*
|
||||||
|
* @param string $interface network interface name
|
||||||
|
* @param int $netid network ID to disconnect from
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function disconnectNetwork(string $interface, int $netid): void
|
||||||
|
{
|
||||||
|
$iface = escapeshellarg($interface);
|
||||||
|
|
||||||
|
exec("sudo wpa_cli -i $iface disconnect $netid");
|
||||||
|
exec("sudo wpa_cli -i $iface remove_network $netid");
|
||||||
|
sleep(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates/adds a network via wpa_cli
|
||||||
|
*
|
||||||
|
* @param string $interface network interface name
|
||||||
|
* @param string $ssid network SSID
|
||||||
|
* @param string $passphrase network passphrase
|
||||||
|
* @param string $protocol security protocol (OPEN or WPA)
|
||||||
|
* @return int|null network ID on success, null on failure
|
||||||
|
*/
|
||||||
|
public function updateNetwork(string $interface, string $ssid, string $passphrase, string $protocol = 'WPA'): ?int
|
||||||
|
{
|
||||||
|
$iface = escapeshellarg($interface);
|
||||||
|
$escapedSsid = escapeshellarg('"' . $ssid . '"');
|
||||||
|
|
||||||
|
$netid = shell_exec("sudo wpa_cli -i $iface add_network");
|
||||||
|
|
||||||
|
if ($netid === null || !is_numeric(trim($netid))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$netid = trim($netid);
|
||||||
|
$commands = [
|
||||||
|
"sudo wpa_cli -i $iface set_network $netid ssid $escapedSsid"
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($protocol === self::SECURITY_OPEN) {
|
||||||
|
$commands[] = "sudo wpa_cli -i $iface set_network $netid key_mgmt NONE";
|
||||||
|
} else {
|
||||||
|
$escapedPsk = escapeshellarg('"' . $passphrase . '"');
|
||||||
|
$commands[] = "sudo wpa_cli -i $iface set_network $netid psk $escapedPsk";
|
||||||
|
}
|
||||||
|
|
||||||
|
$commands[] = "sudo wpa_cli -i $iface enable_network $netid";
|
||||||
|
|
||||||
|
foreach ($commands as $cmd) {
|
||||||
|
exec($cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)$netid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a wpa_supplicant configuration and applies it
|
||||||
|
*
|
||||||
|
* @param array $networks array of network configurations
|
||||||
|
* @param string $interface the network interface name
|
||||||
|
* @return array Array with 'success' (bool) and 'message' (string)
|
||||||
|
*/
|
||||||
|
public function writeWpaSupplicant(array $networks, string $interface): array
|
||||||
|
{
|
||||||
|
$wpa_file = fopen('/tmp/wifidata', 'w');
|
||||||
|
if (!$wpa_file) {
|
||||||
|
return ['success' => false, 'message' => 'Failed to update wifi settings'];
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite($wpa_file, 'ctrl_interface=DIR=' . RASPI_WPA_CTRL_INTERFACE . ' GROUP=netdev' . PHP_EOL);
|
||||||
|
fwrite($wpa_file, 'update_config=1' . PHP_EOL);
|
||||||
|
|
||||||
|
$ok = true;
|
||||||
|
foreach ($networks as $ssid => $network) {
|
||||||
|
if ($network['protocol'] === self::SECURITY_OPEN) {
|
||||||
|
fwrite($wpa_file, "network={".PHP_EOL);
|
||||||
|
fwrite($wpa_file, "\tssid=\"".$ssid."\"".PHP_EOL);
|
||||||
|
fwrite($wpa_file, "\tkey_mgmt=NONE".PHP_EOL);
|
||||||
|
fwrite($wpa_file, "\tscan_ssid=1".PHP_EOL);
|
||||||
|
if (array_key_exists('priority', $network)) {
|
||||||
|
fwrite($wpa_file, "\tpriority=".$network['priority'].PHP_EOL);
|
||||||
|
}
|
||||||
|
fwrite($wpa_file, "}".PHP_EOL);
|
||||||
|
} else {
|
||||||
|
if (strlen($network['passphrase']) >= 8 && strlen($network['passphrase']) <= 63) {
|
||||||
|
unset($wpa_passphrase);
|
||||||
|
unset($line);
|
||||||
|
exec('wpa_passphrase '. $this->ssid2utf8(escapeshellarg($ssid)) . ' ' . escapeshellarg($network['passphrase']), $wpa_passphrase);
|
||||||
|
foreach ($wpa_passphrase as $line) {
|
||||||
|
if (preg_match('/^\s*}\s*$/', $line)) {
|
||||||
|
if (array_key_exists('priority', $network)) {
|
||||||
|
fwrite($wpa_file, "\tpriority=".$network['priority'].PHP_EOL);
|
||||||
|
}
|
||||||
|
fwrite($wpa_file, $line.PHP_EOL);
|
||||||
|
} else {
|
||||||
|
if (preg_match('/\\\\x[0-9A-Fa-f]{2}/', $ssid) && strpos($line, "ssid=\"") !== false) {
|
||||||
|
fwrite($wpa_file, "\tssid=P\"".$ssid."\"".PHP_EOL);
|
||||||
|
} else {
|
||||||
|
fwrite($wpa_file, $line.PHP_EOL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (strlen($network['passphrase']) == 0 && strlen($network['passkey']) == 64) {
|
||||||
|
$line = "\tpsk=" . $network['passkey'];
|
||||||
|
fwrite($wpa_file, "network={".PHP_EOL);
|
||||||
|
fwrite($wpa_file, "\tssid=\"".$ssid."\"".PHP_EOL);
|
||||||
|
fwrite($wpa_file, $line.PHP_EOL);
|
||||||
|
if (array_key_exists('priority', $network)) {
|
||||||
|
fwrite($wpa_file, "\tpriority=".$network['priority'].PHP_EOL);
|
||||||
|
}
|
||||||
|
fwrite($wpa_file, "}".PHP_EOL);
|
||||||
|
} else {
|
||||||
|
$ok = false;
|
||||||
|
fclose($wpa_file);
|
||||||
|
return ['success' => false, 'message' => 'WPA passphrase must be between 8 and 63 characters'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($wpa_file);
|
||||||
|
|
||||||
|
if ($ok) {
|
||||||
|
system('sudo cp /tmp/wifidata ' . RASPI_WPA_SUPPLICANT_CONFIG, $returnval);
|
||||||
|
if ($returnval == 0) {
|
||||||
|
exec('sudo wpa_cli -i ' . escapeshellarg($interface) . ' reconfigure', $reconfigure_out, $reconfigure_return);
|
||||||
|
if ($reconfigure_return == 0) {
|
||||||
|
return ['success' => true, 'message' => 'Wifi settings updated successfully'];
|
||||||
|
} else {
|
||||||
|
return ['success' => false, 'message' => 'Wifi settings updated but cannot restart (cannot execute "wpa_cli reconfigure")'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ['success' => false, 'message' => 'Wifi settings failed to be updated'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['success' => false, 'message' => 'Unknown error'];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,11 +15,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<div class="mt-3">RaspAP is a co-creation of <a href="https://github.com/billz">billz</a> and <a href="https://github.com/sirlagz">SirLagz</a>
|
<div class="mt-3">
|
||||||
with the contributions of our <a href="https://github.com/raspap/raspap-webgui/graphs/contributors">developer community</a>
|
<?php echo sprintf(
|
||||||
and <a href="https://crowdin.com/project/raspap">language translators</a>.
|
_('RaspAP is a co-creation of %1$s and %2$s with the contributions of our %3$s and %4$s. Learn more about joining the project as a %5$s, %6$s or %7$s with immediate access to %8$s available to %9$s.'),
|
||||||
Learn more about joining the project as a <a href="https://docs.raspap.com/#get-involved">code contributor</a>,
|
'<a href="https://github.com/billz">billz</a>',
|
||||||
<a href="https://docs.raspap.com/translations/">translator</a> or <a href="https://github.com/sponsors/RaspAP">financial sponsor</a> with immediate access to <a href="https://docs.raspap.com/insiders/#whats-in-it-for-me">exclusive features</a> available to <strong>Insiders</strong>.</div>
|
'<a href="https://github.com/sirlagz">SirLagz</a>',
|
||||||
|
'<a href="https://github.com/raspap/raspap-webgui/graphs/contributors">' . _('developer community') . '</a>',
|
||||||
|
'<a href="https://crowdin.com/project/raspap">' . _('language translators') . '</a>',
|
||||||
|
'<a href="https://docs.raspap.com/#get-involved">' . _('code contributor') . '</a>',
|
||||||
|
'<a href="https://docs.raspap.com/translations/">' . _('translator') . '</a>',
|
||||||
|
'<a href="https://github.com/sponsors/RaspAP">' . _('financial sponsor') . '</a>',
|
||||||
|
'<a href="https://docs.raspap.com/insiders/#whats-in-it-for-me">' . _('exclusive features') . '</a>',
|
||||||
|
'<strong>' . _('Insiders') . '</strong>'
|
||||||
|
); ?>
|
||||||
|
</div>
|
||||||
<div class="mt-3 project-links">
|
<div class="mt-3 project-links">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6">GitHub <i class="fa-brands fa-github"></i> <a href="https://github.com/RaspAP/" target="_blank" rel="noopener">RaspAP</a></div>
|
<div class="col-6">GitHub <i class="fa-brands fa-github"></i> <a href="https://github.com/RaspAP/" target="_blank" rel="noopener">RaspAP</a></div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<div class="tab-pane fade" id="client-list">
|
<div class="tab-pane fade" id="client-list">
|
||||||
<h4 class="mt-3 mb-3">Client list</h4>
|
<h4 class="mt-3 mb-3"><?php echo _("Client list"); ?></h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<div class="tab-pane active" id="server-settings">
|
<div class="tab-pane active" id="server-settings">
|
||||||
<h4 class="mt-3">DHCP server settings</h4>
|
<h4 class="mt-3"><?php echo _("DHCP server settings"); ?></h4>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="mb-3 col-md-6">
|
<div class="mb-3 col-md-6">
|
||||||
<label for="code">Interface</label>
|
<label for="code"><?php echo _("Interface"); ?></label>
|
||||||
<?php SelectorOptions('interface', $interfaces, $ap_iface, 'cbxdhcpiface', 'loadInterfaceDHCPSelect'); ?>
|
<?php SelectorOptions('interface', $interfaces, $ap_iface, 'cbxdhcpiface', 'loadInterfaceDHCPSelect'); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5 class="mt-1">Static IP options</h5>
|
<h5 class="mt-1"><?php echo _("Static IP options"); ?></h5>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="mb-3 col-md-6" required>
|
<div class="mb-3 col-md-6" required>
|
||||||
<label for="code"><?php echo _("IP Address"); ?></label>
|
<label for="code"><?php echo _("IP Address"); ?></label>
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5 class="mt-1">DHCP options</h5>
|
<h5 class="mt-1"><?php echo _("DHCP options"); ?></h5>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="mb-3 col-md-6">
|
<div class="mb-3 col-md-6">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@@ -107,34 +107,32 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="mb-3 col-md-6">
|
<div class="mb-3 col-md-6">
|
||||||
<label for="code"><?php echo _("Starting IP Address"); ?></label>
|
<label for="txtrangestart"><?php echo _("Starting IP Address"); ?></label>
|
||||||
<input type="text" class="form-control ip_address" id="txtrangestart" name="RangeStart" maxlength="15" />
|
<input type="text" class="form-control ip_address" id="txtrangestart" name="RangeStart" maxlength="15" />
|
||||||
<div class="invalid-feedback">
|
<div class="invalid-feedback">
|
||||||
<?php echo _("Please provide a valid Starting IP Address."); ?>
|
<?php echo _("Please provide a valid Starting IP Address."); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="mb-3 col-md-6">
|
<div class="mb-3 col-md-6">
|
||||||
<label for="code"><?php echo _("Ending IP Address"); ?></label>
|
<label for="txtrangeend"><?php echo _("Ending IP Address"); ?></label>
|
||||||
<input type="text" class="form-control ip_address" id="txtrangeend" name="RangeEnd" maxlength="15" />
|
<input type="text" class="form-control ip_address" id="txtrangeend" name="RangeEnd" maxlength="15" />
|
||||||
<div class="invalid-feedback">
|
<div class="invalid-feedback">
|
||||||
<?php echo _("Please provide a valid Ending IP Address."); ?>
|
<?php echo _("Please provide a valid Ending IP Address."); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="mb-3 col-xs-3 col-sm-3">
|
<div class="mb-3 col-xs-3 col-sm-3">
|
||||||
<label for="code"><?php echo _("Lease Time"); ?></label>
|
<label for="txtrangeleasetime"><?php echo _("Lease Time"); ?></label>
|
||||||
<input type="text" class="form-control" id="txtrangeleasetime" name="RangeLeaseTime" />
|
<input type="text" class="form-control" id="txtrangeleasetime" name="RangeLeaseTime" />
|
||||||
<div class="invalid-feedback">
|
<div class="invalid-feedback">
|
||||||
<?php echo _("Please provide a valid Lease Time."); ?>
|
<?php echo _("Please provide a valid Lease Time."); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-3 col-sm-3">
|
<div class="col-xs-3 col-sm-3">
|
||||||
<label for="code"><?php echo _("Interval"); ?></label>
|
<label for="cbxrangeleasetimeunits"><?php echo _("Interval"); ?></label>
|
||||||
<select id="cbxrangeleasetimeunits" name="RangeLeaseTimeUnits" class="form-select" >
|
<select id="cbxrangeleasetimeunits" name="RangeLeaseTimeUnits" class="form-select" >
|
||||||
<option value="m"><?php echo _("Minute(s)"); ?></option>
|
<option value="m"><?php echo _("Minute(s)"); ?></option>
|
||||||
<option value="h"><?php echo _("Hour(s)"); ?></option>
|
<option value="h"><?php echo _("Hour(s)"); ?></option>
|
||||||
|
|||||||
@@ -18,9 +18,8 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="mb-3 col-md-6">
|
<div class="mb-3 col-md-6">
|
||||||
<label for="cbxhwmode"><?php echo _("Wireless Mode") ;?></label>
|
<label for="cbxhwmode"><?php echo _("Wireless Mode") ;?></label>
|
||||||
<i class="fas fa-question-circle text-muted" data-bs-toggle="tooltip" data-bs-placement="auto" title="<?php echo _("The 802.11ac 5 GHz option is disabled until a compatible wireless regulatory domain is set."); ?>"></i>
|
|
||||||
<?php SelectorOptions('hw_mode', $arr80211Standard, $arrConfig['selected_hw_mode'], 'cbxhwmode', 'getChannel'); ?>
|
<?php SelectorOptions('hw_mode', $arr80211Standard, $arrConfig['selected_hw_mode'], 'cbxhwmode', 'getChannel'); ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="mb-3 col-md-6">
|
<div class="mb-3 col-md-6">
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div class="mb-3 col-md-8 mt-2">
|
<div class="mb-3 col-md-8 mt-2">
|
||||||
<?php
|
<?php
|
||||||
if ($arrHostapdConf['LogEnable'] == 1) {
|
if ($arrHostapdConf['LogEnable'] == 1) {
|
||||||
echo '<textarea class="logoutput text-secondary" id="hostapd-log">'.htmlspecialchars($logdata, ENT_QUOTES).'</textarea>';
|
echo '<textarea class="logoutput text-secondary" id="hostapd-log">'.htmlspecialchars(implode("\n", $logOutput), ENT_QUOTES).'</textarea>';
|
||||||
} else {
|
} else {
|
||||||
echo '<textarea class="logoutput my-3"></textarea>';
|
echo '<textarea class="logoutput my-3"></textarea>';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="cbx80211w"><?php echo _("802.11w"); ?></label>
|
<label for="cbx80211w"><?php echo _("802.11w"); ?></label>
|
||||||
<i class="fas fa-question-circle text-muted" data-bs-toggle="tooltip" data-bs-placement="auto" title="802.11w extends strong cryptographic protection to a select set of robust management frames, including Deauthentication, Disassociation and certain categories of Action Management frames. Collectively, this is known as Management Frame Protection (MFP)."></i>
|
<i class="fas fa-question-circle text-muted" data-bs-toggle="tooltip" data-bs-placement="auto" title="<?php echo _("802.11w extends strong cryptographic protection to a select set of robust management frames, including Deauthentication, Disassociation and certain categories of Action Management frames. Collectively, this is known as Management Frame Protection (MFP)."); ?>"></i>
|
||||||
<?php SelectorOptions('80211w', $arr80211w, $arrConfig['ieee80211w'] ?? 0, 'cbx80211w'); ?>
|
<?php SelectorOptions('80211w', $arr80211w, $arrConfig['ieee80211w'] ?? 0, 'cbx80211w'); ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<?php ob_start() ?>
|
<?php ob_start() ?>
|
||||||
|
<?php ob_start() ?>
|
||||||
<?php if (!RASPI_MONITOR_ENABLED) : ?>
|
<?php if (!RASPI_MONITOR_ENABLED) : ?>
|
||||||
<input type="submit" class="btn btn-outline btn-primary" name="SaveOpenVPNSettings" value="Save settings" />
|
<input type="submit" class="btn btn-outline btn-primary" name="SaveOpenVPNSettings" value="<?php echo _("Save settings"); ?>" />
|
||||||
<?php if ($openvpnstatus[0] == 0) {
|
<?php if ($openvpnstatus[0] == 0) {
|
||||||
echo '<input type="submit" class="btn btn-success" name="StartOpenVPN" value="Start OpenVPN" />' , PHP_EOL;
|
echo '<input type="submit" class="btn btn-success" name="StartOpenVPN" value="' . _("Start OpenVPN") . '" />' , PHP_EOL;
|
||||||
} else {
|
} else {
|
||||||
echo '<input type="submit" class="btn btn-warning" name="StopOpenVPN" value="Stop OpenVPN" />' , PHP_EOL;
|
echo '<input type="submit" class="btn btn-warning" name="StopOpenVPN" value="' . _("Stop OpenVPN") . '" />' , PHP_EOL;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|||||||
@@ -3,6 +3,25 @@
|
|||||||
use RaspAP\Networking\Hotspot\WiFiManager;
|
use RaspAP\Networking\Hotspot\WiFiManager;
|
||||||
$wifi = new WiFiManager();
|
$wifi = new WiFiManager();
|
||||||
|
|
||||||
|
// fix: re-apply locale settings in this template context
|
||||||
|
if (!empty($_SESSION['locale'])) {
|
||||||
|
putenv("LANG=" . $_SESSION['locale']);
|
||||||
|
setlocale(LC_ALL, $_SESSION['locale']);
|
||||||
|
bindtextdomain('messages', realpath(__DIR__ . '/../../locale'));
|
||||||
|
bind_textdomain_codeset('messages', 'UTF-8');
|
||||||
|
textdomain('messages');
|
||||||
|
}
|
||||||
|
|
||||||
|
// set defaults
|
||||||
|
$network = $network ?? [];
|
||||||
|
$network['ssid'] = $network['ssid'] ?? '';
|
||||||
|
$network['ssidutf8'] = $network['ssidutf8'] ?? $network['ssid'];
|
||||||
|
$network['configured'] = $network['configured'] ?? false;
|
||||||
|
$network['connected'] = $network['connected'] ?? false;
|
||||||
|
$network['visible'] = $network['visible'] ?? false;
|
||||||
|
$network['channel'] = $network['channel'] ?? '';
|
||||||
|
$network['protocol'] = $network['protocol'] ?? $wifi::SECURITY_OPEN;
|
||||||
|
$network['passphrase'] = $network['passphrase'] ?? '';
|
||||||
?>
|
?>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@@ -10,7 +29,7 @@ $wifi = new WiFiManager();
|
|||||||
<?php if (strlen($network['ssid']) == 0) {
|
<?php if (strlen($network['ssid']) == 0) {
|
||||||
$network['ssid'] = "(unknown)";
|
$network['ssid'] = "(unknown)";
|
||||||
} ?>
|
} ?>
|
||||||
<h5 class="card-title"><i class="fas fa-wifi me-2"></i><?php echo htmlspecialchars($network['ssidutf8'], ENT_QUOTES); ?></h5>
|
<h5 class="card-title"><i class="fas fa-wifi me-2"></i><?php echo htmlspecialchars($network['ssidutf8'], ENT_QUOTES); ?></h5>
|
||||||
<div class="info-item-wifi"><?php echo _("Status"); ?></div>
|
<div class="info-item-wifi"><?php echo _("Status"); ?></div>
|
||||||
<div>
|
<div>
|
||||||
<?php if ($network['configured']) { ?>
|
<?php if ($network['configured']) { ?>
|
||||||
@@ -57,7 +76,7 @@ $wifi = new WiFiManager();
|
|||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div class="info-item-wifi mb-2"><?php echo _("Passphrase"); ?></div>
|
<div class="info-item-wifi mb-2"><?php echo _("Passphrase"); ?></div>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<?php if ($network['protocol'] === 'Open') { ?>
|
<?php if ($network['protocol'] === $wifi::SECURITY_OPEN) { ?>
|
||||||
<input type="password" disabled class="form-control" aria-describedby="passphrase" name="passphrase<?php echo $index ?>" value="" />
|
<input type="password" disabled class="form-control" aria-describedby="passphrase" name="passphrase<?php echo $index ?>" value="" />
|
||||||
<?php } else { ?>
|
<?php } else { ?>
|
||||||
<input type="password" class="form-control" aria-describedby="passphrase" name="passphrase<?php echo $index ?>" value="<?php echo htmlspecialchars($network['passphrase']); ?>" data-bs-target="#update<?php echo $index ?>" data-colors="#ffd0d0,#d0ffd0">
|
<input type="password" class="form-control" aria-describedby="passphrase" name="passphrase<?php echo $index ?>" value="<?php echo htmlspecialchars($network['passphrase']); ?>" data-bs-target="#update<?php echo $index ?>" data-colors="#ffd0d0,#d0ffd0">
|
||||||
@@ -66,14 +85,18 @@ $wifi = new WiFiManager();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-group btn-block d-flex">
|
<div class="btn-group btn-block d-flex">
|
||||||
<?php if ($network['configured']) { ?>
|
<?php if ($network['configured']) { ?>
|
||||||
<input type="submit" class="btn btn-warning" value="<?php echo _("Update"); ?>" id="update<?php echo $index ?>" name="update<?php echo $index ?>"<?php echo ($network['protocol'] === 'Open' ? ' disabled' : '')?> data-bs-toggle="modal" data-bs-target="#configureClientModal" />
|
<input type="submit" class="btn btn-warning" value="<?php echo _("Update"); ?>" id="update<?php echo $index ?>" name="update<?php echo $index ?>"<?php echo ($network['protocol'] === 'Open' ? ' disabled' : '')?> data-bs-toggle="modal" data-bs-target="#configureClientModal" />
|
||||||
<button type="submit" class="btn btn-info" value="<?php echo $index?>" name="connect"><?php echo _("Connect"); ?></button>
|
<?php if ($network['connected']) { ?>
|
||||||
<?php } else { ?>
|
<button type="submit" class="btn btn-info" value="<?php echo $index?>" name="disconnect<?php echo $index ?>"><?php echo _("Disconnect"); ?></button>
|
||||||
<input type="submit" class="btn btn-info" value="<?php echo _("Add"); ?>" id="update<?php echo $index ?>" name="update<?php echo $index ?>" data-bs-toggle="modal" data-bs-target="#configureClientModal" />
|
<?php } else { ?>
|
||||||
<?php } ?>
|
<button type="submit" class="btn btn-info" value="<?php echo $index?>" name="connect"><?php echo _("Connect"); ?></button>
|
||||||
<input type="submit" class="btn btn-danger" value="<?php echo _("Delete"); ?>" name="delete<?php echo $index ?>"<?php echo ($network['configured'] ? '' : ' disabled')?> data-bs-toggle="modal" data-bs-target="#configureClientModal" />
|
<?php } ?>
|
||||||
</div><!-- /.btn-group -->
|
<?php } else { ?>
|
||||||
|
<input type="submit" class="btn btn-info" value="<?php echo _("Add"); ?>" id="update<?php echo $index ?>" name="update<?php echo $index ?>" data-bs-toggle="modal" data-bs-target="#configureClientModal" />
|
||||||
|
<?php } ?>
|
||||||
|
<input type="submit" class="btn btn-danger" value="<?php echo _("Delete"); ?>" name="delete<?php echo $index ?>"<?php echo ($network['configured'] ? '' : ' disabled')?> data-bs-toggle="modal" data-bs-target="#configureClientModal" />
|
||||||
|
</div><!-- /.btn-group -->
|
||||||
</div><!-- /.card-body -->
|
</div><!-- /.card-body -->
|
||||||
</div><!-- /.card -->
|
</div><!-- /.card -->
|
||||||
|
|||||||
Reference in New Issue
Block a user