/* Copyright 1992 NEC Corporation, Tokyo, Japan.
 *
 * Permission to use, copy, modify, distribute and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of NEC
 * Corporation not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior
 * permission.  NEC Corporation makes no representations about the
 * suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
 * NEC CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN 
 * NO EVENT SHALL NEC CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 
 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 
 * OTHER TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
 * PERFORMANCE OF THIS SOFTWARE. 
 */

#if !defined(lint) && !defined(__CODECENTER__)
static char rcs_id[]="@(#) $Id: misc.c,v 1.16.2.4 2004/04/26 21:48:37 aida_s Exp $";
#endif

/* LINTLIBRARY */

#include "server.h"
#ifdef HAVE_SYSLOG /* !__EMX__ */
# include <syslog.h>
#endif

#ifdef USE_VARARGS
#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#endif

#include <fcntl.h>
#include <signal.h>
#include <pwd.h>
#include <sys/ioctl.h>

#ifndef DICHOME
#define DICHOME     "/usr/lib/canna/dic"
#endif

#ifndef ERRDIR
#define ERRDIR      "/usr/spool/canna"
#endif

#define ERRFILE     "CANNA"
#define ERRFILE2    "msgs"
#define ERRSIZE     64
#ifndef ACCESS_FILE
#define ACCESS_FILE "/etc/hosts.canna"
#endif

static void FatalError pro((const char *f));
static int CreateAccessControlList pro((void));
static void FreeAccessControlList pro((void));


#ifdef DEBUG
#define LOGFILE "/tmp/canna.log"
static FILE *ServerLogFp = (FILE *)0;
static FILE *Fp;
static int DebugMode = 0;
static int LogLevel = 0;
#endif
static int Syslog = 0; /* syslog を通すかどうかのフラグ */

int PortNumberPlus = 0;
int MMountFlag = 0; /* メモリに辞書をロードするかしないかのフラグ */
static char Name[64];

static char *userID=NULL; /* canna server's user id */

#ifdef USE_INET_SOCKET
/* flag for using INET Domain Socket */
#ifdef USE_UNIX_SOCKET
/* Not to use INET domain socket, if can use Unix Domain Socket */
int UseInet = 0;
#else
/* if can use Unix Domain Socket, Use INET domain socket */
int UseInet = 1;
#endif
#ifdef INET6
int UseInet6 = 0;
#endif /* INET6 */
#endif

#define MAX_PREMOUNTS 20

char *PreMountTabl[MAX_PREMOUNTS];
int npremounts = 0;
static char *MyName ;

ACLPtr ACLHead = (ACLPtr)NULL;
static int caught_signal = 0;
static int openlog_done = 0;
static int rkw_initialize_done = 0;

static void Reset();

#ifdef INET6
#define USAGE "Usage: cannaserver [-p num] [-l num] [-u userid] [-syslog] [-inet] [-inet6] [-d] [dichome]"
#else
#define USAGE "Usage: cannaserver [-p num] [-l num] [-u userid] [-syslog] [-inet] [-d] [dichome]"
#endif
static void
Usage()
{
  FatalError(USAGE);
}

extern void getserver_version pro((void));

