[BACK]Return to dgamelaunch.c CVS log [TXT][DIR] Up to [contributed] / dgamelaunch-openbsd

Annotation of dgamelaunch-openbsd/dgamelaunch.c, Revision 1.3

1.1       rubenllo    1: /* dgamelaunch.c
                      2:  *
1.3     ! rubenllo    3:  * (c)2021 RubĂ©n Llorente <porting@use.startmail.com>
1.1       rubenllo    4:  * (c)2001-4 M. Drew Streib <dtype@dtype.org>
                      5:  * also parts (c) 2003-4 Joshua Kwan <joshk@triplehelix.org>,
                      6:  * Brett Carrington <brettcar@segvio.org>,
                      7:  * Jilles Tjoelker <jilles@stack.nl>
                      8:  *
                      9:  * This program is free software; you can redistribute it and/or modify
                     10:  * it under the terms of the GNU General Public License as published by
                     11:  * the Free Software Foundation; either version 2 of the License, or
                     12:  * (at your option) any later version.
                     13:  *
                     14:  * This program is distributed in the hope that it will be useful,
                     15:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
                     16:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     17:  * GNU General Public License for more details.
                     18:  *
                     19:  * You should have received a copy of the GNU General Public License
                     20:  * along with this program; if not, write to the Free Software
                     21:  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
                     22:  */
                     23:
                     24: /*
                     25:  * See this program in action at http://alt.org/nethack/
                     26:  *
                     27:  * This is a little wrapper for nethack (and soon other programs) that
                     28:  * will allow them to be run from a telnetd session, chroot, shed privs,
                     29:  * make a simple login, then play the game.
                     30:  */
                     31:
                     32: #define _GNU_SOURCE
                     33:
                     34: #include "dgamelaunch.h"
                     35: #include "config.h"
                     36: #include "ttyplay.h"
                     37: #include "ttyrec.h"
                     38:
                     39: /* a request from the author: please leave some remnance of
                     40:  * 'based on dgamelaunch version xxx' in any derivative works, or
                     41:  * even keep the line the same altogether. I'm probably happy
                     42:  * to make any changes you need. */
                     43:
                     44: /* ************************************************************* */
                     45:
                     46: /* program stuff */
                     47: #include <sys/types.h>
                     48: #include <sys/time.h>
                     49: #include <sys/wait.h>
                     50: #include <sys/ioctl.h>          /* ttyrec */
                     51: #include <sys/stat.h>
                     52: #ifdef USE_RLIMIT
                     53: #include <sys/resource.h>
                     54: #endif
                     55:
                     56: #ifdef USE_SHMEM
                     57: #include <sys/ipc.h>
                     58: #include <sys/shm.h>
                     59: #endif
                     60:
                     61: #include <libgen.h>
                     62: #include <stdlib.h>
                     63: #include <curses.h>
                     64: #include <locale.h>
                     65:
                     66: #ifdef USE_SQLITE3
                     67: # include <sqlite3.h>
                     68: #endif
                     69:
                     70: #if defined(__FreeBSD__)
                     71: # include <libutil.h>
                     72: #elif defined(__NetBSD__)
                     73: # include <util.h>
1.2       rubenllo   74: #elif defined(__OpenBSD__)
                     75: # include <unistd.h>
