1
2/*
3
4Copyright 1995, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be
13included in all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
19OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21OTHER DEALINGS IN THE SOFTWARE.
22
23Except as contained in this notice, the name of The Open Group shall
24not be used in advertising or otherwise to promote the sale, use or
25other dealings in this Software without prior written authorization
26from The Open Group.
27
28Author: David P. Wiggins, The Open Group
29
30This work benefited from earlier work done by Martha Zimet of NCD
31and Jim Haggerty of Metheus.
32
33*/
34
35#ifdef HAVE_DIX_CONFIG_H
36#include <dix-config.h>
37#endif
38
39#include "dixstruct.h"
40#include "extnsionst.h"
41#include <X11/extensions/recordproto.h>
42#include "set.h"
43#include "swaprep.h"
44#include "inputstr.h"
45#include "eventconvert.h"
46#include "scrnintstr.h"
47
48
49#include <stdio.h>
50#include <assert.h>
51
52#ifdef PANORAMIX
53#include "globals.h"
54#include "panoramiX.h"
55#include "panoramiXsrv.h"
56#include "cursor.h"
57#endif
58
59#include "protocol-versions.h"
60
61static RESTYPE RTContext;   /* internal resource type for Record contexts */
62
63/* How many bytes of protocol data to buffer in a context. Don't set to less
64 * than 32.
65 */
66#define REPLY_BUF_SIZE 1024
67
68/* Record Context structure */
69
70typedef struct {
71    XID		id;		   /* resource id of context */
72    ClientPtr	pRecordingClient;  /* client that has context enabled */
73    struct _RecordClientsAndProtocolRec *pListOfRCAP; /* all registered info */
74    ClientPtr	pBufClient;	   /* client whose protocol is in replyBuffer*/
75    unsigned int continuedReply:1; /* recording a reply that is split up? */
76    char	elemHeaders;	   /* element header flags (time/seq no.) */
77    char	bufCategory;	   /* category of protocol in replyBuffer */
78    int		numBufBytes;	   /* number of bytes in replyBuffer */
79    char	replyBuffer[REPLY_BUF_SIZE]; /* buffered recorded protocol */
80    int		inFlush;           /*  are we inside RecordFlushReplyBuffer */
81} RecordContextRec, *RecordContextPtr;
82
83/*  RecordMinorOpRec - to hold minor opcode selections for extension requests
84 *  and replies
85 */
86
87typedef union {
88    int count; /* first element of array: how many "major" structs to follow */
89    struct {   /* rest of array elements are this */
90	short first;		/* first major opcode */
91	short last;		/* last major opcode */
92	RecordSetPtr pMinOpSet; /*  minor opcode set for above major range */
93    } major;
94} RecordMinorOpRec, *RecordMinorOpPtr;
95
96
97/*  RecordClientsAndProtocolRec, nicknamed RCAP - holds all the client and
98 *  protocol selections passed in a single CreateContext or RegisterClients.
99 *  Generally, a context will have one of these from the create and an
100 *  additional one for each RegisterClients.  RCAPs are freed when all their
101 *  clients are unregistered.
102 */
103
104typedef struct _RecordClientsAndProtocolRec {
105    RecordContextPtr pContext;		 /* context that owns this RCAP */
106    struct _RecordClientsAndProtocolRec *pNextRCAP; /* next RCAP on context */
107    RecordSetPtr     pRequestMajorOpSet; /* requests to record */
108    RecordMinorOpPtr pRequestMinOpInfo;  /* extension requests to record */
109    RecordSetPtr     pReplyMajorOpSet;   /* replies to record */
110    RecordMinorOpPtr pReplyMinOpInfo;    /* extension replies to record */
111    RecordSetPtr     pDeviceEventSet;    /* device events to record */
112    RecordSetPtr     pDeliveredEventSet; /* delivered events to record */
113    RecordSetPtr     pErrorSet;          /* errors to record */
114    XID *	     pClientIDs;	 /* array of clients to record */
115    short 	     numClients;	 /* number of clients in pClientIDs */
116    short	     sizeClients;	 /* size of pClientIDs array */
117    unsigned int     clientStarted:1;	 /* record new client connections? */
118    unsigned int     clientDied:1;	 /* record client disconnections? */
119    unsigned int     clientIDsSeparatelyAllocated:1; /* pClientIDs malloced? */
120} RecordClientsAndProtocolRec, *RecordClientsAndProtocolPtr;
121
122/* how much bigger to make pRCAP->pClientIDs when reallocing */
123#define CLIENT_ARRAY_GROWTH_INCREMENT 4
124
125/* counts the total number of RCAPs belonging to enabled contexts. */
126static int numEnabledRCAPs;
127
128/*  void VERIFY_CONTEXT(RecordContextPtr, XID, ClientPtr)
129 *  In the spirit of the VERIFY_* macros in dix.h, this macro fills in
130 *  the context pointer if the given ID is a valid Record Context, else it
131 *  returns an error.
132 */
133#define VERIFY_CONTEXT(_pContext, _contextid, _client) { \
134    int rc = dixLookupResourceByType((pointer *)&(_pContext), _contextid, \
135                                     RTContext, _client, DixUseAccess); \
136    if (rc != Success) \
137	return rc; \
138}
139
140static int RecordDeleteContext(
141    pointer /*value*/,
142    XID /*id*/
143);
144
145void RecordExtensionInit(void);
146
147/***************************************************************************/
148
149/* client private stuff */
150
151/*  To make declarations less obfuscated, have a typedef for a pointer to a
152 *  Proc function.
153 */
154typedef int (*ProcFunctionPtr)(
155    ClientPtr /*pClient*/
156);
157
158/* Record client private.  Generally a client only has one of these if
159 * any of its requests are being recorded.
160 */
161typedef struct {
162/* ptr to client's proc vector before Record stuck its nose in */
163    ProcFunctionPtr *originalVector;
164
165/* proc vector with pointers for recorded requests redirected to the
166 * function RecordARequest
167 */
168    ProcFunctionPtr recordVector[256];
169} RecordClientPrivateRec, *RecordClientPrivatePtr;
170
171static DevPrivateKeyRec RecordClientPrivateKeyRec;
172#define RecordClientPrivateKey (&RecordClientPrivateKeyRec)
173
174/*  RecordClientPrivatePtr RecordClientPrivate(ClientPtr)
175 *  gets the client private of the given client.  Syntactic sugar.
176 */
177#define RecordClientPrivate(_pClient) (RecordClientPrivatePtr) \
178    dixLookupPrivate(&(_pClient)->devPrivates, RecordClientPrivateKey)
179
180
181/***************************************************************************/
182
183/* global list of all contexts */
184
185static RecordContextPtr *ppAllContexts;
186
187static int numContexts;/* number of contexts in ppAllContexts */
188
189/* number of currently enabled contexts.  All enabled contexts are bunched
190 * up at the front of the ppAllContexts array, from ppAllContexts[0] to
191 * ppAllContexts[numEnabledContexts-1], to eliminate time spent skipping
192 * past disabled contexts.
193 */
194static int numEnabledContexts;
195
196/* RecordFindContextOnAllContexts
197 *
198 * Arguments:
199 *	pContext is the context to search for.
200 *
201 * Returns:
202 *	The index into the array ppAllContexts at which pContext is stored.
203 *	If pContext is not found in ppAllContexts, returns -1.
204 *
205 * Side Effects: none.
206 */
207static int
208RecordFindContextOnAllContexts(RecordContextPtr pContext)
209{
210    int i;
211
212    assert(numContexts >= numEnabledContexts);
213    for (i = 0; i < numContexts; i++)
214    {
215	if (ppAllContexts[i] == pContext)
216	    return i;
217    }
218    return -1;
219} /* RecordFindContextOnAllContexts */
220
221
222/***************************************************************************/
223
224/* RecordFlushReplyBuffer
225 *
226 * Arguments:
227 *	pContext is the context to flush.
228 *	data1 is a pointer to additional data, and len1 is its length in bytes.
229 *	data2 is a pointer to additional data, and len2 is its length in bytes.
230 *
231 * Returns: nothing.
232 *
233 * Side Effects:
234 *	If the context is enabled, any buffered (recorded) protocol is written
235 *	to the recording client, and the number of buffered bytes is set to
236 *	zero.  If len1 is not zero, data1/len1 are then written to the
237 *	recording client, and similarly for data2/len2 (written after
238 *	data1/len1).
239 */
240static void
241RecordFlushReplyBuffer(
242    RecordContextPtr pContext,
243    pointer data1,
244    int len1,
245    pointer data2,
246    int len2
247)
248{
249    if (!pContext->pRecordingClient || pContext->pRecordingClient->clientGone || pContext->inFlush)
250	return;
251    ++pContext->inFlush;
252    if (pContext->numBufBytes)
253	WriteToClient(pContext->pRecordingClient, pContext->numBufBytes,
254		      (char *)pContext->replyBuffer);
255    pContext->numBufBytes = 0;
256    if (len1)
257	WriteToClient(pContext->pRecordingClient, len1, (char *)data1);
258    if (len2)
259	WriteToClient(pContext->pRecordingClient, len2, (char *)data2);
260    --pContext->inFlush;
261} /* RecordFlushReplyBuffer */
262
263
264/* RecordAProtocolElement
265 *
266 * Arguments:
267 *	pContext is the context that is recording a protocol element.
268 *	pClient is the client whose protocol is being recorded.  For
269 *	  device events and EndOfData, pClient is NULL.
270 *	category is the category of the protocol element, as defined
271 *	  by the RECORD spec.
272 *	data is a pointer to the protocol data, and datalen is its length
273 *	  in bytes.
274 *	futurelen is the number of bytes that will be sent in subsequent
275 *	  calls to this function to complete this protocol element.
276 *	  In those subsequent calls, futurelen will be -1 to indicate
277 *	  that the current data is a continuation of the same protocol
278 *	  element.
279 *
280 * Returns: nothing.
281 *
282 * Side Effects:
283 *	The context may be flushed.  The new protocol element will be
284 *	added to the context's protocol buffer with appropriate element
285 *	headers prepended (sequence number and timestamp).  If the data
286 *	is continuation data (futurelen == -1), element headers won't
287 *	be added.  If the protocol element and headers won't fit in
288 *	the context's buffer, it is sent directly to the recording
289 *	client (after any buffered data).
290 */
291static void
292RecordAProtocolElement(RecordContextPtr pContext, ClientPtr pClient,
293		       int category, pointer data, int datalen, int futurelen)
294{
295    CARD32 elemHeaderData[2];
296    int numElemHeaders = 0;
297    Bool recordingClientSwapped = pContext->pRecordingClient->swapped;
298    int n;
299    CARD32 serverTime = 0;
300    Bool gotServerTime = FALSE;
301    int replylen;
302
303    if (futurelen >= 0)
304    { /* start of new protocol element */
305	xRecordEnableContextReply *pRep = (xRecordEnableContextReply *)
306							pContext->replyBuffer;
307	if (pContext->pBufClient != pClient ||
308	    pContext->bufCategory != category)
309	{
310	    RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
311	    pContext->pBufClient = pClient;
312	    pContext->bufCategory = category;
313	}
314
315	if (!pContext->numBufBytes)
316	{
317	    serverTime = GetTimeInMillis();
318	    gotServerTime = TRUE;
319	    pRep->type          = X_Reply;
320	    pRep->category      = category;
321	    pRep->sequenceNumber = pContext->pRecordingClient->sequence;
322	    pRep->length        = 0;
323	    pRep->elementHeader = pContext->elemHeaders;
324	    pRep->serverTime    = serverTime;
325	    if (pClient)
326	    {
327		pRep->clientSwapped =
328				(pClient->swapped != recordingClientSwapped);
329		pRep->idBase = pClient->clientAsMask;
330		pRep->recordedSequenceNumber = pClient->sequence;
331	    }
332	    else /* it's a device event, StartOfData, or EndOfData */
333	    {
334		pRep->clientSwapped = (category != XRecordFromServer) &&
335						recordingClientSwapped;
336		pRep->idBase = 0;
337		pRep->recordedSequenceNumber = 0;
338	    }
339
340	    if (recordingClientSwapped)
341	    {
342		swaps(&pRep->sequenceNumber, n);
343		swapl(&pRep->length, n);
344		swapl(&pRep->idBase, n);
345		swapl(&pRep->serverTime, n);
346		swapl(&pRep->recordedSequenceNumber, n);
347	    }
348	    pContext->numBufBytes = SIZEOF(xRecordEnableContextReply);
349	}
350
351	/* generate element headers if needed */
352
353	if ( ( (pContext->elemHeaders & XRecordFromClientTime)
354	      && category == XRecordFromClient)
355	    ||
356	    ( (pContext->elemHeaders & XRecordFromServerTime)
357	     && category == XRecordFromServer))
358	{
359	    if (gotServerTime)
360		elemHeaderData[numElemHeaders] = serverTime;
361	    else
362		elemHeaderData[numElemHeaders] = GetTimeInMillis();
363	    if (recordingClientSwapped)
364		swapl(&elemHeaderData[numElemHeaders], n);
365	    numElemHeaders++;
366	}
367
368	if ( (pContext->elemHeaders & XRecordFromClientSequence)
369	    &&
370	    (category == XRecordFromClient || category == XRecordClientDied))
371	{
372	    elemHeaderData[numElemHeaders] = pClient->sequence;
373	    if (recordingClientSwapped)
374		swapl(&elemHeaderData[numElemHeaders], n);
375	    numElemHeaders++;
376	}
377
378	/* adjust reply length */
379
380	replylen = pRep->length;
381	if (recordingClientSwapped) swapl(&replylen, n);
382	replylen += numElemHeaders + bytes_to_int32(datalen) +
383            bytes_to_int32(futurelen);
384	if (recordingClientSwapped) swapl(&replylen, n);
385	pRep->length = replylen;
386    } /* end if not continued reply */
387
388    numElemHeaders *= 4;
389
390    /* if space available >= space needed, buffer the data */
391
392    if (REPLY_BUF_SIZE - pContext->numBufBytes >= datalen + numElemHeaders)
393    {
394	if (numElemHeaders)
395	{
396	    memcpy(pContext->replyBuffer + pContext->numBufBytes,
397		   elemHeaderData, numElemHeaders);
398	    pContext->numBufBytes += numElemHeaders;
399	}
400	if (datalen)
401	{
402	    memcpy(pContext->replyBuffer + pContext->numBufBytes,
403		   data, datalen);
404	    pContext->numBufBytes += datalen;
405	}
406    }
407    else
408	RecordFlushReplyBuffer(pContext, (pointer)elemHeaderData,
409			       numElemHeaders, (pointer)data, datalen);
410
411} /* RecordAProtocolElement */
412
413
414/* RecordFindClientOnContext
415 *
416 * Arguments:
417 *	pContext is the context to search.
418 *	clientspec is the resource ID mask identifying the client to search
419 *	  for, or XRecordFutureClients.
420 *	pposition is a pointer to an int, or NULL.  See Returns.
421 *
422 * Returns:
423 *	The RCAP on which clientspec was found, or NULL if not found on
424 *	any RCAP on the given context.
425 *	If pposition was not NULL and the returned RCAP is not NULL,
426 *	*pposition will be set to the index into the returned the RCAP's
427 *	pClientIDs array that holds clientspec.
428 *
429 * Side Effects: none.
430 */
431static RecordClientsAndProtocolPtr
432RecordFindClientOnContext(
433    RecordContextPtr pContext,
434    XID clientspec,
435    int *pposition
436)
437{
438    RecordClientsAndProtocolPtr pRCAP;
439
440    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
441    {
442	int i;
443	for (i = 0; i < pRCAP->numClients; i++)
444	{
445	    if (pRCAP->pClientIDs[i] == clientspec)
446	    {
447		if (pposition)
448		    *pposition = i;
449		return pRCAP;
450	    }
451	}
452    }
453    return NULL;
454} /* RecordFindClientOnContext */
455
456
457/* RecordABigRequest
458 *
459 * Arguments:
460 *	pContext is the recording context.
461 *	client is the client being recorded.
462 *	stuff is a pointer to the big request of client (see the Big Requests
463 *	extension for details.)
464 *
465 * Returns: nothing.
466 *
467 * Side Effects:
468 *	The big request is recorded with the correct length field re-inserted.
469 *
470 * Note: this function exists mainly to make RecordARequest smaller.
471 */
472static void
473RecordABigRequest(RecordContextPtr pContext, ClientPtr client, xReq *stuff)
474{
475    CARD32 bigLength;
476    char n;
477    int bytesLeft;
478
479    /* note: client->req_len has been frobbed by ReadRequestFromClient
480     * (os/io.c) to discount the extra 4 bytes taken by the extended length
481     * field in a big request.  The actual request length to record is
482     * client->req_len + 1 (measured in CARD32s).
483     */
484
485    /* record the request header */
486    bytesLeft = client->req_len << 2;
487    RecordAProtocolElement(pContext, client, XRecordFromClient,
488			   (pointer)stuff, SIZEOF(xReq), bytesLeft);
489
490    /* reinsert the extended length field that was squished out */
491    bigLength = client->req_len + bytes_to_int32(sizeof(bigLength));
492    if (client->swapped)
493	swapl(&bigLength, n);
494    RecordAProtocolElement(pContext, client, XRecordFromClient,
495		(pointer)&bigLength, sizeof(bigLength), /* continuation */ -1);
496    bytesLeft -= sizeof(bigLength);
497
498    /* record the rest of the request after the length */
499    RecordAProtocolElement(pContext, client, XRecordFromClient,
500		(pointer)(stuff + 1), bytesLeft, /* continuation */ -1);
501} /* RecordABigRequest */
502
503
504/* RecordARequest
505 *
506 * Arguments:
507 *	client is a client that the server has dispatched a request to by
508 *	calling client->requestVector[request opcode] .
509 *	The request is in client->requestBuffer.
510 *
511 * Returns:
512 *	Whatever is returned by the "real" Proc function for this request.
513 *	The "real" Proc function is the function that was in
514 *	client->requestVector[request opcode]  before it was replaced by
515 *	RecordARequest.  (See the function RecordInstallHooks.)
516 *
517 * Side Effects:
518 *	The request is recorded by all contexts that have registered this
519 *	request for this client.  The real Proc function is called.
520 */
521static int
522RecordARequest(ClientPtr client)
523{
524    RecordContextPtr pContext;
525    RecordClientsAndProtocolPtr pRCAP;
526    int i;
527    RecordClientPrivatePtr pClientPriv;
528    REQUEST(xReq);
529    int majorop;
530
531    majorop = stuff->reqType;
532    for (i = 0; i < numEnabledContexts; i++)
533    {
534	pContext = ppAllContexts[i];
535	pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask,
536					  NULL);
537	if (pRCAP && pRCAP->pRequestMajorOpSet &&
538	    RecordIsMemberOfSet(pRCAP->pRequestMajorOpSet, majorop))
539	{
540	    if (majorop <= 127)
541	    { /* core request */
542
543		if (stuff->length == 0)
544		    RecordABigRequest(pContext, client, stuff);
545		else
546		    RecordAProtocolElement(pContext, client, XRecordFromClient,
547				(pointer)stuff, client->req_len << 2, 0);
548	    }
549	    else /* extension, check minor opcode */
550	    {
551		int minorop = MinorOpcodeOfRequest(client);
552		int numMinOpInfo;
553		RecordMinorOpPtr pMinorOpInfo = pRCAP->pRequestMinOpInfo;
554
555		assert (pMinorOpInfo);
556		numMinOpInfo = pMinorOpInfo->count;
557		pMinorOpInfo++;
558		assert (numMinOpInfo);
559		for ( ; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++)
560		{
561		    if (majorop >= pMinorOpInfo->major.first &&
562			majorop <= pMinorOpInfo->major.last &&
563			RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
564					    minorop))
565		    {
566			if (stuff->length == 0)
567			    RecordABigRequest(pContext, client, stuff);
568			else
569			    RecordAProtocolElement(pContext, client,
570					XRecordFromClient, (pointer)stuff,
571					client->req_len << 2, 0);
572			break;
573		    }
574		} /* end for each minor op info */
575	    } /* end extension request */
576	} /* end this RCAP wants this major opcode */
577    } /* end for each context */
578    pClientPriv = RecordClientPrivate(client);
579    assert(pClientPriv);
580    return (* pClientPriv->originalVector[majorop])(client);
581} /* RecordARequest */
582
583/* RecordAReply
584 *
585 * Arguments:
586 *	pcbl is &ReplyCallback.
587 *	nulldata is NULL.
588 *	calldata is a pointer to a ReplyInfoRec (include/os.h)
589 *	  which provides information about replies that are being sent
590 *	  to clients.
591 *
592 * Returns: nothing.
593 *
594 * Side Effects:
595 *	The reply is recorded by all contexts that have registered this
596 *	reply type for this client.  If more data belonging to the same
597 *	reply is expected, and if the reply is being recorded by any
598 *	context, pContext->continuedReply is set to 1.
599 *	If pContext->continuedReply was already 1 and this is the last
600 *	chunk of data belonging to this reply, it is set to 0.
601 */
602static void
603RecordAReply(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
604{
605    RecordContextPtr pContext;
606    RecordClientsAndProtocolPtr pRCAP;
607    int eci;
608    int majorop;
609    ReplyInfoRec *pri = (ReplyInfoRec *)calldata;
610    ClientPtr client = pri->client;
611    REQUEST(xReq);
612
613    majorop = stuff->reqType;
614    for (eci = 0; eci < numEnabledContexts; eci++)
615    {
616	pContext = ppAllContexts[eci];
617	pRCAP = RecordFindClientOnContext(pContext, client->clientAsMask,
618					  NULL);
619	if (pRCAP)
620	{
621	    if (pContext->continuedReply)
622	    {
623		RecordAProtocolElement(pContext, client, XRecordFromServer,
624		   (pointer)pri->replyData, pri->dataLenBytes, /* continuation */ -1);
625		if (!pri->bytesRemaining)
626		    pContext->continuedReply = 0;
627	    }
628	    else if (pri->startOfReply && pRCAP->pReplyMajorOpSet &&
629		     RecordIsMemberOfSet(pRCAP->pReplyMajorOpSet, majorop))
630	    {
631		if (majorop <= 127)
632		{ /* core reply */
633		    RecordAProtocolElement(pContext, client, XRecordFromServer,
634		       (pointer)pri->replyData, pri->dataLenBytes, pri->bytesRemaining);
635		    if (pri->bytesRemaining)
636			pContext->continuedReply = 1;
637		}
638		else /* extension, check minor opcode */
639		{
640		    int minorop = MinorOpcodeOfRequest(client);
641		    int numMinOpInfo;
642		    RecordMinorOpPtr pMinorOpInfo = pRCAP->pReplyMinOpInfo;
643		    		    assert (pMinorOpInfo);
644		    numMinOpInfo = pMinorOpInfo->count;
645		    pMinorOpInfo++;
646		    assert (numMinOpInfo);
647		    for ( ; numMinOpInfo; numMinOpInfo--, pMinorOpInfo++)
648		    {
649			if (majorop >= pMinorOpInfo->major.first &&
650			    majorop <= pMinorOpInfo->major.last &&
651			    RecordIsMemberOfSet(pMinorOpInfo->major.pMinOpSet,
652						minorop))
653			{
654			    RecordAProtocolElement(pContext, client,
655				XRecordFromServer, (pointer)pri->replyData,
656				pri->dataLenBytes, pri->bytesRemaining);
657			    if (pri->bytesRemaining)
658				pContext->continuedReply = 1;
659			    break;
660			}
661		    } /* end for each minor op info */
662		} /* end extension reply */
663	    } /* end continued reply vs. start of reply */
664	} /* end client is registered on this context */
665    } /* end for each context */
666} /* RecordAReply */
667
668
669/* RecordADeliveredEventOrError
670 *
671 * Arguments:
672 *	pcbl is &EventCallback.
673 *	nulldata is NULL.
674 *	calldata is a pointer to a EventInfoRec (include/dix.h)
675 *	  which provides information about events that are being sent
676 *	  to clients.
677 *
678 * Returns: nothing.
679 *
680 * Side Effects:
681 *	The event or error is recorded by all contexts that have registered
682 *	it for this client.
683 */
684static void
685RecordADeliveredEventOrError(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
686{
687    EventInfoRec *pei = (EventInfoRec *)calldata;
688    RecordContextPtr pContext;
689    RecordClientsAndProtocolPtr pRCAP;
690    int eci; /* enabled context index */
691    ClientPtr pClient = pei->client;
692
693    for (eci = 0; eci < numEnabledContexts; eci++)
694    {
695	pContext = ppAllContexts[eci];
696	pRCAP = RecordFindClientOnContext(pContext, pClient->clientAsMask,
697					  NULL);
698	if (pRCAP && (pRCAP->pDeliveredEventSet || pRCAP->pErrorSet))
699	{
700	    int ev; /* event index */
701	    xEvent *pev = pei->events;
702	    for (ev = 0; ev < pei->count; ev++, pev++)
703	    {
704		int recordit = 0;
705		if (pRCAP->pErrorSet)
706		{
707		    recordit = RecordIsMemberOfSet(pRCAP->pErrorSet,
708						((xError *)(pev))->errorCode);
709		}
710		else if (pRCAP->pDeliveredEventSet)
711		{
712		    recordit = RecordIsMemberOfSet(pRCAP->pDeliveredEventSet,
713						   pev->u.u.type & 0177);
714		}
715		if (recordit)
716		{
717		    xEvent swappedEvent;
718		    xEvent *pEvToRecord = pev;
719
720		    if (pClient->swapped)
721		    {
722			(*EventSwapVector[pev->u.u.type & 0177])
723			    (pev, &swappedEvent);
724			pEvToRecord = &swappedEvent;
725
726		    }
727		    RecordAProtocolElement(pContext, pClient,
728			XRecordFromServer, pEvToRecord, SIZEOF(xEvent), 0);
729		}
730	    } /* end for each event */
731	} /* end this client is on this context */
732    } /* end for each enabled context */
733} /* RecordADeliveredEventOrError */
734
735
736static void
737RecordSendProtocolEvents(RecordClientsAndProtocolPtr pRCAP,
738			RecordContextPtr pContext,
739			xEvent* pev, int count)
740{
741    int ev; /* event index */
742
743    for (ev = 0; ev < count; ev++, pev++)
744    {
745	if (RecordIsMemberOfSet(pRCAP->pDeviceEventSet,
746		    pev->u.u.type & 0177))
747	{
748	    xEvent swappedEvent;
749	    xEvent *pEvToRecord = pev;
750#ifdef PANORAMIX
751	    xEvent shiftedEvent;
752
753	    if (!noPanoramiXExtension &&
754		    (pev->u.u.type == MotionNotify ||
755		     pev->u.u.type == ButtonPress ||
756		     pev->u.u.type == ButtonRelease ||
757		     pev->u.u.type == KeyPress ||
758		     pev->u.u.type == KeyRelease)) {
759		int scr = XineramaGetCursorScreen(inputInfo.pointer);
760		memcpy(&shiftedEvent, pev, sizeof(xEvent));
761		shiftedEvent.u.keyButtonPointer.rootX +=
762		    screenInfo.screens[scr]->x -
763		    screenInfo.screens[0]->x;
764		shiftedEvent.u.keyButtonPointer.rootY +=
765		    screenInfo.screens[scr]->y -
766		    screenInfo.screens[0]->y;
767		pEvToRecord = &shiftedEvent;
768	    }
769#endif /* PANORAMIX */
770
771	    if (pContext->pRecordingClient->swapped)
772	    {
773		(*EventSwapVector[pEvToRecord->u.u.type & 0177])
774		    (pEvToRecord, &swappedEvent);
775		pEvToRecord = &swappedEvent;
776	    }
777
778	    RecordAProtocolElement(pContext, NULL,
779		    XRecordFromServer,  pEvToRecord, SIZEOF(xEvent), 0);
780	    /* make sure device events get flushed in the absence
781	     * of other client activity
782	     */
783	    SetCriticalOutputPending();
784	}
785    } /* end for each event */
786
787} /* RecordADeviceEvent */
788
789/* RecordADeviceEvent
790 *
791 * Arguments:
792 *	pcbl is &DeviceEventCallback.
793 *	nulldata is NULL.
794 *	calldata is a pointer to a DeviceEventInfoRec (include/dix.h)
795 *	  which provides information about device events that occur.
796 *
797 * Returns: nothing.
798 *
799 * Side Effects:
800 *	The device event is recorded by all contexts that have registered
801 *	it for this client.
802 */
803static void
804RecordADeviceEvent(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
805{
806    DeviceEventInfoRec *pei = (DeviceEventInfoRec *)calldata;
807    RecordContextPtr pContext;
808    RecordClientsAndProtocolPtr pRCAP;
809    int eci; /* enabled context index */
810
811    for (eci = 0; eci < numEnabledContexts; eci++)
812    {
813	pContext = ppAllContexts[eci];
814	for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
815	{
816	    if (pRCAP->pDeviceEventSet)
817	    {
818		int count;
819		xEvent *xi_events = NULL;
820
821		/* TODO check return values */
822		if (IsMaster(pei->device))
823		{
824		    xEvent xE;
825		    EventToCore(pei->event, &xE);
826		    RecordSendProtocolEvents(pRCAP, pContext, &xE, 1);
827		}
828
829		EventToXI(pei->event, &xi_events, &count);
830		RecordSendProtocolEvents(pRCAP, pContext, xi_events, count);
831		free(xi_events);
832	    } /* end this RCAP selects device events */
833	} /* end for each RCAP on this context */
834    } /* end for each enabled context */
835}
836
837
838/* RecordFlushAllContexts
839 *
840 * Arguments:
841 *	pcbl is &FlushCallback.
842 *	nulldata and calldata are NULL.
843 *
844 * Returns: nothing.
845 *
846 * Side Effects:
847 *	All buffered reply data of all enabled contexts is written to
848 *	the recording clients.
849 */
850static void
851RecordFlushAllContexts(
852    CallbackListPtr *pcbl,
853    pointer nulldata,
854    pointer calldata
855)
856{
857    int eci; /* enabled context index */
858    RecordContextPtr pContext;
859
860    for (eci = 0; eci < numEnabledContexts; eci++)
861    {
862	pContext = ppAllContexts[eci];
863
864	/* In most cases we leave it to RecordFlushReplyBuffer to make
865	 * this check, but this function could be called very often, so we
866	 * check before calling hoping to save the function call cost
867	 * most of the time.
868	 */
869	if (pContext->numBufBytes)
870	    RecordFlushReplyBuffer(ppAllContexts[eci], NULL, 0, NULL, 0);
871    }
872} /* RecordFlushAllContexts */
873
874
875/* RecordInstallHooks
876 *
877 * Arguments:
878 *	pRCAP is an RCAP on an enabled or being-enabled context.
879 *	oneclient can be zero or the resource ID mask identifying a client.
880 *
881 * Returns: BadAlloc if a memory allocation error occurred, else Success.
882 *
883 * Side Effects:
884 *	Recording hooks needed by RCAP are installed.
885 *	If oneclient is zero, recording hooks needed for all clients and
886 *	protocol on the RCAP are installed.  If oneclient is non-zero,
887 *	only those hooks needed for the specified client are installed.
888 *
889 *	Client requestVectors may be altered.  numEnabledRCAPs will be
890 *	incremented if oneclient == 0.  Callbacks may be added to
891 *	various callback lists.
892 */
893static int
894RecordInstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
895{
896    int i = 0;
897    XID client;
898
899    if (oneclient)
900	client = oneclient;
901    else
902	client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;
903
904    while (client)
905    {
906	if (client != XRecordFutureClients)
907	{
908	    if (pRCAP->pRequestMajorOpSet)
909	    {
910		RecordSetIteratePtr pIter = NULL;
911		RecordSetInterval interval;
912		ClientPtr pClient = clients[CLIENT_ID(client)];
913
914		if (pClient && !RecordClientPrivate(pClient))
915		{
916		    RecordClientPrivatePtr pClientPriv;
917		    /* no Record proc vector; allocate one */
918		    pClientPriv = (RecordClientPrivatePtr)
919				malloc(sizeof(RecordClientPrivateRec));
920		    if (!pClientPriv)
921			return BadAlloc;
922		    /* copy old proc vector to new */
923		    memcpy(pClientPriv->recordVector, pClient->requestVector,
924			   sizeof (pClientPriv->recordVector));
925		    pClientPriv->originalVector = pClient->requestVector;
926		    dixSetPrivate(&pClient->devPrivates,
927				  RecordClientPrivateKey, pClientPriv);
928		    pClient->requestVector = pClientPriv->recordVector;
929		}
930		while ((pIter = RecordIterateSet(pRCAP->pRequestMajorOpSet,
931						pIter, &interval)))
932		{
933		    unsigned int j;
934		    for (j = interval.first; j <= interval.last; j++)
935			pClient->requestVector[j] = RecordARequest;
936		}
937	    }
938	}
939	if (oneclient)
940	    client = 0;
941	else
942	    client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
943    }
944
945    assert(numEnabledRCAPs >= 0);
946    if (!oneclient && ++numEnabledRCAPs == 1)
947    { /* we're enabling the first context */
948	if (!AddCallback(&EventCallback, RecordADeliveredEventOrError, NULL))
949	    return BadAlloc;
950	if (!AddCallback(&DeviceEventCallback, RecordADeviceEvent, NULL))
951	    return BadAlloc;
952	if (!AddCallback(&ReplyCallback, RecordAReply, NULL))
953	    return BadAlloc;
954	if (!AddCallback(&FlushCallback, RecordFlushAllContexts, NULL))
955	    return BadAlloc;
956	/* Alternate context flushing scheme: delete the line above
957	 * and call RegisterBlockAndWakeupHandlers here passing
958	 * RecordFlushAllContexts.  Is this any better?
959	 */
960    }
961    return Success;
962} /* RecordInstallHooks */
963
964
965/* RecordUninstallHooks
966 *
967 * Arguments:
968 *	pRCAP is an RCAP on an enabled or being-disabled context.
969 *	oneclient can be zero or the resource ID mask identifying a client.
970 *
971 * Returns: nothing.
972 *
973 * Side Effects:
974 *	Recording hooks needed by RCAP may be uninstalled.
975 *	If oneclient is zero, recording hooks needed for all clients and
976 *	protocol on the RCAP may be uninstalled.  If oneclient is non-zero,
977 *	only those hooks needed for the specified client may be uninstalled.
978 *
979 *	Client requestVectors may be altered.  numEnabledRCAPs will be
980 *	decremented if oneclient == 0.  Callbacks may be deleted from
981 *	various callback lists.
982 */
983static void
984RecordUninstallHooks(RecordClientsAndProtocolPtr pRCAP, XID oneclient)
985{
986    int i = 0;
987    XID client;
988
989    if (oneclient)
990	client = oneclient;
991    else
992	client = pRCAP->numClients ? pRCAP->pClientIDs[i++] : 0;
993
994    while (client)
995    {
996	if (client != XRecordFutureClients)
997	{
998	    if (pRCAP->pRequestMajorOpSet)
999	    {
1000		ClientPtr pClient = clients[CLIENT_ID(client)];
1001		int c;
1002		Bool otherRCAPwantsProcVector = FALSE;
1003		RecordClientPrivatePtr pClientPriv = NULL;
1004
1005		assert (pClient);
1006		pClientPriv = RecordClientPrivate(pClient);
1007		assert (pClientPriv);
1008		memcpy(pClientPriv->recordVector, pClientPriv->originalVector,
1009		       sizeof (pClientPriv->recordVector));
1010
1011		for (c = 0; c < numEnabledContexts; c++)
1012		{
1013		    RecordClientsAndProtocolPtr pOtherRCAP;
1014		    RecordContextPtr pContext = ppAllContexts[c];
1015
1016		    if (pContext == pRCAP->pContext) continue;
1017		    pOtherRCAP = RecordFindClientOnContext(pContext, client,
1018							   NULL);
1019		    if (pOtherRCAP && pOtherRCAP->pRequestMajorOpSet)
1020		    {
1021			RecordSetIteratePtr pIter = NULL;
1022			RecordSetInterval interval;
1023
1024			otherRCAPwantsProcVector = TRUE;
1025			while ((pIter = RecordIterateSet(
1026						pOtherRCAP->pRequestMajorOpSet,
1027						pIter, &interval)))
1028			{
1029			    unsigned int j;
1030			    for (j = interval.first; j <= interval.last; j++)
1031				pClient->requestVector[j] = RecordARequest;
1032			}
1033		    }
1034		}
1035		if (!otherRCAPwantsProcVector)
1036		{ /* nobody needs it, so free it */
1037		    pClient->requestVector = pClientPriv->originalVector;
1038		    dixSetPrivate(&pClient->devPrivates,
1039				  RecordClientPrivateKey, NULL);
1040		    free(pClientPriv);
1041		}
1042	    } /* end if this RCAP specifies any requests */
1043	} /* end if not future clients */
1044	if (oneclient)
1045	    client = 0;
1046	else
1047	    client = (i < pRCAP->numClients) ? pRCAP->pClientIDs[i++] : 0;
1048    }
1049
1050    assert(numEnabledRCAPs >= 1);
1051    if (!oneclient && --numEnabledRCAPs == 0)
1052    { /* we're disabling the last context */
1053	DeleteCallback(&EventCallback, RecordADeliveredEventOrError, NULL);
1054	DeleteCallback(&DeviceEventCallback, RecordADeviceEvent, NULL);
1055	DeleteCallback(&ReplyCallback, RecordAReply, NULL);
1056	DeleteCallback(&FlushCallback, RecordFlushAllContexts, NULL);
1057	/* Alternate context flushing scheme: delete the line above
1058	 * and call RemoveBlockAndWakeupHandlers here passing
1059	 * RecordFlushAllContexts.  Is this any better?
1060	 */
1061	/* Having deleted the callback, call it one last time. -gildea */
1062	RecordFlushAllContexts(&FlushCallback, NULL, NULL);
1063    }
1064} /* RecordUninstallHooks */
1065
1066
1067/* RecordDeleteClientFromRCAP
1068 *
1069 * Arguments:
1070 *	pRCAP is an RCAP to delete the client from.
1071 *	position is the index into the array pRCAP->pClientIDs of the
1072 *	client to delete.
1073 *
1074 * Returns: nothing.
1075 *
1076 * Side Effects:
1077 *	Recording hooks needed by client will be uninstalled if the context
1078 *	is enabled.  The designated client will be removed from the
1079 *	pRCAP->pClientIDs array.  If it was the only client on the RCAP,
1080 *	the RCAP is removed from the context and freed.  (Invariant: RCAPs
1081 *	have at least one client.)
1082 */
1083static void
1084RecordDeleteClientFromRCAP(RecordClientsAndProtocolPtr pRCAP, int position)
1085{
1086    if (pRCAP->pContext->pRecordingClient)
1087	RecordUninstallHooks(pRCAP, pRCAP->pClientIDs[position]);
1088    if (position != pRCAP->numClients - 1)
1089	pRCAP->pClientIDs[position] = pRCAP->pClientIDs[pRCAP->numClients - 1];
1090    if (--pRCAP->numClients == 0)
1091    {	/* no more clients; remove RCAP from context's list */
1092	RecordContextPtr pContext = pRCAP->pContext;
1093	if (pContext->pRecordingClient)
1094	    RecordUninstallHooks(pRCAP, 0);
1095	if (pContext->pListOfRCAP == pRCAP)
1096	    pContext->pListOfRCAP = pRCAP->pNextRCAP;
1097	else
1098	{
1099	    RecordClientsAndProtocolPtr prevRCAP;
1100	    for (prevRCAP = pContext->pListOfRCAP;
1101		 prevRCAP->pNextRCAP != pRCAP;
1102		 prevRCAP = prevRCAP->pNextRCAP)
1103		;
1104	    prevRCAP->pNextRCAP = pRCAP->pNextRCAP;
1105	}
1106	/* free the RCAP */
1107	if (pRCAP->clientIDsSeparatelyAllocated)
1108	    free(pRCAP->pClientIDs);
1109	free(pRCAP);
1110    }
1111} /* RecordDeleteClientFromRCAP */
1112
1113
1114/* RecordAddClientToRCAP
1115 *
1116 * Arguments:
1117 *	pRCAP is an RCAP to add the client to.
1118 *	clientspec is the resource ID mask identifying a client, or
1119 *	  XRecordFutureClients.
1120 *
1121 * Returns: nothing.
1122 *
1123 * Side Effects:
1124 *	Recording hooks needed by client will be installed if the context
1125 *	is enabled.  The designated client will be added to the
1126 *	pRCAP->pClientIDs array, which may be realloced.
1127 *	pRCAP->clientIDsSeparatelyAllocated may be set to 1 if there
1128 *	is no more room to hold clients internal to the RCAP.
1129 */
1130static void
1131RecordAddClientToRCAP(RecordClientsAndProtocolPtr pRCAP, XID clientspec)
1132{
1133    if (pRCAP->numClients == pRCAP->sizeClients)
1134    {
1135	if (pRCAP->clientIDsSeparatelyAllocated)
1136	{
1137	    XID *pNewIDs = (XID *)realloc(pRCAP->pClientIDs,
1138			(pRCAP->sizeClients + CLIENT_ARRAY_GROWTH_INCREMENT) *
1139								sizeof(XID));
1140	    if (!pNewIDs)
1141		return;
1142	    pRCAP->pClientIDs = pNewIDs;
1143	    pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
1144	}
1145	else
1146	{
1147	    XID *pNewIDs = (XID *)malloc((pRCAP->sizeClients +
1148				CLIENT_ARRAY_GROWTH_INCREMENT) * sizeof(XID));
1149	    if (!pNewIDs)
1150		return;
1151	    memcpy(pNewIDs, pRCAP->pClientIDs, pRCAP->numClients *sizeof(XID));
1152	    pRCAP->pClientIDs = pNewIDs;
1153	    pRCAP->sizeClients += CLIENT_ARRAY_GROWTH_INCREMENT;
1154	    pRCAP->clientIDsSeparatelyAllocated = 1;
1155	}
1156    }
1157    pRCAP->pClientIDs[pRCAP->numClients++] = clientspec;
1158    if (pRCAP->pContext->pRecordingClient)
1159	RecordInstallHooks(pRCAP, clientspec);
1160} /* RecordDeleteClientFromRCAP */
1161
1162
1163/* RecordDeleteClientFromContext
1164 *
1165 * Arguments:
1166 *	pContext is the context to delete from.
1167 *	clientspec is the resource ID mask identifying a client, or
1168 *	  XRecordFutureClients.
1169 *
1170 * Returns: nothing.
1171 *
1172 * Side Effects:
1173 *	If clientspec is on any RCAP of the context, it is deleted from that
1174 *	RCAP.  (A given clientspec can only be on one RCAP of a context.)
1175 */
1176static void
1177RecordDeleteClientFromContext(RecordContextPtr pContext, XID clientspec)
1178{
1179    RecordClientsAndProtocolPtr pRCAP;
1180    int position;
1181
1182    if ((pRCAP = RecordFindClientOnContext(pContext, clientspec, &position)))
1183	RecordDeleteClientFromRCAP(pRCAP, position);
1184} /* RecordDeleteClientFromContext */
1185
1186
1187/* RecordSanityCheckClientSpecifiers
1188 *
1189 * Arguments:
1190 *	clientspecs is an array of alleged CLIENTSPECs passed by the client.
1191 *	nspecs is the number of elements in clientspecs.
1192 *	errorspec, if non-zero, is the resource id base of a client that
1193 *	  must not appear in clienspecs.
1194 *
1195 * Returns: BadMatch if any of the clientspecs are invalid, else Success.
1196 *
1197 * Side Effects: none.
1198 */
1199static int
1200RecordSanityCheckClientSpecifiers(ClientPtr client, XID *clientspecs, int nspecs, XID errorspec)
1201{
1202    int i;
1203    int clientIndex;
1204    int rc;
1205    pointer value;
1206
1207    for (i = 0; i < nspecs; i++)
1208    {
1209	if (clientspecs[i] == XRecordCurrentClients ||
1210	    clientspecs[i] == XRecordFutureClients ||
1211	    clientspecs[i] == XRecordAllClients)
1212	    continue;
1213	if (errorspec && (CLIENT_BITS(clientspecs[i]) == errorspec) )
1214	    return BadMatch;
1215	clientIndex = CLIENT_ID(clientspecs[i]);
1216	if (clientIndex && clients[clientIndex] &&
1217	    clients[clientIndex]->clientState == ClientStateRunning)
1218	{
1219	    if (clientspecs[i] == clients[clientIndex]->clientAsMask)
1220		continue;
1221            rc = dixLookupResourceByClass(&value, clientspecs[i], RC_ANY,
1222                                          client, DixGetAttrAccess);
1223            if (rc != Success)
1224                return rc;
1225	}
1226	else
1227	    return BadMatch;
1228    }
1229    return Success;
1230} /* RecordSanityCheckClientSpecifiers */
1231
1232
1233/* RecordCanonicalizeClientSpecifiers
1234 *
1235 * Arguments:
1236 *	pClientspecs is an array of CLIENTSPECs that have been sanity
1237 *	  checked.
1238 *	pNumClientspecs is a pointer to the number of elements in pClientspecs.
1239 *	excludespec, if non-zero, is the resource id base of a client that
1240 *	  should not be included in the expansion of XRecordAllClients or
1241 *	  XRecordCurrentClients.
1242 *
1243 * Returns:
1244 *	A pointer to an array of CLIENTSPECs that is the same as the
1245 *	passed array with the following modifications:
1246 *	  - all but the client id bits of resource IDs are stripped off.
1247 *	  - duplicates removed.
1248 *	  - XRecordAllClients expanded to a list of all currently connected
1249 *	    clients + XRecordFutureClients - excludespec (if non-zero)
1250 *	  - XRecordCurrentClients expanded to a list of all currently
1251 *	    connected clients - excludespec (if non-zero)
1252 *	The returned array may be the passed array modified in place, or
1253 *	it may be an malloc'ed array.  The caller should keep a pointer to the
1254 *	original array and free the returned array if it is different.
1255 *
1256 *	*pNumClientspecs is set to the number of elements in the returned
1257 *	array.
1258 *
1259 * Side Effects:
1260 *	pClientspecs may be modified in place.
1261 */
1262static XID *
1263RecordCanonicalizeClientSpecifiers(XID *pClientspecs, int *pNumClientspecs, XID excludespec)
1264{
1265    int i;
1266    int numClients = *pNumClientspecs;
1267
1268    /*  first pass strips off the resource index bits, leaving just the
1269     *  client id bits.  This makes searching for a particular client simpler
1270     *  (and faster.)
1271     */
1272    for (i = 0; i < numClients; i++)
1273    {
1274	XID cs = pClientspecs[i];
1275	if (cs > XRecordAllClients)
1276	    pClientspecs[i] = CLIENT_BITS(cs);
1277    }
1278
1279    for (i = 0; i < numClients; i++)
1280    {
1281	if (pClientspecs[i] == XRecordAllClients ||
1282	    pClientspecs[i] == XRecordCurrentClients)
1283	{ /* expand All/Current */
1284	    int j, nc;
1285	    XID *pCanon = (XID *)malloc(sizeof(XID) * (currentMaxClients + 1));
1286	    if (!pCanon) return NULL;
1287	    for (nc = 0, j = 1; j < currentMaxClients; j++)
1288	    {
1289		ClientPtr client = clients[j];
1290		if (client != NullClient &&
1291		    client->clientState == ClientStateRunning &&
1292		    client->clientAsMask != excludespec)
1293		{
1294		    pCanon[nc++] = client->clientAsMask;
1295		}
1296	    }
1297	    if (pClientspecs[i] == XRecordAllClients)
1298		pCanon[nc++] = XRecordFutureClients;
1299	    *pNumClientspecs = nc;
1300	    return pCanon;
1301	}
1302	else /* not All or Current */
1303	{
1304	    int j;
1305	    for (j = i + 1; j < numClients; )
1306	    {
1307		if (pClientspecs[i] == pClientspecs[j])
1308		{
1309		    pClientspecs[j] = pClientspecs[--numClients];
1310		}
1311		else
1312		    j++;
1313	    }
1314	}
1315    } /* end for each clientspec */
1316    *pNumClientspecs = numClients;
1317    return pClientspecs;
1318} /* RecordCanonicalizeClientSpecifiers */
1319
1320
1321/****************************************************************************/
1322
1323/* stuff for RegisterClients */
1324
1325/* RecordPadAlign
1326 *
1327 * Arguments:
1328 *	size is the number of bytes taken by an object.
1329 *	align is a byte boundary (e.g. 4, 8)
1330 *
1331 * Returns:
1332 *	the number of pad bytes to add at the end of an object of the
1333 *	given size so that an object placed immediately behind it will
1334 *	begin on an <align>-byte boundary.
1335 *
1336 * Side Effects: none.
1337 */
1338static int
1339RecordPadAlign(int size, int align)
1340{
1341    return (align - (size & (align - 1))) & (align - 1);
1342} /* RecordPadAlign */
1343
1344
1345/* RecordSanityCheckRegisterClients
1346 *
1347 * Arguments:
1348 *	pContext is the context being registered on.
1349 *	client is the client that issued a RecordCreateContext or
1350 *	  RecordRegisterClients request.
1351 *	stuff is a pointer to the request.
1352 *
1353 * Returns:
1354 *	Any one of several possible error values if any of the request
1355 *	arguments are invalid.  Success if everything is OK.
1356 *
1357 * Side Effects: none.
1358 */
1359static int
1360RecordSanityCheckRegisterClients(RecordContextPtr pContext, ClientPtr client, xRecordRegisterClientsReq *stuff)
1361{
1362    int err;
1363    xRecordRange *pRange;
1364    int i;
1365    XID recordingClient;
1366
1367    if (((client->req_len << 2) - SIZEOF(xRecordRegisterClientsReq)) !=
1368	4 * stuff->nClients + SIZEOF(xRecordRange) * stuff->nRanges)
1369	return BadLength;
1370
1371    if (stuff->elementHeader &
1372     ~(XRecordFromClientSequence|XRecordFromClientTime|XRecordFromServerTime))
1373    {
1374	client->errorValue = stuff->elementHeader;
1375	return BadValue;
1376    }
1377
1378    recordingClient = pContext->pRecordingClient ?
1379		      pContext->pRecordingClient->clientAsMask : 0;
1380    err = RecordSanityCheckClientSpecifiers(client, (XID *)&stuff[1],
1381					    stuff->nClients, recordingClient);
1382    if (err != Success) return err;
1383
1384    pRange = (xRecordRange *)(((XID *)&stuff[1]) + stuff->nClients);
1385    for (i = 0; i < stuff->nRanges; i++, pRange++)
1386    {
1387	if (pRange->coreRequestsFirst > pRange->coreRequestsLast)
1388	{
1389	    client->errorValue = pRange->coreRequestsFirst;
1390	    return BadValue;
1391	}
1392	if (pRange->coreRepliesFirst > pRange->coreRepliesLast)
1393	{
1394	    client->errorValue = pRange->coreRepliesFirst;
1395	    return BadValue;
1396	}
1397	if ((pRange->extRequestsMajorFirst || pRange->extRequestsMajorLast) &&
1398	    (pRange->extRequestsMajorFirst < 128 ||
1399	     pRange->extRequestsMajorLast < 128 ||
1400	     pRange->extRequestsMajorFirst > pRange->extRequestsMajorLast))
1401	{
1402	    client->errorValue = pRange->extRequestsMajorFirst;
1403	    return BadValue;
1404	}
1405	if (pRange->extRequestsMinorFirst > pRange->extRequestsMinorLast)
1406	{
1407	    client->errorValue = pRange->extRequestsMinorFirst;
1408	    return BadValue;
1409	}
1410	if ((pRange->extRepliesMajorFirst || pRange->extRepliesMajorLast) &&
1411	    (pRange->extRepliesMajorFirst < 128 ||
1412	     pRange->extRepliesMajorLast < 128 ||
1413	     pRange->extRepliesMajorFirst > pRange->extRepliesMajorLast))
1414	{
1415	    client->errorValue = pRange->extRepliesMajorFirst;
1416	    return BadValue;
1417	}
1418	if (pRange->extRepliesMinorFirst > pRange->extRepliesMinorLast)
1419	{
1420	    client->errorValue = pRange->extRepliesMinorFirst;
1421	    return BadValue;
1422	}
1423	if ((pRange->deliveredEventsFirst || pRange->deliveredEventsLast) &&
1424	    (pRange->deliveredEventsFirst < 2 ||
1425	     pRange->deliveredEventsLast < 2 ||
1426	     pRange->deliveredEventsFirst > pRange->deliveredEventsLast))
1427	{
1428	    client->errorValue = pRange->deliveredEventsFirst;
1429	    return BadValue;
1430	}
1431	if ((pRange->deviceEventsFirst || pRange->deviceEventsLast) &&
1432	    (pRange->deviceEventsFirst < 2 ||
1433	     pRange->deviceEventsLast < 2 ||
1434	     pRange->deviceEventsFirst > pRange->deviceEventsLast))
1435	{
1436	    client->errorValue = pRange->deviceEventsFirst;
1437	    return BadValue;
1438	}
1439	if (pRange->errorsFirst > pRange->errorsLast)
1440	{
1441	    client->errorValue = pRange->errorsFirst;
1442	    return BadValue;
1443	}
1444	if (pRange->clientStarted != xFalse && pRange->clientStarted != xTrue)
1445	{
1446	    client->errorValue = pRange->clientStarted;
1447	    return BadValue;
1448	}
1449	if (pRange->clientDied != xFalse && pRange->clientDied != xTrue)
1450	{
1451	    client->errorValue = pRange->clientDied;
1452	    return BadValue;
1453	}
1454    } /* end for each range */
1455    return Success;
1456} /* end RecordSanityCheckRegisterClients */
1457
1458/* This is a tactical structure used to gather information about all the sets
1459 * (RecordSetPtr) that need to be created for an RCAP in the process of
1460 * digesting a list of RECORDRANGEs (converting it to the internal
1461 * representation).
1462 */
1463typedef struct
1464{
1465    int nintervals;	/* number of intervals in following array */
1466    RecordSetInterval *intervals;  /* array of intervals for this set */
1467    int size;		/* size of intevals array; >= nintervals */
1468    int align;		/* alignment restriction for set */
1469    int offset;		/* where to store set pointer rel. to start of RCAP */
1470    short first, last;	/* if for extension, major opcode interval */
1471} SetInfoRec, *SetInfoPtr;
1472
1473/* These constant are used to index into an array of SetInfoRec. */
1474enum {REQ,	/* set info for requests */
1475      REP,	/* set info for replies */
1476      ERR,	/* set info for errors */
1477      DEV,	/* set info for device events */
1478      DLEV,	/* set info for delivered events */
1479      PREDEFSETS};  /* number of predefined array entries */
1480
1481
1482/* RecordAllocIntervals
1483 *
1484 * Arguments:
1485 *	psi is a pointer to a SetInfoRec whose intervals pointer is NULL.
1486 *	nIntervals is the desired size of the intervals array.
1487 *
1488 * Returns: BadAlloc if a memory allocation error occurred, else Success.
1489 *
1490 * Side Effects:
1491 *	If Success is returned, psi->intervals is a pointer to size
1492 *	RecordSetIntervals, all zeroed, and psi->size is set to size.
1493 */
1494static int
1495RecordAllocIntervals(SetInfoPtr psi, int nIntervals)
1496{
1497    assert(!psi->intervals);
1498    psi->intervals = (RecordSetInterval *)
1499			malloc(nIntervals * sizeof(RecordSetInterval));
1500    if (!psi->intervals)
1501	return BadAlloc;
1502    memset(psi->intervals, 0, nIntervals * sizeof(RecordSetInterval));
1503    psi->size = nIntervals;
1504    return Success;
1505} /* end RecordAllocIntervals */
1506
1507
1508/* RecordConvertRangesToIntervals
1509 *
1510 * Arguments:
1511 *	psi is a pointer to the SetInfoRec we are building.
1512 *	pRanges is an array of xRecordRanges.
1513 *	nRanges is the number of elements in pRanges.
1514 *	byteoffset is the offset from the start of an xRecordRange of the
1515 *	  two bytes (1 for first, 1 for last) we are interested in.
1516 *	pExtSetInfo, if non-NULL, indicates that the two bytes mentioned
1517 *	  above are followed by four bytes (2 for first, 2 for last)
1518 *	  representing a minor opcode range, and this information should be
1519 *	  stored in one of the SetInfoRecs starting at pExtSetInfo.
1520 *	pnExtSetInfo is the number of elements in the pExtSetInfo array.
1521 *
1522 * Returns:  BadAlloc if a memory allocation error occurred, else Success.
1523 *
1524 * Side Effects:
1525 *	The slice of pRanges indicated by byteoffset is stored in psi.
1526 *	If pExtSetInfo is non-NULL, minor opcode intervals are stored
1527 *	in an existing SetInfoRec if the major opcode interval matches, else
1528 *	they are stored in a new SetInfoRec, and *pnExtSetInfo is
1529 *	increased accordingly.
1530 */
1531static int
1532RecordConvertRangesToIntervals(
1533    SetInfoPtr psi,
1534    xRecordRange *pRanges,
1535    int nRanges,
1536    int byteoffset,
1537    SetInfoPtr pExtSetInfo,
1538    int *pnExtSetInfo
1539)
1540{
1541    int i;
1542    CARD8 *pCARD8;
1543    int first, last;
1544    int err;
1545
1546    for (i = 0; i < nRanges; i++, pRanges++)
1547    {
1548	pCARD8 = ((CARD8 *)pRanges) + byteoffset;
1549	first = pCARD8[0];
1550	last  = pCARD8[1];
1551	if (first || last)
1552	{
1553	    if (!psi->intervals)
1554	    {
1555		err = RecordAllocIntervals(psi, 2 * (nRanges - i));
1556		if (err != Success)
1557		    return err;
1558	    }
1559	    psi->intervals[psi->nintervals].first = first;
1560	    psi->intervals[psi->nintervals].last  = last;
1561	    psi->nintervals++;
1562	    assert(psi->nintervals <= psi->size);
1563	    if (pExtSetInfo)
1564	    {
1565		SetInfoPtr pesi = pExtSetInfo;
1566		CARD16 *pCARD16 = (CARD16 *)(pCARD8 + 2);
1567		int j;
1568
1569		for (j = 0; j < *pnExtSetInfo; j++, pesi++)
1570		{
1571		    if ( (first == pesi->first) && (last == pesi->last) )
1572			break;
1573		}
1574		if (j == *pnExtSetInfo)
1575		{
1576		    err = RecordAllocIntervals(pesi, 2 * (nRanges - i));
1577		    if (err != Success)
1578			return err;
1579		    pesi->first = first;
1580		    pesi->last  = last;
1581		    (*pnExtSetInfo)++;
1582		}
1583		pesi->intervals[pesi->nintervals].first = pCARD16[0];
1584		pesi->intervals[pesi->nintervals].last  = pCARD16[1];
1585		pesi->nintervals++;
1586		assert(pesi->nintervals <= pesi->size);
1587	    }
1588	}
1589    }
1590    return Success;
1591}  /* end RecordConvertRangesToIntervals */
1592
1593#define offset_of(_structure, _field) \
1594    ((char *)(& (_structure . _field)) - (char *)(&_structure))
1595
1596/* RecordRegisterClients
1597 *
1598 * Arguments:
1599 *	pContext is the context on which to register the clients.
1600 *	client is the client that issued the RecordCreateContext or
1601 *	  RecordRegisterClients request.
1602 *	stuff is a pointer to the request.
1603 *
1604 * Returns:
1605 *	Any one of several possible error values defined by the protocol.
1606 *	Success if everything is OK.
1607 *
1608 * Side Effects:
1609 *	If different element headers are specified, the context is flushed.
1610 *	If any of the specified clients are already registered on the
1611 *	context, they are first unregistered.  A new RCAP is created to
1612 *	hold the specified protocol and clients, and it is linked onto the
1613 *	context.  If the context is enabled, appropriate hooks are installed
1614 *	to record the new clients and protocol.
1615 */
1616static int
1617RecordRegisterClients(RecordContextPtr pContext, ClientPtr client, xRecordRegisterClientsReq *stuff)
1618{
1619    int err;
1620    int i;
1621    SetInfoPtr si;
1622    int maxSets;
1623    int nExtReqSets = 0;
1624    int nExtRepSets = 0;
1625    int extReqSetsOffset = 0;
1626    int extRepSetsOffset = 0;
1627    SetInfoPtr pExtReqSets, pExtRepSets;
1628    int clientListOffset;
1629    XID *pCanonClients;
1630    int clientStarted = 0, clientDied = 0;
1631    xRecordRange *pRanges, rr;
1632    int nClients;
1633    int sizeClients;
1634    int totRCAPsize;
1635    RecordClientsAndProtocolPtr pRCAP;
1636    int pad;
1637    XID recordingClient;
1638
1639    /* do all sanity checking up front */
1640
1641    err = RecordSanityCheckRegisterClients(pContext, client, stuff);
1642    if (err != Success)
1643	return err;
1644
1645    /* if element headers changed, flush buffer */
1646
1647    if (pContext->elemHeaders != stuff->elementHeader)
1648    {
1649	RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
1650	pContext->elemHeaders = stuff->elementHeader;
1651    }
1652
1653    nClients = stuff->nClients;
1654    if (!nClients)
1655	/* if empty clients list, we're done. */
1656	return Success;
1657
1658    recordingClient = pContext->pRecordingClient ?
1659		      pContext->pRecordingClient->clientAsMask : 0;
1660    pCanonClients = RecordCanonicalizeClientSpecifiers((XID *)&stuff[1],
1661						 &nClients, recordingClient);
1662    if (!pCanonClients)
1663	return BadAlloc;
1664
1665    /* We may have to create as many as one set for each "predefined"
1666     * protocol types, plus one per range for extension reuests, plus one per
1667     * range for extension replies.
1668     */
1669    maxSets = PREDEFSETS + 2 * stuff->nRanges;
1670    si = (SetInfoPtr)malloc(sizeof(SetInfoRec) * maxSets);
1671    if (!si)
1672    {
1673	err = BadAlloc;
1674	goto bailout;
1675    }
1676    memset(si, 0, sizeof(SetInfoRec) * maxSets);
1677
1678    /* theoretically you must do this because NULL may not be all-bits-zero */
1679    for (i = 0; i < maxSets; i++)
1680	si[i].intervals = NULL;
1681
1682    pExtReqSets = si + PREDEFSETS;
1683    pExtRepSets = pExtReqSets + stuff->nRanges;
1684
1685    pRanges = (xRecordRange *)(((XID *)&stuff[1]) + stuff->nClients);
1686
1687    err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
1688			offset_of(rr, coreRequestsFirst), NULL, NULL);
1689    if (err != Success) goto bailout;
1690
1691    err = RecordConvertRangesToIntervals(&si[REQ], pRanges, stuff->nRanges,
1692	   offset_of(rr, extRequestsMajorFirst), pExtReqSets, &nExtReqSets);
1693    if (err != Success) goto bailout;
1694
1695    err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
1696			offset_of(rr, coreRepliesFirst), NULL, NULL);
1697    if (err != Success) goto bailout;
1698
1699    err = RecordConvertRangesToIntervals(&si[REP], pRanges, stuff->nRanges,
1700	   offset_of(rr, extRepliesMajorFirst), pExtRepSets, &nExtRepSets);
1701    if (err != Success) goto bailout;
1702
1703    err = RecordConvertRangesToIntervals(&si[ERR], pRanges, stuff->nRanges,
1704			offset_of(rr, errorsFirst), NULL, NULL);
1705    if (err != Success) goto bailout;
1706
1707    err = RecordConvertRangesToIntervals(&si[DLEV], pRanges, stuff->nRanges,
1708			offset_of(rr, deliveredEventsFirst), NULL, NULL);
1709    if (err != Success) goto bailout;
1710
1711    err = RecordConvertRangesToIntervals(&si[DEV], pRanges, stuff->nRanges,
1712			offset_of(rr, deviceEventsFirst), NULL, NULL);
1713    if (err != Success) goto bailout;
1714
1715    /* collect client-started and client-died */
1716
1717    for (i = 0; i < stuff->nRanges; i++)
1718    {
1719	if (pRanges[i].clientStarted) clientStarted = TRUE;
1720	if (pRanges[i].clientDied)    clientDied    = TRUE;
1721    }
1722
1723    /*  We now have all the information collected to create all the sets,
1724     * and we can compute the total memory required for the RCAP.
1725     */
1726
1727    totRCAPsize = sizeof(RecordClientsAndProtocolRec);
1728
1729    /* leave a little room to grow before forcing a separate allocation */
1730    sizeClients = nClients + CLIENT_ARRAY_GROWTH_INCREMENT;
1731    pad = RecordPadAlign(totRCAPsize, sizeof(XID));
1732    clientListOffset = totRCAPsize + pad;
1733    totRCAPsize += pad + sizeClients * sizeof(XID);
1734
1735    if (nExtReqSets)
1736    {
1737	pad = RecordPadAlign(totRCAPsize, sizeof(RecordSetPtr));
1738	extReqSetsOffset = totRCAPsize + pad;
1739	totRCAPsize += pad + (nExtReqSets + 1) * sizeof(RecordMinorOpRec);
1740    }
1741    if (nExtRepSets)
1742    {
1743	pad = RecordPadAlign(totRCAPsize, sizeof(RecordSetPtr));
1744	extRepSetsOffset = totRCAPsize + pad;
1745	totRCAPsize += pad + (nExtRepSets + 1) * sizeof(RecordMinorOpRec);
1746    }
1747
1748    for (i = 0; i < maxSets; i++)
1749    {
1750	if (si[i].nintervals)
1751	{
1752	    si[i].size = RecordSetMemoryRequirements(
1753				si[i].intervals, si[i].nintervals, &si[i].align);
1754	    pad = RecordPadAlign(totRCAPsize, si[i].align);
1755	    si[i].offset = pad + totRCAPsize;
1756	    totRCAPsize += pad + si[i].size;
1757	}
1758    }
1759
1760    /* allocate memory for the whole RCAP */
1761
1762    pRCAP = (RecordClientsAndProtocolPtr)malloc(totRCAPsize);
1763    if (!pRCAP)
1764    {
1765	err = BadAlloc;
1766	goto bailout;
1767    }
1768
1769    /* fill in the RCAP */
1770
1771    pRCAP->pContext = pContext;
1772    pRCAP->pClientIDs = (XID *)((char *)pRCAP + clientListOffset);
1773    pRCAP->numClients  = nClients;
1774    pRCAP->sizeClients = sizeClients;
1775    pRCAP->clientIDsSeparatelyAllocated = 0;
1776    for (i = 0; i < nClients; i++)
1777    {
1778	RecordDeleteClientFromContext(pContext, pCanonClients[i]);
1779	pRCAP->pClientIDs[i] = pCanonClients[i];
1780    }
1781
1782    /* create all the sets */
1783
1784    if (si[REQ].intervals)
1785    {
1786	pRCAP->pRequestMajorOpSet =
1787	    RecordCreateSet(si[REQ].intervals, si[REQ].nintervals,
1788		(RecordSetPtr)((char *)pRCAP + si[REQ].offset), si[REQ].size);
1789    }
1790    else pRCAP->pRequestMajorOpSet = NULL;
1791
1792    if (si[REP].intervals)
1793    {
1794	pRCAP->pReplyMajorOpSet =
1795	    RecordCreateSet(si[REP].intervals, si[REP].nintervals,
1796		(RecordSetPtr)((char *)pRCAP + si[REP].offset), si[REP].size);
1797    }
1798    else pRCAP->pReplyMajorOpSet = NULL;
1799
1800    if (si[ERR].intervals)
1801    {
1802	pRCAP->pErrorSet =
1803	    RecordCreateSet(si[ERR].intervals, si[ERR].nintervals,
1804		(RecordSetPtr)((char *)pRCAP + si[ERR].offset), si[ERR].size);
1805    }
1806    else pRCAP->pErrorSet = NULL;
1807
1808    if (si[DEV].intervals)
1809    {
1810	pRCAP->pDeviceEventSet =
1811	    RecordCreateSet(si[DEV].intervals, si[DEV].nintervals,
1812		(RecordSetPtr)((char *)pRCAP + si[DEV].offset), si[DEV].size);
1813    }
1814    else pRCAP->pDeviceEventSet = NULL;
1815
1816    if (si[DLEV].intervals)
1817    {
1818	pRCAP->pDeliveredEventSet =
1819	    RecordCreateSet(si[DLEV].intervals, si[DLEV].nintervals,
1820	      (RecordSetPtr)((char *)pRCAP + si[DLEV].offset), si[DLEV].size);
1821    }
1822    else pRCAP->pDeliveredEventSet = NULL;
1823
1824    if (nExtReqSets)
1825    {
1826	pRCAP->pRequestMinOpInfo = (RecordMinorOpPtr)
1827					((char *)pRCAP + extReqSetsOffset);
1828	pRCAP->pRequestMinOpInfo[0].count = nExtReqSets;
1829	for (i = 0; i < nExtReqSets; i++, pExtReqSets++)
1830	{
1831	    pRCAP->pRequestMinOpInfo[i+1].major.first = pExtReqSets->first;
1832	    pRCAP->pRequestMinOpInfo[i+1].major.last  = pExtReqSets->last;
1833	    pRCAP->pRequestMinOpInfo[i+1].major.pMinOpSet =
1834		RecordCreateSet(pExtReqSets->intervals,
1835				pExtReqSets->nintervals,
1836		  (RecordSetPtr)((char *)pRCAP + pExtReqSets->offset),
1837				pExtReqSets->size);
1838	}
1839    }
1840    else pRCAP->pRequestMinOpInfo = NULL;
1841
1842    if (nExtRepSets)
1843    {
1844	pRCAP->pReplyMinOpInfo = (RecordMinorOpPtr)
1845					((char *)pRCAP + extRepSetsOffset);
1846	pRCAP->pReplyMinOpInfo[0].count = nExtRepSets;
1847	for (i = 0; i < nExtRepSets; i++, pExtRepSets++)
1848	{
1849	    pRCAP->pReplyMinOpInfo[i+1].major.first = pExtRepSets->first;
1850	    pRCAP->pReplyMinOpInfo[i+1].major.last  = pExtRepSets->last;
1851	    pRCAP->pReplyMinOpInfo[i+1].major.pMinOpSet =
1852		RecordCreateSet(pExtRepSets->intervals,
1853				pExtRepSets->nintervals,
1854		  (RecordSetPtr)((char *)pRCAP + pExtRepSets->offset),
1855				pExtRepSets->size);
1856	}
1857    }
1858    else pRCAP->pReplyMinOpInfo = NULL;
1859
1860    pRCAP->clientStarted = clientStarted;
1861    pRCAP->clientDied    = clientDied;
1862
1863    /* link the RCAP onto the context */
1864
1865    pRCAP->pNextRCAP = pContext->pListOfRCAP;
1866    pContext->pListOfRCAP = pRCAP;
1867
1868    if (pContext->pRecordingClient) /* context enabled */
1869	RecordInstallHooks(pRCAP, 0);
1870
1871bailout:
1872    if (si)
1873    {
1874	for (i = 0; i < maxSets; i++)
1875	    free(si[i].intervals);
1876	free(si);
1877    }
1878    if (pCanonClients && pCanonClients != (XID *)&stuff[1])
1879	free(pCanonClients);
1880    return err;
1881} /* RecordRegisterClients */
1882
1883
1884/* Proc functions all take a client argument, execute the request in
1885 * client->requestBuffer, and return a protocol error status.
1886 */
1887
1888static int
1889ProcRecordQueryVersion(ClientPtr client)
1890{
1891    /* REQUEST(xRecordQueryVersionReq); */
1892    xRecordQueryVersionReply 	rep;
1893    int 		n;
1894
1895    REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
1896    rep.type        	= X_Reply;
1897    rep.sequenceNumber 	= client->sequence;
1898    rep.length         	= 0;
1899    rep.majorVersion  	= SERVER_RECORD_MAJOR_VERSION;
1900    rep.minorVersion  	= SERVER_RECORD_MINOR_VERSION;
1901    if(client->swapped)
1902    {
1903    	swaps(&rep.sequenceNumber, n);
1904	swaps(&rep.majorVersion, n);
1905	swaps(&rep.minorVersion, n);
1906    }
1907    (void)WriteToClient(client, sizeof(xRecordQueryVersionReply),
1908			(char *)&rep);
1909    return Success;
1910} /* ProcRecordQueryVersion */
1911
1912
1913static int
1914ProcRecordCreateContext(ClientPtr client)
1915{
1916    REQUEST(xRecordCreateContextReq);
1917    RecordContextPtr pContext;
1918    RecordContextPtr *ppNewAllContexts = NULL;
1919    int err = BadAlloc;
1920
1921    REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
1922    LEGAL_NEW_RESOURCE(stuff->context, client);
1923
1924    pContext = (RecordContextPtr)malloc(sizeof(RecordContextRec));
1925    if (!pContext)
1926	goto bailout;
1927
1928    /* make sure there is room in ppAllContexts to store the new context */
1929
1930    ppNewAllContexts = (RecordContextPtr *)
1931	realloc(ppAllContexts, sizeof(RecordContextPtr) * (numContexts + 1));
1932    if (!ppNewAllContexts)
1933	goto bailout;
1934    ppAllContexts = ppNewAllContexts;
1935
1936    pContext->id = stuff->context;
1937    pContext->pRecordingClient = NULL;
1938    pContext->pListOfRCAP = NULL;
1939    pContext->elemHeaders = 0;
1940    pContext->bufCategory = 0;
1941    pContext->numBufBytes = 0;
1942    pContext->pBufClient = NULL;
1943    pContext->continuedReply = 0;
1944    pContext->inFlush = 0;
1945
1946    err = RecordRegisterClients(pContext, client,
1947				(xRecordRegisterClientsReq *)stuff);
1948    if (err != Success)
1949	goto bailout;
1950
1951    if (AddResource(pContext->id, RTContext, pContext))
1952    {
1953	ppAllContexts[numContexts++] = pContext;
1954	return Success;
1955    }
1956    else
1957    {
1958	RecordDeleteContext((pointer)pContext, pContext->id);
1959	return BadAlloc;
1960    }
1961bailout:
1962    free(pContext);
1963    return err;
1964} /* ProcRecordCreateContext */
1965
1966
1967static int
1968ProcRecordRegisterClients(ClientPtr client)
1969{
1970    RecordContextPtr pContext;
1971    REQUEST(xRecordRegisterClientsReq);
1972
1973    REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
1974    VERIFY_CONTEXT(pContext, stuff->context, client);
1975
1976    return RecordRegisterClients(pContext, client, stuff);
1977} /* ProcRecordRegisterClients */
1978
1979
1980static int
1981ProcRecordUnregisterClients(ClientPtr client)
1982{
1983    RecordContextPtr pContext;
1984    int err;
1985    REQUEST(xRecordUnregisterClientsReq);
1986    XID *pCanonClients;
1987    int nClients;
1988    int i;
1989
1990    REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
1991    if ((client->req_len << 2) - SIZEOF(xRecordUnregisterClientsReq) !=
1992	4 * stuff->nClients)
1993	return BadLength;
1994    VERIFY_CONTEXT(pContext, stuff->context, client);
1995    err = RecordSanityCheckClientSpecifiers(client, (XID *)&stuff[1],
1996					    stuff->nClients, 0);
1997    if (err != Success)
1998	return err;
1999
2000    nClients = stuff->nClients;
2001    pCanonClients = RecordCanonicalizeClientSpecifiers((XID *)&stuff[1],
2002						 &nClients, 0);
2003    if (!pCanonClients)
2004	return BadAlloc;
2005
2006    for (i = 0; i < nClients; i++)
2007    {
2008	RecordDeleteClientFromContext(pContext, pCanonClients[i]);
2009    }
2010    if (pCanonClients != (XID *)&stuff[1])
2011	free(pCanonClients);
2012    return Success;
2013} /* ProcRecordUnregisterClients */
2014
2015
2016/****************************************************************************/
2017
2018/* stuff for GetContext */
2019
2020/* This is a tactical structure used to hold the xRecordRanges as they are
2021 * being reconstituted from the sets in the RCAPs.
2022 */
2023
2024typedef struct {
2025    xRecordRange *pRanges;  /* array of xRecordRanges for one RCAP */
2026    int size;		/* number of elements in pRanges, >= nRanges */
2027    int nRanges;	/* number of occupied element of pRanges */
2028} GetContextRangeInfoRec, *GetContextRangeInfoPtr;
2029
2030
2031/* RecordAllocRanges
2032 *
2033 * Arguments:
2034 *	pri is a pointer to a GetContextRangeInfoRec to allocate for.
2035 *	nRanges is the number of xRecordRanges desired for pri.
2036 *
2037 * Returns: BadAlloc if a memory allocation error occurred, else Success.
2038 *
2039 * Side Effects:
2040 *	If Success is returned, pri->pRanges points to at least nRanges
2041 *	ranges.  pri->nRanges is set to nRanges.  pri->size is the actual
2042 *	number of ranges.  Newly allocated ranges are zeroed.
2043 */
2044static int
2045RecordAllocRanges(GetContextRangeInfoPtr pri, int nRanges)
2046{
2047    int newsize;
2048    xRecordRange *pNewRange;
2049#define SZINCR 8
2050
2051    newsize = max(pri->size + SZINCR, nRanges);
2052    pNewRange = (xRecordRange *)realloc(pri->pRanges,
2053			 newsize * sizeof(xRecordRange));
2054    if (!pNewRange)
2055	return BadAlloc;
2056
2057    pri->pRanges = pNewRange;
2058    pri->size = newsize;
2059    memset(&pri->pRanges[pri->size - SZINCR], 0, SZINCR * sizeof(xRecordRange));
2060    if (pri->nRanges < nRanges)
2061	pri->nRanges = nRanges;
2062    return Success;
2063} /* RecordAllocRanges */
2064
2065
2066/* RecordConvertSetToRanges
2067 *
2068 * Arguments:
2069 *	pSet is the set to be converted.
2070 *	pri is where the result should be stored.
2071 *	byteoffset is the offset from the start of an xRecordRange of the
2072 *	  two vales (first, last) we are interested in.
2073 *	card8 is TRUE if the vales are one byte each and FALSE if two bytes
2074 *	  each.
2075 *	imax is the largest set value to store in pri->pRanges.
2076 *	pStartIndex, if non-NULL, is the index of the first range in
2077 *	  pri->pRanges that should be stored to.  If NULL,
2078 *	  start at index 0.
2079 *
2080 * Returns: BadAlloc if a memory allocation error occurred, else Success.
2081 *
2082 * Side Effects:
2083 *	If Success is returned, the slice of pri->pRanges indicated by
2084 *	byteoffset and card8 is filled in with the intervals from pSet.
2085 *	if pStartIndex was non-NULL, *pStartIndex is filled in with one
2086 *	more than the index of the last xRecordRange that was touched.
2087 */
2088static int
2089RecordConvertSetToRanges(
2090    RecordSetPtr pSet,
2091    GetContextRangeInfoPtr pri,
2092    int byteoffset,
2093    Bool card8,
2094    unsigned int imax,
2095    int *pStartIndex
2096)
2097{
2098    int nRanges;
2099    RecordSetIteratePtr pIter = NULL;
2100    RecordSetInterval interval;
2101    CARD8 *pCARD8;
2102    CARD16 *pCARD16;
2103    int err;
2104
2105    if (!pSet)
2106	return Success;
2107
2108    nRanges = pStartIndex ? *pStartIndex : 0;
2109    while ((pIter = RecordIterateSet(pSet, pIter, &interval)))
2110    {
2111	if (interval.first > imax) break;
2112	if (interval.last  > imax) interval.last = imax;
2113	nRanges++;
2114	if (nRanges > pri->size)
2115	{
2116	    err = RecordAllocRanges(pri, nRanges);
2117	    if (err != Success)
2118		return err;
2119	}
2120	else
2121	    pri->nRanges = max(pri->nRanges, nRanges);
2122	if (card8)
2123	{
2124	    pCARD8 = ((CARD8 *)&pri->pRanges[nRanges-1]) + byteoffset;
2125	    *pCARD8++ = interval.first;
2126	    *pCARD8   = interval.last;
2127	}
2128	else
2129	{
2130	    pCARD16 = (CARD16 *)
2131			(((char *)&pri->pRanges[nRanges-1]) + byteoffset);
2132	    *pCARD16++ = interval.first;
2133	    *pCARD16   = interval.last;
2134	}
2135    }
2136    if (pStartIndex)
2137	*pStartIndex = nRanges;
2138    return Success;
2139} /* RecordConvertSetToRanges */
2140
2141
2142/* RecordConvertMinorOpInfoToRanges
2143 *
2144 * Arguments:
2145 *	pMinOpInfo is the minor opcode info to convert to xRecordRanges.
2146 *	pri is where the result should be stored.
2147 *	byteoffset is the offset from the start of an xRecordRange of the
2148 *	  four vales (CARD8 major_first, CARD8 major_last,
2149 *	  CARD16 minor_first, CARD16 minor_last) we are going to store.
2150 *
2151 * Returns: BadAlloc if a memory allocation error occurred, else Success.
2152 *
2153 * Side Effects:
2154 *	If Success is returned, the slice of pri->pRanges indicated by
2155 *	byteoffset is filled in with the information from pMinOpInfo.
2156 */
2157static int
2158RecordConvertMinorOpInfoToRanges(
2159    RecordMinorOpPtr pMinOpInfo,
2160    GetContextRangeInfoPtr pri,
2161    int byteoffset
2162)
2163{
2164    int nsets;
2165    int start;
2166    int i;
2167    int err;
2168
2169    if (!pMinOpInfo)
2170	return Success;
2171
2172    nsets = pMinOpInfo->count;
2173    pMinOpInfo++;
2174    start = 0;
2175    for (i = 0; i < nsets; i++)
2176    {
2177	int j, s;
2178	s = start;
2179	err = RecordConvertSetToRanges(pMinOpInfo[i].major.pMinOpSet, pri,
2180				byteoffset + 2, FALSE, 65535, &start);
2181	if (err != Success) return err;
2182	for (j = s; j < start; j++)
2183	{
2184	    CARD8 *pCARD8 = ((CARD8 *)&pri->pRanges[j]) + byteoffset;
2185	    *pCARD8++ = pMinOpInfo[i].major.first;
2186	    *pCARD8   = pMinOpInfo[i].major.last;
2187	}
2188    }
2189    return Success;
2190} /* RecordConvertMinorOpInfoToRanges */
2191
2192
2193/* RecordSwapRanges
2194 *
2195 * Arguments:
2196 *	pRanges is an array of xRecordRanges.
2197 *	nRanges is the number of elements in pRanges.
2198 *
2199 * Returns: nothing.
2200 *
2201 * Side Effects:
2202 *	The 16 bit fields of each xRecordRange are byte swapped.
2203 */
2204static void
2205RecordSwapRanges(xRecordRange *pRanges, int nRanges)
2206{
2207    int i;
2208    register char n;
2209    for (i = 0; i < nRanges; i++, pRanges++)
2210    {
2211	swaps(&pRanges->extRequestsMinorFirst, n);
2212	swaps(&pRanges->extRequestsMinorLast, n);
2213	swaps(&pRanges->extRepliesMinorFirst, n);
2214	swaps(&pRanges->extRepliesMinorLast, n);
2215    }
2216} /* RecordSwapRanges */
2217
2218
2219static int
2220ProcRecordGetContext(ClientPtr client)
2221{
2222    RecordContextPtr pContext;
2223    REQUEST(xRecordGetContextReq);
2224    xRecordGetContextReply rep;
2225    int n;
2226    RecordClientsAndProtocolPtr pRCAP;
2227    int nRCAPs = 0;
2228    GetContextRangeInfoPtr pRangeInfo;
2229    GetContextRangeInfoPtr pri;
2230    int i;
2231    int err;
2232
2233    REQUEST_SIZE_MATCH(xRecordGetContextReq);
2234    VERIFY_CONTEXT(pContext, stuff->context, client);
2235
2236    /* how many RCAPs are there on this context? */
2237
2238    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
2239	nRCAPs++;
2240
2241    /* allocate and initialize space for record range info */
2242
2243    pRangeInfo = (GetContextRangeInfoPtr)malloc(
2244				nRCAPs * sizeof(GetContextRangeInfoRec));
2245    if (!pRangeInfo && nRCAPs > 0)
2246	return BadAlloc;
2247    for (i = 0; i < nRCAPs; i++)
2248    {
2249	pRangeInfo[i].pRanges = NULL;
2250	pRangeInfo[i].size = 0;
2251	pRangeInfo[i].nRanges = 0;
2252    }
2253
2254    /* convert the RCAP (internal) representation of the recorded protocol
2255     * to the wire protocol (external) representation, storing the information
2256     * for the ith RCAP in pri[i]
2257     */
2258
2259    for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
2260	 pRCAP;
2261	 pRCAP = pRCAP->pNextRCAP, pri++)
2262    {
2263	xRecordRange rr;
2264
2265	err = RecordConvertSetToRanges(pRCAP->pRequestMajorOpSet, pri,
2266			offset_of(rr, coreRequestsFirst), TRUE, 127, NULL);
2267	if (err != Success) goto bailout;
2268
2269	err = RecordConvertSetToRanges(pRCAP->pReplyMajorOpSet, pri,
2270			offset_of(rr, coreRepliesFirst), TRUE, 127, NULL);
2271	if (err != Success) goto bailout;
2272
2273	err = RecordConvertSetToRanges(pRCAP->pDeliveredEventSet, pri,
2274			offset_of(rr, deliveredEventsFirst), TRUE, 255, NULL);
2275	if (err != Success) goto bailout;
2276
2277	err = RecordConvertSetToRanges(pRCAP->pDeviceEventSet, pri,
2278			offset_of(rr, deviceEventsFirst), TRUE, 255, NULL);
2279	if (err != Success) goto bailout;
2280
2281	err = RecordConvertSetToRanges(pRCAP->pErrorSet, pri,
2282			      offset_of(rr, errorsFirst), TRUE, 255, NULL);
2283	if (err != Success) goto bailout;
2284
2285	err = RecordConvertMinorOpInfoToRanges(pRCAP->pRequestMinOpInfo,
2286				pri, offset_of(rr, extRequestsMajorFirst));
2287	if (err != Success) goto bailout;
2288
2289	err = RecordConvertMinorOpInfoToRanges(pRCAP->pReplyMinOpInfo,
2290				pri, offset_of(rr, extRepliesMajorFirst));
2291	if (err != Success) goto bailout;
2292
2293	if (pRCAP->clientStarted || pRCAP->clientDied)
2294	{
2295	    if (pri->nRanges == 0)
2296		RecordAllocRanges(pri, 1);
2297	    pri->pRanges[0].clientStarted = pRCAP->clientStarted;
2298	    pri->pRanges[0].clientDied    = pRCAP->clientDied;
2299	}
2300    }
2301
2302    /* calculate number of clients and reply length */
2303
2304    rep.nClients = 0;
2305    rep.length = 0;
2306    for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
2307	 pRCAP;
2308	 pRCAP = pRCAP->pNextRCAP, pri++)
2309    {
2310	rep.nClients += pRCAP->numClients;
2311	rep.length += pRCAP->numClients *
2312		( bytes_to_int32(sizeof(xRecordClientInfo)) +
2313		  pri->nRanges * bytes_to_int32(sizeof(xRecordRange)));
2314    }
2315
2316    /* write the reply header */
2317
2318    rep.type = X_Reply;
2319    rep.sequenceNumber 	= client->sequence;
2320    rep.enabled = pContext->pRecordingClient != NULL;
2321    rep.elementHeader = pContext->elemHeaders;
2322    if(client->swapped)
2323    {
2324    	swaps(&rep.sequenceNumber, n);
2325    	swapl(&rep.length, n);
2326    	swapl(&rep.nClients, n);
2327    }
2328    (void)WriteToClient(client, sizeof(xRecordGetContextReply),
2329			(char *)&rep);
2330
2331    /* write all the CLIENT_INFOs */
2332
2333    for (pRCAP = pContext->pListOfRCAP, pri = pRangeInfo;
2334	 pRCAP;
2335	 pRCAP = pRCAP->pNextRCAP, pri++)
2336    {
2337	xRecordClientInfo rci;
2338	rci.nRanges = pri->nRanges;
2339	if (client->swapped)
2340	{
2341	    swapl(&rci.nRanges, n);
2342	    RecordSwapRanges(pri->pRanges, pri->nRanges);
2343	}
2344	for (i = 0; i < pRCAP->numClients; i++)
2345	{
2346	    rci.clientResource = pRCAP->pClientIDs[i];
2347	    if (client->swapped) swapl(&rci.clientResource, n);
2348	    WriteToClient(client, sizeof(xRecordClientInfo), (char *)&rci);
2349	    WriteToClient(client, sizeof(xRecordRange) * pri->nRanges,
2350			  (char *)pri->pRanges);
2351	}
2352    }
2353    err = Success;
2354
2355bailout:
2356    for (i = 0; i < nRCAPs; i++)
2357    {
2358	free(pRangeInfo[i].pRanges);
2359    }
2360    free(pRangeInfo);
2361    return err;
2362} /* ProcRecordGetContext */
2363
2364
2365static int
2366ProcRecordEnableContext(ClientPtr client)
2367{
2368    RecordContextPtr pContext;
2369    REQUEST(xRecordEnableContextReq);
2370    int i;
2371    RecordClientsAndProtocolPtr pRCAP;
2372
2373    REQUEST_SIZE_MATCH(xRecordGetContextReq);
2374    VERIFY_CONTEXT(pContext, stuff->context, client);
2375    if (pContext->pRecordingClient)
2376	return BadMatch; /* already enabled */
2377
2378    /* install record hooks for each RCAP */
2379
2380    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
2381    {
2382	int err = RecordInstallHooks(pRCAP, 0);
2383	if (err != Success)
2384	{ /* undo the previous installs */
2385	    RecordClientsAndProtocolPtr pUninstallRCAP;
2386	    for (pUninstallRCAP = pContext->pListOfRCAP;
2387		 pUninstallRCAP != pRCAP;
2388		 pUninstallRCAP = pUninstallRCAP->pNextRCAP)
2389	    {
2390		RecordUninstallHooks(pUninstallRCAP, 0);
2391	    }
2392	    return err;
2393	}
2394    }
2395
2396    /* Disallow further request processing on this connection until
2397     * the context is disabled.
2398     */
2399    IgnoreClient(client);
2400    pContext->pRecordingClient = client;
2401
2402    /* Don't allow the data connection to record itself; unregister it. */
2403    RecordDeleteClientFromContext(pContext,
2404				  pContext->pRecordingClient->clientAsMask);
2405
2406    /* move the newly enabled context to the front part of ppAllContexts,
2407     * where all the enabled contexts are
2408     */
2409    i = RecordFindContextOnAllContexts(pContext);
2410    assert(i >= numEnabledContexts);
2411    if (i != numEnabledContexts)
2412    {
2413	ppAllContexts[i] = ppAllContexts[numEnabledContexts];
2414	ppAllContexts[numEnabledContexts] = pContext;
2415    }
2416
2417    ++numEnabledContexts;
2418    assert(numEnabledContexts > 0);
2419
2420    /* send StartOfData */
2421    RecordAProtocolElement(pContext, NULL, XRecordStartOfData, NULL, 0, 0);
2422    RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
2423    return Success;
2424} /* ProcRecordEnableContext */
2425
2426
2427/* RecordDisableContext
2428 *
2429 * Arguments:
2430 *	pContext is the context to disable.
2431 *	nRanges is the number of elements in pRanges.
2432 *
2433 * Returns: nothing.
2434 *
2435 * Side Effects:
2436 *	If the context was enabled, it is disabled.  An EndOfData
2437 *	message is sent to the recording client.  Recording hooks for
2438 *	this context are uninstalled.  The context is moved to the
2439 *	rear part of the ppAllContexts array.  numEnabledContexts is
2440 *	decremented.  Request processing for the formerly recording client
2441 *	is resumed.
2442 */
2443static void
2444RecordDisableContext(RecordContextPtr pContext)
2445{
2446    RecordClientsAndProtocolPtr pRCAP;
2447    int i;
2448
2449    if (!pContext->pRecordingClient) return;
2450    if (!pContext->pRecordingClient->clientGone)
2451    {
2452	RecordAProtocolElement(pContext, NULL, XRecordEndOfData, NULL, 0, 0);
2453	RecordFlushReplyBuffer(pContext, NULL, 0, NULL, 0);
2454	/* Re-enable request processing on this connection. */
2455	AttendClient(pContext->pRecordingClient);
2456    }
2457
2458    for (pRCAP = pContext->pListOfRCAP; pRCAP; pRCAP = pRCAP->pNextRCAP)
2459    {
2460	RecordUninstallHooks(pRCAP, 0);
2461    }
2462
2463    pContext->pRecordingClient = NULL;
2464
2465    /* move the newly disabled context to the rear part of ppAllContexts,
2466     * where all the disabled contexts are
2467     */
2468    i = RecordFindContextOnAllContexts(pContext);
2469    assert( (i != -1) && (i < numEnabledContexts) );
2470    if (i != (numEnabledContexts - 1) )
2471    {
2472	ppAllContexts[i] = ppAllContexts[numEnabledContexts-1];
2473	ppAllContexts[numEnabledContexts-1] = pContext;
2474    }
2475    --numEnabledContexts;
2476    assert(numEnabledContexts >= 0);
2477} /* RecordDisableContext */
2478
2479
2480static int
2481ProcRecordDisableContext(ClientPtr client)
2482{
2483    RecordContextPtr pContext;
2484    REQUEST(xRecordDisableContextReq);
2485
2486    REQUEST_SIZE_MATCH(xRecordDisableContextReq);
2487    VERIFY_CONTEXT(pContext, stuff->context, client);
2488    RecordDisableContext(pContext);
2489    return Success;
2490} /* ProcRecordDisableContext */
2491
2492
2493/* RecordDeleteContext
2494 *
2495 * Arguments:
2496 *	value is the context to delete.
2497 *	id is its resource ID.
2498 *
2499 * Returns: Success.
2500 *
2501 * Side Effects:
2502 *	Disables the context, frees all associated memory, and removes
2503 *	it from the ppAllContexts array.
2504 */
2505static int
2506RecordDeleteContext(pointer value, XID id)
2507{
2508    int i;
2509    RecordContextPtr pContext = (RecordContextPtr)value;
2510    RecordClientsAndProtocolPtr pRCAP;
2511
2512    RecordDisableContext(pContext);
2513
2514    /*  Remove all the clients from all the RCAPs.
2515     *  As a result, the RCAPs will be freed.
2516     */
2517
2518    while ((pRCAP = pContext->pListOfRCAP))
2519    {
2520	int numClients = pRCAP->numClients;
2521	/* when the last client is deleted, the RCAP will go away. */
2522	while(numClients--)
2523	{
2524	    RecordDeleteClientFromRCAP(pRCAP, numClients);
2525	}
2526    }
2527
2528    /* remove context from AllContexts list */
2529
2530    if (-1 != (i = RecordFindContextOnAllContexts(pContext)))
2531    {
2532	ppAllContexts[i] = ppAllContexts[numContexts - 1];
2533	if (--numContexts == 0)
2534	{
2535	    free(ppAllContexts);
2536	    ppAllContexts = NULL;
2537	}
2538    }
2539    free(pContext);
2540
2541    return Success;
2542} /* RecordDeleteContext */
2543
2544
2545static int
2546ProcRecordFreeContext(ClientPtr client)
2547{
2548    RecordContextPtr pContext;
2549    REQUEST(xRecordFreeContextReq);
2550
2551    REQUEST_SIZE_MATCH(xRecordFreeContextReq);
2552    VERIFY_CONTEXT(pContext, stuff->context, client);
2553    FreeResource(stuff->context, RT_NONE);
2554    return Success;
2555} /* ProcRecordFreeContext */
2556
2557
2558static int
2559ProcRecordDispatch(ClientPtr client)
2560{
2561    REQUEST(xReq);
2562
2563    switch (stuff->data)
2564    {
2565	case X_RecordQueryVersion:
2566	    return ProcRecordQueryVersion(client);
2567	case X_RecordCreateContext:
2568	    return ProcRecordCreateContext(client);
2569	case X_RecordRegisterClients:
2570	    return ProcRecordRegisterClients(client);
2571	case X_RecordUnregisterClients:
2572	    return ProcRecordUnregisterClients(client);
2573	case X_RecordGetContext:
2574	    return ProcRecordGetContext(client);
2575	case X_RecordEnableContext:
2576	    return ProcRecordEnableContext(client);
2577	case X_RecordDisableContext:
2578	    return ProcRecordDisableContext(client);
2579	case X_RecordFreeContext:
2580	    return ProcRecordFreeContext(client);
2581       default:
2582	    return BadRequest;
2583    }
2584} /* ProcRecordDispatch */
2585
2586
2587static int
2588SProcRecordQueryVersion(ClientPtr client)
2589{
2590    REQUEST(xRecordQueryVersionReq);
2591    register char 	n;
2592
2593    swaps(&stuff->length, n);
2594    REQUEST_SIZE_MATCH(xRecordQueryVersionReq);
2595    swaps(&stuff->majorVersion, n);
2596    swaps(&stuff->minorVersion,n);
2597    return ProcRecordQueryVersion(client);
2598} /* SProcRecordQueryVersion */
2599
2600
2601static int
2602SwapCreateRegister(xRecordRegisterClientsReq *stuff)
2603{
2604    register char n;
2605    int i;
2606    XID *pClientID;
2607
2608    swapl(&stuff->context, n);
2609    swapl(&stuff->nClients, n);
2610    swapl(&stuff->nRanges, n);
2611    pClientID = (XID *)&stuff[1];
2612    if (stuff->nClients > stuff->length - bytes_to_int32(sz_xRecordRegisterClientsReq))
2613	return BadLength;
2614    for (i = 0; i < stuff->nClients; i++, pClientID++)
2615    {
2616	swapl(pClientID, n);
2617    }
2618    if (stuff->nRanges > stuff->length - bytes_to_int32(sz_xRecordRegisterClientsReq)
2619	- stuff->nClients)
2620	return BadLength;
2621    RecordSwapRanges((xRecordRange *)pClientID, stuff->nRanges);
2622    return Success;
2623} /* SwapCreateRegister */
2624
2625
2626static int
2627SProcRecordCreateContext(ClientPtr client)
2628{
2629    REQUEST(xRecordCreateContextReq);
2630    int			status;
2631    register char 	n;
2632
2633    swaps(&stuff->length, n);
2634    REQUEST_AT_LEAST_SIZE(xRecordCreateContextReq);
2635    if ((status = SwapCreateRegister((pointer)stuff)) != Success)
2636	return status;
2637    return ProcRecordCreateContext(client);
2638} /* SProcRecordCreateContext */
2639
2640
2641static int
2642SProcRecordRegisterClients(ClientPtr client)
2643{
2644    REQUEST(xRecordRegisterClientsReq);
2645    int			status;
2646    register char 	n;
2647
2648    swaps(&stuff->length, n);
2649    REQUEST_AT_LEAST_SIZE(xRecordRegisterClientsReq);
2650    if ((status = SwapCreateRegister((pointer)stuff)) != Success)
2651	return status;
2652    return ProcRecordRegisterClients(client);
2653} /* SProcRecordRegisterClients */
2654
2655
2656static int
2657SProcRecordUnregisterClients(ClientPtr client)
2658{
2659    REQUEST(xRecordUnregisterClientsReq);
2660    register char 	n;
2661
2662    swaps(&stuff->length, n);
2663    REQUEST_AT_LEAST_SIZE(xRecordUnregisterClientsReq);
2664    swapl(&stuff->context, n);
2665    swapl(&stuff->nClients, n);
2666    SwapRestL(stuff);
2667    return ProcRecordUnregisterClients(client);
2668} /* SProcRecordUnregisterClients */
2669
2670
2671static int
2672SProcRecordGetContext(ClientPtr client)
2673{
2674    REQUEST(xRecordGetContextReq);
2675    register char 	n;
2676
2677    swaps(&stuff->length, n);
2678    REQUEST_SIZE_MATCH(xRecordGetContextReq);
2679    swapl(&stuff->context, n);
2680    return ProcRecordGetContext(client);
2681} /* SProcRecordGetContext */
2682
2683static int
2684SProcRecordEnableContext(ClientPtr client)
2685{
2686    REQUEST(xRecordEnableContextReq);
2687    register char 	n;
2688
2689    swaps(&stuff->length, n);
2690    REQUEST_SIZE_MATCH(xRecordEnableContextReq);
2691    swapl(&stuff->context, n);
2692    return ProcRecordEnableContext(client);
2693} /* SProcRecordEnableContext */
2694
2695
2696static int
2697SProcRecordDisableContext(ClientPtr client)
2698{
2699    REQUEST(xRecordDisableContextReq);
2700    register char 	n;
2701
2702    swaps(&stuff->length, n);
2703    REQUEST_SIZE_MATCH(xRecordDisableContextReq);
2704    swapl(&stuff->context, n);
2705    return ProcRecordDisableContext(client);
2706} /* SProcRecordDisableContext */
2707
2708
2709static int
2710SProcRecordFreeContext(ClientPtr client)
2711{
2712    REQUEST(xRecordFreeContextReq);
2713    register char 	n;
2714
2715    swaps(&stuff->length, n);
2716    REQUEST_SIZE_MATCH(xRecordFreeContextReq);
2717    swapl(&stuff->context, n);
2718    return ProcRecordFreeContext(client);
2719} /* SProcRecordFreeContext */
2720
2721
2722static int
2723SProcRecordDispatch(ClientPtr client)
2724{
2725    REQUEST(xReq);
2726
2727    switch (stuff->data)
2728    {
2729	case X_RecordQueryVersion:
2730	    return SProcRecordQueryVersion(client);
2731	case X_RecordCreateContext:
2732	    return SProcRecordCreateContext(client);
2733	case X_RecordRegisterClients:
2734	    return SProcRecordRegisterClients(client);
2735	case X_RecordUnregisterClients:
2736	    return SProcRecordUnregisterClients(client);
2737	case X_RecordGetContext:
2738	    return SProcRecordGetContext(client);
2739	case X_RecordEnableContext:
2740	    return SProcRecordEnableContext(client);
2741	case X_RecordDisableContext:
2742	    return SProcRecordDisableContext(client);
2743	case X_RecordFreeContext:
2744	    return SProcRecordFreeContext(client);
2745       default:
2746	    return BadRequest;
2747    }
2748} /* SProcRecordDispatch */
2749
2750/* RecordConnectionSetupInfo
2751 *
2752 * Arguments:
2753 *	pContext is an enabled context that specifies recording of
2754 *	  connection setup info.
2755 *	pci holds the connection setup info.
2756 *
2757 * Returns: nothing.
2758 *
2759 * Side Effects:
2760 *	The connection setup info is sent to the recording client.
2761 */
2762static void
2763RecordConnectionSetupInfo(RecordContextPtr pContext, NewClientInfoRec *pci)
2764{
2765    int prefixsize = SIZEOF(xConnSetupPrefix);
2766    int restsize = pci->prefix->length * 4;
2767
2768    if (pci->client->swapped)
2769    {
2770	char *pConnSetup = (char *)malloc(prefixsize + restsize);
2771	if (!pConnSetup)
2772	    return;
2773	SwapConnSetupPrefix(pci->prefix, (xConnSetupPrefix*)pConnSetup);
2774	SwapConnSetupInfo((char*)pci->setup, (char*)(pConnSetup + prefixsize));
2775	RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
2776			       (pointer)pConnSetup, prefixsize + restsize, 0);
2777	free(pConnSetup);
2778    }
2779    else
2780    {
2781	/* don't alloc and copy as in the swapped case; just send the
2782	 * data in two pieces
2783	 */
2784	RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
2785			(pointer)pci->prefix, prefixsize, restsize);
2786	RecordAProtocolElement(pContext, pci->client, XRecordClientStarted,
2787			(pointer)pci->setup, restsize, /* continuation */ -1);
2788    }
2789} /* RecordConnectionSetupInfo */
2790
2791
2792/* RecordDeleteContext
2793 *
2794 * Arguments:
2795 *	pcbl is &ClientStateCallback.
2796 *	nullata is NULL.
2797 *	calldata is a pointer to a NewClientInfoRec (include/dixstruct.h)
2798 *	which contains information about client state changes.
2799 *
2800 * Returns: nothing.
2801 *
2802 * Side Effects:
2803 *	If a new client has connected and any contexts have specified
2804 *	XRecordFutureClients, the new client is registered on those contexts.
2805 *	If any of those contexts specify recording of the connection setup
2806 *	info, it is recorded.
2807 *
2808 *	If an existing client has disconnected, it is deleted from any
2809 *	contexts that it was registered on.  If any of those contexts
2810 *	specified XRecordClientDied, they record a ClientDied protocol element.
2811 *	If the disconnectiong client happened to be the data connection of an
2812 *	enabled context, the context is disabled.
2813 */
2814
2815static void
2816RecordAClientStateChange(CallbackListPtr *pcbl, pointer nulldata, pointer calldata)
2817{
2818    NewClientInfoRec *pci = (NewClientInfoRec *)calldata;
2819    int i;
2820    ClientPtr pClient = pci->client;
2821    RecordContextPtr *ppAllContextsCopy = NULL;
2822    int numContextsCopy = 0;
2823
2824    switch (pClient->clientState)
2825    {
2826    case ClientStateRunning: /* new client */
2827	for (i = 0; i < numContexts; i++)
2828	{
2829	    RecordClientsAndProtocolPtr pRCAP;
2830	    RecordContextPtr pContext = ppAllContexts[i];
2831
2832	    if ((pRCAP = RecordFindClientOnContext(pContext,
2833					    XRecordFutureClients, NULL)))
2834	    {
2835		RecordAddClientToRCAP(pRCAP, pClient->clientAsMask);
2836		if (pContext->pRecordingClient && pRCAP->clientStarted)
2837		    RecordConnectionSetupInfo(pContext, pci);
2838	    }
2839	}
2840    break;
2841
2842    case ClientStateGone:
2843    case ClientStateRetained: /* client disconnected */
2844
2845        /* RecordDisableContext modifies contents of ppAllContexts. */
2846	numContextsCopy = numContexts;
2847	ppAllContextsCopy = malloc(numContextsCopy * sizeof(RecordContextPtr));
2848	assert(ppAllContextsCopy);
2849	memcpy(ppAllContextsCopy, ppAllContexts, numContextsCopy * sizeof(RecordContextPtr));
2850
2851	for (i = 0; i < numContextsCopy; i++)
2852	{
2853	    RecordClientsAndProtocolPtr pRCAP;
2854	    RecordContextPtr pContext = ppAllContextsCopy[i];
2855	    int pos;
2856
2857	    if (pContext->pRecordingClient == pClient)
2858		RecordDisableContext(pContext);
2859	    if ((pRCAP = RecordFindClientOnContext(pContext,
2860				    pClient->clientAsMask, &pos)))
2861	    {
2862		if (pContext->pRecordingClient && pRCAP->clientDied)
2863		    RecordAProtocolElement(pContext, pClient,
2864					   XRecordClientDied, NULL, 0, 0);
2865		RecordDeleteClientFromRCAP(pRCAP, pos);
2866	    }
2867	}
2868
2869	free(ppAllContextsCopy);
2870    break;
2871
2872    default:
2873    break;
2874    } /* end switch on client state */
2875} /* RecordAClientStateChange */
2876
2877
2878/* RecordCloseDown
2879 *
2880 * Arguments:
2881 *	extEntry is the extension information for RECORD.
2882 *
2883 * Returns: nothing.
2884 *
2885 * Side Effects:
2886 *	Performs any cleanup needed by RECORD at server shutdown time.
2887 *
2888 */
2889static void
2890RecordCloseDown(ExtensionEntry *extEntry)
2891{
2892    DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
2893} /* RecordCloseDown */
2894
2895
2896/* RecordExtensionInit
2897 *
2898 * Arguments: none.
2899 *
2900 * Returns: nothing.
2901 *
2902 * Side Effects:
2903 *	Enables the RECORD extension if possible.
2904 */
2905void
2906RecordExtensionInit(void)
2907{
2908    ExtensionEntry *extentry;
2909
2910    RTContext = CreateNewResourceType(RecordDeleteContext, "RecordContext");
2911    if (!RTContext)
2912	return;
2913
2914    if (!dixRegisterPrivateKey(RecordClientPrivateKey, PRIVATE_CLIENT, 0))
2915        return;
2916
2917    ppAllContexts = NULL;
2918    numContexts = numEnabledContexts = numEnabledRCAPs = 0;
2919
2920    if (!AddCallback(&ClientStateCallback, RecordAClientStateChange, NULL))
2921	return;
2922
2923    extentry = AddExtension(RECORD_NAME, RecordNumEvents, RecordNumErrors,
2924			    ProcRecordDispatch, SProcRecordDispatch,
2925			    RecordCloseDown, StandardMinorOpcode);
2926    if (!extentry)
2927    {
2928	DeleteCallback(&ClientStateCallback, RecordAClientStateChange, NULL);
2929	return;
2930    }
2931    SetResourceTypeErrorValue(RTContext, extentry->errorBase + XRecordBadContext);
2932
2933} /* RecordExtensionInit */
2934
2935