void
EarlyInit ( argc, argv )
int argc;
char *argv[];	
{
    char *ddname = (char *)NULL;
    char buf[ MAXDATA ];
    int     i;
    int     context;
    struct  passwd *pwent;

    strcpy( Name, argv[ 0 ] );

    for( i = 1; i < argc; i++ ) {
	if( argv[i][0] == '/' ) {
	    ddname = malloc(strlen(argv[i]) + 1);
	    if( ddname )
		strcpy( (char *)ddname, argv[ i ] );
	}

	if( !strcmp( argv[i], "-p") ) {
	  if (++i < argc) {
	    PortNumberPlus = atoi( argv[i] ) ;
	    if (PortNumberPlus < 0 || PortNumberPlus >= 100) {
		fprintf(stderr, "valid port number range is 0 <= num < 100\n");
		exit(2);
	    }
	  }
	  else {
	    fprintf(stderr, "%s\n", USAGE);
	    exit(2);
	    /* NOTREACHED */
	  }
	}
	else if( !strcmp( argv[i], "-u")) {
	  if (++i < argc) {
	    userID = argv[i];
	  }
	  else {
	    fprintf(stderr, "%s\n", USAGE);
	    exit(2);
	    /* NOTREACHED */
	  }
	}
#ifdef USE_INET_SOCKET
	else if( !strcmp( argv[i], "-inet")) {
	  UseInet = 1;
	}
#ifdef INET6
	else if( !strcmp( argv[i], "-inet6") ) {
	  UseInet6 = 1;
	}
#endif /* INET6 */
#endif
#ifdef RK_MMOUNT
	else if( !strcmp( argv[i], "-m") ) {
	  MMountFlag = RK_MMOUNT;
	}
#endif
#ifdef HAVE_SYSLOG
 	else if (!strcmp( argv[i], "-syslog")) {
	  Syslog = 1;
	}
    }

    if (Syslog) {
      openlog("cannaserver", LOG_PID, LOG_DAEMON);
      openlog_done = 1;
    } /* -syslog だったら、ログファイルを初期化する */
#else
    }

/* TCP/IP スタックが利用可能でない時は終了する */
    if (gethostname( buf, MAXDATA ) != 0) {
	fprintf(stderr,"TCP/IP stack is not working\n") ;
	exit( 1 );
    }
#endif

    if( !ddname ) {
	ddname = malloc(strlen(DICHOME) + 1);
	if( !ddname )
	    FatalError("cannaserver:Initialize failed\n");
	strcpy( (char *)ddname, DICHOME );
    }

    if (userID != NULL) {
        pwent = getpwnam(userID);
	if (pwent) {
	    if(setgid(pwent->pw_gid)) {
	        FatalError("cannaserver:couldn't set groupid to canna user's group\n");	  
	    }
	    if (initgroups(userID, pwent->pw_gid)) {
	        FatalError("cannserver: couldn't init supplementary groups\n");
	    }
	    if (setuid(pwent->pw_uid)) {
	        FatalError("cannaserver: couldn't set userid\n");
	    }
	} else if (userID != NULL) {
	    FatalError("cannaserver: -u flag specified, but canna not run as root\n");
	}
    }

#ifdef DEBUG
    DebugMode = 0 ;
    ServerLogFp = stderr ;
		
    for( i = 1; i < argc; i++ ) {
	if( !strcmp( argv[ i ], "-d" )) {
	    DebugMode = 1 ;
	    LogLevel = 5 ;
	}
	
	if( !strcmp( argv[ i ], "-l" ) ) {
	  if (++i < argc) {
	    LogLevel = atoi(argv[ i ]);
	    if( LogLevel <= 0 )
		LogLevel = 1 ;
	  }
	  else {
	    Usage();
	    /* NOTREACHED */
	  }
	}
    }
    
    if (LogLevel && !DebugMode) {
	/* ログファイル作成 */
	if( (Fp = fopen( LOGFILE, "w" ) ) != NULL ){
	    ServerLogFp = Fp ;
	} else {
	    LogLevel = 0;
	    perror("Can't Create Log File!!\n");
	}
    }

#endif /* DEBUG */

    getserver_version() ;

   ir_debug( Dmsg(5, "辞書ホームディレクトリィ = %s\n", ddname ); )

    if ((context = RkwInitialize( (char *)ddname )) < 0)
	FatalError("cannaserver:Initialize failed\n") ;
    rkw_initialize_done = 1;
    free( (char *)ddname ) ;
    RkwCloseContext( context ) ;

    if (gethostname( buf, MAXDATA ) == 0) {
      MyName = malloc(strlen(buf) + 1);
      if (MyName) {
	strcpy(MyName, buf);
      }
    }

   ir_debug( Dmsg(5, "My name is %s\n", MyName ); )

    bzero(PreMountTabl, MAX_PREMOUNTS * sizeof(unsigned char *));

    CreateAccessControlList() ;
}

static void
mysignal(sig, func)
int sig;
RETSIGTYPE (*func) pro((int));
{
#ifdef SA_RESTART
    struct sigaction new_action;

    sigemptyset(&new_action.sa_mask);
    new_action.sa_handler = func;
    new_action.sa_flags = 0
# ifdef	SA_INTERRUPT
	| SA_INTERRUPT /* don't restart */
# endif
	;
    sigaction(sig, &new_action, NULL);
#else
    signal(sig, func);
#endif
}

