TEST_F(ConnectionHandlerTest, ListenerFilterTimeoutResetOnSuccess) {
  InSequence s;
  TestListener* test_listener = addListener(1, true, false, "test_listener");
  Network::MockListener* listener = new Network::MockListener();
  Network::ListenerCallbacks* listener_callbacks;
  EXPECT_CALL(dispatcher_, createListener_(_, _, _))
      .WillOnce(
          Invoke([&](Network::Socket&, Network::ListenerCallbacks& cb, bool) -> Network::Listener* {
            listener_callbacks = &cb;
            return listener;
          }));
  EXPECT_CALL(test_listener->socket_, localAddress());
  handler_->addListener(*test_listener);

  Network::MockListenerFilter* test_filter = new Network::MockListenerFilter();
  EXPECT_CALL(factory_, createListenerFilterChain(_))
      .WillRepeatedly(Invoke([&](Network::ListenerFilterManager& manager) -> bool {
        manager.addAcceptFilter(Network::ListenerFilterPtr{test_filter});
        return true;
      }));
  Network::ListenerFilterCallbacks* listener_filter_cb{};
  EXPECT_CALL(*test_filter, onAccept(_))
      .WillOnce(Invoke([&](Network::ListenerFilterCallbacks& cb) -> Network::FilterStatus {
        listener_filter_cb = &cb;
        return Network::FilterStatus::StopIteration;
      }));
  Network::MockConnectionSocket* accepted_socket = new NiceMock<Network::MockConnectionSocket>();
  Network::IoSocketHandleImpl io_handle{42};
  EXPECT_CALL(*accepted_socket, ioHandle()).WillRepeatedly(ReturnRef(io_handle));

  Event::MockTimer* timeout = new Event::MockTimer(&dispatcher_);
  EXPECT_CALL(*timeout, enableTimer(std::chrono::milliseconds(15000), _));
  listener_callbacks->onAccept(Network::ConnectionSocketPtr{accepted_socket});
  EXPECT_CALL(*test_filter, destroy_());
  EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(nullptr));
  EXPECT_CALL(*timeout, disableTimer());
  listener_filter_cb->continueFilterChain(true);

  EXPECT_CALL(*listener, onDestroy());
}


TEST_F(ConnectionHandlerTest, RemoveListener) {
  InSequence s;

  Network::MockListener* listener = new NiceMock<Network::MockListener>();
  Network::ListenerCallbacks* listener_callbacks;
  TestListener* test_listener = addListener(1, true, false, "test_listener");
  EXPECT_CALL(dispatcher_, createListener_(_, _, _))
      .WillOnce(
          Invoke([&](Network::Socket&, Network::ListenerCallbacks& cb, bool) -> Network::Listener* {
            listener_callbacks = &cb;
            return listener;
          }));
  EXPECT_CALL(test_listener->socket_, localAddress());
  handler_->addListener(*test_listener);

  Network::MockConnectionSocket* connection = new NiceMock<Network::MockConnectionSocket>();
  listener_callbacks->onAccept(Network::ConnectionSocketPtr{connection});
  EXPECT_EQ(0UL, handler_->numConnections());

  // Test stop/remove of not existent listener.
  handler_->stopListeners(0);
  handler_->removeListeners(0);

  EXPECT_CALL(*listener, onDestroy());
  handler_->stopListeners(1);

  EXPECT_CALL(dispatcher_, clearDeferredDeleteList());
  handler_->removeListeners(1);
  EXPECT_EQ(0UL, handler_->numConnections());

  // Test stop/remove of not existent listener.
  handler_->stopListeners(0);
  handler_->removeListeners(0);
}


Stats::Scope& listenerScope() override { return parent_.stats_store_; }


    envoy::api::v2::core::TrafficDirection direction() const override {
      return envoy::api::v2::core::TrafficDirection::UNSPECIFIED;
    }


TEST_F(ConnectionHandlerTest, WildcardListenerWithOriginalDst) {
  TestListener* test_listener1 = addListener(1, true, true, "test_listener1");
  Network::MockListener* listener1 = new Network::MockListener();
  Network::ListenerCallbacks* listener_callbacks1;
  EXPECT_CALL(dispatcher_, createListener_(_, _, _))
      .WillOnce(
          Invoke([&](Network::Socket&, Network::ListenerCallbacks& cb, bool) -> Network::Listener* {
            listener_callbacks1 = &cb;
            return listener1;
          }));
  Network::Address::InstanceConstSharedPtr normal_address(
      new Network::Address::Ipv4Instance("127.0.0.1", 80));
  // Original dst address nor port number match that of the listener's address.
  Network::Address::InstanceConstSharedPtr original_dst_address(
      new Network::Address::Ipv4Instance("127.0.0.2", 8080));
  Network::Address::InstanceConstSharedPtr any_address = Network::Utility::getAddressWithPort(
      *Network::Utility::getIpv4AnyAddress(), normal_address->ip()->port());
  EXPECT_CALL(test_listener1->socket_, localAddress()).WillRepeatedly(ReturnRef(any_address));
  handler_->addListener(*test_listener1);

  Network::MockListenerFilter* test_filter = new Network::MockListenerFilter();
  Network::MockConnectionSocket* accepted_socket = new NiceMock<Network::MockConnectionSocket>();
  EXPECT_CALL(factory_, createListenerFilterChain(_))
      .WillRepeatedly(Invoke([&](Network::ListenerFilterManager& manager) -> bool {
        // Insert the Mock filter.
        manager.addAcceptFilter(Network::ListenerFilterPtr{test_filter});
        return true;
      }));
  EXPECT_CALL(*test_filter, onAccept(_))
      .WillOnce(Invoke([&](Network::ListenerFilterCallbacks& cb) -> Network::FilterStatus {
        cb.socket().restoreLocalAddress(original_dst_address);
        return Network::FilterStatus::Continue;
      }));
  EXPECT_CALL(*test_filter, destroy_());
  EXPECT_CALL(*accepted_socket, restoreLocalAddress(original_dst_address));
  EXPECT_CALL(*accepted_socket, localAddressRestored()).WillOnce(Return(true));
  EXPECT_CALL(*accepted_socket, localAddress()).WillRepeatedly(ReturnRef(original_dst_address));
  EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(filter_chain_.get()));
  Network::MockConnection* connection = new NiceMock<Network::MockConnection>();
  EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection));
  EXPECT_CALL(factory_, createNetworkFilterChain(_, _)).WillOnce(Return(true));
  listener_callbacks1->onAccept(Network::ConnectionSocketPtr{accepted_socket});
  EXPECT_EQ(1UL, handler_->numConnections());

  EXPECT_CALL(*listener1, onDestroy());
}


xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
                 xmlNodePtr inst, xsltElemPreCompPtr castedComp)
{
#ifdef XSLT_REFACTORED
    xsltStyleItemDocumentPtr comp = (xsltStyleItemDocumentPtr) castedComp;
#else
    xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
#endif
    xsltStylesheetPtr style = NULL;
    int ret;
    xmlChar *filename = NULL, *prop, *elements;
    xmlChar *element, *end;
    xmlDocPtr res = NULL;
    xmlDocPtr oldOutput;
    xmlNodePtr oldInsert, root;
    const char *oldOutputFile;
    xsltOutputType oldType;
    xmlChar *URL = NULL;
    const xmlChar *method;
    const xmlChar *doctypePublic;
    const xmlChar *doctypeSystem;
    const xmlChar *version;
    const xmlChar *encoding;
    int redirect_write_append = 0;

    if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
        return;

    if (comp->filename == NULL) {

        if (xmlStrEqual(inst->name, (const xmlChar *) "output")) {
	    /*
	    * The element "output" is in the namespace XSLT_SAXON_NAMESPACE
	    *   (http://icl.com/saxon)
	    * The @file is in no namespace.
	    */
#ifdef WITH_XSLT_DEBUG_EXTRA
            xsltGenericDebug(xsltGenericDebugContext,
                             "Found saxon:output extension\n");
#endif
            URL = xsltEvalAttrValueTemplate(ctxt, inst,
                                                 (const xmlChar *) "file",
                                                 XSLT_SAXON_NAMESPACE);

	    if (URL == NULL)
		URL = xsltEvalAttrValueTemplate(ctxt, inst,
                                                 (const xmlChar *) "href",
                                                 XSLT_SAXON_NAMESPACE);
        } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) {
#ifdef WITH_XSLT_DEBUG_EXTRA
            xsltGenericDebug(xsltGenericDebugContext,
                             "Found xalan:write extension\n");
#endif
            URL = xsltEvalAttrValueTemplate(ctxt, inst,
                                                 (const xmlChar *)
                                                 "select",
                                                 XSLT_XALAN_NAMESPACE);
	    if (URL != NULL) {
		xmlXPathCompExprPtr cmp;
		xmlChar *val;

		/*
		 * Trying to handle bug #59212
		 * The value of the "select" attribute is an
		 * XPath expression.
		 * (see http://xml.apache.org/xalan-j/extensionslib.html#redirect)
		 */
		cmp = xmlXPathCtxtCompile(ctxt->xpathCtxt, URL);
                val = xsltEvalXPathString(ctxt, cmp);
		xmlXPathFreeCompExpr(cmp);
		xmlFree(URL);
		URL = val;
	    }
	    if (URL == NULL)
		URL = xsltEvalAttrValueTemplate(ctxt, inst,
						     (const xmlChar *)
						     "file",
						     XSLT_XALAN_NAMESPACE);
	    if (URL == NULL)
		URL = xsltEvalAttrValueTemplate(ctxt, inst,
						     (const xmlChar *)
						     "href",
						     XSLT_XALAN_NAMESPACE);
        } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) {
            URL = xsltEvalAttrValueTemplate(ctxt, inst,
                                                 (const xmlChar *) "href",
                                                 NULL);
        }

    } else {
        URL = xmlStrdup(comp->filename);
    }

    if (URL == NULL) {
	xsltTransformError(ctxt, NULL, inst,
		         "xsltDocumentElem: href/URI-Reference not found\n");
	return;
    }

    /*
     * If the computation failed, it's likely that the URL wasn't escaped
     */
    filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile);
    if (filename == NULL) {
	xmlChar *escURL;

	escURL=xmlURIEscapeStr(URL, BAD_CAST ":/.?,");
	if (escURL != NULL) {
	    filename = xmlBuildURI(escURL, (const xmlChar *) ctxt->outputFile);
	    xmlFree(escURL);
	}
    }

    if (filename == NULL) {
	xsltTransformError(ctxt, NULL, inst,
		         "xsltDocumentElem: URL computation failed for %s\n",
			 URL);
	xmlFree(URL);
	return;
    }

    /*
     * Security checking: can we write to this resource
     */
    if (ctxt->sec != NULL) {
	ret = xsltCheckWrite(ctxt->sec, ctxt, filename);
	if (ret <= 0) {
            if (ret == 0)
                xsltTransformError(ctxt, NULL, inst,
                     "xsltDocumentElem: write rights for %s denied\n",
                                 filename);
	    xmlFree(URL);
	    xmlFree(filename);
	    return;
	}
    }

    oldOutputFile = ctxt->outputFile;
    oldOutput = ctxt->output;
    oldInsert = ctxt->insert;
    oldType = ctxt->type;
    ctxt->outputFile = (const char *) filename;

    style = xsltNewStylesheet();
    if (style == NULL) {
	xsltTransformError(ctxt, NULL, inst,
                         "xsltDocumentElem: out of memory\n");
        goto error;
    }

    /*
     * Version described in 1.1 draft allows full parameterization
     * of the output.
     */
    prop = xsltEvalAttrValueTemplate(ctxt, inst,
				     (const xmlChar *) "version",
				     NULL);
    if (prop != NULL) {
	if (style->version != NULL)
	    xmlFree(style->version);
	style->version = prop;
    }
    prop = xsltEvalAttrValueTemplate(ctxt, inst,
				     (const xmlChar *) "encoding",
				     NULL);
    if (prop != NULL) {
	if (style->encoding != NULL)
	    xmlFree(style->encoding);
	style->encoding = prop;
    }
    prop = xsltEvalAttrValueTemplate(ctxt, inst,
				     (const xmlChar *) "method",
				     NULL);
    if (prop != NULL) {
	const xmlChar *URI;

	if (style->method != NULL)
	    xmlFree(style->method);
	style->method = NULL;
	if (style->methodURI != NULL)
	    xmlFree(style->methodURI);
	style->methodURI = NULL;

	URI = xsltGetQNameURI(inst, &prop);
	if (prop == NULL) {
	    if (style != NULL) style->errors++;
	} else if (URI == NULL) {
	    if ((xmlStrEqual(prop, (const xmlChar *) "xml")) ||
		(xmlStrEqual(prop, (const xmlChar *) "html")) ||
		(xmlStrEqual(prop, (const xmlChar *) "text"))) {
		style->method = prop;
	    } else {
		xsltTransformError(ctxt, NULL, inst,
				 "invalid value for method: %s\n", prop);
		if (style != NULL) style->warnings++;
	    }
	} else {
	    style->method = prop;
	    style->methodURI = xmlStrdup(URI);
	}
    }
    prop = xsltEvalAttrValueTemplate(ctxt, inst,
				     (const xmlChar *)
				     "doctype-system", NULL);
    if (prop != NULL) {
	if (style->doctypeSystem != NULL)
	    xmlFree(style->doctypeSystem);
	style->doctypeSystem = prop;
    }
    prop = xsltEvalAttrValueTemplate(ctxt, inst,
				     (const xmlChar *)
				     "doctype-public", NULL);
    if (prop != NULL) {
	if (style->doctypePublic != NULL)
	    xmlFree(style->doctypePublic);
	style->doctypePublic = prop;
    }
    prop = xsltEvalAttrValueTemplate(ctxt, inst,
				     (const xmlChar *) "standalone",
				     NULL);
    if (prop != NULL) {
	if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
	    style->standalone = 1;
	} else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
	    style->standalone = 0;
	} else {
	    xsltTransformError(ctxt, NULL, inst,
			     "invalid value for standalone: %s\n",
			     prop);
	    if (style != NULL) style->warnings++;
	}
	xmlFree(prop);
    }

    prop = xsltEvalAttrValueTemplate(ctxt, inst,
				     (const xmlChar *) "indent",
				     NULL);
    if (prop != NULL) {
	if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
	    style->indent = 1;
	} else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
	    style->indent = 0;
	} else {
	    xsltTransformError(ctxt, NULL, inst,
			     "invalid value for indent: %s\n", prop);
	    if (style != NULL) style->warnings++;
	}
	xmlFree(prop);
    }

    prop = xsltEvalAttrValueTemplate(ctxt, inst,
				     (const xmlChar *)
				     "omit-xml-declaration",
				     NULL);
    if (prop != NULL) {
	if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
	    style->omitXmlDeclaration = 1;
	} else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
	    style->omitXmlDeclaration = 0;
	} else {
	    xsltTransformError(ctxt, NULL, inst,
			     "invalid value for omit-xml-declaration: %s\n",
			     prop);
	    if (style != NULL) style->warnings++;
	}
	xmlFree(prop);
    }

    elements = xsltEvalAttrValueTemplate(ctxt, inst,
					 (const xmlChar *)
					 "cdata-section-elements",
					 NULL);
    if (elements != NULL) {
	if (style->stripSpaces == NULL)
	    style->stripSpaces = xmlHashCreate(10);
	if (style->stripSpaces == NULL)
	    return;

	element = elements;
	while (*element != 0) {
	    while (IS_BLANK_CH(*element))
		element++;
	    if (*element == 0)
		break;
	    end = element;
	    while ((*end != 0) && (!IS_BLANK_CH(*end)))
		end++;
	    element = xmlStrndup(element, end - element);
	    if (element) {
		const xmlChar *URI;

#ifdef WITH_XSLT_DEBUG_PARSING
		xsltGenericDebug(xsltGenericDebugContext,
				 "add cdata section output element %s\n",
				 element);
#endif
                URI = xsltGetQNameURI(inst, &element);

		xmlHashAddEntry2(style->stripSpaces, element, URI,
			        (xmlChar *) "cdata");
		xmlFree(element);
	    }
	    element = end;
	}
	xmlFree(elements);
    }

    /*
     * Create a new document tree and process the element template
     */
    XSLT_GET_IMPORT_PTR(method, style, method)
    XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
    XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
    XSLT_GET_IMPORT_PTR(version, style, version)
    XSLT_GET_IMPORT_PTR(encoding, style, encoding)

    if ((method != NULL) &&
	(!xmlStrEqual(method, (const xmlChar *) "xml"))) {
	if (xmlStrEqual(method, (const xmlChar *) "html")) {
	    ctxt->type = XSLT_OUTPUT_HTML;
	    if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
		res = htmlNewDoc(doctypeSystem, doctypePublic);
	    else {
		if (version != NULL) {
#ifdef XSLT_GENERATE_HTML_DOCTYPE
		    xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
#endif
                }
		res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
	    }
	    if (res == NULL)
		goto error;
	    res->dict = ctxt->dict;
	    xmlDictReference(res->dict);
	} else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
	    xsltTransformError(ctxt, NULL, inst,
	     "xsltDocumentElem: unsupported method xhtml\n");
	    ctxt->type = XSLT_OUTPUT_HTML;
	    res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
	    if (res == NULL)
		goto error;
	    res->dict = ctxt->dict;
	    xmlDictReference(res->dict);
	} else if (xmlStrEqual(method, (const xmlChar *) "text")) {
	    ctxt->type = XSLT_OUTPUT_TEXT;
	    res = xmlNewDoc(style->version);
	    if (res == NULL)
		goto error;
	    res->dict = ctxt->dict;
	    xmlDictReference(res->dict);
#ifdef WITH_XSLT_DEBUG
	    xsltGenericDebug(xsltGenericDebugContext,
                     "reusing transformation dict for output\n");
#endif
	} else {
	    xsltTransformError(ctxt, NULL, inst,
			     "xsltDocumentElem: unsupported method (%s)\n",
		             method);
	    goto error;
	}
    } else {
	ctxt->type = XSLT_OUTPUT_XML;
	res = xmlNewDoc(style->version);
	if (res == NULL)
	    goto error;
	res->dict = ctxt->dict;
	xmlDictReference(res->dict);
#ifdef WITH_XSLT_DEBUG
	xsltGenericDebug(xsltGenericDebugContext,
                     "reusing transformation dict for output\n");
#endif
    }
    res->charset = XML_CHAR_ENCODING_UTF8;
    if (encoding != NULL)
	res->encoding = xmlStrdup(encoding);
    ctxt->output = res;
    ctxt->insert = (xmlNodePtr) res;
    xsltApplySequenceConstructor(ctxt, node, inst->children, NULL);

    /*
     * Do some post processing work depending on the generated output
     */
    root = xmlDocGetRootElement(res);
    if (root != NULL) {
        const xmlChar *doctype = NULL;

        if ((root->ns != NULL) && (root->ns->prefix != NULL))
	    doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name);
	if (doctype == NULL)
	    doctype = root->name;

        /*
         * Apply the default selection of the method
         */
        if ((method == NULL) &&
            (root->ns == NULL) &&
            (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
            xmlNodePtr tmp;

            tmp = res->children;
            while ((tmp != NULL) && (tmp != root)) {
                if (tmp->type == XML_ELEMENT_NODE)
                    break;
                if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
                    break;
		tmp = tmp->next;
            }
            if (tmp == root) {
                ctxt->type = XSLT_OUTPUT_HTML;
                res->type = XML_HTML_DOCUMENT_NODE;
                if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
                    res->intSubset = xmlCreateIntSubset(res, doctype,
                                                        doctypePublic,
                                                        doctypeSystem);
#ifdef XSLT_GENERATE_HTML_DOCTYPE
		} else if (version != NULL) {
                    xsltGetHTMLIDs(version, &doctypePublic,
                                   &doctypeSystem);
                    if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
                        res->intSubset =
                            xmlCreateIntSubset(res, doctype,
                                               doctypePublic,
                                               doctypeSystem);
#endif
                }
            }

        }
        if (ctxt->type == XSLT_OUTPUT_XML) {
            XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
                XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
                if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
                res->intSubset = xmlCreateIntSubset(res, doctype,
                                                    doctypePublic,
                                                    doctypeSystem);
        }
    }

    /*
     * Calls to redirect:write also take an optional attribute append.
     * Attribute append="true|yes" which will attempt to simply append
     * to an existing file instead of always opening a new file. The
     * default behavior of always overwriting the file still happens
     * if we do not specify append.
     * Note that append use will forbid use of remote URI target.
     */
    prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"append",
				     NULL);
    if (prop != NULL) {
	if (xmlStrEqual(prop, (const xmlChar *) "true") ||
	    xmlStrEqual(prop, (const xmlChar *) "yes")) {
	    style->omitXmlDeclaration = 1;
	    redirect_write_append = 1;
	} else
	    style->omitXmlDeclaration = 0;
	xmlFree(prop);
    }

    if (redirect_write_append) {
        FILE *f;

	f = fopen((const char *) filename, "ab");
	if (f == NULL) {
	    ret = -1;
	} else {
	    ret = xsltSaveResultToFile(f, res, style);
	    fclose(f);
	}
    } else {
	ret = xsltSaveResultToFilename((const char *) filename, res, style, 0);
    }
    if (ret < 0) {
	xsltTransformError(ctxt, NULL, inst,
                         "xsltDocumentElem: unable to save to %s\n",
                         filename);
#ifdef WITH_XSLT_DEBUG_EXTRA
    } else {
        xsltGenericDebug(xsltGenericDebugContext,
                         "Wrote %d bytes to %s\n", ret, filename);
#endif
    }

  error:
    ctxt->output = oldOutput;
    ctxt->insert = oldInsert;
    ctxt->type = oldType;
    ctxt->outputFile = oldOutputFile;
    if (URL != NULL)
        xmlFree(URL);
    if (filename != NULL)
        xmlFree(filename);
    if (style != NULL)
        xsltFreeStylesheet(style);
    if (res != NULL)
        xmlFreeDoc(res);
}


static int setCompDefaults(struct jpeg_compress_struct *cinfo, int pixelFormat,
                           int subsamp, int jpegQual, int flags)
{
  int retval = 0;
#ifndef NO_GETENV
  char *env = NULL;
#endif

  cinfo->in_color_space = pf2cs[pixelFormat];
  cinfo->input_components = tjPixelSize[pixelFormat];
  jpeg_set_defaults(cinfo);

#ifndef NO_GETENV
  if ((env = getenv("TJ_OPTIMIZE")) != NULL && strlen(env) > 0 &&
      !strcmp(env, "1"))
    cinfo->optimize_coding = TRUE;
  if ((env = getenv("TJ_ARITHMETIC")) != NULL && strlen(env) > 0 &&
      !strcmp(env, "1"))
    cinfo->arith_code = TRUE;
  if ((env = getenv("TJ_RESTART")) != NULL && strlen(env) > 0) {
    int temp = -1;
    char tempc = 0;

    if (sscanf(env, "%d%c", &temp, &tempc) >= 1 && temp >= 0 &&
        temp <= 65535) {
      if (toupper(tempc) == 'B') {
        cinfo->restart_interval = temp;
        cinfo->restart_in_rows = 0;
      } else
        cinfo->restart_in_rows = temp;
    }
  }
#endif

  if (jpegQual >= 0) {
    jpeg_set_quality(cinfo, jpegQual, TRUE);
    if (jpegQual >= 96 || flags & TJFLAG_ACCURATEDCT)
      cinfo->dct_method = JDCT_ISLOW;
    else
      cinfo->dct_method = JDCT_FASTEST;
  }
  if (subsamp == TJSAMP_GRAY)
    jpeg_set_colorspace(cinfo, JCS_GRAYSCALE);
  else if (pixelFormat == TJPF_CMYK)
    jpeg_set_colorspace(cinfo, JCS_YCCK);
  else
    jpeg_set_colorspace(cinfo, JCS_YCbCr);

  if (flags & TJFLAG_PROGRESSIVE)
    jpeg_simple_progression(cinfo);
#ifndef NO_GETENV
  else if ((env = getenv("TJ_PROGRESSIVE")) != NULL && strlen(env) > 0 &&
           !strcmp(env, "1"))
    jpeg_simple_progression(cinfo);
#endif

  cinfo->comp_info[0].h_samp_factor = tjMCUWidth[subsamp] / 8;
  cinfo->comp_info[1].h_samp_factor = 1;
  cinfo->comp_info[2].h_samp_factor = 1;
  if (cinfo->num_components > 3)
    cinfo->comp_info[3].h_samp_factor = tjMCUWidth[subsamp] / 8;
  cinfo->comp_info[0].v_samp_factor = tjMCUHeight[subsamp] / 8;
  cinfo->comp_info[1].v_samp_factor = 1;
  cinfo->comp_info[2].v_samp_factor = 1;
  if (cinfo->num_components > 3)
    cinfo->comp_info[3].v_samp_factor = tjMCUHeight[subsamp] / 8;

  return retval;
}


