/*
 * Copyright 1990, 1991, 1992 Oracle and/or its affiliates. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/******************************************************************
         Copyright 1992, 1993, 1994 by FUJITSU LIMITED
         Copyright 1993, 1994 by Sony Corporation

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
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 names of Digital, FUJITSU
LIMITED and Sony Corporation not be used in advertising or publicity
pertaining to distribution of the software without specific, written
prior permission.

DIGITAL, FUJITSU LIMITED AND SONY CORPORATION DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL, FUJITSU LIMITED
AND SONY 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 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

  Author: Hideki Hiura (hhiura@Sun.COM) Sun Microsystems, Inc.
          Takashi Fujiwara     FUJITSU LIMITED
                               fujiwara@a80.tech.yk.fujitsu.co.jp
          Makoto Wakamatsu     Sony Corporation
                               makoto@sm.sony.co.jp

******************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <X11/Xatom.h>
#include "Xlibint.h"
#include "Xlcint.h"
#include "XlcPublic.h"
#include "XlcPubI.h"
#include "XimTrInt.h"
#include "Ximint.h"

#include <limits.h>

int
_XimCheckDataSize(
    XPointer	 buf,
    int		 len)
{
    CARD16	*buf_s = (CARD16 *)buf;

    if(len < XIM_HEADER_SIZE)
	return -1;
    return  buf_s[1];
}

void
_XimSetHeader(
    XPointer	 buf,
    CARD8	 major_opcode,
    CARD8	 minor_opcode,
    INT16	*len
)
{
    CARD8	*buf_b = (CARD8 *)buf;
    CARD16	*buf_s = (CARD16 *)buf;

    buf_b[0] = major_opcode;
    buf_b[1] = minor_opcode;
    buf_s[1] = ((*len) / 4);
    *len += XIM_HEADER_SIZE;
    return;
}

char
_XimGetMyEndian(void)
{
    CARD16	 test_card = 1;

    if(*((char *)&test_card))
	return LITTLEENDIAN;
    else
	return BIGENDIAN;
}

static Bool
_XimCheckServerName(
    Xim		   im,
    char	  *str)
{
    char	  *server_name = im->core.im_name;
    int		   len;
    int		   str_len;
    int		   category_len = strlen(XIM_SERVER_CATEGORY);
    char	  *pp;
    register char *p;

    if(server_name && *server_name)
	len = strlen(server_name);
    else
	return True;

    if((int)strlen(str) < category_len)
	return False;

    if(strncmp(str, XIM_SERVER_CATEGORY, category_len))
	return False;

    pp = &str[category_len];

    for(;;) {
	for(p = pp; (*p != ',') && (*p); p++);
	str_len = (int)(p - pp);

	if((len == str_len) && (!strncmp(pp, server_name, len)))
	    break;
	if(!(*p))
	    return False;
	pp = p + 1;
    }
    return True;
}

static char *
_XimCheckLocaleName(
    Xim		   im,
    char	  *address,
    int		   address_len,
    char	  *locale_name[],
    int		   len)
{
    int		   category_len;
    char	  *pp;
    register char *p;
    register int   n;
    Bool           finish = False;

    category_len = strlen(XIM_LOCAL_CATEGORY);
    if(address_len < category_len)
	return (char*)NULL;

    if(strncmp(address, XIM_LOCAL_CATEGORY, category_len))
	return (char*)NULL;

    pp = &address[category_len];

    for(;;) {
	for( p = pp; *p && *p != ','; p++);
	if (!*p)
	    finish = True;
	address_len = (int)(p - pp);
	*p = '\0';

	for( n = 0; n < len; n++ )
	    if( locale_name[n] && !_XlcCompareISOLatin1( pp, locale_name[n] ) )
		return locale_name[n];
	if (finish)
	    break;
	pp = p + 1;
    }
    return (char *)NULL;
}

static Bool
_XimCheckTransport(
    char	  *address,
    int		   address_len,
    const char	  *transport,
    int		   len,
    char	 **trans_addr)
{
    int		   category_len = strlen(XIM_TRANSPORT_CATEGORY);
    char	  *pp;
    register char *p;

    if(address_len < category_len)
	return False;

    if(strncmp(address, XIM_TRANSPORT_CATEGORY, category_len))
	return False;

    pp = &address[category_len];

    for(;;) {
	*trans_addr = pp;

	for(p = pp; (*p != '/') && (*p != ',') && (*p); p++);
	if(*p == ',') {
	    pp = p + 1;
	    continue;
	}
	if(!(*p))
	    return False;

	address_len = (int)(p - pp);

	if((len == address_len) && (!strncmp(pp, transport, len)))
	    break;
	pp = p + 1;
    }
    pp = p + 1;
    for(p = pp; (*p != ',') && (*p); p++);
    if (*p)
	*p = '\0';
    return True;
}

static Bool
_CheckSNEvent(
    Display		*display,
    XEvent		*xevent,
    XPointer		 arg)
{
    XSelectionEvent	*event = (XSelectionEvent *)xevent;
    Window		 window = *(Window*)arg;

    if((event->type == SelectionNotify) && (window == event->requestor))
	return True;
    return False;
}

static Bool
_XimGetSelectionNotify(
    Display		 *display,
    Window		  window,
    Atom		  target,
    char		**ret_address)
{
    XEvent		  event;
    XSelectionEvent	 *ev = (XSelectionEvent *)&event;
    Atom		  actual_type;
    int			  actual_format;
    unsigned long	  nitems, bytes_after;

    for(;;) {
	XIfEvent(display, &event, _CheckSNEvent, (XPointer)&window);
	if((ev->type == SelectionNotify) && (window == ev->requestor))
	    break;
    }

    if(ev->property == (Atom)None)
	return False;
    if( XGetWindowProperty( display, window, target, 0L, 1000000L,
			    True, target, &actual_type, &actual_format,
			    &nitems, &bytes_after,
			    (unsigned char **)&*ret_address ) != Success )
	return False;
    return True;
}

static Bool
_XimPreConnectionIM(
    Xim			 im,
    Atom		 selection)
{
    Display		*display = im->core.display;
    Atom		 locales, transport;
    char		*address;
    XLCd		 lcd;
    char		*language;
    char		*territory;
    char		*codeset;
    char		*trans_addr;
    char		*locale_name[4], *locale;
    int			 llen, tlen, clen;
    register int	 i;
    Window		 window;
    char		*str;

    if(!(lcd = im->core.lcd))
	return False;

    for( i = 0; i < 4; i++ )
	locale_name[i] = NULL;
    /* requestor window */
    if(!(window = XCreateSimpleWindow(display, DefaultRootWindow(display),
			 				0, 0, 1, 1, 1, 0, 0)))
	return False;

    /* server name check */
    if( !(str = XGetAtomName( display, selection )) )
	return False;
    if(!_XimCheckServerName(im, str)) {
	XFree( (XPointer)str );
	goto Error;
    }
    XFree( (XPointer)str );

    /* locale name check */
    _XGetLCValues(lcd, XlcNLanguage, &language, XlcNTerritory, &territory,
                    XlcNCodeset, &codeset, NULL);
    llen = strlen( language );
    tlen = territory ? strlen( territory ): 0;
    clen = codeset ? strlen( codeset ): 0;

    if( tlen != 0  &&  clen != 0 ) {
	if( (locale_name[0] = Xmalloc(llen+tlen+clen+3)) != NULL )
	    sprintf( locale_name[0], "%s_%s.%s", language, territory, codeset );
    }
    if( clen != 0 ) {
	if( (locale_name[1] = Xmalloc(llen+clen+2)) != NULL )
	    sprintf( locale_name[1], "%s.%s", language, codeset );
	else
	    goto Error;
    }
    if( tlen != 0 ) {
	if( (locale_name[2] = Xmalloc(llen+tlen+2)) != NULL )
	    sprintf( locale_name[2], "%s_%s", language, territory );
	else
	    goto Error;
    }
    if( (locale_name[3] = strdup(language)) == NULL )
	goto Error;
    if((locales = XInternAtom(display, XIM_LOCALES, True)) == (Atom)None)
	goto Error;

    XConvertSelection(display, selection, locales, locales, window,
		      CurrentTime);
    if(!(_XimGetSelectionNotify(display, window, locales, &address)))
	goto Error;

    if((locale = _XimCheckLocaleName(im, address, strlen(address), locale_name,
				     4)) == NULL) {
	XFree((XPointer)address);
	goto Error;
    }
    im->private.proto.locale_name = locale;
    for( i = 0; i < 4; i++ ) {
	if( locale_name[i] != NULL  &&  locale_name[i] != locale ) {
	    XFree( locale_name[i] );
	    locale_name[i] = NULL;
	}
    }
    XFree((XPointer)address);

    /* transport check */
    if((transport = XInternAtom(display, XIM_TRANSPORT, True)) == (Atom)None)
	goto Error;

    XConvertSelection(display, selection, transport, transport, window,
		      CurrentTime);
    if(!_XimGetSelectionNotify(display, window, transport, &address))
	goto Error;

    for(i = 0; _XimTransportRec[i].transportname ; i++) {
	if( _XimCheckTransport(address, strlen(address),
				_XimTransportRec[i].transportname,
				strlen(_XimTransportRec[i].transportname),
				 &trans_addr)) {
	    if( _XimTransportRec[i].config(im, trans_addr) ) {
		XFree((XPointer)address);
		XDestroyWindow(display, window);
		return True;
	    }
	}
    }

    XFree((XPointer)address);