int
BecomeDaemon ()
{
    int     parent, parentid;

    if (DebugMode) {
	mysignal(SIGPIPE,  SIG_IGN) ;
	return 0; /* デーモンにならない */
    }

    parentid = getpid() ;

#ifndef __EMX__
    if ((parent = fork()) == -1) {
	PrintMsg( "Fork faild\n" );
	exit( 1 ) ;
    }
    if ( parent ) {
	_exit( 0 ) ;
    }
    return parentid;
#else
    return 0;
#endif
}

void
CloseServer()
{
#ifdef HAVE_SYSLOG
    if (Syslog && openlog_done) {
      closelog();
    }
#endif
    if (rkw_initialize_done)
	RkwFinalize() ;
}
/* 初期化に失敗した場合に呼ぶ。EventMgr_run()まで来たら呼ばないこと。 */
static void
FatalError(f)
    const char *f;
{
    fprintf(stderr,"%s\n", f);
    CloseServer();
    exit(2);
    /*NOTREACHED*/
}

#define MAXARGS 10

#ifdef DEBUG

#ifndef USE_VARARGS

/* VARARGS */
void
Dmsg( Pri, f, s0, s1, s2, s3, s4, s5, s6, s7, s8 )
int Pri ;
const char *f;
const char *s0, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8 ;
{
    if (!ServerLogFp)
	ServerLogFp = stderr;
    if ( LogLevel >= Pri ) {
	fprintf(ServerLogFp , f, s0, s1, s2, s3, s4, s5, s6, s7, s8 );
	fflush( ServerLogFp ) ;
    }
}

#else /* USE_VARARGS */

#ifdef __STDC__
void
Dmsg(int Pri, const char *f, ...)
{
  va_list ap;

  va_start(ap, f);

  if (!ServerLogFp) {
    ServerLogFp = stderr;
  }
  if (LogLevel >= Pri) {
    vfprintf(ServerLogFp, f, ap);
    fflush(ServerLogFp);
  }
  va_end(ap);
}
#else
void
Dmsg(Pri, f, va_alist)
int Pri;
const char *f;
va_dcl
{
  va_list ap;
  const char *args[MAXARGS];
  int argno = 0;

  va_start(ap);

  while (++argno < MAXARGS && (args[argno] = va_arg(ap, const char *)))
    ;
  args[MAXARGS - 1] = (const char *)0;
  va_end(ap);

  if (!ServerLogFp) {
    ServerLogFp = stderr;
  }
  if (LogLevel >= Pri) {
    fprintf(ServerLogFp, f, args[0], args[1], args[2], args[3], args[4],
	    args[5], args[6], args[7], args[8]);
    fflush(ServerLogFp);
  }
}
#endif /* !__STDC__ */
#endif /* USE_VARARGS */
#endif

#ifndef USE_VARARGS
void
PrintMsg( f, s0, s1, s2, s3, s4, s5, s6, s7, s8 )
const char *f;
const char *s0, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8 ;
{
    ir_time_t Time ;
    char    *date ;

#ifdef HAVE_SYSLOG
    if (Syslog) {
      syslog(LOG_WARNING, f, s0, s1, s2, s3, s4, s5, s6, s7, s8);
    } else
#endif
    {
      Time = time( NULL ) ;
      date = (char *)ctime( &Time ) ;
      date[24] = '\0' ;
      fprintf( stderr, "%s :", date ) ;
      fprintf( stderr, f, s0, s1, s2, s3, s4, s5, s6, s7, s8 );
      fflush( stderr ) ;
    }
}
#else /* USE_VARARGS */

#if !defined(__STDC__) || (defined(HAVE_SYSLOG) && !defined(HAVE_VSYSLOG))
# define READ_ALL_ARGS
#endif

