1pass.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 18 Jun 2017 19:40:30 -0400
changeset 56 a573346e6f7b
parent 46 fe4f59680246
permissions -rw-r--r--
Added One Time Password support.

This is only for time-based OTP for now ("TOPT" algorithm), but that's more
or less what one expects to see in the wild anyhow.

This is sort of a placeholder UI until I replace the entire existing UI with
something better.
icculus@28
     1
#include <linux/input.h>
icculus@28
     2
#include <fcntl.h>
icculus@0
     3
#include <stdio.h>
icculus@0
     4
#include <stdlib.h>
icculus@0
     5
#include <string.h>
icculus@0
     6
#include <assert.h>
icculus@28
     7
#include <errno.h>
icculus@28
     8
#include <unistd.h>
icculus@31
     9
#include <dirent.h>
icculus@33
    10
#include <signal.h>
icculus@46
    11
#include <mntent.h>
icculus@0
    12
#include "lua.h"
icculus@0
    13
#include "lauxlib.h"
icculus@0
    14
#include "lualib.h"
icculus@0
    15
#include "pkcs5_pbkdf2.h"
icculus@0
    16
#include "aes.h"
icculus@0
    17
#include "base64.h"
icculus@0
    18
#include "md5.h"
icculus@46
    19
#include "sha256.h"
icculus@17
    20
#include "keyhook.h"
icculus@56
    21
#include "otp.h"
icculus@45
    22
icculus@11
    23
#include <gtk/gtk.h>
icculus@45
    24
#include <gdk/gdk.h>
icculus@45
    25
#include <gdk/gdkx.h>
icculus@45
    26
#include <gdk/gdkkeysyms.h>
icculus@45
    27
#include <X11/Xlib.h>
icculus@45
    28
#include <X11/Xlibint.h>
icculus@0
    29
icculus@0
    30
#define STATICARRAYLEN(x) ( (sizeof ((x))) / (sizeof ((x)[0])) )
icculus@0
    31
icculus@28
    32
// plug in a Griffin Powermate, make sure you have access to it, and run with
icculus@28
    33
//  --powermate=/dev/input/eventX
icculus@28
    34
static int powermate_fd = -1;
icculus@28
    35
static int pumpPowermate(void)
icculus@28
    36
{
icculus@28
    37
    struct input_event buf[32];
icculus@28
    38
    int pressed = 0;
icculus@28
    39
    ssize_t br;
icculus@28
    40
icculus@28
    41
    if (powermate_fd == -1)
icculus@28
    42
        return 0;  // nothing to do.
icculus@28
    43
icculus@28
    44
    while ((br = read(powermate_fd, buf, sizeof (buf))) > 0)
icculus@28
    45
    {
icculus@28
    46
        ssize_t i;
icculus@28
    47
        br /= sizeof (buf[0]);
icculus@28
    48
        for (i = 0; i < br; i++)
icculus@28
    49
        {
icculus@28
    50
            struct input_event *ev = &buf[i];
icculus@28
    51
            if ((ev->type == EV_KEY) && (ev->code == BTN_0) && (ev->value))
icculus@28
    52
                pressed = 1;
icculus@28
    53
        } // for
icculus@28
    54
    } // while
icculus@28
    55
icculus@28
    56
    return pressed;
icculus@28
    57
}
icculus@28
    58
icculus@28
    59
static void setPowermateLED(const int enable)
icculus@28
    60
{
icculus@28
    61
    struct input_event ev;
icculus@28
    62
    const int brightness = enable ? 255 : 0;
icculus@28
    63
    const int pulse_speed = 255;
icculus@28
    64
    const int pulse_table = 0;
icculus@28
    65
    const int pulse_awake = enable ? 1 : 0;
icculus@28
    66
    const int pulse_asleep = 0;
icculus@28
    67
icculus@28
    68
    if (powermate_fd == -1)
icculus@28
    69
        return;
icculus@28
    70
icculus@28
    71
    memset(&ev, '\0', sizeof (ev));
icculus@28
    72
    ev.type = EV_MSC;
icculus@28
    73
    ev.code = MSC_PULSELED;
icculus@28
    74
    ev.value = brightness | (pulse_speed << 8) | (pulse_table << 17) | (pulse_asleep << 19) | (pulse_awake << 20);
icculus@28
    75
icculus@28
    76
    if (write(powermate_fd, &ev, sizeof (ev)) != sizeof (ev))
icculus@28
    77
        fprintf(stderr, "WARNING: tried to set Powermate LED and failed: %s\n", strerror(errno));
icculus@28
    78
} // setPowermateLED
icculus@28
    79
icculus@28
    80
icculus@31
    81
static int openPowermate(const char *fname)
icculus@31
    82
{
icculus@31
    83
    static const char const *known_names[] = {
icculus@31
    84
        "Griffin PowerMate", "Griffin SoundKnob"
icculus@31
    85
    };
icculus@31
    86
icculus@31
    87
    char buf[255];
icculus@31
    88
    int ok = 0;
icculus@31
    89
    int fd;
icculus@31
    90
    int i;
icculus@31
    91
icculus@31
    92
    if (!fname)
icculus@31
    93
        return -1;
icculus@31
    94
icculus@31
    95
    if ((fd = open(fname, O_RDWR)) == -1)
icculus@31
    96
        fprintf(stderr, "WARNING: couldn't open Powermate at %s: %s\n", fname, strerror(errno));
icculus@31
    97
icculus@31
    98
    if (ioctl(fd, EVIOCGNAME(sizeof (buf)), buf) == -1)
icculus@31
    99
    {
icculus@31
   100
        fprintf(stderr, "EVIOCGNAME failed for %s: %s\n", fname, strerror(errno));
icculus@31
   101
        close(fd);
icculus@31
   102
        return -1;
icculus@31
   103
    } // if
icculus@31
   104
icculus@31
   105
    for (i = 0; !ok && (i < sizeof (known_names) / sizeof (known_names[0])); i++)
icculus@31
   106
    {
icculus@31
   107
        if (strncmp(buf, known_names[i], strlen(known_names[i])) == 0)
icculus@31
   108
            ok = 1;
icculus@31
   109
    } // for
icculus@31
   110
icculus@31
   111
    if (!ok)
icculus@31
   112
    {
icculus@31
   113
        close(fd);
icculus@31
   114
        return -1;
icculus@31
   115
    } // if
icculus@31
   116
icculus@31
   117
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
icculus@31
   118
    return fd;
icculus@31
   119
} // openPowermate
icculus@31
   120
