int megasas_sriov_start_heartbeat(struct megasas_instance *instance,
					 int initial)
{
	struct megasas_cmd *cmd;
	struct megasas_dcmd_frame *dcmd;
	int retval = 0;

	cmd = megasas_get_cmd(instance);

	if (!cmd) {
		dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_sriov_start_heartbeat: "
		       "Failed to get cmd for scsi%d\n",
		       instance->host->host_no);
		return -ENOMEM;
	}

	dcmd = &cmd->frame->dcmd;

	if (initial) {
		instance->hb_host_mem =
			dma_zalloc_coherent(&instance->pdev->dev,
					      sizeof(struct MR_CTRL_HB_HOST_MEM),
					      &instance->hb_host_mem_h, GFP_KERNEL);
		if (!instance->hb_host_mem) {
			dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate"
			       " memory for heartbeat host memory for scsi%d\n",
			       instance->host->host_no);
			retval = -ENOMEM;
			goto out;
		}
	}

	memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE);

	dcmd->mbox.s[0] = cpu_to_le16(sizeof(struct MR_CTRL_HB_HOST_MEM));
	dcmd->cmd = MFI_CMD_DCMD;
	dcmd->cmd_status = MFI_STAT_INVALID_STATUS;
	dcmd->sge_count = 1;
	dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH);
	dcmd->timeout = 0;
	dcmd->pad_0 = 0;
	dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_CTRL_HB_HOST_MEM));
	dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC);

	megasas_set_dma_settings(instance, dcmd, instance->hb_host_mem_h,
				 sizeof(struct MR_CTRL_HB_HOST_MEM));

	dev_warn(&instance->pdev->dev, "SR-IOV: Starting heartbeat for scsi%d\n",
	       instance->host->host_no);

	if ((instance->adapter_type != MFI_SERIES) &&
	    !instance->mask_interrupts)
		retval = megasas_issue_blocked_cmd(instance, cmd,
			MEGASAS_ROUTINE_WAIT_TIME_VF);
	else
		retval = megasas_issue_polled(instance, cmd);

	if (retval) {
		dev_warn(&instance->pdev->dev, "SR-IOV: MR_DCMD_CTRL_SHARED_HOST"
			"_MEM_ALLOC DCMD %s for scsi%d\n",
			(dcmd->cmd_status == MFI_STAT_INVALID_STATUS) ?
			"timed out" : "failed", instance->host->host_no);
		retval = 1;
	}

out:
	megasas_return_cmd(instance, cmd);

	return retval;
}


static int megasas_create_frame_pool(struct megasas_instance *instance)
{
	int i;
	u16 max_cmd;
	u32 sge_sz;
	u32 frame_count;
	struct megasas_cmd *cmd;

	max_cmd = instance->max_mfi_cmds;

	/*
	 * Size of our frame is 64 bytes for MFI frame, followed by max SG
	 * elements and finally SCSI_SENSE_BUFFERSIZE bytes for sense buffer
	 */
	sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) :
	    sizeof(struct megasas_sge32);

	if (instance->flag_ieee)
		sge_sz = sizeof(struct megasas_sge_skinny);

	/*
	 * For MFI controllers.
	 * max_num_sge = 60
	 * max_sge_sz  = 16 byte (sizeof megasas_sge_skinny)
	 * Total 960 byte (15 MFI frame of 64 byte)
	 *
	 * Fusion adapter require only 3 extra frame.
	 * max_num_sge = 16 (defined as MAX_IOCTL_SGE)
	 * max_sge_sz  = 12 byte (sizeof  megasas_sge64)
	 * Total 192 byte (3 MFI frame of 64 byte)
	 */
	frame_count = (instance->adapter_type == MFI_SERIES) ?
			(15 + 1) : (3 + 1);
	instance->mfi_frame_size = MEGAMFI_FRAME_SIZE * frame_count;
	/*
	 * Use DMA pool facility provided by PCI layer
	 */
	instance->frame_dma_pool = dma_pool_create("megasas frame pool",
					&instance->pdev->dev,
					instance->mfi_frame_size, 256, 0);

	if (!instance->frame_dma_pool) {
		dev_printk(KERN_DEBUG, &instance->pdev->dev, "failed to setup frame pool\n");
		return -ENOMEM;
	}

	instance->sense_dma_pool = dma_pool_create("megasas sense pool",
						   &instance->pdev->dev, 128,
						   4, 0);

	if (!instance->sense_dma_pool) {
		dev_printk(KERN_DEBUG, &instance->pdev->dev, "failed to setup sense pool\n");

		dma_pool_destroy(instance->frame_dma_pool);
		instance->frame_dma_pool = NULL;

		return -ENOMEM;
	}

	/*
	 * Allocate and attach a frame to each of the commands in cmd_list.
	 * By making cmd->index as the context instead of the &cmd, we can
	 * always use 32bit context regardless of the architecture
	 */
	for (i = 0; i < max_cmd; i++) {

		cmd = instance->cmd_list[i];

		cmd->frame = dma_pool_zalloc(instance->frame_dma_pool,
					    GFP_KERNEL, &cmd->frame_phys_addr);

		cmd->sense = dma_pool_alloc(instance->sense_dma_pool,
					    GFP_KERNEL, &cmd->sense_phys_addr);

		/*
		 * megasas_teardown_frame_pool() takes care of freeing
		 * whatever has been allocated
		 */
		if (!cmd->frame || !cmd->sense) {
			dev_printk(KERN_DEBUG, &instance->pdev->dev, "dma_pool_alloc failed\n");
			megasas_teardown_frame_pool(instance);
			return -ENOMEM;
		}

		cmd->frame->io.context = cpu_to_le32(cmd->index);
		cmd->frame->io.pad_0 = 0;
		if ((instance->adapter_type == MFI_SERIES) && reset_devices)
			cmd->frame->hdr.cmd = MFI_CMD_INVALID;
	}

	return 0;
}


megasas_issue_pending_cmds_again(struct megasas_instance *instance)
{
	struct megasas_cmd *cmd;
	struct list_head clist_local;
	union megasas_evt_class_locale class_locale;
	unsigned long flags;
	u32 seq_num;

	INIT_LIST_HEAD(&clist_local);
	spin_lock_irqsave(&instance->hba_lock, flags);
	list_splice_init(&instance->internal_reset_pending_q, &clist_local);
	spin_unlock_irqrestore(&instance->hba_lock, flags);

	while (!list_empty(&clist_local)) {
		cmd = list_entry((&clist_local)->next,
					struct megasas_cmd, list);
		list_del_init(&cmd->list);

		if (cmd->sync_cmd || cmd->scmd) {
			dev_notice(&instance->pdev->dev, "command %p, %p:%d"
				"detected to be pending while HBA reset\n",
					cmd, cmd->scmd, cmd->sync_cmd);

			cmd->retry_for_fw_reset++;

			if (cmd->retry_for_fw_reset == 3) {
				dev_notice(&instance->pdev->dev, "cmd %p, %p:%d"
					"was tried multiple times during reset."
					"Shutting down the HBA\n",
					cmd, cmd->scmd, cmd->sync_cmd);
				instance->instancet->disable_intr(instance);
				atomic_set(&instance->fw_reset_no_pci_access, 1);
				megaraid_sas_kill_hba(instance);
				return;
			}
		}

		if (cmd->sync_cmd == 1) {
			if (cmd->scmd) {
				dev_notice(&instance->pdev->dev, "unexpected"
					"cmd attached to internal command!\n");
			}
			dev_notice(&instance->pdev->dev, "%p synchronous cmd"
						"on the internal reset queue,"
						"issue it again.\n", cmd);
			cmd->cmd_status_drv = MFI_STAT_INVALID_STATUS;
			instance->instancet->fire_cmd(instance,
							cmd->frame_phys_addr,
							0, instance->reg_set);
		} else if (cmd->scmd) {
			dev_notice(&instance->pdev->dev, "%p scsi cmd [%02x]"
			"detected on the internal queue, issue again.\n",
			cmd, cmd->scmd->cmnd[0]);

			atomic_inc(&instance->fw_outstanding);
			instance->instancet->fire_cmd(instance,
					cmd->frame_phys_addr,
					cmd->frame_count-1, instance->reg_set);
		} else {
			dev_notice(&instance->pdev->dev, "%p unexpected cmd on the"
				"internal reset defer list while re-issue!!\n",
				cmd);
		}
	}

	if (instance->aen_cmd) {
		dev_notice(&instance->pdev->dev, "aen_cmd in def process\n");
		megasas_return_cmd(instance, instance->aen_cmd);

		instance->aen_cmd = NULL;
	}

	/*
	 * Initiate AEN (Asynchronous Event Notification)
	 */
	seq_num = instance->last_seq_num;
	class_locale.members.reserved = 0;
	class_locale.members.locale = MR_EVT_LOCALE_ALL;
	class_locale.members.class = MR_EVT_CLASS_DEBUG;

	megasas_register_aen(instance, seq_num, class_locale.word);
}


setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info)
{
	struct ipmi_device_id *id = &smi_info->device_id;
	if (id->manufacturer_id == DELL_IANA_MFR_ID &&
	    smi_info->io.si_type == SI_BT)
		register_xaction_notifier(&dell_poweredge_bt_xaction_notifier);
}


static inline int ipmi_si_is_busy(struct timespec64 *ts)
{
	return ts->tv_nsec != -1;
}


static unsigned char port_inl(const struct si_sm_io *io, unsigned int offset)
{
	unsigned int addr = io->addr_data;

	return (inl(addr + (offset * io->regspacing)) >> io->regshift) & 0xff;
}


static void mem_region_cleanup(struct si_sm_io *io, int num)
{
	unsigned long addr = io->addr_data;
	int idx;

	for (idx = 0; idx < num; idx++)
		release_mem_region(addr + idx * io->regspacing,
				   io->regsize);
}


static void mem_outq(const struct si_sm_io *io, unsigned int offset,
		     unsigned char b)
{
	writeq((u64)b << io->regshift, (io->addr)+(offset * io->regspacing));
}


static void check_bt_irq(struct smi_info *smi_info, bool irq_on)
{
	u8 irqstate = smi_info->io.inputb(&smi_info->io, IPMI_BT_INTMASK_REG);

	irqstate &= IPMI_BT_INTMASK_ENABLE_IRQ_BIT;

	if ((bool)irqstate == irq_on)
		return;

	if (irq_on)
		smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG,
				     IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
	else
		smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG, 0);
}


static inline bool disable_si_irq(struct smi_info *smi_info)
{
	if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) {
		smi_info->interrupt_disabled = true;
		start_check_enables(smi_info);
		return true;
	}
	return false;
}


static unsigned char intf_mem_inw(const struct si_sm_io *io,
				  unsigned int offset)
{
	return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift)
		& 0xff;
}


xsltCompilerNodePop(xsltCompilerCtxtPtr cctxt, xmlNodePtr node)
{
    if (cctxt->inode == NULL) {
	xmlGenericError(xmlGenericErrorContext,
	    "xsltCompilerNodePop: Top-node mismatch.\n");
	return;
    }
    /*
    * NOTE: Be carefull with the @node, since it might be
    *  a doc-node.
    */
    if (cctxt->inode->node != node) {
	xmlGenericError(xmlGenericErrorContext,
	"xsltCompilerNodePop: Node mismatch.\n");
	goto mismatch;
    }
    if (cctxt->inode->depth != cctxt->depth) {
	xmlGenericError(xmlGenericErrorContext,
	"xsltCompilerNodePop: Depth mismatch.\n");
	goto mismatch;
    }
    cctxt->depth--;
    /*
    * Pop information of variables.
    */
    if ((cctxt->ivar) && (cctxt->ivar->depth > cctxt->depth))
	xsltCompilerVarInfoPop(cctxt);

    cctxt->inode = cctxt->inode->prev;
    if (cctxt->inode != NULL)
	cctxt->inode->curChildType = 0;
    return;

mismatch:
    {
	const xmlChar *nsName = NULL, *name = NULL;
	const xmlChar *infnsName = NULL, *infname = NULL;

	if (node) {
	    if (node->type == XML_ELEMENT_NODE) {
		name = node->name;
		if (node->ns != NULL)
		    nsName = node->ns->href;
		else
		    nsName = BAD_CAST "";
	    } else {
		name = BAD_CAST "#document";
		nsName = BAD_CAST "";
	    }
	} else
	    name = BAD_CAST "Not given";

	if (cctxt->inode->node) {
	    if (node->type == XML_ELEMENT_NODE) {
		infname = cctxt->inode->node->name;
		if (cctxt->inode->node->ns != NULL)
		    infnsName = cctxt->inode->node->ns->href;
		else
		    infnsName = BAD_CAST "";
	    } else {
		infname = BAD_CAST "#document";
		infnsName = BAD_CAST "";
	    }
	} else
	    infname = BAD_CAST "Not given";

	xmlGenericError(xmlGenericErrorContext,
	    "xsltCompilerNodePop: Given   : '%s' URI '%s'\n",
	    name, nsName);
	xmlGenericError(xmlGenericErrorContext,
	    "xsltCompilerNodePop: Expected: '%s' URI '%s'\n",
	    infname, infnsName);
    }
}


