static cl_error_t egg_parse_file_headers(egg_handle* handle, egg_file** file)
{
    cl_error_t status = CL_EPARSE;
    cl_error_t retval;

    egg_file* eggFile       = NULL;
    file_header* fileHeader = NULL;
    uint32_t magic          = 0;
    const uint8_t* index    = 0;

    if (!handle || !file) {
        cli_errmsg("egg_parse_file_headers: Invalid args!\n");
        return CL_EARG;
    }

    *file = NULL;

    if (CL_SUCCESS != EGG_VALIDATE_HANDLE(handle)) {
        cli_errmsg("egg_parse_file_headers: Invalid handle values!\n");
        status = CL_EARG;
        goto done;
    }

    /*
     * 1st:
     *   File headers must start with the file_header.
     */
    index = (const uint8_t*)fmap_need_off_once(handle->map, handle->offset, sizeof(file_header));
    if (!index) {
        cli_dbgmsg("egg_parse_file_headers: File buffer too small to contain file header.\n");
        goto done;
    }

    eggFile = (egg_file*)cli_calloc(1, sizeof(egg_file));
    if (NULL == eggFile) {
        cli_errmsg("egg_parse_file_headers: Failed to allocate memory for egg_file.\n");
        status = CL_EMEM;
        goto done;
    }

    fileHeader    = (file_header*)index;
    eggFile->file = fileHeader;

    if (FILE_HEADER_MAGIC != le32_to_host(fileHeader->magic)) {
        cli_dbgmsg("egg_parse_file_headers: Invalid file header magic: %08x (%s).\n", le32_to_host(fileHeader->magic), getMagicHeaderName(le32_to_host(fileHeader->magic)));
        goto done;
    }

    cli_dbgmsg("egg_parse_file_headers: file_header->magic:       %08x (%s)\n", le32_to_host(fileHeader->magic), getMagicHeaderName(le32_to_host(fileHeader->magic)));
    cli_dbgmsg("egg_parse_file_headers: file_header->file_id:     %08x\n", le32_to_host(fileHeader->file_id));
    cli_dbgmsg("egg_parse_file_headers: file_header->file_length: %016" PRIx64 " (%" PRIu64 ")\n",
               le64_to_host(fileHeader->file_length),
               le64_to_host(fileHeader->file_length));

    if (0 == le16_to_host(fileHeader->file_length)) {
        cli_dbgmsg("egg_parse_file_headers: Empty file!\n");
    }

    handle->offset += sizeof(file_header);

    /*
     * 2nd:
     *   After the file_header, the following extra field headers may be present:
     *      a) filename_header
     *      b) comment_header
     *      c) windows_file_information
     *      d) posix_file_information
     *      e) encrypt_header
     *      f) EOFARC
     */

    while (handle->map->len > handle->offset) {

        /* Get the next magic32_t */
        index = (const uint8_t*)fmap_need_off_once(handle->map, handle->offset, sizeof(magic32_t));
        if (!index) {
            cli_dbgmsg("egg_parse_file_headers: File buffer too small to contain end of archive magic bytes.\n");
            goto done;
        }

        magic = le32_to_host(*((uint32_t*)index));

        if (EOFARC == magic) {
            /*
             * File headers should conclude with EOFARC magic bytes.
             */
            handle->offset += sizeof(magic32_t);

            cli_dbgmsg("egg_parse_file_headers: End of archive headers.\n");
            break; /* Break out of the loop */
        } else {
            /*
             * Parse extra fields.
             */
            retval = egg_parse_file_extra_field(handle, eggFile);
            if (CL_SUCCESS != retval) {
                cli_dbgmsg("egg_parse_file_headers: Failed to parse archive header, magic: %08x (%s)\n", magic, getMagicHeaderName(magic));
                break; /* Break out of the loop */
            }
        }
    }

    *file  = eggFile;
    status = CL_SUCCESS;

done:
    if (CL_SUCCESS != status) {
        if (eggFile) {
            egg_free_egg_file(eggFile);
        }
    }

    return status;
}


int blobGrow(blob *b, size_t len)
{
    assert(b != NULL);
#ifdef CL_DEBUG
    assert(b->magic == BLOBCLASS);
#endif

    if (len == 0)
        return CL_SUCCESS;

    if (b->isClosed) {
        /*
		 * Should be cli_dbgmsg, but I want to see them for now,
		 * and cli_dbgmsg doesn't support debug levels
		 */
        cli_warnmsg("Growing closed blob\n");
        b->isClosed = 0;
    }
    if (b->data == NULL) {
        assert(b->len == 0);
        assert(b->size == 0);

        b->data = cli_malloc(len);
        if (b->data)
            b->size = (off_t)len;
    } else {
        unsigned char *ptr = cli_realloc(b->data, b->size + len);

        if (ptr) {
            b->size += (off_t)len;
            b->data = ptr;
        }
    }

    return (b->data) ? CL_SUCCESS : CL_EMEM;
}


typedef struct __attribute__((packed)) {
    uint64_t last_modified_time; /* "100-Nanosecond Time" since the Windows Epoch (00:00:00 UTC, January 1, 1601) */
    uint8_t attribute;           /* See above attribute #defines */
} windows_file_information;


blobGetFilename(const blob *b)
{
    assert(b != NULL);
#ifdef CL_DEBUG
    assert(b->magic == BLOBCLASS);
#endif

    return b->name;
}


static UINT gdi_SurfaceToSurface(RdpgfxClientContext* context,
                                 const RDPGFX_SURFACE_TO_SURFACE_PDU* surfaceToSurface)
{
	UINT status = ERROR_INTERNAL_ERROR;
	UINT16 index;
	BOOL sameSurface;
	UINT32 nWidth, nHeight;
	const RECTANGLE_16* rectSrc;
	RECTANGLE_16 invalidRect;
	gdiGfxSurface* surfaceSrc;
	gdiGfxSurface* surfaceDst;
	rdpGdi* gdi = (rdpGdi*)context->custom;
	EnterCriticalSection(&context->mux);
	rectSrc = &(surfaceToSurface->rectSrc);
	surfaceSrc = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToSurface->surfaceIdSrc);
	sameSurface =
	    (surfaceToSurface->surfaceIdSrc == surfaceToSurface->surfaceIdDest) ? TRUE : FALSE;

	if (!sameSurface)
		surfaceDst =
		    (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToSurface->surfaceIdDest);
	else
		surfaceDst = surfaceSrc;

	if (!surfaceSrc || !surfaceDst)
		goto fail;

	if (!is_rect_valid(rectSrc, surfaceSrc->width, surfaceSrc->height))
		goto fail;

	nWidth = rectSrc->right - rectSrc->left;
	nHeight = rectSrc->bottom - rectSrc->top;

	for (index = 0; index < surfaceToSurface->destPtsCount; index++)
	{
		const RDPGFX_POINT16* destPt = &surfaceToSurface->destPts[index];
		const RECTANGLE_16 rect = { destPt->x, destPt->y, destPt->x + nWidth, destPt->y + nHeight };
		if (!is_rect_valid(&rect, surfaceDst->width, surfaceDst->height))
			goto fail;

		if (!freerdp_image_copy(surfaceDst->data, surfaceDst->format, surfaceDst->scanline,
		                        destPt->x, destPt->y, nWidth, nHeight, surfaceSrc->data,
		                        surfaceSrc->format, surfaceSrc->scanline, rectSrc->left,
		                        rectSrc->top, NULL, FREERDP_FLIP_NONE))
			goto fail;

		invalidRect.left = destPt->x;
		invalidRect.top = destPt->y;
		invalidRect.right = destPt->x + rectSrc->right;
		invalidRect.bottom = destPt->y + rectSrc->bottom;
		region16_union_rect(&surfaceDst->invalidRegion, &surfaceDst->invalidRegion, &invalidRect);
		status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context,
		                      surfaceDst->surfaceId, 1, &invalidRect);

		if (status != CHANNEL_RC_OK)
			goto fail;
	}

	LeaveCriticalSection(&context->mux);

	if (!gdi->inGfxFrame)
	{
		status = CHANNEL_RC_NOT_INITIALIZED;
		IFCALLRET(context->UpdateSurfaces, status, context);
	}

	return status;
fail:
	LeaveCriticalSection(&context->mux);
	return status;
}


static UINT gdi_CreateSurface(RdpgfxClientContext* context,
                              const RDPGFX_CREATE_SURFACE_PDU* createSurface)
{
	UINT rc = ERROR_INTERNAL_ERROR;
	gdiGfxSurface* surface;
	rdpGdi* gdi = (rdpGdi*)context->custom;
	EnterCriticalSection(&context->mux);
	surface = (gdiGfxSurface*)calloc(1, sizeof(gdiGfxSurface));

	if (!surface)
		goto fail;

	surface->codecs = gdi->context->codecs;

	if (!surface->codecs)
	{
		free(surface);
		goto fail;
	}

	surface->surfaceId = createSurface->surfaceId;
	surface->width = gfx_align_scanline(createSurface->width, 16);
	surface->height = gfx_align_scanline(createSurface->height, 16);
	surface->mappedWidth = createSurface->width;
	surface->mappedHeight = createSurface->height;
	surface->outputTargetWidth = createSurface->width;
	surface->outputTargetHeight = createSurface->height;

	switch (createSurface->pixelFormat)
	{
		case GFX_PIXEL_FORMAT_ARGB_8888:
			surface->format = PIXEL_FORMAT_BGRA32;
			break;

		case GFX_PIXEL_FORMAT_XRGB_8888:
			surface->format = PIXEL_FORMAT_BGRX32;
			break;

		default:
			free(surface);
			goto fail;
	}

	surface->scanline = gfx_align_scanline(surface->width * 4, 16);
	surface->data = (BYTE*)_aligned_malloc(surface->scanline * surface->height, 16);

	if (!surface->data)
	{
		free(surface);
		goto fail;
	}

	surface->outputMapped = FALSE;
	region16_init(&surface->invalidRegion);
	rc = context->SetSurfaceData(context, surface->surfaceId, (void*)surface);
fail:
	LeaveCriticalSection(&context->mux);
	return rc;
}


static UINT gdi_SurfaceCommand_AVC420(rdpGdi* gdi, RdpgfxClientContext* context,
                                      const RDPGFX_SURFACE_COMMAND* cmd)
{
#ifdef WITH_GFX_H264
	INT32 rc;
	UINT status = CHANNEL_RC_OK;
	UINT32 i;
	gdiGfxSurface* surface;
	RDPGFX_H264_METABLOCK* meta;
	RDPGFX_AVC420_BITMAP_STREAM* bs;

	surface = (gdiGfxSurface*)context->GetSurfaceData(context, cmd->surfaceId);

	if (!surface)
	{
		WLog_ERR(TAG, "%s: unable to retrieve surfaceData for surfaceId=%" PRIu32 "", __FUNCTION__,
		         cmd->surfaceId);
		return ERROR_NOT_FOUND;
	}

	if (!surface->h264)
	{
		surface->h264 = h264_context_new(FALSE);

		if (!surface->h264)
		{
			WLog_ERR(TAG, "%s: unable to create h264 context", __FUNCTION__);
			return ERROR_NOT_ENOUGH_MEMORY;
		}

		if (!h264_context_reset(surface->h264, surface->width, surface->height))
			return ERROR_INTERNAL_ERROR;
	}

	if (!surface->h264)
		return ERROR_NOT_SUPPORTED;

	bs = (RDPGFX_AVC420_BITMAP_STREAM*)cmd->extra;

	if (!bs)
		return ERROR_INTERNAL_ERROR;

	meta = &(bs->meta);
	rc = avc420_decompress(surface->h264, bs->data, bs->length, surface->data, surface->format,
	                       surface->scanline, surface->width, surface->height, meta->regionRects,
	                       meta->numRegionRects);

	if (rc < 0)
	{
		WLog_WARN(TAG, "avc420_decompress failure: %" PRId32 ", ignoring update.", rc);
		return CHANNEL_RC_OK;
	}

	for (i = 0; i < meta->numRegionRects; i++)
	{
		region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion),
		                    (RECTANGLE_16*)&(meta->regionRects[i]));
	}

	status = IFCALLRESULT(CHANNEL_RC_OK, context->UpdateSurfaceArea, context, surface->surfaceId,
	                      meta->numRegionRects, meta->regionRects);

	if (status != CHANNEL_RC_OK)
		goto fail;

	if (!gdi->inGfxFrame)
	{
		status = CHANNEL_RC_NOT_INITIALIZED;
		IFCALLRET(context->UpdateSurfaces, status, context);
	}

fail:
	return status;
#else
	return ERROR_NOT_SUPPORTED;
#endif
}


static UINT gdi_UpdateSurfaces(RdpgfxClientContext* context)
{
	UINT16 count;
	UINT16 index;
	UINT status = ERROR_INTERNAL_ERROR;
	gdiGfxSurface* surface;
	UINT16* pSurfaceIds = NULL;
	rdpGdi* gdi = (rdpGdi*)context->custom;

	EnterCriticalSection(&context->mux);
	context->GetSurfaceIds(context, &pSurfaceIds, &count);
	status = CHANNEL_RC_OK;

	for (index = 0; index < count; index++)
	{
		surface = (gdiGfxSurface*)context->GetSurfaceData(context, pSurfaceIds[index]);

		if (!surface)
			continue;

		/* Already handled in UpdateSurfaceArea callbacks */
		if (context->UpdateSurfaceArea)
		{
			if (surface->windowId != 0)
				continue;
		}

		if (!surface->outputMapped)
			continue;

		status = gdi_OutputUpdate(gdi, surface);

		if (status != CHANNEL_RC_OK)
			break;
	}

	free(pSurfaceIds);
	LeaveCriticalSection(&context->mux);
	return status;
}


