testcase_write_mangled(Solver *solv, const char *dir, int resultflags, const char *testcasename, const char *resultname)
{
  Pool *pool = solv->pool;
  Repo *repo;
  int i;
  Id arch, repoid;
  Id lowscore;
  FILE *fp;
  Strqueue sq;
  char *cmd, *out, *result;
  const char *s;

  if (!testcasename)
    testcasename = "testcase.t";
  if (!resultname)
    resultname = "solver.result";

#ifdef _WIN32
  if (mkdir(dir) && errno != EEXIST)
#else
  if (mkdir(dir, 0777) && errno != EEXIST)
#endif
    return pool_error(solv->pool, 0, "testcase_write: could not create directory '%s'", dir);
  strqueue_init(&sq);
  FOR_REPOS(repoid, repo)
    {
      const char *name = testcase_repoid2str(pool, repoid);
      char priobuf[50];
      if (repo->subpriority)
	sprintf(priobuf, "%d.%d", repo->priority, repo->subpriority);
      else
	sprintf(priobuf, "%d", repo->priority);
#if !defined(WITHOUT_COOKIEOPEN) && defined(ENABLE_ZLIB_COMPRESSION)
      out = pool_tmpjoin(pool, name, ".repo", ".gz");
#else
      out = pool_tmpjoin(pool, name, ".repo", 0);
#endif
      for (i = 0; out[i]; i++)
	if (out[i] == '/')
	  out[i] = '_';
      cmd = pool_tmpjoin(pool, "repo ", name, " ");
      cmd = pool_tmpappend(pool, cmd, priobuf, " ");
      cmd = pool_tmpappend(pool, cmd, "testtags ", out);
      strqueue_push(&sq, cmd);
      out = pool_tmpjoin(pool, dir, "/", out);
      if (!(fp = solv_xfopen(out, "w")))
	{
	  pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out);
	  strqueue_free(&sq);
	  return 0;
	}
      testcase_write_testtags(repo, fp);
      if (fclose(fp))
	{
	  pool_error(solv->pool, 0, "testcase_write: write error");
	  strqueue_free(&sq);
	  return 0;
	}
    }
  /* hmm, this is not optimal... we currently search for the lowest score */
  lowscore = 0;
  arch = pool->solvables[SYSTEMSOLVABLE].arch;
  for (i = 0; i < pool->lastarch; i++)
    {
      if (pool->id2arch[i] == 1 && !lowscore)
	arch = i;
      if (pool->id2arch[i] > 0x10000 && (!lowscore || pool->id2arch[i] < lowscore))
	{
	  arch = i;
	  lowscore = pool->id2arch[i];
	}
    }
  cmd = pool_tmpjoin(pool, "system ", pool->lastarch ? pool_id2str(pool, arch) : "-", 0);
  for (i = 0; disttype2str[i].str != 0; i++)
    if (pool->disttype == disttype2str[i].type)
      break;
  pool_tmpappend(pool, cmd, " ", disttype2str[i].str ? disttype2str[i].str : "unknown");
  if (pool->installed)
    cmd = pool_tmpappend(pool, cmd, " ", testcase_repoid2str(pool, pool->installed->repoid));
  strqueue_push(&sq, cmd);
  s = testcase_getpoolflags(solv->pool);
  if (*s)
    {
      cmd = pool_tmpjoin(pool, "poolflags ", s, 0);
      strqueue_push(&sq, cmd);
    }

  if (pool->vendorclasses)
    {
      cmd = 0;
      for (i = 0; pool->vendorclasses[i]; i++)
	{
	  cmd = pool_tmpappend(pool, cmd ? cmd : "vendorclass", " ", pool->vendorclasses[i]);
	  if (!pool->vendorclasses[i + 1])
	    {
	      strqueue_push(&sq, cmd);
	      cmd = 0;
	      i++;
	    }
	}
    }

  /* dump disabled packages (must come before the namespace/job lines) */
  if (pool->considered)
    {
      Id p;
      FOR_POOL_SOLVABLES(p)
	if (!MAPTST(pool->considered, p))
	  {
	    cmd = pool_tmpjoin(pool, "disable pkg ", testcase_solvid2str(pool, p), 0);
	    strqueue_push(&sq, cmd);
	  }
    }

  s = testcase_getsolverflags(solv);
  if (*s)
    {
      cmd = pool_tmpjoin(pool, "solverflags ", s, 0);
      strqueue_push(&sq, cmd);
    }

  /* now dump all the ns callback values we know */
  if (pool->nscallback)
    {
      Id rid;
      int d;
      for (rid = 1; rid < pool->nrels; rid++)
	{
	  Reldep *rd = pool->rels + rid;
	  if (rd->flags != REL_NAMESPACE || rd->name == NAMESPACE_OTHERPROVIDERS || rd->name == NAMESPACE_SPLITPROVIDES)
	    continue;
	  /* evaluate all namespace ids, skip empty results */
	  d = pool_whatprovides(pool, MAKERELDEP(rid));
	  if (!d || !pool->whatprovidesdata[d])
	    continue;
	  cmd = pool_tmpjoin(pool, "namespace ", pool_id2str(pool, rd->name), "(");
	  cmd = pool_tmpappend(pool, cmd, pool_id2str(pool, rd->evr), ")");
	  for (;  pool->whatprovidesdata[d]; d++)
	    cmd = pool_tmpappend(pool, cmd, " ", testcase_solvid2str(pool, pool->whatprovidesdata[d]));
	  strqueue_push(&sq, cmd);
	}
    }

  for (i = 0; i < solv->job.count; i += 2)
    {
      cmd = (char *)testcase_job2str(pool, solv->job.elements[i], solv->job.elements[i + 1]);
      cmd = pool_tmpjoin(pool, "job ", cmd, 0);
      strqueue_push(&sq, cmd);
    }

  if ((resultflags & ~TESTCASE_RESULT_REUSE_SOLVER) != 0)
    {
      cmd = 0;
      for (i = 0; resultflags2str[i].str; i++)
	if ((resultflags & resultflags2str[i].flag) != 0)
	  cmd = pool_tmpappend(pool, cmd, cmd ? "," : 0, resultflags2str[i].str);
      cmd = pool_tmpjoin(pool, "result ", cmd ? cmd : "?", 0);
      cmd = pool_tmpappend(pool, cmd, " ", resultname);
      strqueue_push(&sq, cmd);
      result = testcase_solverresult(solv, resultflags);
      if (!strcmp(resultname, "<inline>"))
	{
	  Strqueue rsq;
	  strqueue_init(&rsq);
	  strqueue_split(&rsq, result);
	  for (i = 0; i < rsq.nstr; i++)
	    {
	      cmd = pool_tmpjoin(pool, "#>", rsq.str[i], 0);
	      strqueue_push(&sq, cmd);
	    }
	  strqueue_free(&rsq);
	}
      else
	{
	  out = pool_tmpjoin(pool, dir, "/", resultname);
	  if (!(fp = fopen(out, "w")))
	    {
	      pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out);
	      solv_free(result);
	      strqueue_free(&sq);
	      return 0;
	    }
	  if (result && *result && fwrite(result, strlen(result), 1, fp) != 1)
	    {
	      pool_error(solv->pool, 0, "testcase_write: write error");
	      solv_free(result);
	      strqueue_free(&sq);
	      fclose(fp);
	      return 0;
	    }
	  if (fclose(fp))
	    {
	      pool_error(solv->pool, 0, "testcase_write: write error");
	      solv_free(result);
	      strqueue_free(&sq);
	      return 0;
	    }
	}
      solv_free(result);
    }

  result = strqueue_join(&sq);
  strqueue_free(&sq);
  out = pool_tmpjoin(pool, dir, "/", testcasename);
  if (!(fp = fopen(out, "w")))
    {
      pool_error(solv->pool, 0, "testcase_write: could not open '%s' for writing", out);
      solv_free(result);
      return 0;
    }
  if (*result && fwrite(result, strlen(result), 1, fp) != 1)
    {
      pool_error(solv->pool, 0, "testcase_write: write error");
      solv_free(result);
      fclose(fp);
      return 0;
    }
  if (fclose(fp))
    {
      pool_error(solv->pool, 0, "testcase_write: write error");
      solv_free(result);
      return 0;
    }
  solv_free(result);
  return 1;
}


strqueue_free(Strqueue *q)
{
  int i;
  for (i = 0; i < q->nstr; i++)
    solv_free(q->str[i]);
  q->str = solv_free(q->str);
  q->nstr = 0;
}


str2resultflags(Pool *pool, char *s)	/* modifies the string! */
{
  int i, resultflags = 0;
  while (s)
    {
      char *se = strchr(s, ',');
      if (se)
	*se++ = 0;
      for (i = 0; resultflags2str[i].str; i++)
	if (!strcmp(s, resultflags2str[i].str))
	  {
	    resultflags |= resultflags2str[i].flag;
	    break;
	  }
      if (!resultflags2str[i].str)
	pool_error(pool, 0, "result: unknown flag '%s'", s);
      s = se;
    }
  return resultflags;
}


ClientHttpRequest::onlyIfCached()const
{
    assert(request);
    return request->cache_control &&
           request->cache_control->hasOnlyIfCached();
}


ConnStateData::parseProxyProtocolHeader()
{
    try {
        const auto parsed = ProxyProtocol::Parse(inBuf);
        proxyProtocolHeader_ = parsed.header;
        assert(bool(proxyProtocolHeader_));
        inBuf.consume(parsed.size);
        needProxyProtocolHeader_ = false;
        if (proxyProtocolHeader_->hasForwardedAddresses()) {
            clientConnection->local = proxyProtocolHeader_->destinationAddress;
            clientConnection->remote = proxyProtocolHeader_->sourceAddress;
            if ((clientConnection->flags & COMM_TRANSPARENT))
                clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY spoofing of this new IP.
            debugs(33, 5, "PROXY/" << proxyProtocolHeader_->version() << " upgrade: " << clientConnection);
        }
    } catch (const Parser::BinaryTokenizer::InsufficientInput &) {
        debugs(33, 3, "PROXY protocol: waiting for more than " << inBuf.length() << " bytes");
        return false;
    } catch (const std::exception &e) {
        return proxyProtocolError(e.what());
    }
    return true;
}


ClientRequestContext::clientStoreIdStart()
{
    debugs(33, 5,"'" << http->uri << "'");

    if (Config.accessList.store_id) {
        acl_checklist = clientAclChecklistCreate(Config.accessList.store_id, http);
        acl_checklist->nonBlockingCheck(clientStoreIdAccessCheckDone, this);
    } else
        storeIdStart(http, clientStoreIdDoneWrapper, this);
}


ClientHttpRequest::sslBumpEstablish(Comm::Flag errflag)
{
    // Bail out quickly on Comm::ERR_CLOSING - close handlers will tidy up
    if (errflag == Comm::ERR_CLOSING)
        return;

    if (errflag) {
        debugs(85, 3, HERE << "CONNECT response failure in SslBump: " << errflag);
        getConn()->clientConnection->close();
        return;
    }

#if USE_AUTH
    // Preserve authentication info for the ssl-bumped request
    if (request->auth_user_request != NULL)
        getConn()->setAuth(request->auth_user_request, "SSL-bumped CONNECT");
#endif

    assert(sslBumpNeeded());
    getConn()->switchToHttps(this, sslBumpNeed_);
}


ConnStateData::handleSslBumpHandshakeError(const Security::IoResult &handshakeResult)
{
    auto errCategory = ERR_NONE;

    switch (handshakeResult.category) {
    case Security::IoResult::ioSuccess: {
        static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_SUCCESS");
        updateError(errCategory = ERR_GATEWAY_FAILURE, d);
        break;
    }

    case Security::IoResult::ioWantRead: {
        static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_READ");
        updateError(errCategory = ERR_GATEWAY_FAILURE, d);
        break;
    }

    case Security::IoResult::ioWantWrite: {
        static const auto d = MakeNamedErrorDetail("TLS_ACCEPT_UNEXPECTED_WRITE");
        updateError(errCategory = ERR_GATEWAY_FAILURE, d);
        break;
    }

    case Security::IoResult::ioError:
        debugs(83, (handshakeResult.important ? DBG_IMPORTANT : 2), "ERROR: " << handshakeResult.errorDescription <<
               " while SslBump-accepting a TLS connection on " << clientConnection << ": " << handshakeResult.errorDetail);
        updateError(errCategory = ERR_SECURE_ACCEPT_FAIL, handshakeResult.errorDetail);
        break;

    }

    if (!tunnelOnError(HttpRequestMethod(), errCategory))
        clientConnection->close();
}