xsltLREBuildEffectiveNs(xsltCompilerCtxtPtr cctxt,
			xmlNodePtr elem)
{
    xmlNsPtr ns;
    xsltNsAliasPtr alias;

    if ((cctxt == NULL) || (elem == NULL))
	return(-1);
    if ((cctxt->nsAliases == NULL) || (! cctxt->hasNsAliases))
	return(0);

    alias = cctxt->nsAliases;
    while (alias != NULL) {
	if ( /* If both namespaces are NULL... */
	    ( (elem->ns == NULL) &&
	    ((alias->literalNs == NULL) ||
	    (alias->literalNs->href == NULL)) ) ||
	    /* ... or both namespace are equal */
	    ( (elem->ns != NULL) &&
	    (alias->literalNs != NULL) &&
	    xmlStrEqual(elem->ns->href, alias->literalNs->href) ) )
	{
	    if ((alias->targetNs != NULL) &&
		(alias->targetNs->href != NULL))
	    {
		/*
		* Convert namespace.
		*/
		if (elem->doc == alias->docOfTargetNs) {
		    /*
		    * This is the nice case: same docs.
		    * This will eventually assign a ns-decl which
		    * is shadowed, but this has no negative effect on
		    * the generation of the result tree.
		    */
		    elem->ns = alias->targetNs;
		} else {
		    /*
		    * This target xmlNs originates from a different
		    * stylesheet tree. Try to locate it in the
		    * in-scope namespaces.
		    * OPTIMIZE TODO: Use the compiler-node-info inScopeNs.
		    */
		    ns = xmlSearchNs(elem->doc, elem,
			alias->targetNs->prefix);
		    /*
		    * If no matching ns-decl found, then assign a
		    * ns-decl stored in xmlDoc.
		    */
		    if ((ns == NULL) ||
			(! xmlStrEqual(ns->href, alias->targetNs->href)))
		    {
			/*
			* BIG NOTE: The use of xsltTreeAcquireStoredNs()
			*  is not very efficient, but currently I don't
			*  see an other way of *safely* changing a node's
			*  namespace, since the xmlNs struct in
			*  alias->targetNs might come from an other
			*  stylesheet tree. So we need to anchor it in the
			*  current document, without adding it to the tree,
			*  which would otherwise change the in-scope-ns
			*  semantic of the tree.
			*/
			ns = xsltTreeAcquireStoredNs(elem->doc,
			    alias->targetNs->href,
			    alias->targetNs->prefix);

			if (ns == NULL) {
			    xsltTransformError(NULL, cctxt->style, elem,
				"Internal error in "
				"xsltLREBuildEffectiveNs(): "
				"failed to acquire a stored "
				"ns-declaration.\n");
			    cctxt->style->errors++;
			    return(-1);

			}
		    }
		    elem->ns = ns;
		}
	    } else {
		/*
		* Move into or leave in the NULL namespace.
		*/
		elem->ns = NULL;
	    }
	    break;
	}
	alias = alias->next;
    }
    /*
    * Same with attributes of literal result elements.
    */
    if (elem->properties != NULL) {
	xmlAttrPtr attr = elem->properties;

	while (attr != NULL) {
	    if (attr->ns == NULL) {
		attr = attr->next;
		continue;
	    }
	    alias = cctxt->nsAliases;
	    while (alias != NULL) {
		if ( /* If both namespaces are NULL... */
		    ( (elem->ns == NULL) &&
		    ((alias->literalNs == NULL) ||
		    (alias->literalNs->href == NULL)) ) ||
		    /* ... or both namespace are equal */
		    ( (elem->ns != NULL) &&
		    (alias->literalNs != NULL) &&
		    xmlStrEqual(elem->ns->href, alias->literalNs->href) ) )
		{
		    if ((alias->targetNs != NULL) &&
			(alias->targetNs->href != NULL))
		    {
			if (elem->doc == alias->docOfTargetNs) {
			    elem->ns = alias->targetNs;
			} else {
			    ns = xmlSearchNs(elem->doc, elem,
				alias->targetNs->prefix);
			    if ((ns == NULL) ||
				(! xmlStrEqual(ns->href, alias->targetNs->href)))
			    {
				ns = xsltTreeAcquireStoredNs(elem->doc,
				    alias->targetNs->href,
				    alias->targetNs->prefix);

				if (ns == NULL) {
				    xsltTransformError(NULL, cctxt->style, elem,
					"Internal error in "
					"xsltLREBuildEffectiveNs(): "
					"failed to acquire a stored "
					"ns-declaration.\n");
				    cctxt->style->errors++;
				    return(-1);

				}
			    }
			    elem->ns = ns;
			}
		    } else {
		    /*
		    * Move into or leave in the NULL namespace.
			*/
			elem->ns = NULL;
		    }
		    break;
		}
		alias = alias->next;
	    }

	    attr = attr->next;
	}
    }
    return(0);
}


xsltParseXSLTTemplate(xsltCompilerCtxtPtr cctxt, xmlNodePtr templNode) {
    xsltTemplatePtr templ;
    xmlChar *prop;
    double  priority;

    if ((cctxt == NULL) || (templNode == NULL) ||
        (templNode->type != XML_ELEMENT_NODE))
	return;

    /*
     * Create and link the structure
     */
    templ = xsltNewTemplate();
    if (templ == NULL)
	return;

    xsltCompilerNodePush(cctxt, templNode);
    if (templNode->nsDef != NULL)
	cctxt->inode->inScopeNs =
	    xsltCompilerBuildInScopeNsList(cctxt, templNode);

    templ->next = cctxt->style->templates;
    cctxt->style->templates = templ;
    templ->style = cctxt->style;

    /*
    * Attribute "mode".
    */
    prop = xmlGetNsProp(templNode, (const xmlChar *)"mode", NULL);
    if (prop != NULL) {
        const xmlChar *modeURI;

	/*
	* TODO: We need a standardized function for extraction
	*  of namespace names and local names from QNames.
	*  Don't use xsltGetQNameURI() as it cannot channe�
	*  reports through the context.
	*/
	modeURI = xsltGetQNameURI(templNode, &prop);
	if (prop == NULL) {
	    cctxt->style->errors++;
	    goto error;
	}
	templ->mode = xmlDictLookup(cctxt->style->dict, prop, -1);
	xmlFree(prop);
	prop = NULL;
	if (xmlValidateNCName(templ->mode, 0)) {
	    xsltTransformError(NULL, cctxt->style, templNode,
		"xsl:template: Attribute 'mode': The local part '%s' "
		"of the value is not a valid NCName.\n", templ->name);
	    cctxt->style->errors++;
	    goto error;
	}
	if (modeURI != NULL)
	    templ->modeURI = xmlDictLookup(cctxt->style->dict, modeURI, -1);
#ifdef WITH_XSLT_DEBUG_PARSING
	xsltGenericDebug(xsltGenericDebugContext,
	     "xsltParseXSLTTemplate: mode %s\n", templ->mode);
#endif
    }
    /*
    * Attribute "match".
    */
    prop = xmlGetNsProp(templNode, (const xmlChar *)"match", NULL);
    if (prop != NULL) {
	templ->match  = prop;
	prop = NULL;
    }
    /*
    * Attribute "priority".
    */
    prop = xmlGetNsProp(templNode, (const xmlChar *)"priority", NULL);
    if (prop != NULL) {
	priority = xmlXPathStringEvalNumber(prop);
	templ->priority = (float) priority;
	xmlFree(prop);
	prop = NULL;
    }
    /*
    * Attribute "name".
    */
    prop = xmlGetNsProp(templNode, (const xmlChar *)"name", NULL);
    if (prop != NULL) {
        const xmlChar *nameURI;
	xsltTemplatePtr curTempl;

	/*
	* TODO: Don't use xsltGetQNameURI().
	*/
	nameURI = xsltGetQNameURI(templNode, &prop);
	if (prop == NULL) {
	    cctxt->style->errors++;
	    goto error;
	}
	templ->name = xmlDictLookup(cctxt->style->dict, prop, -1);
	xmlFree(prop);
	prop = NULL;
	if (xmlValidateNCName(templ->name, 0)) {
	    xsltTransformError(NULL, cctxt->style, templNode,
		"xsl:template: Attribute 'name': The local part '%s' of "
		"the value is not a valid NCName.\n", templ->name);
	    cctxt->style->errors++;
	    goto error;
	}
	if (nameURI != NULL)
	    templ->nameURI = xmlDictLookup(cctxt->style->dict, nameURI, -1);
	curTempl = templ->next;
	while (curTempl != NULL) {
	    if ((nameURI != NULL && xmlStrEqual(curTempl->name, templ->name) &&
		xmlStrEqual(curTempl->nameURI, nameURI) ) ||
		(nameURI == NULL && curTempl->nameURI == NULL &&
		xmlStrEqual(curTempl->name, templ->name)))
	    {
		xsltTransformError(NULL, cctxt->style, templNode,
		    "xsl:template: error duplicate name '%s'\n", templ->name);
		cctxt->style->errors++;
		goto error;
	    }
	    curTempl = curTempl->next;
	}
    }
    if (templNode->children != NULL) {
	xsltParseTemplateContent(cctxt->style, templNode);
	/*
	* MAYBE TODO: Custom behaviour: In order to stay compatible with
	* Xalan and MSXML(.NET), we could allow whitespace
	* to appear before an xml:param element; this whitespace
	* will additionally become part of the "template".
	* NOTE that this is totally deviates from the spec, but
	* is the de facto behaviour of Xalan and MSXML(.NET).
	* Personally I wouldn't allow this, since if we have:
	* <xsl:template ...xml:space="preserve">
	*   <xsl:param name="foo"/>
	*   <xsl:param name="bar"/>
	*   <xsl:param name="zoo"/>
	* ... the whitespace between every xsl:param would be
	* added to the result tree.
	*/
    }

    templ->elem = templNode;
    templ->content = templNode->children;
    xsltAddTemplate(cctxt->style, templ, templ->mode, templ->modeURI);

error:
    xsltCompilerNodePop(cctxt, templNode);
    return;
}


xsltLREInfoCreate(xsltCompilerCtxtPtr cctxt,
		  xmlNodePtr elem,
		  int isLRE)
{
    xsltStyleItemLRElementInfoPtr item;

    if ((cctxt == NULL) || (cctxt->inode == NULL))
	return(-1);

    item = (xsltStyleItemLRElementInfoPtr)
	xmlMalloc(sizeof(xsltStyleItemLRElementInfo));
    if (item == NULL) {
	xsltTransformError(NULL, cctxt->style, NULL,
	    "Internal error in xsltLREInfoCreate(): "
	    "memory allocation failed.\n");
	cctxt->style->errors++;
	return(-1);
    }
    memset(item, 0, sizeof(xsltStyleItemLRElementInfo));
    item->type = XSLT_FUNC_LITERAL_RESULT_ELEMENT;
    /*
    * Store it in the stylesheet.
    */
    item->next = cctxt->style->preComps;
    cctxt->style->preComps = (xsltElemPreCompPtr) item;
    /*
    * @inScopeNs are used for execution of XPath expressions
    *  in AVTs.
    */
    item->inScopeNs = cctxt->inode->inScopeNs;

    if (elem)
	xsltLREBuildEffectiveNsNodes(cctxt, item, elem, isLRE);

    cctxt->inode->litResElemInfo = item;
    cctxt->inode->nsChanged = 0;
    cctxt->maxLREs++;
    return(0);
}


xsltAllocateExtraCtxt(xsltTransformContextPtr ctxt)
{
    if (ctxt->extrasNr >= ctxt->extrasMax) {
	int i;
	if (ctxt->extrasNr == 0) {
	    ctxt->extrasMax = 20;
	    ctxt->extras = (xsltRuntimeExtraPtr)
		xmlMalloc(ctxt->extrasMax * sizeof(xsltRuntimeExtra));
	    if (ctxt->extras == NULL) {
		xsltTransformError(ctxt, NULL, NULL,
			"xsltAllocateExtraCtxt: out of memory\n");
		return(0);
	    }
	    for (i = 0;i < ctxt->extrasMax;i++) {
		ctxt->extras[i].info = NULL;
		ctxt->extras[i].deallocate = NULL;
		ctxt->extras[i].val.ptr = NULL;
	    }

	} else {
	    xsltRuntimeExtraPtr tmp;

	    ctxt->extrasMax += 100;
	    tmp = (xsltRuntimeExtraPtr) xmlRealloc(ctxt->extras,
		            ctxt->extrasMax * sizeof(xsltRuntimeExtra));
	    if (tmp == NULL) {
		xsltTransformError(ctxt, NULL, NULL,
			"xsltAllocateExtraCtxt: out of memory\n");
		return(0);
	    }
	    ctxt->extras = tmp;
	    for (i = ctxt->extrasNr;i < ctxt->extrasMax;i++) {
		ctxt->extras[i].info = NULL;
		ctxt->extras[i].deallocate = NULL;
		ctxt->extras[i].val.ptr = NULL;
	    }
	}
    }
    return(ctxt->extrasNr++);
}