static BOOL gdi_apply_alpha(BYTE* data, UINT32 format, UINT32 stride, RECTANGLE_16* rect,
                            UINT32 startOffsetX, UINT32 count, BYTE a)
{
	UINT32 y;
	UINT32 written = 0;
	BOOL first = TRUE;
	const UINT32 bpp = GetBytesPerPixel(format);

	for (y = rect->top; y < rect->bottom; y++)
	{
		UINT32 x;
		BYTE* line = &data[stride * y];

		for (x = first ? rect->left + startOffsetX : rect->left; x < rect->right; x++)
		{
			UINT32 color;
			BYTE r, g, b;
			BYTE* src;

			if (written == count)
				return TRUE;

			src = &line[x * bpp];
			color = ReadColor(src, format);
			SplitColor(color, format, &r, &g, &b, NULL, NULL);
			color = FreeRDPGetColor(format, r, g, b, a);
			WriteColor(src, format, color);
			written++;
		}

		first = FALSE;
	}

	return TRUE;
}


static UINT gdi_MapSurfaceToOutput(RdpgfxClientContext* context,
                                   const RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU* surfaceToOutput)
{
	UINT rc = ERROR_INTERNAL_ERROR;
	gdiGfxSurface* surface;
	EnterCriticalSection(&context->mux);
	surface = (gdiGfxSurface*)context->GetSurfaceData(context, surfaceToOutput->surfaceId);

	if (!surface)
		goto fail;

	surface->outputMapped = TRUE;
	surface->outputOriginX = surfaceToOutput->outputOriginX;
	surface->outputOriginY = surfaceToOutput->outputOriginY;
	surface->outputTargetWidth = surface->mappedWidth;
	surface->outputTargetHeight = surface->mappedHeight;
	region16_clear(&surface->invalidRegion);
	rc = CHANNEL_RC_OK;
fail:
	LeaveCriticalSection(&context->mux);
	return rc;
}


static UINT gdi_SurfaceCommand(RdpgfxClientContext* context, const RDPGFX_SURFACE_COMMAND* cmd)
{
	UINT status = CHANNEL_RC_OK;
	rdpGdi* gdi;

	if (!context || !cmd)
		return ERROR_INVALID_PARAMETER;

	gdi = (rdpGdi*)context->custom;

	EnterCriticalSection(&context->mux);
	WLog_Print(gdi->log, WLOG_TRACE,
	           "surfaceId=%" PRIu32 ", codec=%" PRIu32 ", contextId=%" PRIu32 ", format=%s, "
	           "left=%" PRIu32 ", top=%" PRIu32 ", right=%" PRIu32 ", bottom=%" PRIu32
	           ", width=%" PRIu32 ", height=%" PRIu32 " "
	           "length=%" PRIu32 ", data=%p, extra=%p",
	           cmd->surfaceId, cmd->codecId, cmd->contextId, FreeRDPGetColorFormatName(cmd->format),
	           cmd->left, cmd->top, cmd->right, cmd->bottom, cmd->width, cmd->height, cmd->length,
	           (void*)cmd->data, (void*)cmd->extra);

	switch (cmd->codecId)
	{
		case RDPGFX_CODECID_UNCOMPRESSED:
			status = gdi_SurfaceCommand_Uncompressed(gdi, context, cmd);
			break;

		case RDPGFX_CODECID_CAVIDEO:
			status = gdi_SurfaceCommand_RemoteFX(gdi, context, cmd);
			break;

		case RDPGFX_CODECID_CLEARCODEC:
			status = gdi_SurfaceCommand_ClearCodec(gdi, context, cmd);
			break;

		case RDPGFX_CODECID_PLANAR:
			status = gdi_SurfaceCommand_Planar(gdi, context, cmd);
			break;

		case RDPGFX_CODECID_AVC420:
			status = gdi_SurfaceCommand_AVC420(gdi, context, cmd);
			break;

		case RDPGFX_CODECID_AVC444v2:
		case RDPGFX_CODECID_AVC444:
			status = gdi_SurfaceCommand_AVC444(gdi, context, cmd);
			break;

		case RDPGFX_CODECID_ALPHA:
			status = gdi_SurfaceCommand_Alpha(gdi, context, cmd);
			break;

		case RDPGFX_CODECID_CAPROGRESSIVE:
			status = gdi_SurfaceCommand_Progressive(gdi, context, cmd);
			break;

		case RDPGFX_CODECID_CAPROGRESSIVE_V2:
			WLog_WARN(TAG, "SurfaceCommand 0x%08" PRIX32 " not implemented", cmd->codecId);
			break;

		default:
			WLog_WARN(TAG, "Invalid SurfaceCommand 0x%08" PRIX32 "", cmd->codecId);
			break;
	}

	LeaveCriticalSection(&context->mux);
	return status;
}


static BOOL is_rect_valid(const RECTANGLE_16* rect, size_t width, size_t height)
{
	if (!rect)
		return FALSE;
	if ((rect->left > rect->right) || (rect->right > width))
		return FALSE;
	if ((rect->top > rect->bottom) || (rect->bottom > height))
		return FALSE;
	return TRUE;
}


BOOL gdi_graphics_pipeline_init(rdpGdi* gdi, RdpgfxClientContext* gfx)
{
	return gdi_graphics_pipeline_init_ex(gdi, gfx, NULL, NULL, NULL);
}


const char* rdpgfx_get_codec_id_string(UINT16 codecId)
{
	switch (codecId)
	{
		case RDPGFX_CODECID_UNCOMPRESSED:
			return "RDPGFX_CODECID_UNCOMPRESSED";

		case RDPGFX_CODECID_CAVIDEO:
			return "RDPGFX_CODECID_CAVIDEO";

		case RDPGFX_CODECID_CLEARCODEC:
			return "RDPGFX_CODECID_CLEARCODEC";

		case RDPGFX_CODECID_PLANAR:
			return "RDPGFX_CODECID_PLANAR";

		case RDPGFX_CODECID_AVC420:
			return "RDPGFX_CODECID_AVC420";

		case RDPGFX_CODECID_AVC444:
			return "RDPGFX_CODECID_AVC444";

		case RDPGFX_CODECID_AVC444v2:
			return "RDPGFX_CODECID_AVC444v2";

		case RDPGFX_CODECID_ALPHA:
			return "RDPGFX_CODECID_ALPHA";

		case RDPGFX_CODECID_CAPROGRESSIVE:
			return "RDPGFX_CODECID_CAPROGRESSIVE";

		case RDPGFX_CODECID_CAPROGRESSIVE_V2:
			return "RDPGFX_CODECID_CAPROGRESSIVE_V2";
	}

	return "RDPGFX_CODECID_UNKNOWN";
}


zsearch(i_ctx_t *i_ctx_p)
{
    return search_impl(i_ctx_p, true);
}


zbytestring(i_ctx_t *i_ctx_p)
{
    os_ptr op = osp;
    byte *sbody;
    uint size;

    check_int_leu(*op, max_int);
    size = (uint)op->value.intval;
    sbody = ialloc_bytes(size, ".bytestring");
    if (sbody == 0)
        return_error(gs_error_VMerror);
    make_astruct(op, a_all | icurrent_space, sbody);
    memset(sbody, 0, size);
    return 0;
}


static void cdev_dynamic_release(struct kobject *kobj)
{
	struct cdev *p = container_of(kobj, struct cdev, kobj);
	struct kobject *parent = kobj->parent;

	cdev_purge(p);
	kfree(p);
	kobject_put(parent);
}


void cdev_set_parent(struct cdev *p, struct kobject *kobj)
{
	WARN_ON(!kobj->state_initialized);
	p->kobj.parent = kobj;
}


void cdev_device_del(struct cdev *cdev, struct device *dev)
{
	device_del(dev);
	if (dev->devt)
		cdev_del(cdev);
}


static void cdev_purge(struct cdev *cdev)
{
	spin_lock(&cdev_lock);
	while (!list_empty(&cdev->list)) {
		struct inode *inode;
		inode = container_of(cdev->list.next, struct inode, i_devices);
		list_del_init(&inode->i_devices);
		inode->i_cdev = NULL;
	}
	spin_unlock(&cdev_lock);
}


int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
                                        const char *mode) {
  struct SParser p;
  int status;
  incnny(L);  /* cannot yield during parsing */
  p.z = z; p.name = name; p.mode = mode;
  p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0;
  p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
  p.dyd.label.arr = NULL; p.dyd.label.size = 0;
  luaZ_initbuffer(L, &p.buff);
  status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
  luaZ_freebuffer(L, &p.buff);
  luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
  luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);
  luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size);
  decnny(L);
  return status;
}


void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
  switch (errcode) {
    case LUA_ERRMEM: {  /* memory error? */
      setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
      break;
    }
    case LUA_ERRERR: {
      setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
      break;
    }
    case CLOSEPROTECT: {
      setnilvalue(s2v(oldtop));  /* no error message */
      break;
    }
    default: {
      setobjs2s(L, oldtop, L->top - 1);  /* error message on current top */
      break;
    }
  }
  L->top = oldtop + 1;
}


void luaD_tryfuncTM (lua_State *L, StkId func) {
  const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL);
  StkId p;
  if (unlikely(ttisnil(tm)))
    luaG_typeerror(L, s2v(func), "call");  /* nothing to call */
  for (p = L->top; p > func; p--)  /* open space for metamethod */
    setobjs2s(L, p, p-1);
  L->top++;  /* stack space pre-allocated by the caller */
  setobj2s(L, func, tm);  /* metamethod is the new function to be called */
}


void luaD_hookcall (lua_State *L, CallInfo *ci) {
  int hook = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL : LUA_HOOKCALL;
  Proto *p;
  if (!(L->hookmask & LUA_MASKCALL))  /* some other hook? */
    return;  /* don't call hook */
  p = clLvalue(s2v(ci->func))->p;
  L->top = ci->top;  /* prepare top */
  ci->u.l.savedpc++;  /* hooks assume 'pc' is already incremented */
  luaD_hook(L, hook, -1, 1, p->numparams);
  ci->u.l.savedpc--;  /* correct 'pc' */
}


static void resume (lua_State *L, void *ud) {
  int n = *(cast(int*, ud));  /* number of arguments */
  StkId firstArg = L->top - n;  /* first argument */
  CallInfo *ci = L->ci;
  if (L->status == LUA_OK) {  /* starting a coroutine? */
    luaD_call(L, firstArg - 1, LUA_MULTRET);
  }
  else {  /* resuming from previous yield */
    lua_assert(L->status == LUA_YIELD);
    L->status = LUA_OK;  /* mark that it is running (again) */
    if (isLua(ci))  /* yielded inside a hook? */
      luaV_execute(L, ci);  /* just continue running Lua code */
    else {  /* 'common' yield */
      if (ci->u.c.k != NULL) {  /* does it have a continuation function? */
        lua_unlock(L);
        n = (*ci->u.c.k)(L, LUA_YIELD, ci->u.c.ctx); /* call continuation */
        lua_lock(L);
        api_checknelems(L, n);
      }
      luaD_poscall(L, ci, n);  /* finish 'luaD_call' */
    }
    unroll(L, NULL);  /* run continuation */
  }
}


LUA_API int lua_isyieldable (lua_State *L) {
  return yieldable(L);
}


int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
                      TMS event) {
  if (callbinTM(L, p1, p2, L->top, event))  /* try original event */
    return !l_isfalse(s2v(L->top));
#if defined(LUA_COMPAT_LT_LE)
  else if (event == TM_LE) {
      /* try '!(p2 < p1)' for '(p1 <= p2)' */
      L->ci->callstatus |= CIST_LEQ;  /* mark it is doing 'lt' for 'le' */
      if (callbinTM(L, p2, p1, L->top, TM_LT)) {
        L->ci->callstatus ^= CIST_LEQ;  /* clear mark */
        return l_isfalse(s2v(L->top));
      }
      /* else error will remove this 'ci'; no need to clear mark */
  }
#endif
  luaG_ordererror(L, p1, p2);  /* no metamethod found */
  return 0;  /* to avoid warnings */
}


void luaT_init (lua_State *L) {
  static const char *const luaT_eventname[] = {  /* ORDER TM */
    "__index", "__newindex",
    "__gc", "__mode", "__len", "__eq",
    "__add", "__sub", "__mul", "__mod", "__pow",
    "__div", "__idiv",
    "__band", "__bor", "__bxor", "__shl", "__shr",
    "__unm", "__bnot", "__lt", "__le",
    "__concat", "__call", "__close"
  };
  int i;
  for (i=0; i<TM_N; i++) {
    G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]);
    luaC_fix(L, obj2gco(G(L)->tmname[i]));  /* never collect these names */
  }
}


void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
                      const TValue *slot) {
  int loop;  /* counter to avoid infinite loops */
  const TValue *tm;  /* metamethod */
  for (loop = 0; loop < MAXTAGLOOP; loop++) {
    if (slot == NULL) {  /* 't' is not a table? */
      lua_assert(!ttistable(t));
      tm = luaT_gettmbyobj(L, t, TM_INDEX);
      if (unlikely(notm(tm)))
        luaG_typeerror(L, t, "index");  /* no metamethod */
      /* else will try the metamethod */
    }
    else {  /* 't' is a table */
      lua_assert(isempty(slot));
      tm = fasttm(L, hvalue(t)->metatable, TM_INDEX);  /* table's metamethod */
      if (tm == NULL) {  /* no metamethod? */
        setnilvalue(s2v(val));  /* result is nil */
        return;
      }
      /* else will try the metamethod */
    }
    if (ttisfunction(tm)) {  /* is metamethod a function? */
      luaT_callTMres(L, tm, t, key, val);  /* call it */
      return;
    }
    t = tm;  /* else try to access 'tm[key]' */
    if (luaV_fastget(L, t, key, slot, luaH_get)) {  /* fast track? */
      setobj2s(L, val, slot);  /* done */
      return;
    }
    /* else repeat (tail call 'luaV_finishget') */
  }
  luaG_runerror(L, "'__index' chain too long; possible loop");
}


void luaT_tryconcatTM (lua_State *L) {
  StkId top = L->top;
  if (!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT))
    luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
}


