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