Compare commits

...

87 Commits

Author SHA1 Message Date
Just Call Me Koko
e58c0638ca Merge pull request #551 from justcallmekoko/develop
Develop
2024-06-26 22:02:42 -04:00
Just Call Me Koko
84dc00df50 Create release files 2024-06-26 22:01:31 -04:00
Just Call Me Koko
769cb51496 Eat ass 2024-06-26 19:37:28 -04:00
Just Call Me Koko
edc25a8fd4 title screen 2024-06-26 19:34:21 -04:00
Just Call Me Koko
ac1fae78f5 gskjdfhglkSEJFHASDLKFJHASDFS
djhasdlkfjhsdfglksdjhflksrdjfghdlkfsjghasdlkgadshflkfajsehgliasrfgvawoe4rutnasdo8rugnqwe48yae8o9tuwN89AERGNIASHTADSKLFJGNBASDHGIBSDIKGJHBASKJFHBKJhbdoiugjhw9etgjsdoruthwseorighsdfilghsdoitgudfhogiaesrngoisdurtjngoidsfugnse80rugnero8iu6her09guhjsttgoiusdfjngoiuserhgosdurfhtoisedrughseoirttuhsdfoiguhwertoyiusdthgiueyw5nts9d8gjhsdo8rtiunserithsdrtoigshdroiguwehtoiusdfhbokjhsdafngoiasdufnviowserutghozsdifugnweoi4rufghasdoifutghweroiughasdf9iguhsero9ithsdfiogunwer09gvunsdoitusjndfliguseh54toixudrfng8o9userhngoisudhtoisdfughsodifugh
2024-06-26 19:26:32 -04:00
Just Call Me Koko
95d9e6c177 sdfghsdfglksdfjhdfgssafdlkjasdfgh 2024-06-26 19:20:42 -04:00
Just Call Me Koko
3fc08dc55d hlasd fkjdsf ahqawe oiuysrg f 2024-06-26 19:20:31 -04:00
Just Call Me Koko
ef388d64af Merge pull request #550 from justcallmekoko/master
Merge pull request #549 from justcallmekoko/develop
2024-06-26 19:10:17 -04:00
Just Call Me Koko
ac96072ff2 Merge pull request #549 from justcallmekoko/develop
Develop
2024-06-26 19:09:57 -04:00
Just Call Me Koko
585b7afdb3 Revert everything
Yea don't expect another release. I'm getting tired of this bullshit
2024-06-26 19:07:08 -04:00
Just Call Me Koko
99810dd945 Idk...remove a bunch of shit espressif broke 2024-06-26 19:03:32 -04:00
Just Call Me Koko
3ebab15c69 Update NimBLE usage 2024-06-26 18:42:51 -04:00
Just Call Me Koko
1f10c73c5e Add fixed ESPAsyncWebServer update NimBLE 2024-06-26 18:00:03 -04:00
Just Call Me Koko
8f5fce3877 This...might work? 2024-06-26 17:02:31 -04:00
Just Call Me Koko
5cd0ffb2b9 Do a fancy search 2024-06-26 16:38:47 -04:00
Just Call Me Koko
5c494724b4 Some other bullshit that github or espressif switched around idk 2024-06-26 16:19:55 -04:00
Just Call Me Koko
eeb8b1f138 Do some other shit 2024-06-26 16:15:19 -04:00
Just Call Me Koko
862a0baf8f Do some more workflow stuff 2024-06-26 15:35:46 -04:00
Just Call Me Koko
e09d443598 Fix some workflow stuff 2024-06-26 15:29:31 -04:00
Just Call Me Koko
ccf0c1cbbd Remove title screen and add select stations for mini 2024-06-26 14:12:24 -04:00
Just Call Me Koko
9690c73c35 Add new enclosure top for Marauder Mini 2024-06-06 16:19:41 -04:00
Just Call Me Koko
9836f2c256 Add dev board pro gps enclosure lid 2024-05-17 11:17:22 -04:00
Just Call Me Koko
af96ff60b7 Merge pull request #530 from justcallmekoko/master
Merge pull request #529 from justcallmekoko/develop
2024-04-25 14:10:55 -04:00
Just Call Me Koko
c0554b95ce Merge pull request #529 from justcallmekoko/develop
Develop
2024-04-25 13:55:14 -04:00
Just Call Me Koko
691bf30dbe Show essid or bssid for ap scans 2024-04-25 13:32:05 -04:00
Just Call Me Koko
b076878963 Merge pull request #528 from justcallmekoko/master
Merge pull request #527 from justcallmekoko/develop
2024-04-25 13:01:44 -04:00
Just Call Me Koko
cf103d657b Merge pull request #527 from justcallmekoko/develop
Develop
2024-04-25 12:52:22 -04:00
Just Call Me Koko
59c6f5596f Add commands for save and load 2024-04-25 10:04:36 -04:00
Just Call Me Koko
b3810adefd Add save and load APs 2024-04-24 17:16:16 -04:00
Just Call Me Koko
2fb669cd21 Add load ssids 2024-04-24 09:51:08 -04:00
Just Call Me Koko
253b29b831 Merge branch 'develop' of https://github.com/justcallmekoko/ESP32Marauder into develop 2024-04-24 00:48:38 -04:00
Just Call Me Koko
fcffb486d4 Add save SSIDs menu option 2024-04-24 00:48:30 -04:00
Just Call Me Koko
f0461f588c Merge pull request #513 from justcallmekoko/master
Merge pull request #512 from justcallmekoko/develop
2024-03-13 13:58:01 -04:00
Just Call Me Koko
6a5f95af21 Merge pull request #512 from justcallmekoko/develop
Develop
2024-03-13 12:31:12 -04:00
Just Call Me Koko
5d33fc7fe6 Create flash_download_tool_3.9.5.zip 2024-03-13 12:13:21 -04:00
Just Call Me Koko
e2b714ceb4 Add GPS thread forming enclosure 2024-03-11 16:16:27 -04:00
Just Call Me Koko
7bf1cdd083 More enclosure stuff 2024-03-09 16:16:56 -05:00
Just Call Me Koko
8dd6c4c4d3 Add dual SMA enclosure for Mini 2024-03-04 17:45:28 -05:00
Just Call Me Koko
19aeb32042 Merge pull request #501 from justcallmekoko/master
Merge pull request #500 from justcallmekoko/develop
2024-02-27 13:33:16 -05:00
Just Call Me Koko
ba92978080 Merge pull request #500 from justcallmekoko/develop
UI and Bootloop fixes, Adafruit Reverse Feather
2024-02-27 13:11:48 -05:00
Just Call Me Koko
d183894223 Increased Mini button list size to 100
This will allow for larger AP lists. Mfers in apartment buildings will still be mad
2024-02-24 20:44:12 -05:00
Just Call Me Koko
194549427a Fix mini SD update display text 2024-02-24 20:23:32 -05:00
Just Call Me Koko
4ea3166f1a Fix v6 status bar ram refresh 2024-02-24 20:13:25 -05:00
Just Call Me Koko
4188819965 Fix mini and v6 status bar channel refresh 2024-02-24 19:59:23 -05:00
Just Call Me Koko
2cf39700ee Fix num sats on status bar 2024-02-24 19:41:06 -05:00
Just Call Me Koko
cfb582c847 Fix TFT_eSPI tag name 2024-02-23 00:03:52 -05:00
Just Call Me Koko
5bd4b647b5 Add support for Adafruit Reverse Feather ESP32-S2 2024-02-22 23:54:03 -05:00
Just Call Me Koko
58c93ed401 Add remove SD files for Mini 2024-02-20 15:40:52 -05:00
Just Call Me Koko
a10434329a Merge pull request #485 from justcallmekoko/master
Merge pull request #484 from justcallmekoko/develop
2024-02-16 12:20:02 -05:00
Just Call Me Koko
e80fab89b7 Merge pull request #484 from justcallmekoko/develop
Develop
2024-02-16 12:01:49 -05:00
Just Call Me Koko
8b53b56d48 Merge branch 'develop' of https://github.com/justcallmekoko/ESP32Marauder into develop 2024-02-14 02:30:26 -05:00
Just Call Me Koko
f8cf90a298 Add Mini Keyboard 2024-02-14 02:30:21 -05:00
Just Call Me Koko
c7f1b5cb38 Merge pull request #481 from 0xchocolate/develop
Fix Portal to prompt for sign-in on Android
2024-02-13 15:32:38 -05:00
Just Call Me Koko
4c9adac032 Add mini SMA enclosure 2024-02-13 15:31:47 -05:00
0xchocolate
23015fe49e Change EP default IP address 2024-02-11 10:37:08 -08:00
Just Call Me Koko
0b0247f6ba Fix mini enclosure 2024-02-07 11:45:12 -05:00
Just Call Me Koko
eed4d7c31f Merge pull request #474 from justcallmekoko/master
Merge pull request #473 from justcallmekoko/develop
2024-02-04 14:22:43 -05:00
Just Call Me Koko
c75bd6a098 Merge pull request #473 from justcallmekoko/develop
Develop
2024-02-04 14:07:15 -05:00
Just Call Me Koko
b5337fcff5 Add disable touch 2024-02-04 13:43:01 -05:00
Just Call Me Koko
24a1862ef6 Merge pull request #469 from justcallmekoko/master
Merge pull request #468 from justcallmekoko/develop
2024-02-01 10:35:33 -05:00
Just Call Me Koko
971abc5390 Merge pull request #468 from justcallmekoko/develop
Develop
2024-02-01 10:13:29 -05:00
Just Call Me Koko
43c82dd5a3 Merge pull request #467 from justcallmekoko/unified-serial
Unified serial
2024-02-01 09:55:10 -05:00
Just Call Me Koko
6e26662209 Something about configs 2024-02-01 09:13:54 -05:00
Just Call Me Koko
10e913546b Merge pull request #452 from Willy-JL/unified-serial-pcaps
Unified serial PCAPs
2024-01-18 14:21:49 -05:00
Willy-JL
2af0d2fcc3 Merge branch 'develop' of https://github.com/justcallmekoko/ESP32Marauder into unified-serial-pcaps 2024-01-17 22:16:42 +00:00
Just Call Me Koko
28c037a19a Merge pull request #457 from marivaaldo/feature/fix-m5stickcplus-sdcard
Fix SDInterface on M5StickCPlus
2024-01-17 09:16:23 -05:00
Just Call Me Koko
f8a831f916 Add Mini GPS module power control 2024-01-17 09:15:22 -05:00
Marivaldo Júnior
ad3cf7ec7e fix m5stickcplus sdinterface 2024-01-03 12:40:54 -03:00
Willy-JL
f5d0703432 Build on PR sync too 2024-01-02 12:34:05 +00:00
Willy-JL
470f3f73c7 Block writes when buffer is not open 2024-01-02 12:06:33 +00:00
Willy-JL
9d92c5ceb5 Single buffer_obj.append() with overloads 2024-01-02 12:06:33 +00:00
Willy-JL
558fe18d66 Enable serial PCAP with CLI option 2024-01-02 12:06:33 +00:00
Willy-JL
bddc7e1d9d Send PCAP buffer on main UART console 2024-01-02 12:06:33 +00:00
Willy-JL
e560a888c3 Buffer state and concurrency improvements 2024-01-02 12:06:33 +00:00
Willy-JL
2004a9ecb4 Sd+Serial save() in buffer_obj / out of sd_obj 2024-01-02 12:06:33 +00:00
Willy-JL
c61aaf95cd Sd+Serial pcapOpen() and logOpen() in buffer_obj 2024-01-02 12:06:33 +00:00
Willy-JL
f6c27ed216 Sd+Serial pcapAdd() and logAdd() in buffer_obj 2024-01-02 12:06:33 +00:00
Willy-JL
d0f5d89980 Legacy SD do_save is always true 2024-01-02 12:06:33 +00:00
Willy-JL
fe08c145f6 Fix WarDrive do_save check for valid GPS 2024-01-02 12:06:33 +00:00
Just Call Me Koko
5909158666 Merge pull request #454 from justcallmekoko/nmea2
Nmea2
2024-01-01 17:22:24 -05:00
Just Call Me Koko
6ee3a66226 Merge pull request #435 from aubreyshomo/nmea
NMEA Passthrough: BUG FIX + Queue Display Feature Add
2023-12-18 16:30:55 -05:00
Kragg Malak
4d0594b424 ADDITIONAL SAFETY MEASURE: Queue limited at 30 sentences (configurable at build time). 2023-12-17 18:37:20 -07:00
Kragg Malak
a984b2df4a Resolve Build Test Error: gps_obj now only referenced inside appropriate #ifdef HAS_GPS block. 2023-12-17 00:21:24 -07:00
Kragg Malak
4a130edc2f SAFETY MECHANISM: If scan off, yet queue enabled for any reason, disable it. Prevents prior bug. 2023-12-16 23:46:10 -07:00
Kragg Malak
2d64045c64 BUG FIX: Queue overflow due to no stopping queue on exit from GPS menu fixed. Now calls stopscan. 2023-12-16 23:45:22 -07:00
Kragg Malak
41bbcae40c FEATURE: Displays all the queue there's room for on the screen in NMEA passthrough, tracking real estate used. Always prints available text buffer and two self-genned messages. Prints whatever else in the queue (from start) there's room for. 2023-12-16 23:43:50 -07:00
Just Call Me Koko
f98e41d159 Merge pull request #432 from justcallmekoko/master
Merge pull request #431 from justcallmekoko/develop
2023-12-14 11:42:01 -05:00
92 changed files with 8887 additions and 285820 deletions

View File

@@ -3,7 +3,6 @@ name: Build and Push
on:
workflow_dispatch:
pull_request:
types: [opened, reopened]
jobs:
# This workflow contains a single job called "build"
@@ -22,6 +21,10 @@ jobs:
sketch-names: TestFile.ino
arduino-board-fqbn: esp32:esp32:esp32s2
platform-url: https://github.com/espressif/arduino-esp32/releases/download/2.0.10/package_esp32_dev_index.json
- name: Show Arduino dir structure
run: |
find /home/runner/.arduino15/packages/esp32/hardware/
- name: Pull arduino-esp32 v2.0.4
uses: actions/checkout@v2
@@ -57,19 +60,12 @@ jobs:
repository: bigbrodude6119/ESPAsyncWebServer
ref: master
path: CustomESPAsyncWebServer
#- name: Install TFT_eSPI
# uses: actions/checkout@v2
# with:
# repository: justcallmekoko/TFT_eSPI
# ref: master
# path: CustomTFT_eSPI
- name: Install TFT_eSPI
uses: actions/checkout@v2
with:
repository: Bodmer/TFT_eSPI
ref: 2.2.23
ref: V2.5.34
path: CustomTFT_eSPI
- name: Install lv_arduino
@@ -107,12 +103,12 @@ jobs:
ref: v6.18.2
path: CustomArduinoJson
- name: Install SwitchLib
uses: actions/checkout@v2
with:
repository: justcallmekoko/SwitchLib
ref: main
path: CustomSwitchLib
#- name: Install SwitchLib
# uses: actions/checkout@v2
# with:
# repository: justcallmekoko/SwitchLib
# ref: main
# path: CustomSwitchLib
- name: Install LinkedList
uses: actions/checkout@v2
@@ -139,6 +135,7 @@ jobs:
cp User_Setup_marauder_mini.h CustomTFT_eSPI/
cp User_Setup_og_marauder.h CustomTFT_eSPI/
cp User_Setup_marauder_m5stickc.h CustomTFT_eSPI/
cp User_Setup_marauder_rev_feather.h CustomTFT_eSPI/
pwd
ls -la
ls -la CustomTFT_eSPI
@@ -169,7 +166,6 @@ jobs:
- name: Modify platform.txt
run: |
echo "Chicken"
for i in $(find /home/runner/.arduino15/packages/esp32/hardware/esp32/ -name "platform.txt"); do
sed -i 's/compiler.c.elf.libs.esp32c3=/compiler.c.elf.libs.esp32c3=-zmuldefs /' "$i"
sed -i 's/compiler.c.elf.libs.esp32s3=/compiler.c.elf.libs.esp32s3=-zmuldefs /' "$i"
@@ -183,6 +179,7 @@ jobs:
- name: Configure configs.h for Flipper Zero WiFi Dev Board
run: |
sed -i 's/^ #define MARAUDER_REV_FEATHER/ \/\/#define MARAUDER_REV_FEATHER/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_M5STICKC/ \/\/#define MARAUDER_M5STICKC/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_MINI/ \/\/#define MARAUDER_MINI/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V4/ \/\/#define MARAUDER_V4/' esp32_marauder/configs.h
@@ -205,27 +202,6 @@ jobs:
run: |
mv ./esp32_marauder/build/esp32.esp32.esp32s2/esp32_marauder.ino.bin ./esp32_marauder/build/esp32.esp32.esp32s2/esp32_marauder.flipper.bin
- name: Configure SD Serial for Flipper Zero SD Serial
run: |
pwd
sed -i 's/^ \/\/#define WRITE_PACKETS_SERIAL/ #define WRITE_PACKETS_SERIAL/' esp32_marauder/configs.h
- name: Build Marauder SD Serial for Flipper Zero WiFi Dev Board
uses: ArminJo/arduino-test-compile@v3.2.1
with:
sketch-names: esp32_marauder.ino
arduino-board-fqbn: esp32:esp32:esp32s2:PartitionScheme=min_spiffs,FlashSize=4M,PSRAM=enabled
extra-arduino-cli-args: "--warnings none"
- name: Rename Marauder Flipper SD Serial bin
run: |
mv ./esp32_marauder/build/esp32.esp32.esp32s2/esp32_marauder.ino.bin ./esp32_marauder/build/esp32.esp32.esp32s2/esp32_marauder.flipper_sd_serial.bin
- name: Remove SD Serial
run: |
pwd
sed -i 's/^ #define WRITE_PACKETS_SERIAL/ \/\/#define WRITE_PACKETS_SERIAL/' esp32_marauder/configs.h
- name: Build Marauder for Flipper Zero Multi Board
uses: ArminJo/arduino-test-compile@v3.2.1
with:
@@ -240,9 +216,11 @@ jobs:
- name: Configure TFT_eSPI and configs.h for OG Marauder
run: |
pwd
find /home/runner/ -name "*TFT_eSPI*"
find /home/runner/ -name "*TFT_eSPI*"
sed -i 's/^#include <User_Setup_marauder_rev_feather.h>/\/\/#include <User_Setup_marauder_rev_feather.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^#include <User_Setup_marauder_mini.h>/\/\/#include <User_Setup_marauder_mini.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^\/\/#include <User_Setup_og_marauder.h>/#include <User_Setup_og_marauder.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^ #define MARAUDER_REV_FEATHER/ \/\/#define MARAUDER_REV_FEATHER/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_MINI/ \/\/#define MARAUDER_MINI/' esp32_marauder/configs.h
sed -i 's/^ \/\/#define MARAUDER_V4/ #define MARAUDER_V4/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V6/ \/\/#define MARAUDER_V6/' esp32_marauder/configs.h
@@ -266,8 +244,10 @@ jobs:
- name: Configure TFT_eSPI and configs.h for Marauder v6
run: |
sed -i 's/^#include <User_Setup_marauder_rev_feather.h>/\/\/#include <User_Setup_marauder_rev_feather.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^#include <User_Setup_marauder_mini.h>/\/\/#include <User_Setup_marauder_mini.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^\/\/#include <User_Setup_og_marauder.h>/#include <User_Setup_og_marauder.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^ #define MARAUDER_REV_FEATHER/ \/\/#define MARAUDER_REV_FEATHER/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_MINI/ \/\/#define MARAUDER_MINI/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V4/ \/\/#define MARAUDER_V4/' esp32_marauder/configs.h
sed -i 's/^ \/\/#define MARAUDER_V6/ #define MARAUDER_V6/' esp32_marauder/configs.h
@@ -308,6 +288,7 @@ jobs:
- name: Configure TFT_eSPI and configs.h for Marauder v6.1
run: |
sed -i 's/^ #define MARAUDER_REV_FEATHER/ \/\/#define MARAUDER_REV_FEATHER/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_MINI/ \/\/#define MARAUDER_MINI/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V4/ \/\/#define MARAUDER_V4/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V6/ \/\/#define MARAUDER_V6/' esp32_marauder/configs.h
@@ -333,6 +314,7 @@ jobs:
run: |
sed -i 's/^#include <User_Setup_marauder_mini.h>/\/\/#include <User_Setup_marauder_mini.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^\/\/#include <User_Setup_og_marauder.h>/#include <User_Setup_og_marauder.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^ #define MARAUDER_REV_FEATHER/ \/\/#define MARAUDER_REV_FEATHER/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_MINI/ \/\/#define MARAUDER_MINI/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V4/ \/\/#define MARAUDER_V4/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V6/ \/\/#define MARAUDER_V6/' esp32_marauder/configs.h
@@ -358,6 +340,7 @@ jobs:
run: |
sed -i 's/^\/\/#include <User_Setup_marauder_mini.h>/#include <User_Setup_marauder_mini.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^#include <User_Setup_og_marauder.h>/\/\/#include <User_Setup_og_marauder.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^ #define MARAUDER_REV_FEATHER/ \/\/#define MARAUDER_REV_FEATHER/' esp32_marauder/configs.h
sed -i 's/^ \/\/#define MARAUDER_MINI/ #define MARAUDER_MINI/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V4/ \/\/#define MARAUDER_V4/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V6/ \/\/#define MARAUDER_V6/' esp32_marauder/configs.h
@@ -381,6 +364,7 @@ jobs:
- name: Configure TFT_eSPI and configs.h for ESP32 LDDB
run: |
sed -i 's/^ #define MARAUDER_REV_FEATHER/ \/\/#define MARAUDER_REV_FEATHER/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_MINI/ \/\/#define MARAUDER_MINI/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V4/ \/\/#define MARAUDER_V4/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V6/ \/\/#define MARAUDER_V6/' esp32_marauder/configs.h
@@ -404,6 +388,7 @@ jobs:
- name: Configure TFT_eSPI and configs.h for Marauder Dev Board Pro
run: |
sed -i 's/^ #define MARAUDER_REV_FEATHER/ \/\/#define MARAUDER_REV_FEATHER/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_MINI/ \/\/#define MARAUDER_MINI/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V4/ \/\/#define MARAUDER_V4/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V6/ \/\/#define MARAUDER_V6/' esp32_marauder/configs.h
@@ -431,6 +416,7 @@ jobs:
find /home/runner/ -name "*TFT_eSPI*"
sed -i 's/^#include <User_Setup_marauder_mini.h>/\/\/#include <User_Setup_marauder_mini.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^\/\/#include <User_Setup_marauder_m5stickc.h>/#include <User_Setup_marauder_m5stickc.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^ #define MARAUDER_REV_FEATHER/ \/\/#define MARAUDER_REV_FEATHER/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_MINI/ \/\/#define MARAUDER_MINI/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V4/ \/\/#define MARAUDER_V4/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V6/ \/\/#define MARAUDER_V6/' esp32_marauder/configs.h
@@ -452,6 +438,36 @@ jobs:
- name: Rename Marauder M5StickC bin
run: |
mv ./esp32_marauder/build/esp32.esp32.m5stick-c/esp32_marauder.ino.bin ./esp32_marauder/build/esp32.esp32.m5stick-c/esp32_marauder.m5stickc_plus.bin
- name: Configure TFT_eSPI and configs.h for Marauder Rev Feather
run: |
pwd
find /home/runner/ -name "*TFT_eSPI*"
sed -i 's/^\/\/#include <User_Setup_marauder_rev_feather.h>/#include <User_Setup_marauder_rev_feather.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^#include <User_Setup_marauder_mini.h>/\/\/#include <User_Setup_marauder_mini.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^#include <User_Setup_marauder_m5stickc.h>/\/\/#include <User_Setup_marauder_m5stickc.h>/' /home/runner/Arduino/libraries/CustomTFT_eSPI/User_Setup_Select.h
sed -i 's/^ \/\/#define MARAUDER_REV_FEATHER/ #define MARAUDER_REV_FEATHER/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_MINI/ \/\/#define MARAUDER_MINI/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V4/ \/\/#define MARAUDER_V4/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V6/ \/\/#define MARAUDER_V6/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_V6_1/ \/\/#define MARAUDER_V6_1/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_KIT/ \/\/#define MARAUDER_KIT/' esp32_marauder/configs.h
sed -i 's/^ #define GENERIC_ESP32/ \/\/#define GENERIC_ESP32/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_FLIPPER/ \/\/#define MARAUDER_FLIPPER/' esp32_marauder/configs.h
sed -i 's/^ #define ESP32_LDDB/ \/\/#define ESP32_LDDB/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_DEV_BOARD_PRO/ \/\/#define MARAUDER_DEV_BOARD_PRO/' esp32_marauder/configs.h
sed -i 's/^ #define MARAUDER_M5STICKC/ \/\/#define MARAUDER_M5STICKC/' esp32_marauder/configs.h
- name: Build Marauder for Marauder Rev Feather
uses: ArminJo/arduino-test-compile@v3.2.1
with:
sketch-names: esp32_marauder.ino
arduino-board-fqbn: esp32:esp32:esp32s2:PartitionScheme=min_spiffs,FlashSize=4M,PSRAM=enabled
extra-arduino-cli-args: "--warnings none"
- name: Rename Marauder Marauder Rev Feather
run: |
mv ./esp32_marauder/build/esp32.esp32.esp32s2/esp32_marauder.ino.bin ./esp32_marauder/build/esp32.esp32.esp32s2/esp32_marauder.rev_feather.bin
- name: Display finished bins
run: |
@@ -464,13 +480,6 @@ jobs:
path: ./esp32_marauder/build/esp32.esp32.esp32s2/esp32_marauder.flipper.bin
retention-days: 5
- name: 'Upload Flipper SD Serial Artifact'
uses: actions/upload-artifact@v3
with:
name: esp32_marauder.flipper_sd_serial.bin
path: ./esp32_marauder/build/esp32.esp32.esp32s2/esp32_marauder.flipper_sd_serial.bin
retention-days: 5
- name: 'Upload MultiboardS3 Artifact'
uses: actions/upload-artifact@v3
with:
@@ -534,6 +543,13 @@ jobs:
path: ./esp32_marauder/build/esp32.esp32.m5stick-c/esp32_marauder.m5stickc_plus.bin
retention-days: 5
- name: 'Upload Rev Feather Artifact'
uses: actions/upload-artifact@v3
with:
name: esp32_marauder.rev_feather.bin
path: ./esp32_marauder/build/esp32.esp32.esp32s2/esp32_marauder.rev_feather.bin
retention-days: 5
- name: Create Release
id: create_release
uses: actions/create-release@v1
@@ -558,18 +574,6 @@ jobs:
asset_content_type: application/bin
if: github.event_name != 'pull_request'
- name: Upload Flipper SD Serial Asset
id: upload-flipper-sd-serial-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./esp32_marauder/build/esp32.esp32.esp32s2/esp32_marauder.flipper_sd_serial.bin
asset_name: esp32_marauder.flipper_sd_serial.bin
asset_content_type: application/bin
if: github.event_name != 'pull_request'
- name: Upload MultiboardS3 Asset
id: upload-multiboardS3-release-asset
uses: actions/upload-release-asset@v1
@@ -677,3 +681,15 @@ jobs:
asset_name: esp32_marauder.m5stickc_plus.bin
asset_content_type: application/bin
if: github.event_name != 'pull_request'
- name: Upload Rev Feather Asset
id: upload-rev-feather-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./esp32_marauder/build/esp32.esp32.esp32s2/esp32_marauder.rev_feather.bin
asset_name: esp32_marauder.rev_feather.bin
asset_content_type: application/bin
if: github.event_name != 'pull_request'

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -24,6 +24,7 @@
#include <User_Setup_og_marauder.h>
//#include <User_Setup_marauder_mini.h>
//#include <User_Setup_marauder_m5stickc.h>
//#include <User_Setup_marauder_rev_feather.h>
//#include <User_Setups/Setup1_ILI9341.h> // Setup file configured for my ILI9341
//#include <User_Setups/Setup2_ST7735.h> // Setup file configured for my ST7735

View File

@@ -43,8 +43,8 @@
// For ST7789 and ILI9341 ONLY, define the colour order IF the blue and red are swapped on your display
// Try ONE option at a time to find the correct colour order for your display
#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
// #define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
// #define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
// For M5Stack ESP32 module with integrated ILI9341 display ONLY, remove // in line below

View File

@@ -0,0 +1,59 @@
// ST7789 135 x 240 display with no chip select line
#define ST7789_DRIVER // Configure all registers
#define TFT_WIDTH 135
#define TFT_HEIGHT 240
#define CGRAM_OFFSET // Library will add offsets required
//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red
//#define TFT_INVERSION_ON
//#define TFT_INVERSION_OFF
// DSTIKE stepup
//#define TFT_DC 23
//#define TFT_RST 32
//#define TFT_MOSI 26
//#define TFT_SCLK 27
// Generic ESP32 setup
//#define TFT_MISO 19
//#define TFT_MOSI 23
//#define TFT_SCLK 18
//#define TFT_CS -1 // Not connected
//#define TFT_DC 2
//#define TFT_RST 4 // Connect reset to ensure display initialises
// For NodeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation
#define TFT_MISO 37
#define TFT_MOSI 35
#define TFT_SCLK 36
#define TFT_CS 42 // Define as not used
#define TFT_DC 40 // Data Command control pin
#define TFT_RST 41 // TFT reset pin (could connect to NodeMCU RST, see next line)
#define TFT_BL 45
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
#define SMOOTH_FONT
// #define SPI_FREQUENCY 27000000
#define SPI_FREQUENCY 40000000
#define SPI_READ_FREQUENCY 20000000
#define SPI_TOUCH_FREQUENCY 2500000
// #define SUPPORT_TRANSACTIONS

View File

@@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@@ -200,10 +200,13 @@ PROGMEM static const unsigned char menu_icons[][66] = {
0x1F, 0xD0, 0x3F, 0x3F, 0xE0, 0x3F, 0x5F, 0xF0, 0x3F, 0xEF, 0xE8, 0x3F,
0xF7, 0xE5, 0x3B, 0xFB, 0xDE, 0x3A, 0x7D, 0xFF, 0x3A, 0xBB, 0x7F, 0x3B,
0xD7, 0x9F, 0x3D, 0xEF, 0xFF, 0x3E, 0xFF, 0x0F, 0x3F, 0xFF, 0xFF, 0x3F,
0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0x3F}
0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0x3F},
{0xFF, 0xFF, 0xFD, 0xBF, 0x0B, 0xD0, 0xE7, 0xE7, 0xEF, 0xF7, 0xCF, 0xF3, // DISABLED TOUCH: 34
0xAF, 0xF5, 0x6F, 0xF6, 0x6F, 0xF6, 0xAF, 0xF5, 0xCF, 0xF3, 0x0F, 0xF0,
0xE7, 0xE7, 0x0B, 0xD0, 0xFD, 0xBF, 0xFF, 0xFF}
};
#ifndef MARAUDER_MINI
/*#ifndef MARAUDER_MINI
static const uint8_t MarauderTitle[] PROGMEM = {
0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60,
0x00, 0x60, 0x00, 0x00, 0xFF, 0xE1, 0x00, 0x22, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D,
@@ -1054,7 +1057,7 @@ PROGMEM static const unsigned char menu_icons[][66] = {
0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A,
0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x0A,
0x28, 0xA2, 0x80, 0x0A, 0x28, 0xA2, 0x80, 0x3F, 0xFF, 0xD9};
/*static const uint8_t MarauderTitle[] PROGMEM = {
*//*static const uint8_t MarauderTitle[] PROGMEM = {
0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60,
0x00, 0x60, 0x00, 0x00, 0xff, 0xe1, 0x00, 0x66, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4d, 0x4d,
0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01,
@@ -1452,8 +1455,9 @@ PROGMEM static const unsigned char menu_icons[][66] = {
0xc4, 0x8b, 0x1c, 0x7b, 0xe4, 0x66, 0x6d, 0xaa, 0xbc, 0x05, 0xce, 0x00, 0xe0, 0x50, 0x07, 0x96,
0xd1, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x01, 0xff, 0xd9
};*/
#endif
//#endif
/*
#ifdef MARAUDER_MINI
const uint8_t MarauderTitle[] PROGMEM = {
0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60,
@@ -1829,5 +1833,6 @@ PROGMEM static const unsigned char menu_icons[][66] = {
0x28, 0x00, 0xA2, 0x80, 0x0A, 0x28, 0x03, 0xFF, 0xD9};
#endif
*/
#endif

View File