static void initBitmap(unsigned char *buf, int width, int pitch, int height,
                       int pf, int flags)
{
  int roffset = tjRedOffset[pf];
  int goffset = tjGreenOffset[pf];
  int boffset = tjBlueOffset[pf];
  int ps = tjPixelSize[pf];
  int i, j;

  for (j = 0; j < height; j++) {
    int row = (flags & TJFLAG_BOTTOMUP) ? height - j - 1 : j;

    for (i = 0; i < width; i++) {
      unsigned char r = (i * 256 / width) % 256;
      unsigned char g = (j * 256 / height) % 256;
      unsigned char b = (j * 256 / height + i * 256 / width) % 256;

      memset(&buf[row * pitch + i * ps], 0, ps);
      if (pf == TJPF_GRAY) buf[row * pitch + i * ps] = b;
      else if (pf == TJPF_CMYK)
        rgb_to_cmyk(r, g, b, &buf[row * pitch + i * ps + 0],
                    &buf[row * pitch + i * ps + 1],
                    &buf[row * pitch + i * ps + 2],
                    &buf[row * pitch + i * ps + 3]);
      else {
        buf[row * pitch + i * ps + roffset] = r;
        buf[row * pitch + i * ps + goffset] = g;
        buf[row * pitch + i * ps + boffset] = b;
      }
    }
  }
}


DLLEXPORT int tjDecodeYUVPlanes(tjhandle handle,
                                const unsigned char **srcPlanes,
                                const int *strides, int subsamp,
                                unsigned char *dstBuf, int width, int pitch,
                                int height, int pixelFormat, int flags)
{
  JSAMPROW *row_pointer = NULL;
  JSAMPLE *_tmpbuf[MAX_COMPONENTS];
  JSAMPROW *tmpbuf[MAX_COMPONENTS], *inbuf[MAX_COMPONENTS];
  int i, retval = 0, row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
  JSAMPLE *ptr;
  jpeg_component_info *compptr;
  int (*old_read_markers) (j_decompress_ptr);
  void (*old_reset_marker_reader) (j_decompress_ptr);

  GET_DINSTANCE(handle);
  this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;

  for (i = 0; i < MAX_COMPONENTS; i++) {
    tmpbuf[i] = NULL;  _tmpbuf[i] = NULL;  inbuf[i] = NULL;
  }

  if ((this->init & DECOMPRESS) == 0)
    THROW("tjDecodeYUVPlanes(): Instance has not been initialized for decompression");

  if (!srcPlanes || !srcPlanes[0] || subsamp < 0 || subsamp >= NUMSUBOPT ||
      dstBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
      pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
    THROW("tjDecodeYUVPlanes(): Invalid argument");
  if (subsamp != TJSAMP_GRAY && (!srcPlanes[1] || !srcPlanes[2]))
    THROW("tjDecodeYUVPlanes(): Invalid argument");

  if (setjmp(this->jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error. */
    retval = -1;  goto bailout;
  }

  if (pixelFormat == TJPF_CMYK)
    THROW("tjDecodeYUVPlanes(): Cannot decode YUV images into CMYK pixels.");

  if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];
  dinfo->image_width = width;
  dinfo->image_height = height;

#ifndef NO_PUTENV
  if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
  else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
  else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
#endif

  if (setDecodeDefaults(dinfo, pixelFormat, subsamp, flags) == -1) {
    retval = -1;  goto bailout;
  }
  old_read_markers = dinfo->marker->read_markers;
  dinfo->marker->read_markers = my_read_markers;
  old_reset_marker_reader = dinfo->marker->reset_marker_reader;
  dinfo->marker->reset_marker_reader = my_reset_marker_reader;
  jpeg_read_header(dinfo, TRUE);
  dinfo->marker->read_markers = old_read_markers;
  dinfo->marker->reset_marker_reader = old_reset_marker_reader;

  this->dinfo.out_color_space = pf2cs[pixelFormat];
  if (flags & TJFLAG_FASTDCT) this->dinfo.dct_method = JDCT_FASTEST;
  dinfo->do_fancy_upsampling = FALSE;
  dinfo->Se = DCTSIZE2 - 1;
  jinit_master_decompress(dinfo);
  (*dinfo->upsample->start_pass) (dinfo);

  pw0 = PAD(width, dinfo->max_h_samp_factor);
  ph0 = PAD(height, dinfo->max_v_samp_factor);

  if (pitch == 0) pitch = dinfo->output_width * tjPixelSize[pixelFormat];

  if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph0)) == NULL)
    THROW("tjDecodeYUVPlanes(): Memory allocation failure");
  for (i = 0; i < height; i++) {
    if (flags & TJFLAG_BOTTOMUP)
      row_pointer[i] = &dstBuf[(height - i - 1) * (size_t)pitch];
    else
      row_pointer[i] = &dstBuf[i * (size_t)pitch];
  }
  if (height < ph0)
    for (i = height; i < ph0; i++) row_pointer[i] = row_pointer[height - 1];

  for (i = 0; i < dinfo->num_components; i++) {
    compptr = &dinfo->comp_info[i];
    _tmpbuf[i] =
      (JSAMPLE *)malloc(PAD(compptr->width_in_blocks * DCTSIZE, 32) *
                        compptr->v_samp_factor + 32);
    if (!_tmpbuf[i])
      THROW("tjDecodeYUVPlanes(): Memory allocation failure");
    tmpbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * compptr->v_samp_factor);
    if (!tmpbuf[i])
      THROW("tjDecodeYUVPlanes(): Memory allocation failure");
    for (row = 0; row < compptr->v_samp_factor; row++) {
      unsigned char *_tmpbuf_aligned =
        (unsigned char *)PAD((size_t)_tmpbuf[i], 32);

      tmpbuf[i][row] =
        &_tmpbuf_aligned[PAD(compptr->width_in_blocks * DCTSIZE, 32) * row];
    }
    pw[i] = pw0 * compptr->h_samp_factor / dinfo->max_h_samp_factor;
    ph[i] = ph0 * compptr->v_samp_factor / dinfo->max_v_samp_factor;
    inbuf[i] = (JSAMPROW *)malloc(sizeof(JSAMPROW) * ph[i]);
    if (!inbuf[i])
      THROW("tjDecodeYUVPlanes(): Memory allocation failure");
    ptr = (JSAMPLE *)srcPlanes[i];
    for (row = 0; row < ph[i]; row++) {
      inbuf[i][row] = ptr;
      ptr += (strides && strides[i] != 0) ? strides[i] : pw[i];
    }
  }

  if (setjmp(this->jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error. */
    retval = -1;  goto bailout;
  }

  for (row = 0; row < ph0; row += dinfo->max_v_samp_factor) {
    JDIMENSION inrow = 0, outrow = 0;

    for (i = 0, compptr = dinfo->comp_info; i < dinfo->num_components;
         i++, compptr++)
      jcopy_sample_rows(inbuf[i],
        row * compptr->v_samp_factor / dinfo->max_v_samp_factor, tmpbuf[i], 0,
        compptr->v_samp_factor, pw[i]);
    (dinfo->upsample->upsample) (dinfo, tmpbuf, &inrow,
                                 dinfo->max_v_samp_factor, &row_pointer[row],
                                 &outrow, dinfo->max_v_samp_factor);
  }
  jpeg_abort_decompress(dinfo);

bailout:
  if (dinfo->global_state > DSTATE_START) jpeg_abort_decompress(dinfo);
  if (row_pointer) free(row_pointer);
  for (i = 0; i < MAX_COMPONENTS; i++) {
    if (tmpbuf[i] != NULL) free(tmpbuf[i]);
    if (_tmpbuf[i] != NULL) free(_tmpbuf[i]);
    if (inbuf[i] != NULL) free(inbuf[i]);
  }
  if (this->jerr.warning) retval = -1;
  this->jerr.stopOnWarning = FALSE;
  return retval;
}