prepareLogWithRequestDetails(HttpRequest * request, AccessLogEntry::Pointer &aLogEntry)
{
    assert(request);
    assert(aLogEntry != NULL);

    if (Config.onoff.log_mime_hdrs) {
        MemBuf mb;
        mb.init();
        request->header.packInto(&mb);
        //This is the request after adaptation or redirection
        aLogEntry->headers.adapted_request = xstrdup(mb.buf);

        // the virgin request is saved to aLogEntry->request
        if (aLogEntry->request) {
            mb.reset();
            aLogEntry->request->header.packInto(&mb);
            aLogEntry->headers.request = xstrdup(mb.buf);
        }

#if USE_ADAPTATION
        const Adaptation::History::Pointer ah = request->adaptLogHistory();
        if (ah != NULL) {
            mb.reset();
            ah->lastMeta.packInto(&mb);
            aLogEntry->adapt.last_meta = xstrdup(mb.buf);
        }
#endif

        mb.clean();
    }

#if ICAP_CLIENT
    const Adaptation::Icap::History::Pointer ih = request->icapHistory();
    if (ih != NULL)
        ih->processingTime(aLogEntry->icap.processingTime);
#endif

    aLogEntry->http.method = request->method;
    aLogEntry->http.version = request->http_ver;
    aLogEntry->hier = request->hier;
    aLogEntry->cache.extuser = request->extacl_user.termedBuf();

    // Adapted request, if any, inherits and then collects all the stats, but
    // the virgin request gets logged instead; copy the stats to log them.
    // TODO: avoid losses by keeping these stats in a shared history object?
    if (aLogEntry->request) {
        aLogEntry->request->dnsWait = request->dnsWait;
        aLogEntry->request->error = request->error;
    }
}


HttpHdrRange::~HttpHdrRange()
{
    while (!specs.empty()) {
        delete specs.back();
        specs.pop_back();
    }
}


ConnStateData::swanSong()
{
    debugs(33, 2, HERE << clientConnection);

    flags.readMore = false;
    clientdbEstablished(clientConnection->remote, -1);  /* decrement */

    terminateAll(ERR_NONE, LogTagsErrors());
    checkLogging();

    // XXX: Closing pinned conn is too harsh: The Client may want to continue!
    unpinConnection(true);

    Server::swanSong();

#if USE_AUTH
    // NP: do this bit after closing the connections to avoid side effects from unwanted TCP RST
    setAuth(NULL, "ConnStateData::SwanSong cleanup");
#endif

    flags.swanSang = true;
}


clientAccessCheckDoneWrapper(allow_t answer, void *data)
{
    ClientRequestContext *calloutContext = (ClientRequestContext *) data;

    if (!calloutContext->httpStateIsValid())
        return;

    calloutContext->clientAccessCheckDone(answer);
}


ClientHttpRequest::ClientHttpRequest(ConnStateData * aConn) :
#if USE_ADAPTATION
    AsyncJob("ClientHttpRequest"),
#endif
    request(NULL),
    uri(NULL),
    log_uri(NULL),
    req_sz(0),
    logType(LOG_TAG_NONE),
    calloutContext(NULL),
    maxReplyBodySize_(0),
    entry_(NULL),
    loggingEntry_(NULL),
    conn_(NULL)
#if USE_OPENSSL
    , sslBumpNeed_(Ssl::bumpEnd)
#endif
#if USE_ADAPTATION
    , request_satisfaction_mode(false)
    , request_satisfaction_offset(0)
#endif
{
    setConn(aConn);
    al = new AccessLogEntry;
    al->cache.start_time = current_time;
    if (aConn) {
        al->tcpClient = clientConnection = aConn->clientConnection;
        al->cache.port = aConn->port;
        al->cache.caddr = aConn->log_addr;

#if USE_OPENSSL
        if (aConn->clientConnection != NULL && aConn->clientConnection->isOpen()) {
            if (auto ssl = fd_table[aConn->clientConnection->fd].ssl.get())
                al->cache.sslClientCert.resetWithoutLocking(SSL_get_peer_certificate(ssl));
        }
#endif
    }
    dlinkAdd(this, &active, &ClientActiveRequests);
}


checkNoCacheDoneWrapper(allow_t answer, void *data)
{
    ClientRequestContext *calloutContext = (ClientRequestContext *) data;

    if (!calloutContext->httpStateIsValid())
        return;

    calloutContext->checkNoCacheDone(answer);
}


PolyPurge(string, class)	/* returns pointer to a purged copy */
    register char *string;
    register char class;
{
    register char *ptr;
    static char area[STRINGSIZE];
    ptr = area;
    while (*string)
    {
	if (!MatchClass(class, *string))
	{
	    *(ptr++) = *string;
	}
	string++;
    }
    *ptr = '\0';
    return (area);
}


MatchClass(class, input)
    register char class;
    register char input;
{
    register char c;
    register int retval;
    retval = 0;

    switch (class)
    {
	/* ESCAPE */

    case '?':			/* ?? -> ? */
	if (input == '?')
	{
	    retval = 1;
	}
	break;

	/* ILLOGICAL GROUPINGS (ie: not in ctype.h) */

    case 'V':
    case 'v':			/* vowels */
	c = CRACK_TOLOWER(input);
	if (strchr("aeiou", c))
	{
	    retval = 1;
	}
	break;

    case 'C':
    case 'c':			/* consonants */
	c = CRACK_TOLOWER(input);
	if (strchr("bcdfghjklmnpqrstvwxyz", c))
	{
	    retval = 1;
	}
	break;

    case 'W':
    case 'w':			/* whitespace */
	if (strchr("\t ", input))
	{
	    retval = 1;
	}
	break;

    case 'P':
    case 'p':			/* punctuation */
	if (strchr(".`,:;'!?\"", input))
	{
	    retval = 1;
	}
	break;

    case 'S':
    case 's':			/* symbols */
	if (strchr("$%%^&*()-_+=|\\[]{}#@/~", input))
	{
	    retval = 1;
	}
	break;

	/* LOGICAL GROUPINGS */

    case 'L':
    case 'l':			/* lowercase */
	if (islower(input))
	{
	    retval = 1;
	}
	break;

    case 'U':
    case 'u':			/* uppercase */
	if (isupper(input))
	{
	    retval = 1;
	}
	break;

    case 'A':
    case 'a':			/* alphabetic */
	if (isalpha(input))
	{
	    retval = 1;
	}
	break;

    case 'X':
    case 'x':			/* alphanumeric */
	if (isalnum(input))
	{
	    retval = 1;
	}
	break;

    case 'D':
    case 'd':			/* digits */
	if (isdigit(input))
	{
	    retval = 1;
	}
	break;

    default:
	Debug(1, "MatchClass: unknown class %c\n", class);
	return (0);
	break;
    }

    if (isupper(class))
    {
	return (!retval);
    }
    return (retval);
}


yin_getplace_for_extcomplex_flags(struct lyxml_elem *node, struct lys_ext_instance_complex *ext, LY_STMT stmt,
                                  uint16_t mask)
{
    void *data;
    struct lyext_substmt *info;

    data = lys_ext_complex_get_substmt(stmt, ext, &info);
    if (!data) {
        LOGVAL(ext->module->ctx, LYE_INCHILDSTMT, LY_VLOG_NONE, NULL, node->name, node->parent->name);
        return NULL;
    }
    if (info->cardinality < LY_STMT_CARD_SOME && ((*(uint16_t *)data) & mask)) {
        LOGVAL(ext->module->ctx, LYE_TOOMANY, LY_VLOG_NONE, NULL, node->name, node->parent->name);
        return NULL;
    }

    return data;
}


yin_read_module_(struct ly_ctx *ctx, struct lyxml_elem *yin, const char *revision, int implement)
{
    struct lys_module *module = NULL;
    struct unres_schema *unres;
    const char *value;
    int ret;
    uint8_t i;

    unres = calloc(1, sizeof *unres);
    LY_CHECK_ERR_RETURN(!unres, LOGMEM(ctx), NULL);

    /* check root element */
    if (!yin->name || strcmp(yin->name, "module")) {
        if (ly_strequal("submodule", yin->name, 0)) {
            LOGVAL(ctx, LYE_SUBMODULE, LY_VLOG_NONE, NULL);
        } else {
            LOGVAL(ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, yin->name);
        }
        goto error;
    }

    GETVAL(ctx, value, yin, "name");
    if (lyp_check_identifier(ctx, value, LY_IDENT_NAME, NULL, NULL)) {
        goto error;
    }

    /* in some really invalid situations there can be a circular import and
     * we can check it only after we have parsed the module name */
    for (i = 0; i < ctx->models.parsing_sub_modules_count; ++i) {
        if (!strcmp(ctx->models.parsing_sub_modules[i]->name, value)) {
            LOGVAL(ctx, LYE_CIRC_IMPORTS, LY_VLOG_NONE, NULL, value);
            goto error;
        }
    }

    module = calloc(1, sizeof *module);
    LY_CHECK_ERR_GOTO(!module, LOGMEM(ctx), error);

    module->ctx = ctx;
    module->name = lydict_insert(ctx, value, strlen(value));
    module->type = 0;
    module->implemented = (implement ? 1 : 0);

    /* add into the list of processed modules */
    if (lyp_check_circmod_add(module)) {
        goto error;
    }

    LOGVRB("Reading module \"%s\".", module->name);
    ret = read_sub_module(module, NULL, yin, unres);
    if (ret == -1) {
        goto error;
    }

    if (ret == 1) {
        assert(!unres->count);
    } else {
        /* make this module implemented if was not from start */
        if (!implement && module->implemented && (unres_schema_add_node(module, unres, NULL, UNRES_MOD_IMPLEMENT, NULL) == -1)) {
            goto error;
        }

        /* resolve rest of unres items */
        if (unres->count && resolve_unres_schema(module, unres)) {
            goto error;
        }

        /* check correctness of includes */
        if (lyp_check_include_missing(module)) {
            goto error;
        }
    }

    lyp_sort_revisions(module);

    if (lyp_rfn_apply_ext(module) || lyp_deviation_apply_ext(module)) {
        goto error;
    }

    if (revision) {
        /* check revision of the parsed model */
        if (!module->rev_size || strcmp(revision, module->rev[0].date)) {
            LOGVRB("Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").",
                   module->name, module->rev[0].date, revision);
            goto error;
        }
    }

    /* add into context if not already there */
    if (!ret) {
        if (lyp_ctx_add_module(module)) {
            goto error;
        }

        /* remove our submodules from the parsed submodules list */
        lyp_del_includedup(module, 0);
    } else {
        /* free what was parsed */
        lys_free(module, NULL, 0, 0);

        /* get the model from the context */
        module = (struct lys_module *)ly_ctx_get_module(ctx, value, revision, 0);
        assert(module);
    }

    unres_schema_free(NULL, &unres, 0);
    lyp_check_circmod_pop(ctx);
    LOGVRB("Module \"%s%s%s\" successfully parsed as %s.", module->name, (module->rev_size ? "@" : ""),
           (module->rev_size ? module->rev[0].date : ""), (module->implemented ? "implemented" : "imported"));
    return module;

error:
    /* cleanup */
    unres_schema_free(module, &unres, 1);

    if (!module) {
        if (ly_vecode(ctx) != LYVE_SUBMODULE) {
            LOGERR(ctx, ly_errno, "Module parsing failed.");
        }
        return NULL;
    }

    LOGERR(ctx, ly_errno, "Module \"%s\" parsing failed.", module->name);

    lyp_check_circmod_pop(ctx);
    lys_sub_module_remove_devs_augs(module);
    lyp_del_includedup(module, 1);
    lys_free(module, NULL, 0, 1);
    return NULL;
}


fill_yin_typedef(struct lys_module *module, struct lys_node *parent, struct lyxml_elem *yin, struct lys_tpdf *tpdf,
                 struct unres_schema *unres)
{
    const char *value;
    struct lyxml_elem *node, *next;
    struct ly_ctx *ctx = module->ctx;
    int rc, has_type = 0, c_ext = 0, i;
    void *reallocated;

    GETVAL(ctx, value, yin, "name");
    if (lyp_check_identifier(ctx, value, LY_IDENT_TYPE, module, parent)) {
        goto error;
    }
    tpdf->name = lydict_insert(ctx, value, strlen(value));

    /* generic part - status, description, reference */
    if (read_yin_common(module, NULL, tpdf, LYEXT_PAR_TPDF, yin, OPT_MODULE, unres)) {
        goto error;
    }