Error:
    for( i = 0; i < 4; i++ )
	if( locale_name[i] != NULL )
	    XFree( locale_name[i] );
    XDestroyWindow(display, window);
    return False;
}

static Bool
_XimPreConnect(
    Xim		    im)
{
    Display	   *display = im->core.display;
    Atom	    imserver;
    Atom	    actual_type;
    int		    actual_format;
    unsigned long   nitems;
    unsigned long   bytes_after;
    unsigned char  *prop_return;
    Atom	   *atoms;
    Window	    im_window = 0;
    register int    i;

    if((imserver = XInternAtom(display, XIM_SERVERS, True)) == (Atom)None)
	return False;

    if(XGetWindowProperty(display, RootWindow(display, 0),
			imserver, 0L, 1000000L, False, XA_ATOM, &actual_type,
			&actual_format, &nitems, &bytes_after,
			&prop_return) != Success)
	return False;

    if( (actual_type != XA_ATOM) || (actual_format != 32) ) {
	if( nitems )
	    XFree((XPointer)prop_return);
	return False;
    }

    atoms = (Atom *)prop_return;
    for(i = 0; i < nitems; i++) {
	if((im_window = XGetSelectionOwner(display, atoms[i])) == (Window)None)
	    continue;

	if(_XimPreConnectionIM(im, atoms[i]))
	    break;
    }

    XFree((XPointer)prop_return);
    if(i >= nitems)
	return False;

    im->private.proto.im_window = im_window;
    return True;
}

static Bool
_XimGetAuthProtocolNames(
    Xim		 im,
    CARD16	*buf,
    CARD8	*num,
    INT16	*len)
{
    if (!IS_USE_AUTHORIZATION_FUNC(im)) {
	*num = 0;
	*len = 0;
	return True;
    }
    /*
     * Not yet
     */
    return True;
}

static Bool
_XimSetAuthReplyData(
    Xim		 im,
    XPointer	 buf,
    INT16	*len)
{
    /*
     * Not yet
     */
    *len = 0;
    return True;
}

static Bool
_XimSetAuthNextData(
    Xim		 im,
    XPointer	 buf,
    INT16	*len)
{
    /*
     * Not yet
     */
    *len = 0;
    return True;
}

static Bool
_XimSetAuthRequiredData(
    Xim		 im,
    XPointer	 buf,
    INT16	*len)
{
    /*
     * Not yet
     */
    *len = 0;
    return True;
}

static Bool
_XimCheckAuthSetupData(
    Xim		 im,
    XPointer	 buf)
{
    /*
     * Not yet
     */
    return True;
}

static Bool
_XimCheckAuthNextData(
    Xim		 im,
    XPointer	 buf)
{
    /*
     * Not yet
     */
    return True;
}

#define	NO_MORE_AUTH	2
#define	GOOD_AUTH	1
#define	BAD_AUTH	0

static int
_XimClientAuthCheck(
    Xim		 im,
    XPointer	 buf)
{
    /*
     * Not yet
     */
    return NO_MORE_AUTH;
}

static void
_XimAuthNG(
    Xim		 im)
{
    CARD32	 buf32[BUFSIZE/4];
    CARD8	*buf = (CARD8 *)buf32;
    INT16	 len = 0;

    _XimSetHeader((XPointer)buf, XIM_AUTH_NG, 0, &len);
    (void)_XimWrite(im, len, (XPointer)buf);
    _XimFlush(im);
    return;
}

static Bool
_XimAllRecv(
    Xim		 im,
    INT16	 len,
    XPointer	 data,
    XPointer	 arg)
{
    return True;
}

#define	CLIENT_WAIT1		1
#define	CLIENT_WAIT2		2