icculus@31
   121
icculus@31
   122
static void deinitPowermate(void)
icculus@31
   123
{
icculus@34
   124
    if (powermate_fd != -1)
icculus@31
   125
    {
icculus@31
   126
        setPowermateLED(0);
icculus@31
   127
        close(powermate_fd);
icculus@31
   128
        powermate_fd = -1;
icculus@31
   129
    } // if
icculus@31
   130
} // deinitPowermate
icculus@31
   131
icculus@31
   132
icculus@28
   133
static void initPowermate(int *_argc, char **argv)
icculus@28
   134
{
icculus@28
   135
    const char *arg = "--powermate=";
icculus@28
   136
    const size_t arglen = strlen(arg);
icculus@28
   137
    int argc = *_argc;
icculus@28
   138
    int i;
icculus@28
   139
icculus@28
   140
    for (i = 1; i < argc; i++)
icculus@28
   141
    {
icculus@28
   142
        const char *thisarg = argv[i];
icculus@28
   143
        if (strncmp(thisarg, arg, arglen) != 0)
icculus@28
   144
            continue;
icculus@28
   145
icculus@28
   146
        thisarg += arglen;
icculus@31
   147
icculus@31
   148
        if (strcmp(thisarg, "auto") == 0)
icculus@28
   149
        {
icculus@31
   150
            DIR *dirp = opendir("/dev/input");
icculus@31
   151
            if (dirp)
icculus@31
   152
            {
icculus@31
   153
                struct dirent *dent;
icculus@31
   154
                while ((dent = readdir(dirp)) != NULL)
icculus@31
   155
                {
icculus@31
   156
                    const char *name = dent->d_name;
icculus@31
   157
                    char buf[PATH_MAX];
icculus@31
   158
                    if (strncmp(name, "event", 5) != 0)
icculus@31
   159
                        continue;
icculus@31
   160
                    snprintf(buf, sizeof (buf), "/dev/input/%s", name);
icculus@31
   161
                    if (powermate_fd == -1)
icculus@31
   162
                    {
icculus@31
   163
                        powermate_fd = openPowermate(buf);
icculus@31
   164
                        if (powermate_fd != -1)
icculus@31
   165
                        {
icculus@31
   166
                            printf("Found Powermate at %s\n", buf);
icculus@31
   167
                            break;
icculus@31
   168
                        } // if
icculus@31
   169
                    } // if
icculus@31
   170
                } // while
icculus@31
   171
                closedir(dirp);
icculus@31
   172
            } // if
icculus@31
   173
            thisarg = NULL;
icculus@31
   174
icculus@31
   175
        } // if
icculus@28
   176
icculus@28
   177
        // eliminate this command line.
icculus@28
   178
        memmove(&argv[i], &argv[i+1], (argc-i) * sizeof (char *));
icculus@28
   179
        argc--;
icculus@31
   180
icculus@31
   181
        if (powermate_fd == -1)
icculus@31
   182
            powermate_fd = openPowermate(thisarg);
icculus@28
   183
    } // for
icculus@28
   184
icculus@31
   185
    atexit(deinitPowermate);
icculus@31
   186
icculus@28
   187
    *_argc = argc;
icculus@28
   188
} // initPowermate
icculus@28
   189
icculus@28
   190
icculus@0
   191
static lua_State *luaState = NULL;
icculus@0
   192