static int traversethread (global_State *g, lua_State *th) {
  UpVal *uv;
  StkId o = th->stack;
  if (o == NULL)
    return 1;  /* stack not completely built yet */
  lua_assert(g->gcstate == GCSatomic ||
             th->openupval == NULL || isintwups(th));
  for (; o < th->top; o++)  /* mark live elements in the stack */
    markvalue(g, s2v(o));
  for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
    markobject(g, uv);  /* open upvalues cannot be collected */
  if (g->gcstate == GCSatomic) {  /* final traversal? */
    StkId lim = th->stack + th->stacksize;  /* real end of stack */
    for (; o < lim; o++)  /* clear not-marked stack slice */
      setnilvalue(s2v(o));
    /* 'remarkupvals' may have removed thread from 'twups' list */
    if (!isintwups(th) && th->openupval != NULL) {
      th->twups = g->twups;  /* link it back to the list */
      g->twups = th;
    }
  }
  else if (!g->gcemergency)
    luaD_shrinkstack(th); /* do not change stack in emergency cycle */
  return 1 + th->stacksize;
}


static void restartcollection (global_State *g) {
  g->gray = g->grayagain = NULL;
  g->weak = g->allweak = g->ephemeron = NULL;
  markobject(g, g->mainthread);
  markvalue(g, &g->l_registry);
  markmt(g);
  markbeingfnz(g);  /* mark any finalizing object left from previous cycle */
}


static void traverseweakvalue (global_State *g, Table *h) {
  Node *n, *limit = gnodelast(h);
  /* if there is array part, assume it may have white values (it is not
     worth traversing it now just to check) */
  int hasclears = (h->alimit > 0);
  for (n = gnode(h, 0); n < limit; n++) {  /* traverse hash part */
    if (isempty(gval(n)))  /* entry is empty? */
      clearkey(n);  /* clear its key */
    else {
      lua_assert(!keyisnil(n));
      markkey(g, n);
      if (!hasclears && iscleared(g, gcvalueN(gval(n))))  /* a white value? */
        hasclears = 1;  /* table will have to be cleared */
    }
  }
  if (g->gcstate == GCSatomic && hasclears)
    linkgclist(h, g->weak);  /* has to be cleared later */
  else
    linkgclist(h, g->grayagain);  /* must retraverse it in atomic phase */
}


static lu_mem propagateall (global_State *g) {
  lu_mem tot = 0;
  while (g->gray)
    tot += propagatemark(g);
  return tot;
}


static void *err_unwind(lua_State *L, void *stopcf, int errcode)
{
  TValue *frame = L->base-1;
  void *cf = L->cframe;
  while (cf) {
    int32_t nres = cframe_nres(cframe_raw(cf));
    if (nres < 0) {  /* C frame without Lua frame? */
      TValue *top = restorestack(L, -nres);
      if (frame < top) {  /* Frame reached? */
	if (errcode) {
	  L->cframe = cframe_prev(cf);
	  L->base = frame+1;
	  unwindstack(L, top);
	}
	return cf;
      }
    }
    if (frame <= tvref(L->stack))
      break;
    switch (frame_typep(frame)) {
    case FRAME_LUA:  /* Lua frame. */
    case FRAME_LUAP:
      frame = frame_prevl(frame);
      break;
    case FRAME_C:  /* C frame. */
#if LJ_HASFFI
    unwind_c:
#endif
#if LJ_UNWIND_EXT
      if (errcode) {
	L->cframe = cframe_prev(cf);
	L->base = frame_prevd(frame) + 1;
	unwindstack(L, frame);
      } else if (cf != stopcf) {
	cf = cframe_prev(cf);
	frame = frame_prevd(frame);
	break;
      }
      return NULL;  /* Continue unwinding. */
#else
      UNUSED(stopcf);
      cf = cframe_prev(cf);
      frame = frame_prevd(frame);
      break;
#endif
    case FRAME_CP:  /* Protected C frame. */
      if (cframe_canyield(cf)) {  /* Resume? */
	if (errcode) {
	  hook_leave(G(L));  /* Assumes nobody uses coroutines inside hooks. */
	  L->cframe = NULL;
	  L->status = (uint8_t)errcode;
	}
	return cf;
      }
      if (errcode) {
	L->cframe = cframe_prev(cf);
	L->base = frame_prevd(frame) + 1;
	unwindstack(L, frame);
      }
      return cf;
    case FRAME_CONT:  /* Continuation frame. */
#if LJ_HASFFI
      if ((frame-1)->u32.lo == LJ_CONT_FFI_CALLBACK)
	goto unwind_c;
#endif
      /* fallthrough */
    case FRAME_VARG:  /* Vararg frame. */
      frame = frame_prevd(frame);
      break;
    case FRAME_PCALL:  /* FF pcall() frame. */
    case FRAME_PCALLH:  /* FF pcall() frame inside hook. */
      if (errcode) {
	if (errcode == LUA_YIELD) {
	  frame = frame_prevd(frame);
	  break;
	}
	if (frame_typep(frame) == FRAME_PCALL)
	  hook_leave(G(L));
	L->cframe = cf;
	L->base = frame_prevd(frame) + 1;
	unwindstack(L, L->base);
      }
      return (void *)((intptr_t)cf | CFRAME_UNWIND_FF);
    }
  }
  /* No C frame. */
  if (errcode) {
    L->cframe = NULL;
    L->base = tvref(L->stack)+1;
    unwindstack(L, L->base);
    if (G(L)->panic)
      G(L)->panic(L);
    exit(EXIT_FAILURE);
  }
  return L;  /* Anything non-NULL will do. */
}


LJ_NOINLINE void lj_err_optype_call(lua_State *L, TValue *o)
{
  /* Gross hack if lua_[p]call or pcall/xpcall fail for a non-callable object:
  ** L->base still points to the caller. So add a dummy frame with L instead
  ** of a function. See lua_getstack().
  */
  const BCIns *pc = cframe_Lpc(L);
  if (((ptrdiff_t)pc & FRAME_TYPE) != FRAME_LUA) {
    const char *tname = lj_typename(o);
    setframe_pc(o, pc);
    setframe_gc(o, obj2gco(L));
    L->top = L->base = o+1;
    err_msgv(L, LJ_ERR_BADCALL, tname);
  }
  lj_err_optype(L, o, LJ_ERR_OPCALL);
}


LJ_NOINLINE void lj_err_optype(lua_State *L, cTValue *o, ErrMsg opm)
{
  const char *tname = lj_typename(o);
  const char *opname = err2msg(opm);
  if (curr_funcisL(L)) {
    GCproto *pt = curr_proto(L);
    const BCIns *pc = cframe_Lpc(L) - 1;
    const char *oname = NULL;
    const char *kind = lj_debug_slotname(pt, pc, (BCReg)(o-L->base), &oname);
    if (kind)
      err_msgv(L, LJ_ERR_BADOPRT, opname, kind, oname, tname);
  }
  err_msgv(L, LJ_ERR_BADOPRV, opname, tname);
}


LJ_NOINLINE void lj_err_comp(lua_State *L, cTValue *o1, cTValue *o2)
{
  const char *t1 = lj_typename(o1);
  const char *t2 = lj_typename(o2);
  err_msgv(L, t1 == t2 ? LJ_ERR_BADCMPV : LJ_ERR_BADCMPT, t1, t2);
  /* This assumes the two "boolean" entries are commoned by the C compiler. */
}


LJ_NORET LJ_NOINLINE static void err_msgv(lua_State *L, ErrMsg em, ...)
{
  const char *msg;
  va_list argp;
  va_start(argp, em);
  if (curr_funcisL(L)) L->top = curr_topL(L);
  msg = lj_str_pushvf(L, err2msg(em), argp);
  va_end(argp);
  lj_debug_addloc(L, msg, L->base-1, NULL);
  lj_err_run(L);
}


InitializeTLSSession(rfbClient* client, rfbBool anonTLS, rfbCredential *cred)
{
  if (client->tlsSession) return TRUE;

  client->tlsSession = open_ssl_connection (client, client->sock, anonTLS, cred);

  if (!client->tlsSession)
    return FALSE;

  rfbClientLog("TLS session initialized.\n");

  return TRUE;
}


ReadFromTLS(rfbClient* client, char *out, unsigned int n)
{
  ssize_t ret;

  ret = SSL_read (client->tlsSession, out, n);

  if (ret >= 0)
    return ret;
  else {
    errno = ssl_errno (client->tlsSession, ret);

    if (errno != EAGAIN) {
      rfbClientLog("Error reading from TLS: -.\n");
    }
  }

  return -1;
}


rfbNewUDPConnection(rfbScreenInfoPtr rfbScreen,
                    rfbSocket sock)
{
  if (write(sock, (char*) &ptrAcceleration, 1) < 0) {
	rfbLogPerror("rfbNewUDPConnection: write");
    }
}


rfbReleaseClientIterator(rfbClientIteratorPtr iterator)
{
  if(iterator && iterator->next) rfbDecrClientRef(iterator->next);
  free(iterator);
}


sraSpanListOr(sraSpanList *dest, const sraSpanList *src) {
  sraSpan *d_curr, *s_curr;
  int s_start, s_end;

  if (!dest) {
    if (!src) {
      return;
    } else {
      rfbErr("sraSpanListOr:incompatible spans (only one NULL!)\n");
      return;
    }
  }

  d_curr = dest->front._next;
  s_curr = src->front._next;
  s_start = s_curr->start;
  s_end = s_curr->end;
  while (s_curr != &(src->back)) {

    /* - If we are at end of destination list OR
       If the new span comes before the next destination one */
    if ((d_curr == &(dest->back)) ||
		(d_curr->start >= s_end)) {
      /* - Add the span */
      sraSpanInsertBefore(sraSpanCreate(s_start, s_end,
					s_curr->subspan),
			  d_curr);
      if (d_curr != &(dest->back))
	sraSpanMergePrevious(d_curr);
      s_curr = s_curr->_next;
      s_start = s_curr->start;
      s_end = s_curr->end;
    } else {

      /* - If the new span overlaps the existing one */
      if ((s_start < d_curr->end) &&
	  (s_end > d_curr->start)) {

	/* - Insert new span before the existing destination one? */
	if (s_start < d_curr->start) {
	  sraSpanInsertBefore(sraSpanCreate(s_start,
					    d_curr->start,
					    s_curr->subspan),
			      d_curr);
	  sraSpanMergePrevious(d_curr);
	}

	/* Split the existing span if necessary */
	if (s_end < d_curr->end) {
	  sraSpanInsertAfter(sraSpanCreate(s_end,
					   d_curr->end,
					   d_curr->subspan),
			     d_curr);
	  d_curr->end = s_end;
	}
	if (s_start > d_curr->start) {
	  sraSpanInsertBefore(sraSpanCreate(d_curr->start,
					    s_start,
					    d_curr->subspan),
			      d_curr);
	  d_curr->start = s_start;
	}

	/* Recursively OR subspans */
	sraSpanListOr(d_curr->subspan, s_curr->subspan);

	/* Merge this span with previous or next? */
	if (d_curr->_prev != &(dest->front))
	  sraSpanMergePrevious(d_curr);
	if (d_curr->_next != &(dest->back))
	  sraSpanMergeNext(d_curr);

	/* Move onto the next pair to compare */
	if (s_end > d_curr->end) {
	  s_start = d_curr->end;
	  d_curr = d_curr->_next;
	} else {
	  s_curr = s_curr->_next;
	  s_start = s_curr->start;
	  s_end = s_curr->end;
	}
      } else {
	/* - No overlap.  Move to the next destination span */
	d_curr = d_curr->_next;
      }
    }
  }
}


sraRgnCreateRect(int x1, int y1, int x2, int y2) {
  sraSpanList *vlist, *hlist;
  sraSpan *vspan, *hspan;

  /* - Build the horizontal portion of the span */
  hlist = sraSpanListCreate();
  hspan = sraSpanCreate(x1, x2, NULL);
  sraSpanInsertAfter(hspan, &(hlist->front));

  /* - Build the vertical portion of the span */
  vlist = sraSpanListCreate();
  vspan = sraSpanCreate(y1, y2, hlist);
  sraSpanInsertAfter(vspan, &(vlist->front));

  sraSpanListDestroy(hlist);

  return (sraRegion*)vlist;
}


sraSpanListSubtract(sraSpanList *dest, const sraSpanList *src) {
  sraSpan *d_curr, *s_curr;

  if (!dest) {
    if (!src) {
      return 1;
    } else {
      rfbErr("sraSpanListSubtract:incompatible spans (only one NULL!)\n");
      return FALSE;
    }
  }

  d_curr = dest->front._next;
  s_curr = src->front._next;
  while ((s_curr != &(src->back)) && (d_curr != &(dest->back))) {

    /* - If we haven't reached a destination span yet then move on */
    if (d_curr->start >= s_curr->end) {
      s_curr = s_curr->_next;
      continue;
    }

    /* - If we are beyond the current destination span then skip it */
    if (d_curr->end <= s_curr->start) {
      d_curr = d_curr->_next;
      continue;
    }

    /* - If we partially overlap the current span then split it up */
    if (s_curr->start > d_curr->start) {
      sraSpanInsertBefore(sraSpanCreate(d_curr->start,
					s_curr->start,
					d_curr->subspan),
			  d_curr);
      d_curr->start = s_curr->start;
    }
    if (s_curr->end < d_curr->end) {
      sraSpanInsertAfter(sraSpanCreate(s_curr->end,
				       d_curr->end,
				       d_curr->subspan),
			 d_curr);
      d_curr->end = s_curr->end;
    }

    /* - Now recursively process the affected span */
    if ((!d_curr->subspan) || !sraSpanListSubtract(d_curr->subspan, s_curr->subspan)) {
      /* - The destination subspan is now empty, so we should remove it */
      sraSpan *next = d_curr->_next;
      sraSpanRemove(d_curr);
      sraSpanDestroy(d_curr);
      d_curr = next;
    } else {
      /* Merge this span with previous or next? */
      if (d_curr->_prev != &(dest->front))
	sraSpanMergePrevious(d_curr);
      if (d_curr->_next != &(dest->back))
	sraSpanMergeNext(d_curr);

      /* - Move on to the next span */
      if (s_curr->end > d_curr->end) {
	d_curr = d_curr->_next;
      } else {
	s_curr = s_curr->_next;
      }
    }
  }

  return !sraSpanListEmpty(dest);
}


sraSpanListEmpty(const sraSpanList *list) {
  return (list->front._next == &(list->back));
}


sraSpanDup(const sraSpan *src) {
  sraSpan *span;
  if (!src) return NULL;
  span = sraSpanCreate(src->start, src->end, src->subspan);
  return span;
}


