virDomainGraphicsDefFormat(virBufferPtr buf,
                           virDomainGraphicsDefPtr def,
                           unsigned int flags)
{
    virDomainGraphicsListenDefPtr glisten = virDomainGraphicsGetListen(def, 0);
    const char *type = virDomainGraphicsTypeToString(def->type);
    bool children = false;
    size_t i;

    if (!type) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("unexpected net type %d"), def->type);
        return -1;
    }

    virBufferAsprintf(buf, "<graphics type='%s'", type);

    switch (def->type) {
    case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
        if (!glisten) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("missing listen element for graphics"));
            return -1;
        }

        switch (glisten->type) {
        case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET:
            /* To not break migration we shouldn't print the 'socket' attribute
             * if it's auto-generated or if it's based on config option from
             * qemu.conf.  If the socket is provided by user we need to print it
             * into migratable XML. */
            if (glisten->socket &&
                !((glisten->autoGenerated || glisten->fromConfig) &&
                  (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE))) {
                virBufferEscapeString(buf, " socket='%s'", glisten->socket);
            }
            break;

        case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS:
        case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK:
            if (def->data.vnc.port &&
                (!def->data.vnc.autoport || !(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)))
                virBufferAsprintf(buf, " port='%d'",
                                  def->data.vnc.port);
            else if (def->data.vnc.autoport)
                virBufferAddLit(buf, " port='-1'");

            virBufferAsprintf(buf, " autoport='%s'",
                              def->data.vnc.autoport ? "yes" : "no");

            if (def->data.vnc.websocketGenerated &&
                (flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE))
                virBufferAddLit(buf, " websocket='-1'");
            else if (def->data.vnc.websocket)
                virBufferAsprintf(buf, " websocket='%d'", def->data.vnc.websocket);

            if (flags & VIR_DOMAIN_DEF_FORMAT_STATUS)
                virBufferAsprintf(buf, " websocketGenerated='%s'",
                                  def->data.vnc.websocketGenerated ? "yes" : "no");

            virDomainGraphicsListenDefFormatAddr(buf, glisten, flags);
            break;
        case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE:
        case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST:
            break;
        }

        if (def->data.vnc.keymap)
            virBufferEscapeString(buf, " keymap='%s'",
                                  def->data.vnc.keymap);

        if (def->data.vnc.sharePolicy)
            virBufferAsprintf(buf, " sharePolicy='%s'",
                              virDomainGraphicsVNCSharePolicyTypeToString(
                              def->data.vnc.sharePolicy));

        virDomainGraphicsAuthDefFormatAttr(buf, &def->data.vnc.auth, flags);
        break;

    case VIR_DOMAIN_GRAPHICS_TYPE_SDL:
        if (def->data.sdl.display)
            virBufferEscapeString(buf, " display='%s'",
                                  def->data.sdl.display);

        if (def->data.sdl.xauth)
            virBufferEscapeString(buf, " xauth='%s'",
                                  def->data.sdl.xauth);
        if (def->data.sdl.fullscreen)
            virBufferAddLit(buf, " fullscreen='yes'");

        if (!children && def->data.sdl.gl != VIR_TRISTATE_BOOL_ABSENT) {
            virBufferAddLit(buf, ">\n");
            virBufferAdjustIndent(buf, 2);
            children = true;
        }

        if (def->data.sdl.gl != VIR_TRISTATE_BOOL_ABSENT) {
            virBufferAsprintf(buf, "<gl enable='%s'",
                              virTristateBoolTypeToString(def->data.sdl.gl));
            virBufferAddLit(buf, "/>\n");
        }

        break;

    case VIR_DOMAIN_GRAPHICS_TYPE_RDP:
        if (def->data.rdp.port)
            virBufferAsprintf(buf, " port='%d'",
                              def->data.rdp.port);
        else if (def->data.rdp.autoport)
            virBufferAddLit(buf, " port='0'");

        if (def->data.rdp.autoport)
            virBufferAddLit(buf, " autoport='yes'");

        if (def->data.rdp.replaceUser)
            virBufferAddLit(buf, " replaceUser='yes'");

        if (def->data.rdp.multiUser)
            virBufferAddLit(buf, " multiUser='yes'");

        virDomainGraphicsListenDefFormatAddr(buf, glisten, flags);

        break;

    case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP:
        if (def->data.desktop.display)
            virBufferEscapeString(buf, " display='%s'",
                                  def->data.desktop.display);

        if (def->data.desktop.fullscreen)
            virBufferAddLit(buf, " fullscreen='yes'");

        break;

    case VIR_DOMAIN_GRAPHICS_TYPE_SPICE:
        if (!glisten) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("missing listen element for spice graphics"));
            return -1;
        }

        switch (glisten->type) {
        case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS:
        case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK:
            if (def->data.spice.port)
                virBufferAsprintf(buf, " port='%d'",
                                  def->data.spice.port);

            if (def->data.spice.tlsPort)
                virBufferAsprintf(buf, " tlsPort='%d'",
                                  def->data.spice.tlsPort);

            virBufferAsprintf(buf, " autoport='%s'",
                              def->data.spice.autoport ? "yes" : "no");

            virDomainGraphicsListenDefFormatAddr(buf, glisten, flags);
            break;

        case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE:
            if (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE)
                virBufferAddStr(buf, " autoport='no'");
            break;

        case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET:
            /* If socket is auto-generated based on config option we don't
             * add any listen element into migratable XML because the original
             * listen type is "address".
             * We need to set autoport to make sure that libvirt on destination
             * will parse it as listen type "address", without autoport it is
             * parsed as listen type "none". */
            if ((flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) &&
                glisten->fromConfig) {
                virBufferAddStr(buf, " autoport='yes'");
            }
            break;

        case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST:
            break;
        }

        if (def->data.spice.keymap)
            virBufferEscapeString(buf, " keymap='%s'",
                                  def->data.spice.keymap);

        if (def->data.spice.defaultMode != VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY)
            virBufferAsprintf(buf, " defaultMode='%s'",
              virDomainGraphicsSpiceChannelModeTypeToString(def->data.spice.defaultMode));

        virDomainGraphicsAuthDefFormatAttr(buf, &def->data.spice.auth, flags);
        break;

    case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS:
        if (!def->data.egl_headless.rendernode)
            break;

        if (!children) {
            virBufferAddLit(buf, ">\n");
            virBufferAdjustIndent(buf, 2);
            children = true;
        }

        virBufferAddLit(buf, "<gl");
        virBufferEscapeString(buf, " rendernode='%s'",
                              def->data.egl_headless.rendernode);
        virBufferAddLit(buf, "/>\n");
        break;
    case VIR_DOMAIN_GRAPHICS_TYPE_LAST:
        break;
    }

    for (i = 0; i < def->nListens; i++) {
        if (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) {
            /* If the listen is based on config options from qemu.conf we need
             * to skip it.  It's up to user to properly configure both hosts for
             * migration. */
            if (def->listens[i].fromConfig)
                continue;

            /* If the socket is provided by user in the XML we need to skip this
             * listen type to support migration back to old libvirt since old
             * libvirt supports specifying socket path inside graphics element
             * as 'socket' attribute.  Auto-generated socket is a new feature
             * thus we can generate it in the migrateble XML. */
            if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
                def->listens[i].type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET &&
                def->listens[i].socket &&
                !def->listens[i].autoGenerated)
                continue;

            /* The new listen type none is in the migratable XML represented as
             * port=0 and autoport=no because old libvirt support this
             * configuration for spice. */
            if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE &&
                def->listens[i].type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE)
                continue;
        }
        if (!children) {
            virBufferAddLit(buf, ">\n");
            virBufferAdjustIndent(buf, 2);
            children = true;
        }
        virDomainGraphicsListenDefFormat(buf, &def->listens[i], flags);
    }

    if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
        for (i = 0; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST; i++) {
            int mode = def->data.spice.channels[i];
            if (mode == VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY)
                continue;

            if (!children) {
                virBufferAddLit(buf, ">\n");
                virBufferAdjustIndent(buf, 2);
                children = true;
            }

            virBufferAsprintf(buf, "<channel name='%s' mode='%s'/>\n",
                              virDomainGraphicsSpiceChannelNameTypeToString(i),
                              virDomainGraphicsSpiceChannelModeTypeToString(mode));
        }
        if (!children && (def->data.spice.image || def->data.spice.jpeg ||
                          def->data.spice.zlib || def->data.spice.playback ||
                          def->data.spice.streaming || def->data.spice.copypaste ||
                          def->data.spice.mousemode || def->data.spice.filetransfer ||
                          def->data.spice.gl)) {
            virBufferAddLit(buf, ">\n");
            virBufferAdjustIndent(buf, 2);
            children = true;
        }
        if (def->data.spice.image)
            virBufferAsprintf(buf, "<image compression='%s'/>\n",
                              virDomainGraphicsSpiceImageCompressionTypeToString(def->data.spice.image));
        if (def->data.spice.jpeg)
            virBufferAsprintf(buf, "<jpeg compression='%s'/>\n",
                              virDomainGraphicsSpiceJpegCompressionTypeToString(def->data.spice.jpeg));
        if (def->data.spice.zlib)
            virBufferAsprintf(buf, "<zlib compression='%s'/>\n",
                              virDomainGraphicsSpiceZlibCompressionTypeToString(def->data.spice.zlib));
        if (def->data.spice.playback)
            virBufferAsprintf(buf, "<playback compression='%s'/>\n",
                              virTristateSwitchTypeToString(def->data.spice.playback));
        if (def->data.spice.streaming)
            virBufferAsprintf(buf, "<streaming mode='%s'/>\n",
                              virDomainGraphicsSpiceStreamingModeTypeToString(def->data.spice.streaming));
        if (def->data.spice.mousemode)
            virBufferAsprintf(buf, "<mouse mode='%s'/>\n",
                              virDomainGraphicsSpiceMouseModeTypeToString(def->data.spice.mousemode));
        if (def->data.spice.copypaste)
            virBufferAsprintf(buf, "<clipboard copypaste='%s'/>\n",
                              virTristateBoolTypeToString(def->data.spice.copypaste));
        if (def->data.spice.filetransfer)
            virBufferAsprintf(buf, "<filetransfer enable='%s'/>\n",
                              virTristateBoolTypeToString(def->data.spice.filetransfer));

        virDomainSpiceGLDefFormat(buf, def);
    }

    if (children) {
        virBufferAdjustIndent(buf, -2);
        virBufferAddLit(buf, "</graphics>\n");
    } else {
        virBufferAddLit(buf, "/>\n");
    }

    return 0;
}


virDomainInputDefCheckABIStability(virDomainInputDefPtr src,
                                   virDomainInputDefPtr dst)
{
    if (src->type != dst->type) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target input device type %s does not match source %s"),
                       virDomainInputTypeToString(dst->type),
                       virDomainInputTypeToString(src->type));
        return false;
    }

    if (src->bus != dst->bus) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target input device bus %s does not match source %s"),
                       virDomainInputBusTypeToString(dst->bus),
                       virDomainInputBusTypeToString(src->bus));
        return false;
    }

    if (src->model != dst->model) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target input model %s does not match source %s"),
                       virDomainInputBusTypeToString(dst->model),
                       virDomainInputBusTypeToString(src->model));
        return false;
    }

    if (src->virtio && dst->virtio &&
        !virDomainVirtioOptionsCheckABIStability(src->virtio, dst->virtio))
        return false;

    if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info))
        return false;

    return true;
}


virDomainDefSetMetadata(virDomainDefPtr def,
                        int type,
                        const char *metadata,
                        const char *key,
                        const char *uri)
{
    xmlDocPtr doc = NULL;
    xmlNodePtr old;
    xmlNodePtr new = NULL;
    char *tmp = NULL;
    int ret = -1;

    if (type >= VIR_DOMAIN_METADATA_LAST) {
        virReportError(VIR_ERR_INVALID_ARG,
                       _("unknown metadata type '%d'"), type);
        goto cleanup;
    }

    switch ((virDomainMetadataType) type) {
    case VIR_DOMAIN_METADATA_DESCRIPTION:
        if (STRNEQ_NULLABLE(metadata, ""))
            tmp = g_strdup(metadata);

        VIR_FREE(def->description);
        def->description = tmp;
        break;

    case VIR_DOMAIN_METADATA_TITLE:
        if (STRNEQ_NULLABLE(metadata, ""))
            tmp = g_strdup(metadata);

        VIR_FREE(def->title);
        def->title = tmp;
        break;

    case VIR_DOMAIN_METADATA_ELEMENT:
        if (metadata) {
            /* parse and modify the xml from the user */
            if (!(doc = virXMLParseString(metadata, _("(metadata_xml)"))))
                goto cleanup;

            if (virXMLInjectNamespace(doc->children, uri, key) < 0)
                goto cleanup;

            /* create the root node if needed */
            if (!def->metadata &&
                !(def->metadata = xmlNewNode(NULL, (unsigned char *)"metadata"))) {
                virReportOOMError();
                goto cleanup;
            }

            if (!(new = xmlCopyNode(doc->children, 1))) {
                virReportOOMError();
                goto cleanup;
            }
        }

        /* remove possible other nodes sharing the namespace */
        while ((old = virXMLFindChildNodeByNs(def->metadata, uri))) {
            xmlUnlinkNode(old);
            xmlFreeNode(old);
        }

        if (new &&
            !(xmlAddChild(def->metadata, new))) {
            xmlFreeNode(new);
            virReportOOMError();
            goto cleanup;
        }
        break;

    /* coverity[dead_error_begin] */
    case VIR_DOMAIN_METADATA_LAST:
        break;
    }

    ret = 0;

 cleanup:
    xmlFreeDoc(doc);
    return ret;
}


virDomainDiskBlockIoDefFormat(virBufferPtr buf,
                              virDomainDiskDefPtr def)
{
    if (def->blockio.logical_block_size > 0 ||
        def->blockio.physical_block_size > 0) {
        virBufferAddLit(buf, "<blockio");
        if (def->blockio.logical_block_size > 0) {
            virBufferAsprintf(buf,
                              " logical_block_size='%u'",
                              def->blockio.logical_block_size);
        }
        if (def->blockio.physical_block_size > 0) {
            virBufferAsprintf(buf,
                              " physical_block_size='%u'",
                              def->blockio.physical_block_size);
        }
        virBufferAddLit(buf, "/>\n");
    }
}


virDomainIOThreadIDAdd(virDomainDefPtr def,
                       unsigned int iothread_id)
{
    virDomainIOThreadIDDefPtr iothrid = NULL;

    if (VIR_ALLOC(iothrid) < 0)
        goto error;

    iothrid->iothread_id = iothread_id;

    if (VIR_APPEND_ELEMENT_COPY(def->iothreadids, def->niothreadids,
                                iothrid) < 0)
        goto error;

    return iothrid;

 error:
    virDomainIOThreadIDDefFree(iothrid);
    return NULL;
}


virDomainMemoryRemove(virDomainDefPtr def,
                      int idx)
{
    unsigned long long memory = virDomainDefGetMemoryTotal(def);
    virDomainMemoryDefPtr ret = def->mems[idx];

    VIR_DELETE_ELEMENT(def->mems, idx, def->nmems);

    /* fix total memory size of the domain */
    virDomainDefSetMemoryTotal(def, memory - ret->size);

    return ret;
}


virDomainDefGetVcpus(const virDomainDef *def)
{
    size_t i;
    unsigned int ret = 0;

    for (i = 0; i < def->maxvcpus; i++) {
        if (def->vcpus[i]->online)
            ret++;
    }

    return ret;
}


virDomainObjParseXML(xmlDocPtr xml,
                     xmlXPathContextPtr ctxt,
                     virDomainXMLOptionPtr xmlopt,
                     unsigned int flags)
{
    long val;
    xmlNodePtr config;
    xmlNodePtr oldnode;
    virDomainObjPtr obj;
    size_t i;
    int n;
    int state;
    int reason = 0;
    void *parseOpaque = NULL;
    g_autofree char *tmp = NULL;
    g_autofree xmlNodePtr *nodes = NULL;

    if (!(obj = virDomainObjNew(xmlopt)))
        return NULL;

    if (!(config = virXPathNode("./domain", ctxt))) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("no domain config"));
        goto error;
    }

    oldnode = ctxt->node;
    ctxt->node = config;
    obj->def = virDomainDefParseXML(xml, ctxt, xmlopt, flags);
    ctxt->node = oldnode;
    if (!obj->def)
        goto error;

    if (!(tmp = virXMLPropString(ctxt->node, "state"))) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing domain state"));
        goto error;
    }
    if ((state = virDomainStateTypeFromString(tmp)) < 0) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("invalid domain state '%s'"), tmp);
        goto error;
    }
    VIR_FREE(tmp);

    if ((tmp = virXMLPropString(ctxt->node, "reason"))) {
        if ((reason = virDomainStateReasonFromString(state, tmp)) < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("invalid domain state reason '%s'"), tmp);
            goto error;
        }
    }

    virDomainObjSetState(obj, state, reason);

    if (virXPathLong("string(./@pid)", ctxt, &val) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("invalid pid"));
        goto error;
    }
    obj->pid = (pid_t)val;

    if ((n = virXPathNodeSet("./taint", ctxt, &nodes)) < 0)
        goto error;
    for (i = 0; i < n; i++) {
        char *str = virXMLPropString(nodes[i], "flag");
        if (str) {
            int flag = virDomainTaintTypeFromString(str);
            if (flag < 0) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               _("Unknown taint flag %s"), str);
                VIR_FREE(str);
                goto error;
            }
            VIR_FREE(str);
            virDomainObjTaint(obj, flag);
        }
    }

    if (xmlopt->privateData.parse &&
        xmlopt->privateData.parse(ctxt, obj, &xmlopt->config) < 0)
        goto error;

    if (xmlopt->privateData.getParseOpaque)
        parseOpaque = xmlopt->privateData.getParseOpaque(obj);

    /* callback to fill driver specific domain aspects */
    if (virDomainDefPostParse(obj->def, flags, xmlopt, parseOpaque) < 0)
        goto error;

    /* validate configuration */
    if (virDomainDefValidate(obj->def, flags, xmlopt) < 0)
        goto error;

    return obj;

 error:
    virObjectUnref(obj);
    return NULL;
}


virDomainFsDefCheckABIStability(virDomainFSDefPtr src,
                                virDomainFSDefPtr dst)
{
    if (STRNEQ(src->dst, dst->dst)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target filesystem guest target %s does not match source %s"),
                       dst->dst, src->dst);
        return false;
    }

    if (src->readonly != dst->readonly) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Target filesystem access mode does not match source"));
        return false;
    }

    if (src->model != dst->model) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Target filesystem model does not match source"));
        return false;
    }

    if (src->virtio && dst->virtio &&
        !virDomainVirtioOptionsCheckABIStability(src->virtio, dst->virtio))
        return false;

    if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info))
        return false;

    return true;
}


virDomainAssignControllerIndexes(virDomainDefPtr def)
{
    /* the index attribute of a controller is optional in the XML, but
     * is required to be valid at any time after parse. When no index
     * is provided for a controller, assign one automatically by
     * looking at what indexes are already used for that controller
     * type in the domain - the unindexed controller gets the lowest
     * unused index.
     */
    size_t outer;

    for (outer = 0; outer < def->ncontrollers; outer++) {
        virDomainControllerDefPtr cont = def->controllers[outer];
        virDomainControllerDefPtr prev = NULL;
        size_t inner;

        if (cont->idx != -1)
            continue;

        if (outer > 0 && IS_USB2_CONTROLLER(cont)) {
            /* USB2 controllers are the only exception to the simple
             * "assign the lowest unused index". A group of USB2
             * "companions" should all be at the same index as other
             * USB2 controllers in the group, but only do this
             * automatically if it appears to be the intent. To prove
             * intent: the USB controller on the list just prior to
             * this one must also be a USB2 controller, and there must
             * not yet be a controller with the exact same model of
             * this one and the same index as the previously added
             * controller (e.g., if this controller is a UHCI1, then
             * the previous controller must be an EHCI1 or a UHCI[23],
             * and there must not already be a UHCI1 controller with
             * the same index as the previous controller). If all of
             * these are satisfied, set this controller to the same
             * index as the previous controller.
             */
            int prevIdx;

            prevIdx = outer - 1;
            while (prevIdx >= 0 &&
                   def->controllers[prevIdx]->type != VIR_DOMAIN_CONTROLLER_TYPE_USB)
                prevIdx--;
            if (prevIdx >= 0)
                prev = def->controllers[prevIdx];
            /* if the last USB controller isn't USB2, that breaks
             * the chain, so we need a new index for this new
             * controller
             */
            if (prev && !IS_USB2_CONTROLLER(prev))
                prev = NULL;

            /* if prev != NULL, we've found a potential index to
             * use. Make sure this index isn't already used by an
             * existing USB2 controller of the same model as the new
             * one.
             */
            for (inner = 0; prev && inner < def->ncontrollers; inner++) {
                if (def->controllers[inner]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB &&
                    def->controllers[inner]->idx == prev->idx &&
                    def->controllers[inner]->model == cont->model) {
                    /* we already have a controller of this model with
                     * the proposed index, so we need to move to a new
                     * index for this controller
                     */
                    prev = NULL;
                }
            }
            if (prev)
                cont->idx = prev->idx;
        }
        /* if none of the above applied, prev will be NULL */
        if (!prev)
            cont->idx = virDomainControllerFindUnusedIndex(def, cont->type);
    }
}


virDomainMemorySourceDefFormat(virBufferPtr buf,
                               virDomainMemoryDefPtr def)
{
    g_autofree char *bitmap = NULL;

    if (!def->pagesize && !def->sourceNodes && !def->nvdimmPath)
        return 0;

    virBufferAddLit(buf, "<source>\n");
    virBufferAdjustIndent(buf, 2);

    switch ((virDomainMemoryModel) def->model) {
    case VIR_DOMAIN_MEMORY_MODEL_DIMM:
        if (def->sourceNodes) {
            if (!(bitmap = virBitmapFormat(def->sourceNodes)))
                return -1;

            virBufferAsprintf(buf, "<nodemask>%s</nodemask>\n", bitmap);
        }

        if (def->pagesize)
            virBufferAsprintf(buf, "<pagesize unit='KiB'>%llu</pagesize>\n",
                              def->pagesize);
        break;

    case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
        virBufferEscapeString(buf, "<path>%s</path>\n", def->nvdimmPath);

        if (def->alignsize)
            virBufferAsprintf(buf, "<alignsize unit='KiB'>%llu</alignsize>\n",
                              def->alignsize);

        if (def->nvdimmPmem)
            virBufferAddLit(buf, "<pmem/>\n");
        break;

    case VIR_DOMAIN_MEMORY_MODEL_NONE:
    case VIR_DOMAIN_MEMORY_MODEL_LAST:
        break;
    }

    virBufferAdjustIndent(buf, -2);
    virBufferAddLit(buf, "</source>\n");

    return 0;
}


virDomainDefFindDevice(virDomainDefPtr def,
                       const char *devAlias,
                       virDomainDeviceDefPtr dev,
                       bool reportError)
{
    virDomainDefFindDeviceCallbackData data = { devAlias, dev };

    dev->type = VIR_DOMAIN_DEVICE_NONE;
    virDomainDeviceInfoIterateInternal(def, virDomainDefFindDeviceCallback,
                                       DOMAIN_DEVICE_ITERATE_ALL_CONSOLES,
                                       &data);

    if (dev->type == VIR_DOMAIN_DEVICE_NONE) {
        if (reportError) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("no device found with alias %s"), devAlias);
        } else {
            VIR_DEBUG("no device found with alias %s", devAlias);
        }
        return -1;
    }

    return 0;
}


virDomainHugepagesFormat(virBufferPtr buf,
                         virDomainHugePagePtr hugepages,
                         size_t nhugepages)
{
    size_t i;

    if (nhugepages == 1 &&
        hugepages[0].size == 0) {
        virBufferAddLit(buf, "<hugepages/>\n");
        return;
    }

    virBufferAddLit(buf, "<hugepages>\n");
    virBufferAdjustIndent(buf, 2);

    for (i = 0; i < nhugepages; i++)
        virDomainHugepagesFormatBuf(buf, &hugepages[i]);

    virBufferAdjustIndent(buf, -2);
    virBufferAddLit(buf, "</hugepages>\n");
}


virDomainXMLOptionRunMomentPostParse(virDomainXMLOptionPtr xmlopt,
                                     virDomainMomentDefPtr def)
{
    if (!xmlopt->momentPostParse)
        return virDomainMomentDefPostParse(def);
    return xmlopt->momentPostParse(def);
}


