int c_shquote_unquote_double(char **outp,
                             size_t *n_outp,
                             const char **inp,
                             size_t *n_inp) {
        char *out = *outp;
        size_t n_out = *n_outp;
        const char *in = *inp;
        size_t n_in = *n_inp;
        int r;

        if (n_in == 0 || *in != '\"')
                return -ENOTRECOVERABLE;

        c_shquote_skip_char(&in, &n_in);

        while (n_in > 0) {
                size_t len;

                switch (*in) {
                case '\\':
                        r = c_shquote_unescape_char_quoted(&out, &n_out, &in, &n_in);
                        if (r)
                                return r;

                        break;
                case '\"':
                        c_shquote_skip_char(&in, &n_in);

                        goto out;
                default:
                        /*
                         * Consume until the next escape sequence or the next double
                         * quote. If none exists, consume the rest of the string.
                         */
                        len = c_shquote_strncspn(in, n_in, "\\\"");
                        r = c_shquote_consume_str(&out, &n_out, &in, &n_in, len);
                        if (r)
                                return r;
                }
        }

        return C_SHQUOTE_E_BAD_QUOTING;

out:
        *outp = out;
        *n_outp = n_out;
        *inp = in;
        *n_inp = n_in;
        return 0;
}


static void test_unescape_char_quoted_one(const char *string, size_t n_string, bool escaped) {
        char buf[2];
        char *out = buf;
        size_t n_out = sizeof(buf);
        const char *in = string;
        size_t n_in = n_string;
        int r;

        r = c_shquote_unescape_char_quoted(&out, &n_out, &in, &n_in);
        c_assert(!r);
        c_assert(in == string + 2);
        c_assert(n_in == n_string - 2);

        if (escaped) {
                c_assert(out == buf + 1);
                c_assert(n_out == sizeof(buf) - 1);
                c_assert(buf[0] == string[1]);
        } else {
                c_assert(out == buf + 2);
                c_assert(n_out == sizeof(buf) - 2);
                c_assert(!memcmp(buf, string, 2));
        }
}


static void test_discard_comment(void) {
        const char *string = "#foo\\\n";
        const char *comment;
        size_t n_comment;

        comment = string;
        n_comment = strlen(comment);
        c_shquote_discard_comment(&comment, &n_comment);
        c_assert(n_comment == 1);
        c_assert(comment == string + strlen(string) - 1);

        comment = string;
        n_comment = strlen(comment) - 1;
        c_shquote_discard_comment(&comment, &n_comment);
        c_assert(n_comment == 0);
        c_assert(comment == string + strlen(string) - 1);
}


static void test_consume_str(void) {
        const char *string = "foo";
        char buf[strlen(string)];
        char *out = buf;
        size_t n_out = sizeof(buf);
        const char *in = string;
        size_t n_in = strlen(string);
        int r;

        r = c_shquote_consume_str(&out, &n_out, &in, &n_in, 2);
        c_assert(!r);
        c_assert(!memcmp(buf, string, 2));
        c_assert(out == buf + 2);
        c_assert(n_out == sizeof(buf) - 2);
        c_assert(in == string + 2);
        c_assert(n_in == strlen(string) - 2);

        r = c_shquote_consume_str(&out, &n_out, &in, &n_in, 1);
        c_assert(!r);
        c_assert(!memcmp(buf, string, 3));
        c_assert(out == buf + 3);
        c_assert(n_out == sizeof(buf) - 3);
        c_assert(in == string + 3);
        c_assert(n_in == strlen(string) - 3);

        in = string;
        n_in = 1;

        r = c_shquote_consume_str(&out, &n_out, &in, &n_in, 1);
        c_assert(r == C_SHQUOTE_E_NO_SPACE);

        out = buf;
        n_out = sizeof(buf);

        r = c_shquote_consume_str(&out, &n_out, &in, &n_in, 2);
        c_assert(r < 0);
}


int avcodec_default_get_buffer2(AVCodecContext *avctx, AVFrame *frame, int flags)
{
    int ret;

    if ((ret = update_frame_pool(avctx, frame)) < 0)
        return ret;

#if FF_API_GET_BUFFER
FF_DISABLE_DEPRECATION_WARNINGS
    frame->type = FF_BUFFER_TYPE_INTERNAL;
FF_ENABLE_DEPRECATION_WARNINGS
#endif

    switch (avctx->codec_type) {
    case AVMEDIA_TYPE_VIDEO:
        return video_get_buffer(avctx, frame);
    case AVMEDIA_TYPE_AUDIO:
        return audio_get_buffer(avctx, frame);
    default:
        return -1;
    }
}


int attribute_align_arg avcodec_encode_video(AVCodecContext *avctx, uint8_t *buf, int buf_size,
                                             const AVFrame *pict)
{
    AVPacket pkt;
    int ret, got_packet = 0;

    if (buf_size < FF_MIN_BUFFER_SIZE) {
        av_log(avctx, AV_LOG_ERROR, "buffer smaller than minimum size\n");
        return -1;
    }

    av_init_packet(&pkt);
    pkt.data = buf;
    pkt.size = buf_size;

    ret = avcodec_encode_video2(avctx, &pkt, pict, &got_packet);
    if (!ret && got_packet && avctx->coded_frame) {
        avctx->coded_frame->pts       = pkt.pts;
        avctx->coded_frame->key_frame = !!(pkt.flags & AV_PKT_FLAG_KEY);
    }

    /* free any side data since we cannot return it */
    if (pkt.side_data_elems > 0) {
        int i;
        for (i = 0; i < pkt.side_data_elems; i++)
            av_free(pkt.side_data[i].data);
        av_freep(&pkt.side_data);
        pkt.side_data_elems = 0;
    }

    return ret ? ret : pkt.size;
}


static void compat_free_buffer(void *opaque, uint8_t *data)
{
    CompatReleaseBufPriv *priv = opaque;
    if (priv->avctx.release_buffer)
        priv->avctx.release_buffer(&priv->avctx, &priv->frame);
    av_freep(&priv);
}


int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags)
{
    int ret = get_buffer_internal(avctx, frame, flags);
    if (ret < 0)
        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
    return ret;
}


AVHWAccel *ff_find_hwaccel(AVCodecContext *avctx)
{
    enum AVCodecID codec_id = avctx->codec->id;
    enum AVPixelFormat pix_fmt = avctx->pix_fmt;

    AVHWAccel *hwaccel = NULL;

    while ((hwaccel = av_hwaccel_next(hwaccel)))
        if (hwaccel->id == codec_id
            && hwaccel->pix_fmt == pix_fmt)
            return hwaccel;
    return NULL;
}


int avpriv_bprint_to_extradata(AVCodecContext *avctx, struct AVBPrint *buf)
{
    int ret;
    char *str;

    ret = av_bprint_finalize(buf, &str);
    if (ret < 0)
        return ret;
    avctx->extradata = str;
    /* Note: the string is NUL terminated (so extradata can be read as a
     * string), but the ending character is not accounted in the size (in
     * binary formats you are likely not supposed to mux that character). When
     * extradata is copied, it is also padded with FF_INPUT_BUFFER_PADDING_SIZE
     * zeros. */
    avctx->extradata_size = buf->len;
    return 0;
}


backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
		int casefold, struct berval *filter_value )
{
	/*
	 * maybe we should check type of at->sel_expr here somehow,
	 * to know whether upper_func is applicable, but for now
	 * upper_func stuff is made for Oracle, where UPPER is
	 * safely applicable to NUMBER etc.
	 */
	if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
		ber_len_t	start;

		backsql_strfcat_x( &bsi->bsi_flt_where,
				bsi->bsi_op->o_tmpmemctx,
				"cbl",
				'(', /* ) */
				&at->bam_sel_expr_u,
				(ber_len_t)STRLENOF( "='" ),
					"='" );

		start = bsi->bsi_flt_where.bb_val.bv_len;

		backsql_strfcat_x( &bsi->bsi_flt_where,
				bsi->bsi_op->o_tmpmemctx,
				"bl",
				filter_value,
				(ber_len_t)STRLENOF( /* (' */ "')" ),
					/* (' */ "')" );

		ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );

	} else {
		backsql_strfcat_x( &bsi->bsi_flt_where,
				bsi->bsi_op->o_tmpmemctx,
				"cblbl",
				'(', /* ) */
				&at->bam_sel_expr,
				(ber_len_t)STRLENOF( "='" ), "='",
				filter_value,
				(ber_len_t)STRLENOF( /* (' */ "')" ),
					/* (' */ "')" );
	}

	return 1;
}


backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
{
	backsql_info		*bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
	int			casefold = 0;
	struct berval		*filter_value = NULL;
	MatchingRule		*matching_rule = NULL;
	struct berval		ordering = BER_BVC("<=");
	struct berval		escval;
	int					escaped = 0;

	Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
		at->bam_ad->ad_cname.bv_val );

	/*
	 * need to add this attribute to list of attrs to load,
	 * so that we can do test_filter() later
	 */
	backsql_attrlist_add( bsi, at->bam_ad );

	backsql_merge_from_tbls( bsi, &at->bam_from_tbls );

	if ( !BER_BVISNULL( &at->bam_join_where )
			&& strstr( bsi->bsi_join_where.bb_val.bv_val,
				at->bam_join_where.bv_val ) == NULL )
	{
	       	backsql_strfcat_x( &bsi->bsi_join_where,
				bsi->bsi_op->o_tmpmemctx,
				"lb",
				(ber_len_t)STRLENOF( " AND " ), " AND ",
				&at->bam_join_where );
	}

	if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
		backsql_strfcat_x( &bsi->bsi_flt_where,
			bsi->bsi_op->o_tmpmemctx,
			"l",
			(ber_len_t)STRLENOF( "1=0" ), "1=0" );
		return 1;
	}

	switch ( f->f_choice ) {
	case LDAP_FILTER_EQUALITY:
		filter_value = &f->f_av_value;
		matching_rule = at->bam_ad->ad_type->sat_equality;

		goto equality_match;

		/* fail over into next case */

	case LDAP_FILTER_EXT:
		filter_value = &f->f_mra->ma_value;
		matching_rule = f->f_mr_rule;

equality_match:;
		/* always uppercase strings by now */
#ifdef BACKSQL_UPPERCASE_FILTER
		if ( SLAP_MR_ASSOCIATED( matching_rule,
					bi->sql_caseIgnoreMatch ) )
#endif /* BACKSQL_UPPERCASE_FILTER */
		{
			casefold = 1;
		}

		escaped = backsql_val_escape( bsi->bsi_op, filter_value, &escval );
		if ( escaped )
			filter_value = &escval;

		/* FIXME: directoryString filtering should use a similar
		 * approach to deal with non-prettified values like
		 * " A  non    prettified   value  ", by using a LIKE
		 * filter with all whitespaces collapsed to a single '%' */
		if ( SLAP_MR_ASSOCIATED( matching_rule,
					bi->sql_telephoneNumberMatch ) )
		{
			struct berval	bv;
			ber_len_t	i;

			/*
			 * to check for matching telephone numbers
			 * with intermized chars, e.g. val='1234'
			 * use
			 *
			 * val LIKE '%1%2%3%4%'
			 */

			bv.bv_len = 2 * filter_value->bv_len - 1;
			bv.bv_val = ch_malloc( bv.bv_len + 1 );

			bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
			for ( i = 1; i < filter_value->bv_len; i++ ) {
				bv.bv_val[ 2 * i - 1 ] = '%';
				bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
			}
			bv.bv_val[ 2 * i - 1 ] = '\0';

			(void)backsql_process_filter_like( bsi, at, casefold, &bv );
			ch_free( bv.bv_val );

			break;
		}

		/* NOTE: this is required by objectClass inheritance
		 * and auxiliary objectClass use in filters for slightly
		 * more efficient candidate selection. */
		/* FIXME: a bit too many specializations to deal with
		 * very specific cases... */
		if ( at->bam_ad == slap_schema.si_ad_objectClass
				|| at->bam_ad == slap_schema.si_ad_structuralObjectClass )
		{
			backsql_strfcat_x( &bsi->bsi_flt_where,
					bsi->bsi_op->o_tmpmemctx,
					"lbl",
					(ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
						"(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
					filter_value,
					(ber_len_t)STRLENOF( /* (' */ "')" ),
						/* (' */ "')" );
			break;
		}

		/*
		 * maybe we should check type of at->sel_expr here somehow,
		 * to know whether upper_func is applicable, but for now
		 * upper_func stuff is made for Oracle, where UPPER is
		 * safely applicable to NUMBER etc.
		 */
		(void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
		break;

	case LDAP_FILTER_GE:
		ordering.bv_val = ">=";

		/* fall thru to next case */

	case LDAP_FILTER_LE:
		filter_value = &f->f_av_value;

		/* always uppercase strings by now */
#ifdef BACKSQL_UPPERCASE_FILTER
		if ( at->bam_ad->ad_type->sat_ordering &&
				SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
					bi->sql_caseIgnoreMatch ) )
#endif /* BACKSQL_UPPERCASE_FILTER */
		{
			casefold = 1;
		}

		escaped = backsql_val_escape( bsi->bsi_op, filter_value, &escval );
		if ( escaped )
			filter_value = &escval;

		/*
		 * FIXME: should we uppercase the operands?
		 */
		if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
			ber_len_t	start;

			backsql_strfcat_x( &bsi->bsi_flt_where,
					bsi->bsi_op->o_tmpmemctx,
					"cbbc",
					'(', /* ) */
					&at->bam_sel_expr_u,
					&ordering,
					'\'' );

			start = bsi->bsi_flt_where.bb_val.bv_len;

			backsql_strfcat_x( &bsi->bsi_flt_where,
					bsi->bsi_op->o_tmpmemctx,
					"bl",
					filter_value,
					(ber_len_t)STRLENOF( /* (' */ "')" ),
						/* (' */ "')" );

			ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );

		} else {
			backsql_strfcat_x( &bsi->bsi_flt_where,
					bsi->bsi_op->o_tmpmemctx,
					"cbbcbl",
					'(' /* ) */ ,
					&at->bam_sel_expr,
					&ordering,
					'\'',
					filter_value,
					(ber_len_t)STRLENOF( /* (' */ "')" ),
						/* ( */ "')" );
		}
		break;

	case LDAP_FILTER_PRESENT:
		backsql_strfcat_x( &bsi->bsi_flt_where,
				bsi->bsi_op->o_tmpmemctx,
				"lbl",
				(ber_len_t)STRLENOF( "NOT (" /* ) */),
					"NOT (", /* ) */
				&at->bam_sel_expr,
				(ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
					/* ( */ " IS NULL)" );
		break;

	case LDAP_FILTER_SUBSTRINGS:
		backsql_process_sub_filter( bsi, f, at );
		break;

	case LDAP_FILTER_APPROX:
		/* we do our best */

		filter_value = &f->f_av_value;
		escaped = backsql_val_escape( bsi->bsi_op, filter_value, &escval );
		if ( escaped )
			filter_value = &escval;
		/*
		 * maybe we should check type of at->sel_expr here somehow,
		 * to know whether upper_func is applicable, but for now
		 * upper_func stuff is made for Oracle, where UPPER is
		 * safely applicable to NUMBER etc.
		 */
		(void)backsql_process_filter_like( bsi, at, 1, filter_value );
		break;

	default:
		/* unhandled filter type; should not happen */
		assert( 0 );
		backsql_strfcat_x( &bsi->bsi_flt_where,
				bsi->bsi_op->o_tmpmemctx,
				"l",
				(ber_len_t)STRLENOF( "8=8" ), "8=8" );
		break;

	}

	if ( escaped )
		bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx );

	Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
		at->bam_ad->ad_cname.bv_val );

	return 1;
}


backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
{
	int 		n_attrs = 0;
	AttributeName	*an = NULL;

	if ( bsi->bsi_attrs == NULL ) {
		return 1;
	}

	/*
	 * clear the list (retrieve all attrs)
	 */
	if ( ad == NULL ) {
		bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, bsi->bsi_op->o_tmpmemctx );
		bsi->bsi_attrs = NULL;
		bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
		return 1;
	}

	/* strip ';binary' */
	if ( slap_ad_is_binary( ad ) ) {
		ad = ad->ad_type->sat_ad;
	}

	for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
		an = &bsi->bsi_attrs[ n_attrs ];

		Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
			"attribute \"%s\" is in list\n",
			an->an_name.bv_val );
		/*
		 * We can live with strcmp because the attribute
		 * list has been normalized before calling be_search
		 */
		if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
			return 1;
		}
	}

	Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
		"adding \"%s\" to list\n", ad->ad_cname.bv_val );

	an = (AttributeName *)bsi->bsi_op->o_tmprealloc( bsi->bsi_attrs,
			sizeof( AttributeName ) * ( n_attrs + 2 ),
			bsi->bsi_op->o_tmpmemctx );
	if ( an == NULL ) {
		return -1;
	}

	an[ n_attrs ].an_name = ad->ad_cname;
	an[ n_attrs ].an_desc = ad;
	BER_BVZERO( &an[ n_attrs + 1 ].an_name );

	bsi->bsi_attrs = an;

	return 1;
}


static int mailbox_buf_to_index_record(const char *buf, int version,
                                       struct index_record *record, int dirty)
{
    uint32_t crc;
    uint32_t stored_system_flags = 0;
    int n;

    /* tracking fields - initialise */
    memset(record, 0, sizeof(struct index_record));

    /* parse the shared bits first */
    record->uid = ntohl(*((bit32 *)(buf+OFFSET_UID)));
    record->internaldate = ntohl(*((bit32 *)(buf+OFFSET_INTERNALDATE)));
    record->sentdate = ntohl(*((bit32 *)(buf+OFFSET_SENTDATE)));
    record->size = ntohl(*((bit32 *)(buf+OFFSET_SIZE)));
    record->header_size = ntohl(*((bit32 *)(buf+OFFSET_HEADER_SIZE)));
    record->gmtime = ntohl(*((bit32 *)(buf+OFFSET_GMTIME)));
    uint64_t cache_offset_field = ntohl(*((bit32 *)(buf+OFFSET_CACHE_OFFSET)));
    record->last_updated = ntohl(*((bit32 *)(buf+OFFSET_LAST_UPDATED)));
    stored_system_flags = ntohl(*((bit32 *)(buf+OFFSET_SYSTEM_FLAGS)));

    /* de-serialise system flags and internal flags */
    record->system_flags = stored_system_flags & 0x0000ffff;
    record->internal_flags = stored_system_flags & 0xffff0000;

    for (n = 0; n < MAX_USER_FLAGS/32; n++) {
        record->user_flags[n] = ntohl(*((bit32 *)(buf+OFFSET_USER_FLAGS+4*n)));
    }
    uint64_t cache_version_field = ntohl(*((bit32 *)(buf+OFFSET_CACHE_VERSION)));

    /* keep the low bits of the version field for the version */
    record->cache_version = cache_version_field & 0xffff;
    /* use the high bits of the version field to extend the cache offset */
    record->cache_offset = cache_offset_field | ((cache_version_field & 0xffff0000) << 16);

    if (version < 8)
        return 0;

    if (version < 10) {
        /* modseq was at 72 before the GUID move */
        record->modseq = ntohll(*((bit64 *)(buf+72)));
        return 0;
    }

    message_guid_import(&record->guid, buf+OFFSET_MESSAGE_GUID);
    record->modseq = ntohll(*((bit64 *)(buf+OFFSET_MODSEQ)));
    if (version < 12)
        return 0;

    /* THRID got inserted before cache_crc32 in version 12 */
    if (version < 13) {
        record->cache_crc = ntohl(*((bit32 *)(buf+88)));

        if (dirty) return 0;
        /* check CRC32 */
        crc = crc32_map(buf, 92);
        if (crc != ntohl(*((bit32 *)(buf+92))))
            return IMAP_MAILBOX_CHECKSUM;
        return 0;
    }

    record->cid = ntohll(*(bit64 *)(buf+OFFSET_THRID));
    if (version > 14) {
        record->savedate = ntohl(*((bit32 *)(buf+OFFSET_SAVEDATE)));
    }

    /* createdmodseq was added in version 16, pushing the CRCs down */
    if (version < 16) {
        record->cache_crc = ntohl(*((bit32 *)(buf+96)));

        if (dirty) return 0;
        /* check CRC32 */
        crc = crc32_map(buf, 100);
        if (crc != ntohl(*((bit32 *)(buf+100))))
            return IMAP_MAILBOX_CHECKSUM;
        return 0;
    }

    record->createdmodseq = ntohll(*(bit64 *)(buf+OFFSET_CREATEDMODSEQ));
    record->cache_crc = ntohl(*((bit32 *)(buf+OFFSET_CACHE_CRC)));

    if (dirty) return 0;
    /* check CRC32 */
    crc = crc32_map(buf, OFFSET_RECORD_CRC);
    if (crc != ntohl(*((bit32 *)(buf+OFFSET_RECORD_CRC))))
        return IMAP_MAILBOX_CHECKSUM;

    return 0;
}


static int mailbox_open_advanced(const char *name,
                                 int locktype,
                                 int index_locktype,
                                 const mbentry_t *mbe,
                                 struct mailbox **mailboxptr)
{
    mbentry_t *mbentry = NULL;
    struct mailboxlist *listitem;
    struct mailbox *mailbox = NULL;
    int r = 0;

    assert(*mailboxptr == NULL);

    listitem = find_listitem(name);

    /* already open?  just use this one */
    if (listitem) {
        /* can't reuse an exclusive locked mailbox */
        if (listitem->l->locktype == LOCK_EXCLUSIVE)
            return IMAP_MAILBOX_LOCKED;
        if (locktype == LOCK_EXCLUSIVE)
            return IMAP_MAILBOX_LOCKED;
        /* can't reuse an already locked index */
        if (listitem->m.index_locktype)
            return IMAP_MAILBOX_LOCKED;

        listitem->nopen++;
        mailbox = &listitem->m;

        goto lockindex;
    }

    listitem = create_listitem(name);
    mailbox = &listitem->m;

    // lock the user namespace FIRST before the mailbox namespace
    char *userid = mboxname_to_userid(name);
    if (userid) {
        int haslock = user_isnamespacelocked(userid);
        if (haslock) {
            if (index_locktype != LOCK_SHARED) assert(haslock != LOCK_SHARED);
        }
        else {
            int locktype = index_locktype;
            mailbox->local_namespacelock = user_namespacelock_full(userid, locktype);
        }
        free(userid);
    }

    if (mbe) mbentry = mboxlist_entry_copy(mbe);
    else r = mboxlist_lookup_allow_all(name, &mbentry, NULL);

    if (!r && mbentry->mbtype & MBTYPE_DELETED)
        r = IMAP_MAILBOX_NONEXISTENT;

    if (r) {
        if (mailbox->local_namespacelock)
            mboxname_release(&mailbox->local_namespacelock);
        mboxlist_entry_free(&mbentry);
        remove_listitem(listitem);
        return r;
    }

    /* XXX can we even get here for remote mbentries? */
    if (!mbentry->uniqueid && mbentry_is_local_mailbox(mbentry)) {
        /* Theoretically it shouldn't be possible for an mbentry to not
         * have a uniqueid... so if it happens, complain loudly.
         */
        xsyslog(LOG_ERR, "mbentry has no uniqueid, needs reconstruct",
                         "mboxname=<%s>", name);
    }

    uint32_t legacy_dirs = (mbentry->mbtype & MBTYPE_LEGACY_DIRS);
    r = mboxname_lock(legacy_dirs ? name : mbentry->uniqueid, &listitem->l, locktype);
    if (r) {
        /* locked is not an error - just means we asked for NONBLOCKING */
        if (r != IMAP_MAILBOX_LOCKED)
            xsyslog(LOG_ERR, "IOERROR: lock failed",
                             "mailbox=<%s> error=<%s>",
                             name, error_message(r));
        if (mailbox->local_namespacelock)
            mboxname_release(&mailbox->local_namespacelock);
        mboxlist_entry_free(&mbentry);
        remove_listitem(listitem);
        return r;
    }

    if (!mbentry->name) mbentry->name = xstrdup(name);
    mailbox->mbentry = mbentry;

    if (mbentry->mbtype & MBTYPE_MOVING) {
        r = IMAP_MAILBOX_MOVED;
        goto done;
    }

    if (mbentry->mbtype & MBTYPE_INTERMEDIATE) {
        r = IMAP_MAILBOX_NONEXISTENT;
        goto done;
    }

    if (!mbentry->partition) {
        r = IMAP_MAILBOX_NONEXISTENT;
        goto done;
    }

    if (index_locktype == LOCK_SHARED)
        mailbox->is_readonly = 1;

    r = mailbox_open_index(mailbox);
    if (r) {
        xsyslog(LOG_ERR, "IOERROR: opening index failed",
                         "mailbox=<%s> error=<%s>",
                         mailbox_name(mailbox), error_message(r));
        goto done;
    }

lockindex:
    r = mailbox_lock_index(mailbox, index_locktype);

    /* we always nuke expunged if the version is less than 12 */
    if (mailbox->i.minor_version < 12)
        cleanup_stale_expunged(mailbox);

done:
    if (r) mailbox_close(&mailbox);
    else *mailboxptr = mailbox;

    return r;
}


static int _store_change(struct mailbox *mailbox, struct index_record *record, int flags)
{
    struct index_change *change = _find_change(mailbox, record->recno);

    if (!change) {
        mailbox->index_change_count++;

        /* allocate a space if required */
        if (mailbox->index_change_count > mailbox->index_change_alloc) {
            mailbox->index_change_alloc += 256;
            mailbox->index_changes = xrealloc(mailbox->index_changes, sizeof(struct index_change) * mailbox->index_change_alloc);
        }

        change = &mailbox->index_changes[mailbox->index_change_count-1];
        memset(change, 0, sizeof(struct index_change));

        /* stitch into place */
        uint32_t pos = record->recno % INDEX_MAP_SIZE;
        change->mapnext = mailbox->index_change_map[pos];
        mailbox->index_change_map[pos] = mailbox->index_change_count; /* always non-zero */
    }

    /* finally always copy the data into place */
    change->record = *record;
    change->flags = flags;

    if (mailbox_cacherecord(mailbox, record)) {
        /* failed to load cache record */
        free(change->msgid);
        change->msgid = xstrdup("unknown");
    }
    else {
        char *c_env = xstrndup(cacheitem_base(record, CACHE_ENVELOPE) + 1,
                               cacheitem_size(record, CACHE_ENVELOPE) - 2);
        char *envtokens[NUMENVTOKENS];
        parse_cached_envelope(c_env, envtokens, NUMENVTOKENS);
        free(change->msgid);
        change->msgid = xstrdup(envtokens[ENV_MSGID] ? envtokens[ENV_MSGID] : "unknown");
        free(c_env);
    }

    annotate_state_t *astate = NULL;
    int r = mailbox_get_annotate_state(mailbox, record->uid, &astate);
    if (r) return r;

    struct buf annotval = BUF_INITIALIZER;
    if (record->cid && record->basecid && record->basecid != record->cid)
        buf_printf(&annotval, "%016llx", record->basecid);

    r = annotate_state_writesilent(astate, IMAP_ANNOT_NS "basethrid", "", &annotval);
    buf_free(&annotval);

    if (r) return r;

    return 0;
}


EXPORTED struct entryattlist *mailbox_extract_annots(const struct mailbox *mailbox,
                                                     const struct index_record *record)
{
    struct entryattlist *annots = NULL;
    int r = annotatemore_findall_mailbox(mailbox, record->uid, "*", /*modseq*/0,
                                         load_annot_cb, &annots, /*flags*/0);
    if (r) return NULL;
    return annots;
}