static void CopyRectangle(rfbClient* client, const uint8_t* buffer, int x, int y, int w, int h) {
  int j;

  if (client->frameBuffer == NULL) {
      return;
  }

  if (!CheckRect(client, x, y, w, h)) {
    rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", x, y, w, h);
    return;
  }

#define COPY_RECT(BPP) \
  { \
    int rs = w * BPP / 8, rs2 = client->width * BPP / 8; \
    for (j = ((x * (BPP / 8)) + (y * rs2)); j < (y + h) * rs2; j += rs2) { \
      memcpy(client->frameBuffer + j, buffer, rs); \
      buffer += rs; \
    } \
  }

  switch(client->format.bitsPerPixel) {
  case  8: COPY_RECT(8);  break;
  case 16: COPY_RECT(16); break;
  case 32: COPY_RECT(32); break;
  default:
    rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
  }
}


WriteToRFBServer(rfbClient* client, const char *buf, unsigned int n)
{
  fd_set fds;
  int i = 0;
  int j;
  const char *obuf = buf;
#ifdef LIBVNCSERVER_HAVE_SASL
  const char *output;
  unsigned int outputlen;
  int err;
#endif /* LIBVNCSERVER_HAVE_SASL */

  if (client->serverPort==-1)
    return TRUE; /* vncrec playing */

  if (client->tlsSession) {
    /* WriteToTLS() will guarantee either everything is written, or error/eof returns */
    i = WriteToTLS(client, buf, n);
    if (i <= 0) return FALSE;

    return TRUE;
  }
#ifdef LIBVNCSERVER_HAVE_SASL
  if (client->saslconn) {
    err = sasl_encode(client->saslconn,
                      buf, n,
                      &output, &outputlen);
    if (err != SASL_OK) {
      rfbClientLog("Failed to encode SASL data %s",
                   sasl_errstring(err, NULL, NULL));
      return FALSE;
    }
    obuf = output;
    n = outputlen;
  }
#endif /* LIBVNCSERVER_HAVE_SASL */

  while (i < n) {
    j = write(client->sock, obuf + i, (n - i));
    if (j <= 0) {
      if (j < 0) {
#ifdef WIN32
	 errno=WSAGetLastError();
#endif
	if (errno == EWOULDBLOCK ||
#ifdef LIBVNCSERVER_ENOENT_WORKAROUND
		errno == ENOENT ||
#endif
		errno == EAGAIN) {
	  FD_ZERO(&fds);
	  FD_SET(client->sock,&fds);

	  if (select(client->sock+1, NULL, &fds, NULL, NULL) <= 0) {
	    rfbClientErr("select\n");
	    return FALSE;
	  }
	  j = 0;
	} else {
	  rfbClientErr("write\n");
	  return FALSE;
	}
      } else {
	rfbClientLog("write failed\n");
	return FALSE;
      }
    }
    i += j;
  }
  return TRUE;
}


SetFormatAndEncodings(rfbClient* client)
{
  rfbSetPixelFormatMsg spf;
  union {
    char bytes[sz_rfbSetEncodingsMsg + MAX_ENCODINGS*4];
    rfbSetEncodingsMsg msg;
  } buf;

  rfbSetEncodingsMsg *se = &buf.msg;
  uint32_t *encs = (uint32_t *)(&buf.bytes[sz_rfbSetEncodingsMsg]);
  int len = 0;
  rfbBool requestCompressLevel = FALSE;
  rfbBool requestQualityLevel = FALSE;
  rfbBool requestLastRectEncoding = FALSE;
  rfbClientProtocolExtension* e;

  if (!SupportsClient2Server(client, rfbSetPixelFormat)) return TRUE;

  spf.type = rfbSetPixelFormat;
  spf.pad1 = 0;
  spf.pad2 = 0;
  spf.format = client->format;
  spf.format.redMax = rfbClientSwap16IfLE(spf.format.redMax);
  spf.format.greenMax = rfbClientSwap16IfLE(spf.format.greenMax);
  spf.format.blueMax = rfbClientSwap16IfLE(spf.format.blueMax);

  if (!WriteToRFBServer(client, (char *)&spf, sz_rfbSetPixelFormatMsg))
    return FALSE;

  if (!SupportsClient2Server(client, rfbSetEncodings)) return TRUE;

  se->type = rfbSetEncodings;
  se->pad = 0;
  se->nEncodings = 0;

  if (client->appData.encodingsString) {
    const char *encStr = client->appData.encodingsString;
    int encStrLen;
    do {
      const char *nextEncStr = strchr(encStr, ' ');
      if (nextEncStr) {
	encStrLen = nextEncStr - encStr;
	nextEncStr++;
      } else {
	encStrLen = strlen(encStr);
      }

      if (strncasecmp(encStr,"raw",encStrLen) == 0) {
	encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRaw);
      } else if (strncasecmp(encStr,"copyrect",encStrLen) == 0) {
	encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCopyRect);
#ifdef LIBVNCSERVER_HAVE_LIBZ
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
      } else if (strncasecmp(encStr,"tight",encStrLen) == 0) {
	encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingTight);
	requestLastRectEncoding = TRUE;
	if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9)
	  requestCompressLevel = TRUE;
	if (client->appData.enableJPEG)
	  requestQualityLevel = TRUE;
#endif
#endif
      } else if (strncasecmp(encStr,"hextile",encStrLen) == 0) {
	encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingHextile);
#ifdef LIBVNCSERVER_HAVE_LIBZ
      } else if (strncasecmp(encStr,"zlib",encStrLen) == 0) {
	encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZlib);
	if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9)
	  requestCompressLevel = TRUE;
      } else if (strncasecmp(encStr,"zlibhex",encStrLen) == 0) {
	encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZlibHex);
	if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9)
	  requestCompressLevel = TRUE;
      } else if (strncasecmp(encStr,"trle",encStrLen) == 0) {
	encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingTRLE);
      } else if (strncasecmp(encStr,"zrle",encStrLen) == 0) {
	encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZRLE);
      } else if (strncasecmp(encStr,"zywrle",encStrLen) == 0) {
	encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZYWRLE);
	requestQualityLevel = TRUE;
#endif
      } else if ((strncasecmp(encStr,"ultra",encStrLen) == 0) || (strncasecmp(encStr,"ultrazip",encStrLen) == 0)) {
        /* There are 2 encodings used in 'ultra' */
        encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingUltra);
        encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingUltraZip);
      } else if (strncasecmp(encStr,"corre",encStrLen) == 0) {
	encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCoRRE);
      } else if (strncasecmp(encStr,"rre",encStrLen) == 0) {
	encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRRE);
      } else {
	rfbClientLog("Unknown encoding '%.*s'\n",encStrLen,encStr);
      }

      encStr = nextEncStr;
    } while (encStr && se->nEncodings < MAX_ENCODINGS);

    if (se->nEncodings < MAX_ENCODINGS && requestCompressLevel) {
      encs[se->nEncodings++] = rfbClientSwap32IfLE(client->appData.compressLevel +
					  rfbEncodingCompressLevel0);
    }

    if (se->nEncodings < MAX_ENCODINGS && requestQualityLevel) {
      if (client->appData.qualityLevel < 0 || client->appData.qualityLevel > 9)
        client->appData.qualityLevel = 5;
      encs[se->nEncodings++] = rfbClientSwap32IfLE(client->appData.qualityLevel +
					  rfbEncodingQualityLevel0);
    }
  }
  else {
    if (SameMachine(client->sock)) {
      /* TODO:
      if (!tunnelSpecified) {
      */
      rfbClientLog("Same machine: preferring raw encoding\n");
      encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRaw);
      /*
      } else {
	rfbClientLog("Tunneling active: preferring tight encoding\n");
      }
      */
    }

    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCopyRect);
#ifdef LIBVNCSERVER_HAVE_LIBZ
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingTight);
    requestLastRectEncoding = TRUE;
#endif
#endif
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingHextile);
#ifdef LIBVNCSERVER_HAVE_LIBZ
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZlib);
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZRLE);
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZYWRLE);
#endif
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingUltra);
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingUltraZip);
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCoRRE);
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRRE);

    if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9) {
      encs[se->nEncodings++] = rfbClientSwap32IfLE(client->appData.compressLevel +
					  rfbEncodingCompressLevel0);
    } else /* if (!tunnelSpecified) */ {
      /* If -tunnel option was provided, we assume that server machine is
	 not in the local network so we use default compression level for
	 tight encoding instead of fast compression. Thus we are
	 requesting level 1 compression only if tunneling is not used. */
      encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCompressLevel1);
    }

    if (client->appData.enableJPEG) {
      if (client->appData.qualityLevel < 0 || client->appData.qualityLevel > 9)
	client->appData.qualityLevel = 5;
      encs[se->nEncodings++] = rfbClientSwap32IfLE(client->appData.qualityLevel +
					  rfbEncodingQualityLevel0);
    }
  }

  /* Remote Cursor Support (local to viewer) */
  if (client->appData.useRemoteCursor) {
    if (se->nEncodings < MAX_ENCODINGS)
      encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingXCursor);
    if (se->nEncodings < MAX_ENCODINGS)
      encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRichCursor);
    if (se->nEncodings < MAX_ENCODINGS)
      encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingPointerPos);
  }

  /* Keyboard State Encodings */
  if (se->nEncodings < MAX_ENCODINGS)
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingKeyboardLedState);

  /* New Frame Buffer Size */
  if (se->nEncodings < MAX_ENCODINGS && client->canHandleNewFBSize)
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingNewFBSize);

  /* Last Rect */
  if (se->nEncodings < MAX_ENCODINGS && requestLastRectEncoding)
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingLastRect);

  /* Server Capabilities */
  if (se->nEncodings < MAX_ENCODINGS)
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingSupportedMessages);
  if (se->nEncodings < MAX_ENCODINGS)
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingSupportedEncodings);
  if (se->nEncodings < MAX_ENCODINGS)
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingServerIdentity);

  /* xvp */
  if (se->nEncodings < MAX_ENCODINGS)
    encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingXvp);

  /* client extensions */
  for(e = rfbClientExtensions; e; e = e->next)
    if(e->encodings) {
      int* enc;
      for(enc = e->encodings; *enc; enc++)
        if(se->nEncodings < MAX_ENCODINGS)
          encs[se->nEncodings++] = rfbClientSwap32IfLE(*enc);
    }

  len = sz_rfbSetEncodingsMsg + se->nEncodings * 4;

  se->nEncodings = rfbClientSwap16IfLE(se->nEncodings);

  if (!WriteToRFBServer(client, buf.bytes, len)) return FALSE;

  return TRUE;
}


void session_set_access_time(Session *session)
{
	session->last_access_time = time(NULL);
}


void session_init(Session *session, const void *prefs_account, gboolean is_smtp)
{
	session->type = SESSION_UNKNOWN;
	session->sock = NULL;
	session->server = NULL;
	session->port = 0;
#ifdef USE_GNUTLS
	session->ssl_type = SSL_NONE;
	session->use_tls_sni = TRUE;
#endif
	session->nonblocking = TRUE;
	session->state = SESSION_READY;
	session->last_access_time = time(NULL);

	session->tv_prev = g_date_time_new_now_local();

	session->conn_id = 0;

	session->io_tag = 0;

	session->read_buf_p = session->read_buf;
	session->read_buf_len = 0;

	session->read_msg_buf = g_string_sized_new(1024);
	session->read_data_buf = g_byte_array_new();

	session->write_buf = NULL;
	session->write_buf_p = NULL;
	session->write_buf_len = 0;

	session->write_data = NULL;
	session->write_data_p = NULL;
	session->write_data_len = 0;

	session->timeout_tag = 0;
	session->timeout_interval = 0;

	session->data = NULL;
	session->account = prefs_account;
	session->is_smtp = is_smtp;

	session->ping_tag = -1;

	session->proxy_info = NULL;
}


static int mount_usage(FILE * stream)
{
	fprintf(stream, "\nUsage:  %s <remotetarget> <dir> -o <options>\n",
		thisprogram);
	fprintf(stream, "\nMount the remote target, specified as a UNC name,");
	fprintf(stream, " to a local directory.\n\nOptions:\n");
	fprintf(stream, "\tuser=<arg>\n\tpass=<arg>\n\tdom=<arg>\n");
	fprintf(stream, "\nLess commonly used options:");
	fprintf(stream,
		"\n\tcredentials=<filename>,guest,perm,noperm,setuids,nosetuids,rw,ro,");
	fprintf(stream,
		"\n\tsep=<char>,iocharset=<codepage>,suid,nosuid,exec,noexec,serverino,");
	fprintf(stream,
		"\n\tnoserverino,mapchars,nomapchars,nolock,servernetbiosname=<SRV_RFC1001NAME>");
	fprintf(stream,
		"\n\tcache=<strict|none|loose>,nounix,cifsacl,sec=<authentication mechanism>,");
	fprintf(stream,
		"\n\tsign,seal,fsc,snapshot=<token|time>,nosharesock,persistenthandles,");
	fprintf(stream,
		"\n\tresilienthandles,rdma,vers=<smb_dialect>,cruid");
	fprintf(stream,
		"\n\nOptions not needed for servers supporting CIFS Unix extensions");
	fprintf(stream,
		"\n\t(e.g. unneeded for mounts to most Samba versions):");
	fprintf(stream,
		"\n\tuid=<uid>,gid=<gid>,dir_mode=<mode>,file_mode=<mode>,sfu,");
	fprintf(stream,
		"\n\tmfsymlinks,idsfromsid");
	fprintf(stream, "\n\nRarely used options:");
	fprintf(stream,
		"\n\tport=<tcpport>,rsize=<size>,wsize=<size>,unc=<unc_name>,ip=<ip_address>,");
	fprintf(stream,
		"\n\tdev,nodev,nouser_xattr,netbiosname=<OUR_RFC1001NAME>,hard,soft,intr,");
	fprintf(stream,
		"\n\tnointr,ignorecase,noposixpaths,noacl,prefixpath=<path>,nobrl,");
	fprintf(stream,
		"\n\techo_interval=<seconds>,actimeo=<seconds>,max_credits=<credits>,");
	fprintf(stream,
		"\n\tbsize=<size>");
	fprintf(stream,
		"\n\nOptions are described in more detail in the manual page");
	fprintf(stream, "\n\tman 8 %s\n", thisprogram);
	fprintf(stream, "\nTo display the version number of the mount helper:");
	fprintf(stream, "\n\t%s -V\n", thisprogram);

	if (stream == stderr)
		return EX_USAGE;
	return 0;
}


