chooser.c revision f9f7a7f2
1de3c0529Smrg/*
2 *
3Copyright 1990, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24 *
25 * Author:  Keith Packard, MIT X Consortium
26 */
27
28
29/*
30 * Chooser - display a menu of names and let the user select one
31 */
32
33/*
34 * Layout:
35 *
36 *  +--------------------------------------------------+
37 *  |             +------------------+                 |
38 *  |             |      Label       |                 |
39 *  |             +------------------+                 |
40 *  |    +-+--------------+                            |
41 *  |    |^| name-1       |                            |
42 *  |    ||| name-2       |                            |
43 *  |    |v| name-3       |                            |
44 *  |    | | name-4       |                            |
45 *  |    | | name-5       |                            |
46 *  |    | | name-6       |                            |
47 *  |    +----------------+                            |
48 *  |    cancel  accept  ping                          |
49 *  +--------------------------------------------------+
50 */
51
52#include    <X11/Intrinsic.h>
53#include    <X11/StringDefs.h>
54#include    <X11/Xatom.h>
55
56#include    <X11/Xaw/Paned.h>
57#include    <X11/Xaw/Label.h>
58#include    <X11/Xaw/Viewport.h>
59#include    <X11/Xaw/List.h>
60#include    <X11/Xaw/Box.h>
61#include    <X11/Xaw/Command.h>
62
63#include    "dm.h"
64
65#include    <X11/Xdmcp.h>
66
67#include    <sys/types.h>
68#include    <stdio.h>
69#include    <ctype.h>
70
71#ifdef USE_XINERAMA
72# include    <X11/extensions/Xinerama.h>
73#endif
74
75#if defined(SVR4)
76# include    <sys/sockio.h>
77#endif
78#if defined(SVR4) && defined(PowerMAX_OS)
79# include    <sys/stropts.h>
80#endif
81#if defined(SYSV) && defined(i386)
82# include    <sys/stream.h>
83#endif
84
85#include    "dm_socket.h"
86
87#include    <arpa/inet.h>
88
89#include    <sys/ioctl.h>
90
91#ifdef HAVE_SYS_PARAM_H
92# include <sys/param.h>
93# ifdef BSD
94#  if (BSD >= 199103)
95#   define VARIABLE_IFREQ
96#  endif
97# endif
98#endif
99
100#ifdef XKB
101# include <X11/extensions/XKBbells.h>
102#endif
103
104#define BROADCAST_HOSTNAME  "BROADCAST"
105
106#ifndef ishexdigit
107# define ishexdigit(c)	(isdigit(c) || ('a' <= (c) && (c) <= 'f'))
108#endif
109
110#ifdef hpux
111# include <sys/utsname.h>
112# ifdef HAS_IFREQ
113#  include <net/if.h>
114# endif
115#else
116# ifdef __convex__
117#  include <sync/queue.h>
118#  include <sync/sema.h>
119# endif
120# include <net/if.h>
121#endif /* hpux */
122
123#include    <netdb.h>
124#include    <X11/keysym.h>
125
126static int FromHex (char *s, char *d, int len);
127static int oldline;
128
129static Widget	    toplevel, label, viewport, paned, list, box, cancel, acceptit, ping;
130
131static void	CvtStringToARRAY8(
132    XrmValuePtr	args,
133    Cardinal	*num_args,
134    XrmValuePtr	fromVal,
135    XrmValuePtr	toVal);
136
137static struct _app_resources {
138    ARRAY8Ptr   xdmAddress;
139    ARRAY8Ptr	clientAddress;
140    int		connectionType;
141} app_resources;
142
143#define offset(field) XtOffsetOf(struct _app_resources, field)
144
145#define XtRARRAY8   "ARRAY8"
146
147static XtResource  resources[] = {
148    {"xdmAddress",	"XdmAddress",  XtRARRAY8,	sizeof (ARRAY8Ptr),
149	offset (xdmAddress),	    XtRString,	NULL },
150    {"clientAddress",	"ClientAddress",  XtRARRAY8,	sizeof (ARRAY8Ptr),
151	offset (clientAddress),	    XtRString,	NULL },
152    {"connectionType",	"ConnectionType",   XtRInt,	sizeof (int),
153	offset (connectionType),    XtRImmediate,	(XtPointer) 0 }
154};
155#undef offset
156
157static XrmOptionDescRec options[] = {
158    { "-xdmaddress",	"*xdmAddress",	    XrmoptionSepArg,	NULL },
159    { "-clientaddress",	"*clientAddress",   XrmoptionSepArg,	NULL },
160    { "-connectionType","*connectionType",  XrmoptionSepArg,	NULL },
161};
162
163typedef struct _hostAddr {
164    struct _hostAddr	*next;
165    struct sockaddr	*addr;
166    int			addrlen;
167    xdmOpCode		type;
168} HostAddr;
169
170static HostAddr    *hostAddrdb;
171
172typedef struct _hostName {
173    struct _hostName	*next;
174    char		*fullname;
175    int			willing;
176    ARRAY8		hostname, status;
177    CARD16		connectionType;
178    ARRAY8		hostaddr;
179} HostName;
180
181static HostName    *hostNamedb;
182
183static int  socketFD;
184#if defined(IPv6) && defined(AF_INET6)
185static int  socket6FD;
186#endif
187
188static int  pingTry;
189
190#define PING_INTERVAL	2000
191#define TRIES		3
192
193static XdmcpBuffer	directBuffer, broadcastBuffer;
194static XdmcpBuffer	buffer;
195
196#if (defined(SVR4) && !defined(sun) && !defined(__sgi) && !defined(NCR)) && defined(SIOCGIFCONF)
197
198/* Deal with different SIOCGIFCONF ioctl semantics on these OSs */
199
200static int
201ifioctl (int fd, int cmd, char *arg)
202{
203    struct strioctl ioc;
204    int ret;
205
206    bzero((char *) &ioc, sizeof(ioc));
207    ioc.ic_cmd = cmd;
208    ioc.ic_timout = 0;
209    if (cmd == SIOCGIFCONF)
210    {
211	ioc.ic_len = ((struct ifconf *) arg)->ifc_len;
212	ioc.ic_dp = ((struct ifconf *) arg)->ifc_buf;
213    }
214    else
215    {
216	ioc.ic_len = sizeof(struct ifreq);
217	ioc.ic_dp = arg;
218    }
219    ret = ioctl(fd, I_STR, (char *) &ioc);
220    if (ret >= 0 && cmd == SIOCGIFCONF)
221	((struct ifconf *) arg)->ifc_len = ioc.ic_len;
222    return(ret);
223}
224#else /* (SVR4 && !sun && !NCR) && SIOCGIFCONF */
225# define ifioctl ioctl
226#endif /* (SVR4 && !sun && !NCR) && SIOCGIFCONF */
227
228
229/* ARGSUSED */
230static void
231PingHosts (XtPointer closure, XtIntervalId *id)
232{
233    HostAddr	*hosts;
234    int		 sfd = socketFD;
235
236    for (hosts = hostAddrdb; hosts; hosts = hosts->next)
237    {
238#if defined(IPv6) && defined(AF_INET6)
239	if ( ((struct sockaddr *) hosts->addr)->sa_family == AF_INET6 )
240	    sfd = socket6FD;
241	else
242	    sfd = socketFD;
243#endif
244	if (hosts->type == QUERY)
245	    XdmcpFlush (sfd, &directBuffer,
246			(XdmcpNetaddr) hosts->addr, hosts->addrlen);
247	else
248	    XdmcpFlush (sfd, &broadcastBuffer,
249			(XdmcpNetaddr) hosts->addr, hosts->addrlen);
250    }
251    if (++pingTry < TRIES)
252	XtAddTimeOut (PING_INTERVAL, PingHosts, (XtPointer) 0);
253}
254
255char	**NameTable;
256int	NameTableSize;
257
258static int
259HostnameCompare (const void *a, const void *b)
260{
261    return strcmp (*(char **)a, *(char **)b);
262}
263
264static void
265RebuildTable (int size)
266{
267    char	**newTable = NULL;
268    HostName	*names;
269    int		i;
270
271    if (size)
272    {
273	newTable = malloc (size * sizeof (char *));
274	if (!newTable)
275	    return;
276	for (names = hostNamedb, i = 0; names; names = names->next, i++)
277	    newTable[i] = names->fullname;
278	qsort (newTable, size, sizeof (char *), HostnameCompare);
279    }
280    XawListChange (list, newTable, size, 0, TRUE);
281    free (NameTable);
282    NameTable = newTable;
283    NameTableSize = size;
284}
285
286static int
287AddHostname (ARRAY8Ptr hostname, ARRAY8Ptr status, struct sockaddr *addr, int willing)
288{
289    HostName	*new, **names, *name;
290    ARRAY8	hostAddr = {0, NULL};
291    CARD16	connectionType;
292    int		fulllen;
293
294    switch (addr->sa_family)
295    {
296    case AF_INET:
297	hostAddr.data = (CARD8 *) &((struct sockaddr_in *) addr)->sin_addr;
298	hostAddr.length = 4;
299	connectionType = FamilyInternet;
300	break;
301#if defined(IPv6) && defined(AF_INET6)
302    case AF_INET6:
303	hostAddr.data = (CARD8 *) &((struct sockaddr_in6 *) addr)->sin6_addr;
304	hostAddr.length = 16;
305	connectionType = FamilyInternet6;
306	break;
307#endif
308    default:
309	hostAddr.data = (CARD8 *) "";
310	hostAddr.length = 0;
311	connectionType = FamilyLocal;
312	break;
313    }
314    for (names = &hostNamedb; *names; names = & (*names)->next)
315    {
316	name = *names;
317	if (connectionType == name->connectionType &&
318	    XdmcpARRAY8Equal (&hostAddr, &name->hostaddr))
319	{
320	    if (XdmcpARRAY8Equal (status, &name->status))
321	    {
322		return 0;
323	    }
324	    break;
325	}
326    }
327    if (!*names)
328    {
329	new = calloc (1, sizeof (HostName));
330	if (!new)
331	    return 0;
332	if (hostname->length)
333	{
334	    switch (addr->sa_family)
335	    {
336	    case AF_INET:
337#if defined(IPv6) && defined(AF_INET6)
338	    case AF_INET6:
339#endif
340		{
341		    struct hostent  *hostent;
342		    char	    *host;
343
344		    hostent = gethostbyaddr ((char *)hostAddr.data, hostAddr.length, addr->sa_family);
345		    if (hostent)
346		    {
347			XdmcpDisposeARRAY8 (hostname);
348			host = (char *)hostent->h_name;
349			XdmcpAllocARRAY8 (hostname, strlen (host));
350			memcpy(hostname->data, host, hostname->length);
351		    }
352		}
353	    }
354	}
355	if (!XdmcpAllocARRAY8 (&new->hostaddr, hostAddr.length))
356	{
357	    free (new);
358	    return 0;
359	}
360	memcpy(new->hostaddr.data, hostAddr.data, hostAddr.length);
361	new->connectionType = connectionType;
362	new->hostname = *hostname;
363
364	*names = new;
365	new->next = NULL;
366	NameTableSize++;
367    }
368    else
369    {
370	new = *names;
371	free (new->fullname);
372	XdmcpDisposeARRAY8 (&new->status);
373	XdmcpDisposeARRAY8 (hostname);
374    }
375    new->willing = willing;
376    new->status = *status;
377
378    hostname = &new->hostname;
379    fulllen = hostname->length;
380    if (fulllen < 30)
381	fulllen = 30;
382    fulllen += status->length + 10;
383    new->fullname = malloc (fulllen);
384    if (!new->fullname)
385    {
386	new->fullname = "Unknown";
387    }
388    else
389    {
390	snprintf(new->fullname, fulllen, "%-30.*s %*.*s",
391		 hostname->length, hostname->data,
392		 status->length, status->length, status->data);
393    }
394    RebuildTable (NameTableSize);
395    return 1;
396}
397
398static void
399DisposeHostname (HostName *host)
400{
401    XdmcpDisposeARRAY8 (&host->hostname);
402    XdmcpDisposeARRAY8 (&host->hostaddr);
403    XdmcpDisposeARRAY8 (&host->status);
404    free (host->fullname);
405    free (host);
406}
407
408static void
409EmptyHostnames (void)
410{
411    HostName	*hosts, *next;
412
413    for (hosts = hostNamedb; hosts; hosts = next)
414    {
415	next = hosts->next;
416	DisposeHostname (hosts);
417    }
418    NameTableSize = 0;
419    hostNamedb = NULL;
420    RebuildTable (NameTableSize);
421}
422
423/* ARGSUSED */
424static void
425ReceivePacket (XtPointer closure, int *source, XtInputId *id)
426{
427    XdmcpHeader	    header;
428    ARRAY8	    authenticationName = {0, NULL};
429    ARRAY8	    hostname = {0, NULL};
430    ARRAY8	    status = {0, NULL};
431    int		    saveHostname = 0;
432#if defined(IPv6) && defined(AF_INET6)
433    struct sockaddr_storage addr;
434#else
435    struct sockaddr addr;
436#endif
437    int		    addrlen;
438    int		    sfd = * (int *) closure;
439
440    addrlen = sizeof (addr);
441    if (!XdmcpFill (sfd, &buffer, (XdmcpNetaddr) &addr, &addrlen))
442	return;
443    if (!XdmcpReadHeader (&buffer, &header))
444	return;
445    if (header.version != XDM_PROTOCOL_VERSION)
446	return;
447    switch (header.opcode) {
448    case WILLING:
449	if (XdmcpReadARRAY8 (&buffer, &authenticationName) &&
450	    XdmcpReadARRAY8 (&buffer, &hostname) &&
451	    XdmcpReadARRAY8 (&buffer, &status))
452	{
453	    if (header.length == 6 + authenticationName.length +
454		hostname.length + status.length)
455	    {
456		if (AddHostname (&hostname, &status, (struct sockaddr *) &addr,
457				 header.opcode == (int) WILLING))
458		    saveHostname = 1;
459	    }
460	}
461	XdmcpDisposeARRAY8 (&authenticationName);
462	break;
463    case UNWILLING:
464	if (XdmcpReadARRAY8 (&buffer, &hostname) &&
465	    XdmcpReadARRAY8 (&buffer, &status))
466	{
467	    if (header.length == 4 + hostname.length + status.length)
468	    {
469		if (AddHostname (&hostname, &status, (struct sockaddr *) &addr,
470				 header.opcode == (int) WILLING))
471		    saveHostname = 1;
472
473	    }
474	}
475	break;
476    default:
477	break;
478    }
479    if (!saveHostname)
480    {
481	XdmcpDisposeARRAY8 (&hostname);
482	XdmcpDisposeARRAY8 (&status);
483    }
484}
485
486static void
487RegisterHostaddr (struct sockaddr *addr, int len, xdmOpCode type)
488{
489    HostAddr		*host, **prev;
490
491    host = malloc (sizeof (HostAddr));
492    if (!host)
493	return;
494    host->addr = malloc (len);
495    if (!host->addr)
496    {
497	free (host);
498	return;
499    }
500    memcpy(host->addr, addr, len);
501    host->addrlen = len;
502    host->type = type;
503    for (prev = &hostAddrdb; *prev; prev = &(*prev)->next)
504	;
505    *prev = host;
506    host->next = NULL;
507}
508
509/*
510 * Register the address for this host.
511 * Called with each of the names on the command line.
512 * The special name "BROADCAST" looks up all the broadcast
513 *  addresses on the local host.
514 */
515
516/* Handle variable length ifreq in BNR2 and later */
517#ifdef VARIABLE_IFREQ
518# define ifr_size(p) (sizeof (struct ifreq) + \
519		     (p->ifr_addr.sa_len > sizeof (p->ifr_addr) ? \
520		      p->ifr_addr.sa_len - sizeof (p->ifr_addr) : 0))
521#else
522# define ifr_size(p) (sizeof (struct ifreq))
523#endif
524
525static void
526RegisterHostname (char *name)
527{
528#if !defined(IPv6) || !defined(AF_INET6)
529    struct hostent	*hostent;
530#endif
531    struct sockaddr_in	in_addr;
532    struct ifconf	ifc;
533    register struct ifreq *ifr;
534    struct sockaddr	broad_addr;
535    char		buf[2048], *cp, *cplim;
536
537    if (!strcmp (name, BROADCAST_HOSTNAME))
538    {
539	ifc.ifc_len = sizeof (buf);
540	ifc.ifc_buf = buf;
541	if (ifioctl (socketFD, (int) SIOCGIFCONF, (char *) &ifc) < 0)
542	    return;
543
544	cplim = (char *) ifc.ifc_req + ifc.ifc_len;
545
546	for (cp = (char *) ifc.ifc_req; cp < cplim; cp += ifr_size (ifr))
547	{
548	    ifr = (struct ifreq *) cp;
549	    if (ifr->ifr_addr.sa_family != AF_INET)
550		continue;
551
552	    broad_addr = ifr->ifr_addr;
553	    ((struct sockaddr_in *) &broad_addr)->sin_addr.s_addr =
554		htonl (INADDR_BROADCAST);
555#ifdef SIOCGIFBRDADDR
556	    {
557		struct ifreq    broad_req;
558
559		broad_req = *ifr;
560		if (ifioctl (socketFD, SIOCGIFFLAGS, (char *) &broad_req) != -1 &&
561		    (broad_req.ifr_flags & IFF_BROADCAST) &&
562		    (broad_req.ifr_flags & IFF_UP)
563		    )
564		{
565		    broad_req = *ifr;
566		    if (ifioctl (socketFD, SIOCGIFBRDADDR, &broad_req) != -1)
567			broad_addr = broad_req.ifr_addr;
568		    else
569			continue;
570		}
571		else
572		    continue;
573	    }
574#endif
575	    in_addr = *((struct sockaddr_in *) &broad_addr);
576	    in_addr.sin_port = htons (XDM_UDP_PORT);
577#ifdef BSD44SOCKETS
578	    in_addr.sin_len = sizeof(in_addr);
579#endif
580	    RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr),
581			      BROADCAST_QUERY);
582	}
583    }
584    else
585    {
586	/* address as hex string, e.g., "12180022" (deprecated) */
587	if (strlen(name) == 8 &&
588	    FromHex(name, (char *)&in_addr.sin_addr, strlen(name)) == 0)
589	{
590	    in_addr.sin_family = AF_INET;
591	    in_addr.sin_port = htons (XDM_UDP_PORT);
592#ifdef BSD44SOCKETS
593	    in_addr.sin_len = sizeof(in_addr);
594#endif
595	    RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr),
596				QUERY);
597	}
598#if defined(IPv6) && defined(AF_INET6)
599	else {
600	    char sport[8];
601	    struct addrinfo *ai, *nai, hints;
602	    bzero(&hints,sizeof(hints));
603	    hints.ai_socktype = SOCK_DGRAM;
604	    snprintf(sport, sizeof(sport), "%d", XDM_UDP_PORT);
605	    if (getaddrinfo(name, sport, &hints, &ai) == 0) {
606		for (nai = ai ; nai != NULL ; nai = nai->ai_next) {
607		    if ((nai->ai_family == AF_INET) ||
608		        (nai->ai_family == AF_INET6)) {
609			if (((nai->ai_family == AF_INET) &&
610			  IN_MULTICAST(((struct sockaddr_in *) nai->ai_addr)
611			    ->sin_addr.s_addr))
612			  || ((nai->ai_family == AF_INET6) &&
613			    IN6_IS_ADDR_MULTICAST(
614				&((struct sockaddr_in6 *) nai->ai_addr)
615				  ->sin6_addr)))
616			{
617			    RegisterHostaddr(nai->ai_addr, nai->ai_addrlen,
618			      BROADCAST_QUERY);
619			} else {
620			    RegisterHostaddr(nai->ai_addr, nai->ai_addrlen,
621			      QUERY);
622			}
623		    }
624		}
625		freeaddrinfo(ai);
626	    }
627	}
628#else
629	/* Per RFC 1123, check first for IP address in dotted-decimal form */
630	else if ((in_addr.sin_addr.s_addr = inet_addr(name)) != -1)
631	    in_addr.sin_family = AF_INET;
632	else
633	{
634	    hostent = gethostbyname (name);
635	    if (!hostent)
636		return;
637	    if (hostent->h_addrtype != AF_INET || hostent->h_length != 4)
638		return;
639	    in_addr.sin_family = hostent->h_addrtype;
640	    memcpy(&in_addr.sin_addr, hostent->h_addr, 4);
641	}
642	in_addr.sin_port = htons (XDM_UDP_PORT);
643# ifdef BSD44SOCKETS
644	in_addr.sin_len = sizeof(in_addr);
645# endif
646	RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr),
647			  QUERY);
648#endif /* IPv6 */
649    }
650}
651
652static ARRAYofARRAY8	AuthenticationNames;
653
654static int
655InitXDMCP (char **argv)
656{
657    int	soopts = 1;
658    XdmcpHeader	header;
659    int	i;
660
661    header.version = XDM_PROTOCOL_VERSION;
662    header.opcode = (CARD16) BROADCAST_QUERY;
663    header.length = 1;
664    for (i = 0; i < (int)AuthenticationNames.length; i++)
665	header.length += 2 + AuthenticationNames.data[i].length;
666    XdmcpWriteHeader (&broadcastBuffer, &header);
667    XdmcpWriteARRAYofARRAY8 (&broadcastBuffer, &AuthenticationNames);
668
669    header.version = XDM_PROTOCOL_VERSION;
670    header.opcode = (CARD16) QUERY;
671    header.length = 1;
672    for (i = 0; i < (int)AuthenticationNames.length; i++)
673	header.length += 2 + AuthenticationNames.data[i].length;
674    XdmcpWriteHeader (&directBuffer, &header);
675    XdmcpWriteARRAYofARRAY8 (&directBuffer, &AuthenticationNames);
676    if ((socketFD = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
677	return 0;
678#if defined(IPv6) && defined(AF_INET6)
679    socket6FD = socket (AF_INET6, SOCK_DGRAM, 0);
680#endif
681#ifdef SO_BROADCAST
682    soopts = 1;
683    if (setsockopt (socketFD, SOL_SOCKET, SO_BROADCAST, (char *)&soopts, sizeof (soopts)) < 0)
684	perror ("setsockopt");
685#endif
686
687    XtAddInput (socketFD, (XtPointer) XtInputReadMask, ReceivePacket,
688		(XtPointer) &socketFD);
689#if defined(IPv6) && defined(AF_INET6)
690    if (socket6FD != -1)
691    XtAddInput (socket6FD, (XtPointer) XtInputReadMask, ReceivePacket,
692		(XtPointer) &socket6FD);
693#endif
694    while (*argv)
695    {
696	RegisterHostname (*argv);
697	++argv;
698    }
699    pingTry = 0;
700    PingHosts ((XtPointer)NULL, (XtIntervalId *)NULL);
701    return 1;
702}
703
704static void
705Choose (HostName *h)
706{
707    if (app_resources.xdmAddress)
708    {
709	struct sockaddr_in  in_addr;
710#if defined(IPv6) && defined(AF_INET6)
711	struct sockaddr_in6 in6_addr;
712#endif
713	struct sockaddr	*addr = NULL;
714	int		family;
715	int		len = 0;
716	int		fd;
717	char		buf[1024];
718	XdmcpBuffer	buffer;
719	char		*xdm;
720
721	xdm = (char *) app_resources.xdmAddress->data;
722	family = (xdm[0] << 8) + xdm[1];
723	switch (family) {
724	case AF_INET:
725#ifdef BSD44SOCKETS
726	    in_addr.sin_len = sizeof(in_addr);
727#endif
728	    in_addr.sin_family = family;
729	    memcpy(&in_addr.sin_port, xdm + 2, 2);
730	    memcpy(&in_addr.sin_addr, xdm + 4, 4);
731	    addr = (struct sockaddr *) &in_addr;
732	    len = sizeof (in_addr);
733	    break;
734#if defined(IPv6) && defined(AF_INET6)
735	case AF_INET6:
736	    bzero(&in6_addr, sizeof(in6_addr));
737# ifdef SIN6_LEN
738	    in6_addr.sin6_len = sizeof(in6_addr);
739# endif
740	    in6_addr.sin6_family = family;
741	    memcpy(&in6_addr.sin6_port, xdm + 2, 2);
742	    memcpy(&in6_addr.sin6_addr, xdm + 4, 16);
743	    addr = (struct sockaddr *) &in6_addr;
744	    len = sizeof (in6_addr);
745	    break;
746#endif
747	}
748	if ((fd = socket (family, SOCK_STREAM, 0)) == -1)
749	{
750	    fprintf (stderr, "Cannot create response socket\n");
751	    exit (REMANAGE_DISPLAY);
752	}
753	if (connect (fd, addr, len) == -1)
754	{
755	    fprintf (stderr, "Cannot connect to xdm\n");
756	    exit (REMANAGE_DISPLAY);
757	}
758	buffer.data = (BYTE *) buf;
759	buffer.size = sizeof (buf);
760	buffer.pointer = 0;
761	buffer.count = 0;
762	XdmcpWriteARRAY8 (&buffer, app_resources.clientAddress);
763	XdmcpWriteCARD16 (&buffer, (CARD16) app_resources.connectionType);
764	XdmcpWriteARRAY8 (&buffer, &h->hostaddr);
765	write (fd, (char *)buffer.data, buffer.pointer);
766	close (fd);
767    }
768    else
769    {
770	int i;
771
772	printf ("%u\n", h->connectionType);
773	for (i = 0; i < (int)h->hostaddr.length; i++)
774	    printf ("%u%s", h->hostaddr.data[i],
775		    i == h->hostaddr.length - 1 ? "\n" : " ");
776    }
777}
778
779/*
780  next_line returns the next line in a list
781  across the list end.
782  (0, 1, 2, 3, 0, 1, 2, 3 ....)
783*/
784
785static int
786next_line(unsigned int current, unsigned int size, int event)
787{
788  switch(event) {
789    case Button5:
790      return (current + 1) % size;
791    case Button4:
792      return (current + size - 1) % size;
793    case XK_Down:
794      return (current + 1) % size;
795    case XK_Up:
796      return (current + size - 1) % size;
797  }
798  return -1;
799}
800
801/*
802  Hostselect selects a given chooser line.
803  Returns 1 when host is willing and 0 if not
804*/
805
806static int
807Hostselect (int line)
808{
809  XawListReturnStruct	*r;
810  HostName		*h;
811
812  /* Assume the next host is willing */
813  XawListHighlight (list,line);
814  r = XawListShowCurrent (list);
815  /* copied from DoCheckWilling */
816  for (h = hostNamedb; h; h = h->next)
817  {
818    if (!strcmp (r->string, h->fullname))
819    {
820      if (!h->willing)
821	XawListUnhighlight (list);
822      else
823      {
824	/* Scroll viewport to make sure new selection is visible */
825	Arg		size[2];
826	Dimension	height, portheight;
827	Position	y;
828	int		lineheight, liney;
829
830	XtSetArg (size[0], XtNheight, &height);
831	XtSetArg (size[1], XtNy, &y);
832	XtGetValues (list, size, (Cardinal) 2);
833
834	XtSetArg (size[0], XtNheight, &portheight);
835	XtGetValues (viewport, size, (Cardinal) 1);
836
837	lineheight = height / NameTableSize;
838	liney = lineheight * line;
839
840	if ((y + liney) < 0) {
841	    XawViewportSetCoordinates(viewport, 0, liney);
842	} else if ((y + liney + lineheight) > portheight) {
843	    XawViewportSetCoordinates(viewport, 0,
844				      (liney + lineheight) - portheight);
845	}
846
847	XtFree((char *) r);
848	return 1;
849      }
850    }
851  }
852  XtFree((char *) r);
853  return 0;
854}
855
856/*
857  Selectfirst selects the first willing host
858  in the chooser list (so you can select a host without
859  presence of mouse, but with pressing space or return,
860  or XK_Down / XK_Up
861*/
862
863static void
864Selectfirst (void)
865{
866  int line;
867
868  for (line = 0; line < NameTableSize; line++)
869  {
870    if (Hostselect(line))
871      return;
872  }
873  return;
874}
875
876/*
877  Storeold stores the selected line into global int oldentry
878*/
879
880static void
881Storeold (Widget w, XEvent *event, String *params, Cardinal *num_params)
882{
883  XawListReturnStruct	*r = XawListShowCurrent(list);
884
885  oldline = r->list_index;
886  XtFree((char *) r);
887}
888
889/*
890  Setold restores the global int oldentry
891  when you try to select a host with your mouse
892  who is unwilling as follows:
893  <Btn1Down>:     Store() Set() CheckWilling() Setold() \n\
894*/
895
896static void
897Setold (Widget w, XEvent *event, String *params, Cardinal *num_params)
898{
899
900  if ( (XawListShowCurrent(list))->list_index == XAW_LIST_NONE && oldline != XAW_LIST_NONE)
901    XawListHighlight (list, oldline);
902}
903
904/*
905  HostCycle tries to select the next willing host across
906  the list end and will stop at the first found willing host
907  or after trying all entries.
908*/
909
910static void
911HostCycle(unsigned int line, unsigned int size, KeySym keysym)
912{
913  unsigned int newline = line;
914  /* Do it only once around the chooser list, either direction */
915  while ( (newline = next_line(newline,size,keysym)) != line && newline != -1)
916  {
917    if (Hostselect(newline))
918      return;
919  }
920  /* No other willing host could be found, stay at the old one*/
921  XawListHighlight (list, line);
922  return;
923}
924
925/*
926  Switch_Key handles XK_Up and XK_Down
927  and highlights an appropriate line in the chooser list.
928*/
929
930static void
931Switch_Key (Widget w, XEvent *event, String *params, Cardinal *num_params)
932{
933  char strbuf[128];
934  KeySym keysym = 0;
935  static XComposeStatus compose_status = {NULL, 0};
936  XawListReturnStruct	*r;
937
938  XLookupString (&event->xkey, strbuf, sizeof (strbuf),
939                           &keysym, &compose_status);
940
941  if (keysym != XK_Up && keysym != XK_Down)
942    return;
943
944  r = XawListShowCurrent (list);
945
946  if (r->list_index == XAW_LIST_NONE)
947    Selectfirst();
948  else
949    HostCycle(r->list_index,NameTableSize,keysym);
950
951  XtFree((char *) r);
952  return;
953}
954
955
956
957/*
958  Switch_Btn handles ScrollWheel Forward (Button5) and Backward
959  (Button4) and highlights an appropriate line in the chooser list.
960*/
961
962static void
963Switch_Btn (Widget w, XEvent *event, String *params, Cardinal *num_params)
964{
965    XawListReturnStruct	*r;
966    r = XawListShowCurrent (list);
967
968    if (r->list_index == XAW_LIST_NONE)
969      Selectfirst();
970    else
971      HostCycle(r->list_index,NameTableSize,event->xbutton.button);
972
973    XtFree((char *) r);
974    return;
975}
976
977
978/* ARGSUSED */
979static void
980DoAccept (Widget w, XEvent *event, String *params, Cardinal *num_params)
981{
982    XawListReturnStruct	*r;
983    HostName		*h;
984
985    r = XawListShowCurrent (list);
986    if (r->list_index == XAW_LIST_NONE)
987#ifdef XKB
988	XkbStdBell(XtDisplay(toplevel),XtWindow(w),0,XkbBI_MinorError);
989#else
990	XBell (XtDisplay (toplevel), 0);
991#endif
992    else
993    {
994	for (h = hostNamedb; h; h = h->next)
995	    if (!strcmp (r->string, h->fullname))
996	    {
997		Choose (h);
998	    }
999	exit (OBEYSESS_DISPLAY);
1000    }
1001    XtFree((char *) r);
1002}
1003
1004/* ARGSUSED */
1005static void
1006DoCheckWilling (Widget w, XEvent *event, String *params, Cardinal *num_params)
1007{
1008    XawListReturnStruct	*r;
1009    HostName		*h;
1010
1011    r = XawListShowCurrent (list);
1012    if (r->list_index != XAW_LIST_NONE) {
1013	for (h = hostNamedb; h; h = h->next)
1014	    if (!strcmp (r->string, h->fullname))
1015		if (!h->willing)
1016		    XawListUnhighlight (list);
1017    }
1018    XtFree((char *) r);
1019}
1020
1021/* ARGSUSED */
1022_X_NORETURN
1023static void
1024DoCancel (Widget w, XEvent *event, String *params, Cardinal *num_params)
1025{
1026    exit (OBEYSESS_DISPLAY);
1027}
1028
1029/* ARGSUSED */
1030static void
1031DoPing (Widget w, XEvent *event, String *params, Cardinal *num_params)
1032{
1033    EmptyHostnames ();
1034    pingTry = 0;
1035    PingHosts ((XtPointer)NULL, (XtIntervalId *)NULL);
1036}
1037
1038static XtActionsRec app_actions[] = {
1039    { "Accept",	      DoAccept },
1040    { "Cancel",	      DoCancel },
1041    { "CheckWilling", DoCheckWilling },
1042    { "Ping",	      DoPing },
1043    { "KeySwitch",    Switch_Key },
1044    { "BtnSwitch",    Switch_Btn },
1045    { "Store",	      Storeold },
1046    { "Setold",	      Setold },
1047};
1048
1049int
1050main (int argc, char **argv)
1051{
1052    Arg		position[3];
1053    Dimension   width, height;
1054    Position	x, y;
1055#ifdef USE_XINERAMA
1056    XineramaScreenInfo *screens;
1057    int                 s_num;
1058#endif
1059
1060
1061    toplevel = XtInitialize (argv[0], "Chooser", options, XtNumber(options), &argc, argv);
1062
1063    XtAddConverter(XtRString, XtRARRAY8, CvtStringToARRAY8, NULL, 0);
1064
1065    XtGetApplicationResources (toplevel, (XtPointer) &app_resources, resources,
1066			       XtNumber (resources), NULL, (Cardinal) 0);
1067
1068    XtAddActions (app_actions, XtNumber (app_actions));
1069    paned = XtCreateManagedWidget ("paned", panedWidgetClass, toplevel, NULL, 0);
1070    label = XtCreateManagedWidget ("label", labelWidgetClass, paned, NULL, 0);
1071    viewport = XtCreateManagedWidget ("viewport", viewportWidgetClass, paned, NULL, 0);
1072    list = XtCreateManagedWidget ("list", listWidgetClass, viewport, NULL, 0);
1073    box = XtCreateManagedWidget ("box", boxWidgetClass, paned, NULL, 0);
1074    cancel = XtCreateManagedWidget ("cancel", commandWidgetClass, box, NULL, 0);
1075    acceptit = XtCreateManagedWidget ("accept", commandWidgetClass, box, NULL, 0);
1076    ping = XtCreateManagedWidget ("ping", commandWidgetClass, box, NULL, 0);
1077
1078    /*
1079     * center ourselves on the screen
1080     */
1081    XtSetMappedWhenManaged(toplevel, FALSE);
1082    XtRealizeWidget (toplevel);
1083
1084    XtSetArg (position[0], XtNwidth, &width);
1085    XtSetArg (position[1], XtNheight, &height);
1086    XtGetValues (toplevel, position, (Cardinal) 2);
1087#ifdef USE_XINERAMA
1088    if (
1089	XineramaIsActive(XtDisplay(toplevel)) &&
1090	(screens = XineramaQueryScreens(XtDisplay(toplevel), &s_num)) != NULL
1091       )
1092    {
1093	x = (Position)(screens[0].x_org + (screens[0].width - width) / 2);
1094	y = (Position)(screens[0].y_org + (screens[0].height - height) / 3);
1095
1096	XFree(screens);
1097    }
1098    else
1099#endif
1100    {
1101	x = (Position)(WidthOfScreen (XtScreen (toplevel)) - width) / 2;
1102	y = (Position)(HeightOfScreen (XtScreen (toplevel)) - height) / 3;
1103    }
1104    XtSetArg (position[0], XtNx, x);
1105    XtSetArg (position[1], XtNy, y);
1106    XtSetValues (toplevel, position, (Cardinal) 2);
1107
1108    /*
1109     * Run
1110     */
1111    XtMapWidget(toplevel);
1112    InitXDMCP (argv + 1);
1113    XtMainLoop ();
1114    exit(0);
1115    /*NOTREACHED*/
1116}
1117
1118/* Converts the hex string s of length len into the byte array d.
1119   Returns 0 if s was a legal hex string, 1 otherwise.
1120   */
1121static int
1122FromHex (char *s, char *d, int len)
1123{
1124    int	t;
1125    int ret = len&1;		/* odd-length hex strings are illegal */
1126    while (len >= 2)
1127    {
1128#define HexChar(c)  ('0' <= (c) && (c) <= '9' ? (c) - '0' : (c) - 'a' + 10)
1129
1130	if (!ishexdigit(*s))
1131	    ret = 1;
1132	t = HexChar (*s) << 4;
1133	s++;
1134	if (!ishexdigit(*s))
1135	    ret = 1;
1136	t += HexChar (*s);
1137	s++;
1138	*d++ = t;
1139	len -= 2;
1140    }
1141    return ret;
1142}
1143
1144/*ARGSUSED*/
1145static void
1146CvtStringToARRAY8 (XrmValuePtr args, Cardinal *num_args, XrmValuePtr fromVal, XrmValuePtr toVal)
1147{
1148    static ARRAY8Ptr	dest;
1149    char	*s;
1150    int		len;
1151
1152    dest = (ARRAY8Ptr) XtMalloc (sizeof (ARRAY8));
1153    len = fromVal->size;
1154    s = (char *) fromVal->addr;
1155    if (!XdmcpAllocARRAY8 (dest, len >> 1))
1156	XtStringConversionWarning ((char *) fromVal->addr, XtRARRAY8);
1157    else
1158    {
1159	FromHex (s, (char *) dest->data, len);
1160    }
1161    toVal->addr = (caddr_t) &dest;
1162    toVal->size = sizeof (ARRAY8Ptr);
1163}
1164