virSysinfoSystemParseXML(xmlNodePtr node,
                         xmlXPathContextPtr ctxt,
                         virSysinfoSystemDefPtr *sysdef,
                         unsigned char *domUUID,
                         bool uuid_generated)
{
    VIR_XPATH_NODE_AUTORESTORE(ctxt);
    int ret = -1;
    virSysinfoSystemDefPtr def;
    g_autofree char *tmpUUID = NULL;

    ctxt->node = node;

    if (!virXMLNodeNameEqual(node, "system")) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("XML does not contain expected 'system' element"));
        return ret;
    }

    if (VIR_ALLOC(def) < 0)
        goto cleanup;

    def->manufacturer =
        virXPathString("string(entry[@name='manufacturer'])", ctxt);
    def->product =
        virXPathString("string(entry[@name='product'])", ctxt);
    def->version =
        virXPathString("string(entry[@name='version'])", ctxt);
    def->serial =
        virXPathString("string(entry[@name='serial'])", ctxt);
    tmpUUID = virXPathString("string(entry[@name='uuid'])", ctxt);
    if (tmpUUID) {
        unsigned char uuidbuf[VIR_UUID_BUFLEN];
        char uuidstr[VIR_UUID_STRING_BUFLEN];
        if (virUUIDParse(tmpUUID, uuidbuf) < 0) {
            virReportError(VIR_ERR_XML_DETAIL,
                           "%s", _("malformed <sysinfo> uuid element"));
            goto cleanup;
        }
        if (uuid_generated) {
            memcpy(domUUID, uuidbuf, VIR_UUID_BUFLEN);
        } else if (memcmp(domUUID, uuidbuf, VIR_UUID_BUFLEN) != 0) {
            virReportError(VIR_ERR_XML_DETAIL, "%s",
                           _("UUID mismatch between <uuid> and "
                             "<sysinfo>"));
            goto cleanup;
        }
        /* Although we've validated the UUID as good, virUUIDParse() is
         * lax with respect to allowing extraneous "-" and " ", but the
         * underlying hypervisor may be less forgiving. Use virUUIDFormat()
         * to validate format in xml is right. If not, then format it
         * properly so that it's used correctly later.
         */
        virUUIDFormat(uuidbuf, uuidstr);
        def->uuid = g_strdup(uuidstr);
    }
    def->sku =
        virXPathString("string(entry[@name='sku'])", ctxt);
    def->family =
        virXPathString("string(entry[@name='family'])", ctxt);

    if (!def->manufacturer && !def->product && !def->version &&
        !def->serial && !def->uuid && !def->sku && !def->family) {
        virSysinfoSystemDefFree(def);
        def = NULL;
    }

    *sysdef = g_steal_pointer(&def);
    ret = 0;
 cleanup:
    virSysinfoSystemDefFree(def);
    return ret;
}


virDomainVideoResolutionDefParseXML(xmlNodePtr node)
{
    g_autofree virDomainVideoResolutionDefPtr def = NULL;
    g_autofree char *x = NULL;
    g_autofree char *y = NULL;

    x = virXMLPropString(node, "x");
    y = virXMLPropString(node, "y");

    if (!x || !y) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("missing values for resolution"));
        return NULL;
    }

    def = g_new0(virDomainVideoResolutionDef, 1);

    if (virStrToLong_uip(x, NULL, 10, &def->x) < 0) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("cannot parse video x-resolution '%s'"), x);
        return NULL;
    }

    if (virStrToLong_uip(y, NULL, 10, &def->y) < 0) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("cannot parse video y-resolution '%s'"), y);
        return NULL;
    }

    return g_steal_pointer(&def);
}


virDomainTPMDefCheckABIStability(virDomainTPMDefPtr src,
                                 virDomainTPMDefPtr dst)
{
    if (src->type != dst->type) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Target TPM device type doesn't match source"));
        return false;
    }

    if (src->model != dst->model) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Target TPM device model doesn't match source"));
        return false;
    }

    if (src->version != dst->version) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Target TPM version doesn't match source"));
        return false;
    }

    return virDomainDeviceInfoCheckABIStability(&src->info, &dst->info);
}


virDomainDiskDefCheckABIStability(virDomainDiskDefPtr src,
                                  virDomainDiskDefPtr dst)
{
    if (src->device != dst->device) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target disk device %s does not match source %s"),
                       virDomainDiskDeviceTypeToString(dst->device),
                       virDomainDiskDeviceTypeToString(src->device));
        return false;
    }

    if (src->bus != dst->bus) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target disk bus %s does not match source %s"),
                       virDomainDiskBusTypeToString(dst->bus),
                       virDomainDiskBusTypeToString(src->bus));
        return false;
    }

    if (STRNEQ(src->dst, dst->dst)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target disk %s does not match source %s"),
                       dst->dst, src->dst);
        return false;
    }

    if (STRNEQ_NULLABLE(src->serial, dst->serial)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target disk serial %s does not match source %s"),
                       NULLSTR(dst->serial), NULLSTR(src->serial));
        return false;
    }

    if (STRNEQ_NULLABLE(src->wwn, dst->wwn)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target disk wwn '%s' does not match source '%s'"),
                       NULLSTR(dst->wwn), NULLSTR(src->wwn));
        return false;

    }

    if (src->src->readonly != dst->src->readonly) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Target disk access mode does not match source"));
        return false;
    }

    if (src->model != dst->model) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target disk model %s does not match source %s"),
                       virDomainDiskModelTypeToString(dst->model),
                       virDomainDiskModelTypeToString(src->model));
        return false;
    }

    if (src->virtio && dst->virtio &&
        !virDomainVirtioOptionsCheckABIStability(src->virtio, dst->virtio))
        return false;

    if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info))
        return false;

    return true;
}


virDomainDeviceInfoCheckABIStability(virDomainDeviceInfoPtr src,
                                     virDomainDeviceInfoPtr dst)
{
    if (src->type != dst->type) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target device address type %s does not match source %s"),
                       virDomainDeviceAddressTypeToString(dst->type),
                       virDomainDeviceAddressTypeToString(src->type));
        return false;
    }

    switch ((virDomainDeviceAddressType) src->type) {
    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
        if (src->addr.pci.domain != dst->addr.pci.domain ||
            src->addr.pci.bus != dst->addr.pci.bus ||
            src->addr.pci.slot != dst->addr.pci.slot ||
            src->addr.pci.function != dst->addr.pci.function) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("Target device PCI address " VIR_PCI_DEVICE_ADDRESS_FMT
                             "does not match source " VIR_PCI_DEVICE_ADDRESS_FMT),
                           dst->addr.pci.domain, dst->addr.pci.bus,
                           dst->addr.pci.slot, dst->addr.pci.function,
                           src->addr.pci.domain, src->addr.pci.bus,
                           src->addr.pci.slot, src->addr.pci.function);
            return false;
        }
        break;

    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE:
        if (src->addr.drive.controller != dst->addr.drive.controller ||
            src->addr.drive.bus != dst->addr.drive.bus ||
            src->addr.drive.unit != dst->addr.drive.unit) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("Target device drive address %d:%d:%d "
                             "does not match source %d:%d:%d"),
                           dst->addr.drive.controller, dst->addr.drive.bus,
                           dst->addr.drive.unit,
                           src->addr.drive.controller, src->addr.drive.bus,
                           src->addr.drive.unit);
            return false;
        }
        break;

    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL:
        if (src->addr.vioserial.controller != dst->addr.vioserial.controller ||
            src->addr.vioserial.bus != dst->addr.vioserial.bus ||
            src->addr.vioserial.port != dst->addr.vioserial.port) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("Target device virtio serial address %d:%d:%d "
                             "does not match source %d:%d:%d"),
                           dst->addr.vioserial.controller, dst->addr.vioserial.bus,
                           dst->addr.vioserial.port,
                           src->addr.vioserial.controller, src->addr.vioserial.bus,
                           src->addr.vioserial.port);
            return false;
        }
        break;

    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID:
        if (src->addr.ccid.controller != dst->addr.ccid.controller ||
            src->addr.ccid.slot != dst->addr.ccid.slot) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("Target device ccid address %d:%d "
                             "does not match source %d:%d"),
                           dst->addr.ccid.controller,
                           dst->addr.ccid.slot,
                           src->addr.ccid.controller,
                           src->addr.ccid.slot);
            return false;
        }
        break;

    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA:
        if (src->addr.isa.iobase != dst->addr.isa.iobase ||
            src->addr.isa.irq != dst->addr.isa.irq) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("Target device isa address %d:%d "
                             "does not match source %d:%d"),
                           dst->addr.isa.iobase,
                           dst->addr.isa.irq,
                           src->addr.isa.iobase,
                           src->addr.isa.irq);
            return false;
        }
        break;

    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM:
        if (src->addr.dimm.slot != dst->addr.dimm.slot) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("Target device dimm slot %u does not match "
                             "source %u"),
                           dst->addr.dimm.slot,
                           src->addr.dimm.slot);
            return false;
        }

        if (src->addr.dimm.base != dst->addr.dimm.base) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("Target device dimm base address '%llx' does "
                             "not match source '%llx'"),
                           dst->addr.dimm.base,
                           src->addr.dimm.base);
            return false;
        }
        break;

    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO:
    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390:
    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW:
    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO:
    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE:
    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED:
    case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST:
        break;
    }

    return true;
}


testBackingParse(const void *args)
{
    const struct testBackingParseData *data = args;
    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
    g_autofree char *xml = NULL;
    g_autoptr(virStorageSource) src = NULL;
    int rc;
    int erc = data->rv;
    unsigned int xmlformatflags = VIR_DOMAIN_DEF_FORMAT_SECURE;

    /* expect failure return code with NULL expected data */
    if (!data->expect)
        erc = -1;

    if ((rc = virStorageSourceNewFromBackingAbsolute(data->backing, &src)) != erc) {
        fprintf(stderr, "expected return value '%d' actual '%d'\n", erc, rc);
        return -1;
    }

    if (!src)
        return 0;

    if (src && !data->expect) {
        fprintf(stderr, "parsing of backing store string '%s' should "
                        "have failed\n", data->backing);
        return -1;
    }

    if (virDomainDiskSourceFormat(&buf, src, "source", 0, false, xmlformatflags, true, NULL) < 0 ||
        !(xml = virBufferContentAndReset(&buf))) {
        fprintf(stderr, "failed to format disk source xml\n");
        return -1;
    }

    if (STRNEQ(xml, data->expect)) {
        fprintf(stderr, "\n backing store string '%s'\n"
                        "expected storage source xml:\n%s\n"
                        "actual storage source xml:\n%s\n",
                        data->backing, data->expect, xml);
        return -1;
    }

    return 0;
}


PackLinuxElf64::elf_find_dynamic(unsigned int key) const
{
    Elf64_Dyn const *dynp= dynseg;
    if (dynp)
    for (; (unsigned)((char const *)dynp - (char const *)dynseg) < sz_dynseg
            && Elf64_Dyn::DT_NULL!=dynp->d_tag; ++dynp) if (get_te64(&dynp->d_tag)==key) {
        upx_uint64_t const t= elf_get_offset_from_address(get_te64(&dynp->d_val));
        if (t && t < (upx_uint64_t)file_size) {
            return t + file_image;
        }
        break;
    }
    return 0;
}


PackLinuxElf32::elf_find_dynamic(unsigned int key) const
{
    Elf32_Dyn const *dynp= dynseg;
    if (dynp)
    for (; (unsigned)((char const *)dynp - (char const *)dynseg) < sz_dynseg
            && Elf32_Dyn::DT_NULL!=dynp->d_tag; ++dynp) if (get_te32(&dynp->d_tag)==key) {
        unsigned const t= elf_get_offset_from_address(get_te32(&dynp->d_val));
        if (t && t < file_size) {
            return t + file_image;
        }
        break;
    }
    return 0;
}