LUA_API int lua_gethookcount (lua_State *L) {
  return L->basehookcount;
}


LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
  int status;
  Closure *cl;
  CallInfo *ci;
  TValue *func;
  lua_lock(L);
  if (*what == '>') {
    ci = NULL;
    func = s2v(L->top - 1);
    api_check(L, ttisfunction(func), "function expected");
    what++;  /* skip the '>' */
    L->top--;  /* pop function */
  }
  else {
    ci = ar->i_ci;
    func = s2v(ci->func);
    lua_assert(ttisfunction(func));
  }
  cl = ttisclosure(func) ? clvalue(func) : NULL;
  status = auxgetinfo(L, what, ar, cl, ci);
  if (strchr(what, 'f')) {
    setobj2s(L, L->top, func);
    api_incr_top(L);
  }
  if (strchr(what, 'L'))
    collectvalidlines(L, cl);
  lua_unlock(L);
  return status;
}


void luaE_enterCcall (lua_State *L) {
  int ncalls = getCcalls(L);
  L->nCcalls--;
  if (ncalls <= CSTACKERR) {  /* possible overflow? */
    luaE_freeCI(L);  /* release unused CIs */
    ncalls = getCcalls(L);  /* update call count */
    if (ncalls <= CSTACKERR) {  /* still overflow? */
      if (ncalls <= CSTACKERRMARK)  /* below error-handling zone? */
        luaD_throw(L, LUA_ERRERR);  /* error while handling stack error */
      else if (ncalls >= CSTACKMARK) {
        /* not in error-handling zone; raise the error now */
        L->nCcalls = (CSTACKMARK - 1);  /* enter error-handling zone */
        luaG_runerror(L, "C stack overflow");
      }
      /* else stack is in the error-handling zone;
         allow message handler to work */
    }
  }
}


l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
  luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
                   what, luaT_objtypename(L, o));
}


static const char *gxf (const Proto *p, int pc, Instruction i, int isup) {
  int t = GETARG_B(i);  /* table index */
  const char *name;  /* name of indexed variable */
  if (isup)  /* is an upvalue? */
    name = upvalname(p, t);
  else
    getobjname(p, pc, t, &name);
  return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field";
}


static int check_hbh_len(struct sk_buff *skb)
{
	unsigned char *raw = (u8 *)(ipv6_hdr(skb) + 1);
	u32 pkt_len;
	const unsigned char *nh = skb_network_header(skb);
	int off = raw - nh;
	int len = (raw[1] + 1) << 3;

	if ((raw + len) - skb->data > skb_headlen(skb))
		goto bad;

	off += 2;
	len -= 2;

	while (len > 0) {
		int optlen = nh[off + 1] + 2;

		switch (nh[off]) {
		case IPV6_TLV_PAD0:
			optlen = 1;
			break;

		case IPV6_TLV_PADN:
			break;

		case IPV6_TLV_JUMBO:
			if (nh[off + 1] != 4 || (off & 3) != 2)
				goto bad;
			pkt_len = ntohl(*(__be32 *) (nh + off + 2));
			if (pkt_len <= IPV6_MAXPLEN ||
			    ipv6_hdr(skb)->payload_len)
				goto bad;
			if (pkt_len > skb->len - sizeof(struct ipv6hdr))
				goto bad;
			if (pskb_trim_rcsum(skb,
					    pkt_len + sizeof(struct ipv6hdr)))
				goto bad;
			nh = skb_network_header(skb);
			break;
		default:
			if (optlen > len)
				goto bad;
			break;
		}
		off += optlen;
		len -= optlen;
	}
	if (len == 0)
		return 0;
bad:
	return -1;

}


static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
{
	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
	struct dst_entry *dst;

	skb->dev = bridge_parent(skb->dev);
	if (!skb->dev)
		goto free_skb;
	dst = skb_dst(skb);
	if (dst->hh) {
		neigh_hh_bridge(dst->hh, skb);
		skb->dev = nf_bridge->physindev;
		return br_handle_frame_finish(skb);
	} else if (dst->neighbour) {
		/* the neighbour function below overwrites the complete
		 * MAC header, so we save the Ethernet source address and
		 * protocol number. */
		skb_copy_from_linear_data_offset(skb, -(ETH_HLEN-ETH_ALEN), skb->nf_bridge->data, ETH_HLEN-ETH_ALEN);
		/* tell br_dev_xmit to continue with forwarding */
		nf_bridge->mask |= BRNF_BRIDGED_DNAT;
		return dst->neighbour->output(skb);
	}
free_skb:
	kfree_skb(skb);
	return 0;
}


static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb,
				      const struct net_device *in,
				      const struct net_device *out,
				      int (*okfn)(struct sk_buff *))
{
	struct net_bridge_port *p;
	struct net_bridge *br;
	struct net_device **d = (struct net_device **)(skb->cb);

	p = br_port_get_rcu(out);
	if (p == NULL)
		return NF_ACCEPT;
	br = p->br;

	if (!brnf_call_arptables && !br->nf_call_arptables)
		return NF_ACCEPT;

	if (skb->protocol != htons(ETH_P_ARP)) {
		if (!IS_VLAN_ARP(skb))
			return NF_ACCEPT;
		nf_bridge_pull_encap_header(skb);
	}

	if (arp_hdr(skb)->ar_pln != 4) {
		if (IS_VLAN_ARP(skb))
			nf_bridge_push_encap_header(skb);
		return NF_ACCEPT;
	}
	*d = (struct net_device *)in;
	NF_HOOK(NFPROTO_ARP, NF_ARP_FORWARD, skb, (struct net_device *)in,
		(struct net_device *)out, br_nf_forward_finish);

	return NF_STOLEN;
}


static int br_nf_pre_routing_finish(struct sk_buff *skb)
{
	struct net_device *dev = skb->dev;
	struct iphdr *iph = ip_hdr(skb);
	struct nf_bridge_info *nf_bridge = skb->nf_bridge;
	struct rtable *rt;
	int err;

	if (nf_bridge->mask & BRNF_PKT_TYPE) {
		skb->pkt_type = PACKET_OTHERHOST;
		nf_bridge->mask ^= BRNF_PKT_TYPE;
	}
	nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
	if (dnat_took_place(skb)) {
		if ((err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))) {
			struct in_device *in_dev = __in_dev_get_rcu(dev);

			/* If err equals -EHOSTUNREACH the error is due to a
			 * martian destination or due to the fact that
			 * forwarding is disabled. For most martian packets,
			 * ip_route_output_key() will fail. It won't fail for 2 types of
			 * martian destinations: loopback destinations and destination
			 * 0.0.0.0. In both cases the packet will be dropped because the
			 * destination is the loopback device and not the bridge. */
			if (err != -EHOSTUNREACH || !in_dev || IN_DEV_FORWARD(in_dev))
				goto free_skb;

			rt = ip_route_output(dev_net(dev), iph->daddr, 0,
					     RT_TOS(iph->tos), 0);
			if (!IS_ERR(rt)) {
				/* - Bridged-and-DNAT'ed traffic doesn't
				 *   require ip_forwarding. */
				if (rt->dst.dev == dev) {
					skb_dst_set(skb, &rt->dst);
					goto bridged_dnat;
				}
				ip_rt_put(rt);
			}
free_skb:
			kfree_skb(skb);
			return 0;
		} else {
			if (skb_dst(skb)->dev == dev) {
bridged_dnat:
				skb->dev = nf_bridge->physindev;
				nf_bridge_update_protocol(skb);
				nf_bridge_push_encap_header(skb);
				NF_HOOK_THRESH(NFPROTO_BRIDGE,
					       NF_BR_PRE_ROUTING,
					       skb, skb->dev, NULL,
					       br_nf_pre_routing_finish_bridge,
					       1);
				return 0;
			}
			memcpy(eth_hdr(skb)->h_dest, dev->dev_addr, ETH_ALEN);
			skb->pkt_type = PACKET_HOST;
		}
	} else {
		rt = bridge_parent_rtable(nf_bridge->physindev);
		if (!rt) {
			kfree_skb(skb);
			return 0;
		}
		skb_dst_set_noref(skb, &rt->dst);
	}

	skb->dev = nf_bridge->physindev;
	nf_bridge_update_protocol(skb);
	nf_bridge_push_encap_header(skb);
	NF_HOOK_THRESH(NFPROTO_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
		       br_handle_frame_finish, 1);

	return 0;
}


static int br_nf_dev_queue_xmit(struct sk_buff *skb)
{
	int ret;

	if (skb->nfct != NULL && skb->protocol == htons(ETH_P_IP) &&
	    skb->len + nf_bridge_mtu_reduction(skb) > skb->dev->mtu &&
	    !skb_is_gso(skb)) {
		if (br_parse_ip_options(skb))
			/* Drop invalid packet */
			return NF_DROP;
		ret = ip_fragment(skb, br_dev_queue_push_xmit);
	} else
		ret = br_dev_queue_push_xmit(skb);

	return ret;
}


imapx_copy_move_message_cache (CamelFolder *source_folder,
			       CamelFolder *destination_folder,
			       gboolean delete_originals,
			       const gchar *source_uid,
			       const gchar *destination_uid,
			       GCancellable *cancellable)
{
	CamelIMAPXFolder *imapx_source_folder, *imapx_destination_folder;
	gchar *source_filename, *destination_filename;

	g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (source_folder));
	g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (destination_folder));
	g_return_if_fail (source_uid != NULL);
	g_return_if_fail (destination_uid != NULL);

	imapx_source_folder = CAMEL_IMAPX_FOLDER (source_folder);
	imapx_destination_folder = CAMEL_IMAPX_FOLDER (destination_folder);

	source_filename = camel_data_cache_get_filename  (imapx_source_folder->cache, "cur", source_uid);
	if (!g_file_test (source_filename, G_FILE_TEST_EXISTS)) {
		g_free (source_filename);
		return;
	}

	destination_filename = camel_data_cache_get_filename  (imapx_destination_folder->cache, "cur", destination_uid);
	if (!g_file_test (destination_filename, G_FILE_TEST_EXISTS)) {
		GIOStream *stream;

		/* To create the cache folder structure for the message file */
		stream = camel_data_cache_add (imapx_destination_folder->cache, "cur", destination_uid, NULL);
		if (stream) {
			g_clear_object (&stream);

			/* Remove the empty file, it's gonna be replaced with actual message */
			g_unlink (destination_filename);

			if (delete_originals) {
				if (g_rename (source_filename, destination_filename) == -1 && errno != ENOENT) {
					g_warning ("%s: Failed to rename '%s' to '%s': %s", G_STRFUNC, source_filename, destination_filename, g_strerror (errno));
				}
			} else {
				GFile *source, *destination;
				GError *local_error = NULL;

				source = g_file_new_for_path (source_filename);
				destination = g_file_new_for_path (destination_filename);

				if (source && destination &&
				    !g_file_copy (source, destination, G_FILE_COPY_NONE, cancellable, NULL, NULL, &local_error)) {
					if (local_error) {
						g_warning ("%s: Failed to copy '%s' to '%s': %s", G_STRFUNC, source_filename, destination_filename, local_error->message);
					}
				}

				g_clear_object (&source);
				g_clear_object (&destination);
				g_clear_error (&local_error);
			}
		}
	}

	g_free (source_filename);
	g_free (destination_filename);
}