DLLEXPORT int tjCompress2(tjhandle handle, const unsigned char *srcBuf,
                          int width, int pitch, int height, int pixelFormat,
                          unsigned char **jpegBuf, unsigned long *jpegSize,
                          int jpegSubsamp, int jpegQual, int flags)
{
  int i, retval = 0, alloc = 1;
  JSAMPROW *row_pointer = NULL;

  GET_CINSTANCE(handle)
  this->jerr.stopOnWarning = (flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
  if ((this->init & COMPRESS) == 0)
    THROW("tjCompress2(): Instance has not been initialized for compression");

  if (srcBuf == NULL || width <= 0 || pitch < 0 || height <= 0 ||
      pixelFormat < 0 || pixelFormat >= TJ_NUMPF || jpegBuf == NULL ||
      jpegSize == NULL || jpegSubsamp < 0 || jpegSubsamp >= NUMSUBOPT ||
      jpegQual < 0 || jpegQual > 100)
    THROW("tjCompress2(): Invalid argument");

  if (pitch == 0) pitch = width * tjPixelSize[pixelFormat];

  if ((row_pointer = (JSAMPROW *)malloc(sizeof(JSAMPROW) * height)) == NULL)
    THROW("tjCompress2(): Memory allocation failure");

  if (setjmp(this->jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error. */
    retval = -1;  goto bailout;
  }

  cinfo->image_width = width;
  cinfo->image_height = height;

#ifndef NO_PUTENV
  if (flags & TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1");
  else if (flags & TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1");
  else if (flags & TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1");
#endif

  if (flags & TJFLAG_NOREALLOC) {
    alloc = 0;  *jpegSize = tjBufSize(width, height, jpegSubsamp);
  }
  jpeg_mem_dest_tj(cinfo, jpegBuf, jpegSize, alloc);
  if (setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual, flags) == -1)
    return -1;

  jpeg_start_compress(cinfo, TRUE);
  for (i = 0; i < height; i++) {
    if (flags & TJFLAG_BOTTOMUP)
      row_pointer[i] = (JSAMPROW)&srcBuf[(height - i - 1) * (size_t)pitch];
    else
      row_pointer[i] = (JSAMPROW)&srcBuf[i * (size_t)pitch];
  }
  while (cinfo->next_scanline < cinfo->image_height)
    jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
                         cinfo->image_height - cinfo->next_scanline);
  jpeg_finish_compress(cinfo);

bailout:
  if (cinfo->global_state > CSTATE_START) jpeg_abort_compress(cinfo);
  if (row_pointer) free(row_pointer);
  if (this->jerr.warning) retval = -1;
  this->jerr.stopOnWarning = FALSE;
  return retval;
}


static int dummyDCTFilter(short *coeffs, tjregion arrayRegion,
                          tjregion planeRegion, int componentIndex,
                          int transformIndex, tjtransform *transform)
{
  int i;

  for (i = 0; i < arrayRegion.w * arrayRegion.h; i++)
    coeffs[i] = -coeffs[i];
  return 0;
}


soup_auth_ntlm_init (SoupAuthNTLM *ntlm)
{
#ifdef USE_NTLM_AUTH
	SoupAuthNTLMPrivate *priv = soup_auth_ntlm_get_instance_private (ntlm);
	const char *username = NULL, *slash;

	priv->sso_available = TRUE;
	priv->fd_in = -1;
	priv->fd_out = -1;

	username = getenv ("NTLMUSER");
	if (!username)
		username = g_get_user_name ();

	slash = strpbrk (username, "\\/");
	if (slash) {
		priv->username = g_strdup (slash + 1);
		priv->domain = g_strndup (username, slash - username);
	} else {
		priv->username = g_strdup (username);
		priv->domain = NULL;
	}
#endif
}


soup_auth_ntlm_is_connection_ready (SoupConnectionAuth *auth,
				    SoupMessage        *msg,
				    gpointer            state)
{
	SoupAuthNTLM *auth_ntlm = SOUP_AUTH_NTLM (auth);
	SoupAuthNTLMPrivate *priv = soup_auth_ntlm_get_instance_private (auth_ntlm);
	SoupNTLMConnectionState *conn = state;

	if (priv->password_state == SOUP_NTLM_PASSWORD_REJECTED)
		return FALSE;

	if (priv->password_state == SOUP_NTLM_PASSWORD_PROVIDED)
		return TRUE;

	return conn->state != SOUP_NTLM_FAILED;
}


calc_ntlm2_session_response (const char *nonce,
			     guchar      nt_hash[21],
			     guchar      lm_hash[21],
			     guchar     *lm_resp,
			     gsize       lm_resp_sz,
			     guchar     *nt_resp)
{
	guint32 client_nonce[2];
	guchar ntlmv2_hash[16];
	GChecksum *ntlmv2_cksum;
	gsize ntlmv2_hash_sz = sizeof (ntlmv2_hash);

	/* FIXME: if GLib ever gets a more secure random number
	 * generator, use it here
	 */
	client_nonce[0] = g_random_int();
	client_nonce[1] = g_random_int();

	ntlmv2_cksum = g_checksum_new (G_CHECKSUM_MD5);
	g_checksum_update (ntlmv2_cksum, (const guchar *) nonce, 8);
	g_checksum_update (ntlmv2_cksum, (const guchar *) client_nonce, sizeof (client_nonce));
	g_checksum_get_digest (ntlmv2_cksum, ntlmv2_hash, &ntlmv2_hash_sz);
	g_checksum_free (ntlmv2_cksum);

	/* Send the padded client nonce as a fake lm_resp */
	memset (lm_resp, 0, lm_resp_sz);
	memcpy (lm_resp, client_nonce, sizeof (client_nonce));

	/* Compute nt_hash as usual but with a new nonce */
	calc_response (nt_hash, ntlmv2_hash, nt_resp);
}


des (guint32 ks[16][2], unsigned char block[8])
{
	guint32 left,right,work;

	/* Read input block and place in left/right in big-endian order */
	left = ((guint32)block[0] << 24)
	 | ((guint32)block[1] << 16)
	 | ((guint32)block[2] << 8)
	 | (guint32)block[3];
	right = ((guint32)block[4] << 24)
	 | ((guint32)block[5] << 16)
	 | ((guint32)block[6] << 8)
	 | (guint32)block[7];

	/* Hoey's clever initial permutation algorithm, from Outerbridge
	 * (see Schneier p 478)
	 *
	 * The convention here is the same as Outerbridge: rotate each
	 * register left by 1 bit, i.e., so that "left" contains permuted
	 * input bits 2, 3, 4, ... 1 and "right" contains 33, 34, 35, ... 32
	 * (using origin-1 numbering as in the FIPS). This allows us to avoid
	 * one of the two rotates that would otherwise be required in each of
	 * the 16 rounds.
	 */
	work = ((left >> 4) ^ right) & 0x0f0f0f0f;
	right ^= work;
	left ^= work << 4;
	work = ((left >> 16) ^ right) & 0xffff;
	right ^= work;
	left ^= work << 16;
	work = ((right >> 2) ^ left) & 0x33333333;
	left ^= work;
	right ^= (work << 2);
	work = ((right >> 8) ^ left) & 0xff00ff;
	left ^= work;
	right ^= (work << 8);
	right = (right << 1) | (right >> 31);
	work = (left ^ right) & 0xaaaaaaaa;
	left ^= work;
	right ^= work;
	left = (left << 1) | (left >> 31);

	/* Now do the 16 rounds */
	F(left,right,ks[0]);
	F(right,left,ks[1]);
	F(left,right,ks[2]);
	F(right,left,ks[3]);
	F(left,right,ks[4]);
	F(right,left,ks[5]);
	F(left,right,ks[6]);
	F(right,left,ks[7]);
	F(left,right,ks[8]);
	F(right,left,ks[9]);
	F(left,right,ks[10]);
	F(right,left,ks[11]);
	F(left,right,ks[12]);
	F(right,left,ks[13]);
	F(left,right,ks[14]);
	F(right,left,ks[15]);

	/* Inverse permutation, also from Hoey via Outerbridge and Schneier */
	right = (right << 31) | (right >> 1);
	work = (left ^ right) & 0xaaaaaaaa;
	left ^= work;
	right ^= work;
	left = (left >> 1) | (left  << 31);
	work = ((left >> 8) ^ right) & 0xff00ff;
	right ^= work;
	left ^= work << 8;
	work = ((left >> 2) ^ right) & 0x33333333;
	right ^= work;
	left ^= work << 2;
	work = ((right >> 16) ^ left) & 0xffff;
	left ^= work;
	right ^= work << 16;
	work = ((right >> 4) ^ left) & 0x0f0f0f0f;
	left ^= work;
	right ^= work << 4;

	/* Put the block back into the user's buffer with final swap */
	block[0] = right >> 24;
	block[1] = right >> 16;
	block[2] = right >> 8;
	block[3] = right;
	block[4] = left >> 24;
	block[5] = left >> 16;
	block[6] = left >> 8;
	block[7] = left;
}


soup_ntlm_parse_challenge (const char *challenge,
			   char      **nonce,
			   char      **default_domain,
			   gboolean   *ntlmv2_session,
			   gboolean   *negotiate_target,
			   char		**target_info,
			   size_t	*target_info_sz)
{
	gsize clen;
	NTLMString domain;
	NTLMString target;
	guchar *chall;
	guint32 flags;

	chall = g_base64_decode (challenge, &clen);
	if (clen < NTLM_CHALLENGE_DOMAIN_STRING_OFFSET ||
	    clen < NTLM_CHALLENGE_NONCE_OFFSET + NTLM_CHALLENGE_NONCE_LENGTH) {
		g_free (chall);
		return FALSE;
	}

	memcpy (&flags, chall + NTLM_CHALLENGE_FLAGS_OFFSET, sizeof(flags));
	flags = GUINT_FROM_LE (flags);
	*ntlmv2_session = (flags & NTLM_FLAGS_NEGOTIATE_NTLMV2) ? TRUE : FALSE;
	//To know if NTLMv2 responses should be calculated
	*negotiate_target = (flags & NTLM_FLAGS_NEGOTIATE_TARGET_INFORMATION ) ? TRUE : FALSE;

	if (default_domain) {
		memcpy (&domain, chall + NTLM_CHALLENGE_DOMAIN_STRING_OFFSET, sizeof (domain));
		domain.length = GUINT16_FROM_LE (domain.length);
		domain.offset = GUINT16_FROM_LE (domain.offset);

		if (clen < domain.length + domain.offset) {
			g_free (chall);
			return FALSE;
		}

		*default_domain = g_convert ((char *)chall + domain.offset,
					     domain.length, "UTF-8", "UCS-2LE",
					     NULL, NULL, NULL);
	}

	if (nonce) {
		*nonce = g_memdup (chall + NTLM_CHALLENGE_NONCE_OFFSET,
				   NTLM_CHALLENGE_NONCE_LENGTH);
	}
	//For NTLMv2 response
	if (*negotiate_target && target_info) {
		memcpy (&target, chall + NTLM_CHALLENGE_TARGET_INFORMATION_OFFSET, sizeof (target));
		target.length = GUINT16_FROM_LE (target.length);
		target.offset = GUINT16_FROM_LE (target.offset);

		if (clen < target.length + target.offset) {
			g_free (chall);
			return FALSE;
		}
		*target_info = g_memdup (chall + target.offset, target.length);
		*target_info_sz = target.length;
	}

	g_free (chall);
	return TRUE;
}


psutil_proc_cpu_times(PyObject *self, PyObject *args) {
    int pid;
    char path[100];
    pstatus_t info;
    const char *procfs_path;

    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
        return NULL;
    sprintf(path, "%s/%i/status", procfs_path, pid);
    if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
        return NULL;
    // results are more precise than os.times()
    return Py_BuildValue("dddd",
                         TV2DOUBLE(info.pr_utime),
                         TV2DOUBLE(info.pr_stime),
                         TV2DOUBLE(info.pr_cutime),
                         TV2DOUBLE(info.pr_cstime));
}


psutil_proc_environ(PyObject *self, PyObject *args) {
    int pid;
    PyObject *py_retdict = PyDict_New();
    PyObject *py_key = NULL;
    PyObject *py_val = NULL;
    struct procsinfo procbuf;
    long env_max;
    char *envbuf = NULL;
    char *curvar = NULL;
    char *separator = NULL;
    int ret;

    if (py_retdict == NULL)
        return NULL;
    if (!PyArg_ParseTuple(args, "i", &pid))
        goto error;
    env_max = sysconf(_SC_ARG_MAX);
    envbuf = malloc(env_max);
    if (envbuf == NULL) {
        PyErr_NoMemory();
        goto error;
    }

    procbuf.pi_pid = pid;
    ret = getevars(&procbuf, sizeof(procbuf), envbuf, ARG_MAX);
    if (ret == -1) {
        PyErr_SetFromErrno(PyExc_OSError);
        goto error;
    }

    curvar = envbuf;
    /* getevars will always append an extra NULL to end the arg list,
     * even if the buffer is not big enough (even though it is supposed
     * to be) so the following 'while' is safe */
    while (*curvar != '\0') {
        separator = strchr(curvar, '=');
        if (separator != NULL) {
            py_key = PyUnicode_DecodeFSDefaultAndSize(
                curvar,
                (Py_ssize_t)(separator - curvar)
            );
            if (!py_key)
                goto error;
            py_val = PyUnicode_DecodeFSDefault(separator + 1);
            if (!py_val)
                goto error;
            if (PyDict_SetItem(py_retdict, py_key, py_val))
                goto error;
            Py_CLEAR(py_key);
            Py_CLEAR(py_val);
        }
        curvar = strchr(curvar, '\0') + 1;
    }

    free(envbuf);

    return py_retdict;

error:
    if (envbuf != NULL)
        free(envbuf);
    Py_XDECREF(py_retdict);
    Py_XDECREF(py_key);
    Py_XDECREF(py_val);
    return NULL;
}


psutil_boot_time(PyObject *self, PyObject *args) {
    ULONGLONG uptime;
    time_t pt;
    FILETIME fileTime;
    ULONGLONG ll;

    GetSystemTimeAsFileTime(&fileTime);
    /*
    HUGE thanks to:
    http://johnstewien.spaces.live.com/blog/cns!E6885DB5CEBABBC8!831.entry

    This function converts the FILETIME structure to the 32 bit
    Unix time structure.
    The time_t is a 32-bit value for the number of seconds since
    January 1, 1970. A FILETIME is a 64-bit for the number of
    100-nanosecond periods since January 1, 1601. Convert by
    subtracting the number of 100-nanosecond period between 01-01-1970
    and 01-01-1601, from time_t the divide by 1e+7 to get to the same
    base granularity.
    */
    ll = (((ULONGLONG)
        (fileTime.dwHighDateTime)) << 32) + fileTime.dwLowDateTime;
    pt = (time_t)((ll - 116444736000000000ull) / 10000000ull);

    if (psutil_GetTickCount64 != NULL) {
        // Windows >= Vista
        uptime = psutil_GetTickCount64() / 1000ull;
    }
    else {
        // Windows XP.
        // GetTickCount() time will wrap around to zero if the
        // system is run continuously for 49.7 days.
        uptime = (ULONGLONG)GetTickCount() / 1000ull;
    }
    return Py_BuildValue("K", pt - uptime);
}


psutil_proc_basic_info(PyObject *self, PyObject *args) {
    int pid;
    char path[100];
    psinfo_t info;
    pstatus_t status;
    const char *procfs_path;

    if (! PyArg_ParseTuple(args, "is", &pid, &procfs_path))
        return NULL;

    sprintf(path, "%s/%i/psinfo", procfs_path, pid);
    if (! psutil_file_to_struct(path, (void *)&info, sizeof(info)))
        return NULL;

    if (info.pr_nlwp == 0 && info.pr_lwp.pr_lwpid == 0) {
        // From the /proc docs: "If the process is a zombie, the pr_nlwp
        // and pr_lwp.pr_lwpid flags are zero."
        status.pr_stat = SZOMB;
    } else if (info.pr_flag & SEXIT) {
        // "exiting" processes don't have /proc/<pid>/status
        // There are other "exiting" processes that 'ps' shows as "active"
        status.pr_stat = SACTIVE;
    } else {
        sprintf(path, "%s/%i/status", procfs_path, pid);
        if (! psutil_file_to_struct(path, (void *)&status, sizeof(status)))
            return NULL;
    }

    return Py_BuildValue("KKKdiiiK",
        (unsigned long long) info.pr_ppid,      // parent pid
        (unsigned long long) info.pr_rssize,    // rss
        (unsigned long long) info.pr_size,      // vms
        TV2DOUBLE(info.pr_start),               // create time
        (int) info.pr_lwp.pr_nice,              // nice
        (int) info.pr_nlwp,                     // no. of threads
        (int) status.pr_stat,                   // status code
        (unsigned long long)info.pr_ttydev      // tty nr
        );
}


psutil_disk_partitions(PyObject *self, PyObject *args) {
    FILE *file = NULL;
    struct mntent * mt = NULL;
    PyObject *py_dev = NULL;
    PyObject *py_mountp = NULL;
    PyObject *py_tuple = NULL;
    PyObject *py_retlist = PyList_New(0);

    if (py_retlist == NULL)
        return NULL;

    file = setmntent(MNTTAB, "rb");
    if (file == NULL) {
        PyErr_SetFromErrno(PyExc_OSError);
        goto error;
    }
    mt = getmntent(file);
    while (mt != NULL) {
        py_dev = PyUnicode_DecodeFSDefault(mt->mnt_fsname);
        if (! py_dev)
            goto error;
        py_mountp = PyUnicode_DecodeFSDefault(mt->mnt_dir);
        if (! py_mountp)
            goto error;
        py_tuple = Py_BuildValue(
            "(OOss)",
            py_dev,         // device
            py_mountp,      // mount point
            mt->mnt_type,   // fs type
            mt->mnt_opts);  // options
        if (py_tuple == NULL)
            goto error;
        if (PyList_Append(py_retlist, py_tuple))
            goto error;
        Py_CLEAR(py_dev);
        Py_CLEAR(py_mountp);
        Py_CLEAR(py_tuple);
        mt = getmntent(file);
    }
    endmntent(file);
    return py_retlist;

error:
    Py_XDECREF(py_dev);
    Py_XDECREF(py_mountp);
    Py_XDECREF(py_tuple);
    Py_DECREF(py_retlist);
    if (file != NULL)
        endmntent(file);
    return NULL;
}


psutil_cpu_count_logical(PyObject *self, PyObject *args) {
    unsigned int ncpus;

    ncpus = psutil_get_num_cpus(0);
    if (ncpus != 0)
        return Py_BuildValue("I", ncpus);
    else
        Py_RETURN_NONE;  // mimick os.cpu_count()
}


psutil_proc_cwd(PyObject *self, PyObject *args) {
    long pid;
    struct proc_vnodepathinfo pathinfo;

    if (! PyArg_ParseTuple(args, "l", &pid))
        return NULL;

    if (psutil_proc_pidinfo(
            pid, PROC_PIDVNODEPATHINFO, 0, &pathinfo, sizeof(pathinfo)) <= 0)
    {
        return NULL;
    }

    return PyUnicode_DecodeFSDefault(pathinfo.pvi_cdir.vip_path);
}


psutil_disk_partitions(PyObject *self, PyObject *args) {
    FILE *file = NULL;
    struct mntent *entry;
    const char *mtab_path;
    PyObject *py_dev = NULL;
    PyObject *py_mountp = NULL;
    PyObject *py_tuple = NULL;
    PyObject *py_retlist = PyList_New(0);

    if (py_retlist == NULL)
        return NULL;

    if (!PyArg_ParseTuple(args, "s", &mtab_path))
        return NULL;

    Py_BEGIN_ALLOW_THREADS
    file = setmntent(mtab_path, "r");
    Py_END_ALLOW_THREADS
    if ((file == 0) || (file == NULL)) {
        psutil_debug("setmntent() failed");
        PyErr_SetFromErrnoWithFilename(PyExc_OSError, mtab_path);
        goto error;
    }

    while ((entry = getmntent(file))) {
        if (entry == NULL) {
            PyErr_Format(PyExc_RuntimeError, "getmntent() syscall failed");
            goto error;
        }
        py_dev = PyUnicode_DecodeFSDefault(entry->mnt_fsname);
        if (! py_dev)
            goto error;
        py_mountp = PyUnicode_DecodeFSDefault(entry->mnt_dir);
        if (! py_mountp)
            goto error;
        py_tuple = Py_BuildValue("(OOss)",
                                 py_dev,             // device
                                 py_mountp,          // mount point
                                 entry->mnt_type,    // fs type
                                 entry->mnt_opts);   // options
        if (! py_tuple)
            goto error;
        if (PyList_Append(py_retlist, py_tuple))
            goto error;
        Py_CLEAR(py_dev);
        Py_CLEAR(py_mountp);
        Py_CLEAR(py_tuple);
    }
    endmntent(file);
    return py_retlist;

error:
    if (file != NULL)
        endmntent(file);
    Py_XDECREF(py_dev);
    Py_XDECREF(py_mountp);
    Py_XDECREF(py_tuple);
    Py_DECREF(py_retlist);
    return NULL;
}


psutil_cpu_stats(PyObject *self, PyObject *args) {
    struct vmmeter vmstat;
    kern_return_t ret;
    mach_msg_type_number_t count = sizeof(vmstat) / sizeof(integer_t);
    mach_port_t mport = mach_host_self();

    ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)&vmstat, &count);
    if (ret != KERN_SUCCESS) {
        PyErr_Format(
            PyExc_RuntimeError,
            "host_statistics(HOST_VM_INFO) failed: %s",
            mach_error_string(ret));
        return NULL;
    }
    mach_port_deallocate(mach_task_self(), mport);

    return Py_BuildValue(
        "IIIII",
        vmstat.v_swtch,  // ctx switches
        vmstat.v_intr,  // interrupts
        vmstat.v_soft,  // software interrupts
        vmstat.v_syscall,  // syscalls
        vmstat.v_trap  // traps
    );
}


psutil_swap_mem(PyObject *self, PyObject *args) {
    int rc;
    int pagesize = getpagesize();
    perfstat_memory_total_t memory;

    rc = perfstat_memory_total(
        NULL, &memory, sizeof(perfstat_memory_total_t), 1);
    if (rc <= 0){
        PyErr_SetFromErrno(PyExc_OSError);
        return NULL;
    }

    return Py_BuildValue("KKKK",
        (unsigned long long) memory.pgsp_total * pagesize,
        (unsigned long long) memory.pgsp_free * pagesize,
        (unsigned long long) memory.pgins * pagesize,
        (unsigned long long) memory.pgouts * pagesize
    );
}


psutil_net_if_stats(PyObject* self, PyObject* args) {
    char *nic_name;
    int sock = 0;
    int ret;
    int mtu;
    struct ifreq ifr;
    PyObject *py_is_up = NULL;
    PyObject *py_retlist = NULL;

    if (! PyArg_ParseTuple(args, "s", &nic_name))
        return NULL;

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1)
        goto error;

    strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));

    // is up?
    ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
    if (ret == -1)
        goto error;
    if ((ifr.ifr_flags & IFF_UP) != 0)
        py_is_up = Py_True;
    else
        py_is_up = Py_False;
    Py_INCREF(py_is_up);

    // MTU
    ret = ioctl(sock, SIOCGIFMTU, &ifr);
    if (ret == -1)
        goto error;
    mtu = ifr.ifr_mtu;

    close(sock);
    py_retlist = Py_BuildValue("[Oi]", py_is_up, mtu);
    if (!py_retlist)
        goto error;
    Py_DECREF(py_is_up);
    return py_retlist;

