static void sched_deliver_local(const char *recipient,
				struct caldav_sched_param *sparam,
				struct sched_data *sched_data,
				struct auth_state *authstate)
{
    int r = 0, rights, reqd_privs, deliver_inbox = 1;
    const char *userid = sparam->userid, *attendee = NULL;
    static struct buf resource = BUF_INITIALIZER;
    static unsigned sched_count = 0;
    const char *mailboxname = NULL;
    mbentry_t *mbentry = NULL;
    struct mailbox *mailbox = NULL, *inbox = NULL;
    struct caldav_db *caldavdb = NULL;
    struct caldav_data *cdata;
    icalcomponent *ical = NULL;
    icalproperty_method method;
    icalcomponent_kind kind;
    icalcomponent *comp;
    icalproperty *prop;
    struct transaction_t txn;

    /* Check ACL of sender on recipient's Scheduling Inbox */
    mailboxname = caldav_mboxname(userid, SCHED_INBOX);
    r = mboxlist_lookup(mailboxname, &mbentry, NULL);
    if (r) {
	syslog(LOG_INFO, "mboxlist_lookup(%s) failed: %s",
	       mailboxname, error_message(r));
	sched_data->status =
	    sched_data->ischedule ? REQSTAT_REJECTED : SCHEDSTAT_REJECTED;
	goto done;
    }

    rights = cyrus_acl_myrights(authstate, mbentry->acl);
    mboxlist_entry_free(&mbentry);

    reqd_privs = sched_data->is_reply ? DACL_REPLY : DACL_INVITE;
    if (!(rights & reqd_privs)) {
	sched_data->status =
	    sched_data->ischedule ? REQSTAT_NOPRIVS : SCHEDSTAT_NOPRIVS;
	syslog(LOG_DEBUG, "No scheduling receive ACL for user %s on Inbox %s",
	       httpd_userid, userid);
	goto done;
    }

    /* Open recipient's Inbox for reading */
    if ((r = mailbox_open_irl(mailboxname, &inbox))) {
	syslog(LOG_ERR, "mailbox_open_irl(%s) failed: %s",
	       mailboxname, error_message(r));
	sched_data->status =
	    sched_data->ischedule ? REQSTAT_TEMPFAIL : SCHEDSTAT_TEMPFAIL;
	goto done;
    }

    /* Get METHOD of the iTIP message */
    method = icalcomponent_get_method(sched_data->itip);

    /* Search for iCal UID in recipient's calendars */
    caldavdb = caldav_open_userid(userid, CALDAV_CREATE);
    if (!caldavdb) {
	sched_data->status =
	    sched_data->ischedule ? REQSTAT_TEMPFAIL : SCHEDSTAT_TEMPFAIL;
	goto done;
    }

    caldav_lookup_uid(caldavdb,
		      icalcomponent_get_uid(sched_data->itip), 0, &cdata);

    if (cdata->dav.mailbox) {
	mailboxname = cdata->dav.mailbox;
	buf_setcstr(&resource, cdata->dav.resource);
    }
    else if (sched_data->is_reply) {
	/* Can't find object belonging to organizer - ignore reply */
	sched_data->status =
	    sched_data->ischedule ? REQSTAT_PERMFAIL : SCHEDSTAT_PERMFAIL;
	goto done;
    }
    else if (method == ICAL_METHOD_CANCEL || method == ICAL_METHOD_POLLSTATUS) {
	/* Can't find object belonging to attendee - we're done */
	sched_data->status =
	    sched_data->ischedule ? REQSTAT_SUCCESS : SCHEDSTAT_DELIVERED;
	goto done;
    }
    else {
	/* Can't find object belonging to attendee - use default calendar */
	mailboxname = caldav_mboxname(userid, SCHED_DEFAULT);
	buf_reset(&resource);
	buf_printf(&resource, "%s.ics",
		   icalcomponent_get_uid(sched_data->itip));

	/* Create new attendee object */
	ical = icalcomponent_vanew(ICAL_VCALENDAR_COMPONENT, 0);

	/* Copy over VERSION property */
	prop = icalcomponent_get_first_property(sched_data->itip,
						ICAL_VERSION_PROPERTY);
	icalcomponent_add_property(ical, icalproperty_new_clone(prop));

	/* Copy over PRODID property */
	prop = icalcomponent_get_first_property(sched_data->itip,
						ICAL_PRODID_PROPERTY);
	icalcomponent_add_property(ical, icalproperty_new_clone(prop));

	/* Copy over any CALSCALE property */
	prop = icalcomponent_get_first_property(sched_data->itip,
						ICAL_CALSCALE_PROPERTY);
	if (prop) {
	    icalcomponent_add_property(ical,
				       icalproperty_new_clone(prop));
	}
    }

    /* Open recipient's calendar for reading */
    r = mailbox_open_irl(mailboxname, &mailbox);
    if (r) {
	syslog(LOG_ERR, "mailbox_open_irl(%s) failed: %s",
	       mailboxname, error_message(r));
	sched_data->status =
	    sched_data->ischedule ? REQSTAT_TEMPFAIL : SCHEDSTAT_TEMPFAIL;
	goto done;
    }