static int mailbox_delete_alarms(struct mailbox *mailbox)
{
    if (!(mailbox->i.options & OPT_IMAP_HAS_ALARMS))
        return 0;

    return caldav_alarm_delete_mailbox(mailbox_name(mailbox));
}


EXPORTED const char *mailbox_datapath(struct mailbox *mailbox, uint32_t uid)
{
    static char localbuf[MAX_MAILBOX_PATH];
    uint32_t legacy_dirs = (mailbox_mbtype(mailbox) & MBTYPE_LEGACY_DIRS);
    const char *src;

    src = mboxname_datapath(mailbox_partition(mailbox), mailbox_name(mailbox),
                            legacy_dirs ? NULL : mailbox_uniqueid(mailbox),
                            uid);
    if (!src) return NULL;

    xstrncpy(localbuf, src, MAX_MAILBOX_PATH);
    return localbuf;
}


static int mailbox_update_conversations(struct mailbox *mailbox,
                                        const struct index_record *old,
                                        struct index_record *new)
{
    struct conversations_state *cstate = mailbox_get_cstate(mailbox);

    if (!cstate)
        return 0;

    /* handle unlinked items as if they didn't exist */
    if (old && (old->internal_flags & FLAG_INTERNAL_UNLINKED)) old = NULL;
    if (new && (new->internal_flags & FLAG_INTERNAL_UNLINKED)) new = NULL;

    if (!old && !new)
        return 0;

    int ignorelimits = new ? new->ignorelimits : 1;
    return conversations_update_record(cstate, mailbox, old, new,
                                       /*allowrenumber*/1, ignorelimits);
}


static int parseentry_cb(int type, struct dlistsax_data *d)
{
    struct parseentry_rock *rock = (struct parseentry_rock *)d->rock;

    const char *key = buf_cstring(&d->kbuf);

    switch(type) {
    case DLISTSAX_KVLISTSTART:
        if (!strcmp(key, "A")) {
            rock->doingacl = 1;
        }
        break;
    case DLISTSAX_KVLISTEND:
        if (rock->doingacl) {
            xzfree(rock->h->acl);
            rock->h->acl = buf_release(&rock->aclbuf);
            rock->doingacl = 0;
        }
        break;
    case DLISTSAX_LISTSTART:
        if (!strcmp(key, "U")) {
            rock->doingflags = 1;
            rock->nflags = 0;
        }
        break;
    case DLISTSAX_LISTEND:
        if (rock->doingflags) {
            rock->doingflags = 0;

            /* zero out the rest */
            while (rock->nflags < MAX_USER_FLAGS) {
                xzfree(rock->h->flagname[rock->nflags++]);
            }
        }
        break;
    case DLISTSAX_STRING:
        if (rock->doingacl) {
            buf_append(&rock->aclbuf, &d->kbuf);
            buf_putc(&rock->aclbuf, '\t');
            buf_appendcstr(&rock->aclbuf, d->data);
            buf_putc(&rock->aclbuf, '\t');
        }
        else if (rock->doingflags) {
            xzfree(rock->h->flagname[rock->nflags]);
            rock->h->flagname[rock->nflags++] = xstrdupnull(d->data);
        }
        else {
            if (!strcmp(key, "I")) {
                xzfree(rock->h->uniqueid);
                rock->h->uniqueid = xstrdupnull(d->data);
            }
            else if (!strcmp(key, "N")) {
                xzfree(rock->h->name);
                rock->h->name = xstrdupnull(d->data);
            }
            else if (!strcmp(key, "T")) {
                rock->h->mbtype = mboxlist_string_to_mbtype(d->data);
            }
            else if (!strcmp(key, "Q")) {
                xzfree(rock->h->quotaroot);
                rock->h->quotaroot = xstrdupnull(d->data);
            }
        }
    }

    return 0;
}


EXPORTED int mailbox_reload_index_record(struct mailbox *mailbox,
                                         struct index_record *record)
{
    if (record->recno)
        return mailbox_read_index_record(mailbox, record->recno, record);
    else
        return mailbox_find_index_record(mailbox, record->uid, record);
}


EXPORTED const char *mailbox_uniqueid(const struct mailbox *mailbox)
{
    mbentry_t *mbentry = mailbox->mbentry;

    return mbentry->uniqueid ? mbentry->uniqueid : mailbox->h.uniqueid;
}


static int mailbox_abort_cache(struct mailbox *mailbox)
{
    if (!mailbox->caches.count)
        return 0;

    /* XXX - we need offsets into each file to use
       mappedfile_truncate */

    return 0;
}


static int mailbox_repack_setup(struct mailbox *mailbox, int version,
                                struct mailbox_repack **repackptr)
{
    struct mailbox_repack *repack = xzmalloc(sizeof(struct mailbox_repack));
    const char *fname;
    indexbuffer_t ibuf;
    unsigned char *buf = ibuf.buf;
    int n;

    /* if we're changing version at all, recalculate counts up-front */
    if (version != mailbox->i.minor_version) {
        /* NOTE: this maps in annot_state in mailbox, which will get copied
         * into newmailbox below, which means all the annotate stuff later
         * is going to be good!  The counters will be updated in the original
         * mailbox index, but that's OK because we recalc everything at the
         * end */
        int r = mailbox_index_recalc(mailbox);
        if (r) goto fail;
    }

    /* init */
    repack->mailbox = mailbox;
    repack->crcs = mailbox->i.synccrcs;
    repack->newmailbox = *mailbox; // struct copy
    repack->newmailbox.index_fd = -1;

    /* new files */
    fname = mailbox_meta_newfname(mailbox, META_INDEX);
    repack->newmailbox.index_fd = open(fname, O_RDWR|O_TRUNC|O_CREAT, 0666);
    if (repack->newmailbox.index_fd == -1) {
        xsyslog(LOG_ERR, "IOERROR: create failed",
                         "fname=<%s>",
                         fname);
        goto fail;
    }

    /* update the generation number */
    repack->newmailbox.i.generation_no++;

    /* track the version number */
    repack->newmailbox.i.minor_version = version;
    switch (version) {
    case 6:
        repack->newmailbox.i.start_offset = 76;
        repack->newmailbox.i.record_size = 60;
        break;
    case 7:
        repack->newmailbox.i.start_offset = 76;
        repack->newmailbox.i.record_size = 72;
        break;
    case 8:
        repack->newmailbox.i.start_offset = 92;
        repack->newmailbox.i.record_size = 80;
        break;
    case 9:
        repack->newmailbox.i.start_offset = 96;
        repack->newmailbox.i.record_size = 80;
        break;
    case 10:
        repack->newmailbox.i.start_offset = 96;
        repack->newmailbox.i.record_size = 88;
        break;
    /* 11 was FastMail internal */
    case 12:
        repack->newmailbox.i.start_offset = 128;
        repack->newmailbox.i.record_size = 96;
        break;
    case 13:
        repack->newmailbox.i.start_offset = 128;
        repack->newmailbox.i.record_size = 104;
        break;
    case 14:
        /* version 15 just repurposed a field */
    case 15:
        repack->newmailbox.i.start_offset = 160;
        repack->newmailbox.i.record_size = 104;
        break;
    case 16:
    case 17:
        repack->newmailbox.i.start_offset = 160;
        repack->newmailbox.i.record_size = 112;
        break;
    default:
        fatal("index version not supported", EX_SOFTWARE);
    }

    /* upgrades or downgrades across version 12 boundary?  Sort out seen state */
    if (version >= 12 && mailbox->i.minor_version < 12) {
        /* we need to read the current seen state for the owner */
        struct seendata sd = SEENDATA_INITIALIZER;
        int r = IMAP_MAILBOX_NONEXISTENT;
        if (mailbox->i.options & OPT_IMAP_SHAREDSEEN)
            repack->userid = xstrdup("anyone");
        else
            repack->userid = mboxname_to_userid(mailbox_name(mailbox));

        if (repack->userid) {
            struct seen *seendb = NULL;
            r = seen_open(repack->userid, SEEN_SILENT, &seendb);
            if (!r) r = seen_read(seendb, mailbox_uniqueid(mailbox), &sd);
            seen_close(&seendb);
        }

        if (!r) {
            repack->newmailbox.i.recentuid = sd.lastuid;
            repack->newmailbox.i.recenttime = sd.lastchange;
            repack->seqset = seqset_parse(sd.seenuids, NULL, sd.lastuid);
            seen_freedata(&sd);
        }
    }
    else if (version < 12 && mailbox->i.minor_version >= 12) {
        if (mailbox->i.options & OPT_IMAP_SHAREDSEEN)
            repack->userid = xstrdup("anyone");
        else
            repack->userid = mboxname_to_userid(mailbox_name(mailbox));

        /* we need to create the seen state for the owner from the mailbox */
        if (repack->userid)
            repack->seqset = seqset_init(mailbox->i.last_uid, SEQ_MERGE);
    }

    /* we'll count the records as they get added */
    repack->newmailbox.i.num_records = 0;
    /* we're recreating caches, so there'll be nothing leaked */
    repack->newmailbox.i.leaked_cache_records = 0;

    /* prepare initial header buffer */
    mailbox_index_header_to_buf(&repack->newmailbox.i, buf);

    n = retry_write(repack->newmailbox.index_fd, buf, repack->newmailbox.i.start_offset);
    if (n == -1) goto fail;

    if (version != mailbox->i.minor_version) {
        /* we're might be reading this file as we go, so let's
         * start the job by mapping the new file into the newmailbox!
         */
        repack->newmailbox.index_size = 0;
        repack->newmailbox.index_base = NULL;
        repack->newmailbox.index_len = 0;
        int r = mailbox_refresh_index_map(&repack->newmailbox);
        if (r) goto fail;
    }

    *repackptr = repack;
    return 0;

 fail:
    mailbox_repack_abort(&repack);
    return IMAP_IOERROR;
}


EXPORTED const message_t *mailbox_iter_step(struct mailbox_iter *iter)
{
    if (mailbox_wait_cb) mailbox_wait_cb(mailbox_wait_cb_rock);

    for (iter->recno++; iter->recno <= iter->num_records; iter->recno++) {
        message_set_from_mailbox(iter->mailbox, iter->recno, iter->msg);
        const struct index_record *record = msg_record(iter->msg);
        if (!record->uid) continue; /* can happen on damaged mailboxes */
        if ((record->system_flags & iter->skipflags)) continue;
        if ((record->internal_flags & iter->skipflags)) continue;
        if (iter->changedsince && record->modseq <= iter->changedsince) continue;
        if (iter->uidset) {
            if (record->uid > seqset_last(iter->uidset)) return NULL;
            if (!seqset_ismember(iter->uidset, record->uid)) continue;
        }
        return iter->msg;
    }

    /* guess we're done */
    return NULL;
}


static void mailbox_quota_dirty(struct mailbox *mailbox)
{
    /* track quota use */
    if (!mailbox->quota_dirty) {
        mailbox->quota_dirty = 1;
        mailbox_get_usage(mailbox, mailbox->quota_previously_used);
    }
}


static struct mappedfile *cache_getfile(ptrarray_t *list, const char *fname,
                                        int readonly, uint32_t generation)
{
    struct mappedfile *cachefile = NULL;
    int openflags = readonly ? 0 : MAPPEDFILE_CREATE | MAPPEDFILE_RW;
    int i;

    for (i = 0; i < list->count; i++) {
        cachefile = ptrarray_nth(list, i);
        if (!strcmp(fname, mappedfile_fname(cachefile)))
            return cachefile;
    }

    /* guess we didn't find it - open a new one */
    cachefile = NULL;
    if (mappedfile_open(&cachefile, fname, openflags)) {
        xsyslog(LOG_ERR, "IOERROR: failed to open cache file",
                         "fname=<%s>",
                         fname);
        return NULL;
    }

    if (!readonly && !mappedfile_size(cachefile)) {
        /* zero byte file?  Set the generation */
        uint32_t netgen = htonl(generation);
        mappedfile_pwrite(cachefile, (const char *) &netgen, 4, 0);
        mappedfile_commit(cachefile);
    }

    ptrarray_append(list, cachefile);

    return cachefile;
}


EXPORTED const char *mailbox_quotaroot(const struct mailbox *mailbox)
{
    return mailbox->h.quotaroot;
}


static int _map_local_record(const struct mailbox *mailbox, const char *fname, struct buf *buf)
{
    struct stat sbuf;
    int msgfd;

    msgfd = open(fname, O_RDONLY, 0666);
    if (msgfd == -1) return errno;

    if (fstat(msgfd, &sbuf) == -1) {
        xsyslog(LOG_ERR, "IOERROR: fstat failed",
                         "fname=<%s>",
                         fname);
        fatal("can't fstat message file", EX_OSFILE);
    }

    buf_refresh_mmap(buf, /*onceonly*/1, msgfd, fname, sbuf.st_size, mailbox_name(mailbox));
    close(msgfd);

    return 0;
}


static int do_reconstruct_p(const mbentry_t *mbentry, void *rock)
{
    if ((mbentry->mbtype & (MBTYPE_DELETED|MBTYPE_INTERMEDIATE)))
        return 0;

    mboxlist_findone(&recon_namespace, mbentry->name, 1, 0, 0,
                     do_reconstruct, rock);

    return 0;
}