static Bool
_XimConnection(
    Xim		 im)
{
    CARD32	 buf32[BUFSIZE/4];
    CARD8	*buf = (CARD8 *)buf32;
    CARD8	*buf_b = &buf[XIM_HEADER_SIZE];
    CARD16	*buf_s = (CARD16 *)((XPointer)buf_b);
    INT16	 len;
    CARD8	 num;
    CARD32	 reply32[BUFSIZE/4];
    char	*reply = (char *)reply32;
    XPointer	 preply;
    int		 buf_size;
    int		 ret_code;
    CARD8	 major_opcode;
    int		 wait_mode;
    int		 ret;

    if(!(_XimConnect(im)))	/* Transport Connect */
	return False;

    if(!_XimDispatchInit(im))
	return False;

    _XimRegProtoIntrCallback(im, XIM_ERROR, 0, _XimErrorCallback, (XPointer)im);

    if(!_XimGetAuthProtocolNames(im, &buf_s[4], &num, &len))
	return False;

    im->private.proto.protocol_major_version = PROTOCOLMAJORVERSION;
    im->private.proto.protocol_minor_version = PROTOCOLMINORVERSION;

    buf_b[0] = _XimGetMyEndian();
    buf_b[1] = 0;
    buf_s[1] = PROTOCOLMAJORVERSION;
    buf_s[2] = PROTOCOLMINORVERSION;
    buf_s[3] = num;
    len  += sizeof(CARD8)
          + sizeof(CARD8)
          + sizeof(CARD16)
          + sizeof(CARD16)
          + sizeof(CARD16);

    major_opcode = XIM_CONNECT;
    wait_mode = (IS_USE_AUTHORIZATION_FUNC(im)) ? CLIENT_WAIT1 : CLIENT_WAIT2;

    for(;;) {
	_XimSetHeader((XPointer)buf, major_opcode, 0, &len);
	if (!(_XimWrite(im, len, (XPointer)buf)))
	    return False;
	_XimFlush(im);
	buf_size = BUFSIZE;
	ret_code = _XimRead(im, &len, reply, buf_size, _XimAllRecv, 0);
	if(ret_code == XIM_TRUE) {
	    preply = reply;
	} else if(ret_code == XIM_OVERFLOW) {
	    if(len <= 0) {
		preply = reply;
	    } else {
		buf_size = len;
		preply = Xmalloc(buf_size);
		ret_code = _XimRead(im, &len, preply, buf_size, _XimAllRecv, 0);
		if(ret_code != XIM_TRUE) {
		    Xfree(preply);
		    return False;
		}
	    }
	} else
	    return False;

	major_opcode = *((CARD8 *)preply);
	buf_s = (CARD16 *)((char *)preply + XIM_HEADER_SIZE);

	if (wait_mode == CLIENT_WAIT1) {
	    if (major_opcode == XIM_AUTH_REQUIRED) {
		ret = _XimClientAuthCheck(im, (XPointer)buf_s);
		if(reply != preply)
		    Xfree(preply);
		if (ret == NO_MORE_AUTH) {
		    if (!(_XimSetAuthReplyData(im,
				(XPointer)&buf[XIM_HEADER_SIZE], &len))) {
			_XimAuthNG(im);
			return False;
		    }
		    major_opcode = XIM_AUTH_REPLY;
		    wait_mode = CLIENT_WAIT2;
		} else if (ret == GOOD_AUTH) {
		    if (!(_XimSetAuthNextData(im,
				(XPointer)&buf[XIM_HEADER_SIZE], &len))) {
			_XimAuthNG(im);
			return False;
		    }
		    major_opcode = XIM_AUTH_NEXT;
		} else {	/* BAD_AUTH */
		    _XimAuthNG(im);
		    return False;
		}
	    } else {
		if(reply != preply)
		    Xfree(preply);
		_XimAuthNG(im);
		return False;
	    }
	} else {	/* CLIENT_WAIT2 */
	    if (major_opcode == XIM_CONNECT_REPLY) {
		break;
	    } else if (major_opcode == XIM_AUTH_SETUP) {
		if (!(_XimCheckAuthSetupData(im, (XPointer)buf_s))) {
		    _XimAuthNG(im);
		    return False;
		}
		if(reply != preply)
		    Xfree(preply);
		if (!(_XimSetAuthRequiredData(im,
				(XPointer)&buf[XIM_HEADER_SIZE], &len))) {
		    _XimAuthNG(im);
		    return False;
		}
		major_opcode = XIM_AUTH_REQUIRED;
	    } else if (major_opcode == XIM_AUTH_NEXT) {
		if (!(_XimCheckAuthNextData(im, (XPointer)buf_s))) {
		    _XimAuthNG(im);
		    return False;
		}
		if(reply != preply)
		    Xfree(preply);
		if (!(_XimSetAuthRequiredData(im,
				(XPointer)&buf[XIM_HEADER_SIZE], &len))) {
		    _XimAuthNG(im);
		    return False;
		}
		major_opcode = XIM_AUTH_REQUIRED;
	    } else if (major_opcode == XIM_AUTH_NG) {
		if(reply != preply)
		    Xfree(preply);
		return False;
	    } else {
		_XimAuthNG(im);
		if(reply != preply)
		    Xfree(preply);
		return False;
	    }
	}
    }

    if (!( buf_s[0] == im->private.proto.protocol_major_version
        && buf_s[1] == im->private.proto.protocol_minor_version)) {
	if(reply != preply)
	    Xfree(preply);
	return False;
    }
    if(reply != preply)
	Xfree(preply);
    MARK_SERVER_CONNECTED(im);

    _XimRegProtoIntrCallback(im, XIM_REGISTER_TRIGGERKEYS, 0,
				 _XimRegisterTriggerKeysCallback, (XPointer)im);
    return True;
}

static Bool
_XimDisconnectCheck(
    Xim		 im,
    INT16	 len,
    XPointer	 data,
    XPointer	 arg)
{
    CARD8	 major_opcode = *((CARD8 *)data);
    CARD8	 minor_opcode = *((CARD8 *)data + 1);

    if ((major_opcode == XIM_DISCONNECT_REPLY)
     && (minor_opcode == 0))
	return True;
    if ((major_opcode == XIM_ERROR)
     && (minor_opcode == 0))
	return True;
    return False;
}

static Bool
_XimDisconnect(
    Xim		 im)
{
    CARD32	 buf32[BUFSIZE/4];
    CARD8	*buf = (CARD8 *)buf32;
    INT16	 len = 0;
    CARD32	 reply32[BUFSIZE/4];
    char	*reply = (char *)reply32;
    XPointer	 preply;
    int		 buf_size;
    int		 ret_code;

    if (IS_SERVER_CONNECTED(im)) {
	_XimSetHeader((XPointer)buf, XIM_DISCONNECT, 0, &len);
	if (!(_XimWrite(im, len, (XPointer)buf)))
	    return False;
	_XimFlush(im);
	buf_size = BUFSIZE;
	ret_code = _XimRead(im, &len, (XPointer)reply, buf_size,
						_XimDisconnectCheck, 0);
	if(ret_code == XIM_OVERFLOW) {
	    if(len > 0) {
		buf_size = len;
		preply = Xmalloc(buf_size);
		ret_code = _XimRead(im, &len, preply, buf_size,
						 _XimDisconnectCheck, 0);
		Xfree(preply);
		if(ret_code != XIM_TRUE)
		    return False;
	    }
	} else if(ret_code == XIM_FALSE)
	    return False;

    }
    if (!(_XimShutdown(im)))	/* Transport shutdown */
	return False;
    return True;
}

static Bool
_XimOpenCheck(
    Xim		 im,
    INT16	 len,
    XPointer	 data,
    XPointer	 arg)
{
    CARD8	 major_opcode = *((CARD8 *)data);
    CARD8	 minor_opcode = *((CARD8 *)data + 1);

    if ((major_opcode == XIM_OPEN_REPLY)
     && (minor_opcode == 0))
	return True;
    if ((major_opcode == XIM_ERROR)
     && (minor_opcode == 0))
	return True;
    return False;
}

static Bool
_XimOpen(
    Xim			 im)
{
    CARD32		 buf32[BUFSIZE/4];
    CARD8		*buf = (CARD8 *)buf32;
    CARD8		*buf_b = &buf[XIM_HEADER_SIZE];
    CARD16		*buf_s;
    INT16		 len;
    CARD32		 reply32[BUFSIZE/4];
    char		*reply = (char *)reply32;
    XPointer		 preply;
    int			 buf_size;
    int			 ret_code;
    char		*locale_name;
    size_t		 locale_len;

    locale_name = im->private.proto.locale_name;
    locale_len = strlen(locale_name);
    if (locale_len > UCHAR_MAX)
      return False;
    memset(buf32, 0, sizeof(buf32));
    buf_b[0] = (BYTE)locale_len;		/* length of locale name */
    memcpy(&buf_b[1], locale_name, locale_len);	   /* locale name */
    len = (INT16)(locale_len + sizeof(BYTE));	   /* sizeof length */
    XIM_SET_PAD(buf_b, len);			   /* pad */

    _XimSetHeader((XPointer)buf, XIM_OPEN, 0, &len);
    if (!(_XimWrite(im, len, (XPointer)buf)))
	return False;
    _XimFlush(im);
    buf_size = BUFSIZE;
    ret_code = _XimRead(im, &len, reply, buf_size,
					_XimOpenCheck, 0);
    if(ret_code == XIM_TRUE) {
	preply = reply;
    } else if(ret_code == XIM_OVERFLOW) {
	if(len <= 0) {
	    preply = reply;
	} else {
	    buf_size = len;
	    preply = Xmalloc(buf_size);
	    ret_code = _XimRead(im, &len, preply, buf_size,
					_XimOpenCheck, 0);
	    if(ret_code != XIM_TRUE) {
		Xfree(preply);
		return False;
	    }
	}
    } else
	return False;
    buf_s = (CARD16 *)((char *)preply + XIM_HEADER_SIZE);
    if (*((CARD8 *)preply) == XIM_ERROR) {
	_XimProcError(im, 0, (XPointer)&buf_s[3]);
	if(reply != preply)
	    Xfree(preply);
	return False;
    }

    im->private.proto.imid = buf_s[0];		/* imid */

    if (!(_XimGetAttributeID(im, &buf_s[1]))) {
	if(reply != preply)
	    Xfree(preply);
	return False;
    }
    if(reply != preply)
	Xfree(preply);

    if (!(_XimSetInnerIMResourceList(&(im->private.proto.im_inner_resources),
				&(im->private.proto.im_num_inner_resources))))
	return False;

    if (!(_XimSetInnerICResourceList(&(im->private.proto.ic_inner_resources),
				&(im->private.proto.ic_num_inner_resources))))
	return False;

    _XimSetIMMode(im->core.im_resources, im->core.im_num_resources);
    _XimSetIMMode(im->private.proto.im_inner_resources,
				im->private.proto.im_num_inner_resources);

    /* Transport Callback */
    _XimRegProtoIntrCallback(im, XIM_SET_EVENT_MASK, 0,
				 _XimSetEventMaskCallback, (XPointer)im);
    _XimRegProtoIntrCallback(im, XIM_FORWARD_EVENT, 0,
				 _XimForwardEventCallback, (XPointer)im);
    _XimRegProtoIntrCallback(im, XIM_COMMIT, 0,
				 _XimCommitCallback, (XPointer)im);
    _XimRegProtoIntrCallback(im, XIM_SYNC, 0,
				 _XimSyncCallback, (XPointer)im);

    if(!_XimExtension(im))
	return False;

    /* register a hook for callback protocols */
    _XimRegisterDispatcher(im, _XimCbDispatch, (XPointer)im);

    return True;
}