static const uint8_t zero16[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
icculus@0
   193
static const char saltprefix[] = { 'S', 'a', 'l', 't', 'e', 'd', '_', '_' };
icculus@0
   194
icculus@45
   195
static int makeLuaCallback(lua_State *L, const int idx)
icculus@45
   196
{
icculus@45
   197
    assert(lua_isfunction(L, idx));
icculus@45
   198
    lua_pushvalue(L, idx);  // copy the Lua callback (luaL_ref() pops it).
icculus@45
   199
    return luaL_ref(L, LUA_REGISTRYINDEX);
icculus@45
   200
} // makeLuaCallback
icculus@45
   201
icculus@0
   202
static inline int retvalStringBytes(lua_State *L, const uint8_t *str, size_t len)
icculus@0
   203
{
icculus@0
   204
    if (str != NULL)
icculus@0
   205
        lua_pushlstring(L, (const char *) str, len);
icculus@0
   206
    else
icculus@0
   207
        lua_pushnil(L);
icculus@0
   208
    return 1;
icculus@0
   209
} // retvalStringBytes
icculus@0
   210
icculus@12
   211
static inline int retvalString(lua_State *L, const char *str)
icculus@12
   212
{
icculus@15
   213
    return retvalStringBytes(L, (const uint8_t *) str, str ? strlen(str) : 0);
icculus@12
   214
} // retvalString
icculus@12
   215
icculus@11
   216
static inline int retvalPointer(lua_State *L, void *ptr)
icculus@11
   217
{
icculus@11
   218
    if (ptr != NULL)
icculus@11
   219
        lua_pushlightuserdata(L, ptr);
icculus@11
   220
    else
icculus@11
   221
        lua_pushnil(L);
icculus@11
   222
    return 1;
icculus@11
   223
} // retvalPointer
icculus@11
   224
icculus@0
   225
static inline void xorBlock(uint8_t *dst, const uint8_t *src)
icculus@0
   226
{
icculus@0
   227
    int i;
icculus@0
   228
    for (i = 0; i < 16; i++, dst++, src++)
icculus@0
   229
        *dst ^= *src;
icculus@0
   230
} // xorBlock
icculus@0
   231
icculus@0
   232
static int decryptUsingKeyAndIvec(uint8_t *data, size_t *datalen, const uint8_t *key, const uint8_t *iv)
icculus@0
   233
{
icculus@0
   234
    const size_t blocks = *datalen / 16;
icculus@0
   235
    uint8_t *block = data + ((blocks-1) * 16);   // start at final block, work backwards
icculus@0
   236
    const uint8_t *padding = &block[15];
icculus@0
   237
    uint8_t expkey[aesExpandedKeySize];
icculus@0
   238
    size_t i;
icculus@0
   239
icculus@0
   240
    if (blocks == 0)
icculus@0
   241
        return 1;  // nothing to do.
icculus@0
   242
icculus@0
   243
	aesExpandKey(key, expkey);
icculus@0
   244
icculus@0
   245
    for (i = 0; i < blocks-1; i++)
icculus@0
   246
    {
icculus@0
   247
        aesDecrypt(block, expkey, block);   // decrypt in place.
icculus@0
   248
        xorBlock(block, block-16);
icculus@0
   249
        block -= 16;
icculus@0
   250
    }
icculus@0
   251
    aesDecrypt(block, expkey, block);   // decrypt in place.
icculus@0
   252
    xorBlock(block, iv);   // xor against initial vector for final block.
icculus@0
   253
icculus@0
   254
    if (*padding > 16)
icculus@0
   255
        return 0;  // bad data?
icculus@0
   256
icculus@0
   257
    *datalen -= *padding;
icculus@0
   258
icculus@0
   259
    return 1;
icculus@0
   260
} // decryptBinaryUsingKeyAndIvec
icculus@0
   261
icculus@0
   262
icculus@0
   263
static inline int isSalted(const uint8_t *data, const size_t datalen)
icculus@0
   264
{
icculus@0
   265
    return ( (datalen > sizeof (saltprefix)) &&
icculus@0
   266
             (memcmp(data, saltprefix, sizeof (saltprefix)) == 0) );
icculus@0
   267
} // isSalted
icculus@0
   268
icculus@0
   269
icculus@0
   270
static int decryptUsingPBKDF2(lua_State *L)
icculus@0
   271
{
icculus@0
   272
    const char *base64 = luaL_checkstring(L, 1);
icculus@0
   273
    const char *password = luaL_checkstring(L, 2);
icculus@0
   274
    const int iterations = luaL_checkinteger(L, 3);
icculus@0
   275
    size_t datalen = strlen(base64);
icculus@0
   276
    uint8_t *dataptr = (uint8_t *) malloc(datalen);
icculus@0
   277
    uint8_t *data = dataptr;
icculus@0
   278
    base64_decodestate base64state;
icculus@0
   279
icculus@0
   280
    base64_init_decodestate(&base64state);
icculus@0
   281
    datalen = base64_decode_block(base64, (int) datalen, data, &base64state);
icculus@0
   282
icculus@0
   283
    const uint8_t *salt = zero16;
icculus@0
   284
    int saltlen = sizeof (zero16);
icculus@0
   285
    if (isSalted(data, datalen))
icculus@0
   286
    {
icculus@0
   287
        salt = data + 8;
icculus@0
   288
        saltlen = 8;
icculus@0
   289
        data += 16;
icculus@0
   290
        datalen -= 16;
icculus@0
   291
    } // if
icculus@0
   292
icculus@0
   293
    uint8_t output[32];
icculus@0
   294
    pkcs5_pbkdf2(password, strlen(password), salt, saltlen, output, sizeof (output), (unsigned int) iterations);
icculus@0
   295
icculus@0
   296
    const uint8_t *aeskey = &output[0];
icculus@0
   297
    const uint8_t *aesiv = &output[16];
icculus@0
   298
	if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv))
icculus@0
   299
        retvalStringBytes(L, data, datalen);
icculus@0
   300
    else
icculus@0
   301
        lua_pushnil(L);
icculus@0
   302
icculus@0
   303
    free(dataptr);
icculus@0
   304
    return 1;
icculus@0
   305
} // decryptUsingPBKDF2
icculus@0
   306
icculus@0
   307
icculus@0
   308
static int decryptBase64UsingKey(lua_State *L)
icculus@0
   309
{
icculus@0
   310
    size_t keylen = 0;
icculus@0
   311
    const char *base64 = luaL_checkstring(L, 1);
icculus@0
   312
    const uint8_t *key = (const uint8_t *) luaL_checklstring(L, 2, &keylen);
icculus@0
   313
    size_t datalen = strlen(base64);
icculus@0
   314
    uint8_t *dataptr = (uint8_t *) malloc(datalen);
icculus@0
   315
    uint8_t *data = dataptr;
icculus@0
   316
    base64_decodestate base64state;
icculus@0
   317
icculus@0
   318
    base64_init_decodestate(&base64state);
icculus@0
   319
    datalen = base64_decode_block(base64, (int) datalen, data, &base64state);
icculus@0
   320
icculus@0
   321
    uint8_t aeskey[16];
icculus@0
   322
    uint8_t aesiv[16];
icculus@0
   323
    MD5_CTX md5;
icculus@0
   324
icculus@0
   325
    if (isSalted(data, datalen))
icculus@0
   326
    {
icculus@0
   327
        const uint8_t *salt = data + 8;
icculus@0
   328
        const size_t saltlen = 8;
icculus@0
   329
        data += 16;
icculus@0
   330
        datalen -= 16;
icculus@0
   331
icculus@0
   332
        assert(aesNr == 10);  // AES-256 needs more rounds.
icculus@0
   333
        assert(aesNk == 4);   // hashing size is hardcoded later.
icculus@0
   334
        uint8_t hashing[32];
icculus@0
   335
icculus@0
   336
        MD5_init(&md5);
icculus@0
   337
        MD5_append(&md5, key, keylen);
icculus@0
   338
        MD5_append(&md5, salt, saltlen);
icculus@0
   339
        MD5_finish(&md5, hashing);
icculus@0
   340
icculus@0
   341
        MD5_init(&md5);
icculus@0
   342
        MD5_append(&md5, hashing, 16);
icculus@0
   343
        MD5_append(&md5, key, keylen);
icculus@0
   344
        MD5_append(&md5, salt, saltlen);
icculus@0
   345
        MD5_finish(&md5, &hashing[16]);
icculus@0
   346
icculus@0
   347
        memcpy(aeskey, hashing, 4 * aesNk);
icculus@0
   348
        memcpy(aesiv, &hashing[4 * aesNk], 16);
icculus@0
   349
    } // if
icculus@0
   350
    else
icculus@0
   351
    {
icculus@0
   352
        MD5_init(&md5);
icculus@0
   353
        MD5_append(&md5, key, keylen);
icculus@0
   354
        MD5_finish(&md5, aeskey);
icculus@0
   355
        memset(aesiv, '\0', sizeof (aesiv));
icculus@0
   356
    } // else
icculus@0
   357
icculus@0
   358
	if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv))