EXPORTED int mailbox_user_flag(struct mailbox *mailbox, const char *flag,
                      int *flagnum, int create)
{
    int userflag;
    int emptyflag = -1;

    for (userflag = 0; userflag < MAX_USER_FLAGS; userflag++) {
        // keep track of the first empty slot
        if (!mailbox->h.flagname[userflag]) {
            if (emptyflag == -1)
                emptyflag = userflag;
            continue;
        }

        // name matches?  We've found the flag
        if (!strcasecmp(flag, mailbox->h.flagname[userflag]))
            break;
    }

    if (userflag == MAX_USER_FLAGS) {
        if (!create)
            return IMAP_NOTFOUND;

        if (emptyflag == -1)
            return IMAP_USERFLAG_EXHAUSTED;

        /* stop imapd exhausting flags */
        if (create == 1 && _too_many_flags(flag, emptyflag)) {
            xsyslog(LOG_ERR, "IOERROR: out of flags",
                             "mailbox=<%s> flag=<%s>",
                             mailbox_name(mailbox), flag);
            return IMAP_USERFLAG_EXHAUSTED;
        }

        /* need to be index locked to make flag changes */
        if (!mailbox_index_islocked(mailbox, 1))
            return IMAP_MAILBOX_LOCKED;

        if (!imparse_isatom(flag))
            return IMAP_INVALID_IDENTIFIER;

        /* set the flag and mark the header dirty */
        userflag = emptyflag;
        mailbox->h.flagname[emptyflag] = xstrdup(flag);
        mailbox->header_dirty = 1;
    }

    if (flagnum) *flagnum = userflag;

    return 0;
}


EXPORTED int mailbox_expunge(struct mailbox *mailbox,
                    mailbox_decideproc_t *decideproc, void *deciderock,
                    unsigned *nexpunged, int event_type)
{
    int r = 0;
    int numexpunged = 0;
    const message_t *msg;
    struct mboxevent *mboxevent = NULL;

    assert(mailbox_index_islocked(mailbox, 1));

    /* anything to do? */
    if (!mailbox->i.num_records) {
        if (nexpunged) *nexpunged = 0;
        return 0;
    }

    if (event_type)
        mboxevent = mboxevent_new(event_type);

    if (!decideproc) decideproc = expungedeleted;

    struct mailbox_iter *iter = mailbox_iter_init(mailbox, 0, ITER_SKIP_EXPUNGED);
    while ((msg = mailbox_iter_step(iter))) {
        const struct index_record *record = msg_record(msg);
        if (decideproc(mailbox, record, deciderock)) {
            numexpunged++;

            struct index_record copyrecord = *record;
            /* mark deleted */
            copyrecord.internal_flags |= FLAG_INTERNAL_EXPUNGED;

            r = mailbox_rewrite_index_record(mailbox, &copyrecord);
            if (r) {
                mboxevent_free(&mboxevent);
                mailbox_iter_done(&iter);
                return IMAP_IOERROR;
            }

            mboxevent_extract_record(mboxevent, mailbox, &copyrecord);
        }
    }
    mailbox_iter_done(&iter);

    if (numexpunged > 0) {
        syslog(LOG_NOTICE, "Expunged %d messages from %s",
               numexpunged, mailbox_name(mailbox));

        /* send the MessageExpunge or MessageExpire event notification */
        mboxevent_extract_mailbox(mboxevent, mailbox);
        mboxevent_set_access(mboxevent, NULL, NULL, "", mailbox_name(mailbox), 0);
        mboxevent_set_numunseen(mboxevent, mailbox, -1);
        mboxevent_notify(&mboxevent);
    }
    mboxevent_free(&mboxevent);

    if (nexpunged) *nexpunged = numexpunged;

    return 0;
}


static uint32_t crc_annot(unsigned int uid, const char *entry,
                          const char *userid, const struct buf *value)
{
    struct buf buf = BUF_INITIALIZER;
    uint32_t res = 0;

    // ignore everything with a NULL userid, it's bogus!
    if (!userid) return 0;

    buf_printf(&buf, "%u %s %s ", uid, entry, userid);
    buf_append(&buf, value);
    res = crc32_buf(&buf);
    buf_free(&buf);

    return res;
}


static int common_listxattr(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
                            char *list, size_t size)
{
    char *path;
    int err;

    err = -ENOENT;
    pthread_rwlock_rdlock(&f->tree_lock);
    path = get_path(f, ino);
    if (path != NULL) {
        struct fuse_intr_data d;
        fuse_prepare_interrupt(f, req, &d);
        err = fuse_fs_listxattr(f->fs, path, list, size);
        fuse_finish_interrupt(f, req, &d);
        free(path);
    }
    pthread_rwlock_unlock(&f->tree_lock);
    return err;
}


static void fuse_lib_forget(fuse_req_t req, fuse_ino_t ino,
                            unsigned long nlookup)
{
    struct fuse *f = req_fuse(req);
    if (f->conf.debug)
        fprintf(stderr, "FORGET %llu/%lu\n", (unsigned long long)ino, nlookup);
    forget_node(f, ino, nlookup);
    fuse_reply_none(req);
}


int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
                    struct fuse_file_info *fi)
{
    fuse_get_context()->private_data = fs->user_data;
    if (fs->op.opendir)
        return fs->op.opendir(path, fi);
    else
        return 0;
}


int fuse_fs_unlink(struct fuse_fs *fs, const char *path)
{
    fuse_get_context()->private_data = fs->user_data;
    if (fs->op.unlink)
        return fs->op.unlink(path);
    else
        return -ENOSYS;
}


static void fuse_prepare_interrupt(struct fuse *f, fuse_req_t req,
                                          struct fuse_intr_data *d)
{
    if (f->conf.intr)
        fuse_do_prepare_interrupt(req, d);
}


static void fuse_lib_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
                           mode_t mode)
{
    struct fuse *f = req_fuse_prepare(req);
    struct fuse_entry_param e;
    char *path;
    int err;

    err = -ENOENT;
    pthread_rwlock_rdlock(&f->tree_lock);
    path = get_path_name(f, parent, name);
    if (path != NULL) {
        struct fuse_intr_data d;
        if (f->conf.debug)
            fprintf(stderr, "MKDIR %s\n", path);
        fuse_prepare_interrupt(f, req, &d);
        err = fuse_fs_mkdir(f->fs, path, mode);
        if (!err)
            err = lookup_path(f, parent, name, path, &e, NULL);
        fuse_finish_interrupt(f, req, &d);
        free(path);
    }
    pthread_rwlock_unlock(&f->tree_lock);
    reply_entry(req, &e, err);
}


static void fuse_restore_intr_signal(int signum)
{
    struct sigaction sa;

    memset(&sa, 0, sizeof(struct sigaction));
    sa.sa_handler = SIG_DFL;
    sigaction(signum, &sa, NULL);
}


static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
                       struct fuse_file_info *llfi)
{
    struct fuse *f = req_fuse_prepare(req);
    struct fuse_intr_data d;
    struct fuse_dh *dh;
    struct fuse_file_info fi;
    char *path;
    int err;

    dh = (struct fuse_dh *) malloc(sizeof(struct fuse_dh));
    if (dh == NULL) {
        reply_err(req, -ENOMEM);
        return;
    }
    memset(dh, 0, sizeof(struct fuse_dh));
    dh->fuse = f;
    dh->contents = NULL;
    dh->len = 0;
    dh->filled = 0;
    dh->nodeid = ino;
    fuse_mutex_init(&dh->lock);

    llfi->fh = (uintptr_t) dh;

    memset(&fi, 0, sizeof(fi));
    fi.flags = llfi->flags;

    err = -ENOENT;
    pthread_rwlock_rdlock(&f->tree_lock);
    path = get_path(f, ino);
    if (path != NULL) {
        fuse_prepare_interrupt(f, req, &d);
        err = fuse_fs_opendir(f->fs, path, &fi);
        fuse_finish_interrupt(f, req, &d);
        dh->fh = fi.fh;
    }
    if (!err) {
        if (fuse_reply_open(req, llfi) == -ENOENT) {
            /* The opendir syscall was interrupted, so it must be cancelled */
            fuse_prepare_interrupt(f, req, &d);
            fuse_fs_releasedir(f->fs, path, &fi);
            fuse_finish_interrupt(f, req, &d);
            pthread_mutex_destroy(&dh->lock);
            free(dh);
        }
    } else {
        reply_err(req, err);
        pthread_mutex_destroy(&dh->lock);
        free(dh);
    }
    free(path);
    pthread_rwlock_unlock(&f->tree_lock);
}


static void fuse_lib_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
{
    struct fuse *f = req_fuse_prepare(req);
    char *path;
    int err;

    err = -ENOENT;
    pthread_rwlock_wrlock(&f->tree_lock);
    path = get_path_name(f, parent, name);
    if (path != NULL) {
        struct fuse_intr_data d;
        if (f->conf.debug)
            fprintf(stderr, "RMDIR %s\n", path);
        fuse_prepare_interrupt(f, req, &d);
        err = fuse_fs_rmdir(f->fs, path);
        fuse_finish_interrupt(f, req, &d);
        if (!err)
            remove_node(f, parent, name);
        free(path);
    }
    pthread_rwlock_unlock(&f->tree_lock);
    reply_err(req, err);
}


static void fuse_lib_statfs(fuse_req_t req, fuse_ino_t ino)
{
    struct fuse *f = req_fuse_prepare(req);
    struct statvfs buf;
    char *path;
    int err;

    memset(&buf, 0, sizeof(buf));
    pthread_rwlock_rdlock(&f->tree_lock);
    if (!ino) {
        err = -ENOMEM;
        path = strdup("/");
    } else {
        err = -ENOENT;
        path = get_path(f, ino);
    }
    if (path) {
        struct fuse_intr_data d;
        fuse_prepare_interrupt(f, req, &d);
        err = fuse_fs_statfs(f->fs, path, &buf);
        fuse_finish_interrupt(f, req, &d);
        free(path);
    }
    pthread_rwlock_unlock(&f->tree_lock);

    if (!err)
        fuse_reply_statfs(req, &buf);
    else
        reply_err(req, err);
}


static int rename_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
                        fuse_ino_t newdir, const char *newname, int hide)
{
    struct node *node;
    struct node *newnode;
    int err = 0;

    pthread_mutex_lock(&f->lock);
    node  = lookup_node(f, olddir, oldname);
    newnode  = lookup_node(f, newdir, newname);
    if (node == NULL)
        goto out;

    if (newnode != NULL) {
        if (hide) {
            fprintf(stderr, "fuse: hidden file got created during hiding\n");
            err = -EBUSY;
            goto out;
        }
        unhash_name(f, newnode);
    }

    unhash_name(f, node);
    if (hash_name(f, node, newdir, newname) == -1) {
        err = -ENOMEM;
        goto out;
    }

    if (hide)
        node->is_hidden = 1;

 out:
    pthread_mutex_unlock(&f->lock);
    return err;
}


int fuse_fs_release(struct fuse_fs *fs,  const char *path,
                    struct fuse_file_info *fi)
{
    fuse_get_context()->private_data = fs->user_data;
    if (fs->op.release)
        return fs->op.release(path, fi);
    else
        return 0;
}


static inline void reply_err(fuse_req_t req, int err)
#endif /* __SOLARIS__ */
{
    /* fuse_reply_err() uses non-negated errno values */
    fuse_reply_err(req, -err);
}


NTSTATUS check_path_syntax(char *path)
{
	return check_path_syntax_internal(path, false);
}


static void naldmx_switch_timestamps(GF_NALUDmxCtx *ctx, GF_FilterPacket *pck)
{
	//input pid sets some timescale - we flushed pending data , update cts
	if (!ctx->notime) {
		u64 ts = gf_filter_pck_get_cts(pck);
		if (ts != GF_FILTER_NO_TS) {
			ctx->prev_cts = ctx->cts;
			ctx->cts = ts;
		}
		ts = gf_filter_pck_get_dts(pck);
		if (ts != GF_FILTER_NO_TS) {
			if (ctx->full_au_source) {
				ctx->prev_dts = ctx->dts;
				ctx->dts = ts;
			} else {
				GF_FilterClockType ck_type = gf_filter_pid_get_clock_info(ctx->ipid, NULL, NULL);
				if (ck_type==GF_FILTER_CLOCK_PCR_DISC)
					ctx->dts = ts;
				else if (ctx->dts<ts)
					ctx->dts=ts;

				if (!ctx->prev_dts) ctx->prev_dts = ts;
				else if (ctx->prev_dts != ts) {
					u64 diff = ts;
					diff -= ctx->prev_dts;
					if (!ctx->cur_fps.den)
						ctx->cur_fps.den = (u32) diff;
					else if (ctx->cur_fps.den > diff)
						ctx->cur_fps.den = (u32) diff;

					ctx->prev_dts = ts;
				}
			}
		}
		ctx->pck_duration = gf_filter_pck_get_duration(pck);
		if (ctx->src_pck) gf_filter_pck_unref(ctx->src_pck);
		ctx->src_pck = pck;
		gf_filter_pck_ref_props(&ctx->src_pck);
		//store framing flags. If input_is_au_start, the first NAL of the first frame beginning in this packet will
		//use the DTS/CTS of the input packet, otherwise we will use our internal POC recompute
		gf_filter_pck_get_framing(pck, &ctx->input_is_au_start, NULL);
	}
}


static u64 naludmx_next_start_code(GF_BitStream *bs, u64 offset, u64 fsize, u32 *sc_size)
{
	u32 pos=0, nb_zeros=0;
	while (offset+pos<fsize) {
		u8 b = gf_bs_read_u8(bs);
		pos++;
		switch (b) {
		case 1:
			//break at first 0xXX000001 or 0x00000001
			if (nb_zeros>=2) {
				*sc_size = (nb_zeros==2) ? 3 : 4;
				return offset+pos;
			}
			nb_zeros = 0;
			break;
		case 0:
			nb_zeros++;
			break;
		default:
			nb_zeros=0;
			break;
		}
	}
	//eof
	return 0;
}


