static int store_resource(struct transaction_t *txn, icalcomponent *ical,
			  struct mailbox *mailbox, const char *resource,
			  struct caldav_db *caldavdb, int overwrite,
			  unsigned flags)
{
    int ret = HTTP_CREATED, r;
    icalcomponent *comp;
    icalcomponent_kind kind;
    icalproperty_method meth;
    icalproperty *prop;
    unsigned mykind = 0;
    char *header;
    const char *organizer = NULL;
    const char *prop_annot =
	ANNOT_NS "<" XML_NS_CALDAV ">supported-calendar-component-set";
    struct buf attrib = BUF_INITIALIZER;
    struct buf headbuf = BUF_INITIALIZER;
    struct index_record oldrecord;
    struct caldav_data *cdata;
    quota_t qdiffs[QUOTA_NUMRESOURCES] = QUOTA_DIFFS_DONTCARE_INITIALIZER;
    FILE *f = NULL;
    struct stagemsg *stage;
    const char *uid, *ics;
    uint32_t expunge_uid = 0;
    time_t now = time(NULL);
    char datestr[80];
    struct appendstate as;
    static char sched_tag[64];
    static unsigned store_count = 0;

    /* Check for supported component type */
    comp = icalcomponent_get_first_real_component(ical);
    kind = icalcomponent_isa(comp);
    switch (kind) {
    case ICAL_VEVENT_COMPONENT: mykind = CAL_COMP_VEVENT; break;
    case ICAL_VTODO_COMPONENT: mykind = CAL_COMP_VTODO; break;
    case ICAL_VJOURNAL_COMPONENT: mykind = CAL_COMP_VJOURNAL; break;
    case ICAL_VFREEBUSY_COMPONENT: mykind = CAL_COMP_VFREEBUSY; break;
    case ICAL_VAVAILABILITY_COMPONENT: mykind = CAL_COMP_VAVAILABILITY; break;
#ifdef HAVE_VPOLL
    case ICAL_VPOLL_COMPONENT: mykind = CAL_COMP_VPOLL; break;
#endif
    default:
	txn->error.precond = CALDAV_SUPP_COMP;
	return HTTP_FORBIDDEN;
    }

    if (!annotatemore_lookupmask(mailbox->name, prop_annot, httpd_userid, &attrib)
	&& attrib.len) {
	unsigned long supp_comp = strtoul(buf_cstring(&attrib), NULL, 10);

	buf_free(&attrib);

	if (!(mykind & supp_comp)) {
	    txn->error.precond = CALDAV_SUPP_COMP;
	    return HTTP_FORBIDDEN;
	}
    }

    /* Find iCalendar UID for the current resource, if exists */
    uid = icalcomponent_get_uid(comp);
    caldav_lookup_resource(caldavdb,
			   mailbox->name, resource, 0, &cdata);
    if (cdata->ical_uid && strcmp(cdata->ical_uid, uid)) {
	/* CALDAV:no-uid-conflict */
	txn->error.precond = CALDAV_UID_CONFLICT;
	return HTTP_FORBIDDEN;
    }

    /* XXX - theoretical race, but the mailbox is locked, so nothing
     * else can ACTUALLY change it */
    if (cdata->dav.imap_uid) {
	/* Fetch index record for the resource */
	r = mailbox_find_index_record(mailbox, cdata->dav.imap_uid, &oldrecord);
	if (r) return HTTP_SERVER_ERROR;

	if (overwrite == OVERWRITE_CHECK) {
	    /* Check any preconditions */
	    const char *etag = message_guid_encode(&oldrecord.guid);
	    time_t lastmod = oldrecord.internaldate;
	    int precond = caldav_check_precond(txn, cdata,
					       etag, lastmod);

	    if (precond != HTTP_OK)
		return HTTP_PRECOND_FAILED;
	}

	expunge_uid = cdata->dav.imap_uid;
    }

    /* Check for existing iCalendar UID */
    caldav_lookup_uid(caldavdb, uid, 0, &cdata);
    if (!(flags & NO_DUP_CHECK) &&
	cdata->dav.mailbox && !strcmp(cdata->dav.mailbox, mailbox->name) &&
	strcmp(cdata->dav.resource, resource)) {
	/* CALDAV:no-uid-conflict */
	const char *owner = mboxname_to_userid(cdata->dav.mailbox);

	txn->error.precond = CALDAV_UID_CONFLICT;
	buf_reset(&txn->buf);
	buf_printf(&txn->buf, "%s/user/%s/%s/%s",
		   namespace_calendar.prefix, owner,
		   strrchr(cdata->dav.mailbox, '.')+1, cdata->dav.resource);
	txn->error.resource = buf_cstring(&txn->buf);
	return HTTP_FORBIDDEN;
    }

    /* Prepare to stage the message */
    if (!(f = append_newstage(mailbox->name, now, 0, &stage))) {
	syslog(LOG_ERR, "append_newstage(%s) failed", mailbox->name);
	txn->error.desc = "append_newstage() failed\r\n";
	return HTTP_SERVER_ERROR;
    }

    /* Remove all X-LIC-ERROR properties*/
    icalcomponent_strip_errors(ical);

    ics = icalcomponent_as_ical_string(ical);

    /* Create iMIP header for resource */
    prop = icalcomponent_get_first_property(comp, ICAL_ORGANIZER_PROPERTY);
    if (prop) {
	organizer = icalproperty_get_organizer(prop)+7;
	header = charset_encode_mimeheader(organizer, 0);
	fprintf(f, "From: %s\r\n", header);
	free(header);
    }
    else if (strchr(proxy_userid, '@')) {
	/* XXX  This needs to be done via an LDAP/DB lookup */
	header = charset_encode_mimeheader(proxy_userid, 0);
	fprintf(f, "From: %s\r\n", header);
	free(header);
    }
    else {
	struct buf *headbuf = &txn->buf;

	assert(!headbuf->len);
	buf_printf(headbuf, "%s@%s", proxy_userid, config_servername);
	header = charset_encode_mimeheader(headbuf->s, headbuf->len);
	buf_reset(headbuf);

	fprintf(f, "From: %s\r\n", header);
	free(header);
    }

    prop = icalcomponent_get_first_property(comp, ICAL_SUMMARY_PROPERTY);
    if (prop) {
	header = charset_encode_mimeheader(icalproperty_get_summary(prop), 0);
	fprintf(f, "Subject: %s\r\n", header);
	free(header);
    }
    else fprintf(f, "Subject: %s\r\n", icalcomponent_kind_to_string(kind));

    time_to_rfc822(icaltime_as_timet_with_zone(icalcomponent_get_dtstamp(comp),
					       icaltimezone_get_utc_timezone()),
		   datestr, sizeof(datestr));
    fprintf(f, "Date: %s\r\n", datestr);

    /* XXX - validate uid for mime safety? */
    if (strchr(uid, '@')) {
	fprintf(f, "Message-ID: <%s>\r\n", uid);
    }
    else {
	fprintf(f, "Message-ID: <%s@%s>\r\n", uid, config_servername);
    }

    fprintf(f, "Content-Type: text/calendar; charset=utf-8");
    if ((meth = icalcomponent_get_method(ical)) != ICAL_METHOD_NONE) {
	fprintf(f, "; method=%s", icalproperty_method_to_string(meth));
    }
    fprintf(f, "; component=%s\r\n", icalcomponent_kind_to_string(kind));

    fprintf(f, "Content-Length: %u\r\n", (unsigned) strlen(ics));
    fprintf(f, "Content-Disposition: inline; filename=\"%s\"", resource);
    if (organizer) {
	const char *stag;
	if (flags & NEW_STAG) {
	    sprintf(sched_tag, "%d-%ld-%u", getpid(), now, store_count++);
	    stag = sched_tag;
	}
	else stag = cdata->sched_tag;
	if (stag) fprintf(f, ";\r\n\tschedule-tag=%s", stag);
    }
    fprintf(f, "\r\n");

    /* XXX  Check domain of data and use appropriate CTE */

    fprintf(f, "MIME-Version: 1.0\r\n");
    fprintf(f, "\r\n");

    /* Write the iCal data to the file */
    fprintf(f, "%s", ics);
    qdiffs[QUOTA_STORAGE] = ftell(f);

    fclose(f);

    qdiffs[QUOTA_MESSAGE] = 1;

    /* Prepare to append the iMIP message to calendar mailbox */
    if ((r = append_setup_mbox(&as, mailbox, NULL, NULL, 0, qdiffs, 0, 0, EVENT_MESSAGE_NEW|EVENT_CALENDAR))) {
	syslog(LOG_ERR, "append_setup(%s) failed: %s",
	       mailbox->name, error_message(r));
	ret = HTTP_SERVER_ERROR;
	txn->error.desc = "append_setup() failed\r\n";
    }
    else {
	struct body *body = NULL;

	/* Append the iMIP file to the calendar mailbox */
	if ((r = append_fromstage(&as, &body, stage, now, NULL, 0, 0))) {
	    syslog(LOG_ERR, "append_fromstage() failed");
	    ret = HTTP_SERVER_ERROR;
	    txn->error.desc = "append_fromstage() failed\r\n";
	}
	if (body) {
	    message_free_body(body);
	    free(body);
	}

	if (r) append_abort(&as);
	else {
	    /* Commit the append to the calendar mailbox */
	    if ((r = append_commit(&as))) {
		syslog(LOG_ERR, "append_commit() failed");
		ret = HTTP_SERVER_ERROR;
		txn->error.desc = "append_commit() failed\r\n";
	    }
	    else {
		/* append_commit() returns a write-locked index */
		struct index_record newrecord;

		/* Read index record for new message (always the last one) */
		mailbox_read_index_record(mailbox, mailbox->i.num_records,
					  &newrecord);

		if (expunge_uid) {
		    /* Now that we have the replacement message in place
		       and the mailbox locked, re-read the old record
		       and see if we should overwrite it.  Either way,
		       one of our records will have to be expunged.
		    */
		    int userflag;

		    ret = HTTP_NO_CONTENT;

		    /* Perform the actual expunge */
		    r = mailbox_user_flag(mailbox, DFLAG_UNBIND, &userflag, 1);
		    if (!r) {
			oldrecord.user_flags[userflag/32] |= 1<<(userflag&31);
			oldrecord.system_flags |= FLAG_EXPUNGED;
			r = mailbox_rewrite_index_record(mailbox, &oldrecord);
		    }
		    if (r) {
			syslog(LOG_ERR, "expunging record (%s) failed: %s",
			       mailbox->name, error_message(r));
			txn->error.desc = error_message(r);
			ret = HTTP_SERVER_ERROR;
		    }
		}

		if (!r) {
		    struct resp_body_t *resp_body = &txn->resp_body;

		    if (cdata->organizer && (flags & NEW_STAG)) {
			resp_body->stag = sched_tag;
		    }

		    if ((flags & PREFER_REP) || !(flags & NEW_STAG)) {
			/* Tell client about the new resource */
			resp_body->lastmod = newrecord.internaldate;
			resp_body->etag = message_guid_encode(&newrecord.guid);
		    }
		}
	    }
	}
    }

    append_removestage(stage);

    buf_free(&headbuf);

    return ret;
}


static int propfind_calcompset(const xmlChar *name, xmlNsPtr ns,
			       struct propfind_ctx *fctx,
			       xmlNodePtr resp __attribute__((unused)),
			       struct propstat propstat[],
			       void *rock __attribute__((unused)))
{
    struct buf attrib = BUF_INITIALIZER;
    const char *prop_annot =
	ANNOT_NS "<" XML_NS_CALDAV ">supported-calendar-component-set";
    unsigned long types = 0;
    xmlNodePtr set, node;
    const struct cal_comp_t *comp;
    int r = 0;

    if (!fctx->req_tgt->collection) return HTTP_NOT_FOUND;

    r = annotatemore_lookupmask(fctx->mailbox->name, prop_annot,
				httpd_userid, &attrib);
    if (r) return HTTP_SERVER_ERROR;

    if (attrib.len) {
	types = strtoul(buf_cstring(&attrib), NULL, 10);
    }
    else {
	types = -1;  /* ALL components types */

	/* Apple clients don't like VPOLL */
	types &= ~CAL_COMP_VPOLL;
    }

    buf_free(&attrib);

    if (!types) return HTTP_NOT_FOUND;

    set = xml_add_prop(HTTP_OK, fctx->ns[NS_DAV], &propstat[PROPSTAT_OK],
		       name, ns, NULL, 0);
    /* Create "comp" elements from the stored bitmask */
    for (comp = cal_comps; comp->name; comp++) {
	if (types & comp->type) {
	    node = xmlNewChild(set, fctx->ns[NS_CALDAV],
			       BAD_CAST "comp", NULL);
	    xmlNewProp(node, BAD_CAST "name", BAD_CAST comp->name);
	}
    }

    return 0;
}


static int busytime_by_collection(char *mboxname, int matchlen,
				  int maycreate, void *rock)
{
    struct propfind_ctx *fctx = (struct propfind_ctx *) rock;
    struct calquery_filter *calfilter =
	(struct calquery_filter *) fctx->filter_crit;

    if (calfilter && (calfilter->flags & CHECK_CAL_TRANSP)) {
	/* Check if the collection is marked as transparent */
	struct buf attrib = BUF_INITIALIZER;
	const char *prop_annot =
	    ANNOT_NS "<" XML_NS_CALDAV ">schedule-calendar-transp";

	if (!annotatemore_lookupmask(mboxname, prop_annot, httpd_userid, &attrib)) {
	    if (!strcmp(buf_cstring(&attrib), "transparent")) {
		buf_free(&attrib);
		return 0;
	    }
	    buf_free(&attrib);
	}
    }

    return propfind_by_collection(mboxname, matchlen, maycreate, rock);
}