    if (cdata->dav.imap_uid) {
	struct index_record record;

	/* Load message containing the resource and parse iCal data */
	r = mailbox_find_index_record(mailbox, cdata->dav.imap_uid, &record);
	ical = record_to_ical(mailbox, &record);

	for (comp = icalcomponent_get_first_component(sched_data->itip,
						      ICAL_ANY_COMPONENT);
	     comp;
	     comp = icalcomponent_get_next_component(sched_data->itip,
						     ICAL_ANY_COMPONENT)) {
	    /* Don't allow component type to be changed */
	    int reject = 0;
	    kind = icalcomponent_isa(comp);
	    switch (kind) {
	    case ICAL_VEVENT_COMPONENT:
		if (cdata->comp_type != CAL_COMP_VEVENT) reject = 1;
		break;
	    case ICAL_VTODO_COMPONENT:
		if (cdata->comp_type != CAL_COMP_VTODO) reject = 1;
		break;
	    case ICAL_VJOURNAL_COMPONENT:
		if (cdata->comp_type != CAL_COMP_VJOURNAL) reject = 1;
		break;
	    case ICAL_VFREEBUSY_COMPONENT:
		if (cdata->comp_type != CAL_COMP_VFREEBUSY) reject = 1;
		break;
	    case ICAL_VAVAILABILITY_COMPONENT:
		if (cdata->comp_type != CAL_COMP_VAVAILABILITY) reject = 1;
		break;
#ifdef HAVE_VPOLL
	    case ICAL_VPOLL_COMPONENT:
		if (cdata->comp_type != CAL_COMP_VPOLL) reject = 1;
		break;
#endif
	    default:
		break;
	    }

	    /* Don't allow ORGANIZER to be changed */
	    if (!reject && cdata->organizer) {
		prop =
		    icalcomponent_get_first_property(comp,
						     ICAL_ORGANIZER_PROPERTY);
		if (prop) {
		    const char *organizer =
			organizer = icalproperty_get_organizer(prop);

		    if (!strncasecmp(organizer, "mailto:", 7)) organizer += 7;
		    if (strcmp(cdata->organizer, organizer)) reject = 1;
		}
	    }

	    if (reject) {
		sched_data->status = sched_data->ischedule ?
		    REQSTAT_REJECTED : SCHEDSTAT_REJECTED;
		goto done;
	    }
	}
    }

    switch (method) {
    case ICAL_METHOD_CANCEL:
	/* Get component type */
	comp = icalcomponent_get_first_real_component(ical);
	kind = icalcomponent_isa(comp);

	/* Set STATUS:CANCELLED on all components */
	do {
	    icalcomponent_set_status(comp, ICAL_STATUS_CANCELLED);
	    icalcomponent_set_sequence(comp,
				       icalcomponent_get_sequence(comp)+1);
	} while ((comp = icalcomponent_get_next_component(ical, kind)));

	break;

    case ICAL_METHOD_REPLY:
	attendee = deliver_merge_reply(ical, sched_data->itip);

	break;

    case ICAL_METHOD_REQUEST:
	deliver_inbox = deliver_merge_request(recipient,
					      ical, sched_data->itip);
	break;

    case ICAL_METHOD_POLLSTATUS:
	deliver_inbox = deliver_merge_pollstatus(ical, sched_data->itip);
	break;

    default:
	/* Unknown METHOD -- ignore it */
	syslog(LOG_ERR, "Unknown iTIP method: %s",
	       icalenum_method_to_string(method));

	sched_data->is_reply = 0;
	goto inbox;
    }

    /* Store the (updated) object in the recipients's calendar */
    mailbox_unlock_index(mailbox, NULL);

    r = store_resource(&txn, ical, mailbox, buf_cstring(&resource),
		       caldavdb, OVERWRITE_YES, NEW_STAG);

    if (r == HTTP_CREATED || r == HTTP_NO_CONTENT) {
	sched_data->status =
	    sched_data->ischedule ? REQSTAT_SUCCESS : SCHEDSTAT_DELIVERED;
    }
    else {
	syslog(LOG_ERR, "store_resource(%s) failed: %s (%s)",
	       mailbox->name, error_message(r), txn.error.resource);
	sched_data->status =
	    sched_data->ischedule ? REQSTAT_TEMPFAIL : SCHEDSTAT_TEMPFAIL;
	goto done;
    }

  inbox:
    if (deliver_inbox) {
	/* Create a name for the new iTIP message resource */
	buf_reset(&resource);
	buf_printf(&resource, "%x-%d-%ld-%u.ics",
		   strhash(icalcomponent_get_uid(sched_data->itip)), getpid(),
		   time(0), sched_count++);

	/* Store the message in the recipient's Inbox */
	mailbox_unlock_index(inbox, NULL);

	r = store_resource(&txn, sched_data->itip, inbox,
			   buf_cstring(&resource), caldavdb, OVERWRITE_NO, 0);
	/* XXX  What do we do if storing to Inbox fails? */
    }

    /* XXX  Should this be a config option? - it might have perf implications */
    if (sched_data->is_reply) {
	/* Send updates to attendees - skipping sender of reply */
	comp = icalcomponent_get_first_real_component(ical);
	if (icalcomponent_isa(comp) == ICAL_VPOLL_COMPONENT)
	    sched_pollstatus(recipient, sparam, ical, attendee);
	else
	    sched_request(recipient, sparam, NULL, ical, attendee);
    }

  done:
    if (ical) icalcomponent_free(ical);
    mailbox_close(&inbox);
    mailbox_close(&mailbox);
    caldav_data_fini(cdata);
    if (caldavdb) caldav_close(caldavdb);
}