icculus@0
   359
        retvalStringBytes(L, data, datalen);
icculus@0
   360
    else
icculus@0
   361
        lua_pushnil(L);
icculus@0
   362
icculus@0
   363
    free(dataptr);
icculus@0
   364
    return 1;
icculus@0
   365
} // decryptBase64UsingKey
icculus@0
   366
icculus@0
   367
icculus@56
   368
static int decryptTopt(lua_State *L)
icculus@56
   369
{
icculus@56
   370
    const char *base32_secret = luaL_checkstring(L, 1);
icculus@56
   371
    char otpstr[16];
icculus@56
   372
    const int rc = totp(base32_secret, otpstr, sizeof (otpstr));
icculus@56
   373
    return retvalString(L, (rc == -1) ? NULL : otpstr);
icculus@56
   374
} // decryptTopt
icculus@56
   375
icculus@56
   376
icculus@46
   377
static void calcSha256(const BYTE *buf, const size_t len, BYTE *hash)
icculus@46
   378
{
icculus@46
   379
    SHA256_CTX sha256;
icculus@46
   380
    sha256_init(&sha256);
icculus@46
   381
    sha256_update(&sha256, buf, len);
icculus@46
   382
    sha256_final(&sha256, hash);
icculus@46
   383
} // calcSha256
icculus@46
   384
icculus@46
   385
static int calcSha256_Lua(lua_State *L)
icculus@46
   386
{
icculus@46
   387
    size_t len = 0;
icculus@46
   388
    const char *str = luaL_checklstring(L, 1, &len);
icculus@46
   389
    BYTE hash[32];
icculus@46
   390
    calcSha256(str, len, hash);
icculus@46
   391
    return retvalStringBytes(L, hash, sizeof (hash));
icculus@46
   392
} // calcSha256_Lua
icculus@46
   393
icculus@46
   394
icculus@12
   395
static int runGuiPasswordPrompt(lua_State *L)
icculus@12
   396
{
icculus@12
   397
    const char *hintstr = lua_tostring(L, 1);
icculus@12
   398
    GtkWidget *dialog = gtk_dialog_new_with_buttons(
icculus@12
   399
                            "Master Password", NULL, GTK_DIALOG_MODAL,
icculus@12
   400
                            GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
icculus@12
   401
                            GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
icculus@12
   402
                            NULL);
icculus@12
   403
icculus@12
   404
    GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
icculus@12
   405
icculus@12
   406
    if (hintstr != NULL)
icculus@12
   407
    {
icculus@12
   408
        GtkWidget *label = gtk_label_new(hintstr);
icculus@12
   409
        gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
icculus@12
   410
        gtk_container_add(GTK_CONTAINER(content_area), label);
icculus@12
   411
    } // if
icculus@12
   412
icculus@12
   413
    GtkWidget *entry = gtk_entry_new();
icculus@12
   414
    gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
icculus@12
   415
    gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
icculus@12
   416
    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
icculus@12
   417
    gtk_container_add(GTK_CONTAINER(content_area), entry);
icculus@12
   418
icculus@12
   419
    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
icculus@45
   420
    gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
icculus@45
   421
    gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), TRUE);
icculus@45
   422
    gtk_window_set_skip_pager_hint(GTK_WINDOW(dialog), TRUE);
icculus@12
   423
    gtk_widget_show_all(dialog);
icculus@26
   424
    gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
icculus@12
   425
    const int ok = (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT);
icculus@12
   426
    retvalString(L, ok ? (const char *) gtk_entry_get_text(GTK_ENTRY(entry)) : NULL);
icculus@12
   427
    gtk_widget_destroy(dialog);
icculus@12
   428
icculus@12
   429
    return 1;
icculus@12
   430
} // runGuiPasswordPrompt
icculus@12
   431
icculus@12
   432
icculus@12
   433
static int copyToClipboard(lua_State *L)
icculus@12
   434
{
icculus@12
   435
    const char *str = luaL_checkstring(L, 1);
icculus@12
   436
    gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), str, -1);
icculus@12
   437
    gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), str, -1);
icculus@12
   438
} // copyToClipboard
icculus@12
   439
icculus@45
   440
static gboolean checkForEscapeKey(GtkWidget *widget, GdkEvent *event, gpointer arg)
icculus@45
   441
{
icculus@45
   442
    if ((event->type == GDK_KEY_PRESS) && (event->key.keyval == GDK_KEY_Escape))
icculus@45
   443
    {
icculus@45
   444
        // !!! FIXME: this is a little hacky
icculus@45
   445
        lua_getglobal(luaState, "escapePressed");
icculus@45
   446
        lua_call(luaState, 0, 0);
icculus@45
   447
        return TRUE;
icculus@45
   448
    } // if
icculus@12
   449
icculus@45
   450
    return FALSE;  // pass this to other event handlers.
icculus@45
   451
} // checkForEscapeKey
icculus@45
   452
icculus@45
   453
static gboolean wasSearchDeleteText = FALSE;  // HACK to workaround gtk+ nonsense.
icculus@45
   454
static void searchChanged(GtkEditable *editable, gpointer arg)
icculus@11
   455
{
icculus@45
   456
    const int callback = (int) ((size_t)arg);
icculus@45
   457
    GtkWidget *vbox = gtk_widget_get_parent(GTK_WIDGET(editable));
icculus@45
   458
    lua_rawgeti(luaState, LUA_REGISTRYINDEX, callback);
icculus@45
   459
    lua_pushlightuserdata(luaState, vbox);
icculus@45
   460
    lua_pushstring(luaState, gtk_entry_get_text(GTK_ENTRY(editable)));
icculus@45
   461
    lua_call(luaState, 2, 0);
icculus@45
   462
    gtk_widget_grab_focus(GTK_WIDGET(editable));
icculus@45
   463
    if (wasSearchDeleteText)  // HACK to workaround gtk+ nonsense.
icculus@45
   464
    {
icculus@45
   465
        gtk_editable_set_position(editable, -1);
icculus@45
   466
        wasSearchDeleteText = FALSE;
icculus@45
   467
    } // if
icculus@45
   468
} // searchChanged
icculus@11
   469
icculus@45
   470
// HACK to workaround gtk+ nonsense.
icculus@45
   471