@@ -6,17 +6,17 @@ Buffer::Buffer(){
bufB = (uint8_t*)malloc(BUF_SIZE);
}
void Buffer::createPcapFile(fs::FS* fs, String fn, bool log){
void Buffer::createFile(String name, bool is_pcap){
int i=0;
if (!log) {
if (is_pcap) {
do{
fileName = "/"+fn+"_"+(String)i+".pcap";
fileName = "/"+name+"_"+(String)i+".pcap";
i++;
} while(fs->exists(fileName));
}
else {
do{
fileName = "/"+fn+"_"+(String)i+".log";
fileName = "/"+name+"_"+(String)i+".log";
i++;
} while(fs->exists(fileName));
}
@@ -27,7 +27,7 @@ void Buffer::createPcapFile(fs::FS* fs, String fn, bool log){
file.close();
}
void Buffer::open(bool log){
void Buffer::open(bool is_pcap){
bufSizeA = 0;
bufSizeB = 0;
@@ -35,7 +35,7 @@ void Buffer::open(bool log){
writing = true;
if (!log) {
if (is_pcap) {
write(uint32_t(0xa1b2c3d4)); // magic number
write(uint16_t(2)); // major version number
write(uint16_t(4)); // minor version number
@@ -46,14 +46,35 @@ void Buffer::open(bool log){
}
}
void Buffer::close(fs::FS* fs){
if(!writing) return;
forceSave(fs);
writing = false;
Serial.println(text01);
void Buffer::openFile(String file_name, fs::FS* fs, bool serial, bool is_pcap) {
bool save_pcap = settings_obj.loadSetting<bool>("SavePCAP");
if (!save_pcap) {
this->fs = NULL;
this->serial = false;
writing = false;
return;
}
this->fs = fs;
this->serial = serial;
if (this->fs) {
createFile(file_name, is_pcap);
}
if (this->fs || this->serial) {
open(is_pcap);
} else {
writing = false;
}
}
void Buffer::addPacket(uint8_t* buf, uint32_t len, bool log){
void Buffer::pcapOpen(String file_name, fs::FS* fs, bool serial) {
openFile(file_name, fs, serial, true);
}
void Buffer::logOpen(String file_name, fs::FS* fs, bool serial) {
openFile(file_name, fs, serial, false);
}
void Buffer::add(const uint8_t* buf, uint32_t len, bool is_pcap){
// buffer is full -> drop packet
if((useA && bufSizeA + len >= BUF_SIZE && bufSizeB > 0) || (!useA && bufSizeB + len >= BUF_SIZE && bufSizeA > 0)){
//Serial.print(";");
@@ -74,7 +95,7 @@ void Buffer::addPacket(uint8_t* buf, uint32_t len, bool log){
microSeconds -= seconds*1000*1000; // e.g. 45200400 - 45*1000*1000 = 45200400 - 45000000 = 400us (because we only need the offset)
if (!log) {
if (is_pcap) {
write(seconds); // ts_sec
write(microSeconds); // ts_usec
write(len); // incl_len
@@ -84,6 +105,20 @@ void Buffer::addPacket(uint8_t* buf, uint32_t len, bool log){
write(buf, len); // packet payload
}
void Buffer::append(wifi_promiscuous_pkt_t *packet, int len) {
bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
if (save_packet) {
add(packet->payload, len, true);
}
}
void Buffer::append(String log) {
bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
if (save_packet) {
add((const uint8_t*)log.c_str(), log.length(), false);
}
}
void Buffer::write(int32_t n){
uint8_t buf[4];
buf[0] = n;
@@ -109,8 +144,9 @@ void Buffer::write(uint16_t n){
write(buf,2);
}
void Buffer::write(uint8_t* buf, uint32_t len){
void Buffer::write(const uint8_t* buf, uint32_t len){
if(!writing) return;
while(saving) delay(10);
if(useA){
memcpy(&bufA[bufSizeA], buf, len);
@@ -121,127 +157,86 @@ void Buffer::write(uint8_t* buf, uint32_t len){
}
}
void Buffer::save(fs::FS* fs){
if(saving) return; // makes sure the function isn't called simultaneously on different cores
// buffers are already emptied, therefor saving is unecessary
if((useA && bufSizeB == 0) || (!useA && bufSizeA == 0)){
//Serial.printf("useA: %s, bufA %u, bufB %u\n",useA ? "true" : "false",bufSizeA,bufSizeB); // for debug porpuses
return;
}
//Serial.println("saving file");
uint32_t startTime = millis();
uint32_t finishTime;
file = fs->open(fileName, FILE_APPEND);
if (!file) {
Serial.println(text02 + fileName+"'");
//useSD = false;
return;
}
saving = true;
uint32_t len;
if(useA){
file.write(bufB, bufSizeB);
len = bufSizeB;
bufSizeB = 0;
}
else{
file.write(bufA, bufSizeA);
len = bufSizeA;
bufSizeA = 0;
}
file.close();
finishTime = millis() - startTime;
//Serial.printf("\n%u bytes written for %u ms\n", len, finishTime);
saving = false;
}
void Buffer::forceSave(fs::FS* fs){
uint32_t len = bufSizeA + bufSizeB;
if(len == 0) return;
void Buffer::saveFs(){
file = fs->open(fileName, FILE_APPEND);
if (!file) {
Serial.println(text02+fileName+"'");
//useSD = false;
return;
}
saving = true;
writing = false;
if(useA){
if(bufSizeB > 0){
file.write(bufB, bufSizeB);
bufSizeB = 0;
}
if(bufSizeA > 0){
file.write(bufA, bufSizeA);
bufSizeA = 0;
}
} else {
if(bufSizeA > 0){
file.write(bufA, bufSizeA);
bufSizeA = 0;
}
if(bufSizeB > 0){
file.write(bufB, bufSizeB);
bufSizeB = 0;
}
}
file.close();
//Serial.printf("saved %u bytes\n",len);
saving = false;
writing = true;
}
void Buffer::forceSaveSerial() {
uint32_t len = bufSizeA + bufSizeB;
if(len == 0) return;
void Buffer::saveSerial() {
// Saves to main console UART, user-facing app will ignore these markers
// Uses / and ] in markers as they are illegal characters for SSIDs
const char* mark_begin = "[BUF/BEGIN]";
const size_t mark_begin_len = strlen(mark_begin);
const char* mark_close = "[BUF/CLOSE]";
const size_t mark_close_len = strlen(mark_close);
saving = true;
writing = false;
// Additional buffer and memcpy's so that a single Serial.write() is called
// This is necessary so that other console output isn't mixed into buffer stream
uint8_t* buf = (uint8_t*)malloc(mark_begin_len + bufSizeA + bufSizeB + mark_close_len);
uint8_t* it = buf;
memcpy(it, mark_begin, mark_begin_len);
it += mark_begin_len;
if(useA){
if(bufSizeB > 0){
Serial1.write(bufB, bufSizeB);
bufSizeB = 0;
memcpy(it, bufB, bufSizeB);
it += bufSizeB;
}
if(bufSizeA > 0){
Serial1.write(bufA, bufSizeA);
bufSizeA = 0;
memcpy(it, bufA, bufSizeA);
it += bufSizeA;
}
} else {
if(bufSizeA > 0){
Serial1.write(bufA, bufSizeA);
bufSizeA = 0;
memcpy(it, bufA, bufSizeA);
it += bufSizeA;
}
if(bufSizeB > 0){
Serial1.write(bufB, bufSizeB);
bufSizeB = 0;
memcpy(it, bufB, bufSizeB);
it += bufSizeB;
}
}
saving = false;
writing = true;
memcpy(it, mark_close, mark_close_len);
it += mark_close_len;
Serial.write(buf, it - buf);
free(buf);
}
void Buffer::save() {
saving = true;
if((bufSizeA + bufSizeB) == 0){
saving = false;
return;
}
if(this->fs) saveFs();
if(this->serial) saveSerial();
bufSizeA = 0;
bufSizeB = 0;
saving = false;
}

View File

@@ -6,7 +6,7 @@
#include "Arduino.h"
#include "FS.h"
#include "settings.h"
//#include "SD_MMC.h"
#include "esp_wifi_types.h"
#define BUF_SIZE 3 * 1024 // Had to reduce buffer size to save RAM. GG @spacehuhn
#define SNAP_LEN 2324 // max len of each recieved packet
@@ -18,18 +18,22 @@ extern Settings settings_obj;
class Buffer {
public:
Buffer();
void createPcapFile(fs::FS* fs, String fn = "", bool log = false);
void open(bool log = false);
void close(fs::FS* fs);
void addPacket(uint8_t* buf, uint32_t len, bool log = false);
void save(fs::FS* fs);
void forceSave(fs::FS* fs);
void forceSaveSerial();
void pcapOpen(String file_name, fs::FS* fs, bool serial);
void logOpen(String file_name, fs::FS* fs, bool serial);
void append(wifi_promiscuous_pkt_t *packet, int len);
void append(String log);
void save();
private:
void createFile(String name, bool is_pcap);
void open(bool is_pcap);
void openFile(String file_name, fs::FS* fs, bool serial, bool is_pcap);
void add(const uint8_t* buf, uint32_t len, bool is_pcap);
void write(int32_t n);
void write(uint32_t n);
void write(uint16_t n);
void write(uint8_t* buf, uint32_t len);
void write(const uint8_t* buf, uint32_t len);
void saveFs();
void saveSerial();
uint8_t* bufA;
uint8_t* bufB;
@@ -43,6 +47,8 @@ class Buffer {
String fileName = "/0.pcap";
File file;
fs::FS* fs;
bool serial;
};
#endif

View File

@@ -9,9 +9,6 @@ void CommandLine::RunSetup() {
Serial.println(F("\n\n--------------------------------\n"));
Serial.println(F(" ESP32 Marauder \n"));
Serial.println(" " + version_number + "\n");
#ifdef WRITE_PACKETS_SERIAL
Serial.println(F(" >> Serial \n"));
#endif
Serial.println(F(" By: justcallmekoko\n"));
Serial.println(F("--------------------------------\n\n"));
@@ -248,6 +245,8 @@ void CommandLine::runCommand(String input) {
Serial.println(HELP_SEL_CMD_A);
Serial.println(HELP_SSID_CMD_A);
Serial.println(HELP_SSID_CMD_B);
Serial.println(HELP_SAVE_CMD);
Serial.println(HELP_LOAD_CMD);
// Bluetooth sniff/scan
#ifdef HAS_BT
@@ -508,6 +507,9 @@ void CommandLine::runCommand(String input) {
//// WiFi/Bluetooth Scan/Attack commands
if (!wifi_scan_obj.scanning()) {
// Dump pcap/log to serial too, valid for all scan/attack commands
wifi_scan_obj.save_serial = this->argSearch(&cmd_args, "-serial") != -1;
// Signal strength scan
if (cmd_args.get(0) == SIGSTREN_CMD) {
Serial.println("Starting Signal Strength Scan. Stop with " + (String)STOPSCAN_CMD);
@@ -1035,21 +1037,17 @@ void CommandLine::runCommand(String input) {
//}
// Update via SD
if (sd_sw != -1) {
#ifdef HAS_SD
#ifndef WRITE_PACKETS_SERIAL
if (!sd_obj.supported) {
Serial.println("SD card is not connected. Cannot perform SD Update");
return;
}
wifi_scan_obj.currentScanMode = OTA_UPDATE;
sd_obj.runUpdate();
#else
Serial.println("SD card not initialized. Cannot perform SD Update");
#endif
#else
Serial.println("SD card support disabled. Cannot perform SD Update");
return;
#endif
#ifdef HAS_SD
if (!sd_obj.supported) {
Serial.println("SD card is not connected. Cannot perform SD Update");
return;
}
wifi_scan_obj.currentScanMode = OTA_UPDATE;
sd_obj.runUpdate();
#else
Serial.println("SD card support disabled. Cannot perform SD Update");
return;
#endif
}
}
}
@@ -1266,6 +1264,41 @@ void CommandLine::runCommand(String input) {
return;
}
}
else if (cmd_args.get(0) == SAVE_CMD) {
int ap_sw = this->argSearch(&cmd_args, "-a");
int st_sw = this->argSearch(&cmd_args, "-s");
if (ap_sw != -1) {
#ifdef HAS_SCREEN
menu_function_obj.changeMenu(&menu_function_obj.saveAPsMenu);
#endif
wifi_scan_obj.RunSaveAPList(true);
}
else if (st_sw != -1) {
#ifdef HAS_SCREEN
menu_function_obj.changeMenu(&menu_function_obj.saveSSIDsMenu);
#endif
wifi_scan_obj.RunSaveSSIDList(true);
}
}
else if (cmd_args.get(0) == LOAD_CMD) {
int ap_sw = this->argSearch(&cmd_args, "-a");
int st_sw = this->argSearch(&cmd_args, "-s");
if (ap_sw != -1) {
#ifdef HAS_SCREEN
menu_function_obj.changeMenu(&menu_function_obj.loadAPsMenu);
#endif
wifi_scan_obj.RunLoadAPList();
}
else if (st_sw != -1) {
#ifdef HAS_SCREEN
menu_function_obj.changeMenu(&menu_function_obj.loadSSIDsMenu);
#endif
wifi_scan_obj.RunLoadSSIDList();
}
}
// SSID stuff
else if (cmd_args.get(0) == SSID_CMD) {
int add_sw = this->argSearch(&cmd_args, "-a");

View File

@@ -77,6 +77,8 @@ const char PROGMEM ATTACK_TYPE_RR[] = "rickroll";
const char PROGMEM LIST_AP_CMD[] = "list";
const char PROGMEM SEL_CMD[] = "select";
const char PROGMEM SSID_CMD[] = "ssid";
const char PROGMEM SAVE_CMD[] = "save";
const char PROGMEM LOAD_CMD[] = "load";
// Bluetooth sniff/scan
const char PROGMEM BT_SPAM_CMD[] = "blespam";
@@ -128,6 +130,8 @@ const char PROGMEM HELP_LIST_AP_CMD_C[] = "list -c";
const char PROGMEM HELP_SEL_CMD_A[] = "select -a/-s/-c <index (comma separated)>/-f \"equals <String> or contains <String>\"";
const char PROGMEM HELP_SSID_CMD_A[] = "ssid -a [-g <count>/-n <name>]";
const char PROGMEM HELP_SSID_CMD_B[] = "ssid -r <index>";
const char PROGMEM HELP_SAVE_CMD[] = "save -a/-s";
const char PROGMEM HELP_LOAD_CMD[] = "load -a/-s";
// Bluetooth sniff/scan
const char PROGMEM HELP_BT_SNIFF_CMD[] = "sniffbt";

View File

@@ -28,6 +28,10 @@ void Display::RunSetup()
tft.setRotation(1);
#endif
#ifdef MARAUDER_REV_FEATHER
tft.setRotation(1);
#endif
tft.setCursor(0, 0);
#ifdef HAS_ILI9341
@@ -53,6 +57,14 @@ void Display::RunSetup()
#ifdef KIT
pinMode(KIT_LED_BUILTIN, OUTPUT);
#endif
#ifdef MARAUDER_REV_FEATHER
pinMode(7, OUTPUT);
delay(10);
digitalWrite(7, HIGH);
#endif
}
void Display::drawFrame()
@@ -410,6 +422,7 @@ void Display::scrollAddress(uint16_t vsp) {
// JPEG_functions
/*
void Display::drawJpeg(const char *filename, int xpos, int ypos) {
// Open the named file (the Jpeg decoder library will close it after rendering image)
@@ -440,6 +453,7 @@ void Display::drawJpeg(const char *filename, int xpos, int ypos) {
// Serial.println(F("Jpeg file format not supported!"));
//}
}
*/
/*void Display::setupDraw() {
this->tft.drawLine(0, 0, 10, 0, TFT_MAGENTA);

View File

@@ -110,7 +110,7 @@ class Display
void buildBanner(String msg, int xpos);
void clearScreen();
void displayBuffer(bool do_clear = false);
void drawJpeg(const char *filename, int xpos, int ypos);
//void drawJpeg(const char *filename, int xpos, int ypos);
void getTouchWhileFunction(bool pressed);
void initScrollValues(bool tte = false);
void jpegInfo();

View File

@@ -92,41 +92,41 @@ bool EvilPortal::setHtml() {
return true;
}
Serial.println("Setting HTML...");
#ifndef WRITE_PACKETS_SERIAL
#ifdef HAS_SD
File html_file = sd_obj.getFile("/" + this->target_html_name);
if (!html_file) {
#else
File html_file;
#endif
if (!html_file) {
#ifdef HAS_SCREEN
this->sendToDisplay("Could not find /" + this->target_html_name);
this->sendToDisplay("Touch to exit...");
#endif
Serial.println("Could not find /" + this->target_html_name + ". Use stopscan...");
return false;
}
else {
if (html_file.size() > MAX_HTML_SIZE) {
#ifdef HAS_SCREEN
this->sendToDisplay("Could not find /" + this->target_html_name);
this->sendToDisplay("The given HTML is too large.");
this->sendToDisplay("The Byte limit is " + (String)MAX_HTML_SIZE);
this->sendToDisplay("Touch to exit...");
#endif
Serial.println("Could not find /" + this->target_html_name + ". Use stopscan...");
Serial.println("The provided HTML is too large. Byte limit is " + (String)MAX_HTML_SIZE + "\nUse stopscan...");
return false;
}
else {
if (html_file.size() > MAX_HTML_SIZE) {
#ifdef HAS_SCREEN
this->sendToDisplay("The given HTML is too large.");
this->sendToDisplay("The Byte limit is " + (String)MAX_HTML_SIZE);
this->sendToDisplay("Touch to exit...");
#endif
Serial.println("The provided HTML is too large. Byte limit is " + (String)MAX_HTML_SIZE + "\nUse stopscan...");
return false;
}
String html = "";
while (html_file.available()) {
char c = html_file.read();
if (isPrintable(c))
html.concat(c);
}
strncpy(index_html, html.c_str(), strlen(html.c_str()));
this->has_html = true;
Serial.println("html set");
html_file.close();
return true;
String html = "";
while (html_file.available()) {
char c = html_file.read();
if (isPrintable(c))
html.concat(c);
}
#else
return false;
#endif
strncpy(index_html, html.c_str(), strlen(html.c_str()));
this->has_html = true;
Serial.println("html set");
html_file.close();
return true;
}
}
@@ -143,47 +143,47 @@ bool EvilPortal::setAP(LinkedList<ssid>* ssids, LinkedList<AccessPoint>* access_
// If there are no SSIDs and there are no APs selected, pull from file
// This means the file is last resort
if ((ssids->size() <= 0) && (temp_ap_name == "")) {
#ifndef WRITE_PACKETS_SERIAL
#ifdef HAS_SD
File ap_config_file = sd_obj.getFile("/ap.config.txt");
// Could not open config file. return false
if (!ap_config_file) {
#else
File ap_config_file;
#endif
// Could not open config file. return false
if (!ap_config_file) {
#ifdef HAS_SCREEN
this->sendToDisplay("Could not find /ap.config.txt.");
this->sendToDisplay("Touch to exit...");
#endif
Serial.println("Could not find /ap.config.txt. Use stopscan...");
return false;
}
// Config file good. Proceed
else {
// ap name too long. return false
if (ap_config_file.size() > MAX_AP_NAME_SIZE) {
#ifdef HAS_SCREEN
this->sendToDisplay("Could not find /ap.config.txt.");
this->sendToDisplay("The given AP name is too large.");
this->sendToDisplay("The Byte limit is " + (String)MAX_AP_NAME_SIZE);
this->sendToDisplay("Touch to exit...");
#endif
Serial.println("Could not find /ap.config.txt. Use stopscan...");
Serial.println("The provided AP name is too large. Byte limit is " + (String)MAX_AP_NAME_SIZE + "\nUse stopscan...");
return false;
}
// Config file good. Proceed
else {
// ap name too long. return false
if (ap_config_file.size() > MAX_AP_NAME_SIZE) {
#ifdef HAS_SCREEN
this->sendToDisplay("The given AP name is too large.");
this->sendToDisplay("The Byte limit is " + (String)MAX_AP_NAME_SIZE);
this->sendToDisplay("Touch to exit...");
#endif
Serial.println("The provided AP name is too large. Byte limit is " + (String)MAX_AP_NAME_SIZE + "\nUse stopscan...");
return false;
// AP name length good. Read from file into var
while (ap_config_file.available()) {
char c = ap_config_file.read();
Serial.print(c);
if (isPrintable(c)) {
ap_config.concat(c);
}
// AP name length good. Read from file into var
while (ap_config_file.available()) {
char c = ap_config_file.read();
Serial.print(c);
if (isPrintable(c)) {
ap_config.concat(c);
}
}
#ifdef HAS_SCREEN
this->sendToDisplay("AP name from config file");
this->sendToDisplay("AP name: " + ap_config);
#endif
Serial.println("AP name from config file: " + ap_config);
ap_config_file.close();
}
#else
return false;
#endif
#ifdef HAS_SCREEN
this->sendToDisplay("AP name from config file");
this->sendToDisplay("AP name: " + ap_config);
#endif
Serial.println("AP name from config file: " + ap_config);
ap_config_file.close();
}
}
// There are SSIDs in the list but there could also be an AP selected
// Priority is SSID list before AP selected and config file
@@ -242,10 +242,13 @@ bool EvilPortal::setAP(LinkedList<ssid>* ssids, LinkedList<AccessPoint>* access_
}
void EvilPortal::startAP() {
const IPAddress AP_IP(172, 0, 0, 1);
Serial.print("starting ap ");
Serial.println(apName);
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(AP_IP, AP_IP, IPAddress(255, 255, 255, 0));
WiFi.softAP(apName);
#ifdef HAS_SCREEN
@@ -272,37 +275,6 @@ void EvilPortal::startPortal() {
this->runServer = true;
}
void EvilPortal::convertStringToUint8Array(const String& str, uint8_t*& buf, uint32_t& len) {
len = str.length(); // Obtain the length of the string
buf = new uint8_t[len]; // Dynamically allocate the buffer
// Copy each character from the string to the buffer
for (uint32_t i = 0; i < len; i++) {
buf[i] = static_cast<uint8_t>(str.charAt(i));
}
}
void EvilPortal::addLog(String log, int len) {
bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
if (save_packet) {
uint8_t* logBuffer = nullptr;
uint32_t logLength = 0;
this->convertStringToUint8Array(log, logBuffer, logLength);
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.addPacket(logBuffer, logLength, true);
delete[] logBuffer;
#elif defined(HAS_SD)
sd_obj.addPacket(logBuffer, logLength, true);
delete[] logBuffer;
#else
delete[] logBuffer;
return;
#endif
}
}
void EvilPortal::sendToDisplay(String msg) {
#ifdef HAS_SCREEN
String display_string = "";
@@ -329,7 +301,7 @@ void EvilPortal::main(uint8_t scan_mode) {
String logValue2 = "p: " + this->password;
String full_string = logValue1 + " " + logValue2 + "\n";
Serial.print(full_string);
this->addLog(full_string, full_string.length());
buffer_obj.append(full_string);
#ifdef HAS_SCREEN
this->sendToDisplay(full_string);
#endif

View File

@@ -13,17 +13,12 @@
#include "Display.h"
#include <LinkedList.h>
#endif
#ifndef WRITE_PACKETS_SERIAL
#include "SDInterface.h"
#else
#include "Buffer.h"
#endif
#include "SDInterface.h"
#include "Buffer.h"
#include "lang_var.h"
extern Settings settings_obj;
#ifndef WRITE_PACKETS_SERIAL
extern SDInterface sd_obj;
#endif
extern SDInterface sd_obj;
#ifdef HAS_SCREEN
extern Display display_obj;
#endif
@@ -95,7 +90,6 @@ class EvilPortal {
void setupServer();
void startPortal();
void startAP();
void convertStringToUint8Array(const String& str, uint8_t*& buf, uint32_t& len);
void sendToDisplay(String msg);
public:
@@ -111,7 +105,6 @@ class EvilPortal {
String get_user_name();
String get_password();
void setup();
void addLog(String log, int len);
bool begin(LinkedList<ssid>* ssids, LinkedList<AccessPoint>* access_points);
void main(uint8_t scan_mode);
void setHtmlFromSerial();

View File

@@ -8,38 +8,54 @@ char nmeaBuffer[100];
MicroNMEA nmea(nmeaBuffer, sizeof(nmeaBuffer));
#ifndef GPS_SOFTWARE_SERIAL
HardwareSerial Serial2(GPS_SERIAL_INDEX);
#else
EspSoftwareSerial::UART Serial2;
#endif
HardwareSerial Serial2(GPS_SERIAL_INDEX);
void GpsInterface::begin() {
#ifndef GPS_SOFTWARE_SERIAL
Serial2.begin(9600, SERIAL_8N1, GPS_TX, GPS_RX);
#else
Serial2.begin(9600, SWSERIAL_8N1, GPS_TX, GPS_RX);
#endif
/*#ifdef MARAUDER_MINI
pinMode(26, OUTPUT);
delay(1);
analogWrite(26, 243);
delay(1);
Serial.println("Activated GPS");
delay(100);
#endif*/
Serial2.begin(9600, SERIAL_8N1, GPS_TX, GPS_RX);
MicroNMEA::sendSentence(Serial2, "$PSTMSETPAR,1201,0x00000042");
MicroNMEA::sendSentence(Serial2, "$PSTMSAVEPAR");
MicroNMEA::sendSentence(Serial2, "$PSTMSRR");
delay(4000);
delay(3900);
if (Serial2.available()) {
Serial.println("GPS Attached Successfully");
this->gps_enabled = true;
while (Serial2.available())
Serial2.read();
while (Serial2.available()) {
//Fetch the character one by one
char c = Serial2.read();
//Serial.print(c);
//Pass the character to the library
nmea.process(c);
}
}
else {
this->gps_enabled = false;
Serial.println("GPS Not Found");
}
this->type_flag=GPSTYPE_NATIVE; //enforce default
this->disable_queue(); //init the queue, disabled, kill NULLs
nmea.setUnknownSentenceHandler(gps_nmea_notimp);
}
//passthrough for other objects
@@ -102,8 +118,8 @@ void GpsInterface::enqueue(MicroNMEA& nmea){
if(this->text_in){
int size=text_in->size();
if(size){
#ifdef GPS_TEXT_MAXCOPIES
if(this->text_cycles>=GPS_TEXT_MAXCOPIES){
#ifdef GPS_TEXT_MAXCYCLES
if(this->text_cycles>=GPS_TEXT_MAXCYCLES){
#else
if(this->text_cycles){
#endif
@@ -158,8 +174,8 @@ void GpsInterface::enqueue(MicroNMEA& nmea){
#else
if(size>=5){
#endif
#ifdef GPS_TEXT_MAXCOPIES
if(this->text_cycles>=GPS_TEXT_MAXCOPIES){
#ifdef GPS_TEXT_MAXCYCLES
if(this->text_cycles>=GPS_TEXT_MAXCYCLES){
#else
if(this->text_cycles){
#endif
@@ -196,11 +212,25 @@ void GpsInterface::enqueue(MicroNMEA& nmea){
this->notparsed_nmea_sentence = nmea_sentence.c_str();
if(this->queue_enabled_flag){
if(!this->queue) this->new_queue();
if(enqueue){
String enqueue_me=nmea_sentence.c_str();
this->queue->add(enqueue_me);
nmea_sentence_t line = { unparsed, msg_id, nmea_sentence.c_str() };
if(this->queue){
#ifdef GPS_NMEA_MAXQUEUE
if(this->queue->size()>=GPS_NMEA_MAXQUEUE)
#else
if(this->queue->size()>=30)
#endif
this->flush_queue();
}
else
this->new_queue();
this->queue->add(line);
}
else
if(!this->queue)
this->new_queue();
}
else
this->flush_queue();
@@ -234,12 +264,12 @@ bool GpsInterface::queue_enabled(){
return this->queue_enabled_flag;
}
LinkedList<String>* GpsInterface::get_queue(){
LinkedList<nmea_sentence_t>* GpsInterface::get_queue(){
return this->queue;
}
void GpsInterface::new_queue(){
this->queue=new LinkedList<String>;
this->queue=new LinkedList<nmea_sentence_t>;
}
void GpsInterface::flush_queue(){
@@ -250,7 +280,7 @@ void GpsInterface::flush_queue(){
void GpsInterface::flush_queue_nmea(){
if(this->queue){
if(this->queue->size()){
LinkedList<String> *delme=this->queue;
LinkedList<nmea_sentence_t> *delme=this->queue;
this->new_queue();
delete delme;
}
@@ -500,6 +530,10 @@ String GpsInterface::getNumSatsString() {
return (String)num_sats;
}
int GpsInterface::getNumSats() {
return num_sats;
}
bool GpsInterface::getFixStatus() {
return this->good_fix;
}

View File

@@ -10,7 +10,27 @@
#include "configs.h"
//#define GPS_TEXT_MAXLINES 5 //default:5 lines in the buffer maximum
//#define GPS_TEXT_MAXCOPIES 1 //default:any nonzero number resets, i.e. one copy
//#define GPS_TEXT_MAXCYCLES 1 //default:1
//#define GPS_NMEA_SCRNLINES TEXT_HEIGHT //default: defined TEXT_HEIGHT from configs.h
//#define GPS_NMEA_SCRNWRAP true //default:true, except on MARAUDER_MINI where false
//#define GPS_NMEA_MAXQUEUE 30 //default:30 messages max in queue
#ifdef MARAUDER_MINI
#ifndef GPS_NMEA_SCRNWRAP
#define GPS_NMEA_SCRNWRAP false
#endif
#else
#ifndef GPS_NMEA_SCRNWRAP
#define GPS_NMEA_SCRNWRAP true
#endif
#endif
struct nmea_sentence_t {
bool unparsed;
String type;
String sentence;
};
void gps_nmea_notimp(MicroNMEA& nmea);
@@ -19,6 +39,7 @@ class GpsInterface {
void begin();
void main();
int getNumSats();
String getNumSatsString();
bool getFixStatus();
String getFixStatusAsString();
@@ -38,7 +59,7 @@ class GpsInterface {
void setType(String t);
void enqueue(MicroNMEA& nmea);
LinkedList<String>* get_queue();
LinkedList<nmea_sentence_t>* get_queue();
void flush_queue();
void flush_text();
void new_queue();
@@ -84,9 +105,9 @@ class GpsInterface {
type_t type_flag = GPSTYPE_NATIVE;
bool queue_enabled_flag=0;
LinkedList<String> *queue=NULL;
LinkedList<nmea_sentence_t> *queue=NULL;
int text_cycles=0;
unsigned int text_cycles=0;
LinkedList<String> *text_in=NULL;
LinkedList<String> *text=NULL;

File diff suppressed because it is too large Load Diff

View File

@@ -15,12 +15,12 @@
#include "settings.h"
#ifdef HAS_BUTTONS
#include <SwitchLib.h>
extern SwitchLib u_btn;
extern SwitchLib d_btn;
extern SwitchLib l_btn;
extern SwitchLib r_btn;
extern SwitchLib c_btn;
#include "Switches.h"
extern Switches u_btn;
extern Switches d_btn;
extern Switches l_btn;
extern Switches r_btn;
extern Switches c_btn;
#endif
extern WiFiScan wifi_scan_obj;
@@ -71,6 +71,7 @@ extern Settings settings_obj;
#define LANGUAGE 31
#define STATUS_GPS 32
#define GPS_MENU 33
#define DISABLE_TOUCH 34
PROGMEM void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);
PROGMEM bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data);
@@ -120,7 +121,8 @@ class MenuFunctions
uint32_t initTime = 0;
uint8_t menu_start_index = 0;
uint8_t mini_kb_index = 0;
uint8_t old_gps_sat_count = 0;
// Main menu stuff
Menu mainMenu;
@@ -139,15 +141,21 @@ class MenuFunctions
Menu specSettingMenu;
Menu infoMenu;
Menu languageMenu;
Menu sdDeleteMenu;
// WiFi menu stuff
Menu wifiSnifferMenu;
Menu wifiAttackMenu;
Menu wifiGeneralMenu;
Menu wifiAPMenu;
#ifndef HAS_ILI9341
Menu wifiStationMenu;
#endif
// WiFi General Menu
Menu htmlMenu;
Menu miniKbMenu;
Menu saveFileMenu;
// Bluetooth menu stuff
Menu bluetoothSnifferMenu;
@@ -163,7 +171,6 @@ class MenuFunctions
void addNodes(Menu* menu, String name, uint16_t color, Menu* child, int place, std::function<void()> callable, bool selected = false, String command = "");
void updateStatusBar();
void battery(bool initial = false);
void battery2(bool initial = false);
void showMenuList(Menu* menu, int layer);
@@ -172,6 +179,9 @@ class MenuFunctions
void displaySetting(String key, Menu* menu, int index);
void buttonSelected(uint8_t b, int8_t x = -1);
void buttonNotSelected(uint8_t b, int8_t x = -1);
#if (!defined(HAS_ILI9341) && defined(HAS_BUTTONS))
void miniKeyboard(Menu * targetMenu);
#endif
uint8_t updateTouch(uint16_t *x, uint16_t *y, uint16_t threshold = 600);
@@ -181,6 +191,12 @@ class MenuFunctions
Menu* current_menu;
Menu clearSSIDsMenu;
Menu clearAPsMenu;
// Save Files Menu
Menu saveSSIDsMenu;
Menu loadSSIDsMenu;
Menu saveAPsMenu;
Menu loadAPsMenu;
#ifdef HAS_GPS
// GPS Menu
@@ -192,11 +208,14 @@ class MenuFunctions
uint16_t x = -1, y = -1;
boolean pressed = false;
bool disable_touch;
String loaded_file = "";
void initLVGL();
void deinitLVGL();
void selectEPHTMLGFX();
void updateStatusBar();
void addSSIDGFX();
void addAPGFX();
void addStationGFX();

View File

@@ -32,9 +32,9 @@ bool SDInterface::initSD() {
- CS (jumper to SD Card GND Pin)
*/
enum { SPI_SCK = 0, SPI_MISO = 36, SPI_MOSI = 26 };
SPIClass SPI_EXT;
SPI_EXT.begin(SPI_SCK, SPI_MISO, SPI_MOSI, SD_CS);
if (!SD.begin(SD_CS, SPI_EXT)) {
this->spiExt = new SPIClass();
this->spiExt->begin(SPI_SCK, SPI_MISO, SPI_MOSI, SD_CS);
if (!SD.begin(SD_CS, *(this->spiExt))) {
#else
if (!SD.begin(SD_CS)) {
#endif
@@ -73,14 +73,16 @@ bool SDInterface::initSD() {
this->card_sz = sz;
}
buffer_obj = Buffer();
if (!SD.exists("/SCRIPTS")) {
Serial.println("/SCRIPTS does not exist. Creating...");
SD.mkdir("/SCRIPTS");
Serial.println("/SCRIPTS created");
}
this->sd_files = new LinkedList<String>();
this->sd_files->add("Back");
return true;
}
@@ -100,6 +102,13 @@ File SDInterface::getFile(String path) {
}
}
bool SDInterface::removeFile(String file_path) {
if (SD.remove(file_path))
return true;
else
return false;
}
void SDInterface::listDirToLinkedList(LinkedList<String>* file_names, String str_dir, String ext) {
if (this->supported) {
File dir = SD.open(str_dir);
@@ -110,12 +119,18 @@ void SDInterface::listDirToLinkedList(LinkedList<String>* file_names, String str
{
break;
}
if (entry.isDirectory())
continue;
String file_name = entry.name();
if (ext != "") {
String file_name = entry.name();
if (file_name.endsWith(ext)) {
file_names->add(file_name);
}
}
else
file_names->add(file_name);
}
}
}
@@ -142,33 +157,11 @@ void SDInterface::listDir(String str_dir){
}
}
void SDInterface::addPacket(uint8_t* buf, uint32_t len, bool log) {
if ((this->supported) && (this->do_save)) {
buffer_obj.addPacket(buf, len, log);
}
}
void SDInterface::openCapture(String file_name) {
bool save_pcap = settings_obj.loadSetting<bool>("SavePCAP");
if ((this->supported) && (save_pcap)) {
buffer_obj.createPcapFile(&SD, file_name);
buffer_obj.open();
}
}
void SDInterface::openLog(String file_name) {
bool save_pcap = settings_obj.loadSetting<bool>("SavePCAP");
if ((this->supported) && (save_pcap)) {
buffer_obj.createPcapFile(&SD, file_name, true);
buffer_obj.open(true);
}
}
void SDInterface::runUpdate() {
#ifdef HAS_SCREEN
display_obj.tft.setTextWrap(false);
display_obj.tft.setFreeFont(NULL);
display_obj.tft.setCursor(0, 100);
display_obj.tft.setCursor(0, TFT_HEIGHT / 3);
display_obj.tft.setTextSize(1);
display_obj.tft.setTextColor(TFT_WHITE);
@@ -300,11 +293,7 @@ bool SDInterface::checkDetectPin() {
}
void SDInterface::main() {
if ((this->supported) && (this->do_save)) {
//Serial.println("Saving packet...");
buffer_obj.forceSave(&SD);
}
else if (!this->supported) {
if (!this->supported) {
if (checkDetectPin()) {
delay(100);
this->initSD();

View File

@@ -26,6 +26,9 @@ extern Settings settings_obj;
class SDInterface {
private:
#if defined(MARAUDER_M5STICKC)
SPIClass *spiExt;
#endif
bool checkDetectPin();
public:
@@ -35,22 +38,20 @@ class SDInterface {
uint64_t cardSizeMB;
//uint64_t cardSizeGB;
bool supported = false;
bool do_save = true;
String card_sz;
bool initSD();
LinkedList<String>* sd_files;
void listDir(String str_dir);
void listDirToLinkedList(LinkedList<String>* file_names, String str_dir = "/", String ext = "");
File getFile(String path);
void addPacket(uint8_t* buf, uint32_t len, bool log = false);
void openCapture(String file_name = "");
void openLog(String file_name = "");
void runUpdate();
void performUpdate(Stream &updateSource, size_t updateSize);
void main();
//void savePacket(uint8_t* buf, uint32_t len);
bool removeFile(String file_path);
};
#endif

View File

@@ -0,0 +1,98 @@
#include "Switches.h"
Switches::Switches() {
this->pin = 0;
this->pin = false;
this->pressed = false;
this->hold_lim = 2000;
this->cur_hold = 0;
this->isheld = false;
pinMode(this->pin, INPUT);
return;
}
Switches::Switches(int pin, uint32_t hold_lim, bool pullup) {
this->pin = pin;
this->pullup = pullup;
this->pressed = false;
this->hold_lim = hold_lim;
this->cur_hold = 0;
this->isheld = false;
if (pullup)
pinMode(this->pin, INPUT_PULLUP);
else
pinMode(this->pin, INPUT_PULLDOWN);
return;
}
int Switches::getPin() {
return this->pin;
}
bool Switches::getPullup() {
return this->pullup;
}
bool Switches::isHeld() {
return this->isheld;
}
bool Switches::getButtonState() {
int buttonState = digitalRead(this->pin);
if ((this->pullup) && (buttonState == LOW))
return true;
else if ((!this->pullup) && (buttonState == HIGH))
return true;
else
return false;
}
bool Switches::justPressed() {
bool btn_state = this->getButtonState();
// Button was JUST pressed
if (btn_state && !this->pressed) {
this->hold_init = millis();
this->pressed = btn_state;
return true;
}
else if (btn_state) { // Button is STILL pressed
// Check if button is held
//Serial.println("cur_hold: " + (String)this->cur_hold);
if ((millis() - this->hold_init) < this->hold_lim) {
this->isheld = false;
}
else {
this->isheld = true;
}
this->pressed = btn_state;
return false;
}
else { // Button is not pressed
this->pressed = btn_state;
this->isheld = false;
return false;
}
}
bool Switches::justReleased() {
bool btn_state = this->getButtonState();
// Button was JUST released
if (!btn_state && this->pressed) {
this->isheld = false;
this->pressed = btn_state;
return true;
}
else { // Button is STILL released
this->pressed = btn_state;
return false;
}
}

35
esp32_marauder/Switches.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef Switches_h
#define Switches_h
#include <Arduino.h>
class Switches {
private:
int pin;
uint32_t hold_lim;
uint32_t cur_hold;
uint32_t hold_init = millis();
bool isheld;
bool pullup;
bool pressed;
bool getButtonState();
public:
Switches();
Switches(int pin, uint32_t hold_lim, bool pullup);
int getPin();
bool getPullup();
bool justPressed();
bool justReleased();
bool isHeld();
};
#endif

View File

@@ -242,7 +242,7 @@ extern "C" {
Serial.print(wardrive_line);
if (do_save)
evil_portal_obj.addLog(wardrive_line, wardrive_line.length());
buffer_obj.append(wardrive_line);
}
}
#endif
@@ -305,6 +305,14 @@ WiFiScan::WiFiScan()
{
}
String WiFiScan::macToString(const Station& station) {
char macStr[18]; // 6 pairs of hex digits + 5 colons + null terminator
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
station.mac[0], station.mac[1], station.mac[2],
station.mac[3], station.mac[4], station.mac[5]);
return String(macStr);
}
void WiFiScan::RunSetup() {
if (ieee80211_raw_frame_sanity_check(31337, 0, 0) == 1)
this->wsl_bypass_enabled = true;
@@ -930,15 +938,213 @@ String WiFiScan::freeRAM()
return String(s);
}
void WiFiScan::startPcap(String file_name) {
buffer_obj.pcapOpen(
file_name,
#if defined(HAS_SD)
sd_obj.supported ? &SD :
#endif
NULL,
save_serial // Set with commandline options
);
}
void WiFiScan::startLog(String file_name) {
buffer_obj.logOpen(
file_name,
#if defined(HAS_SD)
sd_obj.supported ? &SD :
#endif
NULL,
save_serial // Set with commandline options
);
}
void WiFiScan::parseBSSID(const char* bssidStr, uint8_t* bssid) {
sscanf(bssidStr, "%02X:%02X:%02X:%02X:%02X:%02X",
&bssid[0], &bssid[1], &bssid[2],
&bssid[3], &bssid[4], &bssid[5]);
}
void WiFiScan::RunLoadAPList() {
#ifdef HAS_SD
File file = sd_obj.getFile("/APs_0.log");
if (!file) {
Serial.println("Could not open /APs_0.log");
#ifdef HAS_SCREEN
display_obj.tft.setTextWrap(false);
display_obj.tft.setFreeFont(NULL);
display_obj.tft.setCursor(0, 100);
display_obj.tft.setTextSize(1);
display_obj.tft.setTextColor(TFT_CYAN);
display_obj.tft.println("Could not open /APs_0.log");
#endif
return;
}
DynamicJsonDocument doc(10048);
DeserializationError error = deserializeJson(doc, file);
if (error) {
Serial.print("JSON deserialize error: ");
Serial.println(error.c_str());
file.close();
#ifdef HAS_SCREEN
display_obj.tft.setTextWrap(false);
display_obj.tft.setFreeFont(NULL);
display_obj.tft.setCursor(0, 100);
display_obj.tft.setTextSize(1);
display_obj.tft.setTextColor(TFT_CYAN);
display_obj.tft.println("Could not deserialize JSON");
display_obj.tft.println(error.c_str());
#endif
return;
}
JsonArray array = doc.as<JsonArray>();
for (JsonObject obj : array) {
AccessPoint ap;
ap.essid = obj["essid"].as<String>();
ap.channel = obj["channel"];
ap.selected = false;
parseBSSID(obj["bssid"], ap.bssid);
ap.stations = new LinkedList<uint8_t>();
access_points->add(ap);
}
file.close();
//doc.clear();
#ifdef HAS_SCREEN
display_obj.tft.setTextWrap(false);
display_obj.tft.setFreeFont(NULL);
display_obj.tft.setCursor(0, 100);
display_obj.tft.setTextSize(1);
display_obj.tft.setTextColor(TFT_CYAN);
display_obj.tft.print("Loaded APs: ");
display_obj.tft.println((String)access_points->size());
#endif
Serial.print("Loaded APs:");
Serial.println((String)access_points->size());
#endif
}
void WiFiScan::RunSaveAPList(bool save_as) {
if (save_as) {
sd_obj.removeFile("/APs_0.log");
this->startLog("APs");
DynamicJsonDocument jsonDocument(2048);
JsonArray jsonArray = jsonDocument.to<JsonArray>();
for (int i = 0; i < access_points->size(); i++) {
const AccessPoint& ap = access_points->get(i);
JsonObject jsonAp = jsonArray.createNestedObject();
jsonAp["essid"] = ap.essid;
jsonAp["channel"] = ap.channel;
char bssidStr[18];
sprintf(bssidStr, "%02X:%02X:%02X:%02X:%02X:%02X",
ap.bssid[0], ap.bssid[1], ap.bssid[2],
ap.bssid[3], ap.bssid[4], ap.bssid[5]);
jsonAp["bssid"] = bssidStr;
}
String jsonString;
serializeJson(jsonArray, jsonString);
buffer_obj.append(jsonString);
#ifdef HAS_SCREEN
display_obj.tft.setTextWrap(false);
display_obj.tft.setFreeFont(NULL);
display_obj.tft.setCursor(0, 100);
display_obj.tft.setTextSize(1);
display_obj.tft.setTextColor(TFT_CYAN);
display_obj.tft.print("Saved APs: ");
display_obj.tft.println((String)access_points->size());
#endif
Serial.print("Saved APs:");
Serial.println((String)access_points->size());
}
}
void WiFiScan::RunLoadSSIDList() {
#ifdef HAS_SD
File log_file = sd_obj.getFile("/SSIDs_0.log");
if (!log_file) {
Serial.println("Could not open /SSIDs_0.log");
#ifdef HAS_SCREEN
display_obj.tft.setTextWrap(false);
display_obj.tft.setFreeFont(NULL);
display_obj.tft.setCursor(0, 100);
display_obj.tft.setTextSize(1);
display_obj.tft.setTextColor(TFT_CYAN);
display_obj.tft.println("Could not open /SSIDs_0.log");
#endif
return;
}
while (log_file.available()) {
String line = log_file.readStringUntil('\n'); // Read until newline character
this->addSSID(line);
}
#ifdef HAS_SCREEN
display_obj.tft.setTextWrap(false);
display_obj.tft.setFreeFont(NULL);
display_obj.tft.setCursor(0, 100);
display_obj.tft.setTextSize(1);
display_obj.tft.setTextColor(TFT_CYAN);
display_obj.tft.print("Loaded SSIDs: ");
display_obj.tft.println((String)ssids->size());
#endif
log_file.close();
Serial.print("Loaded SSIDs: ");
Serial.println((String)ssids->size());
#endif
}
void WiFiScan::RunSaveSSIDList(bool save_as) {
if (save_as) {
sd_obj.removeFile("/SSIDs_0.log");
this->startLog("SSIDs");
for (int i = 0; i < ssids->size(); i++) {
if (i < ssids->size() - 1)
buffer_obj.append(ssids->get(i).essid + "\n");
else
buffer_obj.append(ssids->get(i).essid);
}
#ifdef HAS_SCREEN
display_obj.tft.setTextWrap(false);
display_obj.tft.setFreeFont(NULL);
display_obj.tft.setCursor(0, 100);
display_obj.tft.setTextSize(1);
display_obj.tft.setTextColor(TFT_CYAN);
display_obj.tft.print("Saved SSIDs: ");
display_obj.tft.println((String)ssids->size());
#endif
Serial.print("Saved SSIDs: ");
Serial.println((String)ssids->size());
}
}
void WiFiScan::RunEvilPortal(uint8_t scan_mode, uint16_t color)
{
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.open();
#elif defined(HAS_SD)
sd_obj.openLog("evil_portal");
#else
return;
#endif
startLog("evil_portal");
#ifdef MARAUDER_FLIPPER
flipper_led.sniffLED();
@@ -981,13 +1187,7 @@ void WiFiScan::RunEvilPortal(uint8_t scan_mode, uint16_t color)
// Function to start running a beacon scan
void WiFiScan::RunAPScan(uint8_t scan_mode, uint16_t color)
{
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.open();
#elif defined(HAS_SD)
sd_obj.openCapture("ap");
#else
return;
#endif
startPcap("ap");
#ifdef MARAUDER_FLIPPER
flipper_led.sniffLED();
@@ -1048,7 +1248,9 @@ void WiFiScan::RunAPScan(uint8_t scan_mode, uint16_t color)
uint16_t calData[5] = { 213, 3469, 320, 3446, 1 }; // Landscape TFT DIY
Serial.println("Using TFT DIY");
#endif
display_obj.tft.setTouch(calData);
#ifdef HAS_ILI9341
display_obj.tft.setTouch(calData);
#endif
lv_obj_t * scr = lv_cont_create(NULL, NULL);
@@ -1204,9 +1406,12 @@ void WiFiScan::RunGPSInfo() {
void WiFiScan::RunGPSNmea() {
#ifdef HAS_GPS
LinkedList<String> *buffer=gps_obj.get_queue();
LinkedList<nmea_sentence_t> *buffer=gps_obj.get_queue();
bool queue_enabled=gps_obj.queue_enabled();
String gxgga = gps_obj.generateGXgga();
String gxrmc = gps_obj.generateGXrmc();
if(!buffer||!queue_enabled)
gps_obj.flush_queue();
#ifndef HAS_SCREEN
@@ -1214,29 +1419,54 @@ void WiFiScan::RunGPSNmea() {
gps_obj.flush_text();
#else
// Get screen position ready
display_obj.tft.setTextWrap(true);
int offset=100;
if((SCREEN_HEIGHT / 3)<offset)
offset=SCREEN_HEIGHT/3; //for smaller screens
if(offset<(TOP_FIXED_AREA+6))
offset=TOP_FIXED_AREA+6; //absolute minimium
display_obj.tft.setTextWrap(false);
display_obj.tft.setFreeFont(NULL);
display_obj.tft.setCursor(0, SCREEN_HEIGHT / 3);
display_obj.tft.setCursor(0, offset);
display_obj.tft.setTextSize(1);
display_obj.tft.setTextColor(TFT_CYAN);
display_obj.tft.setTextColor(TFT_GREEN);
// Clean up screen first
display_obj.tft.fillRect(0, (SCREEN_HEIGHT / 3) - 6, SCREEN_WIDTH, SCREEN_HEIGHT - ((SCREEN_HEIGHT / 3) - 6), TFT_BLACK);
display_obj.tft.fillRect(0, offset-6, SCREEN_WIDTH, SCREEN_HEIGHT - (offset-6), TFT_BLACK);
display_obj.tft.setCursor(0, SCREEN_HEIGHT / 3);
#ifdef GPS_NMEA_SCRNLINES
int lines=GPS_NMEA_SCRNLINES;
#else
int lines=TEXT_HEIGHT;
if(lines>((TFT_HEIGHT-offset-BOT_FIXED_AREA)/10))
lines=(TFT_HEIGHT-offset-BOT_FIXED_AREA)/10;
#endif
String text=gps_obj.getText();
if(queue_enabled){
if(gps_obj.getTextQueueSize()>0)
display_obj.tft.print(gps_obj.getTextQueue());
int queue=gps_obj.getTextQueueSize();
if(queue>0){
display_obj.tft.println(gps_obj.getTextQueue());
lines-=queue; //used lines for text display
}
else
if(text != "") display_obj.tft.print(text);
if(text != ""){
display_obj.tft.println(text);
lines--;
}
}
else
if(text != "") display_obj.tft.print(text);
if(text != ""){
display_obj.tft.println(text);
lines--;
}
//This one doesn't contain self-genned GxGGA or GxRMC, nor does it contain GxTXT, processed above
String display_nmea_sentence=gps_obj.getNmeaNotparsed();
#if GPS_NMEA_SCRNWRAP
lines-=((gxgga.length()-1)/STANDARD_FONT_CHAR_LIMIT) + 1;
lines-=((gxrmc.length()-1)/STANDARD_FONT_CHAR_LIMIT) + 1;
display_obj.tft.setTextWrap(GPS_NMEA_SCRNWRAP);
#else
lines-=2; //two self-genned messages
#endif
#endif
if(buffer && queue_enabled){
@@ -1244,15 +1474,35 @@ void WiFiScan::RunGPSNmea() {
if(size){
gps_obj.new_queue();
for(int i=0;i<size;i++){
Serial.println(buffer->get(i));
nmea_sentence_t line=buffer->get(i);
Serial.println(line.sentence);
#ifdef HAS_SCREEN
if(lines>0){
if(line.unparsed){
if(line.type != "" && line.type != "TXT" && line.type != "GGA" && line.type != "RMC"){
int length=line.sentence.length();
if(length){
#if GPS_NMEA_SCRNWRAP
if((((length-1)/STANDARD_FONT_CHAR_LIMIT) + 1)<=lines){
#endif
display_obj.tft.println(line.sentence);
#if GPS_NMEA_SCRNWRAP
lines-=((length-1)/STANDARD_FONT_CHAR_LIMIT) + 1;
#else
lines--;
#endif
#if GPS_NMEA_SCRNWRAP
}
#endif
}
}
}
}
#endif
}
delete buffer;
}
#ifdef HAS_SCREEN
//This matches the else block, but could later display more of the queue...
display_obj.tft.print(display_nmea_sentence);
#endif
} else {
static String old_nmea_sentence="";
String nmea_sentence=gps_obj.getNmeaNotimp();
@@ -1263,17 +1513,24 @@ void WiFiScan::RunGPSNmea() {
}
#ifdef HAS_SCREEN
display_obj.tft.print(display_nmea_sentence);
if(lines>0){
String display_nmea_sentence=gps_obj.getNmeaNotparsed();
int length=display_nmea_sentence.length();
if(length)
#if GPS_NMEA_SCRNWRAP
if((((length-1)/STANDARD_FONT_CHAR_LIMIT) + 1)<=lines)
#endif
display_obj.tft.println(display_nmea_sentence);
}
#endif
}
String gxgga = gps_obj.generateGXgga();
String gxrmc = gps_obj.generateGXrmc();
#ifdef HAS_SCREEN
display_obj.tft.print(gxgga);
display_obj.tft.print(gxrmc);
display_obj.tft.setTextWrap(false);
display_obj.tft.println(gxgga);
display_obj.tft.println(gxrmc);
#if GPS_NMEA_SCRNWRAP
display_obj.tft.setTextWrap(false);
#endif
#endif
gps_obj.sendSentence(Serial, gxgga.c_str());
@@ -1319,11 +1576,7 @@ void WiFiScan::RunInfo()
display_obj.tft.println(text_table4[27] + free_ram);
#endif
#ifdef WRITE_PACKETS_SERIAL
#ifdef HAS_SCREEN
display_obj.tft.println(text_table4[48]);
#endif
#elif defined(HAS_SD)
#if defined(HAS_SD)
if (sd_obj.supported) {
#ifdef HAS_SCREEN
display_obj.tft.println(text_table4[28]);
@@ -1337,8 +1590,6 @@ void WiFiScan::RunInfo()
display_obj.tft.println(text_table4[31]);
#endif
}
#else
return;
#endif
#ifdef HAS_BATTERY
@@ -1373,13 +1624,7 @@ void WiFiScan::RunPacketMonitor(uint8_t scan_mode, uint16_t color)
led_obj.setMode(MODE_SNIFF);
#endif
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.open();
#elif defined(HAS_SD)
sd_obj.openCapture("packet_monitor");
#else
return;
#endif
startPcap("packet_monitor");
#ifdef HAS_ILI9341
@@ -1467,11 +1712,7 @@ void WiFiScan::RunEapolScan(uint8_t scan_mode, uint16_t color)
display_obj.tft.fillScreen(TFT_BLACK);
#endif
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.open();
#elif defined(HAS_SD)
sd_obj.openCapture("eapol");
#endif
startPcap("eapol");
#ifdef HAS_SCREEN
#ifdef TFT_SHIELD
@@ -1496,13 +1737,7 @@ void WiFiScan::RunEapolScan(uint8_t scan_mode, uint16_t color)
display_obj.tftDrawExitScaleButtons();
#endif
#else
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.open();
#elif defined(HAS_SD)
sd_obj.openCapture("eapol");
#else
return;
#endif
startPcap("eapol");
#ifdef HAS_SCREEN
display_obj.TOP_FIXED_AREA_2 = 48;
@@ -1597,13 +1832,7 @@ void WiFiScan::RunMimicFlood(uint8_t scan_mode, uint16_t color) {
void WiFiScan::RunPwnScan(uint8_t scan_mode, uint16_t color)
{
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.open();
#elif defined(HAS_SD)
sd_obj.openCapture("pwnagotchi");
#else
return;
#endif
startPcap("pwnagotchi");
#ifdef MARAUDER_FLIPPER
flipper_led.sniffLED();
@@ -1766,7 +1995,9 @@ void WiFiScan::executeWarDrive() {
String wardrive_line = WiFi.BSSIDstr(i) + "," + ssid + "," + this->security_int_to_string(WiFi.encryptionType(i)) + "," + gps_obj.getDatetime() + "," + (String)WiFi.channel(i) + "," + (String)WiFi.RSSI(i) + "," + gps_obj.getLat() + "," + gps_obj.getLon() + "," + gps_obj.getAlt() + "," + gps_obj.getAccuracy() + ",WIFI\n";
Serial.print((String)this->mac_history_cursor + " | " + wardrive_line);
evil_portal_obj.addLog(wardrive_line, wardrive_line.length());
if (do_save) {
buffer_obj.append(wardrive_line);
}
}
}
this->channelHop();
@@ -1780,23 +2011,21 @@ void WiFiScan::executeWarDrive() {
// Function to start running a beacon scan
void WiFiScan::RunBeaconScan(uint8_t scan_mode, uint16_t color)
{
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.open();
#elif defined(HAS_SD)
if (scan_mode == WIFI_SCAN_AP)
sd_obj.openCapture("beacon");
else if (scan_mode == WIFI_SCAN_WAR_DRIVE) {
#ifdef HAS_GPS
if (gps_obj.getGpsModuleStatus()) {
sd_obj.openLog("wardrive");
String header_line = "WigleWifi-1.4,appRelease=" + (String)MARAUDER_VERSION + ",model=ESP32 Marauder,release=" + (String)MARAUDER_VERSION + ",device=ESP32 Marauder,display=SPI TFT,board=ESP32 Marauder,brand=JustCallMeKoko\nMAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n";
evil_portal_obj.addLog(header_line, header_line.length());
}
#endif
}
#else
return;
#endif
if (scan_mode == WIFI_SCAN_AP)
startPcap("beacon");
else if (scan_mode == WIFI_SCAN_WAR_DRIVE) {
#ifdef HAS_GPS
if (gps_obj.getGpsModuleStatus()) {
startLog("wardrive");
String header_line = "WigleWifi-1.4,appRelease=" + (String)MARAUDER_VERSION + ",model=ESP32 Marauder,release=" + (String)MARAUDER_VERSION + ",device=ESP32 Marauder,display=SPI TFT,board=ESP32 Marauder,brand=JustCallMeKoko\nMAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n";
buffer_obj.append(header_line);
} else {
return;
}
#else
return;
#endif
}
#ifdef MARAUDER_FLIPPER
flipper_led.sniffLED();
@@ -1855,13 +2084,7 @@ void WiFiScan::startWardriverWiFi() {
void WiFiScan::RunStationScan(uint8_t scan_mode, uint16_t color)
{
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.open();
#elif defined(HAS_SD)
sd_obj.openCapture("station");
#else
return;
#endif
startPcap("station");
#ifdef MARAUDER_FLIPPER
flipper_led.sniffLED();
@@ -1904,14 +2127,8 @@ void WiFiScan::RunStationScan(uint8_t scan_mode, uint16_t color)
void WiFiScan::RunRawScan(uint8_t scan_mode, uint16_t color)
{
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.open();
#elif defined(HAS_SD)
if (scan_mode != WIFI_SCAN_SIG_STREN)
sd_obj.openCapture("raw");
#else
return;
#endif
if (scan_mode != WIFI_SCAN_SIG_STREN)
startPcap("raw");
#ifdef MARAUDER_FLIPPER
flipper_led.sniffLED();
@@ -1957,13 +2174,7 @@ void WiFiScan::RunRawScan(uint8_t scan_mode, uint16_t color)
void WiFiScan::RunDeauthScan(uint8_t scan_mode, uint16_t color)
{
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.open();
#elif defined(HAS_SD)
sd_obj.openCapture("deauth");
#else
return;
#endif
startPcap("deauth");
#ifdef MARAUDER_FLIPPER
flipper_led.sniffLED();
@@ -2008,23 +2219,21 @@ void WiFiScan::RunDeauthScan(uint8_t scan_mode, uint16_t color)
// Function for running probe request scan
void WiFiScan::RunProbeScan(uint8_t scan_mode, uint16_t color)
{
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.open();
#elif defined(HAS_SD)
if (scan_mode == WIFI_SCAN_PROBE)
sd_obj.openCapture("probe");
else if (scan_mode == WIFI_SCAN_STATION_WAR_DRIVE) {
#ifdef HAS_GPS
if (gps_obj.getGpsModuleStatus()) {
sd_obj.openLog("station_wardrive");
String header_line = "WigleWifi-1.4,appRelease=" + (String)MARAUDER_VERSION + ",model=ESP32 Marauder,release=" + (String)MARAUDER_VERSION + ",device=ESP32 Marauder,display=SPI TFT,board=ESP32 Marauder,brand=JustCallMeKoko\nMAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n";
evil_portal_obj.addLog(header_line, header_line.length());
}
#endif
}
#else
return;
#endif
if (scan_mode == WIFI_SCAN_PROBE)
startPcap("probe");
else if (scan_mode == WIFI_SCAN_STATION_WAR_DRIVE) {
#ifdef HAS_GPS
if (gps_obj.getGpsModuleStatus()) {
startLog("station_wardrive");
String header_line = "WigleWifi-1.4,appRelease=" + (String)MARAUDER_VERSION + ",model=ESP32 Marauder,release=" + (String)MARAUDER_VERSION + ",device=ESP32 Marauder,display=SPI TFT,board=ESP32 Marauder,brand=JustCallMeKoko\nMAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n";
buffer_obj.append(header_line);
} else {
return;
}
#else
return;
#endif
}
#ifdef MARAUDER_FLIPPER
flipper_led.sniffLED();
@@ -2155,25 +2364,19 @@ void WiFiScan::RunBluetoothScan(uint8_t scan_mode, uint16_t color)
pBLEScan->setAdvertisedDeviceCallbacks(new bluetoothScanAllCallback(), false);
}
else if ((scan_mode == BT_SCAN_WAR_DRIVE) || (scan_mode == BT_SCAN_WAR_DRIVE_CONT)) {
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.open();
#elif defined(HAS_SD)
#ifdef HAS_GPS
if (gps_obj.getGpsModuleStatus()) {
if (scan_mode == BT_SCAN_WAR_DRIVE) {
#ifdef HAS_SD
sd_obj.openLog("bt_wardrive");
#endif
}
else if (scan_mode == BT_SCAN_WAR_DRIVE_CONT) {
#ifdef HAS_SD
sd_obj.openLog("bt_wardrive_cont");
#endif
}
String header_line = "WigleWifi-1.4,appRelease=" + (String)MARAUDER_VERSION + ",model=ESP32 Marauder,release=" + (String)MARAUDER_VERSION + ",device=ESP32 Marauder,display=SPI TFT,board=ESP32 Marauder,brand=JustCallMeKoko\nMAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n";
evil_portal_obj.addLog(header_line, header_line.length());
#ifdef HAS_GPS
if (gps_obj.getGpsModuleStatus()) {
if (scan_mode == BT_SCAN_WAR_DRIVE) {
startLog("bt_wardrive");
}
#endif
else if (scan_mode == BT_SCAN_WAR_DRIVE_CONT) {
startLog("bt_wardrive_cont");
}
String header_line = "WigleWifi-1.4,appRelease=" + (String)MARAUDER_VERSION + ",model=ESP32 Marauder,release=" + (String)MARAUDER_VERSION + ",device=ESP32 Marauder,display=SPI TFT,board=ESP32 Marauder,brand=JustCallMeKoko\nMAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n";
buffer_obj.append(header_line);
} else {
return;
}
#else
return;
#endif
@@ -2338,7 +2541,7 @@ void WiFiScan::pwnSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
Serial.println();
addPacket(snifferPacket, len);
buffer_obj.append(snifferPacket, len);
}
}
}
@@ -2406,16 +2609,25 @@ void WiFiScan::apSnifferCallbackFull(void* buf, wifi_promiscuous_pkt_type_t type
Serial.print(snifferPacket->rx_ctrl.channel);
Serial.print(" BSSID: ");
Serial.print(addr);
display_string.concat(addr);
//display_string.concat(addr);
//Serial.print(" ESSID: ");
//display_string.concat(" -> ");
//for (int i = 0; i < snifferPacket->payload[37]; i++)
//{
// Serial.print((char)snifferPacket->payload[i + 38]);
// display_string.concat((char)snifferPacket->payload[i + 38]);
// essid.concat((char)snifferPacket->payload[i + 38]);
//}
Serial.print(" ESSID: ");
display_string.concat(" -> ");
for (int i = 0; i < snifferPacket->payload[37]; i++)
{
Serial.print((char)snifferPacket->payload[i + 38]);
display_string.concat((char)snifferPacket->payload[i + 38]);
essid.concat((char)snifferPacket->payload[i + 38]);
if (snifferPacket->payload[37] <= 0)
display_string.concat(addr);
else {
for (int i = 0; i < snifferPacket->payload[37]; i++)
{
Serial.print((char)snifferPacket->payload[i + 38]);
display_string.concat((char)snifferPacket->payload[i + 38]);
essid.concat((char)snifferPacket->payload[i + 38]);
}
}
bssid.concat(addr);
@@ -2497,7 +2709,7 @@ void WiFiScan::apSnifferCallbackFull(void* buf, wifi_promiscuous_pkt_type_t type
Serial.println();
addPacket(snifferPacket, len);
buffer_obj.append(snifferPacket, len);
}
}
}
@@ -2623,7 +2835,7 @@ void WiFiScan::apSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
Serial.println();
addPacket(snifferPacket, len);
buffer_obj.append(snifferPacket, len);
}
}
}
@@ -2715,13 +2927,15 @@ void WiFiScan::beaconSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type
char addr[] = "00:00:00:00:00:00";
getMAC(addr, snifferPacket->payload, 10);
Serial.print(addr);
display_string.concat(addr);
Serial.print(" ESSID: ");
display_string.concat(" -> ");
for (int i = 0; i < snifferPacket->payload[37]; i++)
{
Serial.print((char)snifferPacket->payload[i + 38]);
display_string.concat((char)snifferPacket->payload[i + 38]);
if (snifferPacket->payload[37] <= 0)
display_string.concat(addr);
else {
for (int i = 0; i < snifferPacket->payload[37]; i++)
{
Serial.print((char)snifferPacket->payload[i + 38]);
display_string.concat((char)snifferPacket->payload[i + 38]);
}
}
int temp_len = display_string.length();
@@ -2744,7 +2958,7 @@ void WiFiScan::beaconSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type
Serial.println();
addPacket(snifferPacket, len);
buffer_obj.append(snifferPacket, len);
}
else if (wifi_scan_obj.currentScanMode == WIFI_SCAN_WAR_DRIVE) {
#ifdef HAS_GPS
@@ -2819,7 +3033,7 @@ void WiFiScan::beaconSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type
}
String wardrive_line = (String)addr + "," + essid + "," + wifi_scan_obj.security_int_to_string(snifferPacket->rx_ctrl.channel) + "," + gps_obj.getDatetime() + "," + (String)snifferPacket->rx_ctrl.channel + "," + (String)snifferPacket->rx_ctrl.rssi + "," + gps_obj.getLat() + "," + gps_obj.getLon() + "," + gps_obj.getAlt() + "," + gps_obj.getAccuracy() + ",WIFI";
Serial.println(wardrive_line);
//evil_portal_obj.addLog(wardrive_line, wardrive_line.length());
//buffer_obj.append(wardrive_line);
}
}
#endif
@@ -2983,7 +3197,7 @@ void WiFiScan::stationSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t typ
access_points->set(ap_index, ap);
addPacket(snifferPacket, len);
buffer_obj.append(snifferPacket, len);
}
void WiFiScan::rawSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
@@ -3084,7 +3298,7 @@ void WiFiScan::rawSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
Serial.println();
addPacket(snifferPacket, len);
buffer_obj.append(snifferPacket, len);
}
void WiFiScan::deauthSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
@@ -3148,7 +3362,7 @@ void WiFiScan::deauthSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type
Serial.println();
addPacket(snifferPacket, len);
buffer_obj.append(snifferPacket, len);
}
}
}
@@ -3220,7 +3434,7 @@ void WiFiScan::probeSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
Serial.println();
addPacket(snifferPacket, len);
buffer_obj.append(snifferPacket, len);
}
else if (wifi_scan_obj.currentScanMode == WIFI_SCAN_STATION_WAR_DRIVE) {
#ifdef HAS_GPS
@@ -3275,7 +3489,7 @@ void WiFiScan::probeSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
if (do_save) {
String wardrive_line = (String)addr + "," + (String)addr + ",," + gps_obj.getDatetime() + "," + (String)snifferPacket->rx_ctrl.channel + "," + (String)snifferPacket->rx_ctrl.rssi + "," + gps_obj.getLat() + "," + gps_obj.getLon() + "," + gps_obj.getAlt() + "," + gps_obj.getAccuracy() + ",WIFI";
Serial.println(wardrive_line);
evil_portal_obj.addLog(wardrive_line, wardrive_line.length());
buffer_obj.append(wardrive_line);
}
}
#endif
@@ -3366,7 +3580,7 @@ void WiFiScan::beaconListSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t
Serial.println();
addPacket(snifferPacket, len);
buffer_obj.append(snifferPacket, len);
}
}
}
@@ -3827,7 +4041,7 @@ void WiFiScan::wifiSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
#endif
#endif
addPacket(snifferPacket, len);
buffer_obj.append(snifferPacket, len);
}
}
@@ -3915,7 +4129,7 @@ void WiFiScan::eapolSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
#endif
}
addPacket(snifferPacket, len);
buffer_obj.append(snifferPacket, len);
}
void WiFiScan::activeEapolSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type)
@@ -4001,20 +4215,7 @@ void WiFiScan::activeEapolSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t
}
addPacket(snifferPacket, len);
}
void WiFiScan::addPacket(wifi_promiscuous_pkt_t *snifferPacket, int len) {
bool save_packet = settings_obj.loadSetting<bool>(text_table4[7]);
if (save_packet) {
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.addPacket(snifferPacket->payload, len);
#elif defined(HAS_SD)
sd_obj.addPacket(snifferPacket->payload, len);
#else
return;
#endif
}
buffer_obj.append(snifferPacket, len);
}
#ifdef HAS_SCREEN
@@ -4037,7 +4238,9 @@ void WiFiScan::addPacket(wifi_promiscuous_pkt_t *snifferPacket, int len) {
uint16_t t_x = 0, t_y = 0; // To store the touch coordinates
// Do the touch stuff
pressed = display_obj.tft.getTouch(&t_x, &t_y);
#ifdef HAS_ILI9341
pressed = display_obj.tft.getTouch(&t_x, &t_y);
#endif
if (pressed) {
Serial.print("Got touch | X: ");
@@ -4188,7 +4391,9 @@ void WiFiScan::addPacket(wifi_promiscuous_pkt_t *snifferPacket, int len) {
uint16_t t_x = 0, t_y = 0; // To store the touch coordinates
// Do the touch stuff
pressed = display_obj.tft.getTouch(&t_x, &t_y);
#ifdef HAS_ILI9341
pressed = display_obj.tft.getTouch(&t_x, &t_y);
#endif
if (pressed) {
Serial.print("Got touch | X: ");
@@ -4770,4 +4975,9 @@ void WiFiScan::main(uint32_t currentTime)
packets_sent = 0;
}
}
#ifdef HAS_GPS
else if ((currentScanMode == WIFI_SCAN_OFF))
if(gps_obj.queue_enabled())
gps_obj.disable_queue();
#endif
}

View File

@@ -196,7 +196,6 @@ class WiFiScan
//String connected_network = "";
//const String alfa = "1234567890qwertyuiopasdfghjkklzxcvbnm QWERTYUIOPASDFGHJKLZXCVBNM_";
const String alfa = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789-=[];',./`\\_+{}:\"<>?~|!@#$%^&*()";
const char* rick_roll[8] = {
"01 Never gonna give you up",
@@ -332,6 +331,7 @@ class WiFiScan
void RunLvJoinWiFi(uint8_t scan_mode, uint16_t color);
void RunEvilPortal(uint8_t scan_mode, uint16_t color);
bool checkMem();
void parseBSSID(const char* bssidStr, uint8_t* bssid);
public:
@@ -356,6 +356,9 @@ class WiFiScan
String dst_mac = "ff:ff:ff:ff:ff:ff";
byte src_mac[6] = {};
String current_mini_kb_ssid = "";
const String alfa = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789-=[];',./`\\_+{}:\"<>?~|!@#$%^&*()";
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
wifi_config_t ap_config;
@@ -383,14 +386,22 @@ class WiFiScan
void RunClearSSIDs();
void RunClearAPs();
void RunClearStations();
void RunSaveSSIDList(bool save_as = true);
void RunLoadSSIDList();
void RunSaveAPList(bool save_as = true);
void RunLoadAPList();
void channelHop();
uint8_t currentScanMode = 0;
void main(uint32_t currentTime);
void StartScan(uint8_t scan_mode, uint16_t color = 0);
void StopScan(uint8_t scan_mode);
const char* generateRandomName();
//void addLog(String log, int len);
bool save_serial = false;
void startPcap(String file_name);
void startLog(String file_name);
String macToString(const Station& station);
static void getMAC(char *addr, uint8_t* data, uint16_t offset);
static void pwnSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
static void beaconSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
@@ -404,7 +415,6 @@ class WiFiScan
static void activeEapolSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
static void eapolSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
static void wifiSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type);
static void addPacket(wifi_promiscuous_pkt_t *snifferPacket, int len);
/*#ifdef HAS_BT
enum EBLEPayloadType

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -6,15 +6,11 @@
#define POLISH_POTATO
//Indicates that it must redirect the stream with the captured packets to serial (1)
//If not defined, will write packages to SD card if supported
//#define WRITE_PACKETS_SERIAL
//// BOARD TARGETS
//#define MARAUDER_M5STICKC
//#define MARAUDER_MINI
//#define MARAUDER_V4
#define MARAUDER_V6
//#define MARAUDER_V6
//#define MARAUDER_V6_1
//#define MARAUDER_KIT
//#define GENERIC_ESP32
@@ -22,15 +18,18 @@
//#define ESP32_LDDB
//#define MARAUDER_DEV_BOARD_PRO
//#define XIAO_ESP32_S3
//#define MARAUDER_REV_FEATHER
//// END BOARD TARGETS
#define MARAUDER_VERSION "v0.13.6"
#define MARAUDER_VERSION "v1.0.0"
//// HARDWARE NAMES
#ifdef MARAUDER_M5STICKC
#define HARDWARE_NAME "M5Stick-C Plus"
#elif defined(MARAUDER_MINI)
#define HARDWARE_NAME "Marauder Mini"
#elif defined(MARAUDER_REV_FEATHER)
#define HARDWARE_NAME "Adafruit Feather ESP32-S2 Reverse TFT"
#elif defined(MARAUDER_V4)
#define HARDWARE_NAME "Marauder v4"
#elif defined(MARAUDER_V6)
@@ -70,7 +69,7 @@
#ifdef MARAUDER_MINI
//#define FLIPPER_ZERO_HAT
#define HAS_BATTERY
//#define HAS_BATTERY
#define HAS_BT
#define HAS_BUTTONS
#define HAS_NEOPIXEL_LED
@@ -82,6 +81,20 @@
#define HAS_GPS
#endif
#ifdef MARAUDER_REV_FEATHER
//#define FLIPPER_ZERO_HAT
//#define HAS_BATTERY
//#define HAS_BT
#define HAS_BUTTONS
#define HAS_NEOPIXEL_LED
//#define HAS_PWR_MGMT
#define HAS_SCREEN
#define HAS_SD
#define USE_SD
#define HAS_TEMP_SENSOR
#define HAS_GPS
#endif
#ifdef MARAUDER_V4
//#define FLIPPER_ZERO_HAT
#define HAS_BATTERY
@@ -147,10 +160,8 @@
//#define HAS_PWR_MGMT
//#define HAS_SCREEN
#define HAS_GPS
#ifndef WRITE_PACKETS_SERIAL
#define HAS_SD
#define USE_SD
#endif
#define HAS_SD
#define USE_SD
//#define HAS_TEMP_SENSOR
#endif
@@ -196,20 +207,6 @@
#endif
//// END BOARD FEATURES
//// FLIPPER ZERO HAT SETTINGS
#ifdef FLIPPER_ZERO_HAT
//#ifdef MARAUDER_FLIPPER
// #define USE_FLIPPER_SD
//#endif
#ifdef XIAO_ESP32_S3
#define USE_FLIPPER_SD
#endif
#endif
//// END FLIPPER ZERO HAT SETTINGS
//// POWER MANAGEMENT
#ifdef HAS_PWR_MGMT
#ifdef MARAUDER_M5STICKC
@@ -221,12 +218,44 @@
//// BUTTON DEFINITIONS
#ifdef HAS_BUTTONS
#ifdef MARAUDER_REV_FEATHER
#define L_BTN -1
#define C_BTN 1
#define U_BTN 0
#define R_BTN -1
#define D_BTN 2
//#define HAS_L
//#define HAS_R
#define HAS_U
#define HAS_D
#define HAS_C
#define L_PULL true
#define C_PULL false
#define U_PULL true
#define R_PULL true
#define D_PULL false
#endif
#ifdef MARAUDER_MINI
#define L_BTN 13
#define C_BTN 34
#define U_BTN 36
#define R_BTN 39
#define D_BTN 35
#define HAS_L
#define HAS_R
#define HAS_U
#define HAS_D
#define HAS_C
#define L_PULL true
#define C_PULL true
#define U_PULL true
#define R_PULL true
#define D_PULL true
#endif
#ifdef MARAUDER_M5STICKC
@@ -235,6 +264,18 @@
#define U_BTN -1
#define R_BTN -1
#define D_BTN 39
//#define HAS_L
//#define HAS_R
//#define HAS_U
#define HAS_D
#define HAS_C
#define L_PULL true
#define C_PULL true
#define U_PULL true
#define R_PULL true
#define D_PULL true
#endif
#ifdef MARAUDER_V6
@@ -243,6 +284,18 @@
#define U_BTN -1
#define R_BTN -1
#define D_BTN -1
//#define HAS_L
//#define HAS_R
//#define HAS_U
//#define HAS_D
#define HAS_C
#define L_PULL true
#define C_PULL true
#define U_PULL true
#define R_PULL true
#define D_PULL true
#endif
#ifdef MARAUDER_V6_1
@@ -251,6 +304,18 @@
#define U_BTN -1
#define R_BTN -1
#define D_BTN -1
//#define HAS_L
//#define HAS_R
//#define HAS_U
//#define HAS_D
#define HAS_C
#define L_PULL true
#define C_PULL true
#define U_PULL true
#define R_PULL true
#define D_PULL true
#endif
#endif
@@ -302,7 +367,7 @@
//#define MENU_FONT &FreeSans9pt7b
//#define MENU_FONT &FreeSansBold9pt7b
#define BUTTON_SCREEN_LIMIT 6
#define BUTTON_ARRAY_LEN 13
#define BUTTON_ARRAY_LEN 100
#define STATUS_BAR_WIDTH (TFT_HEIGHT/16)
#define LVGL_TICK_PERIOD 6
@@ -544,7 +609,74 @@
//#define MENU_FONT &FreeSans9pt7b
//#define MENU_FONT &FreeSansBold9pt7b
#define BUTTON_SCREEN_LIMIT 10
#define BUTTON_ARRAY_LEN 13
#define BUTTON_ARRAY_LEN 100
#define STATUS_BAR_WIDTH (TFT_HEIGHT/16)
#define LVGL_TICK_PERIOD 6
#define FRAME_X 100
#define FRAME_Y 64
#define FRAME_W 120
#define FRAME_H 50
// Red zone size
#define REDBUTTON_X FRAME_X
#define REDBUTTON_Y FRAME_Y
#define REDBUTTON_W (FRAME_W/2)
#define REDBUTTON_H FRAME_H
// Green zone size
#define GREENBUTTON_X (REDBUTTON_X + REDBUTTON_W)
#define GREENBUTTON_Y FRAME_Y
#define GREENBUTTON_W (FRAME_W/2)
#define GREENBUTTON_H FRAME_H
#define STATUSBAR_COLOR 0x4A49
#endif
#ifdef MARAUDER_REV_FEATHER
#define SCREEN_CHAR_WIDTH 40
//#define TFT_MISO 37
//#define TFT_MOSI 35
//#define TFT_SCLK 36
#define TFT_CS 42
#define TFT_DC 40
#define TFT_RST 41
#define TFT_BL 45
//#define TOUCH_CS 21
#define SD_CS 4
#define SCREEN_BUFFER
#define MAX_SCREEN_BUFFER 9
#define BANNER_TEXT_SIZE 1
#ifndef TFT_WIDTH
#define TFT_WIDTH 240
#endif
#ifndef TFT_HEIGHT
#define TFT_HEIGHT 135
#endif
#define CHAR_WIDTH 6
#define SCREEN_WIDTH TFT_WIDTH // Originally 240
#define SCREEN_HEIGHT TFT_HEIGHT // Originally 320
#define HEIGHT_1 TFT_WIDTH
#define WIDTH_1 TFT_WIDTH
#define STANDARD_FONT_CHAR_LIMIT (TFT_WIDTH/6) // number of characters on a single line with normal font
#define TEXT_HEIGHT (TFT_HEIGHT/10) // Height of text to be printed and scrolled
#define BOT_FIXED_AREA 0 // Number of lines in bottom fixed area (lines counted from bottom of screen)
#define TOP_FIXED_AREA 48 // Number of lines in top fixed area (lines counted from top of screen)
#define YMAX TFT_HEIGHT // Bottom of screen area
#define minimum(a,b) (((a) < (b)) ? (a) : (b))
//#define MENU_FONT NULL
#define MENU_FONT &FreeMono9pt7b // Winner
//#define MENU_FONT &FreeMonoBold9pt7b
//#define MENU_FONT &FreeSans9pt7b
//#define MENU_FONT &FreeSansBold9pt7b
#define BUTTON_SCREEN_LIMIT 5
#define BUTTON_ARRAY_LEN 100
#define STATUS_BAR_WIDTH (TFT_HEIGHT/16)
#define LVGL_TICK_PERIOD 6
@@ -647,6 +779,24 @@
#define BUTTON_PADDING 10
#endif
#ifdef MARAUDER_REV_FEATHER
#define BANNER_TIME 50
#define COMMAND_PREFIX "!"
// Keypad start position, key sizes and spacing
#define KEY_X (TFT_WIDTH/2) // Centre of key
#define KEY_Y (TFT_HEIGHT/4.5)
#define KEY_W TFT_WIDTH // Width and height
#define KEY_H (TFT_HEIGHT/12.8)
#define KEY_SPACING_X 0 // X and Y gap
#define KEY_SPACING_Y 1
#define KEY_TEXTSIZE 1 // Font size multiplier
#define ICON_W 22
#define ICON_H 22
#define BUTTON_PADDING 10
#endif
#ifdef MARAUDER_M5STICKC
#define BANNER_TIME 50
@@ -667,13 +817,7 @@
//// END MENU DEFINITIONS
//// SD DEFINITIONS
#ifdef FLIPPER_ZERO_HAT
#ifdef USE_FLIPPER_SD
#define WRITE_PACKETS_SERIAL
#endif
#elif defined(USE_SD)
#if defined(USE_SD)
#ifdef MARAUDER_V4
#define SD_CS 12
@@ -695,6 +839,10 @@
#define SD_CS 4
#endif
#ifdef MARAUDER_REV_FEATHER
#define SD_CS 5
#endif
#ifdef MARAUDER_M5STICKC
#define SD_CS -1
#endif
@@ -750,6 +898,8 @@
#define MEM_LOWER_LIM 20000
#elif defined(MARAUDER_MINI)
#define MEM_LOWER_LIM 20000
#elif defined(MARAUDER_REV_FEATHER)
#define MEM_LOWER_LIM 20000
#elif defined(MARAUDER_V4)
#define MEM_LOWER_LIM 20000
#elif defined(MARAUDER_V6) || defined(MARAUDER_V6_1)
@@ -776,6 +926,8 @@
#define PIN 17
#elif defined(MARAUDER_DEV_BOARD_PRO)
#define PIN 16
#elif defined(MARAUDER_REV_FEATHER)
#define PIN 33
#else
#define PIN 25
#endif
@@ -783,20 +935,13 @@
#endif
//// END NEOPIXEL STUFF
//// BOARD PIN OVERRIDES
#ifdef XIAO_ESP32_S3
#ifdef USE_FLIPPER_SD
#define XIAO_RX1 1
#define XIAO_TX1 2
#endif
#endif
//// END BOARD PIN OVERRIDES
//// EVIL PORTAL STUFF
#ifdef MARAUDER_M5STICKC
#define MAX_HTML_SIZE 11400
#elif defined(MARAUDER_MINI)
#define MAX_HTML_SIZE 11400
#elif defined(MARAUDER_REV_FEATHER)
#define MAX_HTML_SIZE 11400
#elif defined(MARAUDER_V4)
#define MAX_HTML_SIZE 11400
#elif defined(MARAUDER_V6) || defined(MARAUDER_V6_1)
@@ -846,11 +991,7 @@
#define GPS_RX 22
#define mac_history_len 512
#elif defined(MARAUDER_FLIPPER)
#ifdef WRITE_PACKETS_SERIAL
#define GPS_SOFTWARE_SERIAL
#else
#define GPS_SERIAL_INDEX 1
#endif
#define GPS_SERIAL_INDEX 1
#define GPS_TX 9
#define GPS_RX 21
#define mac_history_len 512
@@ -859,6 +1000,11 @@
#define GPS_TX 33
#define GPS_RX 32
#define mac_history_len 512
#elif defined(MARAUDER_REV_FEATHER)
#define GPS_SERIAL_INDEX 1
#define GPS_TX 6
#define GPS_RX 9
#define mac_history_len 512
#endif
#else
#define mac_history_len 512
@@ -874,6 +1020,8 @@
#define MARAUDER_TITLE_BYTES 13578
#elif defined(MARAUDER_MINI)
#define MARAUDER_TITLE_BYTES 13578
#elif defined(MARAUDER_REV_FEATHER)
#define MARAUDER_TITLE_BYTES 13578
#else
#define MARAUDER_TITLE_BYTES 13578
#endif

View File

@@ -13,7 +13,6 @@ https://www.online-utility.org/image/convert/to/XBM
#endif
#include <WiFi.h>
//#include "Web.h"
#include "EvilPortal.h"
#include <Wire.h>
#include "esp_wifi.h"
@@ -41,11 +40,10 @@ https://www.online-utility.org/image/convert/to/XBM
#include "xiaoLED.h"
#elif defined(MARAUDER_M5STICKC)
#include "stickcLED.h"
#else
#elif defined(HAS_NEOPIXEL_LED)
#include "LedInterface.h"
#endif
//#include "esp_interface.h"
#include "settings.h"
#include "CommandLine.h"
#include "lang_var.h"
@@ -54,42 +52,35 @@ https://www.online-utility.org/image/convert/to/XBM
#include "BatteryInterface.h"
#endif
//#ifdef HAS_TEMP_SENSOR
// #include "TemperatureInterface.h"
//#endif
#ifdef HAS_SCREEN
#include "Display.h"
#include "MenuFunctions.h"
//#include "a32u4_interface.h"
#endif
#ifdef HAS_BUTTONS
#include <SwitchLib.h>
#include "Switches.h"
#if (U_BTN >= 0)
SwitchLib u_btn = SwitchLib(U_BTN, 1000, true);
Switches u_btn = Switches(U_BTN, 1000, U_PULL);
#endif
#if (D_BTN >= 0)
SwitchLib d_btn = SwitchLib(D_BTN, 1000, true);
Switches d_btn = Switches(D_BTN, 1000, D_PULL);
#endif
#if (L_BTN >= 0)
SwitchLib l_btn = SwitchLib(L_BTN, 1000, true);
Switches l_btn = Switches(L_BTN, 1000, L_PULL);
#endif
#if (R_BTN >= 0)
SwitchLib r_btn = SwitchLib(R_BTN, 1000, true);
Switches r_btn = Switches(R_BTN, 1000, R_PULL);
#endif
#if (C_BTN >= 0)
SwitchLib c_btn = SwitchLib(C_BTN, 1000, true);
Switches c_btn = Switches(C_BTN, 1000, C_PULL);
#endif
#endif
WiFiScan wifi_scan_obj;
EvilPortal evil_portal_obj;
//Web web_obj;
Buffer buffer_obj;
//EspInterface esp_obj;
Settings settings_obj;
CommandLine cli_obj;
@@ -101,14 +92,9 @@ CommandLine cli_obj;
BatteryInterface battery_obj;
#endif
//#ifdef HAS_TEMP_SENSOR
// TemperatureInterface temp_obj;
//#endif
#ifdef HAS_SCREEN
Display display_obj;
MenuFunctions menu_function_obj;
//A32u4Interface a32u4_obj;
#endif
#ifdef HAS_SD
@@ -169,8 +155,6 @@ void setup()
axp192_obj.begin();
#endif
//pinMode(FLASH_BUTTON, INPUT);
#ifdef HAS_SCREEN
pinMode(TFT_BL, OUTPUT);
#endif
@@ -198,27 +182,8 @@ void setup()
Serial.begin(115200);
// Starts a second serial channel to stream the captured packets
#ifdef WRITE_PACKETS_SERIAL
#ifdef XIAO_ESP32_S3
Serial1.begin(115200, SERIAL_8N1, XIAO_RX1, XIAO_TX1);
#else
Serial1.begin(115200);
#endif
#endif
//Serial.println("\n\nHello, World!\n");
Serial.println("ESP-IDF version is: " + String(esp_get_idf_version()));
//#ifdef HAS_SCREEN
// Serial.println("Has Screen");
//#else
// Serial.println("Does not have screen");
//#endif
#ifdef HAS_SCREEN
display_obj.RunSetup();
display_obj.tft.setTextColor(TFT_WHITE, TFT_BLACK);
@@ -227,6 +192,7 @@ void setup()
backlightOff();
// Draw the title screen
/*
#ifdef HAS_SCREEN
#ifndef MARAUDER_MINI
display_obj.drawJpeg("/marauder3L.jpg", 0 , 0); // 240 x 320 image
@@ -234,18 +200,15 @@ void setup()
display_obj.drawJpeg("/marauder3L.jpg", 0, 0);
#endif
#endif
*/
#ifdef HAS_SCREEN
//showCenterText(version_number, 250);
#ifndef MARAUDER_MINI
display_obj.tft.drawCentreString(display_obj.version_number, 120, 250, 2);
#endif
#ifdef MARAUDER_MINI
display_obj.tft.drawCentreString(display_obj.version_number, TFT_WIDTH/2, TFT_HEIGHT, 1);
#endif
display_obj.tft.drawCentreString("ESP32 Marauder", TFT_WIDTH/2, TFT_HEIGHT * 0.33, 1);
display_obj.tft.drawCentreString("JustCallMeKoko", TFT_WIDTH/2, TFT_HEIGHT * 0.5, 1);
display_obj.tft.drawCentreString(display_obj.version_number, TFT_WIDTH/2, TFT_HEIGHT * 0.66, 1);
#endif
backlightOn(); // Need this
#ifdef HAS_SCREEN
@@ -275,27 +238,16 @@ void setup()
display_obj.tft.println(text_table0[1]);
#endif
//Serial.println("Internal Temp: " + (String)((temprature_sens_read() - 32) / 1.8));
settings_obj.begin();
//Serial.println("This is a test Channel: " + (String)settings_obj.loadSetting<uint8_t>("Channel"));
//if (settings_obj.loadSetting<bool>( "Force PMKID"))
// Serial.println("This is a test Force PMKID: true");
//else
// Serial.println("This is a test Force PMKID: false");
wifi_scan_obj.RunSetup();
//Serial.println(wifi_scan_obj.freeRAM());
#ifdef HAS_SCREEN
display_obj.tft.println(F(text_table0[2]));
#endif
#ifdef WRITE_PACKETS_SERIAL
buffer_obj = Buffer();
#elif defined(HAS_SD)
buffer_obj = Buffer();
#if defined(HAS_SD)
// Do some SD stuff
if(sd_obj.initSD()) {
#ifdef HAS_SCREEN
@@ -321,23 +273,12 @@ void setup()
display_obj.tft.println(F(text_table0[5]));
#endif
// Temperature stuff
//#ifdef HAS_TEMP_SENSOR
// temp_obj.RunSetup();
//#endif
#ifdef HAS_SCREEN
display_obj.tft.println(F(text_table0[6]));
#endif
#ifdef HAS_BATTERY
battery_obj.battery_level = battery_obj.getBatteryLevel();
// if (battery_obj.i2c_supported) {
// Serial.println(F("IP5306 I2C Supported: true"));
// }
// else
// Serial.println(F("IP5306 I2C Supported: false"));
#endif
// Do some LED stuff
@@ -378,12 +319,6 @@ void setup()
#ifdef HAS_SCREEN
menu_function_obj.RunSetup();
#endif
//Serial.println(F("\n\n--------------------------------\n"));
//Serial.println(F(" ESP32 Marauder \n"));
//Serial.println(" " + version_number + "\n");
//Serial.println(F(" By: justcallmekoko\n"));
//Serial.println(F("--------------------------------\n\n"));
Serial.println(F("CLI Ready"));
cli_obj.RunSetup();
@@ -399,6 +334,22 @@ void loop()
mini = true;
#endif
#ifdef HAS_ILI9341
#ifdef HAS_BUTTONS
if (c_btn.isHeld()) {
if (menu_function_obj.disable_touch)
menu_function_obj.disable_touch = false;
else
menu_function_obj.disable_touch = true;
menu_function_obj.updateStatusBar();
while (!c_btn.justReleased())
delay(1);
}
#endif
#endif
// Update all of our objects
/*#ifdef HAS_SCREEN
bool do_draw = display_obj.draw_tft;
@@ -419,14 +370,14 @@ void loop()
gps_obj.main();
#endif
#ifdef WRITE_PACKETS_SERIAL
buffer_obj.forceSaveSerial();
#elif defined(HAS_SD)
// Detect SD card
#if defined(HAS_SD)
sd_obj.main();
#else
return;
#endif
// Save buffer to SD and/or serial
buffer_obj.save();
#ifdef HAS_BATTERY
battery_obj.main(currentTime);
//temp_obj.main(currentTime);

View File

@@ -179,13 +179,12 @@ PROGMEM const char text4_44[] = " AP Scan ";
PROGMEM const char text4_45[] = "Clearing Stations...";
PROGMEM const char text4_46[] = "Stations Cleared: ";
PROGMEM const char text4_47[] = "Targeted Deauth";
PROGMEM const char text4_48[] = "Using serial to transmit packets";
//Making tables
PROGMEM const char *text_table0[] = {text0_0,text0_1, text0_2, text0_3, text0_4, text0_5, text0_6, text0_7, text0_8};
PROGMEM const char *text_table1[] = {text1_0,text1_1,text1_2,text1_3,text1_4,text1_5,text1_6,text1_7,text1_8,text1_9,text1_10,text1_11,text1_12,text1_13,text1_14,text1_15,text1_16,text1_17,text1_18,text1_19,text1_20,text1_21,text1_22,text1_23,text1_24,text1_25,text1_26,text1_27,text1_28,text1_29,text1_30,text1_31,text1_32,text1_33,text1_34,text1_35,text1_36,text1_37,text1_38,text1_39,text1_40,text1_41,text1_42,text1_43,text1_44,text1_45,text1_46,text1_47,text1_48,text1_49,text1_50,text1_51,text1_52,text1_53,text1_54,text1_55,text1_56,text1_57,text1_58,text1_59,text1_60,text1_61,text1_62};
PROGMEM const char *text_table2[] = {text2_0,text2_1,text2_2,text2_3,text2_4,text2_5,text2_6,text2_7,text2_8,text2_9,text2_10,text2_11,text2_12,text2_13,text2_14};
PROGMEM const char *text_table3[] = {text3_0,text3_1,text3_2,text3_3,text3_4,text3_5};
PROGMEM const char *text_table4[] = {text4_0,text4_1,text4_2,text4_3,text4_4,text4_5,text4_6,text4_7,text1_54,text4_9,text4_10,text4_11,text4_12,text4_13,text4_14,text4_15,text4_16,text4_17,text4_18,text4_19,text4_20,text4_21,text4_22,text4_23,text4_24,text4_25,text4_26,text4_27,text4_28,text4_29,text4_30,text4_31,text4_32,text4_33,text4_34,text4_35,text4_36,text4_37,text4_38,text4_39,text4_40,text4_41,text4_42,text4_43,text4_44,text4_45,text4_46,text4_47, text4_48};
PROGMEM const char *text_table4[] = {text4_0,text4_1,text4_2,text4_3,text4_4,text4_5,text4_6,text4_7,text1_54,text4_9,text4_10,text4_11,text4_12,text4_13,text4_14,text4_15,text4_16,text4_17,text4_18,text4_19,text4_20,text4_21,text4_22,text4_23,text4_24,text4_25,text4_26,text4_27,text4_28,text4_29,text4_30,text4_31,text4_32,text4_33,text4_34,text4_35,text4_36,text4_37,text4_38,text4_39,text4_40,text4_41,text4_42,text4_43,text4_44,text4_45,text4_46,text4_47};
#endif

View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@@ -0,0 +1,87 @@
# ESP Async WebServer
[![License: LGPL 3.0](https://img.shields.io/badge/License-LGPL%203.0-yellow.svg)](https://opensource.org/license/lgpl-3-0/)
[![Continuous Integration](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml/badge.svg)](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/mathieucarbou/library/ESP%20Async%20WebServer.svg)](https://registry.platformio.org/libraries/mathieucarbou/ESP%20Async%20WebServer)
Asynchronous HTTP and WebSocket Server Library for ESP32.
Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.
This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) and includes all the concurrency fixes.
## Changes in this fork
- Removed SPIFFSEditor
- Deployed in PlatformIO registry and Arduino IDE library manager
- CI
- Some code cleanup
- `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client
- `write()` function public in `AsyncEventSource.h`
- Arduino Json 7 compatibility and backward compatible with 6 and 6 (changes in `AsyncJson.h`). The API to use Json has not changed. These are only internal changes.
- `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client
- Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket.
- [#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5) ([@vortigont](https://github.com/vortigont)): set real "Last-Modified" header based on file's LastWrite time
- [#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13) ([@tueddy](https://github.com/tueddy)): Compile with Arduino 3 (ESP-IDF 5.1)
- [#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14) ([@nilo85](https://github.com/nilo85)): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler
- Added `setAuthentication(const String& username, const String& password)`
- Added `StreamConcat` example to shoiw how to stream multiple files in one response
- Remove filename after inline in Content-Disposition header according to RFC2183
- Depends on `mathieucarbou/Async TCP @ ^3.1.4`
- Arduino 3 / ESP-IDF 5.1 compatibility
- Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket`
- Added `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full
## Documentation
Usage and API stays the same as the original library.
Please look at the original libraries for more examples and documentation.
[https://github.com/yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer)
## `AsyncWebSocketMessageBuffer` and `makeBuffer()`
The fork from `yubox-node-org` introduces some breaking API changes compared to the original library, especially regarding the use of `std::shared_ptr<std::vector<uint8_t>>` for WebSocket.
This fork is compatible with the original library from `me-no-dev` regarding WebSocket, and wraps the optimizations done by `yubox-node-org` in the `AsyncWebSocketMessageBuffer` class.
So you have the choice of which API to use.
I strongly suggest to use the optimized API from `yubox-node-org` as it is much more efficient.
Here is an example for serializing a Json document in a websocket message buffer. This code is compatible with any forks, but not optimized:
```cpp
void send(JsonDocument& doc) {
const size_t len = measureJson(doc);
// original API from me-no-dev
AsyncWebSocketMessageBuffer* buffer = _ws->makeBuffer(len);
assert(buffer); // up to you to keep or remove this
serializeJson(doc, buffer->get(), len);
_ws->textAll(buffer);
}
```
Here is an example for serializing a Json document in a more optimized way, and compatible with both forks:
```cpp
void send(JsonDocument& doc) {
const size_t len = measureJson(doc);
#if defined(ASYNCWEBSERVER_FORK_mathieucarbou)
// this fork (originally from yubox-node-org), uses another API with shared pointer that better support concurrent use cases then the original project
auto buffer = std::make_shared<std::vector<uint8_t>>(len);
assert(buffer); // up to you to keep or remove this
serializeJson(doc, buffer->data(), len);
_ws->textAll(std::move(buffer));
#else
// original API from me-no-dev
AsyncWebSocketMessageBuffer* buffer = _ws->makeBuffer(len);
assert(buffer); // up to you to keep or remove this
serializeJson(doc, buffer->get(), len);
_ws->textAll(buffer);
#endif
}
```

View File

@@ -0,0 +1,8 @@
# bundle exec jekyll serve --host=0.0.0.0
title: ESP Async WebServer
description: "Asynchronous HTTP and WebSocket Server Library for ESP32"
remote_theme: pages-themes/cayman@v0.2.0
plugins:
- jekyll-remote-theme

View File

@@ -0,0 +1,87 @@
# ESP Async WebServer
[![License: LGPL 3.0](https://img.shields.io/badge/License-LGPL%203.0-yellow.svg)](https://opensource.org/license/lgpl-3-0/)
[![Continuous Integration](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml/badge.svg)](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml)
[![PlatformIO Registry](https://badges.registry.platformio.org/packages/mathieucarbou/library/ESP%20Async%20WebServer.svg)](https://registry.platformio.org/libraries/mathieucarbou/ESP%20Async%20WebServer)
Asynchronous HTTP and WebSocket Server Library for ESP32.
Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.
This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) and includes all the concurrency fixes.
## Changes in this fork
- Removed SPIFFSEditor
- Deployed in PlatformIO registry and Arduino IDE library manager
- CI
- Some code cleanup
- `SSE_MAX_QUEUED_MESSAGES` to control the maximum number of messages that can be queued for a SSE client
- `write()` function public in `AsyncEventSource.h`
- Arduino Json 7 compatibility and backward compatible with 6 and 6 (changes in `AsyncJson.h`). The API to use Json has not changed. These are only internal changes.
- `WS_MAX_QUEUED_MESSAGES`: control the maximum number of messages that can be queued for a Websocket client
- Resurrected `AsyncWebSocketMessageBuffer` and `makeBuffer()` in order to make the fork API-compatible with the original library from me-no-dev regarding WebSocket.
- [#5](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/5) ([@vortigont](https://github.com/vortigont)): set real "Last-Modified" header based on file's LastWrite time
- [#13](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/13) ([@tueddy](https://github.com/tueddy)): Compile with Arduino 3 (ESP-IDF 5.1)
- [#14](https://github.com/mathieucarbou/ESPAsyncWebServer/pull/14) ([@nilo85](https://github.com/nilo85)): Add support for Auth & GET requests in AsyncCallbackJsonWebHandler
- Added `setAuthentication(const String& username, const String& password)`
- Added `StreamConcat` example to shoiw how to stream multiple files in one response
- Remove filename after inline in Content-Disposition header according to RFC2183
- Depends on `mathieucarbou/Async TCP @ ^3.1.4`
- Arduino 3 / ESP-IDF 5.1 compatibility
- Added all flavors of `binary()`, `text()`, `binaryAll()` and `textAll()` in `AsyncWebSocket`
- Added `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full
## Documentation
Usage and API stays the same as the original library.
Please look at the original libraries for more examples and documentation.
[https://github.com/yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer)
## `AsyncWebSocketMessageBuffer` and `makeBuffer()`
The fork from `yubox-node-org` introduces some breaking API changes compared to the original library, especially regarding the use of `std::shared_ptr<std::vector<uint8_t>>` for WebSocket.
This fork is compatible with the original library from `me-no-dev` regarding WebSocket, and wraps the optimizations done by `yubox-node-org` in the `AsyncWebSocketMessageBuffer` class.
So you have the choice of which API to use.
I strongly suggest to use the optimized API from `yubox-node-org` as it is much more efficient.
Here is an example for serializing a Json document in a websocket message buffer. This code is compatible with any forks, but not optimized:
```cpp
void send(JsonDocument& doc) {
const size_t len = measureJson(doc);
// original API from me-no-dev
AsyncWebSocketMessageBuffer* buffer = _ws->makeBuffer(len);
assert(buffer); // up to you to keep or remove this
serializeJson(doc, buffer->get(), len);
_ws->textAll(buffer);
}
```
Here is an example for serializing a Json document in a more optimized way, and compatible with both forks:
```cpp
void send(JsonDocument& doc) {
const size_t len = measureJson(doc);
#if defined(ASYNCWEBSERVER_FORK_mathieucarbou)
// this fork (originally from yubox-node-org), uses another API with shared pointer that better support concurrent use cases then the original project
auto buffer = std::make_shared<std::vector<uint8_t>>(len);
assert(buffer); // up to you to keep or remove this
serializeJson(doc, buffer->data(), len);
_ws->textAll(std::move(buffer));
#else
// original API from me-no-dev
AsyncWebSocketMessageBuffer* buffer = _ws->makeBuffer(len);
assert(buffer); // up to you to keep or remove this
serializeJson(doc, buffer->get(), len);
_ws->textAll(buffer);
#endif
}
```

View File

@@ -0,0 +1,54 @@
#include <DNSServer.h>
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#include "ESPAsyncWebServer.h"
DNSServer dnsServer;
AsyncWebServer server(80);
class CaptiveRequestHandler : public AsyncWebHandler {
public:
CaptiveRequestHandler() {}
virtual ~CaptiveRequestHandler() {}
bool canHandle(__unused AsyncWebServerRequest *request){
//request->addInterestingHeader("ANY");
return true;
}
void handleRequest(AsyncWebServerRequest *request) {
AsyncResponseStream *response = request->beginResponseStream("text/html");
response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
response->print("<p>This is out captive portal front page.</p>");
response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
response->print("</body></html>");
request->send(response);
}
};
void setup(){
Serial.begin(115200);
Serial.println();
Serial.println("Configuring access point...");
if (!WiFi.softAP("esp-captive")) {
Serial.println("Soft AP creation failed.");
while (1);
}
dnsServer.start(53, "*", WiFi.softAPIP());
server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);//only when requested from AP
//more handlers...
server.begin();
}
void loop(){
dnsServer.processNextRequest();
}

View File

@@ -0,0 +1,108 @@
// Reproduced issue https://github.com/mathieucarbou/ESPAsyncWebServer/issues/26
#include <DNSServer.h>
#ifdef ESP32
#include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#include "ESPAsyncWebServer.h"
DNSServer dnsServer;
AsyncWebServer server(80);
class CaptiveRequestHandler : public AsyncWebHandler {
public:
CaptiveRequestHandler() {}
virtual ~CaptiveRequestHandler() {}
bool canHandle(__unused AsyncWebServerRequest* request) {
// request->addInterestingHeader("ANY");
return true;
}
void handleRequest(AsyncWebServerRequest* request) {
AsyncResponseStream* response = request->beginResponseStream("text/html");
response->print("<!DOCTYPE html><html><head><title>Captive Portal</title></head><body>");
response->print("<p>This is out captive portal front page.</p>");
response->printf("<p>You were trying to reach: http://%s%s</p>", request->host().c_str(), request->url().c_str());
response->printf("<p>Try opening <a href='http://%s'>this link</a> instead</p>", WiFi.softAPIP().toString().c_str());
response->print("</body></html>");
request->send(response);
}
};
bool hit1 = false;
bool hit2 = false;
void setup() {
Serial.begin(115200);
server
.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
Serial.println("Captive portal request...");
Serial.println("WiFi.localIP(): " + WiFi.localIP().toString());
Serial.println("request->client()->localIP(): " + request->client()->localIP().toString());
#if ESP_IDF_VERSION_MAJOR >= 5
Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type()));
Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type()));
#endif
Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER");
Serial.println(WiFi.localIP() == request->client()->localIP());
Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString());
request->send(200, "text/plain", "This is the captive portal");
hit1 = true;
})
.setFilter(ON_AP_FILTER);
server
.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
Serial.println("Website request...");
Serial.println("WiFi.localIP(): " + WiFi.localIP().toString());
Serial.println("request->client()->localIP(): " + request->client()->localIP().toString());
#if ESP_IDF_VERSION_MAJOR >= 5
Serial.println("WiFi.type(): " + String((int)WiFi.localIP().type()));
Serial.println("request->client()->type(): " + String((int)request->client()->localIP().type()));
#endif
Serial.println(WiFi.localIP() == request->client()->localIP() ? "should be: ON_STA_FILTER" : "should be: ON_AP_FILTER");
Serial.println(WiFi.localIP() == request->client()->localIP());
Serial.println(WiFi.localIP().toString() == request->client()->localIP().toString());
request->send(200, "text/plain", "This is the website");
hit2 = true;
})
.setFilter(ON_STA_FILTER);
// assert(WiFi.softAP("esp-captive-portal"));
// dnsServer.start(53, "*", WiFi.softAPIP());
// server.begin();
// Serial.println("Captive portal started!");
// while (!hit1) {
// dnsServer.processNextRequest();
// yield();
// }
// delay(1000); // Wait for the client to process the response
// Serial.println("Captive portal opened, stopping it and connecting to WiFi...");
// dnsServer.stop();
// WiFi.softAPdisconnect();
WiFi.persistent(false);
WiFi.begin("IoT");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.println("Connected to WiFi with IP address: " + WiFi.localIP().toString());
server.begin();
// while (!hit2) {
// delay(10);
// }
// delay(1000); // Wait for the client to process the response
// ESP.restart();
}
void loop() {
}

View File

@@ -0,0 +1,74 @@
//
// A simple server implementation showing how to:
// * serve static messages
// * read GET and POST parameters
// * handle missing pages / 404s
//
#include <Arduino.h>
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
const char* PARAM_MESSAGE = "message";
void notFound(AsyncWebServerRequest *request) {
request->send(404, "text/plain", "Not found");
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.printf("WiFi Failed!\n");
return;
}
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", "Hello, world");
});
// Send a GET request to <IP>/get?message=<message>
server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
String message;
if (request->hasParam(PARAM_MESSAGE)) {
message = request->getParam(PARAM_MESSAGE)->value();
} else {
message = "No message sent";
}
request->send(200, "text/plain", "Hello, GET: " + message);
});
// Send a POST request to <IP>/post with a form field message set to <message>
server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request){
String message;
if (request->hasParam(PARAM_MESSAGE, true)) {
message = request->getParam(PARAM_MESSAGE, true)->value();
} else {
message = "No message sent";
}
request->send(200, "text/plain", "Hello, POST: " + message);
});
server.onNotFound(notFound);
server.begin();
}
void loop() {
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include <Stream.h>
class StreamConcat : public Stream {
public:
StreamConcat(Stream* s1, Stream* s2) : _s1(s1), _s2(s2) {}
size_t write(__unused const uint8_t* p, __unused size_t n) override { return 0; }
size_t write(__unused uint8_t c) override { return 0; }
void flush() override {}
int available() override { return _s1->available() + _s2->available(); }
int read() override {
int c = _s1->read();
return c != -1 ? c : _s2->read();
}
size_t readBytes(char* buffer, size_t length) override {
size_t count = _s1->readBytes(buffer, length);
return count > 0 ? count : _s2->readBytes(buffer, length);
}
int peek() override {
int c = _s1->peek();
return c != -1 ? c : _s2->peek();
}
private:
Stream* _s1;
Stream* _s2;
};

View File

@@ -0,0 +1,73 @@
#include <Arduino.h>
#include <DNSServer.h>
#ifdef ESP32
#include <AsyncTCP.h>
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#endif
#include "StreamConcat.h"
#include "StreamString.h"
#include <ESPAsyncWebServer.h>
#include <LittleFS.h>
DNSServer dnsServer;
AsyncWebServer server(80);
void setup() {
Serial.begin(115200);
LittleFS.begin();
WiFi.mode(WIFI_AP);
WiFi.softAP("esp-captive");
dnsServer.start(53, "*", WiFi.softAPIP());
File file1 = LittleFS.open("/header.html", "w");
file1.print("<html><head><title>ESP Captive Portal</title><meta http-equiv=\"refresh\" content=\"1\"></head><body>");
file1.close();
File file2 = LittleFS.open("/body.html", "w");
file2.print("<h1>Welcome to ESP Captive Portal</h1>");
file2.close();
File file3 = LittleFS.open("/footer.html", "w");
file3.print("</body></html>");
file3.close();
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
File header = LittleFS.open("/header.html", "r");
File body = LittleFS.open("/body.html", "r");
StreamConcat stream1(&header, &body);
StreamString content;
content.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap());
StreamConcat stream2 = StreamConcat(&stream1, &content);
File footer = LittleFS.open("/footer.html", "r");
StreamConcat stream3 = StreamConcat(&stream2, &footer);
request->send(stream3, "text/html", stream3.available());
header.close();
body.close();
footer.close();
});
server.onNotFound([](AsyncWebServerRequest* request) {
request->send(404, "text/plain", "Not found");
});
server.begin();
}
uint32_t last = 0;
void loop() {
// dnsServer.processNextRequest();
if (millis() - last > 2000) {
Serial.printf("FreeHeap: %" PRIu32, ESP.getFreeHeap());
last = millis();
}
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <Stream.h>
class StreamString : public Stream {
public:
size_t write(const uint8_t* p, size_t n) override { return _buffer.concat(reinterpret_cast<const char*>(p), n) ? n : 0; }
size_t write(uint8_t c) override { return _buffer.concat(static_cast<char>(c)) ? 1 : 0; }
void flush() override {}
int available() override { return static_cast<int>(_buffer.length()); }
int read() override {
if (_buffer.length() == 0)
return -1;
char c = _buffer[0];
_buffer.remove(0, 1);
return c;
}
size_t readBytes(char* buffer, size_t length) override {
if (length > _buffer.length())
length = _buffer.length();
// Don't use _str.ToCharArray() because it inserts a terminator
memcpy(buffer, _buffer.c_str(), length);
_buffer.remove(0, static_cast<unsigned int>(length));
return length;
}
int peek() override { return _buffer.length() > 0 ? _buffer[0] : -1; }
const String& buffer() const { return _buffer; }
private:
String _buffer;
};

View File

@@ -0,0 +1,54 @@
{
"name": "ESP Async WebServer",
"version": "2.10.4",
"description": "Asynchronous HTTP and WebSocket Server Library for ESP32. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.",
"keywords": "http,async,websocket,webserver",
"homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer",
"repository": {
"type": "git",
"url": "https://github.com/mathieucarbou/ESPAsyncWebServer.git"
},
"authors": [
{
"name": "Hristo Gochkov"
},
{
"name": "Mathieu Carbou",
"maintainer": true
}
],
"license": "LGPL-3.0",
"frameworks": "arduino",
"platforms": [
"espressif32",
"espressif8266"
],
"dependencies": [
{
"owner": "mathieucarbou",
"name": "Async TCP",
"version": "^3.1.4",
"platforms": "espressif32"
},
{
"owner": "esphome",
"name": "ESPAsyncTCP-esphome",
"version": "^2.0.0",
"platforms": "espressif8266"
},
{
"name": "Hash",
"platforms": "espressif8266"
}
],
"export": {
"include": [
"examples",
"src",
"library.json",
"library.properties",
"LICENSE",
"README.md"
]
}
}

View File

@@ -0,0 +1,10 @@
name=ESP Async WebServer
version=2.10.4
author=Me-No-Dev
maintainer=Mathieu Carbou <mathieu.carbou@gmail.com>
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32
paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc
category=Other
url=https://github.com/mathieucarbou/ESPAsyncWebServer
architectures=esp8266,esp32
license=LGPL-3.0

View File

@@ -0,0 +1,44 @@
[env]
framework = arduino
build_flags =
-Wall -Wextra
-D CONFIG_ARDUHAL_LOG_COLORS
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
lib_deps =
bblanchon/ArduinoJson @ 7.0.4
mathieucarbou/Async TCP @ ^3.1.4
; https://github.com/mathieucarbou/AsyncTCP
; https://github.com/me-no-dev/AsyncTCP
esphome/ESPAsyncTCP-esphome @ 2.0.0
upload_protocol = esptool
monitor_speed = 115200
monitor_filters = esp32_exception_decoder, log2file
[platformio]
lib_dir = .
; src_dir = examples/CaptivePortal
; src_dir = examples/SimpleServer
src_dir = examples/StreamFiles
; src_dir = examples/Filters
[env:arduino]
platform = espressif32
board = esp32dev
[env:arduino-2]
platform = espressif32@6.7.0
board = esp32dev
[env:arduino-3]
platform = espressif32
platform_packages=
platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.0
platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.0/esp32-arduino-libs-3.0.0.zip
board = esp32dev
[env:esp8266]
platform = espressif8266
board = huzzah
lib_deps =
bblanchon/ArduinoJson @ 7.0.4
esphome/ESPAsyncTCP-esphome @ 2.0.0

View File

@@ -0,0 +1,412 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "Arduino.h"
#include "AsyncEventSource.h"
#ifndef ESP8266
#include <rom/ets_sys.h>
#endif
static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){
String ev;
if(reconnect){
ev += F("retry: ");
ev += reconnect;
ev += F("\r\n");
}
if(id){
ev += F("id: ");
ev += String(id);
ev += F("\r\n");
}
if(event != NULL){
ev += F("event: ");
ev += String(event);
ev += F("\r\n");
}
if(message != NULL){
size_t messageLen = strlen(message);
char * lineStart = (char *)message;
char * lineEnd;
do {
char * nextN = strchr(lineStart, '\n');
char * nextR = strchr(lineStart, '\r');
if(nextN == NULL && nextR == NULL){
size_t llen = ((char *)message + messageLen) - lineStart;
char * ldata = (char *)malloc(llen+1);
if(ldata != NULL){
memcpy(ldata, lineStart, llen);
ldata[llen] = 0;
ev += F("data: ");
ev += ldata;
ev += F("\r\n\r\n");
free(ldata);
}
lineStart = (char *)message + messageLen;
} else {
char * nextLine = NULL;
if(nextN != NULL && nextR != NULL){
if(nextR < nextN){
lineEnd = nextR;
if(nextN == (nextR + 1))
nextLine = nextN + 1;
else
nextLine = nextR + 1;
} else {
lineEnd = nextN;
if(nextR == (nextN + 1))
nextLine = nextR + 1;
else
nextLine = nextN + 1;
}
} else if(nextN != NULL){
lineEnd = nextN;
nextLine = nextN + 1;
} else {
lineEnd = nextR;
nextLine = nextR + 1;
}
size_t llen = lineEnd - lineStart;
char * ldata = (char *)malloc(llen+1);
if(ldata != NULL){
memcpy(ldata, lineStart, llen);
ldata[llen] = 0;
ev += F("data: ");
ev += ldata;
ev += F("\r\n");
free(ldata);
}
lineStart = nextLine;
if(lineStart == ((char *)message + messageLen))
ev += F("\r\n");
}
} while(lineStart < ((char *)message + messageLen));
}
return ev;
}
// Message
AsyncEventSourceMessage::AsyncEventSourceMessage(const char * data, size_t len)
: _data(nullptr), _len(len), _sent(0), _acked(0)
{
_data = (uint8_t*)malloc(_len+1);
if(_data == nullptr){
_len = 0;
} else {
memcpy(_data, data, len);
_data[_len] = 0;
}
}
AsyncEventSourceMessage::~AsyncEventSourceMessage() {
if(_data != NULL)
free(_data);
}
size_t AsyncEventSourceMessage::ack(size_t len, uint32_t time) {
(void)time;
// If the whole message is now acked...
if(_acked + len > _len){
// Return the number of extra bytes acked (they will be carried on to the next message)
const size_t extra = _acked + len - _len;
_acked = _len;
return extra;
}
// Return that no extra bytes left.
_acked += len;
return 0;
}
// This could also return void as the return value is not used.
// Leaving as-is for compatibility...
size_t AsyncEventSourceMessage::send(AsyncClient *client) {
if (_sent >= _len) {
return 0;
}
const size_t len_to_send = _len - _sent;
auto position = reinterpret_cast<const char*>(_data + _sent);
const size_t sent_now = client->write(position, len_to_send);
_sent += sent_now;
return sent_now;
}
// Client
AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server)
: _messageQueue(AlternativeLinkedList<AsyncEventSourceMessage *>([](AsyncEventSourceMessage *m){ delete m; }))
{
_client = request->client();
_server = server;
_lastId = 0;
if(request->hasHeader(F("Last-Event-ID")))
_lastId = atoi(request->getHeader(F("Last-Event-ID"))->value().c_str());
_client->setRxTimeout(0);
_client->onError(NULL, NULL);
_client->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; ((AsyncEventSourceClient*)(r))->_onAck(len, time); }, this);
_client->onPoll([](void *r, AsyncClient* c){ (void)c; ((AsyncEventSourceClient*)(r))->_onPoll(); }, this);
_client->onData(NULL, NULL);
_client->onTimeout([this](void *r, AsyncClient* c __attribute__((unused)), uint32_t time){ ((AsyncEventSourceClient*)(r))->_onTimeout(time); }, this);
_client->onDisconnect([this](void *r, AsyncClient* c){ ((AsyncEventSourceClient*)(r))->_onDisconnect(); delete c; }, this);
_server->_addClient(this);
delete request;
}
AsyncEventSourceClient::~AsyncEventSourceClient(){
_lockmq.lock();
_messageQueue.free();
_lockmq.unlock();
close();
}
void AsyncEventSourceClient::_queueMessage(AsyncEventSourceMessage *dataMessage){
if(dataMessage == NULL)
return;
if(!connected()){
delete dataMessage;
return;
}
//length() is not thread-safe, thus acquiring the lock before this call..
_lockmq.lock();
if(_messageQueue.length() >= SSE_MAX_QUEUED_MESSAGES){
#ifdef ESP8266
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
#else
log_e("Too many messages queued: deleting message");
#endif
delete dataMessage;
} else {
_messageQueue.add(dataMessage);
// runqueue trigger when new messages added
if(_client->canSend()) {
_runQueue();
}
}
_lockmq.unlock();
}
void AsyncEventSourceClient::_onAck(size_t len, uint32_t time){
// Same here, acquiring the lock early
_lockmq.lock();
while(len && !_messageQueue.isEmpty()){
len = _messageQueue.front()->ack(len, time);
if(_messageQueue.front()->finished())
_messageQueue.remove(_messageQueue.front());
}
_runQueue();
_lockmq.unlock();
}
void AsyncEventSourceClient::_onPoll(){
_lockmq.lock();
if(!_messageQueue.isEmpty()){
_runQueue();
}
_lockmq.unlock();
}
void AsyncEventSourceClient::_onTimeout(uint32_t time __attribute__((unused))){
_client->close(true);
}
void AsyncEventSourceClient::_onDisconnect(){
_client = NULL;
_server->_handleDisconnect(this);
}
void AsyncEventSourceClient::close(){
if(_client != NULL)
_client->close();
}
void AsyncEventSourceClient::write(const char * message, size_t len){
_queueMessage(new AsyncEventSourceMessage(message, len));
}
void AsyncEventSourceClient::send(const char *message, const char *event, uint32_t id, uint32_t reconnect){
String ev = generateEventMessage(message, event, id, reconnect);
_queueMessage(new AsyncEventSourceMessage(ev.c_str(), ev.length()));
}
size_t AsyncEventSourceClient::packetsWaiting() const {
size_t len;
_lockmq.lock();
len = _messageQueue.length();
_lockmq.unlock();
return len;
}
void AsyncEventSourceClient::_runQueue() {
// Calls to this private method now already protected by _lockmq acquisition
// so no extra call of _lockmq.lock() here..
for (auto i = _messageQueue.begin(); i != _messageQueue.end(); ++i) {
// If it crashes here, iterator (i) has been invalidated as _messageQueue
// has been changed... (UL 2020-11-15: Not supposed to happen any more ;-) )
if (!(*i)->sent()) {
(*i)->send(_client);
}
}
}
// Handler
AsyncEventSource::AsyncEventSource(const String& url)
: _url(url)
, _clients(AlternativeLinkedList<AsyncEventSourceClient *>([](AsyncEventSourceClient *c){ delete c; }))
, _connectcb(NULL)
{}
AsyncEventSource::~AsyncEventSource(){
close();
}
void AsyncEventSource::onConnect(ArEventHandlerFunction cb){
_connectcb = cb;
}
void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb){
_authorizeConnectHandler = cb;
}
void AsyncEventSource::_addClient(AsyncEventSourceClient * client){
/*char * temp = (char *)malloc(2054);
if(temp != NULL){
memset(temp+1,' ',2048);
temp[0] = ':';
temp[2049] = '\r';
temp[2050] = '\n';
temp[2051] = '\r';
temp[2052] = '\n';
temp[2053] = 0;
client->write((const char *)temp, 2053);
free(temp);
}*/
AsyncWebLockGuard l(_client_queue_lock);
_clients.add(client);
if(_connectcb)
_connectcb(client);
}
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient * client){
AsyncWebLockGuard l(_client_queue_lock);
_clients.remove(client);
}
void AsyncEventSource::close(){
// While the whole loop is not done, the linked list is locked and so the
// iterator should remain valid even when AsyncEventSource::_handleDisconnect()
// is called very early
AsyncWebLockGuard l(_client_queue_lock);
for(const auto &c: _clients){
if(c->connected())
c->close();
}
}
// pmb fix
size_t AsyncEventSource::avgPacketsWaiting() const {
size_t aql = 0;
uint32_t nConnectedClients = 0;
AsyncWebLockGuard l(_client_queue_lock);
if (_clients.isEmpty()) {
return 0;
}
for(const auto &c: _clients){
if(c->connected()) {
aql += c->packetsWaiting();
++nConnectedClients;
}
}
return ((aql) + (nConnectedClients/2)) / (nConnectedClients); // round up
}
void AsyncEventSource::send(
const char *message, const char *event, uint32_t id, uint32_t reconnect){
String ev = generateEventMessage(message, event, id, reconnect);
AsyncWebLockGuard l(_client_queue_lock);
for(const auto &c: _clients){
if(c->connected()) {
c->write(ev.c_str(), ev.length());
}
}
}
size_t AsyncEventSource::count() const {
size_t n_clients;
AsyncWebLockGuard l(_client_queue_lock);
n_clients = _clients.count_if([](AsyncEventSourceClient *c){
return c->connected();
});
return n_clients;
}
bool AsyncEventSource::canHandle(AsyncWebServerRequest *request){
if(request->method() != HTTP_GET || !request->url().equals(_url)) {
return false;
}
request->addInterestingHeader(F("Last-Event-ID"));
request->addInterestingHeader("Cookie");
return true;
}
void AsyncEventSource::handleRequest(AsyncWebServerRequest *request){
if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str())) {
return request->requestAuthentication();
}
if(_authorizeConnectHandler != NULL){
if(!_authorizeConnectHandler(request)){
return request->send(401);
}
}
request->send(new AsyncEventSourceResponse(this));
}
// Response
AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource *server){
_server = server;
_code = 200;
_contentType = F("text/event-stream");
_sendContentLength = false;
addHeader(F("Cache-Control"), F("no-cache"));
addHeader(F("Connection"), F("keep-alive"));
}
void AsyncEventSourceResponse::_respond(AsyncWebServerRequest *request){
String out = _assembleHead(request->version());
request->client()->write(out.c_str(), _headLength);
_state = RESPONSE_WAIT_ACK;
}
size_t AsyncEventSourceResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time __attribute__((unused))){
if(len){
new AsyncEventSourceClient(request, _server);
}
return 0;
}

View File

@@ -0,0 +1,147 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef ASYNCEVENTSOURCE_H_
#define ASYNCEVENTSOURCE_H_
#include <Arduino.h>
#ifdef ESP32
#include <AsyncTCP.h>
#ifndef SSE_MAX_QUEUED_MESSAGES
#define SSE_MAX_QUEUED_MESSAGES 32
#endif
#else
#include <ESPAsyncTCP.h>
#ifndef SSE_MAX_QUEUED_MESSAGES
#define SSE_MAX_QUEUED_MESSAGES 8
#endif
#endif
#include <ESPAsyncWebServer.h>
#include "AsyncWebSynchronization.h"
#ifdef ESP8266
#include <Hash.h>
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
#include <../src/Hash.h>
#endif
#endif
#ifdef ESP32
#define DEFAULT_MAX_SSE_CLIENTS 8
#else
#define DEFAULT_MAX_SSE_CLIENTS 4
#endif
class AsyncEventSource;
class AsyncEventSourceResponse;
class AsyncEventSourceClient;
typedef std::function<void(AsyncEventSourceClient *client)> ArEventHandlerFunction;
typedef std::function<bool(AsyncWebServerRequest *request)> ArAuthorizeConnectHandler;
class AsyncEventSourceMessage {
private:
uint8_t * _data;
size_t _len;
size_t _sent;
//size_t _ack;
size_t _acked;
public:
AsyncEventSourceMessage(const char * data, size_t len);
~AsyncEventSourceMessage();
size_t ack(size_t len, uint32_t time __attribute__((unused)));
size_t send(AsyncClient *client);
bool finished(){ return _acked == _len; }
bool sent() { return _sent == _len; }
};
class AsyncEventSourceClient {
private:
AsyncClient *_client;
AsyncEventSource *_server;
uint32_t _lastId;
AlternativeLinkedList<AsyncEventSourceMessage *> _messageQueue;
// ArFi 2020-08-27 for protecting/serializing _messageQueue
AsyncPlainLock _lockmq;
void _queueMessage(AsyncEventSourceMessage *dataMessage);
void _runQueue();
public:
AsyncEventSourceClient(AsyncWebServerRequest *request, AsyncEventSource *server);
~AsyncEventSourceClient();
AsyncClient* client(){ return _client; }
void close();
void write(const char * message, size_t len);
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
bool connected() const { return (_client != NULL) && _client->connected(); }
uint32_t lastId() const { return _lastId; }
size_t packetsWaiting() const;
//system callbacks (do not call)
void _onAck(size_t len, uint32_t time);
void _onPoll();
void _onTimeout(uint32_t time);
void _onDisconnect();
};
class AsyncEventSource: public AsyncWebHandler {
private:
String _url;
AlternativeLinkedList<AsyncEventSourceClient *> _clients;
// Same as for individual messages, protect mutations of _clients list
// since simultaneous access from different tasks is possible
AsyncWebLock _client_queue_lock;
ArEventHandlerFunction _connectcb;
ArAuthorizeConnectHandler _authorizeConnectHandler;
public:
AsyncEventSource(const String& url);
~AsyncEventSource();
const char * url() const { return _url.c_str(); }
void close();
void onConnect(ArEventHandlerFunction cb);
void authorizeConnect(ArAuthorizeConnectHandler cb);
void send(const char *message, const char *event=NULL, uint32_t id=0, uint32_t reconnect=0);
// number of clients connected
size_t count() const;
size_t avgPacketsWaiting() const;
//system callbacks (do not call)
void _addClient(AsyncEventSourceClient * client);
void _handleDisconnect(AsyncEventSourceClient * client);
virtual bool canHandle(AsyncWebServerRequest *request) override final;
virtual void handleRequest(AsyncWebServerRequest *request) override final;
};
class AsyncEventSourceResponse: public AsyncWebServerResponse {
private:
String _content;
AsyncEventSource *_server;
public:
AsyncEventSourceResponse(AsyncEventSource *server);
void _respond(AsyncWebServerRequest *request);
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
bool _sourceValid() const { return true; }
};
#endif /* ASYNCEVENTSOURCE_H_ */

View File

@@ -0,0 +1,282 @@
// AsyncJson.h
/*
Async Response to use with ArduinoJson and AsyncWebServer
Written by Andrew Melvin (SticilFace) with help from me-no-dev and BBlanchon.
Example of callback in use
server.on("/json", HTTP_ANY, [](AsyncWebServerRequest * request) {
AsyncJsonResponse * response = new AsyncJsonResponse();
JsonObject& root = response->getRoot();
root["key1"] = "key number one";
JsonObject& nested = root.createNestedObject("nested");
nested["key1"] = "key number one";
response->setLength();
request->send(response);
});
--------------------
Async Request to use with ArduinoJson and AsyncWebServer
Written by Arsène von Wyss (avonwyss)
Example
AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler("/rest/endpoint");
handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
JsonObject& jsonObj = json.as<JsonObject>();
// ...
});
server.addHandler(handler);
*/
#ifndef ASYNC_JSON_H_
#define ASYNC_JSON_H_
#include <ArduinoJson.h>
#include <ESPAsyncWebServer.h>
#include <Print.h>
#if ARDUINOJSON_VERSION_MAJOR == 6
#ifndef DYNAMIC_JSON_DOCUMENT_SIZE
#define DYNAMIC_JSON_DOCUMENT_SIZE 1024
#endif
#endif
constexpr const char* JSON_MIMETYPE = "application/json";
/*
* Json Response
* */
class ChunkPrint : public Print {
private:
uint8_t* _destination;
size_t _to_skip;
size_t _to_write;
size_t _pos;
public:
ChunkPrint(uint8_t* destination, size_t from, size_t len)
: _destination(destination), _to_skip(from), _to_write(len), _pos{0} {}
virtual ~ChunkPrint() {}
size_t write(uint8_t c) {
if (_to_skip > 0) {
_to_skip--;
return 1;
} else if (_to_write > 0) {
_to_write--;
_destination[_pos++] = c;
return 1;
}
return 0;
}
size_t write(const uint8_t* buffer, size_t size) {
return this->Print::write(buffer, size);
}
};
class AsyncJsonResponse : public AsyncAbstractResponse {
protected:
#if ARDUINOJSON_VERSION_MAJOR == 5
DynamicJsonBuffer _jsonBuffer;
#elif ARDUINOJSON_VERSION_MAJOR == 6
DynamicJsonDocument _jsonBuffer;
#else
JsonDocument _jsonBuffer;
#endif
JsonVariant _root;
bool _isValid;
public:
#if ARDUINOJSON_VERSION_MAJOR == 5
AsyncJsonResponse(bool isArray = false) : _isValid{false} {
_code = 200;
_contentType = JSON_MIMETYPE;
if (isArray)
_root = _jsonBuffer.createArray();
else
_root = _jsonBuffer.createObject();
}
#elif ARDUINOJSON_VERSION_MAJOR == 6
AsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : _jsonBuffer(maxJsonBufferSize), _isValid{false} {
_code = 200;
_contentType = JSON_MIMETYPE;
if (isArray)
_root = _jsonBuffer.createNestedArray();
else
_root = _jsonBuffer.createNestedObject();
}
#else
AsyncJsonResponse(bool isArray = false) : _isValid{false} {
_code = 200;
_contentType = JSON_MIMETYPE;
if (isArray)
_root = _jsonBuffer.add<JsonArray>();
else
_root = _jsonBuffer.add<JsonObject>();
}
#endif
~AsyncJsonResponse() {}
JsonVariant& getRoot() { return _root; }
bool _sourceValid() const { return _isValid; }
size_t setLength() {
#if ARDUINOJSON_VERSION_MAJOR == 5
_contentLength = _root.measureLength();
#else
_contentLength = measureJson(_root);
#endif
if (_contentLength) {
_isValid = true;
}
return _contentLength;
}
size_t getSize() const { return _jsonBuffer.size(); }
#if ARDUINOJSON_VERSION_MAJOR >= 6
bool overflowed() const { return _jsonBuffer.overflowed(); }
#endif
size_t _fillBuffer(uint8_t* data, size_t len) {
ChunkPrint dest(data, _sentLength, len);
#if ARDUINOJSON_VERSION_MAJOR == 5
_root.printTo(dest);
#else
serializeJson(_root, dest);
#endif
return len;
}
};
class PrettyAsyncJsonResponse : public AsyncJsonResponse {
public:
#if ARDUINOJSON_VERSION_MAJOR == 6
PrettyAsyncJsonResponse(bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) : AsyncJsonResponse{isArray, maxJsonBufferSize} {}
#else
PrettyAsyncJsonResponse(bool isArray = false) : AsyncJsonResponse{isArray} {}
#endif
size_t setLength() {
#if ARDUINOJSON_VERSION_MAJOR == 5
_contentLength = _root.measurePrettyLength();
#else
_contentLength = measureJsonPretty(_root);
#endif
if (_contentLength) {
_isValid = true;
}
return _contentLength;
}
size_t _fillBuffer(uint8_t* data, size_t len) {
ChunkPrint dest(data, _sentLength, len);
#if ARDUINOJSON_VERSION_MAJOR == 5
_root.prettyPrintTo(dest);
#else
serializeJsonPretty(_root, dest);
#endif
return len;
}
};
typedef std::function<void(AsyncWebServerRequest* request, JsonVariant& json)> ArJsonRequestHandlerFunction;
class AsyncCallbackJsonWebHandler : public AsyncWebHandler {
private:
protected:
const String _uri;
WebRequestMethodComposite _method;
ArJsonRequestHandlerFunction _onRequest;
size_t _contentLength;
#if ARDUINOJSON_VERSION_MAJOR == 6
const size_t maxJsonBufferSize;
#endif
size_t _maxContentLength;
public:
#if ARDUINOJSON_VERSION_MAJOR == 6
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE)
: _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), maxJsonBufferSize(maxJsonBufferSize), _maxContentLength(16384) {}
#else
AsyncCallbackJsonWebHandler(const String& uri, ArJsonRequestHandlerFunction onRequest)
: _uri(uri), _method(HTTP_GET | HTTP_POST | HTTP_PUT | HTTP_PATCH), _onRequest(onRequest), _maxContentLength(16384) {}
#endif
void setMethod(WebRequestMethodComposite method) { _method = method; }
void setMaxContentLength(int maxContentLength) { _maxContentLength = maxContentLength; }
void onRequest(ArJsonRequestHandlerFunction fn) { _onRequest = fn; }
virtual bool canHandle(AsyncWebServerRequest* request) override final {
if (!_onRequest)
return false;
WebRequestMethodComposite request_method = request->method();
if (!(_method & request_method))
return false;
if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
return false;
if (request_method != HTTP_GET && !request->contentType().equalsIgnoreCase(JSON_MIMETYPE))
return false;
request->addInterestingHeader("ANY");
return true;
}
virtual void handleRequest(AsyncWebServerRequest* request) override final {
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
return request->requestAuthentication();
if (_onRequest) {
if (request->method() == HTTP_GET) {
JsonVariant json;
_onRequest(request, json);
return;
} else if (request->_tempObject != NULL) {
#if ARDUINOJSON_VERSION_MAJOR == 5
DynamicJsonBuffer jsonBuffer;
JsonVariant json = jsonBuffer.parse((uint8_t*)(request->_tempObject));
if (json.success()) {
#elif ARDUINOJSON_VERSION_MAJOR == 6
DynamicJsonDocument jsonBuffer(this->maxJsonBufferSize);
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
if (!error) {
JsonVariant json = jsonBuffer.as<JsonVariant>();
#else
JsonDocument jsonBuffer;
DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject));
if (!error) {
JsonVariant json = jsonBuffer.as<JsonVariant>();
#endif
_onRequest(request, json);
return;
}
}
request->send(_contentLength > _maxContentLength ? 413 : 400);
} else {
request->send(500);
}
}
virtual void handleUpload(__unused AsyncWebServerRequest* request, __unused const String& filename, __unused size_t index, __unused uint8_t* data, __unused size_t len, __unused bool final) override final {
}
virtual void handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) override final {
if (_onRequest) {
_contentLength = total;
if (total > 0 && request->_tempObject == NULL && total < _maxContentLength) {
request->_tempObject = malloc(total);
}
if (request->_tempObject != NULL) {
memcpy((uint8_t*)(request->_tempObject) + index, data, len);
}
}
}
virtual bool isRequestHandlerTrivial() override final { return _onRequest ? false : true; }
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,354 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef ASYNCWEBSOCKET_H_
#define ASYNCWEBSOCKET_H_
#include <Arduino.h>
#ifdef ESP32
#include <AsyncTCP.h>
#ifndef WS_MAX_QUEUED_MESSAGES
#define WS_MAX_QUEUED_MESSAGES 32
#endif
#else
#include <ESPAsyncTCP.h>
#ifndef WS_MAX_QUEUED_MESSAGES
#define WS_MAX_QUEUED_MESSAGES 8
#endif
#endif
#include <ESPAsyncWebServer.h>
#include "AsyncWebSynchronization.h"
#include <list>
#include <deque>
#include <memory>
#ifdef ESP8266
#include <Hash.h>
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
#include <../src/Hash.h>
#endif
#endif
#ifdef ESP32
#define DEFAULT_MAX_WS_CLIENTS 8
#else
#define DEFAULT_MAX_WS_CLIENTS 4
#endif
class AsyncWebSocket;
class AsyncWebSocketResponse;
class AsyncWebSocketClient;
class AsyncWebSocketControl;
typedef struct {
/** Message type as defined by enum AwsFrameType.
* Note: Applications will only see WS_TEXT and WS_BINARY.
* All other types are handled by the library. */
uint8_t message_opcode;
/** Frame number of a fragmented message. */
uint32_t num;
/** Is this the last frame in a fragmented message ?*/
uint8_t final;
/** Is this frame masked? */
uint8_t masked;
/** Message type as defined by enum AwsFrameType.
* This value is the same as message_opcode for non-fragmented
* messages, but may also be WS_CONTINUATION in a fragmented message. */
uint8_t opcode;
/** Length of the current frame.
* This equals the total length of the message if num == 0 && final == true */
uint64_t len;
/** Mask key */
uint8_t mask[4];
/** Offset of the data inside the current frame. */
uint64_t index;
} AwsFrameInfo;
typedef enum { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } AwsClientStatus;
typedef enum { WS_CONTINUATION, WS_TEXT, WS_BINARY, WS_DISCONNECT = 0x08, WS_PING, WS_PONG } AwsFrameType;
typedef enum { WS_MSG_SENDING, WS_MSG_SENT, WS_MSG_ERROR } AwsMessageStatus;
typedef enum { WS_EVT_CONNECT, WS_EVT_DISCONNECT, WS_EVT_PONG, WS_EVT_ERROR, WS_EVT_DATA } AwsEventType;
class AsyncWebSocketMessageBuffer {
friend AsyncWebSocket;
friend AsyncWebSocketClient;
private:
std::shared_ptr<std::vector<uint8_t>> _buffer;
public:
AsyncWebSocketMessageBuffer();
AsyncWebSocketMessageBuffer(size_t size);
AsyncWebSocketMessageBuffer(uint8_t* data, size_t size);
~AsyncWebSocketMessageBuffer();
bool reserve(size_t size);
uint8_t* get() { return _buffer->data(); }
size_t length() const { return _buffer->size(); }
};
class AsyncWebSocketMessage
{
private:
std::shared_ptr<std::vector<uint8_t>> _WSbuffer;
uint8_t _opcode{WS_TEXT};
bool _mask{false};
AwsMessageStatus _status{WS_MSG_ERROR};
size_t _sent{};
size_t _ack{};
size_t _acked{};
public:
AsyncWebSocketMessage(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false);
bool finished() const { return _status != WS_MSG_SENDING; }
bool betweenFrames() const { return _acked == _ack; }
void ack(size_t len, uint32_t time);
size_t send(AsyncClient *client);
};
class AsyncWebSocketClient {
private:
AsyncClient *_client;
AsyncWebSocket *_server;
uint32_t _clientId;
AwsClientStatus _status;
AsyncWebLock _lock;
std::deque<AsyncWebSocketControl> _controlQueue;
std::deque<AsyncWebSocketMessage> _messageQueue;
bool closeWhenFull = true;
uint8_t _pstate;
AwsFrameInfo _pinfo;
uint32_t _lastMessageTime;
uint32_t _keepAlivePeriod;
void _queueControl(uint8_t opcode, const uint8_t *data=NULL, size_t len=0, bool mask=false);
void _queueMessage(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false);
void _runQueue();
void _clearQueue();
public:
void *_tempObject;
AsyncWebSocketClient(AsyncWebServerRequest *request, AsyncWebSocket *server);
~AsyncWebSocketClient();
//client id increments for the given server
uint32_t id() const { return _clientId; }
AwsClientStatus status() const { return _status; }
AsyncClient* client() { return _client; }
const AsyncClient* client() const { return _client; }
AsyncWebSocket *server(){ return _server; }
const AsyncWebSocket *server() const { return _server; }
AwsFrameInfo const &pinfo() const { return _pinfo; }
// - If "true" (default), the connection will be closed if the message queue is full.
// This is the default behavior in yubox-node-org, which is not silently discarding messages but instead closes the connection.
// The big issue with this behavior is that is can cause the UI to automatically re-create a new WS connection, which can be filled again,
// and so on, causing a resource exhaustion.
//
// - If "false", the incoming message will be discarded if the queue is full.
// This is the default behavior in the original ESPAsyncWebServer library from me-no-dev.
// This behavior allows the best performance at the expense of unreliable message delivery in case the queue is full (some messages may be lost).
//
// - In any case, when the queue is full, a message is logged.
// - IT is recommended to use the methods queueIsFull(), availableForWriteAll(), availableForWrite(clientId) to check if the queue is full before sending a message.
//
// Usage:
// - can be set in the onEvent listener when connecting (event type is: WS_EVT_CONNECT)
//
// Use cases:,
// - if using websocket to send logging messages, maybe some loss is acceptable.
// - But if using websocket to send UI update messages, maybe the connection should be closed and the UI redrawn.
void setCloseClientOnQueueFull(bool close) { closeWhenFull = close; }
bool willCloseClientOnQueueFull() const { return closeWhenFull; }
IPAddress remoteIP() const;
uint16_t remotePort() const;
bool shouldBeDeleted() const { return !_client; }
//control frames
void close(uint16_t code=0, const char * message=NULL);
void ping(const uint8_t *data=NULL, size_t len=0);
//set auto-ping period in seconds. disabled if zero (default)
void keepAlivePeriod(uint16_t seconds){
_keepAlivePeriod = seconds * 1000;
}
uint16_t keepAlivePeriod(){
return (uint16_t)(_keepAlivePeriod / 1000);
}
//data packets
void message(std::shared_ptr<std::vector<uint8_t>> buffer, uint8_t opcode=WS_TEXT, bool mask=false) { _queueMessage(buffer, opcode, mask); }
bool queueIsFull() const;
size_t queueLen() const;
size_t printf(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
#ifndef ESP32
size_t printf_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
#endif
void text(std::shared_ptr<std::vector<uint8_t>> buffer);
void text(const uint8_t *message, size_t len);
void text(const char *message, size_t len);
void text(const char *message);
void text(const String &message);
void text(const __FlashStringHelper *message);
void text(AsyncWebSocketMessageBuffer *buffer);
void binary(std::shared_ptr<std::vector<uint8_t>> buffer);
void binary(const uint8_t *message, size_t len);
void binary(const char * message, size_t len);
void binary(const char * message);
void binary(const String &message);
void binary(const __FlashStringHelper *message, size_t len);
void binary(AsyncWebSocketMessageBuffer *buffer);
bool canSend() const;
//system callbacks (do not call)
void _onAck(size_t len, uint32_t time);
void _onError(int8_t);
void _onPoll();
void _onTimeout(uint32_t time);
void _onDisconnect();
void _onData(void *pbuf, size_t plen);
};
typedef std::function<bool(AsyncWebServerRequest *request)> AwsHandshakeHandler;
typedef std::function<void(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)> AwsEventHandler;
//WebServer Handler implementation that plays the role of a socket server
class AsyncWebSocket: public AsyncWebHandler {
private:
String _url;
std::list<AsyncWebSocketClient> _clients;
uint32_t _cNextId;
AwsEventHandler _eventHandler;
AwsHandshakeHandler _handshakeHandler;
bool _enabled;
AsyncWebLock _lock;
public:
AsyncWebSocket(const String& url);
~AsyncWebSocket();
const char * url() const { return _url.c_str(); }
void enable(bool e){ _enabled = e; }
bool enabled() const { return _enabled; }
bool availableForWriteAll();
bool availableForWrite(uint32_t id);
size_t count() const;
AsyncWebSocketClient * client(uint32_t id);
bool hasClient(uint32_t id){ return client(id) != NULL; }
void close(uint32_t id, uint16_t code=0, const char * message=NULL);
void closeAll(uint16_t code=0, const char * message=NULL);
void cleanupClients(uint16_t maxClients = DEFAULT_MAX_WS_CLIENTS);
void ping(uint32_t id, const uint8_t *data=NULL, size_t len=0);
void pingAll(const uint8_t *data=NULL, size_t len=0); // done
void text(uint32_t id, const uint8_t * message, size_t len);
void text(uint32_t id, const char *message, size_t len);
void text(uint32_t id, const char *message);
void text(uint32_t id, const String &message);
void text(uint32_t id, const __FlashStringHelper *message);
void text(uint32_t id, AsyncWebSocketMessageBuffer *buffer);
void text(uint32_t id, std::shared_ptr<std::vector<uint8_t>> buffer);
void textAll(const uint8_t *message, size_t len);
void textAll(const char * message, size_t len);
void textAll(const char * message);
void textAll(const String &message);
void textAll(const __FlashStringHelper *message);
void textAll(AsyncWebSocketMessageBuffer *buffer);
void textAll(std::shared_ptr<std::vector<uint8_t>> buffer);
void binary(uint32_t id, const uint8_t *message, size_t len);
void binary(uint32_t id, const char *message, size_t len);
void binary(uint32_t id, const char *message);
void binary(uint32_t id, const String &message);
void binary(uint32_t id, const __FlashStringHelper *message, size_t len);
void binary(uint32_t id, AsyncWebSocketMessageBuffer *buffer);
void binary(uint32_t id, std::shared_ptr<std::vector<uint8_t>> buffer);
void binaryAll(const uint8_t *message, size_t len);
void binaryAll(const char *message, size_t len);
void binaryAll(const char *message);
void binaryAll(const String &message);
void binaryAll(const __FlashStringHelper *message, size_t len);
void binaryAll(AsyncWebSocketMessageBuffer *buffer);
void binaryAll(std::shared_ptr<std::vector<uint8_t>> buffer);
size_t printf(uint32_t id, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
size_t printfAll(const char *format, ...) __attribute__ ((format (printf, 2, 3)));
#ifndef ESP32
size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__ ((format (printf, 3, 4)));
#endif
size_t printfAll_P(PGM_P formatP, ...) __attribute__ ((format (printf, 2, 3)));
//event listener
void onEvent(AwsEventHandler handler){
_eventHandler = handler;
}
// Handshake Handler
void handleHandshake(AwsHandshakeHandler handler){
_handshakeHandler = handler;
}
//system callbacks (do not call)
uint32_t _getNextId(){ return _cNextId++; }
AsyncWebSocketClient *_newClient(AsyncWebServerRequest *request);
void _handleEvent(AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
virtual bool canHandle(AsyncWebServerRequest *request) override final;
virtual void handleRequest(AsyncWebServerRequest *request) override final;
// messagebuffer functions/objects.
AsyncWebSocketMessageBuffer * makeBuffer(size_t size = 0);
AsyncWebSocketMessageBuffer * makeBuffer(uint8_t * data, size_t size);
const std::list<AsyncWebSocketClient> &getClients() const { return _clients; }
};
//WebServer response to authenticate the socket and detach the tcp client from the web server request
class AsyncWebSocketResponse: public AsyncWebServerResponse {
private:
String _content;
AsyncWebSocket *_server;
public:
AsyncWebSocketResponse(const String& key, AsyncWebSocket *server);
void _respond(AsyncWebServerRequest *request);
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
bool _sourceValid() const { return true; }
};
#endif /* ASYNCWEBSOCKET_H_ */

View File

@@ -0,0 +1,134 @@
#ifndef ASYNCWEBSYNCHRONIZATION_H_
#define ASYNCWEBSYNCHRONIZATION_H_
// Synchronisation is only available on ESP32, as the ESP8266 isn't using FreeRTOS by default
#include <ESPAsyncWebServer.h>
#ifdef ESP32
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
// Modified 'AsyncWebLock' to just only use mutex since pxCurrentTCB is not
// always available. According to example by Arjan Filius, changed name,
// added unimplemented version for ESP8266
class AsyncPlainLock
{
private:
SemaphoreHandle_t _lock;
public:
AsyncPlainLock() {
_lock = xSemaphoreCreateBinary();
// In this fails, the system is likely that much out of memory that
// we should abort anyways. If assertions are disabled, nothing is lost..
assert(_lock);
xSemaphoreGive(_lock);
}
~AsyncPlainLock() {
vSemaphoreDelete(_lock);
}
bool lock() const {
xSemaphoreTake(_lock, portMAX_DELAY);
return true;
}
void unlock() const {
xSemaphoreGive(_lock);
}
};
// This is the ESP32 version of the Sync Lock, using the FreeRTOS Semaphore
class AsyncWebLock
{
private:
SemaphoreHandle_t _lock;
mutable TaskHandle_t _lockedBy{};
public:
AsyncWebLock()
{
_lock = xSemaphoreCreateBinary();
// In this fails, the system is likely that much out of memory that
// we should abort anyways. If assertions are disabled, nothing is lost..
assert(_lock);
_lockedBy = NULL;
xSemaphoreGive(_lock);
}
~AsyncWebLock() {
vSemaphoreDelete(_lock);
}
bool lock() const {
const auto currentTask = xTaskGetCurrentTaskHandle();
if (_lockedBy != currentTask) {
xSemaphoreTake(_lock, portMAX_DELAY);
_lockedBy = currentTask;
return true;
}
return false;
}
void unlock() const {
_lockedBy = NULL;
xSemaphoreGive(_lock);
}
};
#else
// This is the 8266 version of the Sync Lock which is currently unimplemented
class AsyncWebLock
{
public:
AsyncWebLock() {
}
~AsyncWebLock() {
}
bool lock() const {
return false;
}
void unlock() const {
}
};
// Same for AsyncPlainLock, for ESP8266 this is just the unimplemented version above.
using AsyncPlainLock = AsyncWebLock;
#endif
class AsyncWebLockGuard
{
private:
const AsyncWebLock *_lock;
public:
AsyncWebLockGuard(const AsyncWebLock &l) {
if (l.lock()) {
_lock = &l;
} else {
_lock = NULL;
}
}
~AsyncWebLockGuard() {
if (_lock) {
_lock->unlock();
}
}
void unlock() {
if (_lock) {
_lock->unlock();
_lock = NULL;
}
}
};
#endif // ASYNCWEBSYNCHRONIZATION_H_

View File

@@ -0,0 +1,501 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _ESPAsyncWebServer_H_
#define _ESPAsyncWebServer_H_
#include "Arduino.h"
#include <functional>
#include <list>
#include <vector>
#include "FS.h"
#include "StringArray.h"
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#else
#error Platform not supported
#endif
#define ASYNCWEBSERVER_VERSION "2.10.4"
#define ASYNCWEBSERVER_VERSION_MAJOR 2
#define ASYNCWEBSERVER_VERSION_MINOR 10
#define ASYNCWEBSERVER_VERSION_REVISION 4
#define ASYNCWEBSERVER_FORK_mathieucarbou
#ifdef ASYNCWEBSERVER_REGEX
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE
#else
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined")))
#endif
class AsyncWebServer;
class AsyncWebServerRequest;
class AsyncWebServerResponse;
class AsyncWebHeader;
class AsyncWebParameter;
class AsyncWebRewrite;
class AsyncWebHandler;
class AsyncStaticWebHandler;
class AsyncCallbackWebHandler;
class AsyncResponseStream;
#ifndef WEBSERVER_H
typedef enum {
HTTP_GET = 0b00000001,
HTTP_POST = 0b00000010,
HTTP_DELETE = 0b00000100,
HTTP_PUT = 0b00001000,
HTTP_PATCH = 0b00010000,
HTTP_HEAD = 0b00100000,
HTTP_OPTIONS = 0b01000000,
HTTP_ANY = 0b01111111,
} WebRequestMethod;
#endif
#ifndef HAVE_FS_FILE_OPEN_MODE
namespace fs {
class FileOpenMode {
public:
static const char *read;
static const char *write;
static const char *append;
};
};
#else
#include "FileOpenMode.h"
#endif
//if this value is returned when asked for data, packet will not be sent and you will be asked for data again
#define RESPONSE_TRY_AGAIN 0xFFFFFFFF
typedef uint8_t WebRequestMethodComposite;
typedef std::function<void(void)> ArDisconnectHandler;
/*
* PARAMETER :: Chainable object to hold GET/POST and FILE parameters
* */
class AsyncWebParameter {
private:
String _name;
String _value;
size_t _size;
bool _isForm;
bool _isFile;
public:
AsyncWebParameter(const String& name, const String& value, bool form=false, bool file=false, size_t size=0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file){}
const String& name() const { return _name; }
const String& value() const { return _value; }
size_t size() const { return _size; }
bool isPost() const { return _isForm; }
bool isFile() const { return _isFile; }
};
/*
* HEADER :: Chainable object to hold the headers
* */
class AsyncWebHeader {
private:
String _name;
String _value;
public:
AsyncWebHeader() = default;
AsyncWebHeader(const AsyncWebHeader &) = default;
AsyncWebHeader(const String& name, const String& value): _name(name), _value(value){}
AsyncWebHeader(const String& data): _name(), _value(){
if(!data) return;
int index = data.indexOf(':');
if (index < 0) return;
_name = data.substring(0, index);
_value = data.substring(index + 2);
}
AsyncWebHeader &operator=(const AsyncWebHeader &) = default;
const String& name() const { return _name; }
const String& value() const { return _value; }
String toString() const { return _name + F(": ") + _value + F("\r\n"); }
};
/*
* REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect
* */
typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, RCT_MAX } RequestedConnectionType;
typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller;
typedef std::function<String(const String&)> AwsTemplateProcessor;
class AsyncWebServerRequest {
using File = fs::File;
using FS = fs::FS;
friend class AsyncWebServer;
friend class AsyncCallbackWebHandler;
private:
AsyncClient* _client;
AsyncWebServer* _server;
AsyncWebHandler* _handler;
AsyncWebServerResponse* _response;
std::vector<String> _interestingHeaders;
ArDisconnectHandler _onDisconnectfn;
String _temp;
uint8_t _parseState;
uint8_t _version;
WebRequestMethodComposite _method;
String _url;
String _host;
String _contentType;
String _boundary;
String _authorization;
RequestedConnectionType _reqconntype;
void _removeNotInterestingHeaders();
bool _isDigest;
bool _isMultipart;
bool _isPlainPost;
bool _expectingContinue;
size_t _contentLength;
size_t _parsedLength;
std::list<AsyncWebHeader> _headers;
AlternativeLinkedList<AsyncWebParameter *> _params;
std::vector<String> _pathParams;
uint8_t _multiParseState;
uint8_t _boundaryPosition;
size_t _itemStartIndex;
size_t _itemSize;
String _itemName;
String _itemFilename;
String _itemType;
String _itemValue;
uint8_t *_itemBuffer;
size_t _itemBufferIndex;
bool _itemIsFile;
void _onPoll();
void _onAck(size_t len, uint32_t time);
void _onError(int8_t error);
void _onTimeout(uint32_t time);
void _onDisconnect();
void _onData(void *buf, size_t len);
void _addParam(AsyncWebParameter*);
void _addPathParam(const char *param);
bool _parseReqHead();
bool _parseReqHeader();
void _parseLine();
void _parsePlainPostChar(uint8_t data);
void _parseMultipartPostByte(uint8_t data, bool last);
void _addGetParams(const String& params);
void _handleUploadStart();
void _handleUploadByte(uint8_t data, bool last);
void _handleUploadEnd();
public:
File _tempFile;
void *_tempObject;
AsyncWebServerRequest(AsyncWebServer*, AsyncClient*);
~AsyncWebServerRequest();
AsyncClient* client(){ return _client; }
uint8_t version() const { return _version; }
WebRequestMethodComposite method() const { return _method; }
const String& url() const { return _url; }
const String& host() const { return _host; }
const String& contentType() const { return _contentType; }
size_t contentLength() const { return _contentLength; }
bool multipart() const { return _isMultipart; }
const __FlashStringHelper *methodToString() const;
const __FlashStringHelper *requestedConnTypeToString() const;
RequestedConnectionType requestedConnType() const { return _reqconntype; }
bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED);
void onDisconnect (ArDisconnectHandler fn);
//hash is the string representation of:
// base64(user:pass) for basic or
// user:realm:md5(user:realm:pass) for digest
bool authenticate(const char * hash);
bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false);
void requestAuthentication(const char * realm = NULL, bool isDigest = true);
void setHandler(AsyncWebHandler *handler){ _handler = handler; }
void addInterestingHeader(const String& name);
void redirect(const String& url);
void send(AsyncWebServerResponse *response);
void send(int code, const String& contentType=String(), const String& content=String());
void send(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
void send(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
AsyncWebServerResponse *beginResponse(int code, const String& contentType=String(), const String& content=String());
AsyncWebServerResponse *beginResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
AsyncWebServerResponse *beginResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
AsyncWebServerResponse *beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
AsyncResponseStream *beginResponseStream(const String& contentType, size_t bufferSize=1460);
AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
AsyncWebServerResponse *beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
size_t headers() const; // get header count
bool hasHeader(const String& name) const; // check if header exists
bool hasHeader(const __FlashStringHelper * data) const; // check if header exists
AsyncWebHeader* getHeader(const String& name);
const AsyncWebHeader* getHeader(const String& name) const;
AsyncWebHeader* getHeader(const __FlashStringHelper * data);
const AsyncWebHeader* getHeader(const __FlashStringHelper * data) const;
AsyncWebHeader* getHeader(size_t num);
const AsyncWebHeader* getHeader(size_t num) const;
size_t params() const; // get arguments count
bool hasParam(const String& name, bool post=false, bool file=false) const;
bool hasParam(const __FlashStringHelper * data, bool post=false, bool file=false) const;
AsyncWebParameter* getParam(const String& name, bool post=false, bool file=false) const;
AsyncWebParameter* getParam(const __FlashStringHelper * data, bool post, bool file) const;
AsyncWebParameter* getParam(size_t num) const;
size_t args() const { return params(); } // get arguments count
const String& arg(const String& name) const; // get request argument value by name
const String& arg(const __FlashStringHelper * data) const; // get request argument value by F(name)
const String& arg(size_t i) const; // get request argument value by number
const String& argName(size_t i) const; // get request argument name by number
bool hasArg(const char* name) const; // check if argument exists
bool hasArg(const __FlashStringHelper * data) const; // check if F(argument) exists
const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const;
const String& header(const char* name) const;// get request header value by name
const String& header(const __FlashStringHelper * data) const;// get request header value by F(name)
const String& header(size_t i) const; // get request header value by number
const String& headerName(size_t i) const; // get request header name by number
String urlDecode(const String& text) const;
};
/*
* FILTER :: Callback to filter AsyncWebRewrite and AsyncWebHandler (done by the Server)
* */
typedef std::function<bool(AsyncWebServerRequest *request)> ArRequestFilterFunction;
bool ON_STA_FILTER(AsyncWebServerRequest *request);
bool ON_AP_FILTER(AsyncWebServerRequest *request);
/*
* REWRITE :: One instance can be handle any Request (done by the Server)
* */
class AsyncWebRewrite {
protected:
String _from;
String _toUrl;
String _params;
ArRequestFilterFunction _filter;
public:
AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL){
int index = _toUrl.indexOf('?');
if (index > 0) {
_params = _toUrl.substring(index +1);
_toUrl = _toUrl.substring(0, index);
}
}
virtual ~AsyncWebRewrite(){}
AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); }
const String& from(void) const { return _from; }
const String& toUrl(void) const { return _toUrl; }
const String& params(void) const { return _params; }
virtual bool match(AsyncWebServerRequest *request) { return from() == request->url() && filter(request); }
};
/*
* HANDLER :: One instance can be attached to any Request (done by the Server)
* */
class AsyncWebHandler {
protected:
ArRequestFilterFunction _filter;
String _username;
String _password;
public:
AsyncWebHandler():_username(""), _password(""){}
AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; }
AsyncWebHandler& setAuthentication(const char *username, const char *password){ _username = String(username);_password = String(password); return *this; };
AsyncWebHandler& setAuthentication(const String& username, const String& password){ _username = username;_password = password; return *this; };
bool filter(AsyncWebServerRequest *request){ return _filter == NULL || _filter(request); }
virtual ~AsyncWebHandler(){}
virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))){
return false;
}
virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))){}
virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))){}
virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))){}
virtual bool isRequestHandlerTrivial(){return true;}
};
/*
* RESPONSE :: One instance is created for each Request (attached by the Handler)
* */
typedef enum {
RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED
} WebResponseState;
class AsyncWebServerResponse {
protected:
int _code;
std::list<AsyncWebHeader> _headers;
String _contentType;
size_t _contentLength;
bool _sendContentLength;
bool _chunked;
size_t _headLength;
size_t _sentLength;
size_t _ackedLength;
size_t _writtenLength;
WebResponseState _state;
const char* _responseCodeToString(int code);
public:
static const __FlashStringHelper *responseCodeToString(int code);
public:
AsyncWebServerResponse();
virtual ~AsyncWebServerResponse();
virtual void setCode(int code);
virtual void setContentLength(size_t len);
virtual void setContentType(const String& type);
virtual void addHeader(const String& name, const String& value);
virtual String _assembleHead(uint8_t version);
virtual bool _started() const;
virtual bool _finished() const;
virtual bool _failed() const;
virtual bool _sourceValid() const;
virtual void _respond(AsyncWebServerRequest *request);
virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
};
/*
* SERVER :: One instance
* */
typedef std::function<void(AsyncWebServerRequest *request)> ArRequestHandlerFunction;
typedef std::function<void(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final)> ArUploadHandlerFunction;
typedef std::function<void(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction;
class AsyncWebServer {
protected:
AsyncServer _server;
AlternativeLinkedList<AsyncWebRewrite*> _rewrites;
AlternativeLinkedList<AsyncWebHandler*> _handlers;
AsyncCallbackWebHandler* _catchAllHandler;
public:
AsyncWebServer(uint16_t port);
~AsyncWebServer();
void begin();
void end();
#if ASYNC_TCP_SSL_ENABLED
void onSslFileRequest(AcSSlFileHandler cb, void* arg);
void beginSecure(const char *cert, const char *private_key_file, const char *password);
#endif
AsyncWebRewrite& addRewrite(AsyncWebRewrite* rewrite);
bool removeRewrite(AsyncWebRewrite* rewrite);
AsyncWebRewrite& rewrite(const char* from, const char* to);
AsyncWebHandler& addHandler(AsyncWebHandler* handler);
bool removeHandler(AsyncWebHandler* handler);
AsyncCallbackWebHandler& on(const char* uri, ArRequestHandlerFunction onRequest);
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest);
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload);
AsyncCallbackWebHandler& on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody);
AsyncStaticWebHandler& serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control = NULL);
void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned
void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads
void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request)
void reset(); //remove all writers and handlers, with onNotFound/onFileUpload/onRequestBody
void _handleDisconnect(AsyncWebServerRequest *request);
void _attachHandler(AsyncWebServerRequest *request);
void _rewriteRequest(AsyncWebServerRequest *request);
};
class DefaultHeaders {
using headers_t = std::list<AsyncWebHeader>;
headers_t _headers;
public:
DefaultHeaders() = default;
using ConstIterator = headers_t::const_iterator;
void addHeader(const String& name, const String& value){
_headers.emplace_back(name, value);
}
ConstIterator begin() const { return _headers.begin(); }
ConstIterator end() const { return _headers.end(); }
DefaultHeaders(DefaultHeaders const &) = delete;
DefaultHeaders &operator=(DefaultHeaders const &) = delete;
static DefaultHeaders &Instance() {
static DefaultHeaders instance;
return instance;
}
};
#include "WebResponseImpl.h"
#include "WebHandlerImpl.h"
#include "AsyncWebSocket.h"
#include "AsyncEventSource.h"
#endif /* _AsyncWebServer_H_ */

View File

@@ -0,0 +1,2 @@
// to please Arduino Lint
#include "ESPAsyncWebServer.h"

View File

@@ -0,0 +1,174 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef STRINGARRAY_H_
#define STRINGARRAY_H_
#include "stddef.h"
#include "WString.h"
template <typename T>
class AlternativeLinkedListNode {
T _value;
public:
AlternativeLinkedListNode<T>* next;
AlternativeLinkedListNode(const T val): _value(val), next(nullptr) {}
~AlternativeLinkedListNode(){}
const T& value() const { return _value; };
T& value(){ return _value; }
};
template <typename T, template<typename> class Item = AlternativeLinkedListNode>
class AlternativeLinkedList {
public:
typedef Item<T> ItemType;
typedef std::function<void(const T&)> OnRemove;
typedef std::function<bool(const T&)> Predicate;
private:
ItemType* _root;
OnRemove _onRemove;
class Iterator {
ItemType* _node;
public:
Iterator(ItemType* current = nullptr) : _node(current) {}
Iterator(const Iterator& i) : _node(i._node) {}
Iterator& operator ++() { _node = _node->next; return *this; }
bool operator != (const Iterator& i) const { return _node != i._node; }
const T& operator * () const { return _node->value(); }
const T* operator -> () const { return &_node->value(); }
};
public:
typedef const Iterator ConstIterator;
ConstIterator begin() const { return ConstIterator(_root); }
ConstIterator end() const { return ConstIterator(nullptr); }
AlternativeLinkedList(OnRemove onRemove) : _root(nullptr), _onRemove(onRemove) {}
~AlternativeLinkedList(){}
void add(const T& t){
auto it = new ItemType(t);
if(!_root){
_root = it;
} else {
auto i = _root;
while(i->next) i = i->next;
i->next = it;
}
}
T& front() const {
return _root->value();
}
bool isEmpty() const {
return _root == nullptr;
}
size_t length() const {
size_t i = 0;
auto it = _root;
while(it){
i++;
it = it->next;
}
return i;
}
size_t count_if(Predicate predicate) const {
size_t i = 0;
auto it = _root;
while(it){
if (!predicate){
i++;
}
else if (predicate(it->value())) {
i++;
}
it = it->next;
}
return i;
}
const T* nth(size_t N) const {
size_t i = 0;
auto it = _root;
while(it){
if(i++ == N)
return &(it->value());
it = it->next;
}
return nullptr;
}
bool remove(const T& t){
auto it = _root;
auto pit = _root;
while(it){
if(it->value() == t){
if(it == _root){
_root = _root->next;
} else {
pit->next = it->next;
}
if (_onRemove) {
_onRemove(it->value());
}
delete it;
return true;
}
pit = it;
it = it->next;
}
return false;
}
bool remove_first(Predicate predicate){
auto it = _root;
auto pit = _root;
while(it){
if(predicate(it->value())){
if(it == _root){
_root = _root->next;
} else {
pit->next = it->next;
}
if (_onRemove) {
_onRemove(it->value());
}
delete it;
return true;
}
pit = it;
it = it->next;
}
return false;
}
void free(){
while(_root != nullptr){
auto it = _root;
_root = _root->next;
if (_onRemove) {
_onRemove(it->value());
}
delete it;
}
_root = nullptr;
}
};
#endif /* STRINGARRAY_H_ */

View File

@@ -0,0 +1,247 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "WebAuthentication.h"
#include <libb64/cencode.h>
#ifdef ESP32
#include "mbedtls/md5.h"
#else
#include "md5.h"
#endif
// Basic Auth hash = base64("username:password")
bool checkBasicAuthentication(const char * hash, const char * username, const char * password){
if(username == NULL || password == NULL || hash == NULL)
return false;
size_t toencodeLen = strlen(username)+strlen(password)+1;
size_t encodedLen = base64_encode_expected_len(toencodeLen);
if(strlen(hash) != encodedLen)
// Fix from https://github.com/me-no-dev/ESPAsyncWebServer/issues/667
#ifdef ARDUINO_ARCH_ESP32
if(strlen(hash) != encodedLen)
#else
if (strlen(hash) != encodedLen - 1)
#endif
return false;
char *toencode = new char[toencodeLen+1];
if(toencode == NULL){
return false;
}
char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
if(encoded == NULL){
delete[] toencode;
return false;
}
sprintf_P(toencode, PSTR("%s:%s"), username, password);
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && memcmp(hash, encoded, encodedLen) == 0){
delete[] toencode;
delete[] encoded;
return true;
}
delete[] toencode;
delete[] encoded;
return false;
}
static bool getMD5(uint8_t * data, uint16_t len, char * output){//33 bytes or more
#ifdef ESP32
mbedtls_md5_context _ctx;
#else
md5_context_t _ctx;
#endif
uint8_t i;
uint8_t * _buf = (uint8_t*)malloc(16);
if(_buf == NULL)
return false;
memset(_buf, 0x00, 16);
#ifdef ESP32
mbedtls_md5_init(&_ctx);
#if ESP_IDF_VERSION_MAJOR == 5
mbedtls_md5_starts(&_ctx);
mbedtls_md5_update(&_ctx, data, len);
mbedtls_md5_finish(&_ctx, _buf);
#else
mbedtls_md5_starts_ret(&_ctx);
mbedtls_md5_update_ret(&_ctx, data, len);
mbedtls_md5_finish_ret(&_ctx, _buf);
#endif
#else
MD5Init(&_ctx);
MD5Update(&_ctx, data, len);
MD5Final(_buf, &_ctx);
#endif
for(i = 0; i < 16; i++) {
sprintf_P(output + (i * 2), PSTR("%02x"), _buf[i]);
}
free(_buf);
return true;
}
static String genRandomMD5(){
#ifdef ESP8266
uint32_t r = RANDOM_REG32;
#else
uint32_t r = rand();
#endif
char * out = (char*)malloc(33);
if(out == NULL || !getMD5((uint8_t*)(&r), 4, out))
return emptyString;
String res = String(out);
free(out);
return res;
}
static String stringMD5(const String& in){
char * out = (char*)malloc(33);
if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
return emptyString;
String res = String(out);
free(out);
return res;
}
String generateDigestHash(const char * username, const char * password, const char * realm){
if(username == NULL || password == NULL || realm == NULL){
return emptyString;
}
char * out = (char*)malloc(33);
String res = String(username);
res += ':';
res.concat(realm);
res += ':';
String in = res;
in.concat(password);
if(out == NULL || !getMD5((uint8_t*)(in.c_str()), in.length(), out))
return emptyString;
res.concat(out);
free(out);
return res;
}
String requestDigestAuthentication(const char * realm){
String header = F("realm=\"");
if(realm == NULL)
header.concat(F("asyncesp"));
else
header.concat(realm);
header.concat(F("\", qop=\"auth\", nonce=\""));
header.concat(genRandomMD5());
header.concat(F("\", opaque=\""));
header.concat(genRandomMD5());
header += '"';
return header;
}
bool checkDigestAuthentication(const char * header, const __FlashStringHelper *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri){
if(username == NULL || password == NULL || header == NULL || method == NULL){
//os_printf("AUTH FAIL: missing requred fields\n");
return false;
}
String myHeader = String(header);
int nextBreak = myHeader.indexOf(',');
if(nextBreak < 0){
//os_printf("AUTH FAIL: no variables\n");
return false;
}
String myUsername = String();
String myRealm = String();
String myNonce = String();
String myUri = String();
String myResponse = String();
String myQop = String();
String myNc = String();
String myCnonce = String();
myHeader += F(", ");
do {
String avLine = myHeader.substring(0, nextBreak);
avLine.trim();
myHeader = myHeader.substring(nextBreak+1);
nextBreak = myHeader.indexOf(',');
int eqSign = avLine.indexOf('=');
if(eqSign < 0){
//os_printf("AUTH FAIL: no = sign\n");
return false;
}
String varName = avLine.substring(0, eqSign);
avLine = avLine.substring(eqSign + 1);
if(avLine.startsWith(String('"'))){
avLine = avLine.substring(1, avLine.length() - 1);
}
if(varName.equals(F("username"))){
if(!avLine.equals(username)){
//os_printf("AUTH FAIL: username\n");
return false;
}
myUsername = avLine;
} else if(varName.equals(F("realm"))){
if(realm != NULL && !avLine.equals(realm)){
//os_printf("AUTH FAIL: realm\n");
return false;
}
myRealm = avLine;
} else if(varName.equals(F("nonce"))){
if(nonce != NULL && !avLine.equals(nonce)){
//os_printf("AUTH FAIL: nonce\n");
return false;
}
myNonce = avLine;
} else if(varName.equals(F("opaque"))){
if(opaque != NULL && !avLine.equals(opaque)){
//os_printf("AUTH FAIL: opaque\n");
return false;
}
} else if(varName.equals(F("uri"))){
if(uri != NULL && !avLine.equals(uri)){
//os_printf("AUTH FAIL: uri\n");
return false;
}
myUri = avLine;
} else if(varName.equals(F("response"))){
myResponse = avLine;
} else if(varName.equals(F("qop"))){
myQop = avLine;
} else if(varName.equals(F("nc"))){
myNc = avLine;
} else if(varName.equals(F("cnonce"))){
myCnonce = avLine;
}
} while(nextBreak > 0);
String ha1 = (passwordIsHash) ? String(password) : stringMD5(myUsername + ':' + myRealm + ':' + String(password));
String ha2 = String(method) + ':' + myUri;
String response = ha1 + ':' + myNonce + ':' + myNc + ':' + myCnonce + ':' + myQop + ':' + stringMD5(ha2);
if(myResponse.equals(stringMD5(response))){
//os_printf("AUTH SUCCESS\n");
return true;
}
//os_printf("AUTH FAIL: password\n");
return false;
}

View File

@@ -0,0 +1,34 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef WEB_AUTHENTICATION_H_
#define WEB_AUTHENTICATION_H_
#include "Arduino.h"
bool checkBasicAuthentication(const char * header, const char * username, const char * password);
String requestDigestAuthentication(const char * realm);
bool checkDigestAuthentication(const char * header, const __FlashStringHelper *method, const char * username, const char * password, const char * realm, bool passwordIsHash, const char * nonce, const char * opaque, const char * uri);
//for storing hashed versions on the device that can be authenticated against
String generateDigestHash(const char * username, const char * password, const char * realm);
#endif

View File

@@ -0,0 +1,151 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef ASYNCWEBSERVERHANDLERIMPL_H_
#define ASYNCWEBSERVERHANDLERIMPL_H_
#include <string>
#ifdef ASYNCWEBSERVER_REGEX
#include <regex>
#endif
#include "stddef.h"
#include <time.h>
class AsyncStaticWebHandler: public AsyncWebHandler {
using File = fs::File;
using FS = fs::FS;
private:
bool _getFile(AsyncWebServerRequest *request);
bool _fileExists(AsyncWebServerRequest *request, const String& path);
uint8_t _countBits(const uint8_t value) const;
protected:
FS _fs;
String _uri;
String _path;
String _default_file;
String _cache_control;
String _last_modified;
AwsTemplateProcessor _callback;
bool _isDir;
bool _gzipFirst;
uint8_t _gzipStats;
public:
AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control);
virtual bool canHandle(AsyncWebServerRequest *request) override final;
virtual void handleRequest(AsyncWebServerRequest *request) override final;
AsyncStaticWebHandler& setIsDir(bool isDir);
AsyncStaticWebHandler& setDefaultFile(const char* filename);
AsyncStaticWebHandler& setCacheControl(const char* cache_control);
AsyncStaticWebHandler& setLastModified(const char* last_modified);
AsyncStaticWebHandler& setLastModified(struct tm* last_modified);
#ifdef ESP8266
AsyncStaticWebHandler& setLastModified(time_t last_modified);
AsyncStaticWebHandler& setLastModified(); //sets to current time. Make sure sntp is runing and time is updated
#endif
AsyncStaticWebHandler& setTemplateProcessor(AwsTemplateProcessor newCallback) {_callback = newCallback; return *this;}
};
class AsyncCallbackWebHandler: public AsyncWebHandler {
private:
protected:
String _uri;
WebRequestMethodComposite _method;
ArRequestHandlerFunction _onRequest;
ArUploadHandlerFunction _onUpload;
ArBodyHandlerFunction _onBody;
bool _isRegex;
public:
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL), _isRegex(false) {}
void setUri(const String& uri){
_uri = uri;
_isRegex = uri.startsWith("^") && uri.endsWith("$");
}
void setMethod(WebRequestMethodComposite method){ _method = method; }
void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; }
void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; }
void onBody(ArBodyHandlerFunction fn){ _onBody = fn; }
virtual bool canHandle(AsyncWebServerRequest *request) override final{
if(!_onRequest)
return false;
if(!(_method & request->method()))
return false;
#ifdef ASYNCWEBSERVER_REGEX
if (_isRegex) {
std::regex pattern(_uri.c_str());
std::smatch matches;
std::string s(request->url().c_str());
if(std::regex_search(s, matches, pattern)) {
for (size_t i = 1; i < matches.size(); ++i) { // start from 1
request->_addPathParam(matches[i].str().c_str());
}
} else {
return false;
}
} else
#endif
if (_uri.length() && _uri.startsWith("/*.")) {
String uriTemplate = String (_uri);
uriTemplate = uriTemplate.substring(uriTemplate.lastIndexOf("."));
if (!request->url().endsWith(uriTemplate))
return false;
}
else
if (_uri.length() && _uri.endsWith("*")) {
String uriTemplate = String(_uri);
uriTemplate = uriTemplate.substring(0, uriTemplate.length() - 1);
if (!request->url().startsWith(uriTemplate))
return false;
}
else if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
return false;
request->addInterestingHeader("ANY");
return true;
}
virtual void handleRequest(AsyncWebServerRequest *request) override final {
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
return request->requestAuthentication();
if(_onRequest)
_onRequest(request);
else
request->send(500);
}
virtual void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) override final {
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
return request->requestAuthentication();
if(_onUpload)
_onUpload(request, filename, index, data, len, final);
}
virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final {
if((_username != "" && _password != "") && !request->authenticate(_username.c_str(), _password.c_str()))
return request->requestAuthentication();
if(_onBody)
_onBody(request, data, len, index, total);
}
virtual bool isRequestHandlerTrivial() override final {return _onRequest ? false : true;}
};
#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */

View File

@@ -0,0 +1,233 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ESPAsyncWebServer.h"
#include "WebHandlerImpl.h"
AsyncStaticWebHandler::AsyncStaticWebHandler(const char* uri, FS& fs, const char* path, const char* cache_control)
: _fs(fs), _uri(uri), _path(path), _default_file(F("index.htm")), _cache_control(cache_control), _last_modified(), _callback(nullptr)
{
// Ensure leading '/'
if (_uri.length() == 0 || _uri[0] != '/') _uri = String('/') + _uri;
if (_path.length() == 0 || _path[0] != '/') _path = String('/') + _path;
// If path ends with '/' we assume a hint that this is a directory to improve performance.
// However - if it does not end with '/' we, can't assume a file, path can still be a directory.
_isDir = _path[_path.length()-1] == '/';
// Remove the trailing '/' so we can handle default file
// Notice that root will be "" not "/"
if (_uri[_uri.length()-1] == '/') _uri = _uri.substring(0, _uri.length()-1);
if (_path[_path.length()-1] == '/') _path = _path.substring(0, _path.length()-1);
// Reset stats
_gzipFirst = false;
_gzipStats = 0xF8;
}
AsyncStaticWebHandler& AsyncStaticWebHandler::setIsDir(bool isDir){
_isDir = isDir;
return *this;
}
AsyncStaticWebHandler& AsyncStaticWebHandler::setDefaultFile(const char* filename){
_default_file = String(filename);
return *this;
}
AsyncStaticWebHandler& AsyncStaticWebHandler::setCacheControl(const char* cache_control){
_cache_control = String(cache_control);
return *this;
}
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_modified){
_last_modified = last_modified;
return *this;
}
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified){
auto formatP = PSTR("%a, %d %b %Y %H:%M:%S %Z");
char format[strlen_P(formatP) + 1];
strcpy_P(format, formatP);
char result[30];
strftime(result, sizeof(result), format, last_modified);
return setLastModified((const char *)result);
}
#ifdef ESP8266
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(time_t last_modified){
return setLastModified((struct tm *)gmtime(&last_modified));
}
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(){
time_t last_modified;
if(time(&last_modified) == 0) //time is not yet set
return *this;
return setLastModified(last_modified);
}
#endif
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
if(request->method() != HTTP_GET
|| !request->url().startsWith(_uri)
|| !request->isExpectedRequestedConnType(RCT_DEFAULT, RCT_HTTP)
){
return false;
}
if (_getFile(request)) {
// We interested in "If-Modified-Since" header to check if file was modified
if (_last_modified.length())
request->addInterestingHeader(F("If-Modified-Since"));
if(_cache_control.length())
request->addInterestingHeader(F("If-None-Match"));
return true;
}
return false;
}
bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
{
// Remove the found uri
String path = request->url().substring(_uri.length());
// We can skip the file check and look for default if request is to the root of a directory or that request path ends with '/'
bool canSkipFileCheck = (_isDir && path.length() == 0) || (path.length() && path[path.length()-1] == '/');
path = _path + path;
// Do we have a file or .gz file
if (!canSkipFileCheck && _fileExists(request, path))
return true;
// Can't handle if not default file
if (_default_file.length() == 0)
return false;
// Try to add default file, ensure there is a trailing '/' ot the path.
if (path.length() == 0 || path[path.length()-1] != '/')
path += String('/');
path += _default_file;
return _fileExists(request, path);
}
#ifdef ESP32
#define FILE_IS_REAL(f) (f == true && !f.isDirectory())
#else
#define FILE_IS_REAL(f) (f == true)
#endif
bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String& path)
{
bool fileFound = false;
bool gzipFound = false;
String gzip = path + F(".gz");
if (_gzipFirst) {
if (_fs.exists(gzip)) {
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
gzipFound = FILE_IS_REAL(request->_tempFile);
}
if (!gzipFound){
if (_fs.exists(path)) {
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
fileFound = FILE_IS_REAL(request->_tempFile);
}
}
} else {
if (_fs.exists(path)) {
request->_tempFile = _fs.open(path, fs::FileOpenMode::read);
fileFound = FILE_IS_REAL(request->_tempFile);
}
if (!fileFound){
if (_fs.exists(gzip)) {
request->_tempFile = _fs.open(gzip, fs::FileOpenMode::read);
gzipFound = FILE_IS_REAL(request->_tempFile);
}
}
}
bool found = fileFound || gzipFound;
if (found) {
// Extract the file name from the path and keep it in _tempObject
size_t pathLen = path.length();
char * _tempPath = (char*)malloc(pathLen+1);
snprintf_P(_tempPath, pathLen+1, PSTR("%s"), path.c_str());
request->_tempObject = (void*)_tempPath;
// Calculate gzip statistic
_gzipStats = (_gzipStats << 1) + (gzipFound ? 1 : 0);
if (_gzipStats == 0x00) _gzipFirst = false; // All files are not gzip
else if (_gzipStats == 0xFF) _gzipFirst = true; // All files are gzip
else _gzipFirst = _countBits(_gzipStats) > 4; // IF we have more gzip files - try gzip first
}
return found;
}
uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value) const
{
uint8_t w = value;
uint8_t n;
for (n=0; w!=0; n++) w&=w-1;
return n;
}
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
{
// Get the filename from request->_tempObject and free it
String filename = String((char*)request->_tempObject);
free(request->_tempObject);
request->_tempObject = NULL;
if((_username.length() && _password.length()) && !request->authenticate(_username.c_str(), _password.c_str()))
return request->requestAuthentication();
if (request->_tempFile == true) {
time_t lw = request->_tempFile.getLastWrite(); // get last file mod time (if supported by FS)
if (lw) setLastModified(gmtime(&lw));
String etag(lw ? lw : request->_tempFile.size()); // set etag to lastmod timestamp if available, otherwise to size
if (_last_modified.length() && _last_modified == request->header(F("If-Modified-Since"))) {
request->_tempFile.close();
request->send(304); // Not modified
} else if (_cache_control.length() && request->hasHeader(F("If-None-Match")) && request->header(F("If-None-Match")).equals(etag)) {
request->_tempFile.close();
AsyncWebServerResponse * response = new AsyncBasicResponse(304); // Not modified
response->addHeader(F("Cache-Control"), _cache_control);
response->addHeader(F("ETag"), etag);
request->send(response);
} else {
AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, filename, String(), false, _callback);
if (_last_modified.length())
response->addHeader(F("Last-Modified"), _last_modified);
if (_cache_control.length()){
response->addHeader(F("Cache-Control"), _cache_control);
response->addHeader(F("ETag"), etag);
}
request->send(response);
}
} else {
request->send(404);
}
}

View File

@@ -0,0 +1,993 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ESPAsyncWebServer.h"
#include "WebResponseImpl.h"
#include "WebAuthentication.h"
#ifndef ESP8266
#define os_strlen strlen
#endif
#define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '='))
enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL };
AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
: _client(c)
, _server(s)
, _handler(NULL)
, _response(NULL)
, _temp()
, _parseState(0)
, _version(0)
, _method(HTTP_ANY)
, _url()
, _host()
, _contentType()
, _boundary()
, _authorization()
, _reqconntype(RCT_HTTP)
, _isDigest(false)
, _isMultipart(false)
, _isPlainPost(false)
, _expectingContinue(false)
, _contentLength(0)
, _parsedLength(0)
, _params(AlternativeLinkedList<AsyncWebParameter *>([](AsyncWebParameter *p){ delete p; }))
, _multiParseState(0)
, _boundaryPosition(0)
, _itemStartIndex(0)
, _itemSize(0)
, _itemName()
, _itemFilename()
, _itemType()
, _itemValue()
, _itemBuffer(0)
, _itemBufferIndex(0)
, _itemIsFile(false)
, _tempObject(NULL)
{
c->onError([](void *r, AsyncClient* c, int8_t error){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this);
c->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this);
c->onDisconnect([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); delete c; }, this);
c->onTimeout([](void *r, AsyncClient* c, uint32_t time){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this);
c->onData([](void *r, AsyncClient* c, void *buf, size_t len){ (void)c; AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this);
c->onPoll([](void *r, AsyncClient* c){ (void)c; AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this);
}
AsyncWebServerRequest::~AsyncWebServerRequest(){
_headers.clear();
_params.free();
_pathParams.clear();
_interestingHeaders.clear();
if(_response != NULL){
delete _response;
}
if(_tempObject != NULL){
free(_tempObject);
}
if(_tempFile){
_tempFile.close();
}
if(_itemBuffer){
free(_itemBuffer);
}
}
void AsyncWebServerRequest::_onData(void *buf, size_t len){
size_t i = 0;
while (true) {
if(_parseState < PARSE_REQ_BODY){
// Find new line in buf
char *str = (char*)buf;
for (i = 0; i < len; i++) {
if (str[i] == '\n') {
break;
}
}
if (i == len) { // No new line, just add the buffer in _temp
char ch = str[len-1];
str[len-1] = 0;
_temp.reserve(_temp.length()+len);
_temp.concat(str);
_temp.concat(ch);
} else { // Found new line - extract it and parse
str[i] = 0; // Terminate the string at the end of the line.
_temp.concat(str);
_temp.trim();
_parseLine();
if (++i < len) {
// Still have more buffer to process
buf = str+i;
len-= i;
continue;
}
}
} else if(_parseState == PARSE_REQ_BODY){
// A handler should be already attached at this point in _parseLine function.
// If handler does nothing (_onRequest is NULL), we don't need to really parse the body.
const bool needParse = _handler && !_handler->isRequestHandlerTrivial();
if(_isMultipart){
if(needParse){
size_t i;
for(i=0; i<len; i++){
_parseMultipartPostByte(((uint8_t*)buf)[i], i == len - 1);
_parsedLength++;
}
} else
_parsedLength += len;
} else {
if(_parsedLength == 0){
if(_contentType.startsWith(F("application/x-www-form-urlencoded"))){
_isPlainPost = true;
} else if(_contentType == F("text/plain") && __is_param_char(((char*)buf)[0])){
size_t i = 0;
while (i<len && __is_param_char(((char*)buf)[i++]));
if(i < len && ((char*)buf)[i-1] == '='){
_isPlainPost = true;
}
}
}
if(!_isPlainPost) {
//check if authenticated before calling the body
if(_handler) _handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength);
_parsedLength += len;
} else if(needParse) {
size_t i;
for(i=0; i<len; i++){
_parsedLength++;
_parsePlainPostChar(((uint8_t*)buf)[i]);
}
} else {
_parsedLength += len;
}
}
if(_parsedLength == _contentLength){
_parseState = PARSE_REQ_END;
//check if authenticated before calling handleRequest and request auth instead
if(_handler) _handler->handleRequest(this);
else send(501);
}
}
break;
}
}
void AsyncWebServerRequest::_removeNotInterestingHeaders(){
if (std::any_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
[](const String &str){ return str.equalsIgnoreCase(F("ANY")); }))
return; // nothing to do
for(auto iter = std::begin(_headers); iter != std::end(_headers); )
{
const auto name = iter->name();
if (std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
[&name](const String &str){ return str.equalsIgnoreCase(name); }))
iter = _headers.erase(iter);
else
iter++;
}
}
void AsyncWebServerRequest::_onPoll(){
//os_printf("p\n");
if(_response != NULL && _client != NULL && _client->canSend()){
if(!_response->_finished()){
_response->_ack(this, 0, 0);
} else {
AsyncWebServerResponse* r = _response;
_response = NULL;
delete r;
_client->close();
}
}
}
void AsyncWebServerRequest::_onAck(size_t len, uint32_t time){
//os_printf("a:%u:%u\n", len, time);
if(_response != NULL){
if(!_response->_finished()){
_response->_ack(this, len, time);
} else if(_response->_finished()){
AsyncWebServerResponse* r = _response;
_response = NULL;
delete r;
_client->close();
}
}
}
void AsyncWebServerRequest::_onError(int8_t error){
(void)error;
}
void AsyncWebServerRequest::_onTimeout(uint32_t time){
(void)time;
//os_printf("TIMEOUT: %u, state: %s\n", time, _client->stateToString());
_client->close();
}
void AsyncWebServerRequest::onDisconnect (ArDisconnectHandler fn){
_onDisconnectfn=fn;
}
void AsyncWebServerRequest::_onDisconnect(){
//os_printf("d\n");
if(_onDisconnectfn) {
_onDisconnectfn();
}
_server->_handleDisconnect(this);
}
void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){
_params.add(p);
}
void AsyncWebServerRequest::_addPathParam(const char *p){
_pathParams.emplace_back(p);
}
void AsyncWebServerRequest::_addGetParams(const String& params){
size_t start = 0;
while (start < params.length()){
int end = params.indexOf('&', start);
if (end < 0) end = params.length();
int equal = params.indexOf('=', start);
if (equal < 0 || equal > end) equal = end;
String name = params.substring(start, equal);
String value = equal + 1 < end ? params.substring(equal + 1, end) : String();
_addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value)));
start = end + 1;
}
}
bool AsyncWebServerRequest::_parseReqHead(){
// Split the head into method, url and version
int index = _temp.indexOf(' ');
String m = _temp.substring(0, index);
index = _temp.indexOf(' ', index+1);
String u = _temp.substring(m.length()+1, index);
_temp = _temp.substring(index+1);
if(m == F("GET")){
_method = HTTP_GET;
} else if(m == F("POST")){
_method = HTTP_POST;
} else if(m == F("DELETE")){
_method = HTTP_DELETE;
} else if(m == F("PUT")){
_method = HTTP_PUT;
} else if(m == F("PATCH")){
_method = HTTP_PATCH;
} else if(m == F("HEAD")){
_method = HTTP_HEAD;
} else if(m == F("OPTIONS")){
_method = HTTP_OPTIONS;
}
String g;
index = u.indexOf('?');
if(index > 0){
g = u.substring(index +1);
u = u.substring(0, index);
}
_url = urlDecode(u);
_addGetParams(g);
if(!_temp.startsWith(F("HTTP/1.0")))
_version = 1;
_temp = String();
return true;
}
bool strContains(const String &src, const String &find, bool mindcase = true) {
int pos=0, i=0;
const int slen = src.length();
const int flen = find.length();
if (slen < flen) return false;
while (pos <= (slen - flen)) {
for (i=0; i < flen; i++) {
if (mindcase) {
if (src[pos+i] != find[i]) i = flen + 1; // no match
}
else if (tolower(src[pos+i]) != tolower(find[i])) {
i = flen + 1; // no match
}
}
if (i == flen) return true;
pos++;
}
return false;
}
bool AsyncWebServerRequest::_parseReqHeader(){
int index = _temp.indexOf(':');
if(index){
String name = _temp.substring(0, index);
String value = _temp.substring(index + 2);
if(name.equalsIgnoreCase("Host")){
_host = value;
} else if(name.equalsIgnoreCase(F("Content-Type"))){
_contentType = value.substring(0, value.indexOf(';'));
if (value.startsWith(F("multipart/"))){
_boundary = value.substring(value.indexOf('=')+1);
_boundary.replace(String('"'), String());
_isMultipart = true;
}
} else if(name.equalsIgnoreCase(F("Content-Length"))){
_contentLength = atoi(value.c_str());
} else if(name.equalsIgnoreCase(F("Expect")) && value == F("100-continue")){
_expectingContinue = true;
} else if(name.equalsIgnoreCase(F("Authorization"))){
if(value.length() > 5 && value.substring(0,5).equalsIgnoreCase(F("Basic"))){
_authorization = value.substring(6);
} else if(value.length() > 6 && value.substring(0,6).equalsIgnoreCase(F("Digest"))){
_isDigest = true;
_authorization = value.substring(7);
}
} else {
if(name.equalsIgnoreCase(F("Upgrade")) && value.equalsIgnoreCase(F("websocket"))){
// WebSocket request can be uniquely identified by header: [Upgrade: websocket]
_reqconntype = RCT_WS;
} else {
if(name.equalsIgnoreCase(F("Accept")) && strContains(value, F("text/event-stream"), false)){
// WebEvent request can be uniquely identified by header: [Accept: text/event-stream]
_reqconntype = RCT_EVENT;
}
}
}
_headers.emplace_back(name, value);
}
_temp = String();
return true;
}
void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data){
if(data && (char)data != '&')
_temp += (char)data;
if(!data || (char)data == '&' || _parsedLength == _contentLength){
String name = F("body");
String value = _temp;
if(!_temp.startsWith(String('{')) && !_temp.startsWith(String('[')) && _temp.indexOf('=') > 0){
name = _temp.substring(0, _temp.indexOf('='));
value = _temp.substring(_temp.indexOf('=') + 1);
}
_addParam(new AsyncWebParameter(urlDecode(name), urlDecode(value), true));
_temp = String();
}
}
void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last){
_itemBuffer[_itemBufferIndex++] = data;
if(last || _itemBufferIndex == 1460){
//check if authenticated before calling the upload
if(_handler)
_handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false);
_itemBufferIndex = 0;
}
}
enum {
EXPECT_BOUNDARY,
PARSE_HEADERS,
WAIT_FOR_RETURN1,
EXPECT_FEED1,
EXPECT_DASH1,
EXPECT_DASH2,
BOUNDARY_OR_DATA,
DASH3_OR_RETURN2,
EXPECT_FEED2,
PARSING_FINISHED,
PARSE_ERROR
};
void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
#define itemWriteByte(b) do { _itemSize++; if(_itemIsFile) _handleUploadByte(b, last); else _itemValue+=(char)(b); } while(0)
if(!_parsedLength){
_multiParseState = EXPECT_BOUNDARY;
_temp = String();
_itemName = String();
_itemFilename = String();
_itemType = String();
}
if(_multiParseState == WAIT_FOR_RETURN1){
if(data != '\r'){
itemWriteByte(data);
} else {
_multiParseState = EXPECT_FEED1;
}
} else if(_multiParseState == EXPECT_BOUNDARY){
if(_parsedLength < 2 && data != '-'){
_multiParseState = PARSE_ERROR;
return;
} else if(_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data){
_multiParseState = PARSE_ERROR;
return;
} else if(_parsedLength - 2 == _boundary.length() && data != '\r'){
_multiParseState = PARSE_ERROR;
return;
} else if(_parsedLength - 3 == _boundary.length()){
if(data != '\n'){
_multiParseState = PARSE_ERROR;
return;
}
_multiParseState = PARSE_HEADERS;
_itemIsFile = false;
}
} else if(_multiParseState == PARSE_HEADERS){
if((char)data != '\r' && (char)data != '\n')
_temp += (char)data;
if((char)data == '\n'){
if(_temp.length()){
if(_temp.length() > 12 && _temp.substring(0, 12).equalsIgnoreCase(F("Content-Type"))){
_itemType = _temp.substring(14);
_itemIsFile = true;
} else if(_temp.length() > 19 && _temp.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){
_temp = _temp.substring(_temp.indexOf(';') + 2);
while(_temp.indexOf(';') > 0){
String name = _temp.substring(0, _temp.indexOf('='));
String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1);
if(name == F("name")){
_itemName = nameVal;
} else if(name == F("filename")){
_itemFilename = nameVal;
_itemIsFile = true;
}
_temp = _temp.substring(_temp.indexOf(';') + 2);
}
String name = _temp.substring(0, _temp.indexOf('='));
String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1);
if(name == F("name")){
_itemName = nameVal;
} else if(name == F("filename")){
_itemFilename = nameVal;
_itemIsFile = true;
}
}
_temp = String();
} else {
_multiParseState = WAIT_FOR_RETURN1;
//value starts from here
_itemSize = 0;
_itemStartIndex = _parsedLength;
_itemValue = String();
if(_itemIsFile){
if(_itemBuffer)
free(_itemBuffer);
_itemBuffer = (uint8_t*)malloc(1460);
if(_itemBuffer == NULL){
_multiParseState = PARSE_ERROR;
return;
}
_itemBufferIndex = 0;
}
}
}
} else if(_multiParseState == EXPECT_FEED1){
if(data != '\n'){
_multiParseState = WAIT_FOR_RETURN1;
itemWriteByte('\r'); _parseMultipartPostByte(data, last);
} else {
_multiParseState = EXPECT_DASH1;
}
} else if(_multiParseState == EXPECT_DASH1){
if(data != '-'){
_multiParseState = WAIT_FOR_RETURN1;
itemWriteByte('\r'); itemWriteByte('\n'); _parseMultipartPostByte(data, last);
} else {
_multiParseState = EXPECT_DASH2;
}
} else if(_multiParseState == EXPECT_DASH2){
if(data != '-'){
_multiParseState = WAIT_FOR_RETURN1;
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); _parseMultipartPostByte(data, last);
} else {
_multiParseState = BOUNDARY_OR_DATA;
_boundaryPosition = 0;
}
} else if(_multiParseState == BOUNDARY_OR_DATA){
if(_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data){
_multiParseState = WAIT_FOR_RETURN1;
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-');
uint8_t i;
for(i=0; i<_boundaryPosition; i++)
itemWriteByte(_boundary.c_str()[i]);
_parseMultipartPostByte(data, last);
} else if(_boundaryPosition == _boundary.length() - 1){
_multiParseState = DASH3_OR_RETURN2;
if(!_itemIsFile){
_addParam(new AsyncWebParameter(_itemName, _itemValue, true));
} else {
if(_itemSize){
//check if authenticated before calling the upload
if(_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);
_itemBufferIndex = 0;
_addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize));
}
free(_itemBuffer);
_itemBuffer = NULL;
}
} else {
_boundaryPosition++;
}
} else if(_multiParseState == DASH3_OR_RETURN2){
if(data == '-' && (_contentLength - _parsedLength - 4) != 0){
//os_printf("ERROR: The parser got to the end of the POST but is expecting %u bytes more!\nDrop an issue so we can have more info on the matter!\n", _contentLength - _parsedLength - 4);
_contentLength = _parsedLength + 4;//lets close the request gracefully
}
if(data == '\r'){
_multiParseState = EXPECT_FEED2;
} else if(data == '-' && _contentLength == (_parsedLength + 4)){
_multiParseState = PARSING_FINISHED;
} else {
_multiParseState = WAIT_FOR_RETURN1;
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-');
uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]);
_parseMultipartPostByte(data, last);
}
} else if(_multiParseState == EXPECT_FEED2){
if(data == '\n'){
_multiParseState = PARSE_HEADERS;
_itemIsFile = false;
} else {
_multiParseState = WAIT_FOR_RETURN1;
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-');
uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]);
itemWriteByte('\r'); _parseMultipartPostByte(data, last);
}
}
}
void AsyncWebServerRequest::_parseLine(){
if(_parseState == PARSE_REQ_START){
if(!_temp.length()){
_parseState = PARSE_REQ_FAIL;
_client->close();
} else {
_parseReqHead();
_parseState = PARSE_REQ_HEADERS;
}
return;
}
if(_parseState == PARSE_REQ_HEADERS){
if(!_temp.length()){
//end of headers
_server->_rewriteRequest(this);
_server->_attachHandler(this);
_removeNotInterestingHeaders();
if(_expectingContinue){
String response = F("HTTP/1.1 100 Continue\r\n\r\n");
_client->write(response.c_str(), response.length());
}
//check handler for authentication
if(_contentLength){
_parseState = PARSE_REQ_BODY;
} else {
_parseState = PARSE_REQ_END;
if(_handler) _handler->handleRequest(this);
else send(501);
}
} else _parseReqHeader();
}
}
size_t AsyncWebServerRequest::headers() const{
return _headers.size();
}
bool AsyncWebServerRequest::hasHeader(const String& name) const {
for(const auto& h: _headers){
if(h.name().equalsIgnoreCase(name)){
return true;
}
}
return false;
}
bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper * data) const {
return hasHeader(String(data));
}
AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) {
auto iter = std::find_if(std::begin(_headers), std::end(_headers),
[&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); });
if (iter == std::end(_headers))
return nullptr;
return &(*iter);
}
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const String& name) const {
auto iter = std::find_if(std::begin(_headers), std::end(_headers),
[&name](const AsyncWebHeader &header){ return header.name().equalsIgnoreCase(name); });
if (iter == std::end(_headers))
return nullptr;
return &(*iter);
}
AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) {
PGM_P p = reinterpret_cast<PGM_P>(data);
size_t n = strlen_P(p);
char * name = (char*) malloc(n+1);
if (name) {
strcpy_P(name, p);
AsyncWebHeader* result = getHeader( String(name));
free(name);
return result;
} else {
return nullptr;
}
}
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper * data) const {
PGM_P p = reinterpret_cast<PGM_P>(data);
size_t n = strlen_P(p);
char * name = (char*) malloc(n+1);
if (name) {
strcpy_P(name, p);
const AsyncWebHeader* result = getHeader( String(name));
free(name);
return result;
} else {
return nullptr;
}
}
AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) {
if (num >= _headers.size())
return nullptr;
return &(*std::next(std::begin(_headers), num));
}
const AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const {
if (num >= _headers.size())
return nullptr;
return &(*std::next(std::begin(_headers), num));
}
size_t AsyncWebServerRequest::params() const {
return _params.length();
}
bool AsyncWebServerRequest::hasParam(const String& name, bool post, bool file) const {
for(const auto& p: _params){
if(p->name() == name && p->isPost() == post && p->isFile() == file){
return true;
}
}
return false;
}
bool AsyncWebServerRequest::hasParam(const __FlashStringHelper * data, bool post, bool file) const {
return hasParam(String(data).c_str(), post, file);
}
AsyncWebParameter* AsyncWebServerRequest::getParam(const String& name, bool post, bool file) const {
for(const auto& p: _params){
if(p->name() == name && p->isPost() == post && p->isFile() == file){
return p;
}
}
return nullptr;
}
AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper * data, bool post, bool file) const {
return getParam(String(data).c_str(), post, file);
}
AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const {
auto param = _params.nth(num);
return param ? *param : nullptr;
}
void AsyncWebServerRequest::addInterestingHeader(const String& name){
if(std::none_of(std::begin(_interestingHeaders), std::end(_interestingHeaders),
[&name](const String &str){ return str.equalsIgnoreCase(name); }))
_interestingHeaders.push_back(name);
}
void AsyncWebServerRequest::send(AsyncWebServerResponse *response){
_response = response;
if(_response == NULL){
_client->close(true);
_onDisconnect();
return;
}
if(!_response->_sourceValid()){
delete response;
_response = NULL;
send(500);
}
else {
_client->setRxTimeout(0);
_response->_respond(this);
}
}
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const String& content){
return new AsyncBasicResponse(code, contentType, content);
}
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
if(fs.exists(path) || (!download && fs.exists(path+F(".gz"))))
return new AsyncFileResponse(fs, path, contentType, download, callback);
return NULL;
}
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
if(content == true)
return new AsyncFileResponse(content, path, contentType, download, callback);
return NULL;
}
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback){
return new AsyncStreamResponse(stream, contentType, len, callback);
}
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){
return new AsyncCallbackResponse(contentType, len, callback, templateCallback);
}
AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){
if(_version)
return new AsyncChunkedResponse(contentType, callback, templateCallback);
return new AsyncCallbackResponse(contentType, 0, callback, templateCallback);
}
AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& contentType, size_t bufferSize){
return new AsyncResponseStream(contentType, bufferSize);
}
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){
return new AsyncProgmemResponse(code, contentType, content, len, callback);
}
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){
return beginResponse_P(code, contentType, (const uint8_t *)content, strlen_P(content), callback);
}
void AsyncWebServerRequest::send(int code, const String& contentType, const String& content){
send(beginResponse(code, contentType, content));
}
void AsyncWebServerRequest::send(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
if(fs.exists(path) || (!download && fs.exists(path+F(".gz")))){
send(beginResponse(fs, path, contentType, download, callback));
} else send(404);
}
void AsyncWebServerRequest::send(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback){
if(content == true){
send(beginResponse(content, path, contentType, download, callback));
} else send(404);
}
void AsyncWebServerRequest::send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback){
send(beginResponse(stream, contentType, len, callback));
}
void AsyncWebServerRequest::send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){
send(beginResponse(contentType, len, callback, templateCallback));
}
void AsyncWebServerRequest::sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback){
send(beginChunkedResponse(contentType, callback, templateCallback));
}
void AsyncWebServerRequest::send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback){
send(beginResponse_P(code, contentType, content, len, callback));
}
void AsyncWebServerRequest::send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback){
send(beginResponse_P(code, contentType, content, callback));
}
void AsyncWebServerRequest::redirect(const String& url){
AsyncWebServerResponse * response = beginResponse(302);
response->addHeader(F("Location"), url);
send(response);
}
bool AsyncWebServerRequest::authenticate(const char * username, const char * password, const char * realm, bool passwordIsHash){
if(_authorization.length()){
if(_isDigest)
return checkDigestAuthentication(_authorization.c_str(), methodToString(), username, password, realm, passwordIsHash, NULL, NULL, NULL);
else if(!passwordIsHash)
return checkBasicAuthentication(_authorization.c_str(), username, password);
else
return _authorization.equals(password);
}
return false;
}
bool AsyncWebServerRequest::authenticate(const char * hash){
if(!_authorization.length() || hash == NULL)
return false;
if(_isDigest){
String hStr = String(hash);
int separator = hStr.indexOf(':');
if(separator <= 0)
return false;
String username = hStr.substring(0, separator);
hStr = hStr.substring(separator + 1);
separator = hStr.indexOf(':');
if(separator <= 0)
return false;
String realm = hStr.substring(0, separator);
hStr = hStr.substring(separator + 1);
return checkDigestAuthentication(_authorization.c_str(), methodToString(), username.c_str(), hStr.c_str(), realm.c_str(), true, NULL, NULL, NULL);
}
return (_authorization.equals(hash));
}
void AsyncWebServerRequest::requestAuthentication(const char * realm, bool isDigest){
AsyncWebServerResponse * r = beginResponse(401);
if(!isDigest && realm == NULL){
r->addHeader(F("WWW-Authenticate"), F("Basic realm=\"Login Required\""));
} else if(!isDigest){
String header = F("Basic realm=\"");
header.concat(realm);
header += '"';
r->addHeader(F("WWW-Authenticate"), header);
} else {
String header = F("Digest ");
header.concat(requestDigestAuthentication(realm));
r->addHeader(F("WWW-Authenticate"), header);
}
send(r);
}
bool AsyncWebServerRequest::hasArg(const char* name) const {
for(const auto& arg: _params){
if(arg->name() == name){
return true;
}
}
return false;
}
bool AsyncWebServerRequest::hasArg(const __FlashStringHelper * data) const {
return hasArg(String(data).c_str());
}
const String& AsyncWebServerRequest::arg(const String& name) const {
for(const auto& arg: _params){
if(arg->name() == name){
return arg->value();
}
}
return emptyString;
}
const String& AsyncWebServerRequest::arg(const __FlashStringHelper * data) const {
return arg(String(data).c_str());
}
const String& AsyncWebServerRequest::arg(size_t i) const {
return getParam(i)->value();
}
const String& AsyncWebServerRequest::argName(size_t i) const {
return getParam(i)->name();
}
const String& AsyncWebServerRequest::pathArg(size_t i) const {
return i < _pathParams.size() ? _pathParams[i] : emptyString;
}
const String& AsyncWebServerRequest::header(const char* name) const {
const AsyncWebHeader* h = getHeader(String(name));
return h ? h->value() : emptyString;
}
const String& AsyncWebServerRequest::header(const __FlashStringHelper * data) const {
return header(String(data).c_str());
};
const String& AsyncWebServerRequest::header(size_t i) const {
const AsyncWebHeader* h = getHeader(i);
return h ? h->value() : emptyString;
}
const String& AsyncWebServerRequest::headerName(size_t i) const {
const AsyncWebHeader* h = getHeader(i);
return h ? h->name() : emptyString;
}
String AsyncWebServerRequest::urlDecode(const String& text) const {
char temp[] = "0x00";
unsigned int len = text.length();
unsigned int i = 0;
String decoded = String();
decoded.reserve(len); // Allocate the string internal buffer - never longer from source text
while (i < len){
char decodedChar;
char encodedChar = text.charAt(i++);
if ((encodedChar == '%') && (i + 1 < len)){
temp[2] = text.charAt(i++);
temp[3] = text.charAt(i++);
decodedChar = strtol(temp, NULL, 16);
} else if (encodedChar == '+') {
decodedChar = ' ';
} else {
decodedChar = encodedChar; // normal ascii char
}
decoded.concat(decodedChar);
}
return decoded;
}
const __FlashStringHelper *AsyncWebServerRequest::methodToString() const {
if(_method == HTTP_ANY) return F("ANY");
else if(_method & HTTP_GET) return F("GET");
else if(_method & HTTP_POST) return F("POST");
else if(_method & HTTP_DELETE) return F("DELETE");
else if(_method & HTTP_PUT) return F("PUT");
else if(_method & HTTP_PATCH) return F("PATCH");
else if(_method & HTTP_HEAD) return F("HEAD");
else if(_method & HTTP_OPTIONS) return F("OPTIONS");
return F("UNKNOWN");
}
const __FlashStringHelper *AsyncWebServerRequest::requestedConnTypeToString() const {
switch (_reqconntype) {
case RCT_NOT_USED: return F("RCT_NOT_USED");
case RCT_DEFAULT: return F("RCT_DEFAULT");
case RCT_HTTP: return F("RCT_HTTP");
case RCT_WS: return F("RCT_WS");
case RCT_EVENT: return F("RCT_EVENT");
default: return F("ERROR");
}
}
bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2, RequestedConnectionType erct3) {
bool res = false;
if ((erct1 != RCT_NOT_USED) && (erct1 == _reqconntype)) res = true;
if ((erct2 != RCT_NOT_USED) && (erct2 == _reqconntype)) res = true;
if ((erct3 != RCT_NOT_USED) && (erct3 == _reqconntype)) res = true;
return res;
}

