summaryrefslogtreecommitdiffstats
path: root/tdm/kfrontend/tdm_config.c
diff options
context:
space:
mode:
Diffstat (limited to 'tdm/kfrontend/tdm_config.c')
-rw-r--r--tdm/kfrontend/tdm_config.c1477
1 files changed, 1477 insertions, 0 deletions
diff --git a/tdm/kfrontend/tdm_config.c b/tdm/kfrontend/tdm_config.c
new file mode 100644
index 000000000..48f316320
--- /dev/null
+++ b/tdm/kfrontend/tdm_config.c
@@ -0,0 +1,1477 @@
+/*
+
+Read options from tdmrc
+
+Copyright (C) 2001-2005 Oswald Buddenhagen <ossi@kde.org>
+
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <grp.h>
+#ifdef _POSIX_PRIORITY_SCHEDULING
+# include <sched.h>
+#endif
+
+#include <X11/X.h>
+#ifdef FamilyInternet6
+# define IPv6
+#endif
+
+#include <greet.h>
+#include <config.ci>
+
+/*
+ * Section/Entry definition structs
+ */
+
+typedef struct Ent {
+ const char *name;
+ int id;
+ void *ptr;
+ const char *def;
+} Ent;
+
+typedef struct Sect {
+ const char *name;
+ Ent *ents;
+ int numents;
+} Sect;
+
+/*
+ * Parsed ini file structs
+ */
+
+typedef struct Entry {
+ struct Entry *next;
+ const char *val;
+ Ent *ent;
+ int vallen;
+ int line;
+} Entry;
+
+typedef struct Section {
+ struct Section *next;
+ Entry *entries;
+ Sect *sect;
+ const char *name, *dname, *dhost, *dnum, *dclass;
+ int nlen, dlen, dhostl, dnuml, dclassl;
+} Section;
+
+
+/*
+ * Split up display-name/-class for fast comparison
+ */
+typedef struct DSpec {
+ const char *dhost, *dnum, *dclass;
+ int dhostl, dnuml, dclassl;
+} DSpec;
+
+
+/*
+ * Config value storage structures
+ */
+
+typedef struct Value {
+ const char *ptr;
+ int len;
+} Value;
+
+typedef struct Val {
+ Value val;
+ int id;
+} Val;
+
+typedef struct ValArr {
+ Val *ents;
+ int nents, esiz, nchars, nptrs;
+} ValArr;
+
+
+static void *Malloc( size_t size );
+static void *Realloc( void *ptr, size_t size );
+
+#define PRINT_QUOTES
+#define LOG_NAME "tdm_config"
+#define LOG_DEBUG_MASK DEBUG_CONFIG
+#define LOG_PANIC_EXIT 1
+#define STATIC static
+#include <printf.c>
+
+
+static void *
+Malloc( size_t size )
+{
+ void *ret;
+
+ if (!(ret = malloc( size )))
+ LogOutOfMem();
+ return ret;
+}
+
+static void *
+Realloc( void *ptr, size_t size )
+{
+ void *ret;
+
+ if (!(ret = realloc( ptr, size )) && size)
+ LogOutOfMem();
+ return ret;
+}
+
+
+static void
+MkDSpec( DSpec *spec, const char *dname, const char *dclass )
+{
+ spec->dhost = dname;
+ for (spec->dhostl = 0; dname[spec->dhostl] != ':'; spec->dhostl++);
+ spec->dnum = dname + spec->dhostl + 1;
+ spec->dnuml = strlen( spec->dnum );
+ spec->dclass = dclass;
+ spec->dclassl = strlen( dclass );
+}
+
+
+static int rfd, wfd;
+
+static int
+Reader( void *buf, int count )
+{
+ int ret, rlen;
+
+ for (rlen = 0; rlen < count; ) {
+ dord:
+ ret = read( rfd, (void *)((char *)buf + rlen), count - rlen );
+ if (ret < 0) {
+ if (errno == EINTR)
+ goto dord;
+ if (errno == EAGAIN)
+ break;
+ return -1;
+ }
+ if (!ret)
+ break;
+ rlen += ret;
+ }
+ return rlen;
+}
+
+static void
+GRead( void *buf, int count )
+{
+ if (Reader( buf, count ) != count)
+ LogPanic( "Can't read from core\n" );
+}
+
+static void
+GWrite( const void *buf, int count )
+{
+ if (write( wfd, buf, count ) != count)
+ LogPanic( "Can't write to core\n" );
+#ifdef _POSIX_PRIORITY_SCHEDULING
+ if ((debugLevel & DEBUG_HLPCON))
+ sched_yield();
+#endif
+}
+
+static void
+GSendInt( int val )
+{
+ GWrite( &val, sizeof(val) );
+}
+
+static void
+GSendStr( const char *buf )
+{
+ if (buf) {
+ int len = strlen( buf ) + 1;
+ GWrite( &len, sizeof(len) );
+ GWrite( buf, len );
+ } else
+ GWrite( &buf, sizeof(int));
+}
+
+static void
+GSendNStr( const char *buf, int len )
+{
+ int tlen = len + 1;
+ GWrite( &tlen, sizeof(tlen) );
+ GWrite( buf, len );
+ GWrite( "", 1 );
+}
+
+#ifdef XDMCP
+static void
+GSendArr( int len, const char *data )
+{
+ GWrite( &len, sizeof(len) );
+ GWrite( data, len );
+}
+#endif
+
+static int
+GRecvCmd( int *val )
+{
+ if (Reader( val, sizeof(*val) ) != sizeof(*val))
+ return 0;
+ return 1;
+}
+
+static int
+GRecvInt()
+{
+ int val;
+
+ GRead( &val, sizeof(val) );
+ return val;
+}
+
+static char *
+GRecvStr()
+{
+ int len;
+ char *buf;
+
+ len = GRecvInt();
+ if (!len)
+ return 0;
+ if (!(buf = malloc( len )))
+ LogPanic( "No memory for read buffer" );
+ GRead( buf, len );
+ return buf;
+}
+
+
+/* #define WANT_CLOSE 1 */
+
+typedef struct File {
+ char *buf, *eof, *cur;
+#if defined(HAVE_MMAP) && defined(WANT_CLOSE)
+ int ismapped;
+#endif
+} File;
+
+static int
+readFile( File *file, const char *fn, const char *what )
+{
+ int fd;
+ off_t flen;
+
+ if ((fd = open( fn, O_RDONLY )) < 0) {
+ LogInfo( "Cannot open %s file %s\n", what, fn );
+ return 0;
+ }
+
+ flen = lseek( fd, 0, SEEK_END );
+#ifdef HAVE_MMAP
+# ifdef WANT_CLOSE
+ file->ismapped = 0;
+# endif
+ file->buf = mmap( 0, flen + 1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0 );
+# ifdef WANT_CLOSE
+ if (file->buf)
+ file->ismapped = 1;
+ else
+# else
+ if (!file->buf)
+# endif
+#endif
+ {
+ if (!(file->buf = Malloc( flen + 1 ))) {
+ close( fd );
+ return 0;
+ }
+ lseek( fd, 0, SEEK_SET );
+ if (read( fd, file->buf, flen ) != flen) {
+ free( file->buf );
+ LogError( "Cannot read %s file %s\n", what, fn );
+ close( fd );
+ return 0;
+ }
+ }
+ file->eof = (file->cur = file->buf) + flen;
+ close( fd );
+ return 1;
+}
+
+#ifdef WANT_CLOSE
+static void
+freeBuf( File *file )
+{
+# ifdef HAVE_MMAP
+ if (file->ismapped)
+ munmap( file->buf, file->eof - file->buf + 1 );
+ else
+# endif
+ free( file->buf );
+}
+#endif
+
+CONF_READ_VARS
+
+#define C_MTYPE_MASK 0x30000000
+# define C_PATH 0x10000000 /* C_TYPE_STR is a path spec */
+# define C_BOOL 0x10000000 /* C_TYPE_INT is a boolean */
+# define C_ENUM 0x20000000 /* C_TYPE_INT is an enum (option) */
+# define C_GRP 0x30000000 /* C_TYPE_INT is a group spec */
+#define C_INTERNAL 0x40000000 /* don't expose to core */
+#define C_CONFIG 0x80000000 /* process only for finding deps */
+
+#ifdef XDMCP
+static int
+PrequestPort( Value *retval )
+{
+ if (!VxdmcpEnable.ptr) {
+ retval->ptr = (char *)0;
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+static Value
+ emptyStr = { "", 1 },
+ nullValue = { 0, 0 },
+ emptyArgv = { (char *)&nullValue, 0 };
+
+static int
+PnoPassUsers( Value *retval )
+{
+ if (!VnoPassEnable.ptr) {
+ *retval = emptyArgv;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+PautoLoginX( Value *retval )
+{
+ if (!VautoLoginEnable.ptr) {
+ *retval = emptyStr;
+ return 1;
+ }
+ return 0;
+}
+
+CONF_READ_ENTRIES
+
+static const char *tdmrc = TDMCONF "/tdmrc";
+static const char *tdmrc_dist = TDMCONF "/tdmdistrc";
+
+static Section *rootsec;
+
+static void
+ReadConf()
+{
+ const char *nstr, *dstr, *cstr, *dhost, *dnum, *dclass;
+ char *s, *e, *st, *en, *ek, *sl, *pt;
+ Section *cursec;
+ Entry *curent;
+ Ent *ce;
+ int nlen, dlen, clen, dhostl, dnuml, dclassl;
+ int i, line, sectmoan, restl;
+ File file;
+ static int confread;
+
+ if (confread)
+ return;
+ confread = 1;
+
+ Debug( "reading config %s ...\n", tdmrc_dist );
+ if (!readFile( &file, tdmrc_dist, "master configuration" )) {
+ Debug( "reading config %s ...\n", tdmrc );
+ if (!readFile( &file, tdmrc, "master configuration" ))
+ return;
+ }
+ else {
+ tdmrc = tdmrc_dist;
+ }
+
+ for (s = file.buf, line = 0, cursec = 0, sectmoan = 1; s < file.eof; s++) {
+ line++;
+
+ while ((s < file.eof) && isspace( *s ) && (*s != '\n'))
+ s++;
+
+ if ((s < file.eof) && ((*s == '\n') || (*s == '#'))) {
+ sktoeol:
+ while ((s < file.eof) && (*s != '\n'))
+ s++;
+ continue;
+ }
+ sl = s;
+
+ if (*s == '[') {
+ sectmoan = 0;
+ while ((s < file.eof) && (*s != '\n'))
+ s++;
+ e = s - 1;
+ while ((e > sl) && isspace( *e ))
+ e--;
+ if (*e != ']') {
+ cursec = 0;
+ LogError( "Invalid section header at %s:%d\n", tdmrc, line );
+ continue;
+ }
+ nstr = sl + 1;
+ nlen = e - nstr;
+ for (cursec = rootsec; cursec; cursec = cursec->next)
+ if (nlen == cursec->nlen &&
+ !memcmp( nstr, cursec->name, nlen ))
+ {
+ LogInfo( "Multiple occurrences of section [%.*s] in %s. "
+ "Consider merging them.\n", nlen, nstr, tdmrc );
+ goto secfnd;
+ }
+ if (nstr[0] == 'X' && nstr[1] == '-') {
+ cstr = nstr + nlen;
+ clen = 0;
+ while (++clen, *--cstr != '-');
+ if (cstr == nstr + 1)
+ goto illsec;
+ dstr = nstr + 2;
+ dlen = nlen - clen - 2;
+ dhost = dstr;
+ dhostl = 0;
+ for (restl = dlen; restl; restl--) {
+ if (dhost[dhostl] == ':') {
+ dnum = dhost + dhostl + 1;
+ dnuml = 0;
+ for (restl--; restl; restl--) {
+ if (dnum[dnuml] == '_') {
+ dclass = dnum + dnuml + 1;
+ dclassl = restl;
+ goto gotall;
+ }
+ dnuml++;
+ }
+ goto gotnum;
+ }
+ dhostl++;
+ }
+ dnum = "*";
+ dnuml = 1;
+ gotnum:
+ dclass = "*";
+ dclassl = 1;
+ gotall: ;
+ } else {
+ if (nstr[0] == '-')
+ goto illsec;
+ dstr = 0;
+ dlen = 0;
+ dhost = 0;
+ dhostl = 0;
+ dnum = 0;
+ dnuml = 0;
+ dclass = 0;
+ dclassl = 0;
+ cstr = nstr;
+ clen = nlen;
+ }
+ for (i = 0; i < as(allSects); i++)
+ if ((int)strlen( allSects[i]->name ) == clen &&
+ !memcmp( allSects[i]->name, cstr, clen ))
+ goto newsec;
+ illsec:
+ cursec = 0;
+ LogError( "Unrecognized section name [%.*s] at %s:%d\n",
+ nlen, nstr, tdmrc, line );
+ continue;
+ newsec:
+ if (!(cursec = Malloc( sizeof(*cursec) )))
+ return;
+ cursec->name = nstr;
+ cursec->nlen = nlen;
+ cursec->dname = dstr;
+ cursec->dlen = dlen;
+ cursec->dhost = dhost;
+ cursec->dhostl = dhostl;
+ cursec->dnum = dnum;
+ cursec->dnuml = dnuml;
+ cursec->dclass = dclass;
+ cursec->dclassl = dclassl;
+ cursec->sect = allSects[i];
+ cursec->entries = 0;
+ cursec->next = rootsec;
+ rootsec = cursec;
+ /*Debug( "now in section [%.*s], dpy '%.*s', core '%.*s'\n",
+ nlen, nstr, dlen, dstr, clen, cstr );*/
+ secfnd:
+ continue;
+ }
+
+ if (!cursec) {
+ if (sectmoan) {
+ sectmoan = 0;
+ LogError( "Entry outside any section at %s:%d", tdmrc, line );
+ }
+ goto sktoeol;
+ }
+
+ for (; (s < file.eof) && (*s != '\n'); s++)
+ if (*s == '=')
+ goto haveeq;
+ LogError( "Invalid entry (missing '=') at %s:%d\n", tdmrc, line );
+ continue;
+
+ haveeq:
+ for (ek = s - 1; ; ek--) {
+ if (ek < sl) {
+ LogError( "Invalid entry (empty key) at %s:%d\n", tdmrc, line );
+ goto sktoeol;
+ }
+ if (!isspace( *ek ))
+ break;
+ }
+
+ s++;
+ while ((s < file.eof) && isspace( *s ) && (*s != '\n'))
+ s++;
+ for (pt = st = en = s; s < file.eof && *s != '\n'; s++) {
+ if (*s == '\\') {
+ s++;
+ if (s >= file.eof || *s == '\n') {
+ LogError( "Trailing backslash at %s:%d\n", tdmrc, line );
+ break;
+ }
+ switch (*s) {
+ case 's': *pt++ = ' '; break;
+ case 't': *pt++ = '\t'; break;
+ case 'n': *pt++ = '\n'; break;
+ case 'r': *pt++ = '\r'; break;
+ case '\\': *pt++ = '\\'; break;
+ default: *pt++ = '\\'; *pt++ = *s; break;
+ }
+ en = pt;
+ } else {
+ *pt++ = *s;
+ if (*s != ' ' && *s != '\t')
+ en = pt;
+ }
+ }
+
+ nstr = sl;
+ nlen = ek - sl + 1;
+ /*Debug( "read entry '%.*s'='%.*s'\n", nlen, nstr, en - st, st );*/
+ for (i = 0; i < cursec->sect->numents; i++) {
+ ce = cursec->sect->ents + i;
+ if ((int)strlen( ce->name ) == nlen &&
+ !memcmp( ce->name, nstr, nlen ))
+ goto keyok;
+ }
+ LogError( "Unrecognized key '%.*s' in section [%.*s] at %s:%d\n",
+ nlen, nstr, cursec->nlen, cursec->name, tdmrc, line );
+ continue;
+ keyok:
+ for (curent = cursec->entries; curent; curent = curent->next)
+ if (ce == curent->ent) {
+ LogError( "Multiple occurrences of key '%s' in section [%.*s]"
+ " of %s\n",
+ ce->name, cursec->nlen, cursec->name, tdmrc );
+ goto keyfnd;
+ }
+ if (!(curent = Malloc( sizeof(*curent) )))
+ return;
+ curent->ent = ce;
+ curent->line = line;
+ curent->val = st;
+ curent->vallen = en - st;
+ curent->next = cursec->entries;
+ cursec->entries = curent;
+ keyfnd:
+ continue;
+ }
+}
+
+static Entry *
+FindGEnt( int id )
+{
+ Section *cursec;
+ Entry *curent;
+
+ for (cursec = rootsec; cursec; cursec = cursec->next)
+ if (!cursec->dname)
+ for (curent = cursec->entries; curent; curent = curent->next)
+ if (curent->ent->id == id) {
+ Debug( "line %d: %s = %'.*s\n",
+ curent->line, curent->ent->name,
+ curent->vallen, curent->val );
+ return curent;
+ }
+ return 0;
+}
+
+/* Display name match scoring:
+ * - class (any/exact) -> 0/1
+ * - number (any/exact) -> 0/2
+ * - host (any/nonempty/trail/exact) -> 0/4/8/12
+ */
+static Entry *
+FindDEnt( int id, DSpec *dspec )
+{
+ Section *cursec, *bestsec;
+ Entry *curent, *bestent;
+ int score, bestscore;
+
+ bestscore = -1, bestent = 0;
+ for (cursec = rootsec; cursec; cursec = cursec->next)
+ if (cursec->dname) {
+ score = 0;
+ if (cursec->dclassl != 1 || cursec->dclass[0] != '*') {
+ if (cursec->dclassl == dspec->dclassl &&
+ !memcmp( cursec->dclass, dspec->dclass, dspec->dclassl ))
+ score = 1;
+ else
+ continue;
+ }
+ if (cursec->dnuml != 1 || cursec->dnum[0] != '*') {
+ if (cursec->dnuml == dspec->dnuml &&
+ !memcmp( cursec->dnum, dspec->dnum, dspec->dnuml ))
+ score += 2;
+ else
+ continue;
+ }
+ if (cursec->dhostl != 1 || cursec->dhost[0] != '*') {
+ if (cursec->dhostl == 1 && cursec->dhost[0] == '+') {
+ if (dspec->dhostl)
+ score += 4;
+ else
+ continue;
+ } else if (cursec->dhost[0] == '.') {
+ if (cursec->dhostl < dspec->dhostl &&
+ !memcmp( cursec->dhost,
+ dspec->dhost + dspec->dhostl - cursec->dhostl,
+ cursec->dhostl ))
+ score += 8;
+ else
+ continue;
+ } else {
+ if (cursec->dhostl == dspec->dhostl &&
+ !memcmp( cursec->dhost, dspec->dhost, dspec->dhostl ))
+ score += 12;
+ else
+ continue;
+ }
+ }
+ if (score > bestscore) {
+ for (curent = cursec->entries; curent; curent = curent->next)
+ if (curent->ent->id == id) {
+ bestent = curent;
+ bestsec = cursec;
+ bestscore = score;
+ break;
+ }
+ }
+ }
+ if (bestent)
+ Debug( "line %d: %.*s:%.*s_%.*s/%s = %'.*s\n", bestent->line,
+ bestsec->dhostl, bestsec->dhost,
+ bestsec->dnuml, bestsec->dnum,
+ bestsec->dclassl, bestsec->dclass,
+ bestent->ent->name, bestent->vallen, bestent->val );
+ return bestent;
+}
+
+static const char *
+CvtValue( Ent *et, Value *retval, int vallen, const char *val, char **eopts )
+{
+ Value *ents;
+ int i, b, e, tlen, nents, esiz;
+ char buf[80];
+
+ switch (et->id & C_TYPE_MASK) {
+ case C_TYPE_INT:
+ for (i = 0; i < vallen && i < (int)sizeof(buf) - 1; i++)
+ buf[i] = tolower( val[i] );
+ buf[i] = 0;
+ if ((et->id & C_MTYPE_MASK) == C_BOOL) {
+ if (!strcmp( buf, "true" ) ||
+ !strcmp( buf, "on" ) ||
+ !strcmp( buf, "yes" ) ||
+ !strcmp( buf, "1" ))
+ retval->ptr = (char *)1;
+ else if (!strcmp( buf, "false" ) ||
+ !strcmp( buf, "off" ) ||
+ !strcmp( buf, "no" ) ||
+ !strcmp( buf, "0" ))
+ retval->ptr = (char *)0;
+ else
+ return "boolean";
+ return 0;
+ } else if ((et->id & C_MTYPE_MASK) == C_ENUM) {
+ for (i = 0; eopts[i]; i++)
+ if (!memcmp( eopts[i], val, vallen ) && !eopts[i][vallen]) {
+ retval->ptr = (char *)i;
+ return 0;
+ }
+ return "option";
+ } else if ((et->id & C_MTYPE_MASK) == C_GRP) {
+ struct group *ge;
+ if ((ge = getgrnam( buf ))) {
+ retval->ptr = (char *)ge->gr_gid;
+ return 0;
+ }
+ }
+ retval->ptr = 0;
+ if (sscanf( buf, "%li", &retval->ptr ) != 1)
+ return "integer";
+ return 0;
+ case C_TYPE_STR:
+ retval->ptr = val;
+ retval->len = vallen + 1;
+ if ((et->id & C_MTYPE_MASK) == C_PATH)
+ if (vallen && val[vallen-1] == '/')
+ retval->len--;
+ return 0;
+ case C_TYPE_ARGV:
+ if (!(ents = Malloc( sizeof(Value) * (esiz = 10) )))
+ return 0;
+ for (nents = 0, tlen = 0, i = 0; ; i++) {
+ for (; i < vallen && isspace( val[i] ); i++);
+ for (b = i; i < vallen && val[i] != ','; i++);
+ if (b == i)
+ break;
+ for (e = i; e > b && isspace( val[e - 1] ); e--);
+ if (esiz < nents + 2) {
+ Value *entsn = Realloc( ents,
+ sizeof(Value) * (esiz = esiz * 2 + 1) );
+ if (!nents)
+ break;
+ ents = entsn;
+ }
+ ents[nents].ptr = val + b;
+ ents[nents].len = e - b;
+ nents++;
+ tlen += e - b + 1;
+ }
+ ents[nents].ptr = 0;
+ retval->ptr = (char *)ents;
+ retval->len = tlen;
+ return 0;
+ default:
+ LogError( "Internal error: unknown value type in id %#x\n", et->id );
+ return 0;
+ }
+}
+
+static void
+GetValue( Ent *et, DSpec *dspec, Value *retval, char **eopts )
+{
+ Entry *ent;
+ const char *errs;
+
+/* Debug( "Getting value %#x\n", et->id );*/
+ if (dspec)
+ ent = FindDEnt( et->id, dspec );
+ else
+ ent = FindGEnt( et->id );
+ if (ent) {
+ if (!(errs = CvtValue( et, retval, ent->vallen, ent->val, eopts )))
+ return;
+ LogError( "Invalid %s value '%.*s' at %s:%d\n",
+ errs, ent->vallen, ent->val, tdmrc, ent->line );
+ }
+ Debug( "default: %s = %'s\n", et->name, et->def );
+ if ((errs = CvtValue( et, retval, strlen( et->def ), et->def, eopts )))
+ LogError( "Internal error: invalid default %s value '%s' for key %s\n",
+ errs, et->def, et->name );
+}
+
+static int
+AddValue( ValArr *va, int id, Value *val )
+{
+ int nu;
+
+/* Debug( "Addig value %#x\n", id );*/
+ if (va->nents == va->esiz) {
+ va->ents = Realloc( va->ents, sizeof(Val) * (va->esiz += 50) );
+ if (!va->ents)
+ return 0;
+ }
+ va->ents[va->nents].id = id;
+ va->ents[va->nents].val = *val;
+ va->nents++;
+ switch (id & C_TYPE_MASK) {
+ case C_TYPE_INT:
+ break;
+ case C_TYPE_STR:
+ va->nchars += val->len;
+ break;
+ case C_TYPE_ARGV:
+ va->nchars += val->len;
+ for (nu = 0; ((Value *)val->ptr)[nu++].ptr; );
+ va->nptrs += nu;
+ break;
+ }
+ return 1;
+}
+
+static void
+CopyValues( ValArr *va, Sect *sec, DSpec *dspec, int isconfig )
+{
+ Value val;
+ int i;
+
+ Debug( "getting values for section class [%s]\n", sec->name );
+ for (i = 0; i < sec->numents; i++) {
+/*Debug ("value %#x\n", sec->ents[i].id);*/
+ if ((sec->ents[i].id & (int)C_CONFIG) != isconfig)
+ ;
+ else if (sec->ents[i].id & C_INTERNAL) {
+ GetValue( sec->ents + i, dspec, ((Value *)sec->ents[i].ptr), 0 );
+ } else {
+ if (((sec->ents[i].id & C_MTYPE_MASK) == C_ENUM) ||
+ !sec->ents[i].ptr ||
+ !((int (*)( Value * ))sec->ents[i].ptr)(&val)) {
+ GetValue( sec->ents + i, dspec, &val,
+ (char **)sec->ents[i].ptr );
+ }
+ if (!AddValue( va, sec->ents[i].id, &val ))
+ break;
+ }
+ }
+ return;
+}
+
+static void
+SendValues( ValArr *va )
+{
+ Value *cst;
+ int i, nu;
+
+ GSendInt( va->nents );
+ GSendInt( va->nptrs );
+ GSendInt( 0/*va->nints*/ );
+ GSendInt( va->nchars );
+ for (i = 0; i < va->nents; i++) {
+ GSendInt( va->ents[i].id & ~C_PRIVATE );
+ switch (va->ents[i].id & C_TYPE_MASK) {
+ case C_TYPE_INT:
+ GSendInt( (int)va->ents[i].val.ptr );
+ break;
+ case C_TYPE_STR:
+ GSendNStr( va->ents[i].val.ptr, va->ents[i].val.len - 1 );
+ break;
+ case C_TYPE_ARGV:
+ cst = (Value *)va->ents[i].val.ptr;
+ for (nu = 0; cst[nu].ptr; nu++);
+ GSendInt( nu );
+ for (; cst->ptr; cst++)
+ GSendNStr( cst->ptr, cst->len );
+ break;
+ }
+ }
+}
+
+
+#ifdef XDMCP
+static char *
+ReadWord( File *file, int *len, int EOFatEOL )
+{
+ char *wordp, *wordBuffer;
+ int quoted;
+ char c;
+
+ rest:
+ wordp = wordBuffer = file->cur;
+ mloop:
+ quoted = 0;
+ qloop:
+ if (file->cur == file->eof) {
+ doeow:
+ if (wordp == wordBuffer)
+ return 0;
+ retw:
+ *wordp = '\0';
+ *len = wordp - wordBuffer;
+ return wordBuffer;
+ }
+ c = *file->cur++;
+ switch (c) {
+ case '#':
+ if (quoted)
+ break;
+ do {
+ if (file->cur == file->eof)
+ goto doeow;
+ c = *file->cur++;
+ } while (c != '\n');
+ case '\0':
+ case '\n':
+ if (EOFatEOL && !quoted) {
+ file->cur--;
+ goto doeow;
+ }
+ if (wordp != wordBuffer) {
+ file->cur--;
+ goto retw;
+ }
+ goto rest;
+ case ' ':
+ case '\t':
+ if (wordp != wordBuffer)
+ goto retw;
+ goto rest;
+ case '\\':
+ if (!quoted) {
+ quoted = 1;
+ goto qloop;
+ }
+ break;
+ }
+ *wordp++ = c;
+ goto mloop;
+}
+
+#define ALIAS_CHARACTER '%'
+#define EQUAL_CHARACTER '='
+#define NEGATE_CHARACTER '!'
+#define CHOOSER_STRING "CHOOSER"
+#define BROADCAST_STRING "BROADCAST"
+#define NOBROADCAST_STRING "NOBROADCAST"
+#define LISTEN_STRING "LISTEN"
+#define WILDCARD_STRING "*"
+
+typedef struct hostEntry {
+ struct hostEntry *next;
+ int type;
+ union _hostOrAlias {
+ char *aliasPattern;
+ char *hostPattern;
+ struct _display {
+ int connectionType;
+ int hostAddrLen;
+ char *hostAddress;
+ } displayAddress;
+ } entry;
+} HostEntry;
+
+typedef struct listenEntry {
+ struct listenEntry *next;
+ int iface;
+ int mcasts;
+ int nmcasts;
+} ListenEntry;
+
+typedef struct aliasEntry {
+ struct aliasEntry *next;
+ char *name;
+ HostEntry **pHosts;
+ int hosts;
+ int nhosts;
+ int hasBad;
+} AliasEntry;
+
+typedef struct aclEntry {
+ struct aclEntry *next;
+ HostEntry **pEntries;
+ int entries;
+ int nentries;
+ HostEntry **pHosts;
+ int hosts;
+ int nhosts;
+ int flags;
+} AclEntry;
+
+
+static int
+HasGlobCharacters( char *s )
+{
+ for (;;)
+ switch (*s++) {
+ case '?':
+ case '*':
+ return 1;
+ case '\0':
+ return 0;
+ }
+}
+
+#define PARSE_ALL 0
+#define PARSE_NO_BCAST 1
+#define PARSE_NO_PAT 2
+#define PARSE_NO_ALIAS 4
+
+static int
+ParseHost( int *nHosts, HostEntry ***hostPtr, int *nChars,
+ char *hostOrAlias, int len, int parse )
+{
+#if defined(IPv6) && defined(AF_INET6)
+ struct addrinfo *ai;
+#else
+ struct hostent *hostent;
+#endif
+ void *addr;
+ int addr_type, addr_len;
+
+ if (!(**hostPtr = (HostEntry *)Malloc( sizeof(HostEntry))))
+ return 0;
+ if (!(parse & PARSE_NO_BCAST) && !strcmp( hostOrAlias, BROADCAST_STRING ))
+ {
+ (**hostPtr)->type = HOST_BROADCAST;
+ }
+ else if (!(parse & PARSE_NO_ALIAS) && *hostOrAlias == ALIAS_CHARACTER)
+ {
+ (**hostPtr)->type = HOST_ALIAS;
+ (**hostPtr)->entry.aliasPattern = hostOrAlias + 1;
+ *nChars += len;
+ }
+ else if (!(parse & PARSE_NO_PAT) && HasGlobCharacters( hostOrAlias ))
+ {
+ (**hostPtr)->type = HOST_PATTERN;
+ (**hostPtr)->entry.hostPattern = hostOrAlias;
+ *nChars += len + 1;
+ }
+ else
+ {
+ (**hostPtr)->type = HOST_ADDRESS;
+#if defined(IPv6) && defined(AF_INET6)
+ if (getaddrinfo( hostOrAlias, NULL, NULL, &ai ))
+#else
+ if (!(hostent = gethostbyname( hostOrAlias )))
+#endif
+ {
+ LogWarn( "XDMCP ACL: unresolved host %'s\n", hostOrAlias );
+ free( (char *)(**hostPtr) );
+ return 0;
+ }
+#if defined(IPv6) && defined(AF_INET6)
+ addr_type = ai->ai_addr->sa_family;
+ if (ai->ai_family == AF_INET) {
+ addr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr;
+ addr_len = sizeof(struct in_addr);
+ } else /*if (ai->ai_addr->sa_family == AF_INET6)*/ {
+ addr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
+ addr_len = sizeof(struct in6_addr);
+ }
+#else
+ addr_type = hostent->h_addrtype;
+ addr = hostent->h_addr;
+ addr_len = hostent->h_length;
+#endif
+ if (!((**hostPtr)->entry.displayAddress.hostAddress =
+ Malloc( addr_len )))
+ {
+#if defined(IPv6) && defined(AF_INET6)
+ freeaddrinfo( ai );
+#endif
+ free( (char *)(**hostPtr) );
+ return 0;
+ }
+ memcpy( (**hostPtr)->entry.displayAddress.hostAddress, addr, addr_len );
+ *nChars += addr_len;
+ (**hostPtr)->entry.displayAddress.hostAddrLen = addr_len;
+ (**hostPtr)->entry.displayAddress.connectionType = addr_type;
+#if defined(IPv6) && defined(AF_INET6)
+ freeaddrinfo( ai );
+#endif
+ }
+ *hostPtr = &(**hostPtr)->next;
+ (*nHosts)++;
+ return 1;
+}
+
+/* Returns non-0 if string is matched by pattern. Does case folding. */
+static int
+patternMatch( const char *string, const char *pattern )
+{
+ int p, s;
+
+ if (!string)
+ string = "";
+
+ for (;;) {
+ s = *string++;
+ switch (p = *pattern++) {
+ case '*':
+ if (!*pattern)
+ return 1;
+ for (string--; *string; string++)
+ if (patternMatch( string, pattern ))
+ return 1;
+ return 0;
+ case '?':
+ if (s == '\0')
+ return 0;
+ break;
+ case '\0':
+ return s == '\0';
+ case '\\':
+ p = *pattern++;
+ /* fall through */
+ default:
+ if (tolower( p ) != tolower( s ))
+ return 0;
+ }
+ }
+}
+
+#define MAX_DEPTH 32
+
+#define CHECK_NOT 1
+#define CHECK_NO_PAT 2
+
+static int
+checkHostlist( HostEntry **hosts, int nh, AliasEntry *aliases, int na,
+ int depth, int flags )
+{
+ HostEntry *h;
+ AliasEntry *a;
+ int hn, an, am;
+
+ for (h = *hosts, hn = 0; hn < nh; hn++, h = h->next)
+ if (h->type == HOST_ALIAS) {
+ if (depth == MAX_DEPTH) {
+ LogError( "XDMCP ACL: alias recursion involving %%%s\n",
+ h->entry.aliasPattern );
+ return 1;
+ }
+ for (a = aliases, an = 0, am = 0; an < na; an++, a = a->next)
+ if (patternMatch( a->name, h->entry.aliasPattern )) {
+ am = 1;
+ if ((flags & CHECK_NOT) && a->hasBad) {
+ LogError( "XDMCP ACL: alias %%%s with unresolved hosts "
+ "in denying rule\n", a->name );
+ return 1;
+ }
+ if (checkHostlist( a->pHosts, a->nhosts, aliases, na,
+ depth + 1, flags ))
+ return 1;
+ }
+ if (!am) {
+ if (flags & CHECK_NOT) {
+ LogError( "XDMCP ACL: unresolved alias pattern %%%s "
+ "in denying rule\n", h->entry.aliasPattern );
+ return 1;
+ } else
+ LogWarn( "XDMCP ACL: unresolved alias pattern %%%s\n",
+ h->entry.aliasPattern );
+ }
+ } else if (h->type == HOST_PATTERN && (flags & CHECK_NO_PAT))
+ LogWarn( "XDMCP ACL: wildcarded pattern %'s in host-only context\n",
+ h->entry.hostPattern );
+ return 0;
+}
+
+static void
+ReadAccessFile( const char *fname )
+{
+ HostEntry *hostList, **hostPtr = &hostList;
+ AliasEntry *aliasList, **aliasPtr = &aliasList;
+ AclEntry *acList, **acPtr = &acList, *acl;
+ ListenEntry *listenList, **listenPtr = &listenList;
+ char *displayOrAlias, *hostOrAlias;
+ File file;
+ int nHosts, nAliases, nAcls, nListens, nChars, error, bad;
+ int i, len;
+
+ nHosts = nAliases = nAcls = nListens = nChars = error = 0;
+ if (!readFile( &file, fname, "XDMCP access control" ))
+ goto sendacl;
+ while ((displayOrAlias = ReadWord( &file, &len, FALSE ))) {
+ if (*displayOrAlias == ALIAS_CHARACTER)
+ {
+ if (!(*aliasPtr = (AliasEntry *)Malloc( sizeof(AliasEntry)))) {
+ error = 1;
+ break;
+ }
+ (*aliasPtr)->name = displayOrAlias + 1;
+ nChars += len;
+ (*aliasPtr)->hosts = nHosts;
+ (*aliasPtr)->pHosts = hostPtr;
+ (*aliasPtr)->nhosts = 0;
+ (*aliasPtr)->hasBad = 0;
+ while ((hostOrAlias = ReadWord( &file, &len, TRUE ))) {
+ if (ParseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len,
+ PARSE_NO_BCAST ))
+ (*aliasPtr)->nhosts++;
+ else
+ (*aliasPtr)->hasBad = 1;
+ }
+ aliasPtr = &(*aliasPtr)->next;
+ nAliases++;
+ }
+ else if (!strcmp( displayOrAlias, LISTEN_STRING ))
+ {
+ if (!(*listenPtr = (ListenEntry *)Malloc( sizeof(ListenEntry)))) {
+ error = 1;
+ break;
+ }
+ (*listenPtr)->iface = nHosts;
+ if (!(hostOrAlias = ReadWord( &file, &len, TRUE )) ||
+ !strcmp( hostOrAlias, WILDCARD_STRING ) ||
+ !ParseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len,
+ PARSE_NO_BCAST|PARSE_NO_PAT|PARSE_NO_ALIAS ))
+ {
+ (*listenPtr)->iface = -1;
+ }
+ (*listenPtr)->mcasts = nHosts;
+ (*listenPtr)->nmcasts = 0;
+ while ((hostOrAlias = ReadWord( &file, &len, TRUE ))) {
+ if (ParseHost( &nHosts, &hostPtr, &nChars, hostOrAlias, len,
+ PARSE_NO_BCAST|PARSE_NO_PAT|PARSE_NO_ALIAS ))
+ (*listenPtr)->nmcasts++;
+ }
+ listenPtr = &(*listenPtr)->next;
+ nListens++;
+ }
+ else
+ {
+ if (!(*acPtr = (AclEntry *)Malloc( sizeof(AclEntry)))) {
+ error = 1;
+ break;
+ }
+ (*acPtr)->flags = 0;
+ if (*displayOrAlias == NEGATE_CHARACTER) {
+ (*acPtr)->flags |= a_notAllowed;
+ displayOrAlias++;
+ } else if (*displayOrAlias == EQUAL_CHARACTER)
+ displayOrAlias++;
+ (*acPtr)->entries = nHosts;
+ (*acPtr)->pEntries = hostPtr;
+ (*acPtr)->nentries = 1;
+ if (!ParseHost( &nHosts, &hostPtr, &nChars, displayOrAlias, len,
+ PARSE_NO_BCAST ))
+ {
+ bad = 1;
+ if ((*acPtr)->flags & a_notAllowed) {
+ LogError( "XDMCP ACL: unresolved host in denying rule\n" );
+ error = 1;
+ }
+ } else
+ bad = 0;
+ (*acPtr)->hosts = nHosts;
+ (*acPtr)->pHosts = hostPtr;
+ (*acPtr)->nhosts = 0;
+ while ((hostOrAlias = ReadWord( &file, &len, TRUE ))) {
+ if (!strcmp( hostOrAlias, CHOOSER_STRING ))
+ (*acPtr)->flags |= a_useChooser;
+ else if (!strcmp( hostOrAlias, NOBROADCAST_STRING ))
+ (*acPtr)->flags |= a_notBroadcast;
+ else {
+ if (ParseHost( &nHosts, &hostPtr, &nChars,
+ hostOrAlias, len, PARSE_NO_PAT ))
+ (*acPtr)->nhosts++;
+ }
+ }
+ if (!bad) {
+ acPtr = &(*acPtr)->next;
+ nAcls++;
+ }
+ }
+ }
+
+ if (!nListens) {
+ if (!(*listenPtr = (ListenEntry *)Malloc( sizeof(ListenEntry))))
+ error = 1;
+ else {
+ (*listenPtr)->iface = -1;
+ (*listenPtr)->mcasts = nHosts;
+ (*listenPtr)->nmcasts = 0;
+#if defined(IPv6) && defined(AF_INET6) && defined(XDM_DEFAULT_MCAST_ADDR6)
+ if (ParseHost( &nHosts, &hostPtr, &nChars,
+ XDM_DEFAULT_MCAST_ADDR6,
+ sizeof(XDM_DEFAULT_MCAST_ADDR6)-1,
+ PARSE_ALL ))
+ (*listenPtr)->nmcasts++;
+#endif
+ nListens++;
+ }
+ }
+
+ for (acl = acList, i = 0; i < nAcls; i++, acl = acl->next)
+ if (checkHostlist( acl->pEntries, acl->nentries, aliasList, nAliases,
+ 0, (acl->flags & a_notAllowed) ? CHECK_NOT : 0 ) ||
+ checkHostlist( acl->pHosts, acl->nhosts, aliasList, nAliases,
+ 0, CHECK_NO_PAT ))
+ error = 1;
+
+ if (error) {
+ nHosts = nAliases = nAcls = nListens = nChars = 0;
+ sendacl:
+ LogError( "No XDMCP requests will be granted\n" );
+ }
+ GSendInt( nHosts );
+ GSendInt( nListens );
+ GSendInt( nAliases );
+ GSendInt( nAcls );
+ GSendInt( nChars );
+ for (i = 0; i < nHosts; i++, hostList = hostList->next) {
+ GSendInt( hostList->type );
+ switch (hostList->type) {
+ case HOST_ALIAS:
+ GSendStr( hostList->entry.aliasPattern );
+ break;
+ case HOST_PATTERN:
+ GSendStr( hostList->entry.hostPattern );
+ break;
+ case HOST_ADDRESS:
+ GSendArr( hostList->entry.displayAddress.hostAddrLen,
+ hostList->entry.displayAddress.hostAddress );
+ GSendInt( hostList->entry.displayAddress.connectionType );
+ break;
+ }
+ }
+ for (i = 0; i < nListens; i++, listenList = listenList->next) {
+ GSendInt( listenList->iface );
+ GSendInt( listenList->mcasts );
+ GSendInt( listenList->nmcasts );
+ }
+ for (i = 0; i < nAliases; i++, aliasList = aliasList->next) {
+ GSendStr( aliasList->name );
+ GSendInt( aliasList->hosts );
+ GSendInt( aliasList->nhosts );
+ }
+ for (i = 0; i < nAcls; i++, acList = acList->next) {
+ GSendInt( acList->entries );
+ GSendInt( acList->nentries );
+ GSendInt( acList->hosts );
+ GSendInt( acList->nhosts );
+ GSendInt( acList->flags );
+ }
+}
+#endif
+
+
+int main( int argc ATTR_UNUSED, char **argv )
+{
+ DSpec dspec;
+ ValArr va;
+ char *ci, *disp, *dcls, *cfgfile;
+ int what;
+
+ if (!(ci = getenv( "CONINFO" ))) {
+ fprintf( stderr, "This program is part of tdm and should not be run manually.\n" );
+ return 1;
+ }
+ if (sscanf( ci, "%d %d", &rfd, &wfd ) != 2)
+ return 1;
+
+ InitLog();
+
+ if ((debugLevel = GRecvInt()) & DEBUG_WCONFIG)
+ sleep( 100 );
+
+/* Debug ("parsing command line\n");*/
+ if (**++argv)
+ tdmrc_dist = tdmrc = *argv;
+/*
+ while (*++argv) {
+ }
+*/
+
+ for (;;) {
+/* Debug ("Awaiting command ...\n");*/
+ if (!GRecvCmd( &what ))
+ break;
+ switch (what) {
+ case GC_Files:
+/* Debug ("GC_Files\n");*/
+ ReadConf();
+ CopyValues( 0, &secGeneral, 0, C_CONFIG );
+#ifdef XDMCP
+ CopyValues( 0, &secXdmcp, 0, C_CONFIG );
+ GSendInt( 2 );
+#else
+ GSendInt( 1 );
+#endif
+ GSendStr( tdmrc );
+ GSendInt( -1 );
+#ifdef XDMCP
+ GSendNStr( VXaccess.ptr, VXaccess.len - 1 );
+ GSendInt( 0 );
+#endif
+ for (; (what = GRecvInt()) != -1; )
+ switch (what) {
+ case GC_gGlobal:
+ case GC_gDisplay:
+ GSendInt( 0 );
+ break;
+#ifdef XDMCP
+ case GC_gXaccess:
+ GSendInt( 1 );
+ break;
+#endif
+ default:
+ GSendInt( -1 );
+ break;
+ }
+ break;
+ case GC_GetConf:
+/* Debug( "GC_GetConf\n" );*/
+ memset( &va, 0, sizeof(va) );
+ what = GRecvInt();
+ cfgfile = GRecvStr();
+ switch (what) {
+ case GC_gGlobal:
+/* Debug( "GC_gGlobal\n" );*/
+ Debug( "getting global config\n" );
+ ReadConf();
+ CopyValues( &va, &secGeneral, 0, 0 );
+#ifdef XDMCP
+ CopyValues( &va, &secXdmcp, 0, 0 );
+#endif
+ CopyValues( &va, &secShutdown, 0, 0 );
+ SendValues( &va );
+ break;
+ case GC_gDisplay:
+/* Debug( "GC_gDisplay\n" );*/
+ disp = GRecvStr();
+/* Debug( " Display %s\n", disp );*/
+ dcls = GRecvStr();
+/* Debug( " Class %s\n", dcls );*/
+ Debug( "getting config for display %s, class %s\n", disp, dcls );
+ MkDSpec( &dspec, disp, dcls ? dcls : "" );
+ ReadConf();
+ CopyValues( &va, &sec_Core, &dspec, 0 );
+ CopyValues( &va, &sec_Greeter, &dspec, 0 );
+ free( disp );
+ if (dcls)
+ free( dcls );
+ SendValues( &va );
+ break;
+#ifdef XDMCP
+ case GC_gXaccess:
+ ReadAccessFile( cfgfile );
+ break;
+#endif
+ default:
+ Debug( "Unsupported config category %#x\n", what );
+ }
+ free( cfgfile );
+ break;
+ default:
+ Debug( "Unknown config command %#x\n", what );
+ }
+ }
+
+/* Debug( "Config reader exiting ..." );*/
+ return EX_NORMAL;
+}