    LY_TREE_FOR_SAFE(yin->child, next, node) {
        if (strcmp(node->ns->value, LY_NSYIN)) {
            /* extension */
            YIN_CHECK_ARRAY_OVERFLOW_GOTO(ctx, c_ext, tpdf->ext_size, "extensions", "typedef", error);
            c_ext++;
            continue;
        } else if (!strcmp(node->name, "type")) {
            if (has_type) {
                LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_NONE, NULL, node->name, yin->name);
                goto error;
            }
            /* HACK for unres */
            tpdf->type.der = (struct lys_tpdf *)node;
            tpdf->type.parent = tpdf;
            if (unres_schema_add_node(module, unres, &tpdf->type, UNRES_TYPE_DER_TPDF, parent) == -1) {
                goto error;
            }
            has_type = 1;

            /* skip lyxml_free() at the end of the loop, node was freed or at least unlinked in unres processing */
            continue;
        } else if (!strcmp(node->name, "default")) {
            if (tpdf->dflt) {
                LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_NONE, NULL, node->name, yin->name);
                goto error;
            }
            GETVAL(ctx, value, node, "value");
            tpdf->dflt = lydict_insert(ctx, value, strlen(value));

            if (lyp_yin_parse_subnode_ext(module, tpdf, LYEXT_PAR_TPDF, node, LYEXT_SUBSTMT_DEFAULT, 0, unres)) {
                goto error;
            }
        } else if (!strcmp(node->name, "units")) {
            if (tpdf->units) {
                LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_NONE, NULL, node->name, yin->name);
                goto error;
            }
            GETVAL(ctx, value, node, "name");
            tpdf->units = lydict_insert(ctx, value, strlen(value));

            if (lyp_yin_parse_subnode_ext(module, tpdf, LYEXT_PAR_TPDF, node, LYEXT_SUBSTMT_UNITS, 0, unres)) {
                goto error;
            }
        } else {
            LOGVAL(ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, value);
            goto error;
        }

        lyxml_free(ctx, node);
    }

    /* check mandatory value */
    if (!has_type) {
        LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, "type", yin->name);
        goto error;
    }

    /* check default value (if not defined, there still could be some restrictions
     * that need to be checked against a default value from a derived type) */
    if (!(ctx->models.flags & LY_CTX_TRUSTED) &&
            unres_schema_add_node(module, unres, &tpdf->type, UNRES_TYPEDEF_DFLT, (struct lys_node *)(&tpdf->dflt)) == -1) {
        goto error;
    }

    /* finish extensions parsing */
    if (c_ext) {
        /* some extensions may be already present from the substatements */
        reallocated = realloc(tpdf->ext, (c_ext + tpdf->ext_size) * sizeof *tpdf->ext);
        LY_CHECK_ERR_GOTO(!reallocated, LOGMEM(ctx), error);
        tpdf->ext = reallocated;

        /* init memory */
        memset(&tpdf->ext[tpdf->ext_size], 0, c_ext * sizeof *tpdf->ext);

        LY_TREE_FOR_SAFE(yin->child, next, node) {
            rc = lyp_yin_fill_ext(tpdf, LYEXT_PAR_TYPE, 0, 0, module, node, &tpdf->ext, &tpdf->ext_size, unres);
            if (rc) {
                goto error;
            }
        }

        lyp_reduce_ext_list(&tpdf->ext, tpdf->ext_size, c_ext + tpdf->ext_size);
    }

    for (i = 0; i < tpdf->ext_size; ++i) {
        /* set flag, which represent LYEXT_OPT_VALID */
        if (tpdf->ext[i]->flags & LYEXT_OPT_VALID) {
            tpdf->flags |= LYS_VALID_EXT;
            break;
        }
    }

    return EXIT_SUCCESS;

error:
    return EXIT_FAILURE;
}


lyxml_free(struct ly_ctx *ctx, struct lyxml_elem *elem)
{
    FUN_IN;

    if (!elem) {
        return;
    }

    lyxml_unlink_elem(ctx, elem, 2);
    lyxml_free_elem(ctx, elem);
}


