acl-backend-vfile.c revision 65ee37fdc16508f58d99e4c13d4bd9f96c403a6e
/* Copyright (c) 2006-2009 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "bsearch-insert-pos.h"
#include "str.h"
#include "istream.h"
#include "ostream.h"
#include "file-dotlock.h"
#include "nfs-workarounds.h"
#include "mail-storage-private.h"
#include "mail-namespace.h"
#include "acl-cache.h"
#include "acl-backend-vfile.h"
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#define ACL_VFILE_DEFAULT_CACHE_SECS 30
#define VALIDITY_MTIME_NOTFOUND 0
#define VALIDITY_MTIME_NOACCESS -1
struct acl_vfile_validity {
};
struct acl_backend_vfile_validity {
struct acl_vfile_validity mailbox_validity;
};
struct acl_letter_map {
char letter;
const char *name;
};
static const struct acl_letter_map acl_letter_map[] = {
{ 'l', MAIL_ACL_LOOKUP },
{ 'r', MAIL_ACL_READ },
{ 'w', MAIL_ACL_WRITE },
{ 's', MAIL_ACL_WRITE_SEEN },
{ 't', MAIL_ACL_WRITE_DELETED },
{ 'i', MAIL_ACL_INSERT },
{ 'p', MAIL_ACL_POST },
{ 'e', MAIL_ACL_EXPUNGE },
{ 'k', MAIL_ACL_CREATE },
{ 'x', MAIL_ACL_DELETE },
{ 'a', MAIL_ACL_ADMIN },
{ '\0', NULL }
};
static struct dotlock_settings dotlock_set = {
};
static struct acl_backend *acl_backend_vfile_alloc(void)
{
struct acl_backend_vfile *backend;
}
static int
{
struct acl_backend_vfile *backend =
(struct acl_backend_vfile *)_backend;
const char *const *tmp;
tmp++;
else {
return -1;
}
}
i_info("acl vfile: Global ACL directory: %s",
}
sizeof(struct acl_backend_vfile_validity));
return 0;
}
{
struct acl_backend_vfile *backend =
(struct acl_backend_vfile *)_backend;
}
}
static const char *
{
struct mail_namespace *ns;
const char *dir;
} else {
}
return dir;
}
static struct acl_object *
const char *name)
{
struct acl_backend_vfile *backend =
(struct acl_backend_vfile *)_backend;
struct acl_object_vfile *aclobj;
const char *dir;
if (*name == '\0') {
/* the default ACL for mailbox list */
} else {
}
}
static const char *
{
const char *p;
char sep;
}
static int
struct acl_vfile_validity *validity)
{
/* use the cached value */
}
return 0;
}
return 1;
}
return -1;
}
return 1;
}
static bool
{
struct acl_backend_vfile *backend =
(struct acl_backend_vfile *)_backend;
int ret;
if (old_validity != NULL)
else
/* See if the mailbox exists. If we wanted recursive lookups we could
skip this, but at least for now we assume that if an existing
mailbox has no ACL it's equivalent to default ACLs. */
if (ret == 0) {
}
}
return ret > 0;
}
static struct acl_object *
const char *child_name)
{
const char *parent;
/* stop at the first parent that
a) has global ACL file
b) has local ACL file
c) exists */
break;
child_name = parent;
}
/* use the root */
parent = "";
}
}
{
}
static const char *const *
bool dup_strings)
{
/* sort the rights first so we can easily drop duplicates */
/* @UNSAFE */
if (count > 0) {
}
if (dup_strings) {
for (i = 0; i < dest; i++)
}
}
return ret;
}
static const char *const *
{
const char *const *names;
unsigned int i;
/* parse IMAP ACL list */
acl++;
break;
}
return NULL;
}
acl++;
}
if (*acl != '\0') {
/* parse our own extended ACLs */
if (*acl != ':') {
*error_r = "Missing ':' prefix in ACL extensions";
return NULL;
}
}
}
}
static int
unsigned int linenum)
{
struct acl_rights rights;
return 0;
/* <id> [<imap acls>] [:<named acls>] */
if (p == NULL)
p = "";
else {
p++;
}
if (*line != '-')
else {
line++;
}
switch (*line) {
case 'u':
strlen(ACL_ID_NAME_USER_PREFIX)) == 0) {
break;
}
case 'o':
break;
}
case 'g':
strlen(ACL_ID_NAME_GROUP_PREFIX)) == 0) {
break;
strlen(ACL_ID_NAME_GROUP_OVERRIDE_PREFIX)) == 0) {
break;
}
case 'a':
break;
break;
}
default:
break;
}
return -1;
}
return 0;
}
{
struct acl_rights_update rights;
}
static int
bool *is_dir_r)
{
const char *line;
unsigned int linenum;
if (fd == -1) {
} else {
return -1;
}
return 1;
}
return 0;
}
return -1;
}
/* we opened a directory. */
return 0;
}
linenum = 1;
T_BEGIN {
linenum++);
} T_END;
if (ret < 0)
break;
}
if (ret < 0) {
/* parsing failure */
} else if (input->stream_errno != 0) {
ret = 0;
else {
ret = -1;
}
} else {
ret = 0;
else {
ret = -1;
}
} else {
ret = 1;
}
}
return 0;
return -1;
}
return ret;
}
static int
struct acl_vfile_validity *validity)
{
unsigned int i;
int ret;
bool is_dir;
return 0;
for (i = 0;; i++) {
&is_dir);
if (ret != 0)
break;
if (is_dir) {
/* opened a directory. use dir/.DEFAULT instead */
} else {
/* ESTALE - try again */
}
}
return ret <= 0 ? -1 : 0;
}
static int
struct acl_vfile_validity *validity)
{
struct acl_backend_vfile *backend =
return 1;
return 0;
/* if the file used to exist, we have to re-read it */
}
return -1;
}
/* same timestamp, but if it was modified within the
same second we want to refresh it again later (but
do it only after a couple of seconds so we don't
keep re-reading it all the time within those
seconds) */
if (validity->last_read_time != 0 &&
return 0;
}
return 1;
}
{
struct acl_backend_vfile_validity *validity;
return -1;
else
*mtime_r = 0;
return 0;
}
const struct acl_rights *r2)
{
int ret;
/* globals have higher priority than locals */
}
if (ret != 0)
return ret;
}
{
}
static void
bool dup_strings)
{
unsigned int i;
}
}
}
{
struct acl_rights *rights;
return;
/* merge identical identifiers */
/* add i's rights to dest and delete i */
} else {
if (++dest != i)
}
}
}
{
struct acl_rights_update ru;
}
{
struct mail_namespace *ns;
const struct acl_rights *rights;
unsigned int i, count;
return;
for (i = 0; i < count; i++) {
if (!owner_applied &&
/* owner rights weren't explicitly specified.
replace all the current rights */
}
}
/* If [neg_]rights is NULL it needs to be ignored.
The easiest way to do that is to just mark it with
REMOVE mode */
/* first global: reset negative ACLs so local ACLs
can't mess things up via them */
/* make sure owner has admin rights
(at least before global ACLs are applied) */
}
}
}
if (!owner_applied && count > 0)
}
{
struct acl_backend_vfile *backend =
struct acl_backend_vfile_validity *old_validity;
struct acl_backend_vfile_validity validity;
int ret;
if (ret == 0) {
}
if (ret <= 0)
return ret;
/* either global or local ACLs changed, need to re-read both */
} else {
}
&validity.global_validity) < 0)
return -1;
&validity.local_validity) < 0)
return -1;
/* update cache only after we've successfully read everything */
return 0;
}
{
int fd;
/* first lock the ACL file */
if (fd == -1) {
i_error("file_dotlock_open_mode(%s) failed: %m",
aclobj->local_path);
return -1;
}
/* locked successfully, re-read the existing file to make sure we
don't lose any changes. */
if (acl_backend_vfile_object_refresh_cache(_aclobj) < 0) {
return -1;
}
return fd;
}
const char *const **rightsp,
const char *const *modify_rights,
enum acl_modify_mode modify_mode)
{
const char *const *old_rights = *rightsp;
const char *const *new_rights;
unsigned int i, j;
/* nothing to do here */
return FALSE;
}
switch (modify_mode) {
case ACL_MODIFY_MODE_REMOVE:
/* nothing to do */
return FALSE;
}
for (i = 0; old_rights[i] != NULL; i++) {
for (j = 0; modify_rights[j] != NULL; j++) {
break;
}
if (modify_rights[j] == NULL)
}
new_rights = &null;
break;
case ACL_MODIFY_MODE_ADD:
break;
case ACL_MODIFY_MODE_REPLACE:
new_rights = &null;
break;
case ACL_MODIFY_MODE_CLEAR:
/* ACL didn't exist before either */
return FALSE;
}
return TRUE;
}
*rightsp = new_rights;
if (old_rights == NULL)
return new_rights != NULL;
/* see if anything changed */
return TRUE;
}
}
static bool
const struct acl_rights_update *update)
{
struct acl_rights *right;
/* this identifier no longer exists */
}
}
static bool
const struct acl_rights_update *update)
{
struct acl_rights right;
/* nothing to do */
return FALSE;
}
}
{
char c2[2];
unsigned int i, j, pos;
/* use letters if possible */
pos++;
break;
}
}
/* fallback to full name */
}
}
c2[0] = ':';
}
}
static void
bool neg)
{
}
static int
{
const struct acl_rights *rights;
unsigned int i, count;
int ret = 0;
/* rights are sorted with globals at the end, so we can stop at the
first global */
str_truncate(str, 0);
}
if (o_stream_flush(output) < 0) {
ret = -1;
}
/* we really don't want to lose ACL files' contents, so fsync() always
before renaming */
ret = -1;
}
return ret;
}
{
struct acl_backend_vfile_validity *validity;
/* we'll just recalculate or fail it later */
return;
}
}
static int
const struct acl_rights_update *update)
{
struct acl_backend_vfile *backend =
const struct acl_rights *rights;
const char *path;
unsigned int i, count;
int fd;
bool changed;
/* global ACLs can't be updated here */
if (fd == -1)
return -1;
acl_rights_bsearch_cmp, &i))
else
if (!changed) {
return 0;
}
/* ACLs were really changed, write the new ones */
return -1;
}
if (file_dotlock_replace(&dotlock, 0) < 0) {
return -1;
}
/* make sure dovecot-acl-list gets updated if we added any
lookup rights. */
return 0;
}
static struct acl_object_list_iter *
{
struct acl_object_vfile *aclobj =
(struct acl_object_vfile *)_aclobj;
struct acl_object_list_iter *iter;
/* we may have the object cached, but we don't have all the
rights read into memory */
}
return iter;
}
static int
struct acl_rights *rights_r)
{
struct acl_object_vfile *aclobj =
const struct acl_rights *rights;
return 0;
return 1;
}
static void
{
}
struct acl_backend_vfuncs acl_backend_vfile = {
};