static Bool
_XimCloseCheck(
    Xim		 im,
    INT16	 len,
    XPointer	 data,
    XPointer	 arg)
{
    CARD16	*buf_s = (CARD16 *)((CARD8 *)data + XIM_HEADER_SIZE);
    CARD8	 major_opcode = *((CARD8 *)data);
    CARD8	 minor_opcode = *((CARD8 *)data + 1);
    XIMID	 imid = buf_s[0];

    if ((major_opcode == XIM_CLOSE_REPLY)
     && (minor_opcode == 0)
     && (imid == im->private.proto.imid))
	return True;
    if ((major_opcode == XIM_ERROR)
     && (minor_opcode == 0)
     && (buf_s[2] & XIM_IMID_VALID)
     && (imid == im->private.proto.imid))
	return True;
    return False;
}

static Bool
_XimClose(
    Xim		 im)
{
    CARD32	 buf32[BUFSIZE/4];
    CARD8	*buf = (CARD8 *)buf32;
    CARD16	*buf_s = (CARD16 *)&buf[XIM_HEADER_SIZE];
    INT16	 len;
    CARD32	 reply32[BUFSIZE/4];
    char	*reply = (char *)reply32;
    XPointer	 preply;
    int		 buf_size;
    int		 ret_code;

    if (!IS_SERVER_CONNECTED(im))
	return True;

    buf_s[0] = im->private.proto.imid;		/* imid */
    buf_s[1] = 0;				/* unused */
    len = sizeof(CARD16)			/* sizeof imid */
        + sizeof(CARD16);			/* sizeof unused */

    _XimSetHeader((XPointer)buf, XIM_CLOSE, 0, &len);
    if (!(_XimWrite(im, len, (XPointer)buf)))
	return False;
    _XimFlush(im);
    buf_size = BUFSIZE;
    ret_code = _XimRead(im, &len, (XPointer)reply, buf_size,
						_XimCloseCheck, 0);
    if(ret_code == XIM_TRUE) {
	preply = reply;
    } else if(ret_code == XIM_OVERFLOW) {
	if(len <= 0) {
	    preply = reply;
	} else {
	    buf_size = len;
	    preply = Xmalloc(buf_size);
	    ret_code = _XimRead(im, &len, preply, buf_size, _XimCloseCheck, 0);
	    if(ret_code != XIM_TRUE) {
		Xfree(preply);
		return False;
	    }
	}
    } else
	return False;
    buf_s = (CARD16 *)((char *)preply + XIM_HEADER_SIZE);
    if (*((CARD8 *)preply) == XIM_ERROR) {
	_XimProcError(im, 0, (XPointer)&buf_s[3]);
	if(reply != preply)
	    Xfree(preply);
	return False;
    }

    if(reply != preply)
	Xfree(preply);
    return True;
}

void
_XimProtoIMFree(
    Xim		  im)
{
    /* XIMPrivateRec */
    if (im->private.proto.im_onkeylist) {
	Xfree(im->private.proto.im_onkeylist);
	im->private.proto.im_onkeylist = NULL;
    }
    if (im->private.proto.im_offkeylist) {
	Xfree(im->private.proto.im_offkeylist);
	im->private.proto.im_offkeylist = NULL;
    }
    if (im->private.proto.intrproto) {
	_XimFreeProtoIntrCallback(im);
	im->private.proto.intrproto = NULL;
    }
    if (im->private.proto.im_inner_resources) {
	Xfree(im->private.proto.im_inner_resources);
	im->private.proto.im_inner_resources = NULL;
    }
    if (im->private.proto.ic_inner_resources) {
	Xfree(im->private.proto.ic_inner_resources);
	im->private.proto.ic_inner_resources = NULL;
    }
    if (im->private.proto.hold_data) {
	Xfree(im->private.proto.hold_data);
	im->private.proto.hold_data = NULL;
    }
    if (im->private.proto.locale_name) {
	Xfree(im->private.proto.locale_name);
	im->private.proto.locale_name = NULL;
    }
    if (im->private.proto.ctom_conv) {
	_XlcCloseConverter(im->private.proto.ctom_conv);
	im->private.proto.ctom_conv = NULL;
    }
    if (im->private.proto.ctow_conv) {
	_XlcCloseConverter(im->private.proto.ctow_conv);
	im->private.proto.ctow_conv = NULL;
    }
    if (im->private.proto.ctoutf8_conv) {
	_XlcCloseConverter(im->private.proto.ctoutf8_conv);
	im->private.proto.ctoutf8_conv = NULL;
    }
    if (im->private.proto.cstomb_conv) {
	_XlcCloseConverter(im->private.proto.cstomb_conv);
	im->private.proto.cstomb_conv = NULL;
    }
    if (im->private.proto.cstowc_conv) {
	_XlcCloseConverter(im->private.proto.cstowc_conv);
	im->private.proto.cstowc_conv = NULL;
    }
    if (im->private.proto.cstoutf8_conv) {
	_XlcCloseConverter(im->private.proto.cstoutf8_conv);
	im->private.proto.cstoutf8_conv = NULL;
    }
    if (im->private.proto.ucstoc_conv) {
	_XlcCloseConverter(im->private.proto.ucstoc_conv);
	im->private.proto.ucstoc_conv = NULL;
    }
    if (im->private.proto.ucstoutf8_conv) {
	_XlcCloseConverter(im->private.proto.ucstoutf8_conv);
	im->private.proto.ucstoutf8_conv = NULL;
    }

#ifdef XIM_CONNECTABLE
    if (!IS_SERVER_CONNECTED(im) && IS_RECONNECTABLE(im)) {
	return;
    }
#endif /* XIM_CONNECTABLE */

    if (im->private.proto.saved_imvalues) {
        Xfree(im->private.proto.saved_imvalues);
        im->private.proto.saved_imvalues = NULL;
    }
    if (im->private.proto.default_styles) {
	Xfree(im->private.proto.default_styles);
	im->private.proto.default_styles = NULL;
    }

    /* core */
    if (im->core.res_name) {
        Xfree(im->core.res_name);
	im->core.res_name = NULL;
    }
    if (im->core.res_class) {
        Xfree(im->core.res_class);
	im->core.res_class = NULL;
    }
    if (im->core.im_values_list) {
	Xfree(im->core.im_values_list);
	im->core.im_values_list = NULL;
    }
    if (im->core.ic_values_list) {
	Xfree(im->core.ic_values_list);
	im->core.ic_values_list = NULL;
    }
    if (im->core.im_name) {
	Xfree(im->core.im_name);
	im->core.im_name = NULL;
    }
    if (im->core.styles) {
	Xfree(im->core.styles);
	im->core.styles = NULL;
    }
    if (im->core.im_resources) {
        Xfree(im->core.im_resources);
	im->core.im_resources = NULL;
    }
    if (im->core.ic_resources) {
        Xfree(im->core.ic_resources);
	im->core.ic_resources = NULL;
    }

    return;
}