error:
    Py_XDECREF(py_is_up);
    if (sock != 0)
        close(sock);
    PyErr_SetFromErrno(PyExc_OSError);
    return NULL;
}


psutil_sensors_battery(PyObject *self, PyObject *args) {
    SYSTEM_POWER_STATUS sps;

    if (GetSystemPowerStatus(&sps) == 0)
        return PyErr_SetFromWindowsErr(0);
    return Py_BuildValue(
        "iiiI",
        sps.ACLineStatus,  // whether AC is connected: 0=no, 1=yes, 255=unknown
        // status flag:
        // 1, 2, 4 = high, low, critical
        // 8 = charging
        // 128 = no battery
        sps.BatteryFlag,
        sps.BatteryLifePercent,  // percent
        sps.BatteryLifeTime  // remaining secs
    );
}


psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) {
    cpu_set_t cpu_set;
    size_t len;
    long pid;
    int i, seq_len;
    PyObject *py_cpu_set;
    PyObject *py_cpu_seq = NULL;

    if (!PyArg_ParseTuple(args, "lO", &pid, &py_cpu_set))
        return NULL;

    if (!PySequence_Check(py_cpu_set)) {
        PyErr_Format(PyExc_TypeError, "sequence argument expected, got %s",
                     Py_TYPE(py_cpu_set)->tp_name);
        goto error;
    }

    py_cpu_seq = PySequence_Fast(py_cpu_set, "expected a sequence or integer");
    if (!py_cpu_seq)
        goto error;
    seq_len = PySequence_Fast_GET_SIZE(py_cpu_seq);
    CPU_ZERO(&cpu_set);
    for (i = 0; i < seq_len; i++) {
        PyObject *item = PySequence_Fast_GET_ITEM(py_cpu_seq, i);
#if PY_MAJOR_VERSION >= 3
        long value = PyLong_AsLong(item);
#else
        long value = PyInt_AsLong(item);
#endif
        if ((value == -1) || PyErr_Occurred()) {
            if (!PyErr_Occurred())
                PyErr_SetString(PyExc_ValueError, "invalid CPU value");
            goto error;
        }
        CPU_SET(value, &cpu_set);
    }

    len = sizeof(cpu_set);
    if (sched_setaffinity(pid, len, &cpu_set)) {
        PyErr_SetFromErrno(PyExc_OSError);
        goto error;
    }

    Py_DECREF(py_cpu_seq);
    Py_RETURN_NONE;

error:
    if (py_cpu_seq != NULL)
        Py_DECREF(py_cpu_seq);
    return NULL;
}


psutil_net_if_flags(PyObject *self, PyObject *args) {
    char *nic_name;
    int sock = -1;
    int ret;
    struct ifreq ifr;

    if (! PyArg_ParseTuple(args, "s", &nic_name))
        return NULL;

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1)
        goto error;

    strncpy(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name));
    ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
    if (ret == -1)
        goto error;

    close(sock);
    if ((ifr.ifr_flags & IFF_UP) != 0)
        return Py_BuildValue("O", Py_True);
    else
        return Py_BuildValue("O", Py_False);

error:
    if (sock != -1)
        close(sock);
    return PyErr_SetFromErrno(PyExc_OSError);
}


cnt_purge(struct worker *wrk, struct req *req)
{
	struct objcore *oc, *boc;
	enum lookup_e lr;

	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
	AZ(req->objcore);
	AZ(req->stale_oc);

	AN(req->vcl);

	VRY_Prep(req);

	AZ(req->objcore);
	req->hash_always_miss = 1;
	lr = HSH_Lookup(req, &oc, &boc);
	assert (lr == HSH_MISS);
	AZ(oc);
	CHECK_OBJ_NOTNULL(boc, OBJCORE_MAGIC);
	VRY_Finish(req, DISCARD);

	(void)HSH_Purge(wrk, boc->objhead, req->t_req, 0, 0, 0);

	AZ(HSH_DerefObjCore(wrk, &boc, 1));

	VCL_purge_method(req->vcl, wrk, req, NULL, NULL);
	switch (wrk->handling) {
	case VCL_RET_RESTART:
		req->req_step = R_STP_RESTART;
		break;
	case VCL_RET_FAIL:
		req->req_step = R_STP_VCLFAIL;
		break;
	case VCL_RET_SYNTH:
		req->req_step = R_STP_SYNTH;
		break;
	default:
		WRONG("Illegal return from vcl_purge{}");
	}
	return (REQ_FSM_MORE);
}