static void searchDeleteText(GtkEditable *editable, gint start_pos, gint end_pos, gpointer user_data)
icculus@45
   472
{
icculus@45
   473
    wasSearchDeleteText = TRUE;
icculus@45
   474
} // searchDeleteText
icculus@11
   475
icculus@45
   476
static void destroyLuaCallback(const int callback)
icculus@11
   477
{
icculus@45
   478
    //printf("unref callback %d\n", callback);
icculus@45
   479
    luaL_unref(luaState, LUA_REGISTRYINDEX, callback);
icculus@45
   480
} // destroyLuaCallback
icculus@45
   481
icculus@45
   482
static void destroyCallback(GtkWidget *widget, gpointer arg)
icculus@45
   483
{
icculus@45
   484
    destroyLuaCallback((int) ((size_t)arg));
icculus@45
   485
} // destroyCallback
icculus@45
   486
icculus@45
   487
static void destroyTopLevelMenu(GtkWidget *widget, gpointer arg)
icculus@45
   488
{
icculus@45
   489
    // !!! FIXME: hack
icculus@45
   490
    int *cbs = (int *) arg;
icculus@45
   491
    lua_rawgeti(luaState, LUA_REGISTRYINDEX, cbs[1]);
icculus@11
   492
    lua_call(luaState, 0, 0);
icculus@45
   493
    destroyLuaCallback(cbs[0]);
icculus@45
   494
    destroyLuaCallback(cbs[1]);
icculus@45
   495
    free(cbs);
icculus@45
   496
} // destroyTopLevelMenu
icculus@45
   497
icculus@45
   498
#if 0
icculus@45
   499
static gboolean
icculus@45
   500
mappedWindow(GtkWidget *widget, GdkEvent *event, gpointer user_data)
icculus@45
   501
{
icculus@45
   502
    GdkEventClient e;
icculus@45
   503
    memset(&e, '\0', sizeof (e));
icculus@45
   504
    e.type = GDK_CLIENT_EVENT;
icculus@45
   505
    e.window = gtk_widget_get_window(widget);
icculus@45
   506
    e.send_event = 1;
icculus@45
   507
    e.message_type = gdk_atom_intern_static_string("_NET_ACTIVE_WINDOW");
icculus@45
   508
    e.data_format = 32;
icculus@45
   509
    e.data.l[0] = 1;
icculus@45
   510
    e.data.l[1] = (long) gdk_x11_get_server_time(e.window);
icculus@45
   511
    e.data.l[2] = 0;
icculus@45
   512
icculus@45
   513
    gdk_window_raise (e.window);
icculus@45
   514
    gdk_event_send_client_message((GdkEvent *) &e, gdk_x11_drawable_get_xid(gtk_widget_get_root_window(widget)));
icculus@45
   515
    return TRUE;
icculus@45
   516
}
icculus@45
   517
#endif
icculus@45
   518
icculus@45
   519
static int guiCreateTopLevelMenu(lua_State *L)
icculus@45
   520
{
icculus@45
   521
    const char *title = luaL_checkstring(L, 1);
icculus@45
   522
    const int changedCallback = makeLuaCallback(L, 2);
icculus@45
   523
    const int destroyedCallback = makeLuaCallback(L, 3);
icculus@45
   524
icculus@45
   525
    int *cbs = (int *) malloc(sizeof (int) * 2);  // !!! FIXME: hack
icculus@45
   526
    cbs[0] = changedCallback;
icculus@45
   527
    cbs[1] = destroyedCallback;
icculus@45
   528
icculus@45
   529
    GtkWindow *window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
icculus@45
   530
    gtk_window_set_keep_above(window, TRUE);
icculus@45
   531
    gtk_window_set_skip_taskbar_hint(window, TRUE);
icculus@45
   532
    gtk_window_set_skip_pager_hint(window, TRUE);
icculus@45
   533
    g_signal_connect(window, "destroy", G_CALLBACK(destroyTopLevelMenu), cbs);
icculus@45
   534
    gtk_window_set_title(window, title);
icculus@45
   535
    gtk_window_set_position(window, GTK_WIN_POS_MOUSE);
icculus@45
   536
    gtk_window_set_decorated(window, FALSE);
icculus@45
   537
    gtk_window_set_resizable(window, FALSE);
icculus@45
   538
    GtkEntry *search = GTK_ENTRY(gtk_entry_new());
icculus@45
   539
    g_signal_connect(search, "key-press-event", G_CALLBACK(checkForEscapeKey), window);
icculus@45
   540
    gtk_entry_set_text(search, "Search...");
icculus@45
   541
    g_signal_connect(search, "changed", G_CALLBACK(searchChanged), (gpointer) ((size_t)changedCallback));
icculus@45
   542
    g_signal_connect(search, "delete-text", G_CALLBACK(searchDeleteText), 0); // HACK to workaround gtk+ nonsense.
icculus@45
   543
icculus@45
   544
//    g_signal_connect(window, "map-event", G_CALLBACK(mappedWindow), NULL);
icculus@45
   545
icculus@45
   546
icculus@45
   547
    GtkVBox *vbox = GTK_VBOX(gtk_vbox_new(FALSE, 0));
icculus@45
   548
    gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
icculus@45
   549
icculus@45
   550
    gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(search), FALSE, FALSE, 0);
icculus@45
   551
    gtk_widget_show(GTK_WIDGET(search));
icculus@45
   552
icculus@45
   553
    GtkWidget *vsep = GTK_WIDGET(gtk_vseparator_new());
icculus@45
   554
    gtk_box_pack_start(GTK_BOX(vbox), vsep, FALSE, FALSE, 0);
icculus@45
   555
    gtk_widget_show(vsep);
icculus@45
   556
icculus@45
   557
    return retvalPointer(L, vbox);
icculus@45
   558
} // guiCreateTopLevelMenu
icculus@45
   559
icculus@45
   560
static void clickedMenuItem(GtkButton *button, gpointer arg)
icculus@45
   561
{
icculus@45
   562
    lua_rawgeti(luaState, LUA_REGISTRYINDEX, (int) ((size_t)arg));
icculus@45
   563
    lua_pushlightuserdata(luaState, button);
icculus@45
   564
    lua_call(luaState, 1, 0);
icculus@11
   565
} // clickedMenuItem
icculus@11
   566
icculus@45
   567