static int propfind_suppcaldata(const xmlChar *name, xmlNsPtr ns,
				struct propfind_ctx *fctx,
				xmlNodePtr resp __attribute__((unused)),
				struct propstat propstat[],
				void *rock __attribute__((unused)))
{
    xmlNodePtr node;
    struct mime_type_t *mime;

    if (!fctx->req_tgt->collection) return HTTP_NOT_FOUND;

    node = xml_add_prop(HTTP_OK, fctx->ns[NS_DAV], &propstat[PROPSTAT_OK],
			name, ns, NULL, 0);

    for (mime = caldav_mime_types; mime->content_type; mime++) {
	xmlNodePtr type = xmlNewChild(node, fctx->ns[NS_CALDAV],
				      BAD_CAST "calendar-data", NULL);

	/* Trim any charset from content-type */
	buf_reset(&fctx->buf);
	buf_printf(&fctx->buf, "%.*s",
		   (int) strcspn(mime->content_type, ";"), mime->content_type);

	xmlNewProp(type, BAD_CAST "content-type",
		   BAD_CAST buf_cstring(&fctx->buf));

	if (mime->version)
	    xmlNewProp(type, BAD_CAST "version", BAD_CAST mime->version);
    }

    return 0;
}


static int compare_vavail(const void *v1, const void *v2)
{
    struct vavailability *a = (struct vavailability *) v1;
    struct vavailability *b = (struct vavailability *) v2;

    int r = a->priority - b->priority;

    if (r == 0) {
	r = icaltime_compare(a->per.start, b->per.start);

	if (r == 0) r = icaltime_compare(a->per.end, b->per.end);
    }

    return r;
}


static void sched_vpoll_reply(icalcomponent *poll, const char *voter)
{
    icalcomponent *item, *next;
    icalproperty *prop;

    for (item = icalcomponent_get_first_component(poll, ICAL_ANY_COMPONENT);

	 item;
	 item = next) {

	next = icalcomponent_get_next_component(poll, ICAL_ANY_COMPONENT);

	prop = icalcomponent_get_first_property(item, ICAL_POLLITEMID_PROPERTY);
	if (prop) {
	    int id = icalproperty_get_pollitemid(prop);

	    for (prop = icalcomponent_get_first_property(item,
							 ICAL_VOTER_PROPERTY);
		 prop;
		 prop =
		     icalcomponent_get_next_property(item,
						     ICAL_VOTER_PROPERTY)) {
		if (!strcmp(voter, icalproperty_get_voter(prop))) {
		    icalproperty *itemid = icalproperty_new_pollitemid(id);
		    icalparameter *param;

		    for (param =
			     icalproperty_get_first_parameter(prop,
							      ICAL_ANY_PARAMETER);
			 param;
			 param =
			     icalproperty_get_next_parameter(prop,
							     ICAL_ANY_PARAMETER)) {
			switch (icalparameter_isa(param)) {
			case ICAL_PUBLICCOMMENT_PARAMETER:
			case ICAL_RESPONSE_PARAMETER:
			    icalproperty_add_parameter(itemid,
						       icalparameter_new_clone(param));
			    break;

			default:
			    break;
			}
		    }

		    icalcomponent_add_property(poll, itemid);
		}
	    }
	}

	icalcomponent_remove_component(poll, item);
	icalcomponent_free(item);
    }
}


static void sched_pollstatus(const char *organizer __attribute__((unused)),
			     struct caldav_sched_param *sparam __attribute__((unused)),
			     icalcomponent *ical __attribute__((unused)),
			     const char *voter __attribute__((unused)))
{
    return;
}


static void my_caldav_shutdown(void)
{
    buf_free(&ical_prodid_buf);
    freestrlist(cua_domains);

    my_caldav_reset();
    caldav_done();
}


static int caldav_parse_path(const char *path,
			     struct request_target_t *tgt, const char **errstr)
{
    char *p;
    size_t len;
    struct mboxname_parts parts;
    struct buf boxbuf = BUF_INITIALIZER;

    /* Make a working copy of target path */
    strlcpy(tgt->path, path, sizeof(tgt->path));
    tgt->tail = tgt->path + strlen(tgt->path);

    p = tgt->path;

    /* Sanity check namespace */
    len = strlen(namespace_calendar.prefix);
    if (strlen(p) < len ||
	strncmp(namespace_calendar.prefix, p, len) ||
	(path[len] && path[len] != '/')) {
	*errstr = "Namespace mismatch request target path";
	return HTTP_FORBIDDEN;
    }

    /* Default to bare-bones Allow bits for toplevel collections */
    tgt->allow &= ~(ALLOW_POST|ALLOW_WRITE|ALLOW_DELETE);

    /* Skip namespace */
    p += len;
    if (!*p || !*++p) return 0;

    /* Check if we're in user space */
    len = strcspn(p, "/");
    if (!strncmp(p, "user", len)) {
	p += len;
	if (!*p || !*++p) return 0;

	/* Get user id */
	len = strcspn(p, "/");
	tgt->user = p;
	tgt->userlen = len;

	p += len;
	if (!*p || !*++p) {
	    /* Make sure calendar-home-set is terminated with '/' */
	    if (p[-1] != '/') *p++ = '/';
	    goto done;
	}

	len = strcspn(p, "/");
    }

    /* Get collection */
    tgt->collection = p;
    tgt->collen = len;

    p += len;
    if (!*p || !*++p) {
	/* Make sure collection is terminated with '/' */
	if (p[-1] != '/') *p++ = '/';
	goto done;
    }

    /* Get resource */
    len = strcspn(p, "/");
    tgt->resource = p;
    tgt->reslen = len;

    p += len;

    if (*p) {
//	*errstr = "Too many segments in request target path";
	return HTTP_NOT_FOUND;
    }

  done:
    /* Set proper Allow bits and flags based on path components */
    if (tgt->collection) {
	if (!strncmp(tgt->collection, SCHED_INBOX, strlen(SCHED_INBOX)))
	    tgt->flags = TGT_SCHED_INBOX;
	else if (!strncmp(tgt->collection, SCHED_OUTBOX, strlen(SCHED_OUTBOX)))
	    tgt->flags = TGT_SCHED_OUTBOX;

	if (tgt->resource) {
	    if (!tgt->flags) tgt->allow |= ALLOW_WRITE;
	    tgt->allow |= ALLOW_DELETE;
	    tgt->allow &= ~ALLOW_WRITECOL;
	}
	else if (tgt->flags != TGT_SCHED_INBOX) {
	    tgt->allow |= ALLOW_POST;

	    if (strcmp(tgt->collection, SCHED_DEFAULT))
		tgt->allow |= ALLOW_DELETE;
	}
    }
    else if (tgt->user) tgt->allow |= ALLOW_DELETE;

    /* Create mailbox name from the parsed path */

    mboxname_init_parts(&parts);

    if (tgt->user && tgt->userlen) {
	/* holy "avoid copying" batman */
	char *userid = xstrndup(tgt->user, tgt->userlen);
	mboxname_userid_to_parts(userid, &parts);
	free(userid);
    }

    buf_setcstr(&boxbuf, config_getstring(IMAPOPT_CALENDARPREFIX));
    if (tgt->collen) {
	buf_putc(&boxbuf, '.');
	buf_appendmap(&boxbuf, tgt->collection, tgt->collen);
    }
    parts.box = buf_release(&boxbuf); /* tricky, we now need to free parts.box separately */

    mboxname_parts_to_internal(&parts, tgt->mboxname);

    free((char *) parts.box); /* n.b. casting away constness */
    mboxname_free_parts(&parts);

    return 0;
}


static int propfind_rscaleset(const xmlChar *name, xmlNsPtr ns,
			      struct propfind_ctx *fctx,
			      xmlNodePtr resp __attribute__((unused)),
			      struct propstat propstat[],
			      void *rock __attribute__((unused)))
{
    assert(name && ns && fctx && propstat);

    if (fctx->req_tgt->resource) return HTTP_NOT_FOUND;

    if (rscale_calendars) {
	xmlNodePtr top;
	int i, n;

	top = xml_add_prop(HTTP_OK, fctx->ns[NS_DAV], &propstat[PROPSTAT_OK],
			   name, ns, NULL, 0);

	for (i = 0, n = rscale_calendars->num_elements; i < n; i++) {
	    const char **rscale = icalarray_element_at(rscale_calendars, i);

	    xmlNewChild(top, fctx->ns[NS_CALDAV],
			BAD_CAST "supported-rscale", BAD_CAST *rscale);
	}

	return 0;
    }

    return HTTP_NOT_FOUND;
}


static void busytime_query_remote(const char *server __attribute__((unused)),
				  void *data, void *rock)
{
    struct caldav_sched_param *remote = (struct caldav_sched_param *) data;
    struct remote_rock *rrock = (struct remote_rock *) rock;
    icalcomponent *comp;
    struct proplist *list;
    xmlNodePtr resp;
    const char *status = NULL;
    int r;

    comp = icalcomponent_get_first_real_component(rrock->ical);

    /* Add the attendees to the iTIP request */
    for (list = remote->props; list; list = list->next) {
	icalcomponent_add_property(comp, list->prop);
    }

    if (remote->flags == SCHEDTYPE_REMOTE) {
	/* Use iMIP */

	r = imip_send(rrock->ical);

	if (!r) status = REQSTAT_SENT;
	else status = REQSTAT_TEMPFAIL;
    }
    else {
	/* Use iSchedule */
	xmlNodePtr xml;

	r = isched_send(remote, NULL, rrock->ical, &xml);
	if (r) status = REQSTAT_TEMPFAIL;
	else if (xmlStrcmp(xml->name, BAD_CAST "schedule-response")) {
	    if (r) status = REQSTAT_TEMPFAIL;
	}
	else {
	    xmlNodePtr cur;

	    /* Process each response element */
	    for (cur = xml->children; cur; cur = cur->next) {
		xmlNodePtr node;
		xmlChar *recip = NULL, *status = NULL, *content = NULL;

		if (cur->type != XML_ELEMENT_NODE) continue;

		for (node = cur->children; node; node = node->next) {
		    if (node->type != XML_ELEMENT_NODE) continue;

		    if (!xmlStrcmp(node->name, BAD_CAST "recipient"))
			recip = xmlNodeGetContent(node);
		    else if (!xmlStrcmp(node->name, BAD_CAST "request-status"))
			status = xmlNodeGetContent(node);
		    else if (!xmlStrcmp(node->name, BAD_CAST "calendar-data"))
			content = xmlNodeGetContent(node);
		}

		resp =
		    xml_add_schedresponse(rrock->root,
					  !(rrock->txn->req_tgt.allow & ALLOW_ISCHEDULE) ?
					  rrock->ns[NS_DAV] : NULL,
					  recip, status);

		xmlFree(status);
		xmlFree(recip);

		if (content) {
		    xmlNodePtr cdata =
			xmlNewTextChild(resp, NULL,
					BAD_CAST "calendar-data", NULL);
		    xmlAddChild(cdata,
				xmlNewCDataBlock(rrock->root->doc,
						 content,
						 xmlStrlen(content)));
		    xmlFree(content);

		    /* iCal data in resp SHOULD NOT be transformed */
		    rrock->txn->flags.cc |= CC_NOTRANSFORM;
		}
	    }

	    xmlFreeDoc(xml->doc);
	}
    }

    /* Report request-status (if necesary)
     * Remove the attendees from the iTIP request and hash bucket
     */
    for (list = remote->props; list; list = list->next) {
	if (status) {
	    const char *attendee = icalproperty_get_attendee(list->prop);
	    xml_add_schedresponse(rrock->root,
				  !(rrock->txn->req_tgt.allow & ALLOW_ISCHEDULE) ?
				  rrock->ns[NS_DAV] : NULL,
				  BAD_CAST attendee,
				  BAD_CAST status);
	}

	icalcomponent_remove_property(comp, list->prop);
	icalproperty_free(list->prop);
    }

    if (remote->server) free(remote->server);
}


int propfind_schedinbox(const xmlChar *name, xmlNsPtr ns,
			struct propfind_ctx *fctx, xmlNodePtr resp,
			struct propstat propstat[], void *rock)
{
    return propfind_calurl(name, ns, fctx, resp, propstat, rock, SCHED_INBOX);
}


bus_server_quit (gboolean restart)
{
    _restart = restart;
    if (mainloop)
        g_main_loop_quit (mainloop);
}


bus_acquired_handler (GDBusConnection       *connection,
                      const gchar           *name,
                      G_GNUC_UNUSED gpointer user_data)
{
    g_dbus_connection_call (connection,
                            IBUS_SERVICE_PORTAL,
                            IBUS_PATH_IBUS,
                            "org.freedesktop.DBus.Peer",
                            "Ping",
                            g_variant_new ("()"),
                            G_VARIANT_TYPE ("()"),
                            G_DBUS_CALL_FLAGS_NONE,
                            -1,
                            NULL /* cancellable */,
                            (GAsyncReadyCallback)
                                    _server_connect_start_portal_cb,
                            NULL);
}


void dlpar_free_cc_nodes(struct device_node *dn)
{
	if (dn->child)
		dlpar_free_cc_nodes(dn->child);

	if (dn->sibling)
		dlpar_free_cc_nodes(dn->sibling);

	dlpar_free_one_cc_node(dn);
}


static ssize_t dlpar_show(struct class *class, struct class_attribute *attr,
			  char *buf)
{
	return sprintf(buf, "%s\n", "memory,cpu");
}


void mdesc_fill_in_cpu_data(cpumask_t *mask)
{
	struct mdesc_handle *hp;

	mdesc_iterate_over_cpus(fill_in_one_cpu, NULL, mask);

	hp = mdesc_grab();

	set_core_ids(hp);
	set_proc_ids(hp);
	set_sock_ids(hp);

	mdesc_release(hp);

	smp_fill_in_sib_core_maps();
}


u64 mdesc_get_node(struct mdesc_handle *hp, const char *node_name,
		   union md_node_info *node_info)
{
	mdesc_node_info_get_f get_info_func;
	mdesc_node_info_rel_f rel_info_func;
	mdesc_node_match_f node_match_func;
	union md_node_info hp_node_info;
	u64 hp_node;
	int rv;

	if (hp == NULL || node_name == NULL || node_info == NULL)
		return MDESC_NODE_NULL;

	/* Find the ops for the given node name */
	mdesc_get_node_ops(node_name, &get_info_func, &rel_info_func,
			   &node_match_func);

	/* If we didn't find ops for the given node name, it is not supported */
	if (!get_info_func || !rel_info_func || !node_match_func) {
		pr_err("MD: %s node is not supported\n", node_name);
		return -EINVAL;
	}

	mdesc_for_each_node_by_name(hp, hp_node, node_name) {
		rv = get_info_func(hp, hp_node, &hp_node_info);
		if (rv != 0)
			continue;

		if (node_match_func(node_info, &hp_node_info))
			break;

		rel_info_func(&hp_node_info);
	}