static Image *ReadXWDImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
#define CheckOverflowException(length,width,height) \
  (((height) != 0) && ((length)/((size_t) height) != ((size_t) width)))

  char
    *comment;

  Image
    *image;

  int
    x_status;

  MagickBooleanType
    authentic_colormap;

  MagickStatusType
    status;

  Quantum
    index;

  register ssize_t
    x;

  register Quantum
    *q;

  register ssize_t
    i;

  register size_t
    pixel;

  size_t
    length;

  ssize_t
    count,
    y;

  unsigned long
    lsb_first;

  XColor
    *colors;

  XImage
    *ximage;

  XWDFileHeader
    header;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  /*
     Read in header information.
  */
  count=ReadBlob(image,sz_XWDheader,(unsigned char *) &header);
  if (count != sz_XWDheader)
    ThrowReaderException(CorruptImageError,"UnableToReadImageHeader");
  /*
    Ensure the header byte-order is most-significant byte first.
  */
  lsb_first=1;
  if ((int) (*(char *) &lsb_first) != 0)
    MSBOrderLong((unsigned char *) &header,sz_XWDheader);
  /*
    Check to see if the dump file is in the proper format.
  */
  if (header.file_version != XWD_FILE_VERSION)
    ThrowReaderException(CorruptImageError,"FileFormatVersionMismatch");
  if (header.header_size < sz_XWDheader)
    ThrowReaderException(CorruptImageError,"CorruptImage");
  switch (header.visual_class) {
    case StaticGray:
    case GrayScale:
    case StaticColor:
    case PseudoColor:
    case TrueColor:
    case DirectColor:
      break;
    default:
      ThrowReaderException(CorruptImageError,"CorruptImage");
    }
  switch (header.pixmap_format) {
    case XYBitmap:
    case XYPixmap:
    case ZPixmap:
      break;
    default:
      ThrowReaderException(CorruptImageError,"CorruptImage");
  }
  length=(size_t) header.header_size-sz_XWDheader;
  comment=(char *) AcquireQuantumMemory(length+1,sizeof(*comment));
  if (comment == (char *) NULL)
    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
  count=ReadBlob(image,length,(unsigned char *) comment);
  comment[length]='\0';
  (void) SetImageProperty(image,"comment",comment,exception);
  comment=DestroyString(comment);
  if (count != (ssize_t) length)
    ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
  /*
    Initialize the X image.
  */
  ximage=(XImage *) AcquireMagickMemory(sizeof(*ximage));
  if (ximage == (XImage *) NULL)
    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
  ximage->depth=(int) header.pixmap_depth;
  ximage->format=(int) header.pixmap_format;
  ximage->xoffset=(int) header.xoffset;
  ximage->data=(char *) NULL;
  ximage->width=(int) header.pixmap_width;
  ximage->height=(int) header.pixmap_height;
  ximage->bitmap_pad=(int) header.bitmap_pad;
  ximage->bytes_per_line=(int) header.bytes_per_line;
  ximage->byte_order=(int) header.byte_order;
  ximage->bitmap_unit=(int) header.bitmap_unit;
  ximage->bitmap_bit_order=(int) header.bitmap_bit_order;
  ximage->bits_per_pixel=(int) header.bits_per_pixel;
  ximage->red_mask=header.red_mask;
  ximage->green_mask=header.green_mask;
  ximage->blue_mask=header.blue_mask;
  if ((ximage->width < 0) || (ximage->height < 0) || (ximage->depth < 0) ||
      (ximage->format < 0) || (ximage->byte_order < 0) ||
      (ximage->bitmap_bit_order < 0) || (ximage->bitmap_pad < 0) ||
      (ximage->bytes_per_line < 0))
    {
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    }
  if ((ximage->width > 65535) || (ximage->height > 65535))
    {
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    }
  if ((ximage->bits_per_pixel > 32) || (ximage->bitmap_unit > 32))
    {
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    }
  x_status=XInitImage(ximage);
  if (x_status == 0)
    {
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
    }
  /*
    Read colormap.
  */
  authentic_colormap=MagickFalse;
  colors=(XColor *) NULL;
  if (header.ncolors != 0)
    {
      XWDColor
        color;

      length=(size_t) header.ncolors;
      if (length > ((~0UL)/sizeof(*colors)))
        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
      colors=(XColor *) AcquireQuantumMemory(length,sizeof(*colors));
      if (colors == (XColor *) NULL)
        {
          ximage=(XImage *) RelinquishMagickMemory(ximage);
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        }
      for (i=0; i < (ssize_t) header.ncolors; i++)
      {
        count=ReadBlob(image,sz_XWDColor,(unsigned char *) &color);
        if (count != sz_XWDColor)
          {
            colors=(XColor *) RelinquishMagickMemory(colors);
            ximage=(XImage *) RelinquishMagickMemory(ximage);
            ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
          }
        colors[i].pixel=color.pixel;
        colors[i].red=color.red;
        colors[i].green=color.green;
        colors[i].blue=color.blue;
        colors[i].flags=(char) color.flags;
        if (color.flags != 0)
          authentic_colormap=MagickTrue;
      }
      /*
        Ensure the header byte-order is most-significant byte first.
      */
      lsb_first=1;
      if ((int) (*(char *) &lsb_first) != 0)
        for (i=0; i < (ssize_t) header.ncolors; i++)
        {
          MSBOrderLong((unsigned char *) &colors[i].pixel,
            sizeof(colors[i].pixel));
          MSBOrderShort((unsigned char *) &colors[i].red,3*
            sizeof(colors[i].red));
        }
    }
  /*
    Allocate the pixel buffer.
  */
  length=(size_t) ximage->bytes_per_line*ximage->height;
  if (CheckOverflowException(length,ximage->bytes_per_line,ximage->height))
    {
      if (header.ncolors != 0)
        colors=(XColor *) RelinquishMagickMemory(colors);
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    }
  if (ximage->format != ZPixmap)
    {
      size_t
        extent;

      extent=length;
      length*=ximage->depth;
      if (CheckOverflowException(length,extent,ximage->depth))
        {
          if (header.ncolors != 0)
            colors=(XColor *) RelinquishMagickMemory(colors);
          ximage=(XImage *) RelinquishMagickMemory(ximage);
          ThrowReaderException(CorruptImageError,"ImproperImageHeader");
        }
    }
  ximage->data=(char *) AcquireQuantumMemory(length,sizeof(*ximage->data));
  if (ximage->data == (char *) NULL)
    {
      if (header.ncolors != 0)
        colors=(XColor *) RelinquishMagickMemory(colors);
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    }
  count=ReadBlob(image,length,(unsigned char *) ximage->data);
  if (count != (ssize_t) length)
    {
      if (header.ncolors != 0)
        colors=(XColor *) RelinquishMagickMemory(colors);
      ximage->data=DestroyString(ximage->data);
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(CorruptImageError,"UnableToReadImageData");
    }
  /*
    Convert image to MIFF format.
  */
  image->columns=(size_t) ximage->width;
  image->rows=(size_t) ximage->height;
  image->depth=8;
  status=SetImageExtent(image,image->columns,image->rows,exception);
  if (status == MagickFalse)
    {
      if (header.ncolors != 0)
        colors=(XColor *) RelinquishMagickMemory(colors);
      ximage->data=DestroyString(ximage->data);
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      return(DestroyImageList(image));
    }
  if ((header.ncolors == 0U) || (ximage->red_mask != 0) ||
      (ximage->green_mask != 0) || (ximage->blue_mask != 0))
    image->storage_class=DirectClass;
  else
    image->storage_class=PseudoClass;
  image->colors=header.ncolors;
  if (image_info->ping == MagickFalse)
    switch (image->storage_class)
    {
      case DirectClass:
      default:
      {
        register size_t
          color;

        size_t
          blue_mask,
          blue_shift,
          green_mask,
          green_shift,
          red_mask,
          red_shift;

        /*
          Determine shift and mask for red, green, and blue.
        */
        red_mask=ximage->red_mask;
        red_shift=0;
        while ((red_mask != 0) && ((red_mask & 0x01) == 0))
        {
          red_mask>>=1;
          red_shift++;
        }
        green_mask=ximage->green_mask;
        green_shift=0;
        while ((green_mask != 0) && ((green_mask & 0x01) == 0))
        {
          green_mask>>=1;
          green_shift++;
        }
        blue_mask=ximage->blue_mask;
        blue_shift=0;
        while ((blue_mask != 0) && ((blue_mask & 0x01) == 0))
        {
          blue_mask>>=1;
          blue_shift++;
        }
        /*
          Convert X image to DirectClass packets.
        */
        if ((image->colors != 0) && (authentic_colormap != MagickFalse))
          for (y=0; y < (ssize_t) image->rows; y++)
          {
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
            if (q == (Quantum *) NULL)
              break;
            for (x=0; x < (ssize_t) image->columns; x++)
            {
              pixel=XGetPixel(ximage,(int) x,(int) y);
              index=(Quantum) ((pixel >> red_shift) & red_mask);
              if (index < header.ncolors)
                SetPixelRed(image,ScaleShortToQuantum(
                  colors[(ssize_t) index].red),q);
              index=(Quantum) ((pixel >> green_shift) & green_mask);
              if (index < header.ncolors)
                SetPixelGreen(image,ScaleShortToQuantum(
                  colors[(ssize_t) index].green),q);
              index=(Quantum) ((pixel >> blue_shift) & blue_mask);
              if (index < header.ncolors)
                SetPixelBlue(image,ScaleShortToQuantum(
                  colors[(ssize_t) index].blue),q);
              q+=GetPixelChannels(image);
            }
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
              image->rows);
            if (status == MagickFalse)
              break;
          }
        else
          for (y=0; y < (ssize_t) image->rows; y++)
          {
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
            if (q == (Quantum *) NULL)
              break;
            for (x=0; x < (ssize_t) image->columns; x++)
            {
              pixel=XGetPixel(ximage,(int) x,(int) y);
              color=(pixel >> red_shift) & red_mask;
              if (red_mask != 0)
                color=(color*65535UL)/red_mask;
              SetPixelRed(image,ScaleShortToQuantum((unsigned short) color),q);
              color=(pixel >> green_shift) & green_mask;
              if (green_mask != 0)
                color=(color*65535UL)/green_mask;
              SetPixelGreen(image,ScaleShortToQuantum((unsigned short) color),
                q);
              color=(pixel >> blue_shift) & blue_mask;
              if (blue_mask != 0)
                color=(color*65535UL)/blue_mask;
              SetPixelBlue(image,ScaleShortToQuantum((unsigned short) color),q);
              q+=GetPixelChannels(image);
            }
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
              image->rows);
            if (status == MagickFalse)
              break;
          }
        break;
      }
      case PseudoClass:
      {
        /*
          Convert X image to PseudoClass packets.
        */
        if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
          {
            if (header.ncolors != 0)
              colors=(XColor *) RelinquishMagickMemory(colors);
            ximage->data=DestroyString(ximage->data);
            ximage=(XImage *) RelinquishMagickMemory(ximage);
            ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
          }
        for (i=0; i < (ssize_t) image->colors; i++)
        {
          image->colormap[i].red=(MagickRealType) ScaleShortToQuantum(
            colors[i].red);
          image->colormap[i].green=(MagickRealType) ScaleShortToQuantum(
            colors[i].green);
          image->colormap[i].blue=(MagickRealType) ScaleShortToQuantum(
            colors[i].blue);
        }
        for (y=0; y < (ssize_t) image->rows; y++)
        {
          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
          if (q == (Quantum *) NULL)
            break;
          for (x=0; x < (ssize_t) image->columns; x++)
          {
            index=ConstrainColormapIndex(image,XGetPixel(ximage,(int) x,
              (int) y),exception);
            SetPixelIndex(image,index,q);
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
            q+=GetPixelChannels(image);
          }
          if (SyncAuthenticPixels(image,exception) == MagickFalse)
            break;
          status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
            image->rows);
          if (status == MagickFalse)
            break;
        }
        break;
      }
    }
  /*
    Free image and colormap.
  */
  if (header.ncolors != 0)
    colors=(XColor *) RelinquishMagickMemory(colors);
  ximage->data=DestroyString(ximage->data);
  ximage=(XImage *) RelinquishMagickMemory(ximage);
  if (EOFBlob(image) != MagickFalse)
    ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
      image->filename);
  (void) CloseBlob(image);
  return(GetFirstImageInList(image));
}


static void lsi_wait_reselect(LSIState *s)
{
    lsi_request *p;

    trace_lsi_wait_reselect();

    if (s->current) {
        return;
    }
    p = get_pending_req(s);
    if (p) {
        lsi_reselect(s, p);
    }
    if (s->current == NULL) {
        s->waiting = LSI_WAIT_RESELECT;
    }
}


static void lsi_queue_command(LSIState *s)
{
    lsi_request *p = s->current;

    trace_lsi_queue_command(p->tag);
    assert(s->current != NULL);
    assert(s->current->dma_len == 0);
    QTAILQ_INSERT_TAIL(&s->queue, s->current, next);
    s->current = NULL;

    p->pending = 0;
    p->out = (s->sstat1 & PHASE_MASK) == PHASE_DO;
}


static void lsi_scsi_realize(PCIDevice *dev, Error **errp)
{
    LSIState *s = LSI53C895A(dev);
    DeviceState *d = DEVICE(dev);
    uint8_t *pci_conf;

    pci_conf = dev->config;

    /* PCI latency timer = 255 */
    pci_conf[PCI_LATENCY_TIMER] = 0xff;
    /* Interrupt pin A */
    pci_conf[PCI_INTERRUPT_PIN] = 0x01;

    memory_region_init_io(&s->mmio_io, OBJECT(s), &lsi_mmio_ops, s,
                          "lsi-mmio", 0x400);
    memory_region_init_io(&s->ram_io, OBJECT(s), &lsi_ram_ops, s,
                          "lsi-ram", 0x2000);
    memory_region_init_io(&s->io_io, OBJECT(s), &lsi_io_ops, s,
                          "lsi-io", 256);

    address_space_init(&s->pci_io_as, pci_address_space_io(dev), "lsi-pci-io");
    qdev_init_gpio_out(d, &s->ext_irq, 1);

    pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_io);
    pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio_io);
    pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io);
    QTAILQ_INIT(&s->queue);

    scsi_bus_new(&s->bus, sizeof(s->bus), d, &lsi_scsi_info, NULL);
}


static void lsi_update_irq(LSIState *s)
{
    int level;
    static int last_level;

    /* It's unclear whether the DIP/SIP bits should be cleared when the
       Interrupt Status Registers are cleared or when istat0 is read.
       We currently do the formwer, which seems to work.  */
    level = 0;
    if (s->dstat) {
        if (s->dstat & s->dien)
            level = 1;
        s->istat0 |= LSI_ISTAT0_DIP;
    } else {
        s->istat0 &= ~LSI_ISTAT0_DIP;
    }

    if (s->sist0 || s->sist1) {
        if ((s->sist0 & s->sien0) || (s->sist1 & s->sien1))
            level = 1;
        s->istat0 |= LSI_ISTAT0_SIP;
    } else {
        s->istat0 &= ~LSI_ISTAT0_SIP;
    }
    if (s->istat0 & LSI_ISTAT0_INTF)
        level = 1;

    if (level != last_level) {
        trace_lsi_update_irq(level, s->dstat, s->sist1, s->sist0);
        last_level = level;
    }
    lsi_set_irq(s, level);

    if (!s->current && !level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) {
        lsi_request *p;

        trace_lsi_update_irq_disconnected();
        p = get_pending_req(s);
        if (p) {
            lsi_reselect(s, p);
        }
    }
}