GF_FilterPacket *naludmx_start_nalu(GF_NALUDmxCtx *ctx, u32 nal_size, Bool skip_nal_field, Bool *au_start, u8 **pck_data)
{
	GF_FilterPacket *dst_pck = gf_filter_pck_new_alloc(ctx->opid, nal_size + (skip_nal_field ? 0 : ctx->nal_length), pck_data);
	if (!dst_pck) return NULL;

	if (!skip_nal_field) {
		if (!ctx->bs_w) ctx->bs_w = gf_bs_new(*pck_data, ctx->nal_length, GF_BITSTREAM_WRITE);
		else gf_bs_reassign_buffer(ctx->bs_w, *pck_data, ctx->nal_length);
		gf_bs_write_int(ctx->bs_w, nal_size, 8*ctx->nal_length);
	}

	if (*au_start) {
		ctx->first_pck_in_au = dst_pck;
		if (ctx->src_pck) gf_filter_pck_merge_properties(ctx->src_pck, dst_pck);

		gf_filter_pck_set_framing(dst_pck, GF_TRUE, GF_FALSE);
		//we reuse the timing of the input packet for the first nal of the first frame starting in this packet
		if (ctx->input_is_au_start) {
			ctx->input_is_au_start = GF_FALSE;
			gf_filter_pck_set_dts(dst_pck, ctx->dts);
			gf_filter_pck_set_cts(dst_pck, ctx->cts);
		} else {
			//we don't set the CTS, it will be set once we detect frame end
			gf_filter_pck_set_dts(dst_pck, ctx->dts);
		}
		//we use the carousel flag temporarly to indicate the cts must be recomputed
		gf_filter_pck_set_carousel_version(dst_pck, ctx->notime ? 1 : 0);

		gf_filter_pck_set_duration(dst_pck, ctx->pck_duration ? ctx->pck_duration : ctx->cur_fps.den);
		if (ctx->in_seek) {
			gf_filter_pck_set_seek_flag(dst_pck, GF_TRUE);
			if (ctx->first_gdr) {
				ctx->first_gdr = GF_FALSE;
				gf_filter_pck_set_sap(ctx->first_pck_in_au, GF_FILTER_SAP_4);
			}
		}

		naludmx_update_time(ctx);
		*au_start = GF_FALSE;
		ctx->nb_frames++;
	} else {
		gf_filter_pck_set_framing(dst_pck, GF_FALSE, GF_FALSE);
	}
	naludmx_update_nalu_maxsize(ctx, nal_size);

	naludmx_enqueue_or_dispatch(ctx, dst_pck, GF_FALSE);

	return dst_pck;
}


GF_Err gp_rtp_builder_do_mpeg4(GP_RTPPacketizer *builder, u8 *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize)
{
	u8 *sl_buffer, *payl_buffer;
	u32 sl_buffer_size, payl_buffer_size;
	u32 auh_size_tmp, bytesLeftInPacket, infoSize, pckSize;
	u64 pos;
	u8 flush_pck, no_split;

	flush_pck = 0;

	bytesLeftInPacket = data_size;
	/*flush everything*/
	if (!data) {
		if (builder->payload) goto flush_packet;
		return GF_OK;
	}
	if (builder->payload && builder->force_flush) goto flush_packet;

	//go till done
	while (bytesLeftInPacket) {
		no_split = 0;

		if (builder->sl_header.accessUnitStartFlag) {
			//init SL
			if (builder->sl_header.compositionTimeStamp != builder->sl_header.decodingTimeStamp) {
				builder->sl_header.decodingTimeStampFlag = 1;
			}
			builder->sl_header.compositionTimeStampFlag = 1;
			builder->sl_header.accessUnitLength = FullAUSize;

			//init some vars - based on the available size and the TS
			//we decide if we go with the same RTP TS serie or not
			if (builder->payload) {
				//don't store more than what we can (that is 2^slMap->CTSDelta - 1)
				if ( (builder->flags & GP_RTP_PCK_SIGNAL_TS)
				        && (builder->sl_header.compositionTimeStamp - builder->rtp_header.TimeStamp >= (u32) ( 1 << builder->slMap.CTSDeltaLength) ) ) {
					goto flush_packet;
				}
				//don't split AU if # TS , start a new RTP pck
				if (builder->sl_header.compositionTimeStamp != builder->rtp_header.TimeStamp)
					no_split = 1;
			}
		}

		/*new RTP Packet*/
		if (!builder->payload) {
			/*first SL in RTP*/
			builder->first_sl_in_rtp = GF_TRUE;

			/*if this is the end of an AU we will set it to 0 as soon as an AU is splitted*/
			builder->rtp_header.Marker = 1;
			builder->rtp_header.PayloadType = builder->PayloadType;
			builder->rtp_header.SequenceNumber += 1;

			builder->rtp_header.TimeStamp = (u32) builder->sl_header.compositionTimeStamp;
			/*prepare the mapped headers*/
			builder->pck_hdr = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
			builder->payload = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
			builder->bytesInPacket = 0;

			/*in multiSL there is a MSLHSize structure on 2 bytes*/
			builder->auh_size = 0;
			if (builder->has_AU_header) {
				builder->auh_size = 16;
				gf_bs_write_int(builder->pck_hdr, 0, 16);
			}
			flush_pck = 0;
			/*and create packet*/
			builder->OnNewPacket(builder->cbk_obj, &builder->rtp_header);
		}

		//make sure we are not interleaving too much - this should never happen actually
		if (builder->slMap.IndexDeltaLength
		        && !builder->first_sl_in_rtp
		        && (builder->sl_header.AU_sequenceNumber - builder->last_au_sn >= (u32) 1<<builder->slMap.IndexDeltaLength)) {
			//we cannot write this packet here
			goto flush_packet;
		}
		/*check max ptime*/
		if (builder->max_ptime && ( (u32) builder->sl_header.compositionTimeStamp >= builder->rtp_header.TimeStamp + builder->max_ptime) )
			goto flush_packet;

		auh_size_tmp = gf_rtp_build_au_hdr_size(builder, &builder->sl_header);

		infoSize = auh_size_tmp + builder->auh_size;
		infoSize /= 8;
		/*align*/
		if ( (builder->auh_size + auh_size_tmp) % 8) infoSize += 1;

		if (bytesLeftInPacket + infoSize + builder->bytesInPacket <= builder->Path_MTU) {
			//End of our data chunk
			pckSize = bytesLeftInPacket;
			builder->sl_header.accessUnitEndFlag = IsAUEnd;

			builder->auh_size += auh_size_tmp;

			builder->sl_header.paddingFlag = builder->sl_header.paddingBits ? 1 : 0;
		} else {

			//AU cannot fit in packet. If no split, start a new packet
			if (no_split) goto flush_packet;

			builder->auh_size += auh_size_tmp;

			pckSize = builder->Path_MTU - (infoSize + builder->bytesInPacket);
			//that's the end of the rtp packet
			flush_pck = 1;
			//but not of the AU -> marker is 0
			builder->rtp_header.Marker = 0;
		}

		gf_rtp_build_au_hdr_write(builder, pckSize, builder->rtp_header.TimeStamp);

		//notify the user of our data structure
		if (builder->OnDataReference)
			builder->OnDataReference(builder->cbk_obj, pckSize, data_size - bytesLeftInPacket);
		else
			gf_bs_write_data(builder->payload, data + (data_size - bytesLeftInPacket), pckSize);

		bytesLeftInPacket -= pckSize;
		builder->bytesInPacket += pckSize;
		/*update IV*/
		builder->IV += pckSize;
		builder->sl_header.paddingFlag = 0;
		builder->sl_header.accessUnitStartFlag = 0;

		//we are splitting a payload, auto increment SL seq num
		if (bytesLeftInPacket) {
			builder->sl_header.packetSequenceNumber += 1;
		} else if (! (builder->flags & GP_RTP_PCK_USE_MULTI) ) {
			builder->rtp_header.Marker = 1;
			flush_pck = 1;
		}

		//first SL in RTP is done
		builder->first_sl_in_rtp = GF_FALSE;

		//store current sl
		builder->last_au_sn = builder->sl_header.AU_sequenceNumber;

		if (!flush_pck) continue;

		//done with the packet
flush_packet:

		gf_bs_align(builder->pck_hdr);

		/*no aux data yet*/
		if (builder->slMap.AuxiliaryDataSizeLength)	{
			//write RSLH after the MSLH
			gf_bs_write_int(builder->pck_hdr, 0, builder->slMap.AuxiliaryDataSizeLength);
		}
		/*rewrite the size header*/
		if (builder->has_AU_header) {
			pos = gf_bs_get_position(builder->pck_hdr);
			gf_bs_seek(builder->pck_hdr, 0);
			builder->auh_size -= 16;
			gf_bs_write_int(builder->pck_hdr, builder->auh_size, 16);
			gf_bs_seek(builder->pck_hdr, pos);
		}

		sl_buffer = NULL;
		gf_bs_get_content(builder->pck_hdr, &sl_buffer, &sl_buffer_size);
		//delete our bitstream
		gf_bs_del(builder->pck_hdr);
		builder->pck_hdr = NULL;

		payl_buffer = NULL;
		payl_buffer_size = 0;
		if (!builder->OnDataReference)
			gf_bs_get_content(builder->payload, &payl_buffer, &payl_buffer_size);

		gf_bs_del(builder->payload);
		builder->payload = NULL;

		/*notify header*/
		builder->OnData(builder->cbk_obj, sl_buffer, sl_buffer_size, GF_TRUE);
		/*notify payload*/
		if (payl_buffer) {
			builder->OnData(builder->cbk_obj, payl_buffer, payl_buffer_size, GF_FALSE);
			gf_free(payl_buffer);
		}
		/*flush packet*/
		builder->OnPacketDone(builder->cbk_obj, &builder->rtp_header);
		gf_free(sl_buffer);
	}
	//packet is done, update AU markers
	if (IsAUEnd) {
		builder->sl_header.accessUnitStartFlag = 1;
		builder->sl_header.accessUnitEndFlag = 0;
	}
	return GF_OK;
}


static Bool nhmldmx_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
{
	u32 i=0;
	u64 cur_dts = 0;
	u64 byte_offset = 0;
	u32 sample_num = 0;
	GF_XMLNode *node;
	GF_NHMLDmxCtx *ctx = gf_filter_get_udta(filter);

	switch (evt->base.type) {
	case GF_FEVT_PLAY:
		if (ctx->is_playing && (ctx->start_range ==  evt->play.start_range)) {
			return GF_TRUE;
		}

		ctx->start_range = evt->play.start_range;
		ctx->current_child_idx = 0;
		ctx->media_done = ctx->header_end;
		ctx->is_playing = GF_TRUE;
		//post a seek
		ctx->in_seek = GF_TRUE;

		//lcoate previous RAP sample
		while ((node = (GF_XMLNode *) gf_list_enum(ctx->root->content, &i))) {
			u32 j=0;
			u64 dts=0;
			u32 datalen=0;
			Bool is_rap = ctx->has_sap ? GF_FALSE : GF_TRUE;
			s32 cts_offset=0;
			u64 sample_duration = 0;
			GF_XMLAttribute *att;
			if (node->type) continue;
			if (stricmp(node->name, ctx->is_dims ? "DIMSUnit" : "NHNTSample") ) continue;

			while ( (att = (GF_XMLAttribute *)gf_list_enum(node->attributes, &j))) {
				if (!stricmp(att->name, "DTS") || !stricmp(att->name, "time")) {
					u32 h, m, s, ms;
					u64 dst_val;
					if (strchr(att->value, ':') && sscanf(att->value, "%u:%u:%u.%u", &h, &m, &s, &ms) == 4) {
						dts = (u64) ( (Double) ( ((h*3600.0 + m*60.0 + s)*1000 + ms) / 1000.0) * ctx->timescale );
					} else if (sscanf(att->value, ""LLU, &dst_val)==1) {
						dts = dst_val;
					}
				}
				else if (!stricmp(att->name, "CTSOffset")) cts_offset = atoi(att->value);
				else if (!stricmp(att->name, "duration") ) sscanf(att->value, ""LLU, &sample_duration);
				else if (!stricmp(att->name, "isRAP") ) {
					is_rap = (!stricmp(att->value, "yes")) ? GF_TRUE : GF_FALSE;
				}
				else if (!stricmp(att->name, "mediaOffset"))
					byte_offset = (s64) atof(att->value) ;
				else if (!stricmp(att->name, "dataLength"))
					datalen = atoi(att->value);
			}

			dts += cts_offset;
			if ((s64) dts < 0) dts = 0;

			if (dts) cur_dts = dts;
			if (sample_duration) cur_dts += sample_duration;
			else if (ctx->dts_inc) cur_dts += ctx->dts_inc;

			if (cur_dts >= ctx->timescale * evt->play.start_range) {
				break;
			}
			if (is_rap) {
				ctx->current_child_idx = i-1;
				ctx->media_done = byte_offset;
				ctx->sample_num = sample_num;
			}
			byte_offset += datalen;
			sample_num++;
		}

		//cancel event
		return GF_TRUE;

	case GF_FEVT_STOP:
		ctx->is_playing = GF_FALSE;
		//don't cancel event
		return GF_FALSE;

	case GF_FEVT_SET_SPEED:
		//cancel event
		return GF_TRUE;
	default:
		break;
	}
	//by default don't cancel event - to rework once we have downloading in place
	return GF_FALSE;
}