imapx_untagged_fetch (CamelIMAPXServer *is,
                      GInputStream *input_stream,
                      GCancellable *cancellable,
                      GError **error)
{
	struct _fetch_info *finfo;
	gboolean got_body_header;

	g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);

	finfo = imapx_parse_fetch (
		CAMEL_IMAPX_INPUT_STREAM (input_stream), cancellable, error);
	if (finfo == NULL) {
		imapx_free_fetch (finfo);
		return FALSE;
	}

	/* Some IMAP servers respond with BODY[HEADER] when
	 * asked for RFC822.HEADER.  Treat them equivalently. */
	got_body_header =
		((finfo->got & FETCH_HEADER) == 0) &&
		(finfo->header == NULL) &&
		((finfo->got & FETCH_BODY) != 0) &&
		(g_strcmp0 (finfo->section, "HEADER") == 0);

	if (got_body_header) {
		finfo->got |= FETCH_HEADER;
		finfo->got &= ~FETCH_BODY;
		finfo->header = finfo->body;
		finfo->body = NULL;
	}

	if ((finfo->got & (FETCH_BODY | FETCH_UID)) == (FETCH_BODY | FETCH_UID)) {
		GOutputStream *output_stream;
		gconstpointer body_data;
		gsize body_size;

		if (!is->priv->get_message_stream) {
			g_warn_if_fail (is->priv->get_message_stream != NULL);
			imapx_free_fetch (finfo);
			return FALSE;
		}

		/* Fill out the body stream, in the right spot. */

		g_seekable_seek (
			G_SEEKABLE (is->priv->get_message_stream),
			finfo->offset, G_SEEK_SET,
			NULL, NULL);

		output_stream = g_io_stream_get_output_stream (is->priv->get_message_stream);

		body_data = g_bytes_get_data (finfo->body, &body_size);

		/* Sometimes the server, like Microsoft Exchange, reports larger message
		   size than it actually is, which results in no data being read from
		   the server for that particular offset. */
		if (body_size) {
			g_mutex_lock (&is->priv->stream_lock);
			if (!g_output_stream_write_all (
				output_stream, body_data, body_size,
				NULL, cancellable, error)) {
				g_mutex_unlock (&is->priv->stream_lock);
				g_prefix_error (
					error, "%s: ",
					_("Error writing to cache stream"));
				imapx_free_fetch (finfo);
				return FALSE;
			}
			g_mutex_unlock (&is->priv->stream_lock);
		}
	}

	if ((finfo->got & FETCH_FLAGS) && !(finfo->got & FETCH_HEADER)) {
		CamelIMAPXMailbox *select_mailbox;
		CamelIMAPXMailbox *select_pending;

		if (is->priv->fetch_changes_mailbox) {
			if (!is->priv->fetch_changes_mailbox ||
			    !is->priv->fetch_changes_folder ||
			    !is->priv->fetch_changes_infos) {
				g_warn_if_fail (is->priv->fetch_changes_mailbox != NULL);
				g_warn_if_fail (is->priv->fetch_changes_folder != NULL);
				g_warn_if_fail (is->priv->fetch_changes_infos != NULL);
				imapx_free_fetch (finfo);
				return FALSE;
			}
		}

		g_mutex_lock (&is->priv->select_lock);
		select_mailbox = g_weak_ref_get (&is->priv->select_mailbox);
		select_pending = g_weak_ref_get (&is->priv->select_pending);
		g_mutex_unlock (&is->priv->select_lock);

		/* This is either a refresh_info job, check to see if it is
		 * and update if so, otherwise it must've been an unsolicited
		 * response, so update the summary to match. */
		if ((finfo->got & FETCH_UID) != 0 && is->priv->fetch_changes_folder && is->priv->fetch_changes_infos) {
			FetchChangesInfo *nfo;
			gint64 monotonic_time;
			gint n_messages;

			nfo = g_hash_table_lookup (is->priv->fetch_changes_infos, finfo->uid);
			if (!nfo) {
				nfo = g_slice_new0 (FetchChangesInfo);

				g_hash_table_insert (is->priv->fetch_changes_infos, (gpointer) camel_pstring_strdup (finfo->uid), nfo);
			}

			nfo->server_flags = finfo->flags;
			nfo->server_user_flags = finfo->user_flags;
			finfo->user_flags = NULL;

			monotonic_time = g_get_monotonic_time ();
			n_messages = camel_imapx_mailbox_get_messages (is->priv->fetch_changes_mailbox);

			if (n_messages > 0 && is->priv->fetch_changes_last_progress + G_USEC_PER_SEC / 2 < monotonic_time &&
			    is->priv->context && is->priv->context->id <= n_messages) {
				COMMAND_LOCK (is);

				if (is->priv->current_command) {
					guint32 n_messages;

					COMMAND_UNLOCK (is);

					is->priv->fetch_changes_last_progress = monotonic_time;

					n_messages = camel_imapx_mailbox_get_messages (is->priv->fetch_changes_mailbox);
					if (n_messages > 0)
						camel_operation_progress (cancellable, 100 * is->priv->context->id / n_messages);
				} else {
					COMMAND_UNLOCK (is);
				}
			}
		} else if (select_mailbox != NULL) {
			CamelFolder *select_folder;
			CamelMessageInfo *mi = NULL;
			gboolean changed = FALSE;
			gchar *uid = NULL;

			c (is->priv->tagprefix, "flag changed: %lu\n", is->priv->context->id);

			if (select_pending)
				select_folder = imapx_server_ref_folder (is, select_pending);
			else
				select_folder = imapx_server_ref_folder (is, select_mailbox);
			if (!select_folder) {
				g_warn_if_fail (select_folder != NULL);

				g_clear_object (&select_mailbox);
				g_clear_object (&select_pending);
				imapx_free_fetch (finfo);

				return FALSE;
			}

			if (finfo->got & FETCH_UID) {
				uid = finfo->uid;
				finfo->uid = NULL;
			} else {
				uid = camel_imapx_dup_uid_from_summary_index (
					select_folder,
					is->priv->context->id - 1);
			}

			if (uid) {
				mi = camel_folder_summary_get (camel_folder_get_folder_summary (select_folder), uid);
				if (mi) {
					/* It's unsolicited _unless_ select_pending (i.e. during
					 * a QRESYNC SELECT */
					changed = imapx_update_message_info_flags (
						mi, finfo->flags,
						finfo->user_flags,
						camel_imapx_mailbox_get_permanentflags (select_mailbox),
						select_folder,
						(select_pending == NULL));
					c (is->priv->tagprefix, "found uid %s in '%s', changed:%d\n", uid,
						camel_folder_get_full_name (select_folder), changed);
				} else {
					/* This (UID + FLAGS for previously unknown message) might
					 * happen during a SELECT (QRESYNC). We should use it. */
					c (is->priv->tagprefix, "flags changed for unknown uid %s in '%s'\n", uid,
						camel_folder_get_full_name (select_folder));
				}
			}

			if (changed) {
				CamelIMAPXSummary *imapx_summary;

				imapx_summary = CAMEL_IMAPX_SUMMARY (camel_folder_get_folder_summary (select_folder));

				if (imapx_summary && (finfo->got & FETCH_MODSEQ) != 0 &&
				    imapx_summary->modseq < finfo->modseq) {
					c (is->priv->tagprefix, "updating summary modseq %" G_GUINT64_FORMAT "~>%" G_GUINT64_FORMAT " in '%s'\n",
						imapx_summary->modseq, finfo->modseq, camel_folder_get_full_name (select_folder));
					imapx_summary->modseq = finfo->modseq;

					camel_folder_summary_touch (CAMEL_FOLDER_SUMMARY (imapx_summary));
				}

				g_mutex_lock (&is->priv->changes_lock);
				if (is->priv->changes)
					camel_folder_change_info_change_uid (is->priv->changes, uid);
				else
					g_warn_if_fail (is->priv->changes != NULL);
				g_mutex_unlock (&is->priv->changes_lock);
			}
			g_free (uid);

			if (changed && camel_imapx_server_is_in_idle (is)) {
				camel_folder_summary_save (camel_folder_get_folder_summary (select_folder), NULL);
				imapx_update_store_summary (select_folder);

				g_mutex_lock (&is->priv->changes_lock);

				camel_folder_changed (select_folder, is->priv->changes);
				camel_folder_change_info_clear (is->priv->changes);

				g_mutex_unlock (&is->priv->changes_lock);
			}

			g_clear_object (&mi);

			g_object_unref (select_folder);
		}

		g_clear_object (&select_mailbox);
		g_clear_object (&select_pending);
	}

	if ((finfo->got & (FETCH_HEADER | FETCH_UID)) == (FETCH_HEADER | FETCH_UID)) {
		CamelIMAPXMailbox *mailbox;
		CamelFolder *folder;
		CamelMimeParser *mp;
		CamelMessageInfo *mi;
		guint32 messages;
		guint32 unseen;
		guint32 uidnext;

		/* This must be a refresh info job as well, but it has
		 * asked for new messages to be added to the index. */

		if (is->priv->fetch_changes_mailbox) {
			if (!is->priv->fetch_changes_mailbox ||
			    !is->priv->fetch_changes_folder ||
			    !is->priv->fetch_changes_infos) {
				g_warn_if_fail (is->priv->fetch_changes_mailbox != NULL);
				g_warn_if_fail (is->priv->fetch_changes_folder != NULL);
				g_warn_if_fail (is->priv->fetch_changes_infos != NULL);
				imapx_free_fetch (finfo);
				return FALSE;
			}

			folder = g_object_ref (is->priv->fetch_changes_folder);
			mailbox = g_object_ref (is->priv->fetch_changes_mailbox);
		} else {
			mailbox = camel_imapx_server_ref_selected (is);
			folder = mailbox ? imapx_server_ref_folder (is, mailbox) : NULL;
		}

		if (!mailbox || !folder || (!(finfo->got & FETCH_FLAGS) && !is->priv->fetch_changes_infos)) {
			g_clear_object (&mailbox);
			g_clear_object (&folder);
			imapx_free_fetch (finfo);

			return TRUE;
		}

		messages = camel_imapx_mailbox_get_messages (mailbox);
		unseen = camel_imapx_mailbox_get_unseen (mailbox);
		uidnext = camel_imapx_mailbox_get_uidnext (mailbox);

		/* Do we want to save these headers for later too?  Do we care? */

		mp = camel_mime_parser_new ();
		camel_mime_parser_init_with_bytes (mp, finfo->header);
		mi = camel_folder_summary_info_new_from_parser (camel_folder_get_folder_summary (folder), mp);
		g_object_unref (mp);

		if (mi != NULL) {
			guint32 server_flags;
			CamelNamedFlags *server_user_flags;
			gboolean free_user_flags = FALSE;

			camel_message_info_set_abort_notifications (mi, TRUE);

			camel_message_info_set_uid (mi, finfo->uid);

			if ((finfo->got & FETCH_CINFO) && finfo->cinfo) {
				gboolean has_attachment = FALSE;

				camel_message_content_info_traverse (finfo->cinfo, imapx_server_cinfo_has_attachment_cb, &has_attachment);

				camel_message_info_set_flags (mi, CAMEL_MESSAGE_ATTACHMENTS, has_attachment ? CAMEL_MESSAGE_ATTACHMENTS : 0);
			}

			if (!(finfo->got & FETCH_FLAGS) && is->priv->fetch_changes_infos) {
				FetchChangesInfo *nfo;

				nfo = g_hash_table_lookup (is->priv->fetch_changes_infos, finfo->uid);
				if (!nfo) {
					g_warn_if_fail (nfo != NULL);

					camel_message_info_set_abort_notifications (mi, FALSE);
					g_clear_object (&mi);
					g_clear_object (&mailbox);
					g_clear_object (&folder);
					imapx_free_fetch (finfo);

					return FALSE;
				}

				server_flags = nfo->server_flags;
				server_user_flags = nfo->server_user_flags;
			} else {
				server_flags = finfo->flags;
				server_user_flags = finfo->user_flags;
				/* free user_flags ? */
				finfo->user_flags = NULL;
				free_user_flags = TRUE;
			}

			/* If the message is a really new one -- equal or higher than what
			 * we know as UIDNEXT for the folder, then it came in since we last
			 * fetched UIDNEXT and UNREAD count. We'll update UIDNEXT in the
			 * command completion, but update UNREAD count now according to the
			 * message SEEN flag */
			if (!(server_flags & CAMEL_MESSAGE_SEEN)) {
				guint64 uidl;

				uidl = strtoull (finfo->uid, NULL, 10);

				if (uidl >= uidnext) {
					c (is->priv->tagprefix, "Updating unseen count for new message %s\n", finfo->uid);
					camel_imapx_mailbox_set_unseen (mailbox, unseen + 1);
				} else {
					c (is->priv->tagprefix, "Not updating unseen count for new message %s\n", finfo->uid);
				}
			}

			camel_message_info_set_size (mi, finfo->size);
			camel_message_info_set_abort_notifications (mi, FALSE);

			camel_folder_summary_lock (camel_folder_get_folder_summary (folder));

			if (!camel_folder_summary_check_uid (camel_folder_get_folder_summary (folder), finfo->uid)) {
				imapx_set_message_info_flags_for_new_message (mi, server_flags, server_user_flags, FALSE, NULL, camel_imapx_mailbox_get_permanentflags (mailbox));
				camel_folder_summary_add (camel_folder_get_folder_summary (folder), mi, TRUE);

				g_mutex_lock (&is->priv->changes_lock);

				camel_folder_change_info_add_uid (is->priv->changes, finfo->uid);
				camel_folder_change_info_recent_uid (is->priv->changes, finfo->uid);

				g_mutex_unlock (&is->priv->changes_lock);

				if (messages > 0) {
					gint cnt = (camel_folder_summary_count (camel_folder_get_folder_summary (folder)) * 100) / messages;

					camel_operation_progress (cancellable, cnt ? cnt : 1);
				}

				if (camel_imapx_server_is_in_idle (is) && !camel_folder_is_frozen (folder))
					camel_folder_summary_save (camel_folder_get_folder_summary (folder), NULL);
			}

			g_clear_object (&mi);
			camel_folder_summary_unlock (camel_folder_get_folder_summary (folder));

			if (free_user_flags)
				camel_named_flags_free (server_user_flags);

			if (camel_imapx_server_is_in_idle (is) && !camel_folder_is_frozen (folder)) {
				CamelFolderChangeInfo *changes = NULL;

				g_mutex_lock (&is->priv->changes_lock);

				if (camel_folder_change_info_changed (is->priv->changes)) {
					changes = is->priv->changes;
					is->priv->changes = camel_folder_change_info_new ();
				}

				g_mutex_unlock (&is->priv->changes_lock);

				if (changes) {
					imapx_update_store_summary (folder);
					camel_folder_changed (folder, changes);

					camel_folder_change_info_free (changes);
				}
			}
		}

		g_clear_object (&mailbox);
		g_clear_object (&folder);
	}

	imapx_free_fetch (finfo);

	return TRUE;
}


CheckModifierMap(XkbDescPtr xkb, xkbSetMapReq * req, CARD8 **wireRtrn,
                 int *errRtrn)
{
    register CARD8 *wire = *wireRtrn;
    CARD8 *start;
    register unsigned i;
    int first, last;

    if (((req->present & XkbModifierMapMask) == 0) || (req->nModMapKeys < 1)) {
        req->present &= ~XkbModifierMapMask;
        req->nModMapKeys = 0;
        return 1;
    }
    first = req->firstModMapKey;
    last = first + req->nModMapKeys - 1;
    if (first < req->minKeyCode) {
        *errRtrn = _XkbErrCode3(0x61, first, req->minKeyCode);
        return 0;
    }
    if (last > req->maxKeyCode) {
        *errRtrn = _XkbErrCode3(0x62, last, req->maxKeyCode);
        return 0;
    }
    start = wire;
    for (i = 0; i < req->totalModMapKeys; i++, wire += 2) {
        if ((wire[0] < first) || (wire[0] > last)) {
            *errRtrn = _XkbErrCode4(0x63, first, last, wire[0]);
            return 0;
        }
    }
    wire += XkbPaddedSize(wire - start) - (wire - start);
    *wireRtrn = wire;
    return 1;
}