static uint64_t lsi_mmio_read(void *opaque, hwaddr addr,
                              unsigned size)
{
    LSIState *s = opaque;
    return lsi_reg_readb(s, addr & 0xff);
}


static void lsi_script_scsi_interrupt(LSIState *s, int stat0, int stat1)
{
    uint32_t mask0;
    uint32_t mask1;

    trace_lsi_script_scsi_interrupt(stat1, stat0, s->sist1, s->sist0);
    s->sist0 |= stat0;
    s->sist1 |= stat1;
    /* Stop processor on fatal or unmasked interrupt.  As a special hack
       we don't stop processing when raising STO.  Instead continue
       execution and stop at the next insn that accesses the SCSI bus.  */
    mask0 = s->sien0 | ~(LSI_SIST0_CMP | LSI_SIST0_SEL | LSI_SIST0_RSL);
    mask1 = s->sien1 | ~(LSI_SIST1_GEN | LSI_SIST1_HTH);
    mask1 &= ~LSI_SIST1_STO;
    if (s->sist0 & mask0 || s->sist1 & mask1) {
        lsi_stop_script(s);
    }
    lsi_update_irq(s);
}


static int lsi_pre_save(void *opaque)
{
    LSIState *s = opaque;

    if (s->current) {
        assert(s->current->dma_buf == NULL);
        assert(s->current->dma_len == 0);
    }
    assert(QTAILQ_EMPTY(&s->queue));

    return 0;
}


static int parse_ranges(const char *hdr, unsigned long len,
                        struct range **ranges)
{
    int ret = HTTP_BAD_RANGE;
    struct range *new, *tail = *ranges = NULL;
    tok_t tok;
    char *token;

    if (!len) return HTTP_OK;  /* need to know length of representation */

    /* we only handle byte-unit */
    if (!hdr || strncmp(hdr, "bytes=", 6)) return HTTP_OK;

    tok_init(&tok, hdr+6, ",", TOK_TRIMLEFT|TOK_TRIMRIGHT);
    while ((token = tok_next(&tok))) {
        /* default to entire representation */
        unsigned long first = 0;
        unsigned long last = len - 1;
        char *p, *endp;

        if (!(p = strchr(token, '-'))) continue;  /* bad byte-range-set */

        if (p == token) {
            /* suffix-byte-range-spec */
            unsigned long suffix = strtoul(++p, &endp, 10);

            if (endp == p || *endp) continue;  /* bad suffix-length */
            if (!suffix) continue;      /* unsatisfiable suffix-length */

            /* don't start before byte zero */
            if (suffix < len) first = len - suffix;
        }
        else {
            /* byte-range-spec */
            first = strtoul(token, &endp, 10);
            if (endp != p) continue;      /* bad first-byte-pos */
            if (first >= len) continue;   /* unsatisfiable first-byte-pos */

            if (*++p) {
                /* last-byte-pos */
                last = strtoul(p, &endp, 10);
                if (*endp || last < first) continue; /* bad last-byte-pos */

                /* don't go past end of representation */
                if (last >= len) last = len - 1;
            }
        }

        ret = HTTP_PARTIAL;

        /* Coalesce overlapping ranges, or those with a gap < 80 bytes */
        if (tail &&
            first >= tail->first && (long) (first - tail->last) < 80) {
            tail->last = MAX(last, tail->last);
            continue;
        }

        /* Create a new range and append it to linked list */
        new = xzmalloc(sizeof(struct range));
        new->first = first;
        new->last = last;

        if (tail) tail->next = new;
        else *ranges = new;
        tail = new;
    }

    tok_fini(&tok);

    return ret;
}


static void multipart_byteranges(struct transaction_t *txn,
                                 const char *msg_base)
{
    /* Save Content-Range and Content-Type pointers */
    struct range *range = txn->resp_body.range;
    const char *type = txn->resp_body.type;

    /* Start multipart response */
    txn->resp_body.range = NULL;
    txn->resp_body.type = "multipart/byteranges";
    write_multipart_body(HTTP_PARTIAL, txn, NULL, 0, NULL);

    txn->resp_body.type = type;
    while (range) {
        unsigned long offset = range->first;
        unsigned long datalen = range->last - range->first + 1;
        struct range *next = range->next;

        /* Output range as body part */
        txn->resp_body.range = range;
        write_multipart_body(0, txn, msg_base + offset, datalen, NULL);

        /* Cleanup */
        free(range);
        range = next;
    }

    /* End of multipart body */
    write_multipart_body(0, txn, NULL, 0, NULL);
}


EXPORTED void content_md5_hdr(struct transaction_t *txn,
                              const unsigned char *md5)
{
    char base64[MD5_BASE64_LEN+1];

    sasl_encode64((char *) md5, MD5_DIGEST_LENGTH, base64, MD5_BASE64_LEN, NULL);
    simple_hdr(txn, "Content-MD5", base64);
}


static int starttls(struct transaction_t *txn, struct http_connection *conn)
{
    int https = (txn == NULL);
    int result;
    SSL_CTX *ctx = NULL;

    if (!conn) conn = txn->conn;

    result=tls_init_serverengine("http",
                                 5,        /* depth to verify */
                                 !https,   /* can client auth? */
                                 &ctx);

    if (result == -1) {
        syslog(LOG_ERR, "error initializing TLS");

        if (txn) txn->error.desc = "Error initializing TLS";
        return HTTP_SERVER_ERROR;
    }

    if (http2_enabled()) {
#ifdef HAVE_TLS_ALPN
        /* enable TLS ALPN extension */
        SSL_CTX_set_alpn_select_cb(ctx, alpn_select_cb, conn);
#endif
    }

    if (!https) {
        /* tell client to start TLS upgrade (RFC 2817) */
        response_header(HTTP_SWITCH_PROT, txn);
    }

    result=tls_start_servertls(0, /* read */
                               1, /* write */
                               https ? 180 : httpd_timeout,
                               &saslprops,
                               (SSL **) &conn->tls_ctx);

    /* if error */
    if (result == -1) {
        syslog(LOG_NOTICE, "starttls failed: %s", conn->clienthost);

        if (txn) txn->error.desc = "Error negotiating TLS";
        return HTTP_BAD_REQUEST;
    }

    /* tell SASL about the negotiated layer */
    result = saslprops_set_tls(&saslprops, httpd_saslconn);
    if (result != SASL_OK) {
        syslog(LOG_NOTICE, "saslprops_set_tls() failed: cmd_starttls()");
        if (https == 0) {
            fatal("saslprops_set_tls() failed: cmd_starttls()", EX_TEMPFAIL);
        } else {
            shut_down(0);
        }
    }

    /* tell the prot layer about our new layers */
    prot_settls(httpd_in, conn->tls_ctx);
    prot_settls(httpd_out, conn->tls_ctx);

    httpd_tls_required = 0;

    avail_auth_schemes |= AUTH_BASIC;

    return 0;
}


int service_init(int argc __attribute__((unused)),
                 char **argv __attribute__((unused)),
                 char **envp __attribute__((unused)))
{
    int r, events, opt, i;
    int allow_trace = config_getswitch(IMAPOPT_HTTPALLOWTRACE);
    unsigned long version;
    unsigned int status, patch, fix, minor, major;

    LIBXML_TEST_VERSION

    if (geteuid() == 0) fatal("must run as the Cyrus user", EX_USAGE);
    setproctitle_init(argc, argv, envp);

    /* set signal handlers */
    signals_set_shutdown(&shut_down);
    signal(SIGPIPE, SIG_IGN);

    /* load the SASL plugins */
    global_sasl_init(1, 1, mysasl_cb);

    /* setup for sending IMAP IDLE notifications */
    idle_enabled();

    /* Set namespace */
    if ((r = mboxname_init_namespace(&httpd_namespace, 1)) != 0) {
        fatal(error_message(r), EX_CONFIG);
    }

    /* open the mboxevent system */
    events = mboxevent_init();
    apns_enabled = (events & EVENT_APPLEPUSHSERVICE_DAV);

    mboxevent_setnamespace(&httpd_namespace);

    while ((opt = getopt(argc, argv, "sp:q")) != EOF) {
        switch(opt) {
        case 's': /* https (do TLS right away) */
            https = 1;
            if (!tls_enabled()) {
                fatal("https: required OpenSSL options not present",
                      EX_CONFIG);
            }
            break;

        case 'q':
            ignorequota = 1;
            break;

        case 'p': /* external protection */
            extprops_ssf = atoi(optarg);
            break;

        default:
            usage();
        }
    }

    /* Create a protgroup for input from the client and selected backend */
    protin = protgroup_new(2);

    config_httpprettytelemetry = config_getswitch(IMAPOPT_HTTPPRETTYTELEMETRY);

    if (config_getstring(IMAPOPT_HTTPALLOWCORS)) {
        allow_cors =
            split_wildmats((char *) config_getstring(IMAPOPT_HTTPALLOWCORS),
                           NULL);
    }

    /* Construct serverinfo string */
    buf_printf(&serverinfo,
               "Cyrus-HTTP/%s Cyrus-SASL/%u.%u.%u Lib/XML%s Jansson/%s",
               CYRUS_VERSION,
               SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
               LIBXML_DOTTED_VERSION, JANSSON_VERSION);

    http2_init(&serverinfo);
    ws_init(&serverinfo);

#ifdef HAVE_SSL
    version = OPENSSL_VERSION_NUMBER;
    status  = version & 0x0f; version >>= 4;
    patch   = version & 0xff; version >>= 8;
    fix     = version & 0xff; version >>= 8;
    minor   = version & 0xff; version >>= 8;
    major   = version & 0xff;

    buf_printf(&serverinfo, " OpenSSL/%u.%u.%u", major, minor, fix);

    if (status == 0) buf_appendcstr(&serverinfo, "-dev");
    else if (status < 15) buf_printf(&serverinfo, "-beta%u", status);
    else if (patch) buf_putc(&serverinfo, patch + 'a' - 1);
#endif

#ifdef HAVE_ZLIB
    buf_printf(&serverinfo, " Zlib/%s", ZLIB_VERSION);
#endif
#ifdef HAVE_BROTLI
    version = BrotliEncoderVersion();
    fix     = version & 0xfff; version >>= 12;
    minor   = version & 0xfff; version >>= 12;
    major   = version & 0xfff;

    buf_printf(&serverinfo, " Brotli/%u.%u.%u", major, minor, fix);
#endif
#ifdef HAVE_ZSTD
    buf_printf(&serverinfo, " Zstd/%s", ZSTD_versionString());
#endif

    /* Initialize libical */
    ical_support_init();

    /* Do any namespace specific initialization */
    config_httpmodules = config_getbitfield(IMAPOPT_HTTPMODULES);
    for (i = 0; http_namespaces[i]; i++) {
        if (allow_trace) http_namespaces[i]->allow |= ALLOW_TRACE;
        if (http_namespaces[i]->init) http_namespaces[i]->init(&serverinfo);
    }

    compile_time = calc_compile_time(__TIME__, __DATE__);

    prometheus_increment(CYRUS_HTTP_READY_LISTENERS);

    return 0;
}


HIDDEN void *brotli_init()
{
    BrotliEncoderState *brotli = BrotliEncoderCreateInstance(NULL, NULL, NULL);

    if (brotli) {
        BrotliEncoderSetParameter(brotli, BROTLI_PARAM_MODE,
                                  BROTLI_DEFAULT_MODE);
        BrotliEncoderSetParameter(brotli, BROTLI_PARAM_QUALITY,
                                  BROTLI_DEFAULT_QUALITY);
        BrotliEncoderSetParameter(brotli, BROTLI_PARAM_LGWIN,
                                  BROTLI_DEFAULT_WINDOW);
        BrotliEncoderSetParameter(brotli, BROTLI_PARAM_LGBLOCK,
                                  BROTLI_MAX_INPUT_BLOCK_BITS);
    }

    return brotli;
}


EXPORTED char *httpdate_gen(char *buf, size_t len, time_t t)
{
    struct tm *tm = gmtime(&t);

    snprintf(buf, len, "%3s, %02d %3s %4d %02d:%02d:%02d GMT",
             wday[tm->tm_wday],
             tm->tm_mday, monthname[tm->tm_mon], tm->tm_year + 1900,
             tm->tm_hour, tm->tm_min, tm->tm_sec);

    return buf;
}