void
#ifdef __STDC__
PrintMsg(const char *f, ...)
#else
PrintMsg(f, va_alist)
const char *f;
va_dcl
#endif
{
  va_list ap;
#ifdef READ_ALL_ARGS
  const char *args[MAXARGS];
  int argno = 0;
#endif
  ir_time_t Time;
  char    *date;

#ifdef __STDC__
  va_start(ap, f);
#else
  va_start(ap);
#endif

#ifdef READ_ALL_ARGS
  while (++argno < MAXARGS && (args[argno] = va_arg(ap, const char *)))
    ;
  args[MAXARGS - 1] = (const char *)0;
#endif

#ifdef HAVE_SYSLOG
  if (Syslog) {
#ifdef HAVE_VSYSLOG
    vsyslog(LOG_WARNING, f, ap);
#else
    syslog(LOG_WARNING, f, args[0], args[1], args[2], args[3], args[4],
	   args[5], args[6], args[7], args[8]);
#endif
  } else
#endif /* HAVE_SYSLOG */
  {
    Time = time(NULL);
    date = (char *)ctime(&Time);
    date[24] = '\0';
    fprintf(stderr, "%s :", date);
#ifdef __STDC__
    vfprintf(stderr, f, ap);
#else
    fprintf(stderr, f, args[0], args[1], args[2], args[3], args[4],
	    args[5], args[6], args[7], args[8]);
#endif
    fflush( stderr ) ;
  }
  va_end(ap);
}
#endif /* USE_VARARGS */

void
nomem_msg(where)
const char *where;
{
  if (where)
    PrintMsg("%s: out of memory\n", where);
  else
    PrintMsg("out of memory\n");
}

static RETSIGTYPE
Reset(sig)
int	sig;
{
    caught_signal = sig;
#ifdef SIGNALRETURNSINT
    return 0;
#endif
}

int
CheckSignal()
{
    if( caught_signal == SIGTERM ) {
	PrintMsg( "Cannaserver Terminated\n" ) ;
	return 1;
    } else if(caught_signal) {
	PrintMsg( "Caught a signal(%d)\n", caught_signal ) ;
	return 1;
    }
    return 0;
}

static int
AddrAreEqual(x, y)
const Address *x, *y;
{
    int res = 0;
    if (x->family != y->family)
      return 0;
    switch (x->family) {
      case AF_UNIX:
	res = 1;
	break;
      case AF_INET:
	res = IR_ADDR_IN(x)->s_addr == IR_ADDR_IN(y)->s_addr;
	break;
#ifdef INET6
      case AF_INET6:
	res = (IR_ADDR_IN6SCOPE(x) == 0 || IR_ADDR_IN6SCOPE(y) == 0
	    || IR_ADDR_IN6SCOPE(x) == IR_ADDR_IN6SCOPE(y))
	  && IN6_ARE_ADDR_EQUAL(IR_ADDR_IN6(x), IR_ADDR_IN6(y));
	break;
#endif
      default:
	abort();
      /* NOTREACHED */
    }
    return res;
}

