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

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

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

CVSweb