PackLinuxElf32::PackLinuxElf32help1(InputFile *f)
{
    e_type  = get_te16(&ehdri.e_type);
    e_phnum = get_te16(&ehdri.e_phnum);
    e_shnum = get_te16(&ehdri.e_shnum);
    unsigned const e_phentsize = get_te16(&ehdri.e_phentsize);
    if (ehdri.e_ident[Elf32_Ehdr::EI_CLASS]!=Elf32_Ehdr::ELFCLASS32
    || sizeof(Elf32_Phdr) != e_phentsize
    || (Elf32_Ehdr::ELFDATA2MSB == ehdri.e_ident[Elf32_Ehdr::EI_DATA]
            && &N_BELE_RTP::be_policy != bele)
    || (Elf32_Ehdr::ELFDATA2LSB == ehdri.e_ident[Elf32_Ehdr::EI_DATA]
            && &N_BELE_RTP::le_policy != bele)) {
        e_phoff = 0;
        e_shoff = 0;
        sz_phdrs = 0;
        return;
    }
    if (0==e_phnum) throwCantUnpack("0==e_phnum");
    e_phoff = get_te32(&ehdri.e_phoff);
    unsigned const last_Phdr = e_phoff + e_phnum * sizeof(Elf32_Phdr);
    if (last_Phdr < e_phoff || (unsigned long)file_size < last_Phdr) {
        throwCantUnpack("bad e_phoff");
    }
    e_shoff = get_te32(&ehdri.e_shoff);
    unsigned const last_Shdr = e_shoff + e_shnum * sizeof(Elf32_Shdr);
    if (last_Shdr < e_shoff || (unsigned long)file_size < last_Shdr) {
        if (opt->cmd == CMD_COMPRESS) {
            throwCantUnpack("bad e_shoff");
        }
    }
    sz_phdrs = e_phnum * e_phentsize;

    if (f && Elf32_Ehdr::ET_DYN!=e_type) {
        unsigned const len = sz_phdrs + e_phoff;
        alloc_file_image(file_image, len);
        f->seek(0, SEEK_SET);
        f->readx(file_image, len);
        phdri= (Elf32_Phdr       *)(e_phoff + file_image);  // do not free() !!
    }
    if (f && Elf32_Ehdr::ET_DYN==e_type) {
        // The DT_SYMTAB has no designated length.  Read the whole file.
        alloc_file_image(file_image, file_size);
        f->seek(0, SEEK_SET);
        f->readx(file_image, file_size);
        phdri= (Elf32_Phdr *)(e_phoff + file_image);  // do not free() !!
        shdri= (Elf32_Shdr *)(e_shoff + file_image);  // do not free() !!
        if (opt->cmd != CMD_COMPRESS) {
            shdri = NULL;
        }
        sec_dynsym = elf_find_section_type(Elf32_Shdr::SHT_DYNSYM);
        if (sec_dynsym) {
            unsigned t = get_te32(&sec_dynsym->sh_link);
            if (e_shnum <= t)
                throwCantPack("bad dynsym->sh_link");
            sec_dynstr = &shdri[t];
        }

        Elf32_Phdr const *phdr= phdri;
        for (int j = e_phnum; --j>=0; ++phdr)
        if (Elf32_Phdr::PT_DYNAMIC==get_te32(&phdr->p_type)) {
            dynseg= (Elf32_Dyn const *)(check_pt_dynamic(phdr) + file_image);
            invert_pt_dynamic(dynseg);
        }
        else if (PT_LOAD32==get_te32(&phdr->p_type)) {
            check_pt_load(phdr);
        }
        // elf_find_dynamic() returns 0 if 0==dynseg.
        dynstr =      (char const *)elf_find_dynamic(Elf32_Dyn::DT_STRTAB);
        dynsym = (Elf32_Sym const *)elf_find_dynamic(Elf32_Dyn::DT_SYMTAB);
        gashtab = (unsigned const *)elf_find_dynamic(Elf32_Dyn::DT_GNU_HASH);
        hashtab = (unsigned const *)elf_find_dynamic(Elf32_Dyn::DT_HASH);
        jni_onload_sym = elf_lookup("JNI_OnLoad");
        if (jni_onload_sym) {
            jni_onload_va = get_te32(&jni_onload_sym->st_value);
            jni_onload_va = 0;
        }
    }
}


PackLinuxElf64::elf_get_offset_from_address(upx_uint64_t addr) const
{
    Elf64_Phdr const *phdr = phdri;
    int j = e_phnum;
    for (; --j>=0; ++phdr) if (PT_LOAD64 == get_te32(&phdr->p_type)) {
        upx_uint64_t const t = addr - get_te64(&phdr->p_vaddr);
        if (t < get_te64(&phdr->p_filesz)) {
            upx_uint64_t const p_offset = get_te64(&phdr->p_offset);
            if ((u64_t)file_size <= p_offset) { // FIXME: weak
                char msg[40]; snprintf(msg, sizeof(msg),
                    "bad Elf64_Phdr[%d].p_offset %#lx",
                    -1+ e_phnum - j, (long unsigned)p_offset);
                throwCantPack(msg);
            }
            return t + p_offset;
        }
    }
    return 0;
}


char const *PackLinuxElf64::get_str_name(unsigned st_name, unsigned symnum) const
{
    if (strtab_end <= st_name) {
        char msg[70]; snprintf(msg, sizeof(msg),
            "bad .st_name %#x in DT_SYMTAB[%d]", st_name, symnum);
        throwCantPack(msg);
    }
    return &dynstr[st_name];
}


static MagickBooleanType TIFFGetProperties(TIFF *tiff,Image *image,
  ExceptionInfo *exception)
{
  char
    message[MagickPathExtent],
    *text;

  MagickBooleanType
    status;

  uint32
    count,
    type;

  text=(char *) NULL;
  status=MagickTrue;
  if ((TIFFGetField(tiff,TIFFTAG_ARTIST,&text) == 1) &&
      (text != (char *) NULL))
    status=SetImageProperty(image,"tiff:artist",text,exception);
  if ((TIFFGetField(tiff,TIFFTAG_COPYRIGHT,&text) == 1) &&
      (text != (char *) NULL))
    status=SetImageProperty(image,"tiff:copyright",text,exception);
  if ((TIFFGetField(tiff,TIFFTAG_DATETIME,&text) == 1) &&
      (text != (char *) NULL))
    status=SetImageProperty(image,"tiff:timestamp",text,exception);
  if ((TIFFGetField(tiff,TIFFTAG_DOCUMENTNAME,&text) == 1) &&
      (text != (char *) NULL))
    status=SetImageProperty(image,"tiff:document",text,exception);
  if ((TIFFGetField(tiff,TIFFTAG_HOSTCOMPUTER,&text) == 1) &&
      (text != (char *) NULL))
    status=SetImageProperty(image,"tiff:hostcomputer",text,exception);
  if ((TIFFGetField(tiff,TIFFTAG_IMAGEDESCRIPTION,&text) == 1) &&
      (text != (char *) NULL))
    status=SetImageProperty(image,"comment",text,exception);
  if ((TIFFGetField(tiff,TIFFTAG_MAKE,&text) == 1) &&
      (text != (char *) NULL))
    status=SetImageProperty(image,"tiff:make",text,exception);
  if ((TIFFGetField(tiff,TIFFTAG_MODEL,&text) == 1) &&
      (text != (char *) NULL))
    status=SetImageProperty(image,"tiff:model",text,exception);
  if ((TIFFGetField(tiff,TIFFTAG_OPIIMAGEID,&count,&text) == 1) &&
      (text != (char *) NULL))
    {
      if (count >= MagickPathExtent)
        count=MagickPathExtent-1;
      (void) CopyMagickString(message,text,count+1);
      status=SetImageProperty(image,"tiff:image-id",message,exception);
    }
  if ((TIFFGetField(tiff,TIFFTAG_PAGENAME,&text) == 1) &&
      (text != (char *) NULL))
    status=SetImageProperty(image,"label",text,exception);
  if ((TIFFGetField(tiff,TIFFTAG_SOFTWARE,&text) == 1) &&
      (text != (char *) NULL))
    status=SetImageProperty(image,"tiff:software",text,exception);
  if ((TIFFGetField(tiff,33423,&count,&text) == 1) && (text != (char *) NULL))
    {
      if (count >= MagickPathExtent)
        count=MagickPathExtent-1;
      (void) CopyMagickString(message,text,count+1);
      status=SetImageProperty(image,"tiff:kodak-33423",message,exception);
    }
  if ((TIFFGetField(tiff,36867,&count,&text) == 1) && (text != (char *) NULL))
    {
      if (count >= MagickPathExtent)
        count=MagickPathExtent-1;
      (void) CopyMagickString(message,text,count+1);
      status=SetImageProperty(image,"tiff:kodak-36867",message,exception);
    }
  if (TIFFGetField(tiff,TIFFTAG_SUBFILETYPE,&type) == 1)
    switch (type)
    {
      case 0x01:
      {
        status=SetImageProperty(image,"tiff:subfiletype","REDUCEDIMAGE",
          exception);
        break;
      }
      case 0x02:
      {
        status=SetImageProperty(image,"tiff:subfiletype","PAGE",exception);
        break;
      }
      case 0x04:
      {
        status=SetImageProperty(image,"tiff:subfiletype","MASK",exception);
        break;
      }
      default:
        break;
    }
  return(status);
}


_dbus_read_credentials_socket  (DBusSocket       client_fd,
                                DBusCredentials *credentials,
                                DBusError       *error)
{
  struct msghdr msg;
  struct iovec iov;
  char buf;
  dbus_uid_t uid_read;
  dbus_gid_t primary_gid_read;
  dbus_pid_t pid_read;
  int bytes_read;

#ifdef HAVE_CMSGCRED
  union {
    struct cmsghdr hdr;
    char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
  } cmsg;
#endif

  /* The POSIX spec certainly doesn't promise this, but
   * we need these assertions to fail as soon as we're wrong about
   * it so we can do the porting fixups
   */
  _DBUS_STATIC_ASSERT (sizeof (pid_t) <= sizeof (dbus_pid_t));
  _DBUS_STATIC_ASSERT (sizeof (uid_t) <= sizeof (dbus_uid_t));
  _DBUS_STATIC_ASSERT (sizeof (gid_t) <= sizeof (dbus_gid_t));

  uid_read = DBUS_UID_UNSET;
  primary_gid_read = DBUS_GID_UNSET;
  pid_read = DBUS_PID_UNSET;

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

  _dbus_credentials_clear (credentials);

  iov.iov_base = &buf;
  iov.iov_len = 1;

  _DBUS_ZERO(msg);
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;

#if defined(HAVE_CMSGCRED)
  _DBUS_ZERO(cmsg);
  msg.msg_control = (caddr_t) &cmsg;
  msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
#endif

 again:
  bytes_read = recvmsg (client_fd.fd, &msg, 0);

  if (bytes_read < 0)
    {
      if (errno == EINTR)
	goto again;

      /* EAGAIN or EWOULDBLOCK would be unexpected here since we would
       * normally only call read_credentials if the socket was ready
       * for reading
       */

      dbus_set_error (error, _dbus_error_from_errno (errno),
                      "Failed to read credentials byte: %s",
                      _dbus_strerror (errno));
      return FALSE;
    }
  else if (bytes_read == 0)
    {
      /* this should not happen unless we are using recvmsg wrong,
       * so is essentially here for paranoia
       */
      dbus_set_error (error, DBUS_ERROR_FAILED,
                      "Failed to read credentials byte (zero-length read)");
      return FALSE;
    }
  else if (buf != '\0')
    {
      dbus_set_error (error, DBUS_ERROR_FAILED,
                      "Credentials byte was not nul");
      return FALSE;
    }

  _dbus_verbose ("read credentials byte\n");

  {
#ifdef SO_PEERCRED
    /* Supported by at least Linux and OpenBSD, with minor differences.
     *
     * This mechanism passes the process ID through and does not require
     * the peer's cooperation, so we prefer it over all others. Notably,
     * Linux also supports SCM_CREDENTIALS, which is similar to FreeBSD
     * SCM_CREDS; it's implemented in GIO, but we don't use it in dbus at all,
     * because this is much less fragile.
     */
#ifdef __OpenBSD__
    struct sockpeercred cr;
#else
    struct ucred cr;
#endif
    socklen_t cr_len = sizeof (cr);

    if (getsockopt (client_fd.fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) != 0)
      {
        _dbus_verbose ("Failed to getsockopt(SO_PEERCRED): %s\n",
                       _dbus_strerror (errno));
      }
    else if (cr_len != sizeof (cr))
      {
        _dbus_verbose ("Failed to getsockopt(SO_PEERCRED), returned %d bytes, expected %d\n",
                       cr_len, (int) sizeof (cr));
      }
    else
      {
        pid_read = cr.pid;
        uid_read = cr.uid;
#ifdef __linux__
        /* Do other platforms have cr.gid? (Not that it really matters,
         * because the gid is useless to us unless we know the complete
         * group vector, which we only know on Linux.) */
        primary_gid_read = cr.gid;
#endif
      }
#elif defined(HAVE_UNPCBID) && defined(LOCAL_PEEREID)
    /* Another variant of the above - used on NetBSD
     */
    struct unpcbid cr;
    socklen_t cr_len = sizeof (cr);

    if (getsockopt (client_fd.fd, 0, LOCAL_PEEREID, &cr, &cr_len) != 0)
      {
        _dbus_verbose ("Failed to getsockopt(LOCAL_PEEREID): %s\n",
                       _dbus_strerror (errno));
      }
    else if (cr_len != sizeof (cr))
      {
        _dbus_verbose ("Failed to getsockopt(LOCAL_PEEREID), returned %d bytes, expected %d\n",
                       cr_len, (int) sizeof (cr));
      }
    else
      {
        pid_read = cr.unp_pid;
        uid_read = cr.unp_euid;
      }
#elif defined(HAVE_CMSGCRED)
    /* We only check for HAVE_CMSGCRED, but we're really assuming that the
     * presence of that struct implies SCM_CREDS. Supported by at least
     * FreeBSD and DragonflyBSD.
     *
     * This mechanism requires the peer to help us (it has to send us a
     * SCM_CREDS message) but it does pass the process ID through,
     * which makes it better than getpeereid().
     */
    struct cmsgcred *cred;
    struct cmsghdr *cmsgp;

    for (cmsgp = CMSG_FIRSTHDR (&msg);
         cmsgp != NULL;
         cmsgp = CMSG_NXTHDR (&msg, cmsgp))
      {
        if (cmsgp->cmsg_type == SCM_CREDS &&
            cmsgp->cmsg_level == SOL_SOCKET &&
            cmsgp->cmsg_len >= CMSG_LEN (sizeof (struct cmsgcred)))
          {
            cred = (struct cmsgcred *) CMSG_DATA (cmsgp);
            pid_read = cred->cmcred_pid;
            uid_read = cred->cmcred_euid;
            break;
          }
      }

#elif defined(HAVE_GETPEERUCRED)
    /* Supported in at least Solaris >= 10. It should probably be higher
     * up this list, because it carries the pid and we use this code path
     * for audit data. */
    ucred_t * ucred = NULL;
    if (getpeerucred (client_fd.fd, &ucred) == 0)
      {
#ifdef HAVE_ADT
        adt_session_data_t *adth = NULL;
#endif
        pid_read = ucred_getpid (ucred);
        uid_read = ucred_geteuid (ucred);
#ifdef HAVE_ADT
        /* generate audit session data based on socket ucred */
        if (adt_start_session (&adth, NULL, 0) || (adth == NULL))
          {
            _dbus_verbose ("Failed to adt_start_session(): %s\n", _dbus_strerror (errno));
          }
        else
          {
            if (adt_set_from_ucred (adth, ucred, ADT_NEW))
              {
                _dbus_verbose ("Failed to adt_set_from_ucred(): %s\n", _dbus_strerror (errno));
              }
            else
              {
                adt_export_data_t *data = NULL;
                size_t size = adt_export_session_data (adth, &data);
                if (size <= 0)
                  {
                    _dbus_verbose ("Failed to adt_export_session_data(): %s\n", _dbus_strerror (errno));
                  }
                else
                  {
                    _dbus_credentials_add_adt_audit_data (credentials, data, size);
                    free (data);
                  }
              }
            (void) adt_end_session (adth);
          }
#endif /* HAVE_ADT */
      }
    else
      {
        _dbus_verbose ("Failed to getpeerucred() credentials: %s\n", _dbus_strerror (errno));
      }
    if (ucred != NULL)
      ucred_free (ucred);

    /* ----------------------------------------------------------------
     * When adding new mechanisms, please add them above this point
     * if they support passing the process ID through, or below if not.
     * ---------------------------------------------------------------- */

#elif defined(HAVE_GETPEEREID)
    /* getpeereid() originates from D.J. Bernstein and is fairly
     * widely-supported. According to a web search, it might be present in
     * any/all of:
     *
     * - AIX?
     * - Blackberry?
     * - Cygwin
     * - FreeBSD 4.6+ (but we prefer SCM_CREDS: it carries the pid)
     * - Mac OS X
     * - Minix 3.1.8+
     * - MirBSD?
     * - NetBSD 5.0+ (but LOCAL_PEEREID would be better: it carries the pid)
     * - OpenBSD 3.0+ (but we prefer SO_PEERCRED: it carries the pid)
     * - QNX?
     */
    uid_t euid;
    gid_t egid;
    if (getpeereid (client_fd.fd, &euid, &egid) == 0)
      {
        uid_read = euid;
      }
    else
      {
        _dbus_verbose ("Failed to getpeereid() credentials: %s\n", _dbus_strerror (errno));
      }
#else /* no supported mechanism */

#warning Socket credentials not supported on this Unix OS
#warning Please tell https://bugs.freedesktop.org/enter_bug.cgi?product=DBus

    /* Please add other operating systems known to support at least one of
     * the mechanisms above to this list, keeping alphabetical order.
     * Everything not in this list  is best-effort.
     */
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
    defined(__linux__) || \
    defined(__OpenBSD__) || \
    defined(__NetBSD__)
# error Credentials passing not working on this OS is a regression!
#endif

    _dbus_verbose ("Socket credentials not supported on this OS\n");
#endif
  }

  _dbus_verbose ("Credentials:"
                 "  pid "DBUS_PID_FORMAT
                 "  uid "DBUS_UID_FORMAT
                 "\n",
		 pid_read,
		 uid_read);

  if (pid_read != DBUS_PID_UNSET)
    {
      if (!_dbus_credentials_add_pid (credentials, pid_read))
        {
          _DBUS_SET_OOM (error);
          return FALSE;
        }
    }

  if (uid_read != DBUS_UID_UNSET)
    {
      if (!_dbus_credentials_add_unix_uid (credentials, uid_read))
        {
          _DBUS_SET_OOM (error);
          return FALSE;
        }
    }

  if (!add_linux_security_label_to_credentials (client_fd.fd, credentials))
    {
      _DBUS_SET_OOM (error);
      return FALSE;
    }

  /* We don't put any groups in the credentials unless we can put them
   * all there. */
  if (!add_groups_to_credentials (client_fd.fd, credentials, primary_gid_read))
    {
      _DBUS_SET_OOM (error);
      return FALSE;
    }

  return TRUE;
}