xsltLREBuildEffectiveNsNodes(xsltCompilerCtxtPtr cctxt,
			     xsltStyleItemLRElementInfoPtr item,
			     xmlNodePtr elem,
			     int isLRE)
{
    xmlNsPtr ns, tmpns;
    xsltEffectiveNsPtr effNs, lastEffNs = NULL;
    int i, j, holdByElem;
    xsltPointerListPtr extElemNs = cctxt->inode->extElemNs;
    xsltPointerListPtr exclResultNs = cctxt->inode->exclResultNs;

    if ((cctxt == NULL) || (cctxt->inode == NULL) || (elem == NULL) ||
	(item == NULL) || (item->effectiveNs != NULL))
	return(-1);

    if (item->inScopeNs == NULL)
	return(0);

    extElemNs = cctxt->inode->extElemNs;
    exclResultNs = cctxt->inode->exclResultNs;

    for (i = 0; i < item->inScopeNs->totalNumber; i++) {
	ns = item->inScopeNs->list[i];
	/*
	* Skip namespaces designated as excluded namespaces
	* -------------------------------------------------
	*
	* XSLT-20 TODO: In XSLT 2.0 we need to keep namespaces
	*  which are target namespaces of namespace-aliases
	*  regardless if designated as excluded.
	*
	* Exclude the XSLT namespace.
	*/
	if (xmlStrEqual(ns->href, XSLT_NAMESPACE))
	    goto skip_ns;

	/*
	* Apply namespace aliasing
	* ------------------------
	*
	* SPEC XSLT 2.0
	*  "- A namespace node whose string value is a literal namespace
	*     URI is not copied to the result tree.
	*   - A namespace node whose string value is a target namespace URI
	*     is copied to the result tree, whether or not the URI
	*     identifies an excluded namespace."
	*
	* NOTE: The ns-aliasing machanism is non-cascading.
	*  (checked with Saxon, Xalan and MSXML .NET).
	* URGENT TODO: is style->nsAliases the effective list of
	*  ns-aliases, or do we need to lookup the whole
	*  import-tree?
	* TODO: Get rid of import-tree lookup.
	*/
	if (cctxt->hasNsAliases) {
	    xsltNsAliasPtr alias;
	    /*
	    * First check for being a target namespace.
	    */
	    alias = cctxt->nsAliases;
	    do {
		/*
		* TODO: Is xmlns="" handled already?
		*/
		if ((alias->targetNs != NULL) &&
		    (xmlStrEqual(alias->targetNs->href, ns->href)))
		{
		    /*
		    * Recognized as a target namespace; use it regardless
		    * if excluded otherwise.
		    */
		    goto add_effective_ns;
		}
		alias = alias->next;
	    } while (alias != NULL);

	    alias = cctxt->nsAliases;
	    do {
		/*
		* TODO: Is xmlns="" handled already?
		*/
		if ((alias->literalNs != NULL) &&
		    (xmlStrEqual(alias->literalNs->href, ns->href)))
		{
		    /*
		    * Recognized as an namespace alias; do not use it.
		    */
		    goto skip_ns;
		}
		alias = alias->next;
	    } while (alias != NULL);
	}

	/*
	* Exclude excluded result namespaces.
	*/
	if (exclResultNs) {
	    for (j = 0; j < exclResultNs->number; j++)
		if (xmlStrEqual(ns->href, BAD_CAST exclResultNs->items[j]))
		    goto skip_ns;
	}
	/*
	* Exclude extension-element namespaces.
	*/
	if (extElemNs) {
	    for (j = 0; j < extElemNs->number; j++)
		if (xmlStrEqual(ns->href, BAD_CAST extElemNs->items[j]))
		    goto skip_ns;
	}

add_effective_ns:
	/*
	* OPTIMIZE TODO: This information may not be needed.
	*/
	if (isLRE && (elem->nsDef != NULL)) {
	    holdByElem = 0;
	    tmpns = elem->nsDef;
	    do {
		if (tmpns == ns) {
		    holdByElem = 1;
		    break;
		}
		tmpns = tmpns->next;
	    } while (tmpns != NULL);
	} else
	    holdByElem = 0;

	/*
	* Add the effective namespace declaration.
	*/
	effNs = (xsltEffectiveNsPtr) xmlMalloc(sizeof(xsltEffectiveNs));
	if (effNs == NULL) {
	    xsltTransformError(NULL, cctxt->style, elem,
		"Internal error in xsltLREBuildEffectiveNs(): "
		"failed to allocate memory.\n");
	    cctxt->style->errors++;
	    return(-1);
	}
	if (cctxt->psData->effectiveNs == NULL) {
	    cctxt->psData->effectiveNs = effNs;
	    effNs->nextInStore = NULL;
	} else {
	    effNs->nextInStore = cctxt->psData->effectiveNs;
	    cctxt->psData->effectiveNs = effNs;
	}

	effNs->next = NULL;
	effNs->prefix = ns->prefix;
	effNs->nsName = ns->href;
	effNs->holdByElem = holdByElem;

	if (lastEffNs == NULL)
	    item->effectiveNs = effNs;
	else
	    lastEffNs->next = effNs;
	lastEffNs = effNs;

skip_ns:
	{}
    }
    return(0);
}


xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
	           xmlNodePtr inst, xsltElemPreCompPtr castedComp) {
#ifdef XSLT_REFACTORED
    xsltStyleItemPIPtr comp = (xsltStyleItemPIPtr) castedComp;
#else
    xsltStylePreCompPtr comp = (xsltStylePreCompPtr) castedComp;
#endif
    const xmlChar *name;
    xmlChar *value = NULL;
    xmlNodePtr pi;

    if (ctxt->insert == NULL)
	return;
    if (comp->has_name == 0)
	return;
    if (comp->name == NULL) {
	name = xsltEvalAttrValueTemplate(ctxt, inst,
			    (const xmlChar *)"name", NULL);
	if (name == NULL) {
	    xsltTransformError(ctxt, NULL, inst,
		 "xsl:processing-instruction : name is missing\n");
	    goto error;
	}
    } else {
	name = comp->name;
    }
    /* TODO: check that it's both an an NCName and a PITarget. */

    value = xsltEvalTemplateString(ctxt, node, inst);
    if (xmlStrstr(value, BAD_CAST "?>") != NULL) {
	xsltTransformError(ctxt, NULL, inst,
	     "xsl:processing-instruction: '?>' not allowed within PI content\n");
	goto error;
    }
#ifdef WITH_XSLT_DEBUG_PROCESS
    if (value == NULL) {
	XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext,
	     "xsltProcessingInstruction: %s empty\n", name));
    } else {
	XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext,
	     "xsltProcessingInstruction: %s content %s\n", name, value));
    }
#endif

    pi = xmlNewDocPI(ctxt->insert->doc, name, value);
    pi = xsltAddChild(ctxt->insert, pi);

error:
    if ((name != NULL) && (name != comp->name))
        xmlFree((xmlChar *) name);
    if (value != NULL)
	xmlFree(value);
}


xsltCompileXSLTIncludeElem(xsltCompilerCtxtPtr cctxt, xmlNodePtr node) {
    xsltStyleItemIncludePtr item;

    if ((cctxt == NULL) || (node == NULL) || (node->type != XML_ELEMENT_NODE))
	return(NULL);

    node->psvi = NULL;
    item = (xsltStyleItemIncludePtr) xmlMalloc(sizeof(xsltStyleItemInclude));
    if (item == NULL) {
	xsltTransformError(NULL, cctxt->style, node,
		"xsltIncludeComp : malloc failed\n");
	cctxt->style->errors++;
	return(NULL);
    }
    memset(item, 0, sizeof(xsltStyleItemInclude));

    node->psvi = item;
    item->inst = node;
    item->type = XSLT_FUNC_INCLUDE;

    item->next = cctxt->style->preComps;
    cctxt->style->preComps = (xsltElemPreCompPtr) item;

    return(item);
}


xsltGetInheritedNsList(xsltStylesheetPtr style,
	               xsltTemplatePtr template,
	               xmlNodePtr node)
{
    xmlNsPtr cur;
    xmlNsPtr *ret = NULL;
    int nbns = 0;
    int maxns = 10;
    int i;

    if ((style == NULL) || (template == NULL) || (node == NULL) ||
	(template->inheritedNsNr != 0) || (template->inheritedNs != NULL))
	return(0);
    while (node != NULL) {
        if (node->type == XML_ELEMENT_NODE) {
            cur = node->nsDef;
            while (cur != NULL) {
		if (xmlStrEqual(cur->href, XSLT_NAMESPACE))
		    goto skip_ns;

		if ((cur->prefix != NULL) &&
		    (xsltCheckExtPrefix(style, cur->prefix)))
		    goto skip_ns;
		/*
		* Check if this namespace was excluded.
		* Note that at this point only the exclusions defined
		* on the topmost stylesheet element are in the exclusion-list.
		*/
		for (i = 0;i < style->exclPrefixNr;i++) {
		    if (xmlStrEqual(cur->href, style->exclPrefixTab[i]))
			goto skip_ns;
		}
                if (ret == NULL) {
                    ret =
                        (xmlNsPtr *) xmlMalloc((maxns + 1) *
                                               sizeof(xmlNsPtr));
                    if (ret == NULL) {
                        xmlGenericError(xmlGenericErrorContext,
                                        "xsltGetInheritedNsList : out of memory!\n");
                        return(0);
                    }
                    ret[nbns] = NULL;
                }
		/*
		* Skip shadowed namespace bindings.
		*/
                for (i = 0; i < nbns; i++) {
                    if ((cur->prefix == ret[i]->prefix) ||
                        (xmlStrEqual(cur->prefix, ret[i]->prefix)))
                        break;
                }
                if (i >= nbns) {
                    if (nbns >= maxns) {
                        maxns *= 2;
                        ret = (xmlNsPtr *) xmlRealloc(ret,
                                                      (maxns +
                                                       1) *
                                                      sizeof(xmlNsPtr));
                        if (ret == NULL) {
                            xmlGenericError(xmlGenericErrorContext,
                                            "xsltGetInheritedNsList : realloc failed!\n");
                            return(0);
                        }
                    }
                    ret[nbns++] = cur;
                    ret[nbns] = NULL;
                }
skip_ns:
                cur = cur->next;
            }
        }
        node = node->parent;
    }
    if (nbns != 0) {
#ifdef WITH_XSLT_DEBUG_PARSING
        xsltGenericDebug(xsltGenericDebugContext,
                         "template has %d inherited namespaces\n", nbns);
#endif
	template->inheritedNsNr = nbns;
	template->inheritedNs = ret;
    }
    return (nbns);
}


xsltNewPrincipalStylesheetData(void)
{
    xsltPrincipalStylesheetDataPtr ret;

    ret = (xsltPrincipalStylesheetDataPtr)
	xmlMalloc(sizeof(xsltPrincipalStylesheetData));
    if (ret == NULL) {
	xsltTransformError(NULL, NULL, NULL,
	    "xsltNewPrincipalStylesheetData: memory allocation failed.\n");
	return(NULL);
    }
    memset(ret, 0, sizeof(xsltPrincipalStylesheetData));

    /*
    * Global list of in-scope namespaces.
    */
    ret->inScopeNamespaces = xsltPointerListCreate(-1);
    if (ret->inScopeNamespaces == NULL)
	goto internal_err;
    /*
    * Global list of excluded result ns-decls.
    */
    ret->exclResultNamespaces = xsltPointerListCreate(-1);
    if (ret->exclResultNamespaces == NULL)
	goto internal_err;
    /*
    * Global list of extension instruction namespace names.
    */
    ret->extElemNamespaces = xsltPointerListCreate(-1);
    if (ret->extElemNamespaces == NULL)
	goto internal_err;

    return(ret);

internal_err:

    return(NULL);
}


xsltFindElemSpaceHandling(xsltTransformContextPtr ctxt, xmlNodePtr node) {
    xsltStylesheetPtr style;
    const xmlChar *val;

    if ((ctxt == NULL) || (node == NULL))
	return(0);
    style = ctxt->style;
    while (style != NULL) {
	if (node->ns != NULL) {
	    val = (const xmlChar *)
	      xmlHashLookup2(style->stripSpaces, node->name, node->ns->href);
            if (val == NULL) {
                val = (const xmlChar *)
                    xmlHashLookup2(style->stripSpaces, BAD_CAST "*",
                                   node->ns->href);
            }
	} else {
	    val = (const xmlChar *)
		  xmlHashLookup2(style->stripSpaces, node->name, NULL);
	}
	if (val != NULL) {
	    if (xmlStrEqual(val, (xmlChar *) "strip"))
		return(1);
	    if (xmlStrEqual(val, (xmlChar *) "preserve"))
		return(0);
	}
	if (style->stripAll == 1)
	    return(1);
	if (style->stripAll == -1)
	    return(0);

	style = xsltNextImport(style);
    }
    return(0);
}


xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
	     xmlNodePtr node, xmlNodePtr insert, int isLRE,
	     int topElemVisited)
{
    xmlNodePtr copy;

    if (node == NULL)
	return(NULL);
    switch (node->type) {
        case XML_ELEMENT_NODE:
        case XML_ENTITY_REF_NODE:
        case XML_ENTITY_NODE:
        case XML_PI_NODE:
        case XML_COMMENT_NODE:
        case XML_DOCUMENT_NODE:
        case XML_HTML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
        case XML_DOCB_DOCUMENT_NODE:
#endif
	    break;
        case XML_TEXT_NODE: {
	    int noenc = (node->name == xmlStringTextNoenc);
	    return(xsltCopyTextString(ctxt, insert, node->content, noenc));
	    }
        case XML_CDATA_SECTION_NODE:
	    return(xsltCopyTextString(ctxt, insert, node->content, 0));
        case XML_ATTRIBUTE_NODE:
	    return((xmlNodePtr)
		xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node));
        case XML_NAMESPACE_DECL:
	    return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode,
		insert, (xmlNsPtr) node));

        case XML_DOCUMENT_TYPE_NODE:
        case XML_DOCUMENT_FRAG_NODE:
        case XML_NOTATION_NODE:
        case XML_DTD_NODE:
        case XML_ELEMENT_DECL:
        case XML_ATTRIBUTE_DECL:
        case XML_ENTITY_DECL:
        case XML_XINCLUDE_START:
        case XML_XINCLUDE_END:
            return(NULL);
    }
    if (XSLT_IS_RES_TREE_FRAG(node)) {
	if (node->children != NULL)
	    copy = xsltCopyTreeList(ctxt, invocNode,
		node->children, insert, 0, 0);
	else
	    copy = NULL;
	return(copy);
    }
    copy = xmlDocCopyNode(node, insert->doc, 0);
    if (copy != NULL) {
	copy->doc = ctxt->output;
	copy = xsltAddChild(insert, copy);
        if (copy == NULL) {
            xsltTransformError(ctxt, NULL, invocNode,
            "xsltCopyTree: Copying of '%s' failed.\n", node->name);
            return (copy);
        }
	/*
	 * The node may have been coalesced into another text node.
	 */
	if (insert->last != copy)
	    return(insert->last);
	copy->next = NULL;

	if (node->type == XML_ELEMENT_NODE) {
	    /*
	    * Copy in-scope namespace nodes.
	    *
	    * REVISIT: Since we try to reuse existing in-scope ns-decls by
	    *  using xmlSearchNsByHref(), this will eventually change
	    *  the prefix of an original ns-binding; thus it might
	    *  break QNames in element/attribute content.
	    * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation
	    *  context, plus a ns-lookup function, which writes directly
	    *  to a given list, then we wouldn't need to create/free the
	    *  nsList every time.
	    */
	    if ((topElemVisited == 0) &&
		(node->parent != NULL) &&
		(node->parent->type != XML_DOCUMENT_NODE) &&
		(node->parent->type != XML_HTML_DOCUMENT_NODE))
	    {
		xmlNsPtr *nsList, *curns, ns;

		/*
		* If this is a top-most element in a tree to be
		* copied, then we need to ensure that all in-scope
		* namespaces are copied over. For nodes deeper in the
		* tree, it is sufficient to reconcile only the ns-decls
		* (node->nsDef entries).
		*/

		nsList = xmlGetNsList(node->doc, node);
		if (nsList != NULL) {
		    curns = nsList;
		    do {
			/*
			* Search by prefix first in order to break as less
			* QNames in element/attribute content as possible.
			*/
			ns = xmlSearchNs(insert->doc, insert,
			    (*curns)->prefix);

			if ((ns == NULL) ||
			    (! xmlStrEqual(ns->href, (*curns)->href)))
			{
			    ns = NULL;
			    /*
			    * Search by namespace name.
			    * REVISIT TODO: Currently disabled.
			    */
#if 0
			    ns = xmlSearchNsByHref(insert->doc,
				insert, (*curns)->href);
#endif
			}
			if (ns == NULL) {
			    /*
			    * Declare a new namespace on the copied element.
			    */
			    ns = xmlNewNs(copy, (*curns)->href,
				(*curns)->prefix);
			    /* TODO: Handle errors */
			}
			if (node->ns == *curns) {
			    /*
			    * If this was the original's namespace then set
			    * the generated counterpart on the copy.
			    */
			    copy->ns = ns;
			}
			curns++;
		    } while (*curns != NULL);
		    xmlFree(nsList);
		}
	    } else if (node->nsDef != NULL) {
		/*
		* Copy over all namespace declaration attributes.
		*/
		if (node->nsDef != NULL) {
		    if (isLRE)
			xsltCopyNamespaceList(ctxt, copy, node->nsDef);
		    else
			xsltCopyNamespaceListInternal(copy, node->nsDef);
		}
	    }
	    /*
	    * Set the namespace.
	    */
	    if (node->ns != NULL) {
		if (copy->ns == NULL) {
		    /*
		    * This will map copy->ns to one of the newly created
		    * in-scope ns-decls, OR create a new ns-decl on @copy.
		    */
		    copy->ns = xsltGetSpecialNamespace(ctxt, invocNode,
			node->ns->href, node->ns->prefix, copy);
		}
	    } else if ((insert->type == XML_ELEMENT_NODE) &&
		(insert->ns != NULL))
	    {
		/*
		* "Undeclare" the default namespace on @copy with xmlns="".
		*/
		xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy);
	    }
	    /*
	    * Copy attribute nodes.
	    */
	    if (node->properties != NULL) {
		xsltCopyAttrListNoOverwrite(ctxt, invocNode,
		    copy, node->properties);
	    }
	    if (topElemVisited == 0)
		topElemVisited = 1;
	}
	/*
	* Copy the subtree.
	*/
	if (node->children != NULL) {
	    xsltCopyTreeList(ctxt, invocNode,
		node->children, copy, isLRE, topElemVisited);
	}
    } else {
	xsltTransformError(ctxt, NULL, invocNode,
	    "xsltCopyTree: Copying of '%s' failed.\n", node->name);
    }
    return(copy);
}


static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
	void __user *argp = compat_ptr(arg);
	int err;

	if (cmd == HIDPGETCONNLIST) {
		struct hidp_connlist_req cl;
		u32 __user *p = argp;
		u32 uci;

		if (get_user(cl.cnum, p) || get_user(uci, p + 1))
			return -EFAULT;

		cl.ci = compat_ptr(uci);

		if (cl.cnum <= 0)
			return -EINVAL;

		err = hidp_get_connlist(&cl);

		if (!err && put_user(cl.cnum, p))
			err = -EFAULT;

		return err;
	} else if (cmd == HIDPCONNADD) {
		struct compat_hidp_connadd_req ca32;
		struct hidp_connadd_req ca;
		struct socket *csock;
		struct socket *isock;

		if (!capable(CAP_NET_ADMIN))
			return -EPERM;

		if (copy_from_user(&ca32, (void __user *) arg, sizeof(ca32)))
			return -EFAULT;

		ca.ctrl_sock = ca32.ctrl_sock;
		ca.intr_sock = ca32.intr_sock;
		ca.parser = ca32.parser;
		ca.rd_size = ca32.rd_size;
		ca.rd_data = compat_ptr(ca32.rd_data);
		ca.country = ca32.country;
		ca.subclass = ca32.subclass;
		ca.vendor = ca32.vendor;
		ca.product = ca32.product;
		ca.version = ca32.version;
		ca.flags = ca32.flags;
		ca.idle_to = ca32.idle_to;
		memcpy(ca.name, ca32.name, 128);

		csock = sockfd_lookup(ca.ctrl_sock, &err);
		if (!csock)
			return err;

		isock = sockfd_lookup(ca.intr_sock, &err);
		if (!isock) {
			sockfd_put(csock);
			return err;
		}

		err = hidp_connection_add(&ca, csock, isock);
		if (!err && copy_to_user(argp, &ca32, sizeof(ca32)))
			err = -EFAULT;

		sockfd_put(csock);
		sockfd_put(isock);

		return err;
	}

	return hidp_sock_ioctl(sock, cmd, arg);
}


static void rfc2231_join_continuations (PARAMETER **head,
					struct rfc2231_parameter *par)
{
  struct rfc2231_parameter *q;

  char attribute[STRING];
  char charset[STRING];
  char *value = NULL;
  char *valp;
  int encoded;

  size_t l, vl;

  while (par)
  {
    value = NULL; l = 0;

    strfcpy (attribute, par->attribute, sizeof (attribute));

    if ((encoded = par->encoded))
      valp = rfc2231_get_charset (par->value, charset, sizeof (charset));
    else
      valp = par->value;

    do
    {
      if (encoded && par->encoded)
	rfc2231_decode_one (par->value, valp);

      vl = strlen (par->value);

      safe_realloc (&value, l + vl + 1);
      strcpy (value + l, par->value);	/* __STRCPY_CHECKED__ */
      l += vl;

      q = par->next;
      rfc2231_free_parameter (&par);
      if ((par = q))
	valp = par->value;
    } while (par && !strcmp (par->attribute, attribute));

    if (value)
    {
      if (encoded)
	mutt_convert_string (&value, charset, Charset, MUTT_ICONV_HOOK_FROM);
      *head = mutt_new_parameter ();
      (*head)->attribute = safe_strdup (attribute);
      (*head)->value = value;
      head = &(*head)->next;
    }
  }
}


static size_t intel_vgpu_reg_rw_opregion(struct intel_vgpu *vgpu, char *buf,
		size_t count, loff_t *ppos, bool iswrite)
{
	unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) -
			VFIO_PCI_NUM_REGIONS;
	void *base = vgpu->vdev.region[i].data;
	loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;

	if (pos >= vgpu->vdev.region[i].size || iswrite) {
		gvt_vgpu_err("invalid op or offset for Intel vgpu OpRegion\n");
		return -EINVAL;
	}
	count = min(count, (size_t)(vgpu->vdev.region[i].size - pos));
	memcpy(buf, base + pos, count);

	return count;
}


static void kvmgt_protect_table_init(struct kvmgt_guest_info *info)
{
	hash_init(info->ptable);
}


static int kvmgt_get_vfio_device(void *p_vgpu)
{
	struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu;

	vgpu->vdev.vfio_device = vfio_device_get_from_dev(
		mdev_dev(vgpu->vdev.mdev));
	if (!vgpu->vdev.vfio_device) {
		gvt_vgpu_err("failed to get vfio device\n");
		return -ENODEV;
	}
	return 0;
}


static int intel_vgpu_set_msi_trigger(struct intel_vgpu *vgpu,
		unsigned int index, unsigned int start, unsigned int count,
		uint32_t flags, void *data)
{
	struct eventfd_ctx *trigger;

	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
		int fd = *(int *)data;

		trigger = eventfd_ctx_fdget(fd);
		if (IS_ERR(trigger)) {
			gvt_vgpu_err("eventfd_ctx_fdget failed\n");
			return PTR_ERR(trigger);
		}
		vgpu->vdev.msi_trigger = trigger;
	} else if ((flags & VFIO_IRQ_SET_DATA_NONE) && !count)
		intel_vgpu_release_msi_eventfd_ctx(vgpu);

	return 0;
}


static int intel_vgpu_set_intx_mask(struct intel_vgpu *vgpu,
			unsigned int index, unsigned int start,
			unsigned int count, uint32_t flags,
			void *data)
{
	return 0;
}


static void kvmgt_detach_vgpu(void *p_vgpu)
{
	int i;
	struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu;

	if (!vgpu->vdev.region)
		return;

	for (i = 0; i < vgpu->vdev.num_regions; i++)
		if (vgpu->vdev.region[i].ops->release)
			vgpu->vdev.region[i].ops->release(vgpu,
					&vgpu->vdev.region[i]);
	vgpu->vdev.num_regions = 0;
	kfree(vgpu->vdev.region);
	vgpu->vdev.region = NULL;
}


static unsigned long kvmgt_gfn_to_pfn(unsigned long handle, unsigned long gfn)
{
	struct kvmgt_guest_info *info;
	kvm_pfn_t pfn;

	if (!handle_valid(handle))
		return INTEL_GVT_INVALID_ADDR;

	info = (struct kvmgt_guest_info *)handle;

	pfn = gfn_to_pfn(info->kvm, gfn);
	if (is_error_noslot_pfn(pfn))
		return INTEL_GVT_INVALID_ADDR;

	return pfn;
}


report_expiration (krb5_context context,
		   krb5_prompter_fct prompter,
		   krb5_data *data,
		   const char *str,
		   time_t now)
{
    char *p = NULL;

    if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL)
	return;
    (*prompter)(context, data, NULL, p, 0, NULL);
    free(p);
}


