[
  {
    "path": ".gitignore",
    "content": "*bin"
  },
  {
    "path": "Connector-Adapter-Options/Punchdown-Adapter-1/BOM",
    "content": "Choose the wire gauge, purchase (2) of the \"2 WAY\" terminals and purchase the appropriate punch down tool.\nThe combination of parts below are currently untested.\n\nTool            Punch Down Tool                 Hand Tools ASSEMBLY BIT INSUL DIA 1.60; Mfr. #: 069176701602000 ; Mouser Part #: 581-069176701602000\n\nChoose your targets wiring size below.\n* I found that you can abuse a 20AWG connector to accept from 24-20AWG wire, an 18AWG connector would not skin 24AWG wire in my tests.\n  Research is still ongoing regarding an officially recommended connector.\n\n18AWG IDC Punch Down Block\n(D0),(D1)       IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 18 AWG STRANDED ; Mfr. #: 009176002001006; Mouser Part #: 581-009176002001006\n(+),(-)         IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 18 AWG STRANDED ; Mfr. #: 009176002001006; Mouser Part #: 581-009176002001006\n\n20AWG IDC Punch Down Block *\n(D0),(D1)       IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 20 AWG STRANDED ; Mfr. #: 009176002011006; Mouser Part #: 581-009176002011006\n(+),(-)         IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 20 AWG STRANDED ; Mfr. #: 009176002011006; Mouser Part #: 581-009176002011006\n\n22AWG IDC Punch Down\n(D0),(D1)       IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 22 AWG STRANDED; Mfr. #: 009176002022006; Mouser Part #: 581-009176002022006\n(+),(-)         IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 22 AWG STRANDED; Mfr. #: 009176002022006; Mouser Part #: 581-009176002022006\n\n24AWG IDC Punch Down\n(D0),(D1)       IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 24 AWG STRANDED; Mfr. #: 009176002032006; Mouser Part #: 581-009176002032006\n(+),(-)         IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 24 AWG STRANDED; Mfr. #: 009176002032006; Mouser Part #: 581-009176002032006"
  },
  {
    "path": "Connector-Adapter-Options/Punchdown-Adapter-1/Image-Credits.txt",
    "content": "PCB renders generated using https://pcbs.io."
  },
  {
    "path": "Connector-Adapter-Options/Punchdown-Adapter-2/BOM",
    "content": "Choose the wire gauge, purchase (2) of the \"2 WAY\" terminals and purchase the appropriate punch down tool.\nThe combination of parts below are currently untested.\n\nTool            Punch Down Tool                 Hand Tools ASSEMBLY BIT INSUL DIA 1.60; Mfr. #: 069176701602000 ; Mouser Part #: 581-069176701602000\n\nChoose your targets wiring size below.\n* I found that you can abuse a 20AWG connector to accept from 24-20AWG wire, an 18AWG connector would not skin 24AWG wire in my tests.\n  Research is still ongoing regarding an officially recommended connector.\n\n18AWG IDC Punch Down Block\n(D0),(D1)       IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 18 AWG STRANDED ; Mfr. #: 009176002001006; Mouser Part #: 581-009176002001006\n(+),(-)         IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 18 AWG STRANDED ; Mfr. #: 009176002001006; Mouser Part #: 581-009176002001006\n\n20AWG IDC Punch Down Block *\n(D0),(D1)       IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 20 AWG STRANDED ; Mfr. #: 009176002011006; Mouser Part #: 581-009176002011006\n(+),(-)         IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 20 AWG STRANDED ; Mfr. #: 009176002011006; Mouser Part #: 581-009176002011006\n\n22AWG IDC Punch Down\n(D0),(D1)       IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 22 AWG STRANDED; Mfr. #: 009176002022006; Mouser Part #: 581-009176002022006\n(+),(-)         IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 22 AWG STRANDED; Mfr. #: 009176002022006; Mouser Part #: 581-009176002022006\n\n24AWG IDC Punch Down\n(D0),(D1)       IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 24 AWG STRANDED; Mfr. #: 009176002032006; Mouser Part #: 581-009176002032006\n(+),(-)         IDC Terminal - 2 way            Desc.: 2 WAY WIRE TO BD 24 AWG STRANDED; Mfr. #: 009176002032006; Mouser Part #: 581-009176002032006"
  },
  {
    "path": "Connector-Adapter-Options/Punchdown-Adapter-2/Image-Credits.txt",
    "content": "PCB renders generated using https://pcbs.io."
  },
  {
    "path": "Connector-Adapter-Options/README.md",
    "content": "## Connector Options  \n  \n### Standard Screwdown Connection  \n![ScrewDown](screwdown.jpg?raw=true)  \n![ScrewDown-Portable-5375](portable-5375.jpg?raw=true)  \n  \n### Gray Wire Nuts 22-16AWG  \n  \n![Wire-Nuts](wirenuts.jpg?raw=true)  \n##### Note: For 24AWG you need to twist the wires and fold them over once to obtain a secure connection or use smaller wire nuts(They make a 24-18AWG which is perfect but color varies).  Gray Wire Nuts are common though and can be found in most hardware stores.  \n  \n### Screwdown to T-Taps (Wire Splices)  \n  \n#### T-Tap Example 1  \n![T-Tap1](tsplice1.jpg?raw=true)  \n* Uxcell T-Tap Male Insulated Wire Terminal Quick Connector Combo Set 10 Piece, Red  \n  * by Uxcell  \n[$6.82 on Amazon](http://a.co/6joYNzS)  \n##### Note: These may require a light squeeze on the connector with pliers to cut past the insulation properly. Links are not suggested brands but simply examples.  \n  \n#### T-Tap Example 2  \n* Brightfour Quick Wire Splice Connector-T type 1 Pin solderless- Compatible with 22 - 20 AWG Cable for Some Tight-fitting Automotive Uses  \n  * by Brightfour  \n[$8.44 on Amazon](http://a.co/bAw7Fdw)  \n##### Note: These may require a light squeeze on the connector with pliers to cut past the insulation properly. Links are not suggested brands but simply examples.  \n  \n### Screwdown to Punchdown Adapter  \n  \n##### PCB renders generated using https://pcbs.io.  \n  \n#### Side by Side  \n * [Gerber File](Punchdown-Adapter-1/punchdown-adapter-Gerber.zip)  \n * [BOM File](Punchdown-Adapter-1/BOM)  \n * [Order PCB](https://pcbs.io/share/4KXmA)  \n![Punchdown1](punchdown.jpg?raw=true)  \n![Punchdown1-top](Punchdown-Adapter-1/top-adapter.png?raw=true) ![Punchdown1-bottom](Punchdown-Adapter-1/bottom-adapter.png?raw=true)  \n  \n#### Perpendicular  \n * [Gerber File](Punchdown-Adapter-2/punchdown-adapter2-Gerber.zip)  \n * [BOM File](Punchdown-Adapter-2/BOM)  \n * [Order PCB](https://pcbs.io/share/zy0jg)  \nThe image pictured below is a custom build with the programming header removed, and the screwdown block replaced by a 4 pin header which was soldered in place and then broken off once the adapter was added on. I easily performed all of these modifications by hand using both a soldering iron and hot air rework station.  \n![Punchdown2-Custom](pd2-custom.jpg?raw=true)  \n![Punchdown2-top](Punchdown-Adapter-2/top-adapter2.png?raw=true) ![Punchdown2-bottom](Punchdown-Adapter-2/bottom-adapter2.png?raw=true)  "
  },
  {
    "path": "Images/README.md",
    "content": "![Logo](logo.png?raw=true)  \n![Board](board.jpg?raw=true)  \n![Wiring](wiring.jpg?raw=true)  \n![Programmer](programmer.jpg?raw=true)  "
  },
  {
    "path": "Installation-Schematics/README.md",
    "content": "![Implant](implant.png?raw=true)  \n![Portable-Standalone](portable-standalone.png?raw=true)  \n![Benchtop-PSU-Standalone](benchtop-psu.png?raw=true)  \n![Unsupported-Voltages](unsupported-voltages.png?raw=true)  \n![2-RFID-Tool-Units](2-rfid-tool-units.png?raw=true)  "
  },
  {
    "path": "Keypad/README.md",
    "content": "![HID-5355-Bin2Pin-Reference](../Images/5355keypad-bin2pin.jpg?raw=true)  "
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) [2018] [Corey Harding]\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Magstripe/README.md",
    "content": "## ABA Encoding  \n  \nWiegand Magstripe Readers Tested:  \n* HID 3110-6445 Magstripe Pass-Through Reader  \n  * set to rotary position B (Raw Data - All Bits Wiegand)  \n* HID multiCLASS RM40 iCLASS/Magstripe Reader 6220CKN000B  \n  * purchase a reader with the last digit B in item # (ex: 6220CKN000**B**)  \n  \nRFID-Tool Specific Settings  \n * set buffer size to 256 bits or greater  \n  \nSee [aba-decode.php](aba-decode.php) script for converting binary card data to ascii (Script by: AndrewMohawk)  \n * Command Line: /usr/bin/php aba-decode.php 1101000001100000100011001001001010101101111000001010011101101111100010  \n * Web:          https://www.LegacySecurityGroup.com/aba-decode.php  \n  \nBinary:  \n5 bits  \nLittle Endian Format  \n  \nLRC(Longitudinal Redundancy Check):  \nCount # of set bits(1's) in column  \nEVEN = 0  \nODD  = 1  \n  \nTrack 2 Debit/Credit Card Format(for example, as I could not find actual magstripe access control cards):  \n;1234567890123456=YYMMSSSDDDDDDDDDDDDDD?*  \n; = Start Sentinel  \n1234567890123456 = 16 Digit Card #  \n= = End Card #  \nYY = Expiration Year  \nMM = Expiration Month  \nSSS = Service Code (As Understood From Wikipedia: \"201\" means chip required, \"101\" means no chip, be sure to recalculate the LRC if changing, it is not advised to experiment here without knowing the laws involved)  \nDDDDDDDDDDDDDD = Discretionary Data  \n? = End Sentinel  \n*=LRC  \n  \nBinary Reference:  \n11010 ; - Start Sentinel  \n00001 0  \n10000 1  \n01000 2  \n11001 3  \n00100 4  \n10101 5  \n01101 6  \n11100 7  \n00010 8  \n10011 9  \n00111 <  \n01110 >  \n01011 :  \n10110 = - End Card Number  \n11111 ? - End Sentinel  \n00010 LRC  \n"
  },
  {
    "path": "Magstripe/aba-decode.php",
    "content": "<?php\n\n/*\nUSAGE:\nCommand Line: /usr/bin/php aba-decode.php 1101000001100000100011001001001010101101111000001010011101101111100010\nWeb:          www.LegacySecurityGroup.com/aba-decode.php?binary=1101000001100000100011001001001010101101111000001010011101101111100010\n*/\n\n/* Decode Track 2 data from binary */\nif (defined('STDIN')) {\n  $binary = filter_var($argv[1], FILTER_SANITIZE_NUMBER_INT);\n  define( \"LINEBREAK\", PHP_EOL);\n} else {\n  if(isset($_POST['submit'])) {\n    $binary = filter_input(INPUT_POST, 'binary', FILTER_SANITIZE_NUMBER_INT);\n  }\n  else {\n    $binary = filter_input(INPUT_GET, 'binary', FILTER_SANITIZE_NUMBER_INT);\n  }\n  define( \"LINEBREAK\", \"<br>\");\n}\nif (empty($binary)) {\n  $binary = \"1101000001100000100011001001001010101101111000001010011101101111100010\";\n}\n\necho \"https://github.com/rfidtool/ESP-RFID-Tool/blob/master/Magstripe/aba-decode.php\" . LINEBREAK;\necho \"For converting Track 2 Magstripe ABA Binary data to ASCII\" . LINEBREAK . LINEBREAK;\n\necho \"Original script by: AndrewMohawk\" . LINEBREAK;\n// andrew@andrewmohawk.com\necho \"http://www.andrewmohawk.com\" . LINEBREAK . LINEBREAK;\n\necho \"Modified slightly by: Corey Harding\" . LINEBREAK;\necho \"www.LegacySecurityGroup.com / www.Exploit.Agency\" . LINEBREAK . LINEBREAK;\n\nif (!defined('STDIN')) {\n?>\n<html>\n<body>\n<form action=\"<?php basename(__FILE__, '.php'); ?>\" method=\"post\">\n    <input type='text' name=\"binary\" value=\"<?php echo $binary; ?>\" />\n    <input type=\"submit\" name=\"submit\" value=\"Submit\" />\n</form>\n</body>\n</html>\n<?php\n}\n\n// this function by mtroy dot student at gmail dot com taken from http://php.net/manual/en/function.strpos.php\nfunction strpos_r($haystack, $needle)\n{\n    if(strlen($needle) > strlen($haystack))\n        trigger_error(sprintf(\"%s: length of argument 2 must be <= argument 1\", __FUNCTION__), E_USER_WARNING);\n\n    $seeks = array();\n    while($seek = strrpos($haystack, $needle))\n    {\n        array_push($seeks, $seek);\n        $haystack = substr($haystack, 0, $seek);\n    }\n    return $seeks;\n}\n\nfunction processBinary($binary)\n{\n\t$AsciiOutput = \"\";\n\t\n\t//find start sentinel\n\t$start_sentinel = strpos($binary,\"11010\");\n\tif($start_sentinel === false)\n\t{\n\t\techo \"Could not find start sentinel\" . LINEBREAK;\n\t\treturn false;\n\t}\n\t\n\t//find end sentinel\n\t$end_sentinel = false;\n\t$end_sentinel = strrpos($binary,\"11111\");\n\tif(count($end_sentinel) == 0)\n\t{\n\t\techo \"Could not find end sentinel\" . LINEBREAK;\n\t\treturn false;\n\t}\n\t\n\t//Lets decode the data:\n\t$bit_length = 5; // 4 bits for data, 1 bit for odd-parity or LRC checking\n\t\n\t\n\t$data = substr($binary,$start_sentinel,($end_sentinel-$start_sentinel+5));\n\t\n\t$currentBits = \"\";\n\t$currentNum = 0;\n\t$finalString = \"\";\n\t\n\tfor($i=0;$i<strlen($data);$i++)\n\t{\n\t\tif(strlen($currentBits) < $bit_length)\n\t\t{\n\t\t\t$currentBits .= $data[$i];\n\t\t\t\n\t\t}\n\t\t\n\t\tif(strlen($currentBits) == $bit_length)\n\t\t{\n\t\t\t$parityBit = $currentBits[4];\n\t\t\t$dataBits = substr($currentBits,0,4);\n\t\t\t\n\t\t\t$asciiChar = 0;\n\t\t\t\n\t\t\t\n\t\t\tfor($x=0;$x<4;$x++)\n\t\t\t{\n\t\t\t\t$currentNum += $dataBits[$x];\n\t\t\t}\n\t\t\t\n\t\t\t\n\t\t\t \n\t\t\t$dec = bindec($dataBits);\n\t\t\t$dec = str_pad($dec, 2, \"0\", STR_PAD_LEFT); // just so output is nice\n\t\t\t$asciiChar = chr(bindec(strrev($dataBits))+48); // reverse the binary (since its LSB first) then convert to dec, add 48 and then take it to ASCII\n\t\t\techo \"$currentBits - Data ($dataBits) Parity($parityBit) Decimal ($dec) Ascii($asciiChar)\";\n\t\t\tif(($currentNum + $parityBit) % 2 == false)\n\t\t\t{\n\t\t\t\techo \" __ Parity: Invalid\";\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\techo \" __ Parity: Valid\";\n\t\t\t}\n\t\t\t$AsciiOutput .= $asciiChar;\n\t\t\techo LINEBREAK;\n\t\t\t$currentBits = \"\";\n\t\t\t$currentNum = 0;\n\t\t\t\n\t\t}\n\t\t\n\t\t\n\t}\n\techo  LINEBREAK . LINEBREAK . \"Total Out (ascii): $AsciiOutput\" . LINEBREAK;\n}\necho \"Decoding \" . $binary . LINEBREAK . LINEBREAK;\necho \"Trying One way:\" . LINEBREAK . LINEBREAK;\nif (processBinary($binary) == false)\n{\n\t//reverse.\n\techo  LINEBREAK . LINEBREAK;\n\techo \"Trying The Reverse:\" . LINEBREAK . LINEBREAK;\n\tprocessBinary(strrev($binary));\n}"
  },
  {
    "path": "README.md",
    "content": "# ESP-RFID-Tool  \nBy Corey Harding  \n  \n![Logo](Images/logo.png?raw=true)  \n  \nOfficial website:  \n* www.rfid-tool.com  \n  \n![Board](Images/board.jpg?raw=true)  \nApril Brother is currently manufacturing this hardware and it can be purchased direct from China from their  \n* Official Store: https://blog.aprbrother.com/product/esp-rfid-tool  \n* AliExpress Store: https://www.aliexpress.com/item/ESP-RFID-Tool/32850151497.html  \n* Tindie Store: https://www.tindie.com/products/aprbrother/esp-rfid-tool/    \n  \nThird Party Distributors:  \n* Hacker Warehouse(USA): https://hackerwarehouse.com/product/esp-rfid-tool/  \n  \nVideo Demos of Capturing Credentials using various technologies that utilize a Wiegand Interface:  \n* Performing a REPLAY ATTACK using the Experimental TX feature: https://youtu.be/u1y7UZpup9I  \n* Brute forcing PIN codes using the Experimental TX feature: https://youtu.be/OZCaypGBVv4  \n* Portable HID 5375 Long Range RFID Reader: https://youtu.be/B86926CHImE  \n* HID 5355 RFID/Keypad Reader: https://youtu.be/ojIpcY8Y3KQ  \n* HID multiCLASS RM40 RFID/Magstripe Reader: https://youtu.be/9OccRaLxXg8  \n* HID multiCLASS RFID Reader: https://youtu.be/2sCLkpuxAks  \n* RFID/Biometric/Keypad with Electronic Deadbolt: https://youtu.be/0o8r_ufRrFo  \n  \nFirmware:  \n* Official  \n  * ESP-RFID-Tool: https://github.com/rfidtool/ESP-RFID-Tool/releases  \n    * Releases >=1.1.0 are compiled with esp8266 board manager package 2.4.1  \n    * Releases < 1.1.0 are compiled with esp8266 board manager package 2.3.0  \n* Unofficial  \n  * Port of Tastic RFID Thief: https://github.com/exploitagency/ESP-RFID-Thief/releases  \n  \n## Intended use cases  \n* Security researchers performing red team assessments.  \n  * Capturing card values to later be cloned.  \n  * Replaying raw binary captures.*(see note below)  \n  * Fuzzing access control systems.*(see note below)  \n  * Brute forcing PIN codes.*(see note below) \n  * Denial of Service mode.*(see note below) \n* To create a standalone device for capturing credentials or testing badges and or card readers without the need for a Wiegand Controller.  \n  * Add a battery and a card reader to make the unit portable.  \n  * Add a benchtop power supply to use for testing hardware.  \n* Installers troubleshooting issues with a card reader, the data lines, or even testing for a faulty card.  \n* Useful for merchants or surplus resellers who are testing if access control equipment is functional.  \n* Hobbyist experimenting with various systems that utilize a Wiegand Interface.  \n* Simulating an access control system using multiple units of ESP-RFID-Tool.  \n* RFID-Tool is not intended to be used in any unlawful manner.  \n  \n## What is it?  \nA universal data logger that captures the raw binary data from a standard 5V Wiegand Interface. The device is capable of logging the credentials for access control systems or nearly any device that utilizes a Wiegand Interface such as RFID card readers, pin pads, magnetic stripe systems, barcode, and even some biometric readers. Wiegand Interfaces also do not have to be limited to just access control systems. The main target group for this device is 26-37bit HID cards.  For known card types both the binary and hexadecimal data is displayed directly in the log file for easy badge identification and also in case a clone of a card may be needed. For unknown card types only the raw binary data is shown.  \n  \n## How do you install it?  \nThe device may be installed directly into an existing system drawing its power from the preexisting wiring or it can also be used to turn a reader into a standalone portable unit for data logging when a battery is added. Wiring is simple and consists of 4 wires, (+), (-), D0(Green), and D1(White). The device is capable of operating on voltages ranging from around 4.5V up to a maximum of 12v. Commonly found voltages are 12V and 5V. **(See Installation Notes Below)  \n  \n## How do you access the logs?  \nAccess to the log files and various settings is provided via a web based interface. The device has WiFi capabilities and can be set up to spawn its own access point or set to connect to an existing network. In the event you get locked out of the web interface you can bridge the J3 jumper on power up or while resetting the device to regain access without losing your log files.  \n  \n## I want to know a little more...\nThe hardware is based on an ESP12 WiFi chip with a full TCP/IP stack and Microcontroller Unit. The software is open source licensed under the MIT License and will be released the day the product is launched. The software will reside in this GitHub repo so it may be reviewed by the community. We will accept various pull requests from the community after being reviewed if it improves the value of the device for others. The software was written in the Arduino IDE so both hobbyist and professionals will have no issue modifying the code if they so choose.  A Wiegand Interface operates using 3 wires, a ground, Data0, and Data1. A zero is sent when D0 goes low and a one is sent when D1 goes low, the other line remains high during this time. The timing is 40uS from low to high on a line with 2mS between bits. The software logs this binary output to a file and if the device thinks it is a known card format the preamble is guessed(not sent by card reader but it is the same for all cards of a specific format, our primary target is 26-37bit HID cards) and both the raw binary and hexadecimal data is logged. For unkown card formats only the raw binary output is logged with no guess at the preamble and without showing the data in hexadecimal format to prevent confusion. If you know what kind of card was captured, simply look up the preamble and add it to the beginning of the binary then typically convert it to hexadecimal format to make a clone of a card. It is possible the card is actually an unknown format and the preamble was guessed incorrectly with the device thinking it was a known card format(the guess is based on the bit count output by the reader), if this is the case in the log file there is a literal space in the binary between the preamble and the card data, simply ignore the preamble. When replaying a captured credential*(see note below) you do not include the preamble as that is not output by the card reader but instead you only replay*(see note below) the actual card data as output from the reader. You only need to worry about the preamble if making a clone of a card. The primary function of this device is for logging the raw binary data as output from a Wiegand Interface. You can read from 1 bit all the way up to 4,096 bits. The default buffer setting only allows 52 bits and must be manually increased from the settings page accessible from the web interface.  \n  \n## *Experimental TX Mode  \nThe device was made with minimal hardware to keep costs extremely low and in reach of hobbyist and also so security professionals can buy multiple units and also use them without the fear of losing a unit. This being said there are no level shifters on the board.(It is possible that in the future a PRO version may be released) This means that with the current hardware the device can work with a 3V3 Wiegand Interface as well as a 5V Wiegand interface because the hardware operates at 3V3 and is also 5V tolerant. The drawback of this is that not all 5V Wiegand controllers are guaranteed to be triggered when replaying or fuzzing data because only 3V3 is output from the device. There is also a risk when the device is in TX mode and the output goes low for 40uS, if the device were to also receive data during this time there will be a short. For this reason be sure that absoulutely no cards are being scanned while the device is in TX mode. The device does not go into TX mode until you press on a form submit button and as soon as the data is done being transmitted it goes back into listening mode. For the reasons above TX mode is for experimental use only and was designed primarily for easily debugging the software when making changes.(two units were tied together)  \n**Use transmit mode at your own risk, it is not officially supported. Consider it a bonus in the software.**  \n  \n## **Installation Notes  \n[Click here for wiring diagrams](Installation-Schematics/README.md)  \n[Click here to see connector options](/Connector-Adapter-Options/README.md)  \n* Make sure the reader outputs data in the Wiegand format  \n* Connect D0 on device to D0 on reader  \n* Connect D1 on device to D1 on reader  \n* Connect + on device to + on reader  \n* Connect - on device to - on reader  \n  * The ESP-RFID-Tool accepts 4.5-12v but you must also be sure to supply enough voltage and current to power your reader if creating a standalone or portable unit.  \n  * SAFETY: It has come to my attention that the voltage regulator used on the commercially available units that you may find for sale is different than the voltage regulator I originally chose for this project.  Also due to global chip shortages and the possibility of manufacturers substituting components I am downgrading the absolute maximum voltage rating to 12v as in you may be stressing the unit to its limits at this voltage and extra care should be taken.  Most of my smaller portable prototypes ran at around 9v(x6 AA Batteries) and I found this to be more than adequate, remember that you can always power your reader and the RFID-Tool unit separately if needed and I suggest supplying your project with the lowest possible voltage that you can get away with.  In fact, I have found multiple readers that run just fine at less than the recommended voltage.  Note that I did not notice a significant range increase in my original testing between supplying 12v and 24v to my reader(I do not recommend this as I tend to abuse the specs for what I am testing, and I was using a different voltage regulator in my prototype).  Also be advised that most commonly used batteries produce more than the commonly stated nominal voltage at a full charge.  It may also be a good idea to apply a heatsink to your voltage regulator especially if you notice that the unit runs hot at your chosen voltage.  It is also never recommended to leave your device unattended.  Please be safe and take all necessary safety precautions when testing your setup.  \n  * OPTIONAL: Connect 4.5-12v battery into the same + and -(only if building a portable unit, do not add a battery if implanting device into an existing installation, or if you do use a battery do not connect the + wire from the existing installation to your device, only tie in the GND -)  \n  * NOTE: At a minimum D0, D1, and GND(-) must be connected to the reader for proper function, no matter the power source.  \n* Configure settings  \n  * See Below  \n  \n## Making Sense of the Binary Data  \n[Keypads](Keypad/README.md)  \n[Magstripe/ABA Format](Magstripe/README.md)  \n  \n## Flashing Firmware  \nOPTION 1: OTA via the Web Interface:  \n* Download one of the latest releases from  \n  * https://github.com/rfidtool/ESP-RFID-Tool/releases  \n* Login to the device's admin panel and upgrade the firmware.  \n  \nOPTION 2: Arduino IDE:  \n* Use the ESP Flasher R4 by April Brother:  \n  * https://blog.aprbrother.com/product/esp-flasher-r4  \n* Clone/download the source.  \n* Add these libraries:  \n  * ArduinoJson library 5.13.1 by Benoit Blanchon  \n    * https://github.com/bblanchon/ArduinoJson  \n  * ESP8266FtpServer.h  \n    * https://github.com/exploitagency/esp8266FTPServer/tree/feature/bbx10_speedup  \n* Choose the board NodeMCU 1.0.  \n* Upload.  \n  \n## Software Help    \n  \n### Accessing ESP-RFID-Tool Web Interface  \n  \nSSID: \"ESP-RFID-Tool\"  \nURL:  http://192.168.1.1  \n  \n### Configure ESP-RFID-Tool  \n  \n#### Default credentials to access the configuration page:  \n* Username: \"admin\"  \n* Password: \"rfidtool\"  \n  \n#### Default credentials for ftp server:  \n* Username: \"ftp-admin\"  \n* Password: \"rfidtool\"  \n  \n#### WiFi Configuration:  \n  \nNetwork Type:  \n* Access Point Mode: Create a standalone access point(No Internet Connectivity-Requires Close Proximity)  \n* Join Existing Network: Join an existing network(Possible Internet Connectivity-Could use Device Remotely)  \n  \nHidden: Choose whether or not to use a hidden SSID when creating an access point  \n  \nSSID: SSID of the access point to create or of the network you are choosing to join  \nPassword: Password of the access point which you wish to create or of the network you are choosing to join  \nChannel: Channel of the access point you are creating  \n  \nIP: IP to set for device  \nGateway: Gateway to use, make it the same as ESP-RFID-Tool's IP if an access point or the same as the router if joining a network  \nSubnet: Typically set to 255.255.255.0  \n  \n#### Web Interface Administration Settings:  \n  \nUsername: Username to configure/upgrade ESP-RFID-Tool  \nPassword: Password to configure/upgrade ESP-RFID-Tool  \n  \nFTP Server Settings:  \n  \nNote: Supports Passive(PASV) Mode Only!  \n* Enabled: Turn FTP Server ON  \n* Disabled: Turn FTP Server OFF  \nUsername: Username to login to ftp server  \nPassword: Password to login to ftp server  \n  \nPower LED:  \n  \n* Enabled: Turn ON Power LED  \n* Disabled: Turn OFF Power LED  \n  \nRFID Capture Log:  \n  \nUseful to change this value to differentiate between facilities during various security assessments.  \nFile Name: File name to save captured RFID tags to for the current security assessment.  \n  \n#### List Exfiltrated Data  \n  \nDisplays all log files containing RFID tag captures.  \n  \n#### Format File System  \n  \nThis will erase the contents of the SPIFFS file system including ALL RFID tag captures.  \nFormatting may take up to 90 seconds.  \nAll current settings will be retained unless you reboot your device during this process.  \n  \n#### Upgrade ESP-RFID-Tool Firmware  \n  \nAuthenticate using your username and password set in the configuration page.  \n  \nDefault credentials to access the firmware upgrade page:  \n* Username: \"admin\"  \n* Password: \"rfidtool\"  \n  \nSelect \"Browse\" choose the new firmware to be uploaded and then click \"Upgrade\".  \n  \nYou will need to manually reset the device upon the browser alerting you that the upgrade was successful.  \n  \n#### Jumpers  \n    \n* J1: Bridge to reset the board(your FTDI doesn't have a reset button?)  \n* J2: Cut the trace to disable DTR pin after programming then use it as an additional IO(continue updating firmware via web interface)  \n* J3: Bridge this during a power cycle to restore default configuration without losing your log files.  \n  \n#### Restore Default Settings  \n    \n* Option 1: Go to settings under web interface and choose restore default configuration.  \n* Option 2: Bridge J3 before booting the device. (Either before powering on or bridge it and reset the device)   \n* Option 3: Connect via serial(9600 baud) and send the command \"ResetDefaultConfig:\" without quotes.  \n  \n## History\nI pushed the design for the original prototype to GitHub back in September of 2016 albeit under a different repo. I was using an Adafruit Feather Huzzah running some code that I had modified porting the Tastic RFID Thief(by Fran Brown from Bishop Fox) to the ESP12S chip. At the time no sort of Wiegand logger existed offering WiFi capabilities and providing an easy to use web interface for accessing the log files.(I could not find one so I created it) During the second evolution of the project I decided to design dedicated hardware and I lightly upgraded the software. It was open source hardware and it was out there on GitHub but still not easily available to the masses. Not everyone is confident in surface mount soldering and even for me it was time consuming assembling boards for personal use. It was then that I realized there is a need for an affordable device like mine to be mass produced so anyone that has a legitimate for need one can have access to it. During the third stage I redesigned both the software and the hardware and decided to contact April Brother to see about them manufacturing it and selling it for a fair price. That is when ESP-RFID-Tool was born.  \n  \n## Licensing Information  \n    \nCreated by Corey Harding  \nhttps://github.com/rfidtool/ESP-RFID-Tool  \nESP-RFID-Tool software is licensed under the MIT License  \n  \n#### Libraries/borrowed code being used  \n  \n##### Arduino and ESP8266 core libraries  \n* Typically: GNU LESSER GENERAL PUBLIC LICENSE Version 2.1  \n  * Assorted Authors  \n  \n##### ArduinoJson.h  \n* The MIT License (MIT)  \n  * Copyright (c) 2014-2017 Benoit BLANCHON  \n  \n##### ESP8266FtpServer.h  \n* GNU LESSER GENERAL PUBLIC LICENSE Version 2.1  \n  * Originally by nailbuster, later modified by bbx10 and apullin  \n  \n##### WiegandNG.h  \n* GNU LESSER GENERAL PUBLIC LICENSE Version 2.1  \n  * JP Liew  \n  \n##### Wiegand Preamble Calculator Code  \n* No License Specified  \n  * Fran Brown of Bishop Fox  \n  \n##### strrev.h  \n* Custom License(see file)  \n  * Copyright (c) 2007 Dmitry Xmelkov  \n  \n##### aba-decode.py  \n* No License Specified  \n  * Andrew MacPherson(andrewmohawk)  \n"
  },
  {
    "path": "Source Code/esprfidtool/HelpText.h",
    "content": "const char HelpText[] PROGMEM = R\"=====(\n<!DOCTYPE HTML>\n<html>\n<head><title>ESP-RFID-Tool Help Page</title></head>\n<body>\n<a href=\"/\"><- BACK TO INDEX</a><br><br>\n-----<br>\nHELP<br>\n-----<br>\n<br>\nESP-RFID-Tool<br>\n<br>\nCreated by Corey Harding<br>\nwww.LegacySecurityGroup.com / www.Exploit.Agency<br>\n<a href=\"https://github.com/rfidtool/ESP-RFID-Tool\">https://github.com/rfidtool/ESP-RFID-Tool</a> - See Link for Updated Firmware or for more detailed Help<br>\n<br>\nThe ESP-RFID-Tool is a tool created for logging Wiegand data and also for testing devices that contain a Wiegand Interface.  The primary target group is 26-37bit HID Cards but it will also work with most devices that output Wiegand data.  ESP-RFID-Tool can be combined with a RFID reader and a battery to create a portable standalone RFID badge logger, it can also be integrated into existing systems without the need for a battery and instead drawing its power directly from the wiring in the existing installation. The ESP-RFID-Tool can read the data from any device that contains a Wiegand Interface and outputs data from 1 bit long up to 4,096 bits long, although anything other than 26-37bit is experimental.  The ESP-RFID-Tool is not even limited to RFID technologies, many other devices also contain a Wiegand Interface as it is an access control system standard, this includes pin pads(keypad), magnetic stripe(magstripe), there are even non access control related devices that utilize a Wiegand Interface.<br>\n<br>\nThe ESP-RFID-Tool software is distributed under the MIT License. The license and copyright notice can not be removed and must be distributed alongside all future copies of the software.<br>\n<br>\n-----<br>\nAccessing ESP-RFID-Tool Web Interface<br>\n-----<br>\n<br>\nSSID: \"ESP-RFID-Tool\"<br>\nURL:  http://192.168.1.1<br>\n<br>\n-----<br>\nConfigure ESP-RFID-Tool<br>\n-----<br>\n<br>\nDefault credentials to access the configuration page:<br>\nUsername: \"admin\"<br>\nPassword: \"rfidtool\"<br>\n<br>\nDefault credentials for ftp server:<br>\nUsername: \"ftp-admin\"<br>\nPassword: \"rfidtool\"<br>\n<br>\nWiFi Configuration:<br>\n<br>\nNetwork Type:<br>\nAccess Point Mode: Create a standalone access point(No Internet Connectivity-Requires Close Proximity)<br>\nJoin Existing Network: Join an existing network(Possible Internet Connectivity-Could use Device Remotely)<br>\n<br>\nHidden: Choose whether or not to use a hidden SSID when creating an access point<br>\n<br>\nSSID: SSID of the access point to create or of the network you are choosing to join<br>\nPassword: Password of the access point which you wish to create or of the network you are choosing to join<br>\nChannel: Channel of the access point you are creating<br>\n<br>\nIP: IP to set for device<br>\nGateway: Gateway to use, make it the same as ESP-RFID-Tool's IP if an access point or the same as the router if joining a network<br>\nSubnet: Typically set to 255.255.255.0<br>\n<br>\nWeb Interface Administration Settings:<br>\n<br>\nUsername: Username to configure/upgrade ESP-RFID-Tool<br>\nPassword: Password to configure/upgrade ESP-RFID-Tool<br>\n<br>\nFTP Server Settings:<br>\n<br>\nNote: Supports Passive(PASV) Mode Only!<br>\nEnabled: Turn FTP Server ON<br>\nDisabled: Turn FTP Server OFF<br>\nUsername: Username to login to ftp server<br>\nPassword: Password to login to ftp server<br>\n<br>\nPower LED:<br>\n<br>\nEnabled: Turn ON Power LED<br>\nDisabled: Turn OFF Power LED<br>\n<br>\nRFID Capture Log:<br>\n<br>\nUseful to change this value to differentiate between facilities during various security assessments.<br>\nFile Name: File name to save captured RFID tags to for the current security assessment.<br>\n<br>\n-----<br>\nList Exfiltrated Data<br>\n-----<br>\n<br>\nDisplays all log files containing RFID tag captures.<br>\n<br>\n-----<br>\nFormat File System<br>\n-----<br>\n<br>\nThis will erase the contents of the SPIFFS file system including ALL RFID tag captures.<br>\nFormatting may take up to 90 seconds.<br>\nAll current settings will be retained unless you reboot your device during this process.<br>\n<br>\n-----<br>\nUpgrade ESP-RFID-Tool Firmware<br>\n-----<br>\n<br>\nAuthenticate using your username and password set in the configuration page.<br>\n<br>\nDefault credentials to access the firmware upgrade page:<br>\nUsername: \"admin\"<br>\nPassword: \"rfidtool\"<br>\n<br>\nSelect \"Browse\" choose the new firmware to be uploaded and then click \"Upgrade\".<br>\n<br>\nYou will need to manually reset the device upon the browser alerting you that the upgrade was successful.<br>\n<br>\n-----<br>\nLicensing Information<br>\n-----<br>\n<br>\nCreated by Corey Harding<br>\nhttps://github.com/rfidtool/ESP-RFID-Tool<br>\nESP-RFID-Tool software is licensed under the MIT License<br>\n/*<br>\n MIT License<br>\n<br>\n Copyright (c) [2018] [Corey Harding]<br>\n<br>\n Permission is hereby granted, free of charge, to any person obtaining a copy<br>\n of this software and associated documentation files (the \"Software\"), to deal<br>\n in the Software without restriction, including without limitation the rights<br>\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell<br>\n copies of the Software, and to permit persons to whom the Software is<br>\n furnished to do so, subject to the following conditions:<br>\n<br>\n The above copyright notice and this permission notice shall be included in all<br>\n copies or substantial portions of the Software.<br>\n<br>\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE<br>\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,<br>\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE<br>\n SOFTWARE.<br>\n*/<br><br>\n<a href=\"/license\">Click here for additional licensing information</a>\n</body>\n</html>\n)=====\";\n"
  },
  {
    "path": "Source Code/esprfidtool/LICENSE",
    "content": "SOFTWARE LICENSE\n\nMIT License\n\nCopyright (c) [2018] [Corey Harding]\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Source Code/esprfidtool/License.h",
    "content": "const char License[] PROGMEM = R\"=====(\n<!DOCTYPE HTML>\n<html>\n<head><title>ESP-RFID-Tool Licensing Page</title></head>\n<body>\n<a href=\"/\"><- BACK TO INDEX</a><br><br>\n<pre>\nESP-RFID-Tool by Corey Harding: https://www.LegacySecurityGroup.com\nCode available at: https://github.com/rfidtool/ESP-RFID-Tool\n\nESP-RFID-Tool Hardware was created by Corey Harding\nESP-RFID-Tool Software is licensed under the MIT License\n/*\n MIT License\n\n Copyright (c) [2018] [Corey Harding]\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n*/\n\nCore libraries used:\n\n/*\n Arduino.h - Main include file for the Arduino SDK\n Copyright (c) 2005-2013 Arduino Team. All right reserved.\n This library is free software; you can redistribute it and/or\n modify it under the terms of the GNU Lesser General Public\n License as published by the Free Software Foundation; either\n version 2.1 of the License, or (at your option) any later version.\n This library is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n Lesser General Public License for more details.\n You should have received a copy of the GNU Lesser General Public\n License along with this library; if not, write to the Free Software\n Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n*/\n\n/*\n ESP8266WiFi.h - esp8266 Wifi support.\n Based on WiFi.h from Arduino WiFi shield library.\n Copyright (c) 2011-2014 Arduino. All right reserved.\n Modified by Ivan Grokhotkov, December 2014\n This library is free software; you can redistribute it and/or\n modify it under the terms of the GNU Lesser General Public\n License as published by the Free Software Foundation; either\n version 2.1 of the License, or (at your option) any later version.\n This library is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n Lesser General Public License for more details.\n You should have received a copy of the GNU Lesser General Public\n License along with this library; if not, write to the Free Software\n Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n*/\n\n/*\n WiFiClient.h - Library for Arduino Wifi shield.\n Copyright (c) 2011-2014 Arduino. All right reserved.\n This library is free software; you can redistribute it and/or\n modify it under the terms of the GNU Lesser General Public\n License as published by the Free Software Foundation; either\n version 2.1 of the License, or (at your option) any later version.\n This library is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n Lesser General Public License for more details.\n You should have received a copy of the GNU Lesser General Public\n License along with this library; if not, write to the Free Software\n Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n Modified by Ivan Grokhotkov, December 2014 - esp8266 support\n*/\n\n/*\n ESP8266WebServer.h - Dead simple web-server.\n Supports only one simultaneous client, knows how to handle GET and POST.\n Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.\n This library is free software; you can redistribute it and/or\n modify it under the terms of the GNU Lesser General Public\n License as published by the Free Software Foundation; either\n version 2.1 of the License, or (at your option) any later version.\n This library is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n Lesser General Public License for more details.\n You should have received a copy of the GNU Lesser General Public\n License along with this library; if not, write to the Free Software\n Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)\n*/\n\n/*\n Esp8266httpupdateserver.h No license information available.\n*/\n\n/*\n ESP8266mDNS.h\n ESP8266 Multicast DNS (port of CC3000 Multicast DNS library)\n Version 1.1\n Copyright (c) 2013 Tony DiCola (tony@tonydicola.com)\n ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com)\n Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com)\n This is a simple implementation of multicast DNS query support for an Arduino\n running on ESP8266 chip. Only support for resolving address queries is currently\n implemented.\n License (MIT license):\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n The above copyright notice and this permission notice shall be included in\n all copies or substantial portions of the Software.\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n THE SOFTWARE.\n*/\n\n/*\n FS.h - file system wrapper\n Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.\n This file is part of the esp8266 core for Arduino environment.\n This library is free software; you can redistribute it and/or\n modify it under the terms of the GNU Lesser General Public\n License as published by the Free Software Foundation; either\n version 2.1 of the License, or (at your option) any later version.\n This library is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n Lesser General Public License for more details.\n You should have received a copy of the GNU Lesser General Public\n License along with this library; if not, write to the Free Software\n Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n*/\n\n/*\n ArduinoJson.h\n The MIT License (MIT)\n ---------------------\n\n Copyright (c) 2014-2017 Benoit BLANCHON\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n*/\n\n/*\n ESP8266FtpServer.h - by nailbuster, later modified by bbx10 and apullin\n                   GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 2.1, February 1999\n https://github.com/apullin/esp8266FTPServer/blob/feature/bbx10_speedup/LICENSE\n*/\n\n/*\n  WiegandNG.h by JP Liew\n  https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino\n  GNU LGPL License 2.1\n  \n  This library is free software; you can redistribute it and/or modify it under\n  the terms of the GNU Lesser General Public License as published by the\n  Free Software Foundation; either version 2.1 of the License, or (at your option)\n  any later version.\n\n  This library is distributed in the hope that it will be useful, but\n  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for\n  more details.\n*/\n\n/*\n  Wiegand Preamble Calculator Code from the Tastic RFID Thief byFran Brown of Bishop Fox\n  https://www.bishopfox.com/resources/tools/rfid-hacking/attack-tools/\n*/\n\n/*\n  Original source for aba-decode.py by Andrew MacPherson(andrewmohawk)\n  https://andrewmohawk.com/2012/05/29/magnetic-stripes-part-1/\n  https://pastebin.com/h9eVqRxz\n*/\n\n/*\n  strrev.h by Dmitry Xmelkov\n  Copyright (c) 2007  Dmitry Xmelkov\n   All rights reserved.\n   Redistribution and use in source and binary forms, with or without\n   modification, are permitted provided that the following conditions are met:\n   * Redistributions of source code must retain the above copyright\n     notice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above copyright\n     notice, this list of conditions and the following disclaimer in\n     the documentation and/or other materials provided with the\n     distribution.\n   * Neither the name of the copyright holders nor the names of\n     contributors may be used to endorse or promote products derived\n     from this software without specific prior written permission.\n   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n   POSSIBILITY OF SUCH DAMAGE.\n*/\n\nThis is not a comprehensive list, these \"Core Libraries\" may depend on additional\nlibraries, more information can be obtained by viewing the source code of the main libraries.\n</pre>\n</body>\n</html>\n)=====\";\n"
  },
  {
    "path": "Source Code/esprfidtool/WiegandNG.cpp",
    "content": "#include \"WiegandNG.h\"\n\n// pcintbranch\n\nvolatile unsigned long  WiegandNG::_lastPulseTime;  // time last bit pulse received\nvolatile unsigned int WiegandNG::_bitCounted;   // number of bits arrived at Interrupt pins\nvolatile unsigned char  *WiegandNG::_buffer;    // buffer for data retention\nunsigned int      WiegandNG::_bufferSize;   // memory (bytes) allocated for buffer\n\n\nvoid shift_left(volatile unsigned char *ar, int size, int shift)\n{\n  while (shift--) {               // for each bit to shift ...\n    int carry = 0;                // clear the initial carry bit.\n    int lastElement = size-1;\n    for (int i = 0; i < size; i++) {      // for each element of the array, from low byte to high byte\n      if (i!=lastElement) {\n        // condition ? valueIfTrue : valueIfFalse\n        carry = (ar[i+1] & 0x80) ? 1 : 0;\n        ar[i] = carry | (ar[i]<<1);\n      }\n      else {\n        ar[i] <<=1;\n      }\n    }   \n  }\n}  \n\nvoid WiegandNG::clear() {             // reset variables to start new capture\n  _bitCounted=0;\n  _lastPulseTime = millis();\n  memset((unsigned char *)_buffer,0,_bufferSize);\n  interrupts();                 // allow interrupt\n}\n\nvoid WiegandNG::pause() {\n  noInterrupts();                 // disable interrupt so that user can process data \n}\n\nvolatile unsigned char * WiegandNG::getRawData() {\n  return _buffer;                 // return pointer of the buffer\n}\n\nunsigned int WiegandNG::getPacketGap() {\n  return _packetGap;\n}\n\nunsigned int WiegandNG::getBitAllocated() {\n  return _bitAllocated;\n}\n\nunsigned int WiegandNG::getBitCounted() {\n  return _bitCounted;\n}\n\nunsigned int WiegandNG::getBufferSize() {\n  return _bufferSize;\n}\n\nbool WiegandNG::available() {\n  bool ret=false;\n  noInterrupts();\n  unsigned long tempLastPulseTime = _lastPulseTime;\n  interrupts();\n\n  unsigned long sysTick = millis();\n  //  if ((sysTick - _lastPulseTime) > _packetGap) {  // _packetGap (ms) laps\n  if ((sysTick - tempLastPulseTime) > _packetGap) { // _packetGap (ms) laps\n    if(_bitCounted>0) {             // bits found, must have data, return true\n      /*if(_bitCounted<8) {\n        Serial.print(_bitCounted);\n        Serial.print(\", \");\n        Serial.print(sysTick);\n        Serial.print(\", \");\n        Serial.print(_lastPulseTime);\n        Serial.print(\",\");\n        Serial.println(tempLastPulseTime);\n      }*/\n      ret=true;\n    }\n    else\n    {\n      _lastPulseTime = millis();\n    }\n  }\n  return ret;\n}\n\nvoid WiegandNG::ReadD0 () {\n  _bitCounted++;                  // increment bit count for Interrupt connected to D0\n  shift_left(_buffer,_bufferSize,1);        // shift 0 into buffer\n  _lastPulseTime = millis();            // keep track of time last wiegand bit received\n}\n\nvoid WiegandNG::ReadD1() {\n  _bitCounted++;                  // increment bit count for Interrupt connected to D1\n  if (_bitCounted > (_bufferSize * 8)) {\n    _bitCounted=0;                // overflowed, \n  } else {\n    shift_left(_buffer,_bufferSize,1);      // shift 1 into buffer\n    _buffer[_bufferSize-1] |=1;         // set last bit 1\n    _lastPulseTime = millis();          // keep track of time last wiegand bit received\n  }\n}\n\nbool WiegandNG::begin(unsigned int allocateBits, unsigned int packetGap) {\n  bool ret;\n  // newer versions of Arduino provide pin to interrupt mapping\n  ret=begin(2, 3, allocateBits, packetGap);\n  return ret;\n}\n\nbool WiegandNG::begin(uint8_t pinD0, uint8_t pinD1, unsigned int allocateBits, unsigned int packetGap) {\n  if (_buffer != NULL) {\n    delete [] _buffer;\n  }\n  _packetGap = packetGap;\n  _bitAllocated = allocateBits;\n  \n  _bufferSize=(_bitAllocated/8);            // calculate the number of bytes required to store wiegand bits\n  if((_bitAllocated % 8) >0) _bufferSize++;     // add 1 extra byte to cater for bits that are not divisible by 8\n  _buffer = new unsigned char [_bufferSize];      // allocate memory for buffer\n  if(_buffer == NULL) return false;         // not enough memory, return false\n\n  clear();\n  \n  pinMode(pinD0, INPUT);                // set D0 pin as input\n  pinMode(pinD1, INPUT);                // set D1 pin as input\n  attachInterrupt(digitalPinToInterrupt(pinD0), ReadD0, FALLING);     // hardware interrupt - high to low pulse\n  attachInterrupt(digitalPinToInterrupt(pinD1), ReadD1, FALLING);     // hardware interrupt - high to low pulse\n  return true;\n}\n\nWiegandNG::WiegandNG() {\n\n}\n\nWiegandNG::~WiegandNG() {\n  if (_buffer != NULL) {\n    delete [] _buffer;\n  }\n}\n"
  },
  {
    "path": "Source Code/esprfidtool/WiegandNG.h",
    "content": "#ifndef _WIEGAND_NG_H\n#define _WIEGAND_NG_H\n\n#if defined(ARDUINO) && ARDUINO >= 100\n#include \"Arduino.h\"\n#else\n#include \"WProgram.h\"\n#endif\n\nclass WiegandNG {\n\nprivate:\n  static void ReadD0();\n  static void ReadD1();\n  static volatile unsigned long   _lastPulseTime;   // time last bits received\n  static volatile unsigned int  _bitCounted;    // number of bits arrived at Interrupt pins\n  static unsigned int       _bufferSize;    // memory (bytes) allocated for buffer\n  unsigned int          _bitAllocated;    // wiegand bits required\n  unsigned int          _packetGap;     // gap between wiegand packet in millisecond\n  static volatile unsigned char * _buffer;      // buffer for data retention\n  \npublic:\n  bool begin(unsigned int bits, unsigned int packetGap=25);       // default packetGap is 25ms\n  bool begin(uint8_t pinD0, uint8_t pinD1, unsigned int bits, unsigned int packetGap);\n  bool available();\n  void clear();\n  void pause();\n  unsigned int getBitCounted();\n  unsigned int getBitAllocated();\n  unsigned int getBufferSize();\n  unsigned int getPacketGap();\n  volatile unsigned char *getRawData();\n  WiegandNG();\n  ~WiegandNG();\n};\n\n#endif\n\n"
  },
  {
    "path": "Source Code/esprfidtool/aba2str.h",
    "content": "String aba2str (String magstripe, int magStart, int magEnd, String swipeDirection) {\n  //f.println(String()+\"Start pos:\"+magStart);\n  //f.println(String()+\"Start pos:\"+magEnd);\n  String ABA=\"\";\n  String aba2str=\"\";\n  int magCount=abs(magEnd-magStart);\n  //f.println(String()+\"magCount:\"+magCount);\n  aba2str=(String()+\"\\\"Cleaned\\\" Binary:\"+magstripe.substring(magStart,magEnd)+\"\\n\");\n  aba2str+=(String()+\" * Possible \"+swipeDirection+\" Card Data\\(ASCII\\):\");\n  while (magCount>0) {\n    ABA=magstripe.substring(magStart,magStart+4);\n    if (ABA==\"1101\") {aba2str+=(\";\");}\n    else if (ABA==\"0000\") {aba2str+=(\"0\");}\n    else if (ABA==\"1000\") {aba2str+=(\"1\");}\n    else if (ABA==\"0100\") {aba2str+=(\"2\");}\n    else if (ABA==\"1100\") {aba2str+=(\"3\");}\n    else if (ABA==\"0010\") {aba2str+=(\"4\");}\n    else if (ABA==\"1010\") {aba2str+=(\"5\");}\n    else if (ABA==\"0110\") {aba2str+=(\"6\");}\n    else if (ABA==\"1110\") {aba2str+=(\"7\");}\n    else if (ABA==\"0001\") {aba2str+=(\"8\");}\n    else if (ABA==\"1001\") {aba2str+=(\"9\");}\n    else if (ABA==\"0011\") {aba2str+=(\"<\");}\n    else if (ABA==\"0111\") {aba2str+=(\">\");}\n    else if (ABA==\"0101\") {aba2str+=(\":\");}\n    else if (ABA==\"1011\") {aba2str+=(\"=\");}\n    else if (ABA==\"1111\") {aba2str+=(\"?\");}\n    else {aba2str+=(\"_UNKNOWN-CHARACTER_\");}\n    magStart=magStart+5;\n    magCount=magCount-5;\n  }\n  return aba2str;\n}\n"
  },
  {
    "path": "Source Code/esprfidtool/api.h",
    "content": "void apiTX(String apiBIN, int apipulsewidth, int apidatainterval, int wait) {\n  wg.pause();\n  digitalWrite(DATA0, HIGH);\n  pinMode(DATA0,OUTPUT);\n  digitalWrite(DATA1, HIGH);\n  pinMode(DATA1,OUTPUT);\n  for (int i=0; i<=apiBIN.length(); i++) {\n    if (apiBIN.charAt(i) == '0') {\n      digitalWrite(DATA0, LOW);\n      delayMicroseconds(apipulsewidth);\n      digitalWrite(DATA0, HIGH);\n    }\n    else if (apiBIN.charAt(i) == '1') {\n      digitalWrite(DATA1, LOW);\n      delayMicroseconds(apipulsewidth);\n      digitalWrite(DATA1, HIGH);\n    }\n    if (apiBIN.charAt(i) == ',') {\n      delayMicroseconds(wait);\n    }\n    else {\n      delayMicroseconds(apidatainterval);\n    }\n  }\n  apiBIN=\"\";\n  pinMode(DATA0, INPUT);\n  pinMode(DATA1, INPUT);\n  wg.clear();\n}\n\nvoid apiinfo(int prettify) {\n\n  FSInfo fs_info;\n  SPIFFS.info(fs_info);\n  String total;\n  total=fs_info.totalBytes;\n  String used;\n  used=fs_info.usedBytes;\n  String freespace;\n  freespace=fs_info.totalBytes-fs_info.usedBytes;\n  \n  const size_t bufferSize = JSON_ARRAY_SIZE(5) + JSON_OBJECT_SIZE(3);\n  DynamicJsonBuffer jsonAPIbuffer(bufferSize);\n  JsonObject& apilog = jsonAPIbuffer.createObject();\n\n  apilog[\"Device\"] = \"ESP-RFID-Tool\";\n  apilog[\"Firmware\"] = version;\n  apilog[\"API\"] = APIversion;\n  JsonObject& apifs = apilog.createNestedObject(\"File System\");\n  apifs[\"Total Space\"]=total;\n  apifs[\"Used Space\"]=used;\n  apifs[\"Free Space\"]=freespace;\n  apilog[\"Free Memory\"] = String(ESP.getFreeHeap(),DEC);\n  \n  String API_Response=\"\";\n  if (prettify==1) {\n    apilog.prettyPrintTo(API_Response);\n  }\n  else {\n    apilog.printTo(API_Response);\n  }\n  server.send(200, \"application/json\", API_Response);\n  delay(50);\n  jsonAPIbuffer.clear();\n}\n\nvoid apilistlogs(int prettify) {\n  Dir dir = SPIFFS.openDir(\"/\");\n  String FileList = \"\";\n  int logcount=0;\n  \n  while (dir.next()) {\n    File f = dir.openFile(\"r\");\n    String FileName = dir.fileName();\n    if((!FileName.startsWith(\"/payloads/\"))&&(!FileName.startsWith(\"/esploit.json\"))&&(!FileName.startsWith(\"/esportal.json\"))&&(!FileName.startsWith(\"/esprfidtool.json\"))&&(!FileName.startsWith(\"/config.json\"))) {\n      logcount++;\n    }\n    f.close();\n  }\n  \n  const size_t bufferSize = JSON_ARRAY_SIZE(5) + JSON_OBJECT_SIZE(1);\n  DynamicJsonBuffer jsonAPIbuffer(bufferSize);\n  JsonObject& apilog = jsonAPIbuffer.createObject();\n\n  apilog[\"Device\"] = \"ESP-RFID-Tool\";\n  apilog[\"Firmware\"] = version;\n  apilog[\"API\"] = APIversion;\n  apilog[\"Log Count\"] = logcount;\n\n  int currentlog=0;\n  Dir dir2ndrun = SPIFFS.openDir(\"/\");\n  while (dir2ndrun.next()) {\n    File f = dir2ndrun.openFile(\"r\");\n    String FileName = dir2ndrun.fileName();\n    if ((!FileName.startsWith(\"/payloads/\"))&&(!FileName.startsWith(\"/esploit.json\"))&&(!FileName.startsWith(\"/esportal.json\"))&&(!FileName.startsWith(\"/esprfidtool.json\"))&&(!FileName.startsWith(\"/config.json\"))) {\n      currentlog++;\n      FileName.remove(0,1);\n      JsonObject& apilistlogs = apilog.createNestedObject(String(currentlog));\n      apilistlogs[\"File Name\"]=FileName;\n    }\n    f.close();\n  }\n\n  String API_Response=\"\";\n  if (prettify==1) {\n    apilog.prettyPrintTo(API_Response);\n  }\n  else {\n    apilog.printTo(API_Response);\n  }\n  server.send(200, \"application/json\", API_Response);\n  delay(50);\n  jsonAPIbuffer.clear();\n}\n\nvoid apilog(String logfile,int prettify) {\n  File f = SPIFFS.open(String()+\"/\"+logfile, \"r\");\n  if (!f) {\n    server.send(200, \"application/json\", \"Log file not found\");\n    delay(50);\n  }\n  else {\n    int apiCAPTUREcount=0;\n    while(f.available()) {\n      String line = f.readStringUntil('\\n');\n      if(line.indexOf(\",Binary:\") > 0) {\n        apiCAPTUREcount++;\n        int firstIndex = line.indexOf(\",Binary:\");\n        int secondIndex = line.indexOf(\",\", firstIndex + 1);\n        String binaryCaptureLINE=line.substring(firstIndex+8, secondIndex);\n      }\n    }\n    f.close();\n    const size_t bufferSize = JSON_ARRAY_SIZE(6) + JSON_OBJECT_SIZE(4);\n    DynamicJsonBuffer jsonAPIbuffer(bufferSize);\n    JsonObject& apilog = jsonAPIbuffer.createObject();\n\n    apilog[\"Device\"] = \"ESP-RFID-Tool\";\n    apilog[\"Firmware\"] = version;\n    apilog[\"API\"] = APIversion;\n    apilog[\"Log File\"] = logfile;\n    apilog[\"Captures\"] = apiCAPTUREcount;\n\n    int apiCURRENTcapture=0;\n    File f = SPIFFS.open(String()+\"/\"+logfile, \"r\");\n    while(f.available()) {\n      String line = f.readStringUntil('\\n');\n      \n      if(line.indexOf(\",Binary:\") > 0) {\n        apiCURRENTcapture++;\n        int firstIndex = line.indexOf(\",Binary:\");\n        int secondIndex = line.indexOf(\",\", firstIndex + 1);\n        String binaryCaptureLINE=line.substring(firstIndex+8, secondIndex);\n        if ( binaryCaptureLINE.indexOf(\" \") > 0 ) {\n          binaryCaptureLINE=binaryCaptureLINE.substring(binaryCaptureLINE.indexOf(\" \")+1);\n        }\n        binaryCaptureLINE.replace(\"\\r\",\"\");\n        JsonObject& apiCURRENTcaptureOBJECT = apilog.createNestedObject(String(apiCURRENTcapture));\n        apiCURRENTcaptureOBJECT[\"Bit Count\"]=binaryCaptureLINE.length();\n        apiCURRENTcaptureOBJECT[\"Binary\"]=binaryCaptureLINE;\n        if(line.indexOf(\",HEX:\") > 0) {\n          int hfirstIndex = line.indexOf(\",HEX:\");\n          int hsecondIndex = line.indexOf(\",\", hfirstIndex + 1);\n          String hexCURRENT=line.substring(hfirstIndex+5, hsecondIndex);\n          hexCURRENT.replace(\"\\r\",\"\");\n          apiCURRENTcaptureOBJECT[\"Hexadecimal\"]=hexCURRENT;\n        }\n        if(line.indexOf(\",Keypad Code:\") > 0) {\n          int kfirstIndex = line.indexOf(\",Keypad Code:\");\n          int ksecondIndex = line.indexOf(\",\", kfirstIndex + 1);\n          String pinCURRENT=line.substring(kfirstIndex+13, ksecondIndex);\n          pinCURRENT.replace(\"\\r\",\"\");\n          apiCURRENTcaptureOBJECT[\"Keypad Press\"]=pinCURRENT;\n        }\n      }\n    }\n    f.close();\n    String API_Response=\"\";\n    if (prettify==1) {\n      apilog.prettyPrintTo(API_Response);\n    }\n    else {\n      apilog.printTo(API_Response);\n    }\n    server.send(200, \"application/json\", API_Response);\n    delay(50);\n    jsonAPIbuffer.clear();\n  }\n}\n"
  },
  {
    "path": "Source Code/esprfidtool/api_server.h",
    "content": "server.on(\"/api/tx/bin\", [](){\n  String api_binary=\"\";\n  int api_pulsewidth=txdelayus;\n  int api_datainterval=(txdelayms*1000);\n  int prettify=0;\n  int api_wait=100000;\n  if (server.hasArg(\"binary\")) {\n    api_binary=(server.arg(\"binary\"));\n  }\n  if (server.hasArg(\"pulsewidth\")) {\n    api_pulsewidth=(server.arg(\"pulsewidth\").toInt());\n  }\n  if (server.hasArg(\"interval\")) {\n    api_datainterval=(server.arg(\"interval\").toInt());\n  }\n  if (server.hasArg(\"wait\")) {\n    api_wait=(server.arg(\"wait\").toInt());\n  }\n  if (server.hasArg(\"prettify\")) {\n    prettify=1;\n  }\n\n  const size_t bufferSize = JSON_ARRAY_SIZE(4) + JSON_OBJECT_SIZE(5);\n  DynamicJsonBuffer jsonAPIbuffer(bufferSize);\n  JsonObject& apitxbin = jsonAPIbuffer.createObject();\n\n  apitxbin[\"Device\"] = \"ESP-RFID-Tool\";\n  apitxbin[\"Firmware\"] = version;\n  apitxbin[\"API\"] = APIversion;\n\n  JsonObject& apitxbinary = apitxbin.createNestedObject(\"Transmission\");\n  int commacount=0;\n  for (int commalook=0; commalook<=api_binary.length(); commalook++) {\n      if (api_binary.charAt(commalook)==',') {\n        commacount++;\n      }\n  }\n  apitxbinary[\"Bit Count\"]=api_binary.length()-commacount;\n  apitxbinary[\"Binary\"]=api_binary;\n  apitxbinary[\"Wiegand Data Pulse Width\"]=String()+api_pulsewidth+\"us\";\n  apitxbinary[\"Wiegand Data Interval\"]=String()+api_datainterval+\"us\";\n  apitxbinary[\"Delay Between Packets\"]=String()+api_wait+\"us\";\n  \n  if (api_binary==\"\") {\n    server.send(200, \"text/html\", F(\n      \"Binary to tx not specified.<br>\"\n      \"<small>Usage: [server]/api/tx/bin?binary=[binary]&pulsewidth=[delay_us]&interval=[delay_us]&wait=[delay_us_between_packets]</small><br>\"\n      \"<small>Use commas to separate the binary for transmitting multiple packets(useful for sending multiple keypresses for imitating keypads)</small><br>\"\n      \"<small>Example to TX Pin Code 1337# waiting 100,000us between packets(keypresses): /api/tx/bin?binary=11100001,11000011,11000011,10000111,01001011&wait=100000&prettify=1</small><br>\"\n    ));\n  }\n  else {\n    String API_Response=\"\";\n    if (prettify==1) {\n      apitxbin.prettyPrintTo(API_Response);\n    }\n    else {\n      apitxbin.printTo(API_Response);\n    }\n    server.send(200, \"application/json\", API_Response);\n    delay(50);\n    jsonAPIbuffer.clear();\n    apiTX(api_binary,api_pulsewidth,api_datainterval,api_wait);\n  }\n});\n\nserver.on(\"/api/help\", [](){\n  String apihelpHTML=String()+F(\n  \"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>\"\n  \"<b>API Version: \"\n  )+APIversion+F(\n  \"</b><br><br>\"\n  \"<b><a href=\\\"/api/info?prettify=1\\\">/api/info</a></b><br>\"\n  \"<small>Usage: [server]/api/info</small><br>\"\n  \"<br>\"\n  \"<b><a href=\\\"/api/viewlog?logfile=\"\n  )+logname+F(\n  \"&prettify=1\\\">/api/viewlog</a></b><br>\"\n  \"<small>Usage: [server]/api/viewlog?logfile=[log.txt]</small><br>\"\n  \"<br>\"\n  \"<b><a href=\\\"/api/listlogs?prettify=1\\\">/api/listlogs</a></b><br>\"\n  \"<small>Usage: [server]/api/listlogs</small><br>\"\n  \"<br>\"\n  \"<b><a href=\\\"/api/tx/bin?binary=0001&pulsewidth=40&interval=2000&prettify=1\\\">/api/tx/bin</a></b><br>\"\n  \"<small>Usage: [server]/api/tx/bin?binary=[binary]&pulsewidth=[delay_us]&interval=[delay_us]&wait=[delay_us_between_packets]</small><br>\"\n  \"<small>Use commas to separate the binary for transmitting multiple packets(useful for sending multiple keypresses for imitating keypads)</small><br>\"\n  \"<small>Example to TX Pin Code 1337# waiting 100,000us between packets(keypresses): /api/tx/bin?binary=11100001,11000011,11000011,10000111,01001011&wait=100000&prettify=1</small><br>\"\n  \"<br>\"\n  \"<b>Universal Arguments</b><br>\"\n  \"<small>Prettify: [api-url]?[args]<u>&prettify=1</u></small><br>\"\n  );\n  server.send(200, \"text/html\", apihelpHTML);\n});\n\nserver.on(\"/api/info\", [](){\n  int prettify=0;\n  if (server.hasArg(\"prettify\")) {\n    prettify=1;\n  }\n  apiinfo(prettify);\n});\n\nserver.on(\"/api/listlogs\", [](){\n  int prettify=0;\n  if (server.hasArg(\"prettify\")) {\n    prettify=1;\n  }\n  apilistlogs(prettify);\n});\n\nserver.on(\"/api/viewlog\", [](){\n  int prettify=0;\n  if (server.hasArg(\"prettify\")) {\n    prettify=1;\n  }\n  if (server.hasArg(\"logfile\")) {\n    apilog(server.arg(\"logfile\"),prettify);\n  }\n  else {\n    server.send(200, \"application/json\", F(\"Usage: [server]/api/viewlog?logfile=[logfile.txt]\"));\n  }\n});\n"
  },
  {
    "path": "Source Code/esprfidtool/esprfidtool.ino",
    "content": "/*\n * ESP-RFID-Tool\n * by Corey Harding of www.Exploit.Agency / www.LegacySecurityGroup.com\n * ESP-RFID-Tool Software is distributed under the MIT License. The license and copyright notice can not be removed and must be distributed alongside all future copies of the software.\n * MIT License\n    \n    Copyright (c) [2018] [Corey Harding]\n    \n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n    \n    The above copyright notice and this permission notice shall be included in all\n    copies or substantial portions of the Software.\n    \n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n    SOFTWARE.\n*/\n#include \"HelpText.h\"\n#include \"License.h\"\n#include \"version.h\"\n#include \"strrev.h\"\n#include \"aba2str.h\"\n#include <ESP8266WiFi.h>\n#include <WiFiClient.h>\n#include <ESP8266WebServer.h>\n#include <ESP8266HTTPClient.h>\n#include <ESP8266httpUpdate.h>\n#include <ESP8266HTTPUpdateServer.h>\n#include <ESP8266mDNS.h>\n#include <FS.h>\n#include <ArduinoJson.h> // ArduinoJson library 5.11.0 by Benoit Blanchon https://github.com/bblanchon/ArduinoJson\n#include <ESP8266FtpServer.h> // https://github.com/exploitagency/esp8266FTPServer/tree/feature/bbx10_speedup\n#include <DNSServer.h>\n#include <ESP8266mDNS.h>\n\n#define DATA0 14\n#define DATA1 12\n\n#define LED_BUILTIN 2\n#define RESTORE_DEFAULTS_PIN 4 //GPIO 4\nint jumperState = 0; //For restoring default settings\n#include \"WiegandNG.h\" //https://github.com/jpliew/Wiegand-NG-Multi-Bit-Wiegand-Library-for-Arduino\n\n// Port for web server\nESP8266WebServer server(80);\nESP8266WebServer httpServer(1337);\nESP8266HTTPUpdateServer httpUpdater;\nFtpServer ftpSrv;\nconst byte DNS_PORT = 53;\nDNSServer dnsServer;\n\nHTTPClient http;\n\nconst char* update_path = \"/update\";\nint accesspointmode;\nchar ssid[32];\nchar password[64];\nint channel;\nint hidden;\nchar local_IPstr[16];\nchar gatewaystr[16];\nchar subnetstr[16];\nchar update_username[32];\nchar update_password[64];\nchar ftp_username[32];\nchar ftp_password[64];\nint ftpenabled;\nint ledenabled;\nchar logname[31];\nunsigned int bufferlength;\nunsigned int rxpacketgap;\nint txdelayus;\nint txdelayms;\nint safemode;\n\nint dos=0;\nint TXstatus=0;\nString pinHTML;\n\n#include \"pinSEND.h\"\n\nString dataCONVERSION=\"\";\n\nWiegandNG wg;\n\nvoid LogWiegand(WiegandNG &tempwg) {\n  volatile unsigned char *buffer=tempwg.getRawData();\n  unsigned int bufferSize = tempwg.getBufferSize();\n  unsigned int countedBits = tempwg.getBitCounted();\n\n  unsigned int countedBytes = (countedBits/8);\n  if ((countedBits % 8)>0) countedBytes++;\n  //unsigned int bitsUsed = countedBytes * 8;\n\n  bool binChunk2exists=false;\n  volatile unsigned long cardChunk1 = 0;\n  volatile unsigned long cardChunk2 = 0;\n  volatile unsigned long binChunk2 = 0;\n  volatile unsigned long binChunk1 = 0;\n  String binChunk3=\"\";\n  bool unknown=false;\n  binChunk2exists=false;\n  int binChunk2len=0;\n  int j=0;\n  \n  for (unsigned int i=bufferSize-countedBytes; i< bufferSize;i++) {\n    unsigned char bufByte=buffer[i];\n    for(int x=0; x<8;x++) {\n      if ( (((bufferSize-i) *8)-x) <= countedBits) {\n        j++;\n        if((bufByte & 0x80)) {  //write 1\n          if(j<23) {\n            binChunk1 = binChunk1 << 1;\n            binChunk1 |= 1;\n          }\n          else if(j<=52) {\n            binChunk2exists=true;\n            binChunk2len++;\n            binChunk2 = binChunk2 << 1;\n            binChunk2 |= 1;\n          }\n          else if(j>52){\n            binChunk3=binChunk3+\"1\";\n          }\n        }\n        else {  //write 0\n          if(j<23) {\n            binChunk1 = binChunk1 << 1;\n          }\n          else if(j<=52){\n            binChunk2exists=true;\n            binChunk2len++;\n            binChunk2 = binChunk2 << 1;\n          }\n          else if(j>52){\n            binChunk3=binChunk3+\"0\";\n          }\n        }\n      }\n      bufByte<<=1;\n    }\n  }\n  j=0;\n\n  switch (countedBits) {  //Add the preamble to known cards\n    case 26:\n      for(int i = 19; i >= 0; i--) {\n        if(i == 13 || i == 2){\n          bitWrite(cardChunk1, i, 1); // Write preamble 1's to the 13th and 2nd bits\n        }\n        else if(i > 2) {\n          bitWrite(cardChunk1, i, 0); // Write preamble 0's to all other bits above 1\n        }\n        else {\n          bitWrite(cardChunk1, i, bitRead(binChunk1, i + 20)); // Write remaining bits to cardChunk1 from binChunk1\n        }\n        if(i < 20) {\n          bitWrite(cardChunk2, i + 4, bitRead(binChunk1, i)); // Write the remaining bits of binChunk1 to cardChunk2\n        }\n        if(i < 4) {\n          bitWrite(cardChunk2, i, bitRead(binChunk2, i)); // Write the remaining bit of cardChunk2 with binChunk2 bits\n        }\n      }\n      break;\n    case 27:\n      for(int i = 19; i >= 0; i--) {\n        if(i == 13 || i == 3){\n          bitWrite(cardChunk1, i, 1);\n        }\n        else if(i > 3) {\n          bitWrite(cardChunk1, i, 0);\n        }\n        else {\n          bitWrite(cardChunk1, i, bitRead(binChunk1, i + 19));\n        }\n        if(i < 19) {\n          bitWrite(cardChunk2, i + 5, bitRead(binChunk1, i));\n        }\n        if(i < 5) {\n          bitWrite(cardChunk2, i, bitRead(binChunk2, i));\n        }\n      }\n      break;\n    case 28:\n      for(int i = 19; i >= 0; i--) {\n        if(i == 13 || i == 4){\n          bitWrite(cardChunk1, i, 1);\n        }\n        else if(i > 4) {\n          bitWrite(cardChunk1, i, 0);\n        }\n        else {\n          bitWrite(cardChunk1, i, bitRead(binChunk1, i + 18));\n        }\n        if(i < 18) {\n          bitWrite(cardChunk2, i + 6, bitRead(binChunk1, i));\n        }\n        if(i < 6) {\n          bitWrite(cardChunk2, i, bitRead(binChunk2, i));\n        }\n      }\n      break;\n    case 29:\n      for(int i = 19; i >= 0; i--) {\n        if(i == 13 || i == 5){\n          bitWrite(cardChunk1, i, 1);\n        }\n        else if(i > 5) {\n          bitWrite(cardChunk1, i, 0);\n        }\n        else {\n          bitWrite(cardChunk1, i, bitRead(binChunk1, i + 17));\n        }\n        if(i < 17) {\n          bitWrite(cardChunk2, i + 7, bitRead(binChunk1, i));\n        }\n        if(i < 7) {\n          bitWrite(cardChunk2, i, bitRead(binChunk2, i));\n        }\n      }\n      break;\n    case 30:\n      for(int i = 19; i >= 0; i--) {\n        if(i == 13 || i == 6){\n          bitWrite(cardChunk1, i, 1);\n        }\n        else if(i > 6) {\n          bitWrite(cardChunk1, i, 0);\n        }\n        else {\n          bitWrite(cardChunk1, i, bitRead(binChunk1, i + 16));\n        }\n        if(i < 16) {\n          bitWrite(cardChunk2, i + 8, bitRead(binChunk1, i));\n        }\n        if(i < 8) {\n          bitWrite(cardChunk2, i, bitRead(binChunk2, i));\n        }\n      }\n      break;\n    case 31:\n      for(int i = 19; i >= 0; i--) {\n        if(i == 13 || i == 7){\n          bitWrite(cardChunk1, i, 1);\n        }\n        else if(i > 7) {\n          bitWrite(cardChunk1, i, 0);\n        }\n        else {\n          bitWrite(cardChunk1, i, bitRead(binChunk1, i + 15));\n        }\n        if(i < 15) {\n          bitWrite(cardChunk2, i + 9, bitRead(binChunk1, i));\n        }\n        if(i < 9) {\n          bitWrite(cardChunk2, i, bitRead(binChunk2, i));\n        }\n      }\n      break;\n    case 32:\n      for(int i = 19; i >= 0; i--) {\n        if(i == 13 || i == 8){\n          bitWrite(cardChunk1, i, 1);\n        }\n        else if(i > 8) {\n          bitWrite(cardChunk1, i, 0);\n        }\n        else {\n          bitWrite(cardChunk1, i, bitRead(binChunk1, i + 14));\n        }\n        if(i < 14) {\n          bitWrite(cardChunk2, i + 10, bitRead(binChunk1, i));\n        }\n        if(i < 10) {\n          bitWrite(cardChunk2, i, bitRead(binChunk2, i));\n        }\n      }\n      break;\n    case 33:\n      for(int i = 19; i >= 0; i--) {\n        if(i == 13 || i == 9){\n          bitWrite(cardChunk1, i, 1);\n        }\n        else if(i > 9) {\n          bitWrite(cardChunk1, i, 0);\n        }\n        else {\n          bitWrite(cardChunk1, i, bitRead(binChunk1, i + 13));\n        }\n        if(i < 13) {\n          bitWrite(cardChunk2, i + 11, bitRead(binChunk1, i));\n        }\n        if(i < 11) {\n          bitWrite(cardChunk2, i, bitRead(binChunk2, i));\n        }\n      }\n      break;\n    case 34:\n      for(int i = 19; i >= 0; i--) {\n        if(i == 13 || i == 10){\n          bitWrite(cardChunk1, i, 1);\n        }\n        else if(i > 10) {\n          bitWrite(cardChunk1, i, 0);\n        }\n        else {\n          bitWrite(cardChunk1, i, bitRead(binChunk1, i + 12));\n        }\n        if(i < 12) {\n          bitWrite(cardChunk2, i + 12, bitRead(binChunk1, i));\n        }\n        if(i < 12) {\n          bitWrite(cardChunk2, i, bitRead(binChunk2, i));\n        }\n      }\n      break;\n    case 35:\n      for(int i = 19; i >= 0; i--) {\n        if(i == 13 || i == 11){\n          bitWrite(cardChunk1, i, 1);\n        }\n        else if(i > 11) {\n          bitWrite(cardChunk1, i, 0);\n        }\n        else {\n          bitWrite(cardChunk1, i, bitRead(binChunk1, i + 11));\n        }\n        if(i < 11) {\n          bitWrite(cardChunk2, i + 13, bitRead(binChunk1, i));\n        }\n        if(i < 13) {\n          bitWrite(cardChunk2, i, bitRead(binChunk2, i));\n        }\n      }\n      break;\n    case 36:\n      for(int i = 19; i >= 0; i--) {\n        if(i == 13 || i == 12){\n          bitWrite(cardChunk1, i, 1);\n        }\n        else if(i > 12) {\n          bitWrite(cardChunk1, i, 0);\n        }\n        else {\n          bitWrite(cardChunk1, i, bitRead(binChunk1, i + 10));\n        }\n        if(i < 10) {\n          bitWrite(cardChunk2, i + 14, bitRead(binChunk1, i));\n        }\n        if(i < 14) {\n          bitWrite(cardChunk2, i, bitRead(binChunk2, i));\n        }\n      }\n      break;\n    case 37:\n      for(int i = 19; i >= 0; i--) {\n        if(i == 13){\n          bitWrite(cardChunk1, i, 0);\n        }\n        else {\n          bitWrite(cardChunk1, i, bitRead(binChunk1, i + 9));\n        }\n        if(i < 9) {\n          bitWrite(cardChunk2, i + 15, bitRead(binChunk1, i));\n        }\n        if(i < 15) {\n          bitWrite(cardChunk2, i, bitRead(binChunk2, i));\n        }\n      }\n      break;\n    default:  //unknown card\n      unknown=true;\n      //String binChunk3 is like cardChunk0\n      cardChunk1=binChunk2;\n      cardChunk2=binChunk1;\n      break;\n  }\n\n  File f = SPIFFS.open(\"/\"+String(logname), \"a\"); //Open the log in append mode to store capture\n  int preambleLen;\n  if (unknown==true && countedBits!=4 && countedBits!=8 && countedBits!=248) {\n    f.print(F(\"Unknown \"));\n    preambleLen=0;\n  }\n  else {\n    preambleLen=(44-countedBits);\n  }\n  \n  f.print(String()+countedBits+F(\" bit card,\"));\n\n  if (countedBits==4||countedBits==8) {\n    f.print(F(\"Possible keypad entry,\"));\n  }\n\n  if (countedBits==248) {\n    f.print(F(\"possible magstripe card,\"));\n  }\n  String magstripe=\"\";\n\n  if (unknown!=true) {\n    f.print(String()+preambleLen+F(\" bit preamble,\"));\n  }\n  \n  f.print(F(\"Binary:\"));\n\n  //f.print(\" \");  //debug line\n  if (binChunk2exists==true && unknown!=true) {\n    for(int i = (((countedBits+preambleLen)-countedBits)+(countedBits-24)); i--;) {\n      if (i==((((countedBits+preambleLen)-countedBits)+(countedBits-24))-preambleLen-1) && unknown!=true) {\n        f.print(\" \");\n      }\n      f.print(bitRead(cardChunk1, i));\n      if(i == 0){\n        break;\n      }\n    }\n  }\n  \n  if ((countedBits>=24) && unknown!=true) {\n    for(int i = 24; i--;) {\n      f.print(bitRead(cardChunk2, i));\n      if(i == 0){\n        break;\n      }\n    }\n  }\n  else if ((countedBits>=23) && unknown==true) {\n    int i;\n    if (countedBits>=52) {\n      i=22;\n    }\n    else {\n      i =(countedBits-binChunk2len);\n    }\n    for(i; i--;) {\n      f.print(bitRead(binChunk1, i));\n      if (countedBits==248) {\n        magstripe+=bitRead(binChunk1, i);\n      }\n      if(i == 0){\n        break;\n      }\n    }\n  }\n  else {\n    for(int i = countedBits; i--;) {\n      f.print(bitRead(binChunk1, i));\n      if(i == 0){\n        break;\n      }\n    }\n  }\n\n  if (binChunk2exists==true && unknown==true) {\n    int i;\n    if (countedBits>=52) {\n      i=30;\n    }\n    else {\n      i=(binChunk2len);\n    }\n    for(i; i--;) {\n      f.print(bitRead(binChunk2, i));\n      if (countedBits==248) {\n        magstripe+=bitRead(binChunk2, i);\n      }\n      if(i == 0){\n        break;\n      }\n    }\n  }\n\n  if (countedBits>52) {\n    f.print(binChunk3);\n    if (countedBits==248) {\n        magstripe+=binChunk3;\n    }\n  }\n\n  if (countedBits<=52 && unknown!=true) {\n    f.print(\",HEX:\");\n    if (binChunk2exists==true) {\n      f.print(cardChunk1, HEX);\n    }\n    //f.print(\" \"); //debug line\n    f.println(cardChunk2, HEX);\n  }\n  else if (countedBits==4||countedBits==8) {\n    f.print(\",Keypad Code:\");\n    if (binChunk1 == 0B0000||binChunk1 == 0b11110000) {\n      f.print(\"0\");\n    }\n    else if (binChunk1 == 0B0001||binChunk1 == 0b11100001) {\n      f.print(\"1\");\n    }\n    else if (binChunk1 == 0B0010||binChunk1 == 0b11010010) {\n      f.print(\"2\");\n    }\n    else if (binChunk1 == 0B0011||binChunk1 == 0b11000011) {\n      f.print(\"3\");\n    }\n    else if (binChunk1 == 0B0100||binChunk1 == 0b10110100) {\n      f.print(\"4\");\n    }\n    else if (binChunk1 == 0B0101||binChunk1 == 0b10100101) {\n      f.print(\"5\");\n    }\n    else if (binChunk1 == 0B0110||binChunk1 == 0b10010110) {\n      f.print(\"6\");\n    }\n    else if (binChunk1 == 0B0111||binChunk1 == 0b10000111) {\n      f.print(\"7\");\n    }\n    else if (binChunk1 == 0B1000||binChunk1 == 0b01111000) {\n      f.print(\"8\");\n    }\n    else if (binChunk1 == 0B1001||binChunk1 == 0b01101001) {\n      f.print(\"9\");\n    }\n    else if (binChunk1 == 0B1010||binChunk1 == 0b01011010) {\n      f.print(\"*\");\n    }\n    else if (binChunk1 == 0B1011||binChunk1 == 0b01001011) {\n      f.print(\"#\");\n    }\n    else if (binChunk1 == 0b1100||binChunk1 == 0b00111100) {\n      f.print(\"F1\");\n    }\n    else if (binChunk1 == 0b1101||binChunk1 == 0b00101101) {\n      f.print(\"F2\");\n    }\n    else if (binChunk1 == 0b1110||binChunk1 == 0b00011110) {\n      f.print(\"F3\");\n    }\n    else if (binChunk1 == 0b1111||binChunk1 == 0b00001111) {\n      f.print(\"F4\");\n    }\n    else {\n      f.print(\"?\");\n    }\n    f.print(\",HEX:\");\n    if (countedBits==8) {\n      char hexCHAR[3];\n      sprintf(hexCHAR, \"%02X\", binChunk1);\n      f.println(hexCHAR);\n    }\n    else if (countedBits==4) {\n      f.println(binChunk1, HEX);\n    }\n  }\n  else if (countedBits==248) {\n    f.println(\",\");\n  }\n  else {\n    f.println(\"\");\n  }\n\n  if (countedBits==248) {\n    int startSentinel=magstripe.indexOf(\"11010\");\n    int endSentinel=(magstripe.lastIndexOf(\"11111\")+4);\n    int magStart=0;\n    int magEnd=1;\n    //f.print(\"<pre>\");\n  \n    f.print(\" * Trying \\\"Forward\\\" Swipe,\");\n    magStart=startSentinel;\n    magEnd=endSentinel;\n    f.println(aba2str(magstripe,magStart,magEnd,\"\\\"Forward\\\" Swipe\"));\n    \n    f.print(\" * Trying \\\"Reverse\\\" Swipe,\");\n    char magchar[249];\n    magstripe.toCharArray(magchar,249);\n    magstripe=String(strrev(magchar));\n    //f.println(String()+\"Reverse: \"+magstripe);\n    magStart=magstripe.indexOf(\"11010\");\n    magEnd=(magstripe.lastIndexOf(\"11111\")+4);\n    f.println(aba2str(magstripe,magStart,magEnd,\"\\\"Reverse\\\" Swipe\"));\n  \n    //f.print(\"</pre>\");\n    //f.println(String()+F(\" * You can verify the data at the following URL: <a target=\\\"_blank\\\" href=\\\"https://www.legacysecuritygroup.com/aba-decode.php?binary=\")+magstripe+F(\"\\\">https://www.legacysecuritygroup.com/aba-decode.php?binary=\")+magstripe+F(\"</a>\"));\n  }\n\n//Debug\n//  f.print(F(\"Free heap:\"));\n//  f.println(ESP.getFreeHeap(),DEC);\n  \n  unknown=false;\n  binChunk3=\"\";\n  binChunk2exists=false;\n  binChunk1 = 0; binChunk2 = 0;\n  cardChunk1 = 0; cardChunk2 = 0;\n  binChunk2len=0;\n\n  f.close(); //done\n}\n\n#include \"api.h\"\n\nvoid settingsPage()\n{\n  if(!server.authenticate(update_username, update_password))\n    return server.requestAuthentication();\n  String accesspointmodeyes;\n  String accesspointmodeno;\n  if (accesspointmode==1){\n    accesspointmodeyes=\" checked=\\\"checked\\\"\";\n    accesspointmodeno=\"\";\n  }\n  else {\n    accesspointmodeyes=\"\";\n    accesspointmodeno=\" checked=\\\"checked\\\"\";\n  }\n  String ftpenabledyes;\n  String ftpenabledno;\n  if (ftpenabled==1){\n    ftpenabledyes=\" checked=\\\"checked\\\"\";\n    ftpenabledno=\"\";\n  }\n  else {\n    ftpenabledyes=\"\";\n    ftpenabledno=\" checked=\\\"checked\\\"\";\n  }\n  String ledenabledyes;\n  String ledenabledno;\n  if (ledenabled==1){\n    ledenabledyes=\" checked=\\\"checked\\\"\";\n    ledenabledno=\"\";\n  }\n  else {\n    ledenabledyes=\"\";\n    ledenabledno=\" checked=\\\"checked\\\"\";\n  }\n  String hiddenyes;\n  String hiddenno;\n  if (hidden==1){\n    hiddenyes=\" checked=\\\"checked\\\"\";\n    hiddenno=\"\";\n  }\n  else {\n    hiddenyes=\"\";\n    hiddenno=\" checked=\\\"checked\\\"\";\n  }\n  String safemodeyes;\n  String safemodeno;\n  if (safemode==1){\n    safemodeyes=\" checked=\\\"checked\\\"\";\n    safemodeno=\"\";\n  }\n  else {\n    safemodeyes=\"\";\n    safemodeno=\" checked=\\\"checked\\\"\";\n  }\n  server.send(200, \"text/html\", \n  String()+\n  F(\n  \"<!DOCTYPE HTML>\"\n  \"<html>\"\n  \"<head>\"\n  \"<meta name = \\\"viewport\\\" content = \\\"width = device-width, initial-scale = 1.0, maximum-scale = 1.0, user-scalable=0\\\">\"\n  \"<title>ESP-RFID-Tool Settings</title>\"\n  \"<style>\"\n  \"\\\"body { background-color: #808080; font-family: Arial, Helvetica, Sans-Serif; Color: #000000; }\\\"\"\n  \"</style>\"\n  \"</head>\"\n  \"<body>\"\n  \"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>\"\n  \"<h1>ESP-RFID-Tool Settings</h1>\"\n  \"<a href=\\\"/restoredefaults\\\"><button>Restore Default Configuration</button></a>\"\n  \"<hr>\"\n  \"<FORM action=\\\"/settings\\\"  id=\\\"configuration\\\" method=\\\"post\\\">\"\n  \"<P>\"\n  \"<b>WiFi Configuration:</b><br><br>\"\n  \"<b>Network Type</b><br>\"\n  )+\n  F(\"Access Point Mode: <INPUT type=\\\"radio\\\" name=\\\"accesspointmode\\\" value=\\\"1\\\"\")+accesspointmodeyes+F(\"><br>\"\n  \"Join Existing Network: <INPUT type=\\\"radio\\\" name=\\\"accesspointmode\\\" value=\\\"0\\\"\")+accesspointmodeno+F(\"><br><br>\"\n  \"<b>Hidden<br></b>\"\n  \"Yes <INPUT type=\\\"radio\\\" name=\\\"hidden\\\" value=\\\"1\\\"\")+hiddenyes+F(\"><br>\"\n  \"No <INPUT type=\\\"radio\\\" name=\\\"hidden\\\" value=\\\"0\\\"\")+hiddenno+F(\"><br><br>\"\n  \"SSID: <input type=\\\"text\\\" name=\\\"ssid\\\" value=\\\"\")+ssid+F(\"\\\" maxlength=\\\"31\\\" size=\\\"31\\\"><br>\"\n  \"Password: <input type=\\\"password\\\" name=\\\"password\\\" value=\\\"\")+password+F(\"\\\" maxlength=\\\"64\\\" size=\\\"31\\\"><br>\"\n  \"Channel: <select name=\\\"channel\\\" form=\\\"configuration\\\"><option value=\\\"\")+channel+\"\\\" selected>\"+channel+F(\"</option><option value=\\\"1\\\">1</option><option value=\\\"2\\\">2</option><option value=\\\"3\\\">3</option><option value=\\\"4\\\">4</option><option value=\\\"5\\\">5</option><option value=\\\"6\\\">6</option><option value=\\\"7\\\">7</option><option value=\\\"8\\\">8</option><option value=\\\"9\\\">9</option><option value=\\\"10\\\">10</option><option value=\\\"11\\\">11</option><option value=\\\"12\\\">12</option><option value=\\\"13\\\">13</option><option value=\\\"14\\\">14</option></select><br><br>\"\n  \"IP: <input type=\\\"text\\\" name=\\\"local_IPstr\\\" value=\\\"\")+local_IPstr+F(\"\\\" maxlength=\\\"16\\\" size=\\\"31\\\"><br>\"\n  \"Gateway: <input type=\\\"text\\\" name=\\\"gatewaystr\\\" value=\\\"\")+gatewaystr+F(\"\\\" maxlength=\\\"16\\\" size=\\\"31\\\"><br>\"\n  \"Subnet: <input type=\\\"text\\\" name=\\\"subnetstr\\\" value=\\\"\")+subnetstr+F(\"\\\" maxlength=\\\"16\\\" size=\\\"31\\\"><br><br>\"\n  \"<hr>\"\n  \"<b>Web Interface Administration Settings:</b><br><br>\"\n  \"Username: <input type=\\\"text\\\" name=\\\"update_username\\\" value=\\\"\")+update_username+F(\"\\\" maxlength=\\\"31\\\" size=\\\"31\\\"><br>\"\n  \"Password: <input type=\\\"password\\\" name=\\\"update_password\\\" value=\\\"\")+update_password+F(\"\\\" maxlength=\\\"64\\\" size=\\\"31\\\"><br><br>\"\n  \"<hr>\"\n  \"<b>FTP Server Settings</b><br>\"\n  \"<small>Changes require a reboot.</small><br>\"\n  \"Enabled <INPUT type=\\\"radio\\\" name=\\\"ftpenabled\\\" value=\\\"1\\\"\")+ftpenabledyes+F(\"><br>\"\n  \"Disabled <INPUT type=\\\"radio\\\" name=\\\"ftpenabled\\\" value=\\\"0\\\"\")+ftpenabledno+F(\"><br>\"\n  \"FTP Username: <input type=\\\"text\\\" name=\\\"ftp_username\\\" value=\\\"\")+ftp_username+F(\"\\\" maxlength=\\\"31\\\" size=\\\"31\\\"><br>\"\n  \"FTP Password: <input type=\\\"password\\\" name=\\\"ftp_password\\\" value=\\\"\")+ftp_password+F(\"\\\" maxlength=\\\"64\\\" size=\\\"31\\\"><br><br>\"\n  \"<hr>\"\n  \"<b>Power LED:</b><br>\"\n  \"<small>Changes require a reboot.</small><br>\"\n  \"Enabled <INPUT type=\\\"radio\\\" name=\\\"ledenabled\\\" value=\\\"1\\\"\")+ledenabledyes+F(\"><br>\"\n  \"Disabled <INPUT type=\\\"radio\\\" name=\\\"ledenabled\\\" value=\\\"0\\\"\")+ledenabledno+F(\"><br><br>\"\n  \"<hr>\"\n  \"<b>RFID Capture Log:</b><br>\"\n  \"<small>Useful to change this value to differentiate between facilities during various security assessments.</small><br>\"\n  \"File Name: <input type=\\\"text\\\" name=\\\"logname\\\" value=\\\"\")+logname+F(\"\\\" maxlength=\\\"30\\\" size=\\\"31\\\"><br>\"\n  \"<hr>\"\n  \"<b>Experimental Settings:</b><br>\"\n  \"<small>Changes require a reboot.</small><br>\"\n  \"<small>Default Buffer Length is 256 bits with an allowed range of 52-4096 bits.\"\n  \"<br>Default Experimental TX mode timing is 40us Wiegand Data Pulse Width and a 2ms Wiegand Data Interval with an allowed range of 0-1000.\"\n  \"<br>Changing these settings may result in unstable performance.</small><br>\"\n  \"Wiegand RX Buffer Length: <input type=\\\"number\\\" name=\\\"bufferlength\\\" value=\\\"\")+bufferlength+F(\"\\\" maxlength=\\\"30\\\" size=\\\"31\\\" min=\\\"52\\\" max=\\\"4096\\\"> bit(s)<br>\"\n  \"Wiegand RX Packet Length: <input type=\\\"number\\\" name=\\\"rxpacketgap\\\" value=\\\"\")+rxpacketgap+F(\"\\\" maxlength=\\\"30\\\" size=\\\"31\\\" min=\\\"1\\\" max=\\\"4096\\\"> millisecond(s)<br>\"\n  \"Experimental TX Wiegand Data Pulse Width: <input type=\\\"number\\\" name=\\\"txdelayus\\\" value=\\\"\")+txdelayus+F(\"\\\" maxlength=\\\"30\\\" size=\\\"31\\\" min=\\\"0\\\" max=\\\"1000\\\"> microsecond(s)<br>\"\n  \"Experimental TX Wiegand Data Interval: <input type=\\\"number\\\" name=\\\"txdelayms\\\" value=\\\"\")+txdelayms+F(\"\\\" maxlength=\\\"30\\\" size=\\\"31\\\" min=\\\"0\\\" max=\\\"1000\\\"> millisecond(s)<br>\"\n  \"<hr>\"\n  \"<b>Safe Mode:</b><br>\"\n  \"<small>Enable to reboot the device after every capture.<br>Disable to avoid missing quick consecutive captures such as keypad entries.</small><br>\"\n  \"Enabled <INPUT type=\\\"radio\\\" name=\\\"safemode\\\" value=\\\"1\\\"\")+safemodeyes+F(\"><br>\"\n  \"Disabled <INPUT type=\\\"radio\\\" name=\\\"safemode\\\" value=\\\"0\\\"\")+safemodeno+F(\"><br><br>\"\n  \"<hr>\"\n  \"<INPUT type=\\\"radio\\\" name=\\\"SETTINGS\\\" value=\\\"1\\\" hidden=\\\"1\\\" checked=\\\"checked\\\">\"\n  \"<INPUT type=\\\"submit\\\" value=\\\"Apply Settings\\\">\"\n  \"</FORM>\"\n  \"<br><a href=\\\"/reboot\\\"><button>Reboot Device</button></a>\"\n  \"</P>\"\n  \"</body>\"\n  \"</html>\"\n  )\n  );\n}\n\nvoid handleSettings()\n{\n  if (server.hasArg(\"SETTINGS\")) {\n    handleSubmitSettings();\n  }\n  else {\n    settingsPage();\n  }\n}\n\nvoid returnFail(String msg)\n{\n  server.sendHeader(\"Connection\", \"close\");\n  server.sendHeader(\"Access-Control-Allow-Origin\", \"*\");\n  server.send(500, \"text/plain\", msg + \"\\r\\n\");\n}\n\nvoid handleSubmitSettings()\n{\n  String SETTINGSvalue;\n\n  if (!server.hasArg(\"SETTINGS\")) return returnFail(\"BAD ARGS\");\n  \n  SETTINGSvalue = server.arg(\"SETTINGS\");\n  accesspointmode = server.arg(\"accesspointmode\").toInt();\n  server.arg(\"ssid\").toCharArray(ssid, 32);\n  server.arg(\"password\").toCharArray(password, 64);\n  channel = server.arg(\"channel\").toInt();\n  hidden = server.arg(\"hidden\").toInt();\n  server.arg(\"local_IPstr\").toCharArray(local_IPstr, 16);\n  server.arg(\"gatewaystr\").toCharArray(gatewaystr, 16);\n  server.arg(\"subnetstr\").toCharArray(subnetstr, 16);\n  server.arg(\"update_username\").toCharArray(update_username, 32);\n  server.arg(\"update_password\").toCharArray(update_password, 64);\n  server.arg(\"ftp_username\").toCharArray(ftp_username, 32);\n  server.arg(\"ftp_password\").toCharArray(ftp_password, 64);\n  ftpenabled = server.arg(\"ftpenabled\").toInt();\n  ledenabled = server.arg(\"ledenabled\").toInt();\n  server.arg(\"logname\").toCharArray(logname, 31);\n  bufferlength = server.arg(\"bufferlength\").toInt();\n  rxpacketgap = server.arg(\"rxpacketgap\").toInt();\n  txdelayus = server.arg(\"txdelayus\").toInt();\n  txdelayms = server.arg(\"txdelayms\").toInt();\n  safemode = server.arg(\"safemode\").toInt();\n  \n  if (SETTINGSvalue == \"1\") {\n    saveConfig();\n    server.send(200, \"text/html\", F(\"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br><a href=\\\"/reboot\\\"><button>Reboot Device</button></a><br><br>Settings have been saved.<br>Some setting may require manually rebooting before taking effect.<br>If network configuration has changed then be sure to connect to the new network first in order to access the web interface.\"));\n    delay(50);\n    loadConfig();\n  }\n  else if (SETTINGSvalue == \"0\") {\n    settingsPage();\n  }\n  else {\n    returnFail(\"Bad SETTINGS value\");\n  }\n}\n\nbool loadDefaults() {\n  StaticJsonBuffer<500> jsonBuffer;\n  JsonObject& json = jsonBuffer.createObject();\n  json[\"version\"] = version;\n  json[\"accesspointmode\"] = \"1\";\n  json[\"ssid\"] = \"ESP-RFID-Tool\";\n  json[\"password\"] = \"\";\n  json[\"channel\"] = \"6\";\n  json[\"hidden\"] = \"0\";\n  json[\"local_IP\"] = \"192.168.1.1\";\n  json[\"gateway\"] = \"192.168.1.1\";\n  json[\"subnet\"] = \"255.255.255.0\";\n  json[\"update_username\"] = \"admin\";\n  json[\"update_password\"] = \"rfidtool\";\n  json[\"ftp_username\"] = \"ftp-admin\";\n  json[\"ftp_password\"] = \"rfidtool\";\n  json[\"ftpenabled\"] = \"0\";\n  json[\"ledenabled\"] = \"1\";\n  json[\"logname\"] = \"log.txt\";\n  json[\"bufferlength\"] = \"256\";\n  json[\"rxpacketgap\"] = \"15\";\n  json[\"txdelayus\"] = \"40\";\n  json[\"txdelayms\"] = \"2\";\n  json[\"safemode\"] = \"0\";\n  File configFile = SPIFFS.open(\"/esprfidtool.json\", \"w\");\n  json.printTo(configFile);\n  configFile.close();\n  jsonBuffer.clear();\n  loadConfig();\n}\n\nbool loadConfig() {\n  File configFile = SPIFFS.open(\"/esprfidtool.json\", \"r\");\n  if (!configFile) {\n    delay(3500);\n    loadDefaults();\n  }\n\n  size_t size = configFile.size();\n\n  std::unique_ptr<char[]> buf(new char[size]);\n  configFile.readBytes(buf.get(), size);\n  StaticJsonBuffer<500> jsonBuffer;\n  JsonObject& json = jsonBuffer.parseObject(buf.get());\n  \n  if (!json[\"version\"]) {\n    delay(3500);\n    loadDefaults();\n    ESP.restart();\n  }\n\n  //Resets config to factory defaults on an update.\n  if (json[\"version\"]!=version) {\n    delay(3500);\n    loadDefaults();\n    ESP.restart();\n  }\n\n  strcpy(ssid, (const char*)json[\"ssid\"]);\n  strcpy(password, (const char*)json[\"password\"]);\n  channel = json[\"channel\"];\n  hidden = json[\"hidden\"];\n  accesspointmode = json[\"accesspointmode\"];\n  strcpy(local_IPstr, (const char*)json[\"local_IP\"]);\n  strcpy(gatewaystr, (const char*)json[\"gateway\"]);\n  strcpy(subnetstr, (const char*)json[\"subnet\"]);\n\n  strcpy(update_username, (const char*)json[\"update_username\"]);\n  strcpy(update_password, (const char*)json[\"update_password\"]);\n\n  strcpy(ftp_username, (const char*)json[\"ftp_username\"]);\n  strcpy(ftp_password, (const char*)json[\"ftp_password\"]);\n  ftpenabled = json[\"ftpenabled\"];\n  ledenabled = json[\"ledenabled\"];\n  strcpy(logname, (const char*)json[\"logname\"]);\n  bufferlength = json[\"bufferlength\"];\n  rxpacketgap = json[\"rxpacketgap\"];\n  txdelayus = json[\"txdelayus\"];\n  txdelayms = json[\"txdelayms\"];\n  safemode = json[\"safemode\"];\n \n  IPAddress local_IP;\n  local_IP.fromString(local_IPstr);\n  IPAddress gateway;\n  gateway.fromString(gatewaystr);\n  IPAddress subnet;\n  subnet.fromString(subnetstr);\n\n/*\n  Serial.println(accesspointmode);\n  Serial.println(ssid);\n  Serial.println(password);\n  Serial.println(channel);\n  Serial.println(hidden);\n  Serial.println(local_IP);\n  Serial.println(gateway);\n  Serial.println(subnet);\n*/\n  WiFi.persistent(false);\n  //ESP.eraseConfig();\n// Determine if set to Access point mode\n  if (accesspointmode == 1) {\n    WiFi.disconnect(true);\n    WiFi.mode(WIFI_AP);\n\n//    Serial.print(\"Starting Access Point ... \");\n//    Serial.println(WiFi.softAP(ssid, password, channel, hidden) ? \"Success\" : \"Failed!\");\n    WiFi.softAP(ssid, password, channel, hidden);\n\n//    Serial.print(\"Setting up Network Configuration ... \");\n//    Serial.println(WiFi.softAPConfig(local_IP, gateway, subnet) ? \"Success\" : \"Failed!\");\n    WiFi.softAPConfig(local_IP, gateway, subnet);\n\n//    WiFi.reconnect();\n\n//    Serial.print(\"IP address = \");\n//    Serial.println(WiFi.softAPIP());\n  }\n// or Join existing network\n  else if (accesspointmode != 1) {\n    WiFi.disconnect(true);\n    WiFi.mode(WIFI_STA);\n//    Serial.print(\"Setting up Network Configuration ... \");\n    WiFi.config(local_IP, gateway, subnet);\n//    WiFi.config(local_IP, gateway, subnet);\n\n//    Serial.print(\"Connecting to network ... \");\n//    WiFi.begin(ssid, password);\n    WiFi.begin(ssid, password);\n    WiFi.reconnect();\n\n//    Serial.print(\"IP address = \");\n//    Serial.println(WiFi.localIP());\n  }\n  configFile.close();\n  jsonBuffer.clear();\n  return true;\n}\n\nbool saveConfig() {\n  StaticJsonBuffer<500> jsonBuffer;\n  JsonObject& json = jsonBuffer.createObject();\n  json[\"version\"] = version;\n  json[\"accesspointmode\"] = accesspointmode;\n  json[\"ssid\"] = ssid;\n  json[\"password\"] = password;\n  json[\"channel\"] = channel;\n  json[\"hidden\"] = hidden;\n  json[\"local_IP\"] = local_IPstr;\n  json[\"gateway\"] = gatewaystr;\n  json[\"subnet\"] = subnetstr;\n  json[\"update_username\"] = update_username;\n  json[\"update_password\"] = update_password;\n  json[\"ftp_username\"] = ftp_username;\n  json[\"ftp_password\"] = ftp_password;\n  json[\"ftpenabled\"] = ftpenabled;\n  json[\"ledenabled\"] = ledenabled;\n  json[\"logname\"] = logname;\n  json[\"bufferlength\"] = bufferlength;\n  json[\"rxpacketgap\"] = rxpacketgap;\n  json[\"txdelayus\"] = txdelayus;\n  json[\"txdelayms\"] = txdelayms;\n  json[\"safemode\"] = safemode;\n\n  File configFile = SPIFFS.open(\"/esprfidtool.json\", \"w\");\n  json.printTo(configFile);\n  configFile.close();\n  jsonBuffer.clear();\n  return true;\n}\n\nFile fsUploadFile;\nString webString;\n\nvoid ListLogs(){\n  String directory;\n  directory=\"/\";\n  FSInfo fs_info;\n  SPIFFS.info(fs_info);\n  String total;\n  total=fs_info.totalBytes;\n  String used;\n  used=fs_info.usedBytes;\n  String freespace;\n  freespace=fs_info.totalBytes-fs_info.usedBytes;\n  Dir dir = SPIFFS.openDir(directory);\n  String FileList = String()+F(\"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>File System Info Calculated in Bytes<br><b>Total:</b> \")+total+\" <b>Free:</b> \"+freespace+\" \"+\" <b>Used:</b> \"+used+\"<br><br><small>NOTE: Larger log files will need to be downloaded instead of viewed from the browser.</small><br><table border='1'><tr><td><b>Display File Contents</b></td><td><b>Size in Bytes</b></td><td><b>Download File</b></td><td><b>Delete File</b></td></tr>\";\n  while (dir.next()) {\n    String FileName = dir.fileName();\n    File f = dir.openFile(\"r\");\n    FileList += \" \";\n    if((!FileName.startsWith(\"/payloads/\"))&&(!FileName.startsWith(\"/esploit.json\"))&&(!FileName.startsWith(\"/esportal.json\"))&&(!FileName.startsWith(\"/esprfidtool.json\"))&&(!FileName.startsWith(\"/config.json\"))) FileList += \"<tr><td><a href=\\\"/viewlog?payload=\"+FileName+\"\\\">\"+FileName+\"</a></td>\"+\"<td>\"+f.size()+\"</td><td><a href=\\\"\"+FileName+\"\\\"><button>Download File</button></td><td><a href=\\\"/deletelog?payload=\"+FileName+\"\\\"><button>Delete File</button></td></tr>\";\n    f.close();\n  }\n  FileList += \"</table>\";\n  server.send(200, \"text/html\", FileList);\n}\n\nbool RawFile(String rawfile) {\n  if (SPIFFS.exists(rawfile)) {\n    if(!server.authenticate(update_username, update_password)){\n      server.requestAuthentication();}\n    File file = SPIFFS.open(rawfile, \"r\");\n    size_t sent = server.streamFile(file, \"application/octet-stream\");\n    file.close();\n    return true;\n  }\n  return false;\n}\n\nvoid ViewLog(){\n  webString=\"\";\n  String payload;\n  String ShowPL;\n  payload += server.arg(0);\n  File f = SPIFFS.open(payload, \"r\");\n  String webString = f.readString();\n  f.close();\n  ShowPL = String()+F(\n    \"<html><head></head><body>\"\n    \"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>\"\n    \"<a href=\\\"/logs\\\">List Exfiltrated Data</a> - <a href=\\\"/experimental\\\">Experimental TX Mode</a> - <a href=\\\"/data-convert\\\">Data Conversion Tools</a><br><br>\"\n    \"<FORM action=\\\"/api/tx/bin\\\" id=\\\"api_tx\\\" method=\\\"get\\\"  target=\\\"_blank\\\">\"\n      \"<small>Binary: </small><INPUT form=\\\"api_tx\\\" type=\\\"text\\\" name=\\\"binary\\\" value=\\\"\\\" pattern=\\\"[01,]{1,}\\\" required title=\\\"Allowed characters(0,1,\\\",\\\"), must not be empty\\\" minlength=\\\"1\\\" size=\\\"52\\\"> \"\n      \"<INPUT form=\\\"api_tx\\\" type=\\\"submit\\\" value=\\\"Transmit\\\"><br>\"\n      \"<small>Pulse Width: </small><INPUT form=\\\"api_tx\\\" type=\\\"number\\\" name=\\\"pulsewidth\\\" value=\\\"40\\\" minlength=\\\"1\\\" min=\\\"0\\\" size=\\\"8\\\"><small>us</small> \"\n      \"<small>Data Interval: </small><INPUT form=\\\"api_tx\\\" type=\\\"number\\\" name=\\\"interval\\\" value=\\\"2000\\\" minlength=\\\"1\\\" min=\\\"0\\\" size=\\\"8\\\"><small>us</small> \"\n      \"<small>Delay Between Packets: </small><INPUT form=\\\"api_tx\\\" type=\\\"number\\\" name=\\\"wait\\\" value=\\\"100000\\\" minlength=\\\"1\\\" min=\\\"0\\\" size=\\\"8\\\"><small>us</small><br>\"\n      \"<INPUT form=\\\"api_tx\\\" type=\\\"hidden\\\" name=\\\"prettify\\\" id=\\\"prettify\\\" value=\\\"1\\\">\"\n    \"</FORM>\"\n    \"<small>Use commas to separate the binary for transmitting multiple packets(useful for sending multiple keypresses for imitating keypads)</small><br>\"\n    \"<hr>\"\n    \"<a href=\\\"\")+payload+F(\"\\\"><button>Download File</button><a><small> - </small><a href=\\\"/deletelog?payload=\")+payload+F(\"\\\"><button>Delete File</button></a>\"\n    \"<pre>\")\n    +payload+\n    F(\"\\n\"\n    \"Note: Preambles shown are only a guess based on card length and may not be accurate for every card format.\\n\"\n    \"-----\\n\")\n    +webString+\n    F(\"</pre></body></html>\")\n    ;\n  webString=\"\";\n  server.send(200, \"text/html\", ShowPL);\n}\n\n// Start Networking\nvoid setup() {\n  Serial.begin(9600);\n  Serial.println(F(\".....\"));\n  Serial.println(String()+F(\"ESP-RFID-Tool v\")+version);\n  //SPIFFS.format();\n  \n  SPIFFS.begin();\n  \n //loadDefaults(); //uncomment to restore default settings if double reset fails for some reason\n\n//Jump RESTORE_DEFAULTS_PIN to GND while powering on device to reset the device to factory defaults\n  pinMode(RESTORE_DEFAULTS_PIN, INPUT_PULLUP);\n  jumperState = digitalRead(RESTORE_DEFAULTS_PIN);\n  if (jumperState == LOW) {\n    Serial.println(String()+F(\"Pin \")+RESTORE_DEFAULTS_PIN+F(\"Grounded\"));\n    Serial.println(F(\"Loading default config...\"));\n    loadDefaults();\n  }\n  \n  loadConfig();\n\n  if(!wg.begin(DATA0,DATA1,bufferlength,rxpacketgap)) {       \n    Serial.println(F(\"Could not begin Wiegand logging,\"));            \n    Serial.println(F(\"Out of memory!\"));\n  }\n\n//Set up Web Pages\n  server.on(\"/\",[]() {\n    FSInfo fs_info;\n    SPIFFS.info(fs_info);\n    String total;\n    total=fs_info.totalBytes;\n    String used;\n    used=fs_info.usedBytes;\n    String freespace;\n    freespace=fs_info.totalBytes-fs_info.usedBytes;\n    server.send(200, \"text/html\", String()+F(\"<html><body><b>ESP-RFID-Tool v\")+version+F(\"</b><br>\"\n    \"<img width='86' height='86' src='data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iNTYuNTkwNzI5bW0iCiAgIGhlaWdodD0iNDMuODMwODMzbW0iCiAgIHZpZXdCb3g9IjAgMCA1Ni41OTA3MjggNDMuODMwODM0IgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmczMDAwIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjkyLjIgKDVjM2U4MGQsIDIwMTctMDgtMDYpIgogICBzb2RpcG9kaTpkb2NuYW1lPSJyZmlkdG9vbDQuc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzMjk5NCI+CiAgICA8ZmlsdGVyCiAgICAgICBzdHlsZT0iY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzOnNSR0I7IgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkNyb3NzIEJsdXIiCiAgICAgICBpZD0iZmlsdGVyMzEyMCI+CiAgICAgIDxmZUNvbG9yTWF0cml4CiAgICAgICAgIGluPSJTb3VyY2VHcmFwaGljIgogICAgICAgICB2YWx1ZXM9IjEgMCAwIDAgMCAwIDEgMCAwIDAgMCAwIDEgMCAwIC0wLjIxMjUgLTAuNzE1NCAtMC4wNzIxIDEgMCAiCiAgICAgICAgIHJlc3VsdD0iY29sb3JtYXRyaXgiCiAgICAgICAgIGlkPSJmZUNvbG9yTWF0cml4MzExMCIgLz4KICAgICAgPGZlQ29tcG9zaXRlCiAgICAgICAgIGluPSJTb3VyY2VHcmFwaGljIgogICAgICAgICBpbjI9ImNvbG9ybWF0cml4IgogICAgICAgICBvcGVyYXRvcj0iYXJpdGhtZXRpYyIKICAgICAgICAgazI9IjEiCiAgICAgICAgIGszPSIwIgogICAgICAgICBrND0iMCIKICAgICAgICAgcmVzdWx0PSJjb21wb3NpdGUiCiAgICAgICAgIGlkPSJmZUNvbXBvc2l0ZTMxMTIiIC8+CiAgICAgIDxmZUdhdXNzaWFuQmx1cgogICAgICAgICBzdGREZXZpYXRpb249IjAuNzUgMC4wMSIKICAgICAgICAgcmVzdWx0PSJibHVyMSIKICAgICAgICAgaWQ9ImZlR2F1c3NpYW5CbHVyMzExNCIgLz4KICAgICAgPGZlR2F1c3NpYW5CbHVyCiAgICAgICAgIGluPSJjb21wb3NpdGUiCiAgICAgICAgIHN0ZERldmlhdGlvbj0iMC4wMSAwLjc1IgogICAgICAgICByZXN1bHQ9ImJsdXIyIgogICAgICAgICBpZD0iZmVHYXVzc2lhbkJsdXIzMTE2IiAvPgogICAgICA8ZmVCbGVuZAogICAgICAgICBpbj0iYmx1cjIiCiAgICAgICAgIGluMj0iYmx1cjEiCiAgICAgICAgIG1vZGU9ImRhcmtlbiIKICAgICAgICAgcmVzdWx0PSJibGVuZCIKICAgICAgICAgaWQ9ImZlQmxlbmQzMTE4IiAvPgogICAgPC9maWx0ZXI+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSIyLjA2MTY4NDgiCiAgICAgaW5rc2NhcGU6Y3g9IjE5NC44Njc5IgogICAgIGlua3NjYXBlOmN5PSI4Ny4zNDU1MTUiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9Im1tIgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImxheWVyMSIKICAgICBzaG93Z3JpZD0iZmFsc2UiCiAgICAgZml0LW1hcmdpbi10b3A9IjAiCiAgICAgZml0LW1hcmdpbi1sZWZ0PSIwIgogICAgIGZpdC1tYXJnaW4tcmlnaHQ9IjAiCiAgICAgZml0LW1hcmdpbi1ib3R0b209IjAiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxMzY2IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9IjY1OSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMzEiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIgLz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGEyOTk3Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZSAvPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiCiAgICAgaWQ9ImxheWVyMSIKICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTgxLjkzNTEsLTIyNi4xMTMpIj4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDM1NTYiCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjI2NDU4MzMzLDAsMCwwLjI2NDU4MzMzLDE2Mi44OTQyMywyMTguNDkzODEpIgogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS4wMDAwMDAxMjtmaWx0ZXI6dXJsKCNmaWx0ZXIzMTIwKSIKICAgICAgIGQ9Im0gMjI3LjkwODIsMTY2LjE0NDYyIC01LjEwMTU2LDQuNDYyODkgYyAtMjQuNDMxNTUsMjEuMzcyMTMgLTY1LjU3MjQsMjEuNzYwMjYgLTg5LjQ3MjY2LDAuODQzNzUgbCAtNS4zMjgxMiwtNC42NjIxMSAtMi4wNTY2NCwyLjA1ODYgLTIuMDU4NiwyLjA1NjY0IDMuNzU1ODYsMy4zMzc4OSBjIDEwLjgwMDY4LDkuNTk5ODUgMjQuMDYxLDE1Ljc4Mjk4IDM5LjAxMzY4LDE4LjE5MTQgNi40NjIwNCwxLjA0MDg1IDI0LjA5MTgsLTAuMjg4MjYgMzEuNDcyNjUsLTIuMzczMDQgOS45MTM0LC0yLjgwMDE0IDIxLjQ1OTMxLC04LjkxNTM3IDI4LjExOTE0LC0xNC44OTI1OCA1LjM1Nzc1LC00LjgwODU4IDUuMzkxNjQsLTQuODczNzkgMy41MzMyMSwtNi45Mzk0NSB6IG0gLTExLjEzMDg2LC0xMC4zMTA1NSBjIC0wLjg2NDc4LDAuMDgzOCAtMi4wOTk2NCwxLjA1MTExIC00Ljg1MTU2LDMuMjQyMTkgLTEwLjEwMjYsOC4wNDM4NiAtMjEuMzM5MTMsMTEuOTczMjkgLTM0LjMwNDY5LDExLjk5MjE5IC0xMy4wNDM0MSwwLjAxOTMgLTIyLjU2ODY2LC0zLjE4Njk0IC0zMy4zMTgzNiwtMTEuMjE0ODQgbCAtNS4zNTkzNywtNC4wMDE5NiAtMi4wMDE5NSwyLjAwMzkxIC0yLjAwMzkxLDIuMDAxOTUgNS4zMDI3Myw0LjE5MTQxIGMgNy42MzQ2OSw2LjAzNDg1IDE3LjU2Mzg2LDEwLjYwODU1IDI2LjkxOTkzLDEyLjQwMDM5IDEuMzc0OTksMC4yNjM0MyA2Ljk5OTk5LDAuMzU1ODggMTIuNSwwLjIwNTA4IDEyLjI3OTIzLC0wLjMzNjc2IDIxLjgwMTUyLC0zLjM3NzM4IDMxLjY0ODQzLC0xMC4xMDU0NyA4LjkwMzAyLC02LjA4MzE1IDkuNjAyNDQsLTYuOTgxOTYgNy4zNTU0NywtOS40NjQ4NSAtMC43NjQzNSwtMC44NDQ1OSAtMS4yMTQxMSwtMS4zMTUxOSAtMS44ODY3MiwtMS4yNSB6IG0gLTY2LjI2OTUzLC0xMS43OTQ5MiAtMS44MjgxMiwyLjAxOTUzIGMgLTEuNzcyODMsMS45NTg5NyAtMS43NzA2NiwyLjA4NTc1IDAuMDc2Miw0LjE1MDM5IDIuOTAwNDgsMy4yNDI2OSA4LjYwNDAzLDYuNTcyNTUgMTUuMDcwMzEsOC43OTY4OCAxMy44MjM3LDQuNzU1MjkgMzMuNjY4MzYsMC42NDA3MyA0Mi43ODEyNSwtOC44NzEwOSAyLjU0Nzg5LC0yLjY1OTQzIDIuNTU5OTUsLTIuNzM4NzYgMC42Njk5MiwtNC40NDkyMiAtMS44NDQ0NCwtMS42NjkxOSAtMi4xMTIwOSwtMS41OTY3NiAtNi40ODYzMiwxLjc0MjE4IC03LjY1NTcsNS44NDM2OCAtMTQuNjIzNTksNy45MDgwOCAtMjUuMTMwODYsNy40NDUzMiAtNy4zNDEwNiwtMC4zMjMzIC0xMC4wMTM4MiwtMC45MDY0NyAtMTQuNSwtMy4xNjYwMiAtMy4wMjQ5OSwtMS41MjM2IC02LjY1OTIzLC0zLjg3MTggLTguMDc2MTgsLTUuMjE4NzUgeiBtIDQzLjc1MzkxLC0xMS4zMDI3MyAtMy44MDA3OCwyLjYyMTA5IGMgLTcuNjkyMzIsNS4zMDI1NyAtMTcuNTkyMzYsNS4zOTA4NiAtMjUuNDQ1MzIsMC4yMjg1MiAtMy41ODg2MiwtMi4zNTkwNyAtMy42NzI2NSwtMi4zNjQ5NCAtNS4zODg2NywtMC40Njg3NSAtMi4yNDk4NywyLjQ4NjEgLTEuMDc2NTIsMy45ODExMiA1LjgxMjUsNy40MTQwNiA4Ljg1NTk3LDQuNDEzMTMgMjEuMzc3ODcsMy4wOTkzMiAyOS41LC0zLjA5NTcgbCAzLjQxNjAyLC0yLjYwNTQ3IC0yLjA0Njg4LC0yLjA0Njg4IHogbSAtMTEuNDA4MiwtMTEuMDEzNjcgYyAtMC41NzAxOSwtMC4wNjA0IC0xLjIwNzcsMC4zMDQ0NSAtMi4zNDM3NSwxLjA0ODgyIC0xLjk4MTM4LDEuMjk4MjcgLTIuOTA5MjQsMS4zNzU1OCAtNS4wMTk1NCwwLjQxNDA3IC0yLjE2NCwtMC45ODYwMSAtMi45MDkzMywtMC44OTA5IC00LjUzOTA2LDAuNTgzOTggLTEuOTI4NzMsMS43NDUzOSAtMS45MjYzLDEuNzgyNzcgMC4yNDAyNCwzLjUzNzExIDIuOTgyNjUsMi40MTUzMSAxMC4wNDIxOSwyLjM0NDg4IDEzLjA5OTYxLC0wLjEzMDg2IDIuMjk5MiwtMS44NjE3OSAyLjMxMDA1LC0xLjk1MTA4IDAuNSwtMy45NTExNyAtMC44NjU1LC0wLjk1NjM1IC0xLjM2NzMyLC0xLjQ0MTU2IC0xLjkzNzUsLTEuNTAxOTUgeiBtIC0zLjU2MjUsLTEzLjM2NzIzIGMgLTAuMzk5NjQsLTAuMDIxMyAtMC44NzkyMSwwLjA3NDggLTEuNDM3NSwwLjI4OTA2IC0xLjg1Mjk2LDAuNzExMDQgLTIuOTAyNTksNS42ODgzMiAtMS4zOTY0OSw2LjYxOTE0IDEuNzg0OTYsMS4xMDMxNyAzLjk4MzU1LC0wLjcxNTggNC4yODUxNiwtMy41NDI5NyAwLjIzMjM4LC0yLjE3OTczIC0wLjI1MjI2LC0zLjMwMTQ2IC0xLjQ1MTE3LC0zLjM2NTIzIHogbSAtMjcuODk4NDQsLTMuNjg5NDYgYyAyLjYyODAzLC0wLjEyMDQ0IDcuMzMzNTgsMS40Njg5NyA4LjQ5MDIzLDMuMDUwNzkgMi4wMzI3NSwyLjc3OTk1IDEuNjA5MTMsMTAuMDMxNTQgLTAuNzIyNjUsMTIuMzYzMjggLTEuMzMzMzEsMS4zMzMzNCAtMy4zMzMzMiwyIC02LDIgaCAtNCB2IC04LjQxNzk3IGMgMCwtNS44NDMwNyAwLjM4MjIyLC04LjU0NDkyIDEuMjUsLTguODM1OTQgMC4yNzQyMiwtMC4wOTIgMC42MDY5OCwtMC4xNDI5NSAwLjk4MjQyLC0wLjE2MDE2IHogbSA3NC40NDcyNiwtMC40NjY3OSBjIDAuODg4MjYsLTAuMDU0NSAxLjg3NTIsMC4wNTQyIDIuOTU4OTksMC4zMjYxNyA1LjM4NDczLDEuMzUxNDggNi43ODA5OSwxMi4yMjI2OCAyLjEyNjk1LDE2LjU1ODU5IC0yLjc4Njg3LDIuNTk2MzUgLTUuMDQyNjEsMi41MjI0IC04LjI5Mjk3LC0wLjI3MzQzIC0yLjE0Njc3LC0xLjg0NjU3IC0yLjYzODY3LC0zLjEzNTc3IC0yLjYzODY3LC02LjkxMDE2IDAsLTYuMTc5MTMgMS45OTY1OCwtOS40NjQ4NyA1Ljg0NTcsLTkuNzAxMTcgeiBtIC0xNDUuMzIyMjYyLC0wLjAxMTcgYyAwLjgwODE0NywwLjAyODUgMS43ODA5NCwwLjIxNjAxIDIuOTQxNDA2LDAuNTQ4ODMgMy40Mzk3NDgsMC45ODY0OSA0LjYwMTA0NSwzLjU1NDM1IDIuNjYyMTEsNS44OTA2MiAtMC42NzQyNjgsMC44MTI0OSAtMi45MTQyMzMsMS40NTMxMyAtNS4wODM5ODUsMS40NTMxMyAtMy43NzEzNjMsMCAtMy44NzY5NTMsLTAuMDk2NiAtMy44NzY5NTMsLTMuNTMxMjUgMCwtMy4xMDUxIDAuOTMyOTgyLC00LjQ0Njg1IDMuMzU3NDIyLC00LjM2MTMzIHogbSAxNzAuNDI5NjkyLC0wLjAzNTIgYyAxLjc0OTk3LC0wLjAzNDYgMy40NjAzMSwwLjU0MzUxIDQuNTQ2ODcsMS43NDQxNCAzLjYzNzA4LDQuMDE4OTYgMi44MDg5OSwxMy45NjY4NSAtMS4zNDc2NiwxNi4xOTE0MSAtMi45MjIyNSwxLjU2MzkzIC00Ljg4NjQxLDEuMTgxOTUgLTcuNTMxMjUsLTEuNDYyODkgLTMuMjUzODcsLTMuMjUzODggLTMuNTExNzMsLTExLjQ4ODI3IC0wLjQ1NTA3LC0xNC41NDQ5MyAxLjI0NjcxLC0xLjI0NjczIDMuMDM3MTMsLTEuODkzMTUgNC43ODcxMSwtMS45Mjc3MyB6IG0gMTkuNzEyODksLTIuMDcyMjcgYyAtNC44ODYyNSwwIC02LjU1NTUyLDAuMzMzODQgLTYuMjUzOTEsMS4yNSAwLjIyNjQzLDAuNjg3NSAxLjM4NzYyLDEuMzk0NTcgMi41ODIwMywxLjU3MDMxIDIuMDMxODQsMC4yOTkgMi4xNzE4OCwwLjg2MjIxIDIuMTcxODgsOC43NTAwMSAwLDguMzI1NzcgLTAuMDMxLDguNDI5NjggLTIuNSw4LjQyOTY4IC0xLjU4MDg3LDAgLTIuNSwwLjU2NDUgLTIuNSwxLjUzNTE1IDAsMS4yOTg3NiAxLjU3Nzk4LDEuNDkxMTQgMTAuMjUsMS4yNSBsIDEwLjI1LC0wLjI4NTE1IDAuMzA0NjgsLTQuNzUgYyAwLjI0MTMzLC0zLjc2MDY3IC0wLjAxOTEsLTQuNzUgLTEuMjUsLTQuNzUgLTEuMTEwMjcsMCAtMS41NTQ2OCwxLjAwMDc1IC0xLjU1NDY4LDMuNSB2IDMuNSBoIC01IC01IHYgLTguNDI5NjggYyAwLC02LjkwMTgyIDAuMTA4MTIsLTguMTk1NDEgMS40OTYwOSwtOC42MTEzMyAwLjE5ODI4LC0wLjA1OTQgMC40MjE3OSwtMC4xMDEzMSAwLjY3NTc4LC0wLjEzODY4IDEuMTk0NDEsLTAuMTc1NzQgMi4zNTc1OSwtMC44ODI4MSAyLjU4Mzk5LC0xLjU3MDMxIDAuMzAxNzIsLTAuOTE2MjcgLTEuMzY5NjUsLTEuMjUgLTYuMjU1ODYsLTEuMjUgeiBtIC02OC4wNjgzNiwwIGMgLTkuMDc5MDMsMCAtOS40MTQzMSwwLjA4MDMgLTkuOTk2MSwyLjM5ODQ0IC0wLjgyNzM3LDMuMjk2NDMgLTAuMTkwMDYsNi44NTk5NyAxLjMxNDQ2LDcuMzU1NDYgMC44NDQwNCwwLjI3Nzk2IDEuMjUsLTAuNzUyMjkgMS4yNSwtMy4xNzE4NyAwLC0zLjMxNDYgMC4yMjQ4LC0zLjU4MjAzIDMsLTMuNTgyMDMgaCAzIHYgOC41IGMgMCw4LjM5OTg1IC0wLjAyOTQsOC41IC0yLjUsOC41IC0xLjU1NTU4LDAgLTIuNSwwLjU2NjY1IC0yLjUsMS41IGggMC4wMDIgYyAwLDEuMTc5NDggMS4zODg4NywxLjUgNi41LDEuNSAxLjI3Nzc3LDAgMi4zMjI5MiwtMC4wMTk5IDMuMTcxODcsLTAuMDY4NCAyLjU0Njg4LC0wLjE0NTQzIDMuMzI4MTMsLTAuNTQ3MDMgMy4zMjgxMywtMS40MzE2NCAwLC0wLjkzMzM1IC0wLjk0NDQyLC0xLjUgLTIuNSwtMS41IC0yLjQ2OTc3LDAgLTIuNSwtMC4xIC0yLjUsLTguNSB2IC04LjUgaCAzIGMgMi43NjE4OSwwIDMsMC4yNzc3NyAzLDMuNSAwLDMuMzkzNjEgMS4yMTM0Niw0LjU3OTMyIDIuNTIxNDgsMi40NjI4OSAwLjM1MTA1LC0wLjU3MDE3IDAuMzM4ODMsLTIuODIwMiAtMC4wMjkzLC01IGwgLTAuNjY5OTIsLTMuOTYyODkgeiBtIC01MS4zNTM1MiwwIGMgLTYuNjQxMTIsMCAtOS4yNDQ5OCwxLjIzMTU5IC01LjU3ODEyLDIuNjM4NjcgMi4yMzEwNSwwLjg1NjE4IDIuMTc0NzksMTYuODg3NTUgLTAuMDYyNSwxNy43NDYwOSAtMC44NTk3MywwLjMyOTk2IC0xLjI4Mzg0LDEuMDUzNTQgLTAuOTQxNDEsMS42MDc0MyAwLjkzMzc3LDEuNTEwODYgMTEuMjA3NzgsMS4yMTE4IDE0LjM1OTM3LC0wLjQxNzk3IDMuNDgyMjcsLTEuODAwNzYgNS4xNDQ1NCwtNS40MjAyOSA1LjE0NDU0LC0xMS4xOTkyMiAwLC0zLjY3MzM2IC0wLjU1MTQ3LC01LjA4Mjc2IC0yLjkyMTg4LC03LjQ1MzEyIC0yLjY4MDAyLC0yLjY4MDA3IC0zLjUxMTQ2LC0yLjkyMTg4IC0xMCwtMi45MjE4OCB6IG0gLTIxLjA3ODEyLDAgYyAtNi4wODU1MywwIC04LjA2MTYzLDAuMzE4NTQgLTcuNzU1ODYsMS4yNSAwLjIyNTcxLDAuNjg3NSAxLjgzODg2LDEuMzkwMTYgMy41ODM5OCwxLjU2MjUgbCAzLjE3MTg4LDAuMzE0NDUgdiA4LjQzNTU1IDguNDM3NSBoIC0zLjUgYyAtMi4xMzg5LDAgLTMuMjEzNzYsMC4zNDYzNCAtMy40NDkyMiwxLjEzNDc2IC0wLjAzMzYsMC4xMTI2NCAtMC4wNTA4LDAuMjM0MjggLTAuMDUwOCwwLjM2NTI0IDAsMC45MDYxNSAwLjg3NDk5LDEuMjk2ODYgNC4wMzEyNSwxLjQzNTU0IDEuMDUyMDgsMC4wNDYyIDIuMzU3NjMsMC4wNjQ1IDMuOTY4NzUsMC4wNjQ1IDEuNjExMSwwIDIuOTE2NjcsLTAuMDE4MiAzLjk2ODc1LC0wLjA2NDUgMy4xNTYyNCwtMC4xMzg2NyA0LjAzMTI1LC0wLjUyOTMgNC4wMzEyNSwtMS40MzU1NCAwLC0wLjEyNTAxIC0wLjAxNTYsLTAuMjQyMTggLTAuMDQ2OSwtMC4zNTE1NiAtMC4yMTg3NSwtMC43NjU2NSAtMS4yMDMxMSwtMS4xNDg0NCAtMi45NTMxMiwtMS4xNDg0NCBoIC0zIHYgLTguNDMzNiBjIDAsLTguMzk1ODcgMC4wMTMsLTguNDM0NzggMi42NzM4MiwtOC43NDk5OSAxLjQ3MDA5LC0wLjE3NDAxIDIuODU2NCwtMC44Nzg5MSAzLjA4MjA0LC0xLjU2NjQxIDAuMzA1ODcsLTAuOTMxMzkgLTEuNjcwMywtMS4yNSAtNy43NTU4NiwtMS4yNSB6IG0gLTIzLjkzOTQ2LDAgYyAtNi4yNDQzNDMsMCAtMTAuMDYwNTQ0LDAuMzkxMDggLTEwLjA2MDU0NCwxLjAzMTI1IDAsMC41NjcyNyAwLjY3NTAwNSwxLjI5MDcgMS41LDEuNjA3NDIgMS4xNjg1MTcsMC40NDg0MSAxLjUsMi40MzA3NyAxLjUsOC45Njg3NiAwLDYuNzkzNzcgLTAuMjg1ODI3LDguMzkyNTcgLTEuNSw4LjM5MjU3IC0wLjcyMTg3MSwwIC0xLjMyODI2NywwLjUxNzMxIC0xLjQ2ODc1LDEuMTk5MjIgLTAuMDIwMDcsMC4wOTc0IC0wLjAzMTI1LDAuMTk3NjUgLTAuMDMxMjUsMC4zMDA3OCAwLDEuMTY2NjIgMS4zMzMzNDIsMS41IDYuMDAwMDA0LDEuNSAxLjE2NjY3LDAgMi4xMjQ5OSwtMC4wMjA4IDIuOTA2MjUsLTAuMDcwMyAyLjM0Mzc1LC0wLjE0ODQ0IDMuMDkzNzUsLTAuNTU0NjkgMy4wOTM3NSwtMS40Mjk2OSAwLC0wLjEyNTc4IC0wLjAxNTMsLTAuMjQzNyAtMC4wNDY5LC0wLjM1MzUyIC0wLjIyMDY3LC0wLjc2ODY0IC0xLjIxNTA0LC0xLjE0NjQ4IC0zLjAwOTc2LC0xLjE0NjQ4IC0wLjM3MjM0LDAgLTAuNjk5OTEsLTAuMDAzIC0wLjk4NjMzLC0wLjAxMzcgLTAuMjg2NDMsLTAuMDExMSAtMC41MzE3NiwtMC4wMzA5IC0wLjc0MjE5LC0wLjA2ODQgLTAuMjEwNDMsLTAuMDM3NSAtMC4zODQ5NiwtMC4wOTI3IC0wLjUyOTMsLTAuMTcxODkgLTAuNzIxNjgsLTAuMzk2MDEgLTAuNjc5NTEsLTEuNDA4MzcgLTAuNDkyMTgsLTMuOTk2MDggMC4yNTU3OSwtMy41MzU3NiAwLjY4NDk1LC00LjMwMzg2IDIuNTYwNTQsLTQuNTcwMzIgMS4zNTIyOCwtMC4xOTIwNCAyLjQ5NjEsMC4zMDgxOCAyLjg1NzQyLDEuMjUgMC4zMzEzOSwwLjg2MzU4IDEuMDA0OTUsMS41NzAzMiAxLjQ5NjEsMS41NzAzMiAxLjIyODQ2LDAgMS4xMzE5MiwtNi43MzQwNCAtMC4xMDc0MiwtNy41IC0wLjU1LC0wLjMzOTkzIC0xLDAuMDgzOSAtMSwwLjk0MTQgMCwwLjY2NiAtMC4zNzE2MSwxLjEwNzUgLTEuMTUwMzksMS4zNDU3MSAtMC4xNTU3NiwwLjA0NzYgLTAuMzI2OTUsMC4wODc3IC0wLjUxNTYzLDAuMTE5MTQgLTAuMzc3MzcsMC4wNjI5IC0wLjgyMTM3LDAuMDkzNyAtMS4zMzM5OCwwLjA5MzcgLTIuNjY2NjUsMCAtMywtMC4zMzMzMiAtMywtMyAwLC0yLjk2NTk5IDAuMDY0NSwtMy4wMDAwMSA1LjQzMTY0LC0zLjAwMDAxIDQuNjk0NDcsMCA1LjQ3MTc2LDAuMjc1ODggNS43NDgwNCwyLjAzOTA3IDAuNTQ4NzUsMy41MDI0OSAyLjMwNTQ1LDIuNjQyMDYgMi42MzA4NiwtMS4yODkwNyBsIDAuMzEwNTUsLTMuNzUgeiBNIDc1Ljc5ODgyOCwxMDIuMDQzIGMgLTIuNjM2NjcxLDAuMDcxNCAtNC42Mzg2NzIsMC40MDE2NCAtNC42Mzg2NzIsMS4wMzcxMSAwLDAuNTUgMC42NzQ5NjcsMSAxLjUsMSAxLjIyMDY3NCwwIDEuNSwxLjY1NDM3IDEuNSw4Ljg5MjU5IDAsNi45ODI0MSAtMC4zMjIyOTksOS4wMTY4MyAtMS41LDkuNDY4NzUgLTAuODI1MDMzLDAuMzE2NTcgLTEuNSwxLjA0MDE5IC0xLjUsMS42MDc0MiB2IDAuMDAyIGMgMCwxLjM2NTI0IDguNjk0MDk5LDEuMzMxMjMgOS41NDEwMTYsLTAuMDM5MSAwLjM2MzIxMiwtMC41ODc3MyAtMC4yODI3NjcsLTEuMzEyODggLTEuNDM5NDUzLC0xLjYxNTI0IC0xLjYxMjAwNywtMC40MjE1NCAtMi4xMDE1NjMsLTEuMzM3MjEgLTIuMTAxNTYzLC0zLjkzMzU5IDAsLTIuOTQyODUgMC4zMjYwMDQsLTMuMzgyODIgMi41MDE5NTMsLTMuMzgyODIgMS44NTYzNTMsMCAzLjQyODE2MywxLjI4OTcxIDYuMDkzNzUsNSAzLjUwMzQ3MSw0Ljg3NjYxIDcuNDA0Mjk3LDYuNzIwNTMgNy40MDQyOTcsMy41IDAsLTAuODI1MDIgLTAuNTYyNDY2LC0xLjUwNjExIC0xLjI1LC0xLjUxMzY3IC0wLjY4NzQ5NiwtMC4wMDcgLTIuMjk2Njg4LC0xLjYyNzA3IC0zLjU3NjE3MiwtMy42MDE1NyBsIC0yLjMyNjE3MiwtMy41ODk4MyAyLjA3NjE3MiwtMS40NTUwOCBjIDIuOTE5NDU5LC0yLjA0NDk1IDIuODM0ODA3LC03LjY5NTAyIC0wLjE0NjQ4NCwtOS43ODMyMSAtMS41ODczMDcsLTEuMTExNzcgLTcuNzQ0MjIsLTEuNzEyNzEgLTEyLjEzODY3MiwtMS41OTM3NSB6IG0gMTc1LjEzMDg2MiwtMC41OTE3OSBjIC00LjE3MTE1LC0wLjAzMzcgLTguMjU1NjEsMi41NDE3OSAtOS43NjU2Myw3LjExNzE5IC0xLjU1MDM2LDQuNjk3NzMgLTAuNTgxNzQsOS42NzE0MSAyLjYyNSwxMy40ODI0MiAyLjA1MDk2LDIuNDM3NDIgMy4zMjU3MiwzLjAyOTMgNi41MjUzOSwzLjAyOTMgMi4xODcyNSwwIDQuODQyMjQsLTAuNDYyOTcgNS45MDAzOSwtMS4wMjkzIDUuNDc5MywtMi45MzI0MiA2LjU1ODI5LC0xNC44MTI4NSAxLjc3OTMsLTE5LjU5MTc5IC0yLjAyODkxLC0yLjAyODkyIC00LjU2MTc3LC0yLjk4NzYyIC03LjA2NDQ1LC0zLjAwNzgyIHogbSAtMjQuNjA3NDIsLTAuMjY1NjIgYyAtMS40NjczMSwwLjA2NjIgLTIuOTMxOTYsMC42NzU1OSAtNC44NTc0MywxLjg0OTYxIC02LjAyMDg2LDMuNjcxMTYgLTYuOTA5NDUsMTQuMTY2NzIgLTEuNjQ0NTMsMTkuNDMxNjQgMi44MTc2NCwyLjgxNzYzIDguNjg2NDQsMy41Njg1IDEyLjM5NDUzLDEuNTgzOTggNi45NTg0OSwtMy43MjQwNCA2LjI5NDg5LC0xNy42NzE2MiAtMS4wMjE0OCwtMjEuNDU1MDggLTEuOTMzNTcsLTAuOTk5ODcgLTMuNDAzNzksLTEuNDc2MzYgLTQuODcxMDksLTEuNDEwMTUgeiBtIC00Ny40OTgwNSwtNi4wNjY0NDkgYyAtMi42Nzk4MiwwLjA3MDk0IC01LjM2MDM3LDAuNzAzNTk2IC02Ljc5NDkyLDEuODY1MjM0IC0wLjI4NzQsMC4yMzI3MiAtMC41MzkwNSwwLjQzODUzNCAtMC43NTU4NiwwLjYyMzA0NyAtMC4yMTY4MSwwLjE4NDUxMyAtMC4zOTg4MywwLjM0OTMwMyAtMC41NDY4OCwwLjUgLTAuODg4MjgsMC45MDQxODMgLTAuNTU0OCwxLjMyODA4NyAwLjgwMjc0LDIuODI4MTI4IDEuNzMwOTgsMS45MTI3NCAyLjAxMTA2LDEuOTQxODggNC4yODMyLDAuNDUzMTIgMS45OTQ3NiwtMS4zMDcwMyAyLjkwMTEsLTEuMzc2NjggNS4wNTY2NCwtMC4zOTQ1MyAyLjI2NTE4LDEuMDMyMDggMi45MDcwOSwwLjkxNTE4IDQuNjY3OTcsLTAuODQ1NyAyLjAyMzc1LC0yLjAyMzU5OSAyLjAyMzczLC0yLjA1NTI0NSAwLjA4MiwtMy41MjM0NCAtMS40MzU4LC0xLjA4NTU5NCAtNC4xMTUxLC0xLjU3NjgwMSAtNi43OTQ5MiwtMS41MDU4NTkgeiBtIDAuMzI2MTcsLTE2LjA5MTc5NyBjIC00LjAzMTMzLDAuMDAzMiAtOC4wNjAxMiwwLjgxMDc1NyAtMTEuNDkwMjMsMi40MjE4NzUgLTYuNTEyMDMsMy4wNTg3MDUgLTkuMjUyMTUsNi4wMTgzMjcgLTcuNTE1NjMsOC4xMTMyODEgMS40NDc1OSwxLjc0MzIzOSAzLjYxMDQxLDEuOTgyNDU2IDQuNTI1MzksMC41MDE5NTMgMC4xNzIzOSwtMC4yNzg5MjkgMC43ODc5NSwtMC43NjYyNTEgMS42NDI1OCwtMS4zMjgxMjUgMC40MjczMSwtMC4yODA5MzcgMC45MTUwMywtMC41ODAwNDMgMS40MzU1NSwtMC44ODA4NTkgMC41MjA1MSwtMC4zMDA4MTYgMS4wNzQ1NywtMC42MDM4MzUgMS42MzY3MiwtMC44OTA2MjUgNy4yMTU4MywtMy42ODEyMjIgMTcuOTIwMTYsLTIuNDUxNjAyIDIzLjg2MzI4LDIuNzQyMTg3IDEuMzcwMywxLjE5NzU0NCAxLjgzNDgzLDEuMTE1NDAyIDMuMzg4NjcsLTAuNjAxNTYyIDIuMzUzOTYsLTIuNjAxMTA5IDAuODk2OTIsLTQuNDcxMjcxIC01Ljk3NjU2LC03LjY3NTc4MSAtMy40NDQ1NywtMS42MDUxNjYgLTcuNDc4NDQsLTIuNDA1NTggLTExLjUwOTc3LC0yLjQwMjM0NCB6IG0gLTAuMjI0MzgsLTE1LjkyNTc4MiBjIC0xMC4yMDc4NiwwLjA2NDM5IC0yMC40Mzk1MywzLjYyNTYwNCAtMjguNzIwNywxMC41OTM3NSAtMi45NzQzNCwyLjUwMjcyOCAtMy4wNDQ1MiwyLjcyNzYwNSAtMS40MzE2NCw0LjUwOTc2NiAwLjkzMzUsMS4wMzE1NDcgMS45MDA1LDEuODc1IDIuMTQ4NDQsMS44NzUgMC4yNDgxMiwwIDIuMjM1LC0xLjQxMzIzIDQuNDE2MDEsLTMuMTQwNjI1IDEyLjk4NjM4LC0xMC4yODU0MTcgMzMuNzQwODksLTEwLjY4NzM4NyA0NS45MzE2NCwtMC44OTA2MjUgNC40Mjc0OSwzLjU1ODA0NyA1LjAyMDcyLDMuNjUwMTI5IDcuMjk4ODMsMS4xMzI4MTMgMS42NTIwMywtMS44MjU0NzQgMS40OTY2OSwtMi4wNTkwOSAtNC4wMzEyNSwtNi4wMTc1NzkgLTcuNjE0ODcsLTUuNDUyODgzIC0xNi42MDQzOSwtOC4xMTkzMTQgLTI1LjYxMTMzLC04LjA2MjUgeiBtIDEuNjM4NjgsLTE1Ljk1MzEyNCBjIC0xNC42MjEyOSwtMC4wMDMgLTMwLjQ5MTc5LDUuMzkwNTIzIC00MC44MTQ0NSwxNC40Nzg1MTUgLTMuODE4LDMuMzYxMzIzIC0zLjg2ODA0LDMuNDg4ODY2IC0yLjA5NTcsNS40NDcyNjYgMC45OTgyOCwxLjEwMzA5MyAyLjAxNDc5LDIuMDA1ODU5IDIuMjU3ODEsMi4wMDU4NTkgMC4yNDI4NywwIDIuMzc4MTgsLTEuNzA0NzI5IDQuNzQ2MDksLTMuNzg5MDYyIDguNzgyMzQsLTcuNzMwNTMzIDIxLjY0NTI3LC0xMi4yMTgxMTcgMzQuOTYyODksLTEyLjE5OTIxOSAxMi43MTgwOCwwLjAxODc4IDIzLjg5Mjg5LDMuOTM5MTA3IDMzLjM3NSwxMS43MDg5ODQgNC4zMzAxMywzLjU0ODIyMSA0LjMzODI4LDMuNTUwMzkzIDYuMzI4MTMsMS41NjA1NDcgMS45ODk4OCwtMS45ODk4ODMgMS45ODYzMywtMS45OTU5NDIgLTIuMzM1OTQsLTUuNzI2NTYyIC01LjgwODQxLC01LjAxMzM5MyAtMTYuMzQ3NTIsLTEwLjIzMzE4IC0yNC41ODM5OCwtMTIuMTc1NzgyIC0zLjc0ODg2LC0wLjg4NDE3NCAtNy43NDU4OCwtMS4zMDk3MDYgLTExLjgzOTg1LC0xLjMxMDU0NiB6IG0gLTEuNTUyNzMsLTE1LjkwMjM0NCBjIC0xMC43MTM4MywwLjA0MDk4IC0yMS41MjcwNSwyLjMwNjg4NyAtMzEuNjYyMTEsNi45MDIzNDQgLTYuNjU3MjYsMy4wMTg1MTkgLTE3LjMxNjQ1LDEwLjE2OTY2NSAtMjAuNTkzNzUsMTMuODE0NDUzIC0xLjgxNDEsMi4wMTc1NDkgLTEuODIxNDcsMi4yMDk0MDYgLTAuMTYyMTEsNC4wNDI5NjggMS43MDM1OCwxLjg4MjQzMiAxLjg3OTQzLDEuODE0MTg4IDcuOTA0MywtMy4wNTY2NCAyNS44MTI4MSwtMjAuODY4Mzk3IDYzLjc4MjM0LC0yMS4wNTQwNTQgODguNjQ0NTMsLTAuNDMzNTk0IGwgNS40ODA0Nyw0LjU0Njg3NSAyLjAxOTUzLC0yLjA0Njg3NSAyLjAxNzU4LC0yLjA0ODgyOCAtNC41LC0zLjg3MzA0NyBDIDIxNC40NDk0NSwzNy4yODk5MDcgMTk2Ljg2ODExLDMxLjE3Nzc5NiAxNzkuMDExNzIsMzEuMjQ2MDk0IFoiIC8+CiAgPC9nPgo8L3N2Zz4K'><br><i>\"\n    \"by Corey Harding<br>\"\n    \"www.RFID-Tool.com<br>\"\n    \"www.LegacySecurityGroup.com / www.Exploit.Agency</i><br>\"\n    \"-----<br>\"\n    \"File System Info Calculated in Bytes<br>\"\n    \"<b>Total:</b> \")+total+\" <b>Free:</b> \"+freespace+\" \"+\" <b>Used:</b> \"+used+F(\"<br>-----<br>\"\n    \"<a href=\\\"/logs\\\">List Exfiltrated Data</a><br>-<br>\"\n    \"<a href=\\\"/experimental\\\">Experimental TX Mode</a><br>-<br>\"\n    \"<a href=\\\"/data-convert\\\">Data Conversion Tools</a><br>-<br>\"\n    \"<a href=\\\"/settings\\\">Configure Settings</a><br>-<br>\"\n    \"<a href=\\\"/format\\\">Format File System</a><br>-<br>\"\n    \"<a href=\\\"/firmware\\\">Upgrade Firmware</a><br>-<br>\"\n    \"<a href=\\\"/api/help\\\">API Info</a><br>-<br>\"\n    \"<a href=\\\"/help\\\">Help</a>\"\n    \"</body></html>\"));\n  });\n\n  server.onNotFound([]() {\n    if (!RawFile(server.uri()))\n      server.send(404, \"text/plain\", F(\"Error 404 File Not Found\"));\n  });\n  server.on(\"/settings\", handleSettings);\n\n  server.on(\"/firmware\", [](){\n    server.send(200, \"text/html\", String()+F(\"<html><body style=\\\"height: 100%;\\\"><a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>Open Arduino IDE.<br>Pull down \\\"Sketch\\\" Menu then select \\\"Export Compiled Binary\\\".<br>On this page click \\\"Browse\\\", select the binary you exported earlier, then click \\\"Update\\\".<br>You may need to manually reboot the device to reconnect.<br><iframe style =\\\"border: 0; height: 100%;\\\" src=\\\"http://\")+local_IPstr+F(\":1337/update\\\"><a href=\\\"http://\")+local_IPstr+F(\":1337/update\\\">Click here to Upload Firmware</a></iframe></body></html>\"));\n  });\n\n  server.on(\"/restoredefaults\", [](){\n    server.send(200, \"text/html\", F(\"<html><body>This will restore the device to the default configuration.<br><br>Are you sure?<br><br><a href=\\\"/restoredefaults/yes\\\">YES</a> - <a href=\\\"/\\\">NO</a></body></html>\"));\n  });\n\n  server.on(\"/restoredefaults/yes\", [](){\n    if(!server.authenticate(update_username, update_password))\n      return server.requestAuthentication();\n    server.send(200, \"text/html\", F(\"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>Network<br>---<br>SSID: <b>ESP-RFID-Tool</b><br><br>Administration<br>---<br>USER: <b>admin</b> PASS: <b>rfidtool</b>\"));\n    delay(50);\n    loadDefaults();\n    ESP.restart();\n  });\n\n  server.on(\"/deletelog\", [](){\n    String deletelog;\n    deletelog += server.arg(0);\n    server.send(200, \"text/html\", String()+F(\"<html><body>This will delete the file: \")+deletelog+F(\".<br><br>Are you sure?<br><br><a href=\\\"/deletelog/yes?payload=\")+deletelog+F(\"\\\">YES</a> - <a href=\\\"/\\\">NO</a></body></html>\"));\n  });\n\n  server.on(\"/viewlog\", ViewLog);\n\n  server.on(\"/deletelog/yes\", [](){\n    if(!server.authenticate(update_username, update_password))\n      return server.requestAuthentication();\n    String deletelog;\n    deletelog += server.arg(0);\n    if (!deletelog.startsWith(\"/payloads/\")) server.send(200, \"text/html\", String()+F(\"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br><a href=\\\"/logs\\\">List Exfiltrated Data</a><br><br>Deleting file: \")+deletelog);\n    delay(50);\n    SPIFFS.remove(deletelog);\n  });\n\n  server.on(\"/format\", [](){\n    server.send(200, \"text/html\", F(\"<html><body><a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>This will reformat the SPIFFS File System.<br><br>Are you sure?<br><br><a href=\\\"/format/yes\\\">YES</a> - <a href=\\\"/\\\">NO</a></body></html>\"));\n  });\n\n  server.on(\"/logs\", ListLogs);\n\n  server.on(\"/reboot\", [](){\n    if(!server.authenticate(update_username, update_password))\n    return server.requestAuthentication();\n    server.send(200, \"text/html\", F(\"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>Rebooting Device...\"));\n    delay(50);\n    ESP.restart();\n  });\n  \n  server.on(\"/format/yes\", [](){\n    if(!server.authenticate(update_username, update_password))\n      return server.requestAuthentication();\n    server.send(200, \"text/html\", F(\"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>Formatting file system: This may take up to 90 seconds\"));\n    delay(50);\n//    Serial.print(\"Formatting file system...\");\n    SPIFFS.format();\n//    Serial.println(\" Success\");\n    saveConfig();\n  });\n  \n  server.on(\"/help\", []() {\n    server.send_P(200, \"text/html\", HelpText);\n  });\n  \n  server.on(\"/license\", []() {\n    server.send_P(200, \"text/html\", License);\n  });\n\n  server.on(\"/data-convert\", [](){\n\n    if (server.hasArg(\"bin2hexHTML\")) {\n\n      int bin2hexBUFFlen=(((server.arg(\"bin2hexHTML\")).length())+1);\n      char bin2hexCHAR[bin2hexBUFFlen];\n      (server.arg(\"bin2hexHTML\")).toCharArray(bin2hexCHAR,bin2hexBUFFlen);\n\n      dataCONVERSION+=String()+F(\"Binary: \")+bin2hexCHAR+F(\"<br><br>\");\n\n      String hexTEMP=\"\";\n\n      int binCOUNT=(bin2hexBUFFlen-1);\n      for (int currentBINpos=0; currentBINpos<binCOUNT; currentBINpos=currentBINpos+4) {\n        char hexCHAR[2];\n        char tempNIBBLE[5];\n        strncpy(tempNIBBLE, &bin2hexCHAR[currentBINpos], 4);\n        tempNIBBLE[4]='\\0';\n        sprintf(hexCHAR, \"%X\", (strtol(tempNIBBLE, NULL, 2)));\n        hexTEMP+=hexCHAR;\n      }\n\n      dataCONVERSION+=String()+F(\"Hexadecimal: \")+hexTEMP+F(\"<br><small>You may want to drop the leading zero(if there is one) and if your cloning software does not handle it for you.</small><br><br>\");\n      hexTEMP=\"\";\n      \n      dataCONVERSION+=F(\"<br><br>\");\n      \n      bin2hexBUFFlen=0;\n    }\n\n    if (server.hasArg(\"hex2binHTML\")) {\n\n      int hex2binBUFFlen=(((server.arg(\"hex2binHTML\")).length())+1);\n      char hex2binCHAR[hex2binBUFFlen];\n      (server.arg(\"hex2binHTML\")).toCharArray(hex2binCHAR,hex2binBUFFlen);\n\n      dataCONVERSION+=String()+F(\"Hexadecimal: \")+hex2binCHAR+F(\"<br><br>\");\n\n      String binTEMP=\"\";\n\n      int charCOUNT=(hex2binBUFFlen-1);\n      for (int currentHEXpos=0; currentHEXpos<charCOUNT; currentHEXpos++) {\n        char binCHAR[5];\n        char tempHEX[2];\n        strncpy(tempHEX, &hex2binCHAR[currentHEXpos], 1);\n        tempHEX[1]='\\0';\n        int decimal=(unsigned char)strtoul(tempHEX, NULL, 16);\n        itoa(decimal,binCHAR,2);\n        while (strlen(binCHAR) < 4) {\n          char *dup;\n          sprintf(binCHAR,\"%s%s\",\"0\",(dup=strdup(binCHAR)));\n          free(dup);\n        }\n        binTEMP+=binCHAR;\n      }\n\n      dataCONVERSION+=String()+F(\"Binary: \")+binTEMP+F(\"<br><br>\");\n      binTEMP=\"\";\n      \n      dataCONVERSION+=F(\"<br><br>\");\n      \n      hex2binBUFFlen=0;\n    }\n    \n    if (server.hasArg(\"abaHTML\")) {\n      String abaHTML=(server.arg(\"abaHTML\"));\n\n      dataCONVERSION=\"Trying \\\"Forward\\\" Swipe<br>\";\n      dataCONVERSION+=(\"Forward Binary:\"+abaHTML+\"<br>\");\n      int abaStart=abaHTML.indexOf(\"11010\");\n      int abaEnd=(abaHTML.lastIndexOf(\"11111\")+4);\n      dataCONVERSION+=aba2str(abaHTML,abaStart,abaEnd,\"\\\"Forward\\\" Swipe\");\n      \n      dataCONVERSION+=\" * Trying \\\"Reverse\\\" Swipe<br>\";\n      int abaBUFFlen=((abaHTML.length())+1);\n      char abachar[abaBUFFlen];\n      abaHTML.toCharArray(abachar,abaBUFFlen);\n      abaHTML=String(strrev(abachar));\n      dataCONVERSION+=(\"Reversed Binary:\"+abaHTML+\"<br>\");\n      abaStart=abaHTML.indexOf(\"11010\");\n      abaEnd=(abaHTML.lastIndexOf(\"11111\")+4);\n      dataCONVERSION+=aba2str(abaHTML,abaStart,abaEnd,\"\\\"Reverse\\\" Swipe\");\n    \n      //dataCONVERSION+=(String()+F(\" * You can verify the data at the following URL:<br><a target=\\\"_blank\\\" href=\\\"https://www.legacysecuritygroup.com/aba-decode.php?binary=\")+abaHTML+F(\"\\\">https://www.legacysecuritygroup.com/aba-decode.php?binary=\")+abaHTML+F(\"</a>\"));\n      dataCONVERSION.replace(\"*\", \"<br><br>\");\n      dataCONVERSION.replace(\":\", \": \");\n\n      abaHTML=\"\";\n      abaStart=0;\n      abaEnd=0;\n    }\n    \n    server.send(200, \"text/html\", String()+F(\n      \"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>\")\n      +dataCONVERSION+\n      F(\n      \"<hr>\"\n      \"<FORM action=\\\"/data-convert\\\" id=\\\"aba2ascii\\\" method=\\\"post\\\">\"\n      \"<b>Convert ABA Binary Data to ASCII:</b><br>\"\n      \"<INPUT form=\\\"aba2ascii\\\" type=\\\"text\\\" name=\\\"abaHTML\\\" value=\\\"\\\" pattern=\\\"[0-1]{1,}\\\" required title=\\\"Only 0's & 1's allowed, must not be empty\\\" minlength=\\\"1\\\" size=\\\"52\\\"><br>\"\n      \"<INPUT form=\\\"aba2ascii\\\" type=\\\"submit\\\" value=\\\"Convert\\\"><br>\"\n      \"</FORM>\"\n      \"<br>\"\n      \"<FORM action=\\\"/data-convert\\\" id=\\\"bin2hex\\\" method=\\\"post\\\">\"\n      \"<b>Convert Binary Data to Hexadecimal:</b><br>\"\n      \"<small>For use with card cloning, typically includes both the preamble and card data(binary before and after the space in log).</small><br>\"\n      \"<INPUT form=\\\"bin2hex\\\" type=\\\"text\\\" name=\\\"bin2hexHTML\\\" value=\\\"\\\" pattern=\\\"[0-1]{1,}\\\" required title=\\\"Only 0's & 1's allowed, no spaces allowed, must not be empty\\\" minlength=\\\"1\\\" size=\\\"52\\\"><br>\"\n      \"<INPUT form=\\\"bin2hex\\\" type=\\\"submit\\\" value=\\\"Convert\\\"><br>\"\n      \"</FORM>\"\n      \"<br>\"\n      \"<FORM action=\\\"/data-convert\\\" id=\\\"hex2bin\\\" method=\\\"post\\\">\"\n      \"<b>Convert Hexadecimal Data to Binary:</b><br>\"\n      \"<small>In some situations you may want to add a leading zero to pad the output to come up with the correct number of bits.</small><br>\"\n      \"<INPUT form=\\\"hex2bin\\\" type=\\\"text\\\" name=\\\"hex2binHTML\\\" value=\\\"\\\" pattern=\\\"[0-9a-fA-F]{1,}\\\" required title=\\\"Only characters 0-9 A-F a-f allowed, no spaces allowed, must not be empty\\\" minlength=\\\"1\\\" size=\\\"52\\\"><br>\"\n      \"<INPUT form=\\\"hex2bin\\\" type=\\\"submit\\\" value=\\\"Convert\\\"><br>\"\n      \"</FORM>\"\n      )\n    );\n      \n    dataCONVERSION=\"\";\n  });\n\n  #include \"api_server.h\"\n\n  server.on(\"/stoptx\", [](){\n    server.send(200, \"text/html\", F(\"<html><body>This will kill any ongoing transmissions.<br><br>Are you sure?<br><br><a href=\\\"/stoptx/yes\\\">YES</a> - <a href=\\\"/\\\">NO</a></body></html>\"));\n  });\n\n  server.on(\"/stoptx/yes\", [](){\n    TXstatus=0;\n    server.send(200, \"text/html\", F(\"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br><a href=\\\"/experimental\\\"><- BACK TO EXPERIMENTAL TX MODE</a><br><br>All transmissions have been stopped.\"));\n  });\n\n  server.on(\"/experimental\", [](){\n    String experimentalStatus=\"Awaiting Instructions\";\n\n    if (server.hasArg(\"pinHTML\")||server.hasArg(\"bruteEND\")) {\n      pinHTML=server.arg(\"pinHTML\");\n      int pinBITS=server.arg(\"pinBITS\").toInt();\n      int pinHTMLDELAY=server.arg(\"pinHTMLDELAY\").toInt();\n      int bruteforcing;\n      int brutePAD=(server.arg(\"bruteSTART\").length());\n      if (server.hasArg(\"bruteSTART\")) {\n        bruteforcing=1;\n      }\n      else {\n        bruteforcing=0;\n      }\n\n      TXstatus=1;\n      \n      wg.pause();\n      digitalWrite(DATA0, HIGH);\n      pinMode(DATA0,OUTPUT);\n      digitalWrite(DATA1, HIGH);\n      pinMode(DATA1,OUTPUT);\n\n      pinHTML.replace(\"F1\",\"C\");\n      pinHTML.replace(\"F2\",\"D\");\n      pinHTML.replace(\"F3\",\"E\");\n      pinHTML.replace(\"F4\",\"F\");\n\n      experimentalStatus=String()+\"Transmitting \"+pinBITS+\"bit Wiegand Format PIN: \"+pinHTML+\" with a \"+pinHTMLDELAY+\"ms delay between \\\"keypresses\\\"\";\n      delay(50);\n      \n      int bruteSTART;\n      int bruteEND;\n      if (server.hasArg(\"bruteSTART\")) {\n        bruteSTART=server.arg(\"bruteSTART\").toInt();\n      }\n      else {\n        bruteSTART=0;\n      }\n      \n      if (server.hasArg(\"bruteEND\")) {\n        bruteEND=server.arg(\"bruteEND\").toInt();\n      }\n      else {\n        bruteEND=0;\n      }\n\n      if (server.hasArg(\"bruteSTART\")) {\n        server.send(200, \"text/html\", String()+\"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br><a href=\\\"/experimental\\\"><- BACK TO EXPERIMENTAL TX MODE</a><br><br>Brute forcing \"+pinBITS+\"bit Wiegand Format PIN from \"+(server.arg(\"bruteSTART\"))+\" to \"+(server.arg(\"bruteEND\"))+\" with a \"+pinHTMLDELAY+\"ms delay between \\\"keypresses\\\"<br>This may take a while, your device will be busy until the sequence has been completely transmitted!<br>Please \\\"STOP CURRENT TRANSMISSION\\\" before attempting to use your device or simply wait for the transmission to finish.<br>You can view if the brute force attempt has completed by returning to the Experimental TX page and checking the status located under \\\"Transmit Status\\\"<br><br><a href=\\\"/stoptx\\\"><button>STOP CURRENT TRANSMISSION</button></a>\");\n        delay(50);\n      }\n\n      String bruteSTARTchar=\"\";\n      String bruteENDchar=\"\";\n      if (server.hasArg(\"bruteSTARTchar\")&&(server.arg(\"bruteSTARTchar\")!=\"\")) {\n        bruteSTARTchar=(server.arg(\"bruteSTARTchar\"));\n        bruteSTARTchar.replace(\"F1\",\"C\");\n        bruteSTARTchar.replace(\"F2\",\"D\");\n        bruteSTARTchar.replace(\"F3\",\"E\");\n        bruteSTARTchar.replace(\"F4\",\"F\");\n      }\n      if (server.hasArg(\"bruteENDchar\")&&(server.arg(\"bruteENDchar\")!=\"\")) {\n        bruteENDchar=(server.arg(\"bruteENDchar\"));\n        bruteENDchar=(server.arg(\"bruteENDchar\"));\n        bruteENDchar.replace(\"F1\",\"C\");\n        bruteENDchar.replace(\"F2\",\"D\");\n        bruteENDchar.replace(\"F3\",\"E\");\n        bruteENDchar.replace(\"F4\",\"F\");\n      }\n\n      unsigned long bruteFAILdelay=0;\n      unsigned long bruteFAILS=0;\n      int bruteFAILmultiplier=0;\n      int bruteFAILmultiplierCURRENT=0;\n      int bruteFAILmultiplierAFTER=0;\n      int delayAFTERpin=0;\n      int bruteFAILSmax=0;\n      bruteFAILSmax=(server.arg(\"bruteFAILSmax\")).toInt();\n      delayAFTERpin=(server.arg(\"delayAFTERpin\")).toInt();\n      bruteFAILdelay=(server.arg(\"bruteFAILdelay\")).toInt();\n      bruteFAILmultiplier=(server.arg(\"bruteFAILmultiplier\")).toInt();\n      bruteFAILmultiplierAFTER=(server.arg(\"bruteFAILmultiplierAFTER\")).toInt();\n\n      for (int brute=bruteSTART; brute<=bruteEND; brute++) {\n\n        if (bruteforcing==1) {\n          pinHTML=String(brute);\n          while (pinHTML.length()<brutePAD) {\n            pinHTML=\"0\"+pinHTML;\n          }\n        }\n\n        if (bruteSTARTchar!=\"\") {\n          pinHTML=bruteSTARTchar+pinHTML;\n        }\n\n        if (bruteENDchar!=\"\") {\n          pinHTML=pinHTML+bruteENDchar;\n        }\n          \n        for (int i=0; i<=pinHTML.length(); i++) {\n          if (pinHTML.charAt(i) == '0') {\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"0000\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"11110000\");\n            }\n          }\n          else if (pinHTML.charAt(i) == '1') {\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"0001\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"11100001\");\n            }\n          }\n          else if (pinHTML.charAt(i) == '2') {\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"0010\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"11010010\");\n            }\n          }\n          else if (pinHTML.charAt(i) == '3') {\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"0011\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"11000011\");\n            }\n          }\n          else if (pinHTML.charAt(i) == '4') {\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"0100\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"10110100\");\n            }\n          }\n          else if (pinHTML.charAt(i) == '5') {\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"0101\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"10100101\");\n            }\n          }\n          else if (pinHTML.charAt(i) == '6') {\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"0110\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"10010110\");\n            }\n          }\n          else if (pinHTML.charAt(i) == '7') {\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"0111\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"10000111\");\n            }\n          }\n          else if (pinHTML.charAt(i) == '8') {\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"1000\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"01111000\");\n            }\n          }\n          else if (pinHTML.charAt(i) == '9') {\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"1001\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"01101001\");\n            }\n          }\n          else if ((pinHTML.charAt(i) == '*')||(pinHTML.charAt(i) == 'A')) {\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"1010\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"01011010\");\n            }\n          }\n          else if ((pinHTML.charAt(i) == '#')||(pinHTML.charAt(i) == 'B')) {\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"1011\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"01001011\");\n            }\n          }\n          else if (pinHTML.charAt(i) == 'C') { //F1\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"1100\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"00111100\");\n            }\n          }\n          else if (pinHTML.charAt(i) == 'D') { //F2\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"1101\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"00101101\");\n            }\n          }\n          else if (pinHTML.charAt(i) == 'E') { //F3\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"1110\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"00011110\");\n            }\n          }\n          else if (pinHTML.charAt(i) == 'F') { //F4\n            if (pinBITS==4) {\n              pinSEND(pinHTMLDELAY,\"1111\");\n            }\n            else if (pinBITS==8) {\n              pinSEND(pinHTMLDELAY,\"00001111\");\n            }\n          }\n        }\n\n        server.handleClient();\n        if (TXstatus!=1) {\n          break;\n        }\n\n        bruteFAILS++;\n\n        if (bruteFAILS>=4294967000) {\n          bruteFAILS=(4294966000);\n        }\n        if (bruteFAILdelay>=4294967000) {\n          bruteFAILdelay=(4294966000);\n        }\n        \n        if (bruteFAILmultiplier!=0) {\n          bruteFAILmultiplierCURRENT++;\n          if (bruteFAILmultiplierCURRENT>=bruteFAILmultiplierAFTER) {\n            bruteFAILmultiplierCURRENT=0;\n            bruteFAILdelay=(bruteFAILdelay*bruteFAILmultiplier);\n          }\n        }\n        \n        if ((bruteFAILS>=bruteFAILSmax)&&(bruteFAILSmax!=0)) {\n          delay(bruteFAILdelay*1000);\n        }\n        else {\n          delay(delayAFTERpin);\n        }\n        \n      }\n      pinMode(DATA0, INPUT);\n      pinMode(DATA1, INPUT);\n      wg.clear();\n      pinHTML=\"\";\n      pinHTMLDELAY=100;\n      TXstatus=0;\n      bruteforcing=0;\n      brutePAD=0;\n      bruteSTARTchar=\"\";\n      bruteENDchar=\"\";\n      bruteFAILdelay=0;\n      bruteFAILS=0;\n      bruteFAILmultiplier=0;\n      bruteFAILmultiplierCURRENT=0;\n      bruteFAILmultiplierAFTER=0;\n      delayAFTERpin=0;\n      bruteFAILSmax=0;\n    }\n\n\n    if (server.hasArg(\"binHTML\")) {\n      String binHTML=server.arg(\"binHTML\");\n      wg.pause();\n      digitalWrite(DATA0, HIGH);\n      pinMode(DATA0,OUTPUT);\n      digitalWrite(DATA1, HIGH);\n      pinMode(DATA1,OUTPUT);\n\n      for (int i=0; i<=binHTML.length(); i++) {\n        if (binHTML.charAt(i) == '0') {\n          digitalWrite(DATA0, LOW);\n          delayMicroseconds(txdelayus);\n          digitalWrite(DATA0, HIGH);\n        }\n        else if (binHTML.charAt(i) == '1') {\n          digitalWrite(DATA1, LOW);\n          delayMicroseconds(txdelayus);\n          digitalWrite(DATA1, HIGH);\n        }\n        delay(txdelayms);\n      }\n\n      pinMode(DATA0, INPUT);\n      pinMode(DATA1, INPUT);\n      wg.clear();\n\n      experimentalStatus=String()+\"Transmitting Binary: \"+binHTML;\n      binHTML=\"\";\n    }\n\n    if (server.arg(\"fuzzType\")==\"simultaneous\") {\n\n      int fuzzTimes=0;\n      dos=0;\n      if ((server.arg(\"fuzzTimes\"))==\"dos\") {\n        dos=1;\n        server.send(200, \"text/html\", String()+\n        \"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>\"\n        \"<a href=\\\"/experimental\\\"><- BACK TO EXPERIMENTAL TX MODE</a><br><br>\"\n        \"Denial of Service mode active.<br>Transmitting D0 and D1 bits simultaneously until stopped.\"\n        \"<br>This may take a while, your device will be busy until the sequence has been completely transmitted!\"\n        \"<br>Please \\\"STOP CURRENT TRANSMISSION\\\" before attempting to use your device or simply wait for the transmission to finish.<br>\"\n        \"You can view if the fuzzing attempt has completed by returning to the Experimental TX page and checking the status located under \\\"Transmit Status\\\"<br><br>\"\n        \"<a href=\\\"/stoptx\\\"><button>STOP CURRENT TRANSMISSION</button></a>\");\n        delay(50);\n      }\n      else {\n        fuzzTimes=server.arg(\"fuzzTimes\").toInt();\n        server.send(200, \"text/html\", String()+\n        \"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>\"\n        \"<a href=\\\"/experimental\\\"><- BACK TO EXPERIMENTAL TX MODE</a><br><br>\"\n        \"Transmitting D0 and D1 bits simultaneously \"+fuzzTimes+\" times.\"\n        \"<br>This may take a while, your device will be busy until the sequence has been completely transmitted!\"\n        \"<br>Please \\\"STOP CURRENT TRANSMISSION\\\" before attempting to use your device or simply wait for the transmission to finish.<br>\"\n        \"You can view if the fuzzing attempt has completed by returning to the Experimental TX page and checking the status located under \\\"Transmit Status\\\"<br><br>\"\n        \"<a href=\\\"/stoptx\\\"><button>STOP CURRENT TRANSMISSION</button></a>\");\n        delay(50);\n      }\n      \n      wg.pause();\n      digitalWrite(DATA0, HIGH);\n      pinMode(DATA0,OUTPUT);\n      digitalWrite(DATA1, HIGH);\n      pinMode(DATA1,OUTPUT);\n\n      TXstatus=1;\n\n      for (int i=0; i<=fuzzTimes || dos==1; i++) {\n        digitalWrite(DATA0, LOW);\n        digitalWrite(DATA1, LOW);\n        delayMicroseconds(txdelayus);\n        digitalWrite(DATA0, HIGH);\n        digitalWrite(DATA1, HIGH);\n        delay(txdelayms);\n        server.handleClient();\n        if (TXstatus!=1) {\n          break;\n        }\n      }\n\n      pinMode(DATA0, INPUT);\n      pinMode(DATA1, INPUT);\n      wg.clear();\n      TXstatus=0;\n      dos=0;\n\n      //experimentalStatus=String()+\"Transmitting D0 and D1 bits simultaneously \"+fuzzTimes+\" times.\";\n    }\n\n    if (server.arg(\"fuzzType\")==\"alternating\") {\n\n      int fuzzTimes=0;\n      dos=0;\n      if ((server.arg(\"fuzzTimes\"))==\"dos\") {\n        dos=1;\n        server.send(200, \"text/html\", String()+\n        \"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>\"\n        \"<a href=\\\"/experimental\\\"><- BACK TO EXPERIMENTAL TX MODE</a><br><br>\"\n        \"Denial of Service mode active.<br>Transmitting bits alternating between D0 and D1 until stopped.\"\n        \"<br>This may take a while, your device will be busy until the sequence has been completely transmitted!\"\n        \"<br>Please \\\"STOP CURRENT TRANSMISSION\\\" before attempting to use your device or simply wait for the transmission to finish.<br>\"\n        \"You can view if the fuzzing attempt has completed by returning to the Experimental TX page and checking the status located under \\\"Transmit Status\\\"<br><br>\"\n        \"<a href=\\\"/stoptx\\\"><button>STOP CURRENT TRANSMISSION</button></a>\");\n        delay(50);\n      }\n      else {\n        fuzzTimes=server.arg(\"fuzzTimes\").toInt();\n        server.send(200, \"text/html\", String()+\n        \"<a href=\\\"/\\\"><- BACK TO INDEX</a><br><br>\"\n        \"<a href=\\\"/experimental\\\"><- BACK TO EXPERIMENTAL TX MODE</a><br><br>\"\n        \"Transmitting \"+fuzzTimes+\" bits alternating between D0 and D1.\"\n        \"<br>This may take a while, your device will be busy until the sequence has been completely transmitted!\"\n        \"<br>Please \\\"STOP CURRENT TRANSMISSION\\\" before attempting to use your device or simply wait for the transmission to finish.<br>\"\n        \"You can view if the fuzzing attempt has completed by returning to the Experimental TX page and checking the status located under \\\"Transmit Status\\\"<br><br>\"\n        \"<a href=\\\"/stoptx\\\"><button>STOP CURRENT TRANSMISSION</button></a>\");\n        delay(50);\n      }\n      \n      wg.pause();\n      digitalWrite(DATA0, HIGH);\n      pinMode(DATA0,OUTPUT);\n      digitalWrite(DATA1, HIGH);\n      pinMode(DATA1,OUTPUT);\n\n      String binALT=\"\";\n      TXstatus=1;\n\n      for (int i=0; i<fuzzTimes || dos==1; i++) {\n        if (i%2==0) {\n          digitalWrite(DATA0, LOW);\n          delayMicroseconds(txdelayus);\n          digitalWrite(DATA0, HIGH);\n          binALT=binALT+\"0\";\n        }\n        else {\n           digitalWrite(DATA1, LOW);\n           delayMicroseconds(txdelayus);\n           digitalWrite(DATA1, HIGH);\n           binALT=binALT+\"1\";\n        }\n        delay(txdelayms);\n        server.handleClient();\n        if (TXstatus!=1) {\n          break;\n        }\n      }\n\n      pinMode(DATA0, INPUT);\n      pinMode(DATA1, INPUT);\n      wg.clear();\n      TXstatus=0;\n      dos=0;\n\n      //experimentalStatus=String()+\"Transmitting alternating bits: \"+binALT;\n      binALT=\"\";\n    }\n\n    if (server.arg(\"pushType\")==\"Ground\") {\n      Serial.end();\n      digitalWrite(3,LOW);\n      pinMode(3,OUTPUT);\n      delay(server.arg(\"pushTime\").toInt());\n      pinMode(3,INPUT);\n      Serial.begin(9600);\n\n      experimentalStatus=String()+\"Grounding \\\"Push to Open\\\" wire for \"+(server.arg(\"pushTime\").toInt())+\"ms.\";\n    }\n\n    if (server.arg(\"pushType\")==\"High\") {\n      Serial.end();\n      digitalWrite(3,HIGH);\n      pinMode(3,OUTPUT);\n      delay(server.arg(\"pushTime\").toInt());\n      pinMode(3,INPUT);\n      Serial.begin(9600);\n\n      experimentalStatus=String()+\"Outputting 3.3V on \\\"Push to Open\\\" wire for \"+(server.arg(\"pushTime\").toInt())+\"ms.\";\n    }\n\n    String activeTX=\"\";\n    if (TXstatus==1) {\n      \n      if (pinHTML!=\"\") {\n        String currentPIN=pinHTML;\n        activeTX=\"Brute forcing PIN: \"+currentPIN+\"<br><a href=\\\"/stoptx\\\"><button>STOP CURRENT TRANSMISSION</button></a>\";\n        currentPIN=\"\";\n      }\n      else if (dos==1) {\n        activeTX=\"Denial of Service mode active...<br><a href=\\\"/stoptx\\\"><button>STOP CURRENT TRANSMISSION</button></a>\";\n      }\n      else {\n        activeTX=\"Transmitting...<br><a href=\\\"/stoptx\\\"><button>STOP CURRENT TRANSMISSION</button></a>\";\n      }\n      \n    }\n    else {\n      activeTX=\"INACTIVE<br><button>NOTHING TO STOP</button>\";\n    }\n\n    server.send(200, \"text/html\", \n      String()+\n      F(\n      \"<!DOCTYPE HTML>\"\n      \"<html>\"\n      \"<head>\"\n      \"<title>Experimental TX Mode</title>\"\n      \"</head>\"\n      \"<body>\"\n      )+experimentalStatus+\"<br><br>\"\n      +F(\n      \"<b>Transmit Status:</b> \")+activeTX+F(\"<br><br>\"\n      \"<a href=\\\"/\\\"><- BACK TO INDEX</a><br>\"\n      \"<P>\"\n      \"<h1>Experimental TX Mode</h1>\"\n      \"<hr>\"\n      \"<small>\"\n      \"<b>Warning:</b> This mode is highly experimental, use at your own risk!<br>\"\n      \"Note: Timings for the Wiegand Data Pulse Width and Wiegand Data Interval may be changed on the settings page.\"\n      \"</small>\"\n      \"<br>\"\n      \"<hr>\"\n      \"<br>\"\n      \"<FORM action=\\\"/experimental\\\" id=\\\"transmitbinary\\\" method=\\\"post\\\">\"\n      \"<b>Binary Data:</b><br>\"\n      \"<small>Typically no need to include preamble</small><br>\"\n      \"<INPUT form=\\\"transmitbinary\\\" type=\\\"text\\\" name=\\\"binHTML\\\" value=\\\"\\\" pattern=\\\"[0-1]{1,}\\\" required title=\\\"Only 0's & 1's allowed, must not be empty\\\" minlength=\\\"1\\\" size=\\\"52\\\"><br>\"\n      \"<INPUT form=\\\"transmitbinary\\\" type=\\\"submit\\\" value=\\\"Transmit\\\"><br>\"\n      \"</FORM>\"\n      \"<br>\"\n      \"<hr>\"\n      \"<br>\"\n      \"<FORM action=\\\"/experimental\\\" id=\\\"transmitpin\\\" method=\\\"post\\\">\"\n      \"<b>Transmit PIN:</b><br>\"\n      \"<small>Available keys 0-9, * or A, # or B, F1 or C, F2 or D, F3 or E, F4 or F</small><br>\"\n      \"<small>PIN: </small><INPUT form=\\\"transmitpin\\\" type=\\\"text\\\" name=\\\"pinHTML\\\" value=\\\"\\\" pattern=\\\"[0-9*#A-F]{1,}\\\" required title=\\\"Available keys 0-9, * or A, # or B, F1 or C, F2 or D, F3 or E, F4 or F, must not be empty\\\" minlength=\\\"1\\\" size=\\\"52\\\"><br>\"\n      \"<small>Delay between \\\"keypresses\\\": </small><INPUT form=\\\"transmitpin\\\" type=\\\"number\\\" name=\\\"pinHTMLDELAY\\\" value=\\\"100\\\" minlength=\\\"1\\\" min=\\\"0\\\" size=\\\"8\\\"><small>ms</small><br>\"\n      \"<INPUT form=\\\"transmitpin\\\" type=\\\"radio\\\" name=\\\"pinBITS\\\" id=\\\"pinBITS\\\" value=\\\"4\\\" checked required> <small>4bit Wiegand PIN Format</small>   \"\n      \"<INPUT form=\\\"transmitpin\\\" type=\\\"radio\\\" name=\\\"pinBITS\\\" id=\\\"pinBITS\\\" value=\\\"8\\\" required> <small>8bit Wiegand PIN Format</small><br>\"\n      \"<INPUT form=\\\"transmitpin\\\" type=\\\"submit\\\" value=\\\"Transmit\\\"><br>\"\n      \"</FORM>\"\n      \"<br>\"\n      \"<hr>\"\n      \"<br>\"\n      \"<FORM action=\\\"/experimental\\\" id=\\\"brutepin\\\" method=\\\"post\\\">\"\n      \"<b>Bruteforce PIN:</b><br>\"\n      \"<small>Delay between \\\"keypresses\\\": </small><INPUT form=\\\"brutepin\\\" type=\\\"number\\\" name=\\\"pinHTMLDELAY\\\" value=\\\"3\\\" minlength=\\\"1\\\" min=\\\"0\\\" size=\\\"8\\\"><small>ms</small><br>\"\n      \"<small>Delay between entering complete PINs: </small><INPUT form=\\\"brutepin\\\" type=\\\"number\\\" name=\\\"delayAFTERpin\\\" value=\\\"0\\\" minlength=\\\"1\\\" min=\\\"0\\\" size=\\\"8\\\"><small>ms</small><br>\"\n      \"<small>PIN begins with character(s): </small><INPUT form=\\\"brutepin\\\" type=\\\"text\\\" name=\\\"bruteSTARTchar\\\" value=\\\"\\\" pattern=\\\"[0-9*#A-F]{0,}\\\" title=\\\"Available keys 0-9, * or A, # or B, F1 or C, F2 or D, F3 or E, F4 or F, must not be empty\\\" size=\\\"8\\\"><br>\"\n      \"<small>PIN start position: </small><INPUT form=\\\"brutepin\\\" type=\\\"number\\\" name=\\\"bruteSTART\\\" value=\\\"0000\\\" minlength=\\\"1\\\" min=\\\"0\\\" size=\\\"8\\\"><br>\"\n      \"<small>PIN end position: </small><INPUT form=\\\"brutepin\\\" type=\\\"number\\\" name=\\\"bruteEND\\\" value=\\\"9999\\\" minlength=\\\"1\\\" min=\\\"0\\\" size=\\\"8\\\"><br>\"\n      \"<small>PIN ends with character(s): </small><INPUT form=\\\"brutepin\\\" type=\\\"text\\\" name=\\\"bruteENDchar\\\" value=\\\"#\\\" pattern=\\\"[0-9*#A-F]{0,}\\\" title=\\\"Available keys 0-9, * or A, # or B, F1 or C, F2 or D, F3 or E, F4 or F, must not be empty\\\" size=\\\"8\\\"><br>\"\n      \"<small>NOTE: The advanced timing settings listed below override the \\\"Delay between entering complete PINs\\\" setting(listed above) when the conditions listed below are met.</small><br>\"\n      \"<small>Number of failed PIN attempts(X) before a delay: </small><INPUT form=\\\"brutepin\\\" type=\\\"number\\\" name=\\\"bruteFAILSmax\\\" value=\\\"0\\\" minlength=\\\"1\\\" min=\\\"0\\\" size=\\\"8\\\"><br>\"\n      \"<small>Delay in seconds(Y) after [X] failed PINs: </small><INPUT form=\\\"brutepin\\\" type=\\\"number\\\" name=\\\"bruteFAILdelay\\\" value=\\\"0\\\" minlength=\\\"1\\\" min=\\\"0\\\" size=\\\"8\\\"><small>s</small><br>\"\n      \"<small>Multiply delay [Y] by <INPUT form=\\\"brutepin\\\" type=\\\"number\\\" name=\\\"bruteFAILmultiplier\\\" value=\\\"0\\\" minlength=\\\"1\\\" min=\\\"0\\\" size=\\\"4\\\"> after every <INPUT form=\\\"brutepin\\\" type=\\\"number\\\" name=\\\"bruteFAILmultiplierAFTER\\\" value=\\\"0\\\" minlength=\\\"1\\\" min=\\\"0\\\" size=\\\"4\\\"> failed pin attempts</small><br>\"\n      \"<INPUT form=\\\"brutepin\\\" type=\\\"radio\\\" name=\\\"pinBITS\\\" id=\\\"pinBITS\\\" value=\\\"4\\\" checked required> <small>4bit Wiegand PIN Format</small>   \"\n      \"<INPUT form=\\\"brutepin\\\" type=\\\"radio\\\" name=\\\"pinBITS\\\" id=\\\"pinBITS\\\" value=\\\"8\\\" required> <small>8bit Wiegand PIN Format</small><br>\"\n      \"<INPUT form=\\\"brutepin\\\" type=\\\"submit\\\" value=\\\"Transmit\\\"></FORM><br>\"\n      \"<br>\"\n      \"<hr>\"\n      \"<br>\"\n      \"<b>Fuzzing:</b><br><br>\"\n      \"<FORM action=\\\"/experimental\\\" id=\\\"fuzz\\\" method=\\\"post\\\">\"\n      \"<b>Number of bits:</b>\"\n      \"<INPUT form=\\\"fuzz\\\" type=\\\"number\\\" name=\\\"fuzzTimes\\\" value=\\\"100\\\" minlength=\\\"1\\\" min=\\\"1\\\" max=\\\"2147483647\\\" size=\\\"32\\\"><br>\"\n      //\"<INPUT form=\\\"fuzz\\\" type=\\\"text\\\" name=\\\"fuzzTimes\\\" value=\\\"\\\" pattern=\\\"^[1-9]+[0-9]*$\\\" required title=\\\"Must be a number > 0, must not be empty \\\" minlength=\\\"1\\\" size=\\\"32\\\"><br>\"\n      \"<INPUT form=\\\"fuzz\\\" type=\\\"radio\\\" name=\\\"fuzzType\\\" id=\\\"simultaneous\\\" value=\\\"simultaneous\\\" required> <small>Transmit a bit simultaneously on D0 and D1 (X bits per each line)</small><br>\"\n      \"<INPUT form=\\\"fuzz\\\" type=\\\"radio\\\" name=\\\"fuzzType\\\" id=\\\"alternating\\\" value=\\\"alternating\\\"> <small>Transmit X bits alternating between D0 and D1 each bit (01010101,etc)</small><br>\"\n      \"<INPUT form=\\\"fuzz\\\" type=\\\"submit\\\" value=\\\"Fuzz\\\"><br>\"\n      \"</FORM>\"\n      \"<br>\"\n      \"<hr>\"\n      \"<br>\"\n      \"<b>Denial Of Service Mode:</b><br><br>\"\n      \"<FORM action=\\\"/experimental\\\" id=\\\"dos\\\" method=\\\"post\\\">\"\n      \"<b>Type of Attack:</b>\"\n      \"<INPUT hidden=\\\"1\\\" form=\\\"dos\\\" type=\\\"text\\\" name=\\\"fuzzTimes\\\" value=\\\"dos\\\"><br>\"\n      \"<INPUT form=\\\"dos\\\" type=\\\"radio\\\" name=\\\"fuzzType\\\" id=\\\"simultaneous\\\" value=\\\"simultaneous\\\" required> <small>Transmit a bit simultaneously on D0 and D1 until stopped</small><br>\"\n      \"<INPUT form=\\\"dos\\\" type=\\\"radio\\\" name=\\\"fuzzType\\\" id=\\\"alternating\\\" value=\\\"alternating\\\"> <small>Transmit bits alternating between D0 and D1 each bit (01010101,etc) until stopped</small><br>\"\n      \"<INPUT form=\\\"dos\\\" type=\\\"submit\\\" value=\\\"Start DoS\\\"><br>\"\n      \"</FORM>\"\n      \"<br>\"\n      \"<hr>\"\n      \"<br>\"\n      \"<b>Push Button for Door Open:</b><br>\"\n      \"<small>Connect \\\"Push to Open\\\" wire from the reader to the RX pin(GPIO3) on the programming header on ESP-RFID-Tool.</small><br>\"\n      \"<small>Warning! Selecting the wrong trigger signal type may cause damage to the connected hardware.</small><br><br>\"\n      \"<FORM action=\\\"/experimental\\\" id=\\\"push\\\" method=\\\"post\\\">\"\n      \"<b>Time in ms to push the door open button:</b>\"\n      \"<INPUT form=\\\"push\\\" type=\\\"text\\\" name=\\\"pushTime\\\" value=\\\"50\\\" pattern=\\\"^[1-9]+[0-9]*$\\\" required title=\\\"Must be a number > 0, must not be empty\\\" minlength=\\\"1\\\" size=\\\"32\\\"><br>\"\n      \"<b>Does the wire expect a High or Low signal to open the door:</b>\"\n      \"<INPUT form=\\\"push\\\" type=\\\"radio\\\" name=\\\"pushType\\\" id=\\\"Ground\\\" value=\\\"Ground\\\" checked required> <small>Low Signal[Ground]</small>   \"\n      \"<INPUT form=\\\"push\\\" type=\\\"radio\\\" name=\\\"pushType\\\" id=\\\"Ground\\\" value=\\\"High\\\" required> <small>High Signal[3.3V]</small><br>\"\n      \"<INPUT form=\\\"push\\\" type=\\\"submit\\\" value=\\\"Push\\\"><br>\"\n      \"</FORM>\"\n      \"<br>\"\n      \"<hr>\"\n      \"<br>\"\n      \"</P>\"\n      \"</body>\"\n      \"</html>\"\n      )\n    );\n\n    if (server.args()>=1) {\n      if (safemode==1) {\n        delay(50);\n        ESP.restart();\n      }\n    }\n\n  });\n\n  server.begin();\n  WiFiClient client;\n  client.setNoDelay(1);\n\n//  Serial.println(\"Web Server Started\");\n\n  MDNS.begin(\"ESP\");\n\n  httpUpdater.setup(&httpServer, update_path, update_username, update_password);\n  httpServer.begin();\n\n  MDNS.addService(\"http\", \"tcp\", 1337);\n  \n  if (ftpenabled==1){\n    ftpSrv.begin(String(ftp_username),String(ftp_password));\n  }\n\n//Start RFID Reader\n  pinMode(LED_BUILTIN, OUTPUT);  // LED\n  if (ledenabled==1){\n    digitalWrite(LED_BUILTIN, LOW);\n  }\n  else{\n    digitalWrite(LED_BUILTIN, HIGH);\n  }\n\n}\n//\n\n//Do It!\n\n///////////////////////////////////////////////////////\n// LOOP function\nvoid loop()\n{\n  if (ftpenabled==1){\n    ftpSrv.handleFTP();\n  }\n  server.handleClient();\n  httpServer.handleClient();\n  while (Serial.available()) {\n    String cmd = Serial.readStringUntil(':');\n    if(cmd == \"ResetDefaultConfig\"){\n      loadDefaults();\n      ESP.restart();\n    }\n  }\n\n//Serial.print(\"Free heap-\");\n//Serial.println(ESP.getFreeHeap(),DEC);\n\n  if(wg.available()) {\n    wg.pause();             // pause Wiegand pin interrupts\n    LogWiegand(wg);\n    wg.clear();             // compulsory to call clear() to enable interrupts for subsequent data\n    if (safemode==1) {\n      ESP.restart();\n    }\n  }\n\n}\n"
  },
  {
    "path": "Source Code/esprfidtool/pinSEND.h",
    "content": "void pinSEND(int pinDELAY,String pinBIN) {\n  for (int i=0; i<=pinBIN.length(); i++) {\n    if (pinBIN.charAt(i) == '0') {\n      digitalWrite(DATA0, LOW);\n      delayMicroseconds(txdelayus);\n      digitalWrite(DATA0, HIGH);\n    }\n    else if (pinBIN.charAt(i) == '1') {\n      digitalWrite(DATA1, LOW);\n      delayMicroseconds(txdelayus);\n      digitalWrite(DATA1, HIGH);\n    }\n    delay(txdelayms);\n  }\n  yield();\n  delay(pinDELAY);\n  pinBIN=\"\";\n  pinDELAY=100;\n}\n"
  },
  {
    "path": "Source Code/esprfidtool/strrev.h",
    "content": "/* Copyright (c) 2007  Dmitry Xmelkov\n   All rights reserved.\n   Redistribution and use in source and binary forms, with or without\n   modification, are permitted provided that the following conditions are met:\n   * Redistributions of source code must retain the above copyright\n     notice, this list of conditions and the following disclaimer.\n   * Redistributions in binary form must reproduce the above copyright\n     notice, this list of conditions and the following disclaimer in\n     the documentation and/or other materials provided with the\n     distribution.\n   * Neither the name of the copyright holders nor the names of\n     contributors may be used to endorse or promote products derived\n     from this software without specific prior written permission.\n   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n   POSSIBILITY OF SUCH DAMAGE.\n */\n\n/* $Id$  */\nchar * strrev (char *s)\n{\n    char *p1, *p2;\n    \n    for (p2 = s; *p2; ) p2++;\n    p1 = s;\n    while (p1 < p2) {\n      char c1 = *p1;\n      char c2 = *--p2;\n      *p1++ = c2;\n      *p2 = c1;\n    }\n    return s;\n}\n"
  },
  {
    "path": "Source Code/esprfidtool/version.h",
    "content": "String version = \"1.2.1\";\nString APIversion = \"1.0.4\";\n"
  }
]