1 #ifndef lint
2 static char *rcsid = "$Id: imxport.c,v 1.11 1999/05/04 05:44:11 ishisone Exp $";
3 #endif
4 /*
5  * Copyright (c) 1994  Software Research Associates, Inc.
6  *
7  * Permission to use, copy, modify, and distribute this software and its
8  * documentation for any purpose and without fee is hereby granted, provided
9  * that the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of Software Research Associates not be
12  * used in advertising or publicity pertaining to distribution of the
13  * software without specific, written prior permission.  Software Research
14  * Associates makes no representations about the suitability of this software
15  * for any purpose.  It is provided "as is" without express or implied
16  * warranty.
17  *
18  * Author:  Makoto Ishisone, Software Research Associates, Inc., Japan
19  */
20 
21 #include "im.h"
22 
23 #include "MyDispatch.h"
24 
25 #include <X11/Xatom.h>
26 
27 #if (defined(IM_UNIX_TRANSPORT) || defined(IM_TCP_TRANSPORT))
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #endif
31 
32 #ifdef IM_UNIX_TRANSPORT
33 #include <sys/un.h>
34 #include <sys/stat.h>
35 #endif
36 
37 #ifdef IM_TCP_TRANSPORT
38 #include <netinet/in.h>
39 #include <netdb.h>
40 #endif
41 
42 extern int errno;
43 
44 /*
45  * X transport version number
46  *   This implementation uses:
47  *	+ ClientMessage (both single and multiple)
48  *	+ Property with notification by ClientMessage
49  *   So the major version is 0, minor version is 2.
50  *
51  * X transport dividing size
52  *   This size is the threshold between ClientMessage transfer
53  *   and Property transfer.  If the data to be sent is small,
54  *   transfer via ClientMessage is faster.  But since single
55  *   ClientMessage can transfer only 20bytes, there must be a
56  *   certain size above which the transfer via Property is
57  *   faster.
58  */
59 #define ServerMajorTransportVersion	0
60 #define ServerMinorTransportVersion	2
61 #define XTransportDividingSize		(20 * 5)
62 
63 
64 static int dummyDispatcher _Pt_((IMConnection *conn));
65 static void dumpBuf _Pt_((IMBuffer *ibp, char *title));
66 static Window communicationWindow _Pt_((Widget w));
67 static IMConnection *newConnection _Pt_((Widget protocol));
68 
69 #if defined(IM_TCP_TRANSPORT) || defined(IM_UNIX_TRANSPORT)
70 static void socketinput _Pt_((XtPointer cldata, int *fdp, XtInputId *idp));
71 static int socketFlush _Pt_((IMConnection *conn));
72 static void socketShutdown _Pt_((IMConnection *conn));
73 
74 static IMTransportOps SocketTransportOps = {
75     socketFlush,
76     socketShutdown,
77 };
78 #endif /* IM_TCP_TRANSPORT || IM_UNIX_TRANSPORT */
79 
80 #ifdef IM_X_TRANSPORT
81 static void xinput _Pt_((XEvent *ev, XtPointer cldata));
82 static void xdestroy _Pt_((XEvent *ev, XtPointer cldata));
83 static int xBrokenPipe _Pt_((Display *dpy, XErrorEvent *eev,
84 			     XPointer client_data));
85 static int xFlush _Pt_((IMConnection *conn));
86 static void xShutdown _Pt_((IMConnection *conn));
87 
88 static IMTransportOps XTransportOps = {
89     xFlush,
90     xShutdown,
91 };
92 #endif /* IM_X_TRANSPORT */
93 
94 
95 /*- dummyDispatcher: dummy dispatcher routine -*/
96 /* ARGSUSED */
97 static int
dummyDispatcher(conn)98 dummyDispatcher(conn)
99 IMConnection *conn;
100 {
101 #ifdef DEBUG
102     printf("dummyDispatcher -- this function should not be called!\n");
103 #endif
104     return 0;	/* for lint */
105 }
106 
107 /*- dumpBuf: dump input/output buffer (for debug) -*/
108 static void
dumpBuf(ibp,title)109 dumpBuf(ibp, title)
110 IMBuffer *ibp;
111 char *title;
112 {
113     int len = IMBUFLEN(ibp);
114     unsigned char *data = (unsigned char *)IMBUFDATA(ibp);
115     int i;
116 
117     printf("%s (%d bytes)", title, len);
118     for (i = 0; i < len; i++) {
119 	if (i % 16 == 0) printf("\n\t");
120 	printf("%02x ", *data++);
121     }
122     printf("\n");
123 }
124 
125 /*- communicationWindow: create a window for communication with client -*/
126 static Window
communicationWindow(w)127 communicationWindow(w)
128 Widget w;
129 {
130     return XCreateSimpleWindow(XtDisplay(w), XtWindow(w), 0, 0, 1, 1, 0, 0, 0);
131 }
132 
133 /*- newConnection: allocate IMConnection structure and initialize -*/
134 static IMConnection *
newConnection(protocol)135 newConnection(protocol)
136 Widget protocol;
137 {
138     IMConnection *conn;
139     static int connection_serial_number = 0;
140 
141     conn = (IMConnection *)XtMalloc(sizeof(IMConnection));
142 
143     conn->serial = ++connection_serial_number;		/* start from 1 */
144     conn->dispatcher = dummyDispatcher;
145 
146     IMBufInit(&conn->in_buf);
147     IMBufInit(&conn->out_buf);
148 
149     conn->byte_order = ORDER_UNKNOWN;
150     conn->im_list = NULL;
151     conn->proto_widget = protocol;
152 
153     conn->schedule = 0;
154     conn->queue_next = NULL;
155 
156     conn->next = NULL;
157 
158     return conn;
159 }
160 
161 #if defined(IM_TCP_TRANSPORT) || defined(IM_UNIX_TRANSPORT)
162 /*- socketinput: handler for input from TCP/Unix socket -*/
163 /* ARGSUSED */
164 static void
socketinput(cldata,fdp,idp)165 socketinput(cldata, fdp, idp)
166 XtPointer cldata;
167 int *fdp;
168 XtInputId *idp;
169 {
170     IMConnection *conn = (IMConnection *)cldata;
171     int fd = conn->transport.priv.sock.fd;
172     char buf[4096];
173     int n;
174     int cond;
175 
176     if ((n = read(fd, buf, sizeof(buf))) < 0) {
177 	cond = TRANSPORT_ERROR;
178     } else if (n == 0) {
179 	cond = TRANSPORT_EOF;
180     } else {
181 	IMBufAdd(&conn->in_buf, buf, n);
182 	cond = TRANSPORT_OK;
183     }
184 
185     if (DDEBUG_CONDITION(100)) dumpBuf(IM_INBUF(conn), "** input buffer");
186 
187     IMDispatch(conn, cond);
188 }
189 
190 /*- socketFlush: output to socket -*/
191 static int
socketFlush(conn)192 socketFlush(conn)
193 IMConnection *conn;
194 {
195     int fd = conn->transport.priv.sock.fd;
196     IMBuffer *ibp = IM_OUTBUF(conn);
197     int n;
198 
199     if ((n = write(fd, ibp->buf, IMBUFLEN(ibp))) < 0) {
200 	return TRANSPORT_ERROR;
201     } else {
202 	IMBufDiscard(ibp, n);
203 	return (IMBUFLEN(ibp) == 0) ? TRANSPORT_OK : TRANSPORT_PARTIAL;
204     }
205 }
206 
207 /*- socketShutdown: close socket and stop input callback associate with it -*/
208 static void
socketShutdown(conn)209 socketShutdown(conn)
210 IMConnection *conn;
211 {
212     int fd = conn->transport.priv.sock.fd;
213 
214     (void)shutdown(fd, 2);
215     (void)close(fd);
216     XtRemoveInput(conn->transport.priv.sock.id);
217 }
218 #endif /* IM_TCP_TRANSPORT || IM_UNIX_TRANSPORT */
219 
220 #ifdef IM_X_TRANSPORT
221 /*- xinput: handler for input via X inter-client communication -*/
222 static void
xinput(ev,cldata)223 xinput(ev, cldata)
224 XEvent *ev;
225 XtPointer cldata;
226 {
227     XClientMessageEvent *event = (XClientMessageEvent *)ev;
228     IMConnection *conn = (IMConnection *)cldata;
229     int cond;
230     Atom msg_type;
231 
232     TRACE(("imlib:xinput()\n"));
233 
234     if (event->type != ClientMessage ||
235 	event->window != conn->transport.priv.x.server) {
236 	return;
237     }
238 
239     msg_type = event->message_type;
240 
241     if (event->format == 32) {
242 	/*
243 	 * indirect reference -- data resides in a property,
244 	 * whose name is stored in the event.
245 	 */
246 	Atom propatom = event->data.l[1];
247 	long offset = 0;
248 	unsigned long remain;
249 
250 	if (msg_type != IMProtocolAtom(conn->proto_widget)) return;
251 
252 	do {
253 	    Atom actualtype;
254 	    int actualformat;
255 	    unsigned long nitems;
256 	    char *data = NULL;
257 
258 	    XGetWindowProperty(event->display, event->window, propatom,
259 			       offset, 1000, True, AnyPropertyType,
260 			       &actualtype, &actualformat, &nitems,
261 			       &remain, (unsigned char **)&data);
262 	    if (data == NULL) return;
263 	    if (actualformat != 8) {
264 		cond = TRANSPORT_ERROR;
265 	    } else {
266 		IMBufAdd(&conn->in_buf, data, (int)nitems);
267 		offset += nitems;
268 		cond = TRANSPORT_OK;
269 	    }
270 	    XFree(data);
271 	} while (remain > 0);
272     } else if (event->format == 8) {
273 	if (msg_type != IMProtocolAtom(conn->proto_widget) &&
274 	    msg_type != IMMoreDataAtom(conn->proto_widget)) {
275 	    return;
276 	}
277 	IMBufAdd(&conn->in_buf, event->data.b, 20);
278 	cond = TRANSPORT_OK;
279     } else {
280 	return;
281     }
282 
283     if (DDEBUG_CONDITION(100)) dumpBuf(IM_INBUF(conn), "** input buffer");
284 
285     IMDispatch(conn, cond);
286 }
287 
288 /*- xdestroy: handler for client comm. window destruction -*/
289 static void
xdestroy(ev,cldata)290 xdestroy(ev, cldata)
291 XEvent *ev;
292 XtPointer cldata;
293 {
294     XDestroyWindowEvent *event = (XDestroyWindowEvent *)ev;
295     IMConnection *conn = (IMConnection *)cldata;
296 
297     TRACE(("imlib:xdestroy()\n"));
298 
299     if (event->type != DestroyNotify ||
300 	event->window != conn->transport.priv.x.client) {
301 	return;
302     }
303     /*
304      * Call IMDispatch with TRANSPORT_ERROR, in order to
305      * shutdown connection and process queued operations.
306      */
307     IMDispatch(conn, TRANSPORT_ERROR);
308 }
309 
310 /*- xBrokenPipe: asyncronous BadWindow error handler -*/
311 /* ARGSUSED */
312 static int
xBrokenPipe(dpy,eev,client_data)313 xBrokenPipe(dpy, eev, client_data)
314 Display *dpy;
315 XErrorEvent *eev;
316 XPointer client_data;
317 {
318     TRACE(("xBrokenPipe()\n"));
319 
320     if (eev->error_code == BadWindow) {
321 	/*
322 	 * Search for the connection using window that caused the error.
323 	 * Note that we cannot pass the connection via client_data,
324 	 * Since the connection might be already destroyed by a previous
325 	 * error.
326 	 */
327 	Window bad_win = (Window)eev->resourceid;
328 	IMConnection *conn;
329 
330 	conn = IMConnectionList((Widget)client_data);
331 	while (conn != NULL) {
332 	    if (bad_win == conn->transport.priv.x.client) {
333 		DPRINT(("BadWindow on connection #%d\n", conn->serial));
334 		IMDispatch(conn, TRANSPORT_ERROR);
335 		break;
336 	    }
337 	    conn = conn->next;
338 	}
339 	return 0;
340     } else {
341 	return 1;
342     }
343 }
344 
345 /*- xFlush: output via X inter-client communication mechanism -*/
346 static int
xFlush(conn)347 xFlush(conn)
348 IMConnection *conn;
349 {
350     IMBuffer *ibp = IM_OUTBUF(conn);
351     XClientMessageEvent repl;
352     Widget w = conn->proto_widget;
353     Window client_win = conn->transport.priv.x.client;
354     Display *dpy = XtDisplay(w);
355     int length;
356     XAEHandle handle;
357 
358     if ((length = IMBUFLEN(ibp)) == 0) return TRANSPORT_OK;
359 
360     repl.type = ClientMessage;
361     repl.window = client_win;
362 
363     handle = XAESet(dpy, xBrokenPipe, (void (*)())NULL, (XPointer)w);
364 
365     if (IMBUFLEN(ibp) < XTransportDividingSize) {
366 	char *data = IMBUFDATA(ibp);
367 
368 	repl.format = 8;
369 	repl.message_type = IMMoreDataAtom(w);
370 	while (length > 20) {
371 	    bcopy(data, repl.data.b, 20);
372 	    XSendEvent(dpy, client_win, False, NoEventMask, (XEvent *)&repl);
373 	    data += 20;
374 	    length -= 20;
375 	}
376 	repl.message_type = IMProtocolAtom(w);
377 	bzero(repl.data.b, 20);
378 	bcopy(data, repl.data.b, length);
379 	XSendEvent(dpy, client_win, False, NoEventMask, (XEvent *)&repl);
380     } else {
381 	repl.format = 32;
382 	repl.message_type = IMProtocolAtom(w);
383 	repl.data.l[0] = length;
384 	repl.data.l[1] = IMKi2CommAtom(w);
385 	XChangeProperty(dpy, client_win, IMKi2CommAtom(w), XA_STRING,
386 			8, PropModeAppend,
387 			(unsigned char *)IMBUFDATA(ibp), IMBUFLEN(ibp));
388 	XSendEvent(dpy, client_win, False, NoEventMask, (XEvent *)&repl);
389     }
390 
391     XFlush(dpy);
392     XAEUnset(handle);
393 
394     IMBufClear(ibp);
395     return TRANSPORT_OK;
396 }
397 
398 /*- xShutdown: close communication channel by destroying comm. window -*/
399 static void
xShutdown(conn)400 xShutdown(conn)
401 IMConnection *conn;
402 {
403     Display *dpy = XtDisplay(conn->proto_widget);
404     MyRemoveAllEventHandler(dpy, conn->transport.priv.x.client);
405     MyRemoveAllEventHandler(dpy, conn->transport.priv.x.server);
406     XDestroyWindow(dpy, conn->transport.priv.x.server);
407 }
408 #endif /* IM_X_TRANSPORT */
409 
410 
411 /*
412  * Public functions
413  */
414 
415 #ifdef IM_TCP_TRANSPORT
416 int
IMCreateTCPService(portp,listenaddr)417 IMCreateTCPService(portp, listenaddr)
418 int *portp;
419 char *listenaddr;
420 {
421     struct sockaddr_in addr;
422     int optval = 1;
423     int sock;
424 
425     TRACE(("IMCreateTCPService(port=%d)\n", *portp));
426 
427     if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
428 	DPRINT(("socket(PF_INET) failed with %d\n", errno));
429 	return -1;
430     }
431 
432 #ifdef SO_REUSEADDR
433     (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
434 		     (char *)&optval, sizeof(optval));
435 #endif /* SO_REUSEADDR */
436 
437     memset((void *)&addr, 0, sizeof(addr));
438     if (listenaddr[0] == '\0') {
439 	    addr.sin_addr.s_addr = htonl(INADDR_ANY);
440     } else {
441 	    if (!inet_aton(listenaddr, &addr.sin_addr)) {
442 		    struct hostent  *hp;
443 		    struct in_addr  **pptr;
444 
445 		    if ((hp = gethostbyname((const char *)listenaddr)) == NULL) {
446 			    DPRINT(("Can't resolve %s\n", listenaddr));
447 			    return -1;
448 		    }
449 		    pptr = (struct in_addr **)hp->h_addr_list;
450 		    memcpy((void *)&addr.sin_addr, pptr[0], sizeof(struct in_addr));
451 	    }
452     }
453     addr.sin_family = AF_INET;
454     addr.sin_port = htons(*portp);
455 
456     if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
457 	DPRINT(("bind() failed with %d\n", errno));
458 	return -1;
459     }
460     if (*portp == 0) {
461 	int addr_len = sizeof(addr);
462 
463 	if (getsockname(sock, (struct sockaddr *)&addr, &addr_len) < 0) {
464 	    DPRINT(("getsockname() failed with %d\n", errno));
465 	    return -1;
466 	}
467 	*portp = ntohs(addr.sin_port);
468 	TRACE(("\tport=%d\n", *portp));
469     }
470     if (listen(sock, 4) < 0) {
471 	DPRINT(("listen() failed with %d\n", errno));
472 	return -1;
473     }
474     return sock;
475 }
476 
477 IMConnection *
IMTCPConnection(protocol,wellknownfd)478 IMTCPConnection(protocol, wellknownfd)
479 Widget protocol;
480 int wellknownfd;
481 {
482     IMConnection *conn;
483     int fd;
484     struct sockaddr_in addr;
485     int addrlen = sizeof(addr);
486     XtInputId id;
487 
488     TRACE(("IMTCPConnection()\n"));
489 
490     if ((fd = accept(wellknownfd, (struct sockaddr *)&addr, &addrlen)) < 0) {
491 	DPRINT(("accept() failed with %d\n", errno));
492 	return NULL;
493     }
494     conn = newConnection(protocol);
495     conn->transport.ops = &SocketTransportOps;
496     conn->transport.priv.sock.fd = fd;
497     DDPRINT(2, ("new connection #%d: transport=TCP socket=%d\n",
498 		conn->serial, fd));
499 
500     id = XtAppAddInput(XtWidgetToApplicationContext(protocol), fd,
501 		       (XtPointer)XtInputReadMask, socketinput,
502 		       (XtPointer)conn);
503     conn->transport.priv.sock.id = id;
504 
505     return conn;
506 }
507 #endif /* IM_TCP_TRANSPORT */
508 
509 #ifdef IM_UNIX_TRANSPORT
510 int
IMCreateUnixService(path)511 IMCreateUnixService(path)
512 char *path;
513 {
514     struct sockaddr_un addr;
515     int sock;
516     mode_t oldumask;
517 
518     TRACE(("IMCreateUnixService(%s)\n", path));
519     if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
520 	DPRINT(("socket(PF_UNIX) failed with %d\n", errno));
521 	return -1;
522     }
523 
524     bzero((char *)&addr, sizeof(addr));
525     addr.sun_family = AF_UNIX;
526     (void)strcpy(addr.sun_path, path);
527 
528     /*
529      * Remove socket which is created by the previous process.
530      */
531     (void)unlink(path);
532     oldumask = umask(S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
533 
534     if (bind(sock, (struct sockaddr *)&addr, strlen(path) + 2) < 0) {
535 	DPRINT(("bind() failed with %d\n", errno));
536 	umask(oldumask);
537 	return -1;
538     }
539 
540     umask(oldumask);
541 
542     if (listen(sock, 4) < 0) {
543 	DPRINT(("listen() failed with %d\n", errno));
544 	return -1;
545     }
546 
547     return sock;
548 }
549 
550 IMConnection *
IMUnixConnection(protocol,wellknownfd)551 IMUnixConnection(protocol, wellknownfd)
552 Widget protocol;
553 int wellknownfd;
554 {
555     IMConnection *conn;
556     int fd;
557     struct sockaddr_un addr;
558     int addrlen = sizeof(addr);
559     XtInputId id;
560 
561     TRACE(("IMUnixConnection()\n"));
562 
563     if ((fd = accept(wellknownfd, (struct sockaddr *)&addr, &addrlen)) < 0) {
564 	DPRINT(("accept() failed with %d\n", errno));
565 	return NULL;
566     }
567     conn = newConnection(protocol);
568     conn->transport.ops = &SocketTransportOps;
569     conn->transport.priv.sock.fd = fd;
570     DDPRINT(2, ("new connection #%d: transport=UNIX socket=%d\n",
571 		conn->serial, fd));
572 
573     id = XtAppAddInput(XtWidgetToApplicationContext(protocol), fd,
574 		       (XtPointer)XtInputReadMask, socketinput,
575 		       (XtPointer)conn);
576     conn->transport.priv.sock.id = id;
577 
578     return conn;
579 }
580 #endif /* IM_UNIX_TRANSPORT */
581 
582 #ifdef IM_X_TRANSPORT
583 IMConnection *
IMXConnection(protocol,xev)584 IMXConnection(protocol, xev)
585 Widget protocol;
586 XEvent *xev;
587 {
588     XClientMessageEvent *event = (XClientMessageEvent *)xev;
589     XClientMessageEvent repl;
590     Display *dpy = XtDisplay(protocol);
591     Window client_window;
592     IMConnection *conn;
593     XAEHandle h;
594 
595     TRACE(("IMXConnection()\n"));
596 
597     if (event->type != ClientMessage ||
598 	event->display != dpy ||
599 	event->window != XtWindow(protocol) ||
600 	event->message_type != IMXConnectAtom(protocol) ||
601 	event->format != 32) {
602 	TRACE(("\tinvalid event\n"));
603 #ifdef DEBUG
604 	if (event->type != ClientMessage) printf("not ClientMessage\n");
605 	if (event->display != dpy) printf("wrong display\n");
606 	if (event->window != XtWindow(protocol)) printf("wrong window\n");
607 	if (event->message_type != IMXConnectAtom(protocol)) {
608 	    printf("wrong message type (%ld should be %ld)\n",
609 		   event->message_type, IMXConnectAtom(protocol));
610 	}
611 	if (event->format != 32) printf("wrong format\n");
612 #endif
613 	return NULL;
614     }
615     client_window = event->data.l[0];
616     if (!IMValidateWindow(dpy, client_window, (IMWindowProfile *)NULL)) {
617 	DPRINT(("client window %08lx does not exist\n", client_window));
618 	return NULL;
619     }
620     conn = newConnection(protocol);
621     conn->transport.ops = &XTransportOps;
622     conn->transport.priv.x.client = client_window;
623     conn->transport.priv.x.server = communicationWindow(protocol);
624     DDPRINT(2, ("new connection #%d: transport=X client=%08lx\n",
625 		conn->serial, client_window));
626     TRACE(("\ttransport version: %ld.%ld\n",
627 	   event->data.l[1], event->data.l[2]));
628 
629     repl.type = ClientMessage;
630     repl.window = client_window;
631     repl.message_type = IMXConnectAtom(protocol);
632     repl.format = 32;
633     repl.data.l[0] = conn->transport.priv.x.server;
634     repl.data.l[1] = ServerMajorTransportVersion;
635     repl.data.l[2] = ServerMinorTransportVersion;
636     repl.data.l[3] = XTransportDividingSize;
637     /* make it safe... */
638     h = XAESetIgnoreErrors(dpy);
639     XSendEvent(dpy, client_window, False, NoEventMask, (XEvent *)&repl);
640     MyAddEventHandler(dpy, client_window, DestroyNotify, StructureNotifyMask,
641 		      xdestroy, (XtPointer)conn);
642     XAEUnset(h);
643 
644     MyAddEventHandler(dpy, conn->transport.priv.x.server,
645 		      ClientMessage, NoEventMask, xinput, (XtPointer)conn);
646 
647     return conn;
648 }
649 #endif /* IM_X_TRANSPORT */
650 
651 int
IMFlush(conn)652 IMFlush(conn)
653 IMConnection *conn;
654 {
655     TRACE(("IMFlush(#%d)\n", conn->serial));
656 
657     if (DDEBUG_CONDITION(100)) dumpBuf(IM_OUTBUF(conn), "** output buffer");
658 
659     return (*conn->transport.ops->flush)(conn);
660 }
661 
662 void
IMShutdown(conn)663 IMShutdown(conn)
664 IMConnection *conn;
665 {
666     TRACE(("IMShutdown(#%d)\n", conn->serial));
667     (*conn->transport.ops->shutdown)(conn);
668 }
669 
670 void
IMCloseConnection(conn)671 IMCloseConnection(conn)
672 IMConnection *conn;
673 {
674     IMIM *imp;
675 
676     DDPRINT(2, ("IMCloseConnection(#%d)\n", conn->serial));
677 
678     imp = conn->im_list;
679     while (imp != NULL) {
680 	IMIM *next = imp->next;
681 
682 	IMDestroyIM(imp);
683 	imp = next;
684     }
685 
686     IMBufClear(&conn->in_buf);
687     IMBufClear(&conn->out_buf);
688 #ifdef notdef
689     destroyAuth(conn->server_auth);
690     destroyAuth(conn->client_auth);
691 #endif
692     IMShutdown(conn);
693 
694     IMUnregisterConnection(conn);
695 
696     XtFree((char *)conn);
697 }
698