CheckDeviceLedFBs(DeviceIntPtr dev,
                  int class,
                  int id, xkbGetDeviceInfoReply * rep, ClientPtr client)
{
    int nFBs = 0;
    int length = 0;
    Bool classOk;

    if (class == XkbDfltXIClass) {
        if (dev->kbdfeed)
            class = KbdFeedbackClass;
        else if (dev->leds)
            class = LedFeedbackClass;
        else {
            client->errorValue = _XkbErrCode2(XkbErr_BadClass, class);
            return XkbKeyboardErrorCode;
        }
    }
    classOk = FALSE;
    if ((dev->kbdfeed) &&
        ((class == KbdFeedbackClass) || (class == XkbAllXIClasses))) {
        KbdFeedbackPtr kf;

        classOk = TRUE;
        for (kf = dev->kbdfeed; (kf); kf = kf->next) {
            if ((id != XkbAllXIIds) && (id != XkbDfltXIId) &&
                (id != kf->ctrl.id))
                continue;
            nFBs++;
            length += SIZEOF(xkbDeviceLedsWireDesc);
            if (!kf->xkb_sli)
                kf->xkb_sli = XkbAllocSrvLedInfo(dev, kf, NULL, 0);
            length += ComputeDeviceLedInfoSize(dev, rep->present, kf->xkb_sli);
            if (id != XkbAllXIIds)
                break;
        }
    }
    if ((dev->leds) &&
        ((class == LedFeedbackClass) || (class == XkbAllXIClasses))) {
        LedFeedbackPtr lf;

        classOk = TRUE;
        for (lf = dev->leds; (lf); lf = lf->next) {
            if ((id != XkbAllXIIds) && (id != XkbDfltXIId) &&
                (id != lf->ctrl.id))
                continue;
            nFBs++;
            length += SIZEOF(xkbDeviceLedsWireDesc);
            if (!lf->xkb_sli)
                lf->xkb_sli = XkbAllocSrvLedInfo(dev, NULL, lf, 0);
            length += ComputeDeviceLedInfoSize(dev, rep->present, lf->xkb_sli);
            if (id != XkbAllXIIds)
                break;
        }
    }
    if (nFBs > 0) {
        rep->nDeviceLedFBs = nFBs;
        rep->length += (length / 4);
        return Success;
    }
    if (classOk)
        client->errorValue = _XkbErrCode2(XkbErr_BadId, id);
    else
        client->errorValue = _XkbErrCode2(XkbErr_BadClass, class);
    return XkbKeyboardErrorCode;
}


XkbWriteGeomOverlay(char *wire, XkbOverlayPtr ol, Bool swap)
{
    register int r;
    XkbOverlayRowPtr row;
    xkbOverlayWireDesc *olWire;

    olWire = (xkbOverlayWireDesc *) wire;
    olWire->name = ol->name;
    olWire->nRows = ol->num_rows;
    olWire->pad1 = 0;
    olWire->pad2 = 0;
    if (swap) {
        swapl(&olWire->name);
    }
    wire = (char *) &olWire[1];
    for (r = 0, row = ol->rows; r < ol->num_rows; r++, row++) {
        unsigned int k;
        XkbOverlayKeyPtr key;
        xkbOverlayRowWireDesc *rowWire;

        rowWire = (xkbOverlayRowWireDesc *) wire;
        rowWire->rowUnder = row->row_under;
        rowWire->nKeys = row->num_keys;
        rowWire->pad1 = 0;
        wire = (char *) &rowWire[1];
        for (k = 0, key = row->keys; k < row->num_keys; k++, key++) {
            xkbOverlayKeyWireDesc *keyWire;

            keyWire = (xkbOverlayKeyWireDesc *) wire;
            memcpy(keyWire->over, key->over.name, XkbKeyNameLength);
            memcpy(keyWire->under, key->under.name, XkbKeyNameLength);
            wire = (char *) &keyWire[1];
        }
    }
    return wire;
}


_XkbSetNames(ClientPtr client, DeviceIntPtr dev, xkbSetNamesReq * stuff)
{
    XkbDescRec *xkb;
    XkbNamesRec *names;
    CARD32 *tmp;
    xkbNamesNotify nn;

    tmp = (CARD32 *) &stuff[1];
    xkb = dev->key->xkbInfo->desc;
    names = xkb->names;

    if (XkbAllocNames(xkb, stuff->which, stuff->nRadioGroups,
                      stuff->nKeyAliases) != Success) {
        return BadAlloc;
    }

    memset(&nn, 0, sizeof(xkbNamesNotify));
    nn.changed = stuff->which;
    tmp = (CARD32 *) &stuff[1];
    if (stuff->which & XkbKeycodesNameMask)
        names->keycodes = *tmp++;
    if (stuff->which & XkbGeometryNameMask)
        names->geometry = *tmp++;
    if (stuff->which & XkbSymbolsNameMask)
        names->symbols = *tmp++;
    if (stuff->which & XkbPhysSymbolsNameMask)
        names->phys_symbols = *tmp++;
    if (stuff->which & XkbTypesNameMask)
        names->types = *tmp++;
    if (stuff->which & XkbCompatNameMask)
        names->compat = *tmp++;
    if ((stuff->which & XkbKeyTypeNamesMask) && (stuff->nTypes > 0)) {
        register unsigned i;
        register XkbKeyTypePtr type;

        type = &xkb->map->types[stuff->firstType];
        for (i = 0; i < stuff->nTypes; i++, type++) {
            type->name = *tmp++;
        }
        nn.firstType = stuff->firstType;
        nn.nTypes = stuff->nTypes;
    }
    if (stuff->which & XkbKTLevelNamesMask) {
        register XkbKeyTypePtr type;
        register unsigned i;
        CARD8 *width;

        width = (CARD8 *) tmp;
        tmp = (CARD32 *) (((char *) tmp) + XkbPaddedSize(stuff->nKTLevels));
        type = &xkb->map->types[stuff->firstKTLevel];
        for (i = 0; i < stuff->nKTLevels; i++, type++) {
            if (width[i] > 0) {
                if (type->level_names) {
                    register unsigned n;

                    for (n = 0; n < width[i]; n++) {
                        type->level_names[n] = tmp[n];
                    }
                }
                tmp += width[i];
            }
        }
        nn.firstLevelName = 0;
        nn.nLevelNames = stuff->nTypes;
    }
    if (stuff->which & XkbIndicatorNamesMask) {
        tmp = _XkbCopyMaskedAtoms(tmp, names->indicators, XkbNumIndicators,
                                  stuff->indicators);
        nn.changedIndicators = stuff->indicators;
    }
    if (stuff->which & XkbVirtualModNamesMask) {
        tmp = _XkbCopyMaskedAtoms(tmp, names->vmods, XkbNumVirtualMods,
                                  stuff->virtualMods);
        nn.changedVirtualMods = stuff->virtualMods;
    }
    if (stuff->which & XkbGroupNamesMask) {
        tmp = _XkbCopyMaskedAtoms(tmp, names->groups, XkbNumKbdGroups,
                                  stuff->groupNames);
        nn.changedVirtualMods = stuff->groupNames;
    }
    if (stuff->which & XkbKeyNamesMask) {
        memcpy((char *) &names->keys[stuff->firstKey], (char *) tmp,
               stuff->nKeys * XkbKeyNameLength);
        tmp += stuff->nKeys;
        nn.firstKey = stuff->firstKey;
        nn.nKeys = stuff->nKeys;
    }
    if (stuff->which & XkbKeyAliasesMask) {
        if (stuff->nKeyAliases > 0) {
            register int na = stuff->nKeyAliases;

            if (XkbAllocNames(xkb, XkbKeyAliasesMask, 0, na) != Success)
                return BadAlloc;
            memcpy((char *) names->key_aliases, (char *) tmp,
                   stuff->nKeyAliases * sizeof(XkbKeyAliasRec));
            tmp += stuff->nKeyAliases * 2;
        }
        else if (names->key_aliases != NULL) {
            free(names->key_aliases);
            names->key_aliases = NULL;
            names->num_key_aliases = 0;
        }
        nn.nAliases = names->num_key_aliases;
    }
    if (stuff->which & XkbRGNamesMask) {
        if (stuff->nRadioGroups > 0) {
            register unsigned i, nrg;

            nrg = stuff->nRadioGroups;
            if (XkbAllocNames(xkb, XkbRGNamesMask, nrg, 0) != Success)
                return BadAlloc;

            for (i = 0; i < stuff->nRadioGroups; i++) {
                names->radio_groups[i] = tmp[i];
            }
            tmp += stuff->nRadioGroups;
        }
        else if (names->radio_groups) {
            free(names->radio_groups);
            names->radio_groups = NULL;
            names->num_rg = 0;
        }
        nn.nRadioGroups = names->num_rg;
    }
    if (nn.changed) {
        Bool needExtEvent;

        needExtEvent = (nn.changed & XkbIndicatorNamesMask) != 0;
        XkbSendNamesNotify(dev, &nn);
        if (needExtEvent) {
            XkbSrvLedInfoPtr sli;
            xkbExtensionDeviceNotify edev;
            register int i;
            register unsigned bit;

            sli = XkbFindSrvLedInfo(dev, XkbDfltXIClass, XkbDfltXIId,
                                    XkbXI_IndicatorsMask);
            sli->namesPresent = 0;
            for (i = 0, bit = 1; i < XkbNumIndicators; i++, bit <<= 1) {
                if (names->indicators[i] != None)
                    sli->namesPresent |= bit;
            }
            memset(&edev, 0, sizeof(xkbExtensionDeviceNotify));
            edev.reason = XkbXI_IndicatorNamesMask;
            edev.ledClass = KbdFeedbackClass;
            edev.ledID = dev->kbdfeed->ctrl.id;
            edev.ledsDefined = sli->namesPresent | sli->mapsPresent;
            edev.ledState = sli->effectiveState;
            edev.firstBtn = 0;
            edev.nBtns = 0;
            edev.supported = XkbXI_AllFeaturesMask;
            edev.unsupported = 0;
            XkbSendExtensionDeviceNotify(dev, client, &edev);
        }
    }
    return Success;
}


CheckKeySyms(ClientPtr client,
             XkbDescPtr xkb,
             xkbSetMapReq * req,
             int nTypes,
             CARD8 *mapWidths,
             CARD16 *symsPerKey, xkbSymMapWireDesc ** wireRtrn, int *errorRtrn, Bool doswap)
{
    register unsigned i;
    XkbSymMapPtr map;
    xkbSymMapWireDesc *wire = *wireRtrn;

    if (!(XkbKeySymsMask & req->present))
        return 1;
    CHK_REQ_KEY_RANGE2(0x11, req->firstKeySym, req->nKeySyms, req, (*errorRtrn),
                       0);
    for (i = 0; i < req->nKeySyms; i++) {
        KeySym *pSyms;
        register unsigned nG;

        if (client->swapped && doswap) {
            swaps(&wire->nSyms);
        }
        nG = XkbNumGroups(wire->groupInfo);
        if (nG > XkbNumKbdGroups) {
            *errorRtrn = _XkbErrCode3(0x14, i + req->firstKeySym, nG);
            return 0;
        }
        if (nG > 0) {
            register int g, w;

            for (g = w = 0; g < nG; g++) {
                if (wire->ktIndex[g] >= (unsigned) nTypes) {
                    *errorRtrn = _XkbErrCode4(0x15, i + req->firstKeySym, g,
                                              wire->ktIndex[g]);
                    return 0;
                }
                if (mapWidths[wire->ktIndex[g]] > w)
                    w = mapWidths[wire->ktIndex[g]];
            }
            if (wire->width != w) {
                *errorRtrn =
                    _XkbErrCode3(0x16, i + req->firstKeySym, wire->width);
                return 0;
            }
            w *= nG;
            symsPerKey[i + req->firstKeySym] = w;
            if (w != wire->nSyms) {
                *errorRtrn =
                    _XkbErrCode4(0x16, i + req->firstKeySym, wire->nSyms, w);
                return 0;
            }
        }
        else if (wire->nSyms != 0) {
            *errorRtrn = _XkbErrCode3(0x17, i + req->firstKeySym, wire->nSyms);
            return 0;
        }
        pSyms = (KeySym *) &wire[1];
        wire = (xkbSymMapWireDesc *) &pSyms[wire->nSyms];
    }

    map = &xkb->map->key_sym_map[i];
    for (; i <= (unsigned) xkb->max_key_code; i++, map++) {
        register int g, nG, w;

        nG = XkbKeyNumGroups(xkb, i);
        for (w = g = 0; g < nG; g++) {
            if (map->kt_index[g] >= (unsigned) nTypes) {
                *errorRtrn = _XkbErrCode4(0x18, i, g, map->kt_index[g]);
                return 0;
            }
            if (mapWidths[map->kt_index[g]] > w)
                w = mapWidths[map->kt_index[g]];
        }
        symsPerKey[i] = w * nG;
    }
    *wireRtrn = wire;
    return 1;
}


XkbSendIndicatorMap(ClientPtr client,
                    XkbIndicatorPtr indicators, xkbGetIndicatorMapReply * rep)
{
    int length;
    CARD8 *map;
    register int i;
    register unsigned bit;

    if (rep->length > 0) {
        CARD8 *to;

        to = map = xallocarray(rep->length, 4);
        if (map) {
            xkbIndicatorMapWireDesc *wire = (xkbIndicatorMapWireDesc *) to;

            length = rep->length * 4;

            for (i = 0, bit = 1; i < XkbNumIndicators; i++, bit <<= 1) {
                if (rep->which & bit) {
                    wire->flags = indicators->maps[i].flags;
                    wire->whichGroups = indicators->maps[i].which_groups;
                    wire->groups = indicators->maps[i].groups;
                    wire->whichMods = indicators->maps[i].which_mods;
                    wire->mods = indicators->maps[i].mods.mask;
                    wire->realMods = indicators->maps[i].mods.real_mods;
                    wire->virtualMods = indicators->maps[i].mods.vmods;
                    wire->ctrls = indicators->maps[i].ctrls;
                    if (client->swapped) {
                        swaps(&wire->virtualMods);
                        swapl(&wire->ctrls);
                    }
                    wire++;
                }
            }
            to = (CARD8 *) wire;
            if ((to - map) != length) {
                client->errorValue = _XkbErrCode2(0xff, length);
                free(map);
                return BadLength;
            }
        }
        else
            return BadAlloc;
    }
    else
        map = NULL;
    if (client->swapped) {
        swaps(&rep->sequenceNumber);
        swapl(&rep->length);
        swapl(&rep->which);
        swapl(&rep->realIndicators);
    }
    WriteToClient(client, SIZEOF(xkbGetIndicatorMapReply), rep);
    if (map) {
        WriteToClient(client, length, map);
        free((char *) map);
    }
    return Success;
}