EXPORTED int etagcmp(const char *hdr, const char *etag)
{
    size_t len;

    if (!etag) return -1;               /* no representation       */
    if (!strcmp(hdr, "*")) return 0;    /* any representation      */

    len = strlen(etag);
    if (!strncmp(hdr, "W/", 2)) hdr+=2; /* skip weak prefix        */
    if (*hdr++ != '\"') return 1;       /* match/skip open DQUOTE  */
    if (strlen(hdr) != len+1) return 1; /* make sure lengths match */
    if (hdr[len] != '\"') return 1;     /* match close DQUOTE      */

    return strncmp(hdr, etag, len);
}


void shut_down(int code)
{
    int i;
    int bytes_in = 0;
    int bytes_out = 0;

    in_shutdown = 1;

    if (allow_cors) free_wildmats(allow_cors);

    /* Do any namespace specific cleanup */
    for (i = 0; http_namespaces[i]; i++) {
        if (http_namespaces[i]->enabled && http_namespaces[i]->shutdown)
            http_namespaces[i]->shutdown();
    }

    xmlCleanupParser();

    proc_cleanup();

    /* close backend connections */
    i = 0;
    while (backend_cached && backend_cached[i]) {
        proxy_downserver(backend_cached[i]);
        free(backend_cached[i]->context);
        free(backend_cached[i]);
        i++;
    }
    if (backend_cached) free(backend_cached);

    index_text_extractor_destroy();

    annotatemore_close();

    if (httpd_in) {
        prot_NONBLOCK(httpd_in);
        prot_fill(httpd_in);
        bytes_in = prot_bytes_in(httpd_in);
        prot_free(httpd_in);
    }

    if (httpd_out) {
        prot_flush(httpd_out);
        bytes_out = prot_bytes_out(httpd_out);
        prot_free(httpd_out);

        /* one less active connection */
        prometheus_decrement(CYRUS_HTTP_ACTIVE_CONNECTIONS);
    }
    else {
        /* one less ready listener */
        prometheus_decrement(CYRUS_HTTP_READY_LISTENERS);
    }

    prometheus_increment(code ? CYRUS_HTTP_SHUTDOWN_TOTAL_STATUS_ERROR
                              : CYRUS_HTTP_SHUTDOWN_TOTAL_STATUS_OK);

    if (protin) protgroup_free(protin);

    if (config_auditlog)
        syslog(LOG_NOTICE,
               "auditlog: traffic sessionid=<%s> bytes_in=<%d> bytes_out=<%d>",
               session_id(), bytes_in, bytes_out);

#ifdef HAVE_SSL
    tls_shutdown_serverengine();
#endif

    saslprops_free(&saslprops);

    http2_done();

    cyrus_done();

    exit(code);
}


static int proppatch_toresource(xmlNodePtr prop, unsigned set,
                         struct proppatch_ctx *pctx,
                         struct propstat propstat[],
                         void *rock __attribute__((unused)))
{
    xmlChar *freeme = NULL;
    annotate_state_t *astate = NULL;
    struct buf value = BUF_INITIALIZER;
    int r = 1; /* default to error */

    /* flags only store "exists" */

    if (!strcmp((const char *)prop->ns->href, XML_NS_SYSFLAG)) {
        struct flaggedresources *frp;
        int isset;
        for (frp = fres; frp->name; frp++) {
            if (strcasecmp((const char *)prop->name, frp->name)) continue;
            r = 0; /* ok to do nothing */
            isset = pctx->record->system_flags & frp->flag;
            if (set) {
                if (isset) goto done;
                pctx->record->system_flags |= frp->flag;
            }
            else {
                if (!isset) goto done;
                pctx->record->system_flags &= ~frp->flag;
            }
            r = mailbox_rewrite_index_record(pctx->mailbox, pctx->record);
            goto done;
        }
        goto done;
    }

    if (!strcmp((const char *)prop->ns->href, XML_NS_USERFLAG)) {
        int userflag = 0;
        int isset;
        r = mailbox_user_flag(pctx->mailbox, (const char *)prop->name, &userflag, 1);
        if (r) goto done;
        isset = pctx->record->user_flags[userflag/32] & (1<<userflag%31);
        if (set) {
            if (isset) goto done;
            pctx->record->user_flags[userflag/32] |= (1<<userflag%31);
        }
        else {
            if (!isset) goto done;
            pctx->record->user_flags[userflag/32] &= ~(1<<userflag%31);
        }
        r = mailbox_rewrite_index_record(pctx->mailbox, pctx->record);
        goto done;
    }

    /* otherwise it's a database annotation */

    buf_reset(&pctx->buf);
    buf_printf(&pctx->buf, DAV_ANNOT_NS "<%s>%s",
               (const char *) prop->ns->href, prop->name);

    if (set) {
        freeme = xmlNodeGetContent(prop);
        buf_init_ro_cstr(&value, (const char *)freeme);
    }

    r = mailbox_get_annotate_state(pctx->mailbox, pctx->record->uid, &astate);
    if (!r) r = annotate_state_writemask(astate, buf_cstring(&pctx->buf),
                                         httpd_userid, &value);
    /* we need to rewrite the record to update the modseq because the layering
     * of annotations and mailboxes is broken */
    if (!r) r = mailbox_rewrite_index_record(pctx->mailbox, pctx->record);

 done:

    if (!r) {
        xml_add_prop(HTTP_OK, pctx->ns[NS_DAV], &propstat[PROPSTAT_OK],
                     prop->name, prop->ns, NULL, 0);
    }
    else {
        xml_add_prop(HTTP_SERVER_ERROR, pctx->ns[NS_DAV],
                     &propstat[PROPSTAT_ERROR], prop->name, prop->ns, NULL, 0);
    }

    if (freeme) xmlFree(freeme);

    return 0;
}


HIDDEN int preload_proplist(xmlNodePtr proplist, struct propfind_ctx *fctx)
{
    int ret = 0;
    xmlNodePtr prop;
    const struct prop_entry *entry;
    struct propfind_entry_list *tail = NULL;

    switch (fctx->mode) {
    case PROPFIND_ALL:
    case PROPFIND_NAME:
        /* Add live properties for allprop/propname */
        for (entry = fctx->lprops; entry->name; entry++) {
            if (entry->flags & PROP_ALLPROP) {
                /* Pre-screen request based on prop flags */
                int allowed = prescreen_prop(entry, NULL, fctx);

                if (allowed || fctx->mode == PROPFIND_ALL) {
                    struct propfind_entry_list *nentry =
                        xzmalloc(sizeof(struct propfind_entry_list));

                    ensure_ns(fctx->ns, entry->ns, fctx->root,
                              known_namespaces[entry->ns].href,
                              known_namespaces[entry->ns].prefix);

                    nentry->name = xmlStrdup(BAD_CAST entry->name);
                    nentry->ns = fctx->ns[entry->ns];
                    if (allowed) {
                        nentry->flags = entry->flags;
                        nentry->get = entry->get;
                        nentry->prop = NULL;
                        nentry->rock = entry->rock;
                    }

                    /* Append new entry to linked list */
                    if (tail) {
                        tail->next = nentry;
                        tail = nentry;
                    }
                    else tail = fctx->elist = nentry;
                }
            }
        }
        /* Fall through and build hash table of namespaces */
        GCC_FALLTHROUGH

    case PROPFIND_EXPAND:
        /* Add all namespaces attached to the response to our hash table */
        if (!fctx->ns_table->size) {
            xmlNsPtr nsDef;

            construct_hash_table(fctx->ns_table, 10, 1);

            for (nsDef = fctx->root->nsDef; nsDef; nsDef = nsDef->next) {
                hash_insert((const char *) nsDef->href, nsDef, fctx->ns_table);
            }
        }
    }

    /* Iterate through requested properties */
    for (prop = proplist; !*fctx->ret && prop; prop = prop->next) {
        if (prop->type == XML_ELEMENT_NODE) {
            struct propfind_entry_list *nentry;
            xmlChar *name, *namespace = NULL;
            xmlNsPtr ns;
            const char *ns_href;
            unsigned i;

            if (!prop->ns) return HTTP_BAD_REQUEST;

            ns = prop->ns;
            ns_href = (const char *) ns->href;

            if (fctx->mode == PROPFIND_EXPAND) {
                /* Get name/namespace from <property> */
                name = xmlGetProp(prop, BAD_CAST "name");
                namespace = xmlGetProp(prop, BAD_CAST "namespace");

                if (namespace) {
                    ns_href = (const char *) namespace;
                    ns = NULL;
                }
            }
            else {
                /* node IS the property */
                name = xmlStrdup(prop->name);
            }

            /* Look for this namespace in our known array */
            for (i = 0; i < NUM_NAMESPACE; i++) {
                if (!strcmp(ns_href, known_namespaces[i].href)) {
                    ensure_ns(fctx->ns, i, fctx->root,
                              known_namespaces[i].href,
                              known_namespaces[i].prefix);
                    ns = fctx->ns[i];
                    break;
                }
            }

            if (namespace) {
                if (!ns) {
                    /* Look for namespace in hash table */
                    ns = hash_lookup(ns_href, fctx->ns_table);
                    if (!ns) {
                        char prefix[6];
                        snprintf(prefix, sizeof(prefix),
                                 "X%X", strhash(ns_href) & 0xffff);
                        ns = xmlNewNs(fctx->root,
                                      BAD_CAST ns_href, BAD_CAST prefix);
                        hash_insert(ns_href, ns, fctx->ns_table);
                    }
                }
                xmlFree(namespace);
            }

            /* Look for a match against our known properties */
            for (entry = fctx->lprops;
                 entry->name &&
                     (strcmp((const char *) name, entry->name) ||
                      strcmp((const char *) ns->href,
                             known_namespaces[entry->ns].href));
                 entry++);

            /* Skip properties already included by allprop */
            if (fctx->mode == PROPFIND_ALL && (entry->flags & PROP_ALLPROP)) {
                xmlFree(name);
                continue;
            }

            nentry = xzmalloc(sizeof(struct propfind_entry_list));
            nentry->name = name;
            nentry->ns = ns;
            if (entry->name) {
                /* Found a match - Pre-screen request based on prop flags */
                if (prescreen_prop(entry, prop, fctx)) {
                    nentry->flags = entry->flags;
                    nentry->get = entry->get;
                    nentry->prop = prop;
                    nentry->rock = entry->rock;
                }
                ret = *fctx->ret;
            }
            else {
                /* No match, treat as a dead property.
                   Need to look at both collections and resources */
                nentry->flags = PROP_COLLECTION | PROP_RESOURCE;
                nentry->get = propfind_fromdb;
                nentry->prop = NULL;
                nentry->rock = NULL;
            }

            /* Append new entry to linked list */
            if (tail) {
                tail->next = nentry;
                tail = nentry;
            }
            else tail = fctx->elist = nentry;
        }
    }

    return ret;
}


int propfind_collectionname(const xmlChar *name, xmlNsPtr ns,
                            struct propfind_ctx *fctx,
                            xmlNodePtr prop, xmlNodePtr resp,
                            struct propstat propstat[], void *rock)
{
    int r = propfind_fromdb(name, ns, fctx, prop, resp, propstat, rock);

    if (r && fctx->mbentry && !fctx->req_tgt->resource) {
        /* Special case empty displayname -- use last segment of path */
        xmlNodePtr node = xml_add_prop(HTTP_OK, fctx->ns[NS_DAV],
                                       &propstat[PROPSTAT_OK], name, ns, NULL, 0);
        buf_setcstr(&fctx->buf, strrchr(fctx->mbentry->name, '.') + 1);
        xmlAddChild(node, xmlNewCDataBlock(fctx->root->doc,
                                           BAD_CAST buf_cstring(&fctx->buf),
                                           buf_len(&fctx->buf)));
        return 0;
    }

    return r;
}


void dav_free_propfilter(struct prop_filter *prop)
{
    struct param_filter *param, *next;

    xmlFree(prop->name);
    if (prop->match) {
        xmlFree(prop->match->text);
        free(prop->match);
    }
    for (param = prop->param; param; param = next) {
        next = param->next;

        xmlFree(param->name);
        if (param->match) {
            xmlFree(param->match->text);
            free(param->match);
        }
        free(param);
    }
    free(prop);
}


static int propfind_alturiset(const xmlChar *name, xmlNsPtr ns,
                              struct propfind_ctx *fctx,
                              xmlNodePtr prop __attribute__((unused)),
                              xmlNodePtr resp __attribute__((unused)),
                              struct propstat propstat[],
                              void *rock __attribute__((unused)))
{
    xml_add_prop(HTTP_OK, fctx->ns[NS_DAV],
                 &propstat[PROPSTAT_OK], name, ns, NULL, 0);

    return 0;
}