	rel_info_func(&hp_node_info);

	return hp_node;
}


static void get_mondo_data(struct mdesc_handle *hp, u64 mp,
			   struct trap_per_cpu *tb)
{
	static int printed;
	const u64 *val;

	val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits", NULL);
	get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7, ilog2(max_cpus * 2));

	val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits", NULL);
	get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7, 8);

	val = mdesc_get_property(hp, mp, "q-resumable-#bits", NULL);
	get_one_mondo_bits(val, &tb->resum_qmask, 6, 7);

	val = mdesc_get_property(hp, mp, "q-nonresumable-#bits", NULL);
	get_one_mondo_bits(val, &tb->nonresum_qmask, 2, 2);
	if (!printed++) {
		pr_info("SUN4V: Mondo queue sizes "
			"[cpu(%u) dev(%u) r(%u) nr(%u)]\n",
			tb->cpu_mondo_qmask + 1,
			tb->dev_mondo_qmask + 1,
			tb->resum_qmask + 1,
			tb->nonresum_qmask + 1);
	}
}


static void rel_vdev_port_node_info(union md_node_info *node_info)
{
	if (node_info && node_info->vdev_port.name) {
		kfree_const(node_info->vdev_port.name);
		node_info->vdev_port.name = NULL;
	}
}


vpeekc_any(void)
{
    int		c;

    c = vpeekc();
    if (c == NUL && typebuf.tb_len > 0)
	c = ESC;
    return c;
}


inchar(
    char_u	*buf,
    int		maxlen,
    long	wait_time)	    /* milli seconds */
{
    int		len = 0;	    /* init for GCC */
    int		retesc = FALSE;	    /* return ESC with gotint */
    int		script_char;
    int		tb_change_cnt = typebuf.tb_change_cnt;

    if (wait_time == -1L || wait_time > 100L)  /* flush output before waiting */
    {
	cursor_on();
	out_flush_cursor(FALSE, FALSE);
#if defined(FEAT_GUI) && defined(FEAT_MOUSESHAPE)
	if (gui.in_use && postponed_mouseshape)
	    update_mouseshape(-1);
#endif
    }

    /*
     * Don't reset these when at the hit-return prompt, otherwise a endless
     * recursive loop may result (write error in swapfile, hit-return, timeout
     * on char wait, flush swapfile, write error....).
     */
    if (State != HITRETURN)
    {
	did_outofmem_msg = FALSE;   /* display out of memory message (again) */
	did_swapwrite_msg = FALSE;  /* display swap file write error again */
    }
    undo_off = FALSE;		    /* restart undo now */

    /*
     * Get a character from a script file if there is one.
     * If interrupted: Stop reading script files, close them all.
     */
    script_char = -1;
    while (scriptin[curscript] != NULL && script_char < 0
#ifdef FEAT_EVAL
	    && !ignore_script
#endif
	    )
    {

#ifdef MESSAGE_QUEUE
	parse_queued_messages();
#endif

	if (got_int || (script_char = getc(scriptin[curscript])) < 0)
	{
	    /* Reached EOF.
	     * Careful: closescript() frees typebuf.tb_buf[] and buf[] may
	     * point inside typebuf.tb_buf[].  Don't use buf[] after this! */
	    closescript();
	    /*
	     * When reading script file is interrupted, return an ESC to get
	     * back to normal mode.
	     * Otherwise return -1, because typebuf.tb_buf[] has changed.
	     */
	    if (got_int)
		retesc = TRUE;
	    else
		return -1;
	}
	else
	{
	    buf[0] = script_char;
	    len = 1;
	}
    }

    if (script_char < 0)	/* did not get a character from script */
    {
	/*
	 * If we got an interrupt, skip all previously typed characters and
	 * return TRUE if quit reading script file.
	 * Stop reading typeahead when a single CTRL-C was read,
	 * fill_input_buf() returns this when not able to read from stdin.
	 * Don't use buf[] here, closescript() may have freed typebuf.tb_buf[]
	 * and buf may be pointing inside typebuf.tb_buf[].
	 */
	if (got_int)
	{
#define DUM_LEN MAXMAPLEN * 3 + 3
	    char_u	dum[DUM_LEN + 1];

	    for (;;)
	    {
		len = ui_inchar(dum, DUM_LEN, 0L, 0);
		if (len == 0 || (len == 1 && dum[0] == 3))
		    break;
	    }
	    return retesc;
	}

	/*
	 * Always flush the output characters when getting input characters
	 * from the user and not just peeking.
	 */
	if (wait_time == -1L || wait_time > 10L)
	    out_flush();

	/*
	 * Fill up to a third of the buffer, because each character may be
	 * tripled below.
	 */
	len = ui_inchar(buf, maxlen / 3, wait_time, tb_change_cnt);
    }

    /* If the typebuf was changed further down, it is like nothing was added by
     * this call. */
    if (typebuf_changed(tb_change_cnt))
	return 0;

    /* Note the change in the typeahead buffer, this matters for when
     * vgetorpeek() is called recursively, e.g. using getchar(1) in a timer
     * function. */
    if (len > 0 && ++typebuf.tb_change_cnt == 0)
	typebuf.tb_change_cnt = 1;

    return fix_input_buffer(buf, len);
}


validate_maphash(void)
{
    if (!maphash_valid)
    {
	vim_memset(maphash, 0, sizeof(maphash));
	maphash_valid = TRUE;
    }
}


AppendNumberToRedobuff(long n)
{
    if (!block_redo)
	add_num_buff(&redobuff, n);
}


stuffnumReadbuff(long n)
{
    add_num_buff(&readbuf1, n);
}


vpeekc_nomap(void)
{
    int		c;

    ++no_mapping;
    ++allow_keys;
    c = vpeekc();
    --no_mapping;
    --allow_keys;
    return c;
}


free_buff(buffheader_T *buf)
{
    buffblock_T	*p, *np;

    for (p = buf->bh_first.b_next; p != NULL; p = np)
    {
	np = p->b_next;
	vim_free(p);
    }
    buf->bh_first.b_next = NULL;
}


start_redo(long count, int old_redo)
{
    int	    c;

    /* init the pointers; return if nothing to redo */
    if (read_redo(TRUE, old_redo) == FAIL)
	return FAIL;

    c = read_redo(FALSE, old_redo);

    /* copy the buffer name, if present */
    if (c == '"')
    {
	add_buff(&readbuf2, (char_u *)"\"", 1L);
	c = read_redo(FALSE, old_redo);

	/* if a numbered buffer is used, increment the number */
	if (c >= '1' && c < '9')
	    ++c;
	add_char_buff(&readbuf2, c);

	/* the expression register should be re-evaluated */
	if (c == '=')
	{
	    add_char_buff(&readbuf2, CAR);
	    cmd_silent = TRUE;
	}

	c = read_redo(FALSE, old_redo);
    }

    if (c == 'v')   /* redo Visual */
    {
	VIsual = curwin->w_cursor;
	VIsual_active = TRUE;
	VIsual_select = FALSE;
	VIsual_reselect = TRUE;
	redo_VIsual_busy = TRUE;
	c = read_redo(FALSE, old_redo);
    }

    /* try to enter the count (in place of a previous count) */
    if (count)
    {
	while (VIM_ISDIGIT(c))	/* skip "old" count */
	    c = read_redo(FALSE, old_redo);
	add_num_buff(&readbuf2, count);
    }

    /* copy from the redo buffer into the stuff buffer */
    add_char_buff(&readbuf2, c);
    copy_redo(old_redo);
    return OK;
}


typebuf_typed(void)
{
    return typebuf.tb_maplen == 0;
}


get_inserted(void)
{
    return get_buffcont(&redobuff, FALSE);
}


add_map(char_u *map, int mode)
{
    char_u	*s;
    char_u	*cpo_save = p_cpo;

    p_cpo = (char_u *)"";	/* Allow <> notation */
    s = vim_strsave(map);
    if (s != NULL)
    {
	(void)do_map(0, s, mode, FALSE);
	vim_free(s);
    }
    p_cpo = cpo_save;
}


start_stuff(void)
{
    if (readbuf1.bh_first.b_next != NULL)
    {
	readbuf1.bh_curr = &(readbuf1.bh_first);
	readbuf1.bh_space = 0;
    }
    if (readbuf2.bh_first.b_next != NULL)
    {
	readbuf2.bh_curr = &(readbuf2.bh_first);
	readbuf2.bh_space = 0;
    }
}


void tcp_cwnd_reduction(struct sock *sk, int newly_acked_sacked, int flag)
{
	struct tcp_sock *tp = tcp_sk(sk);
	int sndcnt = 0;
	int delta = tp->snd_ssthresh - tcp_packets_in_flight(tp);

	if (newly_acked_sacked <= 0 || WARN_ON_ONCE(!tp->prior_cwnd))
		return;

	tp->prr_delivered += newly_acked_sacked;
	if (delta < 0) {
		u64 dividend = (u64)tp->snd_ssthresh * tp->prr_delivered +
			       tp->prior_cwnd - 1;
		sndcnt = div_u64(dividend, tp->prior_cwnd) - tp->prr_out;
	} else if ((flag & (FLAG_RETRANS_DATA_ACKED | FLAG_LOST_RETRANS)) ==
		   FLAG_RETRANS_DATA_ACKED) {
		sndcnt = min_t(int, delta,
			       max_t(int, tp->prr_delivered - tp->prr_out,
				     newly_acked_sacked) + 1);
	} else {
		sndcnt = min(delta, newly_acked_sacked);
	}
	/* Force a fast retransmit upon entering fast recovery */
	sndcnt = max(sndcnt, (tp->prr_out ? 0 : 1));
	tp->snd_cwnd = tcp_packets_in_flight(tp) + sndcnt;
}


static void tcp_cwnd_validate(struct sock *sk, bool is_cwnd_limited)
{
	const struct tcp_congestion_ops *ca_ops = inet_csk(sk)->icsk_ca_ops;
	struct tcp_sock *tp = tcp_sk(sk);

	/* Track the maximum number of outstanding packets in each
	 * window, and remember whether we were cwnd-limited then.
	 */
	if (!before(tp->snd_una, tp->max_packets_seq) ||
	    tp->packets_out > tp->max_packets_out) {
		tp->max_packets_out = tp->packets_out;
		tp->max_packets_seq = tp->snd_nxt;
		tp->is_cwnd_limited = is_cwnd_limited;
	}

	if (tcp_is_cwnd_limited(sk)) {
		/* Network is feed fully. */
		tp->snd_cwnd_used = 0;
		tp->snd_cwnd_stamp = tcp_jiffies32;
	} else {
		/* Network starves. */
		if (tp->packets_out > tp->snd_cwnd_used)
			tp->snd_cwnd_used = tp->packets_out;

		if (sock_net(sk)->ipv4.sysctl_tcp_slow_start_after_idle &&
		    (s32)(tcp_jiffies32 - tp->snd_cwnd_stamp) >= inet_csk(sk)->icsk_rto &&
		    !ca_ops->cong_control)
			tcp_cwnd_application_limited(sk);

		/* The following conditions together indicate the starvation
		 * is caused by insufficient sender buffer:
		 * 1) just sent some data (see tcp_write_xmit)
		 * 2) not cwnd limited (this else condition)
		 * 3) no more data to send (tcp_write_queue_empty())
		 * 4) application is hitting buffer limit (SOCK_NOSPACE)
		 */
		if (tcp_write_queue_empty(sk) && sk->sk_socket &&
		    test_bit(SOCK_NOSPACE, &sk->sk_socket->flags) &&
		    (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
			tcp_chrono_start(sk, TCP_CHRONO_SNDBUF_LIMITED);
	}
}


void tcp_synack_rtt_meas(struct sock *sk, struct request_sock *req)
{
	struct rate_sample rs;
	long rtt_us = -1L;

	if (req && !req->num_retrans && tcp_rsk(req)->snt_synack)
		rtt_us = tcp_stamp_us_delta(tcp_clock_us(), tcp_rsk(req)->snt_synack);

	tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, rtt_us, -1L, rtt_us, &rs);
}


static u64 tcp_compute_delivery_rate(const struct tcp_sock *tp)
{
	u32 rate = READ_ONCE(tp->rate_delivered);
	u32 intv = READ_ONCE(tp->rate_interval_us);
	u64 rate64 = 0;

	if (rate && intv) {
		rate64 = (u64)rate * tp->mss_cache * USEC_PER_SEC;
		do_div(rate64, intv);
	}
	return rate64;
}


static void tcp_timeout_mark_lost(struct sock *sk)
{
	struct tcp_sock *tp = tcp_sk(sk);
	struct sk_buff *skb, *head;
	bool is_reneg;			/* is receiver reneging on SACKs? */

	head = tcp_rtx_queue_head(sk);
	is_reneg = head && (TCP_SKB_CB(head)->sacked & TCPCB_SACKED_ACKED);
	if (is_reneg) {
		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSACKRENEGING);
		tp->sacked_out = 0;
		/* Mark SACK reneging until we recover from this loss event. */
		tp->is_sack_reneg = 1;
	} else if (tcp_is_reno(tp)) {
		tcp_reset_reno_sack(tp);
	}

	skb = head;
	skb_rbtree_walk_from(skb) {
		if (is_reneg)
			TCP_SKB_CB(skb)->sacked &= ~TCPCB_SACKED_ACKED;
		else if (tcp_is_rack(sk) && skb != head &&
			 tcp_rack_skb_timeout(tp, skb, 0) > 0)
			continue; /* Don't mark recently sent ones lost yet */
		tcp_mark_skb_lost(sk, skb);
	}
	tcp_verify_left_out(tp);
	tcp_clear_all_retrans_hints(tp);
}


