bool RenderTarget_clear(JSContext *cx, unsigned argc, jsval *vp)
{
    JS::CallArgs args = JS::CallArgsFromVp( argc, vp ); Value value=args.computeThis( cx );
    RenderTarget *target=(RenderTarget *) JS_GetPrivate( &value.toObject() );

    GLfloat r = -1;
    GLfloat g = -1;
    GLfloat b = -1;
    GLfloat a = -1;
	
    GLint depth=1.0;

    if ( argc >= 1 && args[0].isObject() )
    {
        RootedObject object(cx, &args[0].toObject() );
        RootedValue red(cx), green(cx), blue(cx), alpha(cx);

        JS_GetProperty( cx, HandleObject(object), "r", MutableHandleValue(&red) );
        JS_GetProperty( cx, HandleObject(object), "g", MutableHandleValue(&green) );
        JS_GetProperty( cx, HandleObject(object), "b", MutableHandleValue(&blue) );
        JS_GetProperty( cx, HandleObject(object), "a", MutableHandleValue(&alpha) );

        r=red.toNumber(); g=green.toNumber(); b=blue.toNumber(); a=alpha.toNumber();
    }

    if ( argc >= 2 ) depth=args[1].toInt32();

    target->clear( r, g, b, a, depth );

    return true;
}
JSObject *registerRenderTarget( JSContext *cx, JSObject *object )
{
    RootedObject obj(cx, object ); RootedObject parentobj(cx);

    JSObject * newObject=JS_InitClass( cx, HandleObject(obj), HandleObject(parentobj), &RenderTargetClass,  
        RenderTargetConstructor, 0,
        NULL, NULL,
        NULL, NULL);

    return newObject;
}
void JSWrapperClass::install( void )
{
    __g_classMap.insert( std::pair<std::string, JSWrapperClass *>( m_name, this ) );

    RootedObject parent( m_cx, &m_parentObject->getSMValue()->toObject() ); RootedObject parentProto( m_cx );

    JSObject * newObject=JS_InitClass( m_cx, HandleObject(parent), HandleObject(parentProto), m_jsClass,  
        m_constructor, 0,
        NULL, NULL,
        NULL, NULL);
}
JSObject *JSWrapperClass::instantiate( JS::CallArgs *args )
{
    JSObject *object = JS_NewObjectForConstructor( m_cx, m_jsClass, *args );
    RootedObject obj( m_cx, object ); RootedValue v( m_cx, JS::UndefinedValue() );

    for (std::map<std::string,bool (*) (JSContext *cx, unsigned argc, jsval *vp)>::iterator it = m_funcs.begin(); it != m_funcs.end(); it++)
    {
        JS_DefineFunction( m_cx, HandleObject(obj), it->first.c_str(), it->second, 0, 0);
    }

    args->rval().set( OBJECT_TO_JSVAL( object ) );

    for ( int i=0; i < m_properties.size(); ++i )
    {
        JS_DefineProperty( m_cx, HandleObject(obj), m_properties[i].c_str(), HandleValue(&v), JSPROP_SHARED, (JSNative) m_getProperties[i], (JSNative) m_setProperties[i] );
    }

    return object;
}
bool RenderTarget_setScissor(JSContext *cx, unsigned argc, jsval *vp)
{
    JS::CallArgs args = JS::CallArgsFromVp( argc, vp ); Value value=args.computeThis( cx );
    RenderTarget *target=(RenderTarget *) JS_GetPrivate( &value.toObject() );

    if ( argc == 1 && args[0].isObject() )
    {
        RootedObject object(cx, &args[0].toObject() );
        RootedValue x(cx), y(cx), width(cx), height(cx);

        JS_GetProperty( cx, HandleObject(object), "x", MutableHandleValue(&x) );
        JS_GetProperty( cx, HandleObject(object), "y", MutableHandleValue(&y) );
        JS_GetProperty( cx, HandleObject(object), "width", MutableHandleValue(&width) );
        JS_GetProperty( cx, HandleObject(object), "height", MutableHandleValue(&height) );   

        target->setScissor( x.toInt32(), y.toInt32(), width.toInt32(), height.toInt32() );     
    } else target->setScissor( 0, 0, 0, 0 );  

    return true;
}
bool RenderTarget_dispose(JSContext *cx, unsigned argc, jsval *vp)
{
    JS::CallArgs args = JS::CallArgsFromVp( argc, vp ); Value value=args.computeThis( cx );

    RenderTarget *target=(RenderTarget *) JS_GetPrivate( &value.toObject() );
    target->dispose();

    // --- VG.Renderer().removeResource(this);
    
    RootedValue *renderer=g_host->executeScript( "VG.Renderer()" );
    RootedObject rendererObject( cx, &renderer->toObject() );

    RootedValue rc( cx );
    RootedValue thisValue( cx, value );

    bool ok=Call( cx, HandleObject( rendererObject ), "removeResource", HandleValueArray( thisValue ), MutableHandleValue( &rc ) );

    return true;
}
bool RenderTargetConstructor( JSContext *cx, unsigned argc, jsval *vp )
{
    //printf( "RenderTarget Constructor!%d\n", argc );

    JS::CallArgs args = JS::CallArgsFromVp( argc, vp );

    JSObject *object = JS_NewObjectForConstructor( cx, &RenderTargetClass, args );
    RootedObject obj(cx, object ); RootedValue v(cx, JS::UndefinedValue() );
    JS_DefineFunctions( cx, HandleObject(obj), rendertarget_functions );
    JS_DefineProperty( cx, HandleObject(obj), "w", HandleValue(&v), JSPROP_SHARED, (JSPropertyOp) GetRenderTargetProperty, (JSStrictPropertyOp) SetRenderTargetProperty );
    JS_DefineProperty( cx, HandleObject(obj), "h", HandleValue(&v), JSPROP_SHARED, (JSPropertyOp) GetRenderTargetProperty, (JSStrictPropertyOp) SetRenderTargetProperty );
    JS_DefineProperty( cx, HandleObject(obj), "autoResize", HandleValue(&v), JSPROP_SHARED, (JSPropertyOp) GetRenderTargetProperty, (JSStrictPropertyOp) SetRenderTargetProperty );
    JS_DefineProperty( cx, HandleObject(obj), "main", HandleValue(&v), JSPROP_SHARED, (JSPropertyOp) GetRenderTargetProperty, (JSStrictPropertyOp) SetRenderTargetProperty );

    GLint w=0;
    GLint h=0;
    bool main=false;

    if ( argc >= 1 ) w=args[0].toInt32();
    if ( argc >= 2 ) h=args[1].toInt32();
    if ( argc >= 3 ) main=args[2].toBoolean();

    RenderTarget *target = new RenderTarget( w, h, main );

    JS_SetPrivate( object, target );
    args.rval().set( OBJECT_TO_JSVAL( object ) );

    //VG.Renderer().addResource(this);

    RootedValue rendererRC(cx); RootedObject global( cx,JS_GetGlobalForObject( cx, object ) );
    bool ok = JS_EvaluateScript( cx, HandleObject( global ), "VG.Renderer()", strlen("VG.Renderer()"), "unknown", 1, MutableHandleValue(&rendererRC) );    
    if ( ok )
    {
        RootedValue objectValue( cx, OBJECT_TO_JSVAL(object) );
        RootedObject renderer(cx, &rendererRC.toObject() ); MutableHandleValue handleValue( &rendererRC );
        ok=Call( cx, HandleObject(renderer), "addResource", HandleValueArray( objectValue ), handleValue );
    }

    return true;
}
REQUEST_NOTIFICATION_STATUS CIISxpressHttpModule::OnSendResponse(IN IHttpContext* pHttpContext, IN ISendResponseProvider* pProvider)
{
	const TCHAR* const pszMethodName = __FUNCTIONT__;	

	// only proceed if filter is enabled
	if (m_Config.GetEnabled() == false)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("module is disabled\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::FilterDisabled);
		return RQ_NOTIFICATION_CONTINUE;
	}

	IHttpRequest* pHttpRequest = pHttpContext->GetRequest();
	if (pHttpRequest == NULL)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetRequest() returned NULL\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}	

	IHttpResponse* pHttpResponse = pHttpContext->GetResponse();
	if (pHttpResponse == NULL)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetResponse() returned NULL\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}	

	const HTTP_REQUEST* pRawRequest = pHttpRequest->GetRawHttpRequest();
	if (pRawRequest == NULL)
	{		
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetRawHttpRequest() returned NULL\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}		

	const HTTP_RESPONSE* pRawResponse = pHttpResponse->GetRawHttpResponse();
	if (pRawResponse == NULL)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetRawHttpResponse() returned NULL\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}		

	// we only handle GET or POST (if POST handling is enabled)
	if (pRawRequest->Verb != HttpVerbGET && !(pRawRequest->Verb == HttpVerbPOST && m_Config.HandlePOSTResponses()))
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("request was not GET or POST\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::RequestMethod);
		return RQ_NOTIFICATION_CONTINUE;
	}

	// only handle status code 200
	if (pRawResponse->StatusCode != 200)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("response code is not 200 (%u)\n"),  pRawResponse->StatusCode);
		PerfCountersAddRejectedResponse(IISxpressNativePerf::ResponseCode);
		return RQ_NOTIFICATION_CONTINUE;
	}

	// determine if the request came from localhost
	if (m_Config.GetCompressLocalhost() == false && IsUserLocalhost(pRawRequest) == true)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext,_T("localhost is disabled\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::LocalhostDisabled);
		return RQ_NOTIFICATION_CONTINUE;
	}

	// ***********************************************************************************************************************************

	const char* pszUserAgent = EnsureNotNull(pHttpRequest->GetHeader(HttpHeaderUserAgent));	

	bool excludedUserAgent = false;
	if (m_Config.GetUserAgentExclusionEnabled())
	{
		DWORD dwUserAgentCacheCookie = m_Config.GetLoadCookie();
		
		if (!m_UserAgentCache.GetUserAgentState(dwUserAgentCacheCookie, pszUserAgent, excludedUserAgent))
		{
			HttpUserAgent::UserAgentProducts<std::string> agent;
			if (agent.ParseUserAgentString(pszUserAgent) == S_OK)
			{
				const HttpUserAgent::RuleUserAgents<std::string>& ruleAgents = m_Config.GetExcludedUserAgents();
				if (ruleAgents.Compare(agent))
				{
					excludedUserAgent = true;
				}

				m_UserAgentCache.AddUserAgentState(dwUserAgentCacheCookie, pszUserAgent, excludedUserAgent);
			}
		}
	}

	if (excludedUserAgent)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("the user agent '%s' has been excluded\n"),  pszUserAgent);		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::NeverRuleMatch);
		return RQ_NOTIFICATION_CONTINUE;
	}	

	// ***********************************************************************************************************************************

	// we must have only one chunk and data from memory or file
	if (pRawResponse->EntityChunkCount == 0)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext,_T("can't convert multi-entity buffer, incoming buffer is zero length\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::InvalidContentLength);
		return RQ_NOTIFICATION_CONTINUE;
	}
	else if (pRawResponse->EntityChunkCount != 1)
	{		
		// turn the multi-chunk response into a single chunk - NB. the response
		// must all be in memory
		if (MakeResponseSingleEntityBlock(pHttpContext, pHttpResponse, pRawResponse) == false)
		{
			AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext,_T("can't convert multi-entity buffer\n"));
			PerfCountersAddRejectedResponse(IISxpressNativePerf::MemoryAllocationFailed);
			return RQ_NOTIFICATION_CONTINUE;
		}				
	}	
	else if (pRawResponse->pEntityChunks->DataChunkType != HttpDataChunkFromMemory &&
			pRawResponse->pEntityChunks->DataChunkType != HttpDataChunkFromFileHandle)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext,_T("response isn't HttpDataChunkFromMemory or HttpDataChunkFromFileHandle\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}

	PHTTP_DATA_CHUNK pEntityChunk = pRawResponse->pEntityChunks;

	if (pEntityChunk == NULL)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("the response does not contain any data\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;	
	}

	// TODO: review max size here
	// we don't handle non-zero starting offsets or very large files (>250MB)
	if (pEntityChunk->DataChunkType == HttpDataChunkFromFileHandle &&
		(pEntityChunk->FromFileHandle.ByteRange.StartingOffset.QuadPart > 0 ||
		pEntityChunk->FromFileHandle.ByteRange.Length.QuadPart > (250 * 1024 * 1024)))
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("response has offset > 0 or is too big\n"));
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}

	PCSTR pszContentEncoding = pHttpResponse->GetHeader(HttpHeaderContentEncoding);
	if (pszContentEncoding != NULL && pszContentEncoding[0] != '\0')
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("response already has content encoding\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::AlreadyEncoded);
		return RQ_NOTIFICATION_CONTINUE;
	}

	PCSTR pszTransferEncoding = pHttpResponse->GetHeader(HttpHeaderTransferEncoding);
	if (pszTransferEncoding != NULL && pszTransferEncoding[0] != '\0')
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("response already has transfer encoding\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::AlreadyEncoded);
		return RQ_NOTIFICATION_CONTINUE;
	}

	USHORT nContentTypeLength = 0;
	PCSTR pszContentType = pHttpResponse->GetHeader(HttpHeaderContentType, &nContentTypeLength);			
	if (pszContentType == NULL || nContentTypeLength == 0)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetHeader(HttpHeaderContentType) returned NULL\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::MissingContentType);
		return RQ_NOTIFICATION_CONTINUE;
	}

	USHORT nLastModifiedLength = 0;
	PCSTR pszLastModified = pHttpResponse->GetHeader(HttpHeaderLastModified, &nLastModifiedLength);			

	// ***********************************************************************
	
	USHORT nAcceptEncodingLength = 0;
	PCSTR pszAcceptEncoding = pHttpRequest->GetHeader(HttpHeaderAcceptEncoding, &nAcceptEncodingLength);	
	if (pszAcceptEncoding == NULL || nAcceptEncodingLength == 0 ||
		(strstr(pszAcceptEncoding, "deflate") == NULL && strstr(pszAcceptEncoding, "gzip") == NULL && strstr(pszAcceptEncoding, "bzip2") == NULL))
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("the client does not accept compressed responses\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::IncompatibleClient);
		return RQ_NOTIFICATION_CONTINUE;
	}

	// ***********************************************************************

	const WCHAR* pszScriptTranslated = pHttpContext->GetScriptTranslated();
	if (pszScriptTranslated == NULL)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetScriptTranslated() returned NULL\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}
		
	// get the url
	CAtlStringA sScriptTranslated(pszScriptTranslated);

	// get the site id
	CAtlStringA sInstanceId;
	sInstanceId.Format("%u", pHttpRequest->GetSiteId());
	
	// get the server name
	PCSTR pszServerName = NULL;		
	DWORD dwServerNameLength = 0;
	pHttpContext->GetServerVariable("SERVER_NAME", &pszServerName, &dwServerNameLength);
	if (pszServerName == NULL || dwServerNameLength == 0)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetServerVariable(\"SERVER_NAME\") returned NULL\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}

	// get the server port 		
	PCSTR pszServerPort = NULL;		
	DWORD dwServerPortLength = 0;
	pHttpContext->GetServerVariable("SERVER_PORT", &pszServerPort, &dwServerPortLength);
	if (pszServerPort == NULL || dwServerPortLength == 0)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetServerVariable(\"SERVER_PORT\") returned NULL\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}

	DWORD dwContentLength = 0;
	if (pEntityChunk->DataChunkType == HttpDataChunkFromMemory)
	{
		dwContentLength = pRawResponse->pEntityChunks->FromMemory.BufferLength;
	}
	else if (pEntityChunk->DataChunkType == HttpDataChunkFromFileHandle)
	{
		dwContentLength = pEntityChunk->FromFileHandle.ByteRange.Length.LowPart;
	}
	else
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("the DataChunkType (%u) is not supported\n"), pEntityChunk->DataChunkType);		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}
	
	IISInfo iisinfo;
	iisinfo.pszInstanceId = sInstanceId;		
	iisinfo.pszServerName = pszServerName;
	iisinfo.pszServerPort = pszServerPort;
	iisinfo.pszURLMapPath = sScriptTranslated;		

	if (m_Config.GetDebugEnabled() || m_Config.GetLoggingLevel() >= IISXPRESS_LOGGINGLEVEL_ENH)	
	{				
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("IIS: server name='%hs'\n"), pszServerName);		
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("IIS: server port='%hs'\n"), pszServerPort);
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("IIS: instance id='%hs'\n"), sInstanceId);
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("IIS: URL map path='%hs'\n"), sScriptTranslated);
	}			

	// ***********************************************************************

	const WCHAR* pszURI = pHttpContext->GetScriptName();
	if (pszURI == NULL)
	{
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("GetScriptName() returned NULL\n"));		
		PerfCountersAddRejectedResponse(IISxpressNativePerf::Internal);
		return RQ_NOTIFICATION_CONTINUE;
	}

	// get the uri
	CAtlStringA sURI(pszURI);

	// get the query string
	CAtlStringA sQueryString;
	if (pRawRequest->CookedUrl.pQueryString != NULL)
	{
		sQueryString = pRawRequest->CookedUrl.pQueryString + 1;
	}

	// get the remote address
	// TODO: consider using a cache lookup here 
	CAtlStringA sRemoteAddress;
	if (pRawRequest != NULL && pRawRequest->Address.pRemoteAddress != NULL)			
	{
		if (pRawRequest->Address.pRemoteAddress->sa_family == AF_INET)
		{
			struct sockaddr_in* paddrin = (struct sockaddr_in*) pRawRequest->Address.pRemoteAddress;

			// TODO: make this a bit more efficient
			sRemoteAddress.Format("%u.%u.%u.%u", 
				(unsigned) paddrin->sin_addr.S_un.S_un_b.s_b1,
				(unsigned) paddrin->sin_addr.S_un.S_un_b.s_b2,
				(unsigned) paddrin->sin_addr.S_un.S_un_b.s_b3,
				(unsigned) paddrin->sin_addr.S_un.S_un_b.s_b4);
		}
		else if (pRawRequest->Address.pRemoteAddress->sa_family == AF_INET6)
		{
			SOCKADDR_IN6* paddrin = (SOCKADDR_IN6*) pRawRequest->Address.pRemoteAddress;

			// TODO: make this a bit more efficient
			sRemoteAddress.Format("%0x:%0x:%0x:%0x:%0x:%0x:%0x:%0x",				
				::htons(paddrin->sin6_addr.u.Word[0]), ::htons(paddrin->sin6_addr.u.Word[1]),
				::htons(paddrin->sin6_addr.u.Word[2]), ::htons(paddrin->sin6_addr.u.Word[3]),
				::htons(paddrin->sin6_addr.u.Word[4]), ::htons(paddrin->sin6_addr.u.Word[5]),
				::htons(paddrin->sin6_addr.u.Word[6]), ::htons(paddrin->sin6_addr.u.Word[7]));
		}
	}	
		
	RequestInfo requestinfo;				
	requestinfo.pszAcceptEncoding = pszAcceptEncoding;
	requestinfo.pszHostname = EnsureNotNull(pHttpRequest->GetHeader(HttpHeaderHost));
	requestinfo.pszQueryString = sQueryString;
	requestinfo.pszRemoteAddress = sRemoteAddress;
	requestinfo.pszURI = sURI;
	requestinfo.pszUserAgent = pszUserAgent;

	if (m_Config.GetDebugEnabled() || m_Config.GetLoggingLevel() >= IISXPRESS_LOGGINGLEVEL_BASIC)	
	{				
		char* pszMethod = "Unknown";		
		switch (pRawRequest->Verb)
		{
		case HttpVerbGET: pszMethod = "GET"; break;
		case HttpVerbPOST: pszMethod = "POST"; break;
		}

		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: method='%hs'\n"), pszMethod);		
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: hostname='%hs'\n"), requestinfo.pszHostname);		
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: user agent='%hs'\n"), requestinfo.pszUserAgent);		
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: URI='%hs'\n"), requestinfo.pszURI);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: accept-encoding='%hs'\n"), requestinfo.pszAcceptEncoding);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: client address='%hs'\n"), requestinfo.pszRemoteAddress);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: query string='%hs'\n"), requestinfo.pszQueryString);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: content-type='%hs'\n"), pszContentType);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: content-length=%u\n"), dwContentLength);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: response code=%u\n"), pRawResponse->StatusCode);	
		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("REQUEST: last-modified='%hs'\n"), pszLastModified != NULL ? pszLastModified : "null");	
	}			

	// ***********************************************************************	
	
	ResponseInfo responseinfo;		
	responseinfo.dwContentLength = dwContentLength;
	responseinfo.dwResponseCode = pRawResponse->StatusCode;	
	responseinfo.pszContentType = pszContentType;
	responseinfo.pszLastModified = pszLastModified;

	// get a connection to the server from the GIT (if it is connected)
	CComPtr<IIISxpressHTTPRequest> pIISxpressHTTPRequest;
	if (m_pGlobalIISxpressHTTPRequest.GetCookie() != 0)	
	{
		m_pGlobalIISxpressHTTPRequest.CopyTo(&pIISxpressHTTPRequest);
	}

	if (pIISxpressHTTPRequest == NULL)
	{
		// revoke the pointer in the GIT
		m_pGlobalIISxpressHTTPRequest.Revoke();

		// try to connect to the server
		if (GetHTTPRequestObject(pHttpContext, &pIISxpressHTTPRequest) == false || pIISxpressHTTPRequest == NULL)	
		{
			AppendLogMessage(IISXPRESS_LOGGINGLEVEL_ENH, pszMethodName, pHttpContext, _T("unable to connect to server\n"));					
			PerfCountersAddRejectedResponse(IISxpressNativePerf::NoCompressionServer);
			return RQ_NOTIFICATION_CONTINUE;
		}		

		// store the resulting pointer in the GIT
		m_pGlobalIISxpressHTTPRequest = pIISxpressHTTPRequest;
	}			

	const DWORD dwTimerLoggingLevel = IISXPRESS_LOGGINGLEVEL_FULL;
	bool useTimer = (m_Config.GetDebugEnabled() || m_Config.GetLoggingLevel() >= dwTimerLoggingLevel);

	// start timer
	__int64 nStartTimer = useTimer ? g_Timer.GetMicroSecTimerCount() : 0;

	CAtlStringA sCacheKey;
	if (m_nCacheEnabled != 0)
	{	
		// update the cache perf counters based on the cache cookie
		PerfCountersUpdateCacheStatus(false);	

		if (pszLastModified != NULL && sQueryString.GetLength() == 0)
		{
			ATLASSERT(pszAcceptEncoding != NULL);

			// create the unique key for the response
			ResponseCache::CreateResponseCacheKey(
				sInstanceId, sInstanceId.GetLength(),
				sURI, sURI.GetLength(),
				responseinfo.pszContentType, nContentTypeLength,
				dwContentLength, 
				pszLastModified, nLastModifiedLength,
				pszAcceptEncoding, nAcceptEncodingLength,
				sCacheKey);

			// try to get a compressed response from the cache
			HCACHEITEM hCacheItem = NULL;
			HRESULT hr = m_ResponseCache.LookupEntry(sCacheKey, &hCacheItem);
			if (hr == S_OK)
			{
				// get the item from the cache
				ResponseCacheItem* pCacheItem;
				DWORD dwCacheItemSize = 0;
				hr = m_ResponseCache.GetData(hCacheItem, (void**) &pCacheItem, &dwCacheItemSize);	

				void* pBuffer = pHttpContext->AllocateRequestMemory(pCacheItem->dwContentLength);
				if (pBuffer != NULL)
				{
					pHttpResponse->SetHeader(HttpHeaderContentLength, pCacheItem->sContentLength, 
						(USHORT) pCacheItem->sContentLength.GetLength(), TRUE);

					pHttpResponse->SetHeader(HttpHeaderContentEncoding, pCacheItem->sContentEncoding, 
						(USHORT) pCacheItem->sContentEncoding.GetLength(), TRUE);

					pEntityChunk->DataChunkType = HttpDataChunkFromMemory;
					pEntityChunk->FromMemory.pBuffer = pBuffer;
					pEntityChunk->FromMemory.BufferLength = pCacheItem->dwContentLength;
					memcpy(pEntityChunk->FromMemory.pBuffer, pCacheItem->pbyData, pCacheItem->dwContentLength);

					hr = m_ResponseCache.ReleaseEntry(hCacheItem);

					pIISxpressHTTPRequest->NotifyCacheHit(&iisinfo, &requestinfo, &responseinfo, pCacheItem->dwContentLength);

					PerfCountersAddCachedResponse(dwContentLength, pCacheItem->dwContentLength);

					// calculate timer duration
					__int64 nEndTimer = useTimer ? g_Timer.GetMicroSecTimerCount() : 0;
					__int64 nInterval = nEndTimer - nStartTimer;

					AppendLogMessage(dwTimerLoggingLevel, pszMethodName, pHttpContext, _T("response resolved from cache, call took %I64d us\n"), nInterval);

					return RQ_NOTIFICATION_CONTINUE;
				}
				
				// we failed to handle the cached response, so just proceed as normal (we need to free the
				// cache entry tho)
				hr = m_ResponseCache.ReleaseEntry(hCacheItem);
			}		
		}
	}

	DWORD dwFilterContext = 0;
	HRESULT hr = pIISxpressHTTPRequest->OnSendResponse(&iisinfo, &requestinfo, &responseinfo, &dwFilterContext);

	// calculate timer duration
	__int64 nEndTimer = useTimer ? g_Timer.GetMicroSecTimerCount() : 0;
	__int64 nInterval = nEndTimer - nStartTimer;

	if (FAILED(hr) == TRUE)
	{
		// get the facility code of the failure
		DWORD dwFacility = HRESULT_FACILITY(hr);

		// if it isn't NULL or ITF then assume the server is now invalid
		if (dwFacility != FACILITY_NULL && dwFacility != FACILITY_ITF)
		{
			// the GIT is invalid, dump it
			m_pGlobalIISxpressHTTPRequest.Revoke();

			// the server connection is invalid, dump it
			pIISxpressHTTPRequest = NULL;		
		}		
	}

	AppendLogMessage(dwTimerLoggingLevel, pszMethodName, pHttpContext, _T("OnSendResponse() returns 0x%08x, call took %I64d us\n"), hr, nInterval);	

	if (FAILED(hr) == TRUE)
	{			
		// TODO: need to handle generic HRs
		PerfCountersAddRejectedResponse(hr);

		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("won't handle response (0x%08x)\n"), hr);					

		return RQ_NOTIFICATION_CONTINUE;
	}

	AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("will handle response (0x%08x)\n"), hr);		

	if (pEntityChunk->DataChunkType == HttpDataChunkFromFileHandle)
	{
		DWORD dwAllocatedBlockSize = 0;
		BYTE* pbyData = AllocateMemoryBlockForOverlappedIO(pHttpContext, dwContentLength, dwAllocatedBlockSize);
		if (pbyData != NULL)
		{		
			// we are about to perform overlapped IO, so setup the OVERLAPPED struct into a default state
			OVERLAPPED overlapped;
			memset(&overlapped, 0, sizeof(overlapped));

			HandleObject FileEvent;
			bool bHandleFromPool = m_hEventHandlePool.GetHandleObject(FileEvent);									

			// if we failed to get a handle from the pool then we need to make one
			if (bHandleFromPool == false)
			{
				HANDLE hReadOK = ::CreateEvent(NULL, TRUE, FALSE, NULL);
				FileEvent = HandleObject(hReadOK);
			}

			// use the event handle so we can track the IO completion
			overlapped.hEvent = FileEvent;

			// ask for a file read (asynchronous)		
			DWORD dwStatus = ::ReadFile(pEntityChunk->FromFileHandle.FileHandle, pbyData, dwAllocatedBlockSize, NULL, &overlapped);

			// wait for the IO to complete (NB. don't really use this since INFINITE can block forever)			
			::WaitForSingleObject(FileEvent, INFINITE);

			// we must return the handle (if it came from the pool)
			if (bHandleFromPool == true)
			{
				m_hEventHandlePool.ReturnHandleObject(FileEvent);
			}

			pEntityChunk->DataChunkType = HttpDataChunkFromMemory;
			pEntityChunk->FromMemory.BufferLength = dwContentLength;
			pEntityChunk->FromMemory.pBuffer = pbyData;
		}
		else
		{
			hr = E_OUTOFMEMORY;
		}
	}
	else if (pEntityChunk->DataChunkType == HttpDataChunkFromMemory)
	{
		// allocate the memory block
		BYTE* pbyData = (BYTE*) pHttpContext->AllocateRequestMemory(dwContentLength);			
		if (pbyData != NULL)
		{
			// copy the response data
			memcpy(pbyData, pEntityChunk->FromMemory.pBuffer, pEntityChunk->FromMemory.BufferLength);

			// change the buffer pointer
			pEntityChunk->FromMemory.pBuffer = pbyData;
		}
		else
		{
			hr = E_OUTOFMEMORY;
		}
	}		

	// allocate the response stream
	CComPtr<IStream> pStream;
	CComObject<CResponseStream>* pResponseStream = NULL;	
	if (hr == S_OK)
	{
		hr = CComObject<CResponseStream>::CreateInstance(&pResponseStream);
		if (hr == S_OK)
		{		
			pStream = pResponseStream;
		}
	}

	// catch any memory issues
	if (FAILED(hr) == TRUE)
	{			
		// abort the compression
		pIISxpressHTTPRequest->AbortRequest(dwFilterContext);

		// TODO: need to handle generic HRs
		PerfCountersAddRejectedResponse(hr);

		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("won't handle response (0x%08x)\n"), hr);					

		return RQ_NOTIFICATION_CONTINUE;
	}

	pResponseStream->AttachBuffer(pEntityChunk->FromMemory.pBuffer, dwContentLength, 0);

	char szContentEncoding[32] = "";
	hr = pIISxpressHTTPRequest->OnSendRawData(dwFilterContext, pStream, dwContentLength, FALSE, (signed char*) szContentEncoding, _countof(szContentEncoding));
	if (hr == S_OK)
	{		
		DWORD dwOriginalSize = dwContentLength;
		DWORD dwCompressedSize = pResponseStream->GetOffset();				

		// set the new content length into the header
		CAtlStringA sContentLength;
		sContentLength.Format("%u", dwCompressedSize);
		pHttpResponse->SetHeader(HttpHeaderContentLength, sContentLength, (USHORT) sContentLength.GetLength(), TRUE);

		// set the new size into the buffer
		pRawResponse->pEntityChunks->FromMemory.BufferLength = dwCompressedSize;
		
		pHttpResponse->SetHeader(HttpHeaderContentEncoding, szContentEncoding, (USHORT) strlen(szContentEncoding), TRUE);

		// add the item to the cache if we have a key (it means the data is cachable)
		if (dwCompressedSize < dwOriginalSize &&
			m_nCacheEnabled != 0 && sCacheKey.GetLength() > 0)
		{
			ResponseCacheItem* pCacheItem = new ResponseCacheItem();
			if (pCacheItem != NULL)
			{
				pCacheItem->sContentEncoding = szContentEncoding;
				pCacheItem->sContentLength = sContentLength;
				pCacheItem->dwContentLength = dwCompressedSize;
				pCacheItem->pbyData = new BYTE[dwCompressedSize];
				if (pCacheItem->pbyData != NULL)
				{
					// copy the compressed data and add it to the cache
					memcpy(pCacheItem->pbyData, pEntityChunk->FromMemory.pBuffer, dwCompressedSize);
					m_ResponseCache.Add(sCacheKey, pCacheItem, pCacheItem->dwContentLength, NULL, NULL, NULL, m_ResponseCacheItemDeallocator);

					PerfCountersUpdateCacheStatus(true);
				}
			}
		}

		PerfCountersAddCompressedResponse(dwContentLength, dwCompressedSize);

		// make sure the context is released
		hr = pIISxpressHTTPRequest->OnEndOfRequest(dwFilterContext, NULL, 0, FALSE, NULL, 0);		
	}
	else
	{
		// abort the compression
		pIISxpressHTTPRequest->AbortRequest(dwFilterContext);

		// TODO: need to handle generic HRs
		PerfCountersAddRejectedResponse(hr);

		AppendLogMessage(IISXPRESS_LOGGINGLEVEL_BASIC, pszMethodName, pHttpContext, _T("won't handle response (0x%08x)\n"), hr);							
	}

	return RQ_NOTIFICATION_CONTINUE;
}
Beispiel #9
0
int
comm_object(char *str, HandleOps *ops, Handle **hp, Ref **rp, int now)
{
  int c, ok = 0;
  Pool *p;

  if (str == NULL)
    return 0;
  if (strcmp(str, "-") == 0 || access(str, 0) == 0) {
    Handle *h = HandleReferringTo('<', str, ops, hp);
    /*
     * If we haven't read something from this file yet,
     * forget it.
     */
    if (h) {
      if (HandleObject(h)) {
	ok = 1;
	if (rp) {
	  HandleUpdRef(&h, NULL, rp);
	}
      } else if (((p = PoolByName(HandleName(h), ops))) == NULL ||
		 (p->flags & PF_ANY) || (!p->seekable && !now)) {
		
	/* When reading plain files, always demand an object. When
	 * reading others (pipe, tty), demand one if 'now' set. Use
	 * PF_ANY flag as an alternate hint of reading an object,
	 * since reading commands leaves no object attached to h.
	 */
	ok = 1;
      } else {
	/* Failed */
	HandleDelete(h);
	if (hp) {
	  *hp = NULL;
	}
      }
    }
    /* If not ok, close the file.
     * If 'now' and not some sort of pipe, also close the file.
     */
    if ((p = PoolByName(str, ops)) != NULL && (!ok || (now && p->seekable))) {
      if (now && ok) {
	/* Read as much as possible if we need it right now. */
	while(PoolInputFile(p) != NULL &&
	      (c = async_iobfnextc(PoolInputFile(p), 0)) != NODATA &&
	      c != EOF && (*ops->strmin)(p, hp, rp))
	  ;
      }
      PoolClose(p);
      MyPoolDelete(p);
    } else if (iobfile(PoolInputFile(p)) == stdin 
	       && PoolOutputFile(p) == NULL) {
      p = PoolStreamOpen(PoolName(p), stdout, 1, ops);
    }
    return ok;
  } else if (strpbrk(str, "({ \t\n")) {
    static Pool *pcache;	/* Cache a pool for handling strings */
    static bool inuse = false;	/* Use cached pool unless already in use */
    IOBFILE *inf = iobfileopen(fmemopen(str, strlen(str), "rb"));
    /* Caching implies this first pool has a long lifetime;
     * suitable for expressing (interest (...)) 
     */
    if (!inuse) {
      if ((p = pcache) == NULL) {
	p = pcache = PoolStreamTemp(str, inf, stdout, 2, ops);
      } else {
	p->inf  = inf; /* hack */
	p->outf = stdout; /* hack */
      }
      inuse = true;
    } else {
      p = PoolStreamTemp(str, inf, stdout, 2, ops);
    }
    if (p == NULL) {
      return 0;		/* Failed */
    }
    while(iobfnextc(inf, 0) != EOF) {
      ok = (*ops->strmin)(p, hp, rp);
    }
    PoolClose(p);
    if (p == pcache) {
      inuse = false;
    } else {
      MyPoolDelete(p); /* Delete temp pool unless it's our cached one */
    }
  } else {
    /* Print the "No such file..." error left by access() */
    fprintf(stderr, "%s: %s\n", str, sperror());
  }
  return ok;
}
Beispiel #10
0
/* see the comments in src/lib/gprim/geom/geomstream.c */
int
TxStreamIn(Pool *p, Handle **hp, Texture **txp)
{
  IOBFILE *stream;
  char *fname;
  Handle *h = NULL;
  Texture *tx = NULL;
  float val[16];
  struct txkw *kw;
  char *w, *raww;
  int i, k = 0;
  int brack = 0;
  int empty = 1;
  bool braces = true;
  /*int plus = 0;*/
  bool more, mine = true; /* Questionable -- we'll report all errors */

  if ((stream = PoolInputFile(p)) == NULL) {
    return 0;
  }
  fname = PoolName(p);

  more = false;
  do {
    iobfnextc(stream, 0);

    switch(i = iobfgetc(stream)) {
    case ':':
    case '<':
      w = iobfdelimtok("{}()", stream, 0);
      /*
       * Consider doing a path search.
       * Do this before calling HandleReferringTo()
       * to prevent spurious error messages.
       */
      if (i == '<' && (h = HandleByName(w, &TextureOps)) == NULL && w[0] != '/') {
	w = findfile(fname, raww = w);
	if (w == NULL) {
	  OOGLSyntax(PoolInputFile(p),
		     "Error reading \"%s\": can't find file \"%s\"",
		     fname, raww);
	}
      } else if (h) {
	HandleDelete(h);
      }
      h = HandleReferringTo(i, w, &TextureOps, NULL);
      if (h != NULL) {
	tx = (Texture *)HandleObject(h);
	RefIncr((Ref*)tx);
      }
      break;

    case EOF: brack = 0; break;
    case '{': brack++; braces = true; break;
    case '}':
      if (brack-- <= 0) {
	iobfungetc(i, stream);
      }
      break;
    case '-':
    case '!':
      /*plus = -1;*/
      break;
    case '+':
      /*plus = 1;*/
      break;
    case '*':  break;

    default:
      more = false;
      iobfungetc(i, stream);
      w = iobfdelimtok("{}()", stream, 0);
      if (w == NULL) {
	break;
      }	    

      for (i = sizeof(tx_kw)/sizeof(tx_kw[0]), kw = tx_kw; --i >= 0; kw++)
	if (!strcmp(kw->word, w))
	  break;
      if (i < 0) {
	if (mine)
	  OOGLSyntax(stream, "%s: unknown texture keyword %s",
		     fname, w);
	return 0;
      }
      if (tx == NULL) {
	tx = TxCreate(TX_END);
      }

      if (kw->args < 0) {
	char allowed[256], *tail = allowed;
	w = iobfdelimtok("{}()", stream, 0);
	if (w == NULL) w = "";
	allowed[0] = '\0';
	for (k = 1; strcmp((kw+k)->word, w); k++) {
	  sprintf(tail, " %s", (kw+k)->word);
	  tail += strlen(tail);
	  if (k + kw->args >= 0) {
	    OOGLSyntax(stream, "%s: %s %s: expected one of: %s",
		       fname, kw->word, w, allowed);
	    TxDelete(tx);
	    return 0;
	  }
	}
      } else if (kw->args > 0) {
	int n = iobfgetnf(stream, kw->args, val, 0);
	if (n != kw->args) {
	  OOGLSyntax(stream, "%s: %s expected %d numeric values",
		     fname, w, kw->args);
	  TxDelete(tx);
	  return 0;
	}
      }

      empty++;

      switch((int)kw->aval) {
      case -1:
	mine = more = true;
	empty--;
	break;

      case TX_APPLY:
	tx->apply = (enum apply_enum)(kw+k)->aval;
	break;

      case TX_FILE:
      case TX_ALPHAFILE:
	raww = iobfdelimtok("{}()", stream, 0);
	w = findfile(fname, raww);
	if (w == NULL) {
	  OOGLSyntax(stream,
		     "Warning: reading \"%s\": can't find file \"%s\", ignoring texture",
		     fname, raww);
	} else {
	  TxSet(tx, kw->aval, w, TX_END);
	}
	break;

#if 0 /* does not belong here */
      case TX_XSIZE:
      case TX_YSIZE:
      case TX_CHANNELS:
	if (val[0] < 1 ||
	    val[0] > (tx_kw[i].aval==TX_CHANNELS) ? 4 : 100000) {
	  OOGLSyntax(stream, "%s: Bad value for %s: %s",
		     fname, kw->word, w);
	  TxDelete(tx);
	  return 0;
	}
	TxSet(tx, kw->aval, (int)val[0], TX_END);
	break;
#endif

#if 0 /* not implemented */
      case TX_COORDS:
	tx->coords = (kw+k)->aval;
	break;
#endif
      case TX_BACKGROUND: {
	/* We allow ColorA for compatibility, but the texture
	 * background color really is only RGB, not RGBA (see
	 * glTexEnvf(3)). So: if the next character is not a closing
	 * brace and not '\n', consume the next float which should be
	 * the alpha component
	 */
	float dummy;
	int c;

	if ((c = iobfnextc(stream, 1)) != '\n' && c != '}' && c != EOF) {
	  if (iobfgetnf(stream, 1, &dummy, 0) < 1) {
	    OOGLSyntax(stream, "%s: background color expected", fname);
	    TxDelete(tx);
	    return false;
	  }
	}
	TxSet(tx, kw->aval, val, TX_END);
	break;
      }
      case TX_HANDLE_IMAGE:
	if (!ImgStreamIn(p, &tx->imghandle, &tx->image)) {
	  OOGLSyntax(stream, "%s: texture image definition expected",
		     fname);
	  TxDelete(tx);
	  return false;
	}
	if (tx->filename) {
	  OOGLFree(tx->filename);
	  tx->filename = NULL;
	}
	if (tx->alphafilename) {
	  OOGLFree(tx->alphafilename);
	  tx->alphafilename = NULL;
	}
	if (tx->imghandle) {
	  HandleRegister(&tx->imghandle, (Ref *)tx, &tx->image,
			 TxUpdateImage);
	}
	break;
      case TX_HANDLE_TRANSFORM:
	if (!TransStreamIn(p, &tx->tfmhandle, tx->tfm)) {
	  OOGLSyntax(stream, "%s: 4x4 texture transform expected",
		     fname);
	  TxDelete(tx);
	  return false;
	}
	if (tx->tfmhandle) {
	  HandleRegister(&tx->tfmhandle, (Ref *)tx,
			 tx->tfm, TransUpdate);
	}
	break;
      case TX_DOCLAMP:
	tx->flags = (kw+k)->aval;
	break;


      default:
	break;
      }
      /*plus = 0;*/
    }
  } while (brack > 0 || more);

  /* handle file and alphafile constructs */
  if (h == NULL && tx->filename) {
    struct stat st;
    char hname[2*(4+(INO_T_LSIZE+DEV_T_LSIZE+TIME_T_LSIZE)*SIZEOF_LONG)+1];
    char *ptr;

    if (tx->imghandle) {
      HandlePDelete(&tx->imghandle);
      tx->imghandle = NULL;
    }
    if (tx->image) {
      ImgDelete(tx->image);
      tx->image = NULL;
    }
	
    if (stat(tx->filename, &st) < 0) {
      OOGLSyntax(stream, "%s: cannot stat file %s", fname, tx->filename);
      TxDelete(tx);
      return 0;
    }

    ptr = hname;
    ptr += stat_to_handle(ptr, st.st_dev, st.st_ino, st.st_mtime);

    if (tx->alphafilename) {
      if (stat(tx->alphafilename, &st) < 0) {
	OOGLSyntax(stream,
		   "%s: cannot stat file %s", fname, tx->filename);
	TxDelete(tx);
	return 0;
      }
      ptr += stat_to_handle(ptr, st.st_dev, st.st_ino, st.st_mtime);
    }
    /* we share texture images defined by the same files, as was
     * the previous behaviour. However, this is implemented using
     * references and handles to image objects.
     */
    tx->imghandle = HandleByName(hname, &ImageOps);
    if (tx->imghandle != NULL) {
      tx->image = REFGET(Image, HandleObject(tx->imghandle));
    } else {
      /* This means there is no image, create one */
      tx->image = tx->alphafilename
	? ImgCreate(IMG_DATA_CHAN_FILE,
		    IMGF_AUTO, NULL, tx->filename,
		    IMG_DATA_CHAN_FILE,
		    IMGF_ALPHA, NULL, tx->alphafilename,
		    IMG_END)
	: ImgCreate(IMG_DATA_CHAN_FILE,
		    IMGF_AUTO, NULL, tx->filename,
		    IMG_END);
      if (!tx->image) {
	OOGLSyntax(stream,
		   "%s: cannot create image from given file(s) "
		   "(\"%s\"/\"%s\"",
		   fname, tx->filename, tx->alphafilename);
	TxDelete(tx);
	return 0;
      }
      /* Generate a new reference */
      tx->imghandle = HandleAssign(hname, &ImageOps, (Ref *)tx->image);
      tx->imghandle->permanent = false;
    }
  }

  /* Pass the ownership of h and tx to the caller if requested */

  if (hp != NULL) {
    /* pass on ownership of the handle h to the caller of this function */
    if (*hp != NULL) {
      if (*hp != h) {
	HandlePDelete(hp);
      } else {
	HandleDelete(*hp);
      }
    }
    *hp = h;
  } else if (h) {
    /* Otherwise delete h because we are its owner. Note that
     * HandleReferringTo() has passed the ownership of h to us;
     * explicitly defined handles (hdefine and define constructs)
     * will not be deleted by this call.
     */
    HandleDelete(h);
  }

  /* same logic as for hp */
  if (txp != NULL) {
    if (*txp != NULL) {
      TxDelete(*txp);
    }
    *txp = tx;
  } else if(tx) {
    TxDelete(tx);
  }

  return (tx != NULL || h != NULL || (empty && braces));
}