Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117886156
mboxlist.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
48 KB
Referenced Files
None
Subscribers
None
mboxlist.c
View Options
/* mboxlist.c -- Mailbox list manipulation routines
*
* Copyright (c) 1998-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
/*
* $Id: mboxlist.c,v 1.141 2000/12/18 04:53:39 leg Exp $
*/
#include
<config.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#ifdef HAVE_UNISTD_H
#include
<unistd.h>
#endif
#include
<errno.h>
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<sys/uio.h>
#include
<fcntl.h>
#include
<ctype.h>
#include
<time.h>
#include
<syslog.h>
#include
<com_err.h>
#include
<sys/ipc.h>
#include
<sys/msg.h>
extern
int
errno
;
#include
"acl.h"
#include
"auth.h"
#include
"glob.h"
#include
"assert.h"
#include
"imapconf.h"
#include
"cyrusdb.h"
#include
"util.h"
#include
"mailbox.h"
#include
"exitcodes.h"
#include
"imap_err.h"
#include
"xmalloc.h"
#include
"acap.h"
#include
"acapmbox.h"
#include
"mboxname.h"
#include
"mboxlist.h"
#define DB CONFIG_DB_MBOX
#define SUBDB CONFIG_DB_SUBS
cyrus_acl_canonproc_t
mboxlist_ensureOwnerRights
;
struct
db
*
mbdb
;
static
int
mboxlist_dbopen
=
0
;
static
int
mboxlist_opensubs
();
static
void
mboxlist_closesubs
();
static
struct
quota
*
mboxlist_newquota
;
static
int
mboxlist_changequota
();
static
char
*
mboxlist_hash_usersubs
(
const
char
*
userid
);
#define FNAME_SUBSSUFFIX ".sub"
static
int
delete_user
(
const
char
*
user
)
{
/* delete seen state */
/* delete subscriptions */
char
*
fname
=
mboxlist_hash_usersubs
(
user
);
(
void
)
unlink
(
fname
);
free
(
fname
);
return
0
;
}
/*
* Convert a partition into a path
*/
static
int
mboxlist_getpath
(
const
char
*
partition
,
const
char
*
name
,
char
**
pathp
)
{
int
partitionlen
;
char
optionbuf
[
MAX_MAILBOX_NAME
+
1
];
static
char
pathresult
[
MAX_MAILBOX_PATH
];
const
char
*
root
;
assert
(
partition
&&
pathp
);
partitionlen
=
strlen
(
partition
);
if
(
partitionlen
>
sizeof
(
optionbuf
)
-11
)
{
return
IMAP_PARTITION_UNKNOWN
;
}
strcpy
(
optionbuf
,
"partition-"
);
strcat
(
optionbuf
,
partition
);
root
=
config_getstring
(
optionbuf
,
(
char
*
)
0
);
if
(
!
root
)
{
return
IMAP_PARTITION_UNKNOWN
;
}
mailbox_hash_mbox
(
pathresult
,
root
,
name
);
*
pathp
=
pathresult
;
return
0
;
}
char
*
mboxlist_makeentry
(
int
mbtype
,
char
*
part
,
char
*
acl
)
{
char
*
mboxent
=
(
char
*
)
xmalloc
(
sizeof
(
char
)
*
(
30
+
strlen
(
acl
)
+
strlen
(
part
)));
sprintf
(
mboxent
,
"%d %s %s"
,
mbtype
,
part
,
acl
);
return
mboxent
;
}
/*
* Lookup 'name' in the mailbox list.
* The capitalization of 'name' is canonicalized to the way it appears
* in the mailbox list.
* If 'path' is non-nil, a pointer to the full pathname of the mailbox
* is placed in the char * pointed to by it. If 'acl' is non-nil, a pointer
* to the mailbox ACL is placed in the char * pointed to by it.
*/
static
int
mboxlist_mylookup
(
const
char
*
name
,
int
*
typep
,
char
**
pathp
,
char
**
partp
,
char
**
aclp
,
struct
txn
**
tid
,
int
wrlock
)
{
int
acllen
;
static
char
partition
[
MAX_PARTITION_LEN
];
static
char
*
aclresult
;
static
int
aclresultalloced
;
int
r
;
const
char
*
data
;
char
*
p
,
*
q
;
int
datalen
;
int
namelen
;
int
mbtype
;
namelen
=
strlen
(
name
);
if
(
namelen
==
0
)
{
return
IMAP_MAILBOX_NONEXISTENT
;
}
if
(
wrlock
)
{
r
=
DB
->
fetchlock
(
mbdb
,
name
,
namelen
,
&
data
,
&
datalen
,
tid
);
}
else
{
r
=
DB
->
fetch
(
mbdb
,
name
,
namelen
,
&
data
,
&
datalen
,
tid
);
}
switch
(
r
)
{
case
CYRUSDB_OK
:
if
(
data
==
NULL
)
{
return
IMAP_MAILBOX_NONEXISTENT
;
break
;
}
/* copy out interesting parts */
mbtype
=
strtol
(
data
,
&
p
,
10
);
if
(
typep
)
*
typep
=
mbtype
;
if
(
*
p
==
' '
)
p
++
;
q
=
partition
;
while
(
*
p
!=
' '
)
{
/* copy out partition name */
*
q
++
=
*
p
++
;
}
*
q
=
'\0'
;
p
++
;
if
(
partp
)
{
*
partp
=
partition
;
}
/* construct pathname if requested */
if
(
pathp
)
{
if
(
mbtype
&
MBTYPE_REMOTE
)
{
*
pathp
=
partition
;
}
else
{
r
=
mboxlist_getpath
(
partition
,
name
,
pathp
);
if
(
r
)
{
return
r
;
}
}
}
/* the rest is ACL; return it if requested */
if
(
aclp
)
{
acllen
=
datalen
-
(
p
-
data
);
if
(
acllen
>=
aclresultalloced
)
{
aclresultalloced
=
acllen
+
100
;
aclresult
=
xrealloc
(
aclresult
,
aclresultalloced
);
}
memcpy
(
aclresult
,
p
,
acllen
);
aclresult
[
acllen
]
=
'\0'
;
*
aclp
=
aclresult
;
}
break
;
case
CYRUSDB_AGAIN
:
return
IMAP_AGAIN
;
break
;
default
:
syslog
(
LOG_ERR
,
"DBERROR: error fetching %s: %s"
,
name
,
cyrusdb_strerror
(
r
));
return
IMAP_IOERROR
;
break
;
}
return
0
;
}
/*
* Lookup 'name' in the mailbox list.
* The capitalization of 'name' is canonicalized to the way it appears
* in the mailbox list.
* If 'path' is non-nil, a pointer to the full pathname of the mailbox
* is placed in the char * pointed to by it. If 'acl' is non-nil, a pointer
* to the mailbox ACL is placed in the char * pointed to by it.
*/
int
mboxlist_lookup
(
const
char
*
name
,
char
**
pathp
,
char
**
aclp
,
void
*
tid
__attribute__
((
unused
)))
{
return
mboxlist_mylookup
(
name
,
NULL
,
pathp
,
NULL
,
aclp
,
NULL
,
0
);
}
int
mboxlist_findstage
(
const
char
*
name
,
char
*
stagedir
)
{
char
optionbuf
[
MAX_MAILBOX_NAME
+
1
];
const
char
*
root
;
char
*
partition
;
int
r
;
assert
(
stagedir
!=
NULL
);
/* Find mailbox */
r
=
mboxlist_mylookup
(
name
,
NULL
,
NULL
,
&
partition
,
NULL
,
NULL
,
0
);
switch
(
r
)
{
case
0
:
break
;
default
:
return
r
;
break
;
}
strcpy
(
optionbuf
,
"partition-"
);
strcpy
(
optionbuf
+
10
,
partition
);
root
=
config_getstring
(
optionbuf
,
(
char
*
)
0
);
if
(
!
root
)
{
return
IMAP_PARTITION_UNKNOWN
;
}
sprintf
(
stagedir
,
"%s/stage./"
,
root
);
return
0
;
}
/*
* Check/set up for mailbox creation
*/
int
mboxlist_mycreatemailboxcheck
(
char
*
name
,
int
mbtype
,
char
*
partition
,
int
isadmin
,
char
*
userid
,
struct
auth_state
*
auth_state
,
char
**
newacl
,
char
**
newpartition
,
int
RMW
,
struct
txn
**
tid
)
{
int
r
;
char
*
p
;
char
*
acl
,
*
path
;
char
*
defaultacl
,
*
identifier
,
*
rights
;
char
parent
[
MAX_MAILBOX_NAME
+
1
];
unsigned
long
parentlen
;
char
*
parentname
=
NULL
;
char
*
parentpartition
=
NULL
;
char
*
parentacl
=
NULL
;
unsigned
long
parentpartitionlen
=
0
;
unsigned
long
parentacllen
=
0
;
/* Check for invalid name/partition */
if
(
partition
&&
strlen
(
partition
)
>
MAX_PARTITION_LEN
)
{
return
IMAP_PARTITION_UNKNOWN
;
}
r
=
mboxname_policycheck
(
name
);
if
(
r
)
return
r
;
/* User has admin rights over their own mailbox namespace */
if
(
mboxname_userownsmailbox
(
userid
,
name
))
{
isadmin
=
1
;
}
/* Check to see if new mailbox exists */
r
=
mboxlist_mylookup
(
name
,
NULL
,
&
path
,
NULL
,
&
acl
,
tid
,
RMW
);
switch
(
r
)
{
case
0
:
r
=
IMAP_MAILBOX_EXISTS
;
/* Lie about error if privacy demands */
if
(
!
isadmin
&&
!
(
cyrus_acl_myrights
(
auth_state
,
acl
)
&
ACL_LOOKUP
))
{
r
=
IMAP_PERMISSION_DENIED
;
}
return
r
;
break
;
case
IMAP_MAILBOX_NONEXISTENT
:
break
;
default
:
return
r
;
break
;
}
/* Search for a parent */
strcpy
(
parent
,
name
);
parentlen
=
0
;
while
((
parentlen
==
0
)
&&
(
p
=
strrchr
(
parent
,
'.'
)))
{
*
p
=
'\0'
;
r
=
mboxlist_mylookup
(
parent
,
NULL
,
NULL
,
&
parentpartition
,
&
parentacl
,
tid
,
0
);
switch
(
r
)
{
case
0
:
parentlen
=
strlen
(
parent
);
parentname
=
parent
;
parentpartitionlen
=
strlen
(
parentpartition
);
parentacllen
=
strlen
(
parentacl
);
break
;
case
IMAP_MAILBOX_NONEXISTENT
:
break
;
default
:
return
r
;
break
;
}
}
if
(
parentlen
!=
0
)
{
/* check acl */
if
(
!
isadmin
&&
!
(
cyrus_acl_myrights
(
auth_state
,
parentacl
)
&
ACL_CREATE
))
{
return
IMAP_PERMISSION_DENIED
;
}
/* Copy partition, if not specified */
if
(
partition
==
NULL
)
{
partition
=
xmalloc
(
parentpartitionlen
+
1
);
memcpy
(
partition
,
parentpartition
,
parentpartitionlen
);
partition
[
parentpartitionlen
]
=
'\0'
;
}
else
{
partition
=
xstrdup
(
partition
);
}
/* Copy ACL */
acl
=
xmalloc
(
parentacllen
+
1
);
memcpy
(
acl
,
parentacl
,
parentacllen
);
acl
[
parentacllen
]
=
'\0'
;
/* Canonicalize case of parent prefix */
strlcpy
(
name
,
parent
,
strlen
(
parent
));
}
else
{
/* parentlen == 0, no parent mailbox */
if
(
!
isadmin
)
{
return
IMAP_PERMISSION_DENIED
;
}
acl
=
xstrdup
(
""
);
if
(
!
strncmp
(
name
,
"user."
,
5
))
{
if
(
strchr
(
name
+
5
,
'.'
))
{
/* Disallow creating user.X.* when no user.X */
free
(
acl
);
return
IMAP_PERMISSION_DENIED
;
}
/* disallow wildcards in userids with inboxes. */
if
(
strchr
(
name
,
'*'
)
||
strchr
(
name
,
'%'
)
||
strchr
(
name
,
'?'
))
{
return
IMAP_MAILBOX_BADNAME
;
}
/*
* Users by default have all access to their personal mailbox(es),
* Nobody else starts with any access to same.
*/
cyrus_acl_set
(
&
acl
,
name
+
5
,
ACL_MODE_SET
,
ACL_ALL
,
(
cyrus_acl_canonproc_t
*
)
0
,
(
void
*
)
0
);
}
else
{
defaultacl
=
identifier
=
xstrdup
(
config_getstring
(
"defaultacl"
,
"anyone lrs"
));
for
(;;)
{
while
(
*
identifier
&&
isspace
((
int
)
*
identifier
))
identifier
++
;
rights
=
identifier
;
while
(
*
rights
&&
!
isspace
((
int
)
*
rights
))
rights
++
;
if
(
!*
rights
)
break
;
*
rights
++
=
'\0'
;
while
(
*
rights
&&
isspace
((
int
)
*
rights
))
rights
++
;
if
(
!*
rights
)
break
;
p
=
rights
;
while
(
*
p
&&
!
isspace
((
int
)
*
p
))
p
++
;
if
(
*
p
)
*
p
++
=
'\0'
;
cyrus_acl_set
(
&
acl
,
identifier
,
ACL_MODE_SET
,
cyrus_acl_strtomask
(
rights
),
(
cyrus_acl_canonproc_t
*
)
0
,
(
void
*
)
0
);
identifier
=
p
;
}
free
(
defaultacl
);
}
if
(
!
partition
)
{
partition
=
(
char
*
)
config_defpartition
;
if
(
strlen
(
partition
)
>
MAX_PARTITION_LEN
)
{
/* Configuration error */
fatal
(
"name of default partition is too long"
,
EC_CONFIG
);
}
}
partition
=
xstrdup
(
partition
);
}
if
(
newpartition
)
*
newpartition
=
partition
;
else
free
(
partition
);
if
(
newacl
)
*
newacl
=
acl
;
else
free
(
acl
);
return
0
;
}
int
mboxlist_createmailboxcheck
(
char
*
name
,
int
mbtype
,
char
*
partition
,
int
isadmin
,
char
*
userid
,
struct
auth_state
*
auth_state
,
char
**
newacl
,
char
**
newpartition
)
{
return
mboxlist_mycreatemailboxcheck
(
name
,
mbtype
,
partition
,
isadmin
,
userid
,
auth_state
,
newacl
,
newpartition
,
0
,
NULL
);
}
/*
* Create a mailbox
*
* 1. start mailboxes transaction
* 2. verify ACL's to best of ability (CRASH: abort)
* 3. open ACAP connection if necessary
* 4. verify parent ACL's if need to
* 5. create ACAP entry and set as reserved (CRASH: ACAP inconsistant)
* 6. create on disk (CRASH: ACAP inconsistant, disk inconsistant)
* 8. commit local transaction (CRASH: ACAP inconsistant)
* 9. set ACAP entry as commited (CRASH: commited)
*
*/
int
mboxlist_createmailbox
(
char
*
name
,
int
mbtype
,
char
*
partition
,
int
isadmin
,
char
*
userid
,
struct
auth_state
*
auth_state
)
{
int
r
;
char
*
acl
=
NULL
;
const
char
*
root
=
NULL
;
char
*
newpartition
=
NULL
;
struct
mailbox
newmailbox
;
struct
txn
*
tid
=
NULL
;
char
*
mboxent
=
NULL
;
acapmbox_data_t
mboxdata
;
int
madereserved
=
0
;
/* made reserved entry on ACAP server */
acapmbox_handle_t
*
acaphandle
=
NULL
;
retry
:
tid
=
NULL
;
/* 2. verify ACL's to best of ability (CRASH: abort) */
r
=
mboxlist_mycreatemailboxcheck
(
name
,
mbtype
,
partition
,
isadmin
,
userid
,
auth_state
,
&
acl
,
&
newpartition
,
1
,
&
tid
);
switch
(
r
)
{
case
0
:
break
;
case
IMAP_AGAIN
:
goto
retry
;
default
:
goto
done
;
}
if
(
!
(
mbtype
&
MBTYPE_REMOTE
))
{
char
buf
[
MAX_PARTITION_LEN
+
30
];
/* Get partition's path */
sprintf
(
buf
,
"partition-%s"
,
newpartition
);
root
=
config_getstring
(
buf
,
(
char
*
)
0
);
if
(
!
root
)
{
r
=
IMAP_PARTITION_UNKNOWN
;
goto
done
;
}
if
(
strlen
(
root
)
+
strlen
(
name
)
+
20
>
MAX_MAILBOX_PATH
)
{
r
=
IMAP_MAILBOX_BADNAME
;
goto
done
;
}
}
/* 3. open ACAP connection if necessary */
acaphandle
=
acapmbox_get_handle
();
/* 4. create ACAP entry and set as reserved (CRASH: ACAP inconsistant) */
acapmbox_new
(
&
mboxdata
,
NULL
,
name
);
r
=
acapmbox_create
(
acaphandle
,
&
mboxdata
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"ACAP: unable to reserve %s: %s
\n
"
,
name
,
error_message
(
r
));
goto
done
;
}
madereserved
=
1
;
/* so we can roll back on failure */
/* 5. add the new entry */
mboxent
=
mboxlist_makeentry
(
mbtype
,
newpartition
,
acl
);
r
=
DB
->
store
(
mbdb
,
name
,
strlen
(
name
),
mboxent
,
strlen
(
mboxent
),
&
tid
);
switch
(
r
)
{
case
CYRUSDB_OK
:
break
;
case
CYRUSDB_AGAIN
:
goto
retry
;
break
;
default
:
syslog
(
LOG_ERR
,
"DBERROR: error updating database: %s"
,
name
,
cyrusdb_strerror
(
r
));
r
=
IMAP_IOERROR
;
goto
done
;
}
done
:
/* ALL DATABASE OPERATIONS DONE; NEED TO DO FILESYSTEM OPERATIONS */
if
(
!
r
&&
!
(
mbtype
&
MBTYPE_REMOTE
))
{
char
buf
[
MAX_MAILBOX_PATH
];
/* Create new mailbox and move new mailbox list file into place */
mailbox_hash_mbox
(
buf
,
root
,
name
);
r
=
mailbox_create
(
name
,
buf
,
acl
,
((
mbtype
&
MBTYPE_NETNEWS
)
?
MAILBOX_FORMAT_NETNEWS
:
MAILBOX_FORMAT_NORMAL
),
&
newmailbox
);
mboxdata
.
uidvalidity
=
newmailbox
.
uidvalidity
;
mboxdata
.
acl
=
acl
;
mboxdata
.
total
=
newmailbox
.
exists
;
if
(
!
r
)
{
mailbox_close
(
&
newmailbox
);
}
}
if
(
r
)
{
/* CREATE failed */
int
r2
;
r2
=
0
;
if
(
tid
)
r2
=
DB
->
abort
(
mbdb
,
tid
);
switch
(
r2
)
{
case
0
:
break
;
default
:
syslog
(
LOG_ERR
,
"DBERROR: failed on abort: %s"
,
cyrusdb_strerror
(
r2
));
}
/* delete ACAP entry if we made it */
if
(
madereserved
==
1
)
{
r2
=
acapmbox_delete
(
acaphandle
,
name
);
if
(
r2
)
{
syslog
(
LOG_ERR
,
"ACAP: unable to unreserve %s: %s
\n
"
,
name
,
error_message
(
r2
));
}
}
}
else
{
/* all is well */
switch
(
r
=
DB
->
commit
(
mbdb
,
tid
))
{
case
0
:
break
;
default
:
syslog
(
LOG_ERR
,
"DBERROR: failed on commit: %s"
,
cyrusdb_strerror
(
r
));
r
=
IMAP_IOERROR
;
}
}
/* 9. set ACAP entry as commited (CRASH: commited) */
if
(
!
r
)
{
r
=
acapmbox_markactive
(
acaphandle
,
&
mboxdata
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"ACAP: unable to commit %s: %s
\n
"
,
name
,
error_message
(
r
));
}
}
acapmbox_release_handle
(
acaphandle
);
if
(
acl
)
free
(
acl
);
if
(
newpartition
)
free
(
newpartition
);
if
(
mboxent
)
free
(
mboxent
);
return
r
;
}
/* insert an entry for the proxy */
int
mboxlist_insertremote
(
char
*
name
,
int
mbtype
,
char
*
host
,
char
*
acl
,
void
**
rettid
)
{
char
*
mboxent
;
int
r
=
0
;
assert
(
name
!=
NULL
&&
host
!=
NULL
);
mboxent
=
mboxlist_makeentry
(
mbtype
|
MBTYPE_REMOTE
,
host
,
acl
);
/* database put */
r
=
DB
->
store
(
mbdb
,
name
,
strlen
(
name
),
mboxent
,
strlen
(
mboxent
),
NULL
);
switch
(
r
)
{
case
CYRUSDB_OK
:
break
;
case
CYRUSDB_AGAIN
:
abort
();
/* shouldn't happen ! */
break
;
default
:
syslog
(
LOG_ERR
,
"DBERROR: error updating database: %s"
,
name
,
cyrusdb_strerror
(
r
));
r
=
IMAP_IOERROR
;
break
;
}
free
(
mboxent
);
return
r
;
}
/*
* Delete a mailbox.
* Deleting the mailbox user.FOO deletes the user "FOO". It may only be
* performed by an admin. The operation removes the user "FOO"'s
* subscriptions and all sub-mailboxes of user.FOO
*
* 1. Begin transaction
* 2. Verify ACL's
* 3. remove from database
* 4. remove from disk
* 5. commit transaction
* 6. Open ACAP connection if necessary
* 7. delete from ACAP
*
*/
int
mboxlist_deletemailbox
(
char
*
name
,
int
isadmin
,
char
*
userid
,
struct
auth_state
*
auth_state
,
int
checkacl
)
{
int
r
;
char
*
acl
;
long
access
;
int
deleteuser
=
0
;
/* if we are deleting user.<user> */
struct
mailbox
mailbox
;
int
deletequotaroot
=
0
;
char
*
path
;
struct
txn
*
tid
=
NULL
;
int
isremote
=
0
;
int
mbtype
;
retry
:
/* Check for request to delete a user:
user.<x> with no dots after it */
if
(
!
strncmp
(
name
,
"user."
,
5
)
&&
!
strchr
(
name
+
5
,
'.'
))
{
/* Can't DELETE INBOX (your own inbox) */
if
(
!
strcmp
(
name
+
5
,
userid
))
{
r
=
IMAP_MAILBOX_NOTSUPPORTED
;
goto
done
;
}
/* Only admins may delete user */
if
(
!
isadmin
)
{
r
=
IMAP_PERMISSION_DENIED
;
goto
done
;
}
r
=
mboxlist_mylookup
(
name
,
NULL
,
NULL
,
NULL
,
&
acl
,
&
tid
,
1
);
switch
(
r
)
{
case
0
:
break
;
case
IMAP_AGAIN
:
goto
retry
;
break
;
default
:
DB
->
abort
(
mbdb
,
tid
);
goto
done
;
break
;
}
/* Check ACL before doing anything stupid
* We don't have to lie about the error code since we know
* the user is an admin.
*/
if
(
checkacl
&&
(
!
(
cyrus_acl_myrights
(
auth_state
,
acl
)
&
ACL_CREATE
)))
{
r
=
IMAP_PERMISSION_DENIED
;
DB
->
abort
(
mbdb
,
tid
);
goto
done
;
}
deleteuser
=
1
;
}
r
=
mboxlist_mylookup
(
name
,
&
mbtype
,
&
path
,
NULL
,
&
acl
,
&
tid
,
1
);
switch
(
r
)
{
case
0
:
break
;
case
IMAP_AGAIN
:
goto
retry
;
break
;
default
:
DB
->
abort
(
mbdb
,
tid
);
goto
done
;
}
isremote
=
mbtype
&
MBTYPE_REMOTE
;
/* check if user has Delete right */
access
=
cyrus_acl_myrights
(
auth_state
,
acl
);
if
(
checkacl
&&
!
(
access
&
ACL_CREATE
))
{
/* User has admin rights over their own mailbox namespace */
if
(
mboxname_userownsmailbox
(
userid
,
name
))
{
isadmin
=
1
;
}
/* Lie about error if privacy demands */
r
=
(
isadmin
||
(
access
&
ACL_LOOKUP
))
?
IMAP_PERMISSION_DENIED
:
IMAP_MAILBOX_NONEXISTENT
;
DB
->
abort
(
mbdb
,
tid
);
goto
done
;
}
/* delete entry */
r
=
DB
->
delete
(
mbdb
,
name
,
strlen
(
name
),
&
tid
);
switch
(
r
)
{
case
CYRUSDB_OK
:
/* success */
break
;
case
CYRUSDB_AGAIN
:
goto
retry
;
default
:
syslog
(
LOG_ERR
,
"DBERROR: error deleting %s: %s"
,
name
,
cyrusdb_strerror
(
r
));
r
=
IMAP_IOERROR
;
goto
done
;
}
/* commit db operations */
if
(
!
r
)
{
r
=
DB
->
commit
(
mbdb
,
tid
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"DBERROR: failed on commit: %s"
,
cyrusdb_strerror
(
r
));
r
=
IMAP_IOERROR
;
}
}
if
(
r
||
isremote
)
goto
done
;
if
(
!
r
)
r
=
mailbox_open_header_path
(
name
,
path
,
acl
,
0
,
&
mailbox
,
0
);
if
(
!
r
)
r
=
mailbox_delete
(
&
mailbox
,
deletequotaroot
);
if
(
!
r
)
{
/* open ACAP connection if necessary */
acapmbox_handle_t
*
acaphandle
=
acapmbox_get_handle
();
/* delete from ACAP */
r
=
acapmbox_delete
(
acaphandle
,
name
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"ACAP: can't delete mailbox entry '%s': %s"
,
name
,
error_message
(
r
));
}
acapmbox_release_handle
(
acaphandle
);
}
/*
* See if we have to remove mailbox's quota root
*/
if
(
!
r
&&
mailbox
.
quota
.
root
!=
NULL
)
{
/* look for any other mailboxes in this quotaroot */
}
if
(
!
r
&&
deleteuser
)
{
/* call the delete user function */
delete_user
(
name
+
5
);
}
done
:
return
r
;
}
/*
* Rename/move a single mailbox (recursive renames are handled at a
* higher level)
*
* 1. start transaction
* 2. verify acls
* 3. open acap connection if needed
* 4. Delete entry from berkeley db
* 5. ACAP make the new entry
* 7. delete from disk
* 8. commit transaction
* 9. set new ACAP entry commited
* 10. delete old ACAP entry
* */
/* note: partition moving should really be moved to another function */
int
mboxlist_renamemailbox
(
char
*
oldname
,
char
*
newname
,
char
*
partition
,
int
isadmin
,
char
*
userid
,
struct
auth_state
*
auth_state
)
{
int
r
;
long
access
;
int
isusermbox
=
0
;
int
partitionmove
=
0
;
int
mbtype
;
char
*
oldpath
=
NULL
;
char
newpath
[
MAX_MAILBOX_PATH
];
struct
mailbox
newmailbox
;
acapmbox_data_t
mboxdata
;
char
*
oldacl
;
const
char
*
root
=
NULL
;
struct
txn
*
tid
=
NULL
;
char
*
newpartition
=
NULL
;
char
*
mboxent
=
NULL
;
int
acap_madenew
=
0
;
retry
:
/* lookup the mailbox to make sure it exists and get its acl */
r
=
mboxlist_mylookup
(
oldname
,
&
mbtype
,
&
oldpath
,
NULL
,
&
oldacl
,
&
tid
,
1
);
switch
(
r
)
{
case
0
:
break
;
case
IMAP_AGAIN
:
goto
retry
;
default
:
goto
done
;
}
/* Check ability to delete old mailbox */
if
(
!
strcmp
(
oldname
,
newname
)
&&
!
(
mbtype
&
MBTYPE_REMOTE
))
{
/* Attempt to move mailbox across partition */
if
(
!
isadmin
||
!
partition
)
{
r
=
IMAP_MAILBOX_EXISTS
;
goto
done
;
}
partitionmove
=
1
;
root
=
config_partitiondir
(
partition
);
if
(
!
root
)
{
r
=
IMAP_PARTITION_UNKNOWN
;
goto
done
;
}
if
(
!
strncmp
(
root
,
oldpath
,
strlen
(
root
))
&&
oldpath
[
strlen
(
root
)]
==
'/'
)
{
/* partitions are the same or share common prefix */
r
=
IMAP_MAILBOX_EXISTS
;
goto
done
;
}
}
else
if
(
!
strncmp
(
oldname
,
"user."
,
5
)
&&
!
strchr
(
oldname
+
5
,
'.'
))
{
if
(
!
strcmp
(
oldname
+
5
,
userid
))
{
/* Special case of renaming inbox */
access
=
cyrus_acl_myrights
(
auth_state
,
oldacl
);
if
(
!
(
access
&
ACL_CREATE
))
{
r
=
IMAP_PERMISSION_DENIED
;
goto
done
;
}
isusermbox
=
1
;
}
else
{
/* Even admins can't rename users */
r
=
IMAP_MAILBOX_NOTSUPPORTED
;
goto
done
;
}
}
else
{
access
=
cyrus_acl_myrights
(
auth_state
,
oldacl
);
if
(
!
(
access
&
ACL_CREATE
)
&&
!
isadmin
)
{
r
=
(
isadmin
||
(
access
&
ACL_LOOKUP
))
?
IMAP_PERMISSION_DENIED
:
IMAP_MAILBOX_NONEXISTENT
;
goto
done
;
}
}
/* Check ability to create new mailbox */
if
(
!
partitionmove
)
{
if
(
!
strncmp
(
newname
,
"user."
,
5
)
&&
!
strchr
(
newname
+
5
,
'.'
))
{
/* Even admins can't rename to user's inboxes */
r
=
IMAP_MAILBOX_NOTSUPPORTED
;
goto
done
;
}
r
=
mboxlist_mycreatemailboxcheck
(
newname
,
0
,
partition
,
isadmin
,
userid
,
auth_state
,
NULL
,
&
newpartition
,
1
,
&
tid
);
switch
(
r
)
{
case
0
:
break
;
case
IMAP_AGAIN
:
goto
retry
;
break
;
default
:
/* not allowed to create the new mailbox */
goto
done
;
break
;
}
}
else
{
newpartition
=
xstrdup
(
partition
);
}
if
(
!
(
mbtype
&
MBTYPE_REMOTE
))
{
/* Get partition's path */
root
=
config_partitiondir
(
newpartition
);
if
(
!
root
)
{
r
=
IMAP_PARTITION_UNKNOWN
;
goto
done
;
}
}
if
(
!
r
&&
!
partitionmove
)
{
/* 3. open ACAP connection if necessary */
acapmbox_handle_t
*
acaphandle
=
acapmbox_get_handle
();
/* 5. ACAP make the new entry, set as reserved */
acapmbox_new
(
&
mboxdata
,
NULL
,
newname
);
r
=
acapmbox_create
(
acaphandle
,
&
mboxdata
);
if
(
r
!=
ACAP_OK
)
{
goto
done
;
}
acap_madenew
=
1
;
acapmbox_release_handle
(
acaphandle
);
}
if
(
!
isusermbox
)
{
/* 4. Delete entry from berkeley db */
r
=
DB
->
delete
(
mbdb
,
oldname
,
strlen
(
oldname
),
&
tid
);
switch
(
r
)
{
case
0
:
/* success */
break
;
case
CYRUSDB_AGAIN
:
goto
retry
;
break
;
default
:
syslog
(
LOG_ERR
,
"DBERROR: error deleting %s: %s"
,
oldname
,
cyrusdb_strerror
(
r
));
r
=
IMAP_IOERROR
;
goto
done
;
break
;
}
}
/* create new entry */
mboxent
=
mboxlist_makeentry
(
mbtype
,
newpartition
,
oldacl
);
/* put it into the db */
r
=
DB
->
store
(
mbdb
,
newname
,
strlen
(
newname
),
mboxent
,
strlen
(
mboxent
),
&
tid
);
switch
(
r
)
{
case
0
:
break
;
case
CYRUSDB_AGAIN
:
goto
retry
;
default
:
syslog
(
LOG_ERR
,
"DBERROR: error renaming %s: %s"
,
newname
,
cyrusdb_strerror
(
r
));
r
=
IMAP_IOERROR
;
goto
done
;
}
done
:
/* ALL DATABASE OPERATIONS DONE; NEED TO DO FILESYSTEM OPERATIONS */
if
(
!
r
&&
!
(
mbtype
&
MBTYPE_REMOTE
))
{
/* Rename the actual mailbox */
assert
(
root
!=
NULL
);
/* from above */
mailbox_hash_mbox
(
newpath
,
root
,
newname
);
r
=
mailbox_rename
(
oldname
,
oldpath
,
oldacl
,
newname
,
newpath
,
isusermbox
,
NULL
,
NULL
,
&
newmailbox
);
mboxdata
.
uidvalidity
=
newmailbox
.
uidvalidity
;
mboxdata
.
acl
=
oldacl
;
mboxdata
.
total
=
newmailbox
.
exists
;
if
(
!
r
)
{
mailbox_close
(
&
newmailbox
);
}
}
if
(
r
!=
0
)
{
int
r2
=
0
;
if
(
tid
)
r2
=
DB
->
abort
(
mbdb
,
tid
);
if
(
r2
)
{
syslog
(
LOG_ERR
,
"DBERROR: can't abort: %s"
,
cyrusdb_strerror
(
r2
));
}
/* unroll acap operations if necessary */
if
(
acap_madenew
)
{
acapmbox_handle_t
*
acaphandle
=
acapmbox_get_handle
();
r2
=
acapmbox_delete
(
acaphandle
,
newname
);
if
(
r2
)
syslog
(
LOG_ERR
,
"ACAP: can't rollback %s: %s"
,
newname
,
error_message
(
r
));
acapmbox_release_handle
(
acaphandle
);
}
}
else
{
/* commit now */
switch
(
r
=
DB
->
commit
(
mbdb
,
tid
))
{
case
0
:
break
;
default
:
syslog
(
LOG_ERR
,
"DBERROR: failed on commit: %s"
,
cyrusdb_strerror
(
r
));
r
=
IMAP_IOERROR
;
break
;
}
}
if
(
!
r
&&
!
partitionmove
)
{
acapmbox_handle_t
*
acaphandle
=
acapmbox_get_handle
();
r
=
acapmbox_markactive
(
acaphandle
,
&
mboxdata
);
if
(
r
)
syslog
(
LOG_ERR
,
"ACAP: can't commit %s: %s"
,
newname
,
error_message
(
r
));
acapmbox_release_handle
(
acaphandle
);
}
if
(
!
r
&&
!
partitionmove
)
{
acapmbox_handle_t
*
acaphandle
=
acapmbox_get_handle
();
/* delete old ACAP entry */
r
=
acapmbox_delete
(
acaphandle
,
oldname
);
if
(
r
)
syslog
(
LOG_ERR
,
"ACAP: can't delete %s: %s"
,
oldname
,
error_message
(
r
));
acapmbox_release_handle
(
acaphandle
);
}
/* free memory */
if
(
newpartition
)
free
(
newpartition
);
if
(
mboxent
)
free
(
mboxent
);
return
r
;
}
/*
* Change the ACL for mailbox 'name' so that 'identifier' has the
* rights enumerated in the string 'rights'. If 'rights' is the null
* pointer, removes the ACL entry for 'identifier'. 'isadmin' is
* nonzero if user is a mailbox admin. 'userid' is the user's login id.
*
*
* 1. Start transaction
* 2. Check rights
* 3. Open ACAP connection if necessary
* 4. Set db entry
* 5. Change on disk
* 6. Commit transaction
* 7. Change ACAP entry
*
*/
int
mboxlist_setacl
(
char
*
name
,
char
*
identifier
,
char
*
rights
,
int
isadmin
,
char
*
userid
,
struct
auth_state
*
auth_state
)
{
int
useridlen
=
strlen
(
userid
);
int
r
;
int
access
;
int
mode
=
ACL_MODE_SET
;
int
isusermbox
=
0
;
struct
mailbox
mailbox
;
char
*
acl
,
*
newacl
=
NULL
;
char
*
partition
,
*
path
;
char
*
mboxent
=
NULL
;
int
mbtype
;
struct
txn
*
tid
=
NULL
;
if
(
!
strncmp
(
name
,
"user."
,
5
)
&&
!
strchr
(
userid
,
'.'
)
&&
!
strncmp
(
name
+
5
,
userid
,
useridlen
)
&&
(
name
[
5
+
useridlen
]
==
'\0'
||
name
[
5
+
useridlen
]
==
'.'
))
{
isusermbox
=
1
;
}
retry
:
/* lookup the mailbox to make sure it exists and get its acl */
r
=
mboxlist_mylookup
(
name
,
&
mbtype
,
&
path
,
&
partition
,
&
acl
,
&
tid
,
1
);
switch
(
r
)
{
case
0
:
break
;
case
IMAP_AGAIN
:
goto
retry
;
default
:
goto
done
;
}
if
(
!
r
&&
!
isadmin
&&
!
isusermbox
)
{
access
=
cyrus_acl_myrights
(
auth_state
,
acl
);
if
(
!
(
access
&
ACL_ADMIN
))
{
r
=
(
access
&
ACL_LOOKUP
)
?
IMAP_PERMISSION_DENIED
:
IMAP_MAILBOX_NONEXISTENT
;
goto
done
;
}
}
/* Make change to ACL */
newacl
=
xstrdup
(
acl
);
if
(
rights
)
{
mode
=
ACL_MODE_SET
;
if
(
*
rights
==
'+'
)
{
rights
++
;
mode
=
ACL_MODE_ADD
;
}
else
if
(
*
rights
==
'-'
)
{
rights
++
;
mode
=
ACL_MODE_REMOVE
;
}
if
(
cyrus_acl_set
(
&
newacl
,
identifier
,
mode
,
cyrus_acl_strtomask
(
rights
),
isusermbox
?
mboxlist_ensureOwnerRights
:
0
,
(
void
*
)
userid
))
{
r
=
IMAP_INVALID_IDENTIFIER
;
goto
done
;
}
}
else
{
if
(
cyrus_acl_remove
(
&
newacl
,
identifier
,
isusermbox
?
mboxlist_ensureOwnerRights
:
0
,
(
void
*
)
userid
))
{
r
=
IMAP_INVALID_IDENTIFIER
;
goto
done
;
}
}
/* ok, make the change */
mboxent
=
mboxlist_makeentry
(
mbtype
,
partition
,
newacl
);
r
=
DB
->
store
(
mbdb
,
name
,
strlen
(
name
),
mboxent
,
strlen
(
mboxent
),
&
tid
);
switch
(
r
)
{
case
0
:
break
;
case
CYRUSDB_AGAIN
:
goto
retry
;
default
:
syslog
(
LOG_ERR
,
"DBERROR: error updating acl %s: %s"
,
name
,
cyrusdb_strerror
(
r
));
r
=
IMAP_IOERROR
;
goto
done
;
}
if
(
!
(
mbtype
&
MBTYPE_REMOTE
))
{
/* open & lock mailbox header */
r
=
mailbox_open_header_path
(
name
,
path
,
acl
,
NULL
,
&
mailbox
,
0
);
if
(
!
r
)
{
r
=
mailbox_lock_header
(
&
mailbox
);
if
(
!
r
)
{
/* set it in the /var/spool part */
(
void
)
mailbox_write_header
(
&
mailbox
);
}
mailbox_close
(
&
mailbox
);
}
}
done
:
if
(
mboxent
)
free
(
mboxent
);
if
(
r
)
{
int
r2
;
if
((
r2
=
DB
->
abort
(
mbdb
,
tid
))
!=
0
)
{
syslog
(
LOG_ERR
,
"DBERROR: error aborting txn: %s"
,
cyrusdb_strerror
(
r2
));
r2
=
IMAP_IOERROR
;
}
}
else
{
/* commit now */
switch
(
r
=
DB
->
commit
(
mbdb
,
tid
))
{
case
0
:
break
;
default
:
syslog
(
LOG_ERR
,
"DBERROR: failed on commit: %s"
,
cyrusdb_strerror
(
r
));
r
=
IMAP_IOERROR
;
}
}
/* 7. Change ACAP entry */
if
(
!
r
)
{
acapmbox_handle_t
*
acaphandle
=
acapmbox_get_handle
();
r
=
acapmbox_setproperty_acl
(
acaphandle
,
name
,
newacl
);
}
if
(
newacl
)
free
(
newacl
);
return
r
;
}
struct
find_rock
{
struct
glob
*
g
;
int
inbox
;
int
inboxoffset
;
const
char
*
inboxcase
;
const
char
*
usermboxname
;
int
usermboxnamelen
;
int
checkmboxlist
;
int
isadmin
;
struct
auth_state
*
auth_state
;
int
(
*
proc
)(
char
*
,
int
,
int
,
void
*
rock
);
void
*
procrock
;
};
/* return non-zero if we like this one */
static
int
find_p
(
void
*
rockp
,
const
char
*
key
,
int
keylen
)
{
char
namebuf
[
MAX_MAILBOX_NAME
+
1
];
char
namematchbuf
[
MAX_MAILBOX_NAME
+
1
];
struct
find_rock
*
rock
=
(
struct
find_rock
*
)
rockp
;
long
minmatch
;
struct
glob
*
g
=
rock
->
g
;
/* foreach match, do this test */
minmatch
=
0
;
while
(
minmatch
>=
0
)
{
long
matchlen
;
memcpy
(
namebuf
,
key
,
keylen
);
namebuf
[
keylen
]
=
'\0'
;
strcpy
(
namematchbuf
,
namebuf
);
if
(
!
rock
->
inbox
&&
rock
->
usermboxname
&&
!
strncmp
(
namebuf
,
rock
->
usermboxname
,
rock
->
usermboxnamelen
)
&&
(
keylen
==
rock
->
usermboxnamelen
||
namebuf
[
rock
->
usermboxnamelen
]
==
'.'
))
{
/* this would've been output with the inbox stuff, so skip it */
return
0
;
}
if
(
rock
->
inboxoffset
)
{
namematchbuf
[
rock
->
inboxoffset
]
=
rock
->
inboxcase
[
0
];
namematchbuf
[
rock
->
inboxoffset
+
1
]
=
rock
->
inboxcase
[
1
];
namematchbuf
[
rock
->
inboxoffset
+
2
]
=
rock
->
inboxcase
[
2
];
namematchbuf
[
rock
->
inboxoffset
+
3
]
=
rock
->
inboxcase
[
3
];
namematchbuf
[
rock
->
inboxoffset
+
4
]
=
rock
->
inboxcase
[
4
];
}
matchlen
=
glob_test
(
g
,
namematchbuf
+
rock
->
inboxoffset
,
keylen
-
rock
->
inboxoffset
,
&
minmatch
);
if
(
matchlen
==
-1
)
break
;
/* if we get here, close enough for us to spend the time
acting interested */
return
1
;
}
return
0
;
}
static
int
find_cb
(
void
*
rockp
,
const
char
*
key
,
int
keylen
,
const
char
*
data
,
int
datalen
)
{
char
namebuf
[
MAX_MAILBOX_NAME
+
1
];
char
namematchbuf
[
MAX_MAILBOX_NAME
+
1
];
struct
find_rock
*
rock
=
(
struct
find_rock
*
)
rockp
;
int
r
=
0
;
long
minmatch
;
struct
glob
*
g
=
rock
->
g
;
/* foreach match, do this test */
minmatch
=
0
;
while
(
minmatch
>=
0
)
{
long
matchlen
;
memcpy
(
namebuf
,
key
,
keylen
);
namebuf
[
keylen
]
=
'\0'
;
strcpy
(
namematchbuf
,
namebuf
);
if
(
!
rock
->
inbox
&&
rock
->
usermboxname
&&
!
strncmp
(
namebuf
,
rock
->
usermboxname
,
rock
->
usermboxnamelen
)
&&
(
keylen
==
rock
->
usermboxnamelen
||
namebuf
[
rock
->
usermboxnamelen
]
==
'.'
))
{
/* this would've been output with the inbox stuff, so skip it */
return
0
;
}
if
(
rock
->
inboxoffset
)
{
namematchbuf
[
rock
->
inboxoffset
]
=
rock
->
inboxcase
[
0
];
namematchbuf
[
rock
->
inboxoffset
+
1
]
=
rock
->
inboxcase
[
1
];
namematchbuf
[
rock
->
inboxoffset
+
2
]
=
rock
->
inboxcase
[
2
];
namematchbuf
[
rock
->
inboxoffset
+
3
]
=
rock
->
inboxcase
[
3
];
namematchbuf
[
rock
->
inboxoffset
+
4
]
=
rock
->
inboxcase
[
4
];
}
matchlen
=
glob_test
(
g
,
namematchbuf
+
rock
->
inboxoffset
,
keylen
-
rock
->
inboxoffset
,
&
minmatch
);
if
(
matchlen
==
-1
)
break
;
/* make sure it's in the mailboxes db */
if
(
rock
->
checkmboxlist
)
{
r
=
mboxlist_lookup
(
namebuf
,
NULL
,
NULL
,
NULL
);
}
else
{
r
=
0
;
/* don't bother checking */
}
if
(
!
r
&&
!
rock
->
isadmin
)
{
/* check the acls */
const
char
*
p
,
*
acl
;
char
aclbuf
[
1024
];
int
rights
;
int
acllen
;
p
=
strchr
(
data
,
' '
);
if
(
!
p
)
{
syslog
(
LOG_ERR
,
"%s: can't find partition"
,
namebuf
);
return
IMAP_IOERROR
;
}
p
++
;
acl
=
strchr
(
p
,
' '
);
if
(
!
acl
)
{
syslog
(
LOG_ERR
,
"%s: can't find acl"
,
namebuf
);
return
IMAP_IOERROR
;
}
acl
++
;
acllen
=
datalen
-
(
acl
-
data
);
if
(
acllen
<
sizeof
(
aclbuf
)
-
1
)
{
memcpy
(
aclbuf
,
acl
,
acllen
);
aclbuf
[
acllen
]
=
'\0'
;
rights
=
cyrus_acl_myrights
(
rock
->
auth_state
,
aclbuf
);
}
else
{
char
*
a
=
xstrndup
(
acl
,
datalen
-
(
acl
-
data
));
rights
=
cyrus_acl_myrights
(
rock
->
auth_state
,
a
);
free
(
a
);
}
if
(
!
(
rights
&
ACL_LOOKUP
))
{
r
=
IMAP_MAILBOX_NONEXISTENT
;
}
}
switch
(
r
)
{
case
0
:
/* found the entry; output it */
r
=
(
*
rock
->
proc
)(
namematchbuf
+
rock
->
inboxoffset
,
matchlen
,
1
,
rock
->
procrock
);
break
;
case
IMAP_MAILBOX_NONEXISTENT
:
/* didn't find the entry */
r
=
0
;
break
;
default
:
break
;
}
if
(
r
)
break
;
}
return
r
;
}
/*
* Find all mailboxes that match 'pattern'.
* 'isadmin' is nonzero if user is a mailbox admin. 'userid'
* is the user's login id. For each matching mailbox, calls
* 'proc' with the name of the mailbox. If 'proc' ever returns
* a nonzero value, mboxlist_findall immediately stops searching
* and returns that value. 'rock' is passed along as an argument to proc in
* case it wants some persistant storage or extra data.
*/
/* Find all mailboxes that match 'pattern'. */
int
mboxlist_findall
(
char
*
pattern
,
int
isadmin
,
char
*
userid
,
struct
auth_state
*
auth_state
,
int
(
*
proc
)(),
void
*
rock
)
{
struct
find_rock
cbrock
;
char
usermboxname
[
MAX_MAILBOX_NAME
+
1
];
int
usermboxnamelen
=
0
;
const
char
*
data
;
int
datalen
;
int
r
=
0
;
char
*
p
;
int
prefixlen
;
cbrock
.
g
=
glob_init
(
pattern
,
GLOB_HIERARCHY
|
GLOB_INBOXCASE
);
cbrock
.
inboxcase
=
glob_inboxcase
(
cbrock
.
g
);
cbrock
.
isadmin
=
isadmin
;
cbrock
.
auth_state
=
auth_state
;
cbrock
.
checkmboxlist
=
0
;
/* don't duplicate work */
cbrock
.
proc
=
proc
;
cbrock
.
procrock
=
rock
;
/* Build usermboxname */
if
(
userid
&&
!
strchr
(
userid
,
'.'
)
&&
strlen
(
userid
)
+
5
<
MAX_MAILBOX_NAME
)
{
strcpy
(
usermboxname
,
"user."
);
strcat
(
usermboxname
,
userid
);
usermboxnamelen
=
strlen
(
usermboxname
);
}
else
{
userid
=
0
;
}
/* Check for INBOX first of all */
if
(
userid
)
{
if
(
GLOB_TEST
(
cbrock
.
g
,
"INBOX"
)
!=
-1
)
{
r
=
DB
->
fetch
(
mbdb
,
usermboxname
,
usermboxnamelen
,
&
data
,
&
datalen
,
NULL
);
if
(
!
r
&&
data
)
{
r
=
(
*
proc
)(
cbrock
.
inboxcase
,
5
,
1
,
rock
);
}
}
else
if
(
!
strncmp
(
pattern
,
usermboxname
,
usermboxnamelen
)
&&
GLOB_TEST
(
cbrock
.
g
,
usermboxname
)
!=
-1
)
{
r
=
DB
->
fetch
(
mbdb
,
usermboxname
,
usermboxnamelen
,
&
data
,
&
datalen
,
NULL
);
if
(
!
r
&&
data
)
{
r
=
(
*
proc
)(
usermboxname
,
usermboxnamelen
,
1
,
rock
);
}
}
strcpy
(
usermboxname
+
usermboxnamelen
,
"."
);
usermboxnamelen
++
;
cbrock
.
usermboxname
=
usermboxname
;
cbrock
.
usermboxnamelen
=
usermboxnamelen
;
}
else
{
cbrock
.
usermboxname
=
NULL
;
cbrock
.
usermboxnamelen
=
0
;
}
if
(
r
)
goto
done
;
/* Find fixed-string pattern prefix */
for
(
p
=
pattern
;
*
p
;
p
++
)
{
if
(
*
p
==
'*'
||
*
p
==
'%'
||
*
p
==
'?'
)
break
;
}
prefixlen
=
p
-
pattern
;
*
p
=
'\0'
;
/*
* If user.X.* or INBOX.* can match pattern,
* search for those mailboxes next
*/
if
(
userid
&&
(
!
strncmp
(
usermboxname
,
pattern
,
usermboxnamelen
-1
)
||
!
strncasecmp
(
"inbox."
,
pattern
,
prefixlen
<
6
?
prefixlen
:
6
)))
{
if
(
!
strncmp
(
usermboxname
,
pattern
,
usermboxnamelen
-1
))
{
cbrock
.
inboxoffset
=
0
;
}
else
{
cbrock
.
inboxoffset
=
strlen
(
userid
);
}
cbrock
.
inbox
=
1
;
/* iterate through prefixes matching usermboxname */
DB
->
foreach
(
mbdb
,
usermboxname
,
usermboxnamelen
,
&
find_p
,
&
find_cb
,
&
cbrock
,
NULL
);
}
cbrock
.
inbox
=
0
;
cbrock
.
inboxoffset
=
0
;
if
(
usermboxnamelen
)
{
usermboxname
[
--
usermboxnamelen
]
=
'\0'
;
cbrock
.
usermboxname
=
usermboxname
;
cbrock
.
usermboxnamelen
=
usermboxnamelen
;
}
/* search for all remaining mailboxes.
just bother looking at the ones that have the same pattern prefix. */
DB
->
foreach
(
mbdb
,
pattern
,
prefixlen
,
&
find_p
,
&
find_cb
,
&
cbrock
,
NULL
);
done
:
glob_free
(
&
cbrock
.
g
);
return
r
;
}
/*
* Set the quota on or create a quota root
*/
int
mboxlist_setquota
(
const
char
*
root
,
int
newquota
)
{
char
quota_path
[
MAX_MAILBOX_PATH
];
char
pattern
[
MAX_MAILBOX_PATH
];
struct
quota
quota
;
int
r
;
if
(
!
root
[
0
]
||
root
[
0
]
==
'.'
||
strchr
(
root
,
'/'
)
||
strchr
(
root
,
'*'
)
||
strchr
(
root
,
'%'
)
||
strchr
(
root
,
'?'
))
{
return
IMAP_MAILBOX_BADNAME
;
}
memset
(
&
quota
,
0
,
sizeof
(
struct
quota
));
quota
.
root
=
(
char
*
)
root
;
mailbox_hash_quota
(
quota_path
,
root
);
if
((
quota
.
fd
=
open
(
quota_path
,
O_RDWR
,
0
))
!=
-1
)
{
/* Just lock and change it */
r
=
mailbox_lock_quota
(
&
quota
);
quota
.
limit
=
newquota
;
if
(
!
r
)
r
=
mailbox_write_quota
(
&
quota
);
if
(
quota
.
fd
!=
-1
)
{
close
(
quota
.
fd
);
}
return
r
;
}
/*
* Have to create a new quota root
*/
/* look for a top-level mailbox in the proposed quotaroot */
r
=
mboxlist_lookup
(
quota
.
root
,
NULL
,
NULL
,
NULL
);
if
(
r
)
{
return
r
;
}
/* perhaps create .NEW, lock, check if it got recreated, move in place */
quota
.
lock_count
=
1
;
quota
.
used
=
0
;
quota
.
limit
=
newquota
;
r
=
mailbox_write_quota
(
&
quota
);
if
(
r
)
{
return
r
;
}
strcpy
(
pattern
,
quota
.
root
);
strcat
(
pattern
,
".*"
);
mboxlist_newquota
=
&
quota
;
/* top level mailbox */
mboxlist_changequota
(
quota
.
root
,
0
,
0
);
/* submailboxes */
mboxlist_findall
(
pattern
,
1
,
0
,
0
,
mboxlist_changequota
,
NULL
);
r
=
mailbox_write_quota
(
&
quota
);
if
(
quota
.
fd
!=
-1
)
{
close
(
quota
.
fd
);
}
return
r
;
}
/*
* Retrieve internal information, for reconstructing mailboxes file
*/
void
mboxlist_getinternalstuff
(
const
char
**
listfnamep
,
const
char
**
newlistfnamep
,
const
char
**
basep
,
unsigned
long
*
sizep
)
{
printf
(
"yikes! don't reconstruct me!
\n
"
);
abort
();
}
/*
* ACL access canonicalization routine which ensures that 'owner'
* retains lookup, administer, and create rights over a mailbox.
*/
int
mboxlist_ensureOwnerRights
(
rock
,
identifier
,
access
)
void
*
rock
;
const
char
*
identifier
;
int
access
;
{
char
*
owner
=
(
char
*
)
rock
;
if
(
strcmp
(
identifier
,
owner
)
!=
0
)
return
access
;
return
access
|
ACL_LOOKUP
|
ACL_ADMIN
|
ACL_CREATE
;
}
/*
* Helper function to change the quota root for 'name' to that pointed
* to by the static global struct pointer 'mboxlist_newquota'.
*/
static
int
mboxlist_changequota
(
name
,
matchlen
,
maycreate
)
char
*
name
;
int
matchlen
;
int
maycreate
;
{
int
r
;
struct
mailbox
mailbox
;
r
=
mailbox_open_header
(
name
,
0
,
&
mailbox
);
if
(
r
)
goto
error_noclose
;
r
=
mailbox_lock_header
(
&
mailbox
);
if
(
r
)
goto
error
;
r
=
mailbox_open_index
(
&
mailbox
);
if
(
r
)
goto
error
;
r
=
mailbox_lock_index
(
&
mailbox
);
if
(
r
)
goto
error
;
if
(
mailbox
.
quota
.
root
)
{
if
(
strlen
(
mailbox
.
quota
.
root
)
>=
strlen
(
mboxlist_newquota
->
root
))
{
/* Part of a child quota root */
mailbox_close
(
&
mailbox
);
return
0
;
}
r
=
mailbox_lock_quota
(
&
mailbox
.
quota
);
if
(
r
)
goto
error
;
if
(
mailbox
.
quota
.
used
>=
mailbox
.
quota_mailbox_used
)
{
mailbox
.
quota
.
used
-=
mailbox
.
quota_mailbox_used
;
}
else
{
mailbox
.
quota
.
used
=
0
;
}
r
=
mailbox_write_quota
(
&
mailbox
.
quota
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"LOSTQUOTA: unable to record free of %u bytes in quota %s"
,
mailbox
.
quota_mailbox_used
,
mailbox
.
quota
.
root
);
}
mailbox_unlock_quota
(
&
mailbox
.
quota
);
free
(
mailbox
.
quota
.
root
);
}
mailbox
.
quota
.
root
=
xstrdup
(
mboxlist_newquota
->
root
);
r
=
mailbox_write_header
(
&
mailbox
);
if
(
r
)
goto
error
;
mboxlist_newquota
->
used
+=
mailbox
.
quota_mailbox_used
;
mailbox_close
(
&
mailbox
);
return
0
;
error
:
mailbox_close
(
&
mailbox
);
error_noclose
:
syslog
(
LOG_ERR
,
"LOSTQUOTA: unable to change quota root for %s to %s: %s"
,
name
,
mboxlist_newquota
->
root
,
error_message
(
r
));
return
0
;
}
void
mboxlist_init
(
int
myflags
)
{
int
r
;
char
dbdir
[
1024
];
int
flags
=
0
;
/* create the name of the db file */
strcpy
(
dbdir
,
config_dir
);
strcat
(
dbdir
,
FNAME_DBDIR
);
if
(
myflags
&
MBOXLIST_RECOVER
)
flags
|=
CYRUSDB_RECOVER
;
r
=
DB
->
init
(
dbdir
,
flags
);
if
(
r
!=
CYRUSDB_OK
)
{
fatal
(
"can't initialize mboxlist environment"
,
EC_TEMPFAIL
);
}
if
(
myflags
&
MBOXLIST_SYNC
)
{
r
=
DB
->
sync
();
}
r
=
acap_init
();
if
(
r
!=
ACAP_OK
)
{
syslog
(
LOG_ERR
,
"acap_init failed()"
);
}
}
void
mboxlist_open
(
char
*
fname
)
{
int
ret
;
char
*
tofree
=
NULL
;
/* create db file name */
if
(
!
fname
)
{
fname
=
xmalloc
(
strlen
(
config_dir
)
+
sizeof
(
FNAME_MBOXLIST
));
tofree
=
fname
;
strcpy
(
fname
,
config_dir
);
strcat
(
fname
,
FNAME_MBOXLIST
);
}
ret
=
DB
->
open
(
fname
,
&
mbdb
);
if
(
ret
!=
0
)
{
syslog
(
LOG_ERR
,
"DBERROR: opening %s: %s"
,
fname
,
cyrusdb_strerror
(
ret
));
/* Exiting TEMPFAIL because Sendmail thinks this
EC_OSFILE == permanent failure. */
fatal
(
"can't read mailboxes file"
,
EC_TEMPFAIL
);
}
if
(
tofree
)
free
(
tofree
);
mboxlist_dbopen
=
1
;
}
void
mboxlist_close
(
void
)
{
int
r
;
if
(
mboxlist_dbopen
)
{
r
=
DB
->
close
(
mbdb
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"DBERROR: error closing mailboxes: %s"
,
cyrusdb_strerror
(
r
));
}
mboxlist_dbopen
=
0
;
}
}
void
mboxlist_done
(
void
)
{
int
r
;
r
=
DB
->
done
();
if
(
r
)
{
syslog
(
LOG_ERR
,
"DBERROR: error exiting application: %s"
,
cyrusdb_strerror
(
r
));
}
/* finish ACAP API here */
}
/* hash the userid to a file containing the subscriptions for that user */
static
char
*
mboxlist_hash_usersubs
(
const
char
*
userid
)
{
char
*
fname
=
xmalloc
(
strlen
(
config_dir
)
+
sizeof
(
FNAME_USERDIR
)
+
strlen
(
userid
)
+
sizeof
(
FNAME_SUBSSUFFIX
)
+
10
);
char
c
;
c
=
(
char
)
tolower
((
int
)
*
userid
);
if
(
!
islower
((
int
)
c
))
{
c
=
'q'
;
}
sprintf
(
fname
,
"%s%s%c/%s%s"
,
config_dir
,
FNAME_USERDIR
,
c
,
userid
,
FNAME_SUBSSUFFIX
);
return
fname
;
}
/*
* Open the subscription list for 'userid'.
*
* On success, returns zero.
* On failure, returns an error code.
*/
static
int
mboxlist_opensubs
(
const
char
*
userid
,
struct
db
**
ret
)
{
int
r
=
0
;
char
*
subsfname
;
char
inboxname
[
MAX_MAILBOX_NAME
+
1
];
/* Users without INBOXes may not keep subscriptions */
if
(
strchr
(
userid
,
'.'
)
||
strlen
(
userid
)
+
6
>
MAX_MAILBOX_NAME
)
{
return
IMAP_PERMISSION_DENIED
;
}
strcpy
(
inboxname
,
"user."
);
strcat
(
inboxname
,
userid
);
if
(
mboxlist_lookup
(
inboxname
,
NULL
,
NULL
,
NULL
)
!=
0
)
{
return
IMAP_PERMISSION_DENIED
;
}
/* Build subscription list filename */
subsfname
=
mboxlist_hash_usersubs
(
userid
);
r
=
SUBDB
->
open
(
subsfname
,
ret
);
if
(
r
!=
CYRUSDB_OK
)
{
r
=
IMAP_IOERROR
;
}
free
(
subsfname
);
return
r
;
}
/*
* Close a subscription file
*/
static
void
mboxlist_closesubs
(
struct
db
*
sub
)
{
SUBDB
->
close
(
sub
);
}
/*
* Find subscribed mailboxes that match 'pattern'.
* 'isadmin' is nonzero if user is a mailbox admin. 'userid'
* is the user's login id. For each matching mailbox, calls
* 'proc' with the name of the mailbox.
*/
int
mboxlist_findsub
(
char
*
pattern
,
int
isadmin
,
char
*
userid
,
struct
auth_state
*
auth_state
,
int
(
*
proc
)(),
void
*
rock
,
int
force
)
{
struct
db
*
subs
=
NULL
;
struct
find_rock
cbrock
;
char
usermboxname
[
MAX_MAILBOX_NAME
+
1
];
int
usermboxnamelen
=
0
;
const
char
*
data
;
int
datalen
;
int
r
=
0
;
char
*
p
;
int
prefixlen
;
/* open the subscription file that contains the mailboxes the
user is subscribed to */
if
((
r
=
mboxlist_opensubs
(
userid
,
&
subs
))
!=
0
)
{
goto
done
;
}
cbrock
.
g
=
glob_init
(
pattern
,
GLOB_HIERARCHY
|
GLOB_INBOXCASE
);
cbrock
.
inboxcase
=
glob_inboxcase
(
cbrock
.
g
);
cbrock
.
isadmin
=
1
;
/* user can always see their subs */
cbrock
.
auth_state
=
auth_state
;
cbrock
.
checkmboxlist
=
!
force
;
cbrock
.
proc
=
proc
;
cbrock
.
procrock
=
rock
;
/* Build usermboxname */
if
(
userid
&&
!
strchr
(
userid
,
'.'
)
&&
strlen
(
userid
)
+
5
<
MAX_MAILBOX_NAME
)
{
strcpy
(
usermboxname
,
"user."
);
strcat
(
usermboxname
,
userid
);
usermboxnamelen
=
strlen
(
usermboxname
);
}
else
{
userid
=
0
;
}
/* Check for INBOX first of all */
if
(
userid
)
{
if
(
GLOB_TEST
(
cbrock
.
g
,
"INBOX"
)
!=
-1
)
{
r
=
SUBDB
->
fetch
(
subs
,
usermboxname
,
usermboxnamelen
,
&
data
,
&
datalen
,
NULL
);
if
(
!
r
&&
data
)
{
r
=
(
*
proc
)(
cbrock
.
inboxcase
,
5
,
1
,
rock
);
}
}
else
if
(
!
strncmp
(
pattern
,
usermboxname
,
usermboxnamelen
)
&&
GLOB_TEST
(
cbrock
.
g
,
usermboxname
)
!=
-1
)
{
r
=
SUBDB
->
fetch
(
subs
,
usermboxname
,
usermboxnamelen
,
&
data
,
&
datalen
,
NULL
);
if
(
!
r
&&
data
)
{
r
=
(
*
proc
)(
usermboxname
,
usermboxnamelen
,
1
,
rock
);
}
}
strcpy
(
usermboxname
+
usermboxnamelen
,
"."
);
usermboxnamelen
++
;
cbrock
.
usermboxname
=
usermboxname
;
cbrock
.
usermboxnamelen
=
usermboxnamelen
;
}
if
(
r
)
goto
done
;
/* Find fixed-string pattern prefix */
for
(
p
=
pattern
;
*
p
;
p
++
)
{
if
(
*
p
==
'*'
||
*
p
==
'%'
||
*
p
==
'?'
)
break
;
}
prefixlen
=
p
-
pattern
;
*
p
=
'\0'
;
/*
* If user.X.* or INBOX.* can match pattern,
* search for those mailboxes next
*/
if
(
userid
&&
(
!
strncmp
(
usermboxname
,
pattern
,
usermboxnamelen
-1
)
||
!
strncasecmp
(
"inbox."
,
pattern
,
prefixlen
<
6
?
prefixlen
:
6
)))
{
if
(
!
strncmp
(
usermboxname
,
pattern
,
usermboxnamelen
-1
))
{
cbrock
.
inboxoffset
=
0
;
}
else
{
cbrock
.
inboxoffset
=
strlen
(
userid
);
}
cbrock
.
inbox
=
1
;
/* iterate through prefixes matching usermboxname */
SUBDB
->
foreach
(
subs
,
usermboxname
,
usermboxnamelen
,
&
find_p
,
&
find_cb
,
&
cbrock
,
NULL
);
cbrock
.
usermboxname
=
usermboxname
;
cbrock
.
usermboxnamelen
=
usermboxnamelen
;
}
else
{
cbrock
.
usermboxname
=
NULL
;
cbrock
.
usermboxnamelen
=
0
;
}
cbrock
.
inbox
=
0
;
cbrock
.
inboxoffset
=
0
;
if
(
usermboxnamelen
)
{
usermboxname
[
--
usermboxnamelen
]
=
'\0'
;
cbrock
.
usermboxname
=
usermboxname
;
cbrock
.
usermboxnamelen
=
usermboxnamelen
;
}
/* search for all remaining mailboxes.
just bother looking at the ones that have the same pattern prefix. */
SUBDB
->
foreach
(
subs
,
pattern
,
prefixlen
,
&
find_p
,
&
find_cb
,
&
cbrock
,
NULL
);
done
:
if
(
subs
)
mboxlist_closesubs
(
subs
);
glob_free
(
&
cbrock
.
g
);
return
r
;
}
/*
* Change 'user's subscription status for mailbox 'name'.
* Subscribes if 'add' is nonzero, unsubscribes otherwise.
* if 'force' is set, force the subscription through even if
* we don't know about 'name'.
*/
int
mboxlist_changesub
(
const
char
*
name
,
const
char
*
userid
,
struct
auth_state
*
auth_state
,
int
add
,
int
force
)
{
int
r
;
char
*
acl
;
struct
db
*
subs
;
if
((
r
=
mboxlist_opensubs
(
userid
,
&
subs
))
!=
0
)
{
return
r
;
}
if
(
add
&&
!
force
)
{
/* Ensure mailbox exists and can be either seen or read by user */
if
((
r
=
mboxlist_lookup
(
name
,
NULL
,
&
acl
,
NULL
))
!=
0
)
{
mboxlist_closesubs
(
subs
);
return
r
;
}
if
((
cyrus_acl_myrights
(
auth_state
,
acl
)
&
(
ACL_READ
|
ACL_LOOKUP
))
==
0
)
{
mboxlist_closesubs
(
subs
);
return
IMAP_MAILBOX_NONEXISTENT
;
}
}
if
(
add
)
{
r
=
SUBDB
->
store
(
subs
,
name
,
strlen
(
name
),
""
,
0
,
NULL
);
}
else
{
r
=
SUBDB
->
delete
(
subs
,
name
,
strlen
(
name
),
NULL
);
/* if it didn't exist, that's ok */
if
(
r
==
CYRUSDB_EXISTS
)
r
=
CYRUSDB_OK
;
}
switch
(
r
)
{
case
CYRUSDB_OK
:
r
=
0
;
break
;
default
:
r
=
IMAP_IOERROR
;
break
;
}
mboxlist_closesubs
(
subs
);
return
r
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Mon, Apr 6, 2:24 AM (1 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18832030
Default Alt Text
mboxlist.c (48 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline