diff --git a/TagTinker b/TagTinker
new file mode 160000
index 0000000000..12b0c04860
--- /dev/null
+++ b/TagTinker
@@ -0,0 +1 @@
+Subproject commit 12b0c0486031367725fb9c53d948801fc6f00f0a
diff --git a/sd_files/portals/media/gorilla.jpg b/sd_files/portals/media/gorilla.jpg
new file mode 100644
index 0000000000..4e018713ad
Binary files /dev/null and b/sd_files/portals/media/gorilla.jpg differ
diff --git a/sd_files/portals/media/rickroll.gif b/sd_files/portals/media/rickroll.gif
new file mode 100644
index 0000000000..4df6611e52
Binary files /dev/null and b/sd_files/portals/media/rickroll.gif differ
diff --git a/src/modules/wifi/evil_portal.cpp b/src/modules/wifi/evil_portal.cpp
index 46a84b7d9a..efbc232145 100644
--- a/src/modules/wifi/evil_portal.cpp
+++ b/src/modules/wifi/evil_portal.cpp
@@ -14,10 +14,10 @@ EvilPortal::EvilPortal(
)
: apName(tssid), _channel(channel), _deauth(deauth), _verifyPwd(verifyPwd), _autoMode(autoMode),
_backgroundMode(backgroundMode), webServer(80), _launchTime(millis()) {
-
+
_originalWifiMode = WiFi.getMode();
_wifiWasConnected = (WiFi.status() == WL_CONNECTED);
-
+
if (!setup()) return;
cleanlyStopWebUiForWiFiFeature();
beginAP();
@@ -67,7 +67,8 @@ bool EvilPortal::setup() {
}
options = {
- {"Custom Html", [this]() { loadCustomHtml(); }}
+ {"Custom Html", [this]() { loadCustomHtml(); }},
+ {"Meme Portal", [this]() { loadMemeHtml(); }}
};
addOptionToMainMenu();
@@ -200,6 +201,18 @@ void EvilPortal::setupRoutes() {
request->send(response);
});
+ webServer.on("/media", HTTP_GET, [this](AsyncWebServerRequest *request) {
+ if (fsMedia == nullptr || _memeFilePath == "") {
+ request->send(404, "text/plain", "Not found");
+ return;
+ }
+ String contentType = "image/jpeg";
+ if (_memeFilePath.endsWith(".gif")) contentType = "image/gif";
+ else if (_memeFilePath.endsWith(".png")) contentType = "image/png";
+ else if (_memeFilePath.endsWith(".mp4")) contentType = "video/mp4";
+ request->send(*fsMedia, _memeFilePath, contentType);
+ });
+
webServer.on("/", [this](AsyncWebServerRequest *request) { portalController(request); });
webServer.on("/post", [this](AsyncWebServerRequest *request) { credsController(request); });
@@ -246,17 +259,17 @@ void EvilPortal::restartWiFi(bool reset) {
webServer.end();
dnsServer.stop();
vTaskDelay(100 / portTICK_PERIOD_MS);
-
+
_captiveHandler = nullptr;
-
+
wifiDisconnect();
WiFi.softAP(apName, emptyString, _channel);
vTaskDelay(100 / portTICK_PERIOD_MS);
-
+
setupRoutes();
dnsServer.start(53, "*", WiFi.softAPIP());
webServer.begin();
-
+
if (reset) resetCapturedCredentials();
}
@@ -309,24 +322,24 @@ void EvilPortal::loop() {
}},
{"Resume", [&shouldRedraw]() { shouldRedraw = true; }}
};
-
+
loopOptions(options);
if (exitPortal) {
displayTextLine("Shutting down...");
vTaskDelay(100 / portTICK_PERIOD_MS);
-
+
webServer.end();
vTaskDelay(200 / portTICK_PERIOD_MS);
-
+
dnsServer.stop();
vTaskDelay(100 / portTICK_PERIOD_MS);
-
+
WiFi.mode(_originalWifiMode);
vTaskDelay(100 / portTICK_PERIOD_MS);
-
+
wifiDisconnect();
vTaskDelay(100 / portTICK_PERIOD_MS);
-
+
return;
}
shouldRedraw = true;
@@ -380,7 +393,7 @@ void EvilPortal::recordPageView() {
bool EvilPortal::shouldTerminate() {
unsigned long currentTime = millis();
unsigned long elapsed = currentTime - _launchTime;
-
+
if (_durationExtended) {
return elapsed > (_extendedDurationSec * 1000);
} else {
@@ -390,7 +403,7 @@ bool EvilPortal::shouldTerminate() {
void EvilPortal::checkAndExtendDuration() {
if (_durationExtended) return;
-
+
if (hasRecentActivity()) {
_durationExtended = true;
Serial.println("[PORTAL] Activity detected, extending duration");
@@ -488,6 +501,41 @@ void EvilPortal::loadCustomHtml() {
}
}
+void EvilPortal::loadMemeHtml() {
+ fsMedia = nullptr;
+
+ options = {};
+ if (sdcardMounted)
+ options.push_back({"SD Card", [this]() { fsMedia = &SD; }});
+ if (checkLittleFsSizeNM())
+ options.push_back({"LittleFS", [this]() { fsMedia = &LittleFS; }});
+
+ if (options.empty()) { loadDefaultHtml(); return; }
+ loopOptions(options);
+ if (returnToMenu || fsMedia == nullptr) return;
+
+ String startPath = fsMedia->exists("/portals/media") ? "/portals/media" : "/";
+
+ apName = "";
+ apName_from_keyboard();
+ if (returnToMenu) return;
+
+ String pickedFile = loopSD(*fsMedia, true, "JPG|JPEG|PNG|GIF|MP4", startPath);
+ if (pickedFile == "" || returnToMenu) return;
+
+ _memeFilePath = pickedFile;
+ String tag;
+ if (pickedFile.endsWith(".mp4"))
+ tag = "";
+ else
+ tag = "";
+
+ htmlPage = "