pk_rd_pa_reply_dh(krb5_context context,
		  const heim_octet_string *indata,
		  const heim_oid *dataType,
		  const char *realm,
		  krb5_pk_init_ctx ctx,
		  krb5_enctype etype,
		  const krb5_krbhst_info *hi,
		  const DHNonce *c_n,
		  const DHNonce *k_n,
                  unsigned nonce,
                  PA_DATA *pa,
                  krb5_keyblock **key)
{
    const unsigned char *p;
    unsigned char *dh_gen_key = NULL;
    struct krb5_pk_cert *host = NULL;
    BIGNUM *kdc_dh_pubkey = NULL;
    KDCDHKeyInfo kdc_dh_info;
    heim_oid contentType = { 0, NULL };
    krb5_data content;
    krb5_error_code ret;
    int dh_gen_keylen = 0;
    size_t size;

    krb5_data_zero(&content);
    memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));

    if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
	krb5_set_error_message(context, EINVAL,
			       N_("PKINIT: Invalid content type", ""));
	return EINVAL;
    }

    ret = pk_verify_sign(context,
			 indata->data,
			 indata->length,
			 ctx->id,
			 &contentType,
			 &content,
			 &host);
    if (ret)
	goto out;

    /* make sure that it is the kdc's certificate */
    ret = pk_verify_host(context, realm, hi, ctx, host);
    if (ret)
	goto out;

    if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
	ret = KRB5KRB_AP_ERR_MSG_TYPE;
	krb5_set_error_message(context, ret,
			       N_("pkinit - dh reply contains wrong oid", ""));
	goto out;
    }

    ret = decode_KDCDHKeyInfo(content.data,
			      content.length,
			      &kdc_dh_info,
			      &size);

    if (ret) {
	krb5_set_error_message(context, ret,
			       N_("pkinit - failed to decode "
				  "KDC DH Key Info", ""));
	goto out;
    }

    if (kdc_dh_info.nonce != nonce) {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	krb5_set_error_message(context, ret,
			       N_("PKINIT: DH nonce is wrong", ""));
	goto out;
    }

    if (kdc_dh_info.dhKeyExpiration) {
	if (k_n == NULL) {
	    ret = KRB5KRB_ERR_GENERIC;
	    krb5_set_error_message(context, ret,
				   N_("pkinit; got key expiration "
				      "without server nonce", ""));
	    goto out;
	}
	if (c_n == NULL) {
	    ret = KRB5KRB_ERR_GENERIC;
	    krb5_set_error_message(context, ret,
				   N_("pkinit; got DH reuse but no "
				      "client nonce", ""));
	    goto out;
	}
    } else {
	if (k_n) {
	    ret = KRB5KRB_ERR_GENERIC;
	    krb5_set_error_message(context, ret,
				   N_("pkinit: got server nonce "
				      "without key expiration", ""));
	    goto out;
	}
	c_n = NULL;
    }

    p = kdc_dh_info.subjectPublicKey.data;
    size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;

    if (ctx->keyex == USE_DH) {
	DHPublicKey k;
	ret = decode_DHPublicKey(p, size, &k, NULL);
	if (ret) {
	    krb5_set_error_message(context, ret,
				   N_("pkinit: can't decode "
				      "without key expiration", ""));
	    goto out;
	}

	kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
	free_DHPublicKey(&k);
	if (kdc_dh_pubkey == NULL) {
	    ret = ENOMEM;
	    goto out;
	}

	size = DH_size(ctx->u.dh);

	dh_gen_key = malloc(size);
	if (dh_gen_key == NULL) {
	    ret = krb5_enomem(context);
	    goto out;
	}

	dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
	if (dh_gen_keylen == -1) {
	    ret = KRB5KRB_ERR_GENERIC;
	    dh_gen_keylen = 0;
	    krb5_set_error_message(context, ret,
				   N_("PKINIT: Can't compute Diffie-Hellman key", ""));
	    goto out;
	}
	if (dh_gen_keylen < (int)size) {
	    size -= dh_gen_keylen;
	    memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
	    memset(dh_gen_key, 0, size);
	}

    } else {
        ret = _krb5_pk_rd_pa_reply_ecdh_compute_key(context, ctx, p,
                                                    size, &dh_gen_key,
                                                    &dh_gen_keylen);
        if (ret)
          goto out;
    }

    if (dh_gen_keylen <= 0) {
	ret = EINVAL;
	krb5_set_error_message(context, ret,
			       N_("PKINIT: resulting DH key <= 0", ""));
	dh_gen_keylen = 0;
	goto out;
    }

    *key = malloc (sizeof (**key));
    if (*key == NULL) {
	ret = krb5_enomem(context);
	goto out;
    }

    ret = _krb5_pk_octetstring2key(context,
				   etype,
				   dh_gen_key, dh_gen_keylen,
				   c_n, k_n,
				   *key);
    if (ret) {
	krb5_set_error_message(context, ret,
			       N_("PKINIT: can't create key from DH key", ""));
	free(*key);
	*key = NULL;
	goto out;
    }

 out:
    if (kdc_dh_pubkey)
	BN_free(kdc_dh_pubkey);
    if (dh_gen_key) {
	memset(dh_gen_key, 0, dh_gen_keylen);
	free(dh_gen_key);
    }
    if (host)
	_krb5_pk_cert_free(host);
    if (content.data)
	krb5_data_free(&content);
    der_free_oid(&contentType);
    free_KDCDHKeyInfo(&kdc_dh_info);

    return ret;
}


pk_verify_host(krb5_context context,
	       const char *realm,
	       const krb5_krbhst_info *hi,
	       struct krb5_pk_init_ctx_data *ctx,
	       struct krb5_pk_cert *host)
{
    krb5_error_code ret = 0;

    if (ctx->require_eku) {
	ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
				   &asn1_oid_id_pkkdcekuoid, 0);
	if (ret) {
	    krb5_set_error_message(context, ret,
				   N_("No PK-INIT KDC EKU in kdc certificate", ""));
	    return ret;
	}
    }
    if (ctx->require_krbtgt_otherName) {
	hx509_octet_string_list list;
	size_t i;
	int matched = 0;

	ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
						       host->cert,
						       &asn1_oid_id_pkinit_san,
						       &list);
	if (ret) {
	    krb5_set_error_message(context, ret,
				   N_("Failed to find the PK-INIT "
				      "subjectAltName in the KDC "
				      "certificate", ""));

	    return ret;
	}

	/*
	 * subjectAltNames are multi-valued, and a single KDC may serve
	 * multiple realms. The SAN validation here must accept
	 * the KDC's cert if *any* of the SANs match the expected KDC.
	 * It is OK for *some* of the SANs to not match, provided at least
	 * one does.
	 */
	for (i = 0; matched == 0 && i < list.len; i++) {
	    KRB5PrincipalName r;

	    ret = decode_KRB5PrincipalName(list.val[i].data,
					   list.val[i].length,
					   &r,
					   NULL);
	    if (ret) {
		krb5_set_error_message(context, ret,
				       N_("Failed to decode the PK-INIT "
					  "subjectAltName in the "
					  "KDC certificate", ""));

		break;
	    }

	    if (r.principalName.name_string.len == 2 &&
		strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) == 0
		&& strcmp(r.principalName.name_string.val[1], realm) == 0
		&& strcmp(r.realm, realm) == 0)
		matched = 1;

	    free_KRB5PrincipalName(&r);
	}
	hx509_free_octet_string_list(&list);
	if (matched == 0) {
	    ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
	    /* XXX: Lost in translation... */
	    krb5_set_error_message(context, ret,
				   N_("KDC have wrong realm name in "
				      "the certificate", ""));
	}
    }
    if (ret)
	return ret;

    if (hi) {
	ret = hx509_verify_hostname(context->hx509ctx, host->cert,
				    ctx->require_hostname_match,
				    HX509_HN_HOSTNAME,
				    hi->hostname,
				    hi->ai->ai_addr, hi->ai->ai_addrlen);

	if (ret)
	    krb5_set_error_message(context, ret,
				   N_("Address mismatch in "
				      "the KDC certificate", ""));
    }
    return ret;
}


_krb5_pk_set_user_id(krb5_context context,
		     krb5_principal principal,
		     krb5_pk_init_ctx ctx,
		     struct hx509_certs_data *certs)
{
    hx509_certs c = hx509_certs_ref(certs);
    hx509_query *q = NULL;
    int ret;

    if (ctx->id->certs)
	hx509_certs_free(&ctx->id->certs);
    if (ctx->id->cert) {
	hx509_cert_free(ctx->id->cert);
	ctx->id->cert = NULL;
    }

    ctx->id->certs = c;
    ctx->anonymous = 0;

    ret = hx509_query_alloc(context->hx509ctx, &q);
    if (ret) {
	pk_copy_error(context, context->hx509ctx, ret,
		      "Allocate query to find signing certificate");
	return ret;
    }

    hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
    hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);

    if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
	ctx->id->flags |= PKINIT_BTMM;
    }

    ret = find_cert(context, ctx->id, q, &ctx->id->cert);
    hx509_query_free(context->hx509ctx, q);

    if (ret == 0 && _krb5_have_debug(context, 2)) {
	hx509_name name;
	char *str, *sn;
	heim_integer i;

	ret = hx509_cert_get_subject(ctx->id->cert, &name);
	if (ret)
	    goto out;

	ret = hx509_name_to_string(name, &str);
	hx509_name_free(&name);
	if (ret)
	    goto out;

	ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
	if (ret) {
	    free(str);
	    goto out;
	}

	ret = der_print_hex_heim_integer(&i, &sn);
	der_free_heim_integer(&i);
	if (ret) {
	    free(name);
	    goto out;
	}

	_krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
	free(str);
	free(sn);
    }
 out:

    return ret;
}


pa_data_to_key_plain(krb5_context context,
		     const krb5_principal client,
		     krb5_get_init_creds_ctx *ctx,
		     krb5_salt salt,
		     krb5_data *s2kparams,
		     krb5_enctype etype,
		     krb5_keyblock **key)
{
    krb5_error_code ret;

    ret = (*ctx->keyproc)(context, etype, ctx->keyseed,
			   salt, s2kparams, key);
    return ret;
}


thumbnailer_unref (Thumbnailer *thumb)
{
  g_return_if_fail (thumb != NULL);
  g_return_if_fail (thumb->ref_count > 0);

  if (g_atomic_int_dec_and_test (&thumb->ref_count))
    {
      g_free (thumb->path);
      g_free (thumb->command);
      g_strfreev (thumb->mime_types);

      g_slice_free (Thumbnailer, thumb);
    }
}


thumbnailer_reload (Thumbnailer *thumb)
{
  g_return_val_if_fail (thumb != NULL, NULL);

  g_free (thumb->command);
  thumb->command = NULL;
  g_strfreev (thumb->mime_types);
  thumb->mime_types = NULL;

  return thumbnailer_load (thumb);
}


gnome_desktop_thumbnail_factory_new (GnomeDesktopThumbnailSize size)
{
  GnomeDesktopThumbnailFactory *factory;

  factory = g_object_new (GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY, NULL);

  factory->priv->size = size;

  return factory;
}


init_thumbnailers_dirs (gpointer data)
{
  const gchar * const *data_dirs;
  GPtrArray *thumbs_dirs;
  guint i;

  data_dirs = g_get_system_data_dirs ();
  thumbs_dirs = g_ptr_array_new ();

  g_ptr_array_add (thumbs_dirs, g_build_filename (g_get_user_data_dir (), "thumbnailers", NULL));
  for (i = 0; data_dirs[i] != NULL; i++)
    g_ptr_array_add (thumbs_dirs, g_build_filename (data_dirs[i], "thumbnailers", NULL));
  g_ptr_array_add (thumbs_dirs, NULL);

  return g_ptr_array_free (thumbs_dirs, FALSE);
}


gnome_desktop_thumbnail_factory_generate_thumbnail (GnomeDesktopThumbnailFactory *factory,
						    const char            *uri,
						    const char            *mime_type)
{
  GdkPixbuf *pixbuf;
  char *script;
  int size;

  g_return_val_if_fail (uri != NULL, NULL);
  g_return_val_if_fail (mime_type != NULL, NULL);

  /* Doesn't access any volatile fields in factory, so it's threadsafe */

  size = 128;
  if (factory->priv->size == GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE)
    size = 256;

  pixbuf = get_preview_thumbnail (uri, size);
  if (pixbuf != NULL)
    return pixbuf;

  script = NULL;
  g_mutex_lock (&factory->priv->lock);
  if (!gnome_desktop_thumbnail_factory_is_disabled (factory, mime_type))
    {
      Thumbnailer *thumb;

      thumb = g_hash_table_lookup (factory->priv->mime_types_map, mime_type);
      if (thumb)
        script = g_strdup (thumb->command);
    }
  g_mutex_unlock (&factory->priv->lock);

  if (script)
    {
      GBytes *data;
      GError *error = NULL;

      data = gnome_desktop_thumbnail_script_exec (script, size, uri, &error);
      if (data)
        {
          pixbuf = pixbuf_new_from_bytes (data, &error);
          if (!pixbuf)
            {
              g_debug ("Could not load thumbnail pixbuf: %s", error->message);
              g_error_free (error);
            }
          g_bytes_unref (data);
        }
      else
        {
          g_debug ("Thumbnail script ('%s') failed for '%s': %s",
                   script, uri, error ? error->message : "no details");
          g_clear_error (&error);
        }
    }
  else
    {
      g_debug ("Could not find thumbnailer for mime-type '%s'",
               mime_type);
    }

  g_free (script);

  return pixbuf;
}