static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible)
{
	struct tcp_sock *tp = tcp_sk(sk);
	unsigned long rtt, delay;

	    /* More than one full frame received... */
	if (((tp->rcv_nxt - tp->rcv_wup) > inet_csk(sk)->icsk_ack.rcv_mss &&
	     /* ... and right edge of window advances far enough.
	      * (tcp_recvmsg() will send ACK otherwise).
	      * If application uses SO_RCVLOWAT, we want send ack now if
	      * we have not received enough bytes to satisfy the condition.
	      */
	    (tp->rcv_nxt - tp->copied_seq < sk->sk_rcvlowat ||
	     __tcp_select_window(sk) >= tp->rcv_wnd)) ||
	    /* We ACK each frame or... */
	    tcp_in_quickack_mode(sk) ||
	    /* Protocol state mandates a one-time immediate ACK */
	    inet_csk(sk)->icsk_ack.pending & ICSK_ACK_NOW) {
send_now:
		tcp_send_ack(sk);
		return;
	}

	if (!ofo_possible || RB_EMPTY_ROOT(&tp->out_of_order_queue)) {
		tcp_send_delayed_ack(sk);
		return;
	}

	if (!tcp_is_sack(tp) ||
	    tp->compressed_ack >= sock_net(sk)->ipv4.sysctl_tcp_comp_sack_nr)
		goto send_now;

	if (tp->compressed_ack_rcv_nxt != tp->rcv_nxt) {
		tp->compressed_ack_rcv_nxt = tp->rcv_nxt;
		if (tp->compressed_ack > TCP_FASTRETRANS_THRESH)
			NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPACKCOMPRESSED,
				      tp->compressed_ack - TCP_FASTRETRANS_THRESH);
		tp->compressed_ack = 0;
	}

	if (++tp->compressed_ack <= TCP_FASTRETRANS_THRESH)
		goto send_now;

	if (hrtimer_is_queued(&tp->compressed_ack_timer))
		return;

	/* compress ack timer : 5 % of rtt, but no more than tcp_comp_sack_delay_ns */

	rtt = tp->rcv_rtt_est.rtt_us;
	if (tp->srtt_us && tp->srtt_us < rtt)
		rtt = tp->srtt_us;

	delay = min_t(unsigned long, sock_net(sk)->ipv4.sysctl_tcp_comp_sack_delay_ns,
		      rtt * (NSEC_PER_USEC >> 3)/20);
	sock_hold(sk);
	hrtimer_start(&tp->compressed_ack_timer, ns_to_ktime(delay),
		      HRTIMER_MODE_REL_PINNED_SOFT);
}


int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)
{
	u32 delta_truesize;

	if (skb_unclone(skb, GFP_ATOMIC))
		return -ENOMEM;

	delta_truesize = __pskb_trim_head(skb, len);

	TCP_SKB_CB(skb)->seq += len;
	skb->ip_summed = CHECKSUM_PARTIAL;

	if (delta_truesize) {
		skb->truesize	   -= delta_truesize;
		sk->sk_wmem_queued -= delta_truesize;
		sk_mem_uncharge(sk, delta_truesize);
		sock_set_flag(sk, SOCK_QUEUE_SHRUNK);
	}

	/* Any change of skb->len requires recalculation of tso factor. */
	if (tcp_skb_pcount(skb) > 1)
		tcp_set_skb_tso_segs(skb, tcp_skb_mss(skb));

	return 0;
}


static void tcp_xmit_recovery(struct sock *sk, int rexmit)
{
	struct tcp_sock *tp = tcp_sk(sk);

	if (rexmit == REXMIT_NONE || sk->sk_state == TCP_SYN_SENT)
		return;

	if (unlikely(rexmit == 2)) {
		__tcp_push_pending_frames(sk, tcp_current_mss(sk),
					  TCP_NAGLE_OFF);
		if (after(tp->snd_nxt, tp->high_seq))
			return;
		tp->frto = 0;
	}
	tcp_xmit_retransmit_queue(sk);
}


static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,
				    struct tcp_fastopen_cookie *cookie)
{
	struct tcp_sock *tp = tcp_sk(sk);
	struct sk_buff *data = tp->syn_data ? tcp_rtx_queue_head(sk) : NULL;
	u16 mss = tp->rx_opt.mss_clamp, try_exp = 0;
	bool syn_drop = false;

	if (mss == tp->rx_opt.user_mss) {
		struct tcp_options_received opt;

		/* Get original SYNACK MSS value if user MSS sets mss_clamp */
		tcp_clear_options(&opt);
		opt.user_mss = opt.mss_clamp = 0;
		tcp_parse_options(sock_net(sk), synack, &opt, 0, NULL);
		mss = opt.mss_clamp;
	}

	if (!tp->syn_fastopen) {
		/* Ignore an unsolicited cookie */
		cookie->len = -1;
	} else if (tp->total_retrans) {
		/* SYN timed out and the SYN-ACK neither has a cookie nor
		 * acknowledges data. Presumably the remote received only
		 * the retransmitted (regular) SYNs: either the original
		 * SYN-data or the corresponding SYN-ACK was dropped.
		 */
		syn_drop = (cookie->len < 0 && data);
	} else if (cookie->len < 0 && !tp->syn_data) {
		/* We requested a cookie but didn't get it. If we did not use
		 * the (old) exp opt format then try so next time (try_exp=1).
		 * Otherwise we go back to use the RFC7413 opt (try_exp=2).
		 */
		try_exp = tp->syn_fastopen_exp ? 2 : 1;
	}

	tcp_fastopen_cache_set(sk, mss, cookie, syn_drop, try_exp);

	if (data) { /* Retransmit unacked data in SYN */
		skb_rbtree_walk_from(data) {
			if (__tcp_retransmit_skb(sk, data, 1))
				break;
		}
		tcp_rearm_rto(sk);
		NET_INC_STATS(sock_net(sk),
				LINUX_MIB_TCPFASTOPENACTIVEFAIL);
		return true;
	}
	tp->syn_data_acked = tp->syn_data;
	if (tp->syn_data_acked) {
		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPFASTOPENACTIVE);
		/* SYN-data is counted as two separate packets in tcp_ack() */
		if (tp->delivered > 1)
			--tp->delivered;
	}

	tcp_fastopen_add_skb(sk, synack);

	return false;
}


static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb)
{
	struct tcp_sock *tp = tcp_sk(sk);

	/* Advance write_seq and place onto the write_queue. */
	tp->write_seq = TCP_SKB_CB(skb)->end_seq;
	__skb_header_release(skb);
	tcp_add_write_queue_tail(sk, skb);
	sk->sk_wmem_queued += skb->truesize;
	sk_mem_charge(sk, skb->truesize);
}


static bool tcp_syn_flood_action(const struct sock *sk,
				 const struct sk_buff *skb,
				 const char *proto)
{
	struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue;
	const char *msg = "Dropping request";
	bool want_cookie = false;
	struct net *net = sock_net(sk);

#ifdef CONFIG_SYN_COOKIES
	if (net->ipv4.sysctl_tcp_syncookies) {
		msg = "Sending cookies";
		want_cookie = true;
		__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPREQQFULLDOCOOKIES);
	} else
#endif
		__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPREQQFULLDROP);

	if (!queue->synflood_warned &&
	    net->ipv4.sysctl_tcp_syncookies != 2 &&
	    xchg(&queue->synflood_warned, 1) == 0)
		net_info_ratelimited("%s: Possible SYN flooding on port %d. %s.  Check SNMP counters.\n",
				     proto, ntohs(tcp_hdr(skb)->dest), msg);

	return want_cookie;
}


static void tcp_openreq_init(struct request_sock *req,
			     const struct tcp_options_received *rx_opt,
			     struct sk_buff *skb, const struct sock *sk)
{
	struct inet_request_sock *ireq = inet_rsk(req);

	req->rsk_rcv_wnd = 0;		/* So that tcp_send_synack() knows! */
	req->cookie_ts = 0;
	tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq;
	tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;
	tcp_rsk(req)->snt_synack = 0;
	tcp_rsk(req)->last_oow_ack_time = 0;
	req->mss = rx_opt->mss_clamp;
	req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0;
	ireq->tstamp_ok = rx_opt->tstamp_ok;
	ireq->sack_ok = rx_opt->sack_ok;
	ireq->snd_wscale = rx_opt->snd_wscale;
	ireq->wscale_ok = rx_opt->wscale_ok;
	ireq->acked = 0;
	ireq->ecn_ok = 0;
	ireq->ir_rmt_port = tcp_hdr(skb)->source;
	ireq->ir_num = ntohs(tcp_hdr(skb)->dest);
	ireq->ir_mark = inet_request_mark(sk, skb);
#if IS_ENABLED(CONFIG_SMC)
	ireq->smc_ok = rx_opt->smc_ok;
#endif
}


static void tcp_update_pacing_rate(struct sock *sk)
{
	const struct tcp_sock *tp = tcp_sk(sk);
	u64 rate;

	/* set sk_pacing_rate to 200 % of current rate (mss * cwnd / srtt) */
	rate = (u64)tp->mss_cache * ((USEC_PER_SEC / 100) << 3);

	/* current rate is (cwnd * mss) / srtt
	 * In Slow Start [1], set sk_pacing_rate to 200 % the current rate.
	 * In Congestion Avoidance phase, set it to 120 % the current rate.
	 *
	 * [1] : Normal Slow Start condition is (tp->snd_cwnd < tp->snd_ssthresh)
	 *	 If snd_cwnd >= (tp->snd_ssthresh / 2), we are approaching
	 *	 end of slow start and should slow down.
	 */
	if (tp->snd_cwnd < tp->snd_ssthresh / 2)
		rate *= sock_net(sk)->ipv4.sysctl_tcp_pacing_ss_ratio;
	else
		rate *= sock_net(sk)->ipv4.sysctl_tcp_pacing_ca_ratio;

	rate *= max(tp->snd_cwnd, tp->packets_out);

	if (likely(tp->srtt_us))
		do_div(rate, tp->srtt_us);

	/* WRITE_ONCE() is needed because sch_fq fetches sk_pacing_rate
	 * without any lock. We want to make sure compiler wont store
	 * intermediate values in this location.
	 */
	WRITE_ONCE(sk->sk_pacing_rate, min_t(u64, rate,
					     sk->sk_max_pacing_rate));
}


void __tcp_send_ack(struct sock *sk, u32 rcv_nxt)
{
	struct sk_buff *buff;

	/* If we have been reset, we may not send again. */
	if (sk->sk_state == TCP_CLOSE)
		return;

	/* We are not putting this on the write queue, so
	 * tcp_transmit_skb() will set the ownership to this
	 * sock.
	 */
	buff = alloc_skb(MAX_TCP_HEADER,
			 sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN));
	if (unlikely(!buff)) {
		inet_csk_schedule_ack(sk);
		inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN;
		inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
					  TCP_DELACK_MAX, TCP_RTO_MAX);
		return;
	}

	/* Reserve space for headers and prepare control bits. */
	skb_reserve(buff, MAX_TCP_HEADER);
	tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK);

	/* We do not want pure acks influencing TCP Small Queues or fq/pacing
	 * too much.
	 * SKB_TRUESIZE(max(1 .. 66, MAX_TCP_HEADER)) is unfortunately ~784
	 */
	skb_set_tcp_pure_ack(buff);

	/* Send it off, this clears delayed acks for us. */
	__tcp_transmit_skb(sk, buff, 0, (__force gfp_t)0, rcv_nxt);
}


static inline int tcp_dupack_heuristics(const struct tcp_sock *tp)
{
	return tp->sacked_out + 1;
}


static int tcp_repair_options_est(struct sock *sk,
		struct tcp_repair_opt __user *optbuf, unsigned int len)
{
	struct tcp_sock *tp = tcp_sk(sk);
	struct tcp_repair_opt opt;

	while (len >= sizeof(opt)) {
		if (copy_from_user(&opt, optbuf, sizeof(opt)))
			return -EFAULT;

		optbuf++;
		len -= sizeof(opt);

		switch (opt.opt_code) {
		case TCPOPT_MSS:
			tp->rx_opt.mss_clamp = opt.opt_val;
			tcp_mtup_init(sk);
			break;
		case TCPOPT_WINDOW:
			{
				u16 snd_wscale = opt.opt_val & 0xFFFF;
				u16 rcv_wscale = opt.opt_val >> 16;

				if (snd_wscale > TCP_MAX_WSCALE || rcv_wscale > TCP_MAX_WSCALE)
					return -EFBIG;

				tp->rx_opt.snd_wscale = snd_wscale;
				tp->rx_opt.rcv_wscale = rcv_wscale;
				tp->rx_opt.wscale_ok = 1;
			}
			break;
		case TCPOPT_SACK_PERM:
			if (opt.opt_val != 0)
				return -EINVAL;

			tp->rx_opt.sack_ok |= TCP_SACK_SEEN;
			break;
		case TCPOPT_TIMESTAMP:
			if (opt.opt_val != 0)
				return -EINVAL;

			tp->rx_opt.tstamp_ok = 1;
			break;
		}
	}

	return 0;
}