_dbus_delete_directory (const DBusString *filename,
                        DBusError        *error)
{
  const char *filename_c;

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

  filename_c = _dbus_string_get_const_data (filename);

  if (rmdir (filename_c) != 0)
    {
      dbus_set_error (error, DBUS_ERROR_FAILED,
                      "Failed to remove directory %s: %s\n",
                      filename_c, _dbus_strerror (errno));
      return FALSE;
    }

  return TRUE;
}


_dbus_pid_for_log (void)
{
  return getpid ();
}


_dbus_error_from_gai (int gai_res,
                      int saved_errno)
{
  switch (gai_res)
    {
#ifdef EAI_FAMILY
      case EAI_FAMILY:
        /* ai_family not supported (at all) */
        return DBUS_ERROR_NOT_SUPPORTED;
#endif

#ifdef EAI_SOCKTYPE
      case EAI_SOCKTYPE:
        /* ai_socktype not supported (at all) */
        return DBUS_ERROR_NOT_SUPPORTED;
#endif

#ifdef EAI_MEMORY
      case EAI_MEMORY:
        /* Out of memory */
        return DBUS_ERROR_NO_MEMORY;
#endif

#ifdef EAI_SYSTEM
      case EAI_SYSTEM:
        /* Unspecified system error, details in errno */
        return _dbus_error_from_errno (saved_errno);
#endif

      case 0:
        /* It succeeded, but we didn't get any addresses? */
        return DBUS_ERROR_FAILED;

      /* EAI_AGAIN: Transient failure */
      /* EAI_BADFLAGS: invalid ai_flags (programming error) */
      /* EAI_FAIL: Non-recoverable failure */
      /* EAI_NODATA: host exists but has no addresses */
      /* EAI_NONAME: host does not exist */
      /* EAI_OVERFLOW: argument buffer overflow */
      /* EAI_SERVICE: service not available for specified socket
       * type (we should never see this because we use numeric
       * ports) */
      default:
        return DBUS_ERROR_FAILED;
    }
}


close_ignore_error (int fd)
{
  close (fd);
}


_dbus_write_socket (DBusSocket        fd,
                    const DBusString *buffer,
                    int               start,
                    int               len)
{
#if HAVE_DECL_MSG_NOSIGNAL
  const char *data;
  int bytes_written;

  data = _dbus_string_get_const_data_len (buffer, start, len);

 again:

  bytes_written = send (fd.fd, data, len, MSG_NOSIGNAL);

  if (bytes_written < 0 && errno == EINTR)
    goto again;

  return bytes_written;

#else
  return _dbus_write (fd.fd, buffer, start, len);
#endif
}


add_groups_to_credentials (int              client_fd,
                           DBusCredentials *credentials,
                           dbus_gid_t       primary)
{
#if defined(__linux__) && defined(SO_PEERGROUPS)
  _DBUS_STATIC_ASSERT (sizeof (gid_t) <= sizeof (dbus_gid_t));
  gid_t *buf = NULL;
  socklen_t len = 1024;
  dbus_bool_t oom = FALSE;
  /* libdbus has a different representation of group IDs just to annoy you */
  dbus_gid_t *converted_gids = NULL;
  dbus_bool_t need_primary = TRUE;
  size_t n_gids;
  size_t i;

  n_gids = ((size_t) len) / sizeof (gid_t);
  buf = dbus_new (gid_t, n_gids);

  if (buf == NULL)
    return FALSE;

  while (getsockopt (client_fd, SOL_SOCKET, SO_PEERGROUPS, buf, &len) < 0)
    {
      int e = errno;
      gid_t *replacement;

      _dbus_verbose ("getsockopt failed with %s, len now %lu\n",
                     _dbus_strerror (e), (unsigned long) len);

      if (e != ERANGE || (size_t) len <= n_gids * sizeof (gid_t))
        {
          _dbus_verbose ("Failed to getsockopt(SO_PEERGROUPS): %s\n",
                         _dbus_strerror (e));
          goto out;
        }

      /* If not enough space, len is updated to be enough.
       * Try again with a large enough buffer. */
      n_gids = ((size_t) len) / sizeof (gid_t);
      replacement = dbus_realloc (buf, len);

      if (replacement == NULL)
        {
          oom = TRUE;
          goto out;
        }

      buf = replacement;
      _dbus_verbose ("will try again with %lu\n", (unsigned long) len);
    }

  if (len <= 0)
    {
      _dbus_verbose ("getsockopt(SO_PEERGROUPS) yielded <= 0 bytes: %ld\n",
                     (long) len);
      goto out;
    }

  if (len > n_gids * sizeof (gid_t))
    {
      _dbus_verbose ("%lu > %zu", (unsigned long) len, n_gids * sizeof (gid_t));
      _dbus_assert_not_reached ("getsockopt(SO_PEERGROUPS) overflowed");
    }

  if (len % sizeof (gid_t) != 0)
    {
      _dbus_verbose ("getsockopt(SO_PEERGROUPS) did not return an "
                     "integer multiple of sizeof(gid_t): %lu should be "
                     "divisible by %zu",
                     (unsigned long) len, sizeof (gid_t));
      goto out;
    }

  /* Allocate an extra space for the primary group ID */
  n_gids = ((size_t) len) / sizeof (gid_t);

  /* If n_gids is less than this, then (n_gids + 1) certainly doesn't
   * overflow, and neither does multiplying that by sizeof(dbus_gid_t).
   * This is using _DBUS_INT32_MAX as a conservative lower bound for
   * the maximum size_t. */
  if (n_gids >= (_DBUS_INT32_MAX / sizeof (dbus_gid_t)) - 1)
    {
      _dbus_verbose ("getsockopt(SO_PEERGROUPS) returned a huge number "
                     "of groups (%lu bytes), ignoring",
                     (unsigned long) len);
      goto out;
    }

  converted_gids = dbus_new (dbus_gid_t, n_gids + 1);

  if (converted_gids == NULL)
    {
      oom = TRUE;
      goto out;
    }

  for (i = 0; i < n_gids; i++)
    {
      converted_gids[i] = (dbus_gid_t) buf[i];

      if (converted_gids[i] == primary)
        need_primary = FALSE;
    }

  if (need_primary && primary != DBUS_GID_UNSET)
    {
      converted_gids[n_gids] = primary;
      n_gids++;
    }

  _dbus_credentials_take_unix_gids (credentials, converted_gids, n_gids);

out:
  dbus_free (buf);
  return !oom;
#else
  /* no error */
  return TRUE;
#endif
}


_dbus_socketpair (DBusSocket *fd1,
                  DBusSocket *fd2,
                  dbus_bool_t blocking,
                  DBusError  *error)
{
#ifdef HAVE_SOCKETPAIR
  int fds[2];
  int retval;

#ifdef SOCK_CLOEXEC
  dbus_bool_t cloexec_done;

  retval = socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, fds);
  cloexec_done = retval >= 0;

  if (retval < 0 && (errno == EINVAL || errno == EPROTOTYPE))
#endif
    {
      retval = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
    }

  if (retval < 0)
    {
      dbus_set_error (error, _dbus_error_from_errno (errno),
                      "Could not create full-duplex pipe");
      return FALSE;
    }

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

#ifdef SOCK_CLOEXEC
  if (!cloexec_done)
#endif
    {
      _dbus_fd_set_close_on_exec (fds[0]);
      _dbus_fd_set_close_on_exec (fds[1]);
    }

  if (!blocking &&
      (!_dbus_set_fd_nonblocking (fds[0], NULL) ||
       !_dbus_set_fd_nonblocking (fds[1], NULL)))
    {
      dbus_set_error (error, _dbus_error_from_errno (errno),
                      "Could not set full-duplex pipe nonblocking");

      _dbus_close (fds[0], NULL);
      _dbus_close (fds[1], NULL);

      return FALSE;
    }

  fd1->fd = fds[0];
  fd2->fd = fds[1];

  _dbus_verbose ("full-duplex pipe %d <-> %d\n",
                 fd1->fd, fd2->fd);

  return TRUE;
#else
  _dbus_warn ("_dbus_socketpair() not implemented on this OS");
  dbus_set_error (error, DBUS_ERROR_FAILED,
                  "_dbus_socketpair() not implemented on this OS");
  return FALSE;
#endif
}


_dbus_atomic_inc (DBusAtomic *atomic)
{
#if DBUS_USE_SYNC
  return __sync_add_and_fetch(&atomic->value, 1)-1;
#else
  dbus_int32_t res;

  pthread_mutex_lock (&atomic_mutex);
  res = atomic->value;
  atomic->value += 1;
  pthread_mutex_unlock (&atomic_mutex);

  return res;
#endif
}


