Last active
September 23, 2024 22:33
-
-
Save ageis/025736fbfb296c8729f6461a607ec90e to your computer and use it in GitHub Desktop.
A utility to display QR codes in the CLI/terminal. To build, run `gcc showqrcode.c -ldl -o showqrcode`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// showqrcode: A utility to display QR codes in the terminal. | |
// Copyright © 2019 Kevin Gallagher <[email protected]> | |
// Modified and based upon original code from libpam-google-authenticator: | |
// https://github.com/google/google-authenticator-libpam | |
// The original license is printed below. | |
// | |
// Helper program to generate a new secret for use in two-factor | |
// authentication. | |
// | |
// Copyright 2010 Google Inc. | |
// Author: Markus Gutschke | |
// | |
// Licensed under the Apache License, Version 2.0 (the "License"); | |
// you may not use this file except in compliance with the License. | |
// You may obtain a copy of the License at | |
// | |
// http://www.apache.org/licenses/LICENSE-2.0 | |
// | |
// Unless required by applicable law or agreed to in writing, software | |
// distributed under the License is distributed on an "AS IS" BASIS, | |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
// See the License for the specific language governing permissions and | |
// limitations under the License. | |
#include <dlfcn.h> | |
#include <getopt.h> | |
#include <stdio.h> | |
#include <stdio_ext.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include <unistd.h> | |
#include <regex.h> | |
#include <locale.h> | |
#define ANSI_RESET "\x1B[0m" | |
#define ANSI_BLACKONGREY "\x1B[30;47;27m" | |
#define ANSI_WHITE "\x1B[27m" | |
#define ANSI_BLACK "\x1B[7m" | |
#define UTF8_BOTH "\xE2\x96\x88" | |
#define UTF8_TOPHALF "\xE2\x96\x80" | |
#define UTF8_BOTTOMHALF "\xE2\x96\x84" | |
static enum { QR_UNSET=0, QR_NONE, QR_ANSI, QR_UTF8 } qr_mode = QR_UTF8; | |
static int displayQRCode(const char* url) { | |
FILE * out = stdout; | |
setbuf(out, NULL); | |
void *qrencode = dlopen("libqrencode.so.4", RTLD_NOW | RTLD_LOCAL); | |
if (!qrencode) { | |
qrencode = dlopen("libqrencode.so.3", RTLD_NOW | RTLD_LOCAL); | |
} | |
if (!qrencode) { | |
qrencode = dlopen("libqrencode.3.dylib", RTLD_NOW | RTLD_LOCAL); | |
} | |
if (!qrencode) { | |
qrencode = dlopen("libqrencode.so.2", RTLD_NOW | RTLD_LOCAL); | |
} | |
if (!qrencode) { | |
return 0; | |
} | |
typedef struct { | |
int version; | |
int width; | |
unsigned char *data; | |
} QRcode; | |
QRcode *(*QRcode_encodeString8bit)(const char *, int, int) = | |
(QRcode *(*)(const char *, int, int)) | |
dlsym(qrencode, "QRcode_encodeString8bit"); | |
void (*QRcode_free)(QRcode *qrcode) = | |
(void (*)(QRcode *))dlsym(qrencode, "QRcode_free"); | |
if (!QRcode_encodeString8bit || !QRcode_free) { | |
dlclose(qrencode); | |
return 0; | |
} | |
QRcode *qrcode = QRcode_encodeString8bit(url, 0, 1); | |
const char *ptr = (char *)qrcode->data; | |
if (qr_mode == QR_UTF8) { | |
fprintf(out, "%s", ANSI_BLACKONGREY); | |
for (int i = 0; i < qrcode->width + 4; ++i) { | |
printf(" "); | |
} | |
puts(ANSI_RESET); | |
for (int y = 0; y < qrcode->width; y += 2) { | |
fprintf(out, "%s", ANSI_BLACKONGREY" "); | |
for (int x = 0; x < qrcode->width; ++x) { | |
const int top = qrcode->data[y*qrcode->width + x] & 1; | |
int bottom = 0; | |
if (y+1 < qrcode->width) { | |
bottom = qrcode->data[(y+1)*qrcode->width + x] & 1; | |
} | |
if (top) { | |
if (bottom) { | |
fprintf(out, "%s", UTF8_BOTH); | |
} else { | |
fprintf(out, "%s", UTF8_TOPHALF); | |
} | |
} else { | |
if (bottom) { | |
fprintf(out, "%s", UTF8_BOTTOMHALF); | |
} else { | |
printf(" "); | |
} | |
} | |
} | |
puts(" "ANSI_RESET); | |
} | |
fprintf(out, "%s", ANSI_BLACKONGREY); | |
for (int i = 0; i < qrcode->width + 4; ++i) { | |
printf(" "); | |
} | |
puts(ANSI_RESET); | |
fflush(out); | |
} | |
QRcode_free(qrcode); | |
dlclose(qrencode); | |
return 1; | |
} | |
static void usage(void) { | |
puts( | |
"showqrcode [<options>]\n" | |
" -h, --help Print this message\n" | |
" -d, --data=<string> Encode arbitrary strings\n" | |
" -p, --pipe Accept input from STDIN\n" | |
" -u, --url=<otpauth://> Supply the \"otpauth://\" URL to encode\n"); | |
} | |
int main(int argc, char *argv[]) { | |
int opt; | |
int idx = -1; | |
char *url = NULL; | |
char *data = NULL; | |
char str[256]; | |
int otp_regex; | |
int d = 0, p = 0, u = 0; | |
regex_t regex; | |
regmatch_t rematch[2]; | |
size_t nmatch = 2; | |
setlocale(LC_ALL, "en_US.UTF-8"); | |
if(argc <= 1) { | |
fprintf(stderr, "Error: Expected at least one argument.\n"); | |
usage(); | |
_exit(1); | |
} | |
otp_regex = regcomp(®ex, "^otpauth://.*", 0); | |
static struct option options[] = { | |
{ "help", 0, 0, 'h' }, | |
{ "data", 1, 0, 'd' }, | |
{ "pipe", 0, 0, 'p' }, | |
{ "url", 1, 0, 'u' }, | |
{ 0, 0, 0, 0 } | |
}; | |
static const char optstring[] = "+h:d:pu:"; | |
while (opt = getopt_long(argc, argv, "+h:d:pu:", options, NULL), opt != -1) { | |
switch (opt) { | |
case '0': | |
if (options[opt].val != 0) { | |
break; | |
} | |
if (optarg) { | |
break; | |
} | |
case '-': | |
break; | |
case 'h': | |
usage(); | |
exit(0); | |
break; | |
case 'd': | |
data = strdup(optarg); | |
d = 1; | |
break; | |
case 'p': | |
data = fgets(str, 256, stdin); | |
p = 1; | |
break; | |
case 'u': | |
url = strdup(optarg); | |
u = 1; | |
otp_regex = regexec(®ex, url, 0, NULL, 0); | |
if (otp_regex) { | |
fprintf(stderr, "The input URL %s is not in the correct format.\n", url); | |
_exit(1); | |
} | |
if (strlen(url) < 16) { | |
fprintf(stderr, "The input URL %s does not contain enough data.\n", url); | |
_exit(1); | |
} | |
break; | |
case '?': | |
usage(); | |
_exit(1); | |
break; | |
default: | |
fprintf(stderr, "Error: getopt returned unexpected value.\n"); | |
} | |
if ((d && u) || (d && p) || (p && u)) { | |
fprintf(stderr, "Error: you may only specify one mode at a time.\n"); | |
usage(); | |
_exit(1); | |
} | |
} | |
if (data) { | |
url = strdup(data); | |
} else if ((!url) && (!data)) { | |
fprintf(stderr, "Error: No input to encode.\n"); | |
_exit(1); | |
} | |
//if (isatty(1)) { | |
if (!displayQRCode(url)) { | |
printf( | |
"Failed to use libqrencode to show QR code visually for scanning.\n" | |
"Consider typing the OTP secret into your app manually.\n"); | |
_exit(1); | |
} | |
//} else { | |
// _exit(1); | |
//} | |
fclose(stdout); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment