├── Static └── ScreenShot.png ├── MitnalPkg ├── Console.h ├── OAuth.h ├── MitnalPkg.dec ├── Twitter.h ├── Graphics.h ├── Http.h ├── Mitnal.inf ├── Common.h ├── Common.c ├── MitnalPkg.dsc ├── Graphics.c ├── Console.c ├── OAuth.c ├── Main.c ├── Twitter.c └── Http.c ├── .gitignore ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── README.md ├── Makefile ├── LICENSE ├── Dockerfile └── Conf └── target.txt /Static/ScreenShot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arata-nvm/mitnal/HEAD/Static/ScreenShot.png -------------------------------------------------------------------------------- /MitnalPkg/Console.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | EFI_STATUS InitConsole(); 5 | 6 | void EFIAPI PrintUtf16(IN CHAR16 *Format, ...); 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /edk2 2 | /image 3 | /dist 4 | /MitnalPkg/Secrets 5 | /MitnalPkg/hmac 6 | /MitnalPkg/sha 7 | /MitnalPkg/json.h 8 | /MitnalPkg/font 9 | OVMF* 10 | certdb 11 | -------------------------------------------------------------------------------- /MitnalPkg/OAuth.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | EFI_STATUS GenerateOAuthHeader(IN CHAR8 *Method, IN CHAR8 *Url, IN CHAR8 *Query, OUT CHAR8 *Buffer, IN UINTN BufferSize); 5 | -------------------------------------------------------------------------------- /MitnalPkg/MitnalPkg.dec: -------------------------------------------------------------------------------- 1 | [Defines] 2 | DEC_SPECIFICATION = 0x00010005 3 | PACKAGE_NAME = MitnalPkg 4 | PACKAGE_GUID = 09e58811-3de4-4cd7-b849-de8ef654c9b4 5 | PACKAGE_VERSION = 0.1 6 | -------------------------------------------------------------------------------- /MitnalPkg/Twitter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | typedef struct { 5 | const CHAR8 *CreatedAt; 6 | const CHAR8 *UserName; 7 | const CHAR8 *Text; 8 | } TWEET; 9 | 10 | EFI_STATUS Tweet(IN CHAR8 *Message); 11 | 12 | EFI_STATUS HomeTimeline(OUT TWEET *Tweets, OUT UINTN *TweetCount); 13 | -------------------------------------------------------------------------------- /MitnalPkg/Graphics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define FONT_SIZE 16 5 | #define FONT_SIZE_HALF (FONT_SIZE / 2) 6 | 7 | extern UINTN gHorizontalResolution; 8 | extern UINTN gVerticalResolution; 9 | 10 | EFI_STATUS InitGraphics(IN EFI_HANDLE ImageHandle); 11 | 12 | void DrawChar(UINTN x, UINTN y, CHAR16 Char); 13 | -------------------------------------------------------------------------------- /MitnalPkg/Http.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | typedef struct { 6 | EFI_HTTP_METHOD Method; 7 | CHAR16 *Url; 8 | 9 | EFI_HTTP_HEADER *Headers; 10 | UINTN HeaderCount; 11 | 12 | VOID *Body; 13 | UINTN BodyLength; 14 | } HTTP_REQUEST_CONTEXT; 15 | 16 | EFI_STATUS InitHttpProtocol(); 17 | 18 | EFI_STATUS SendRequest(IN HTTP_REQUEST_CONTEXT *Context); 19 | 20 | EFI_STATUS ReceiveResponse(OUT UINT8 *Buffer, IN OUT UINTN *BufferSize); 21 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/cpp/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] Debian / Ubuntu version (use Debian 11/9, Ubuntu 18.04/21.04 on local arm64/Apple Silicon): debian-11, debian-10, debian-9, ubuntu-21.04, ubuntu-20.04, ubuntu-18.04 4 | ARG VARIANT="bullseye" 5 | FROM mcr.microsoft.com/vscode/devcontainers/cpp:0-${VARIANT} 6 | 7 | # [Optional] Uncomment this section to install additional packages. 8 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 9 | # && apt-get -y install --no-install-recommends 10 | -------------------------------------------------------------------------------- /MitnalPkg/Mitnal.inf: -------------------------------------------------------------------------------- 1 | [Defines] 2 | INF_VERSION = 0x00010006 3 | BASE_NAME = Mitnal 4 | FILE_GUID = 09e58811-3de4-4cd7-b849-de8ef654c9b4 5 | MODULE_TYPE = UEFI_APPLICATION 6 | VERSION_STRING = 0.1 7 | ENTRY_POINT = UefiMain 8 | 9 | [Sources] 10 | Common.c 11 | Console.c 12 | Graphics.c 13 | Http.c 14 | Main.c 15 | OAuth.c 16 | Twitter.c 17 | hmac/hmac_sha1.c 18 | sha/sha1.c 19 | 20 | [Packages] 21 | MdePkg/MdePkg.dec 22 | 23 | [LibraryClasses] 24 | UefiLib 25 | UefiApplicationEntryPoint 26 | 27 | [Protocols] 28 | gEfiHttpServiceBindingProtocolGuid 29 | gEfiHttpProtocolGuid 30 | gEfiTimestampProtocolGuid -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mitnal 2 | 3 | Twitter client for UEFI. 4 | 5 | ## Screenshot 6 | 7 | ![](Static/ScreenShot.png) 8 | 9 | ## Requirements 10 | 11 | You need to enable "HTTP Protocol Stack Support" in your UEFI. 12 | 13 | ## How to build 14 | 15 | Create a `MitnalPkg/Secrets` file as follows: 16 | 17 | ```c 18 | static const CHAR8 *gConsumerKey = ""; 19 | static const CHAR8 *gConsumerSecret = ""; 20 | static const CHAR8 *gAccessToken = ""; 21 | static const CHAR8 *gAccessTokenSecret = ""; 22 | ``` 23 | 24 | Then, 25 | 26 | ```bash 27 | $ make docker/build 28 | $ make run 29 | ``` 30 | 31 | ## Usage 32 | 33 | Here is a list of available commands: 34 | 35 | - `home`: show your timeline 36 | - `tweet hello`: tweet 'hello' 37 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build 2 | build: 3 | bash -c 'cd edk2 && source ./edksetup.sh && build' 4 | mkdir -p dist 5 | cp ./edk2/Build/OvmfX64/DEBUG_CLANG38/FV/OVMF.fd ./dist/ 6 | cp ./edk2/Build/MitnalX64/DEBUG_CLANG38/X64/Mitnal.efi ./dist/ 7 | 8 | .PHONY: run 9 | run: 10 | mkdir -p ./image/EFI/BOOT 11 | cp ./dist/Mitnal.efi ./image/EFI/BOOT/BOOTX64.efi 12 | qemu-system-x86_64 \ 13 | -bios ./dist/OVMF.fd \ 14 | -drive if=ide,format=raw,file=fat:rw:image,index=0,media=disk \ 15 | -fw_cfg name=etc/edk2/https/cacerts,file=./dist/certdb 16 | 17 | .PHONY: docker/build 18 | docker/build: 19 | mkdir -p dist 20 | docker build -t arata-nvm/mitnal . 21 | docker container create --name mitnal-temp arata-nvm/mitnal 22 | docker cp mitnal-temp:/edk2/Build/OvmfX64/RELEASE_CLANG38/FV/OVMF.fd ./dist/ 23 | docker cp mitnal-temp:/edk2/Build/MitnalX64/RELEASE_CLANG38/X64/Mitnal.efi ./dist/ 24 | docker cp mitnal-temp:/certdb ./dist/ 25 | docker container rm mitnal-temp 26 | 27 | -------------------------------------------------------------------------------- /MitnalPkg/Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Console.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #define HANDLE_ERROR(status) \ 8 | if (EFI_ERROR(status)) { \ 9 | PrintUtf16(L"error: %r at %a:%d\n", Status, __FILE__, __LINE__); \ 10 | return status; \ 11 | } 12 | 13 | #define false (0 != 0) 14 | 15 | #ifndef __cplusplus 16 | typedef BOOLEAN bool; 17 | #endif 18 | typedef BOOLEAN boolean; 19 | typedef CHAR8 byte; 20 | 21 | extern EFI_BOOT_SERVICES *gBS; 22 | 23 | VOID UrlEncode(CHAR8 *Str, CHAR8 *EncodedStr); 24 | 25 | VOID *malloc(UINTN Size); 26 | 27 | VOID free(VOID *Buffer); 28 | 29 | VOID memcpy(VOID *Dst, const VOID *Src, UINTN Len); 30 | 31 | VOID memset(VOID *Dst, const UINT8 Value, UINTN Len); 32 | 33 | uintmax_t strtoumax(const char *nptr, char **endptr, int base); 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 arata-nvm 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /MitnalPkg/Common.c: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | #include 3 | 4 | // ref: https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3986.html#unreserved 5 | void UrlEncode(CHAR8 *Str, CHAR8 *StrEnc) { 6 | static const CHAR8 HEX[] = "0123456789ABCDEF"; 7 | 8 | UINTN StrSize = AsciiStrLen(Str); 9 | CHAR8 *ptr = StrEnc; 10 | for (UINTN i = 0; i < StrSize; i++) { 11 | CHAR8 c = Str[i]; 12 | if ((0x30 <= c && c <= 0x39) || (0x41 <= c && c <= 0x5a) || (0x61 <= c && c <= 0x7a) || c == 0x2d || c == 0x2e || c == 0x5f || c == 0x7e) { 13 | *ptr++ = c; 14 | continue; 15 | } 16 | 17 | *ptr++ = '%'; 18 | *ptr++ = HEX[c >> 4]; 19 | *ptr++ = HEX[c & 0xf]; 20 | } 21 | *ptr = 0; 22 | } 23 | 24 | VOID *malloc(UINTN Size) { 25 | VOID *Buffer; 26 | gBS->AllocatePool( 27 | EfiBootServicesData, 28 | Size, 29 | (VOID **)&Buffer); 30 | return Buffer; 31 | } 32 | 33 | VOID free(VOID *Buffer) { 34 | gBS->FreePool(Buffer); 35 | } 36 | 37 | VOID memcpy(VOID *Dst, const VOID *Src, UINTN Len) { 38 | CopyMem(Dst, Src, Len); 39 | } 40 | 41 | VOID memset(VOID *Dst, const UINT8 Value, UINTN Len) { 42 | SetMem(Dst, Len, Value); 43 | } 44 | 45 | uintmax_t 46 | strtoumax(const char *nptr, char **endptr, int base) { 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /MitnalPkg/MitnalPkg.dsc: -------------------------------------------------------------------------------- 1 | [Defines] 2 | PLATFORM_NAME = MitnalPkg 3 | PLATFORM_GUID = 09e58811-3de4-4cd7-b849-de8ef654c9b4 4 | PLATFORM_VERSION = 0.1 5 | DSC_SPECIFICATION = 0x00010005 6 | OUTPUT_DIRECTORY = Build/Mitnal$(ARCH) 7 | SUPPORTED_ARCHITECTURES = X64 8 | BUILD_TARGETS = DEBUG|RELEASE|NOOPT 9 | 10 | [LibraryClasses] 11 | UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf 12 | UefiLib|MdePkg/Library/UefiLib/UefiLib.inf 13 | 14 | BaseLib|MdePkg/Library/BaseLib/BaseLib.inf 15 | BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf 16 | DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf 17 | DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf 18 | MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf 19 | PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf 20 | PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf 21 | UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf 22 | UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf 23 | 24 | [Components] 25 | MitnalPkg/Mitnal.inf 26 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/cpp 3 | { 4 | "name": "C++", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | // Update 'VARIANT' to pick an Debian / Ubuntu OS version: debian-11, debian-10, debian-9, ubuntu-21.04, ubuntu-20.04, ubuntu-18.04 8 | // Use Debian 11, Debian 9, Ubuntu 18.04 or Ubuntu 21.04 on local arm64/Apple Silicon 9 | "args": { 10 | "VARIANT": "debian-11" 11 | } 12 | }, 13 | "runArgs": [ 14 | "--cap-add=SYS_PTRACE", 15 | "--security-opt", 16 | "seccomp=unconfined", 17 | ], 18 | // Set *default* container specific settings.json values on container create. 19 | "settings": {}, 20 | // Add the IDs of extensions you want installed when the container is created. 21 | "extensions": [ 22 | "ms-vscode.cpptools", 23 | "ms-vscode.cmake-tools" 24 | ], 25 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 26 | // "forwardPorts": [], 27 | // Use 'postCreateCommand' to run commands after the container is created. 28 | // "postCreateCommand": "gcc -v", 29 | // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 30 | "remoteUser": "vscode" 31 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 ubuntu:20.04 2 | 3 | RUN apt update \ 4 | && apt install -y build-essential git uuid-dev python3-distutils clang llvm nasm acpica-tools p11-kit 5 | 6 | RUN git clone --recursive https://github.com/tianocore/edk2.git \ 7 | && cd edk2 \ 8 | && git checkout stable/202011 \ 9 | && make -C BaseTools/Source/C 10 | 11 | RUN sed -i 's/DEFINE NETWORK_TLS_ENABLE = FALSE/DEFINE NETWORK_TLS_ENABLE = TRUE/' edk2/OvmfPkg/OvmfPkgX64.dsc \ 12 | && sed -i 's/DEFINE NETWORK_HTTP_BOOT_ENABLE = FALSE/DEFINE NETWORK_HTTP_BOOT_ENABLE = TRUE/' edk2/OvmfPkg/OvmfPkgX64.dsc 13 | 14 | RUN cd edk2 && bash -c 'source edksetup.sh && build -a X64 -b RELEASE -t CLANG38 -p OvmfPkg/OvmfPkgX64.dsc' 15 | 16 | COPY MitnalPkg edk2/MitnalPkg 17 | 18 | RUN git clone https://github.com/Akagi201/hmac-sha1 \ 19 | && mv hmac-sha1/src/sha edk2/MitnalPkg/ \ 20 | && mv hmac-sha1/src/hmac edk2/MitnalPkg/ \ 21 | && sed -i '11i #include \n' edk2/MitnalPkg/hmac/hmac.h 22 | 23 | RUN git clone https://github.com/sheredom/json.h \ 24 | && mv json.h/json.h edk2/MitnalPkg/ \ 25 | && sed -i '/#include /d' edk2/MitnalPkg/json.h \ 26 | && sed -i '/#include /d' edk2/MitnalPkg/json.h 27 | 28 | RUN git clone https://github.com/tanakamasayuki/efont \ 29 | && mv efont/src edk2/MitnalPkg/font \ 30 | && sed -i '/#include /d' edk2/MitnalPkg/font/efont.h 31 | 32 | RUN cd edk2 && bash -c 'source edksetup.sh && build -a X64 -b RELEASE -t CLANG38 -p MitnalPkg/MitnalPkg.dsc' 33 | 34 | RUN p11-kit extract --format=edk2-cacerts --filter=ca-anchors --purpose=server-auth certdb 35 | -------------------------------------------------------------------------------- /MitnalPkg/Graphics.c: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | #include "Console.h" 3 | #include 4 | 5 | #define PROGMEM 6 | #include "font/efont.h" 7 | 8 | static EFI_GRAPHICS_OUTPUT_PROTOCOL *gGop; 9 | static UINT8 *gFrameBuffer; 10 | static UINTN gFrameBufferSize; 11 | static UINTN gPixelsPerScanLine; 12 | UINTN gHorizontalResolution; 13 | UINTN gVerticalResolution; 14 | 15 | EFI_STATUS InitGraphics(IN EFI_HANDLE ImageHandle) { 16 | EFI_STATUS Status; 17 | 18 | UINTN HandleSize = 0; 19 | EFI_HANDLE *Handles = NULL; 20 | Status = gBS->LocateHandleBuffer( 21 | ByProtocol, 22 | &gEfiGraphicsOutputProtocolGuid, 23 | NULL, 24 | &HandleSize, 25 | &Handles); 26 | HANDLE_ERROR(Status) 27 | 28 | Status = gBS->OpenProtocol( 29 | Handles[0], 30 | &gEfiGraphicsOutputProtocolGuid, 31 | (VOID **)&gGop, 32 | ImageHandle, 33 | NULL, 34 | EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); 35 | HANDLE_ERROR(Status) 36 | 37 | gBS->FreePool(Handles); 38 | 39 | gFrameBuffer = (UINT8 *)gGop->Mode->FrameBufferBase; 40 | gFrameBufferSize = gGop->Mode->FrameBufferSize; 41 | gPixelsPerScanLine = gGop->Mode->Info->PixelsPerScanLine; 42 | gHorizontalResolution = gGop->Mode->Info->HorizontalResolution; 43 | gVerticalResolution = gGop->Mode->Info->VerticalResolution; 44 | 45 | ZeroMem(gFrameBuffer, gFrameBufferSize); 46 | 47 | return EFI_SUCCESS; 48 | } 49 | 50 | void PutPixel(UINTN x, UINTN y, BOOLEAN colored) { 51 | UINT8 *ptr = gFrameBuffer + 4 * (gPixelsPerScanLine * y + x); 52 | UINT8 c = colored ? 255 : 0; 53 | ptr[0] = c; 54 | ptr[1] = c; 55 | ptr[2] = c; 56 | } 57 | 58 | void DrawChar(UINTN x, UINTN y, CHAR16 Char) { 59 | UINT16 Buffer[16]; 60 | getefontData((UINT8 *)&Buffer, Char); 61 | 62 | for (UINTN dy = 0; dy < 16; dy++) { 63 | for (UINTN dx = 0; dx < 16; dx++) { 64 | UINT16 FontData = (Buffer[dy] >> 8) | (Buffer[dy] << 8); 65 | BOOLEAN Colored = (FontData >> (15 - dx)) & 0x1; 66 | PutPixel(x + dx, y + dy, Colored); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /MitnalPkg/Console.c: -------------------------------------------------------------------------------- 1 | #include "Console.h" 2 | #include "Common.h" 3 | #include "Graphics.h" 4 | #include 5 | #include 6 | 7 | static UINTN gColumn = 0; 8 | static UINTN gRow = 0; 9 | static UINTN gMaxColumn; 10 | static UINTN gMaxRow; 11 | static CHAR16 *gBuffer; 12 | 13 | EFI_STATUS InitConsole() { 14 | EFI_STATUS Status; 15 | 16 | gMaxColumn = gHorizontalResolution / FONT_SIZE_HALF - 1; 17 | gMaxRow = gVerticalResolution / FONT_SIZE; 18 | 19 | Status = gBS->AllocatePool( 20 | EfiBootServicesData, 21 | gMaxColumn * gMaxRow, 22 | (VOID **)&gBuffer); 23 | HANDLE_ERROR(Status) 24 | ZeroMem(gBuffer, gMaxColumn * gMaxRow); 25 | 26 | return EFI_SUCCESS; 27 | } 28 | 29 | static BOOLEAN IsHalfWidth(CHAR16 Char) { 30 | return Char <= 0xff; 31 | } 32 | 33 | static VOID Scroll() { 34 | for (UINTN y = 0; y < gMaxRow - 1; y++) { 35 | for (UINTN x = 0; x < gMaxColumn; x++) { 36 | gBuffer[x + y * gMaxColumn] = gBuffer[x + (y + 1) * gMaxColumn]; 37 | DrawChar(x * FONT_SIZE_HALF, y * FONT_SIZE, gBuffer[x + y * gMaxColumn]); 38 | if (!IsHalfWidth(gBuffer[x + y * gMaxColumn])) { 39 | x++; 40 | } 41 | } 42 | } 43 | 44 | for (UINTN x = 0; x < gMaxColumn; x++) { 45 | gBuffer[x + (gMaxRow - 1) * gMaxColumn] = 0; 46 | DrawChar(x * FONT_SIZE_HALF, (gMaxRow - 1) * FONT_SIZE, L' '); 47 | } 48 | } 49 | 50 | static VOID PrintChar(CHAR16 Char) { 51 | if (Char == L'\b') { 52 | if (gColumn > 0) { 53 | gColumn--; 54 | // DrawChar(gColumn, gRow, L' '); 55 | } 56 | } else if (Char == L'\r') { 57 | gColumn = 0; 58 | } else if (Char == L'\n') { 59 | gColumn = 0; 60 | gRow++; 61 | } else { 62 | gBuffer[gColumn + gRow * gMaxColumn] = Char; 63 | DrawChar(gColumn * FONT_SIZE_HALF, gRow * FONT_SIZE, gBuffer[gColumn + gRow * gMaxColumn]); 64 | if (IsHalfWidth(Char)) { 65 | gColumn++; 66 | } else { 67 | gColumn += 2; 68 | } 69 | } 70 | 71 | if (gColumn == gMaxColumn) { 72 | gColumn = 0; 73 | gRow++; 74 | } 75 | if (gRow == gMaxRow) { 76 | Scroll(); 77 | gRow--; 78 | } 79 | } 80 | 81 | void EFIAPI PrintUtf16(IN CHAR16 *Format, ...) { 82 | VA_LIST Marker; 83 | 84 | VA_START(Marker, Format); 85 | 86 | CHAR16 Buffer[256]; 87 | UnicodeVSPrint(Buffer, sizeof(Buffer), Format, Marker); 88 | 89 | for (UINTN i = 0; Buffer[i] != 0; i++) { 90 | PrintChar(Buffer[i]); 91 | } 92 | 93 | VA_END(Marker); 94 | } -------------------------------------------------------------------------------- /MitnalPkg/OAuth.c: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | #include "hmac/hmac.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Secrets" 9 | 10 | // fake value 11 | static const CHAR8 *gNonce = "JtB6znKJNVo"; 12 | static const UINT64 gTimestamp = 1643528823; 13 | 14 | EFI_STATUS HmacSha1(IN CHAR8 *Key, IN CHAR8 *Data, OUT CHAR8 *Result, OUT UINTN *ResultSize) { 15 | UINTN DigestSize; 16 | CHAR8 ResultRaw[256]; 17 | 18 | hmac_sha1((UINT8 *)Key, AsciiStrLen(Key), (UINT8 *)Data, AsciiStrLen(Data), (UINT8 *)ResultRaw, (size_t *)&DigestSize); 19 | Base64Encode((UINT8 *)ResultRaw, DigestSize, Result, ResultSize); 20 | 21 | return EFI_SUCCESS; 22 | } 23 | 24 | EFI_STATUS GenerateSignature(IN CHAR8 *Method, IN CHAR8 *Url, IN CHAR8 *Query, OUT CHAR8 *Signature, IN UINTN SignatureSize) { 25 | CHAR8 Key[256]; 26 | CHAR8 Base[1024]; 27 | 28 | CHAR8 UrlEnc[256]; 29 | CHAR8 Param[1024]; 30 | CHAR8 ParamEnc[1024]; 31 | 32 | AsciiSPrint(Key, sizeof(Key), "%a&%a", gConsumerSecret, gAccessTokenSecret); 33 | 34 | if (Query == NULL) { 35 | AsciiSPrint( 36 | Param, sizeof(Param), 37 | "oauth_consumer_key=%a&oauth_nonce=%a&oauth_signature_method=HMAC-SHA1&oauth_timestamp=%d&oauth_token=%a&oauth_version=1.0", 38 | gConsumerKey, gNonce, gTimestamp, gAccessToken); 39 | } else { 40 | AsciiSPrint( 41 | Param, sizeof(Param), 42 | "oauth_consumer_key=%a&oauth_nonce=%a&oauth_signature_method=HMAC-SHA1&oauth_timestamp=%d&oauth_token=%a&oauth_version=1.0&%a", 43 | gConsumerKey, gNonce, gTimestamp, gAccessToken, Query); 44 | } 45 | 46 | UrlEncode(Url, UrlEnc); 47 | UrlEncode(Param, ParamEnc); 48 | AsciiSPrint(Base, sizeof(Base), "%a&%a&%a", Method, UrlEnc, ParamEnc); 49 | 50 | HmacSha1(Key, Base, Signature, &SignatureSize); 51 | 52 | return EFI_SUCCESS; 53 | } 54 | 55 | EFI_STATUS GenerateOAuthHeader(IN CHAR8 *Method, IN CHAR8 *Url, IN CHAR8 *Query, OUT CHAR8 *Buffer, IN UINTN BufferSize) { 56 | EFI_STATUS Status; 57 | 58 | CHAR8 Signature[256]; 59 | CHAR8 SignatureEnc[256]; 60 | 61 | Status = GenerateSignature(Method, Url, Query, Signature, sizeof(Signature)); 62 | HANDLE_ERROR(Status) 63 | 64 | UrlEncode(Signature, SignatureEnc); 65 | AsciiSPrint( 66 | Buffer, 67 | BufferSize, 68 | "OAuth oauth_consumer_key=\"%a\", oauth_nonce=\"%a\", oauth_signature=\"%a\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"%d\", oauth_token=\"%a\", oauth_version=\"1.0\"", 69 | gConsumerKey, 70 | gNonce, 71 | SignatureEnc, 72 | gTimestamp, 73 | gAccessToken); 74 | 75 | return EFI_SUCCESS; 76 | } 77 | -------------------------------------------------------------------------------- /MitnalPkg/Main.c: -------------------------------------------------------------------------------- 1 | #include "Common.h" 2 | #include "Console.h" 3 | #include "Graphics.h" 4 | #include "Http.h" 5 | #include "Twitter.h" 6 | #include 7 | 8 | char *efontUFT8toUTF16(uint16_t *pUTF16, char *pUTF8); 9 | 10 | VOID UTF8toUTF16(OUT CHAR16 *pUTF16, IN CHAR8 *pUTF8) { 11 | UINTN i = 0; 12 | CHAR8 *ptr = pUTF8; 13 | while (*ptr != 0) { 14 | ptr = efontUFT8toUTF16(&pUTF16[i], ptr); 15 | i++; 16 | } 17 | } 18 | 19 | EFI_STATUS PostTweet(IN CHAR16 *Content) { 20 | EFI_STATUS Status; 21 | CHAR8 Content8[256]; 22 | 23 | UnicodeStrToAsciiStrS(Content, Content8, sizeof(Content8)); 24 | Status = Tweet(Content8); 25 | HANDLE_ERROR(Status) 26 | 27 | return EFI_SUCCESS; 28 | } 29 | 30 | EFI_STATUS ShowTimeline() { 31 | EFI_STATUS Status; 32 | 33 | TWEET *Tweets; 34 | Status = gBS->AllocatePool( 35 | EfiBootServicesData, 36 | sizeof(TWEET) * 32, 37 | (VOID **)&Tweets); 38 | HANDLE_ERROR(Status) 39 | ZeroMem(Tweets, sizeof(TWEET) * 32); 40 | 41 | UINTN TweetCount; 42 | Status = HomeTimeline(Tweets, &TweetCount); 43 | HANDLE_ERROR(Status) 44 | 45 | CHAR16 CreatedAt[256]; 46 | CHAR16 UserName[256]; 47 | CHAR16 Text[256]; 48 | for (UINTN i = TweetCount - 1; i != 0; i--) { 49 | ZeroMem(CreatedAt, sizeof(CreatedAt)); 50 | ZeroMem(UserName, sizeof(UserName)); 51 | ZeroMem(Text, sizeof(Text)); 52 | UTF8toUTF16(CreatedAt, (CHAR8 *)Tweets[i].CreatedAt); 53 | UTF8toUTF16(UserName, (CHAR8 *)Tweets[i].UserName); 54 | UTF8toUTF16(Text, (CHAR8 *)Tweets[i].Text); 55 | 56 | PrintUtf16(L"[%s] <%s> %s\n", CreatedAt, UserName, Text); 57 | } 58 | 59 | gBS->FreePool(Tweets); 60 | 61 | return EFI_SUCCESS; 62 | } 63 | 64 | EFI_STATUS ExecuteCommand(IN CHAR16 *Command) { 65 | if (!StrCmp(Command, L"home")) { 66 | return ShowTimeline(); 67 | } else if (!StrCmp(Command, L"exit")) { 68 | return EFI_ABORTED; 69 | } else { 70 | CHAR16 *Arg = StrStr(Command, L" ") + 1; 71 | *(Arg - 1) = 0; 72 | 73 | if (!StrCmp(Command, L"tweet")) { 74 | return PostTweet(Arg); 75 | } else { 76 | PrintUtf16(L"command not found\n"); 77 | } 78 | } 79 | 80 | return EFI_SUCCESS; 81 | } 82 | 83 | VOID ReadLine(IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn, OUT CHAR16 *Buffer, IN UINTN BufferSize) { 84 | UINTN i; 85 | for (i = 0; i < BufferSize - 1; i++) { 86 | EFI_INPUT_KEY Key; 87 | while (ConIn->ReadKeyStroke(ConIn, &Key) != EFI_SUCCESS) 88 | ; 89 | 90 | if (Key.UnicodeChar == L'\r') { 91 | PrintUtf16(L"\n"); 92 | break; 93 | } else if (Key.UnicodeChar == L'\b') { 94 | if (i == 0) { 95 | continue; 96 | } 97 | i--; 98 | } 99 | 100 | PrintUtf16(L"%c", Key.UnicodeChar); 101 | Buffer[i] = Key.UnicodeChar; 102 | } 103 | Buffer[i] = 0; 104 | } 105 | 106 | EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) { 107 | EFI_STATUS Status; 108 | 109 | Status = InitGraphics(ImageHandle); 110 | HANDLE_ERROR(Status) 111 | 112 | Status = InitConsole(); 113 | HANDLE_ERROR(Status) 114 | 115 | Status = InitHttpProtocol(); 116 | HANDLE_ERROR(Status) 117 | 118 | CHAR16 Buffer[256]; 119 | while (TRUE) { 120 | PrintUtf16(L"> "); 121 | ReadLine(SystemTable->ConIn, Buffer, sizeof(Buffer)); 122 | Status = ExecuteCommand(Buffer); 123 | HANDLE_ERROR(Status) 124 | } 125 | 126 | return EFI_SUCCESS; 127 | } 128 | -------------------------------------------------------------------------------- /MitnalPkg/Twitter.c: -------------------------------------------------------------------------------- 1 | #include "Twitter.h" 2 | #include "Common.h" 3 | #include "Http.h" 4 | #include "OAuth.h" 5 | #include "json.h" 6 | #include 7 | 8 | #define BUFFER_SIZE 0x100000 9 | 10 | static CHAR8 *API_URL_TWEET = "https://api.twitter.com/1.1/statuses/update.json"; 11 | static CHAR8 *API_URL_HOME = "https://api.twitter.com/1.1/statuses/home_timeline.json"; 12 | // static CHAR8 *API_URL_TIMELINE = "https://api.twitter.com/1.1/statuses/user_timeline.json"; 13 | 14 | EFI_STATUS Tweet(IN CHAR8 *Message) { 15 | EFI_STATUS Status; 16 | 17 | CHAR8 MessageEnc[256]; 18 | CHAR8 Query[256]; 19 | CHAR16 Url[256]; 20 | CHAR8 OAuthHeader[512]; 21 | 22 | UrlEncode(Message, MessageEnc); 23 | AsciiSPrint(Query, sizeof(Query), "status=%a", MessageEnc); 24 | UnicodeSPrint(Url, sizeof(Url), L"%a?%a", API_URL_TWEET, Query); 25 | 26 | EFI_HTTP_HEADER RequestHeader[2]; 27 | RequestHeader[0].FieldName = "Host"; 28 | RequestHeader[0].FieldValue = "api.twitter.com"; 29 | 30 | GenerateOAuthHeader("POST", API_URL_TWEET, Query, OAuthHeader, sizeof(OAuthHeader)); 31 | RequestHeader[1].FieldName = "Authorization"; 32 | RequestHeader[1].FieldValue = OAuthHeader; 33 | 34 | HTTP_REQUEST_CONTEXT Context; 35 | Context.Method = HttpMethodPost; 36 | Context.Url = Url; 37 | Context.HeaderCount = 2; 38 | Context.Headers = RequestHeader; 39 | Context.BodyLength = 0; 40 | Context.Body = NULL; 41 | 42 | Status = SendRequest(&Context); 43 | HANDLE_ERROR(Status) 44 | 45 | UINT8 *Buffer; 46 | Status = gBS->AllocatePool( 47 | EfiBootServicesData, 48 | BUFFER_SIZE, 49 | (VOID **)&Buffer); 50 | HANDLE_ERROR(Status) 51 | 52 | UINTN BufferSize = BUFFER_SIZE; 53 | Status = ReceiveResponse(Buffer, &BufferSize); 54 | HANDLE_ERROR(Status) 55 | 56 | gBS->FreePool(Buffer); 57 | 58 | return EFI_SUCCESS; 59 | } 60 | 61 | void ParseTweets(IN UINT8 *Buffer, IN UINTN BufferSize, OUT TWEET *Tweets, OUT UINTN *TweetCount) { 62 | struct json_value_s *root = json_parse(Buffer, BufferSize); 63 | struct json_array_s *array = json_value_as_array(root); 64 | 65 | UINTN i = 0; 66 | for (struct json_array_element_s *ae = array->start; ae != NULL; ae = ae->next) { 67 | struct json_object_s *object = json_value_as_object(ae->value); 68 | for (struct json_object_element_s *oe = object->start; oe != NULL; oe = oe->next) { 69 | if (!AsciiStrCmp(oe->name->string, "created_at")) { 70 | CHAR8 *CreatedAt = (CHAR8 *)json_value_as_string(json_extract_value(oe->value))->string; 71 | 72 | CreatedAt = CreatedAt + 4; 73 | CreatedAt[12] = 0; 74 | 75 | Tweets[i].CreatedAt = CreatedAt; 76 | } else if (!AsciiStrCmp(oe->name->string, "text")) { 77 | Tweets[i].Text = json_value_as_string(json_extract_value(oe->value))->string; 78 | } else if (!AsciiStrCmp(oe->name->string, "user")) { 79 | struct json_object_s *object = json_value_as_object(oe->value); 80 | for (struct json_object_element_s *oe2 = object->start; oe2 != NULL; oe2 = oe2->next) { 81 | if (!AsciiStrCmp(oe2->name->string, "name")) { 82 | Tweets[i].UserName = json_value_as_string(json_extract_value(oe2->value))->string; 83 | } 84 | } 85 | } 86 | } 87 | i++; 88 | } 89 | *TweetCount = i; 90 | 91 | free(root); 92 | } 93 | 94 | EFI_STATUS HomeTimeline(OUT TWEET *Tweets, OUT UINTN *TweetCount) { 95 | EFI_STATUS Status; 96 | 97 | CHAR16 Url[256]; 98 | CHAR8 OAuthHeader[512]; 99 | 100 | AsciiStrToUnicodeStrS(API_URL_HOME, Url, sizeof(Url)); 101 | 102 | EFI_HTTP_HEADER RequestHeader[2]; 103 | RequestHeader[0].FieldName = "Host"; 104 | RequestHeader[0].FieldValue = "api.twitter.com"; 105 | 106 | GenerateOAuthHeader("GET", API_URL_HOME, NULL, OAuthHeader, sizeof(OAuthHeader)); 107 | RequestHeader[1].FieldName = "Authorization"; 108 | RequestHeader[1].FieldValue = OAuthHeader; 109 | 110 | HTTP_REQUEST_CONTEXT Context; 111 | Context.Method = HttpMethodGet; 112 | Context.Url = Url; 113 | Context.HeaderCount = 2; 114 | Context.Headers = RequestHeader; 115 | Context.BodyLength = 0; 116 | Context.Body = NULL; 117 | 118 | Status = SendRequest(&Context); 119 | HANDLE_ERROR(Status) 120 | 121 | UINT8 *Buffer; 122 | Status = gBS->AllocatePool( 123 | EfiBootServicesData, 124 | BUFFER_SIZE, 125 | (VOID **)&Buffer); 126 | HANDLE_ERROR(Status) 127 | 128 | UINTN BufferSize = BUFFER_SIZE; 129 | Status = ReceiveResponse(Buffer, &BufferSize); 130 | HANDLE_ERROR(Status) 131 | 132 | ParseTweets(Buffer, BufferSize, Tweets, TweetCount); 133 | 134 | gBS->FreePool(Buffer); 135 | 136 | return EFI_SUCCESS; 137 | } 138 | -------------------------------------------------------------------------------- /Conf/target.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
3 | # 4 | # SPDX-License-Identifier: BSD-2-Clause-Patent 5 | # 6 | # 7 | # ALL Paths are Relative to WORKSPACE 8 | 9 | # Separate multiple LIST entries with a SINGLE SPACE character, do not use comma characters. 10 | # Un-set an option by either commenting out the line, or not setting a value. 11 | 12 | # 13 | # PROPERTY Type Use Description 14 | # ---------------- -------- -------- ----------------------------------------------------------- 15 | # ACTIVE_PLATFORM Filename Recommended Specify the WORKSPACE relative Path and Filename 16 | # of the platform description file that will be used for the 17 | # build. This line is required if and only if the current 18 | # working directory does not contain one or more description 19 | # files. 20 | ACTIVE_PLATFORM = MitnalPkg/MitnalPkg.dsc 21 | 22 | # TARGET List Optional Zero or more of the following: DEBUG, RELEASE, NOOPT 23 | # UserDefined; separated by a space character. 24 | # If the line is missing or no value is specified, all 25 | # valid targets specified in the platform description file 26 | # will attempt to be built. The following line will build 27 | # DEBUG platform target. 28 | TARGET = DEBUG 29 | 30 | # TARGET_ARCH List Optional What kind of architecture is the binary being target for. 31 | # One, or more, of the following, IA32, IPF, X64, EBC, ARM 32 | # or AArch64. 33 | # Multiple values can be specified on a single line, using 34 | # space characters to separate the values. These are used 35 | # during the parsing of a platform description file, 36 | # restricting the build output target(s.) 37 | # The Build Target ARCH is determined by (precedence high to low): 38 | # Command-line: -a ARCH option 39 | # target.txt: TARGET_ARCH values 40 | # DSC file: [Defines] SUPPORTED_ARCHITECTURES tag 41 | # If not specified, then all valid architectures specified 42 | # in the platform file, for which tools are available, will be 43 | # built. 44 | TARGET_ARCH = X64 45 | 46 | # TOOL_DEFINITION_FILE Filename Optional Specify the name of the filename to use for specifying 47 | # the tools to use for the build. If not specified, 48 | # WORKSPACE/Conf/tools_def.txt will be used for the build. 49 | TOOL_CHAIN_CONF = Conf/tools_def.txt 50 | 51 | # TAGNAME List Optional Specify the name(s) of the tools_def.txt TagName to use. 52 | # If not specified, all applicable TagName tools will be 53 | # used for the build. The list uses space character separation. 54 | TOOL_CHAIN_TAG = CLANG38 55 | 56 | # MAX_CONCURRENT_THREAD_NUMBER NUMBER Optional The number of concurrent threads. If not specified or set 57 | # to zero, tool automatically detect number of processor 58 | # threads. Recommend to set this value to one less than the 59 | # number of your computer cores or CPUs. When value set to 1, 60 | # means disable multi-thread build, value set to more than 1, 61 | # means user specify the thread number to build. Not specify 62 | # the default value in this file. 63 | # MAX_CONCURRENT_THREAD_NUMBER = 1 64 | 65 | 66 | # BUILD_RULE_CONF Filename Optional Specify the file name to use for the build rules that are followed 67 | # when generating Makefiles. If not specified, the file: 68 | # WORKSPACE/Conf/build_rule.txt will be used 69 | BUILD_RULE_CONF = Conf/build_rule.txt 70 | 71 | -------------------------------------------------------------------------------- /MitnalPkg/Http.c: -------------------------------------------------------------------------------- 1 | #include "Http.h" 2 | #include "Common.h" 3 | #include "Console.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static EFI_HTTP_PROTOCOL *gHttpProtocol; 10 | 11 | static BOOLEAN gRequestCallbackComplete = FALSE; 12 | static BOOLEAN gResponseCallbackComplete = FALSE; 13 | 14 | static VOID EFIAPI RequestCallback(IN EFI_EVENT Event, IN VOID *Context) { 15 | gRequestCallbackComplete = TRUE; 16 | } 17 | 18 | static VOID EFIAPI ResponseCallback(IN EFI_EVENT Event, IN VOID *Context) { 19 | gResponseCallbackComplete = TRUE; 20 | } 21 | 22 | EFI_STATUS InitHttpProtocol() { 23 | EFI_STATUS Status; 24 | 25 | EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; 26 | Status = gBS->LocateProtocol( 27 | &gEfiHttpServiceBindingProtocolGuid, 28 | NULL, 29 | (VOID **)&ServiceBinding); 30 | HANDLE_ERROR(Status) 31 | 32 | EFI_HANDLE *Handle = NULL; 33 | Status = ServiceBinding->CreateChild(ServiceBinding, (VOID **)&Handle); 34 | HANDLE_ERROR(Status) 35 | 36 | Status = gBS->HandleProtocol(Handle, &gEfiHttpProtocolGuid, (VOID **)&gHttpProtocol); 37 | HANDLE_ERROR(Status) 38 | 39 | EFI_HTTP_CONFIG_DATA ConfigData; 40 | ConfigData.HttpVersion = HttpVersion11; 41 | ConfigData.TimeOutMillisec = 0; 42 | ConfigData.LocalAddressIsIPv6 = FALSE; 43 | 44 | EFI_HTTPv4_ACCESS_POINT Ipv4Node; 45 | ZeroMem(&Ipv4Node, sizeof(Ipv4Node)); 46 | Ipv4Node.UseDefaultAddress = TRUE; 47 | ConfigData.AccessPoint.IPv4Node = &Ipv4Node; 48 | 49 | Status = gHttpProtocol->Configure(gHttpProtocol, &ConfigData); 50 | HANDLE_ERROR(Status) 51 | 52 | return EFI_SUCCESS; 53 | } 54 | 55 | EFI_STATUS SendRequest(IN HTTP_REQUEST_CONTEXT *Context) { 56 | EFI_STATUS Status; 57 | 58 | EFI_HTTP_REQUEST_DATA RequestData; 59 | RequestData.Method = Context->Method; 60 | RequestData.Url = Context->Url; 61 | 62 | EFI_HTTP_MESSAGE RequestMessage; 63 | RequestMessage.Data.Request = &RequestData; 64 | RequestMessage.HeaderCount = Context->HeaderCount; 65 | RequestMessage.Headers = Context->Headers; 66 | RequestMessage.BodyLength = Context->BodyLength; 67 | RequestMessage.Body = Context->Body; 68 | 69 | EFI_HTTP_TOKEN RequestToken; 70 | RequestToken.Event = NULL; 71 | Status = gBS->CreateEvent( 72 | EVT_NOTIFY_SIGNAL, 73 | TPL_CALLBACK, 74 | RequestCallback, 75 | NULL, 76 | &RequestToken.Event); 77 | HANDLE_ERROR(Status) 78 | RequestToken.Status = EFI_SUCCESS; 79 | RequestToken.Message = &RequestMessage; 80 | 81 | gRequestCallbackComplete = FALSE; 82 | Status = gHttpProtocol->Request(gHttpProtocol, &RequestToken); 83 | HANDLE_ERROR(Status) 84 | while (!gRequestCallbackComplete) 85 | ; 86 | 87 | return EFI_SUCCESS; 88 | } 89 | 90 | EFI_STATUS ReceiveResponse(OUT UINT8 *Buffer, IN OUT UINTN *BufferSize) { 91 | EFI_STATUS Status; 92 | 93 | UINT8 *TempBuffer; 94 | Status = gBS->AllocatePool( 95 | EfiBootServicesData, 96 | 0x10000, 97 | (VOID **)&TempBuffer); 98 | HANDLE_ERROR(Status) 99 | ZeroMem(TempBuffer, sizeof(TempBuffer)); 100 | 101 | EFI_HTTP_RESPONSE_DATA ResponseData; 102 | ResponseData.StatusCode = HTTP_STATUS_UNSUPPORTED_STATUS; 103 | 104 | EFI_HTTP_MESSAGE ResponseMessage; 105 | ResponseMessage.Data.Response = &ResponseData; 106 | ResponseMessage.HeaderCount = 0; 107 | ResponseMessage.Headers = NULL; 108 | ResponseMessage.BodyLength = sizeof(TempBuffer); 109 | ResponseMessage.Body = TempBuffer; 110 | 111 | EFI_HTTP_TOKEN ResponseToken; 112 | ResponseToken.Event = NULL; 113 | Status = gBS->CreateEvent( 114 | EVT_NOTIFY_SIGNAL, 115 | TPL_CALLBACK, 116 | ResponseCallback, 117 | NULL, 118 | &ResponseToken.Event); 119 | HANDLE_ERROR(Status) 120 | ResponseToken.Status = EFI_SUCCESS; 121 | ResponseToken.Message = &ResponseMessage; 122 | 123 | gResponseCallbackComplete = FALSE; 124 | Status = gHttpProtocol->Response(gHttpProtocol, &ResponseToken); 125 | HANDLE_ERROR(Status) 126 | while (!gResponseCallbackComplete) 127 | ; 128 | 129 | for (UINTN i = 0; i < ResponseMessage.HeaderCount; i++) { 130 | if (!AsciiStrCmp(ResponseMessage.Headers[i].FieldName, "content-length")) { 131 | *BufferSize = AsciiStrDecimalToUintn(ResponseMessage.Headers[i].FieldValue); 132 | } 133 | } 134 | 135 | UINTN ContentDownloaded = ResponseMessage.BodyLength; 136 | CopyMem(Buffer, TempBuffer, ResponseMessage.BodyLength); 137 | 138 | while (ContentDownloaded < *BufferSize) { 139 | ResponseMessage.Data.Response = NULL; 140 | if (ResponseMessage.Headers != NULL) { 141 | gBS->FreePool(ResponseMessage.Headers); 142 | } 143 | ResponseMessage.HeaderCount = 0; 144 | ResponseMessage.BodyLength = sizeof(TempBuffer); 145 | ZeroMem(TempBuffer, sizeof(TempBuffer)); 146 | 147 | gResponseCallbackComplete = FALSE; 148 | Status = gHttpProtocol->Response(gHttpProtocol, &ResponseToken); 149 | HANDLE_ERROR(Status) 150 | while (!gResponseCallbackComplete) 151 | ; 152 | 153 | CopyMem(Buffer + ContentDownloaded, TempBuffer, ResponseMessage.BodyLength); 154 | ContentDownloaded += ResponseMessage.BodyLength; 155 | } 156 | 157 | gBS->FreePool(TempBuffer); 158 | 159 | return EFI_SUCCESS; 160 | } 161 | --------------------------------------------------------------------------------