static Status
_XimProtoCloseIM(
    XIM		 xim)
{
    Xim		 im = (Xim)xim;
    XIC		 ic;
    XIC		 next;
    Status	 status;

    ic = im->core.ic_chain;
    while (ic) {
	(*ic->methods->destroy) (ic);
	next = ic->core.next;
#ifdef XIM_CONNECTABLE
	if (!(!IS_SERVER_CONNECTED(im) && IS_RECONNECTABLE(im))) {
	    Xfree (ic);
	}
#else
	Xfree (ic);
#endif /* XIM_CONNECTABLE */
	ic = next;
    }
#ifdef XIM_CONNECTABLE
    if (!(!IS_SERVER_CONNECTED(im) && IS_RECONNECTABLE(im)))
	im->core.ic_chain = NULL;
#else
    im->core.ic_chain = NULL;
#endif

    _XimUnregisterServerFilter(im);
    _XimResetIMInstantiateCallback(im);
    status = (Status)_XimClose(im);
    status = (Status)_XimDisconnect(im) && status;
    _XimProtoIMFree(im);
#ifdef XIM_CONNECTABLE
    if (!IS_SERVER_CONNECTED(im) && IS_RECONNECTABLE(im)) {
	_XimReconnectModeSetAttr(im);
        for (ic = im->core.ic_chain; ic; ic = ic->core.next) {
	    _XimReconnectModeCreateIC(ic);
        }
	return 0;
    }
#endif /* XIM_CONNECTABLE */
    _XimDestroyIMStructureList(im);
    return status;
}

#ifdef XIM_CONNECTABLE
static Bool
_XimCheckIMQuarkList(
    XrmQuark		*quark_list,
    int			 num_quark,
    XrmQuark		 quark)
{
    register int	 i;

    for (i = 0; i < num_quark; i++) {
	if (quark_list[i] == quark) {
	    return True;
	}
    }
    return False;
}

static Bool
_XimSaveIMValues(
    Xim			 im,
    XIMArg		*arg)
{
    register XIMArg	*p;
    register int	 n;
    XrmQuark		*quark_list;
    XrmQuark		*tmp;
    XrmQuark		 quark;
    int			 num_quark;

    if (quark_list = im->private.proto.saved_imvalues) {
	num_quark = im->private.proto.num_saved_imvalues;
	for (p = arg; p && p->name; p++) {
	    quark = XrmStringToQuark(p->name);
	    if (_XimCheckIMQuarkList(quark_list, num_quark, quark)) {
		continue;
	    }
	    if (!(tmp = Xrealloc(quark_list,
				(sizeof(XrmQuark) * (num_quark + 1))))) {
		im->private.proto.saved_imvalues = quark_list;
		im->private.proto.num_saved_imvalues = num_quark;
		return False;
	    }
	    num_quark++;
	    quark_list = tmp;
	    quark_list[num_quark] = quark;
	}
	im->private.proto.saved_imvalues = quark_list;
	im->private.proto.num_saved_imvalues = num_quark;
	return True;
    }

    for (p = arg, n = 0; p && p->name; p++, n++);

    if (!(quark_list = Xmalloc(sizeof(XrmQuark) * n))) {
	return False;
    }

    im->private.proto.saved_imvalues = quark_list;
    im->private.proto.num_saved_imvalues = n;
    for (p = arg; p && p->name; p++, quark_list++) {
	*quark_list = XrmStringToQuark(p->name);
    }

    return True;
}

static char *
_XimDelayModeSetIMValues(
    Xim			 im,
    XIMArg		*arg)
{
    XimDefIMValues	 im_values;
    char		*name;
    XIMArg		*values;

    _XimGetCurrentIMValues(im, &im_values);
    name = _XimSetIMValueData(im, (XPointer)&im_values, values,
		im->core.im_resources, im->core.im_num_resources);
    _XimSetCurrentIMValues(im, &im_values);

    return name;
}
#endif /* XIM_CONNECTABLE */

static Bool
_XimSetIMValuesCheck(
    Xim          im,
    INT16        len,
    XPointer	 data,
    XPointer     arg)
{
    CARD16	*buf_s = (CARD16 *)((CARD8 *)data + XIM_HEADER_SIZE);
    CARD8	 major_opcode = *((CARD8 *)data);
    CARD8	 minor_opcode = *((CARD8 *)data + 1);
    XIMID	 imid = buf_s[0];

    if ((major_opcode == XIM_SET_IM_VALUES_REPLY)
     && (minor_opcode == 0)
     && (imid == im->private.proto.imid))
	return True;
    if ((major_opcode == XIM_ERROR)
     && (minor_opcode == 0)
     && (buf_s[2] & XIM_IMID_VALID)
     && (imid == im->private.proto.imid))
	return True;
    return False;
}

static char *
_XimProtoSetIMValues(
    XIM			 xim,
    XIMArg		*arg)
{
    Xim			 im = (Xim)xim;
    XimDefIMValues	 im_values;
    INT16		 len;
    CARD16		*buf_s;
    char		*tmp;
    CARD32		 tmp_buf32[BUFSIZE/4];
    char		*tmp_buf = (char *)tmp_buf32;
    char		*buf;
    int			 buf_size;
    char		*data;
    int			 data_len;
    int			 ret_len;
    int			 total;
    XIMArg		*arg_ret;
    CARD32		 reply32[BUFSIZE/4];
    char		*reply = (char *)reply32;
    XPointer		 preply;
    int			 ret_code;
    char		*name;

#ifndef XIM_CONNECTABLE
    if (!IS_SERVER_CONNECTED(im))
	return arg->name;
#else
    if (!_XimSaveIMValues(im, arg))
	return arg->name;

    if (!IS_SERVER_CONNECTED(im)) {
	if (IS_CONNECTABLE(im)) {
	    if (!_XimConnectServer(im)) {
	        return _XimDelayModeSetIMValues(im, arg);
	    }
        } else {
	    return arg->name;
        }
    }
#endif /* XIM_CONNECTABLE */

    _XimGetCurrentIMValues(im, &im_values);
    memset(tmp_buf, 0, sizeof(tmp_buf32));
    buf = tmp_buf;
    buf_size = XIM_HEADER_SIZE + sizeof(CARD16) + sizeof(INT16);
    data_len = BUFSIZE - buf_size;
    total = 0;
    arg_ret = arg;
    for (;;) {
	data = &buf[buf_size];
	if ((name = _XimEncodeIMATTRIBUTE(im, im->core.im_resources,
		im->core.im_num_resources, arg, &arg_ret, data, data_len,
		&ret_len, (XPointer)&im_values, XIM_SETIMVALUES))) {
	    break;
	}

	total += ret_len;
	if (!(arg = arg_ret)) {
	    break;
	}

	buf_size += ret_len;
	if (buf == tmp_buf) {
	    if (!(tmp = Xcalloc(buf_size + data_len, 1))) {
		return arg->name;
	    }
	    memcpy(tmp, buf, buf_size);
	    buf = tmp;
	} else {
	    if (!(tmp = Xrealloc(buf, (buf_size + data_len)))) {
		Xfree(buf);
		return arg->name;
	    }
            memset(&tmp[buf_size], 0, data_len);
	    buf = tmp;
	}
    }
    _XimSetCurrentIMValues(im, &im_values);

    if (!total)
	return (char *)NULL;

    buf_s = (CARD16 *)&buf[XIM_HEADER_SIZE];
    buf_s[0] = im->private.proto.imid;
    buf_s[1] = (INT16)total;

    len = (INT16)(sizeof(CARD16) + sizeof(INT16) + total);
    _XimSetHeader((XPointer)buf, XIM_SET_IM_VALUES, 0, &len);
    if (!(_XimWrite(im, len, (XPointer)buf))) {
	if (buf != tmp_buf)
	    Xfree(buf);
	return arg->name;
    }
    _XimFlush(im);
    if (buf != tmp_buf)
	Xfree(buf);
    buf_size = BUFSIZE;
    ret_code = _XimRead(im, &len, (XPointer)reply, buf_size,
					 _XimSetIMValuesCheck, 0);
    if(ret_code == XIM_TRUE) {
	preply = reply;
    } else if(ret_code == XIM_OVERFLOW) {
	if(len <= 0) {
	    preply = reply;
	} else {
	    buf_size = (int)len;
	    preply = Xmalloc(buf_size);
	    ret_code = _XimRead(im, &len, reply, buf_size,
					_XimSetIMValuesCheck, 0);
	    if(ret_code != XIM_TRUE) {
		Xfree(preply);
		return arg->name;
	    }
	}
    } else
	return arg->name;
    buf_s = (CARD16 *)((char *)preply + XIM_HEADER_SIZE);
    if (*((CARD8 *)preply) == XIM_ERROR) {
	_XimProcError(im, 0, (XPointer)&buf_s[3]);
	if(reply != preply)
	    Xfree(preply);
	return arg->name;
    }
    if(reply != preply)
	Xfree(preply);

    return name;
}