AddrList *
GetAddrListFromName(hostname)
const char   *hostname;
{
    AddrList *res = NULL;
#ifdef INET6
    struct addrinfo hints, *info;
    struct addrinfo *infolists[2];
    int i;
#else
    const struct hostent *hent;
    const char *const *haddrp;
    struct in_addr numaddr;
#endif

    if (!strcmp(hostname, "unix")) {
      res = calloc(1, sizeof(AddrList));
      if (!res)
	return NULL;
      res->addr.family = AF_UNIX;
      res->addr.len = 0;
      res->next = NULL;
      return res;
    }

#ifdef INET6
    bzero(&hints, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    infolists[0] = infolists[1] = NULL;
    if (UseInet6) {
	hints.ai_family = PF_INET6;
	getaddrinfo(hostname, NULL, &hints, &infolists[0]);
    }
    if (UseInet) {
	hints.ai_family = PF_INET;
	getaddrinfo(hostname, NULL, &hints, &infolists[1]);
    }

    for (i = 0; i < 2; i++) {
      for (info = infolists[i]; info; info = info->ai_next) {
	AddrList *newnode;
	if (info->ai_family == AF_INET6
	    &&IN6_IS_ADDR_V4MAPPED(
	      &((struct sockaddr_in6 *)info->ai_addr)->sin6_addr))
	  continue;
	newnode = calloc(1, sizeof(AddrList));
	if (!newnode) {
	  freeaddrinfo(infolists[i]);
	  goto fail;
	}
	newnode->addr.family = info->ai_family;
	newnode->addr.len = info->ai_addrlen;
	memcpy(&newnode->addr.saddr, info->ai_addr, info->ai_addrlen);
	newnode->next = res;
	res = newnode;
      }
      if (infolists[i])
	  freeaddrinfo(infolists[i]);
    }
#else /* !INET6 */
    if (
#ifdef HAVE_INET_ATON
	inet_aton(hostname, &numaddr)
#else
	((numaddr.s_addr = inet_addr(hostname)) != (canna_in_addr_t)-1)
#endif
       ) {
      res = calloc(1, sizeof(AddrList));
      if (!res)
	goto fail;
      res->addr.family = AF_INET;
      res->addr.len = sizeof(struct sockaddr_in);
      res->addr.saddr.sin_addr = numaddr;
      res->next = 0;
      return res;
    }
    hent = gethostbyname(hostname);
    if (hent == NULL || hent->h_addrtype != AF_INET)
      return NULL;
#ifndef HAVE_STRUCT_HOSTENT_H_ADDR_LIST
    haddrp = &hent->h_addr;
#else
    for (haddrp = hent->h_addr_list; *haddrp; haddrp++)
#endif
    {
      AddrList *newnode = calloc(1, sizeof(AddrList));
      if (!newnode)
	goto fail;
      newnode->addr.family = AF_INET;
      newnode->addr.len = sizeof(struct sockaddr_in);
      newnode->addr.saddr.sin_addr = *(const struct in_addr *)*haddrp;
      newnode->next = res;
      res = newnode;
    }
#endif /* !INET6 */
    return res;
fail:
    while(res) {
      AddrList *next = res->next;
      free(res);
      res = next;
    }
    return NULL;
}

AddrList *
SearchAddrList(list, addrp)
const AddrList *list;
const Address *addrp;
{
    for (; list; list = list->next)
      if (AddrAreEqual(&list->addr, addrp))
	break;
    return (AddrList *)list;
}

void
FreeAddrList(list)
AddrList *list;
{
    while(list) {
      AddrList *next = list->next;
      free(list);
      list = next;
    };
}

static int
CreateAccessControlList()
{
    char   buf[BUFSIZE];
    char   *wp, *p ;
    ACLPtr  current;
    ACLPtr  prev = (ACLPtr)NULL ;
    FILE    *fp ;
    int namelen;

    if( (fp = fopen( ACCESS_FILE, "r" )) == (FILE *)NULL )
	return( -1 ) ;

    if (ACLHead) {
      FreeAccessControlList();
    }

    while( fgets( (char *)buf, BUFSIZE, fp ) != (char *)NULL ) {
	buf[ strlen( (char *)buf )-1 ] = '\0' ;
	wp = buf ;
#ifdef INET6
	if( *wp == '\0' )
	    continue;
	else if( *wp == '[' ) {
	    size_t bodylen;
	    wp++;
	    p = strchr( wp, ']' );
	    if( !p )
		continue;
	    *( p++ ) = '\0';
	    if( *p == ':' )
		p++;
	    else if( *p != '\0' )
		continue;
	    /* ここでの形式チェックは厳密でなくてよい */
	    bodylen = strspn( wp, "0123456789ABCDEFabcdef:." );
	    if( !bodylen || !( wp[bodylen] == '\0' || wp[bodylen] == '%' )
		    || strchr( wp, ':' ) == NULL )
		continue;
	} else {
	    p = strchr( wp, ':' );
	    if( p )
		*( p++ ) = '\0';
	    else
		p = wp + strlen( wp );
	}
#else /* !INET6 */
	if( !strtok( (char *)wp, ":" ) )
	    continue ;
	p = wp + strlen( (char *)wp ) + 1;
#endif /* !INET6 */

	if( !(current = (ACLPtr)malloc( sizeof( ACLRec ) )) ) {
	    PrintMsg("Can't create access control list!!" ) ;	
	    fclose( fp ) ;
	    FreeAccessControlList() ;
	    return( -1 ) ;
	}

	bzero( current, sizeof( ACLRec ) ) ;

	namelen = strlen(wp);
	current->hostname = malloc(namelen + 1);
	if (current->hostname) {
	  strcpy(current->hostname, wp);
	}

	/* AccessControlListをインターネットアドレスで管理する */
	/* hosts.cannaからホスト名を求める */
	/* ホスト名からインターネットアドレスを求めて ACLRecに登録する  */
	current->hostaddrs = GetAddrListFromName(wp);
	if (!current->hostaddrs) {
	  /* アドレスが見つからない場合 */
	  /* インターネットアドレス表記が間違っているので無視する */
	  /* hostsにエントリが無いことをメッセージにだした方が良いか */
	  /* も知れない */
	  if (current->hostname)
	    free((char *)current->hostname);
	  free((char *)current);
	  continue;
	}
	/* 今のところアドレスが重複していてもそのまま覚えておく */

	wp = p;

	if( strlen( (char *)wp ) ) {
	    current->usernames = malloc(strlen(wp) + 1);
	    if (current->usernames) {
	        strcpy((char *)current->usernames, wp);
		for( p = current->usernames; *p != '\0'; p++ ) {
		    if( *p == ',' ) {
			*p = '\0' ;
			current->usercnt ++ ;
		    }
		}
		current->usercnt ++ ;
	    }
	}
	if( ACLHead ) {
	    current->prev = prev ;
	    prev->next = current ;
	} else {
	    ACLHead = current ;
	    current->prev = (ACLPtr)NULL ;
	}
	current->next = (ACLPtr)NULL ;
	prev = current ;
    }

    fclose( fp ) ;
    return 0;
}

static void
FreeAccessControlList() 
{
    ACLPtr  wp, tailp = (ACLPtr)NULL;

    if( !(wp = ACLHead) )
	return ;

    for( ; wp != (ACLPtr)NULL; wp = wp->next ) {
	    if( wp->hostname )
		free( wp->hostname ) ;
	    if( wp->usernames )
		free( wp->usernames ) ;
	    FreeAddrList( wp->hostaddrs ) ;
	    tailp = wp ;
    }

    for( wp = tailp; wp != (ACLPtr)NULL; wp = wp->prev ) {
	if( wp->next )
	    free( wp->next ) ;
    }
    ACLHead = (ACLPtr)NULL ;
}

int
CheckAccessControlList(hostaddrp, username)
Address *hostaddrp;
const char *username;
{
  int i;
  char *userp;
  ACLPtr wp;

  if (!ACLHead) return 0;

  ir_debug(Dmsg(5, "My name is %s\n", MyName));

  for (wp = ACLHead ; wp ; wp = wp->next) {
    /* AccessControlListで持っているインタネットアドレスと一致する
       ものをサーチする */
    if (SearchAddrList(wp->hostaddrs, hostaddrp)) {
      if (wp->usernames) {
	for (i = 0, userp = wp->usernames ; i < wp->usercnt ; i++) {
	  if (!strcmp(userp, username)) {
	    return 0;
	  }
	  userp += strlen(userp) + 1;
	}
	return -1;
      }
      else {
	return 0;
      }
    }
  }
  return -1;
}

int
NumberAccessControlList()
{
  ACLPtr wp;
  int n;

  for (wp = ACLHead, n = 0; wp ; wp = wp->next) {
    n++;
  }
  return n;
}

int
SetDicHome( client, cxnum )
ClientPtr client ;
int cxnum ;
{
    char dichome[ 256 ] ;

    if (cxnum < 0)
	return( -1 ) ;

    if (client->username && client->username[0]) {
      if (client->groupname && client->groupname[0]) {
	if (strlen(DDUSER) + strlen(client->username) +
	    strlen(DDGROUP) + strlen(client->groupname) +
	    strlen(DDPATH) + 4 >= 256)
	  return ( -1 );
	sprintf(dichome, "%s/%s:%s/%s:%s",
		DDUSER, client->username,
		DDGROUP, client->groupname,
		DDPATH);
      }
      else {
	if (strlen(DDUSER) + strlen(client->username) +
	    strlen(DDPATH) + 2 >= 256)
	  return ( -1 );
	sprintf(dichome, "%s/%s:%s",
		DDUSER, client->username,
		DDPATH);
      }
    }
    else {
      strcpy(dichome, DDPATH);
    }

   ir_debug( Dmsg(5,"辞書ホームディレクトリィ：%s\n", dichome ); )
    if( RkwSetDicPath( cxnum, dichome ) == -1 ) {
	return( -1 ) ;
    }
    return( 1 ) ;
}

ClientPtr *
get_all_other_clients(self, count)
ClientPtr self;
size_t *count;
{
    EventMgrIterator curr, end;
    ClientPtr *res, *p;

    *count = 0;
    EventMgr_clibuf_end(global_event_mgr, &end);

    for (EventMgr_clibuf_first(global_event_mgr, &curr);
	    curr.it_val != end.it_val;
	    EventMgrIterator_next(&curr)) {
	ClientPtr who = ClientBuf_getclient(curr.it_val);
	if (who && who != self)
	    ++*count;
    }

    res = p = malloc(*count * sizeof(ClientPtr));
    if (!res) {
	*count = 0;
	return res;
    }

    for (EventMgr_clibuf_first(global_event_mgr, &curr);
	    curr.it_val != end.it_val;
	    EventMgrIterator_next(&curr)) {
	ClientPtr who = ClientBuf_getclient(curr.it_val);
	if (who && who != self)
	    *p++ = who;
    }
    return res;
}

void
AllSync()
{
    EventMgrIterator curr, end;

    EventMgr_clibuf_first(global_event_mgr, &curr);
    EventMgr_clibuf_end(global_event_mgr, &end);

    for (EventMgr_clibuf_first(global_event_mgr, &curr);
	    curr.it_val != end.it_val;
	    EventMgrIterator_next(&curr)) {
	ClientPtr client = ClientBuf_getclient(curr.it_val);
	int i;
	if (!client)
	    continue;
	for (i = 0; i < client->ncon; ++i)
	    RkwSync(client->context_flag[i], NULL);
    }
}

void
DetachTTY()
{
  char    errfile[ERRSIZE];
  int     errfd;
  
#ifdef DEBUG
  if (!DebugMode)
  {
#endif 
    /* 標準エラー出力をエラーファイルに切り替えて、標準入出力をクローズする */

    if(!Syslog) {    
      sprintf(errfile,"%s/%s%d%s", ERRDIR, ERRFILE, PortNumberPlus, ERRFILE2);
    
      if((errfd = open(errfile, O_CREAT | O_RDWR | O_TRUNC, 0644)) < 0) {
	(void)fprintf(stderr, "Warning: %s: %s open faild\n", Name, errfile);
	(void)perror("");
      } else {
	if(dup2( errfd, fileno(stderr)) < 0) {
	  (void)fprintf(stderr, "Warning: %s: %s dup2 faild\n", Name, errfile);
	  (void)perror("");
	  close(fileno(stderr));
	}
      }
      close(fileno(stdin));
      close(fileno(stdout));
      close(errfd);
    }
    /*
     * TTY の切り離し
     */
#if defined(HAVE_SETSID)
    (void)setsid();
#elif defined(__EMX__)
    (void)_setsid();
#elif defined(SETPGRP_VOID)
    /* defined(SYSV) || defined(linux) || defined(__OSF__) */
    setpgrp();
#else
    setpgrp(0, getpid());
#endif
    
#if defined(TIOCNOTTY) && !defined(HAVE_SETSID)
    {
      int fd = open("/dev/tty", O_RDWR, 0);
      if (fd >= 0) {
	(void)ioctl(fd, TIOCNOTTY, (char *)0);
	(void)close(fd);
      }
    }
#endif
    
#ifdef DEBUG
  }
#endif
  
    /*
     * シグナル処理
     */
    mysignal(SIGHUP,   SIG_IGN);
    mysignal(SIGINT,   Reset);
    mysignal(SIGALRM,  SIG_IGN);
    mysignal(SIGPIPE,  SIG_IGN) ;
    mysignal(SIGTERM,  Reset); /* for killserver */

    umask( 002 ) ;
}