int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)
{
	struct inet_connection_sock *icsk = inet_csk(sk);
	struct tcp_sock *tp = tcp_sk(sk);
	unsigned int cur_mss;
	int diff, len, err;

	/* Inconclusive MTU probe */
	if (icsk->icsk_mtup.probe_size)
		icsk->icsk_mtup.probe_size = 0;

	/* Do not sent more than we queued. 1/4 is reserved for possible
	 * copying overhead: fragmentation, tunneling, mangling etc.
	 */
	if (refcount_read(&sk->sk_wmem_alloc) >
	    min_t(u32, sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2),
		  sk->sk_sndbuf))
		return -EAGAIN;

	if (skb_still_in_host_queue(sk, skb))
		return -EBUSY;

	if (before(TCP_SKB_CB(skb)->seq, tp->snd_una)) {
		if (unlikely(before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))) {
			WARN_ON_ONCE(1);
			return -EINVAL;
		}
		if (tcp_trim_head(sk, skb, tp->snd_una - TCP_SKB_CB(skb)->seq))
			return -ENOMEM;
	}

	if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk))
		return -EHOSTUNREACH; /* Routing failure or similar. */

	cur_mss = tcp_current_mss(sk);

	/* If receiver has shrunk his window, and skb is out of
	 * new window, do not retransmit it. The exception is the
	 * case, when window is shrunk to zero. In this case
	 * our retransmit serves as a zero window probe.
	 */
	if (!before(TCP_SKB_CB(skb)->seq, tcp_wnd_end(tp)) &&
	    TCP_SKB_CB(skb)->seq != tp->snd_una)
		return -EAGAIN;

	len = cur_mss * segs;
	if (skb->len > len) {
		if (tcp_fragment(sk, TCP_FRAG_IN_RTX_QUEUE, skb, len,
				 cur_mss, GFP_ATOMIC))
			return -ENOMEM; /* We'll try again later. */
	} else {
		if (skb_unclone(skb, GFP_ATOMIC))
			return -ENOMEM;

		diff = tcp_skb_pcount(skb);
		tcp_set_skb_tso_segs(skb, cur_mss);
		diff -= tcp_skb_pcount(skb);
		if (diff)
			tcp_adjust_pcount(sk, skb, diff);
		if (skb->len < cur_mss)
			tcp_retrans_try_collapse(sk, skb, cur_mss);
	}

	/* RFC3168, section 6.1.1.1. ECN fallback */
	if ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN_ECN) == TCPHDR_SYN_ECN)
		tcp_ecn_clear_syn(sk, skb);

	/* Update global and local TCP statistics. */
	segs = tcp_skb_pcount(skb);
	TCP_ADD_STATS(sock_net(sk), TCP_MIB_RETRANSSEGS, segs);
	if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN)
		__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSYNRETRANS);
	tp->total_retrans += segs;
	tp->bytes_retrans += skb->len;

	/* make sure skb->data is aligned on arches that require it
	 * and check if ack-trimming & collapsing extended the headroom
	 * beyond what csum_start can cover.
	 */
	if (unlikely((NET_IP_ALIGN && ((unsigned long)skb->data & 3)) ||
		     skb_headroom(skb) >= 0xFFFF)) {
		struct sk_buff *nskb;

		tcp_skb_tsorted_save(skb) {
			nskb = __pskb_copy(skb, MAX_TCP_HEADER, GFP_ATOMIC);
			err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) :
				     -ENOBUFS;
		} tcp_skb_tsorted_restore(skb);

		if (!err) {
			tcp_update_skb_after_send(sk, skb, tp->tcp_wstamp_ns);
			tcp_rate_skb_sent(sk, skb);
		}
	} else {
		err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
	}

	/* To avoid taking spuriously low RTT samples based on a timestamp
	 * for a transmit that never happened, always mark EVER_RETRANS
	 */
	TCP_SKB_CB(skb)->sacked |= TCPCB_EVER_RETRANS;

	if (BPF_SOCK_OPS_TEST_FLAG(tp, BPF_SOCK_OPS_RETRANS_CB_FLAG))
		tcp_call_bpf_3arg(sk, BPF_SOCK_OPS_RETRANS_CB,
				  TCP_SKB_CB(skb)->seq, segs, err);

	if (likely(!err)) {
		trace_tcp_retransmit_skb(sk, skb);
	} else if (err != -EBUSY) {
		NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL, segs);
	}
	return err;
}


void tcp_select_initial_window(const struct sock *sk, int __space, __u32 mss,
			       __u32 *rcv_wnd, __u32 *window_clamp,
			       int wscale_ok, __u8 *rcv_wscale,
			       __u32 init_rcv_wnd)
{
	unsigned int space = (__space < 0 ? 0 : __space);

	/* If no clamp set the clamp to the max possible scaled window */
	if (*window_clamp == 0)
		(*window_clamp) = (U16_MAX << TCP_MAX_WSCALE);
	space = min(*window_clamp, space);

	/* Quantize space offering to a multiple of mss if possible. */
	if (space > mss)
		space = rounddown(space, mss);

	/* NOTE: offering an initial window larger than 32767
	 * will break some buggy TCP stacks. If the admin tells us
	 * it is likely we could be speaking with such a buggy stack
	 * we will truncate our initial window offering to 32K-1
	 * unless the remote has sent us a window scaling option,
	 * which we interpret as a sign the remote TCP is not
	 * misinterpreting the window field as a signed quantity.
	 */
	if (sock_net(sk)->ipv4.sysctl_tcp_workaround_signed_windows)
		(*rcv_wnd) = min(space, MAX_TCP_WINDOW);
	else
		(*rcv_wnd) = min_t(u32, space, U16_MAX);

	if (init_rcv_wnd)
		*rcv_wnd = min(*rcv_wnd, init_rcv_wnd * mss);

	*rcv_wscale = 0;
	if (wscale_ok) {
		/* Set window scaling on max possible window */
		space = max_t(u32, space, sock_net(sk)->ipv4.sysctl_tcp_rmem[2]);
		space = max_t(u32, space, sysctl_rmem_max);
		space = min_t(u32, space, *window_clamp);
		*rcv_wscale = clamp_t(int, ilog2(space) - 15,
				      0, TCP_MAX_WSCALE);
	}
	/* Set the clamp no higher than max representable value */
	(*window_clamp) = min_t(__u32, U16_MAX << (*rcv_wscale), *window_clamp);
}


static void tcp_ecn_send(struct sock *sk, struct sk_buff *skb,
			 struct tcphdr *th, int tcp_header_len)
{
	struct tcp_sock *tp = tcp_sk(sk);

	if (tp->ecn_flags & TCP_ECN_OK) {
		/* Not-retransmitted data segment: set ECT and inject CWR. */
		if (skb->len != tcp_header_len &&
		    !before(TCP_SKB_CB(skb)->seq, tp->snd_nxt)) {
			INET_ECN_xmit(sk);
			if (tp->ecn_flags & TCP_ECN_QUEUE_CWR) {
				tp->ecn_flags &= ~TCP_ECN_QUEUE_CWR;
				th->cwr = 1;
				skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;
			}
		} else if (!tcp_ca_needs_ecn(sk)) {
			/* ACK or retransmitted segment: clear ECT|CE */
			INET_ECN_dontxmit(sk);
		}
		if (tp->ecn_flags & TCP_ECN_DEMAND_CWR)
			th->ece = 1;
	}
}


static void DBGUNDO(struct sock *sk, const char *msg)
{
#if FASTRETRANS_DEBUG > 1
	struct tcp_sock *tp = tcp_sk(sk);
	struct inet_sock *inet = inet_sk(sk);

	if (sk->sk_family == AF_INET) {
		pr_debug("Undo %s %pI4/%u c%u l%u ss%u/%u p%u\n",
			 msg,
			 &inet->inet_daddr, ntohs(inet->inet_dport),
			 tp->snd_cwnd, tcp_left_out(tp),
			 tp->snd_ssthresh, tp->prior_ssthresh,
			 tp->packets_out);
	}
#if IS_ENABLED(CONFIG_IPV6)
	else if (sk->sk_family == AF_INET6) {
		pr_debug("Undo %s %pI6/%u c%u l%u ss%u/%u p%u\n",
			 msg,
			 &sk->sk_v6_daddr, ntohs(inet->inet_dport),
			 tp->snd_cwnd, tcp_left_out(tp),
			 tp->snd_ssthresh, tp->prior_ssthresh,
			 tp->packets_out);
	}
#endif
#endif
}


static void tcp_event_data_sent(struct tcp_sock *tp,
				struct sock *sk)
{
	struct inet_connection_sock *icsk = inet_csk(sk);
	const u32 now = tcp_jiffies32;

	if (tcp_packets_in_flight(tp) == 0)
		tcp_ca_event(sk, CA_EVENT_TX_START);

	/* If this is the first data packet sent in response to the
	 * previous received data,
	 * and it is a reply for ato after last received packet,
	 * increase pingpong count.
	 */
	if (before(tp->lsndtime, icsk->icsk_ack.lrcvtime) &&
	    (u32)(now - icsk->icsk_ack.lrcvtime) < icsk->icsk_ack.ato)
		inet_csk_inc_pingpong_cnt(sk);

	tp->lsndtime = now;
}


static int __tcp_grow_window(const struct sock *sk, const struct sk_buff *skb)
{
	struct tcp_sock *tp = tcp_sk(sk);
	/* Optimize this! */
	int truesize = tcp_win_from_space(sk, skb->truesize) >> 1;
	int window = tcp_win_from_space(sk, sock_net(sk)->ipv4.sysctl_tcp_rmem[2]) >> 1;

	while (tp->rcv_ssthresh <= window) {
		if (truesize <= skb->len)
			return 2 * inet_csk(sk)->icsk_ack.rcv_mss;

		truesize >>= 1;
		window >>= 1;
	}
	return 0;
}


static int tcp_prune_queue(struct sock *sk)
{
	struct tcp_sock *tp = tcp_sk(sk);

	NET_INC_STATS(sock_net(sk), LINUX_MIB_PRUNECALLED);

	if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
		tcp_clamp_window(sk);
	else if (tcp_under_memory_pressure(sk))
		tp->rcv_ssthresh = min(tp->rcv_ssthresh, 4U * tp->advmss);

	if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)
		return 0;

	tcp_collapse_ofo_queue(sk);
	if (!skb_queue_empty(&sk->sk_receive_queue))
		tcp_collapse(sk, &sk->sk_receive_queue, NULL,
			     skb_peek(&sk->sk_receive_queue),
			     NULL,
			     tp->copied_seq, tp->rcv_nxt);
	sk_mem_reclaim(sk);

	if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)
		return 0;

	/* Collapsing did not help, destructive actions follow.
	 * This must not ever occur. */

	tcp_prune_ofo_queue(sk);

	if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf)
		return 0;

	/* If we are really being abused, tell the caller to silently
	 * drop receive data on the floor.  It will get retransmitted
	 * and hopefully then we'll have sufficient space.
	 */
	NET_INC_STATS(sock_net(sk), LINUX_MIB_RCVPRUNED);

	/* Massive buffer overcommit. */
	tp->pred_flags = 0;
	return -1;
}


int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs)
{
	struct tcp_sock *tp = tcp_sk(sk);
	int err = __tcp_retransmit_skb(sk, skb, segs);

	if (err == 0) {
#if FASTRETRANS_DEBUG > 0
		if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) {
			net_dbg_ratelimited("retrans_out leaked\n");
		}
#endif
		TCP_SKB_CB(skb)->sacked |= TCPCB_RETRANS;
		tp->retrans_out += tcp_skb_pcount(skb);
	}

	/* Save stamp of the first (attempted) retransmit. */
	if (!tp->retrans_stamp)
		tp->retrans_stamp = tcp_skb_timestamp(skb);

	if (tp->undo_retrans < 0)
		tp->undo_retrans = 0;
	tp->undo_retrans += tcp_skb_pcount(skb);
	return err;
}


static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now,
				       int large_allowed)
{
	struct tcp_sock *tp = tcp_sk(sk);
	u32 new_size_goal, size_goal;

	if (!large_allowed)
		return mss_now;

	/* Note : tcp_tso_autosize() will eventually split this later */
	new_size_goal = sk->sk_gso_max_size - 1 - MAX_TCP_HEADER;
	new_size_goal = tcp_bound_to_half_wnd(tp, new_size_goal);

	/* We try hard to avoid divides here */
	size_goal = tp->gso_segs * mss_now;
	if (unlikely(new_size_goal < size_goal ||
		     new_size_goal >= size_goal + mss_now)) {
		tp->gso_segs = min_t(u16, new_size_goal / mss_now,
				     sk->sk_gso_max_segs);
		size_goal = tp->gso_segs * mss_now;
	}

	return max(size_goal, mss_now);
}


static void tcp_skb_mark_lost(struct tcp_sock *tp, struct sk_buff *skb)
{
	if (!(TCP_SKB_CB(skb)->sacked & (TCPCB_LOST|TCPCB_SACKED_ACKED))) {
		tcp_verify_retransmit_hint(tp, skb);

		tp->lost_out += tcp_skb_pcount(skb);
		tcp_sum_lost(tp, skb);
		TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
	}
}


const u8 *tcp_parse_md5sig_option(const struct tcphdr *th)
{
	int length = (th->doff << 2) - sizeof(*th);
	const u8 *ptr = (const u8 *)(th + 1);

	/* If not enough data remaining, we can short cut */
	while (length >= TCPOLEN_MD5SIG) {
		int opcode = *ptr++;
		int opsize;

		switch (opcode) {
		case TCPOPT_EOL:
			return NULL;
		case TCPOPT_NOP:
			length--;
			continue;
		default:
			opsize = *ptr++;
			if (opsize < 2 || opsize > length)
				return NULL;
			if (opcode == TCPOPT_MD5SIG)
				return opsize == TCPOLEN_MD5SIG ? ptr : NULL;
		}
		ptr += opsize - 2;
		length -= opsize;
	}
	return NULL;
}


load_contents_open_callback (GObject      *obj,
                             GAsyncResult *open_res,
                             gpointer      user_data)
{
  GFile *file = G_FILE (obj);
  GFileInputStream *stream;
  LoadContentsData *data = user_data;
  GError *error = NULL;

  stream = g_file_read_finish (file, open_res, &error);

  if (stream)
    {
      g_byte_array_set_size (data->content,
                             data->pos + GET_CONTENT_BLOCK_SIZE);
      g_input_stream_read_async (G_INPUT_STREAM (stream),
                                 data->content->data + data->pos,
                                 GET_CONTENT_BLOCK_SIZE,
                                 0,
                                 g_task_get_cancellable (data->task),
                                 load_contents_read_callback,
                                 data);
    }
  else
    {
      g_task_return_error (data->task, error);
      g_object_unref (data->task);
    }
}


g_file_poll_mountable (GFile               *file,
                       GCancellable        *cancellable,
                       GAsyncReadyCallback  callback,
                       gpointer             user_data)
{
  GFileIface *iface;

  g_return_if_fail (G_IS_FILE (file));

  iface = G_FILE_GET_IFACE (file);

  if (iface->poll_mountable == NULL)
    {
      g_task_report_new_error (file, callback, user_data,
                               g_file_poll_mountable,
                               G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
                               _("Operation not supported"));
      return;
    }

  (* iface->poll_mountable) (file,
                             cancellable,
                             callback,
                             user_data);
}


load_contents_data_free (LoadContentsData *data)
{
  if (data->content)
    g_byte_array_free (data->content, TRUE);
  g_free (data->etag);
  g_free (data);
}


set_display_name_async_thread (GTask        *task,
                               gpointer      object,
                               gpointer      task_data,
                               GCancellable *cancellable)
{
  GError *error = NULL;
  char *name = task_data;
  GFile *file;

  file = g_file_set_display_name (G_FILE (object), name, cancellable, &error);

  if (file == NULL)
    g_task_return_error (task, error);
  else
    g_task_return_pointer (task, file, g_object_unref);
}