static int guiAddMenuItem(lua_State *L)
icculus@12
   568
{
icculus@45
   569
    GtkWidget *vbox = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   570
    const char *label = luaL_checkstring(L, 2);
icculus@46
   571
    const int checked = lua_toboolean(L, 3);
icculus@46
   572
    const int callback = makeLuaCallback(L, 4);
icculus@46
   573
icculus@46
   574
    if (checked)
icculus@46
   575
    {
icculus@46
   576
        // !!! FIXME: this is pretty lousy.
icculus@46
   577
        const size_t len = strlen(label) + 5;
icculus@46
   578
        char *buf = (char *) alloca(len);
icculus@46
   579
        snprintf(buf, len, "[X] %s", label);
icculus@46
   580
        label = buf;
icculus@46
   581
    } // if
icculus@46
   582
icculus@45
   583
    GtkWidget *item = GTK_WIDGET(gtk_button_new_with_label(label));
icculus@45
   584
    g_signal_connect(item, "key-press-event", G_CALLBACK(checkForEscapeKey), NULL);
icculus@45
   585
    g_signal_connect(item, "clicked", G_CALLBACK(clickedMenuItem), (gpointer) ((size_t)callback));
icculus@45
   586
    g_signal_connect(item, "destroy", G_CALLBACK(destroyCallback), (gpointer) ((size_t)callback));
icculus@11
   587
icculus@45
   588
    //gtk_button_set_image(button, gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU));
icculus@45
   589
    gtk_button_set_alignment (GTK_BUTTON(item), 0.0f, 0.5f);
icculus@45
   590
    gtk_button_set_relief(GTK_BUTTON(item), GTK_RELIEF_NONE);
icculus@45
   591
    gtk_box_pack_start(GTK_BOX(vbox), item, FALSE, FALSE, 0);
icculus@45
   592
    gtk_widget_show(item);
icculus@45
   593
    return retvalPointer(L, item);
icculus@45
   594
} // guiAddMenuItem
icculus@45
   595
icculus@45
   596
static int guiRemoveAllMenuItems(lua_State *L)
icculus@11
   597
{
icculus@45
   598
    GtkWidget *vbox = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   599
    GList *children = gtk_container_get_children(GTK_CONTAINER(vbox));
icculus@45
   600
    GList *iter;
icculus@11
   601
icculus@45
   602
    gtk_widget_hide(vbox);
icculus@45
   603
    for (iter = children; iter != NULL; iter = g_list_next(iter))
icculus@11
   604
    {
icculus@45
   605
        if (G_OBJECT_TYPE(iter->data) == GTK_TYPE_BUTTON)
icculus@45
   606
             gtk_widget_destroy(GTK_WIDGET(iter->data));
icculus@45
   607
    } // for
icculus@45
   608
    g_list_free(children);
icculus@11
   609
icculus@45
   610
    return 0;
icculus@45
   611
} // guiRemoveAllMenuItems
icculus@11
   612
icculus@45
   613
static int guiDestroyMenu(lua_State *L)
icculus@45
   614
{
icculus@45
   615
    GtkWidget *widget = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   616
    gtk_widget_destroy(gtk_widget_get_toplevel(widget));
icculus@45
   617
    return 0;
icculus@45
   618
} // guiDestroyMenu
icculus@11
   619
icculus@45
   620
static int guiShowWindow(lua_State *L)
icculus@11
   621
{
icculus@45
   622
    GtkWidget *widget = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   623
    //gtk_container_resize_children(GTK_CONTAINER(vbox));
icculus@45
   624
    gtk_widget_show(widget);
icculus@45
   625
    GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
icculus@45
   626
    gtk_window_present(GTK_WINDOW(toplevel));
icculus@11
   627
    return 0;
icculus@45
   628
} // guiShowWindow
icculus@11
   629
icculus@45
   630
static int guiCreateSubMenu(lua_State *L)
icculus@45
   631
{
icculus@45
   632
    GtkWidget *widget = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   633
    GtkWidget *topwindow = gtk_widget_get_toplevel(widget);
icculus@45
   634
    GtkWindow *window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
icculus@45
   635
    gtk_window_set_keep_above(window, TRUE);
icculus@45
   636
    gtk_window_set_skip_taskbar_hint(window, TRUE);
icculus@45
   637
    gtk_window_set_skip_pager_hint(window, TRUE);
icculus@45
   638
    //g_signal_connect(window, "destroy", G_CALLBACK(destroySubMenu), topwindow);
icculus@45
   639
    gtk_window_set_decorated(window, FALSE);
icculus@11
   640
icculus@45
   641
    GtkVBox *vbox = GTK_VBOX(gtk_vbox_new(FALSE, 0));
icculus@45
   642
    gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
icculus@45
   643
icculus@45
   644
    // line the new submenu up...
icculus@45
   645
    //  !!! FIXME: if overflow off right end of screen, go to the left of (widget) instead.
icculus@45
   646
    gint basex, basey, x, y;
icculus@45
   647
    gtk_window_get_position(GTK_WINDOW(topwindow), &basex, &basey);
icculus@45
   648
    gtk_widget_translate_coordinates(widget, topwindow, 0, 0, &x, &y);
icculus@45
   649
    x += basex;
icculus@45
   650
    y += basey;
icculus@45
   651
    x += widget->allocation.width;
icculus@45
   652
    gtk_window_move(window, x, y);
icculus@45
   653
icculus@45
   654
    return retvalPointer(L, vbox);
icculus@45
   655
} // guiCreateSubMenu
icculus@11
   656
icculus@11
   657
icculus@28
   658
static int setPowermateLED_Lua(lua_State *L)
icculus@28
   659
{
icculus@28
   660
    const int enable = lua_toboolean(L, 1);
icculus@28
   661
    setPowermateLED(enable);
icculus@28
   662
    return 0;
icculus@28
   663
} // setPowermateLED_Lua
icculus@28
   664
icculus@28
   665
icculus@17
   666
static void keyhookPressed(void)
icculus@17
   667
{
icculus@17
   668
    lua_getglobal(luaState, "keyhookPressed");
icculus@17
   669
    lua_call(luaState, 0, 0);
icculus@17
   670
} // keyhookPressed
icculus@17
   671
icculus@17
   672
icculus@29
   673
