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