gnome_desktop_thumbnail_factory_class_init (GnomeDesktopThumbnailFactoryClass *class)
{
  GObjectClass *gobject_class;

  gobject_class = G_OBJECT_CLASS (class);

  gobject_class->finalize = gnome_desktop_thumbnail_factory_finalize;

  g_type_class_add_private (class, sizeof (GnomeDesktopThumbnailFactoryPrivate));
}


remove_thumbnailer (GnomeDesktopThumbnailFactory *factory,
                    const gchar                  *path)
{
  GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
  GList *l;
  Thumbnailer *thumb;

  g_mutex_lock (&priv->lock);

  for (l = priv->thumbnailers; l; l = g_list_next (l))
    {
      thumb = (Thumbnailer *)l->data;

      if (strcmp (thumb->path, path) == 0)
        {
          priv->thumbnailers = g_list_delete_link (priv->thumbnailers, l);
          g_hash_table_foreach_remove (priv->mime_types_map,
                                       (GHRFunc)remove_thumbnailer_from_mime_type_map,
                                       (gpointer)path);
          thumbnailer_unref (thumb);

          break;
        }
    }

  g_mutex_unlock (&priv->lock);
}


thumbnail_path (const char                *uri,
                GnomeDesktopThumbnailSize  size)
{
  char *path, *file;

  file = thumbnail_filename (uri);
  path = g_build_filename (g_get_user_cache_dir (),
                           "thumbnails",
                           size == GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE ? "large" : "normal",
                           file,
                           NULL);
  g_free (file);
  return path;
}


pixbuf_new_from_bytes (GBytes  *bytes,
		       GError **error)
{
  g_autoptr(GdkPixbufLoader) loader = NULL;

  loader = gdk_pixbuf_loader_new_with_mime_type ("image/png", error);
  if (!loader)
    return NULL;

  if (!gdk_pixbuf_loader_write (loader,
				g_bytes_get_data (bytes, NULL),
				g_bytes_get_size (bytes),
				error))
    {
      return NULL;
    }

  if (!gdk_pixbuf_loader_close (loader, error))
    return NULL;

  return g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
}


static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p)
{
	struct fsl_hv_ioctl_memcpy param;

	struct page **pages = NULL;
	void *sg_list_unaligned = NULL;
	struct fh_sg_list *sg_list = NULL;

	unsigned int num_pages;
	unsigned long lb_offset; /* Offset within a page of the local buffer */

	unsigned int i;
	long ret = 0;
	int num_pinned; /* return value from get_user_pages() */
	phys_addr_t remote_paddr; /* The next address in the remote buffer */
	uint32_t count; /* The number of bytes left to copy */

	/* Get the parameters from the user */
	if (copy_from_user(&param, p, sizeof(struct fsl_hv_ioctl_memcpy)))
		return -EFAULT;

	/*
	 * One partition must be local, the other must be remote.  In other
	 * words, if source and target are both -1, or are both not -1, then
	 * return an error.
	 */
	if ((param.source == -1) == (param.target == -1))
		return -EINVAL;

	/*
	 * The array of pages returned by get_user_pages() covers only
	 * page-aligned memory.  Since the user buffer is probably not
	 * page-aligned, we need to handle the discrepancy.
	 *
	 * We calculate the offset within a page of the S/G list, and make
	 * adjustments accordingly.  This will result in a page list that looks
	 * like this:
	 *
	 *      ----    <-- first page starts before the buffer
	 *     |    |
	 *     |////|-> ----
	 *     |////|  |    |
	 *      ----   |    |
	 *             |    |
	 *      ----   |    |
	 *     |////|  |    |
	 *     |////|  |    |
	 *     |////|  |    |
	 *      ----   |    |
	 *             |    |
	 *      ----   |    |
	 *     |////|  |    |
	 *     |////|  |    |
	 *     |////|  |    |
	 *      ----   |    |
	 *             |    |
	 *      ----   |    |
	 *     |////|  |    |
	 *     |////|-> ----
	 *     |    |   <-- last page ends after the buffer
	 *      ----
	 *
	 * The distance between the start of the first page and the start of the
	 * buffer is lb_offset.  The hashed (///) areas are the parts of the
	 * page list that contain the actual buffer.
	 *
	 * The advantage of this approach is that the number of pages is
	 * equal to the number of entries in the S/G list that we give to the
	 * hypervisor.
	 */
	lb_offset = param.local_vaddr & (PAGE_SIZE - 1);
	if (param.count == 0 ||
	    param.count > U64_MAX - lb_offset - PAGE_SIZE + 1)
		return -EINVAL;
	num_pages = (param.count + lb_offset + PAGE_SIZE - 1) >> PAGE_SHIFT;

	/* Allocate the buffers we need */

	/*
	 * 'pages' is an array of struct page pointers that's initialized by
	 * get_user_pages().
	 */
	pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
	if (!pages) {
		pr_debug("fsl-hv: could not allocate page list\n");
		return -ENOMEM;
	}

	/*
	 * sg_list is the list of fh_sg_list objects that we pass to the
	 * hypervisor.
	 */
	sg_list_unaligned = kmalloc(num_pages * sizeof(struct fh_sg_list) +
		sizeof(struct fh_sg_list) - 1, GFP_KERNEL);
	if (!sg_list_unaligned) {
		pr_debug("fsl-hv: could not allocate S/G list\n");
		ret = -ENOMEM;
		goto exit;
	}
	sg_list = PTR_ALIGN(sg_list_unaligned, sizeof(struct fh_sg_list));

	/* Get the physical addresses of the source buffer */
	num_pinned = get_user_pages_fast(param.local_vaddr - lb_offset,
		num_pages, param.source != -1 ? FOLL_WRITE : 0, pages);

	if (num_pinned != num_pages) {
		/* get_user_pages() failed */
		pr_debug("fsl-hv: could not lock source buffer\n");
		ret = (num_pinned < 0) ? num_pinned : -EFAULT;
		goto exit;
	}

	/*
	 * Build the fh_sg_list[] array.  The first page is special
	 * because it's misaligned.
	 */
	if (param.source == -1) {
		sg_list[0].source = page_to_phys(pages[0]) + lb_offset;
		sg_list[0].target = param.remote_paddr;
	} else {
		sg_list[0].source = param.remote_paddr;
		sg_list[0].target = page_to_phys(pages[0]) + lb_offset;
	}
	sg_list[0].size = min_t(uint64_t, param.count, PAGE_SIZE - lb_offset);

	remote_paddr = param.remote_paddr + sg_list[0].size;
	count = param.count - sg_list[0].size;

	for (i = 1; i < num_pages; i++) {
		if (param.source == -1) {
			/* local to remote */
			sg_list[i].source = page_to_phys(pages[i]);
			sg_list[i].target = remote_paddr;
		} else {
			/* remote to local */
			sg_list[i].source = remote_paddr;
			sg_list[i].target = page_to_phys(pages[i]);
		}
		sg_list[i].size = min_t(uint64_t, count, PAGE_SIZE);

		remote_paddr += sg_list[i].size;
		count -= sg_list[i].size;
	}

	param.ret = fh_partition_memcpy(param.source, param.target,
		virt_to_phys(sg_list), num_pages);

exit:
	if (pages) {
		for (i = 0; i < num_pages; i++)
			if (pages[i])
				put_page(pages[i]);
	}

	kfree(sg_list_unaligned);
	kfree(pages);

	if (!ret)
		if (copy_to_user(&p->ret, &param.ret, sizeof(__u32)))
			return -EFAULT;

	return ret;
}


int fsl_hv_failover_unregister(struct notifier_block *nb)
{
	return blocking_notifier_chain_unregister(&failover_subscribers, nb);
}


static irqreturn_t fsl_hv_state_change_thread(int irq, void *data)
{
	struct doorbell_isr *dbisr = data;

	blocking_notifier_call_chain(&failover_subscribers, dbisr->partition,
				     NULL);

	return IRQ_HANDLED;
}


static void printSADSCPOpcode(const uint8_t *buf)
{
	unsigned char sadscp_tol;
	sadscp_tol = buf[12];	/* tolerance_fields */

	syslog(LOG_DEBUG, "PCP SADSCP: Opcode specific information.\n");
	syslog(LOG_DEBUG, "Delay tolerance %d \n", (sadscp_tol>>6)&3);
	syslog(LOG_DEBUG, "Loss tolerance %d \n",  (sadscp_tol>>4)&3);
	syslog(LOG_DEBUG, "Jitter tolerance %d \n",  (sadscp_tol>>2)&3);
	syslog(LOG_DEBUG, "RRR %d \n", sadscp_tol&3);
	syslog(LOG_DEBUG, "AppName Length %d \n", buf[13]);
	syslog(LOG_DEBUG, "Application name %.*s \n", buf[13], buf + 14);
}


static void parsePCPMAP_version2(const uint8_t *map_v2,
		pcp_info_t *pcp_msg_info)
{
	pcp_msg_info->is_map_op = 1;
	memcpy(pcp_msg_info->nonce, map_v2, 12);
	pcp_msg_info->protocol = map_v2[12];
	pcp_msg_info->int_port = READNU16(map_v2 + 16);
	pcp_msg_info->ext_port = READNU16(map_v2 + 18);

	pcp_msg_info->ext_ip = (struct in6_addr *)(map_v2 + 20);
}


int ProcessIncomingPCPPacket(int s, unsigned char *buff, int len,
                             const struct sockaddr *senderaddr,
                             const struct sockaddr_in6 *receiveraddr)
{
	pcp_info_t pcp_msg_info;
	struct lan_addr_s * lan_addr;
	char addr_str[64];

	memset(&pcp_msg_info, 0, sizeof(pcp_info_t));

	if(senderaddr->sa_family == AF_INET) {
		const struct sockaddr_in * senderaddr_v4 =
			(const struct sockaddr_in *)senderaddr;
		pcp_msg_info.sender_ip.s6_addr[11] = 0xff;
		pcp_msg_info.sender_ip.s6_addr[10] = 0xff;
		memcpy(pcp_msg_info.sender_ip.s6_addr+12,
		       &senderaddr_v4->sin_addr, 4);
	} else if(senderaddr->sa_family == AF_INET6) {
		const struct sockaddr_in6 * senderaddr_v6 =
			(const struct sockaddr_in6 *)senderaddr;
		pcp_msg_info.sender_ip = senderaddr_v6->sin6_addr;
	} else {
		syslog(LOG_WARNING, "unknown PCP packet sender address family %d",
		       senderaddr->sa_family);
		return 0;
	}

	if(sockaddr_to_string(senderaddr, addr_str, sizeof(addr_str)))
		syslog(LOG_DEBUG, "PCP request received from %s %dbytes",
		       addr_str, len);

	if(buff[1] & 128) {
		/* discarding PCP responses silently */
		return 0;
	}

	/* If we're in allow third party-mode, we probably don't care
	 * about locality either. Let's hope firewall is ok. */
	if (!GETFLAG(PCP_ALLOWTHIRDPARTYMASK)) {
		lan_addr = get_lan_for_peer(senderaddr);
		if(lan_addr == NULL) {
			syslog(LOG_WARNING, "PCP packet sender %s not from a LAN, ignoring",
			       addr_str);
			return 0;
		}
	}

	if (processPCPRequest(buff, len, &pcp_msg_info) ) {

		createPCPResponse(buff, &pcp_msg_info);

		if(len < PCP_MIN_LEN)
			len = PCP_MIN_LEN;
		else
			len = (len + 3) & ~3;	/* round up resp. length to multiple of 4 */
		len = sendto_or_schedule2(s, buff, len, 0, senderaddr,
		           (senderaddr->sa_family == AF_INET) ?
		                  sizeof(struct sockaddr_in) :
		                  sizeof(struct sockaddr_in6),
		           receiveraddr);
		if( len < 0 ) {
			syslog(LOG_ERR, "sendto(pcpserver): %m");
		}
	}

	return 0;
}


static void ext4_rereserve_cluster(struct inode *inode, ext4_lblk_t lblk)
{
	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
	struct ext4_inode_info *ei = EXT4_I(inode);

	dquot_reclaim_block(inode, EXT4_C2B(sbi, 1));

	spin_lock(&ei->i_block_reservation_lock);
	ei->i_reserved_data_blocks++;
	percpu_counter_add(&sbi->s_dirtyclusters_counter, 1);
	spin_unlock(&ei->i_block_reservation_lock);

	percpu_counter_add(&sbi->s_freeclusters_counter, 1);
	ext4_remove_pending(inode, lblk);
}