static int pumpLua(void)
icculus@29
   674
{
icculus@29
   675
    lua_getglobal(luaState, "pumpLua");
icculus@29
   676
    lua_call(luaState, 0, 0);
icculus@29
   677
} // pumpLua
icculus@29
   678
icculus@29
   679
icculus@17
   680
static gboolean keyhookPumper(void *arg)
icculus@17
   681
{
icculus@29
   682
    pumpLua();
icculus@17
   683
    if (pumpKeyHook())
icculus@17
   684
        keyhookPressed();
icculus@28
   685
    else if (pumpPowermate())
icculus@28
   686
        keyhookPressed();
icculus@28
   687
icculus@17
   688
    return TRUE;  // keep firing timer
icculus@17
   689
} // keyhookPumper
icculus@17
   690
icculus@17
   691
icculus@11
   692
static int giveControlToGui(lua_State *L)
icculus@11
   693
{
icculus@17
   694
    if (initKeyHook())
icculus@17
   695
    {
icculus@17
   696
        atexit(deinitKeyHook);
icculus@17
   697
        g_timeout_add(200, (GSourceFunc) keyhookPumper, (gpointer) NULL);
icculus@17
   698
    } // if
icculus@17
   699
icculus@11
   700
    gtk_main();
icculus@11
   701
    return 0;
icculus@11
   702
} // giveControlToGui
icculus@11
   703
icculus@7
   704
icculus@46
   705
static int getMountedDisks(lua_State *L)
icculus@46
   706
{
icculus@46
   707
    lua_newtable(L);
icculus@46
   708
    int luai = 1;
icculus@46
   709
icculus@46
   710
    FILE *mounts = setmntent("/etc/mtab", "r");
icculus@46
   711
    if (mounts != NULL)
icculus@46
   712
    {
icculus@46
   713
        struct mntent *ent = NULL;
icculus@46
   714
        while ((ent = getmntent(mounts)) != NULL)
icculus@46
   715
        {
icculus@46
   716
            lua_pushinteger(luaState, luai);
icculus@46
   717
            lua_pushstring(luaState, ent->mnt_dir);
icculus@46
   718
            lua_settable(luaState, -3);
icculus@46
   719
            luai++;
icculus@46
   720
        } // while
icculus@46
   721
        endmntent(mounts);
icculus@46
   722
    } // if
icculus@46
   723
icculus@46
   724
    return 1;  // return the table.
icculus@46
   725
} // getMountedDisks
icculus@46
   726
icculus@46
   727
icculus@0
   728
static void *luaAlloc(void *ud, void *ptr, size_t osize, size_t nsize)
icculus@0
   729
{
icculus@0
   730
    if (nsize == 0)
icculus@0
   731
    {
icculus@0
   732
        free(ptr);
icculus@0
   733
        return NULL;
icculus@0
   734
    } // if
icculus@0
   735
    return realloc(ptr, nsize);
icculus@0
   736
} // luaAlloc
icculus@0
   737
icculus@0
   738
icculus@0
   739
static inline void luaSetCFunc(lua_State *L, lua_CFunction f, const char *sym)
icculus@0
   740
{
icculus@0
   741
    lua_pushcfunction(L, f);
icculus@0
   742
    lua_setglobal(luaState, sym);
icculus@0
   743
} // luaSetCFunc
icculus@0
   744
icculus@0
   745
icculus@0
   746
static int luaFatal(lua_State *L)
icculus@0
   747
{
icculus@0
   748
    const char *errstr = lua_tostring(L, -1);
icculus@0
   749
    fprintf(stderr, "Lua panic: %s\n", errstr ? errstr : "(?)");
icculus@0
   750
    fflush(stderr);
icculus@0
   751
    exit(1);
icculus@0
   752
} // luaFatal
icculus@0
   753
icculus@0
   754
icculus@11
   755
static void deinitLua(void)
icculus@11
   756
{
icculus@11
   757
    if (luaState != NULL)
icculus@11
   758
    {
icculus@11
   759
        lua_close(luaState);
icculus@11
   760
        luaState = NULL;
icculus@11
   761
    } // if
icculus@11
   762
} // deinitLua
icculus@11
   763
icculus@11
   764
icculus@7
   765
static int initLua(const int argc, char **argv)
icculus@0
   766
{
icculus@11
   767
    atexit(deinitLua);
icculus@11
   768
icculus@0
   769
    assert(luaState == NULL);
icculus@0
   770
    luaState = lua_newstate(luaAlloc, NULL);
icculus@0
   771
icculus@0
   772
    lua_atpanic(luaState, luaFatal);
icculus@0
   773
    assert(lua_checkstack(luaState, 20));  // Just in case.
icculus@2
   774
    luaL_openlibs(luaState);
icculus@0
   775
icculus@0
   776
    // Set up initial C functions, etc we want to expose to Lua code...
icculus@0
   777
    luaSetCFunc(luaState, decryptUsingPBKDF2, "decryptUsingPBKDF2");
icculus@0
   778
    luaSetCFunc(luaState, decryptBase64UsingKey, "decryptBase64UsingKey");
icculus@56
   779
    luaSetCFunc(luaState, decryptTopt, "decryptTopt");
icculus@11
   780
    luaSetCFunc(luaState, giveControlToGui, "giveControlToGui");
icculus@12
   781
    luaSetCFunc(luaState, runGuiPasswordPrompt, "runGuiPasswordPrompt");
icculus@12
   782
    luaSetCFunc(luaState, copyToClipboard, "copyToClipboard");
icculus@28
   783
    luaSetCFunc(luaState, setPowermateLED_Lua, "setPowermateLED");
icculus@46
   784
    luaSetCFunc(luaState, calcSha256_Lua, "calcSha256");
icculus@46
   785
    luaSetCFunc(luaState, getMountedDisks, "getMountedDisks");
icculus@0
   786
icculus@45
   787
    luaSetCFunc(luaState, guiCreateTopLevelMenu, "guiCreateTopLevelMenu");
icculus@45
   788
    luaSetCFunc(luaState, guiCreateSubMenu, "guiCreateSubMenu");
icculus@45
   789
    luaSetCFunc(luaState, guiAddMenuItem, "guiAddMenuItem");
icculus@45
   790
    luaSetCFunc(luaState, guiRemoveAllMenuItems, "guiRemoveAllMenuItems");
icculus@45
   791
    luaSetCFunc(luaState, guiDestroyMenu, "guiDestroyMenu");
icculus@45
   792
    luaSetCFunc(luaState, guiShowWindow, "guiShowWindow");
icculus@45
   793
icculus@7
   794
    // Set up argv table...
icculus@7
   795
    lua_newtable(luaState);
icculus@7
   796
    int i;
icculus@28
   797
    int luai = 1;
icculus@7
   798
    for (i = 0; i < argc; i++)
icculus@7
   799
    {
icculus@28
   800
        if (argv[i])
icculus@28
   801
        {
icculus@28
   802
            lua_pushinteger(luaState, luai);
icculus@28
   803
            lua_pushstring(luaState, argv[i]);
icculus@28
   804
            lua_settable(luaState, -3);
icculus@28
   805
            luai++;
icculus@28
   806
        } // if
icculus@7
   807
    } // for
icculus@7
   808
    lua_setglobal(luaState, "argv");
icculus@7
   809
icculus@3
   810
    // Transfer control to Lua...
icculus@0
   811
    if (luaL_dofile(luaState, "1pass.lua") != 0)
icculus@16
   812
        luaFatal(luaState);
icculus@0
   813
icculus@0
   814
    return 1;
icculus@0
   815
} // initLua
icculus@0
   816