View File

@@ -0,0 +1,138 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef ASYNCWEBSERVERRESPONSEIMPL_H_
#define ASYNCWEBSERVERRESPONSEIMPL_H_
#ifdef Arduino_h
// arduino is not compatible with std::vector
#undef min
#undef max
#endif
#include <vector>
#include <memory>
// It is possible to restore these defines, but one can use _min and _max instead. Or std::min, std::max.
class AsyncBasicResponse: public AsyncWebServerResponse {
private:
String _content;
public:
AsyncBasicResponse(int code, const String& contentType=String(), const String& content=String());
void _respond(AsyncWebServerRequest *request);
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
bool _sourceValid() const { return true; }
};
class AsyncAbstractResponse: public AsyncWebServerResponse {
private:
String _head;
// Data is inserted into cache at begin().
// This is inefficient with vector, but if we use some other container,
// we won't be able to access it as contiguous array of bytes when reading from it,
// so by gaining performance in one place, we'll lose it in another.
std::vector<uint8_t> _cache;
size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len);
size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen);
protected:
AwsTemplateProcessor _callback;
public:
AsyncAbstractResponse(AwsTemplateProcessor callback=nullptr);
void _respond(AsyncWebServerRequest *request);
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
bool _sourceValid() const { return false; }
virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; }
};
#ifndef TEMPLATE_PLACEHOLDER
#define TEMPLATE_PLACEHOLDER '%'
#endif
#define TEMPLATE_PARAM_NAME_LENGTH 32
class AsyncFileResponse: public AsyncAbstractResponse {
using File = fs::File;
using FS = fs::FS;
private:
File _content;
String _path;
void _setContentType(const String& path);
public:
AsyncFileResponse(FS &fs, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
AsyncFileResponse(File content, const String& path, const String& contentType=String(), bool download=false, AwsTemplateProcessor callback=nullptr);
~AsyncFileResponse();
bool _sourceValid() const { return !!(_content); }
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
};
class AsyncStreamResponse: public AsyncAbstractResponse {
private:
Stream *_content;
public:
AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback=nullptr);
bool _sourceValid() const { return !!(_content); }
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
};
class AsyncCallbackResponse: public AsyncAbstractResponse {
private:
AwsResponseFiller _content;
size_t _filledLength;
public:
AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
bool _sourceValid() const { return !!(_content); }
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
};
class AsyncChunkedResponse: public AsyncAbstractResponse {
private:
AwsResponseFiller _content;
size_t _filledLength;
public:
AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback=nullptr);
bool _sourceValid() const { return !!(_content); }
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
};
class AsyncProgmemResponse: public AsyncAbstractResponse {
private:
const uint8_t * _content;
size_t _readLength;
public:
AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
bool _sourceValid() const { return true; }
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
};
class cbuf;
class AsyncResponseStream: public AsyncAbstractResponse, public Print {
private:
std::unique_ptr<cbuf> _content;
public:
AsyncResponseStream(const String& contentType, size_t bufferSize);
~AsyncResponseStream();
bool _sourceValid() const { return (_state < RESPONSE_END); }
virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override;
size_t write(const uint8_t *data, size_t len);
size_t write(uint8_t data);
using Print::write;
};
#endif /* ASYNCWEBSERVERRESPONSEIMPL_H_ */