int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
{
	struct super_block *sb = inode->i_sb;
	ext4_lblk_t punch_start, punch_stop;
	handle_t *handle;
	unsigned int credits;
	loff_t new_size, ioffset;
	int ret;

	/*
	 * We need to test this early because xfstests assumes that a
	 * collapse range of (0, 1) will return EOPNOTSUPP if the file
	 * system does not support collapse range.
	 */
	if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
		return -EOPNOTSUPP;

	/* Collapse range works only on fs block size aligned offsets. */
	if (offset & (EXT4_CLUSTER_SIZE(sb) - 1) ||
	    len & (EXT4_CLUSTER_SIZE(sb) - 1))
		return -EINVAL;

	if (!S_ISREG(inode->i_mode))
		return -EINVAL;

	trace_ext4_collapse_range(inode, offset, len);

	punch_start = offset >> EXT4_BLOCK_SIZE_BITS(sb);
	punch_stop = (offset + len) >> EXT4_BLOCK_SIZE_BITS(sb);

	/* Call ext4_force_commit to flush all data in case of data=journal. */
	if (ext4_should_journal_data(inode)) {
		ret = ext4_force_commit(inode->i_sb);
		if (ret)
			return ret;
	}

	inode_lock(inode);
	/*
	 * There is no need to overlap collapse range with EOF, in which case
	 * it is effectively a truncate operation
	 */
	if (offset + len >= i_size_read(inode)) {
		ret = -EINVAL;
		goto out_mutex;
	}

	/* Currently just for extent based files */
	if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
		ret = -EOPNOTSUPP;
		goto out_mutex;
	}

	/* Wait for existing dio to complete */
	inode_dio_wait(inode);

	/*
	 * Prevent page faults from reinstantiating pages we have released from
	 * page cache.
	 */
	down_write(&EXT4_I(inode)->i_mmap_sem);

	ret = ext4_break_layouts(inode);
	if (ret)
		goto out_mmap;

	/*
	 * Need to round down offset to be aligned with page size boundary
	 * for page size > block size.
	 */
	ioffset = round_down(offset, PAGE_SIZE);
	/*
	 * Write tail of the last page before removed range since it will get
	 * removed from the page cache below.
	 */
	ret = filemap_write_and_wait_range(inode->i_mapping, ioffset, offset);
	if (ret)
		goto out_mmap;
	/*
	 * Write data that will be shifted to preserve them when discarding
	 * page cache below. We are also protected from pages becoming dirty
	 * by i_mmap_sem.
	 */
	ret = filemap_write_and_wait_range(inode->i_mapping, offset + len,
					   LLONG_MAX);
	if (ret)
		goto out_mmap;
	truncate_pagecache(inode, ioffset);

	credits = ext4_writepage_trans_blocks(inode);
	handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, credits);
	if (IS_ERR(handle)) {
		ret = PTR_ERR(handle);
		goto out_mmap;
	}

	down_write(&EXT4_I(inode)->i_data_sem);
	ext4_discard_preallocations(inode);

	ret = ext4_es_remove_extent(inode, punch_start,
				    EXT_MAX_BLOCKS - punch_start);
	if (ret) {
		up_write(&EXT4_I(inode)->i_data_sem);
		goto out_stop;
	}

	ret = ext4_ext_remove_space(inode, punch_start, punch_stop - 1);
	if (ret) {
		up_write(&EXT4_I(inode)->i_data_sem);
		goto out_stop;
	}
	ext4_discard_preallocations(inode);

	ret = ext4_ext_shift_extents(inode, handle, punch_stop,
				     punch_stop - punch_start, SHIFT_LEFT);
	if (ret) {
		up_write(&EXT4_I(inode)->i_data_sem);
		goto out_stop;
	}

	new_size = i_size_read(inode) - len;
	i_size_write(inode, new_size);
	EXT4_I(inode)->i_disksize = new_size;

	up_write(&EXT4_I(inode)->i_data_sem);
	if (IS_SYNC(inode))
		ext4_handle_sync(handle);
	inode->i_mtime = inode->i_ctime = current_time(inode);
	ext4_mark_inode_dirty(handle, inode);
	ext4_update_inode_fsync_trans(handle, inode, 1);

out_stop:
	ext4_journal_stop(handle);
out_mmap:
	up_write(&EXT4_I(inode)->i_mmap_sem);
out_mutex:
	inode_unlock(inode);
	return ret;
}


static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
{
    bool startstop = qemu_spice_display_is_running(&d->ssd);

    trace_qxl_hard_reset(d->id, loadvm);

    if (startstop) {
        qemu_spice_display_stop();
    }

    qxl_spice_reset_cursor(d);
    qxl_spice_reset_image_cache(d);
    qxl_reset_surfaces(d);
    qxl_reset_memslots(d);

    /* pre loadvm reset must not touch QXLRam.  This lives in
     * device memory, is migrated together with RAM and thus
     * already loaded at this point */
    if (!loadvm) {
        qxl_reset_state(d);
    }
    qemu_spice_create_host_memslot(&d->ssd);
    qxl_soft_reset(d);

    if (d->migration_blocker) {
        migrate_del_blocker(d->migration_blocker);
        error_free(d->migration_blocker);
        d->migration_blocker = NULL;
    }

    if (startstop) {
        qemu_spice_display_start();
    }
}


static ram_addr_t qxl_rom_size(void)
{
#define QXL_REQUIRED_SZ (sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes))
#define QXL_ROM_SZ 8192

    QEMU_BUILD_BUG_ON(QXL_REQUIRED_SZ > QXL_ROM_SZ);
    return QXL_ROM_SZ;
}


static void interface_set_compression_level(QXLInstance *sin, int level)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);

    trace_qxl_interface_set_compression_level(qxl->id, level);
    qxl->shadow_rom.compression_level = cpu_to_le32(level);
    qxl->rom->compression_level = cpu_to_le32(level);
    qxl_rom_set_dirty(qxl);
}


static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async)
{
    if (d->mode == QXL_MODE_UNDEFINED) {
        return 0;
    }
    trace_qxl_destroy_primary(d->id);
    d->mode = QXL_MODE_UNDEFINED;
    qemu_spice_destroy_primary_surface(&d->ssd, 0, async);
    qxl_spice_reset_cursor(d);
    return 1;
}


static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);

    trace_qxl_interface_get_init_info(qxl->id);
    info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
    info->memslot_id_bits = MEMSLOT_SLOT_BITS;
    info->num_memslots = NUM_MEMSLOTS;
    info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
    info->internal_groupslot_id = 0;
    info->qxl_ram_size =
        le32_to_cpu(qxl->shadow_rom.num_pages) << QXL_PAGE_BITS;
    info->n_surfaces = qxl->ssd.num_surfaces;
}


static void interface_release_resource(QXLInstance *sin,
                                       QXLReleaseInfoExt ext)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
    QXLReleaseRing *ring;
    uint64_t *item, id;

    if (!ext.info) {
        return;
    }
    if (ext.group_id == MEMSLOT_GROUP_HOST) {
        /* host group -> vga mode update request */
        QXLCommandExt *cmdext = (void *)(intptr_t)(ext.info->id);
        SimpleSpiceUpdate *update;
        g_assert(cmdext->cmd.type == QXL_CMD_DRAW);
        update = container_of(cmdext, SimpleSpiceUpdate, ext);
        qemu_spice_destroy_update(&qxl->ssd, update);
        return;
    }

    /*
     * ext->info points into guest-visible memory
     * pci bar 0, $command.release_info
     */
    ring = &qxl->ram->release_ring;
    SPICE_RING_PROD_ITEM(qxl, ring, item);
    if (!item) {
        return;
    }
    if (*item == 0) {
        /* stick head into the ring */
        id = ext.info->id;
        ext.info->next = 0;
        qxl_ram_set_dirty(qxl, &ext.info->next);
        *item = id;
        qxl_ring_set_dirty(qxl);
    } else {
        /* append item to the list */
        qxl->last_release->next = ext.info->id;
        qxl_ram_set_dirty(qxl, &qxl->last_release->next);
        ext.info->next = 0;
        qxl_ram_set_dirty(qxl, &ext.info->next);
    }
    qxl->last_release = ext.info;
    qxl->num_free_res++;
    trace_qxl_ring_res_put(qxl->id, qxl->num_free_res);
    qxl_push_free_res(qxl, 0);
}


void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
{
    trace_qxl_set_guest_bug(qxl->id);
    qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
    qxl->guest_bug = 1;
    if (qxl->guestdebug) {
        va_list ap;
        va_start(ap, msg);
        fprintf(stderr, "qxl-%d: guest bug: ", qxl->id);
        vfprintf(stderr, msg, ap);
        fprintf(stderr, "\n");
        va_end(ap);
    }
}


static void qxl_reset_memslots(PCIQXLDevice *d)
{
    qxl_spice_reset_memslots(d);
    memset(&d->guest_slots, 0, sizeof(d->guest_slots));
}


Goffset JPXStream::getPos() {
  return priv->counter * priv->ncomps + priv->ccounter;
}


get_upnp_rules_state_list(int max_rules_number_target)
{
	/*char ifname[IFNAMSIZ];*/
	int proto;
	unsigned short iport;
	unsigned int timestamp;
	struct rule_state * tmp;
	struct rule_state * list = 0;
	struct rule_state * * p;
	int i = 0;
	time_t current_time;

	/*ifname[0] = '\0';*/
	tmp = malloc(sizeof(struct rule_state));
	if(!tmp)
		return 0;
	current_time = upnp_time();
	nextruletoclean_timestamp = 0;
	while(get_redirect_rule_by_index(i, /*ifname*/0, &tmp->eport, 0, 0,
	                              &iport, &proto, 0, 0, 0,0, &timestamp,
								  &tmp->packets, &tmp->bytes) >= 0)
	{
		tmp->to_remove = 0;
		if(timestamp > 0) {
			/* need to remove this port mapping ? */
			if(timestamp <= (unsigned int)current_time)
				tmp->to_remove = 1;
			else if((nextruletoclean_timestamp <= (unsigned int)current_time)
			       || (timestamp < nextruletoclean_timestamp))
				nextruletoclean_timestamp = timestamp;
		}
		tmp->proto = (short)proto;
		/* add tmp to list */
		tmp->next = list;
		list = tmp;
		/* prepare next iteration */
		i++;
		tmp = malloc(sizeof(struct rule_state));
		if(!tmp)
			break;
	}
#ifdef PCP_PEER
	i=0;
	while(get_peer_rule_by_index(i, /*ifname*/0, &tmp->eport, 0, 0,
		                              &iport, &proto, 0, 0, 0,0,0, &timestamp,
									  &tmp->packets, &tmp->bytes) >= 0)
	{
		tmp->to_remove = 0;
		if(timestamp > 0) {
			/* need to remove this port mapping ? */
			if(timestamp <= (unsigned int)current_time)
				tmp->to_remove = 1;
			else if((nextruletoclean_timestamp <= (unsigned int)current_time)
				   || (timestamp < nextruletoclean_timestamp))
				nextruletoclean_timestamp = timestamp;
		}
		tmp->proto = (short)proto;
		/* add tmp to list */
		tmp->next = list;
		list = tmp;
		/* prepare next iteration */
		i++;
		tmp = malloc(sizeof(struct rule_state));
		if(!tmp)
			break;
	}
#endif
	free(tmp);
	/* remove the redirections that need to be removed */
	for(p = &list, tmp = list; tmp; tmp = *p)
	{
		if(tmp->to_remove)
		{
			syslog(LOG_NOTICE, "remove port mapping %hu %s because it has expired",
			       tmp->eport, proto_itoa(tmp->proto));
			_upnp_delete_redir(tmp->eport, tmp->proto);
			*p = tmp->next;
			free(tmp);
			i--;
		} else {
			p = &(tmp->next);
		}
	}
	/* return empty list if not enough redirections */
	if(i<=max_rules_number_target)
		while(list)
		{
			tmp = list;
			list = tmp->next;
			free(tmp);
		}
	/* return list */
	return list;
}


static int connecthostport(const char * host, unsigned short port, char * result)
{
	int s, n;
	char hostname[INET6_ADDRSTRLEN];
	char port_str[8], ifname[8], tmp[4];
	struct addrinfo *ai, *p;
	struct addrinfo hints;

	memset(&hints, 0, sizeof(hints));
	/* hints.ai_flags = AI_ADDRCONFIG; */
#ifdef AI_NUMERICSERV
	hints.ai_flags = AI_NUMERICSERV;
#endif
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
	/* hints.ai_protocol = IPPROTO_TCP; */
	snprintf(port_str, sizeof(port_str), "%hu", port);
	strcpy(hostname, host);
	if(!strncmp(host, "fe80", 4))
	{
		printf("Using an linklocal address\n");
		strcpy(ifname, "%");
		snprintf(tmp, sizeof(tmp), "%d", linklocal_index);
		strcat(ifname, tmp);
		strcat(hostname, ifname);
		printf("host: %s\n", hostname);
	}
	n = getaddrinfo(hostname, port_str, &hints, &ai);
	if(n != 0)
	{
		fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
		return -1;
	}
	s = -1;
	for(p = ai; p; p = p->ai_next)
	{
#ifdef DEBUG
		char tmp_host[256];
		char tmp_service[256];
		printf("ai_family=%d ai_socktype=%d ai_protocol=%d ai_addrlen=%d\n ",
		       p->ai_family, p->ai_socktype, p->ai_protocol, p->ai_addrlen);
		getnameinfo(p->ai_addr, p->ai_addrlen, tmp_host, sizeof(tmp_host),
		            tmp_service, sizeof(tmp_service),
		            NI_NUMERICHOST | NI_NUMERICSERV);
		printf(" host=%s service=%s\n", tmp_host, tmp_service);
#endif
		inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)p->ai_addr)->sin6_addr), result, INET6_ADDRSTRLEN);
		return 0;
	}
	freeaddrinfo(ai);
}