icculus@0
   817
icculus@33
   818
static void deinitAll()
icculus@33
   819
{
icculus@33
   820
    deinitPowermate();
icculus@33
   821
    deinitKeyHook();
icculus@33
   822
    deinitLua();
icculus@33
   823
} // deinitAll
icculus@33
   824
icculus@33
   825
icculus@33
   826
static void killerSignalCatcher(int sig)
icculus@33
   827
{
icculus@33
   828
    static int been_run = 0;
icculus@33
   829
    if (been_run)
icculus@33
   830
    {
icculus@33
   831
        fprintf(stderr, "Caught signal %d, terminating HARD.\n", sig);
icculus@33
   832
        _exit(0);
icculus@33
   833
    } // if
icculus@33
   834
icculus@33
   835
    been_run = 1;
icculus@33
   836
    fprintf(stderr, "Caught signal %d, terminating.\n", sig);
icculus@33
   837
    exit(0);  // trigger atexit handlers.
icculus@33
   838
} // killerSignalCatcher
icculus@33
   839
icculus@33
   840
icculus@33
   841
static void initSignals(void)
icculus@33
   842
{
icculus@33
   843
    signal(SIGINT, killerSignalCatcher);
icculus@33
   844
    signal(SIGQUIT, killerSignalCatcher);
icculus@33
   845
    signal(SIGILL, killerSignalCatcher);
icculus@33
   846
    signal(SIGFPE, killerSignalCatcher);
icculus@33
   847
    signal(SIGTERM, killerSignalCatcher);
icculus@33
   848
    signal(SIGPIPE, killerSignalCatcher);
icculus@33
   849
    signal(SIGSEGV, killerSignalCatcher);
icculus@33
   850
    signal(SIGABRT, killerSignalCatcher);
icculus@33
   851
    signal(SIGHUP, killerSignalCatcher);
icculus@33
   852
} // initSignals
icculus@33
   853
icculus@33
   854
icculus@39
   855
// this was from PhysicsFS ( https://icculus.org/physfs/ ), zlib license.
icculus@39
   856
static char *readSymLink(const char *path)
icculus@39
   857
{
icculus@39
   858
    ssize_t len = 64;
icculus@39
   859
    ssize_t rc = -1;
icculus@39
   860
    char *retval = NULL;
icculus@39
   861
icculus@39
   862
    while (1)
icculus@39
   863
    {
icculus@39
   864
         char *ptr = (char *) realloc(retval, (size_t) len);
icculus@39
   865
         if (ptr == NULL)
icculus@39
   866
             break;   // out of memory.
icculus@39
   867
         retval = ptr;
icculus@39
   868
icculus@39
   869
         rc = readlink(path, retval, len);
icculus@39
   870
         if (rc == -1)
icculus@39
   871
             break;  // not a symlink, i/o error, etc.
icculus@39
   872
icculus@39
   873
         else if (rc < len)
icculus@39
   874
         {
icculus@39
   875
             retval[rc] = '\0';  // readlink doesn't null-terminate.
icculus@39
   876
             return retval;  // we're good to go.
icculus@39
   877
         } // else if
icculus@39
   878
icculus@39
   879
         len *= 2;  // grow buffer, try again.
icculus@39
   880
    } // while
icculus@39
   881
icculus@39
   882
    if (retval != NULL)
icculus@39
   883
        free(retval);
icculus@39
   884
    return NULL;
icculus@39
   885
} // readSymLink
icculus@39
   886
icculus@39
   887
icculus@39
   888
static void chdirToApp(void)
icculus@39
   889
{
icculus@39
   890
    char *ptr;
icculus@39
   891
    char *path = readSymLink("/proc/self/exe");
icculus@39
   892
    
icculus@39
   893
    if (path == NULL)
icculus@39
   894
        return;  // maybe we're already there, fail later if not.
icculus@39
   895
icculus@39
   896
    ptr = strrchr(path, '/');
icculus@39
   897
    if (ptr != NULL)
icculus@39
   898
    {
icculus@39
   899
        *ptr = '\0';
icculus@39
   900
        if (chdir(path) == -1)
icculus@39
   901
            fprintf(stderr, "Couldn't chdir() to app directory?! %s\n", strerror(errno));
icculus@39
   902
    } // if
icculus@39
   903
icculus@39
   904
    free(path);
icculus@39
   905
} // chdirToApp
icculus@39
   906
icculus@39
   907
icculus@0
   908
int main(int argc, char **argv)
icculus@0
   909
{
icculus@39
   910
    chdirToApp();
icculus@33
   911
    initSignals();
icculus@28
   912
    initPowermate(&argc, argv);
icculus@11
   913
    gtk_init(&argc, &argv);
icculus@0
   914
icculus@7
   915
    if (!initLua(argc, argv))  // this will move control to 1pass.lua
icculus@0
   916
        return 1;
icculus@0
   917
icculus@0
   918
    return 0;
icculus@0
   919
} // main
icculus@0
   920
icculus@0
   921
// end of 1pass.c ...
icculus@0
   922