static GF_Err nhmldmx_send_sample(GF_Filter *filter, GF_NHMLDmxCtx *ctx)
{
	GF_XMLNode *node, *childnode;
	u64 sample_duration = 0;
	char szMediaTemp[GF_MAX_PATH], szXmlFrom[1000], szXmlTo[1000];
	char *szSubSampleName = ctx->is_dims ? "DIMSSubUnit" : "NHNTSubSample";

	while ((node = (GF_XMLNode *) gf_list_enum(ctx->root->content, &ctx->current_child_idx))) {
		u8 *data;
		GF_FilterPacket *pck;
		u32 j, dims_flags;
		GF_FilterSAPType sap_type;
		GF_XMLAttribute *att;
		u64 dts=0;
		GF_Err e=GF_OK;
		s32 cts_offset;
		u64 offset=0, byte_offset = GF_FILTER_NO_BO;
		u32 nb_subsamples = 0;
		Bool redundant_rap, append, has_subbs, first_subsample_is_first = GF_FALSE;
		u32 compress_type;
		char *base_data = NULL;
		if (node->type) continue;
		if (stricmp(node->name, ctx->is_dims ? "DIMSUnit" : "NHNTSample") ) continue;

		strcpy(szMediaTemp, "");
		strcpy(szXmlFrom, "");
		strcpy(szXmlTo, "");

		/*by default handle all samples as contiguous*/
		ctx->samp_buffer_size = 0;
		dims_flags = 0;
		append = GF_FALSE;
		compress_type = ctx->compress_type;
		sample_duration = 0;
		redundant_rap = 0;
		sap_type = ctx->has_sap ? GF_FILTER_SAP_NONE : GF_FILTER_SAP_1;

		cts_offset = 0;
		if (ctx->last_dts != GF_FILTER_NO_TS)
			dts = ctx->last_dts;
		else
			dts = 0;

		ctx->sample_num++;

		j=0;
		while ( (att = (GF_XMLAttribute *)gf_list_enum(node->attributes, &j))) {
			if (!stricmp(att->name, "DTS") || !stricmp(att->name, "time")) {
				u32 h, m, s, ms;
				u64 dst_val;
				if (strchr(att->value, ':') && sscanf(att->value, "%u:%u:%u.%u", &h, &m, &s, &ms) == 4) {
					dts = (u64) ( (Double) ( ((h*3600.0 + m*60.0 + s)*1000 + ms) / 1000.0) * ctx->timescale );
				} else if (sscanf(att->value, ""LLU, &dst_val)==1) {
					dts = dst_val;
				}
			}
			else if (!stricmp(att->name, "CTSOffset")) cts_offset = atoi(att->value);
			else if (!stricmp(att->name, "isRAP") ) {
				sap_type = (!stricmp(att->value, "yes")) ? GF_FILTER_SAP_1 : GF_FILTER_SAP_NONE;
			}
			else if (!stricmp(att->name, "isSyncShadow")) redundant_rap = !stricmp(att->value, "yes") ? 1 : 0;
			else if (!stricmp(att->name, "SAPType") ) sap_type = atoi(att->value);
			else if (!stricmp(att->name, "mediaOffset")) offset = (s64) atof(att->value) ;
			else if (!stricmp(att->name, "dataLength")) ctx->samp_buffer_size = atoi(att->value);
			else if (!stricmp(att->name, "mediaFile")) {
				if (!strncmp(att->value, "data:", 5)) {
					char *base = strstr(att->value, "base64,");
					if (!base) {
						GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[NHMLDmx] Data encoding scheme not recognized in sample %d - skipping\n", ctx->sample_num));
					} else {
						base_data = att->value;
					}
				} else if (!strnicmp(att->value, "gmem://", 7)) {
					GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[NHMLDmx] Invalid url %s for NHML import\n", att->value));
				} else {
					char *url = gf_url_concatenate(ctx->src_url, att->value);
					if (!url) {
						GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[NHMLDmx] Failed to get full url for %s\n", att->value));
					} else {
						strncpy(szMediaTemp, url, GF_MAX_PATH-1);
						szMediaTemp[GF_MAX_PATH-1] = 0;
						gf_free(url);
					}
				}
			}
			else if (!stricmp(att->name, "xmlFrom")) strcpy(szXmlFrom, att->value);
			else if (!stricmp(att->name, "xmlTo")) strcpy(szXmlTo, att->value);
			/*DIMS flags*/
			else if (!stricmp(att->name, "is-Scene") && !stricmp(att->value, "yes"))
				dims_flags |= GF_DIMS_UNIT_S;
			else if (!stricmp(att->name, "is-RAP") && !stricmp(att->value, "yes")) {
				dims_flags |= GF_DIMS_UNIT_M;
				sap_type = GF_FILTER_SAP_1;
			}
			else if (!stricmp(att->name, "is-redundant") && !stricmp(att->value, "yes"))
				dims_flags |= GF_DIMS_UNIT_I;
			else if (!stricmp(att->name, "redundant-exit") && !stricmp(att->value, "yes"))
				dims_flags |= GF_DIMS_UNIT_D;
			else if (!stricmp(att->name, "priority") && !stricmp(att->value, "high"))
				dims_flags |= GF_DIMS_UNIT_P;
			else if (!stricmp(att->name, "compress") && !stricmp(att->value, "yes"))
				dims_flags |= GF_DIMS_UNIT_C;
			else if (!stricmp(att->name, "duration") )
				sscanf(att->value, ""LLU, &sample_duration);
		}
		if (sap_type==GF_FILTER_SAP_1)
			dims_flags |= GF_DIMS_UNIT_M;

		if (ctx->is_dims)
			compress_type = (dims_flags & GF_DIMS_UNIT_C) ? 2 : 0 ;

		if (ctx->is_img) sample_duration = ctx->duration.den;

		has_subbs = GF_FALSE;
		j=0;
		while ((childnode = (GF_XMLNode *) gf_list_enum(node->content, &j))) {
			if (childnode->type) continue;
			if (!stricmp(childnode->name, "BS")) {
				has_subbs = GF_TRUE;
				break;
			}
		}

		if (strlen(szXmlFrom) && strlen(szXmlTo)) {
			char *xml_file;
			if (strlen(szMediaTemp)) xml_file = szMediaTemp;
			else xml_file = ctx->szMedia;
			ctx->samp_buffer_size = 0;
			e = nhml_sample_from_xml(ctx, xml_file, szXmlFrom, szXmlTo);
		} else if (ctx->is_dims && !strlen(szMediaTemp)) {

			char *content = gf_xml_dom_serialize(node, GF_TRUE, GF_FALSE);

			ctx->samp_buffer_size = 3 + (u32) strlen(content);
			if (ctx->samp_buffer_alloc < ctx->samp_buffer_size) {
				ctx->samp_buffer_alloc = ctx->samp_buffer_size;
				ctx->samp_buffer = gf_realloc(ctx->samp_buffer, ctx->samp_buffer_size);
			}
			nhml_get_bs(&ctx->bs_w, ctx->samp_buffer, ctx->samp_buffer_size, GF_BITSTREAM_WRITE);
			gf_bs_write_u16(ctx->bs_w, ctx->samp_buffer_size - 2);
			gf_bs_write_u8(ctx->bs_w, (u8) dims_flags);
			gf_bs_write_data(ctx->bs_w, content, (ctx->samp_buffer_size - 3));
			gf_free(content);

			/*same DIMS unit*/
			if (ctx->last_dts == dts)
				append = GF_TRUE;

		} else if (has_subbs) {
			gf_bs_reassign_buffer(ctx->bs_w, ctx->samp_buffer, ctx->samp_buffer_alloc);
			gf_xml_parse_bit_sequence_bs(node, ctx->src_url, ctx->bs_w);
			gf_bs_get_content(ctx->bs_w, &ctx->samp_buffer, &ctx->samp_buffer_size);
			if (ctx->samp_buffer_size > ctx->samp_buffer_alloc)
				ctx->samp_buffer_alloc = ctx->samp_buffer_size;

		} else if (base_data) {
			char *start = strchr(base_data, ',');
			if (start) {
				u32 len = (u32)strlen(start+1);
				if (len > ctx->samp_buffer_alloc) {
					ctx->samp_buffer_alloc = len;
					ctx->samp_buffer = gf_realloc(ctx->samp_buffer, sizeof(char)*ctx->samp_buffer_alloc);
				}
				ctx->samp_buffer_size = gf_base64_decode(start, len, ctx->samp_buffer, ctx->samp_buffer_alloc);
			}
		} else {
			Bool close = GF_FALSE;
			FILE *f = ctx->mdia;

			j = 0;
			while ((childnode = (GF_XMLNode *)gf_list_enum(node->content, &j))) {
				if (childnode->type) continue;
				if (!stricmp(childnode->name, szSubSampleName)) {
					nb_subsamples++;
				}
			}

			if (strlen(szMediaTemp)) {
				f = gf_fopen(szMediaTemp, "rb");
				if (!f) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] import failure in sample %d: file %s not found", ctx->sample_num, close ? szMediaTemp : ctx->szMedia));
					return GF_NON_COMPLIANT_BITSTREAM;
				}
				close = GF_TRUE;
				if (offset) gf_fseek(f, offset, SEEK_SET);
				//when using dedicated source files per samples, we don't allow for data reference yet
			} else {
				if (!offset) offset = ctx->media_done;
				byte_offset = offset;
			}

			if (f) {
				if (!ctx->samp_buffer_size) {
					u64 ssize = gf_fsize(f);
					assert(ssize < 0x80000000);
					ctx->samp_buffer_size = (u32) ssize;
				}
				gf_fseek(f, offset, SEEK_SET);

				if (ctx->is_dims) {
					u32 read;
					if (ctx->samp_buffer_size + 3 > ctx->samp_buffer_alloc) {
						ctx->samp_buffer_alloc = ctx->samp_buffer_size + 3;
						ctx->samp_buffer = (char*)gf_realloc(ctx->samp_buffer, sizeof(char) * ctx->samp_buffer_alloc);
					}
					nhml_get_bs(&ctx->bs_w, ctx->samp_buffer, ctx->samp_buffer_alloc, GF_BITSTREAM_WRITE);
					gf_bs_write_u16(ctx->bs_w, ctx->samp_buffer_size+1);
					gf_bs_write_u8(ctx->bs_w, (u8) dims_flags);
					read = (u32) gf_fread( ctx->samp_buffer + 3, ctx->samp_buffer_size, f);
					if (ctx->samp_buffer_size != read) {
						GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Failed to fully read sample %d: dataLength %d read %d\n", ctx->sample_num, ctx->samp_buffer_size, read));
					}
					ctx->samp_buffer_size += 3;

					/*same DIMS unit*/
					if (ctx->last_dts == dts)
						append = GF_TRUE;
				} else {
					u32 read;
					if (ctx->samp_buffer_alloc < ctx->samp_buffer_size) {
						ctx->samp_buffer_alloc = ctx->samp_buffer_size;
						ctx->samp_buffer = (char*)gf_realloc(ctx->samp_buffer, sizeof(char) * ctx->samp_buffer_alloc);
					}
					read = (u32) gf_fread(ctx->samp_buffer, ctx->samp_buffer_size, f);
					if (ctx->samp_buffer_size != read) {
						GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Failed to fully read sample %d: dataLength %d read %d\n", ctx->sample_num, ctx->samp_buffer_size, read));
					}
				}
				if (close) gf_fclose(f);
			} else if (!nb_subsamples) {
				GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] No media file associated with sample %d!\n", ctx->sample_num));
				e = GF_URL_ERROR;
			}
		}

		if (e) return e;

		//override DIMS flags
		if (ctx->is_dims) {
			if (strstr(ctx->samp_buffer + 3, "svg ")) dims_flags |= GF_DIMS_UNIT_S;
			if (dims_flags & GF_DIMS_UNIT_S) dims_flags |= GF_DIMS_UNIT_P;
			ctx->samp_buffer[2] = dims_flags;
		}

		if (compress_type) {
#ifndef GPAC_DISABLE_ZLIB
			e = compress_sample_data(ctx, compress_type, ctx->use_dict ? &ctx->dictionary : NULL, ctx->is_dims ? 3 : 0);
			if (e) return e;

			if (ctx->is_dims) {
				nhml_get_bs(&ctx->bs_w, ctx->samp_buffer, ctx->samp_buffer_size, GF_BITSTREAM_WRITE);
				gf_bs_write_u16(ctx->bs_w, ctx->samp_buffer_size-2);
			}
#else
			GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Error: your version of GPAC was compiled with no libz support. Abort."));
			return GF_NOT_SUPPORTED;
#endif
		}

		if (ctx->is_dims && (ctx->samp_buffer_size > 0xFFFF)) {
			GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] DIMS import failure: sample %d data is too long - maximum size allowed: 65532 bytes", ctx->sample_num));
			return GF_NON_COMPLIANT_BITSTREAM;
		}
		if (ctx->samp_buffer_size) {
			pck = gf_filter_pck_new_alloc(ctx->opid, ctx->samp_buffer_size, &data);
			if (!pck) return GF_OUT_OF_MEM;

			memcpy(data, ctx->samp_buffer, ctx->samp_buffer_size);
			gf_filter_pck_set_framing(pck, append ? GF_FALSE : GF_TRUE, nb_subsamples ? GF_FALSE : GF_TRUE);
			if (!append) {
				gf_filter_pck_set_sap(pck, sap_type);
				gf_filter_pck_set_dts(pck, dts);
				gf_filter_pck_set_cts(pck, dts+cts_offset);
				if (redundant_rap && !ctx->is_dims) gf_filter_pck_set_dependency_flags(pck, 0x1);

				if (sample_duration || ctx->dts_inc)
					gf_filter_pck_set_duration(pck, sample_duration ? (u32) sample_duration : ctx->dts_inc);

				if (byte_offset != GF_FILTER_NO_BO)
					gf_filter_pck_set_byte_offset(pck, byte_offset);

				if (ctx->in_seek) {
					if (dts+cts_offset >= ctx->start_range * ctx->timescale)
						ctx->in_seek = GF_FALSE;
					else
						gf_filter_pck_set_seek_flag(pck, GF_TRUE);
				}
			}
			gf_filter_pck_send(pck);
		} else {
			first_subsample_is_first = GF_TRUE;
		}

		if (nb_subsamples) {
			if (ctx->samp_buffer_alloc<14*nb_subsamples) {
				ctx->samp_buffer_alloc = 14*nb_subsamples;
				ctx->samp_buffer = gf_realloc(ctx->samp_buffer, ctx->samp_buffer_alloc);
			}
			assert(ctx->samp_buffer);
			nhml_get_bs(&ctx->bs_w, ctx->samp_buffer, ctx->samp_buffer_alloc, GF_BITSTREAM_WRITE);
		}

		j = 0;
		while (!append && nb_subsamples && (childnode = (GF_XMLNode *)gf_list_enum(node->content, &j))) {
			if (childnode->type) continue;
			if (!stricmp(childnode->name, szSubSampleName)) {
				u32 k = 0;
				while ((att = (GF_XMLAttribute *)gf_list_enum(childnode->attributes, &k))) {
					if (!stricmp(att->name, "mediaFile")) {
						u64 subsMediaFileSize = 0;
						FILE *f = NULL;
						char *sub_file_url = gf_url_concatenate(ctx->src_url, att->value);
						if (sub_file_url) {
							f = gf_fopen(sub_file_url, "rb");
							gf_free(sub_file_url);
						}

						if (!f) {
							GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[NHMLDmx] Error: mediaFile \"%s\" not found for subsample in sample %d. Abort.\n", att->value, ctx->sample_num));
							return GF_URL_ERROR;
						}
						subsMediaFileSize = gf_fsize(f);
						assert(subsMediaFileSize < 0x80000000);

						//send continuation frame
						pck = gf_filter_pck_new_alloc(ctx->opid, (u32) subsMediaFileSize, &data);
						if (!pck) {
							gf_fclose(f);
							return GF_OUT_OF_MEM;
						}
						subsMediaFileSize = (u32) gf_fread(data, (u32) subsMediaFileSize, f);
						gf_fclose(f);

						nb_subsamples--;
						if (first_subsample_is_first) {
							gf_filter_pck_set_framing(pck, GF_TRUE, nb_subsamples ? GF_FALSE : GF_TRUE);

							gf_filter_pck_set_sap(pck, sap_type);
							gf_filter_pck_set_dts(pck, dts);
							gf_filter_pck_set_cts(pck, dts+cts_offset);
							if (redundant_rap && !ctx->is_dims) gf_filter_pck_set_dependency_flags(pck, 0x1);

							if (sample_duration || ctx->dts_inc)
								gf_filter_pck_set_duration(pck, sample_duration ? (u32) sample_duration : ctx->dts_inc);

							if (ctx->in_seek) {
								if (dts+cts_offset >= ctx->start_range * ctx->timescale)
									ctx->in_seek = GF_FALSE;
								else
									gf_filter_pck_set_seek_flag(pck, GF_TRUE);
							}

							first_subsample_is_first = GF_FALSE;
						} else {
							gf_filter_pck_set_framing(pck, GF_FALSE, nb_subsamples ? GF_FALSE : GF_TRUE);
						}

						gf_bs_write_u32(ctx->bs_w, 0); //flags
						gf_bs_write_u32(ctx->bs_w, (u32) subsMediaFileSize);
						gf_bs_write_u32(ctx->bs_w, 0); //reserved
						gf_bs_write_u8(ctx->bs_w, 0); //priority
						gf_bs_write_u8(ctx->bs_w, 0); //discardable

						if (!nb_subsamples) {
							u32 subs_size = (u32) gf_bs_get_position(ctx->bs_w);
							gf_filter_pck_set_property(pck, GF_PROP_PCK_SUBS, &PROP_DATA(ctx->samp_buffer, subs_size) );
						}

						gf_filter_pck_send(pck);
					}
				}
			}
		}

		ctx->last_dts = dts;

		if (sample_duration)
			ctx->last_dts += sample_duration;
		else
			ctx->last_dts += ctx->dts_inc;
		ctx->media_done += ctx->samp_buffer_size;

		if (gf_filter_pid_would_block(ctx->opid))
			return GF_OK;
	}
	ctx->parsing_state = 2;
	return GF_OK;