int ldb_build_mod_req(struct ldb_request **ret_req,
			struct ldb_context *ldb,
			TALLOC_CTX *mem_ctx,
			const struct ldb_message *message,
			struct ldb_control **controls,
			void *context,
			ldb_request_callback_t callback,
			struct ldb_request *parent)
{
	struct ldb_request *req;

	*ret_req = NULL;

	req = ldb_build_req_common(mem_ctx, ldb, controls,
				   context, callback, parent);
	if (req == NULL) {
		ldb_set_errstring(ldb, "Out of Memory");
		return LDB_ERR_OPERATIONS_ERROR;
	}

	req->operation = LDB_MODIFY;
	req->op.mod.message = message;

	*ret_req = req;
	return LDB_SUCCESS;
}


int ldb_transaction_prepare_commit(struct ldb_context *ldb)
{
	struct ldb_module *next_module;
	int status;

	if (ldb->prepare_commit_done) {
		return LDB_SUCCESS;
	}

	/* commit only when all nested transactions are complete */
	if (ldb->transaction_active > 1) {
		return LDB_SUCCESS;
	}

	ldb->prepare_commit_done = true;

	if (ldb->transaction_active < 0) {
		ldb_debug(ldb, LDB_DEBUG_FATAL,
			  "prepare commit called but no ldb transactions are active!");
		ldb->transaction_active = 0;
		return LDB_ERR_OPERATIONS_ERROR;
	}

	/* call prepare transaction if available */
	FIRST_OP_NOERR(ldb, prepare_commit);
	if (next_module == NULL) {
		return LDB_SUCCESS;
	}

	ldb_reset_err_string(ldb);

	status = next_module->ops->prepare_commit(next_module);
	if (status != LDB_SUCCESS) {
		ldb->transaction_active--;
		/* if a next_module fails the prepare then we need
		   to call the end transaction for everyone */
		FIRST_OP(ldb, del_transaction);
		next_module->ops->del_transaction(next_module);
		if (ldb->err_string == NULL) {
			/* no error string was setup by the backend */
			ldb_asprintf_errstring(ldb,
					       "ldb transaction prepare commit: %s (%d)",
					       ldb_strerror(status),
					       status);
		}
		if ((next_module && next_module->ldb->flags & LDB_FLG_ENABLE_TRACING)) {
			ldb_debug(next_module->ldb, LDB_DEBUG_TRACE, "prepare commit transaction error: %s",
				  ldb_errstring(next_module->ldb));
		}
	}

	return status;
}


int ldb_modify_default_callback(struct ldb_request *req, struct ldb_reply *ares)
{
	struct ldb_result *res;
	unsigned int n;
	int ret;

	res = talloc_get_type(req->context, struct ldb_result);

	if (!ares) {
		return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
	}

	if (ares->error != LDB_SUCCESS) {
		ret = ares->error;
		talloc_free(ares);
		return ldb_request_done(req, ret);
	}

	switch (ares->type) {
	case LDB_REPLY_REFERRAL:
		if (res->refs) {
			for (n = 0; res->refs[n]; n++) /*noop*/ ;
		} else {
			n = 0;
		}

		res->refs = talloc_realloc(res, res->refs, char *, n + 2);
		if (! res->refs) {
			return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
		}

		res->refs[n] = talloc_move(res->refs, &ares->referral);
		res->refs[n + 1] = NULL;
		break;

	case LDB_REPLY_DONE:
		talloc_free(ares);
		return ldb_request_done(req, LDB_SUCCESS);
	default:
		talloc_free(ares);
		ldb_asprintf_errstring(req->handle->ldb, "Invalid LDB reply type %d", ares->type);
		return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
	}

	talloc_free(ares);
	return ldb_request_done(req, LDB_SUCCESS);
}


int ldb_op_default_callback(struct ldb_request *req, struct ldb_reply *ares)
{
	int ret;

	if (!ares) {
		return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
	}

	if (ares->error != LDB_SUCCESS) {
		ret = ares->error;
		talloc_free(ares);
		return ldb_request_done(req, ret);
	}

	if (ares->type != LDB_REPLY_DONE) {
		ldb_asprintf_errstring(req->handle->ldb, "Invalid LDB reply type %d", ares->type);
		TALLOC_FREE(ares);
		return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
	}

	talloc_free(ares);
	return ldb_request_done(req, LDB_SUCCESS);
}


void ldb_set_flags(struct ldb_context *ldb, unsigned flags)
{
	ldb->flags = flags;
}


_PUBLIC_ enum ndr_err_code ndr_pull_dns_res_rec(struct ndr_pull *ndr,
						int ndr_flags,
						struct dns_res_rec *r)
{
	uint32_t _flags_save_STRUCT = ndr->flags;
	uint32_t _saved_offset1;
	uint32_t pad, length;

	ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX |
				   LIBNDR_FLAG_NOALIGN);
	if (ndr_flags & NDR_SCALARS) {
		NDR_CHECK(ndr_pull_align(ndr, 4));
		NDR_CHECK(ndr_pull_dns_string(ndr, NDR_SCALARS, &r->name));
		NDR_CHECK(ndr_pull_dns_qtype(ndr, NDR_SCALARS, &r->rr_type));
		NDR_CHECK(ndr_pull_dns_qclass(ndr, NDR_SCALARS, &r->rr_class));
		NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->ttl));
		NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &r->length));
		_saved_offset1 = ndr->offset;
		if (r->length > 0) {
			NDR_CHECK(ndr_token_store(ndr, &ndr->array_size_list,
						  &r->rdata,
						  r->length));
			NDR_CHECK(ndr_pull_set_switch_value(ndr, &r->rdata,
							    r->rr_type));
			NDR_CHECK(ndr_pull_dns_rdata(ndr, NDR_SCALARS,
						     &r->rdata));
		} else {
			ZERO_STRUCT(r->rdata);
		}
		length = ndr->offset - _saved_offset1;
		if (length > r->length) {
			return ndr_pull_error(ndr, NDR_ERR_LENGTH, "TODO");
		}

		r->unexpected = data_blob_null;
		pad = r->length - length;
		if (pad > 0) {
			NDR_PULL_NEED_BYTES(ndr, pad);
			r->unexpected = data_blob_talloc(ndr->current_mem_ctx,
							 ndr->data +
							 ndr->offset,
							 pad);
			if (r->unexpected.data == NULL) {
				return ndr_pull_error(ndr,
						      NDR_ERR_ALLOC,
						      "Failed to allocate a " \
						      "data blob");
			}
			ndr->offset += pad;
		}

		NDR_CHECK(ndr_pull_trailer_align(ndr, 4));
	}
	if (ndr_flags & NDR_BUFFERS) {
		NDR_CHECK(ndr_pull_dns_rdata(ndr, NDR_BUFFERS, &r->rdata));
	}
	ndr->flags = _flags_save_STRUCT;
	return NDR_ERR_SUCCESS;
}


enum ndr_err_code ndr_push_NETLOGON_SAM_LOGON_RESPONSE_EX_with_flags(struct ndr_push *ndr, int ndr_flags, const struct NETLOGON_SAM_LOGON_RESPONSE_EX *r)
{
	{
		uint32_t _flags_save_STRUCT = ndr->flags;
		ndr_set_flags(&ndr->flags, LIBNDR_FLAG_NOALIGN);
		if (ndr_flags & NDR_SCALARS) {
			NDR_CHECK(ndr_push_align(ndr, 4));
			NDR_CHECK(ndr_push_netlogon_command(ndr, NDR_SCALARS, r->command));
			NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, r->sbz));
			NDR_CHECK(ndr_push_nbt_server_type(ndr, NDR_SCALARS, r->server_type));
			NDR_CHECK(ndr_push_GUID(ndr, NDR_SCALARS, &r->domain_uuid));
			NDR_CHECK(ndr_push_nbt_string(ndr, NDR_SCALARS, r->forest));
			NDR_CHECK(ndr_push_nbt_string(ndr, NDR_SCALARS, r->dns_domain));
			NDR_CHECK(ndr_push_nbt_string(ndr, NDR_SCALARS, r->pdc_dns_name));
			NDR_CHECK(ndr_push_nbt_string(ndr, NDR_SCALARS, r->domain_name));
			NDR_CHECK(ndr_push_nbt_string(ndr, NDR_SCALARS, r->pdc_name));
			NDR_CHECK(ndr_push_nbt_string(ndr, NDR_SCALARS, r->user_name));
			NDR_CHECK(ndr_push_nbt_string(ndr, NDR_SCALARS, r->server_site));
			NDR_CHECK(ndr_push_nbt_string(ndr, NDR_SCALARS, r->client_site));
			if (r->nt_version & NETLOGON_NT_VERSION_5EX_WITH_IP) {
				NDR_CHECK(ndr_push_uint8(ndr, NDR_SCALARS, ndr_size_nbt_sockaddr(&r->sockaddr, ndr->flags)));
				{
					struct ndr_push *_ndr_sockaddr;
					NDR_CHECK(ndr_push_subcontext_start(ndr, &_ndr_sockaddr, 0, ndr_size_nbt_sockaddr(&r->sockaddr, ndr->flags)));
					NDR_CHECK(ndr_push_nbt_sockaddr(_ndr_sockaddr, NDR_SCALARS|NDR_BUFFERS, &r->sockaddr));
					NDR_CHECK(ndr_push_subcontext_end(ndr, _ndr_sockaddr, 0, ndr_size_nbt_sockaddr(&r->sockaddr, ndr->flags)));
				}
			}
			if (r->nt_version & NETLOGON_NT_VERSION_WITH_CLOSEST_SITE) {
				NDR_CHECK(ndr_push_nbt_string(ndr, NDR_SCALARS, r->next_closest_site));
			}
			NDR_CHECK(ndr_push_netlogon_nt_version_flags(ndr, NDR_SCALARS, r->nt_version));
			NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, r->lmnt_token));
			NDR_CHECK(ndr_push_uint16(ndr, NDR_SCALARS, r->lm20_token));
		}
		if (ndr_flags & NDR_BUFFERS) {
			NDR_CHECK(ndr_push_GUID(ndr, NDR_BUFFERS, &r->domain_uuid));
		}
		ndr->flags = _flags_save_STRUCT;
	}
	return NDR_ERR_SUCCESS;
}


static int paged_search_callback(struct ldb_request *req,
				 struct ldb_reply *ares)
{
	struct paged_context *ac;
	struct results_store *store;
	int ret;
	const struct ldb_val *guid_blob;
	struct GUID guid;
	NTSTATUS status;

	ac = talloc_get_type(req->context, struct paged_context);
	store = ac->store;

	if (!ares) {
		return ldb_module_done(ac->req, NULL, NULL,
					LDB_ERR_OPERATIONS_ERROR);
	}
	if (ares->error != LDB_SUCCESS) {
		return ldb_module_done(ac->req, ares->controls,
					ares->response, ares->error);
	}

	switch (ares->type) {
	case LDB_REPLY_ENTRY:
		if (store->results == NULL) {
			store->num_entries = 0;
			store->result_array_size = 16;
			store->results = talloc_array(store, struct GUID,
						     store->result_array_size);
			if (store->results == NULL) {
				return ldb_module_done(ac->req, NULL, NULL,
						     LDB_ERR_OPERATIONS_ERROR);
			}
		} else if (store->num_entries == store->result_array_size) {
			if (store->result_array_size > INT_MAX/2) {
				return ldb_module_done(ac->req, NULL, NULL,
						     LDB_ERR_OPERATIONS_ERROR);
			}
			store->result_array_size *= 2;
			store->results = talloc_realloc(store, store->results,
							struct GUID,
						store->result_array_size);
			if (store->results == NULL) {
				return ldb_module_done(ac->req, NULL, NULL,
						     LDB_ERR_OPERATIONS_ERROR);
			}
		}

		guid_blob = ldb_dn_get_extended_component(ares->message->dn,
							  "GUID");
		if (guid_blob == NULL) {
			return ldb_module_done(ac->req, NULL, NULL,
					       LDB_ERR_OPERATIONS_ERROR);
		}
		status = GUID_from_ndr_blob(guid_blob, &guid);
		if (!NT_STATUS_IS_OK(status)) {
			return ldb_module_done(ac->req, NULL, NULL,
					       LDB_ERR_OPERATIONS_ERROR);
		}

		/* Redundant paranoid check */
		if (store->num_entries > store->result_array_size) {
			return ldb_module_done(ac->req, NULL, NULL,
					       LDB_ERR_OPERATIONS_ERROR);
		}

		store->results[store->num_entries] = guid;
		store->num_entries++;
		break;

	case LDB_REPLY_REFERRAL:
		ret = save_referral(store, ares->referral);
		if (ret != LDB_SUCCESS) {
			return ldb_module_done(ac->req, NULL, NULL, ret);
		}
		break;

	case LDB_REPLY_DONE:
		if (store->num_entries != 0) {
			store->results = talloc_realloc(store, store->results,
							struct GUID,
							store->num_entries);
			if (store->results == NULL) {
				return ldb_module_done(ac->req, NULL, NULL,
						     LDB_ERR_OPERATIONS_ERROR);
			}
		}
		store->result_array_size = store->num_entries;

		ac->store->controls = talloc_move(ac->store, &ares->controls);
		ret = paged_results(ac, ares);
		if (ret != LDB_SUCCESS) {
			/* paged_results will have called ldb_module_done
			 * if an error occurred
			 */
			return ret;
		}
		return ldb_module_done(ac->req, ac->controls,
					ares->response, ret);
	}