View File

@@ -0,0 +1,704 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ESPAsyncWebServer.h"
#include "WebResponseImpl.h"
#include "cbuf.h"
// Since ESP8266 does not link memchr by default, here's its implementation.
void* memchr(void* ptr, int ch, size_t count)
{
unsigned char* p = static_cast<unsigned char*>(ptr);
while(count--)
if(*p++ == static_cast<unsigned char>(ch))
return --p;
return nullptr;
}
/*
* Abstract Response
* */
const char* AsyncWebServerResponse::_responseCodeToString(int code) {
return reinterpret_cast<const char *>(responseCodeToString(code));
}
const __FlashStringHelper *AsyncWebServerResponse::responseCodeToString(int code) {
switch (code) {
case 100: return F("Continue");
case 101: return F("Switching Protocols");
case 200: return F("OK");
case 201: return F("Created");
case 202: return F("Accepted");
case 203: return F("Non-Authoritative Information");
case 204: return F("No Content");
case 205: return F("Reset Content");
case 206: return F("Partial Content");
case 300: return F("Multiple Choices");
case 301: return F("Moved Permanently");
case 302: return F("Found");
case 303: return F("See Other");
case 304: return F("Not Modified");
case 305: return F("Use Proxy");
case 307: return F("Temporary Redirect");
case 400: return F("Bad Request");
case 401: return F("Unauthorized");
case 402: return F("Payment Required");
case 403: return F("Forbidden");
case 404: return F("Not Found");
case 405: return F("Method Not Allowed");
case 406: return F("Not Acceptable");
case 407: return F("Proxy Authentication Required");
case 408: return F("Request Time-out");
case 409: return F("Conflict");
case 410: return F("Gone");
case 411: return F("Length Required");
case 412: return F("Precondition Failed");
case 413: return F("Request Entity Too Large");
case 414: return F("Request-URI Too Large");
case 415: return F("Unsupported Media Type");
case 416: return F("Requested range not satisfiable");
case 417: return F("Expectation Failed");
case 500: return F("Internal Server Error");
case 501: return F("Not Implemented");
case 502: return F("Bad Gateway");
case 503: return F("Service Unavailable");
case 504: return F("Gateway Time-out");
case 505: return F("HTTP Version not supported");
default: return F("");
}
}
AsyncWebServerResponse::AsyncWebServerResponse()
: _code(0)
, _contentType()
, _contentLength(0)
, _sendContentLength(true)
, _chunked(false)
, _headLength(0)
, _sentLength(0)
, _ackedLength(0)
, _writtenLength(0)
, _state(RESPONSE_SETUP)
{
for(const auto &header: DefaultHeaders::Instance()) {
_headers.emplace_back(header);
}
}
AsyncWebServerResponse::~AsyncWebServerResponse() = default;
void AsyncWebServerResponse::setCode(int code){
if(_state == RESPONSE_SETUP)
_code = code;
}
void AsyncWebServerResponse::setContentLength(size_t len){
if(_state == RESPONSE_SETUP)
_contentLength = len;
}
void AsyncWebServerResponse::setContentType(const String& type){
if(_state == RESPONSE_SETUP)
_contentType = type;
}
void AsyncWebServerResponse::addHeader(const String& name, const String& value){
_headers.emplace_back(name, value);
}
String AsyncWebServerResponse::_assembleHead(uint8_t version){
if(version){
addHeader(F("Accept-Ranges"), F("none"));
if(_chunked)
addHeader(F("Transfer-Encoding"), F("chunked"));
}
String out = String();
int bufSize = 300;
char buf[bufSize];
snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, _responseCodeToString(_code));
out.concat(buf);
if(_sendContentLength) {
snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength);
out.concat(buf);
}
if(_contentType.length()) {
snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str());
out.concat(buf);
}
for(const auto& header: _headers){
snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str());
out.concat(buf);
}
_headers.clear();
out.concat(F("\r\n"));
_headLength = out.length();
return out;
}
bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; }
bool AsyncWebServerResponse::_finished() const { return _state > RESPONSE_WAIT_ACK; }
bool AsyncWebServerResponse::_failed() const { return _state == RESPONSE_FAILED; }
bool AsyncWebServerResponse::_sourceValid() const { return false; }
void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); }
size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ (void)request; (void)len; (void)time; return 0; }
/*
* String/Code Response
* */
AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const String& content){
_code = code;
_content = content;
_contentType = contentType;
if(_content.length()){
_contentLength = _content.length();
if(!_contentType.length())
_contentType = F("text/plain");
}
addHeader(F("Connection"), F("close"));
}
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
_state = RESPONSE_HEADERS;
String out = _assembleHead(request->version());
size_t outLen = out.length();
size_t space = request->client()->space();
if(!_contentLength && space >= outLen){
_writtenLength += request->client()->write(out.c_str(), outLen);
_state = RESPONSE_WAIT_ACK;
} else if(_contentLength && space >= outLen + _contentLength){
out += _content;
outLen += _contentLength;
_writtenLength += request->client()->write(out.c_str(), outLen);
_state = RESPONSE_WAIT_ACK;
} else if(space && space < outLen){
String partial = out.substring(0, space);
_content = out.substring(space) + _content;
_contentLength += outLen - space;
_writtenLength += request->client()->write(partial.c_str(), partial.length());
_state = RESPONSE_CONTENT;
} else if(space > outLen && space < (outLen + _contentLength)){
size_t shift = space - outLen;
outLen += shift;
_sentLength += shift;
out += _content.substring(0, shift);
_content = _content.substring(shift);
_writtenLength += request->client()->write(out.c_str(), outLen);
_state = RESPONSE_CONTENT;
} else {
_content = out + _content;
_contentLength += outLen;
_state = RESPONSE_CONTENT;
}
}
size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
(void)time;
_ackedLength += len;
if(_state == RESPONSE_CONTENT){
size_t available = _contentLength - _sentLength;
size_t space = request->client()->space();
//we can fit in this packet
if(space > available){
_writtenLength += request->client()->write(_content.c_str(), available);
_content = String();
_state = RESPONSE_WAIT_ACK;
return available;
}
//send some data, the rest on ack
String out = _content.substring(0, space);
_content = _content.substring(space);
_sentLength += space;
_writtenLength += request->client()->write(out.c_str(), space);
return space;
} else if(_state == RESPONSE_WAIT_ACK){
if(_ackedLength >= _writtenLength){
_state = RESPONSE_END;
}
}
return 0;
}
/*
* Abstract Response
* */
AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback): _callback(callback)
{
// In case of template processing, we're unable to determine real response size
if(callback) {
_contentLength = 0;
_sendContentLength = false;
_chunked = true;
}
}
void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
addHeader(F("Connection"), F("close"));
_head = _assembleHead(request->version());
_state = RESPONSE_HEADERS;
_ack(request, 0, 0);
}
size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
(void)time;
if(!_sourceValid()){
_state = RESPONSE_FAILED;
request->client()->close();
return 0;
}
_ackedLength += len;
size_t space = request->client()->space();
size_t headLen = _head.length();
if(_state == RESPONSE_HEADERS){
if(space >= headLen){
_state = RESPONSE_CONTENT;
space -= headLen;
} else {
String out = _head.substring(0, space);
_head = _head.substring(space);
_writtenLength += request->client()->write(out.c_str(), out.length());
return out.length();
}
}
if(_state == RESPONSE_CONTENT){
size_t outLen;
if(_chunked){
if(space <= 8){
return 0;
}
outLen = space;
} else if(!_sendContentLength){
outLen = space;
} else {
outLen = ((_contentLength - _sentLength) > space)?space:(_contentLength - _sentLength);
}
uint8_t *buf = (uint8_t *)malloc(outLen+headLen);
if (!buf) {
// os_printf("_ack malloc %d failed\n", outLen+headLen);
return 0;
}
if(headLen){
memcpy(buf, _head.c_str(), _head.length());
}
size_t readLen = 0;
if(_chunked){
// HTTP 1.1 allows leading zeros in chunk length. Or spaces may be added.
// See RFC2616 sections 2, 3.6.1.
readLen = _fillBufferAndProcessTemplates(buf+headLen+6, outLen - 8);
if(readLen == RESPONSE_TRY_AGAIN){
free(buf);
return 0;
}
outLen = sprintf_P((char*)buf+headLen, PSTR("%x"), readLen) + headLen;
while(outLen < headLen + 4) buf[outLen++] = ' ';
buf[outLen++] = '\r';
buf[outLen++] = '\n';
outLen += readLen;
buf[outLen++] = '\r';
buf[outLen++] = '\n';
} else {
readLen = _fillBufferAndProcessTemplates(buf+headLen, outLen);
if(readLen == RESPONSE_TRY_AGAIN){
free(buf);
return 0;
}
outLen = readLen + headLen;
}
if(headLen){
_head = String();
}
if(outLen){
_writtenLength += request->client()->write((const char*)buf, outLen);
}
if(_chunked){
_sentLength += readLen;
} else {
_sentLength += outLen - headLen;
}
free(buf);
if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || (!_chunked && _sentLength == _contentLength)){
_state = RESPONSE_WAIT_ACK;
}
return outLen;
} else if(_state == RESPONSE_WAIT_ACK){
if(!_sendContentLength || _ackedLength >= _writtenLength){
_state = RESPONSE_END;
if(!_chunked && !_sendContentLength)
request->client()->close(true);
}
}
return 0;
}
size_t AsyncAbstractResponse::_readDataFromCacheOrContent(uint8_t* data, const size_t len)
{
// If we have something in cache, copy it to buffer
const size_t readFromCache = std::min(len, _cache.size());
if(readFromCache) {
memcpy(data, _cache.data(), readFromCache);
_cache.erase(_cache.begin(), _cache.begin() + readFromCache);
}
// If we need to read more...
const size_t needFromFile = len - readFromCache;
const size_t readFromContent = _fillBuffer(data + readFromCache, needFromFile);
return readFromCache + readFromContent;
}
size_t AsyncAbstractResponse::_fillBufferAndProcessTemplates(uint8_t* data, size_t len)
{
if(!_callback)
return _fillBuffer(data, len);
const size_t originalLen = len;
len = _readDataFromCacheOrContent(data, len);
// Now we've read 'len' bytes, either from cache or from file
// Search for template placeholders
uint8_t* pTemplateStart = data;
while((pTemplateStart < &data[len]) && (pTemplateStart = (uint8_t*)memchr(pTemplateStart, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart + 1))) { // data[0] ... data[len - 1]
uint8_t* pTemplateEnd = (pTemplateStart < &data[len - 1]) ? (uint8_t*)memchr(pTemplateStart + 1, TEMPLATE_PLACEHOLDER, &data[len - 1] - pTemplateStart) : nullptr;
// temporary buffer to hold parameter name
uint8_t buf[TEMPLATE_PARAM_NAME_LENGTH + 1];
String paramName;
// If closing placeholder is found:
if(pTemplateEnd) {
// prepare argument to callback
const size_t paramNameLength = std::min((size_t)sizeof(buf) - 1, (size_t)(pTemplateEnd - pTemplateStart - 1));
if(paramNameLength) {
memcpy(buf, pTemplateStart + 1, paramNameLength);
buf[paramNameLength] = 0;
paramName = String(reinterpret_cast<char*>(buf));
} else { // double percent sign encountered, this is single percent sign escaped.
// remove the 2nd percent sign
memmove(pTemplateEnd, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
len += _readDataFromCacheOrContent(&data[len - 1], 1) - 1;
++pTemplateStart;
}
} else if(&data[len - 1] - pTemplateStart + 1 < TEMPLATE_PARAM_NAME_LENGTH + 2) { // closing placeholder not found, check if it's in the remaining file data
memcpy(buf, pTemplateStart + 1, &data[len - 1] - pTemplateStart);
const size_t readFromCacheOrContent = _readDataFromCacheOrContent(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PARAM_NAME_LENGTH + 2 - (&data[len - 1] - pTemplateStart + 1));
if(readFromCacheOrContent) {
pTemplateEnd = (uint8_t*)memchr(buf + (&data[len - 1] - pTemplateStart), TEMPLATE_PLACEHOLDER, readFromCacheOrContent);
if(pTemplateEnd) {
// prepare argument to callback
*pTemplateEnd = 0;
paramName = String(reinterpret_cast<char*>(buf));
// Copy remaining read-ahead data into cache
_cache.insert(_cache.begin(), pTemplateEnd + 1, buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
pTemplateEnd = &data[len - 1];
}
else // closing placeholder not found in file data, store found percent symbol as is and advance to the next position
{
// but first, store read file data in cache
_cache.insert(_cache.begin(), buf + (&data[len - 1] - pTemplateStart), buf + (&data[len - 1] - pTemplateStart) + readFromCacheOrContent);
++pTemplateStart;
}
}
else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
++pTemplateStart;
}
else // closing placeholder not found in content data, store found percent symbol as is and advance to the next position
++pTemplateStart;
if(paramName.length()) {
// call callback and replace with result.
// Everything in range [pTemplateStart, pTemplateEnd] can be safely replaced with parameter value.
// Data after pTemplateEnd may need to be moved.
// The first byte of data after placeholder is located at pTemplateEnd + 1.
// It should be located at pTemplateStart + numBytesCopied (to begin right after inserted parameter value).
const String paramValue(_callback(paramName));
const char* pvstr = paramValue.c_str();
const unsigned int pvlen = paramValue.length();
const size_t numBytesCopied = std::min(pvlen, static_cast<unsigned int>(&data[originalLen - 1] - pTemplateStart + 1));
// make room for param value
// 1. move extra data to cache if parameter value is longer than placeholder AND if there is no room to store
if((pTemplateEnd + 1 < pTemplateStart + numBytesCopied) && (originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1) < len)) {
_cache.insert(_cache.begin(), &data[originalLen - (pTemplateStart + numBytesCopied - pTemplateEnd - 1)], &data[len]);
//2. parameter value is longer than placeholder text, push the data after placeholder which not saved into cache further to the end
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[originalLen] - pTemplateStart - numBytesCopied);
len = originalLen; // fix issue with truncated data, not sure if it has any side effects
} else if(pTemplateEnd + 1 != pTemplateStart + numBytesCopied)
//2. Either parameter value is shorter than placeholder text OR there is enough free space in buffer to fit.
// Move the entire data after the placeholder
memmove(pTemplateStart + numBytesCopied, pTemplateEnd + 1, &data[len] - pTemplateEnd - 1);
// 3. replace placeholder with actual value
memcpy(pTemplateStart, pvstr, numBytesCopied);
// If result is longer than buffer, copy the remainder into cache (this could happen only if placeholder text itself did not fit entirely in buffer)
if(numBytesCopied < pvlen) {
_cache.insert(_cache.begin(), pvstr + numBytesCopied, pvstr + pvlen);
} else if(pTemplateStart + numBytesCopied < pTemplateEnd + 1) { // result is copied fully; if result is shorter than placeholder text...
// there is some free room, fill it from cache
const size_t roomFreed = pTemplateEnd + 1 - pTemplateStart - numBytesCopied;
const size_t totalFreeRoom = originalLen - len + roomFreed;
len += _readDataFromCacheOrContent(&data[len - roomFreed], totalFreeRoom) - roomFreed;
} else { // result is copied fully; it is longer than placeholder text
const size_t roomTaken = pTemplateStart + numBytesCopied - pTemplateEnd - 1;
len = std::min(len + roomTaken, originalLen);
}
}
} // while(pTemplateStart)
return len;
}
/*
* File Response
* */
AsyncFileResponse::~AsyncFileResponse(){
if(_content)
_content.close();
}
void AsyncFileResponse::_setContentType(const String& path){
#if HAVE_EXTERN_GET_CONTENT_TYPE_FUNCTION
extern const __FlashStringHelper *getContentType(const String &path);
_contentType = getContentType(path);
#else
if (path.endsWith(F(".html"))) _contentType = F("text/html");
else if (path.endsWith(F(".htm"))) _contentType = F("text/html");
else if (path.endsWith(F(".css"))) _contentType = F("text/css");
else if (path.endsWith(F(".json"))) _contentType = F("application/json");
else if (path.endsWith(F(".js"))) _contentType = F("application/javascript");
else if (path.endsWith(F(".png"))) _contentType = F("image/png");
else if (path.endsWith(F(".gif"))) _contentType = F("image/gif");
else if (path.endsWith(F(".jpg"))) _contentType = F("image/jpeg");
else if (path.endsWith(F(".ico"))) _contentType = F("image/x-icon");
else if (path.endsWith(F(".svg"))) _contentType = F("image/svg+xml");
else if (path.endsWith(F(".eot"))) _contentType = F("font/eot");
else if (path.endsWith(F(".woff"))) _contentType = F("font/woff");
else if (path.endsWith(F(".woff2"))) _contentType = F("font/woff2");
else if (path.endsWith(F(".ttf"))) _contentType = F("font/ttf");
else if (path.endsWith(F(".xml"))) _contentType = F("text/xml");
else if (path.endsWith(F(".pdf"))) _contentType = F("application/pdf");
else if (path.endsWith(F(".zip"))) _contentType = F("application/zip");
else if(path.endsWith(F(".gz"))) _contentType = F("application/x-gzip");
else _contentType = F("text/plain");
#endif
}
AsyncFileResponse::AsyncFileResponse(FS &fs, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
_code = 200;
_path = path;
if(!download && !fs.exists(_path) && fs.exists(_path + F(".gz"))){
_path = _path + F(".gz");
addHeader(F("Content-Encoding"), F("gzip"));
_callback = nullptr; // Unable to process zipped templates
_sendContentLength = true;
_chunked = false;
}
_content = fs.open(_path, fs::FileOpenMode::read);
_contentLength = _content.size();
if(contentType.length() == 0)
_setContentType(path);
else
_contentType = contentType;
int filenameStart = path.lastIndexOf('/') + 1;
char buf[26+path.length()-filenameStart];
char* filename = (char*)path.c_str() + filenameStart;
if(download) {
// set filename and force download
snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename);
} else {
// set filename and force rendering
snprintf_P(buf, sizeof (buf), PSTR("inline"));
}
addHeader(F("Content-Disposition"), buf);
}
AsyncFileResponse::AsyncFileResponse(File content, const String& path, const String& contentType, bool download, AwsTemplateProcessor callback): AsyncAbstractResponse(callback){
_code = 200;
_path = path;
if(!download && String(content.name()).endsWith(F(".gz")) && !path.endsWith(F(".gz"))){
addHeader(F("Content-Encoding"), F("gzip"));
_callback = nullptr; // Unable to process gzipped templates
_sendContentLength = true;
_chunked = false;
}
_content = content;
_contentLength = _content.size();
if(contentType.length() == 0)
_setContentType(path);
else
_contentType = contentType;
int filenameStart = path.lastIndexOf('/') + 1;
char buf[26+path.length()-filenameStart];
char* filename = (char*)path.c_str() + filenameStart;
if(download) {
snprintf_P(buf, sizeof (buf), PSTR("attachment; filename=\"%s\""), filename);
} else {
snprintf_P(buf, sizeof (buf), PSTR("inline"));
}
addHeader(F("Content-Disposition"), buf);
}
size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
return _content.read(data, len);
}
/*
* Stream Response
* */
AsyncStreamResponse::AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
_code = 200;
_content = &stream;
_contentLength = len;
_contentType = contentType;
}
size_t AsyncStreamResponse::_fillBuffer(uint8_t *data, size_t len){
size_t available = _content->available();
size_t outLen = (available > len)?len:available;
size_t i;
for(i=0;i<outLen;i++)
data[i] = _content->read();
return outLen;
}
/*
* Callback Response
* */
AsyncCallbackResponse::AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback): AsyncAbstractResponse(templateCallback) {
_code = 200;
_content = callback;
_contentLength = len;
if(!len)
_sendContentLength = false;
_contentType = contentType;
_filledLength = 0;
}
size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){
size_t ret = _content(data, len, _filledLength);
if(ret != RESPONSE_TRY_AGAIN){
_filledLength += ret;
}
return ret;
}
/*
* Chunked Response
* */
AsyncChunkedResponse::AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor processorCallback): AsyncAbstractResponse(processorCallback) {
_code = 200;
_content = callback;
_contentLength = 0;
_contentType = contentType;
_sendContentLength = false;
_chunked = true;
_filledLength = 0;
}
size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){
size_t ret = _content(data, len, _filledLength);
if(ret != RESPONSE_TRY_AGAIN){
_filledLength += ret;
}
return ret;
}
/*
* Progmem Response
* */
AsyncProgmemResponse::AsyncProgmemResponse(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback): AsyncAbstractResponse(callback) {
_code = code;
_content = content;
_contentType = contentType;
_contentLength = len;
_readLength = 0;
}
size_t AsyncProgmemResponse::_fillBuffer(uint8_t *data, size_t len){
size_t left = _contentLength - _readLength;
if (left > len) {
memcpy_P(data, _content + _readLength, len);
_readLength += len;
return len;
}
memcpy_P(data, _content + _readLength, left);
_readLength += left;
return left;
}
/*
* Response Stream (You can print/write/printf to it, up to the contentLen bytes)
* */
AsyncResponseStream::AsyncResponseStream(const String& contentType, size_t bufferSize)
{
_code = 200;
_contentLength = 0;
_contentType = contentType;
_content = std::unique_ptr<cbuf>(new cbuf(bufferSize)); //std::make_unique<cbuf>(bufferSize);
}
AsyncResponseStream::~AsyncResponseStream() = default;
size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){
return _content->read((char*)buf, maxLen);
}
size_t AsyncResponseStream::write(const uint8_t *data, size_t len){
if(_started())
return 0;
if(len > _content->room()){
size_t needed = len - _content->room();
_content->resizeAdd(needed);
}
size_t written = _content->write((const char*)data, len);
_contentLength += written;
return written;
}
size_t AsyncResponseStream::write(uint8_t data){
return write(&data, 1);
}

