Monday, February 7, 2011

fun with the cli, dynamic runtime linking and QR codes

So in the last post playing around with google-authenticator I noticed the wicked smart folks @ google using QR codes to encode the auth key. What I did not notice was their extremely cool usage of dlopen to pull in an encoder if it happened to be on the system. I was missing the encode library so I also did not notice their code can dump a QR directly to the vt100 terminal!

This simple dlopen() magic does the trick:
void *qrencode = dlopen ("", RTLD_NOW | RTLD_LOCAL);

dlopen() is actually an awesome programming idea for dependency on shared libraries so code builds quickly if you don't have the depend, and also does not hork at runtime if you are missing things - it certainly helps avoid link time crazy!

Anyway, I chopped out the code that generates QR's on the xterm (code is hacked out directly from the google-authenticator.c code). The simple main function is idiot proof (yeah, I know life is too short for error checking) just takes whatever you want to code as argv[1] and spits out the required QR. First up a few headers and defines for the terminal to get it built:
#include <dlfcn.h>  // for the dynamic linker
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define ANSI_RESET        "\x1B[0m"
#define ANSI_BLACKONGREY  "\x1B[30;47;27m"
#define ANSI_WHITE        "\x1B[27m"
#define ANSI_BLACK        "\x1B[7m"

Then a simple main function:
int main (int argc, char *argv[]){
   char txt[1024];
   strcat (txt, argv[1]);
   displayQRCode (txt);
   printf ("The QR text above is: %s\n", txt);

Finally the drawing code displayQRCode() is also pretty simple:
static void displayQRCode (const char *txt){
  const char *url = txt;
  int x, y, i = 0;
  void *qrencode = dlopen ("", RTLD_NOW | RTLD_LOCAL);
  if (qrencode){
      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){
          QRcode *qrcode = QRcode_encodeString8bit (url, 0, 1);
          char *ptr = (char *) qrcode->data;
          for (i = 0; i < 2; ++i){
              printf (ANSI_BLACKONGREY);
              for (x = 0; x < qrcode->width + 4; ++x)
                printf ("  ");
              puts (ANSI_RESET);
          for (y = 0; y < qrcode->width; ++y){
              printf (ANSI_BLACKONGREY "    ");
              int isBlack = 0;
              for (x = 0; x < qrcode->width; ++x){
                  if (*ptr++ & 1){
                      if (!isBlack){printf (ANSI_BLACK);}
                      isBlack = 1;
                  else{ if (isBlack){printf (ANSI_WHITE);}
                      isBlack = 0;
                  printf ("  ");
              if (isBlack){printf (ANSI_WHITE);}
              puts ("    " ANSI_RESET);
          for (i = 0; i < 2; ++i){
              printf (ANSI_BLACKONGREY);
              for (x = 0; x < qrcode->width + 4; ++x)
                printf ("  ");
              puts (ANSI_RESET);
          QRcode_free (qrcode);
      dlclose (qrencode);

With a simple:
cc -o qr qr.c -ldl

You get quick and easy qr codes dumped out to your xterm, in this case an encoded URL to this site - this is a screen grab of my xterm:

I snagged a copy of QRReader from the app store to check, and sure enough, simple, fast easy, done and done.

Check out the photo of the crazy angle I took the QR screen grab from!

[any opinions here are all mine, and have absolutely nothing to do with my employer]
(c) 2011 James Cuff