XkbWriteGeomDoodads(char *wire, int num_doodads, XkbDoodadPtr doodad, Bool swap)
{
    register int i;
    xkbDoodadWireDesc *doodadWire;

    for (i = 0; i < num_doodads; i++, doodad++) {
        doodadWire = (xkbDoodadWireDesc *) wire;
        wire = (char *) &doodadWire[1];
        memset(doodadWire, 0, SIZEOF(xkbDoodadWireDesc));
        doodadWire->any.name = doodad->any.name;
        doodadWire->any.type = doodad->any.type;
        doodadWire->any.priority = doodad->any.priority;
        doodadWire->any.top = doodad->any.top;
        doodadWire->any.left = doodad->any.left;
        if (swap) {
            swapl(&doodadWire->any.name);
            swaps(&doodadWire->any.top);
            swaps(&doodadWire->any.left);
        }
        switch (doodad->any.type) {
        case XkbOutlineDoodad:
        case XkbSolidDoodad:
            doodadWire->shape.angle = doodad->shape.angle;
            doodadWire->shape.colorNdx = doodad->shape.color_ndx;
            doodadWire->shape.shapeNdx = doodad->shape.shape_ndx;
            if (swap) {
                swaps(&doodadWire->shape.angle);
            }
            break;
        case XkbTextDoodad:
            doodadWire->text.angle = doodad->text.angle;
            doodadWire->text.width = doodad->text.width;
            doodadWire->text.height = doodad->text.height;
            doodadWire->text.colorNdx = doodad->text.color_ndx;
            if (swap) {
                swaps(&doodadWire->text.angle);
                swaps(&doodadWire->text.width);
                swaps(&doodadWire->text.height);
            }
            wire = XkbWriteCountedString(wire, doodad->text.text, swap);
            wire = XkbWriteCountedString(wire, doodad->text.font, swap);
            break;
        case XkbIndicatorDoodad:
            doodadWire->indicator.shapeNdx = doodad->indicator.shape_ndx;
            doodadWire->indicator.onColorNdx = doodad->indicator.on_color_ndx;
            doodadWire->indicator.offColorNdx = doodad->indicator.off_color_ndx;
            break;
        case XkbLogoDoodad:
            doodadWire->logo.angle = doodad->logo.angle;
            doodadWire->logo.colorNdx = doodad->logo.color_ndx;
            doodadWire->logo.shapeNdx = doodad->logo.shape_ndx;
            wire = XkbWriteCountedString(wire, doodad->logo.logo_name, swap);
            break;
        default:
            ErrorF("[xkb] Unknown doodad type %d in XkbWriteGeomDoodads\n",
                   doodad->any.type);
            ErrorF("[xkb] Ignored\n");
            break;
        }
    }
    return wire;
}


ProcXkbSetCompatMap(ClientPtr client)
{
    DeviceIntPtr dev;
    char *data;
    int rc;

    REQUEST(xkbSetCompatMapReq);
    REQUEST_AT_LEAST_SIZE(xkbSetCompatMapReq);

    if (!(client->xkbClientFlags & _XkbClientInitialized))
        return BadAccess;

    CHK_KBD_DEVICE(dev, stuff->deviceSpec, client, DixManageAccess);

    data = (char *) &stuff[1];

    /* check first using a dry-run */
    rc = _XkbSetCompatMap(client, dev, stuff, data, TRUE);
    if (rc != Success)
        return rc;
    if (stuff->deviceSpec == XkbUseCoreKbd) {
        DeviceIntPtr other;

        for (other = inputInfo.devices; other; other = other->next) {
            if ((other != dev) && other->key && !IsMaster(other) &&
                GetMaster(other, MASTER_KEYBOARD) == dev) {
                rc = XaceHook(XACE_DEVICE_ACCESS, client, other,
                              DixManageAccess);
                if (rc == Success) {
                    /* dry-run */
                    rc = _XkbSetCompatMap(client, other, stuff, data, TRUE);
                    if (rc != Success)
                        return rc;
                }
            }
        }
    }

    /* Yay, the dry-runs succeed. Let's apply */
    rc = _XkbSetCompatMap(client, dev, stuff, data, FALSE);
    if (rc != Success)
        return rc;
    if (stuff->deviceSpec == XkbUseCoreKbd) {
        DeviceIntPtr other;

        for (other = inputInfo.devices; other; other = other->next) {
            if ((other != dev) && other->key && !IsMaster(other) &&
                GetMaster(other, MASTER_KEYBOARD) == dev) {
                rc = XaceHook(XACE_DEVICE_ACCESS, client, other,
                              DixManageAccess);
                if (rc == Success) {
                    rc = _XkbSetCompatMap(client, other, stuff, data, FALSE);
                    if (rc != Success)
                        return rc;
                }
            }
        }
    }

    return Success;
}


ProcXkbSetIndicatorMap(ClientPtr client)
{
    int i, bit;
    int nIndicators;
    DeviceIntPtr dev;
    xkbIndicatorMapWireDesc *from;
    int rc;

    REQUEST(xkbSetIndicatorMapReq);
    REQUEST_AT_LEAST_SIZE(xkbSetIndicatorMapReq);

    if (!(client->xkbClientFlags & _XkbClientInitialized))
        return BadAccess;

    CHK_KBD_DEVICE(dev, stuff->deviceSpec, client, DixSetAttrAccess);

    if (stuff->which == 0)
        return Success;

    for (nIndicators = i = 0, bit = 1; i < XkbNumIndicators; i++, bit <<= 1) {
        if (stuff->which & bit)
            nIndicators++;
    }
    if (stuff->length != ((SIZEOF(xkbSetIndicatorMapReq) +
                           (nIndicators * SIZEOF(xkbIndicatorMapWireDesc))) /
                          4)) {
        return BadLength;
    }

    from = (xkbIndicatorMapWireDesc *) &stuff[1];
    for (i = 0, bit = 1; i < XkbNumIndicators; i++, bit <<= 1) {
        if (stuff->which & bit) {
            if (client->swapped) {
                swaps(&from->virtualMods);
                swapl(&from->ctrls);
            }
            CHK_MASK_LEGAL(i, from->whichGroups, XkbIM_UseAnyGroup);
            CHK_MASK_LEGAL(i, from->whichMods, XkbIM_UseAnyMods);
            from++;
        }
    }

    from = (xkbIndicatorMapWireDesc *) &stuff[1];
    rc = _XkbSetIndicatorMap(client, dev, stuff->which, from);
    if (rc != Success)
        return rc;

    if (stuff->deviceSpec == XkbUseCoreKbd) {
        DeviceIntPtr other;

        for (other = inputInfo.devices; other; other = other->next) {
            if ((other != dev) && other->key && !IsMaster(other) &&
                GetMaster(other, MASTER_KEYBOARD) == dev) {
                rc = XaceHook(XACE_DEVICE_ACCESS, client, other,
                              DixSetAttrAccess);
                if (rc == Success)
                    _XkbSetIndicatorMap(client, other, stuff->which, from);
            }
        }
    }

    return Success;
}


SendDeviceLedFBs(DeviceIntPtr dev,
                 int class, int id, unsigned wantLength, ClientPtr client)
{
    int length = 0;

    if (class == XkbDfltXIClass) {
        if (dev->kbdfeed)
            class = KbdFeedbackClass;
        else if (dev->leds)
            class = LedFeedbackClass;
    }
    if ((dev->kbdfeed) &&
        ((class == KbdFeedbackClass) || (class == XkbAllXIClasses))) {
        KbdFeedbackPtr kf;

        for (kf = dev->kbdfeed; (kf); kf = kf->next) {
            if ((id == XkbAllXIIds) || (id == XkbDfltXIId) ||
                (id == kf->ctrl.id)) {
                length += SendDeviceLedInfo(kf->xkb_sli, client);
                if (id != XkbAllXIIds)
                    break;
            }
        }
    }
    if ((dev->leds) &&
        ((class == LedFeedbackClass) || (class == XkbAllXIClasses))) {
        LedFeedbackPtr lf;

        for (lf = dev->leds; (lf); lf = lf->next) {
            if ((id == XkbAllXIIds) || (id == XkbDfltXIId) ||
                (id == lf->ctrl.id)) {
                length += SendDeviceLedInfo(lf->xkb_sli, client);
                if (id != XkbAllXIIds)
                    break;
            }
        }
    }
    if (length == wantLength)
        return Success;
    else
        return BadLength;
}


ProcXkbSelectEvents(ClientPtr client)
{
    unsigned legal;
    DeviceIntPtr dev;
    XkbInterestPtr masks;

    REQUEST(xkbSelectEventsReq);

    REQUEST_AT_LEAST_SIZE(xkbSelectEventsReq);

    if (!(client->xkbClientFlags & _XkbClientInitialized))
        return BadAccess;

    CHK_ANY_DEVICE(dev, stuff->deviceSpec, client, DixUseAccess);

    if (((stuff->affectWhich & XkbMapNotifyMask) != 0) && (stuff->affectMap)) {
        client->mapNotifyMask &= ~stuff->affectMap;
        client->mapNotifyMask |= (stuff->affectMap & stuff->map);
    }
    if ((stuff->affectWhich & (~XkbMapNotifyMask)) == 0)
        return Success;

    masks = XkbFindClientResource((DevicePtr) dev, client);
    if (!masks) {
        XID id = FakeClientID(client->index);

        if (!AddResource(id, RT_XKBCLIENT, dev))
            return BadAlloc;
        masks = XkbAddClientResource((DevicePtr) dev, client, id);
    }
    if (masks) {
        union {
            CARD8 *c8;
            CARD16 *c16;
            CARD32 *c32;
        } from, to;
        register unsigned bit, ndx, maskLeft, dataLeft, size;

        from.c8 = (CARD8 *) &stuff[1];
        dataLeft = (stuff->length * 4) - SIZEOF(xkbSelectEventsReq);
        maskLeft = (stuff->affectWhich & (~XkbMapNotifyMask));
        for (ndx = 0, bit = 1; (maskLeft != 0); ndx++, bit <<= 1) {
            if ((bit & maskLeft) == 0)
                continue;
            maskLeft &= ~bit;
            switch (ndx) {
            case XkbNewKeyboardNotify:
                to.c16 = &client->newKeyboardNotifyMask;
                legal = XkbAllNewKeyboardEventsMask;
                size = 2;
                break;
            case XkbStateNotify:
                to.c16 = &masks->stateNotifyMask;
                legal = XkbAllStateEventsMask;
                size = 2;
                break;
            case XkbControlsNotify:
                to.c32 = &masks->ctrlsNotifyMask;
                legal = XkbAllControlEventsMask;
                size = 4;
                break;
            case XkbIndicatorStateNotify:
                to.c32 = &masks->iStateNotifyMask;
                legal = XkbAllIndicatorEventsMask;
                size = 4;
                break;
            case XkbIndicatorMapNotify:
                to.c32 = &masks->iMapNotifyMask;
                legal = XkbAllIndicatorEventsMask;
                size = 4;
                break;
            case XkbNamesNotify:
                to.c16 = &masks->namesNotifyMask;
                legal = XkbAllNameEventsMask;
                size = 2;
                break;
            case XkbCompatMapNotify:
                to.c8 = &masks->compatNotifyMask;
                legal = XkbAllCompatMapEventsMask;
                size = 1;
                break;
            case XkbBellNotify:
                to.c8 = &masks->bellNotifyMask;
                legal = XkbAllBellEventsMask;
                size = 1;
                break;
            case XkbActionMessage:
                to.c8 = &masks->actionMessageMask;
                legal = XkbAllActionMessagesMask;
                size = 1;
                break;
            case XkbAccessXNotify:
                to.c16 = &masks->accessXNotifyMask;
                legal = XkbAllAccessXEventsMask;
                size = 2;
                break;
            case XkbExtensionDeviceNotify:
                to.c16 = &masks->extDevNotifyMask;
                legal = XkbAllExtensionDeviceEventsMask;
                size = 2;
                break;
            default:
                client->errorValue = _XkbErrCode2(33, bit);
                return BadValue;
            }

            if (stuff->clear & bit) {
                if (size == 2)
                    to.c16[0] = 0;
                else if (size == 4)
                    to.c32[0] = 0;
                else
                    to.c8[0] = 0;
            }
            else if (stuff->selectAll & bit) {
                if (size == 2)
                    to.c16[0] = ~0;
                else if (size == 4)
                    to.c32[0] = ~0;
                else
                    to.c8[0] = ~0;
            }
            else {
                if (dataLeft < (size * 2))
                    return BadLength;
                if (size == 2) {
                    CHK_MASK_MATCH(ndx, from.c16[0], from.c16[1]);
                    CHK_MASK_LEGAL(ndx, from.c16[0], legal);
                    to.c16[0] &= ~from.c16[0];
                    to.c16[0] |= (from.c16[0] & from.c16[1]);
                }
                else if (size == 4) {
                    CHK_MASK_MATCH(ndx, from.c32[0], from.c32[1]);
                    CHK_MASK_LEGAL(ndx, from.c32[0], legal);
                    to.c32[0] &= ~from.c32[0];
                    to.c32[0] |= (from.c32[0] & from.c32[1]);
                }
                else {
                    CHK_MASK_MATCH(ndx, from.c8[0], from.c8[1]);
                    CHK_MASK_LEGAL(ndx, from.c8[0], legal);
                    to.c8[0] &= ~from.c8[0];
                    to.c8[0] |= (from.c8[0] & from.c8[1]);
                    size = 2;
                }
                from.c8 += (size * 2);
                dataLeft -= (size * 2);
            }
        }
        if (dataLeft > 2) {
            ErrorF("[xkb] Extra data (%d bytes) after SelectEvents\n",
                   dataLeft);
            return BadLength;
        }
        return Success;
    }
    return BadAlloc;
}