#ifdef XIM_CONNECTABLE
static char *
_XimDelayModeGetIMValues(
    Xim			 im,
    XIMArg		*arg)
{
    XimDefIMValues	 im_values;

    _XimGetCurrentIMValues(im, &im_values);
    return(_XimGetIMValueData(im, (XPointer)&im_values, arg,
			im->core.im_resources, im->core.im_num_resources));
}
#endif /* XIM_CONNECTABLE */

static Bool
_XimGetIMValuesCheck(
    Xim          im,
    INT16        len,
    XPointer	 data,
    XPointer     arg)
{
    CARD16	*buf_s = (CARD16 *)((CARD8 *)data + XIM_HEADER_SIZE);
    CARD8	 major_opcode = *((CARD8 *)data);
    CARD8	 minor_opcode = *((CARD8 *)data + 1);
    XIMID	 imid = buf_s[0];

    if ((major_opcode == XIM_GET_IM_VALUES_REPLY)
     && (minor_opcode == 0)
     && (imid == im->private.proto.imid))
	return True;
    if ((major_opcode == XIM_ERROR)
     && (minor_opcode == 0)
     && (buf_s[2] & XIM_IMID_VALID)
     && (imid == im->private.proto.imid))
	return True;
    return False;
}

static char *
_XimProtoGetIMValues(
    XIM			 xim,
    XIMArg		*arg)
{
    Xim			 im = (Xim)xim;
    register XIMArg	*p;
    register int	 n;
    CARD8		*buf;
    CARD16		*buf_s;
    INT16		 len;
    CARD32		 reply32[BUFSIZE/4];
    char		*reply = (char *)reply32;
    XPointer		 preply = NULL;
    int			 buf_size;
    int			 ret_code;
    char		*makeid_name;
    char		*decode_name;
    CARD16		*data = NULL;
    INT16		 data_len = 0;

#ifndef XIM_CONNECTABLE
    if (!IS_SERVER_CONNECTED(im))
	return arg->name;
#else
    if (!IS_SERVER_CONNECTED(im)) {
	if (IS_CONNECTABLE(im)) {
	    if (!_XimConnectServer(im)) {
	        return _XimDelayModeGetIMValues(im, arg);
	    }
        } else {
	    return arg->name;
        }
    }
#endif /* XIM_CONNECTABLE */

    for (n = 0, p = arg; p->name; p++)
	n++;

    if (!n)
	return (char *)NULL;

    buf_size =  sizeof(CARD16) * n;
    buf_size += XIM_HEADER_SIZE
	     + sizeof(CARD16)
	     + sizeof(INT16)
	     + XIM_PAD(buf_size);

    if (!(buf = Xcalloc(buf_size, 1)))
	return arg->name;
    buf_s = (CARD16 *)&buf[XIM_HEADER_SIZE];

    makeid_name = _XimMakeIMAttrIDList(im, im->core.im_resources,
				im->core.im_num_resources, arg,
				&buf_s[2], &len, XIM_GETIMVALUES);

    if (len) {
	buf_s[0] = im->private.proto.imid;	/* imid */
	buf_s[1] = len;				/* length of im-attr-id */
	XIM_SET_PAD(&buf_s[2], len);		/* pad */
	len += sizeof(CARD16)			/* sizeof imid */
	     + sizeof(INT16);			/* sizeof length of attr */

	_XimSetHeader((XPointer)buf, XIM_GET_IM_VALUES, 0, &len);
	if (!(_XimWrite(im, len, (XPointer)buf))) {
	    Xfree(buf);
	    return arg->name;
	}
	_XimFlush(im);
	Xfree(buf);
    	buf_size = BUFSIZE;
    	ret_code = _XimRead(im, &len, (XPointer)reply, buf_size,
    						_XimGetIMValuesCheck, 0);
	if(ret_code == XIM_TRUE) {
	    preply = reply;
	} else if(ret_code == XIM_OVERFLOW) {
	    if(len <= 0) {
		preply = reply;
	    } else {
		buf_size = len;
		preply = Xmalloc(buf_size);
		ret_code = _XimRead(im, &len, preply, buf_size,
						_XimGetIMValuesCheck, 0);
		if(ret_code != XIM_TRUE) {
		    Xfree(preply);
		    return arg->name;
		}
	    }
	} else
	    return arg->name;
	buf_s = (CARD16 *)((char *)preply + XIM_HEADER_SIZE);
	if (*((CARD8 *)preply) == XIM_ERROR) {
	    _XimProcError(im, 0, (XPointer)&buf_s[3]);
	    if(reply != preply)
		Xfree(preply);
	    return arg->name;
	}
	data = &buf_s[2];
	data_len = buf_s[1];
    }
    decode_name = _XimDecodeIMATTRIBUTE(im, im->core.im_resources,
			im->core.im_num_resources, data, data_len,
			arg, XIM_GETIMVALUES);
    if (reply != preply)
	Xfree(preply);

    if (decode_name)
	return decode_name;
    else
	return makeid_name;
}

static XIMMethodsRec     im_methods = {
    _XimProtoCloseIM,           /* close */
    _XimProtoSetIMValues,       /* set_values */
    _XimProtoGetIMValues,       /* get_values */
    _XimProtoCreateIC,          /* create_ic */
    _Ximctstombs,		/* ctstombs */
    _Ximctstowcs,		/* ctstowcs */
    _Ximctstoutf8		/* ctstoutf8 */
};