1.1       rubenllo   76: #elif defined(__APPLE__)
                     77: # include <unistd.h>
                     78: #else
                     79: # include <crypt.h>
                     80: #endif
                     81:
                     82: #ifdef __linux__
                     83: # include <pty.h>
                     84: #endif
                     85:
                     86: #include <fcntl.h>
                     87: #include <pwd.h>
                     88: #include <grp.h>
                     89: #include <time.h>
                     90: #include <errno.h>
                     91: #include <dirent.h>
                     92: #include <string.h>
                     93: #include <signal.h>
                     94: #include <assert.h>
                     95: #include <ctype.h>
                     96: #include <unistd.h>
                     97: #include <termios.h>
                     98:
                     99: extern FILE* yyin;
                    100: extern int yyparse ();
                    101:
                    102: /* global variables */
                    103:
                    104: char * __progname;
                    105: int  g_idle_alarm_enabled = 0;
                    106: int  showplayers = 0;
                    107: int  initplayer = 0;
                    108: void (*g_chain_winch)(int);
                    109:
                    110: #ifndef USE_SQLITE3
                    111: int f_num = 0;
                    112: struct dg_user **users = NULL;
                    113: #endif
                    114: struct dg_user *me = NULL;
                    115: struct dg_banner banner;
                    116:
                    117: static struct dg_watchcols default_watchcols[] = {
                    118:     {SORTMODE_NONE, SORTMODE_NONE,        1, "", "%s)"},
                    119:     {SORTMODE_USERNAME, SORTMODE_USERNAME,    4, "Username", "%-15s"},
                    120:     {SORTMODE_GAMENUM, SORTMODE_GAMENUM,    21, "Game", "%-5s"},
                    121:     {SORTMODE_WINDOWSIZE, SORTMODE_WINDOWSIZE, 28, " Size", "%s"},
                    122:     {SORTMODE_STARTTIME, SORTMODE_STARTTIME,  37, "Start date & time", "%s"},
                    123:     {SORTMODE_IDLETIME, SORTMODE_IDLETIME,   58, "Idle time", "%-10s"},
                    124: #ifdef USE_SHMEM
                    125:     {SORTMODE_WATCHERS, SORTMODE_WATCHERS,   70, "Watchers", "%s"},
                    126: #endif
                    127: };
                    128:
                    129: int color_remap[16] = {
                    130:     COLOR_PAIR(9) | A_NORMAL,
                    131:     COLOR_PAIR(COLOR_BLUE) | A_NORMAL,
                    132:     COLOR_PAIR(COLOR_GREEN) | A_NORMAL,
                    133:     COLOR_PAIR(COLOR_CYAN) | A_NORMAL,
                    134:     COLOR_PAIR(COLOR_RED) | A_NORMAL,
                    135:     COLOR_PAIR(COLOR_MAGENTA) | A_NORMAL,
                    136:     COLOR_PAIR(COLOR_YELLOW) | A_NORMAL,
                    137:     COLOR_PAIR(COLOR_BLACK) | A_NORMAL,
                    138:     COLOR_PAIR(10) | A_BOLD,
                    139:     COLOR_PAIR(COLOR_BLUE) | A_BOLD,
                    140:     COLOR_PAIR(COLOR_GREEN) | A_BOLD,
                    141:     COLOR_PAIR(COLOR_CYAN) | A_BOLD,
                    142:     COLOR_PAIR(COLOR_RED) | A_BOLD,
                    143:     COLOR_PAIR(COLOR_MAGENTA) | A_BOLD,
                    144:     COLOR_PAIR(COLOR_YELLOW) | A_BOLD,
                    145:     COLOR_PAIR(COLOR_WHITE) | A_BOLD,
                    146: };
                    147:
                    148: static struct dg_watchcols *default_watchcols_list[DGL_MAXWATCHCOLS + 1];
                    149:
                    150: struct dg_user *
                    151: cpy_me(struct dg_user *me)
                    152: {
                    153:     struct dg_user *tmp = malloc(sizeof(struct dg_user));
                    154:
                    155:     if (tmp && me) {
                    156: #ifdef USE_SQLITE3
                    157:        tmp->id = me->id;
                    158: #endif
                    159:        if (me->username) tmp->username = strdup(me->username);
                    160:        if (me->email)    tmp->email    = strdup(me->email);
                    161:        if (me->env)      tmp->env      = strdup(me->env);
                    162:        if (me->password) tmp->password = strdup(me->password);
                    163:        tmp->flags = me->flags;
                    164:     }
                    165:     return tmp;
                    166: }
                    167:
                    168: #ifndef HAVE_SETENV
                    169: int
                    170: mysetenv (const char* name, const char* value, int overwrite)
                    171: {
                    172:   int retval;
                    173:   char *buf = NULL;
                    174:
                    175:   if (getenv(name) == NULL || overwrite)
                    176:   {
                    177:     size_t len = strlen(name) + 1 + strlen(value) + 1; /* NAME=VALUE\0 */
                    178:     buf = malloc(len);
                    179:     snprintf(buf, len, "%s=%s", name, value);
                    180:     retval = putenv(buf);
                    181:   }
                    182:   else
                    183:     retval = -1;
                    184:
                    185:   return retval;
                    186: }
                    187: #else /* use native setenv */
                    188: # define mysetenv setenv
                    189: #endif
                    190:
                    191: /* ************************************************************* */
                    192: /* for ttyrec */
                    193:
                    194: void
                    195: ttyrec_getpty ()
                    196: {
                    197: #ifdef HAVE_OPENPTY
                    198:     if (openpty (&master, &slave, NULL, NULL, NULL) == -1) {
                    199:        debug_write("cannot openpty");
                    200:        graceful_exit (61);
                    201:     }
                    202: #else
                    203:     if ((master = open ("/dev/ptmx", O_RDWR)) < 0) {
                    204:        debug_write("cannot open /dev/ptmx");
                    205:        graceful_exit (62);
                    206:     }
                    207:   grantpt (master);
                    208:   unlockpt (master);
                    209:   if ((slave = open ((const char *) ptsname (master), O_RDWR)) < 0)
                    210:     {
                    211:        debug_write("cannot open master ptsname");
                    212:       graceful_exit (65);
                    213:     }
                    214: #endif
                    215:   ioctl (slave, TIOCSWINSZ, (char *) &win);
                    216:   tcsetattr(slave, TCSANOW, &tt);
                    217: }
                    218:
                    219: /* ************************************************************* */
                    220:
                    221: static int dgl_signal_blocked = 0;
                    222: static sigset_t dgl_signal_blockmask;
                    223: static sigset_t dgl_signal_oldmask;
                    224:
                    225: void
                    226: signals_block()
                    227: {
                    228:     if (!dgl_signal_blocked) {
                    229:        sigemptyset(&dgl_signal_blockmask);
                    230:        sigaddset(&dgl_signal_blockmask, SIGHUP);
                    231:        sigaddset(&dgl_signal_blockmask, SIGINT);
                    232:        sigaddset(&dgl_signal_blockmask, SIGQUIT);
                    233:        sigaddset(&dgl_signal_blockmask, SIGTERM);
                    234:        sigprocmask(SIG_BLOCK, &dgl_signal_blockmask, &dgl_signal_oldmask);
                    235:        dgl_signal_blocked = 1;
                    236:     }
                    237: }
                    238:
                    239: void
                    240: signals_release()
                    241: {
                    242:     if (dgl_signal_blocked) {
                    243:        sigprocmask(SIG_SETMASK, &dgl_signal_oldmask, NULL);
                    244:        dgl_signal_blocked = 0;
                    245:     }
                    246: }
                    247:
                    248:
                    249: /* ************************************************************* */
                    250:
                    251: char *
                    252: get_mainmenu_name()
                    253: {
                    254:     if (loggedin) {
                    255:        if (me && (me->flags & DGLACCT_ADMIN)) return "mainmenu_admin";
                    256:        return "mainmenu_user";
                    257:     }
                    258:     return "mainmenu_anon";
                    259: }
                    260:
                    261:
                    262: char*
                    263: gen_ttyrec_filename ()
                    264: {
                    265:   time_t rawtime;
                    266:   struct tm *ptm;
                    267:   char *ttyrec_filename = calloc(100, sizeof(char));
                    268:
                    269:   /* append time to filename */
                    270:   time (&rawtime);
                    271:   ptm = gmtime (&rawtime);
                    272:   snprintf (ttyrec_filename, 100, "%04i-%02i-%02i.%02i:%02i:%02i.ttyrec",
                    273:             ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday,
                    274:             ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
                    275:   return ttyrec_filename;
                    276: }
                    277:
                    278: /* ************************************************************* */
                    279:
                    280: char*
                    281: gen_inprogress_lock (int game, pid_t pid, char* ttyrec_filename)
                    282: {
                    283:   char *lockfile = NULL, filebuf[80];
                    284:   int fd;
                    285:   size_t len, wrlen;
                    286:   struct flock fl = { 0 };
                    287:
                    288:   snprintf (filebuf, sizeof(filebuf), "%d\n%d\n%d\n",
                    289:                  pid, win.ws_row, win.ws_col);
                    290:
                    291:   wrlen = strlen(filebuf);
                    292:
                    293:   fl.l_type = F_WRLCK;
                    294:   fl.l_whence = SEEK_SET;
                    295:   fl.l_start = 0;
                    296:   fl.l_len = 0;
                    297:
                    298:   len = strlen(dgl_format_str(game, me, myconfig[game]->inprogressdir, NULL)) + strlen(me->username) + strlen(ttyrec_filename) + 13;
                    299:   lockfile = calloc(len, sizeof(char));
                    300:
                    301:   snprintf (lockfile, len, "%s%s:%s", dgl_format_str(game, me, myconfig[game]->inprogressdir, NULL),
                    302:             me->username, ttyrec_filename);
                    303:
                    304:   fd = open (lockfile, O_WRONLY | O_CREAT, 0644);
                    305:   if (fcntl (fd, F_SETLKW, &fl) == -1) {
                    306:       debug_write("cannot fnctl inprogress-lock");
                    307:     graceful_exit (68);
                    308:   }
                    309:
                    310:   if (write (fd, filebuf, wrlen) != wrlen) {
                    311:       debug_write("inprogress-lock write");
                    312:       graceful_exit(70);
                    313:   }
                    314:
                    315:   return lockfile;
                    316: }
                    317:
                    318: /* ************************************************************* */
                    319:
                    320: #ifdef USE_SHMEM
                    321: int hup_shm_idx = -1;
                    322: char *hup_shm_ttyrec_fn = NULL;
                    323: #endif
                    324:
                    325: void
                    326: catch_sighup (int signum)
                    327: {
                    328:   if (child)
                    329:     {
                    330:       sleep (10);
                    331:       kill (child, SIGHUP);
                    332:       sleep (5);
                    333:     }
                    334: #ifdef USE_SHMEM
                    335:   signals_block();
                    336:   if (hup_shm_idx != -1) {
                    337:       struct dg_shm *shm_dg_data = NULL;
                    338:       struct dg_shm_game *shm_dg_game = NULL;
                    339:       shm_init(&shm_dg_data, &shm_dg_game);
                    340:
                    341:       shm_sem_wait(shm_dg_data);
                    342:       if (shm_dg_game[hup_shm_idx].in_use &&
                    343:          !strcmp(shm_dg_game[hup_shm_idx].ttyrec_fn, hup_shm_ttyrec_fn) &&
                    344:          (shm_dg_game[hup_shm_idx].nwatchers > 0)) {
                    345:          shm_dg_game[hup_shm_idx].nwatchers--;
                    346:       }
                    347:       shm_sem_post(shm_dg_data);
                    348:       hup_shm_idx = -1;
                    349:       free(hup_shm_ttyrec_fn);
                    350:   }
                    351:   signals_release();
                    352: #endif
                    353:   debug_write("catchup sighup");
                    354:   graceful_exit (7);
                    355: }
                    356:
                    357: /* ************************************************************* */
                    358:
                    359: int
                    360: dgl_getch(void)
                    361: {
                    362:     const int c = getch();
                    363:     idle_alarm_reset();
                    364:     return c;
                    365: }
                    366:
                    367: /* ************************************************************* */
                    368:
                    369: static void
                    370: dgl_idle_kill(int signal)
                    371: {
                    372:     kill(0, SIGHUP);
                    373: }
                    374:
                    375: void
                    376: idle_alarm_set_enabled(int enabled)
                    377: {
                    378:     signal(SIGALRM, SIG_IGN);
                    379:     g_idle_alarm_enabled = enabled;
                    380:     idle_alarm_reset();
                    381:     if (enabled)
                    382:         signal(SIGALRM, dgl_idle_kill);
                    383: }
                    384:
                    385: void
                    386: idle_alarm_reset(void)
                    387: {
                    388:     if (g_idle_alarm_enabled && globalconfig.menu_max_idle_time > 0)
                    389:         alarm(globalconfig.menu_max_idle_time);
                    390: }
                    391:
                    392: /* ************************************************************* */
                    393:
                    394:
                    395: char *
                    396: bannerstrmangle(char *buf, char *bufnew, int buflen, char *fromstr, char *tostr)
                    397: {
                    398:     char *loc;
                    399:     char *b = buf;
                    400:
                    401:     memset (bufnew, 0, buflen);
                    402:
                    403:     if (strstr(b, fromstr)) {
                    404:        int i = 0;
                    405:        while ((loc = strstr (b, fromstr)) != NULL) {
                    406:            for (; i < buflen; i++) {
                    407:                if (loc != b)
                    408:                    bufnew[i] = *(b++);
                    409:                else {
                    410:                    strlcat (bufnew, tostr, buflen);
                    411:                    b += strlen(fromstr);
                    412:                    i += strlen(tostr);
                    413:                    break;
                    414:                 }
                    415:
                    416:                if (strlen (b) == 0)
                    417:                    break;
                    418:            }
                    419:        }
                    420:
                    421:        if (*b)
                    422:            strlcat(bufnew, b, buflen);
                    423:     } else strncpy(bufnew, buf, buflen);
                    424:     return bufnew;
                    425: }
                    426:
                    427: void
                    428: banner_var_add(char *name, char *value, int special)
                    429: {
                    430:     struct dg_banner_var *tmp = (struct dg_banner_var *)malloc(sizeof(struct dg_banner_var));
                    431:
                    432:     if (!tmp) return;
                    433:
                    434:     tmp->name = strdup(name);
                    435:     tmp->value = strdup(value);
                    436:     tmp->special = special;
                    437:     tmp->next = globalconfig.banner_var_list;
                    438:     globalconfig.banner_var_list = tmp;
                    439: }
                    440:
                    441: void
                    442: banner_var_free()
                    443: {
                    444:     struct dg_banner_var *tmp;
                    445:     struct dg_banner_var *bv = globalconfig.banner_var_list;
                    446:     while (bv) {
                    447:        tmp = bv->next;
                    448:        free(bv->name);
                    449:        free(bv->value);
                    450:        free(bv);
                    451:        bv = tmp;
                    452:     }
                    453:     globalconfig.banner_var_list = NULL;
                    454: }
                    455:
                    456: char *
                    457: banner_var_resolve(struct dg_banner_var *bv)
                    458: {
                    459:   static char tmpbuf[DGL_BANNER_LINELEN+1];
                    460:   time_t tstamp;
                    461:   struct tm *ptm;
                    462:   if (!bv) return NULL;
                    463:   if (!bv->special) return bv->value;
                    464:   time(&tstamp);
                    465:   ptm = gmtime(&tstamp);
                    466:   strftime(tmpbuf, DGL_BANNER_LINELEN, bv->value, ptm);
                    467:   return tmpbuf;
                    468: }
                    469:
                    470: char *
                    471: banner_var_value(char *name)
                    472: {
                    473:     struct dg_banner_var *bv = globalconfig.banner_var_list;
                    474:     while (bv) {
                    475:        if (!strcmp(bv->name, name)) return banner_var_resolve(bv);
                    476:        bv = bv->next;
                    477:     }
                    478:     return NULL;
                    479: }
                    480:
                    481: void
                    482: freebanner(struct dg_banner *ban)
                    483: {
                    484:     unsigned int l;
                    485:     if (!ban) return;
                    486:     l = ban->len;
                    487:
                    488:     while (l > 0) {
                    489:        l--;
                    490:        free(ban->lines[l]);
                    491:     }
                    492:     free(ban->lines);
                    493:     ban->len = 0;
                    494:     ban->lines = NULL;
                    495: }
                    496:
                    497: void
                    498: banner_addline(struct dg_banner *ban, char *line)
                    499: {
                    500:     size_t len = strlen(line);
                    501:     if (!ban) return;
                    502:     ban->len++;
                    503:     ban->lines = realloc (ban->lines, sizeof (char *) * ban->len);
                    504:     if (len >= DGL_BANNER_LINELEN) {
                    505:        len = DGL_BANNER_LINELEN;
                    506:        ban->lines[ban->len - 1] = malloc(len);
                    507:        strncpy(ban->lines[ban->len - 1], line, len);
                    508:        ban->lines[ban->len - 1][len-1] = '\0';
                    509:     } else
                    510:        ban->lines[ban->len - 1] = strdup(line);
                    511: }
                    512:
                    513: void
                    514: loadbanner (char *fname, struct dg_banner *ban)
                    515: {
                    516:   FILE *bannerfile;
                    517:   char buf[DGL_BANNER_LINELEN+1];
                    518:   if (ban->len > 23) return;
                    519:
                    520:   memset (buf, 0, DGL_BANNER_LINELEN);
                    521:
                    522:   bannerfile = fopen (fname, "r");
                    523:
                    524:   if (!bannerfile)
                    525:     {
                    526:        if (ban->len == 0)
                    527:            banner_addline(ban, "### dgamelaunch " PACKAGE_VERSION " - network console game launcher");
                    528:        snprintf(buf, DGL_BANNER_LINELEN, "### NOTE: administrator has not installed a %s file", fname);
                    529:        banner_addline(ban, buf);
                    530:        return;
                    531:     }
                    532:
                    533:   while (fgets (buf, DGL_BANNER_LINELEN, bannerfile) != NULL)
                    534:     {
                    535:       char bufnew[DGL_BANNER_LINELEN+1];
                    536:       int slen;
                    537:
                    538:       memset (bufnew, 0, DGL_BANNER_LINELEN);
                    539:
                    540:       slen = strlen(buf);
                    541:       if ((slen > 0) && (buf[slen-1] == '\n')) buf[slen-1] = '\0';
                    542:
                    543:       strncpy(bufnew, buf, DGL_BANNER_LINELEN);
                    544:       if (strstr(bufnew, "$INCLUDE(")) {
                    545:          char *fn = bufnew + 9;
                    546:          char *fn_end = strchr(fn, ')');
                    547:          if (fn_end) {
                    548:              *fn_end = '\0';
                    549:              if (strcmp(fname, fn)) {
                    550:                  loadbanner(fn, ban);
                    551:              }
                    552:          }
                    553:       } else {
                    554:          char tmpbufnew[DGL_BANNER_LINELEN+1];
                    555:          struct dg_banner_var *bv = globalconfig.banner_var_list;
                    556:          while (bv) {
                    557:              strncpy(bufnew, bannerstrmangle(bufnew, tmpbufnew, DGL_BANNER_LINELEN, bv->name, banner_var_resolve(bv)), DGL_BANNER_LINELEN);
                    558:              bv = bv->next;
                    559:          }
                    560:          strncpy(bufnew, bannerstrmangle(bufnew, tmpbufnew, DGL_BANNER_LINELEN, "$VERSION", PACKAGE_STRING), DGL_BANNER_LINELEN);
                    561:          if (me && loggedin) {
                    562:              strncpy(bufnew, bannerstrmangle(bufnew, tmpbufnew, DGL_BANNER_LINELEN, "$USERNAME", me->username), DGL_BANNER_LINELEN);
                    563:          } else {
                    564:              strncpy(bufnew, bannerstrmangle(bufnew, tmpbufnew, DGL_BANNER_LINELEN, "$USERNAME", "[Anonymous]"), DGL_BANNER_LINELEN);
                    565:          }
                    566:          banner_addline(ban, bufnew);
                    567:       }
                    568:
                    569:       memset (buf, 0, DGL_BANNER_LINELEN);
                    570:
                    571:       if (ban->len >= 24)
                    572:          break;
                    573:   }
                    574:
                    575:   fclose (bannerfile);
                    576: }
                    577:
                    578: void
                    579: drawbanner (struct dg_banner *ban)
                    580: {
                    581:   unsigned int i;
                    582:   char *tmpch, *tmpch2, *splch;
                    583:   int attr = 0, oattr = 0;
                    584:
                    585:   if (!ban) return;
                    586:
                    587:   for (i = 0; i < ban->len; i++) {
                    588:       char *tmpbuf = strdup(ban->lines[i]);
                    589:       char *tmpbuf2 = tmpbuf;
                    590:       int ok = 0;
                    591:       int x = 1;
                    592:       do {
                    593:          ok = 0;
                    594:          if ((tmpch = strstr(tmpbuf2, "$ATTR("))) {
                    595:              if ((tmpch2 = strstr(tmpch, ")"))) {
                    596:                  int spl = 0;
                    597:                  char *nxttmpch;
                    598:                  ok = 1;
                    599:                  oattr = attr;
                    600:                  attr = A_NORMAL;
                    601:                  *tmpch = *tmpch2 = '\0';
                    602:                  tmpch += 6;
                    603:                  nxttmpch = tmpch;
                    604:                  do {
                    605:                      spl = 0;
                    606:                      splch = strchr(tmpch, ';');
                    607:                      if (splch && *splch) {
                    608:                          spl = 1;
                    609:                          nxttmpch = splch;
                    610:                          *nxttmpch = '\0';
                    611:                          nxttmpch++;
                    612:                      }
                    613:                      if (tmpch && *tmpch) {
                    614:                          switch (*tmpch) {
                    615:                          default: break;
                    616:                          case '0': case '1': case '2': case '3': case '4':
                    617:                          case '5': case '6': case '7': case '8': case '9':
                    618:                              {
                    619:                                  int num = atoi(tmpch);
                    620:                                  if (num >= 0 && num <= 15)
                    621:                                      attr |= color_remap[num];
                    622:                              }
                    623:                              break;
                    624:                          case 'b': attr |= A_BOLD; break;
                    625:                          case 's': attr |= A_STANDOUT; break;
                    626:                          case 'u': attr |= A_UNDERLINE; break;
                    627:                          case 'r': attr |= A_REVERSE; break;
                    628:                          case 'd': attr |= A_DIM; break;
                    629:                          }
                    630:                      } else attr = A_NORMAL;
                    631:                      tmpch = nxttmpch;
                    632:                  } while (spl);
                    633:
                    634:                  mvaddstr(1 + i, x, tmpbuf2);
                    635:                  if (oattr) attroff(oattr);
                    636:                  if (attr) attron(attr);
                    637:                  x += strlen(tmpbuf2);
                    638:                  tmpch2++;
                    639:                  tmpbuf2 = tmpch2;
                    640:              } else
                    641:                  mvaddstr (1 + i, x, tmpbuf2);
                    642:          } else
                    643:              mvaddstr (1 + i, x, tmpbuf2);
                    644:       } while (ok);
                    645:       free(tmpbuf);
                    646:   }
                    647: }
                    648:
                    649: void
                    650: shm_sem_wait(struct dg_shm *shm_dg_data)
                    651: {
                    652: #ifdef USE_SHMEM
                    653:     if (sem_wait(&(shm_dg_data->dg_sem)) == -1) {
                    654:        debug_write("sem_wait");
                    655:        graceful_exit(77);
                    656:     }
                    657: #endif
                    658: }
                    659:
                    660: void
                    661: shm_sem_post(struct dg_shm *shm_dg_data)
                    662: {
                    663: #ifdef USE_SHMEM
                    664:     if (sem_post(&(shm_dg_data->dg_sem)) == -1) {
                    665:        debug_write("sem_post");
                    666:        graceful_exit(78);
                    667:     }
                    668: #endif
                    669: }
                    670:
                    671: void
                    672: shm_update(struct dg_shm *shm_dg_data, struct dg_game **games, int len)
                    673: {
                    674: #ifdef USE_SHMEM
                    675:     int di, i;
                    676:     struct dg_shm_game *shm_dg_game = (struct dg_shm_game *)(shm_dg_data + sizeof(struct dg_shm));
                    677:
                    678:     signals_block();
                    679:     shm_sem_wait(shm_dg_data);
                    680:
                    681:     for (di = 0; di < shm_dg_data->max_n_games; di++)
                    682:        if (shm_dg_game[di].in_use) {
                    683:            int delgame = 1;
                    684:            for (i = 0; i < len; i++) {
                    685:                if (!strcmp(games[i]->ttyrec_fn, shm_dg_game[di].ttyrec_fn)) {
                    686:                    delgame = 0;
                    687:                    games[i]->is_in_shm = 1;
                    688:                    games[i]->shm_idx = di;
                    689:                    games[i]->nwatchers = shm_dg_game[di].nwatchers;
                    690:                    break;
                    691:                }
                    692:            }
                    693:            if (delgame) {
                    694:                shm_dg_game[di].in_use = 0;
                    695:                if (shm_dg_data->cur_n_games > 0) shm_dg_data->cur_n_games--;
                    696:            }
                    697:        }
                    698:
                    699:     if (shm_dg_data->cur_n_games < shm_dg_data->max_n_games) {
                    700:        for (i = 0; i < len; i++)
                    701:            if (!games[i]->is_in_shm) {
                    702:                for (di = 0; di < shm_dg_data->max_n_games; di++)
                    703:                    if (!shm_dg_game[di].in_use) {
                    704:                        shm_dg_game[di].in_use = 1;
                    705:                        shm_dg_game[di].nwatchers = 0;
                    706:                        games[i]->nwatchers = 0;
                    707:                        games[i]->is_in_shm = 1;
                    708:                        games[i]->shm_idx = di;
                    709:                        shm_dg_data->cur_n_games++;
                    710:                        strncpy(shm_dg_game[di].ttyrec_fn, games[i]->ttyrec_fn, 150);
                    711:                        break;
                    712:                    }
                    713:            }
                    714:     }
                    715:
                    716:     shm_sem_post(shm_dg_data);
                    717:     signals_release();
                    718: #endif
                    719: }
                    720:
                    721: void
                    722: shm_mk_keys(key_t *shm_key, key_t *shm_sem_key)
                    723: {
                    724: #ifdef USE_SHMEM
                    725:     if ((*shm_key = ftok(globalconfig.passwd, 'R')) == -1) {
                    726:        debug_write("ftok shm_key");
                    727:        graceful_exit(71);
                    728:     }
                    729:     if ((*shm_sem_key = ftok(globalconfig.passwd, 'S')) == -1) {
                    730:        debug_write("ftok shm_sem_key");
                    731:        graceful_exit(72);
                    732:     }
                    733: #endif
                    734: }
                    735:
                    736: #ifdef USE_SHMEM
                    737: int
                    738: shm_free()
                    739: {
                    740:     key_t shm, sem;
                    741:     int   shm_id;
                    742:     int shm_size = sizeof(struct dg_shm) + shm_n_games * sizeof(struct dg_shm_game);
                    743:     shm_mk_keys(&shm, &sem);
                    744:     if ((shm_id = shmget(shm, shm_size, 0644)) != -1) {
                    745:        shmctl(shm_id, IPC_RMID, NULL);
                    746:        return 0;
                    747:     }
                    748:     return 1;
                    749: }
                    750: #endif
                    751:
                    752: void
                    753: shm_init(struct dg_shm **shm_dg_data, struct dg_shm_game **shm_dg_game)
                    754: {
                    755: #ifdef USE_SHMEM
                    756:   key_t shm_key;
                    757:   key_t shm_sem_key;
                    758:   int   shm_id;
                    759:   int   shm_size;
                    760:   void *shm_data = NULL;
                    761:   int   shm_data_existed = 0;
                    762:
                    763:   shm_mk_keys(&shm_key, &shm_sem_key);
                    764:
                    765:   /* max. shm_n_games simultaneous games recorded in the shared memory */
                    766:   shm_size = sizeof(struct dg_shm) + shm_n_games * sizeof(struct dg_shm_game);
                    767:
                    768:   /* connect to (and possibly create) the segment */
                    769:   if ((shm_id = shmget(shm_key, shm_size, 0644 | IPC_CREAT | IPC_EXCL)) == -1) {
                    770:       /* creation failed, so it already exists. attach to it */
                    771:       shm_data_existed = 1;
                    772:       if ((shm_id = shmget(shm_key, shm_size, 0644)) == -1) {
                    773:          debug_write("shmget");
                    774:          graceful_exit(73);
                    775:       }
                    776:   }
                    777:
                    778:   /* attach to the segment to get a pointer to it: */
                    779:   shm_data = shmat(shm_id, (void *)0, 0);
                    780:   if (shm_data == (char *)(-1)) {
                    781:       debug_write("shmat");
                    782:       graceful_exit(74);
                    783:   }
                    784:   if (!shm_data) {
                    785:       debug_write("shm_data == null");
                    786:       graceful_exit(75);
                    787:   }
                    788:
                    789:   (*shm_dg_data) = (struct dg_shm *)shm_data;
                    790:   (*shm_dg_game) = (struct dg_shm_game *)((*shm_dg_data) + sizeof(struct dg_shm));
                    791:
                    792:   if (!shm_data_existed && shm_data) {
                    793:       memset(*shm_dg_game, 0, shm_n_games*sizeof(struct dg_shm_game));
                    794:       (*shm_dg_data)->max_n_games = shm_n_games;
                    795:       (*shm_dg_data)->cur_n_games = 0;
                    796:       if (sem_init(&((*shm_dg_data)->dg_sem), 1,1) == -1) {
                    797:          debug_write("sem_init");
                    798:          graceful_exit(76);
                    799:       }
                    800:   }
                    801: #endif /* USE_SHMEM */
                    802: }
                    803:
                    804: #ifdef USE_SHMEM
                    805: void
                    806: shm_dump()
                    807: {
                    808:     struct dg_shm *shm_dg_data = NULL;
                    809:     struct dg_shm_game *shm_dg_game = NULL;
                    810:     int di, unused = -1;
                    811:     shm_init(&shm_dg_data, &shm_dg_game);
                    812:     shm_sem_wait(shm_dg_data);
                    813:     for (di = 0; di < shm_dg_data->max_n_games; di++) {
                    814:        if (shm_dg_game[di].in_use) {
                    815:            if (unused != -1) {
                    816:                if (unused != di-1)
                    817:                    fprintf(stderr, "%i-%i:\tunused\n", unused, di-1);
                    818:                else
                    819:                    fprintf(stderr, "%i:\tunused\n", unused);
                    820:                unused = -1;
                    821:            }
                    822:            fprintf(stderr, "%i:\t\"%s\"\twatchers:%li\n", di, shm_dg_game[di].ttyrec_fn, shm_dg_game[di].nwatchers);
                    823:        } else {
                    824:            if (unused == -1) unused = di;
                    825:        }
                    826:     }
                    827:     if (unused != -1) {
                    828:        if (unused != di-1)
                    829:            fprintf(stderr, "%i-%i:\tunused\n", unused, di-1);
                    830:        else
                    831:            fprintf(stderr, "%i:\tunused\n", unused);
                    832:        unused = -1;
                    833:     }
                    834:     shm_sem_post(shm_dg_data);
                    835:     shmdt(shm_dg_data);
                    836: }
                    837: #endif
                    838:
                    839: static
                    840: struct dg_watchcols **
                    841: globalconfig_watch_columns()
                    842: {
                    843:     if (globalconfig.n_watch_columns)
                    844:         return globalconfig.watch_columns;
                    845:
                    846:     if (!*default_watchcols_list) {
                    847:         int i;
                    848:         for (i = 0; i < ARRAY_SIZE(default_watchcols); ++i)
                    849:             default_watchcols_list[i] = &default_watchcols[i];
                    850:     }
                    851:     return default_watchcols_list;
                    852: }
                    853:
                    854: static
                    855: int
                    856: watchcol_find_index(struct dg_watchcols **watchcols,
                    857:                     int sortmode)
                    858: {
                    859:     int i;
                    860:     for (i = 0; watchcols[i]; ++i)
                    861:         if (watchcols[i]->sortmode == sortmode)
                    862:             return i;
                    863:     return -1;
                    864: }
                    865:
                    866: static
                    867: void
                    868: sortmode_increment(struct dg_watchcols **watchcols,
                    869:                    dg_sortmode *sortmode,
                    870:                    int direction)
                    871: {
                    872:     int watch_column_index = watchcol_find_index(watchcols, *sortmode);
                    873:     int n_watchcols;
                    874:     int wrap_count = 0;
                    875:     const dg_sortmode old_sortmode = *sortmode;
                    876:
                    877:     for (n_watchcols = 0; watchcols[n_watchcols]; ++n_watchcols)
                    878:         ;
                    879:
                    880:     if (watch_column_index == -1 || !n_watchcols)
                    881:         return;
                    882:
                    883:     do {
                    884:         watch_column_index += direction;
                    885:
                    886:         if (watch_column_index < 0) {
                    887:             watch_column_index = n_watchcols - 1;
                    888:             ++wrap_count;
                    889:         } else if (watch_column_index >= n_watchcols) {
                    890:             watch_column_index = 0;
                    891:             ++wrap_count;
                    892:         }
                    893:
                    894:         *sortmode = watchcols[watch_column_index]->sortmode;
                    895:     } while (wrap_count < 2 && !*sortmode);
                    896:
                    897:     if (!*sortmode)
                    898:         *sortmode = old_sortmode;
                    899: }
                    900:
                    901: char *
                    902: get_timediff(time_t ctime, long seconds)
                    903: {
                    904:     static char data[32];
                    905:     long secs, mins, hours;
                    906:
                    907:     secs = (ctime - seconds);
                    908:
                    909:     if (showplayers) {
                    910:        snprintf(data, 10, "%ld", secs);
                    911:        return data;
                    912:     }
                    913:
                    914:     hours = (secs / 3600);
                    915:     secs -= (hours * 3600);
                    916:     mins = (secs / 60) % 60;
                    917:     secs -= (mins*60);
                    918:     if (hours)
                    919:        snprintf(data, 10, "%ldh %ldm", hours, mins);
                    920:     else if (mins)
                    921:        snprintf(data, 10, "%ldm %lds", mins, secs);
                    922:     else if (secs > 4)
                    923:        snprintf(data, 30, "%lds", secs);
                    924:     else
                    925:        snprintf(data, 10, " ");
                    926:     return data;
                    927: }
                    928:
                    929: static
                    930: void
                    931: game_get_column_data(struct dg_game *game,
                    932:                      char selectorchar,
                    933:                      time_t ctime, struct dg_shm_game *shm_dg_game,
                    934:                      char *data, int bufsz, int *hilite,
                    935:                      dg_sortmode which_data)
                    936: {
                    937:     *data = 0;
                    938:
                    939:     switch (which_data) {
                    940:     default: break;
                    941:     case SORTMODE_NONE:
                    942:         data[0] = selectorchar; data[1] = '\0';
                    943:         break;
                    944:
                    945:     case SORTMODE_USERNAME:
                    946:         snprintf(data, bufsz, "%s", game->name);
                    947:         break;
                    948:
                    949:     case SORTMODE_GAMENUM:
                    950:         snprintf(data, bufsz, "%s",
                    951:                  myconfig[game->gamenum]->shortname);
                    952:         break;
                    953:
                    954:     case SORTMODE_WINDOWSIZE:
                    955:         snprintf(data, bufsz, "%3dx%3d", game->ws_col, game->ws_row);
                    956:        if (showplayers)
                    957:                snprintf(data, bufsz, "%dx%d", game->ws_col, game->ws_row);
                    958:        else
                    959:                snprintf(data, bufsz, "%3dx%3d", game->ws_col, game->ws_row);
                    960:         if ((game->ws_col > COLS || game->ws_row > LINES))
                    961:             *hilite = CLR_RED;
                    962:         break;
                    963:
                    964:     case SORTMODE_STARTTIME:
                    965:         snprintf(data, bufsz, "%s %s", game->date,
                    966:                  game->time);
                    967:         break;
                    968:
                    969:     case SORTMODE_DURATION:
                    970:        {
                    971:            /* TODO: populate_games() should put st_ctime into game struct */
                    972:            struct tm timetm;
                    973:            char tmptimebuf[32];
                    974:            snprintf(tmptimebuf, 30, "%s %s", game->date, game->time);
                    975:            tmptimebuf[31] = '\0';
                    976:            strptime(tmptimebuf, "%Y-%m-%d %H:%M:%S", &timetm);
                    977:            snprintf(data, 10, "%s", get_timediff(ctime, mktime(&timetm)));
                    978:        }
                    979:        break;
                    980:
                    981:     case SORTMODE_IDLETIME:
                    982:        snprintf(data, 10, "%s", get_timediff(ctime, game->idle_time));
                    983:         break;
                    984:
                    985:     case SORTMODE_EXTRA_INFO:
                    986:         if (game->extra_info)
                    987:             strlcpy(data, game->extra_info, bufsz);
                    988:         break;
                    989:
                    990: #ifdef USE_SHMEM
                    991:     case SORTMODE_WATCHERS:
                    992:         snprintf(data, bufsz, "%li",
                    993:                  (game->is_in_shm ?
                    994:                   shm_dg_game[game->shm_idx].nwatchers : -1));
                    995:         break;
                    996: #endif
                    997:     }
                    998:     data[bufsz - 1] = '\0';
                    999: }
                   1000:
                   1001: void
                   1002: inprogressmenu (int gameid)
                   1003: {
                   1004:     const char *selectorchars = "abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ";
                   1005:   int i, menuchoice, len = 20, offset = 0;
                   1006:   static dg_sortmode sortmode = NUM_SORTMODES;
                   1007:   struct dg_game **games = NULL;
                   1008:   char ttyrecname[130], gametype[10], idletime[10];
                   1009:   sigset_t oldmask, toblock;
                   1010:   int idx = -1;
                   1011:   int shm_idx = -1;
                   1012:   int max_height = -1;
                   1013:   int selected = -1;
                   1014:
                   1015:   int resizex = -1;
                   1016:   int resizey = -1;
                   1017:
                   1018:   char *selectedgame = NULL;
                   1019:
                   1020:   int abs_max_height;
                   1021:   int top_banner_hei = 5;
                   1022:   int btm_banner_hei = 3;
                   1023:   int btm;
                   1024:
                   1025:   int title_attr = A_STANDOUT;
                   1026:   int selected_attr = A_BOLD;
                   1027:
                   1028:   int require_enter = 0; /* TODO: make configurable */
                   1029:
                   1030:   time_t ctime;
                   1031:
                   1032:   struct dg_shm *shm_dg_data = NULL;
                   1033:   struct dg_shm_game *shm_dg_game = NULL;
                   1034:
                   1035:   struct dg_watchcols **watchcols = globalconfig_watch_columns();
                   1036:   struct dg_watchcols **curr_watchcol;
                   1037:
                   1038:   if (sortmode == NUM_SORTMODES)
                   1039:       sortmode = globalconfig.sortmode;
                   1040:
                   1041:   abs_max_height = strlen(selectorchars);
                   1042:
                   1043:   shm_init(&shm_dg_data, &shm_dg_game);
                   1044:
                   1045:   games = populate_games (gameid, &len, NULL); /* FIXME: should be 'me' instead of 'NULL' */
                   1046:   shm_update(shm_dg_data, games, len);
                   1047:   games = sort_games (games, len, sortmode);
                   1048:
                   1049:   while (1)
                   1050:     {
                   1051:        term_resize_check();
                   1052:        max_height = dgl_local_LINES - (top_banner_hei + btm_banner_hei) - 1;
                   1053:        if (max_height < 2) {
                   1054:            free_populated_games(games, len);
                   1055:            return;
                   1056:        }
                   1057:        if (max_height > abs_max_height) max_height = abs_max_height;
                   1058:
                   1059:       if (len == 0)
                   1060:         offset = 0;
                   1061:
                   1062:       erase ();
                   1063:       drawbanner (&banner);
                   1064:
                   1065:       if (len > 0) {
                   1066:          while (offset >= len) { offset -= max_height; }
                   1067:          if (offset < 0) offset = 0;
                   1068:          mvaddstr (3, 1, "The following games are in progress:");
                   1069:
                   1070:          for (curr_watchcol = watchcols; *curr_watchcol; ++curr_watchcol) {
                   1071:               struct dg_watchcols *wcol = *curr_watchcol;
                   1072:              char *col = wcol->colname;
                   1073:              int x = wcol->x;
                   1074:              while (*col == ' ') { x++; col++; }
                   1075:              if (sortmode == wcol->sortmode) attron(title_attr);
                   1076:              mvprintw(top_banner_hei, x, col);
                   1077:              if (sortmode == wcol->sortmode) attroff(title_attr);
                   1078:          }
                   1079:       }
                   1080:
                   1081:       signals_block();
                   1082:       shm_sem_wait(shm_dg_data);
                   1083:
                   1084:       (void) time(&ctime);
                   1085:
                   1086:       for (i = 0; i < max_height; i++)
                   1087:         {
                   1088:           if (i + offset >= len)
                   1089:             break;
                   1090:
                   1091:          if (i + offset == selected) attron(selected_attr);
                   1092:
                   1093:          for (curr_watchcol = watchcols; *curr_watchcol; ++curr_watchcol) {
                   1094:               struct dg_watchcols *col = *curr_watchcol;
                   1095:              char tmpbuf[80];
                   1096:              int hilite = 0;
                   1097:               game_get_column_data(games[i + offset],
                   1098:                                    selectorchars[i],
                   1099:                                    ctime, shm_dg_game,
                   1100:                                    tmpbuf, sizeof tmpbuf, &hilite,
                   1101:                                    (dg_sortmode)col->dat);
                   1102:              if (hilite) attron(hilite);
                   1103:              mvprintw(top_banner_hei + 1 + i, col->x, col->fmt, tmpbuf);
                   1104:              if (hilite) {
                   1105:                  attron(CLR_NORMAL);
                   1106:                  hilite = 0;
                   1107:              }
                   1108:          }
                   1109:
                   1110:          if (i + offset == selected) attroff(selected_attr);
                   1111:
                   1112:         }
                   1113:
                   1114:       shm_sem_post(shm_dg_data);
                   1115:       signals_release();
                   1116:
                   1117:       btm = dgl_local_LINES-btm_banner_hei-top_banner_hei;
                   1118:       if (len <= max_height)
                   1119:          btm = i+1;
                   1120:
                   1121:       if (len > 0) {
                   1122:          mvprintw ((btm+top_banner_hei), 1, "(%d-%d of %d)", offset + 1, offset + i, len);
                   1123:          mvaddstr ((btm+2+top_banner_hei), 1, "Watch which game? ('?' for help) => ");
                   1124:       } else {
                   1125:          mvprintw(top_banner_hei,4,"Sorry, no games available for viewing.");
                   1126:          mvaddstr((btm+2+top_banner_hei), 1, "Press 'q' to return, or '?' for help => ");
                   1127:       }
                   1128:
                   1129:       refresh ();
                   1130:
                   1131:       switch ((menuchoice = dgl_getch ()))
                   1132:         {
                   1133:        case KEY_DOWN:
                   1134:            selected++;
                   1135:            if (selected >= len) selected = 0;
                   1136:            while (selected < offset) offset -= max_height;
                   1137:            while (selected >= offset+max_height) offset += max_height;
                   1138:            break;
                   1139:        case KEY_UP:
                   1140:            if (selected != -1) {
                   1141:                if (selected == 0) selected = len;
                   1142:                selected--;
                   1143:            } else selected = len-1;
                   1144:            while (selected < offset) offset -= max_height;
                   1145:            while (selected >= offset+max_height) offset += max_height;
                   1146:            break;
                   1147:        case '*':
                   1148:            if (len > 0) {
                   1149:                int cnt = 20;
                   1150:                (void) time(&ctime);
                   1151:                do {
                   1152:                    idx = random() % len;
                   1153:                } while ((--cnt > 0) ||
                   1154:                         !((games[idx]->ws_col <= COLS) &&
                   1155:                          (games[idx]->ws_row <= LINES) &&
                   1156:                          ((ctime - games[idx]->idle_time) < 15)));
                   1157:                selected = idx;
                   1158:                goto watchgame;
                   1159:            }
                   1160:            break;
                   1161:        case '?':
                   1162:            (void) runmenuloop(dgl_find_menu("watchmenu_help"));
                   1163:            break;
                   1164:        case '/':
                   1165:            {
                   1166:                int match = -1;
                   1167:               int firstmatch = -1;
                   1168:               int nmatches = 0;
                   1169:                char findname[DGL_PLAYERNAMELEN+1];
                   1170:               if (len <= 0) break;
                   1171:                findname[0] = '\0';
                   1172:               mvprintw ((btm+2+top_banner_hei), 1, "Watch which player? =>                 "); /* stupid... */
                   1173:               mvaddstr ((btm+2+top_banner_hei), 1, "Watch which player? => ");
                   1174:                if ((mygetnstr(findname, DGL_PLAYERNAMELEN, 1) == OK) && (strlen(findname) > 1)) {
                   1175:                   int mlen = strlen(findname);
                   1176:                    for (i = 0; i < len; i++)
                   1177:                        if (!strncasecmp(games[i]->name, findname, mlen)) {
                   1178:                           if (firstmatch == -1) firstmatch = i;
                   1179:                            match = i;
                   1180:                           nmatches++;
                   1181:                        }
                   1182:                   if (nmatches > 1)
                   1183:                       match = firstmatch;
                   1184:                    if (match > -1) {
                   1185:                       idx = match;
                   1186:                       selected = idx;
                   1187:                       goto watchgame;
                   1188:                    }
                   1189:                }
                   1190:            }
                   1191:            break;
                   1192:        case KEY_NPAGE:
                   1193:         case '>':
                   1194:            if ((offset + max_height) < len) offset += max_height;
                   1195:           break;
                   1196:        case KEY_PPAGE:
                   1197:         case '<':
                   1198:           if ((offset - max_height) < 0)
                   1199:              offset = 0;
                   1200:           else
                   1201:             offset -= max_height;
                   1202:           break;
                   1203:
                   1204:        case ERR:
                   1205:        case 'q': case 'Q':
                   1206:         case '\x1b':
                   1207:            goto leavewatchgame;
                   1208:        case KEY_RIGHT:
                   1209:        case '.':
                   1210:             sortmode_increment(watchcols, &sortmode, 1);
                   1211:            break;
                   1212:        case KEY_LEFT:
                   1213:        case ',':
                   1214:             sortmode_increment(watchcols, &sortmode, -1);
                   1215:            break;
                   1216:
                   1217:        case 12: case 18: /* ^L, ^R */
                   1218:           if (globalconfig.utf8esc) (void) write(1, "\033%G", 3);
                   1219:          clear ();
                   1220:          break;
                   1221:
                   1222:        case 13:
                   1223:        case 10:
                   1224:        case KEY_ENTER:
                   1225:            if (selected >= 0 && selected < len) {
                   1226:                idx = selected;
                   1227:                goto watchgame;
                   1228:            }
                   1229:            break;
                   1230:
                   1231:         default:
                   1232:            if (strchr(selectorchars, menuchoice) && (len > 0)) {
                   1233:                int sidx = strchr(selectorchars, menuchoice) - selectorchars;
                   1234:
                   1235:                if ((sidx > max_height) || (sidx >= len)) {
                   1236:                    selected = -1;
                   1237:                    break;
                   1238:                }
                   1239:
                   1240:              idx = sidx + offset;
                   1241:              if (require_enter) {
                   1242:                  if (selected == idx) selected = -1;
                   1243:                  else selected = idx;
                   1244:                  break;
                   1245:              } else selected = idx;
                   1246: watchgame:
                   1247:              if (!(idx >= 0 && idx < len && games[idx] && games[idx]->name))
                   1248:                  goto leavewatchgame;
                   1249:               /* valid choice has been made */
                   1250:               chosen_name = strdup (games[idx]->name);
                   1251:               snprintf (ttyrecname, 130, "%s",
                   1252:                         games[idx]->ttyrec_fn);
                   1253:
                   1254:               clear ();
                   1255:               refresh ();
                   1256:               endwin ();
                   1257:              if (globalconfig.utf8esc) (void) write(1, "\033%G", 3);
                   1258: #ifdef USE_SHMEM
                   1259:              signals_block();
                   1260:              if (games[idx]->is_in_shm) {
                   1261:                  shm_idx = games[idx]->shm_idx;
                   1262:                  shm_sem_wait(shm_dg_data);
                   1263:                  if (shm_dg_game[shm_idx].in_use &&
                   1264:                      !strcmp(shm_dg_game[shm_idx].ttyrec_fn, games[idx]->ttyrec_fn)) {
                   1265:                      shm_dg_game[shm_idx].nwatchers++;
                   1266:                      games[idx]->nwatchers++;
                   1267:                  }
                   1268:                  hup_shm_idx = shm_idx;
                   1269:                  hup_shm_ttyrec_fn = strdup(games[idx]->ttyrec_fn);
                   1270:                  shm_sem_post(shm_dg_data);
                   1271:              }
                   1272:              signals_release();
                   1273: #endif
                   1274:              resizey = games[idx]->ws_row;
                   1275:              resizex = games[idx]->ws_col;
                   1276:              if (loggedin)
                   1277:                  setproctitle("%s [watching %s]", me->username, chosen_name);
                   1278:              else
                   1279:                  setproctitle("<Anonymous> [watching %s]", chosen_name);
                   1280:               ttyplay_main (ttyrecname, 1, resizex, resizey);
                   1281:              if (loggedin)
                   1282:                  setproctitle("%s", me->username);
                   1283:              else
                   1284:                  setproctitle("<Anonymous>");
                   1285: #ifdef USE_SHMEM
                   1286:              signals_block();
                   1287:              if (games[idx]->is_in_shm) {
                   1288:                  hup_shm_idx = -1;
                   1289:                  free(hup_shm_ttyrec_fn);
                   1290:                  shm_sem_wait(shm_dg_data);
                   1291:                  if (shm_dg_game[shm_idx].in_use &&
                   1292:                      !strcmp(shm_dg_game[shm_idx].ttyrec_fn, games[idx]->ttyrec_fn) &&
                   1293:                      (shm_dg_game[shm_idx].nwatchers > 0)) {
                   1294:                      shm_dg_game[shm_idx].nwatchers--;
                   1295:                      games[idx]->nwatchers--;
                   1296:                  }
                   1297:                  shm_sem_post(shm_dg_data);
                   1298:              }
                   1299:              signals_release();
                   1300: #endif
                   1301:               initcurses ();
                   1302:              redrawwin(stdscr);
                   1303:             }
                   1304:         }
                   1305:
                   1306:       if (selected >= 0 && selected < len)
                   1307:          selectedgame = strdup(games[selected]->name);
                   1308:       games = populate_games (gameid, &len, NULL); /* FIXME: should be 'me' instead of 'NULL' */
                   1309:       shm_update(shm_dg_data, games, len);
                   1310:       games = sort_games (games, len, sortmode);
                   1311:       if (selectedgame) {
                   1312:          selected = -1;
                   1313:          for (i = 0; i < len; i++)
                   1314:              if (!strcmp(games[i]->name, selectedgame)) {
                   1315:                  selected = i;
                   1316:                  break;
                   1317:              }
                   1318:          free(selectedgame);
                   1319:          selectedgame = NULL;
                   1320:       }
                   1321:     }
                   1322: leavewatchgame:
                   1323:   free_populated_games(games, len);
                   1324: #ifdef USE_SHMEM
                   1325:   shmdt(shm_dg_data);
                   1326: #endif
                   1327: }
                   1328:
                   1329: void
                   1330: inprogressdisplay (int gameid)
                   1331: {
                   1332:     const char *selectorchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ";
                   1333:   int i, len = 20;
                   1334:   static dg_sortmode sortmode = NUM_SORTMODES;
                   1335:   struct dg_game **games = NULL;
                   1336:   int shm_idx = -1;
                   1337:
                   1338:   time_t ctime;
                   1339:
                   1340:   struct dg_shm *shm_dg_data = NULL;
                   1341:   struct dg_shm_game *shm_dg_game = NULL;
                   1342:
                   1343:   struct dg_watchcols **watchcols = globalconfig_watch_columns();
                   1344:   struct dg_watchcols **curr_watchcol;
                   1345:
                   1346:   if (sortmode == NUM_SORTMODES)
                   1347:       sortmode = globalconfig.sortmode;
                   1348:
                   1349:   shm_init(&shm_dg_data, &shm_dg_game);
                   1350:
                   1351:   games = populate_games (gameid, &len, NULL); /* FIXME: should be 'me' instead of 'NULL' */
                   1352:   shm_update(shm_dg_data, games, len);
                   1353:   games = sort_games (games, len, sortmode);
                   1354:
                   1355:   signals_block();
                   1356:   shm_sem_wait(shm_dg_data);
                   1357:
                   1358:   (void) time(&ctime);
                   1359:
                   1360:   for (i = 0; i < 100; i++) {
                   1361:     if (i >= len)
                   1362:       break;
                   1363:
                   1364:     for (curr_watchcol = watchcols; *curr_watchcol; ++curr_watchcol) {
                   1365:       struct dg_watchcols *col = *curr_watchcol;
                   1366:       if ((dg_sortmode)col->dat == SORTMODE_NONE)
                   1367:         continue;
                   1368:       char tmpbuf[80];
                   1369:       int hilite = 0;
                   1370:       game_get_column_data(games[i],
                   1371:         selectorchars[i],
                   1372:         ctime, shm_dg_game,
                   1373:         tmpbuf, sizeof tmpbuf, &hilite, (dg_sortmode)col->dat);
                   1374:       fprintf(stdout, "%s#", tmpbuf); /* format in col->fmt */
                   1375:     }
                   1376:     fprintf(stdout, "\n");
                   1377:   }
                   1378:
                   1379:   shm_sem_post(shm_dg_data);
                   1380:   signals_release();
                   1381:
                   1382:   free_populated_games(games, len);
                   1383:
                   1384: #ifdef USE_SHMEM
                   1385:   shmdt(shm_dg_data);
                   1386: #endif
                   1387: }
                   1388:
                   1389: /* ************************************************************* */
                   1390:
                   1391: /*
                   1392:  * Check email address, returns 1 if valid, 0 otherwise.
                   1393:  * Doesn't recognize addresses with parts in double-quotes.
                   1394:  * Addresses with a colon in them are always rejected.
                   1395:  */
                   1396: int
                   1397: check_email (char *s)
                   1398: {
                   1399:   char *atomchars = "!#$%&'*+-/=?^_`{|}~" "0123456789"
                   1400:     "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
                   1401:   int f;
                   1402:
                   1403:   if (*s == '@')
                   1404:     return 0;
                   1405:
                   1406:   while (*s != '\0' && *s != '@')
                   1407:     {
                   1408:       if (strchr(atomchars, *s) == NULL)
                   1409:         return 0;
                   1410:       s++;
                   1411:       if (*s == '.')
                   1412:         s++;
                   1413:     }
                   1414:
                   1415:   if (*s == '\0')
                   1416:     return 0;
                   1417:   s++;
                   1418:
                   1419:   f = 0;
                   1420:   while (*s != '\0')
                   1421:     {
                   1422:       if (strchr(atomchars, *s) == NULL)
                   1423:         return 0;
                   1424:       s++;
                   1425:       if (*s == '.')
                   1426:         s++, f = 1;
                   1427:     }
                   1428:
                   1429:   return f;
                   1430: }
                   1431:
                   1432: void
                   1433: change_email ()
                   1434: {
                   1435:   char buf[81];
                   1436:
                   1437:   clear();
                   1438:
                   1439:   if (me->flags & DGLACCT_EMAIL_LOCK) {
                   1440:       drawbanner(&banner);
                   1441:       mvprintw(5, 1, "Sorry, you cannot change the email.--More--");
                   1442:       dgl_getch();
                   1443:       return;
                   1444:   }
                   1445:
                   1446:   for (;;)
                   1447:   {
                   1448:       drawbanner(&banner);
                   1449:
                   1450:     mvprintw(3, 1, "Your current email is: %s", me->email);
                   1451:     mvaddstr(4, 1, "Please enter a new one (max 80 chars; blank line aborts)");
                   1452:     mvaddstr(6, 1, "=> ");
                   1453:
                   1454:     if (mygetnstr (buf, 80, 1) != OK)
                   1455:        return;
                   1456:
                   1457:     if (*buf == '\0')
                   1458:       return;
                   1459:     else if (!strcmp(me->email, buf))
                   1460:     {
                   1461:       clear();
                   1462:       mvaddstr (8, 1, "That's the same one as before. Try again?");
                   1463:       move(1,1);
                   1464:     }
                   1465:     else if (check_email (buf))
                   1466:     {
                   1467:       mvprintw (8, 1, "Changing email address to '%s'. Confirm (y/n): ", buf);
                   1468:       if (dgl_getch() == 'y')
                   1469:       {
                   1470:        free(me->email);
                   1471:        me->email = strdup(buf);
                   1472:        writefile(0);
                   1473:        return;
                   1474:       }
                   1475:       else
                   1476:       {
                   1477:        mvaddstr(9, 1, "No changes made. Press any key to continue...");
                   1478:        dgl_getch();
                   1479:        return;
                   1480:       }
                   1481:     }
                   1482:     else
                   1483:     {
                   1484:       clear();
                   1485:       mvaddstr (8, 1, "That doesn't look like an email address to me.");
                   1486:       move(1,1);
                   1487:     }
                   1488:   }
                   1489: }
                   1490:
                   1491: int
                   1492: changepw (int dowrite)
                   1493: {
                   1494:   char buf[DGL_PASSWDLEN+1];
                   1495:   int error = 2;
                   1496:
                   1497:   /* A precondition is that struct `me' exists because we can be not-yet-logged-in. */
                   1498:   if (!me) {
                   1499:       debug_write("no 'me' in changepw");
                   1500:     graceful_exit (122);        /* Die. */
                   1501:   }
                   1502:
                   1503:   if (me->flags & DGLACCT_PASSWD_LOCK) {
                   1504:       clear();
                   1505:       drawbanner(&banner);
                   1506:       mvprintw(5, 1, "Sorry, you cannot change the password.--More--");
                   1507:       dgl_getch();
                   1508:       return 0;
                   1509:   }
                   1510:
                   1511:   while (error)
                   1512:     {
                   1513:       char repeatbuf[DGL_PASSWDLEN+1];
                   1514:       clear ();
                   1515:
                   1516:       drawbanner (&banner);
                   1517:
                   1518:       mvprintw (5, 1,
                   1519:                 "Please enter a%s password. Remember that this is sent over the net",
                   1520:                 loggedin ? " new" : "");
                   1521:       mvaddstr (6, 1,
                   1522:                 "in plaintext, so make it something new and expect it to be relatively");
                   1523:       mvaddstr (7, 1, "insecure.");
                   1524:       mvprintw (8, 1,
                   1525:                 "%i character max. No ':' characters. Blank line to abort.", DGL_PASSWDLEN);
                   1526:       mvaddstr (10, 1, "=> ");
                   1527:
                   1528:       if (error == 1)
                   1529:         {
                   1530:           mvaddstr (15, 1, "Sorry, the passwords don't match. Try again.");
                   1531:           move (10, 4);
                   1532:         }
                   1533:
                   1534:       refresh ();
                   1535:
                   1536:       if (mygetnstr (buf, DGL_PASSWDLEN, 0) != OK)
                   1537:          return 0;
                   1538:
                   1539:       if (*buf == '\0')
                   1540:         return 0;
                   1541:
                   1542:       if (strchr (buf, ':') != NULL) {
                   1543:          debug_write("cannot have ':' in passwd");
                   1544:         graceful_exit (112);
                   1545:       }
                   1546:
                   1547:       mvaddstr (12, 1, "And again:");
                   1548:       mvaddstr (13, 1, "=> ");
                   1549:
                   1550:       if (mygetnstr (repeatbuf, DGL_PASSWDLEN, 0) != OK)
                   1551:          return 0;
                   1552:
                   1553:       if (!strcmp (buf, repeatbuf))
                   1554:         error = 0;
                   1555:       else
                   1556:         error = 1;
                   1557:     }
                   1558:
                   1559:   free(me->password);
1.2       rubenllo 1560: #if defined(__OpenBSD__)
                   1561:   me->password = (char *) malloc((_PASSWORD_LEN+1)*sizeof(char));
                   1562:  if ( crypt_newhash(buf, "blowfish,12", me->password, _PASSWORD_LEN) != 0)
                   1563:   graceful_exit(1);
                   1564: #else
1.1       rubenllo 1565:   me->password = strdup (crypt (buf, buf));
1.2       rubenllo 1566: #endif
1.1       rubenllo 1567:
                   1568:   if (dowrite)
                   1569:     writefile (0);
                   1570:
                   1571:   return 1;
                   1572: }
                   1573:
                   1574: /* ************************************************************* */
                   1575:
                   1576: void
                   1577: wall_email(char *from, char *msg)
                   1578: {
                   1579:     int len, i;
                   1580:     struct dg_game **games = NULL;
                   1581:     char spool_fn[1024+1];
                   1582:     FILE *user_spool = NULL;
                   1583:     struct flock fl = { 0 };
                   1584:
                   1585:     fl.l_type = F_WRLCK;
                   1586:     fl.l_whence = SEEK_SET;
                   1587:     fl.l_start = 0;
                   1588:     fl.l_len = 0;
                   1589:
                   1590:     if (!from || !msg) return;
                   1591:
                   1592:     if (strlen(from) < 1) {
                   1593:        fprintf(stderr, "Error: wall: 'from' username is too short!\n");
                   1594:        debug_write("wall: 'from' username too short");
                   1595:        graceful_exit(121);
                   1596:     }
                   1597:
                   1598:     if (strlen(msg) >= DGL_MAILMSGLEN) {
                   1599:        fprintf(stderr, "Error: wall: message too long!\n");
                   1600:        debug_write("wall: message too long");
                   1601:        graceful_exit(120);
                   1602:     }
                   1603:
                   1604:     games = populate_games(-1, &len, me);
                   1605:
                   1606:     if (len == 0) {
                   1607:        fprintf(stderr, "Error: wall: no one's logged in!\n");
                   1608:        debug_write("wall: no people playing");
                   1609:        graceful_exit(118);
                   1610:     }
                   1611:
                   1612:     for (i = 0; i < len; i++) {
                   1613:        int game = games[i]->gamenum;
                   1614:        int fnamelen;
                   1615:        if (strlen(myconfig[game]->spool) < 1) continue;
                   1616:
                   1617:        snprintf (spool_fn, 1024, "%s/%s", myconfig[game]->spool, games[i]->name);
                   1618:
                   1619:        if ((user_spool = fopen (spool_fn, "a")) == NULL) continue;
                   1620:
                   1621:        while (fcntl(fileno(user_spool), F_SETLK, &fl) == -1) {
                   1622:            if (errno != EAGAIN) continue;
                   1623:            sleep (1);
                   1624:        }
                   1625:        fprintf(user_spool, "%s:%s\n", from, msg);
                   1626:        fclose(user_spool);
                   1627:     }
                   1628:     free_populated_games(games, len);
                   1629: }
                   1630:
                   1631: void
                   1632: domailuser (char *username)
                   1633: {
                   1634:   unsigned int len, i;
                   1635:   char *spool_fn, message[DGL_MAILMSGLEN+1];
                   1636:   FILE *user_spool = NULL;
                   1637:   time_t now;
                   1638:   int mail_empty = 1;
                   1639:   int game;
                   1640:   struct flock fl = { 0 };
                   1641:
                   1642:   fl.l_type = F_WRLCK;
                   1643:   fl.l_whence = SEEK_SET;
                   1644:   fl.l_start = 0;
                   1645:   fl.l_len = 0;
                   1646:
                   1647:   assert (loggedin);
                   1648:
                   1649:   game = 0; /*TODO: find_curr_player_game(username) */
                   1650:
                   1651:   if (strlen(myconfig[game]->spool) < 1) return;
                   1652:
                   1653:   len = strlen(myconfig[game]->spool) + strlen (username) + 1;
                   1654:   spool_fn = malloc (len + 1);
                   1655:   time (&now);
                   1656:   snprintf (spool_fn, len + 1, "%s/%s", myconfig[game]->spool, username);
                   1657:
                   1658:   /* print the enter your message line */
                   1659:   clear ();
                   1660:   drawbanner (&banner);
                   1661:   mvprintw (5, 1,
                   1662:             "Enter your message here. It is to be one line only and %i characters or less.",
                   1663:            DGL_MAILMSGLEN);
                   1664:   mvaddstr (7, 1, "=> ");
                   1665:
                   1666:   if (mygetnstr (message, DGL_MAILMSGLEN, 1) != OK)
                   1667:       return;
                   1668:
                   1669:   for (i = 0; i < strlen (message); i++)
                   1670:     {
                   1671:       if (message[i] != ' ' && message[i] != '\n' && message[i] != '\t')
                   1672:         mail_empty = 0;
                   1673:     }
                   1674:
                   1675:   if (mail_empty)
                   1676:     {
                   1677:       mvaddstr (9, 1, "This scroll appears to be blank.");
                   1678:       mvaddstr (10, 1, "(Aborting your message.)");
                   1679:       mvaddstr (12, 1, "--More--");
                   1680:       dgl_getch ();
                   1681:       return;
                   1682:     }
                   1683:
                   1684:   if ((user_spool = fopen (spool_fn, "a")) == NULL)
                   1685:     {
                   1686:       mvaddstr (9, 1,
                   1687:                 "You fall into the water!  You sink like a rock.");
                   1688:       mvprintw (10, 1,
                   1689:                 "(Couldn't open %s'%c spool file.  Aborting.)",
                   1690:                 username, (username[strlen (username) - 1] != 's') ? 's' : 0);
                   1691:       mvaddstr (12, 1, "--More--");
                   1692:       dgl_getch ();
                   1693:       return;
                   1694:     }
                   1695:
                   1696:   mvaddstr (9, 1, "Sending your scroll...");
                   1697:   refresh ();
                   1698:
                   1699:   /* Getting a lock on the mailspool... */
                   1700:   while (fcntl (fileno (user_spool), F_SETLK, &fl) == -1)
                   1701:     {
                   1702:       if (errno != EAGAIN)
                   1703:         {
                   1704:           mvaddstr (10, 1,
                   1705:                     "(Received a weird error from fcntl.  Aborting.)");
                   1706:          mvaddstr (12, 1, "--More--");
                   1707:           dgl_getch ();
                   1708:           return;
                   1709:         }
                   1710:       sleep (1);
                   1711:     }
                   1712:
                   1713:   fprintf (user_spool, "%s:%s\n", me->username, message);
                   1714:
                   1715:   /*
                   1716:    * Don't unlock the file ourselves, this way it will be done automatically
                   1717:    * after all data has been written. (Using file locking with stdio is icky.)
                   1718:    */
                   1719:
                   1720:   fclose (user_spool);
                   1721:
                   1722:   mvaddstr (9, 1, "Scroll delivered!         ");
                   1723:   move(9, 19); /* Pedantry! */
                   1724:   refresh ();
                   1725:   sleep (2);
                   1726:
                   1727:   return;
                   1728: }
                   1729:
                   1730:
                   1731: /* ************************************************************* */
                   1732:
                   1733: void
                   1734: freefile ()
                   1735: {
                   1736: #ifndef USE_SQLITE3
                   1737:   int i;
                   1738:
                   1739:   /* free existing mem, clear existing entries */
                   1740:   for (i = 0; i < f_num; i++)
                   1741:     {
                   1742:       if (users[i] != me)
                   1743:       {
                   1744:        free (users[i]->password);
                   1745:        free (users[i]->username);
                   1746:        free (users[i]->email);
                   1747:        free (users[i]->env);
                   1748:        free (users[i]);
                   1749:       }
                   1750:     }
                   1751:
                   1752:   if (users)
                   1753:     free (users);
                   1754:
                   1755:   users = NULL;
                   1756:   f_num = 0;
                   1757: #endif
                   1758: }
                   1759:
                   1760: /* ************************************************************* */
                   1761:
                   1762: void
                   1763: initcurses ()
                   1764: {
                   1765:   printf("\033[2J");
                   1766:   if (newterm(NULL, stdout, stdin) == NULL) {
                   1767:       if (!globalconfig.defterm || (newterm(globalconfig.defterm, stdout, stdin) == NULL)) {
                   1768:          debug_write("cannot create newterm");
                   1769:          graceful_exit(60);
                   1770:       }
                   1771:       mysetenv("TERM", globalconfig.defterm, 1);
                   1772:   }
                   1773:   cbreak ();
                   1774:   noecho ();
                   1775:   nonl ();
                   1776:   intrflush (stdscr, FALSE);
                   1777:   keypad (stdscr, TRUE);
                   1778: #ifdef USE_NCURSES_COLOR
                   1779:   start_color();
                   1780:   use_default_colors();
                   1781:
                   1782:   init_pair(COLOR_BLACK, COLOR_WHITE, COLOR_BLACK);
                   1783:   init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
                   1784:   init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
                   1785:   init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
                   1786:   init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
                   1787:   init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
                   1788:   init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
                   1789:   init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
                   1790:   init_pair(9, 0, COLOR_BLACK);
                   1791:   init_pair(10, COLOR_BLACK, COLOR_BLACK);
                   1792:   init_pair(11, -1, -1);
                   1793:
                   1794:   if (globalconfig.utf8esc) (void) write(1, "\033%G", 3);
                   1795: #endif
                   1796:   clear();
                   1797:   refresh();
                   1798: }
                   1799:
                   1800: /* ************************************************************* */
                   1801:
                   1802: void
                   1803: autologin (char* user, char *pass)
                   1804: {
                   1805:   struct dg_user *tmp;
                   1806:   tmp = userexist(user, 0);
                   1807:   if (tmp) {
                   1808:       me = cpy_me(tmp);
                   1809:       if ((passwordgood(pass) || initplayer == 1) && !(me->flags & DGLACCT_LOGIN_LOCK)) {
                   1810:          loggedin = 1;
                   1811:          setproctitle ("%s", me->username);
                   1812:          dgl_exec_cmdqueue(globalconfig.cmdqueue[DGLTIME_LOGIN], 0, me);
                   1813:       }
                   1814:   }
                   1815: }
                   1816:
                   1817: void
                   1818: loginprompt (int from_ttyplay)
                   1819: {
                   1820:   char user_buf[DGL_PLAYERNAMELEN+1], pw_buf[DGL_PASSWDLEN+2];
                   1821:   int error = 2;
                   1822:
                   1823:   loggedin = 0;
                   1824:
                   1825:   while (error)
                   1826:     {
                   1827:       clear ();
                   1828:
                   1829:       drawbanner (&banner);
                   1830:
                   1831:       if (from_ttyplay == 1)
                   1832:        mvaddstr (4, 1, "This operation requires you to be logged in.");
                   1833:
                   1834:       mvaddstr (5, 1,
                   1835:                 "Please enter your username. (blank entry aborts)");
                   1836:       mvaddstr (7, 1, "=> ");
                   1837:
                   1838:       if (error == 1)
                   1839:         {
                   1840:           mvaddstr (9, 1, "There was a problem with your last entry.");
                   1841:           move (7, 4);
                   1842:         }
                   1843:
                   1844:       refresh ();
                   1845:
                   1846:       if (mygetnstr (user_buf, DGL_PLAYERNAMELEN, 1) != OK)
                   1847:          return;
                   1848:
                   1849:       if (*user_buf == '\0')
                   1850:         return;
                   1851:
                   1852:       error = 1;
                   1853:
                   1854:       {
                   1855:          struct dg_user *tmpme;
                   1856:          if ((tmpme = userexist(user_buf, 0))) {
                   1857:              me = cpy_me(tmpme);
                   1858:              error = 0;
                   1859:          }
                   1860:       }
                   1861:     }
                   1862:
                   1863:   clear ();
                   1864:
                   1865:   drawbanner (&banner);
                   1866:
                   1867:   mvaddstr (5, 1, "Please enter your password.");
                   1868:   mvaddstr (7, 1, "=> ");
                   1869:
                   1870:   refresh ();
                   1871:
                   1872:   if (mygetnstr (pw_buf, DGL_PASSWDLEN, 0) != OK)
                   1873:       return;
                   1874:
                   1875:   if (passwordgood (pw_buf))
                   1876:     {
                   1877:        if (me->flags & DGLACCT_LOGIN_LOCK) {
                   1878:            clear ();
                   1879:            mvprintw(5, 1, "Sorry, that account has been banned.--More--");
                   1880:            dgl_getch();
                   1881:            return;
                   1882:        }
                   1883:
                   1884:       loggedin = 1;
                   1885:       if (from_ttyplay)
                   1886:          setproctitle("%s [watching %s]", me->username, chosen_name);
                   1887:       else
                   1888:          setproctitle("%s", me->username);
                   1889:       dgl_exec_cmdqueue(globalconfig.cmdqueue[DGLTIME_LOGIN], 0, me);
                   1890:     }
                   1891:   else
                   1892:   {
                   1893:     me = NULL;
                   1894:     if (from_ttyplay == 1)
                   1895:     {
                   1896:       mvaddstr(9, 1, "Login failed. Returning to game.");
                   1897:       refresh();
                   1898:       sleep(2);
                   1899:     }
                   1900:   }
                   1901: }
                   1902:
                   1903: /* ************************************************************* */
                   1904:
                   1905: void
                   1906: newuser ()
                   1907: {
                   1908:   char buf[1024], dirname[100];
                   1909:   int error = 2;
                   1910:   unsigned int i;
                   1911:
                   1912:   loggedin = 0;
                   1913:
                   1914: #ifndef USE_SQLITE3
                   1915:   if (f_num >= globalconfig.max)
                   1916:   {
                   1917:       clear ();
                   1918:
                   1919:       drawbanner (&banner);
                   1920:
                   1921:       mvaddstr (5, 1, "Sorry, too many users have registered now.");
                   1922:       mvaddstr (6, 1, "You might email the server administrator.");
                   1923:       mvaddstr (7, 1, "Press return to return to the menu. ");
                   1924:       dgl_getch ();
                   1925:
                   1926:       return;
                   1927:   }
                   1928: #endif
                   1929:
                   1930:   if (me)
                   1931:     free (me);
                   1932:
                   1933:   me = calloc (1, sizeof (struct dg_user));
                   1934:
                   1935:   while (error)
                   1936:     {
                   1937:       clear ();
                   1938:
                   1939:       sprintf(buf, "%i character max.", globalconfig.max_newnick_len);
                   1940:
                   1941:       drawbanner (&banner);
                   1942:
                   1943:       mvaddstr (5, 1, "Welcome new user. Please enter a username.");
                   1944:       mvaddstr (6, 1,
                   1945:                 "Only characters and numbers are allowed, with no spaces.");
                   1946:       mvaddstr (7, 1, buf);
                   1947:       mvaddstr (9, 1, "=> ");
                   1948:
                   1949:       if (error == 1)
                   1950:         {
                   1951:           mvaddstr (11, 1, "There was a problem with your last entry.");
                   1952:           move (9, 4);
                   1953:         }
                   1954:
                   1955:       refresh ();
                   1956:
                   1957:       if (mygetnstr (buf, globalconfig.max_newnick_len, 1) != OK)
                   1958:          buf[0] = 0;
                   1959:
                   1960:       if (*buf == '\0') {
                   1961:          free(me);
                   1962:          me = NULL;
                   1963:          return;
                   1964:       }
                   1965:
                   1966:       if (!userexist(buf, 1)) {
                   1967:          error = 0;
                   1968:       } else
                   1969:          error = 1;
                   1970:
                   1971:       for (i = 0; i < strlen (buf); i++)
                   1972:         {
                   1973:           if (!isalnum((int)buf[i]))
                   1974:             error = 1;
                   1975:         }
                   1976:
                   1977:       if (strlen (buf) < 2)
                   1978:         error = 1;
                   1979:
                   1980:       if (strlen (buf) == 0)
                   1981:       {
                   1982:        free(me);
                   1983:        me = NULL;
                   1984:         return;
                   1985:       }
                   1986:     }
                   1987:
                   1988:   me->username = strdup (buf);
                   1989:
                   1990:   /* password step */
                   1991:
                   1992:   clear ();
                   1993:
                   1994:   if (!changepw (0))                  /* Calling changepw instead to prompt twice. */
                   1995:   {
                   1996:     free(me->username);
                   1997:     free(me);
                   1998:     me = NULL;
                   1999:     return;
                   2000:   }
                   2001:
                   2002:   /* email step */
                   2003:
                   2004:   error = 2;
                   2005:   while (error != 0)
                   2006:     {
                   2007:       clear ();
                   2008:
                   2009:       drawbanner (&banner);
                   2010:
                   2011:       mvaddstr (5, 1, "Please enter your email address.");
                   2012:       mvaddstr (6, 1, "This is sent _nowhere_ but will be used if you ask"
                   2013:         " the sysadmin for lost");
                   2014:       mvaddstr (7, 1, "password help. Please use a correct one. It only"
                   2015:         " benefits you.");
                   2016:       mvaddstr (8, 1, "80 character max. No ':' characters. Blank line"
                   2017:         " aborts.");
                   2018:       mvaddstr (10, 1, "=> ");
                   2019:
                   2020:       if (error == 1)
                   2021:         {
                   2022:           mvaddstr (12, 1, "There was a problem with your last entry.");
                   2023:           move (10, 4);
                   2024:         }
                   2025:
                   2026:       refresh ();
                   2027:       if (mygetnstr (buf, 80, 1) != OK)
                   2028:          buf[0] = 0;
                   2029:
                   2030:       if (check_email (buf))
                   2031:         error = 0;
                   2032:       else
                   2033:         error = 1;
                   2034:
                   2035:       if (*buf == '\0')
                   2036:       {
                   2037:         free (me->username);
                   2038:         free (me->password);
                   2039:         free (me);
                   2040:         me = NULL;
                   2041:         return;
                   2042:       }
                   2043:     }
                   2044:
                   2045:   me->email = strdup (buf);
                   2046:   me->env = calloc (1, 1);
                   2047:   me->flags = 0;
                   2048:
                   2049:   loggedin = 1;
                   2050:
                   2051:   setproctitle ("%s", me->username);
                   2052:
                   2053:   dgl_exec_cmdqueue(globalconfig.cmdqueue[DGLTIME_REGISTER], 0, me);
                   2054:
                   2055:   writefile (1);
                   2056: }
                   2057:
                   2058: /* ************************************************************* */
                   2059:
                   2060: int
                   2061: passwordgood (char *cpw)
                   2062: {
1.2       rubenllo 2063: #if defined(__OpenBSD__)
                   2064:   if ( crypt_checkpass(cpw, me->password) == 0)
                   2065:       return 1;
                   2066:   return 0;
                   2067: #else
1.1       rubenllo 2068:   char *crypted;
                   2069:   assert (me != NULL);
                   2070:
                   2071:   crypted = crypt (cpw, cpw);
                   2072:   if (crypted == NULL)
                   2073:       return 0;
                   2074:   if (!strncmp (crypted, me->password, DGL_PASSWDLEN))
                   2075:     return 1;
                   2076:   if (!strncmp (cpw, me->password, DGL_PASSWDLEN))
                   2077:     return 1;
                   2078:
                   2079:   return 0;
1.2       rubenllo 2080: #endif
1.1       rubenllo 2081: }
                   2082:
                   2083: /* ************************************************************* */
                   2084:
                   2085: int
                   2086: readfile (int nolock)
                   2087: {
                   2088: #ifndef USE_SQLITE3
                   2089:   FILE *fp = NULL, *fpl = NULL;
                   2090:   char buf[1200];
                   2091:   struct flock fl = { 0 };
                   2092:
                   2093:   fl.l_type = F_RDLCK;
                   2094:   fl.l_whence = SEEK_SET;
                   2095:   fl.l_start = 0;
                   2096:   fl.l_len = 0;
                   2097:
                   2098:   memset (buf, 0, 1024);
                   2099:
                   2100:   /* read new stuff */
                   2101:
                   2102:   if (!nolock)
                   2103:     {
                   2104:       fpl = fopen (globalconfig.lockfile, "r");
                   2105:       if (!fpl) {
                   2106:          debug_write("cannot fopen lockfile");
                   2107:         graceful_exit (106);
                   2108:       }
                   2109:       if (fcntl (fileno (fpl), F_SETLKW, &fl) == -1) {
                   2110:          debug_write("cannot fcntl lockfile");
                   2111:         graceful_exit (95);
                   2112:       }
                   2113:     }
                   2114:
                   2115:   fp = fopen (globalconfig.passwd, "r");
                   2116:   if (!fp) {
                   2117:       debug_write("cannot fopen passwd file");
                   2118:     graceful_exit (106);
                   2119:   }
                   2120:
                   2121:   /* once per name in the file */
                   2122:   while (fgets (buf, 1200, fp))
                   2123:     {
                   2124:       char *b = buf, *n = buf;
                   2125:
                   2126:       users = realloc (users, sizeof (struct dg_user *) * (f_num + 1));
                   2127:       users[f_num] = malloc (sizeof (struct dg_user));
                   2128:       users[f_num]->username = (char *) calloc (DGL_PLAYERNAMELEN+2, sizeof (char));
                   2129:       users[f_num]->email = (char *) calloc (82, sizeof (char));
                   2130:       users[f_num]->password = (char *) calloc (DGL_PASSWDLEN+2, sizeof (char));
                   2131:       users[f_num]->env = (char *) calloc (1026, sizeof (char));
                   2132:
                   2133:       /* name field, must be valid */
                   2134:       while (*b != ':')
                   2135:         {
                   2136:           if (!isalnum((int)*b))
                   2137:             return 1;
                   2138:           users[f_num]->username[(b - n)] = *b;
                   2139:           b++;
                   2140:           if ((b - n) >= DGL_PLAYERNAMELEN) {
                   2141:              debug_write("name field too long");
                   2142:             graceful_exit (100);
                   2143:          }
                   2144:         }
                   2145:
                   2146:       /* advance to next field */
                   2147:       n = b + 1;
                   2148:       b = n;
                   2149:
                   2150:       /* email field */
                   2151:       while (*b != ':')
                   2152:         {
                   2153:           users[f_num]->email[(b - n)] = *b;
                   2154:           b++;
                   2155:           if ((b - n) > 80) {
                   2156:              debug_write("email field too long");
                   2157:             graceful_exit (101);
                   2158:          }
                   2159:         }
                   2160:
                   2161:       /* advance to next field */
                   2162:       n = b + 1;
                   2163:       b = n;
                   2164:
                   2165:       /* pw field */
                   2166:       while (*b != ':')
                   2167:         {
                   2168:           users[f_num]->password[(b - n)] = *b;
                   2169:           b++;
                   2170:           if ((b - n) >= DGL_PASSWDLEN) {
                   2171:              debug_write("passwd field too long");
                   2172:             graceful_exit (102);
                   2173:          }
                   2174:         }
                   2175:
                   2176:       /* advance to next field */
                   2177:       n = b + 1;
                   2178:       b = n;
                   2179:
                   2180:       /* env field */
                   2181:       while ((*b != '\n') && (*b != 0) && (*b != EOF))
                   2182:         {
                   2183:           users[f_num]->env[(b - n)] = *b;
                   2184:           b++;
                   2185:           if ((b - n) >= 1024) {
                   2186:              debug_write("env field too long");
                   2187:             graceful_exit (103);
                   2188:          }
                   2189:         }
                   2190:
                   2191:       f_num++;
                   2192:       /* prevent a buffer overrun here */
                   2193:       if (f_num > globalconfig.max)
                   2194:       {
                   2195:        fprintf(stderr,"ERROR: number of users in database exceeds maximum. Exiting.\n");
                   2196:        debug_write("too many users in database");
                   2197:         graceful_exit (109);
                   2198:       }
                   2199:     }
                   2200:
                   2201:   if (!nolock)
                   2202:       fclose (fpl);
                   2203:   fclose (fp);
                   2204: #endif
                   2205:   return 0;
                   2206: }
                   2207:
                   2208: /* ************************************************************* */
                   2209:
                   2210: #ifndef USE_SQLITE3
                   2211: struct dg_user *userexist_tmp_me = NULL;
                   2212:
                   2213: struct dg_user *
                   2214: userexist (char *cname, int isnew)
                   2215: {
                   2216:   int i;
                   2217:
                   2218:   if (userexist_tmp_me) {
                   2219:       free(userexist_tmp_me->username);
                   2220:       free(userexist_tmp_me->email);
                   2221:       free(userexist_tmp_me->env);
                   2222:       free(userexist_tmp_me->password);
                   2223:       free(userexist_tmp_me);
                   2224:       userexist_tmp_me = NULL;
                   2225:   }
                   2226:
                   2227:   for (i = 0; i < f_num; i++)
                   2228:     {
                   2229:        if (!strncasecmp (cname, users[i]->username, (isnew ? globalconfig.max_newnick_len : DGL_PLAYERNAMELEN))) {
                   2230:            userexist_tmp_me = cpy_me(users[i]);
                   2231:            return userexist_tmp_me;
                   2232:        }
                   2233:     }
                   2234:
                   2235:   return NULL;
                   2236: }
                   2237: #else
                   2238:
                   2239: struct dg_user *userexist_tmp_me = NULL;
                   2240:
                   2241: static int
                   2242: userexist_callback(void *NotUsed, int argc, char **argv, char **colname)
                   2243: {
                   2244:     int i;
                   2245:     NotUsed = NULL;
                   2246:
                   2247:     userexist_tmp_me = malloc(sizeof(struct dg_user));
                   2248:
                   2249:     for (i = 0; i < argc; i++) {
                   2250:        if (!strcmp(colname[i], "username"))
                   2251:            userexist_tmp_me->username = strdup(argv[i]);
                   2252:        else if (!strcmp(colname[i], "email"))
                   2253:            userexist_tmp_me->email = strdup(argv[i]);
                   2254:        else if (!strcmp(colname[i], "env"))
                   2255:            userexist_tmp_me->env = strdup(argv[i]);
                   2256:        else if (!strcmp(colname[i], "password"))
                   2257:            userexist_tmp_me->password = strdup(argv[i]);
                   2258:        else if (!strcmp(colname[i], "flags"))
                   2259:            userexist_tmp_me->flags = atoi(argv[i]);
                   2260:        else if (!strcmp(colname[i], "id"))
                   2261:            userexist_tmp_me->id = atoi(argv[i]);
                   2262:     }
                   2263:     return 0;
                   2264: }
                   2265:
                   2266: struct dg_user *
                   2267: userexist (char *cname, int isnew)
                   2268: {
                   2269:     sqlite3 *db;
                   2270:     char *errmsg = NULL;
                   2271:     int ret, retry = 10;
                   2272:
                   2273:     char *qbuf;
                   2274:
                   2275:     char tmpbuf[DGL_PLAYERNAMELEN+2];
                   2276:
                   2277:     memset(tmpbuf, 0, DGL_PLAYERNAMELEN+2);
                   2278:     strncpy(tmpbuf, cname, (isnew ? globalconfig.max_newnick_len : DGL_PLAYERNAMELEN));
                   2279:
                   2280:     /* Check that the nick doesn't interfere with already registered nicks */
                   2281:     if (isnew && (strlen(cname) >= globalconfig.max_newnick_len))
                   2282:        strcat(tmpbuf, "%");
                   2283:
                   2284:     qbuf = sqlite3_mprintf("select * from dglusers where username = '%q' collate nocase limit 1", tmpbuf);
                   2285:
                   2286:     ret = sqlite3_open(globalconfig.passwd, &db);
                   2287:     if (ret) {
                   2288:        sqlite3_close(db);
                   2289:        debug_write("sqlite3_open failed");
                   2290:        graceful_exit(96);
                   2291:     }
                   2292:
                   2293:     if (userexist_tmp_me) {
                   2294:        free(userexist_tmp_me->username);
                   2295:        free(userexist_tmp_me->email);
                   2296:        free(userexist_tmp_me->env);
                   2297:        free(userexist_tmp_me->password);
                   2298:        free(userexist_tmp_me);
                   2299:        userexist_tmp_me = NULL;
                   2300:     }
                   2301:
                   2302:     sqlite3_busy_timeout(db, 10000);
                   2303:     ret = sqlite3_exec(db, qbuf, userexist_callback, 0, &errmsg);
                   2304:
                   2305:     sqlite3_free(qbuf);
                   2306:
                   2307:     if (ret != SQLITE_OK) {
                   2308:        sqlite3_close(db);
                   2309:        debug_write("sqlite3_exec failed");
                   2310:        graceful_exit(108);
                   2311:     }
                   2312:     sqlite3_close(db);
                   2313:
                   2314:     return userexist_tmp_me;
                   2315: }
                   2316: #endif
                   2317:
                   2318: /* ************************************************************* */
                   2319:
                   2320: void
                   2321: write_canned_rcfile (int game, char *target)
                   2322: {
                   2323:   FILE *canned, *newfile;
                   2324:   char buf[1024], *rfn;
                   2325:   size_t bytes, len;
                   2326:
                   2327:   len = strlen(myconfig[game]->rcfile) + 2;
                   2328:   rfn = malloc(len);
                   2329:   snprintf (rfn, len, "/%s", myconfig[game]->rcfile);
                   2330:
                   2331:   if (!(newfile = fopen (target, "w")))
                   2332:     {
                   2333:     bail:
                   2334:       mvaddstr (13, 1,
                   2335:                 "You don't know how to write that! You write \"%s was here\" and the scroll disappears.");
                   2336:       mvaddstr (14, 1,
                   2337:                 "(Sorry, but I couldn't open one of the config files. This is a bug.)");
                   2338:       return;
                   2339:     }
                   2340:
                   2341:   if (!(canned = fopen (rfn, "r")))
                   2342:     goto bail;
                   2343:
                   2344:   free(rfn);
                   2345:
                   2346:   while ((bytes = fread (buf, 1, 1024, canned)) > 0)
                   2347:     {
                   2348:       if (fwrite (buf, 1, bytes, newfile) != bytes)
                   2349:         {
                   2350:           if (ferror (newfile))
                   2351:             {
                   2352:               mvaddstr (13, 1, "Your hand slips while engraving.");
                   2353:               mvaddstr (14, 1,
                   2354:                         "(Encountered a problem writing the new file. This is a bug.)");
                   2355:               fclose (canned);
                   2356:               fclose (newfile);
                   2357:               return;
                   2358:             }
                   2359:         }
                   2360:     }
                   2361:
                   2362:   fclose (canned);
                   2363:   fclose (newfile);
                   2364:   chmod (target, default_fmode);
                   2365: }
                   2366:
                   2367:
                   2368:
                   2369: /* ************************************************************* */
                   2370:
                   2371: #ifndef USE_SQLITE3
                   2372: void
                   2373: writefile (int requirenew)
                   2374: {
                   2375:   FILE *fp, *fpl;
                   2376:   int i = 0;
                   2377:   int my_done = 0;
                   2378:   struct flock fl = { 0 };
                   2379:
                   2380:   fl.l_type = F_WRLCK;
                   2381:   fl.l_whence = SEEK_SET;
                   2382:   fl.l_start = 0;
                   2383:   fl.l_len = 0;
                   2384:
                   2385:   signals_block();
                   2386:
                   2387:   fpl = fopen (globalconfig.lockfile, "r+");
                   2388:   if (!fpl)
                   2389:     {
                   2390:        signals_release();
                   2391:       debug_write("writefile locking failed");
                   2392:       graceful_exit (115);
                   2393:     }
                   2394:   if (fcntl (fileno (fpl), F_SETLK, &fl))
                   2395:     {
                   2396:        signals_release();
                   2397:       debug_write("writefile fcntl failed");
                   2398:       graceful_exit (107);
                   2399:     }
                   2400:
                   2401:   fl.l_type = F_UNLCK;
                   2402:
                   2403:   freefile ();
                   2404:   readfile (1);
                   2405:
                   2406:   fp = fopen (globalconfig.passwd, "w");
                   2407:   if (!fp)
                   2408:     {
                   2409:        signals_release();
                   2410:       debug_write("passwd file fopen failed");
                   2411:       graceful_exit (99);
                   2412:     }
                   2413:
                   2414:   for (i = 0; i < f_num; i++)
                   2415:     {
                   2416:       if (loggedin && !strncmp (me->username, users[i]->username, DGL_PLAYERNAMELEN))
                   2417:         {
                   2418:           if (requirenew)
                   2419:             {
                   2420:               /* this is if someone managed to register at the same time
                   2421:                * as someone else. just die. */
                   2422:              fclose(fp);
                   2423:              fclose(fpl);
                   2424:              signals_release();
                   2425:              debug_write("two users registering at the same time");
                   2426:               graceful_exit (111);
                   2427:             }
                   2428:           fprintf (fp, "%s:%s:%s:%s\n", me->username, me->email, me->password,
                   2429:                    me->env);
                   2430:           my_done = 1;
                   2431:         }
                   2432:       else
                   2433:         {
                   2434:           fprintf (fp, "%s:%s:%s:%s\n", users[i]->username, users[i]->email,
                   2435:                    users[i]->password, users[i]->env);
                   2436:         }
                   2437:     }
                   2438:   if (loggedin && !my_done)
                   2439:     {                           /* new entry */
                   2440:       if (f_num < globalconfig.max)
                   2441:         fprintf (fp, "%s:%s:%s:%s\n", me->username, me->email, me->password,
                   2442:                  me->env);
                   2443:       else /* Oops, someone else registered the last available slot first */
                   2444:        {
                   2445:           fclose(fp);
                   2446:          fclose(fpl);
                   2447:          signals_release();
                   2448:          debug_write("too many users in passwd db already");
                   2449:           graceful_exit (116);
                   2450:        }
                   2451:     }
                   2452:
                   2453:   fclose (fp);
                   2454:   fclose (fpl);
                   2455:
                   2456:   signals_release();
                   2457: }
                   2458: #else
                   2459: void
                   2460: writefile (int requirenew)
                   2461: {
                   2462:     sqlite3 *db;
                   2463:     char *errmsg = NULL;
                   2464:     int ret, retry = 10;
                   2465:
                   2466:     char *qbuf;
                   2467:
                   2468:     if (requirenew) {
                   2469:        qbuf = sqlite3_mprintf("insert into dglusers (username, email, env, password, flags) values ('%q', '%q', '%q', '%q', %li)", me->username, me->email, me->env, me->password, me->flags);
                   2470:     } else {
                   2471:        qbuf = sqlite3_mprintf("update dglusers set username='%q', email='%q', env='%q', password='%q', flags=%li where id=%i", me->username, me->email, me->env, me->password, me->flags, me->id);
                   2472:     }
                   2473:
                   2474:     ret = sqlite3_open(globalconfig.passwd, &db);
                   2475:     if (ret) {
                   2476:        sqlite3_close(db);
                   2477:        debug_write("writefile sqlite3_open failed");
                   2478:        graceful_exit(97);
                   2479:     }
                   2480:
                   2481:     sqlite3_busy_timeout(db, 10000);
                   2482:     ret = sqlite3_exec(db, qbuf, NULL, NULL, &errmsg);
                   2483:
                   2484:     sqlite3_free(qbuf);
                   2485:
                   2486:     if (ret != SQLITE_OK) {
                   2487:        sqlite3_close(db);
                   2488:        debug_write("writefile sqlite3_exec failed");
                   2489:        graceful_exit(98);
                   2490:     }
                   2491:     sqlite3_close(db);
                   2492: }
                   2493: #endif
                   2494:
                   2495: /* ************************************************************* */
                   2496:
                   2497: int
                   2498: purge_stale_locks (int game)
                   2499: {
                   2500:   DIR *pdir;
                   2501:   struct dirent *dent;
                   2502:   char* dir;
                   2503:   size_t len;
                   2504:   short firsttime = 1;
                   2505:
                   2506:   dir = strdup(dgl_format_str(game, me, myconfig[game]->inprogressdir, NULL));
                   2507:
                   2508:   if (!(pdir = opendir (dir))) {
                   2509:       debug_write("purge_stale_locks dir open failed");
                   2510:     graceful_exit (200);
                   2511:   }
                   2512:
                   2513:   free(dir);
                   2514:
                   2515:   while ((dent = readdir (pdir)) != NULL)
                   2516:     {
                   2517:       FILE *ipfile;
                   2518:       char *colon, *fn;
                   2519:       char buf[16];
                   2520:       pid_t pid;
                   2521:       size_t len;
                   2522:       int seconds = 0;
                   2523:
                   2524:       if (!strcmp (dent->d_name, ".") || !strcmp (dent->d_name, ".."))
                   2525:         continue;
                   2526:
                   2527:       colon = strchr (dent->d_name, ':');
                   2528:       /* should never happen */
                   2529:       if (!colon) {
                   2530:          debug_write("purge_stale_locks !colon");
                   2531:         graceful_exit (201);
                   2532:       }
                   2533:       if (colon - dent->d_name != strlen(me->username))
                   2534:         continue;
                   2535:       if (strncmp (dent->d_name, me->username, colon - dent->d_name))
                   2536:         continue;
                   2537:
                   2538:       len = strlen (dent->d_name) + strlen(dgl_format_str(game, me, myconfig[game]->inprogressdir, NULL)) + 1;
                   2539:       fn = malloc (len);
                   2540:
                   2541:       snprintf (fn, len, "%s%s", dgl_format_str(game, me, myconfig[game]->inprogressdir, NULL), dent->d_name);
                   2542:
                   2543:       if (!(ipfile = fopen (fn, "r"))) {
                   2544:          debug_write("purge_stale_locks fopen inprogressdir fail");
                   2545:         graceful_exit (202);
                   2546:       }
                   2547:
                   2548:       if (fgets (buf, 16, ipfile) == NULL) {
                   2549:          debug_write("purge_stale_locks fgets ipfile fail");
                   2550:         graceful_exit (203);
                   2551:       }
                   2552:
                   2553:       fclose (ipfile);
                   2554:
                   2555:       if (firsttime)
                   2556:       {
                   2557:        clear ();
                   2558:        drawbanner (&banner);
                   2559:
                   2560: #define HUP_WAIT 10 /* seconds before HUPPING */
                   2561:        mvprintw (3, 1,
                   2562:            "There are some stale %s processes, will recover in %d  seconds.",
                   2563:             myconfig[game]->game_name, HUP_WAIT);
                   2564:        mvaddstr (4, 1,
                   2565:            "Press a key NOW if you don't want this to happen!");
                   2566:
                   2567:        move (3, 51 + strlen(myconfig[game]->game_name)); /* pedantry */
                   2568:        halfdelay(10);
                   2569:
                   2570:        for (seconds = HUP_WAIT - 1; seconds >= 0; seconds--)
                   2571:        {
                   2572:          if (dgl_getch() != ERR)
                   2573:          {
                   2574:            nocbreak(); /* leave half-delay */
                   2575:            cbreak();
                   2576:            return 0;
                   2577:          }
                   2578:          mvprintw (3, 50 + strlen(myconfig[game]->game_name), "%d%s", seconds, (seconds > 9) ? "" : " ");
                   2579:        }
                   2580:
                   2581:        nocbreak();
                   2582:        cbreak();
                   2583:
                   2584:        firsttime = 0;
                   2585:       }
                   2586:
                   2587:       clear ();
                   2588:       refresh ();
                   2589:
                   2590:       pid = atoi (buf);
                   2591:
                   2592:       kill (pid, SIGHUP);
                   2593:
                   2594:       errno = 0;
                   2595:
                   2596:       /* Wait for it to stop running */
                   2597:       seconds = 0;
                   2598:       while (kill (pid, 0) == 0)
                   2599:         {
                   2600:           seconds++;
                   2601:           sleep (1);
                   2602:           if (seconds == 10)
                   2603:             {
                   2604:               mvprintw (3, 1,
                   2605:                         "Couldn't terminate one of your stale %s processes gracefully.", myconfig[game]->game_name);
                   2606:               mvaddstr (4, 1, "Force its termination? [yn] ");
                   2607:               if (tolower (dgl_getch ()) == 'y')
                   2608:                 {
                   2609:                   kill (pid, SIGTERM);
                   2610:                   break;
                   2611:                 }
                   2612:               else
                   2613:                 {
                   2614:                   endwin ();
                   2615:                   fprintf (stderr, "Sorry, no %s for you now, please "
                   2616:                            "contact the admin.\n", myconfig[game]->game_name);
                   2617:                  debug_write("could not terminate stale processes");
                   2618:                   graceful_exit (1);
                   2619:                 }
                   2620:             }
                   2621:         }
                   2622:
                   2623:       /* Don't remove the lock file until the process is dead. */
                   2624:       unlink (fn);
                   2625:       free (fn);
                   2626:     }
                   2627:
                   2628:   closedir (pdir);
                   2629:   return 1;
                   2630: }
                   2631:
                   2632:
                   2633: int
                   2634: runmenuloop(struct dg_menu *menu)
                   2635: {
                   2636:     struct dg_banner ban;
                   2637:     struct dg_menuoption *tmpopt;
                   2638:     int userchoice = 0;
                   2639:     int doclear = 1;
                   2640:
                   2641:     if (!menu) return 1;
                   2642:
                   2643:     ban.lines = NULL;
                   2644:     ban.len = 0;
                   2645:
                   2646:     loadbanner(menu->banner_fn, &ban);
                   2647:     while (1) {
                   2648:        term_resize_check();
                   2649:        if (doclear) {
                   2650:            doclear = 0;
                   2651:            if (globalconfig.utf8esc) (void) write(1, "\033%G", 3);
                   2652:            clear();
                   2653:        }
                   2654:        drawbanner(&ban);
                   2655:        if (menu->cursor_x >= 0 && menu->cursor_y >= 0)
                   2656:            mvprintw(menu->cursor_y, menu->cursor_x, "");
                   2657:        refresh();
                   2658:        userchoice = dgl_getch();
                   2659:        if (userchoice == ERR) {
                   2660:            freebanner(&ban);
                   2661:            return 1;
                   2662:        }
                   2663:        tmpopt = menu->options;
                   2664:        while (tmpopt) {
                   2665:            if (strchr(tmpopt->keys, userchoice)) {
                   2666:                dgl_exec_cmdqueue(tmpopt->cmdqueue, selected_game, me);
                   2667:                doclear = 1;
                   2668:                break;
                   2669:            } else {
                   2670:                tmpopt = tmpopt->next;
                   2671:            }
                   2672:        }
                   2673:
                   2674:        if (return_from_submenu) {
                   2675:            freebanner(&ban);
                   2676:            return_from_submenu = 0;
                   2677:            return 0;
                   2678:        }
                   2679:
                   2680:        if (check_retard(0)) {
                   2681:            freebanner(&ban);
                   2682:            debug_write("retard");
                   2683:            graceful_exit(119);
                   2684:        }
                   2685:     }
                   2686: }
                   2687:
                   2688: int
                   2689: main (int argc, char** argv)
                   2690: {
                   2691:   /* for chroot and program execution */
                   2692:     char atrcfilename[81], *p, *auth = NULL;
                   2693:   unsigned int len;
                   2694:   int c, i;
                   2695:   int userchoice;
                   2696:   char *tmp;
                   2697:   char *wall_email_str = NULL;
                   2698: #ifdef USE_RLIMIT
                   2699:   struct rlimit lim;
                   2700: #endif
                   2701:
                   2702: #ifndef HAVE_SETPROCTITLE
                   2703:   /* save argc, argv */
                   2704:   char** saved_argv;
                   2705:   int saved_argc;
                   2706:
                   2707:   saved_argc = argc;
                   2708:
                   2709:   saved_argv = malloc(sizeof(char**) * (argc + 1));
                   2710:   for (i = 0; i < argc; i++)
                   2711:     saved_argv[i] = strdup(argv[i]);
                   2712:   saved_argv[i] = '\0';
                   2713:
                   2714:   compat_init_setproctitle(argc, argv);
                   2715:   argv = saved_argv;
                   2716: #endif
                   2717:
                   2718:   p = getenv("DGLAUTH");
                   2719:
                   2720:   /* Linux telnetd allows importing the USER envvar via telnet,
                   2721:    * while FreeBSD does not.  FreeBSD, on the other hand, does allow
                   2722:    * the LOGNAME envvar.  Check USER first, then LOGNAME.
                   2723:    */
                   2724:   if (p == NULL) {
                   2725:       p = getenv("USER");
                   2726:   }
                   2727:
                   2728:   if (p == NULL) {
                   2729:       p = getenv("LOGNAME");
                   2730:   }
                   2731:
                   2732:   if (p && *p != '\0')
                   2733:     auth = strdup(p);
                   2734:   /* else auth is still NULL */
                   2735:
                   2736:   /* just to be sure */
                   2737:   unsetenv("DGLAUTH"); unsetenv("USER"); unsetenv("LOGNAME");
                   2738:
                   2739:   __progname = basename(strdup(argv[0]));
                   2740:
                   2741:   while ((c = getopt(argc, argv, "csqh:pi:aeW:SD")) != -1)
                   2742:   {
                   2743:       /* Stop processing arguments at -c, so that user-provided
                   2744:        * commands (via ssh for example) to the dgamelaunch login
                   2745:        * shell are ignored.
                   2746:        */
                   2747:     if (c == 'c') break;
                   2748:     switch (c)
                   2749:     {
                   2750:       case 's':
                   2751:        showplayers = 1; break;
                   2752:
                   2753:       case 'q':
                   2754:        silent = 1; break;
                   2755:
                   2756:       case 'i':
                   2757:        if (optarg && *optarg != '\0') {
                   2758:                if (p && *p != '\0')
                   2759:                        *p = '\0';
                   2760:
                   2761:                p = strdup(optarg);
                   2762:                initplayer = 1;
                   2763:
                   2764:                if (auth && *auth != '\0')
                   2765:                        *auth = '\0';
                   2766:        }
                   2767:        break;
                   2768:
                   2769:     case 'W':
                   2770:        wall_email_str = strdup(optarg);
                   2771:        break;
                   2772:
                   2773:     case 'S': /* Free the shared memory block */
                   2774: #ifdef USE_SHMEM
                   2775:        if (shm_free()) {
                   2776:            if (!silent) fprintf(stderr, "nonexistent shmem block.\n");
                   2777:        } else {
                   2778:            if (!silent) fprintf(stderr, "shmem block freed.\n");
                   2779:        }
                   2780: #else
                   2781:        if (!silent) fprintf(stderr, "warning: dgamelaunch was compiled without shmem.\n");
                   2782: #endif
                   2783:        graceful_exit(0);
                   2784:        break;
                   2785:
                   2786:     case 'D': /* dump the shared memory block data */
                   2787: #ifdef USE_SHMEM
                   2788:        shm_dump();
                   2789: #else
                   2790:        if (!silent) fprintf(stderr, "warning: dgamelaunch was compiled without shmem.\n");
                   2791: #endif
                   2792:        graceful_exit(0);
                   2793:        break;
                   2794:
                   2795:       default:
                   2796:        break; /*ignore */
                   2797:     }
                   2798:   }
                   2799:
                   2800:   while (optind < argc)
                   2801:   {
                   2802:     size_t len = strlen(argv[optind]);
                   2803:     memset(argv[optind++], 0, len);
                   2804:   }
                   2805:   setproctitle("<Anonymous>");
                   2806:
                   2807:   srand(time(0));
                   2808:
                   2809:   create_config();
                   2810:
                   2811:   /* signal handlers */
                   2812:   signal (SIGHUP, catch_sighup);
                   2813:   signal (SIGINT, catch_sighup);
                   2814:   signal (SIGQUIT, catch_sighup);
                   2815:   signal (SIGTERM, catch_sighup);
                   2816:
                   2817:   (void) tcgetattr (0, &tt);
                   2818:
                   2819:   if (!globalconfig.flowctrl) {
                   2820:       tt.c_iflag &= ~(IXON | IXOFF | IXANY); /* Disable XON/XOFF */
                   2821:       (void) tcsetattr(0, TCSANOW, &tt);
                   2822:   }
                   2823:
                   2824:   if (-1 == ioctl (0, TIOCGWINSZ, (char *) &win) || win.ws_row < 4 ||
                   2825:                  win.ws_col < 4) /* Rudimentary validity check */
                   2826:     {
                   2827:       win.ws_row = 24;
                   2828:       win.ws_col = 80;
                   2829:       win.ws_xpixel = win.ws_col * 8;
                   2830:       win.ws_ypixel = win.ws_row * 8;
                   2831:     }
                   2832:
                   2833:   /* get master tty just before chroot (lives in /dev) */
                   2834:   ttyrec_getpty ();
                   2835:
                   2836: #ifdef USE_RLIMIT
                   2837: #ifdef USE_RLIMIT_CORE
                   2838:   /* enable and set core dump size */
                   2839:   if (!getrlimit(RLIMIT_CORE, &lim)) {
                   2840:       lim.rlim_cur = USE_RLIMIT_CORE;
                   2841:       setrlimit(RLIMIT_CORE, &lim);
                   2842:   }
                   2843: #endif
                   2844: #ifdef USE_RLIMIT_AS
                   2845:   /* set maximum memory usage */
                   2846:   if (!getrlimit(RLIMIT_AS, &lim)) {
                   2847:       lim.rlim_cur = USE_RLIMIT_AS;
                   2848:       setrlimit(RLIMIT_AS, &lim);
                   2849:   }
                   2850: #endif
                   2851: #endif
                   2852:
                   2853:   if (geteuid () != globalconfig.shed_uid)
                   2854:     {
                   2855:       /* chroot */
                   2856:       if (chroot (globalconfig.chroot))
                   2857:        {
                   2858:          perror ("cannot change root directory");
                   2859:          graceful_exit (2);
                   2860:        }
                   2861:
                   2862:       if (chdir ("/"))
                   2863:        {
                   2864:          perror ("cannot chdir to root directory");
                   2865:          graceful_exit (3);
                   2866:        }
                   2867:
                   2868:       /* shed privs. this is done immediately after chroot. */
                   2869:       if (setgroups (1, &globalconfig.shed_gid) == -1)
                   2870:        {
                   2871:          perror ("setgroups");
                   2872:          graceful_exit (4);
                   2873:        }
                   2874:
                   2875:       if (setgid (globalconfig.shed_gid) == -1)
                   2876:        {
                   2877:          perror ("setgid");
                   2878:          graceful_exit (5);
                   2879:        }
                   2880:
                   2881:       if (setuid (globalconfig.shed_uid) == -1)
                   2882:        {
                   2883:          perror ("setuid");
                   2884:          graceful_exit (6);
                   2885:        }
                   2886:     }
                   2887:
                   2888:   if (globalconfig.locale) {
                   2889:       setlocale(LC_CTYPE, globalconfig.locale);
                   2890:   }
                   2891:
                   2892:   if (showplayers) {
                   2893:     inprogressdisplay(-1);
                   2894:     graceful_exit (0);
                   2895:   }
                   2896:
                   2897:   if (wall_email_str) {
                   2898:       char *emailfrom = wall_email_str;
                   2899:       char *emailmsg = strchr(wall_email_str, ':');
                   2900:       if (!emailmsg) {
                   2901:          debug_write("wall: no mail msg");
                   2902:          graceful_exit(117);
                   2903:       }
                   2904:       *emailmsg = '\0';
                   2905:       emailmsg++;
                   2906:       if (emailmsg)
                   2907:          wall_email(emailfrom, emailmsg);
                   2908:       graceful_exit(0);
                   2909:   }
                   2910:
                   2911:   banner.len = 0;
                   2912:   banner.lines = NULL;
                   2913:   loadbanner(globalconfig.banner, &banner);
                   2914:
                   2915:   dgl_exec_cmdqueue(globalconfig.cmdqueue[DGLTIME_DGLSTART], 0, NULL);
                   2916:
                   2917:   if (initplayer) {
                   2918:          char *user, *pass;
                   2919:
                   2920:          user = strdup(p);
                   2921:          pass = strdup(p);
                   2922:
                   2923:          autologin(user, pass);
                   2924:
                   2925:          if (loggedin) {
                   2926:                dgl_exec_cmdqueue(globalconfig.cmdqueue[DGLTIME_REGISTER], 0, me);
                   2927:                fprintf(stdout, "Setup of %s succeeded.\n", me->username);
                   2928:                graceful_exit(0);
                   2929:          }
                   2930:          else {
                   2931:                fprintf(stdout, "Setup of %s failed.\n", p);
                   2932:                graceful_exit(10);
                   2933:          }
                   2934:   }
                   2935:
                   2936:   /* simple login routine, uses ncurses */
                   2937:   if (readfile (0)) {
                   2938:       debug_write("log in fail");
                   2939:     graceful_exit (110);
                   2940:   }
                   2941:
                   2942:   if (auth)
                   2943:   {
                   2944:     char *user, *pass, *p;
                   2945:
                   2946:     p = strchr(auth, ':');
                   2947:
                   2948:     if (p)
                   2949:     {
                   2950:       pass = p + 1;
                   2951:
                   2952:       if (*pass != '\0')
                   2953:       {
                   2954:         *p = '\0';
                   2955:         user = auth;
                   2956:         autologin(user, pass);
                   2957:       }
                   2958:     }
                   2959:   }
                   2960:
                   2961:   initcurses ();
                   2962:
                   2963:   g_chain_winch = signal(SIGWINCH, sigwinch_func);
                   2964:
                   2965:   term_resize_check();
                   2966:
                   2967:   idle_alarm_set_enabled(1);
                   2968:
                   2969:   while (1) {
                   2970:       if (runmenuloop(dgl_find_menu(get_mainmenu_name())))
                   2971:          break;
                   2972:   }
                   2973:
                   2974:   idle_alarm_set_enabled(0);
                   2975:
                   2976:   /* NOW we can safely kill this */
                   2977:   freefile ();
                   2978:
                   2979:   if (me)
                   2980:     free (me);
                   2981:
                   2982:   freebanner(&banner);
                   2983:   banner_var_free();
                   2984:   graceful_exit (20);
                   2985:
                   2986:   return 1;
                   2987: }

CVSweb