g_file_open_readwrite_finish (GFile         *file,
                              GAsyncResult  *res,
                              GError       **error)
{
  GFileIface *iface;

  g_return_val_if_fail (G_IS_FILE (file), NULL);
  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);

  if (g_async_result_legacy_propagate_error (res, error))
    return NULL;

  iface = G_FILE_GET_IFACE (file);
  return (* iface->open_readwrite_finish) (file, res, error);
}


build_attribute_list_for_copy (GFile                  *file,
                               GFileCopyFlags          flags,
                               char                  **out_attributes,
                               GCancellable           *cancellable,
                               GError                **error)
{
  gboolean ret = FALSE;
  GFileAttributeInfoList *attributes = NULL, *namespaces = NULL;
  GString *s = NULL;
  gboolean first;
  int i;
  gboolean copy_all_attributes;
  gboolean skip_perms;

  copy_all_attributes = flags & G_FILE_COPY_ALL_METADATA;
  skip_perms = (flags & G_FILE_COPY_TARGET_DEFAULT_PERMS) != 0;

  /* Ignore errors here, if the target supports no attributes there is
   * nothing to copy.  We still honor the cancellable though.
   */
  attributes = g_file_query_settable_attributes (file, cancellable, NULL);
  if (g_cancellable_set_error_if_cancelled (cancellable, error))
    goto out;

  namespaces = g_file_query_writable_namespaces (file, cancellable, NULL);
  if (g_cancellable_set_error_if_cancelled (cancellable, error))
    goto out;

  if (attributes == NULL && namespaces == NULL)
    goto out;

  first = TRUE;
  s = g_string_new ("");

  if (attributes)
    {
      for (i = 0; i < attributes->n_infos; i++)
        {
          if (should_copy (&attributes->infos[i], copy_all_attributes, skip_perms))
            {
              if (first)
                first = FALSE;
              else
                g_string_append_c (s, ',');

              g_string_append (s, attributes->infos[i].name);
            }
        }
    }

  if (namespaces)
    {
      for (i = 0; i < namespaces->n_infos; i++)
        {
          if (should_copy (&namespaces->infos[i], copy_all_attributes, FALSE))
            {
              if (first)
                first = FALSE;
              else
                g_string_append_c (s, ',');

              g_string_append (s, namespaces->infos[i].name);
              g_string_append (s, "::*");
            }
        }
    }

  ret = TRUE;
  *out_attributes = g_string_free (s, FALSE);
  s = NULL;
 out:
  if (s)
    g_string_free (s, TRUE);
  if (attributes)
    g_file_attribute_info_list_unref (attributes);
  if (namespaces)
    g_file_attribute_info_list_unref (namespaces);

  return ret;
}


file_peek_path_generic (GFile *file)
{
  const char *path;
  static GQuark _file_path_quark = 0;

  if (G_UNLIKELY (_file_path_quark) == 0)
    _file_path_quark = g_quark_from_static_string ("gio-file-path");

  /* We need to be careful about threading, as two threads calling
   * g_file_peek_path() on the same file could race: both would see
   * (g_object_get_qdata(…) == NULL) to begin with, both would generate and add
   * the path, but the second thread to add it would end up freeing the path
   * set by the first thread. The first thread would still return the pointer
   * to that freed path, though, resulting an a read-after-free. Handle that
   * with a compare-and-swap loop. The g_object_*_qdata() functions are atomic. */

  while (TRUE)
    {
      gchar *new_path = NULL;

      path = g_object_get_qdata ((GObject*)file, _file_path_quark);

      if (path != NULL)
        break;

      if (g_file_has_uri_scheme (file, "trash") ||
          g_file_has_uri_scheme (file, "recent"))
        new_path = file_get_target_path (file);
      else
        new_path = g_file_get_path (file);
      if (new_path == NULL)
        return NULL;

      /* By passing NULL here, we ensure we never replace existing data: */
      if (g_object_replace_qdata ((GObject *) file, _file_path_quark,
                                  NULL, (gpointer) new_path,
                                  (GDestroyNotify) g_free, NULL))
        break;
      else
        g_free (new_path);
    }

  return path;
}


g_file_real_trash_finish (GFile         *file,
                          GAsyncResult  *res,
                          GError       **error)
{
  g_return_val_if_fail (g_task_is_valid (res, file), FALSE);

  return g_task_propagate_boolean (G_TASK (res), error);
}


g_file_query_default_handler_async (GFile              *file,
                                    int                 io_priority,
                                    GCancellable       *cancellable,
                                    GAsyncReadyCallback callback,
                                    gpointer            user_data)
{
  GTask *task;
  char *uri_scheme;

  task = g_task_new (file, cancellable, callback, user_data);
  g_task_set_source_tag (task, g_file_query_default_handler_async);

  uri_scheme = g_file_get_uri_scheme (file);
  if (uri_scheme && uri_scheme[0] != '\0')
    {
      GAppInfo *appinfo;

      /* FIXME: The following still uses blocking calls. */
      appinfo = g_app_info_get_default_for_uri_scheme (uri_scheme);
      g_free (uri_scheme);

      if (appinfo != NULL)
        {
          g_task_return_pointer (task, g_steal_pointer (&appinfo), g_object_unref);
          g_object_unref (task);
          return;
        }
    }
  else
    g_free (uri_scheme);

  g_file_query_info_async (file,
                           G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
                           0,
                           io_priority,
                           cancellable,
                           query_default_handler_query_info_cb,
                           g_steal_pointer (&task));
}


g_file_make_directory_with_parents (GFile         *file,
                                    GCancellable  *cancellable,
                                    GError       **error)
{
  GFile *work_file = NULL;
  GList *list = NULL, *l;
  GError *my_error = NULL;

  g_return_val_if_fail (G_IS_FILE (file), FALSE);

  if (g_cancellable_set_error_if_cancelled (cancellable, error))
    return FALSE;

  /* Try for the simple case of not having to create any parent
   * directories.  If any parent directory needs to be created, this
   * call will fail with NOT_FOUND. If that happens, then that value of
   * my_error persists into the while loop below.
   */
  g_file_make_directory (file, cancellable, &my_error);
  if (!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
    {
      if (my_error)
        g_propagate_error (error, my_error);
      return my_error == NULL;
    }

  work_file = g_object_ref (file);

  /* Creates the parent directories as needed. In case any particular
   * creation operation fails for lack of other parent directories
   * (NOT_FOUND), the directory is added to a list of directories to
   * create later, and the value of my_error is retained until the next
   * iteration of the loop.  After the loop my_error should either be
   * empty or contain a real failure condition.
   */
  while (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
    {
      GFile *parent_file;

      parent_file = g_file_get_parent (work_file);
      if (parent_file == NULL)
        break;

      g_clear_error (&my_error);
      g_file_make_directory (parent_file, cancellable, &my_error);
      /* Another process may have created the directory in between the
       * G_IO_ERROR_NOT_FOUND and now
       */
      if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
        g_clear_error (&my_error);

      g_object_unref (work_file);
      work_file = g_object_ref (parent_file);

      if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
        list = g_list_prepend (list, parent_file);  /* Transfer ownership of ref */
      else
        g_object_unref (parent_file);
    }

  /* All directories should be able to be created now, so an error at
   * this point means the whole operation must fail -- except an EXISTS
   * error, which means that another process already created the
   * directory in between the previous failure and now.
   */
  for (l = list; my_error == NULL && l; l = l->next)
    {
      g_file_make_directory ((GFile *) l->data, cancellable, &my_error);
      if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
        g_clear_error (&my_error);
    }

  if (work_file)
    g_object_unref (work_file);

  /* Clean up */
  while (list != NULL)
    {
      g_object_unref ((GFile *) list->data);
      list = g_list_remove (list, list->data);
    }

  /* At this point an error in my_error means a that something
   * unexpected failed in either of the loops above, so the whole
   * operation must fail.
   */
  if (my_error != NULL)
    {
      g_propagate_error (error, my_error);
      return FALSE;
    }

  return g_file_make_directory (file, cancellable, error);
}


g_file_enumerate_children_async (GFile               *file,
                                 const char          *attributes,
                                 GFileQueryInfoFlags  flags,
                                 int                  io_priority,
                                 GCancellable        *cancellable,
                                 GAsyncReadyCallback  callback,
                                 gpointer             user_data)
{
  GFileIface *iface;

  g_return_if_fail (G_IS_FILE (file));

  iface = G_FILE_GET_IFACE (file);
  (* iface->enumerate_children_async) (file,
                                       attributes,
                                       flags,
                                       io_priority,
                                       cancellable,
                                       callback,
                                       user_data);
}


replace_contents_open_callback (GObject      *obj,
                                GAsyncResult *open_res,
                                gpointer      user_data)
{
  GFile *file = G_FILE (obj);
  GFileOutputStream *stream;
  ReplaceContentsData *data = user_data;
  GError *error = NULL;

  stream = g_file_replace_finish (file, open_res, &error);

  if (stream)
    {
      const gchar *content;
      gsize length;

      content = g_bytes_get_data (data->content, &length);
      g_output_stream_write_async (G_OUTPUT_STREAM (stream),
                                   content + data->pos,
                                   length - data->pos,
                                   0,
                                   g_task_get_cancellable (data->task),
                                   replace_contents_write_callback,
                                   data);
    }
  else
    {
      g_task_return_error (data->task, error);
      g_object_unref (data->task);
    }
}


g_file_get_child_for_display_name (GFile      *file,
                                   const char *display_name,
                                   GError **error)
{
  GFileIface *iface;

  g_return_val_if_fail (G_IS_FILE (file), NULL);
  g_return_val_if_fail (display_name != NULL, NULL);

  iface = G_FILE_GET_IFACE (file);

  return (* iface->get_child_for_display_name) (file, display_name, error);
}


g_file_make_symbolic_link (GFile         *file,
                           const char    *symlink_value,
                           GCancellable  *cancellable,
                           GError       **error)
{
  GFileIface *iface;

  g_return_val_if_fail (G_IS_FILE (file), FALSE);
  g_return_val_if_fail (symlink_value != NULL, FALSE);

  if (g_cancellable_set_error_if_cancelled (cancellable, error))
    return FALSE;

  if (*symlink_value == '\0')
    {
      g_set_error_literal (error, G_IO_ERROR,
                           G_IO_ERROR_INVALID_ARGUMENT,
                           _("Invalid symlink value given"));
      return FALSE;
    }

  iface = G_FILE_GET_IFACE (file);

  if (iface->make_symbolic_link == NULL)
    {
      g_set_error_literal (error, G_IO_ERROR,
                           G_IO_ERROR_NOT_SUPPORTED,
                           _("Operation not supported"));
      return FALSE;
    }

  return (* iface->make_symbolic_link) (file, symlink_value, cancellable, error);
}


g_file_real_create_async (GFile               *file,
                          GFileCreateFlags     flags,
                          int                  io_priority,
                          GCancellable        *cancellable,
                          GAsyncReadyCallback  callback,
                          gpointer             user_data)
{
  GFileCreateFlags *data;
  GTask *task;

  data = g_new0 (GFileCreateFlags, 1);
  *data = flags;

  task = g_task_new (file, cancellable, callback, user_data);
  g_task_set_source_tag (task, g_file_real_create_async);
  g_task_set_task_data (task, data, g_free);
  g_task_set_priority (task, io_priority);

  g_task_run_in_thread (task, create_async_thread);
  g_object_unref (task);
}


g_file_real_set_attributes_async (GFile               *file,
                                  GFileInfo           *info,
                                  GFileQueryInfoFlags  flags,
                                  int                  io_priority,
                                  GCancellable        *cancellable,
                                  GAsyncReadyCallback  callback,
                                  gpointer             user_data)
{
  GTask *task;
  SetInfoAsyncData *data;

  data = g_new0 (SetInfoAsyncData, 1);
  data->info = g_file_info_dup (info);
  data->flags = flags;

  task = g_task_new (file, cancellable, callback, user_data);
  g_task_set_source_tag (task, g_file_real_set_attributes_async);
  g_task_set_task_data (task, data, (GDestroyNotify)set_info_data_free);
  g_task_set_priority (task, io_priority);

  g_task_run_in_thread (task, set_info_async_thread);
  g_object_unref (task);
}


g_file_replace (GFile             *file,
                const char        *etag,
                gboolean           make_backup,
                GFileCreateFlags   flags,
                GCancellable      *cancellable,
                GError           **error)
{
  GFileIface *iface;

  g_return_val_if_fail (G_IS_FILE (file), NULL);

  if (g_cancellable_set_error_if_cancelled (cancellable, error))
    return NULL;

  iface = G_FILE_GET_IFACE (file);

  if (iface->replace == NULL)
    {
      g_set_error_literal (error, G_IO_ERROR,
                           G_IO_ERROR_NOT_SUPPORTED,
                           _("Operation not supported"));
      return NULL;
    }

  /* Handle empty tag string as NULL in consistent way. */
  if (etag && *etag == 0)
    etag = NULL;

  return (* iface->replace) (file, etag, make_backup, flags, cancellable, error);
}


g_file_delete (GFile         *file,
               GCancellable  *cancellable,
               GError       **error)
{
  GFileIface *iface;

  g_return_val_if_fail (G_IS_FILE (file), FALSE);

  if (g_cancellable_set_error_if_cancelled (cancellable, error))
    return FALSE;

  iface = G_FILE_GET_IFACE (file);

  if (iface->delete_file == NULL)
    {
      g_set_error_literal (error, G_IO_ERROR,
                           G_IO_ERROR_NOT_SUPPORTED,
                           _("Operation not supported"));
      return FALSE;
    }

  return (* iface->delete_file) (file, cancellable, error);
}


g_file_unmount_mountable_finish (GFile         *file,
                                 GAsyncResult  *result,
                                 GError       **error)
{
  GFileIface *iface;

  g_return_val_if_fail (G_IS_FILE (file), FALSE);
  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);

  if (g_async_result_legacy_propagate_error (result, error))
    return FALSE;
  else if (g_async_result_is_tagged (result, g_file_unmount_mountable_with_operation))
    return g_task_propagate_boolean (G_TASK (result), error);

  iface = G_FILE_GET_IFACE (file);
  return (* iface->unmount_mountable_finish) (file, result, error);
}


g_file_create (GFile             *file,
               GFileCreateFlags   flags,
               GCancellable      *cancellable,
               GError           **error)
{
  GFileIface *iface;

  g_return_val_if_fail (G_IS_FILE (file), NULL);

  if (g_cancellable_set_error_if_cancelled (cancellable, error))
    return NULL;

  iface = G_FILE_GET_IFACE (file);

  if (iface->create == NULL)
    {
      g_set_error_literal (error, G_IO_ERROR,
                           G_IO_ERROR_NOT_SUPPORTED,
                           _("Operation not supported"));
      return NULL;
    }

  return (* iface->create) (file, flags, cancellable, error);
}


g_file_parse_name (const char *parse_name)
{
  g_return_val_if_fail (parse_name != NULL, NULL);

  return g_vfs_parse_name (g_vfs_get_default (), parse_name);
}


open_readwrite_async_thread (GTask        *task,
                             gpointer      object,
                             gpointer      task_data,
                             GCancellable *cancellable)
{
  GFileIOStream *stream;
  GError *error = NULL;

  stream = g_file_open_readwrite (G_FILE (object), cancellable, &error);

  if (stream == NULL)
    g_task_return_error (task, error);
  else
    g_task_return_pointer (task, stream, g_object_unref);
}


g_file_set_display_name_finish (GFile         *file,
                                GAsyncResult  *res,
                                GError       **error)
{
  GFileIface *iface;

  g_return_val_if_fail (G_IS_FILE (file), NULL);
  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);

  if (g_async_result_legacy_propagate_error (res, error))
    return NULL;

  iface = G_FILE_GET_IFACE (file);
  return (* iface->set_display_name_finish) (file, res, error);
}


