static dav_error *dav_acl_patch_validate(const dav_resource * resource, const apr_xml_elem * elem, int operation, void **context, int *defer_to_dead) { apr_pool_t *pool = resource->pool; dav_repos_resource *db_r = resource->info->db_r; dav_repos_db *db = resource->info->db; request_rec *rec = resource->info->rec; dav_elem_private *priv = elem->priv; dav_error *err = NULL; TRACE(); if (priv->propid == DAV_PROPID_group_member_set) { *defer_to_dead = 0; if (db_r->resourcetype!=dav_repos_GROUP || operation!=DAV_PROP_OP_SET) return dav_new_error(pool, HTTP_FORBIDDEN, 0, "Not a set operation on a group"); apr_xml_elem *href_elem = dav_find_child(elem, "href"); apr_hash_t *new_members = apr_hash_make(pool); apr_array_header_t *to_remove = NULL; while (href_elem && !err) { const char *prin_uri = dav_xml_get_cdata(href_elem, pool, 1); const char *prin_name = get_name_from_principal_URL(rec, prin_uri); if (prin_name == NULL) err = dav_new_error (pool, HTTP_CONFLICT, 0, "Not a DAV:principal-URL"); else apr_hash_set(new_members, prin_name, APR_HASH_KEY_STRING, ""); href_elem = href_elem->next; } if (err) return err; if (apr_hash_count(new_members)) err = dbms_calculate_group_changes(db, db_r, new_members,&to_remove); else err = dbms_get_group_members(db, db_r, &to_remove); if (err) return err; apr_hash_set(new_members, "-to-remove-", APR_HASH_KEY_STRING, to_remove); *context = new_members; } return err; }
DAV_DECLARE(dav_get_props_result) dav_get_props(dav_propdb *propdb, apr_xml_doc *doc) { const dav_hooks_db *db_hooks = propdb->db_hooks; apr_xml_elem *elem = dav_find_child(doc->root, "prop"); apr_text_header hdr_good = { 0 }; apr_text_header hdr_bad = { 0 }; apr_text_header hdr_ns = { 0 }; int have_good = 0; dav_get_props_result result = { 0 }; char *marks_liveprop; dav_xmlns_info *xi; int xi_filled = 0; /* ### NOTE: we should pass in TWO buffers -- one for keys, one for the marks */ /* we will ALWAYS provide a "good" result, even if it is EMPTY */ apr_text_append(propdb->p, &hdr_good, "<D:propstat>" DEBUG_CR "<D:prop>" DEBUG_CR); /* ### the marks should be in a buffer! */ /* allocate zeroed-memory for the marks. These marks indicate which liveprop namespaces we've generated into the output xmlns buffer */ /* same for the liveprops */ marks_liveprop = apr_pcalloc(propdb->p, dav_get_liveprop_ns_count() + 1); xi = dav_xmlns_create(propdb->p); for (elem = elem->first_child; elem; elem = elem->next) { dav_elem_private *priv; dav_error *err; dav_prop_insert inserted; dav_prop_name name; /* ** First try live property providers; if they don't handle ** the property, then try looking it up in the propdb. */ if (elem->priv == NULL) { elem->priv = apr_pcalloc(propdb->p, sizeof(*priv)); } priv = elem->priv; /* cache the propid; dav_get_props() could be called many times */ if (priv->propid == 0) dav_find_liveprop(propdb, elem); if (priv->propid != DAV_PROPID_CORE_UNKNOWN) { /* insert the property. returns 1 if an insertion was done. */ if ((err = dav_insert_liveprop(propdb, elem, DAV_PROP_INSERT_VALUE, &hdr_good, &inserted)) != NULL) { /* ### need to propagate the error to the caller... */ /* ### skip it for now, as if nothing was inserted */ } if (inserted == DAV_PROP_INSERT_VALUE) { have_good = 1; /* ** Add the liveprop's namespace URIs. Note that provider==NULL ** for core properties. */ if (priv->provider != NULL) { const char * const * scan_ns_uri; for (scan_ns_uri = priv->provider->namespace_uris; *scan_ns_uri != NULL; ++scan_ns_uri) { long ns; ns = dav_get_liveprop_ns_index(*scan_ns_uri); if (marks_liveprop[ns]) continue; marks_liveprop[ns] = 1; dav_insert_xmlns(propdb->p, "lp", ns, *scan_ns_uri, &hdr_ns); } } /* property added. move on to the next property. */ continue; } else if (inserted == DAV_PROP_INSERT_NOTDEF) { /* nothing to do. fall thru to allow property to be handled as a dead property */ } #if DAV_DEBUG else { #if 0 /* ### need to change signature to return an error */ return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR, 0, 0, "INTERNAL DESIGN ERROR: insert_liveprop " "did not insert what was asked for."); #endif } #endif } /* The property wasn't a live property, so look in the dead property database. */ /* make sure propdb is really open */ if (propdb->deferred) { /* ### what to do with db open error? */ (void) dav_really_open_db(propdb, 1 /*ro*/); } if (elem->ns == APR_XML_NS_NONE) name.ns = ""; else name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns); name.name = elem->name; /* only bother to look if a database exists */ if (propdb->db != NULL) { int found; if ((err = (*db_hooks->output_value)(propdb->db, &name, xi, &hdr_good, &found)) != NULL) { /* ### what to do? continue doesn't seem right... */ continue; } if (found) { have_good = 1; /* if we haven't added the db's namespaces, then do so... */ if (!xi_filled) { (void) (*db_hooks->define_namespaces)(propdb->db, xi); xi_filled = 1; } continue; } } /* not found as a live OR dead property. add a record to the "bad" propstats */ /* make sure we've started our "bad" propstat */ if (hdr_bad.first == NULL) { apr_text_append(propdb->p, &hdr_bad, "<D:propstat>" DEBUG_CR "<D:prop>" DEBUG_CR); } /* output this property's name (into the bad propstats) */ dav_output_prop_name(propdb->p, &name, xi, &hdr_bad); } apr_text_append(propdb->p, &hdr_good, "</D:prop>" DEBUG_CR "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR "</D:propstat>" DEBUG_CR); /* default to start with the good */ result.propstats = hdr_good.first; /* we may not have any "bad" results */ if (hdr_bad.first != NULL) { /* "close" the bad propstat */ apr_text_append(propdb->p, &hdr_bad, "</D:prop>" DEBUG_CR "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR "</D:propstat>" DEBUG_CR); /* if there are no good props, then just return the bad */ if (!have_good) { result.propstats = hdr_bad.first; } else { /* hook the bad propstat to the end of the good one */ hdr_good.last->next = hdr_bad.first; } } /* add in all the various namespaces, and return them */ dav_xmlns_generate(xi, &hdr_ns); result.xmlns = hdr_ns.first; return result; }
DAV_DECLARE_NONSTD(void) dav_prop_exec(dav_prop_ctx *ctx) { dav_propdb *propdb = ctx->propdb; dav_error *err = NULL; dav_elem_private *priv = ctx->prop->priv; const dav_hooks_acl *acl_hooks = dav_get_acl_hooks(ctx->r); dav_prop_name name; ctx->rollback = apr_pcalloc(propdb->p, sizeof(*ctx->rollback)); if (ctx->prop->ns == APR_XML_NS_NONE) name.ns = ""; else name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, ctx->prop->ns); name.name = ctx->prop->name; if (ctx->is_liveprop) { err = (*priv->provider->patch_exec)(propdb->resource, ctx->prop, ctx->operation, ctx->liveprop_ctx, &ctx->rollback->liveprop); } else { /* save the old value so that we can do a rollback. */ if ((err = (*propdb->db_hooks ->get_rollback)(propdb->db, &name, &ctx->rollback->deadprop)) != NULL) goto error; if (ctx->operation == DAV_PROP_OP_SET) { /* Note: propdb->mapping was set in dav_prop_validate() */ err = (*propdb->db_hooks->store)(propdb->db, &name, ctx->prop, propdb->mapping); /* ** If an error occurred, then assume that we didn't change the ** value. Remove the rollback item so that we don't try to set ** its value during the rollback. */ /* ### euh... where is the removal? */ } else if (ctx->operation == DAV_PROP_OP_DELETE) { /* ** Delete the property. Ignore errors -- the property is there, or ** we are deleting it for a second time. */ /* ### but what about other errors? */ (void) (*propdb->db_hooks->remove)(propdb->db, &name); } } if(acl_hooks) { /* update any DAV:property ACE(s) that depend on this property */ const char *newval = dav_xml_get_cdata(dav_find_child(ctx->prop, "href"), propdb->p, 1); /* @NOTE: works only in presence of transactions, * currently no rollback state being stored for ACE changes */ /* assuming cdata is NULL for D:remove prop elem */ if(newval) (*acl_hooks->update_principal_property_aces)( (dav_resource *)propdb->resource, &name, newval); } error: /* push a more specific error here */ if (err != NULL) { if (err->status == HTTP_CONFLICT) { /* semantics of the value are not appropriate for the property */ ctx->err = err; } else { /* ** Use HTTP_INTERNAL_SERVER_ERROR because we shouldn't have seen ** any other errors at this point. */ ctx->err = dav_push_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR, DAV_ERR_PROP_EXEC, "Could not execute PROPPATCH.", err); } } }
dav_error *dav_repos_deliver_principal_property_search(request_rec * r, const dav_resource * resource, const apr_xml_doc * doc, ap_filter_t * output) { /* this buffers the output for a bit and is automatically flushed, at appropriate times, by the Apache filter system. */ apr_bucket_brigade *bb; apr_pool_t *pool = resource->pool; dav_repos_db *db = resource->info->db; dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r; apr_xml_elem *principal_property_search; apr_xml_elem *elem; apr_xml_elem *prop; apr_xml_elem *props; apr_xml_elem *match; int flag; TRACE(); principal_property_search = dav_find_child(doc->root, "principal-property-search"); props = dav_find_child(doc->root, "prop"); sabridge_get_collection_children(db, db_r, 1, "read", NULL, NULL, NULL); bb = apr_brigade_create(pool, output->c->bucket_alloc); r->status = HTTP_MULTI_STATUS; send_xml(bb, output, "<D:multistatus xmlns:D=\"DAV:\">" DEBUG_CR); while (db_r != NULL) { flag = 1; for (elem = principal_property_search->first_child; elem && flag; elem = elem->next) { if (!strcmp(elem->name, "property-search")) { prop = dav_find_child(elem, "prop"); match = dav_find_child(elem, "match"); dav_repos_build_lpr_hash(db_r); const char *val = apr_hash_get(db_r->lpr_hash, prop->first_child->name, APR_HASH_KEY_STRING); if (!strstr(val, match->first_cdata.first->text)) flag = 0; } } if (flag) { send_xml(bb, output, "<D:response>"); send_xml(bb, output, dav_repos_mk_href(pool, db_r->uri)); send_xml(bb, output, "<D:propstat>"); send_xml(bb, output, "<D:prop>"); for (props = props->first_child; props; props = props->next) { const char *val; val = apr_hash_get(db_r->lpr_hash, props->name, APR_HASH_KEY_STRING); const char *str = apr_psprintf(pool, "<D:%s>%s</D:%s>" DEBUG_CR, props->name, apr_xml_quote_string(pool, val, 0), props->name); send_xml(bb, output, str); } send_xml(bb, output, "</D:prop>"); send_xml(bb, output, "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR); send_xml(bb, output, "</D:propstat>"); send_xml(bb, output, "</D:response>"); } db_r = db_r->next; } send_xml(bb, output, "</D:multistatus>"); /* flush the contents of the brigade */ ap_fflush(output, bb); return NULL; }
dav_error *dav_repos_deliver_principal_match(request_rec * r, const dav_resource * resource, const apr_xml_doc * doc, ap_filter_t * output) { /* this buffers the output for a bit and is automatically flushed, at appropriate times, by the Apache filter system. */ apr_bucket_brigade *bb; apr_pool_t *pool = resource->pool; dav_repos_db *db = resource->info->db; dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r; apr_xml_elem *principal_properties; char *req_username = r->user; long principal_id; dav_error *err = NULL; TRACE(); if (!req_username) req_username = "******"; if((err = dbms_get_principal_id_from_name(pool, db, req_username, &principal_id))) { return err; } principal_properties = dav_find_child(doc->root, "principal-property"); sabridge_get_collection_children(db, db_r, DAV_INFINITY, "read", NULL, NULL, NULL); bb = apr_brigade_create(pool, output->c->bucket_alloc); r->status = HTTP_MULTI_STATUS; send_xml(bb, output, "<D:multistatus xmlns:D=\"DAV:\">" DEBUG_CR); while ((db_r = db_r->next)) { // Currently supporting DAV:owner only if ((principal_properties && !strcmp(principal_properties->name, "owner") && db_r->owner_id == principal_id) || (!principal_properties && // Found no principal_properties !strcmp(db_r->displayname, req_username))) { send_xml(bb, output, "<D:response>"); const char *str = apr_psprintf(pool, "<D:href>%s</D:href>" DEBUG_CR, apr_xml_quote_string(pool, db_r->uri, 0)); send_xml(bb, output, str); send_xml(bb, output, "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR); send_xml(bb, output, "</D:response>"); } } send_xml(bb, output, "</D:multistatus>"); /* flush the contents of the brigade */ ap_fflush(output, bb); return NULL; }
dav_error *dav_repos_deliver_acl_principal_prop_set(request_rec * r, const dav_resource * resource, const apr_xml_doc * doc, ap_filter_t * output) { /* this buffers the output for a bit and is automatically flushed, at appropriate times, by the Apache filter system. */ apr_bucket_brigade *bb; apr_pool_t *pool = resource->pool; dav_repos_db *db = resource->info->db; dav_repos_resource *db_r = (dav_repos_resource *) resource->info->db_r; apr_xml_elem *props; dav_repos_resource *principals = NULL; TRACE(); props = dav_find_child(doc->root, "prop"); dbms_get_principals(db, pool, db_r, principals); bb = apr_brigade_create(pool, output->c->bucket_alloc); r->status = HTTP_MULTI_STATUS; send_xml(bb, output, "<D:multistatus xmlns:D=\"DAV:\">" DEBUG_CR); while (principals != NULL) { sabridge_get_property(db, principals); dav_repos_build_lpr_hash(principals); send_xml(bb, output, "<D:response>"); send_xml(bb, output, dav_repos_mk_href(pool, principals->uri)); send_xml(bb, output, "<D:propstat>"); send_xml(bb, output, "<D:prop>"); for (props = props->first_child; props; props = props->next) { const char *val; val = apr_hash_get(principals->lpr_hash, props->name, APR_HASH_KEY_STRING); const char *str = apr_psprintf(pool, "<D:%s>%s</D:%s>" DEBUG_CR, props->name, apr_xml_quote_string(pool, val, 0), props->name); send_xml(bb, output, str); } send_xml(bb, output, "</D:prop>"); send_xml(bb, output, "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR); send_xml(bb, output, "</D:propstat>"); send_xml(bb, output, "</D:response>"); principals = principals->next; } send_xml(bb, output, "</D:multistatus>"); /* flush the contents of the brigade */ ap_fflush(output, bb); return NULL; }