static void nhml_node_end(void *sax_cbck, const char *node_name, const char *name_space)
{
	XMLBreaker *breaker = (XMLBreaker *)sax_cbck;
	char *node_id = (char *)gf_list_last(breaker->id_stack);
	gf_list_rem_last(breaker->id_stack);
	if (breaker->from_is_end && breaker->from_id && !strcmp(breaker->from_id, node_id)) {
		breaker->from_pos = gf_xml_sax_get_node_end_pos(breaker->sax);
		breaker->from_is_end = GF_FALSE;
	}
	if (breaker->to_is_end && breaker->to_id && !strcmp(breaker->to_id, node_id)) {
		breaker->to_pos = gf_xml_sax_get_node_end_pos(breaker->sax);
		breaker->to_is_end = GF_FALSE;
	}
	gf_free(node_id);
	if (!breaker->to_is_start && !breaker->from_is_start && !breaker->to_is_end && !breaker->from_is_end) {
		gf_xml_sax_suspend(breaker->sax, GF_TRUE);
	}
}


static void
S_setup_eval_state(pTHX_ regmatch_info *const reginfo)
{
    MAGIC *mg;
    regexp *const rex = ReANY(reginfo->prog);
    regmatch_info_aux_eval *eval_state = reginfo->info_aux_eval;

    eval_state->rex = rex;

    if (reginfo->sv) {
        /* Make $_ available to executed code. */
        if (reginfo->sv != DEFSV) {
            SAVE_DEFSV;
            DEFSV_set(reginfo->sv);
        }

        if (!(mg = mg_find_mglob(reginfo->sv))) {
            /* prepare for quick setting of pos */
            mg = sv_magicext_mglob(reginfo->sv);
            mg->mg_len = -1;
        }
        eval_state->pos_magic = mg;
        eval_state->pos       = mg->mg_len;
        eval_state->pos_flags = mg->mg_flags;
    }
    else
        eval_state->pos_magic = NULL;

    if (!PL_reg_curpm) {
        /* PL_reg_curpm is a fake PMOP that we can attach the current
         * regex to and point PL_curpm at, so that $1 et al are visible
         * within a /(?{})/. It's just allocated once per interpreter the
         * first time its needed */
        Newxz(PL_reg_curpm, 1, PMOP);
#ifdef USE_ITHREADS
        {
            SV* const repointer = &PL_sv_undef;
            /* this regexp is also owned by the new PL_reg_curpm, which
               will try to free it.  */
            av_push(PL_regex_padav, repointer);
            PL_reg_curpm->op_pmoffset = av_tindex(PL_regex_padav);
            PL_regex_pad = AvARRAY(PL_regex_padav);
        }
#endif
    }
    SET_reg_curpm(reginfo->prog);
    eval_state->curpm = PL_curpm;
    PL_curpm = PL_reg_curpm;
    if (RXp_MATCH_COPIED(rex)) {
        /*  Here is a serious problem: we cannot rewrite subbeg,
            since it may be needed if this match fails.  Thus
            $` inside (?{}) could fail... */
        eval_state->subbeg     = rex->subbeg;
        eval_state->sublen     = rex->sublen;
        eval_state->suboffset  = rex->suboffset;
        eval_state->subcoffset = rex->subcoffset;
#ifdef PERL_ANY_COW
        eval_state->saved_copy = rex->saved_copy;
#endif
        RXp_MATCH_COPIED_off(rex);
    }
    else
        eval_state->subbeg = NULL;
    rex->subbeg = (char *)reginfo->strbeg;
    rex->suboffset = 0;
    rex->subcoffset = 0;
    rex->sublen = reginfo->strend - reginfo->strbeg;


S_regcppush(pTHX_ const regexp *rex, I32 parenfloor, U32 maxopenparen)
{
    const int retval = PL_savestack_ix;
    const int paren_elems_to_push =
                (maxopenparen - parenfloor) * REGCP_PAREN_ELEMS;
    const UV total_elems = paren_elems_to_push + REGCP_OTHER_ELEMS;
    const UV elems_shifted = total_elems << SAVE_TIGHT_SHIFT;
    I32 p;
    GET_RE_DEBUG_FLAGS_DECL;

    PERL_ARGS_ASSERT_REGCPPUSH;

    if (paren_elems_to_push < 0)
        Perl_croak(aTHX_ "panic: paren_elems_to_push, %i < 0, maxopenparen: %i parenfloor: %i REGCP_PAREN_ELEMS: %u",
                   (int)paren_elems_to_push, (int)maxopenparen,
                   (int)parenfloor, (unsigned)REGCP_PAREN_ELEMS);

    if ((elems_shifted >> SAVE_TIGHT_SHIFT) != total_elems)
	Perl_croak(aTHX_ "panic: paren_elems_to_push offset %"UVuf
		   " out of range (%lu-%ld)",
		   total_elems,
                   (unsigned long)maxopenparen,
                   (long)parenfloor);

    SSGROW(total_elems + REGCP_FRAME_ELEMS);

    DEBUG_BUFFERS_r(
	if ((int)maxopenparen > (int)parenfloor)
	    PerlIO_printf(Perl_debug_log,
		"rex=0x%"UVxf" offs=0x%"UVxf": saving capture indices:\n",
		PTR2UV(rex),
		PTR2UV(rex->offs)
	    );
    );
    for (p = parenfloor+1; p <= (I32)maxopenparen;  p++) {
/* REGCP_PARENS_ELEMS are pushed per pairs of parentheses. */
	SSPUSHIV(rex->offs[p].end);
	SSPUSHIV(rex->offs[p].start);
	SSPUSHINT(rex->offs[p].start_tmp);
	DEBUG_BUFFERS_r(PerlIO_printf(Perl_debug_log,
	    "    \\%"UVuf": %"IVdf"(%"IVdf")..%"IVdf"\n",
	    (UV)p,
	    (IV)rex->offs[p].start,
	    (IV)rex->offs[p].start_tmp,
	    (IV)rex->offs[p].end
	));
    }
/* REGCP_OTHER_ELEMS are pushed in any case, parentheses or no. */
    SSPUSHINT(maxopenparen);
    SSPUSHINT(rex->lastparen);
    SSPUSHINT(rex->lastcloseparen);
    SSPUSHUV(SAVEt_REGCONTEXT | elems_shifted); /* Magic cookie. */

    return retval;
}


CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn)
{
  const char *ptr;
  if(!data->state.this_is_a_follow) {
    /* Free to avoid leaking memory on multiple requests*/
    free(data->state.first_host);

    data->state.first_host = strdup(conn->host.name);
    if(!data->state.first_host)
      return CURLE_OUT_OF_MEMORY;

    data->state.first_remote_port = conn->remote_port;
    data->state.first_remote_protocol = conn->handler->protocol;
  }
  Curl_safefree(data->state.aptr.host);

  ptr = Curl_checkheaders(data, STRCONST("Host"));
  if(ptr && (!data->state.this_is_a_follow ||
             strcasecompare(data->state.first_host, conn->host.name))) {
#if !defined(CURL_DISABLE_COOKIES)
    /* If we have a given custom Host: header, we extract the host name in
       order to possibly use it for cookie reasons later on. We only allow the
       custom Host: header if this is NOT a redirect, as setting Host: in the
       redirected request is being out on thin ice. Except if the host name
       is the same as the first one! */
    char *cookiehost = Curl_copy_header_value(ptr);
    if(!cookiehost)
      return CURLE_OUT_OF_MEMORY;
    if(!*cookiehost)
      /* ignore empty data */
      free(cookiehost);
    else {
      /* If the host begins with '[', we start searching for the port after
         the bracket has been closed */
      if(*cookiehost == '[') {
        char *closingbracket;
        /* since the 'cookiehost' is an allocated memory area that will be
           freed later we cannot simply increment the pointer */
        memmove(cookiehost, cookiehost + 1, strlen(cookiehost) - 1);
        closingbracket = strchr(cookiehost, ']');
        if(closingbracket)
          *closingbracket = 0;
      }
      else {
        int startsearch = 0;
        char *colon = strchr(cookiehost + startsearch, ':');
        if(colon)
          *colon = 0; /* The host must not include an embedded port number */
      }
      Curl_safefree(data->state.aptr.cookiehost);
      data->state.aptr.cookiehost = cookiehost;
    }
#endif

    if(strcmp("Host:", ptr)) {
      data->state.aptr.host = aprintf("Host:%s\r\n", &ptr[5]);
      if(!data->state.aptr.host)
        return CURLE_OUT_OF_MEMORY;
    }
    else
      /* when clearing the header */
      data->state.aptr.host = NULL;
  }
  else {
    /* When building Host: headers, we must put the host name within
       [brackets] if the host name is a plain IPv6-address. RFC2732-style. */
    const char *host = conn->host.name;

    if(((conn->given->protocol&CURLPROTO_HTTPS) &&
        (conn->remote_port == PORT_HTTPS)) ||
       ((conn->given->protocol&CURLPROTO_HTTP) &&
        (conn->remote_port == PORT_HTTP)) )
      /* if(HTTPS on port 443) OR (HTTP on port 80) then don't include
         the port number in the host string */
      data->state.aptr.host = aprintf("Host: %s%s%s\r\n",
                                    conn->bits.ipv6_ip?"[":"",
                                    host,
                                    conn->bits.ipv6_ip?"]":"");
    else
      data->state.aptr.host = aprintf("Host: %s%s%s:%d\r\n",
                                    conn->bits.ipv6_ip?"[":"",
                                    host,
                                    conn->bits.ipv6_ip?"]":"",
                                    conn->remote_port);

    if(!data->state.aptr.host)
      /* without Host: we can't make a nice request */
      return CURLE_OUT_OF_MEMORY;
  }
  return CURLE_OK;
}


