Mercurial > kinput2.yaz
diff lib/imlib/imdispatch.c @ 0:92745d501b9a
initial import from kinput2-v3.1
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Mon, 08 Mar 2010 04:44:30 +0900 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/imlib/imdispatch.c Mon Mar 08 04:44:30 2010 +0900 @@ -0,0 +1,484 @@ +#ifndef lint +static char *rcsid = "$Id: imdispatch.c,v 1.9 1994/06/02 10:36:07 ishisone Exp $"; +#endif +/* + * Copyright (c) 1994 Software Research Associates, Inc. + * + * 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 name of Software Research Associates not be + * used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. Software Research + * Associates makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * Author: Makoto Ishisone, Software Research Associates, Inc., Japan + */ + +#include "im.h" +#include "imreq.h" + +static int checkRequest _Pt_((IMConnection *conn, + int *majorp, int *minorp, int *arglenp)); +static void ServerAuthPhase _Pt_((IMConnection *conn)); +static void CommunicationPhase _Pt_((IMConnection *conn)); +static int initialDispatcher _Pt_((IMConnection *conn)); +static int mainDispatcher _Pt_((IMConnection *conn)); + +/*- checkRequest: read and examine request header -*/ +static int +checkRequest(conn, majorp, minorp, arglenp) +IMConnection *conn; +int *majorp; +int *minorp; +int *arglenp; +{ + IMBuffer *ibp = IM_INBUF(conn); + int len; + + /* check if there is enough data for the request header */ + if (IMBUFLEN(ibp) < 4) return 0; + +#ifdef XIM_BC + if (conn->has_length_bug) { + len = (int)IMGetC16(conn, 2); + } else { + len = (int)IMGetC16(conn, 2) * 4; + } +#else /* XIM_BC */ + len = (int)IMGetC16(conn, 2) * 4; +#endif /* XIM_BC */ + + TRACE(("checkRequest(): length=%d, buflen=%d\n", len, IMBUFLEN(ibp))); + + if (IMBUFLEN(ibp) >= len + 4) { + /* + * All the request data is in the buffer. + * Retrieve the request header, and discard it. + */ + *majorp = IMGetC8(conn, 0); + *minorp = IMGetC8(conn, 1); + *arglenp = len; + IMBufDiscard(ibp, 4); + return 1; + } else { + return 0; + } +} + +#ifdef XIM_BC +/*- countConnectRequestLen: count length of XIM_CONNECT request -*/ +int +countConnectRequestLen(conn) +IMConnection *conn; +{ + IMBuffer *ibp = IM_INBUF(conn); + int data_length = IMBUFLEN(ibp); + int num_proto_names; + int offset; + int i; + + /* + * Encoding of XIM_CONNECT request is: + * + * 4 request header + * 1 CARD8 byte order + * 1 unused + * 2 CARD16 major protocol version + * 2 CARD16 minor protocol version + * 2 CARD16 number of auth protocol names + * N LISTofSTRING auth protocol names + * + * where STRING is: + * 2 CARD16 length (N) + * N LISTofChar string + * p pad, p = Pad(2+N) + */ + + /* skip request header */ + data_length -= 4; + offset = 4; + + if (data_length < 8) return 0; /* incomplete data */ + + /* get the number of auth protocol names */ + num_proto_names = (int)IMGetC16(conn, offset + 6); + + /* skip fixed part */ + data_length -= 8; + offset += 8; + + /* count variable (i.e. list of string) part */ + for (i = 0; i < num_proto_names; i++) { + int str_len, pad_len; + + if (data_length < 2) return 0; /* incomplete data */ + str_len = (int)IMGetC16(conn, offset); + pad_len = (4 - ((2 + str_len) % 4)) % 4; + data_length -= 2 + str_len + pad_len; + offset += 2 + str_len + pad_len; + if (data_length < 0) return 0; /* incomplete data */ + } + + return offset - 4; /* 4 for the header */ +} +#endif /* XIM_BC */ + +/* + * Phase shifters + */ + +/*- ServerAuthPhase: move to server authentication phase -*/ +static void +ServerAuthPhase(conn) +IMConnection *conn; +{ + /* not yet implemented */ + IMSendSimpleRequest(conn, XIM_AUTH_NG, 0); + IMSchedule(conn, SCHED_CLOSE); +} + +/*- CommunicationPhase: move to comunication (i.e. main) phase -*/ +static void +CommunicationPhase(conn) +IMConnection *conn; +{ + IMPutHeader(conn, XIM_CONNECT_REPLY, 0, 4); + IMPutC16(conn, XIM_MAJOR_PROTOCOL_VERSION); + IMPutC16(conn, XIM_MINOR_PROTOCOL_VERSION); + IMSchedule(conn, SCHED_WRITE); + + conn->dispatcher = mainDispatcher; +} + +/* + * Request dispatchers + */ + +/*- initialDispatcher: the initial dispatcher -*/ +static int +initialDispatcher(conn) +IMConnection *conn; +{ + IMBuffer *ibp = IM_INBUF(conn); + int major, minor, arglen; + int major_protocol, minor_protocol; + int num_auth; +#ifdef XIM_BC + int counted_len; + int field_len; +#endif /* XIM_BC */ + + TRACE(("imlib:initialDispatcher(#%d)\n", conn->serial)); + + /* + * The first request must be 'XIM_CONNECT'. + */ + if (conn->byte_order == ORDER_UNKNOWN) { + char *p = IMBUFDATA(ibp); + + /* + * Check the byte-order first. + * In order to do it, we need at least 5 bytes of data. + */ + if (IMBUFLEN(ibp) < 5) return 0; + + /* + * Check the byte-order byte (5th byte of the data). + */ + switch (p[4]) { + case 0x42: /* 'B' -- big endian */ + conn->byte_order = ORDER_BIG; + TRACE(("\tbyte order is big endian\n")); + break; + case 0x6c: /* 'l' -- little endian */ + conn->byte_order = ORDER_LITTLE; + TRACE(("\tbyte order is little endian\n")); + break; + default: /* invalid request */ + /* + * what can we do here? since we don't know the byte order + * of the client, we cannot send error reply. but we can + * send XIM_AUTH_NG, because this request does not have + * byte order dependency. + */ + DDPRINT(2, ("invalid byte order field (%c)\n", p[4])); + goto send_ng; + } + } + +#ifdef XIM_BC + /* + * Xlib implementation of early X11R6 has a bug in the + * length field of request header. The field should + * represent the length of request data as the number + * of 4byte units, but the buggy Xlib puts number of + * bytes instead. + */ + + /* length of data by examining contents */ + counted_len = countConnectRequestLen(conn); /* in bytes */ + if (counted_len == 0) return 0; /* incomplete */ + /* length of data by reading length field */ + field_len = IMGetC16(conn, 2); /* num of 4byte element */ + + /* + * If the request packet comforms to the specification, + * field_len * 4 == counted_len + * but in case of buggy Xlib implementation, + * field_len == counted_len + */ + if (counted_len == field_len * 4) { + conn->has_length_bug = 0; + } else if (counted_len == field_len) { + /* buggy Xlib implementation */ + DPRINT(("connection #%d has length field bug\n", conn->serial)); + conn->has_length_bug = 1; + } else { + /* totally broken */ + DPRINT(("connection #%d is broken\n", conn->serial)); + goto send_ng; + } +#endif + + /* + * See if the entire data of the request is ready. + */ + if (!checkRequest(conn, &major, &minor, &arglen)) return 0; + + /* + * Check the arguments... + */ + if (major != XIM_CONNECT || minor != 0 || arglen < 8) { + DPRINT(("invalid initial request (major=%d, minor=%d, arglen=%d\n", + major, minor, arglen)); + goto send_ng; + } + + major_protocol = (int)IMGetC16(conn, 2); + minor_protocol = (int)IMGetC16(conn, 4); + TRACE(("\tprototol version: major=%d, minor=%d\n", + major_protocol, minor_protocol)); + conn->major_protocol_version = major_protocol; + conn->minor_protocol_version = minor_protocol; + + num_auth = (int)IMGetC16(conn, 6); + TRACE(("\tnumber of auth protocols: %d\n", num_auth)); + + if (major_protocol > XIM_MAJOR_PROTOCOL_VERSION || + (major_protocol == XIM_MAJOR_PROTOCOL_VERSION && + minor_protocol > XIM_MINOR_PROTOCOL_VERSION)) { + DPRINT(("unsupported protocol (%d,%d)\n", + major_protocol, minor_protocol)); + goto send_ng; + } + + if (num_auth > 0) { + ServerAuthPhase(conn); +#ifdef notdef + } else if (do_client_authentication) { + ClientAuthPhase(conn); +#endif + } else { + CommunicationPhase(conn); + } + + IMBufDiscard(ibp, arglen); + + return 1; + + send_ng: + DDPRINT(2, ("send XIM_AUTH_NG to #%d\n", conn->serial)); + IMSendSimpleRequest(conn, XIM_AUTH_NG, 0); + IMSchedule(conn, SCHED_SHUTDOWN); + return 0; +} + +/*- mainDispatcher: main dispatcher -*/ +static int +mainDispatcher(conn) +IMConnection *conn; +{ + IMBuffer *ibp = IM_INBUF(conn); + int major, minor, arglen; + IMRequest *req; + + TRACE(("imlib:mainDispatcher(#%d)\n", conn->serial)); + + /* + * When X transport is used, NUL bytes might appear between + * requests. So discard them first. + */ + IMBufDiscardNUL(ibp); + + /* + * Check if the entire request data is on the input buffer. + */ + if (!checkRequest(conn, &major, &minor, &arglen)) return 0; + + /* + * Check major opcode. + */ + req = IMMainDispatchTable[major]; + if (req == NULL) { + DDPRINT(2, ("bad major opcode(%d)\n", major)); + IMSendBadProtocol(conn, "Invalid major opcode"); + goto ret; + } + + /* + * Then, check minor opcode. + */ + while (req != NULL) { + if (req->minor == minor) break; + req = req->next; + } + if (req == NULL) { + DDPRINT(2, ("bad minor opcode(%d,%d)\n", major, minor)); + IMSendBadProtocol(conn, "Invalid minor opcode"); + goto ret; + } + + /* + * Opcode is valid. Call the request processing routine. + */ + DDPRINT(2, ("** processing %s request...\n", req->name)); + (*req->proc)(conn, major, minor, arglen); + + ret: + /* + * Discard the argument portion. + */ + IMBufDiscard(ibp, arglen); + + return 1; +} + +/* + * Public functions + */ + +void +IMSetInitialDispatcher(conn) +IMConnection *conn; +{ + TRACE(("IMSetInitialDispatcher(#%d)\n", conn->serial)); + conn->dispatcher = initialDispatcher; +} + +void +IMDispatch(conn, cond) +IMConnection *conn; +int cond; +{ + TRACE(("IMDispatch(#%d)\n", conn->serial)); + + switch (cond) { + case TRANSPORT_OK: + /* + * Call dispatcher while data is ready. + */ + while ((*conn->dispatcher)(conn)) + /* empty body */; + + /* + * Do compaction to the input buffer. + */ + IMBufCompact(IM_INBUF(conn)); + break; + + case TRANSPORT_ERROR: + DDPRINT(2, ("transport error\n")); + IMSchedule(conn, SCHED_SHUTDOWN); + break; + + case TRANSPORT_EOF: + DDPRINT(2, ("transport EOF\n")); + IMSchedule(conn, SCHED_CLOSE); + break; + } + + /* + * If there's something to be done (i.e. scheduled), + * do it. + */ + if (!IMQueueEmpty(conn->proto_widget)) { + IMProcessQueue(conn->proto_widget); + } +} + +void +IMSchedule(conn, type) +IMConnection *conn; +int type; +{ + TRACE(("IMSchedule(#%d, %d)\n", conn->serial, type)); + + if (conn->schedule & type) return; /* already scheduled */ + + if (conn->schedule == 0) { + TRACE(("insert into the queue\n")); + IMPushQueue(conn); + } + conn->schedule |= type; +} + +void +IMProcessQueue(w) +Widget w; +{ + IMConnection *conn; + IMConnection *tmp = NULL; + + TRACE(("IMProcessQueue()\n")); + + while ((conn = IMPopQueue(w)) != NULL) { + int schedule; + + schedule = conn->schedule; + conn->schedule = 0; + + if (schedule & SCHED_SHUTDOWN) { + /* + * This connection is dead. Don't need to + * flush output before closing. + */ + IMCloseConnection(conn); + } else { + if (schedule & SCHED_WRITE) { + /* + * Flush output buffer. + */ + int ret; + + ret = IMFlush(conn); + switch (ret) { + case TRANSPORT_ERROR: + IMCloseConnection(conn); + continue; + case TRANSPORT_PARTIAL: + /* to be queued again */ + conn->schedule = schedule; + conn->queue_next = tmp; + tmp = conn; + continue; + } + } + if (schedule & SCHED_CLOSE) { + IMCloseConnection(conn); + } + } + } + + /* reschedule */ + while (tmp != NULL) { + IMConnection *next = tmp->queue_next; + + TRACE(("reschedule #%d\n", tmp->serial)); + IMPushQueue(tmp); + tmp = next; + } +}