static Bool
_XimSetEncodingByName(
    Xim		  im,
    char	**buf,
    int		 *len)
{
    char	*encoding = (char *)NULL;
    int		 encoding_len;
    int		 compound_len;
    BYTE	*ret;

    _XGetLCValues(im->core.lcd, XlcNCodeset, &encoding, NULL);
    if (!encoding) {
	*buf = (char *)NULL;
	*len = 0;
	return True;
    }
    encoding_len = strlen(encoding);
    compound_len = strlen("COMPOUND_TEXT");
    *len = encoding_len + sizeof(BYTE) + compound_len + sizeof(BYTE);
    if (!(ret = Xmalloc(*len))) {
	return False;
    }
    *buf = (char *)ret;

    ret[0] = (BYTE)encoding_len;
    memcpy(&ret[1], encoding, encoding_len);
    ret += (encoding_len + sizeof(BYTE));
    ret[0] = (BYTE)compound_len;
    memcpy(&ret[1], "COMPOUND_TEXT", compound_len);
    return True;
}

static Bool
_XimSetEncodingByDetail(
    Xim		 im,
    char	**buf,
    int		 *len)
{
    *len = 0;
    *buf = NULL;
    return True;
}

static Bool
_XimGetEncoding(
    Xim		 im,
    CARD16	*buf,
    char	*name,
    int		 name_len,
    char	*detail,
    int		 detail_len)
{
    XLCd	 lcd = im->core.lcd;
    CARD16	 category = buf[0];
    CARD16	 idx = buf[1];
    int		 len;
    XlcConv	 ctom_conv = NULL;
    XlcConv	 ctow_conv = NULL;
    XlcConv	 ctoutf8_conv = NULL;
    XlcConv	 conv;
    XimProtoPrivateRec *private = &im->private.proto;

    if (idx == (CARD16)XIM_Default_Encoding_IDX) { /* XXX */
	if (!(ctom_conv = _XlcOpenConverter(lcd,
				 XlcNCompoundText, lcd, XlcNMultiByte)))
	    return False;
	if (!(ctow_conv = _XlcOpenConverter(lcd,
				 XlcNCompoundText, lcd, XlcNWideChar)))
	    return False;
	if (!(ctoutf8_conv = _XlcOpenConverter(lcd,
				 XlcNCompoundText, lcd, XlcNUtf8String)))
	    return False;
    }

    if (category == XIM_Encoding_NameCategory) {
	while (name_len > 0) {
	    len = (int)name[0];
	    if (!strncmp(&name[1], "COMPOUND_TEXT", len)) {
		if (!(ctom_conv = _XlcOpenConverter(lcd,
				 XlcNCompoundText, lcd, XlcNMultiByte)))
		    return False;
		if (!(ctow_conv = _XlcOpenConverter(lcd,
				 XlcNCompoundText, lcd, XlcNWideChar)))
		    return False;
		if (!(ctoutf8_conv = _XlcOpenConverter(lcd,
				 XlcNCompoundText, lcd, XlcNUtf8String)))
		    return False;
		break;
	    } else {
		/*
		 * Not yet
		 */
	    }
	    len += sizeof(BYTE);
	    name_len -= len;
	    name += len;
	}
    } else if (category == XIM_Encoding_DetailCategory) {
	/*
	 * Not yet
	 */
    } else {
	return False;
    }

    private->ctom_conv = ctom_conv;
    private->ctow_conv = ctow_conv;
    private->ctoutf8_conv = ctoutf8_conv;

    if (!(conv = _XlcOpenConverter(lcd,	XlcNCharSet, lcd, XlcNMultiByte)))
	return False;
    private->cstomb_conv = conv;

    if (!(conv = _XlcOpenConverter(lcd,	XlcNCharSet, lcd, XlcNWideChar)))
	return False;
    private->cstowc_conv = conv;

    if (!(conv = _XlcOpenConverter(lcd,	XlcNCharSet, lcd, XlcNUtf8String)))
	return False;
    private->cstoutf8_conv = conv;

    if (!(conv = _XlcOpenConverter(lcd,	XlcNUcsChar, lcd, XlcNChar)))
	return False;
    private->ucstoc_conv = conv;

    if (!(conv = _XlcOpenConverter(lcd,	XlcNUcsChar, lcd, XlcNUtf8String)))
	return False;
    private->ucstoutf8_conv = conv;

    return True;
}

static Bool
_XimEncodingNegoCheck(
    Xim		 im,
    INT16	 len,
    XPointer	 data,
    XPointer	 arg)
{
    CARD16	*buf_s = (CARD16 *)((CARD8 *)data + XIM_HEADER_SIZE);
    CARD8	 major_opcode = *((CARD8 *)data);
    CARD8	 minor_opcode = *((CARD8 *)data + 1);
    XIMID	 imid = buf_s[0];

    if ((major_opcode == XIM_ENCODING_NEGOTIATION_REPLY)
     && (minor_opcode == 0)
     && (imid == im->private.proto.imid))
	return True;
    if ((major_opcode == XIM_ERROR)
     && (minor_opcode == 0)
     && (buf_s[2] & XIM_IMID_VALID)
     && (imid == im->private.proto.imid))
	return True;
    return False;
}

static Bool
_XimEncodingNegotiation(
    Xim		 im)
{
    char	*name_ptr = 0;
    int		 name_len = 0;
    char	*detail_ptr = 0;
    int		 detail_len = 0;
    CARD8	*buf;
    CARD16	*buf_s;
    INT16	 len;
    CARD32	 reply32[BUFSIZE/4];
    char	*reply = (char *)reply32;
    XPointer	 preply;
    int		 buf_size;
    int		 ret_code;

    if (!(_XimSetEncodingByName(im, &name_ptr, &name_len)))
	return False;

    if (!(_XimSetEncodingByDetail(im, &detail_ptr, &detail_len)))
	goto free_name_ptr;

    len = sizeof(CARD16)
	+ sizeof(INT16)
	+ name_len
	+ XIM_PAD(name_len)
	+ sizeof(INT16)
	+ sizeof(CARD16)
	+ detail_len;

    if (!(buf = Xcalloc(XIM_HEADER_SIZE + len, 1)))
	goto free_detail_ptr;

    buf_s = (CARD16 *)&buf[XIM_HEADER_SIZE];

    buf_s[0] = im->private.proto.imid;
    buf_s[1] = (INT16)name_len;
    if (name_ptr)
	(void)memcpy((char *)&buf_s[2], name_ptr, name_len);
    XIM_SET_PAD(&buf_s[2], name_len);
    buf_s = (CARD16 *)((char *)&buf_s[2] + name_len);
    buf_s[0] = detail_len;
    buf_s[1] = 0;
    if (detail_ptr)
	(void)memcpy((char *)&buf_s[2], detail_ptr, detail_len);

    _XimSetHeader((XPointer)buf, XIM_ENCODING_NEGOTIATION, 0, &len);
    if (!(_XimWrite(im, len, (XPointer)buf))) {
	Xfree(buf);
	goto free_detail_ptr;
    }
    _XimFlush(im);
    Xfree(buf);
    buf_size = BUFSIZE;
    ret_code = _XimRead(im, &len, (XPointer)reply, buf_size,
					_XimEncodingNegoCheck, 0);
    if(ret_code == XIM_TRUE) {
	preply = reply;
    } else if(ret_code == XIM_OVERFLOW) {
	if(len <= 0) {
	    preply = reply;
	} else {
	    buf_size = len;
	    preply = Xmalloc(buf_size);
	    ret_code = _XimRead(im, &len, preply, buf_size,
					_XimEncodingNegoCheck, 0);
	    if(ret_code != XIM_TRUE)
		goto free_preply;
	}
    } else
	goto free_detail_ptr;
    buf_s = (CARD16 *)((char *)preply + XIM_HEADER_SIZE);
    if (*((CARD8 *)preply) == XIM_ERROR) {
	_XimProcError(im, 0, (XPointer)&buf_s[3]);
	goto free_preply;
    }

    if (!(_XimGetEncoding(im, &buf_s[1], name_ptr, name_len,
						detail_ptr, detail_len)))
	goto free_preply;

    Xfree(name_ptr);
    Xfree(detail_ptr);

    if(reply != preply)
	Xfree(preply);

    return True;

free_preply:
    if (reply != preply)
	Xfree(preply);

free_detail_ptr:
    Xfree(detail_ptr);

free_name_ptr:
    Xfree(name_ptr);

    return False;
}

