virDomainSmartcardDefParseXML(virDomainXMLOptionPtr xmlopt,
                              xmlNodePtr node,
                              xmlXPathContextPtr ctxt,
                              unsigned int flags)
{
    xmlNodePtr cur;
    virDomainSmartcardDefPtr def;
    size_t i;
    g_autofree char *mode = NULL;
    g_autofree char *type = NULL;

    if (VIR_ALLOC(def) < 0)
        return NULL;

    mode = virXMLPropString(node, "mode");
    if (mode == NULL) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("missing smartcard device mode"));
        goto error;
    }
    if ((def->type = virDomainSmartcardTypeFromString(mode)) < 0) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("unknown smartcard device mode: %s"),
                       mode);
        goto error;
    }

    switch (def->type) {
    case VIR_DOMAIN_SMARTCARD_TYPE_HOST:
        break;

    case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES:
        i = 0;
        cur = node->children;
        while (cur) {
            if (cur->type == XML_ELEMENT_NODE &&
                virXMLNodeNameEqual(cur, "certificate")) {
                if (i == 3) {
                    virReportError(VIR_ERR_XML_ERROR, "%s",
                                   _("host-certificates mode needs "
                                     "exactly three certificates"));
                    goto error;
                }
                def->data.cert.file[i] = (char *)xmlNodeGetContent(cur);
                if (!def->data.cert.file[i]) {
                    virReportOOMError();
                    goto error;
                }
                i++;
            } else if (cur->type == XML_ELEMENT_NODE &&
                       virXMLNodeNameEqual(cur, "database") &&
                       !def->data.cert.database) {
                def->data.cert.database = (char *)xmlNodeGetContent(cur);
                if (!def->data.cert.database) {
                    virReportOOMError();
                    goto error;
                }
                if (*def->data.cert.database != '/') {
                    virReportError(VIR_ERR_XML_ERROR,
                                   _("expecting absolute path: %s"),
                                   def->data.cert.database);
                    goto error;
                }
            }
            cur = cur->next;
        }
        if (i < 3) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("host-certificates mode needs "
                             "exactly three certificates"));
            goto error;
        }
        break;

    case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH:
        type = virXMLPropString(node, "type");
        if (type == NULL) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("passthrough mode requires a character "
                             "device type attribute"));
            goto error;
        }

        if (!(def->data.passthru = virDomainChrSourceDefNew(xmlopt)))
            goto error;

        if ((def->data.passthru->type = virDomainChrTypeFromString(type)) < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown type presented to host for "
                             "character device: %s"), type);
            goto error;
        }

        cur = node->children;
        if (virDomainChrSourceDefParseXML(def->data.passthru, cur, flags,
                                          NULL, ctxt) < 0)
            goto error;

        if (def->data.passthru->type == VIR_DOMAIN_CHR_TYPE_SPICEVMC) {
            def->data.passthru->data.spicevmc
                = VIR_DOMAIN_CHR_SPICEVMC_SMARTCARD;
        }

        break;

    default:
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("unknown smartcard mode"));
        goto error;
    }

    if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0)
        goto error;
    if (def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
        def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Controllers must use the 'ccid' address type"));
        goto error;
    }

    return def;

 error:
    virDomainSmartcardDefFree(def);
    return NULL;
}