repos.c 74.9 KB
Newer Older
powelld's avatar
powelld committed
        s = apr_psprintf(p, "<lp%d:%s/>" DEBUG_CR, global_ns, info->name);
    }
    else {
        /* assert: what == DAV_PROP_INSERT_SUPPORTED */
        s = apr_psprintf(p,
                         "<D:supported-live-property D:name=\"%s\" "
                         "D:namespace=\"%s\"/>" DEBUG_CR,
                         info->name, dav_fs_namespace_uris[info->ns]);
    }
    apr_text_append(p, phdr, s);

    /* we inserted what was asked for */
    return what;
}

static int dav_fs_is_writable(const dav_resource *resource, int propid)
{
    const dav_liveprop_spec *info;

#ifdef DAV_FS_HAS_EXECUTABLE
    /* if we have the executable property, and this isn't a collection,
       then the property is writable. */
    if (propid == DAV_PROPID_FS_executable && !resource->collection)
        return 1;
#endif

    (void) dav_get_liveprop_info(propid, &dav_fs_liveprop_group, &info);
    return info->is_writable;
}

static dav_error *dav_fs_patch_validate(const dav_resource *resource,
                                        const apr_xml_elem *elem,
                                        int operation,
                                        void **context,
                                        int *defer_to_dead)
{
    const apr_text *cdata;
    const apr_text *f_cdata;
    char value;
    dav_elem_private *priv = elem->priv;

    if (priv->propid != DAV_PROPID_FS_executable) {
        *defer_to_dead = 1;
        return NULL;
    }

    if (operation == DAV_PROP_OP_DELETE) {
        return dav_new_error(resource->info->pool, HTTP_CONFLICT, 0, 0,
                             "The 'executable' property cannot be removed.");
    }

    cdata = elem->first_cdata.first;

    /* ### hmm. this isn't actually looking at all the possible text items */
    f_cdata = elem->first_child == NULL
        ? NULL
        : elem->first_child->following_cdata.first;

    /* DBG3("name=%s  cdata=%s  f_cdata=%s",elem->name,cdata ? cdata->text : "[null]",f_cdata ? f_cdata->text : "[null]"); */

    if (cdata == NULL) {
        if (f_cdata == NULL) {
            return dav_new_error(resource->info->pool, HTTP_CONFLICT, 0, 0,
                                 "The 'executable' property expects a single "
                                 "character, valued 'T' or 'F'. There was no "
                                 "value submitted.");
        }
        cdata = f_cdata;
    }
    else if (f_cdata != NULL)
        goto too_long;

    if (cdata->next != NULL || strlen(cdata->text) != 1)
        goto too_long;

    value = cdata->text[0];
    if (value != 'T' && value != 'F') {
        return dav_new_error(resource->info->pool, HTTP_CONFLICT, 0, 0,
                             "The 'executable' property expects a single "
                             "character, valued 'T' or 'F'. The value "
                             "submitted is invalid.");
    }

    *context = (void *)((long)(value == 'T'));

    return NULL;

  too_long:
    return dav_new_error(resource->info->pool, HTTP_CONFLICT, 0, 0,
                         "The 'executable' property expects a single "
                         "character, valued 'T' or 'F'. The value submitted "
                         "has too many characters.");

}

static dav_error *dav_fs_patch_exec(const dav_resource *resource,
                                    const apr_xml_elem *elem,
                                    int operation,
                                    void *context,
                                    dav_liveprop_rollback **rollback_ctx)
{
    long value = context != NULL;
    apr_fileperms_t perms = resource->info->finfo.protection;
    apr_status_t status;
    long old_value = (perms & APR_UEXECUTE) != 0;

    /* assert: prop == executable. operation == SET. */

    /* don't do anything if there is no change. no rollback info either. */
    /* DBG2("new value=%d  (old=%d)", value, old_value); */
    if (value == old_value)
        return NULL;

    perms &= ~APR_UEXECUTE;
    if (value)
        perms |= APR_UEXECUTE;

    if ((status = apr_file_perms_set(resource->info->pathname, perms))
        != APR_SUCCESS) {
        return dav_new_error(resource->info->pool,
                             HTTP_INTERNAL_SERVER_ERROR, 0, status,
                             "Could not set the executable flag of the "
                             "target resource.");
    }

    /* update the resource and set up the rollback context */
    resource->info->finfo.protection = perms;
    *rollback_ctx = (dav_liveprop_rollback *)old_value;

    return NULL;
}