	return LDB_SUCCESS;
}


static int paged_search(struct ldb_module *module, struct ldb_request *req)
{
	struct ldb_context *ldb;
	struct ldb_control *control;
	struct ldb_control *vlv_control;
	struct private_data *private_data;
	struct ldb_paged_control *paged_ctrl;
	struct ldb_request *search_req;
	struct paged_context *ac;
	int ret;

	ldb = ldb_module_get_ctx(module);

	/* check if there's a paged request control */
	control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID);
	if (control == NULL) {
		/* not found go on */
		return ldb_next_request(module, req);
	}

	paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
	if (!paged_ctrl) {
		return LDB_ERR_PROTOCOL_ERROR;
	}

	private_data = talloc_get_type(ldb_module_get_private(module),
					struct private_data);

	vlv_control = ldb_request_get_control(req, LDB_CONTROL_VLV_REQ_OID);
	if (vlv_control != NULL) {
		/*
		 * VLV and paged_results are not allowed at the same
		 * time
		 */
		return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
	}

	ac = talloc_zero(req, struct paged_context);
	if (ac == NULL) {
		ldb_set_errstring(ldb, "Out of Memory");
		return LDB_ERR_OPERATIONS_ERROR;
	}

	ac->module = module;
	ac->req = req;
	ac->size = paged_ctrl->size;
	if (ac->size < 0) {
		/*
		 * Apparently some clients send more than 2^31. This
		 * violates the ldap standard, but we need to cope.
		 * In the future, if maximum result sizes are implemented in
		 * Samba, we should also clamp the page size to the maximum
		 * result size.
		 */
		ac->size = 0x7FFFFFFF;
	}

	/* check if it is a continuation search the store */
	if (paged_ctrl->cookie_len == 0) {
		struct ldb_control *ext_ctrl;
		struct ldb_control **controls;
		static const char * const attrs[1] = { NULL };

		if (paged_ctrl->size == 0) {
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ac->store = new_store(private_data);
		if (ac->store == NULL) {
			return LDB_ERR_OPERATIONS_ERROR;
		}

		controls = req->controls;
		ext_ctrl = ldb_request_get_control(req,
					LDB_CONTROL_EXTENDED_DN_OID);
		if (ext_ctrl == NULL) {
			/*
			 * Add extended_dn control to the request if there
			 * isn't already one.  We'll get the GUID out of it in
			 * the callback.  This is a workaround for the case
			 * where ntsecuritydescriptor forbids fetching GUIDs
			 * for the current user.
			 */
			struct ldb_request *req_extended_dn;
			struct ldb_extended_dn_control *ext_ctrl_data;
			req_extended_dn = talloc_zero(req, struct ldb_request);
			req_extended_dn->controls = req->controls;
			ext_ctrl_data = talloc_zero(req,
					struct ldb_extended_dn_control);
			ext_ctrl_data->type = 1;

			ret = ldb_request_add_control(req_extended_dn,
					      LDB_CONTROL_EXTENDED_DN_OID,
						      true,
						      ext_ctrl_data);
			if (ret != LDB_SUCCESS) {
				return ret;
			}
			controls = req_extended_dn->controls;
		}

		ret = ldb_build_search_req_ex(&search_req, ldb, ac,
						req->op.search.base,
						req->op.search.scope,
						req->op.search.tree,
						attrs,
						controls,
						ac,
						paged_search_callback,
						req);
		if (ret != LDB_SUCCESS) {
			return ret;
		}

		ac->store->expr = talloc_steal(ac->store, req->op.search.tree);
		ac->store->expr_str = ldb_filter_from_tree(ac->store,
							  req->op.search.tree);
		ac->store->attrs = paged_copy_attrs(ac->store,
						    req->op.search.attrs);

		/* save it locally and remove it from the list */
		/* we do not need to replace them later as we
		 * are keeping the original req intact */
		if (!ldb_save_controls(control, search_req, NULL)) {
			return LDB_ERR_OPERATIONS_ERROR;
		}
		ac->store->down_controls =
		    paged_results_copy_down_controls(ac->store, req->controls);
		if (ac->store->down_controls == NULL) {
			return LDB_ERR_OPERATIONS_ERROR;
		}

		return ldb_next_request(module, search_req);

	} else {
		struct results_store *current = NULL;
		char *expr_str;
		bool bool_ret;

		/* TODO: age out old outstanding requests */
		for (current = private_data->store; current != NULL;
		     current = current->next) {
			if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
				current->timestamp = time(NULL);
				break;
			}
		}
		if (current == NULL) {
			return LDB_ERR_UNWILLING_TO_PERFORM;
		}

		/* Get the expression string and make sure it didn't change */
		expr_str = ldb_filter_from_tree(ac, req->op.search.tree);
		if (expr_str == NULL) {
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = strcmp(current->expr_str, expr_str);
		if (ret != 0) {
			return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
		}

		bool_ret = paged_controls_same(req, current->down_controls);
		if (bool_ret == false) {
			return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
		}

		bool_ret = paged_attrs_same(req->op.search.attrs,
					    current->attrs);
		if (bool_ret == false) {
			return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
		}

		DLIST_PROMOTE(private_data->store, current);

		ac->store = current;

		/* check if it is an abandon */
		if (ac->size == 0) {
			return ldb_module_done(req, NULL, NULL,
								LDB_SUCCESS);
		}

		ret = paged_results(ac, NULL);
		if (ret != LDB_SUCCESS) {
			return ldb_module_done(req, NULL, NULL, ret);
		}
		return ldb_module_done(req, ac->controls, NULL, LDB_SUCCESS);
	}
}


static struct results_store *new_store(struct private_data *priv)
{
	struct results_store *store;
	int i;
	int best = 0;
	time_t oldest = TIME_T_MAX;
	for (i = 0; i < priv->n_stores; i++) {
		if (priv->store[i] == NULL) {
			best = i;
			break;
		} else if (priv->store[i]->timestamp < oldest){
			best = i;
			oldest = priv->store[i]->timestamp;
		}
	}

	store = talloc_zero(priv, struct results_store);
	if (store == NULL) {
		return NULL;
	}
	if (priv->store[best] != NULL) {
		TALLOC_FREE(priv->store[best]);
	}
	priv->store[best] = store;
	store->timestamp = time(NULL);
	return store;
}


AcceptTcpConnection(int listenSock)
{
  int sock;
  struct sockaddr_in addr;
  socklen_t addrlen = sizeof(addr);
  int one = 1;

  sock = accept(listenSock, (struct sockaddr *) &addr, &addrlen);
  if (sock < 0) {
    rfbClientErr("AcceptTcpConnection: accept\n");
    return -1;
  }

  if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
		 (char *)&one, sizeof(one)) < 0) {
    rfbClientErr("AcceptTcpConnection: setsockopt\n");
    close(sock);
    return -1;
  }

  return sock;
}


hybiDecodeCleanupBasics(ws_ctx_t *wsctx)
{
  /* keep opcode, cleanup rest */
  wsctx->header.opcode = WS_OPCODE_INVALID;
  wsctx->header.payloadLen = 0;
  wsctx->header.mask.u = 0;
  wsctx->header.headerLen = 0;
  wsctx->header.data = NULL;
  wsctx->header.nRead = 0;
  wsctx->nReadPayload = 0;
  wsctx->carrylen = 0;
  wsctx->readPos = (unsigned char *)wsctx->codeBufDecode;
  wsctx->readlen = 0;
  wsctx->hybiDecodeState = WS_HYBI_STATE_HEADER_PENDING;
  wsctx->writePos = NULL;
}


static int tls_close (CONNECTION* conn)
{
  int rc;

  rc = ssl_socket_close (conn);
  conn->conn_read = raw_socket_read;
  conn->conn_write = raw_socket_write;
  conn->conn_close = raw_socket_close;
  conn->conn_poll = raw_socket_poll;

  return rc;
}


void mutt_socket_free (CONNECTION* conn)
{
  CONNECTION* iter;
  CONNECTION* tmp;

  iter = Connections;

  /* head is special case, doesn't need prev updated */
  if (iter == conn)
  {
    Connections = iter->next;
    FREE (&iter);
    return;
  }

  while (iter->next)
  {
    if (iter->next == conn)
    {
      tmp = iter->next;
      iter->next = tmp->next;
      FREE (&tmp);
      return;
    }
    iter = iter->next;
  }
}


static int socket_preconnect (void)
{
  int rc;
  int save_errno;

  if (mutt_strlen (Preconnect))
  {
    dprint (2, (debugfile, "Executing preconnect: %s\n", Preconnect));
    rc = mutt_system (Preconnect);
    dprint (2, (debugfile, "Preconnect result: %d\n", rc));
    if (rc)
    {
      save_errno = errno;
      mutt_perror (_("Preconnect command failed."));
      mutt_sleep (1);

      return save_errno;
    }
  }

  return 0;
}


static int tls_check_certificate (CONNECTION* conn)
{
  tlssockdata *data = conn->sockdata;
  gnutls_session_t state = data->state;
  const gnutls_datum_t *cert_list;
  unsigned int cert_list_size = 0;
  gnutls_certificate_status_t certstat;
  int certerr, i, preauthrc, savedcert, rc = 0;
  int max_preauth_pass = -1;
  int rcsettrust;

  /* tls_verify_peers() calls gnutls_certificate_verify_peers2(),
   * which verifies the auth_type is GNUTLS_CRD_CERTIFICATE
   * and that get_certificate_type() for the server is GNUTLS_CRT_X509.
   * If it returns 0, certstat will be set with failure codes for the first
   * cert in the chain (from CA to host) with an error.
   */
  if (tls_verify_peers (state, &certstat) != 0)
    return 0;

  cert_list = gnutls_certificate_get_peers (state, &cert_list_size);
  if (!cert_list)
  {
    mutt_error (_("Unable to get certificate from peer"));
    mutt_sleep (2);
    return 0;
  }

  /* tls_verify_peers doesn't check hostname or expiration, so walk
   * from most specific to least checking these. If we see a saved certificate,
   * its status short-circuits the remaining checks. */
  preauthrc = 0;
  for (i = 0; i < cert_list_size; i++)
  {
    rc = tls_check_preauth(&cert_list[i], certstat, conn->account.host, i,
                           &certerr, &savedcert);
    preauthrc += rc;
    if (!preauthrc)
      max_preauth_pass = i;

    if (savedcert)
    {
      if (!preauthrc)
        return 1;
      else
        break;
    }
  }

  /* then check interactively, starting from chain root */
  for (i = cert_list_size - 1; i >= 0; i--)
  {
    rc = tls_check_one_certificate (&cert_list[i], certstat, conn->account.host,
                                    i, cert_list_size);

    /* Stop checking if the menu cert is aborted or rejected. */
    if (!rc)
      break;

    /* add signers to trust set, then reverify */
    if (i)
    {
      rcsettrust = gnutls_certificate_set_x509_trust_mem (data->xcred,
                                                          &cert_list[i],
                                                          GNUTLS_X509_FMT_DER);
      if (rcsettrust != 1)
        dprint (1, (debugfile, "error trusting certificate %d: %d\n", i, rcsettrust));

      if (tls_verify_peers (state, &certstat) != 0)
        return 0;

      /* If the cert chain now verifies, and all lower certs already
       * passed preauth, we are done.
       */
      if (!certstat && (max_preauth_pass >= i - 1))
        return 1;
    }
  }

  return rc;
}


CONNECTION* mutt_socket_head (void)
{
  return Connections;
}


int mutt_socket_close (CONNECTION* conn)
{
  int rc = -1;

  if (conn->fd < 0)
    dprint (1, (debugfile, "mutt_socket_close: Attempt to close closed connection.\n"));
  else
    rc = conn->conn_close (conn);

  conn->fd = -1;
  conn->ssf = 0;
  conn->bufpos = 0;
  conn->available = 0;

  return rc;
}