static char *get_netscape_format(const struct Cookie *co)
{
  return aprintf(
    "%s"     /* httponly preamble */
    "%s%s\t" /* domain */
    "%s\t"   /* tailmatch */
    "%s\t"   /* path */
    "%s\t"   /* secure */
    "%" CURL_FORMAT_CURL_OFF_T "\t"   /* expires */
    "%s\t"   /* name */
    "%s",    /* value */
    co->httponly?"#HttpOnly_":"",
    /*
     * Make sure all domains are prefixed with a dot if they allow
     * tailmatching. This is Mozilla-style.
     */
    (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
    co->domain?co->domain:"unknown",
    co->tailmatch?"TRUE":"FALSE",
    co->path?co->path:"/",
    co->secure?"TRUE":"FALSE",
    co->expires,
    co->name,
    co->value?co->value:"");
}


struct curl_slist *Curl_cookie_list(struct Curl_easy *data)
{
  struct curl_slist *list;
  Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
  list = cookie_list(data);
  Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
  return list;
}


CURLcode Curl_http_range(struct Curl_easy *data,
                         Curl_HttpReq httpreq)
{
  if(data->state.use_range) {
    /*
     * A range is selected. We use different headers whether we're downloading
     * or uploading and we always let customized headers override our internal
     * ones if any such are specified.
     */
    if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
       !Curl_checkheaders(data, STRCONST("Range"))) {
      /* if a line like this was already allocated, free the previous one */
      free(data->state.aptr.rangeline);
      data->state.aptr.rangeline = aprintf("Range: bytes=%s\r\n",
                                           data->state.range);
    }
    else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) &&
            !Curl_checkheaders(data, STRCONST("Content-Range"))) {

      /* if a line like this was already allocated, free the previous one */
      free(data->state.aptr.rangeline);

      if(data->set.set_resume_from < 0) {
        /* Upload resume was asked for, but we don't know the size of the
           remote part so we tell the server (and act accordingly) that we
           upload the whole file (again) */
        data->state.aptr.rangeline =
          aprintf("Content-Range: bytes 0-%" CURL_FORMAT_CURL_OFF_T
                  "/%" CURL_FORMAT_CURL_OFF_T "\r\n",
                  data->state.infilesize - 1, data->state.infilesize);

      }
      else if(data->state.resume_from) {
        /* This is because "resume" was selected */
        curl_off_t total_expected_size =
          data->state.resume_from + data->state.infilesize;
        data->state.aptr.rangeline =
          aprintf("Content-Range: bytes %s%" CURL_FORMAT_CURL_OFF_T
                  "/%" CURL_FORMAT_CURL_OFF_T "\r\n",
                  data->state.range, total_expected_size-1,
                  total_expected_size);
      }
      else {
        /* Range was selected and then we just pass the incoming range and
           append total size */
        data->state.aptr.rangeline =
          aprintf("Content-Range: bytes %s/%" CURL_FORMAT_CURL_OFF_T "\r\n",
                  data->state.range, data->state.infilesize);
      }
      if(!data->state.aptr.rangeline)
        return CURLE_OUT_OF_MEMORY;
    }
  }
  return CURLE_OK;
}


bool ElectronBrowserClient::HandleExternalProtocol(
    const GURL& url,
    content::WebContents::Getter web_contents_getter,
    int child_id,
    int frame_tree_node_id,
    content::NavigationUIData* navigation_data,
    bool is_main_frame,
    network::mojom::WebSandboxFlags sandbox_flags,
    ui::PageTransition page_transition,
    bool has_user_gesture,
    const absl::optional<url::Origin>& initiating_origin,
    mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory) {
  base::PostTask(
      FROM_HERE, {BrowserThread::UI},
      base::BindOnce(&HandleExternalProtocolInUI, url,
                     std::move(web_contents_getter), has_user_gesture));
  return true;
}


void WebContents::ReadyToCommitNavigation(
    content::NavigationHandle* navigation_handle) {
  // Don't focus content in an inactive window.
  if (!owner_window())
    return;
#if defined(OS_MAC)
  if (!owner_window()->IsActive())
    return;
#else
  if (!owner_window()->widget()->IsActive())
    return;
#endif
  // Don't focus content after subframe navigations.
  if (!navigation_handle->IsInMainFrame())
    return;
  // Only focus for top-level contents.
  if (type_ != Type::kBrowserWindow)
    return;
  web_contents()->SetInitialFocus();
}


void BindElectronWebContentsUtility(
    mojo::PendingAssociatedReceiver<electron::mojom::ElectronWebContentsUtility>
        receiver,
    content::RenderFrameHost* frame_host) {
  ElectronWebContentsUtilityHandlerImpl::Create(frame_host,
                                                std::move(receiver));
}


void ElectronBrowserClient::RenderProcessWillLaunch(
    content::RenderProcessHost* host) {
  // When a render process is crashed, it might be reused.
  int process_id = host->GetID();

  auto* browser_context = host->GetBrowserContext();
  ALLOW_UNUSED_LOCAL(browser_context);

#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
  host->AddFilter(
      new extensions::ExtensionMessageFilter(process_id, browser_context));
  host->AddFilter(new extensions::ExtensionsGuestViewMessageFilter(
      process_id, browser_context));
  host->AddFilter(
      new ElectronExtensionMessageFilter(process_id, browser_context));
#endif

  // ensure the ProcessPreferences is removed later
  host->AddObserver(this);
}


void CBINDInstallDlg::OnBrowse() {

	CDirBrowse browse;

	if (browse.DoModal() == IDOK) 	{
		//m_targetDir = browse.m_selectedDir;
		UpdateData(FALSE);
	}
}


int CBINDInstallDlg::MsgBox(int id, UINT type, ...) {
	CString format;
	va_list va;
	char buf[BUFSIZ];

	format.LoadString(id);
	memset(buf, 0, BUFSIZ);

	va_start(va, type);
	vsprintf(buf, format, va);
	va_end(va);

	return(MessageBox(buf, NULL, type));
}


void CBINDInstallDlg::SetItemStatus(UINT nID, BOOL bSuccess) {
	GetDlgItem(nID)->SetWindowText(bSuccess == TRUE ? "Done" : "Failed");
}


static const std::string urldecode(const std::string &str) {
  using namespace std;
  string result;
  string::size_type i;
  for (i = 0; i < str.size(); ++i) {
    if (str[i] == '+') {
      result += ' ';
    } else if (str[i] == '%' && str.size() > i + 2) {
      const unsigned char ch1 =
          from_hex(static_cast<unsigned char>(str[i + 1]));
      const unsigned char ch2 =
          from_hex(static_cast<unsigned char>(str[i + 2]));
      const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
      result += static_cast<char>(ch);
      i += 2;
    } else {
      result += str[i];
    }
  }
  return result;
}


static bool ParseAnimationChannel(
    AnimationChannel *channel, std::string *err, const json &o,
    bool store_original_json_for_extras_and_extensions) {
  int samplerIndex = -1;
  int targetIndex = -1;
  if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
                            "AnimationChannel")) {
    if (err) {
      (*err) += "`sampler` field is missing in animation channels\n";
    }
    return false;
  }

  json_const_iterator targetIt;
  if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) {
    const json &target_object = GetValue(targetIt);

    if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
      if (err) {
        (*err) += "`node` field is missing in animation.channels.target\n";
      }
      return false;
    }

    if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
                             true)) {
      if (err) {
        (*err) += "`path` field is missing in animation.channels.target\n";
      }
      return false;
    }
    ParseExtensionsProperty(&channel->target_extensions, err, target_object);
    if (store_original_json_for_extras_and_extensions) {
      json_const_iterator it;
      if (FindMember(target_object, "extensions", it)) {
        channel->target_extensions_json_string = JsonToString(GetValue(it));
      }
    }
  }

  channel->sampler = samplerIndex;
  channel->target_node = targetIndex;

  ParseExtensionsProperty(&channel->extensions, err, o);
  ParseExtrasProperty(&(channel->extras), o);

  if (store_original_json_for_extras_and_extensions) {
    {
      json_const_iterator it;
      if (FindMember(o, "extensions", it)) {
        channel->extensions_json_string = JsonToString(GetValue(it));
      }
    }
    {
      json_const_iterator it;
      if (FindMember(o, "extras", it)) {
        channel->extras_json_string = JsonToString(GetValue(it));
      }
    }
  }

  return true;
}


bool IsArray(const json &o) {
#ifdef TINYGLTF_USE_RAPIDJSON
  return o.IsArray();
#else
  return o.is_array();
#endif
}


static bool WriteGltfFile(const std::string &output,
                          const std::string &content) {
#ifdef _WIN32
#if defined(_MSC_VER)
  std::ofstream gltfFile(UTF8ToWchar(output).c_str());
#elif defined(__GLIBCXX__)
  int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
                               _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
  __gnu_cxx::stdio_filebuf<char> wfile_buf(
      file_descriptor, std::ios_base::out | std::ios_base::binary);
  std::ostream gltfFile(&wfile_buf);
  if (!wfile_buf.is_open()) return false;
#else
  std::ofstream gltfFile(output.c_str());
  if (!gltfFile.is_open()) return false;
#endif
#else
  std::ofstream gltfFile(output.c_str());
  if (!gltfFile.is_open()) return false;
#endif
  return WriteGltfStream(gltfFile, content);
}


static void SerializeGltfSampler(Sampler &sampler, json &o) {
  if (sampler.magFilter != -1) {
    SerializeNumberProperty("magFilter", sampler.magFilter, o);
  }
  if (sampler.minFilter != -1) {
    SerializeNumberProperty("minFilter", sampler.minFilter, o);
  }
  // SerializeNumberProperty("wrapR", sampler.wrapR, o);
  SerializeNumberProperty("wrapS", sampler.wrapS, o);
  SerializeNumberProperty("wrapT", sampler.wrapT, o);

  if (sampler.extras.Type() != NULL_TYPE) {
    SerializeValue("extras", sampler.extras, o);
  }
}


static bool ParseParameterProperty(Parameter *param, std::string *err,
                                   const json &o, const std::string &prop,
                                   bool required) {
  // A parameter value can either be a string or an array of either a boolean or
  // a number. Booleans of any kind aren't supported here. Granted, it
  // complicates the Parameter structure and breaks it semantically in the sense
  // that the client probably works off the assumption that if the string is
  // empty the vector is used, etc. Would a tagged union work?
  if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
    // Found string property.
    return true;
  } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
                                      false)) {
    // Found a number array.
    return true;
  } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
    return param->has_number_value = true;
  } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
                               false)) {
    return true;
  } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
    return true;
  } else {
    if (required) {
      if (err) {
        (*err) += "parameter must be a string or number / number array.\n";
      }
    }
    return false;
  }
}


rapidjson::Document::AllocatorType &GetAllocator() {
  assert(s_pActiveDocument);  // Root json node must be JsonDocument type
  return s_pActiveDocument->GetAllocator();
}


std::string ExpandFilePath(const std::string &filepath, void *) {
  // https://github.com/syoyo/tinygltf/issues/368
  //
  // No file path expansion in built-in FS function anymore, since glTF URI
  // should not contain tilde('~') and environment variables, and for security
  // reason(`wordexp`).
  //
  // Users need to supply `base_dir`(in `LoadASCIIFromString`,
  // `LoadBinaryFromMemory`) in expanded absolute path.

  return filepath;

#if 0
#ifdef _WIN32
  // Assume input `filepath` is encoded in UTF-8
  std::wstring wfilepath = UTF8ToWchar(filepath);
  DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
  wchar_t *wstr = new wchar_t[wlen];
  ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);

  std::wstring ws(wstr);
  delete[] wstr;
  return WcharToUTF8(ws);

#else

#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
    defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
  // no expansion
  std::string s = filepath;
#else
  std::string s;
  wordexp_t p;

  if (filepath.empty()) {
    return "";
  }

  // Quote the string to keep any spaces in filepath intact.
  std::string quoted_path = "\"" + filepath + "\"";
  // char** w;
  int ret = wordexp(quoted_path.c_str(), &p, 0);
  if (ret) {
    // err
    s = filepath;
    return s;
  }

  // Use first element only.
  if (p.we_wordv) {
    s = std::string(p.we_wordv[0]);
    wordfree(&p);
  } else {
    s = filepath;
  }

#endif

  return s;
#endif
#endif
}


  int TextureIndex() const {
    const auto it = json_double_value.find("index");
    if (it != std::end(json_double_value)) {
      return int(it->second);
    }
    return -1;
  }


static void SerializeGltfMesh(Mesh &mesh, json &o) {
  json primitives;
  JsonReserveArray(primitives, mesh.primitives.size());
  for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
    json primitive;
    const Primitive &gltfPrimitive = mesh.primitives[i];  // don't make a copy
    {
      json attributes;
      for (auto attrIt = gltfPrimitive.attributes.begin();
           attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
        SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
      }

      JsonAddMember(primitive, "attributes", std::move(attributes));
    }

    // Indicies is optional
    if (gltfPrimitive.indices > -1) {
      SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
    }
    // Material is optional
    if (gltfPrimitive.material > -1) {
      SerializeNumberProperty<int>("material", gltfPrimitive.material,
                                   primitive);
    }
    SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);

    // Morph targets
    if (gltfPrimitive.targets.size()) {
      json targets;
      JsonReserveArray(targets, gltfPrimitive.targets.size());
      for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
        json targetAttributes;
        std::map<std::string, int> targetData = gltfPrimitive.targets[k];
        for (std::map<std::string, int>::iterator attrIt = targetData.begin();
             attrIt != targetData.end(); ++attrIt) {
          SerializeNumberProperty<int>(attrIt->first, attrIt->second,
                                       targetAttributes);
        }
        JsonPushBack(targets, std::move(targetAttributes));
      }
      JsonAddMember(primitive, "targets", std::move(targets));
    }

    SerializeExtensionMap(gltfPrimitive.extensions, primitive);

    if (gltfPrimitive.extras.Type() != NULL_TYPE) {
      SerializeValue("extras", gltfPrimitive.extras, primitive);
    }

    JsonPushBack(primitives, std::move(primitive));
  }

  JsonAddMember(o, "primitives", std::move(primitives));

  if (mesh.weights.size()) {
    SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
  }

  if (mesh.name.size()) {
    SerializeStringProperty("name", mesh.name, o);
  }

  SerializeExtensionMap(mesh.extensions, o);
  if (mesh.extras.Type() != NULL_TYPE) {
    SerializeValue("extras", mesh.extras, o);
  }
}


static std::string MimeToExt(const std::string &mimeType) {
  if (mimeType == "image/jpeg") {
    return "jpg";
  } else if (mimeType == "image/png") {
    return "png";
  } else if (mimeType == "image/bmp") {
    return "bmp";
  } else if (mimeType == "image/gif") {
    return "gif";
  }

  return "";
}


  void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
    store_original_json_for_extras_and_extensions_ = enabled;
  }