static void dav_fs_patch_commit(const dav_resource *resource,
                                int operation,
                                void *context,
                                dav_liveprop_rollback *rollback_ctx)
{
    /* nothing to do */
}

static dav_error *dav_fs_patch_rollback(const dav_resource *resource,
                                        int operation,
                                        void *context,
                                        dav_liveprop_rollback *rollback_ctx)
{
    apr_fileperms_t perms = resource->info->finfo.protection & ~APR_UEXECUTE;
    apr_status_t status;
    int value = rollback_ctx != NULL;

    /* assert: prop == executable. operation == SET. */

    /* restore the executable bit */
    if (value)
        perms |= APR_UEXECUTE;

    if ((status = apr_file_perms_set(resource->info->pathname, perms))
        != APR_SUCCESS) {
        return dav_new_error(resource->info->pool,
                             HTTP_INTERNAL_SERVER_ERROR, 0, status,
                             "After a failure occurred, the resource's "
                             "executable flag could not be restored.");
    }

    /* restore the resource's state */
    resource->info->finfo.protection = perms;

    return NULL;
}


static const dav_hooks_liveprop dav_hooks_liveprop_fs =
{
    dav_fs_insert_prop,
    dav_fs_is_writable,
    dav_fs_namespace_uris,
    dav_fs_patch_validate,
    dav_fs_patch_exec,
    dav_fs_patch_commit,
    dav_fs_patch_rollback
};

static const dav_provider dav_fs_provider =
{
    &dav_hooks_repository_fs,
    &dav_hooks_db_dbm,
    &dav_hooks_locks_fs,
    NULL,               /* vsn */
    NULL,               /* binding */
    NULL,               /* search */

    NULL                /* ctx */
};

void dav_fs_gather_propsets(apr_array_header_t *uris)
{
#ifdef DAV_FS_HAS_EXECUTABLE
    *(const char **)apr_array_push(uris) =
        "<http://apache.org/dav/propset/fs/1>";
#endif
}

int dav_fs_find_liveprop(const dav_resource *resource,
                         const char *ns_uri, const char *name,
                         const dav_hooks_liveprop **hooks)
{
    /* don't try to find any liveprops if this isn't "our" resource */
    if (resource->hooks != &dav_hooks_repository_fs)
        return 0;
    return dav_do_find_liveprop(ns_uri, name, &dav_fs_liveprop_group, hooks);
}

void dav_fs_insert_all_liveprops(request_rec *r, const dav_resource *resource,
                                 dav_prop_insert what, apr_text_header *phdr)
{
    /* don't insert any liveprops if this isn't "our" resource */
    if (resource->hooks != &dav_hooks_repository_fs)
        return;

    if (!resource->exists) {
        /* a lock-null resource */
        /*
        ** ### technically, we should insert empty properties. dunno offhand
        ** ### what part of the spec said this, but it was essentially thus:
        ** ### "the properties should be defined, but may have no value".
        */
        return;
    }

    (void) dav_fs_insert_prop(resource, DAV_PROPID_creationdate,
                              what, phdr);
    (void) dav_fs_insert_prop(resource, DAV_PROPID_getcontentlength,
                              what, phdr);
    (void) dav_fs_insert_prop(resource, DAV_PROPID_getlastmodified,
                              what, phdr);
    (void) dav_fs_insert_prop(resource, DAV_PROPID_getetag,
                              what, phdr);

#ifdef DAV_FS_HAS_EXECUTABLE
    /* Only insert this property if it is defined for this platform. */
    (void) dav_fs_insert_prop(resource, DAV_PROPID_FS_executable,
                              what, phdr);
#endif

    /* ### we know the others aren't defined as liveprops */
}

void dav_fs_register(apr_pool_t *p)
{
    /* register the namespace URIs */
    dav_register_liveprop_group(p, &dav_fs_liveprop_group);

    /* register the repository provider */
    dav_register_provider(p, "filesystem", &dav_fs_provider);
}