static int tls_check_preauth (const gnutls_datum_t *certdata,
                              gnutls_certificate_status_t certstat,
                              const char *hostname, int chainidx, int* certerr,
                              int* savedcert)
{
  gnutls_x509_crt_t cert;

  *certerr = CERTERR_VALID;
  *savedcert = 0;

  if (gnutls_x509_crt_init (&cert) < 0)
  {
    mutt_error (_("Error initialising gnutls certificate data"));
    mutt_sleep (2);
    return -1;
  }

  if (gnutls_x509_crt_import (cert, certdata, GNUTLS_X509_FMT_DER) < 0)
  {
    mutt_error (_("Error processing certificate data"));
    mutt_sleep (2);
    gnutls_x509_crt_deinit (cert);
    return -1;
  }

  /* Note: tls_negotiate() contains a call to
   * gnutls_certificate_set_verify_flags() with a flag disabling
   * GnuTLS checking of the dates.  So certstat shouldn't have the
   * GNUTLS_CERT_EXPIRED and GNUTLS_CERT_NOT_ACTIVATED bits set. */
  if (option (OPTSSLVERIFYDATES) != MUTT_NO)
  {
    if (gnutls_x509_crt_get_expiration_time (cert) < time(NULL))
      *certerr |= CERTERR_EXPIRED;
    if (gnutls_x509_crt_get_activation_time (cert) > time(NULL))
      *certerr |= CERTERR_NOTYETVALID;
  }

  if (chainidx == 0 && option (OPTSSLVERIFYHOST) != MUTT_NO
      && !gnutls_x509_crt_check_hostname (cert, hostname)
      && !tls_check_stored_hostname (certdata, hostname))
    *certerr |= CERTERR_HOSTNAME;

  if (certstat & GNUTLS_CERT_REVOKED)
  {
    *certerr |= CERTERR_REVOKED;
    certstat ^= GNUTLS_CERT_REVOKED;
  }

  /* see whether certificate is in our cache (certificates file) */
  if (tls_compare_certificates (certdata))
  {
    *savedcert = 1;

    /* We check above for certs with bad dates or that are revoked.
     * These must be accepted manually each time.  Otherwise, we
     * accept saved certificates as valid. */
    if (*certerr == CERTERR_VALID)
    {
      gnutls_x509_crt_deinit (cert);
      return 0;
    }
  }

  if (certstat & GNUTLS_CERT_INVALID)
  {
    *certerr |= CERTERR_NOTTRUSTED;
    certstat ^= GNUTLS_CERT_INVALID;
  }

  if (certstat & GNUTLS_CERT_SIGNER_NOT_FOUND)
  {
    /* NB: already cleared if cert in cache */
    *certerr |= CERTERR_NOTTRUSTED;
    certstat ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
  }

  if (certstat & GNUTLS_CERT_SIGNER_NOT_CA)
  {
    /* NB: already cleared if cert in cache */
    *certerr |= CERTERR_SIGNERNOTCA;
    certstat ^= GNUTLS_CERT_SIGNER_NOT_CA;
  }

  if (certstat & GNUTLS_CERT_INSECURE_ALGORITHM)
  {
    /* NB: already cleared if cert in cache */
    *certerr |= CERTERR_INSECUREALG;
    certstat ^= GNUTLS_CERT_INSECURE_ALGORITHM;
  }

  /* we've been zeroing the interesting bits in certstat -
   * don't return OK if there are any unhandled bits we don't
   * understand */
  if (certstat != 0)
    *certerr |= CERTERR_OTHER;

  gnutls_x509_crt_deinit (cert);

  if (*certerr == CERTERR_VALID)
    return 0;

  return -1;
}


static int ssl_socket_close (CONNECTION * conn)
{
  sslsockdata *data = conn->sockdata;
  if (data)
  {
    if (data->isopen)
      SSL_shutdown (data->ssl);

    /* hold onto this for the life of mutt, in case we want to reconnect.
     * The purist in me wants a mutt_exit hook. */
#if 0
    X509_free (data->cert);
#endif
    SSL_free (data->ssl);
    SSL_CTX_free (data->ctx);
    FREE (&conn->sockdata);
  }

  return raw_socket_close (conn);
}


static int tls_check_stored_hostname (const gnutls_datum_t *cert,
                                      const char *hostname)
{
  char buf[80];
  FILE *fp;
  char *linestr = NULL;
  size_t linestrsize;
  int linenum = 0;
  regex_t preg;
  regmatch_t pmatch[3];

  /* try checking against names stored in stored certs file */
  if ((fp = fopen (SslCertFile, "r")))
  {
    if (REGCOMP(&preg, "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
                REG_ICASE) != 0)
    {
      safe_fclose (&fp);
      return 0;
    }

    buf[0] = '\0';
    tls_fingerprint (GNUTLS_DIG_MD5, buf, sizeof (buf), cert);
    while ((linestr = mutt_read_line(linestr, &linestrsize, fp, &linenum, 0)) != NULL)
    {
      if (linestr[0] == '#' && linestr[1] == 'H')
      {
        if (regexec(&preg, linestr, 3, pmatch, 0) == 0)
        {
          linestr[pmatch[1].rm_eo] = '\0';
          linestr[pmatch[2].rm_eo] = '\0';
          if (strcmp(linestr + pmatch[1].rm_so, hostname) == 0 &&
              strcmp(linestr + pmatch[2].rm_so, buf) == 0)
          {
            regfree(&preg);
            FREE(&linestr);
            safe_fclose (&fp);
            return 1;
          }
        }
      }
    }

    regfree(&preg);
    safe_fclose (&fp);
  }

  /* not found a matching name */
  return 0;
}


int nntp_check_msgid(struct Mailbox *m, const char *msgid)
{
  if (!m)
    return -1;

  struct NntpMboxData *mdata = m->mdata;
  char buf[1024];

  FILE *fp = mutt_file_mkstemp();
  if (!fp)
  {
    mutt_perror(_("Can't create temporary file"));
    return -1;
  }

  snprintf(buf, sizeof(buf), "HEAD %s\r\n", msgid);
  int rc = nntp_fetch_lines(mdata, buf, sizeof(buf), NULL, fetch_tempfile, fp);
  if (rc)
  {
    mutt_file_fclose(&fp);
    if (rc < 0)
      return -1;
    if (mutt_str_startswith(buf, "430", CASE_MATCH))
      return 1;
    mutt_error("HEAD: %s", buf);
    return -1;
  }

  /* parse header */
  if (m->msg_count == m->email_max)
    mx_alloc_memory(m);
  m->emails[m->msg_count] = email_new();
  struct Email *e = m->emails[m->msg_count];
  e->edata = nntp_edata_new();
  e->edata_free = nntp_edata_free;
  e->env = mutt_rfc822_read_header(fp, e, false, false);
  mutt_file_fclose(&fp);

  /* get article number */
  if (e->env->xref)
    nntp_parse_xref(m, e);
  else
  {
    snprintf(buf, sizeof(buf), "STAT %s\r\n", msgid);
    if (nntp_query(mdata, buf, sizeof(buf)) < 0)
    {
      email_free(&e);
      return -1;
    }
    sscanf(buf + 4, ANUM, &nntp_edata_get(e)->article_num);
  }

  /* reset flags */
  e->read = false;
  e->old = false;
  e->deleted = false;
  e->changed = true;
  e->received = e->date_sent;
  e->index = m->msg_count++;
  mailbox_changed(m, NT_MAILBOX_INVALID);
  return 0;
}


static void imap_logout(struct ImapAccountData *adata)
{
  /* we set status here to let imap_handle_untagged know we _expect_ to
   * receive a bye response (so it doesn't freak out and close the conn) */
  if (adata->state == IMAP_DISCONNECTED)
  {
    return;
  }

  adata->status = IMAP_BYE;
  imap_cmd_start(adata, "LOGOUT");
  if ((C_ImapPollTimeout <= 0) || (mutt_socket_poll(adata->conn, C_ImapPollTimeout) != 0))
  {
    while (imap_cmd_step(adata) == IMAP_RES_CONTINUE)
      ; // do nothing
  }
  mutt_socket_close(adata->conn);
  adata->state = IMAP_DISCONNECTED;
}


static int imap_ac_add(struct Account *a, struct Mailbox *m)
{
  if (!a || !m || (m->type != MUTT_IMAP))
    return -1;

  struct ImapAccountData *adata = a->adata;

  if (!adata)
  {
    struct ConnAccount cac = { { 0 } };
    char mailbox[PATH_MAX];

    if (imap_parse_path(mailbox_path(m), &cac, mailbox, sizeof(mailbox)) < 0)
      return -1;

    adata = imap_adata_new(a);
    adata->conn = mutt_conn_new(&cac);
    if (!adata->conn)
    {
      imap_adata_free((void **) &adata);
      return -1;
    }

    mutt_account_hook(m->realpath);

    if (imap_login(adata) < 0)
    {
      imap_adata_free((void **) &adata);
      return -1;
    }

    a->adata = adata;
    a->adata_free = imap_adata_free;
  }

  if (!m->mdata)
  {
    struct Url *url = url_parse(mailbox_path(m));
    struct ImapMboxData *mdata = imap_mdata_new(adata, url->path);

    /* fixup path and realpath, mainly to replace / by /INBOX */
    char buf[1024];
    imap_qualify_path(buf, sizeof(buf), &adata->conn->account, mdata->name);
    mutt_buffer_strcpy(&m->pathbuf, buf);
    mutt_str_replace(&m->realpath, mailbox_path(m));

    m->mdata = mdata;
    m->mdata_free = imap_mdata_free;
    url_free(&url);
  }
  return 0;
}


static int nntp_date(struct NntpAccountData *adata, time_t *now)
{
  if (adata->hasDATE)
  {
    struct NntpMboxData mdata = { 0 };
    char buf[1024];
    struct tm tm = { 0 };

    mdata.adata = adata;
    mdata.group = NULL;
    mutt_str_strfcpy(buf, "DATE\r\n", sizeof(buf));
    if (nntp_query(&mdata, buf, sizeof(buf)) < 0)
      return -1;

    if (sscanf(buf, "111 %4d%2d%2d%2d%2d%2d%*s", &tm.tm_year, &tm.tm_mon,
               &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6)
    {
      tm.tm_year -= 1900;
      tm.tm_mon--;
      *now = timegm(&tm);
      if (*now >= 0)
      {
        mutt_debug(LL_DEBUG1, "server time is %lu\n", *now);
        return 0;
      }
    }
  }
  *now = mutt_date_epoch();
  return 0;
}


static int imap_mbox_close(struct Mailbox *m)
{
  if (!m)
    return -1;

  struct ImapAccountData *adata = imap_adata_get(m);
  struct ImapMboxData *mdata = imap_mdata_get(m);

  /* Check to see if the mailbox is actually open */
  if (!adata || !mdata)
    return 0;

  /* imap_mbox_open_append() borrows the struct ImapAccountData temporarily,
   * just for the connection.
   *
   * So when these are equal, it means we are actually closing the
   * mailbox and should clean up adata.  Otherwise, we don't want to
   * touch adata - it's still being used.  */
  if (m == adata->mailbox)
  {
    if ((adata->status != IMAP_FATAL) && (adata->state >= IMAP_SELECTED))
    {
      /* mx_mbox_close won't sync if there are no deleted messages
       * and the mailbox is unchanged, so we may have to close here */
      if (m->msg_deleted == 0)
      {
        adata->closing = true;
        imap_exec(adata, "CLOSE", IMAP_CMD_QUEUE);
      }
      adata->state = IMAP_AUTHENTICATED;
    }

    mutt_debug(LL_DEBUG3, "closing %s, restoring %s\n", m->pathbuf.data,
               (adata->prev_mailbox ? adata->prev_mailbox->pathbuf.data : "(none)"));
    adata->mailbox = adata->prev_mailbox;
    imap_mbox_select(adata->prev_mailbox);
    imap_mdata_cache_reset(m->mdata);
  }

  return 0;
}


static bool cmd_queue_full(struct ImapAccountData *adata)
{
  if (((adata->nextcmd + 1) % adata->cmdslots) == adata->lastcmd)
    return true;

  return false;
}


static int nntp_group_poll(struct NntpMboxData *mdata, bool update_stat)
{
  char buf[1024] = { 0 };
  anum_t count, first, last;

  /* use GROUP command to poll newsgroup */
  if (nntp_query(mdata, buf, sizeof(buf)) < 0)
    return -1;
  if (sscanf(buf, "211 " ANUM " " ANUM " " ANUM, &count, &first, &last) != 3)
    return 0;
  if ((first == mdata->first_message) && (last == mdata->last_message))
    return 0;

  /* articles have been renumbered */
  if (last < mdata->last_message)
  {
    mdata->last_cached = 0;
    if (mdata->newsrc_len)
    {
      mutt_mem_realloc(&mdata->newsrc_ent, sizeof(struct NewsrcEntry));
      mdata->newsrc_len = 1;
      mdata->newsrc_ent[0].first = 1;
      mdata->newsrc_ent[0].last = 0;
    }
  }
  mdata->first_message = first;
  mdata->last_message = last;
  if (!update_stat)
    return 1;

  /* update counters */
  else if (!last || (!mdata->newsrc_ent && !mdata->last_cached))
    mdata->unread = count;
  else
    nntp_group_unread_stat(mdata);
  return 1;
}