#ifdef XIM_CONNECTABLE
static Bool
_XimSendSavedIMValues(
    Xim			 im)
{
    XimDefIMValues	 im_values;
    INT16		 len;
    CARD16		*buf_s;
    char		*tmp;
    CARD32		 tmp_buf32[BUFSIZE/4];
    char		*tmp_buf = (char *)tmp_buf32;
    char		*buf;
    int			 buf_size;
    char		*data;
    int			 data_len;
    int			 ret_len;
    int			 total;
    int			 idx;
    CARD32		 reply32[BUFSIZE/4];
    char		*reply = (char *)reply32;
    XPointer		 preply;
    int			 ret_code;

    _XimGetCurrentIMValues(im, &im_values);
    memset(tmp_buf, 0, sizeof(tmp_buf32));
    buf = tmp_buf;
    buf_size = XIM_HEADER_SIZE + sizeof(CARD16) + sizeof(INT16);
    data_len = BUFSIZE - buf_size;
    total = 0;
    idx = 0;
    for (;;) {
	data = &buf[buf_size];
	if (!_XimEncodeSavedIMATTRIBUTE(im, im->core.im_resources,
		im->core.im_num_resources, &idx, data, data_len,
		&ret_len, (XPointer)&im_values, XIM_SETIMVALUES)) {
	    if (buf != tmp_buf)
		Xfree(buf);
	    return False;
	}

	total += ret_len;
	if (idx == -1) {
	    break;
	}

	buf_size += ret_len;
	if (buf == tmp_buf) {
	    if (!(tmp = Xcalloc(buf_size + data_len, 1))) {
		return False;
	    }
	    memcpy(tmp, buf, buf_size);
	    buf = tmp;
	} else {
	    if (!(tmp = Xrealloc(buf, (buf_size + data_len)))) {
		Xfree(buf);
		return False;
	    }
            memset(&tmp[buf_size], 0, data_len);
	    buf = tmp;
	}
    }

    if (!total)
	return True;

    buf_s = (CARD16 *)&buf[XIM_HEADER_SIZE];
    buf_s[0] = im->private.proto.imid;
    buf_s[1] = (INT16)total;

    len = (INT16)(sizeof(CARD16) + sizeof(INT16) + total);
    _XimSetHeader((XPointer)buf, XIM_SET_IM_VALUES, 0, &len);
    if (!(_XimWrite(im, len, (XPointer)buf))) {
	if (buf != tmp_buf)
	    Xfree(buf);
	return False;
    }
    _XimFlush(im);
    if (buf != tmp_buf)
	Xfree(buf);
    buf_size = BUFSIZE;
    ret_code = _XimRead(im, &len, (XPointer)reply, buf_size,
    					 _XimSetIMValuesCheck, 0);
    if(ret_code == XIM_TRUE) {
	preply = reply;
    } else if(ret_code == XIM_OVERFLOW) {
	if(len <= 0) {
	    preply = reply;
	} else {
	    buf_size = (int)len;
	    preply = Xmalloc(buf_size);
	    ret_code = _XimRead(im, &len, reply, buf_size,
					_XimSetIMValuesCheck, 0);
    	    if(ret_code != XIM_TRUE) {
		Xfree(preply);
		return False;
	    }
	}
    } else
	return False;

    buf_s = (CARD16 *)((char *)preply + XIM_HEADER_SIZE);
    if (*((CARD8 *)preply) == XIM_ERROR) {
	_XimProcError(im, 0, (XPointer)&buf_s[3]);
    	if(reply != preply)
	    Xfree(preply);
	return False;
    }
    if(reply != preply)
	Xfree(preply);

    return True;
}

static void
_XimDelayModeIMFree(
    Xim		 im)
{
    if (im->core.im_resources) {
	Xfree(im->core.im_resources);
	im->core.im_resources = NULL;
    }
    if (im->core.ic_resources) {
	Xfree(im->core.ic_resources);
	im->core.ic_resources = NULL;
    }
    if (im->core.im_values_list) {
	Xfree(im->core.im_values_list);
	im->core.im_values_list = NULL;
    }
    if (im->core.ic_values_list) {
	Xfree(im->core.ic_values_list);
	im->core.ic_values_list = NULL;
    }
    return;
}

Bool
_XimConnectServer(
    Xim		 im)
{
    Xim		 save_im;

    if (!(save_im = Xmalloc(sizeof(XimRec))))
	return False;
    memcpy((char *)save_im, (char *)im, sizeof(XimRec));

    if (_XimPreConnect(im) && _XimConnection(im)
			&& _XimOpen(im) && _XimEncodingNegotiation(im)) {
	if (_XimSendSavedIMValues(im)) {
	    _XimDelayModeIMFree(save_im);
	    _XimRegisterServerFilter(im);
	    Xfree(save_im);
	    return True;
	}
    }
    memcpy((char *)im, (char *)save_im, sizeof(XimRec));
    Xfree(save_im);
    return False;
}

Bool
_XimDelayModeSetAttr(
    Xim			 im)
{
    XimDefIMValues	 im_values;

    if(!_XimSetIMResourceList(&im->core.im_resources,
						&im->core.im_num_resources)) {
	return False;
    }
    if(!_XimSetICResourceList(&im->core.ic_resources,
						&im->core.ic_num_resources)) {
	return False;
    }

    _XimSetIMMode(im->core.im_resources, im->core.im_num_resources);

    _XimGetCurrentIMValues(im, &im_values);
    if(!_XimSetLocalIMDefaults(im, (XPointer)&im_values,
			im->core.im_resources, im->core.im_num_resources)) {
	return False;
    }
    _XimSetCurrentIMValues(im, &im_values);
    if (im->private.proto.default_styles) {
        if (im->core.styles)
	    Xfree(im->core.styles);
        im->core.styles = im->private.proto.default_styles;
    }

    return True;
}

static Bool
_XimReconnectModeSetAttr(
    Xim			 im)
{
    XimDefIMValues	 im_values;

    if(!_XimSetIMResourceList(&im->core.im_resources,
						&im->core.im_num_resources)) {
	return False;
    }
    if(!_XimSetICResourceList(&im->core.ic_resources,
						&im->core.ic_num_resources)) {
	return False;
    }

    _XimSetIMMode(im->core.im_resources, im->core.im_num_resources);

    if (im->private.proto.default_styles) {
        if (im->core.styles)
	    Xfree(im->core.styles);
        im->core.styles = im->private.proto.default_styles;
    }

    return True;
}
#endif /* XIM_CONNECTABLE */

Bool
_XimProtoOpenIM(
    Xim		 im)
{
    _XimInitialResourceInfo();

    im->methods = &im_methods;

#ifdef XIM_CONNECTABLE
    _XimSetProtoResource(im);
#endif /* XIM_CONNECTABLE */

    if (_XimPreConnect(im)) {
	if (_XimConnection(im) && _XimOpen(im) && _XimEncodingNegotiation(im)) {
	    _XimRegisterServerFilter(im);
	    return True;
	}
	_XimShutdown(im);
#ifdef XIM_CONNECTABLE
    } else if (IS_DELAYBINDABLE(im)) {
	if (_XimDelayModeSetAttr(im))
	    return True;
#endif /* XIM_CONNECTABLE */
    }
    _XimProtoIMFree(im);
    return False;
}