handle_client_data_anonymous_mech (DBusAuth         *auth,
                                  const DBusString *data)
{

  return TRUE;
}


process_ok(DBusAuth *auth,
          const DBusString *args_from_ok) {

  int end_of_hex;

  /* "args_from_ok" should be the GUID, whitespace already pulled off the front */
  _dbus_assert (_dbus_string_get_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server) == 0);

  /* We decode the hex string to binary, using guid_from_server as scratch... */

  end_of_hex = 0;
  if (!_dbus_string_hex_decode (args_from_ok, 0, &end_of_hex,
                                & DBUS_AUTH_CLIENT (auth)->guid_from_server, 0))
    return FALSE;

  /* now clear out the scratch */
  _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);

  if (end_of_hex != _dbus_string_get_length (args_from_ok) ||
      end_of_hex == 0)
    {
      _dbus_verbose ("Bad GUID from server, parsed %d bytes and had %d bytes from server\n",
                     end_of_hex, _dbus_string_get_length (args_from_ok));
      goto_state (auth, &common_state_need_disconnect);
      return TRUE;
    }

  if (!_dbus_string_copy (args_from_ok, 0, &DBUS_AUTH_CLIENT (auth)->guid_from_server, 0)) {
      _dbus_string_set_length (& DBUS_AUTH_CLIENT (auth)->guid_from_server, 0);
      return FALSE;
  }

  _dbus_verbose ("Got GUID '%s' from the server\n",
                 _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server));

  if (auth->unix_fd_possible)
    return send_negotiate_unix_fd(auth);

  _dbus_verbose("Not negotiating unix fd passing, since not possible\n");
  return send_begin (auth);
}


_dbus_auth_set_credentials (DBusAuth               *auth,
                            DBusCredentials        *credentials)
{
  _dbus_credentials_clear (auth->credentials);
  return _dbus_credentials_add_credentials (auth->credentials,
                                            credentials);
}


handle_server_data_anonymous_mech (DBusAuth         *auth,
                                   const DBusString *data)
{
  if (_dbus_string_get_length (data) > 0)
    {
      /* Client is allowed to send "trace" data, the only defined
       * meaning is that if it contains '@' it is an email address,
       * and otherwise it is anything else, and it's supposed to be
       * UTF-8
       */
      if (!_dbus_string_validate_utf8 (data, 0, _dbus_string_get_length (data)))
        {
          _dbus_verbose ("%s: Received invalid UTF-8 trace data from ANONYMOUS client\n",
                         DBUS_AUTH_NAME (auth));
          return send_rejected (auth);
        }

      _dbus_verbose ("%s: ANONYMOUS client sent trace string: '%s'\n",
                     DBUS_AUTH_NAME (auth),
                     _dbus_string_get_const_data (data));
    }

  /* We want to be anonymous (clear in case some other protocol got midway through I guess) */
  _dbus_credentials_clear (auth->desired_identity);

  /* Copy process ID from the socket credentials
   */
  if (!_dbus_credentials_add_credential (auth->authorized_identity,
                                         DBUS_CREDENTIAL_UNIX_PROCESS_ID,
                                         auth->credentials))
    return FALSE;

  /* Anonymous is always allowed */
  if (!send_ok (auth))
    return FALSE;

  _dbus_verbose ("%s: authenticated client as anonymous\n",
                 DBUS_AUTH_NAME (auth));

  return TRUE;
}


_dbus_auth_get_guid_from_server (DBusAuth *auth)
{
  _dbus_assert (DBUS_AUTH_IS_CLIENT (auth));

  if (auth->state == &common_state_authenticated)
    return _dbus_string_get_const_data (& DBUS_AUTH_CLIENT (auth)->guid_from_server);
  else
    return NULL;
}


handle_client_initial_response_anonymous_mech (DBusAuth         *auth,
                                               DBusString       *response)
{
  /* Our initial response is a "trace" string which must be valid UTF-8
   * and must be an email address if it contains '@'.
   * We just send the dbus implementation info, like a user-agent or
   * something, because... why not. There's nothing guaranteed here
   * though, we could change it later.
   */
  DBusString plaintext;

  if (!_dbus_string_init (&plaintext))
    return FALSE;

  if (!_dbus_string_append (&plaintext,
                            "libdbus " DBUS_VERSION_STRING))
    goto failed;

  if (!_dbus_string_hex_encode (&plaintext, 0,
				response,
				_dbus_string_get_length (response)))
    goto failed;

  _dbus_string_free (&plaintext);

  return TRUE;

 failed:
  _dbus_string_free (&plaintext);
  return FALSE;
}


send_auth (DBusAuth *auth, const DBusAuthMechanismHandler *mech)
{
  DBusString auth_command;

  if (!_dbus_string_init (&auth_command))
    return FALSE;

  if (!_dbus_string_append (&auth_command,
                            "AUTH "))
    {
      _dbus_string_free (&auth_command);
      return FALSE;
    }

  if (!_dbus_string_append (&auth_command,
                            mech->mechanism))
    {
      _dbus_string_free (&auth_command);
      return FALSE;
    }

  if (mech->client_initial_response_func != NULL)
    {
      if (!_dbus_string_append (&auth_command, " "))
        {
          _dbus_string_free (&auth_command);
          return FALSE;
        }

      if (!(* mech->client_initial_response_func) (auth, &auth_command))
        {
          _dbus_string_free (&auth_command);
          return FALSE;
        }
    }

  if (!_dbus_string_append (&auth_command,
                            "\r\n"))
    {
      _dbus_string_free (&auth_command);
      return FALSE;
    }

  if (!_dbus_string_copy (&auth_command, 0,
                          &auth->outgoing,
                          _dbus_string_get_length (&auth->outgoing)))
    {
      _dbus_string_free (&auth_command);
      return FALSE;
    }

  _dbus_string_free (&auth_command);
  shutdown_mech (auth);
  auth->mech = mech;
  goto_state (auth, &client_state_waiting_for_data);

  return TRUE;
}


_dbus_auth_delete_unused_bytes (DBusAuth *auth)
{
  if (!DBUS_AUTH_IN_END_STATE (auth))
    return;

  _dbus_string_set_length (&auth->incoming, 0);
}


static int realloc_context_ids(mm_context_t *ctx)
{
	int i, id;

	/*
	 * id 0 (aka. ctx->id) is special, we always allocate a new one, even if
	 * there wasn't one allocated previously (which happens in the exec
	 * case where ctx is newly allocated).
	 *
	 * We have to be a bit careful here. We must keep the existing ids in
	 * the array, so that we can test if they're non-zero to decide if we
	 * need to allocate a new one. However in case of error we must free the
	 * ids we've allocated but *not* any of the existing ones (or risk a
	 * UAF). That's why we decrement i at the start of the error handling
	 * loop, to skip the id that we just tested but couldn't reallocate.
	 */
	for (i = 0; i < ARRAY_SIZE(ctx->extended_id); i++) {
		if (i == 0 || ctx->extended_id[i]) {
			id = hash__alloc_context_id();
			if (id < 0)
				goto error;

			ctx->extended_id[i] = id;
		}
	}

	/* The caller expects us to return id */
	return ctx->id;

error:
	for (i--; i >= 0; i--) {
		if (ctx->extended_id[i])
			ida_free(&mmu_context_ida, ctx->extended_id[i]);
	}

	return id;
}


int mdiobus_register_device(struct mdio_device *mdiodev)
{
	int err;

	if (mdiodev->bus->mdio_map[mdiodev->addr])
		return -EBUSY;

	if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) {
		err = mdiobus_register_gpiod(mdiodev);
		if (err)
			return err;
	}

	mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;

	return 0;
}


int mdiobus_unregister_device(struct mdio_device *mdiodev)
{
	if (mdiodev->bus->mdio_map[mdiodev->addr] != mdiodev)
		return -EINVAL;

	mdiodev->bus->mdio_map[mdiodev->addr] = NULL;

	return 0;
}


static int mdiobus_register_gpiod(struct mdio_device *mdiodev)
{
	struct gpio_desc *gpiod = NULL;

	/* Deassert the optional reset signal */
	if (mdiodev->dev.of_node)
		gpiod = fwnode_get_named_gpiod(&mdiodev->dev.of_node->fwnode,
					       "reset-gpios", 0, GPIOD_OUT_LOW,
					       "PHY reset");
	if (PTR_ERR(gpiod) == -ENOENT ||
	    PTR_ERR(gpiod) == -ENOSYS)
		gpiod = NULL;
	else if (IS_ERR(gpiod))
		return PTR_ERR(gpiod);

	mdiodev->reset = gpiod;

	/* Assert the reset signal again */
	mdio_device_reset(mdiodev, 1);

	return 0;
}


static void local_cleanup(struct nfc_llcp_local *local)
{
	nfc_llcp_socket_release(local, false, ENXIO);
	del_timer_sync(&local->link_timer);
	skb_queue_purge(&local->tx_queue);
	cancel_work_sync(&local->tx_work);
	cancel_work_sync(&local->rx_work);
	cancel_work_sync(&local->timeout_work);
	kfree_skb(local->rx_pending);
	del_timer_sync(&local->sdreq_timer);
	cancel_work_sync(&local->sdreq_timeout_work);
	nfc_llcp_free_sdp_tlv_list(&local->pending_sdreqs);
}


static char *find_include(const char *prefix, const char *file) {
	char *pfx = NULL, *ret = NULL, *env = r_sys_getenv (R_EGG_INCDIR_ENV);
	if (!prefix) {
		prefix = "";
	}
	if (*prefix == '$') {
		char *out = r_sys_getenv (prefix + 1);
		pfx = out? out: strdup ("");
	} else {
		pfx = strdup (prefix);
		if (!pfx) {
			free (env);
			return NULL;
		}
	}

	if (env) {
		char *str, *ptr = strchr (env, ':');
		// eprintf ("MUST FIND IN PATH (%s)\n", env);
		str = env;
		while (str) {
			if (ptr) {
				*ptr = 0;
			}
			free (ret);
			ret = r_str_appendf (NULL, "%s/%s", pfx, file);
			{
				char *filepath = r_str_appendf (NULL, "%s/%s/%s", str, pfx, file);
				// eprintf ("try (%s)\n", filepath);
				if (r_file_exists (filepath)) {
					free (env);
					free (pfx);
					free (ret);
					return filepath;
				}
				free (filepath);
			}
			if (!ptr) {
				break;
			}
			str = ptr + 1;
			ptr = strchr (str, ':');
		}
		free (env);
	} else {
		ret = r_str_appendf (NULL, "%s/%s", pfx, file);
	}
	free (pfx);
	return ret;
}


R_API char *r_egg_mkvar(REgg *egg, char *out, const char *_str, int delta) {
	int i, len, qi;
	char *oldstr = NULL, *str = NULL, foo[32], *q, *ret = NULL;

	delta += egg->lang.stackfixed;	// XXX can be problematic
	if (!_str) {
		return NULL;	/* fix segfault, but not badparsing */
	}
	/* XXX memory leak */
	ret = str = oldstr = strdup (skipspaces (_str));
	// if (num || str[0]=='0') { sprintf(out, "$%d", num); ret = out; }
	if ((q = strchr (str, ':'))) {
		*q = '\0';
		qi = atoi (q + 1);
		egg->lang.varsize = (qi == 1)? 'b': 'l';
	} else {
		egg->lang.varsize = 'l';
	}
	if (*str == '*' || *str == '&') {
		egg->lang.varxs = *str;
		str++;
	} else {
		egg->lang.varxs = 0;
	}
	if (str[0] == '.') {
		REggEmit *e = egg->remit;
		if (!strncmp (str + 1, "ret", 3)) {
			strcpy (out, e->retvar);
		} else if (!strncmp (str + 1, "fix", 3)) {
			int idx = (int)r_num_math (NULL, str + 4) + delta + e->size;
			e->get_var (egg, 0, out, idx - egg->lang.stackfixed);
			// sprintf(out, "%d(%%"R_BP")", -(atoi(str+4)+delta+R_SZ-egg->lang.stackfixed));
		} else if (!strncmp (str + 1, "var", 3)) {
			int idx = (int)r_num_math (NULL, str + 4) + delta + e->size;
			e->get_var (egg, 0, out, idx);
			// sprintf(out, "%d(%%"R_BP")", -(atoi(str+4)+delta+R_SZ));
		} else if (!strncmp (str + 1, "rarg", 4)) {
			if (e->get_ar) {
				int idx = (int)r_num_math (NULL, str + 5);
				e->get_ar (egg, out, idx);
			}
		} else if (!strncmp (str + 1, "arg", 3)) {
			if (str[4]) {
				if (egg->lang.stackframe == 0) {
					e->get_var (egg, 1, out, 4);	// idx-4);
				} else {
					int idx = (int)r_num_math (NULL, str + 4) + delta + e->size;
					e->get_var (egg, 2, out, idx + 4);
				}
			} else {
				/* TODO: return size of syscall */
				if (egg->lang.callname) {
					for (i = 0; i < egg->lang.nsyscalls; i++) {
						if (!strcmp (egg->lang.syscalls[i].name, egg->lang.callname)) {
							free (oldstr);
							return strdup (egg->lang.syscalls[i].arg);
						}
					}
					eprintf ("Unknown arg for syscall '%s'\n", egg->lang.callname);
				} else {
					eprintf ("NO CALLNAME '%s'\n", egg->lang.callname);
				}
			}
		} else if (!strncmp (str + 1, "reg", 3)) {
			// XXX: can overflow if out is small
			if (egg->lang.attsyntax) {
				snprintf (out, 32, "%%%s", e->regs (egg, atoi (str + 4)));
			} else {
				snprintf (out, 32, "%s", e->regs (egg, atoi (str + 4)));
			}
		} else {
			out = str;	/* TODO: show error, invalid var name? */
			eprintf ("Something is really wrong\n");
		}
		ret = strdup (out);
		free (oldstr);
	} else if (*str == '"' || *str == '\'') {
		int mustfilter = *str == '"';
		/* TODO: check for room in egg->lang.stackfixed area */
		str++;
		len = strlen (str) - 1;
		if (!egg->lang.stackfixed || egg->lang.stackfixed < len) {
			eprintf ("WARNING: No room in the static stackframe! (%d must be %d)\n",
				egg->lang.stackfixed, len);
		}
		str[len] = '\0';
		snprintf (foo, sizeof (foo) - 1, ".fix%d", egg->lang.nargs * 16);	/* XXX FIX DELTA !!!1 */
		free (egg->lang.dstvar);
		egg->lang.dstvar = strdup (skipspaces (foo));
		rcc_pushstr (egg, str, mustfilter);
		ret = r_egg_mkvar (egg, out, foo, 0);
		free (oldstr);
	}
	return ret;
}