lyxml_dump_text(struct lyout *out, const char *text, LYXML_DATA_TYPE type)
{
    unsigned int i, n;

    if (!text) {
        return 0;
    }

    for (i = n = 0; text[i]; i++) {
        switch (text[i]) {
        case '&':
            n += ly_print(out, "&");
            break;
        case '<':
            n += ly_print(out, "<");
            break;
        case '>':
            /* not needed, just for readability */
            n += ly_print(out, ">");
            break;
        case '"':
            if (type == LYXML_DATA_ATTR) {
                n += ly_print(out, """);
                break;
            }
            /* falls through */
        default:
            ly_write(out, &text[i], 1);
            n++;
        }
    }

    return n;
}


lyxml_parse_path(struct ly_ctx *ctx, const char *filename, int options)
{
    FUN_IN;

    struct lyxml_elem *elem = NULL;
    size_t length;
    int fd;
    char *addr;

    if (!filename || !ctx) {
        LOGARG;
        return NULL;
    }

    fd = open(filename, O_RDONLY);
    if (fd == -1) {
        LOGERR(ctx, LY_EINVAL,"Opening file \"%s\" failed.", filename);
        return NULL;
    }
    if (lyp_mmap(ctx, fd, 0, &length, (void **)&addr)) {
        LOGERR(ctx, LY_ESYS, "Mapping file descriptor into memory failed (%s()).", __func__);
        goto error;
    } else if (!addr) {
        /* empty XML file */
        goto error;
    }

    elem = lyxml_parse_mem(ctx, addr, options);
    lyp_munmap(addr, length);
    close(fd);

    return elem;

error:
    if (fd != -1) {
        close(fd);
    }

    return NULL;
}


lyxml_free_withsiblings(struct ly_ctx *ctx, struct lyxml_elem *elem)
{
    FUN_IN;

    struct lyxml_elem *iter, *aux;

    if (!elem) {
        return;
    }

    /* optimization - avoid freeing (unlinking) the last node of the siblings list */
    /* so, first, free the node's predecessors to the beginning of the list ... */
    for(iter = elem->prev; iter->next; iter = aux) {
        aux = iter->prev;
        lyxml_free(ctx, iter);
    }
    /* ... then, the node is the first in the siblings list, so free them all */
    LY_TREE_FOR_SAFE(elem, aux, iter) {
        lyxml_free(ctx, iter);
    }
}


lys_ext_instance_presence(struct lys_ext *def, struct lys_ext_instance **ext, uint8_t ext_size)
{
    FUN_IN;

    uint8_t index;

    if (!def || (ext_size && !ext)) {
        LOGARG;
        return -1;
    }

    /* search for the extension instance */
    for (index = 0; index < ext_size; index++) {
        if (ext[index]->module->ctx == def->module->ctx) {
            /* from the same context */
            if (ext[index]->def == def) {
                return index;
            }
        } else {
            /* from different contexts */
            if (ly_strequal0(ext[index]->def->name, def->name)
                    && ly_strequal0(lys_main_module(ext[index]->def->module)->name, lys_main_module(def->module)->name)) {
                return index;
            }
        }
    }

    /* not found */
    return -1;
}


ly_load_plugins_dir(DIR *dir, const char *dir_path, int ext_or_type)
{
    struct dirent *file;
    size_t len;
    char *str, *name;
    void *dlhandler;
    int ret;

#ifdef STATIC
    return;
#endif /* STATIC */

    while ((file = readdir(dir))) {
        /* required format of the filename is *LY_PLUGIN_SUFFIX */
        len = strlen(file->d_name);
        if (len < LY_PLUGIN_SUFFIX_LEN + 1 ||
                strcmp(&file->d_name[len - LY_PLUGIN_SUFFIX_LEN], LY_PLUGIN_SUFFIX)) {
            continue;
        }

        /* and construct the filepath */
        if (asprintf(&str, "%s/%s", dir_path, file->d_name) == -1) {
            LOGMEM(NULL);
            return;
        }

        /* load the plugin */
        dlhandler = dlopen(str, RTLD_NOW);
        if (!dlhandler) {
            LOGERR(NULL, LY_ESYS, "Loading \"%s\" as a plugin failed (%s).", str, dlerror());
            free(str);
            continue;
        }
        if (ly_set_contains(&dlhandlers, dlhandler) != -1) {
            /* the plugin is already loaded */
            LOGVRB("Plugin \"%s\" already loaded.", str);
            free(str);

            /* keep the refcount of the shared object correct */
            dlclose(dlhandler);
            continue;
        }
        dlerror();    /* Clear any existing error */

        /* store the name without the suffix */
        name = strndup(file->d_name, len - LY_PLUGIN_SUFFIX_LEN);
        if (!name) {
            LOGMEM(NULL);
            dlclose(dlhandler);
            free(str);
            return;
        }

        if (ext_or_type) {
            ret = lyext_load_plugin(dlhandler, name);
        } else {
            ret = lytype_load_plugin(dlhandler, name);
        }
        if (!ret) {
            LOGVRB("Plugin \"%s\" successfully loaded.", str);
            /* spends name */
            ly_add_loaded_plugin(name);
            /* keep the handler */
            ly_set_add(&dlhandlers, dlhandler, LY_SET_OPT_USEASLIST);
        } else {
            free(name);
            dlclose(dlhandler);
        }
        free(str);

        if (ret == -1) {
            /* finish on error */
            break;
        }
    }
}


resolve_unres_schema_types(struct unres_schema *unres, enum UNRES_ITEM types, struct ly_ctx *ctx, int forward_ref,
                           int print_all_errors, uint32_t *resolved)
{
    uint32_t i, unres_count, res_count;
    int ret = 0, rc;
    struct ly_err_item *prev_eitem;
    enum int_log_opts prev_ilo;
    LY_ERR prev_ly_errno = LY_SUCCESS;
    struct ly_set *ext_parents = NULL, *ext_par_types = NULL;

    /* if there can be no forward references, every failure is final, so we can print it directly */
    if (forward_ref) {
        prev_ly_errno = ly_errno;
        ly_ilo_change(ctx, ILO_STORE, &prev_ilo, &prev_eitem);
    }

    if ((types & UNRES_EXT)) {
        ext_parents = ly_set_new();
        ext_par_types = ly_set_new();
        LY_CHECK_ERR_GOTO(!ext_parents || !ext_par_types, ret = -1, finish);
    }

    do {
        unres_count = 0;
        res_count = 0;

        for (i = 0; i < unres->count; ++i) {
            /* UNRES_TYPE_LEAFREF must be resolved (for storing leafref target pointers);
             * if-features are resolved here to make sure that we will have all if-features for
             * later check of feature circular dependency */
            if (unres->type[i] & types) {
                ++unres_count;
                rc = resolve_unres_schema_item(unres->module[i], unres->item[i], unres->type[i], unres->str_snode[i], unres);
                if (unres->type[i] == UNRES_EXT_FINALIZE) {
                    /* to avoid double free */
                    unres->type[i] = UNRES_RESOLVED;
                }

                if (unres->type[i] == UNRES_EXT) {
                    if (!rc) {
                        ret = save_skipped_ext_parent(ext_parents, ext_par_types,
                                *(struct lys_ext_instance***) unres->item[i],
                                (struct unres_ext*) unres->str_snode[i]);
                    }
                    if (!rc || rc == -1) {
                        free_ext_data(ctx, unres->str_snode[i]);
                    }
                    LY_CHECK_GOTO(ret, finish);
                }

                if (!rc || (unres->type[i] == UNRES_XPATH)) {
                    /* invalid XPath can never cause an error, only a warning */
                    if (unres->type[i] == UNRES_LIST_UNIQ) {
                        /* free the allocated structure */
                        free(unres->item[i]);
                    }

                    unres->type[i] = UNRES_RESOLVED;

                    ++(*resolved);
                    ++res_count;
                } else if ((rc == EXIT_FAILURE) && forward_ref) {
                    /* forward reference, erase errors */
                    ly_err_free_next(ctx, prev_eitem);
                } else if (print_all_errors) {
                    /* just so that we quit the loop */
                    ++res_count;
                    ret = -1;
                } else {
                    if (forward_ref) {
                        ly_ilo_restore(ctx, prev_ilo, prev_eitem, 1);
                    }
                    ret = -1;
                    goto finish;
                }
            }
        }
    } while (res_count && (res_count < unres_count));

    if (res_count < unres_count) {
        assert(forward_ref);
        /* just print the errors (but we must free the ones we have and get them again :-/ ) */
        ly_ilo_restore(ctx, prev_ilo, prev_eitem, 0);

        for (i = 0; i < unres->count; ++i) {
            if (unres->type[i] & types) {
                resolve_unres_schema_item(unres->module[i], unres->item[i], unres->type[i], unres->str_snode[i], unres);
            }
        }
        ret = -1;
        goto finish;
    }

    if (forward_ref) {
        /* restore log */
        ly_ilo_restore(ctx, prev_ilo, prev_eitem, 0);
        ly_errno = prev_ly_errno;
    }

    if ((types & UNRES_EXT)) {
        assert(ext_parents->number == ext_par_types->number);
        for (i = 0; i < ext_parents->number; ++i) {
            ret = compact_ext_list(ctx, ext_parents->set.g[i], (LYEXT_PAR)(uintptr_t)ext_parents->set.g[i]);
            LY_CHECK_GOTO(ret == -1, finish);
        }
    }

finish:
    ly_set_free(ext_parents);
    ly_set_free(ext_par_types);
    return ret;
}


resolve_augment(struct lys_node_augment *aug, struct lys_node *uses, struct unres_schema *unres)
{
    int rc;
    struct lys_node *sub, *next;
    struct lys_module *mod;
    struct ly_set *set;
    struct ly_ctx *ctx;
    struct lys_node_case *c;

    assert(aug);
    mod = lys_main_module(aug->module);
    ctx = mod->ctx;

    /* set it as not applied for now */
    aug->flags |= LYS_NOTAPPLIED;

    /* it can already be resolved in case we returned EXIT_FAILURE from if block below */
    if (!aug->target) {
        /* resolve target node */
        rc = resolve_schema_nodeid(aug->target_name, uses, (uses ? NULL : lys_node_module((struct lys_node *)aug)), &set, 0, 0);
        if (rc == -1) {
            LOGVAL(ctx, LYE_PATH, LY_VLOG_LYS, aug);
            return -1;
        }
        if (!set) {
            LOGVAL(ctx, LYE_INRESOLV, LY_VLOG_LYS, aug, "augment", aug->target_name);
            return EXIT_FAILURE;
        }
        aug->target = set->set.s[0];
        ly_set_free(set);
    }

    /* make this module implemented if the target module is (if the target is in an unimplemented module,
     * it is fine because when we will be making that module implemented, its augment will be applied
     * and that augment target module made implemented, recursively) */
    if (mod->implemented && !lys_node_module(aug->target)->implemented) {
        lys_node_module(aug->target)->implemented = 1;
        if (unres_schema_add_node(lys_node_module(aug->target), unres, NULL, UNRES_MOD_IMPLEMENT, NULL) == -1) {
            return -1;
        }
    }

    /* check for mandatory nodes - if the target node is in another module
     * the added nodes cannot be mandatory
     */
    if (!aug->parent && (lys_node_module((struct lys_node *)aug) != lys_node_module(aug->target))
            && (rc = lyp_check_mandatory_augment(aug, aug->target))) {
        return rc;
    }

    /* check augment target type and then augment nodes type */
    if (aug->target->nodetype & (LYS_CONTAINER | LYS_LIST)) {
        LY_TREE_FOR(aug->child, sub) {
            if (!(sub->nodetype & (LYS_ANYDATA | LYS_CONTAINER | LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_USES
                                   | LYS_CHOICE | LYS_ACTION | LYS_NOTIF))) {
                LOGVAL(ctx, LYE_INCHILDSTMT, LY_VLOG_LYS, aug, strnodetype(sub->nodetype), "augment");
                LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Cannot augment \"%s\" with a \"%s\".",
                       strnodetype(aug->target->nodetype), strnodetype(sub->nodetype));
                return -1;
            }
        }
    } else if (aug->target->nodetype & (LYS_CASE | LYS_INPUT | LYS_OUTPUT | LYS_NOTIF)) {
        LY_TREE_FOR(aug->child, sub) {
            if (!(sub->nodetype & (LYS_ANYDATA | LYS_CONTAINER | LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_USES | LYS_CHOICE))) {
                LOGVAL(ctx, LYE_INCHILDSTMT, LY_VLOG_LYS, aug, strnodetype(sub->nodetype), "augment");
                LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Cannot augment \"%s\" with a \"%s\".",
                       strnodetype(aug->target->nodetype), strnodetype(sub->nodetype));
                return -1;
            }
        }
    } else if (aug->target->nodetype == LYS_CHOICE) {
        LY_TREE_FOR(aug->child, sub) {
            if (!(sub->nodetype & (LYS_CASE | LYS_ANYDATA | LYS_CONTAINER | LYS_LEAF | LYS_LIST | LYS_LEAFLIST))) {
                LOGVAL(ctx, LYE_INCHILDSTMT, LY_VLOG_LYS, aug, strnodetype(sub->nodetype), "augment");
                LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Cannot augment \"%s\" with a \"%s\".",
                       strnodetype(aug->target->nodetype), strnodetype(sub->nodetype));
                return -1;
            }
        }
    } else {
        LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, aug, aug->target_name, "target-node");
        LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Invalid augment target node type \"%s\".", strnodetype(aug->target->nodetype));
        return -1;
    }

    /* check identifier uniqueness as in lys_node_addchild() */
    LY_TREE_FOR(aug->child, sub) {
        if (lys_check_id(sub, aug->target, NULL)) {
            return -1;
        }
    }

    if (aug->target->nodetype == LYS_CHOICE) {
        /* in the case of implicit cases, we have to create the nodes representing them
         * to serve as a target of the augments, they won't be printed, but they are expected in the tree */
        LY_TREE_FOR_SAFE(aug->child, next, sub) {
            if (sub->nodetype == LYS_CASE) {
                /* explicit case */
                continue;
            }

            c = calloc(1, sizeof *c);
            LY_CHECK_ERR_RETURN(!c, LOGMEM(ctx), EXIT_FAILURE);
            c->name = lydict_insert(ctx, sub->name, 0);
            c->flags = LYS_IMPLICIT | (sub->flags & LYS_CONFIG_MASK);
            c->module = sub->module;
            c->nodetype = LYS_CASE;
            c->parent = sub->parent;
            c->prev = sub->prev != sub ? sub->prev : (struct lys_node*)c;
            if (c->prev->next) {
                c->prev->next = (struct lys_node*)c;
            } else {
                c->parent->child = (struct lys_node*)c;
            }
            c->next = sub->next;
            if (c->next) {
                c->next->prev = (struct lys_node*)c;
            } else {
                c->parent->child->prev = (struct lys_node*)c;
            }
            c->child = sub;
            sub->prev = sub;
            sub->next = NULL;
            sub->parent = (struct lys_node*)c;
        }
    }

    LY_TREE_DFS_BEGIN(aug->child, next, sub) {
        if (sub->nodetype == LYS_ACTION) {
            /* we need to check parents */
            for (sub = aug->target; sub; sub = lys_parent(sub)) {
                if ((sub->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))
                        || ((sub->nodetype == LYS_LIST) && !((struct lys_node_list *)sub)->keys)) {
                    LOGVAL(ctx, LYE_INPAR, LY_VLOG_LYS, aug->target, strnodetype(sub->nodetype), "action");
                    LOGVAL(ctx, LYE_INRESOLV, LY_VLOG_LYS, aug, "augment", aug->target_name);
                    return -1;
                }
            }
            break;
        }
        LY_TREE_DFS_END(aug->child, next, sub);
    }

    if (!aug->child) {
        /* empty augment, nothing to connect, but it is techincally applied */
        LOGWRN(ctx, "Augment \"%s\" without children.", aug->target_name);
        aug->flags &= ~LYS_NOTAPPLIED;
    } else if ((aug->parent || mod->implemented) && apply_aug(aug, unres)) {
        /* we try to connect the augment only in case the module is implemented or
         * the augment applies on the used grouping, anyway we failed here */
        return -1;
    }

    return EXIT_SUCCESS;
}


lys_ext_instance_substmt(const struct lys_ext_instance *ext)
{
    FUN_IN;

    if (!ext) {
        return NULL;
    }

    switch (ext->insubstmt) {
    case LYEXT_SUBSTMT_SELF:
    case LYEXT_SUBSTMT_MODIFIER:
    case LYEXT_SUBSTMT_VERSION:
        return NULL;
    case LYEXT_SUBSTMT_ARGUMENT:
        if (ext->parent_type == LYEXT_PAR_EXT) {
            return ((struct lys_ext_instance*)ext->parent)->arg_value;
        }
        break;
    case LYEXT_SUBSTMT_BASE:
        if (ext->parent_type == LYEXT_PAR_TYPE) {
            return ((struct lys_type*)ext->parent)->info.ident.ref[ext->insubstmt_index];
        } else if (ext->parent_type == LYEXT_PAR_IDENT) {
            return ((struct lys_ident*)ext->parent)->base[ext->insubstmt_index];
        }
        break;
    case LYEXT_SUBSTMT_BELONGSTO:
        if (ext->parent_type == LYEXT_PAR_MODULE && ((struct lys_module*)ext->parent)->type) {
            return ((struct lys_submodule*)ext->parent)->belongsto;
        }
        break;
    case LYEXT_SUBSTMT_CONFIG:
    case LYEXT_SUBSTMT_MANDATORY:
        if (ext->parent_type == LYEXT_PAR_NODE) {
            return &((struct lys_node*)ext->parent)->flags;
        } else if (ext->parent_type == LYEXT_PAR_DEVIATE) {
            return &((struct lys_deviate*)ext->parent)->flags;
        } else if (ext->parent_type == LYEXT_PAR_REFINE) {
            return &((struct lys_refine*)ext->parent)->flags;
        }
        break;
    case LYEXT_SUBSTMT_CONTACT:
        if (ext->parent_type == LYEXT_PAR_MODULE) {
            return ((struct lys_module*)ext->parent)->contact;
        }
        break;
    case LYEXT_SUBSTMT_DEFAULT:
        if (ext->parent_type == LYEXT_PAR_NODE) {
            switch (((struct lys_node*)ext->parent)->nodetype) {
            case LYS_LEAF:
            case LYS_LEAFLIST:
                /* in case of leaf, the index is supposed to be 0, so it will return the
                 * correct pointer despite the leaf structure does not have dflt as array */
                return ((struct lys_node_leaflist*)ext->parent)->dflt[ext->insubstmt_index];
            case LYS_CHOICE:
                return ((struct lys_node_choice*)ext->parent)->dflt;
            default:
                /* internal error */
                break;
            }
        } else if (ext->parent_type == LYEXT_PAR_TPDF) {
            return ((struct lys_tpdf*)ext->parent)->dflt;
        } else if (ext->parent_type == LYEXT_PAR_DEVIATE) {
            return ((struct lys_deviate*)ext->parent)->dflt[ext->insubstmt_index];
        } else if (ext->parent_type == LYEXT_PAR_REFINE) {
            return &((struct lys_refine*)ext->parent)->dflt[ext->insubstmt_index];
        }
        break;
    case LYEXT_SUBSTMT_DESCRIPTION:
        switch (ext->parent_type) {
        case LYEXT_PAR_NODE:
            return ((struct lys_node*)ext->parent)->dsc;
        case LYEXT_PAR_MODULE:
            return ((struct lys_module*)ext->parent)->dsc;
        case LYEXT_PAR_IMPORT:
            return ((struct lys_import*)ext->parent)->dsc;
        case LYEXT_PAR_INCLUDE:
            return ((struct lys_include*)ext->parent)->dsc;
        case LYEXT_PAR_EXT:
            return ((struct lys_ext*)ext->parent)->dsc;
        case LYEXT_PAR_FEATURE:
            return ((struct lys_feature*)ext->parent)->dsc;
        case LYEXT_PAR_TPDF:
            return ((struct lys_tpdf*)ext->parent)->dsc;
        case LYEXT_PAR_TYPE_BIT:
            return ((struct lys_type_bit*)ext->parent)->dsc;
        case LYEXT_PAR_TYPE_ENUM:
            return ((struct lys_type_enum*)ext->parent)->dsc;
        case LYEXT_PAR_RESTR:
            return ((struct lys_restr*)ext->parent)->dsc;
        case LYEXT_PAR_WHEN:
            return ((struct lys_when*)ext->parent)->dsc;
        case LYEXT_PAR_IDENT:
            return ((struct lys_ident*)ext->parent)->dsc;
        case LYEXT_PAR_DEVIATION:
            return ((struct lys_deviation*)ext->parent)->dsc;
        case LYEXT_PAR_REVISION:
            return ((struct lys_revision*)ext->parent)->dsc;
        case LYEXT_PAR_REFINE:
            return ((struct lys_refine*)ext->parent)->dsc;
        default:
            break;
        }
        break;
    case LYEXT_SUBSTMT_ERRTAG:
        if (ext->parent_type == LYEXT_PAR_RESTR) {
            return ((struct lys_restr*)ext->parent)->eapptag;
        }
        break;
    case LYEXT_SUBSTMT_ERRMSG:
        if (ext->parent_type == LYEXT_PAR_RESTR) {
            return ((struct lys_restr*)ext->parent)->emsg;
        }
        break;
    case LYEXT_SUBSTMT_DIGITS:
        if (ext->parent_type == LYEXT_PAR_TYPE && ((struct lys_type*)ext->parent)->base == LY_TYPE_DEC64) {
            return &((struct lys_type*)ext->parent)->info.dec64.dig;
        }
        break;
    case LYEXT_SUBSTMT_KEY:
        if (ext->parent_type == LYEXT_PAR_NODE && ((struct lys_node*)ext->parent)->nodetype == LYS_LIST) {
            return ((struct lys_node_list*)ext->parent)->keys;
        }
        break;
    case LYEXT_SUBSTMT_MAX:
        if (ext->parent_type == LYEXT_PAR_NODE) {
            if (((struct lys_node*)ext->parent)->nodetype == LYS_LIST) {
                return &((struct lys_node_list*)ext->parent)->max;
            } else if (((struct lys_node*)ext->parent)->nodetype == LYS_LEAFLIST) {
                return &((struct lys_node_leaflist*)ext->parent)->max;
            }
        } else if (ext->parent_type == LYEXT_PAR_REFINE) {
            return &((struct lys_refine*)ext->parent)->mod.list.max;
        }
        break;
    case LYEXT_SUBSTMT_MIN:
        if (ext->parent_type == LYEXT_PAR_NODE) {
            if (((struct lys_node*)ext->parent)->nodetype == LYS_LIST) {
                return &((struct lys_node_list*)ext->parent)->min;
            } else if (((struct lys_node*)ext->parent)->nodetype == LYS_LEAFLIST) {
                return &((struct lys_node_leaflist*)ext->parent)->min;
            }
        } else if (ext->parent_type == LYEXT_PAR_REFINE) {
            return &((struct lys_refine*)ext->parent)->mod.list.min;
        }
        break;
    case LYEXT_SUBSTMT_NAMESPACE:
        if (ext->parent_type == LYEXT_PAR_MODULE && !((struct lys_module*)ext->parent)->type) {
            return ((struct lys_module*)ext->parent)->ns;
        }
        break;
    case LYEXT_SUBSTMT_ORDEREDBY:
        if (ext->parent_type == LYEXT_PAR_NODE &&
                (((struct lys_node*)ext->parent)->nodetype & (LYS_LIST | LYS_LEAFLIST))) {
            return &((struct lys_node_list*)ext->parent)->flags;
        }
        break;
    case LYEXT_SUBSTMT_ORGANIZATION:
        if (ext->parent_type == LYEXT_PAR_MODULE) {
            return ((struct lys_module*)ext->parent)->org;
        }
        break;
    case LYEXT_SUBSTMT_PATH:
        if (ext->parent_type == LYEXT_PAR_TYPE && ((struct lys_type*)ext->parent)->base == LY_TYPE_LEAFREF) {
            return ((struct lys_type*)ext->parent)->info.lref.path;
        }
        break;
    case LYEXT_SUBSTMT_POSITION:
        if (ext->parent_type == LYEXT_PAR_TYPE_BIT) {
            return &((struct lys_type_bit*)ext->parent)->pos;
        }
        break;
    case LYEXT_SUBSTMT_PREFIX:
        if (ext->parent_type == LYEXT_PAR_MODULE) {
            /* covers also lys_submodule */
            return ((struct lys_module*)ext->parent)->prefix;
        } else if (ext->parent_type == LYEXT_PAR_IMPORT) {
            return ((struct lys_import*)ext->parent)->prefix;
        }
        break;
    case LYEXT_SUBSTMT_PRESENCE:
        if (ext->parent_type == LYEXT_PAR_NODE && ((struct lys_node*)ext->parent)->nodetype == LYS_CONTAINER) {
            return ((struct lys_node_container*)ext->parent)->presence;
        } else if (ext->parent_type == LYEXT_PAR_REFINE) {
            return ((struct lys_refine*)ext->parent)->mod.presence;
        }
        break;
    case LYEXT_SUBSTMT_REFERENCE:
        switch (ext->parent_type) {
        case LYEXT_PAR_NODE:
            return ((struct lys_node*)ext->parent)->ref;
        case LYEXT_PAR_MODULE:
            return ((struct lys_module*)ext->parent)->ref;
        case LYEXT_PAR_IMPORT:
            return ((struct lys_import*)ext->parent)->ref;
        case LYEXT_PAR_INCLUDE:
            return ((struct lys_include*)ext->parent)->ref;
        case LYEXT_PAR_EXT:
            return ((struct lys_ext*)ext->parent)->ref;
        case LYEXT_PAR_FEATURE:
            return ((struct lys_feature*)ext->parent)->ref;
        case LYEXT_PAR_TPDF:
            return ((struct lys_tpdf*)ext->parent)->ref;
        case LYEXT_PAR_TYPE_BIT:
            return ((struct lys_type_bit*)ext->parent)->ref;
        case LYEXT_PAR_TYPE_ENUM:
            return ((struct lys_type_enum*)ext->parent)->ref;
        case LYEXT_PAR_RESTR:
            return ((struct lys_restr*)ext->parent)->ref;
        case LYEXT_PAR_WHEN:
            return ((struct lys_when*)ext->parent)->ref;
        case LYEXT_PAR_IDENT:
            return ((struct lys_ident*)ext->parent)->ref;
        case LYEXT_PAR_DEVIATION:
            return ((struct lys_deviation*)ext->parent)->ref;
        case LYEXT_PAR_REVISION:
            return ((struct lys_revision*)ext->parent)->ref;
        case LYEXT_PAR_REFINE:
            return ((struct lys_refine*)ext->parent)->ref;
        default:
            break;
        }
        break;
    case LYEXT_SUBSTMT_REQINSTANCE:
        if (ext->parent_type == LYEXT_PAR_TYPE) {
            if (((struct lys_type*)ext->parent)->base == LY_TYPE_LEAFREF) {
                return &((struct lys_type*)ext->parent)->info.lref.req;
            } else if (((struct lys_type*)ext->parent)->base == LY_TYPE_INST) {
                return &((struct lys_type*)ext->parent)->info.inst.req;
            }
        }
        break;
    case LYEXT_SUBSTMT_REVISIONDATE:
        if (ext->parent_type == LYEXT_PAR_IMPORT) {
            return ((struct lys_import*)ext->parent)->rev;
        } else if (ext->parent_type == LYEXT_PAR_INCLUDE) {
            return ((struct lys_include*)ext->parent)->rev;
        }
        break;
    case LYEXT_SUBSTMT_STATUS:
        switch (ext->parent_type) {
        case LYEXT_PAR_NODE:
        case LYEXT_PAR_IDENT:
        case LYEXT_PAR_TPDF:
        case LYEXT_PAR_EXT:
        case LYEXT_PAR_FEATURE:
        case LYEXT_PAR_TYPE_ENUM:
        case LYEXT_PAR_TYPE_BIT:
            /* in all structures the flags member is at the same offset */
            return &((struct lys_node*)ext->parent)->flags;
        default:
            break;
        }
        break;
    case LYEXT_SUBSTMT_UNIQUE:
        if (ext->parent_type == LYEXT_PAR_DEVIATE) {
            return &((struct lys_deviate*)ext->parent)->unique[ext->insubstmt_index];
        } else if (ext->parent_type == LYEXT_PAR_NODE && ((struct lys_node*)ext->parent)->nodetype == LYS_LIST) {
            return &((struct lys_node_list*)ext->parent)->unique[ext->insubstmt_index];
        }
        break;
    case LYEXT_SUBSTMT_UNITS:
        if (ext->parent_type == LYEXT_PAR_NODE &&
                (((struct lys_node*)ext->parent)->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
            /* units is at the same offset in both lys_node_leaf and lys_node_leaflist */
            return ((struct lys_node_leaf*)ext->parent)->units;
        } else if (ext->parent_type == LYEXT_PAR_TPDF) {
            return ((struct lys_tpdf*)ext->parent)->units;
        } else if (ext->parent_type == LYEXT_PAR_DEVIATE) {
            return ((struct lys_deviate*)ext->parent)->units;
        }
        break;
    case LYEXT_SUBSTMT_VALUE:
        if (ext->parent_type == LYEXT_PAR_TYPE_ENUM) {
            return &((struct lys_type_enum*)ext->parent)->value;
        }
        break;
    case LYEXT_SUBSTMT_YINELEM:
        if (ext->parent_type == LYEXT_PAR_EXT) {
            return &((struct lys_ext*)ext->parent)->flags;
        }
        break;
    }
    LOGINT(ext->module->ctx);
    return NULL;
}


lys_extension_instances_free(struct ly_ctx *ctx, struct lys_ext_instance **e, unsigned int size,
                             void (*private_destructor)(const struct lys_node *node, void *priv))
{
    unsigned int i, j, k;
    struct lyext_substmt *substmt;
    void **pp, **start;
    struct lys_node *siter, *snext;

#define EXTCOMPLEX_FREE_STRUCT(STMT, TYPE, FUNC, FREE, ARGS...)                               \
    pp = lys_ext_complex_get_substmt(STMT, (struct lys_ext_instance_complex *)e[i], NULL);    \
    if (!pp || !(*pp)) { break; }                                                             \
    if (substmt[j].cardinality >= LY_STMT_CARD_SOME) { /* process array */                    \
        for (start = pp = *pp; *pp; pp++) {                                                   \
            FUNC(ctx, (TYPE *)(*pp), ##ARGS, private_destructor);                             \
            if (FREE) { free(*pp); }                                                          \
        }                                                                                \
        free(start);                                                                          \
    } else { /* single item */                                                                \
        FUNC(ctx, (TYPE *)(*pp), ##ARGS, private_destructor);                                 \
        if (FREE) { free(*pp); }                                                              \
    }

    if (!size || !e) {
        return;
    }

    for (i = 0; i < size; i++) {
        if (!e[i]) {
            continue;
        }

        if (e[i]->flags & (LYEXT_OPT_INHERIT)) {
            /* no free, this is just a shadow copy of the original extension instance */
        } else {
            if (e[i]->flags & (LYEXT_OPT_YANG)) {
                free(e[i]->def);     /* remove name of instance extension */
                e[i]->def = NULL;
                yang_free_ext_data((struct yang_ext_substmt *)e[i]->parent); /* remove backup part of yang file */
            }
            /* remove private object */
            if (e[i]->priv && private_destructor) {
                private_destructor((struct lys_node*)e[i], e[i]->priv);
            }
            lys_extension_instances_free(ctx, e[i]->ext, e[i]->ext_size, private_destructor);
            lydict_remove(ctx, e[i]->arg_value);
        }

        if (e[i]->def && e[i]->def->plugin && e[i]->def->plugin->type == LYEXT_COMPLEX
                && ((e[i]->flags & LYEXT_OPT_CONTENT) == 0)) {
            substmt = ((struct lys_ext_instance_complex *)e[i])->substmt;
            for (j = 0; substmt[j].stmt; j++) {
                switch(substmt[j].stmt) {
                case LY_STMT_DESCRIPTION:
                case LY_STMT_REFERENCE:
                case LY_STMT_UNITS:
                case LY_STMT_ARGUMENT:
                case LY_STMT_DEFAULT:
                case LY_STMT_ERRTAG:
                case LY_STMT_ERRMSG:
                case LY_STMT_PREFIX:
                case LY_STMT_NAMESPACE:
                case LY_STMT_PRESENCE:
                case LY_STMT_REVISIONDATE:
                case LY_STMT_KEY:
                case LY_STMT_BASE:
                case LY_STMT_BELONGSTO:
                case LY_STMT_CONTACT:
                case LY_STMT_ORGANIZATION:
                case LY_STMT_PATH:
                    lys_extcomplex_free_str(ctx, (struct lys_ext_instance_complex *)e[i], substmt[j].stmt);
                    break;
                case LY_STMT_TYPE:
                    EXTCOMPLEX_FREE_STRUCT(LY_STMT_TYPE, struct lys_type, lys_type_free, 1);
                    break;
                case LY_STMT_TYPEDEF:
                    EXTCOMPLEX_FREE_STRUCT(LY_STMT_TYPEDEF, struct lys_tpdf, lys_tpdf_free, 1);
                    break;
                case LY_STMT_IFFEATURE:
                    EXTCOMPLEX_FREE_STRUCT(LY_STMT_IFFEATURE, struct lys_iffeature, lys_iffeature_free, 0, 1, 0);
                    break;
                case LY_STMT_MAX:
                case LY_STMT_MIN:
                case LY_STMT_POSITION:
                case LY_STMT_VALUE:
                    pp = (void**)&((struct lys_ext_instance_complex *)e[i])->content[substmt[j].offset];
                    if (substmt[j].cardinality >= LY_STMT_CARD_SOME && *pp) {
                        for(k = 0; ((uint32_t**)(*pp))[k]; k++) {
                            free(((uint32_t**)(*pp))[k]);
                        }
                    }
                    free(*pp);
                    break;
                case LY_STMT_DIGITS:
                    if (substmt[j].cardinality >= LY_STMT_CARD_SOME) {
                        /* free the array */
                        pp = (void**)&((struct lys_ext_instance_complex *)e[i])->content[substmt[j].offset];
                        free(*pp);
                    }
                    break;
                case LY_STMT_MODULE:
                    /* modules are part of the context, so they will be freed there */
                    if (substmt[j].cardinality >= LY_STMT_CARD_SOME) {
                        /* free the array */
                        pp = (void**)&((struct lys_ext_instance_complex *)e[i])->content[substmt[j].offset];
                        free(*pp);
                    }
                    break;
                case LY_STMT_ACTION:
                case LY_STMT_ANYDATA:
                case LY_STMT_ANYXML:
                case LY_STMT_CASE:
                case LY_STMT_CHOICE:
                case LY_STMT_CONTAINER:
                case LY_STMT_GROUPING:
                case LY_STMT_INPUT:
                case LY_STMT_LEAF:
                case LY_STMT_LEAFLIST:
                case LY_STMT_LIST:
                case LY_STMT_NOTIFICATION:
                case LY_STMT_OUTPUT:
                case LY_STMT_RPC:
                case LY_STMT_USES:
                    pp = (void**)&((struct lys_ext_instance_complex *)e[i])->content[substmt[j].offset];
                    LY_TREE_FOR_SAFE((struct lys_node *)(*pp), snext, siter) {
                        lys_node_free(ctx, siter, NULL, 0);
                    }
                    *pp = NULL;
                    break;
                case LY_STMT_UNIQUE:
                    pp = lys_ext_complex_get_substmt(LY_STMT_UNIQUE, (struct lys_ext_instance_complex *)e[i], NULL);
                    if (!pp || !(*pp)) {
                        break;
                    }
                    if (substmt[j].cardinality >= LY_STMT_CARD_SOME) { /* process array */
                        for (start = pp = *pp; *pp; pp++) {
                            for (k = 0; k < (*(struct lys_unique**)pp)->expr_size; k++) {
                                lydict_remove(ctx, (*(struct lys_unique**)pp)->expr[k]);
                            }
                            free((*(struct lys_unique**)pp)->expr);
                            free(*pp);
                        }
                        free(start);
                    } else { /* single item */
                        for (k = 0; k < (*(struct lys_unique**)pp)->expr_size; k++) {
                            lydict_remove(ctx, (*(struct lys_unique**)pp)->expr[k]);
                        }
                        free((*(struct lys_unique**)pp)->expr);
                        free(*pp);
                    }
                    break;
                case LY_STMT_LENGTH:
                case LY_STMT_MUST:
                case LY_STMT_PATTERN:
                case LY_STMT_RANGE:
                    EXTCOMPLEX_FREE_STRUCT(substmt[j].stmt, struct lys_restr, lys_restr_free, 1);
                    break;
                case LY_STMT_WHEN:
                    EXTCOMPLEX_FREE_STRUCT(LY_STMT_WHEN, struct lys_when, lys_when_free, 0);
                    break;
                case LY_STMT_REVISION:
                    pp = lys_ext_complex_get_substmt(LY_STMT_REVISION, (struct lys_ext_instance_complex *)e[i], NULL);
                    if (!pp || !(*pp)) {
                        break;
                    }
                    if (substmt[j].cardinality >= LY_STMT_CARD_SOME) { /* process array */
                        for (start = pp = *pp; *pp; pp++) {
                            lydict_remove(ctx, (*(struct lys_revision**)pp)->dsc);
                            lydict_remove(ctx, (*(struct lys_revision**)pp)->ref);
                            lys_extension_instances_free(ctx, (*(struct lys_revision**)pp)->ext,
                                                         (*(struct lys_revision**)pp)->ext_size, private_destructor);
                            free(*pp);
                        }
                        free(start);
                    } else { /* single item */
                        lydict_remove(ctx, (*(struct lys_revision**)pp)->dsc);
                        lydict_remove(ctx, (*(struct lys_revision**)pp)->ref);
                        lys_extension_instances_free(ctx, (*(struct lys_revision**)pp)->ext,
                                                     (*(struct lys_revision**)pp)->ext_size, private_destructor);
                        free(*pp);
                    }
                    break;
                default:
                    /* nothing to free */
                    break;
                }
            }
        }

        free(e[i]);
    }
    free(e);

#undef EXTCOMPLEX_FREE_STRUCT
}


lys_free(struct lys_module *module, void (*private_destructor)(const struct lys_node *node, void *priv), int free_subs, int remove_from_ctx)
{
    struct ly_ctx *ctx;
    int i;

    if (!module) {
        return;
    }

    /* remove schema from the context */
    ctx = module->ctx;
    if (remove_from_ctx && ctx->models.used) {
        for (i = 0; i < ctx->models.used; i++) {
            if (ctx->models.list[i] == module) {
                /* move all the models to not change the order in the list */
                ctx->models.used--;
                if (i < ctx->models.used) {
                    memmove(&ctx->models.list[i], ctx->models.list[i + 1], (ctx->models.used - i) * sizeof *ctx->models.list);
                }
                ctx->models.list[ctx->models.used] = NULL;
                /* we are done */
                break;
            }
        }
    }

    /* common part with struct ly_submodule */
    module_free_common(module, private_destructor);

    /* include */
    for (i = 0; i < module->inc_size; i++) {
        lydict_remove(ctx, module->inc[i].dsc);
        lydict_remove(ctx, module->inc[i].ref);
        lys_extension_instances_free(ctx, module->inc[i].ext, module->inc[i].ext_size, private_destructor);
        /* complete submodule free is done only from main module since
         * submodules propagate their includes to the main module */
        if (free_subs) {
            lys_submodule_free(module->inc[i].submodule, private_destructor);
        }
    }
    free(module->inc);

    /* specific items to free */
    lydict_remove(ctx, module->ns);

    free(module);
}


lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format)
{
    FUN_IN;

    return lys_parse_mem_(ctx, data, format, NULL, 0, 1);
}


lys_set_private(const struct lys_node *node, void *priv)
{
    FUN_IN;

    void *prev;

    if (!node) {
        LOGARG;
        return NULL;
    }

    prev = node->priv;
    ((struct lys_node *)node)->priv = priv;

    return prev;
}


CryptRsaSign(
	     TPMT_SIGNATURE      *sigOut,
	     OBJECT              *key,           // IN: key to use
	     TPM2B_DIGEST        *hIn,           // IN: the digest to sign
	     RAND_STATE          *rand           // IN: the random number generator
	     //      to use (mostly for testing)
	     )
{
    TPM_RC                retVal = TPM_RC_SUCCESS;
    UINT16                modSize;
    size_t                outlen;
    int                   padding;
    EVP_PKEY             *pkey = NULL;
    EVP_PKEY_CTX         *ctx = NULL;
    const EVP_MD         *md;
    const char           *digestname;
    TPMI_ALG_HASH         hashAlg;

    // parameter checks
    pAssert(sigOut != NULL && key != NULL && hIn != NULL);
    modSize = key->publicArea.unique.rsa.t.size;
    // for all non-null signatures, the size is the size of the key modulus
    sigOut->signature.rsapss.sig.t.size = modSize;
    TEST(sigOut->sigAlg);

    switch(sigOut->sigAlg)
         {
          case ALG_NULL_VALUE:
            sigOut->signature.rsapss.sig.t.size = 0;
            return TPM_RC_SUCCESS;
          case ALG_RSAPSS_VALUE:
            padding = RSA_PKCS1_PSS_PADDING;
            hashAlg = sigOut->signature.rsapss.hash;
            break;
          case ALG_RSASSA_VALUE:
            padding = RSA_PKCS1_PADDING;
            hashAlg = sigOut->signature.rsassa.hash;
            break;
          default:
            ERROR_RETURN(TPM_RC_SCHEME);
         }

    digestname = GetDigestNameByHashAlg(hashAlg);
    if (digestname == NULL)
        ERROR_RETURN(TPM_RC_VALUE);

    md = EVP_get_digestbyname(digestname);
    if (md == NULL)
        ERROR_RETURN(TPM_RC_FAILURE);

    retVal = InitOpenSSLRSAPrivateKey(key, &pkey);
    if (retVal != TPM_RC_SUCCESS)
        return retVal;

    ctx = EVP_PKEY_CTX_new(pkey, NULL);
    if (ctx == NULL ||
        EVP_PKEY_sign_init(ctx) <= 0)
        ERROR_RETURN(TPM_RC_FAILURE);

    if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0 ||
        EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0)
        ERROR_RETURN(TPM_RC_FAILURE);

    outlen = sigOut->signature.rsapss.sig.t.size;
    if (EVP_PKEY_sign(ctx,
                      sigOut->signature.rsapss.sig.t.buffer, &outlen,
                      hIn->b.buffer, hIn->b.size) <= 0)
        ERROR_RETURN(TPM_RC_FAILURE);

    sigOut->signature.rsapss.sig.t.size = outlen;

 Exit:
    EVP_PKEY_free(pkey);
    EVP_PKEY_CTX_free(ctx);

    return retVal;
}


OaepEncode(
	   TPM2B       *padded,        // OUT: the pad data
	   TPM_ALG_ID   hashAlg,       // IN: algorithm to use for padding
	   const TPM2B *label,         // IN: null-terminated string (may be NULL)
	   TPM2B       *message,       // IN: the message being padded
	   RAND_STATE  *rand           // IN: the random number generator to use
	   )
{
    INT32        padLen;
    INT32        dbSize;
    INT32        i;
    BYTE         mySeed[MAX_DIGEST_SIZE];
    BYTE        *seed = mySeed;
    UINT16       hLen = CryptHashGetDigestSize(hashAlg);
    BYTE         mask[MAX_RSA_KEY_BYTES];
    BYTE        *pp;
    BYTE        *pm;
    TPM_RC       retVal = TPM_RC_SUCCESS;
    pAssert(padded != NULL && message != NULL);
    // A value of zero is not allowed because the KDF can't produce a result
    // if the digest size is zero.
    if(hLen == 0)
	return TPM_RC_VALUE;
    // Basic size checks
    //  make sure digest isn't too big for key size
    if(padded->size < (2 * hLen) + 2)
	ERROR_RETURN(TPM_RC_HASH);
    // and that message will fit messageSize <= k - 2hLen - 2
    if(message->size > (padded->size - (2 * hLen) - 2))
	ERROR_RETURN(TPM_RC_VALUE);
    // Hash L even if it is null
    // Offset into padded leaving room for masked seed and byte of zero
    pp = &padded->buffer[hLen + 1];
    if(CryptHashBlock(hashAlg, label->size, (BYTE *)label->buffer,
		      hLen, pp) != hLen)
	ERROR_RETURN(TPM_RC_FAILURE);
    // concatenate PS of k  mLen  2hLen  2
    padLen = padded->size - message->size - (2 * hLen) - 2;
    MemorySet(&pp[hLen], 0, padLen);
    pp[hLen + padLen] = 0x01;
    padLen += 1;
    memcpy(&pp[hLen + padLen], message->buffer, message->size);
    // The total size of db = hLen + pad + mSize;
    dbSize = hLen + padLen + message->size;
    // If testing, then use the provided seed. Otherwise, use values
    // from the RNG
    CryptRandomGenerate(hLen, mySeed);
    DRBG_Generate(rand, mySeed, (UINT16)hLen);
    // mask = MGF1 (seed, nSize  hLen  1)
    CryptMGF1(dbSize, mask, hashAlg, hLen, seed);
    // Create the masked db
    pm = mask;
    for(i = dbSize; i > 0; i--)
	*pp++ ^= *pm++;
    pp = &padded->buffer[hLen + 1];
    // Run the masked data through MGF1
    if(CryptMGF1(hLen, &padded->buffer[1], hashAlg, dbSize, pp) != (unsigned)hLen)
	ERROR_RETURN(TPM_RC_VALUE);
    // Now XOR the seed to create masked seed
    pp = &padded->buffer[1];
    pm = seed;
    for(i = hLen; i > 0; i--)
	*pp++ ^= *pm++;
    // Set the first byte to zero
    padded->buffer[0] = 0x00;
 Exit:
    return retVal;
}


CryptRsaEncrypt(
		TPM2B_PUBLIC_KEY_RSA        *cOut,          // OUT: the encrypted data
		TPM2B                       *dIn,           // IN: the data to encrypt
		OBJECT                      *key,           // IN: the key used for encryption
		TPMT_RSA_DECRYPT            *scheme,        // IN: the type of padding and hash
		//     if needed
		const TPM2B                 *label,         // IN: in case it is needed
		RAND_STATE                  *rand           // IN: random number generator
		//     state (mostly for testing)
		)
{
    TPM_RC                       retVal = TPM_RC_SUCCESS;
    TPM2B_PUBLIC_KEY_RSA         dataIn;
    //
    // if the input and output buffers are the same, copy the input to a scratch
    // buffer so that things don't get messed up.
    if(dIn == &cOut->b)
	{
	    MemoryCopy2B(&dataIn.b, dIn, sizeof(dataIn.t.buffer));
	    dIn = &dataIn.b;
	}
    // All encryption schemes return the same size of data
    cOut->t.size = key->publicArea.unique.rsa.t.size;
    TEST(scheme->scheme);
    switch(scheme->scheme)
	{
	  case ALG_NULL_VALUE:  // 'raw' encryption
	      {
		  INT32            i;
		  INT32            dSize = dIn->size;
		  // dIn can have more bytes than cOut as long as the extra bytes
		  // are zero. Note: the more significant bytes of a number in a byte
		  // buffer are the bytes at the start of the array.
		  for(i = 0; (i < dSize) && (dIn->buffer[i] == 0); i++);
		  dSize -= i;
		  if(dSize > cOut->t.size)
		      ERROR_RETURN(TPM_RC_VALUE);
		  // Pad cOut with zeros if dIn is smaller
		  memset(cOut->t.buffer, 0, cOut->t.size - dSize);
		  // And copy the rest of the value
		  memcpy(&cOut->t.buffer[cOut->t.size - dSize], &dIn->buffer[i], dSize);
		  // If the size of dIn is the same as cOut dIn could be larger than
		  // the modulus. If it is, then RSAEP() will catch it.
	      }
	      break;
	  case ALG_RSAES_VALUE:
	    retVal = RSAES_PKCS1v1_5Encode(&cOut->b, dIn, rand);
	    break;
	  case ALG_OAEP_VALUE:
	    retVal = OaepEncode(&cOut->b, scheme->details.oaep.hashAlg, label, dIn,
				rand);
	    break;
	  default:
	    ERROR_RETURN(TPM_RC_SCHEME);
	    break;
	}
    // All the schemes that do padding will come here for the encryption step
    // Check that the Encoding worked
    if(retVal == TPM_RC_SUCCESS)
	// Padding OK so do the encryption
	retVal = RSAEP(&cOut->b, key);
 Exit:
    return retVal;
}


OaepEncode(
	   TPM2B       *padded,        // OUT: the pad data
	   TPM_ALG_ID   hashAlg,       // IN: algorithm to use for padding
	   const TPM2B *label,         // IN: null-terminated string (may be NULL)
	   TPM2B       *message,       // IN: the message being padded
	   RAND_STATE  *rand           // IN: the random number generator to use
	   )
{
    INT32        padLen;
    INT32        dbSize;
    INT32        i;
    BYTE         mySeed[MAX_DIGEST_SIZE];
    BYTE        *seed = mySeed;
    INT32        hLen = CryptHashGetDigestSize(hashAlg);
    BYTE         mask[MAX_RSA_KEY_BYTES];
    BYTE        *pp;
    BYTE        *pm;
    TPM_RC       retVal = TPM_RC_SUCCESS;
    pAssert(padded != NULL && message != NULL);
    // A value of zero is not allowed because the KDF can't produce a result
    // if the digest size is zero.
    if(hLen <= 0)
	return TPM_RC_VALUE;
    // Basic size checks
    //  make sure digest isn't too big for key size
    if(padded->size < (2 * hLen) + 2)
	ERROR_RETURN(TPM_RC_HASH);
    // and that message will fit messageSize <= k - 2hLen - 2
    if(message->size > (padded->size - (2 * hLen) - 2))
	ERROR_RETURN(TPM_RC_VALUE);
    // Hash L even if it is null
    // Offset into padded leaving room for masked seed and byte of zero
    pp = &padded->buffer[hLen + 1];
    if(CryptHashBlock(hashAlg, label->size, (BYTE *)label->buffer,
		      hLen, pp) != hLen)
	ERROR_RETURN(TPM_RC_FAILURE);
    // concatenate PS of k  mLen  2hLen  2
    padLen = padded->size - message->size - (2 * hLen) - 2;
    MemorySet(&pp[hLen], 0, padLen);
    pp[hLen + padLen] = 0x01;
    padLen += 1;
    memcpy(&pp[hLen + padLen], message->buffer, message->size);
    // The total size of db = hLen + pad + mSize;
    dbSize = hLen + padLen + message->size;
    // If testing, then use the provided seed. Otherwise, use values
    // from the RNG
    CryptRandomGenerate(hLen, mySeed);
    DRBG_Generate(rand, mySeed, (UINT16)hLen);
    // mask = MGF1 (seed, nSize  hLen  1)
    CryptMGF1(dbSize, mask, hashAlg, hLen, seed);
    // Create the masked db
    pm = mask;
    for(i = dbSize; i > 0; i--)
	*pp++ ^= *pm++;
    pp = &padded->buffer[hLen + 1];
    // Run the masked data through MGF1
    if(CryptMGF1(hLen, &padded->buffer[1], hashAlg, dbSize, pp) != (unsigned)hLen)
	ERROR_RETURN(TPM_RC_VALUE);
    // Now XOR the seed to create masked seed
    pp = &padded->buffer[1];
    pm = seed;
    for(i = hLen; i > 0; i--)
	*pp++ ^= *pm++;
    // Set the first byte to zero
    padded->buffer[0] = 0x00;
 Exit:
    return retVal;
}


void SWTPM_NVRAM_Set_TPMVersion(TPMLIB_TPMVersion version)
{
    tpmversion = version;
}


void pidfile_remove(void)
{
    if (!g_pidfile)
        return;

    unlink(g_pidfile);

    free(g_pidfile);
    g_pidfile = NULL;
}


SWTPM_NVRAM_StoreData_Intern(const unsigned char *data,
                             uint32_t length,
                             uint32_t tpm_number,
                             const char *name,
                             TPM_BOOL encrypt         /* encrypt if key is set */)
{
    TPM_RESULT    rc = 0;
    uint32_t      lrc;
    int           irc;
    int           fd = -1;
    char          tmpfile[FILENAME_MAX];  /* rooted temporary file */
    char          filename[FILENAME_MAX]; /* rooted file name from name */
    unsigned char *filedata = NULL;
    uint32_t      filedata_length = 0;
    tlv_data      td[3];
    size_t        td_len = 0;
    uint16_t      flags = 0;

    TPM_DEBUG(" SWTPM_NVRAM_StoreData: To name %s\n", name);
    if (rc == 0) {
        /* map name to the rooted filename */
        rc = SWTPM_NVRAM_GetFilenameForName(filename, sizeof(filename),
                                            tpm_number, name, false);
    }

    if (rc == 0) {
        /* map name to the rooted temporary file */
        rc = SWTPM_NVRAM_GetFilenameForName(tmpfile, sizeof(tmpfile),
                                            tpm_number, name, true);
    }

    if (rc == 0) {
        /* open the file */
        TPM_DEBUG(" SWTPM_NVRAM_StoreData: Opening file %s\n", tmpfile);
        fd = open(tmpfile, O_WRONLY|O_CREAT|O_TRUNC|O_NOFOLLOW,
                  tpmstate_get_mode());                        /* closed @1 */
        if (fd < 0) {
            logprintf(STDERR_FILENO,
                      "SWTPM_NVRAM_StoreData: Error (fatal) opening %s for "
                      "write failed, %s\n", tmpfile, strerror(errno));
            rc = TPM_FAIL;
        }
    }

    if (rc == 0) {
        if (encrypt && SWTPM_NVRAM_Has_FileKey()) {
            td_len = 3;
            rc = SWTPM_NVRAM_EncryptData(&filekey, &td[0], &td_len,
                                         TAG_ENCRYPTED_DATA, data, length,
                                         TAG_IVEC_ENCRYPTED_DATA);
            if (rc) {
                logprintf(STDERR_FILENO,
                          "SWTPM_NVRAM_EncryptData failed: 0x%02x\n", rc);
            } else {
                TPM_DEBUG("  SWTPM_NVRAM_StoreData: Encrypted %u bytes before "
                          "write, will write %u bytes\n", length,
                          td[0].tlv.length);
            }
            flags |= BLOB_FLAG_ENCRYPTED;
            if (SWTPM_NVRAM_FileKey_Size() == SWTPM_AES256_BLOCK_SIZE)
                flags |= BLOB_FLAG_ENCRYPTED_256BIT_KEY;
        } else {
            td_len = 1;
            td[0] = TLV_DATA_CONST(TAG_DATA, length, data);
        }
    }

    if (rc == 0)
        rc = tlv_data_append(&filedata, &filedata_length, td, td_len);

    if (rc == 0)
        rc = SWTPM_NVRAM_PrependHeader(&filedata, &filedata_length, flags);

    /* write the data to the file */
    if (rc == 0) {
        TPM_DEBUG("  SWTPM_NVRAM_StoreData: Writing %u bytes of data\n", length);
        lrc = write_full(fd, filedata, filedata_length);
        if (lrc != filedata_length) {
            logprintf(STDERR_FILENO,
                      "TPM_NVRAM_StoreData: Error (fatal), data write "
                      "of %u only wrote %u\n", filedata_length, lrc);
            rc = TPM_FAIL;
        }
    }
    if (fd >= 0) {
        TPM_DEBUG("  SWTPM_NVRAM_StoreData: Closing file %s\n", tmpfile);
        irc = close(fd);             /* @1 */
        if (irc != 0) {
            logprintf(STDERR_FILENO,
                      "SWTPM_NVRAM_StoreData: Error (fatal) closing file\n");
            rc = TPM_FAIL;
        }
        else {
            TPM_DEBUG("  SWTPM_NVRAM_StoreData: Closed file %s\n", tmpfile);
        }
    }

    if (rc == 0 && fd >= 0) {
        irc = rename(tmpfile, filename);
        if (irc != 0) {
            logprintf(STDERR_FILENO,
                      "SWTPM_NVRAM_StoreData: Error (fatal) renaming file: %s\n",
                      strerror(errno));
            rc = TPM_FAIL;
        } else {
            TPM_DEBUG("  SWTPM_NVRAM_StoreData: Renamed file to %s\n", filename);
        }
    }

    if (rc != 0 && fd >= 0) {
        unlink(tmpfile);
    }

    tlv_data_free(td, td_len);
    free(filedata);

    TPM_DEBUG(" SWTPM_NVRAM_StoreData: rc=%d\n", rc);

    return rc;
}


void mark_page_dirty_in_slot(struct kvm *kvm,
			     struct kvm_memory_slot *memslot,
		 	     gfn_t gfn)
{
	if (memslot && kvm_slot_dirty_track_enabled(memslot)) {
		unsigned long rel_gfn = gfn - memslot->base_gfn;
		u32 slot = (memslot->as_id << 16) | memslot->id;

		if (kvm->dirty_ring_size)
			kvm_dirty_ring_push(kvm_dirty_ring_get(kvm),
					    slot, rel_gfn);
		else
			set_bit_le(rel_gfn, memslot->dirty_bitmap);
	}
}


static int get_digest_rec(request_rec *r, digest_header_rec *resp)
{
    const char *auth_line;
    apr_size_t l;
    int vk = 0, vv = 0;
    char *key, *value;

    auth_line = apr_table_get(r->headers_in,
                             (PROXYREQ_PROXY == r->proxyreq)
                                 ? "Proxy-Authorization"
                                 : "Authorization");
    if (!auth_line) {
        resp->auth_hdr_sts = NO_HEADER;
        return !OK;
    }

    resp->scheme = ap_getword_white(r->pool, &auth_line);
    if (strcasecmp(resp->scheme, "Digest")) {
        resp->auth_hdr_sts = NOT_DIGEST;
        return !OK;
    }

    l = strlen(auth_line);

    key   = apr_palloc(r->pool, l+1);
    value = apr_palloc(r->pool, l+1);

    while (auth_line[0] != '\0') {

        /* find key */

        while (apr_isspace(auth_line[0])) {
            auth_line++;
        }
        vk = 0;
        while (auth_line[0] != '=' && auth_line[0] != ','
               && auth_line[0] != '\0' && !apr_isspace(auth_line[0])) {
            key[vk++] = *auth_line++;
        }
        key[vk] = '\0';
        while (apr_isspace(auth_line[0])) {
            auth_line++;
        }

        /* find value */

        vv = 0;
        if (auth_line[0] == '=') {
            auth_line++;
            while (apr_isspace(auth_line[0])) {
                auth_line++;
            }

            if (auth_line[0] == '\"') {         /* quoted string */
                auth_line++;
                while (auth_line[0] != '\"' && auth_line[0] != '\0') {
                    if (auth_line[0] == '\\' && auth_line[1] != '\0') {
                        auth_line++;            /* escaped char */
                    }
                    value[vv++] = *auth_line++;
                }
                if (auth_line[0] != '\0') {
                    auth_line++;
                }
            }
            else {                               /* token */
                while (auth_line[0] != ',' && auth_line[0] != '\0'
                       && !apr_isspace(auth_line[0])) {
                    value[vv++] = *auth_line++;
                }
            }
        }
        value[vv] = '\0';

        while (auth_line[0] != ',' && auth_line[0] != '\0') {
            auth_line++;
        }
        if (auth_line[0] != '\0') {
            auth_line++;
        }

        if (!strcasecmp(key, "username"))
            resp->username = apr_pstrdup(r->pool, value);
        else if (!strcasecmp(key, "realm"))
            resp->realm = apr_pstrdup(r->pool, value);
        else if (!strcasecmp(key, "nonce"))
            resp->nonce = apr_pstrdup(r->pool, value);
        else if (!strcasecmp(key, "uri"))
            resp->uri = apr_pstrdup(r->pool, value);
        else if (!strcasecmp(key, "response"))
            resp->digest = apr_pstrdup(r->pool, value);
        else if (!strcasecmp(key, "algorithm"))
            resp->algorithm = apr_pstrdup(r->pool, value);
        else if (!strcasecmp(key, "cnonce"))
            resp->cnonce = apr_pstrdup(r->pool, value);
        else if (!strcasecmp(key, "opaque"))
            resp->opaque = apr_pstrdup(r->pool, value);
        else if (!strcasecmp(key, "qop"))
            resp->message_qop = apr_pstrdup(r->pool, value);
        else if (!strcasecmp(key, "nc"))
            resp->nonce_count = apr_pstrdup(r->pool, value);
    }

    if (!resp->username || !resp->realm || !resp->nonce || !resp->uri
        || !resp->digest
        || (resp->message_qop && (!resp->cnonce || !resp->nonce_count))) {
        resp->auth_hdr_sts = INVALID;
        return !OK;
    }

    if (resp->opaque) {
        resp->opaque_num = (unsigned long) strtol(resp->opaque, NULL, 16);
    }

    resp->auth_hdr_sts = VALID;
    return OK;
}


static int pre_init(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
{
    apr_status_t rv;
    void *retained;

    rv = ap_mutex_register(pconf, client_mutex_type, NULL, APR_LOCK_DEFAULT, 0);
    if (rv != APR_SUCCESS)
        return !OK;
    rv = ap_mutex_register(pconf, opaque_mutex_type, NULL, APR_LOCK_DEFAULT, 0);
    if (rv != APR_SUCCESS)
        return !OK;

    retained = ap_retained_data_get(RETAINED_DATA_ID);
    if (retained == NULL) {
        retained = ap_retained_data_create(RETAINED_DATA_ID, SECRET_LEN);
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(01757)
                     "generating secret for digest authentication");
#if APR_HAS_RANDOM
        rv = apr_generate_random_bytes(retained, SECRET_LEN);
#else
#error APR random number support is missing
#endif
        if (rv != APR_SUCCESS) {
            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, APLOGNO(01758)
                         "error generating secret");
            return !OK;
        }
    }
    secret = retained;
    return OK;
}


static int session_fixups(request_rec * r)
{
    session_dir_conf *conf = ap_get_module_config(r->per_dir_config,
                                                  &session_module);

    session_rec *z = NULL;

    /* if an error occurs or no session has been configured, we ignore
     * the broken session and allow it to be recreated from scratch on save
     * if necessary.
     */
    ap_session_load(r, &z);

    if (conf->env) {
        if (z) {
            session_identity_encode(r, z);
            if (z->encoded) {
                apr_table_set(r->subprocess_env, HTTP_SESSION, z->encoded);
                z->encoded = NULL;
            }
        }
        apr_table_unset(r->headers_in, "Session");
    }

    return OK;

}


AP_DECLARE(int) ap_some_authn_required(request_rec *r)
{
    int access_status;
    char *olduser = r->user;
    int rv = FALSE;

    switch (ap_satisfies(r)) {
    case SATISFY_ALL:
    case SATISFY_NOSPEC:
        if ((access_status = ap_run_access_checker(r)) != OK) {
            break;
        }

        access_status = ap_run_access_checker_ex(r);
        if (access_status == DECLINED) {
            rv = TRUE;
        }

        break;
    case SATISFY_ANY:
        if ((access_status = ap_run_access_checker(r)) == OK) {
            break;
        }

        access_status = ap_run_access_checker_ex(r);
        if (access_status == DECLINED) {
            rv = TRUE;
        }

        break;
    }

    r->user = olduser;
    return rv;
}


AP_DECLARE(int) ap_if_walk(request_rec *r)
{
    core_dir_config *dconf = ap_get_core_module_config(r->per_dir_config);
    int status = ap_if_walk_sub(r, dconf);
    return status;
}


AP_DECLARE(int) ap_process_request_internal(request_rec *r)
{
    int file_req = (r->main && r->filename);
    int access_status;
    core_dir_config *d;
    core_server_config *sconf =
        ap_get_core_module_config(r->server->module_config);

    /* Ignore embedded %2F's in path for proxy requests */
    if (!r->proxyreq && r->parsed_uri.path) {
        d = ap_get_core_module_config(r->per_dir_config);
        if (d->allow_encoded_slashes) {
            access_status = ap_unescape_url_keep2f(r->parsed_uri.path, d->decode_encoded_slashes);
        }
        else {
            access_status = ap_unescape_url(r->parsed_uri.path);
        }
        if (access_status) {
            if (access_status == HTTP_NOT_FOUND) {
                if (! d->allow_encoded_slashes) {
                    ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00026)
                                  "found %%2f (encoded '/') in URI "
                                  "(decoded='%s'), returning 404",
                                  r->parsed_uri.path);
                }
            }
            return access_status;
        }
    }

    ap_getparents(r->uri);     /* OK --- shrinking transformations... */
    if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) {
        ap_no2slash(r->uri);
        if (r->parsed_uri.path) {
            ap_no2slash(r->parsed_uri.path);
        }
     }

    /* All file subrequests are a huge pain... they cannot bubble through the
     * next several steps.  Only file subrequests are allowed an empty uri,
     * otherwise let translate_name kill the request.
     */
    if (!file_req) {
        if ((access_status = ap_location_walk(r))) {
            return access_status;
        }
        if ((access_status = ap_if_walk(r))) {
            return access_status;
        }

        d = ap_get_core_module_config(r->per_dir_config);
        if (d->log) {
            r->log = d->log;
        }

        if ((access_status = ap_run_translate_name(r))) {
            return decl_die(access_status, "translate", r);
        }
    }

    /* Reset to the server default config prior to running map_to_storage
     */
    r->per_dir_config = r->server->lookup_defaults;

    if ((access_status = ap_run_map_to_storage(r))) {
        /* This request wasn't in storage (e.g. TRACE) */
        return access_status;
    }

    /* Rerun the location walk, which overrides any map_to_storage config.
     */
    if ((access_status = ap_location_walk(r))) {
        return access_status;
    }
    if ((access_status = ap_if_walk(r))) {
        return access_status;
    }

    d = ap_get_core_module_config(r->per_dir_config);
    if (d->log) {
        r->log = d->log;
    }

    if ((access_status = ap_run_post_perdir_config(r))) {
        return access_status;
    }

    /* Only on the main request! */
    if (r->main == NULL) {
        if ((access_status = ap_run_header_parser(r))) {
            return access_status;
        }
    }

    /* Skip authn/authz if the parent or prior request passed the authn/authz,
     * and that configuration didn't change (this requires optimized _walk()
     * functions in map_to_storage that use the same merge results given
     * identical input.)  If the config changes, we must re-auth.
     */
    if (r->prev && (r->prev->per_dir_config == r->per_dir_config)) {
        r->user = r->prev->user;
        r->ap_auth_type = r->prev->ap_auth_type;
    }
    else if (r->main && (r->main->per_dir_config == r->per_dir_config)) {
        r->user = r->main->user;
        r->ap_auth_type = r->main->ap_auth_type;
    }
    else {
        /* A module using a confusing API (ap_get_basic_auth_pw) caused
        ** r->user to be filled out prior to check_authn hook. We treat
        ** it is inadvertent.
        */
        if (r->user && apr_table_get(r->notes, AP_GET_BASIC_AUTH_PW_NOTE)) {
            r->user = NULL;
        }

        switch (ap_satisfies(r)) {
        case SATISFY_ALL:
        case SATISFY_NOSPEC:
            if ((access_status = ap_run_access_checker(r)) != OK) {
                return decl_die(access_status,
                                "check access (with Satisfy All)", r);
            }

            access_status = ap_run_access_checker_ex(r);
            if (access_status == DECLINED
                || (access_status == OK && ap_run_force_authn(r) == OK)) {
                if ((access_status = ap_run_check_user_id(r)) != OK) {
                    return decl_die(access_status, "check user", r);
                }
                if (r->user == NULL) {
                    /* don't let buggy authn module crash us in authz */
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00027)
                                  "No authentication done but request not "
                                  "allowed without authentication for %s. "
                                  "Authentication not configured?",
                                  r->uri);
                    access_status = HTTP_INTERNAL_SERVER_ERROR;
                    return decl_die(access_status, "check user", r);
                }
                if ((access_status = ap_run_auth_checker(r)) != OK) {
                    return decl_die(access_status, "check authorization", r);
                }
            }
            else if (access_status == OK) {
                ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
                              "request authorized without authentication by "
                              "access_checker_ex hook: %s", r->uri);
            }
            else {
                return decl_die(access_status, "check access", r);
            }
            break;
        case SATISFY_ANY:
            if ((access_status = ap_run_access_checker(r)) == OK) {
                ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
                              "request authorized without authentication by "
                              "access_checker hook and 'Satisfy any': %s",
                              r->uri);
                break;
            }

            access_status = ap_run_access_checker_ex(r);
            if (access_status == DECLINED
                || (access_status == OK && ap_run_force_authn(r) == OK)) {
                if ((access_status = ap_run_check_user_id(r)) != OK) {
                    return decl_die(access_status, "check user", r);
                }
                if (r->user == NULL) {
                    /* don't let buggy authn module crash us in authz */
                    ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00028)
                                  "No authentication done but request not "
                                  "allowed without authentication for %s. "
                                  "Authentication not configured?",
                                  r->uri);
                    access_status = HTTP_INTERNAL_SERVER_ERROR;
                    return decl_die(access_status, "check user", r);
                }
                if ((access_status = ap_run_auth_checker(r)) != OK) {
                    return decl_die(access_status, "check authorization", r);
                }
            }
            else if (access_status == OK) {
                ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
                              "request authorized without authentication by "
                              "access_checker_ex hook: %s", r->uri);
            }
            else {
                return decl_die(access_status, "check access", r);
            }
            break;
        }
    }
    /* XXX Must make certain the ap_run_type_checker short circuits mime
     * in mod-proxy for r->proxyreq && r->parsed_uri.scheme
     *                              && !strcmp(r->parsed_uri.scheme, "http")
     */
    if ((access_status = ap_run_type_checker(r)) != OK) {
        return decl_die(access_status, "find types", r);
    }

    if ((access_status = ap_run_fixups(r)) != OK) {
        ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r, "fixups hook gave %d: %s",
                      access_status, r->uri);
        return access_status;
    }

    return OK;
}


_rsa_sec_compute_root_tr(const struct rsa_public_key *pub,
			 const struct rsa_private_key *key,
			 void *random_ctx, nettle_random_func *random,
			 mp_limb_t *x, const mp_limb_t *m)
{
  mp_size_t nn;
  mpz_t mz;
  mpz_t xz;
  int res;

  mpz_init(xz);

  nn = mpz_size (pub->n);

  res = rsa_compute_root_tr(pub, key, random_ctx, random, xz,
			    mpz_roinit_n(mz, m, nn));

  if (res)
    mpz_limbs_copy(x, xz, nn);

  mpz_clear(xz);
  return res;
}


static apr_status_t add_trailer(h2_stream *stream,
                                const char *name, size_t nlen,
                                const char *value, size_t vlen,
                                size_t max_field_len, int *pwas_added)
{
    conn_rec *c = stream->session->c;
    char *hname, *hvalue;
    const char *existing;

    *pwas_added = 0;
    if (nlen == 0 || name[0] == ':') {
        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, c,
                      H2_STRM_LOG(APLOGNO(03060), stream,
                      "pseudo header in trailer"));
        return APR_EINVAL;
    }
    if (h2_req_ignore_trailer(name, nlen)) {
        return APR_SUCCESS;
    }
    if (!stream->trailers) {
        stream->trailers = apr_table_make(stream->pool, 5);
    }
    hname = apr_pstrndup(stream->pool, name, nlen);
    h2_util_camel_case_header(hname, nlen);
    existing = apr_table_get(stream->trailers, hname);
    if (max_field_len
        && ((existing? strlen(existing)+2 : 0) + vlen + nlen + 2 > max_field_len)) {
        /* "key: (oldval, )?nval" is too long */
        return APR_EINVAL;
    }
    if (!existing) *pwas_added = 1;
    hvalue = apr_pstrndup(stream->pool, value, vlen);
    apr_table_mergen(stream->trailers, hname, hvalue);
    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
                  H2_STRM_MSG(stream, "added trailer '%s: %s'"), hname, hvalue);

    return APR_SUCCESS;
}