chooser.c revision b7d26471
1/*
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 = malloc (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			memmove( hostname->data, host, hostname->length);
351		    }
352		}
353	    }
354	}
355	if (!XdmcpAllocARRAY8 (&new->hostaddr, hostAddr.length))
356	{
357	    free (new->fullname);
358	    free (new);
359	    return 0;
360	}
361	memmove( new->hostaddr.data, hostAddr.data, hostAddr.length);
362	new->connectionType = connectionType;
363	new->hostname = *hostname;
364
365	*names = new;
366	new->next = NULL;
367	NameTableSize++;
368    }
369    else
370    {
371	new = *names;
372	free (new->fullname);
373	XdmcpDisposeARRAY8 (&new->status);
374	XdmcpDisposeARRAY8 (hostname);
375    }
376    new->willing = willing;
377    new->status = *status;
378
379    hostname = &new->hostname;
380    fulllen = hostname->length;
381    if (fulllen < 30)
382	fulllen = 30;
383    fulllen += status->length + 10;
384    new->fullname = malloc (fulllen);
385    if (!new->fullname)
386    {
387	new->fullname = "Unknown";
388    }
389    else
390    {
391	snprintf(new->fullname, fulllen, "%-30.*s %*.*s",
392		 hostname->length, hostname->data,
393		 status->length, status->length, status->data);
394    }
395    RebuildTable (NameTableSize);
396    return 1;
397}
398
399static void
400DisposeHostname (HostName *host)
401{
402    XdmcpDisposeARRAY8 (&host->hostname);
403    XdmcpDisposeARRAY8 (&host->hostaddr);
404    XdmcpDisposeARRAY8 (&host->status);
405    free (host->fullname);
406    free (host);
407}
408
409static void
410EmptyHostnames (void)
411{
412    HostName	*hosts, *next;
413
414    for (hosts = hostNamedb; hosts; hosts = next)
415    {
416	next = hosts->next;
417	DisposeHostname (hosts);
418    }
419    NameTableSize = 0;
420    hostNamedb = NULL;
421    RebuildTable (NameTableSize);
422}
423
424/* ARGSUSED */
425static void
426ReceivePacket (XtPointer closure, int *source, XtInputId *id)
427{
428    XdmcpHeader	    header;
429    ARRAY8	    authenticationName = {0, NULL};
430    ARRAY8	    hostname = {0, NULL};
431    ARRAY8	    status = {0, NULL};
432    int		    saveHostname = 0;
433#if defined(IPv6) && defined(AF_INET6)
434    struct sockaddr_storage addr;
435#else
436    struct sockaddr addr;
437#endif
438    int		    addrlen;
439    int		    sfd = * (int *) closure;
440
441    addrlen = sizeof (addr);
442    if (!XdmcpFill (sfd, &buffer, (XdmcpNetaddr) &addr, &addrlen))
443	return;
444    if (!XdmcpReadHeader (&buffer, &header))
445	return;
446    if (header.version != XDM_PROTOCOL_VERSION)
447	return;
448    switch (header.opcode) {
449    case WILLING:
450	if (XdmcpReadARRAY8 (&buffer, &authenticationName) &&
451	    XdmcpReadARRAY8 (&buffer, &hostname) &&
452	    XdmcpReadARRAY8 (&buffer, &status))
453	{
454	    if (header.length == 6 + authenticationName.length +
455		hostname.length + status.length)
456	    {
457		if (AddHostname (&hostname, &status, (struct sockaddr *) &addr,
458				 header.opcode == (int) WILLING))
459		    saveHostname = 1;
460	    }
461	}
462	XdmcpDisposeARRAY8 (&authenticationName);
463	break;
464    case UNWILLING:
465	if (XdmcpReadARRAY8 (&buffer, &hostname) &&
466	    XdmcpReadARRAY8 (&buffer, &status))
467	{
468	    if (header.length == 4 + hostname.length + status.length)
469	    {
470		if (AddHostname (&hostname, &status, (struct sockaddr *) &addr,
471				 header.opcode == (int) WILLING))
472		    saveHostname = 1;
473
474	    }
475	}
476	break;
477    default:
478	break;
479    }
480    if (!saveHostname)
481    {
482	XdmcpDisposeARRAY8 (&hostname);
483	XdmcpDisposeARRAY8 (&status);
484    }
485}
486
487static void
488RegisterHostaddr (struct sockaddr *addr, int len, xdmOpCode type)
489{
490    HostAddr		*host, **prev;
491
492    host = malloc (sizeof (HostAddr));
493    if (!host)
494	return;
495    host->addr = malloc (len);
496    if (!host->addr)
497    {
498	free (host);
499	return;
500    }
501    memmove( (char *) host->addr, (char *) addr, len);
502    host->addrlen = len;
503    host->type = type;
504    for (prev = &hostAddrdb; *prev; prev = &(*prev)->next)
505	;
506    *prev = host;
507    host->next = NULL;
508}
509
510/*
511 * Register the address for this host.
512 * Called with each of the names on the command line.
513 * The special name "BROADCAST" looks up all the broadcast
514 *  addresses on the local host.
515 */
516
517/* Handle variable length ifreq in BNR2 and later */
518#ifdef VARIABLE_IFREQ
519# define ifr_size(p) (sizeof (struct ifreq) + \
520		     (p->ifr_addr.sa_len > sizeof (p->ifr_addr) ? \
521		      p->ifr_addr.sa_len - sizeof (p->ifr_addr) : 0))
522#else
523# define ifr_size(p) (sizeof (struct ifreq))
524#endif
525
526static void
527RegisterHostname (char *name)
528{
529#if !defined(IPv6) || !defined(AF_INET6)
530    struct hostent	*hostent;
531#endif
532    struct sockaddr_in	in_addr;
533    struct ifconf	ifc;
534    register struct ifreq *ifr;
535    struct sockaddr	broad_addr;
536    char		buf[2048], *cp, *cplim;
537
538    if (!strcmp (name, BROADCAST_HOSTNAME))
539    {
540	ifc.ifc_len = sizeof (buf);
541	ifc.ifc_buf = buf;
542	if (ifioctl (socketFD, (int) SIOCGIFCONF, (char *) &ifc) < 0)
543	    return;
544
545	cplim = (char *) ifc.ifc_req + ifc.ifc_len;
546
547	for (cp = (char *) ifc.ifc_req; cp < cplim; cp += ifr_size (ifr))
548	{
549	    ifr = (struct ifreq *) cp;
550	    if (ifr->ifr_addr.sa_family != AF_INET)
551		continue;
552
553	    broad_addr = ifr->ifr_addr;
554	    ((struct sockaddr_in *) &broad_addr)->sin_addr.s_addr =
555		htonl (INADDR_BROADCAST);
556#ifdef SIOCGIFBRDADDR
557	    {
558		struct ifreq    broad_req;
559
560		broad_req = *ifr;
561		if (ifioctl (socketFD, SIOCGIFFLAGS, (char *) &broad_req) != -1 &&
562		    (broad_req.ifr_flags & IFF_BROADCAST) &&
563		    (broad_req.ifr_flags & IFF_UP)
564		    )
565		{
566		    broad_req = *ifr;
567		    if (ifioctl (socketFD, SIOCGIFBRDADDR, &broad_req) != -1)
568			broad_addr = broad_req.ifr_addr;
569		    else
570			continue;
571		}
572		else
573		    continue;
574	    }
575#endif
576	    in_addr = *((struct sockaddr_in *) &broad_addr);
577	    in_addr.sin_port = htons (XDM_UDP_PORT);
578#ifdef BSD44SOCKETS
579	    in_addr.sin_len = sizeof(in_addr);
580#endif
581	    RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr),
582			      BROADCAST_QUERY);
583	}
584    }
585    else
586    {
587	/* address as hex string, e.g., "12180022" (deprecated) */
588	if (strlen(name) == 8 &&
589	    FromHex(name, (char *)&in_addr.sin_addr, strlen(name)) == 0)
590	{
591	    in_addr.sin_family = AF_INET;
592	    in_addr.sin_port = htons (XDM_UDP_PORT);
593#ifdef BSD44SOCKETS
594	    in_addr.sin_len = sizeof(in_addr);
595#endif
596	    RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr),
597				QUERY);
598	}
599#if defined(IPv6) && defined(AF_INET6)
600	else {
601	    char sport[8];
602	    struct addrinfo *ai, *nai, hints;
603	    bzero(&hints,sizeof(hints));
604	    hints.ai_socktype = SOCK_DGRAM;
605	    snprintf(sport, sizeof(sport), "%d", XDM_UDP_PORT);
606	    if (getaddrinfo(name, sport, &hints, &ai) == 0) {
607		for (nai = ai ; nai != NULL ; nai = nai->ai_next) {
608		    if ((nai->ai_family == AF_INET) ||
609		        (nai->ai_family == AF_INET6)) {
610			if (((nai->ai_family == AF_INET) &&
611			  IN_MULTICAST(((struct sockaddr_in *) nai->ai_addr)
612			    ->sin_addr.s_addr))
613			  || ((nai->ai_family == AF_INET6) &&
614			    IN6_IS_ADDR_MULTICAST(
615				&((struct sockaddr_in6 *) nai->ai_addr)
616				  ->sin6_addr)))
617			{
618			    RegisterHostaddr(nai->ai_addr, nai->ai_addrlen,
619			      BROADCAST_QUERY);
620			} else {
621			    RegisterHostaddr(nai->ai_addr, nai->ai_addrlen,
622			      QUERY);
623			}
624		    }
625		}
626		freeaddrinfo(ai);
627	    }
628	}
629#else
630	/* Per RFC 1123, check first for IP address in dotted-decimal form */
631	else if ((in_addr.sin_addr.s_addr = inet_addr(name)) != -1)
632	    in_addr.sin_family = AF_INET;
633	else
634	{
635	    hostent = gethostbyname (name);
636	    if (!hostent)
637		return;
638	    if (hostent->h_addrtype != AF_INET || hostent->h_length != 4)
639		return;
640	    in_addr.sin_family = hostent->h_addrtype;
641	    memmove( &in_addr.sin_addr, hostent->h_addr, 4);
642	}
643	in_addr.sin_port = htons (XDM_UDP_PORT);
644# ifdef BSD44SOCKETS
645	in_addr.sin_len = sizeof(in_addr);
646# endif
647	RegisterHostaddr ((struct sockaddr *)&in_addr, sizeof (in_addr),
648			  QUERY);
649#endif /* IPv6 */
650    }
651}
652
653static ARRAYofARRAY8	AuthenticationNames;
654
655static int
656InitXDMCP (char **argv)
657{
658    int	soopts = 1;
659    XdmcpHeader	header;
660    int	i;
661
662    header.version = XDM_PROTOCOL_VERSION;
663    header.opcode = (CARD16) BROADCAST_QUERY;
664    header.length = 1;
665    for (i = 0; i < (int)AuthenticationNames.length; i++)
666	header.length += 2 + AuthenticationNames.data[i].length;
667    XdmcpWriteHeader (&broadcastBuffer, &header);
668    XdmcpWriteARRAYofARRAY8 (&broadcastBuffer, &AuthenticationNames);
669
670    header.version = XDM_PROTOCOL_VERSION;
671    header.opcode = (CARD16) QUERY;
672    header.length = 1;
673    for (i = 0; i < (int)AuthenticationNames.length; i++)
674	header.length += 2 + AuthenticationNames.data[i].length;
675    XdmcpWriteHeader (&directBuffer, &header);
676    XdmcpWriteARRAYofARRAY8 (&directBuffer, &AuthenticationNames);
677    if ((socketFD = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
678	return 0;
679#if defined(IPv6) && defined(AF_INET6)
680    socket6FD = socket (AF_INET6, SOCK_DGRAM, 0);
681#endif
682#ifdef SO_BROADCAST
683    soopts = 1;
684    if (setsockopt (socketFD, SOL_SOCKET, SO_BROADCAST, (char *)&soopts, sizeof (soopts)) < 0)
685	perror ("setsockopt");
686#endif
687
688    XtAddInput (socketFD, (XtPointer) XtInputReadMask, ReceivePacket,
689		(XtPointer) &socketFD);
690#if defined(IPv6) && defined(AF_INET6)
691    if (socket6FD != -1)
692    XtAddInput (socket6FD, (XtPointer) XtInputReadMask, ReceivePacket,
693		(XtPointer) &socket6FD);
694#endif
695    while (*argv)
696    {
697	RegisterHostname (*argv);
698	++argv;
699    }
700    pingTry = 0;
701    PingHosts ((XtPointer)NULL, (XtIntervalId *)NULL);
702    return 1;
703}
704
705static void
706Choose (HostName *h)
707{
708    if (app_resources.xdmAddress)
709    {
710	struct sockaddr_in  in_addr;
711#if defined(IPv6) && defined(AF_INET6)
712	struct sockaddr_in6 in6_addr;
713#endif
714	struct sockaddr	*addr = NULL;
715	int		family;
716	int		len = 0;
717	int		fd;
718	char		buf[1024];
719	XdmcpBuffer	buffer;
720	char		*xdm;
721
722	xdm = (char *) app_resources.xdmAddress->data;
723	family = (xdm[0] << 8) + xdm[1];
724	switch (family) {
725	case AF_INET:
726#ifdef BSD44SOCKETS
727	    in_addr.sin_len = sizeof(in_addr);
728#endif
729	    in_addr.sin_family = family;
730	    memmove( &in_addr.sin_port, xdm + 2, 2);
731	    memmove( &in_addr.sin_addr, xdm + 4, 4);
732	    addr = (struct sockaddr *) &in_addr;
733	    len = sizeof (in_addr);
734	    break;
735#if defined(IPv6) && defined(AF_INET6)
736	case AF_INET6:
737	    bzero(&in6_addr, sizeof(in6_addr));
738# ifdef SIN6_LEN
739	    in6_addr.sin6_len = sizeof(in6_addr);
740# endif
741	    in6_addr.sin6_family = family;
742	    memmove( &in6_addr.sin6_port, xdm + 2, 2);
743	    memmove( &in6_addr.sin6_addr, xdm + 4, 16);
744	    addr = (struct sockaddr *) &in6_addr;
745	    len = sizeof (in6_addr);
746	    break;
747#endif
748	}
749	if ((fd = socket (family, SOCK_STREAM, 0)) == -1)
750	{
751	    fprintf (stderr, "Cannot create response socket\n");
752	    exit (REMANAGE_DISPLAY);
753	}
754	if (connect (fd, addr, len) == -1)
755	{
756	    fprintf (stderr, "Cannot connect to xdm\n");
757	    exit (REMANAGE_DISPLAY);
758	}
759	buffer.data = (BYTE *) buf;
760	buffer.size = sizeof (buf);
761	buffer.pointer = 0;
762	buffer.count = 0;
763	XdmcpWriteARRAY8 (&buffer, app_resources.clientAddress);
764	XdmcpWriteCARD16 (&buffer, (CARD16) app_resources.connectionType);
765	XdmcpWriteARRAY8 (&buffer, &h->hostaddr);
766	write (fd, (char *)buffer.data, buffer.pointer);
767	close (fd);
768    }
769    else
770    {
771	int i;
772
773	printf ("%u\n", h->connectionType);
774	for (i = 0; i < (int)h->hostaddr.length; i++)
775	    printf ("%u%s", h->hostaddr.data[i],
776		    i == h->hostaddr.length - 1 ? "\n" : " ");
777    }
778}
779
780/*
781  next_line returns the next line in a list
782  across the list end.
783  (0, 1, 2, 3, 0, 1, 2, 3 ....)
784*/
785
786static int
787next_line(unsigned int current, unsigned int size, int event)
788{
789  switch(event) {
790    case Button5:
791      return (current + 1) % size;
792    case Button4:
793      return (current + size - 1) % size;
794    case XK_Down:
795      return (current + 1) % size;
796    case XK_Up:
797      return (current + size - 1) % size;
798  }
799  return -1;
800}
801
802/*
803  Hostselect selects a given chooser line.
804  Returns 1 when host is willing and 0 if not
805*/
806
807static int
808Hostselect (int line)
809{
810  XawListReturnStruct	*r;
811  HostName		*h;
812
813  /* Assume the next host is willing */
814  XawListHighlight (list,line);
815  r = XawListShowCurrent (list);
816  /* copied from DoCheckWilling */
817  for (h = hostNamedb; h; h = h->next)
818  {
819    if (!strcmp (r->string, h->fullname))
820    {
821      if (!h->willing)
822	XawListUnhighlight (list);
823      else
824      {
825	/* Scroll viewport to make sure new selection is visible */
826	Arg		size[2];
827	Dimension	height, portheight;
828	Position	y;
829	int		lineheight, liney;
830
831	XtSetArg (size[0], XtNheight, &height);
832	XtSetArg (size[1], XtNy, &y);
833	XtGetValues (list, size, (Cardinal) 2);
834
835	XtSetArg (size[0], XtNheight, &portheight);
836	XtGetValues (viewport, size, (Cardinal) 1);
837
838	lineheight = height / NameTableSize;
839	liney = lineheight * line;
840
841	if ((y + liney) < 0) {
842	    XawViewportSetCoordinates(viewport, 0, liney);
843	} else if ((y + liney + lineheight) > portheight) {
844	    XawViewportSetCoordinates(viewport, 0,
845				      (liney + lineheight) - portheight);
846	}
847
848	XtFree((char *) r);
849	return 1;
850      }
851    }
852  }
853  XtFree((char *) r);
854  return 0;
855}
856
857/*
858  Selectfirst selects the first willing host
859  in the chooser list (so you can select a host without
860  presence of mouse, but with pressing space or return,
861  or XK_Down / XK_Up
862*/
863
864static void
865Selectfirst (void)
866{
867  int line;
868
869  for (line = 0; line < NameTableSize; line++)
870  {
871    if (Hostselect(line))
872      return;
873  }
874  return;
875}
876
877/*
878  Storeold stores the selected line into global int oldentry
879*/
880
881static void
882Storeold (Widget w, XEvent *event, String *params, Cardinal *num_params)
883{
884  XawListReturnStruct	*r = XawListShowCurrent(list);
885
886  oldline = r->list_index;
887  XtFree((char *) r);
888}
889
890/*
891  Setold restores the global int oldentry
892  when you try to select a host with your mouse
893  who is unwilling as follows:
894  <Btn1Down>:     Store() Set() CheckWilling() Setold() \n\
895*/
896
897static void
898Setold (Widget w, XEvent *event, String *params, Cardinal *num_params)
899{
900
901  if ( (XawListShowCurrent(list))->list_index == XAW_LIST_NONE && oldline != XAW_LIST_NONE)
902    XawListHighlight (list, oldline);
903}
904
905/*
906  HostCycle tries to select the next willing host across
907  the list end and will stop at the first found willing host
908  or after trying all entries.
909*/
910
911static void
912HostCycle(unsigned int line, unsigned int size, KeySym keysym)
913{
914  unsigned int newline = line;
915  /* Do it only once around the chooser list, either direction */
916  while ( (newline = next_line(newline,size,keysym)) != line && newline != -1)
917  {
918    if (Hostselect(newline))
919      return;
920  }
921  /* No other willing host could be found, stay at the old one*/
922  XawListHighlight (list, line);
923  return;
924}
925
926/*
927  Switch_Key handles XK_Up and XK_Down
928  and highlights an appropriate line in the chooser list.
929*/
930
931static void
932Switch_Key (Widget w, XEvent *event, String *params, Cardinal *num_params)
933{
934  char strbuf[128];
935  KeySym keysym = 0;
936  static XComposeStatus compose_status = {NULL, 0};
937  XawListReturnStruct	*r;
938
939  XLookupString (&event->xkey, strbuf, sizeof (strbuf),
940                           &keysym, &compose_status);
941
942  if (keysym != XK_Up && keysym != XK_Down)
943    return;
944
945  r = XawListShowCurrent (list);
946
947  if (r->list_index == XAW_LIST_NONE)
948    Selectfirst();
949  else
950    HostCycle(r->list_index,NameTableSize,keysym);
951
952  XtFree((char *) r);
953  return;
954}
955
956
957
958/*
959  Switch_Btn handles ScrollWheel Forward (Button5) and Backward
960  (Button4) and highlights an appropriate line in the chooser list.
961*/
962
963static void
964Switch_Btn (Widget w, XEvent *event, String *params, Cardinal *num_params)
965{
966    XawListReturnStruct	*r;
967    r = XawListShowCurrent (list);
968
969    if (r->list_index == XAW_LIST_NONE)
970      Selectfirst();
971    else
972      HostCycle(r->list_index,NameTableSize,event->xbutton.button);
973
974    XtFree((char *) r);
975    return;
976}
977
978
979/* ARGSUSED */
980static void
981DoAccept (Widget w, XEvent *event, String *params, Cardinal *num_params)
982{
983    XawListReturnStruct	*r;
984    HostName		*h;
985
986    r = XawListShowCurrent (list);
987    if (r->list_index == XAW_LIST_NONE)
988#ifdef XKB
989	XkbStdBell(XtDisplay(toplevel),XtWindow(w),0,XkbBI_MinorError);
990#else
991	XBell (XtDisplay (toplevel), 0);
992#endif
993    else
994    {
995	for (h = hostNamedb; h; h = h->next)
996	    if (!strcmp (r->string, h->fullname))
997	    {
998		Choose (h);
999	    }
1000	exit (OBEYSESS_DISPLAY);
1001    }
1002    XtFree((char *) r);
1003}
1004
1005/* ARGSUSED */
1006static void
1007DoCheckWilling (Widget w, XEvent *event, String *params, Cardinal *num_params)
1008{
1009    XawListReturnStruct	*r;
1010    HostName		*h;
1011
1012    r = XawListShowCurrent (list);
1013    if (r->list_index != XAW_LIST_NONE) {
1014	for (h = hostNamedb; h; h = h->next)
1015	    if (!strcmp (r->string, h->fullname))
1016		if (!h->willing)
1017		    XawListUnhighlight (list);
1018    }
1019    XtFree((char *) r);
1020}
1021
1022/* ARGSUSED */
1023_X_NORETURN
1024static void
1025DoCancel (Widget w, XEvent *event, String *params, Cardinal *num_params)
1026{
1027    exit (OBEYSESS_DISPLAY);
1028}
1029
1030/* ARGSUSED */
1031static void
1032DoPing (Widget w, XEvent *event, String *params, Cardinal *num_params)
1033{
1034    EmptyHostnames ();
1035    pingTry = 0;
1036    PingHosts ((XtPointer)NULL, (XtIntervalId *)NULL);
1037}
1038
1039static XtActionsRec app_actions[] = {
1040    { "Accept",	      DoAccept },
1041    { "Cancel",	      DoCancel },
1042    { "CheckWilling", DoCheckWilling },
1043    { "Ping",	      DoPing },
1044    { "KeySwitch",    Switch_Key },
1045    { "BtnSwitch",    Switch_Btn },
1046    { "Store",	      Storeold },
1047    { "Setold",	      Setold },
1048};
1049
1050int
1051main (int argc, char **argv)
1052{
1053    Arg		position[3];
1054    Dimension   width, height;
1055    Position	x, y;
1056#ifdef USE_XINERAMA
1057    XineramaScreenInfo *screens;
1058    int                 s_num;
1059#endif
1060
1061
1062    toplevel = XtInitialize (argv[0], "Chooser", options, XtNumber(options), &argc, argv);
1063
1064    XtAddConverter(XtRString, XtRARRAY8, CvtStringToARRAY8, NULL, 0);
1065
1066    XtGetApplicationResources (toplevel, (XtPointer) &app_resources, resources,
1067			       XtNumber (resources), NULL, (Cardinal) 0);
1068
1069    XtAddActions (app_actions, XtNumber (app_actions));
1070    paned = XtCreateManagedWidget ("paned", panedWidgetClass, toplevel, NULL, 0);
1071    label = XtCreateManagedWidget ("label", labelWidgetClass, paned, NULL, 0);
1072    viewport = XtCreateManagedWidget ("viewport", viewportWidgetClass, paned, NULL, 0);
1073    list = XtCreateManagedWidget ("list", listWidgetClass, viewport, NULL, 0);
1074    box = XtCreateManagedWidget ("box", boxWidgetClass, paned, NULL, 0);
1075    cancel = XtCreateManagedWidget ("cancel", commandWidgetClass, box, NULL, 0);
1076    acceptit = XtCreateManagedWidget ("accept", commandWidgetClass, box, NULL, 0);
1077    ping = XtCreateManagedWidget ("ping", commandWidgetClass, box, NULL, 0);
1078
1079    /*
1080     * center ourselves on the screen
1081     */
1082    XtSetMappedWhenManaged(toplevel, FALSE);
1083    XtRealizeWidget (toplevel);
1084
1085    XtSetArg (position[0], XtNwidth, &width);
1086    XtSetArg (position[1], XtNheight, &height);
1087    XtGetValues (toplevel, position, (Cardinal) 2);
1088#ifdef USE_XINERAMA
1089    if (
1090	XineramaIsActive(XtDisplay(toplevel)) &&
1091	(screens = XineramaQueryScreens(XtDisplay(toplevel), &s_num)) != NULL
1092       )
1093    {
1094	x = (Position)(screens[0].x_org + (screens[0].width - width) / 2);
1095	y = (Position)(screens[0].y_org + (screens[0].height - height) / 3);
1096
1097	XFree(screens);
1098    }
1099    else
1100#endif
1101    {
1102	x = (Position)(WidthOfScreen (XtScreen (toplevel)) - width) / 2;
1103	y = (Position)(HeightOfScreen (XtScreen (toplevel)) - height) / 3;
1104    }
1105    XtSetArg (position[0], XtNx, x);
1106    XtSetArg (position[1], XtNy, y);
1107    XtSetValues (toplevel, position, (Cardinal) 2);
1108
1109    /*
1110     * Run
1111     */
1112    XtMapWidget(toplevel);
1113    InitXDMCP (argv + 1);
1114    XtMainLoop ();
1115    exit(0);
1116    /*NOTREACHED*/
1117}
1118
1119/* Converts the hex string s of length len into the byte array d.
1120   Returns 0 if s was a legal hex string, 1 otherwise.
1121   */
1122static int
1123FromHex (char *s, char *d, int len)
1124{
1125    int	t;
1126    int ret = len&1;		/* odd-length hex strings are illegal */
1127    while (len >= 2)
1128    {
1129#define HexChar(c)  ('0' <= (c) && (c) <= '9' ? (c) - '0' : (c) - 'a' + 10)
1130
1131	if (!ishexdigit(*s))
1132	    ret = 1;
1133	t = HexChar (*s) << 4;
1134	s++;
1135	if (!ishexdigit(*s))
1136	    ret = 1;
1137	t += HexChar (*s);
1138	s++;
1139	*d++ = t;
1140	len -= 2;
1141    }
1142    return ret;
1143}
1144
1145/*ARGSUSED*/
1146static void
1147CvtStringToARRAY8 (XrmValuePtr args, Cardinal *num_args, XrmValuePtr fromVal, XrmValuePtr toVal)
1148{
1149    static ARRAY8Ptr	dest;
1150    char	*s;
1151    int		len;
1152
1153    dest = (ARRAY8Ptr) XtMalloc (sizeof (ARRAY8));
1154    len = fromVal->size;
1155    s = (char *) fromVal->addr;
1156    if (!XdmcpAllocARRAY8 (dest, len >> 1))
1157	XtStringConversionWarning ((char *) fromVal->addr, XtRARRAY8);
1158    else
1159    {
1160	FromHex (s, (char *) dest->data, len);
1161    }
1162    toVal->addr = (caddr_t) &dest;
1163    toVal->size = sizeof (ARRAY8Ptr);
1164}
1165