View File

@@ -0,0 +1,198 @@
/*
Asynchronous WebServer library for Espressif MCUs
Copyright (c) 2016 Hristo Gochkov. All rights reserved.
This file is part of the esp8266 core for Arduino environment.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ESPAsyncWebServer.h"
#include "WebHandlerImpl.h"
bool ON_STA_FILTER(AsyncWebServerRequest *request) {
return WiFi.localIP() == request->client()->localIP();
}
bool ON_AP_FILTER(AsyncWebServerRequest *request) {
return WiFi.localIP() != request->client()->localIP();
}
#ifndef HAVE_FS_FILE_OPEN_MODE
const char *fs::FileOpenMode::read = "r";
const char *fs::FileOpenMode::write = "w";
const char *fs::FileOpenMode::append = "a";
#endif
AsyncWebServer::AsyncWebServer(uint16_t port)
: _server(port)
, _rewrites(AlternativeLinkedList<AsyncWebRewrite*>([](AsyncWebRewrite* r){ delete r; }))
, _handlers(AlternativeLinkedList<AsyncWebHandler*>([](AsyncWebHandler* h){ delete h; }))
{
_catchAllHandler = new AsyncCallbackWebHandler();
if(_catchAllHandler == NULL)
return;
_server.onClient([](void *s, AsyncClient* c){
if(c == NULL)
return;
c->setRxTimeout(3);
AsyncWebServerRequest *r = new AsyncWebServerRequest((AsyncWebServer*)s, c);
if(r == NULL){
c->close(true);
c->free();
delete c;
}
}, this);
}
AsyncWebServer::~AsyncWebServer(){
reset();
end();
if(_catchAllHandler) delete _catchAllHandler;
}
AsyncWebRewrite& AsyncWebServer::addRewrite(AsyncWebRewrite* rewrite){
_rewrites.add(rewrite);
return *rewrite;
}
bool AsyncWebServer::removeRewrite(AsyncWebRewrite *rewrite){
return _rewrites.remove(rewrite);
}
AsyncWebRewrite& AsyncWebServer::rewrite(const char* from, const char* to){
return addRewrite(new AsyncWebRewrite(from, to));
}
AsyncWebHandler& AsyncWebServer::addHandler(AsyncWebHandler* handler){
_handlers.add(handler);
return *handler;
}
bool AsyncWebServer::removeHandler(AsyncWebHandler *handler){
return _handlers.remove(handler);
}
void AsyncWebServer::begin(){
_server.setNoDelay(true);
_server.begin();
}
void AsyncWebServer::end(){
_server.end();
}
#if ASYNC_TCP_SSL_ENABLED
void AsyncWebServer::onSslFileRequest(AcSSlFileHandler cb, void* arg){
_server.onSslFileRequest(cb, arg);
}
void AsyncWebServer::beginSecure(const char *cert, const char *key, const char *password){
_server.beginSecure(cert, key, password);
}
#endif
void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request){
delete request;
}
void AsyncWebServer::_rewriteRequest(AsyncWebServerRequest *request){
for(const auto& r: _rewrites){
if (r->match(request)){
request->_url = r->toUrl();
request->_addGetParams(r->params());
}
}
}
void AsyncWebServer::_attachHandler(AsyncWebServerRequest *request){
for(const auto& h: _handlers){
if (h->filter(request) && h->canHandle(request)){
request->setHandler(h);
return;
}
}
request->addInterestingHeader(F("ANY"));
request->setHandler(_catchAllHandler);
}
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody){
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
handler->setUri(uri);
handler->setMethod(method);
handler->onRequest(onRequest);
handler->onUpload(onUpload);
handler->onBody(onBody);
addHandler(handler);
return *handler;
}
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
handler->setUri(uri);
handler->setMethod(method);
handler->onRequest(onRequest);
handler->onUpload(onUpload);
addHandler(handler);
return *handler;
}
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, WebRequestMethodComposite method, ArRequestHandlerFunction onRequest){
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
handler->setUri(uri);
handler->setMethod(method);
handler->onRequest(onRequest);
addHandler(handler);
return *handler;
}
AsyncCallbackWebHandler& AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
handler->setUri(uri);
handler->onRequest(onRequest);
addHandler(handler);
return *handler;
}
AsyncStaticWebHandler& AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_control){
AsyncStaticWebHandler* handler = new AsyncStaticWebHandler(uri, fs, path, cache_control);
addHandler(handler);
return *handler;
}
void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){
_catchAllHandler->onRequest(fn);
}
void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn){
_catchAllHandler->onUpload(fn);
}
void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){
_catchAllHandler->onBody(fn);
}
void AsyncWebServer::reset(){
_rewrites.free();
_handlers.free();
if (_catchAllHandler != NULL){
_catchAllHandler->onRequest(NULL);
_catchAllHandler->onUpload(NULL);
_catchAllHandler->onBody(NULL);
}
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

View File

@@ -0,0 +1,6 @@
#define ce2b329463664e43e1607d0384f46ac1NszJo7o8A3Pedgwr_width 16
#define ce2b329463664e43e1607d0384f46ac1NszJo7o8A3Pedgwr_height 16
static char ce2b329463664e43e1607d0384f46ac1NszJo7o8A3Pedgwr_bits[] = {
0xFF, 0xFF, 0xFD, 0xBF, 0x0B, 0xD0, 0xE7, 0xE7, 0xEF, 0xF7, 0xCF, 0xF3,
0xAF, 0xF5, 0x6F, 0xF6, 0x6F, 0xF6, 0xAF, 0xF5, 0xCF, 0xF3, 0x0F, 0xF0,
0xE7, 0xE7, 0x0B, 0xD0, 0xFD, 0xBF, 0xFF, 0xFF, };