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