GetCommonLinkProperties(struct upnphttp * h, const char * action, const char * ns)
{
	/* WANAccessType : set depending on the hardware :
	 * DSL, POTS (plain old Telephone service), Cable, Ethernet */
	static const char resp[] =
		"<u:%sResponse "
		"xmlns:u=\"%s\">"
		"<NewWANAccessType>%s</NewWANAccessType>"
		"<NewLayer1UpstreamMaxBitRate>%lu</NewLayer1UpstreamMaxBitRate>"
		"<NewLayer1DownstreamMaxBitRate>%lu</NewLayer1DownstreamMaxBitRate>"
		"<NewPhysicalLinkStatus>%s</NewPhysicalLinkStatus>"
		"</u:%sResponse>";

	char body[2048];
	int bodylen;
	struct ifdata data;
	const char * status = "Up";	/* Up, Down (Required),
	                             * Initializing, Unavailable (Optional) */
	const char * wan_access_type = "Cable"; /* DSL, POTS, Cable, Ethernet */
	char ext_ip_addr[INET_ADDRSTRLEN];

	if((downstream_bitrate == 0) || (upstream_bitrate == 0))
	{
		if(getifstats(ext_if_name, &data) >= 0)
		{
			if(downstream_bitrate == 0) downstream_bitrate = data.baudrate;
			if(upstream_bitrate == 0) upstream_bitrate = data.baudrate;
		}
	}
	if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN, NULL, NULL) < 0) {
		status = "Down";
	}
	bodylen = snprintf(body, sizeof(body), resp,
	    action, ns, /* was "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
	    wan_access_type,
	    upstream_bitrate, downstream_bitrate,
	    status, action);
	BuildSendAndCloseSoapResp(h, body, bodylen);
}


AddPortMapping(struct upnphttp * h, const char * action, const char * ns)
{
	int r;

	/*static const char resp[] =
		"<u:AddPortMappingResponse "
		"xmlns:u=\"" SERVICE_TYPE_WANIPC "\"/>";*/
	static const char resp[] =
		"<u:%sResponse "
		"xmlns:u=\"%s\"/>";

	char body[512];
	int bodylen;
	struct NameValueParserData data;
	char * int_ip, * int_port, * ext_port, * protocol, * desc;
	char * leaseduration_str;
	unsigned int leaseduration;
	char * r_host;
	unsigned short iport, eport;

	struct hostent *hp; /* getbyhostname() */
	char ** ptr; /* getbyhostname() */
	struct in_addr result_ip;/*unsigned char result_ip[16];*/ /* inet_pton() */

	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
	int_ip = GetValueFromNameValueList(&data, "NewInternalClient");
	if (int_ip) {
		/* trim */
		while(int_ip[0] == ' ')
			int_ip++;
	}
#ifdef UPNP_STRICT
	if (!int_ip || int_ip[0] == '\0')
	{
		ClearNameValueList(&data);
		SoapError(h, 402, "Invalid Args");
		return;
	}
#endif

	/* IGD 2 MUST support both wildcard and specific IP address values
	 * for RemoteHost (only the wildcard value was REQUIRED in release 1.0) */
	r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
#ifndef SUPPORT_REMOTEHOST
#ifdef UPNP_STRICT
	if (r_host && (r_host[0] != '\0') && (0 != strcmp(r_host, "*")))
	{
		ClearNameValueList(&data);
		SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
		return;
	}
#endif
#endif

#ifndef UPNP_STRICT
	/* if <NewInternalClient> arg is empty, use client address
	 * see https://github.com/miniupnp/miniupnp/issues/236 */
	if (!int_ip || int_ip[0] == '\0')
	{
		int_ip = h->clientaddr_str;
		memcpy(&result_ip, &(h->clientaddr), sizeof(struct in_addr));
	}
	else
#endif
	/* if ip not valid assume hostname and convert */
	if (inet_pton(AF_INET, int_ip, &result_ip) <= 0)
	{
		hp = gethostbyname(int_ip);
		if(hp && hp->h_addrtype == AF_INET)
		{
			for(ptr = hp->h_addr_list; ptr && *ptr; ptr++)
		   	{
				int_ip = inet_ntoa(*((struct in_addr *) *ptr));
				result_ip = *((struct in_addr *) *ptr);
				/* TODO : deal with more than one ip per hostname */
				break;
			}
		}
		else
		{
			syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip);
			ClearNameValueList(&data);
			SoapError(h, 402, "Invalid Args");
			return;
		}
	}

	/* check if NewInternalAddress is the client address */
	if(GETFLAG(SECUREMODEMASK))
	{
		if(h->clientaddr.s_addr != result_ip.s_addr)
		{
			syslog(LOG_INFO, "Client %s tried to redirect port to %s",
			       inet_ntoa(h->clientaddr), int_ip);
			ClearNameValueList(&data);
			SoapError(h, 718, "ConflictInMappingEntry");
			return;
		}
	}

	int_port = GetValueFromNameValueList(&data, "NewInternalPort");
	ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
	protocol = GetValueFromNameValueList(&data, "NewProtocol");
	desc = GetValueFromNameValueList(&data, "NewPortMappingDescription");
	leaseduration_str = GetValueFromNameValueList(&data, "NewLeaseDuration");

	if (!int_port || !ext_port || !protocol)
	{
		ClearNameValueList(&data);
		SoapError(h, 402, "Invalid Args");
		return;
	}

	eport = (unsigned short)atoi(ext_port);
	iport = (unsigned short)atoi(int_port);

	if (strcmp(ext_port, "*") == 0 || eport == 0)
	{
		ClearNameValueList(&data);
		SoapError(h, 716, "Wildcard not permited in ExtPort");
		return;
	}

	leaseduration = leaseduration_str ? atoi(leaseduration_str) : 0;
#ifdef IGD_V2
	/* PortMappingLeaseDuration can be either a value between 1 and
	 * 604800 seconds or the zero value (for infinite lease time).
	 * Note that an infinite lease time can be only set by out-of-band
	 * mechanisms like WWW-administration, remote management or local
	 * management.
	 * If a control point uses the value 0 to indicate an infinite lease
	 * time mapping, it is REQUIRED that gateway uses the maximum value
	 * instead (e.g. 604800 seconds) */
	if(leaseduration == 0 || leaseduration > 604800)
		leaseduration = 604800;
#endif

	syslog(LOG_INFO, "%s: ext port %hu to %s:%hu protocol %s for: %s leaseduration=%u rhost=%s",
	       action, eport, int_ip, iport, protocol, desc, leaseduration,
	       r_host ? r_host : "NULL");

	r = upnp_redirect(r_host, eport, int_ip, iport, protocol, desc, leaseduration);

	ClearNameValueList(&data);

	/* possible error codes for AddPortMapping :
	 * 402 - Invalid Args
	 * 501 - Action Failed
	 * 715 - Wildcard not permited in SrcAddr
	 * 716 - Wildcard not permited in ExtPort
	 * 718 - ConflictInMappingEntry
	 * 724 - SamePortValuesRequired (deprecated in IGD v2)
     * 725 - OnlyPermanentLeasesSupported
             The NAT implementation only supports permanent lease times on
             port mappings (deprecated in IGD v2)
     * 726 - RemoteHostOnlySupportsWildcard
             RemoteHost must be a wildcard and cannot be a specific IP
             address or DNS name (deprecated in IGD v2)
     * 727 - ExternalPortOnlySupportsWildcard
             ExternalPort must be a wildcard and cannot be a specific port
             value (deprecated in IGD v2)
     * 728 - NoPortMapsAvailable
             There are not enough free ports available to complete the mapping
             (added in IGD v2)
	 * 729 - ConflictWithOtherMechanisms (added in IGD v2) */
	switch(r)
	{
	case 0:	/* success */
		bodylen = snprintf(body, sizeof(body), resp,
		                   action, ns/*SERVICE_TYPE_WANIPC*/);
		BuildSendAndCloseSoapResp(h, body, bodylen);
		break;
	case -4:
#ifdef IGD_V2
		SoapError(h, 729, "ConflictWithOtherMechanisms");
		break;
#endif /* IGD_V2 */
	case -2:	/* already redirected */
	case -3:	/* not permitted */
		SoapError(h, 718, "ConflictInMappingEntry");
		break;
	default:
		SoapError(h, 501, "ActionFailed");
	}
}


SoapError(struct upnphttp * h, int errCode, const char * errDesc)
{
	static const char resp[] =
		"<s:Envelope "
		"xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
		"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
		"<s:Body>"
		"<s:Fault>"
		"<faultcode>s:Client</faultcode>"
		"<faultstring>UPnPError</faultstring>"
		"<detail>"
		"<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">"
		"<errorCode>%d</errorCode>"
		"<errorDescription>%s</errorDescription>"
		"</UPnPError>"
		"</detail>"
		"</s:Fault>"
		"</s:Body>"
		"</s:Envelope>";

	char body[2048];
	int bodylen;

	syslog(LOG_INFO, "Returning UPnPError %d: %s", errCode, errDesc);
	bodylen = snprintf(body, sizeof(body), resp, errCode, errDesc);
	BuildResp2_upnphttp(h, 500, "Internal Server Error", body, bodylen);
	SendRespAndClose_upnphttp(h);
}


upnpevents_renewSubscription(const char * sid, int sidlen, int timeout)
{
	struct subscriber * sub;
	for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
		if((sidlen == 41) && (memcmp(sid, sub->uuid, 41) == 0)) {
#ifdef UPNP_STRICT
			/* check if the subscription already timeouted */
			if(sub->timeout && upnp_time() > sub->timeout)
				continue;
#endif
			sub->timeout = (timeout ? upnp_time() + timeout : 0);
			return sub->uuid;
		}
	}
	return NULL;
}


SendSSDPMSEARCHResponse(int s, const struct sockaddr * sockname,
                        const char * st, size_t st_len, const char * usn,
                        const char * server, const char * location)
{
	int l, n;
	char buf[1024];
	socklen_t sockname_len;
	/*
	 * follow guideline from document "UPnP Device Architecture 1.0"
	 * uppercase is recommended.
	 * DATE: is recommended
	 * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
	 * - check what to put in the 'Cache-Control' header
	 *
	 * have a look at the document "UPnP Device Architecture v1.1 */
	l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
		"CACHE-CONTROL: max-age=120\r\n"
		/*"DATE: ...\r\n"*/
		"ST: %.*s\r\n"
		"USN: %s\r\n"
		"EXT:\r\n"
		"SERVER: %s\r\n"
		"LOCATION: %s\r\n"
		"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
		"01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
		"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
		"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
		"\r\n",
		(int)st_len, st, usn,
		server, location,
		upnp_bootid, upnp_bootid, upnp_configid);
#ifdef ENABLE_IPV6
	sockname_len = (sockname->sa_family == PF_INET6)
	             ? sizeof(struct sockaddr_in6)
	             : sizeof(struct sockaddr_in);
#else	/* ENABLE_IPV6 */
	sockname_len = sizeof(struct sockaddr_in);
#endif	/* ENABLE_IPV6 */
	n = sendto_or_schedule(s, buf, l, 0, sockname, sockname_len);
	if(n < 0) {
		syslog(LOG_ERR, "%s: sendto(udp): %m", __func__);
	}
}


failure_log(address_item * addr, uschar * driver_kind, uschar * now)
{
void * reset_point;
gstring * g = reset_point = string_get(256);

/* Build up the log line for the message and main logs */

/* Create the address string for logging. Must not do this earlier, because
an OK result may be changed to FAIL when a pipe returns text. */

g = string_log_address(g, addr, LOGGING(all_parents), FALSE);

if (LOGGING(sender_on_delivery))
  g = string_append(g, 3, US" F=<", sender_address, US">");

if (*queue_name)
  g = string_append(g, 2, US" Q=", queue_name);

/* Return path may not be set if no delivery actually happened */

if (used_return_path && LOGGING(return_path_on_delivery))
  g = string_append(g, 3, US" P=<", used_return_path, US">");

if (addr->router)
  g = string_append(g, 2, US" R=", addr->router->name);
if (addr->transport)
  g = string_append(g, 2, US" T=", addr->transport->name);

if (addr->host_used)
  g = d_hostlog(g, addr);

#ifdef SUPPORT_TLS
g = d_tlslog(g, addr);
#endif

if (addr->basic_errno > 0)
  g = string_append(g, 2, US": ", US strerror(addr->basic_errno));

if (addr->message)
  g = string_append(g, 2, US": ", addr->message);

(void) string_from_gstring(g);

/* Do the logging. For the message log, "routing failed" for those cases,
just to make it clearer. */

if (driver_kind)
  deliver_msglog("%s %s failed for %s\n", now, driver_kind, g->s);
else
  deliver_msglog("%s %s\n", now, g->s);

log_write(0, LOG_MAIN, "** %s", g->s);

#ifndef DISABLE_EVENT
msg_event_raise(US"msg:fail:delivery", addr);
#endif

store_reset(reset_point);
return;
}