R_API void r_egg_load(REgg *egg, const char *code, int format) {
	switch (format) {
	case 'a': // assembly
		r_buf_append_bytes (egg->buf, (const ut8 *)code, strlen (code));
		break;
	default:
		r_buf_append_bytes (egg->src, (const ut8 *)code, strlen (code));
		break;
	}
}


R_API int r_egg_patch(REgg *egg, int off, const ut8 *buf, int len) {
	struct egg_patch_t *ep = R_NEW (struct egg_patch_t);
	if (!ep) {
		return false;
	}
	ep->b = r_buf_new_with_bytes (buf, len);
	if (!ep->b) {
		egg_patch_free (ep);
		return false;
	}
	ep->off = off;
	r_list_append (egg->patches, ep);
	return true;
}


R_API void r_egg_math(REgg *egg) { //, char eq, const char *vs, char type, const char *sr
	// TODO
	//e->mathop (egg, op, type, eq, p);
}


R_API int r_egg_compile(REgg *egg) {
	r_buf_seek (egg->src, 0, R_BUF_SET);
	char b;
	int r = r_buf_read (egg->src, (ut8 *)&b, sizeof (b));
	if (r != sizeof (b) || !egg->remit) {
		return true;
	}
	// only emit begin if code is found
	r_egg_lang_init (egg);
	for (; b; ) {
		r_egg_lang_parsechar (egg, b);
		if (egg->lang.elem_n >= sizeof (egg->lang.elem)) {
			eprintf ("ERROR: elem too large.\n");
			break;
		}
		int r = r_buf_read (egg->src, (ut8 *)&b, sizeof (b));
		if (r != sizeof (b)) {
			break;
		}
		// XXX: some parse fail errors are false positives :(
	}
	if (egg->context>0) {
		eprintf ("ERROR: expected '}' at the end of the file. %d left\n", egg->context);
		return false;
	}
	// TODO: handle errors here
	return true;
}


moveToFreeBindingList(XML_Parser parser, BINDING *bindings)
{
  while (bindings) {
    BINDING *b = bindings;
    bindings = bindings->nextTagBinding;
    b->nextTagBinding = parser->m_freeBindingList;
    parser->m_freeBindingList = b;
  }
}


XML_SetProcessingInstructionHandler(XML_Parser parser,
                                    XML_ProcessingInstructionHandler handler)
{
  if (parser != NULL)
    parser->m_processingInstructionHandler = handler;
}


XML_ParserReset(XML_Parser parser, const XML_Char *encodingName)
{
  TAG *tStk;
  OPEN_INTERNAL_ENTITY *openEntityList;

  if (parser == NULL)
      return XML_FALSE;

  if (parser->m_parentParser)
    return XML_FALSE;
  /* move m_tagStack to m_freeTagList */
  tStk = parser->m_tagStack;
  while (tStk) {
    TAG *tag = tStk;
    tStk = tStk->parent;
    tag->parent = parser->m_freeTagList;
    moveToFreeBindingList(parser, tag->bindings);
    tag->bindings = NULL;
    parser->m_freeTagList = tag;
  }
  /* move m_openInternalEntities to m_freeInternalEntities */
  openEntityList = parser->m_openInternalEntities;
  while (openEntityList) {
    OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
    openEntityList = openEntity->next;
    openEntity->next = parser->m_freeInternalEntities;
    parser->m_freeInternalEntities = openEntity;
  }
  moveToFreeBindingList(parser, parser->m_inheritedBindings);
  FREE(parser, parser->m_unknownEncodingMem);
  if (parser->m_unknownEncodingRelease)
    parser->m_unknownEncodingRelease(parser->m_unknownEncodingData);
  poolClear(&parser->m_tempPool);
  poolClear(&parser->m_temp2Pool);
  FREE(parser, (void *)parser->m_protocolEncodingName);
  parser->m_protocolEncodingName = NULL;
  parserInit(parser, encodingName);
  dtdReset(parser->m_dtd, &parser->m_mem);
  return XML_TRUE;
}


ignoreSectionProcessor(XML_Parser parser,
                       const char *start,
                       const char *end,
                       const char **endPtr)
{
  enum XML_Error result = doIgnoreSection(parser, parser->m_encoding, &start, end,
                                          endPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
  if (result != XML_ERROR_NONE)
    return result;
  if (start) {
    parser->m_processor = prologProcessor;
    return prologProcessor(parser, start, end, endPtr);
  }
  return result;
}


XML_SetEndNamespaceDeclHandler(XML_Parser parser,
                               XML_EndNamespaceDeclHandler end) {
  if (parser != NULL)
    parser->m_endNamespaceDeclHandler = end;
}


XML_GetSpecifiedAttributeCount(XML_Parser parser)
{
  if (parser == NULL)
    return -1;
  return parser->m_nSpecifiedAtts;
}


XML_SetParamEntityParsing(XML_Parser parser,
                          enum XML_ParamEntityParsing peParsing)
{
  if (parser == NULL)
    return 0;
  /* block after XML_Parse()/XML_ParseBuffer() has been called */
  if (parser->m_parsingStatus.parsing == XML_PARSING || parser->m_parsingStatus.parsing == XML_SUSPENDED)
    return 0;
#ifdef XML_DTD
  parser->m_paramEntityParsing = peParsing;
  return 1;
#else
  return peParsing == XML_PARAM_ENTITY_PARSING_NEVER;
#endif
}


keylen(KEY s)
{
  size_t len = 0;
  for (; *s; s++, len++);
  return len;
}


poolGrow(STRING_POOL *pool)
{
  if (pool->freeBlocks) {
    if (pool->start == 0) {
      pool->blocks = pool->freeBlocks;
      pool->freeBlocks = pool->freeBlocks->next;
      pool->blocks->next = NULL;
      pool->start = pool->blocks->s;
      pool->end = pool->start + pool->blocks->size;
      pool->ptr = pool->start;
      return XML_TRUE;
    }
    if (pool->end - pool->start < pool->freeBlocks->size) {
      BLOCK *tem = pool->freeBlocks->next;
      pool->freeBlocks->next = pool->blocks;
      pool->blocks = pool->freeBlocks;
      pool->freeBlocks = tem;
      memcpy(pool->blocks->s, pool->start,
             (pool->end - pool->start) * sizeof(XML_Char));
      pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
      pool->start = pool->blocks->s;
      pool->end = pool->start + pool->blocks->size;
      return XML_TRUE;
    }
  }
  if (pool->blocks && pool->start == pool->blocks->s) {
    BLOCK *temp;
    int blockSize = (int)((unsigned)(pool->end - pool->start)*2U);
    size_t bytesToAllocate;

    /* NOTE: Needs to be calculated prior to calling `realloc`
             to avoid dangling pointers: */
    const ptrdiff_t offsetInsideBlock = pool->ptr - pool->start;

    if (blockSize < 0) {
      /* This condition traps a situation where either more than
       * INT_MAX/2 bytes have already been allocated.  This isn't
       * readily testable, since it is unlikely that an average
       * machine will have that much memory, so we exclude it from the
       * coverage statistics.
       */
      return XML_FALSE; /* LCOV_EXCL_LINE */
    }

    bytesToAllocate = poolBytesToAllocateFor(blockSize);
    if (bytesToAllocate == 0)
      return XML_FALSE;

    temp = (BLOCK *)
      pool->mem->realloc_fcn(pool->blocks, (unsigned)bytesToAllocate);
    if (temp == NULL)
      return XML_FALSE;
    pool->blocks = temp;
    pool->blocks->size = blockSize;
    pool->ptr = pool->blocks->s + offsetInsideBlock;
    pool->start = pool->blocks->s;
    pool->end = pool->start + blockSize;
  }
  else {
    BLOCK *tem;
    int blockSize = (int)(pool->end - pool->start);
    size_t bytesToAllocate;

    if (blockSize < 0) {
      /* This condition traps a situation where either more than
       * INT_MAX bytes have already been allocated (which is prevented
       * by various pieces of program logic, not least this one, never
       * mind the unlikelihood of actually having that much memory) or
       * the pool control fields have been corrupted (which could
       * conceivably happen in an extremely buggy user handler
       * function).  Either way it isn't readily testable, so we
       * exclude it from the coverage statistics.
       */
      return XML_FALSE;  /* LCOV_EXCL_LINE */
    }

    if (blockSize < INIT_BLOCK_SIZE)
      blockSize = INIT_BLOCK_SIZE;
    else {
      /* Detect overflow, avoiding _signed_ overflow undefined behavior */
      if ((int)((unsigned)blockSize * 2U) < 0) {
        return XML_FALSE;
      }
      blockSize *= 2;
    }

    bytesToAllocate = poolBytesToAllocateFor(blockSize);
    if (bytesToAllocate == 0)
      return XML_FALSE;

    tem = (BLOCK *)pool->mem->malloc_fcn(bytesToAllocate);
    if (!tem)
      return XML_FALSE;
    tem->size = blockSize;
    tem->next = pool->blocks;
    pool->blocks = tem;
    if (pool->ptr != pool->start)
      memcpy(tem->s, pool->start,
             (pool->ptr - pool->start) * sizeof(XML_Char));
    pool->ptr = tem->s + (pool->ptr - pool->start);
    pool->start = tem->s;
    pool->end = tem->s + blockSize;
  }
  return XML_TRUE;
}


reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
                            const char *start, const char *end)
{
  const XML_Char *target;
  XML_Char *data;
  const char *tem;
  if (!parser->m_processingInstructionHandler) {
    if (parser->m_defaultHandler)
      reportDefault(parser, enc, start, end);
    return 1;
  }
  start += enc->minBytesPerChar * 2;
  tem = start + XmlNameLength(enc, start);
  target = poolStoreString(&parser->m_tempPool, enc, start, tem);
  if (!target)
    return 0;
  poolFinish(&parser->m_tempPool);
  data = poolStoreString(&parser->m_tempPool, enc,
                        XmlSkipS(enc, tem),
                        end - enc->minBytesPerChar*2);
  if (!data)
    return 0;
  normalizeLines(data);
  parser->m_processingInstructionHandler(parser->m_handlerArg, target, data);
  poolClear(&parser->m_tempPool);
  return 1;
}


XML_SetEntityDeclHandler(XML_Parser parser,
                         XML_EntityDeclHandler handler)
{
  if (parser != NULL)
    parser->m_entityDeclHandler = handler;
}


cdataSectionProcessor(XML_Parser parser,
                      const char *start,
                      const char *end,
                      const char **endPtr)
{
  enum XML_Error result = doCdataSection(parser, parser->m_encoding, &start, end,
                                         endPtr, (XML_Bool)!parser->m_parsingStatus.finalBuffer);
  if (result != XML_ERROR_NONE)
    return result;
  if (start) {
    if (parser->m_parentParser) {  /* we are parsing an external entity */
      parser->m_processor = externalEntityContentProcessor;
      return externalEntityContentProcessor(parser, start, end, endPtr);
    }
    else {
      parser->m_processor = contentProcessor;
      return contentProcessor(parser, start, end, endPtr);
    }
  }
  return result;
}


static php_stream_filter_status_t php_iconv_stream_filter_do_filter(
		php_stream *stream, php_stream_filter *filter,
		php_stream_bucket_brigade *buckets_in,
		php_stream_bucket_brigade *buckets_out,
		size_t *bytes_consumed, int flags)
{
	php_stream_bucket *bucket = NULL;
	size_t consumed = 0;
	php_iconv_stream_filter *self = (php_iconv_stream_filter *)Z_PTR(filter->abstract);

	while (buckets_in->head != NULL) {
		bucket = buckets_in->head;

		php_stream_bucket_unlink(bucket);

		if (php_iconv_stream_filter_append_bucket(self, stream, filter,
				buckets_out, bucket->buf, bucket->buflen, &consumed,
				php_stream_is_persistent(stream)) != SUCCESS) {
			goto out_failure;
		}

		php_stream_bucket_delref(bucket);
	}

	if (flags != PSFS_FLAG_NORMAL) {
		if (php_iconv_stream_filter_append_bucket(self, stream, filter,
				buckets_out, NULL, 0, &consumed,
				php_stream_is_persistent(stream)) != SUCCESS) {
			goto out_failure;
		}
	}

	if (bytes_consumed != NULL) {
		*bytes_consumed = consumed;
	}

	return PSFS_PASS_ON;

out_failure:
	if (bucket != NULL) {
		php_stream_bucket_delref(bucket);
	}
	return PSFS_ERR_FATAL;
}