Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F120835428
mboxname.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
31 KB
Referenced Files
None
Subscribers
None
mboxname.c
View Options
/* mboxname.c -- Mailbox list manipulation routines
*
* Copyright (c) 1994-2008 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 legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@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: mboxname.c,v 1.51 2010/06/28 12:04:30 brong Exp $
*/
#include
<config.h>
#include
<stdio.h>
#include
<string.h>
#include
<errno.h>
#include
<syslog.h>
#ifdef HAVE_UNISTD_H
#include
<unistd.h>
#endif
#include
"assert.h"
#include
"exitcodes.h"
#include
"glob.h"
#include
"global.h"
#include
"imap_err.h"
#include
"mailbox.h"
#include
"util.h"
#include
"xmalloc.h"
#include
"mboxname.h"
#include
"mboxlist.h"
#include
"cyr_lock.h"
struct
mboxlocklist
{
struct
mboxlocklist
*
next
;
struct
mboxlock
l
;
int
nopen
;
};
static
struct
mboxlocklist
*
open_mboxlocks
=
NULL
;
/* Mailbox patterns which the design of the server prohibits */
static
char
*
badmboxpatterns
[]
=
{
""
,
"*
\t
*"
,
"*
\n
*"
,
"*/*"
,
".*"
,
"*."
,
"*..*"
,
"user"
,
"*.INBOX.INBOX*"
,
};
#define NUM_BADMBOXPATTERNS (sizeof(badmboxpatterns)/sizeof(*badmboxpatterns))
#define XX 127
/*
* Table for decoding modified base64 for IMAP UTF-7 mailbox names
*/
static
const
char
index_mod64
[
256
]
=
{
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
62
,
63
,
XX
,
XX
,
XX
,
52
,
53
,
54
,
55
,
56
,
57
,
58
,
59
,
60
,
61
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
11
,
12
,
13
,
14
,
15
,
16
,
17
,
18
,
19
,
20
,
21
,
22
,
23
,
24
,
25
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
26
,
27
,
28
,
29
,
30
,
31
,
32
,
33
,
34
,
35
,
36
,
37
,
38
,
39
,
40
,
41
,
42
,
43
,
44
,
45
,
46
,
47
,
48
,
49
,
50
,
51
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
};
#define CHARMOD64(c) (index_mod64[(unsigned char)(c)])
static
struct
mboxlocklist
*
create_lockitem
(
const
char
*
name
)
{
struct
mboxlocklist
*
item
=
xmalloc
(
sizeof
(
struct
mboxlocklist
));
item
->
next
=
open_mboxlocks
;
open_mboxlocks
=
item
;
item
->
nopen
=
1
;
item
->
l
.
name
=
xstrdup
(
name
);
item
->
l
.
lock_fd
=
-1
;
item
->
l
.
locktype
=
0
;
return
item
;
}
struct
mboxlocklist
*
find_lockitem
(
const
char
*
name
)
{
struct
mboxlocklist
*
item
;
struct
mboxlocklist
*
previtem
=
NULL
;
/* remove from the active list */
for
(
item
=
open_mboxlocks
;
item
;
item
=
item
->
next
)
{
if
(
!
strcmp
(
name
,
item
->
l
.
name
))
return
item
;
previtem
=
item
;
}
return
NULL
;
}
void
remove_lockitem
(
struct
mboxlocklist
*
remitem
)
{
struct
mboxlocklist
*
item
;
struct
mboxlocklist
*
previtem
=
NULL
;
for
(
item
=
open_mboxlocks
;
item
;
item
=
item
->
next
)
{
if
(
item
==
remitem
)
{
if
(
previtem
)
previtem
->
next
=
item
->
next
;
else
open_mboxlocks
=
item
->
next
;
if
(
item
->
l
.
lock_fd
!=
-1
)
close
(
item
->
l
.
lock_fd
);
free
(
item
->
l
.
name
);
free
(
item
);
return
;
}
previtem
=
item
;
}
fatal
(
"didn't find item in list"
,
EC_SOFTWARE
);
}
/* name locking support */
int
mboxname_lock
(
const
char
*
mboxname
,
struct
mboxlock
**
mboxlockptr
,
int
locktype
)
{
const
char
*
fname
;
int
r
=
0
;
struct
mboxlocklist
*
lockitem
;
fname
=
mboxname_lockpath
(
mboxname
);
if
(
!
fname
)
return
IMAP_MAILBOX_BADNAME
;
lockitem
=
find_lockitem
(
mboxname
);
/* already open? just use this one */
if
(
lockitem
)
{
if
(
locktype
==
LOCK_NONBLOCKING
)
locktype
=
LOCK_EXCLUSIVE
;
/* can't change locktype! */
if
(
lockitem
->
l
.
locktype
!=
locktype
)
return
IMAP_MAILBOX_LOCKED
;
lockitem
->
nopen
++
;
goto
done
;
}
lockitem
=
create_lockitem
(
mboxname
);
/* assume success, and only create directory on failure.
* More efficient on a common codepath */
lockitem
->
l
.
lock_fd
=
open
(
fname
,
O_CREAT
|
O_TRUNC
|
O_RDWR
,
0666
);
if
(
lockitem
->
l
.
lock_fd
==
-1
)
{
if
(
cyrus_mkdir
(
fname
,
0755
)
==
-1
)
{
r
=
IMAP_IOERROR
;
goto
done
;
}
lockitem
->
l
.
lock_fd
=
open
(
fname
,
O_CREAT
|
O_TRUNC
|
O_RDWR
,
0666
);
}
/* but if it still didn't succeed, we have problems */
if
(
lockitem
->
l
.
lock_fd
==
-1
)
{
r
=
IMAP_IOERROR
;
goto
done
;
}
switch
(
locktype
)
{
case
LOCK_SHARED
:
r
=
lock_shared
(
lockitem
->
l
.
lock_fd
);
if
(
!
r
)
lockitem
->
l
.
locktype
=
LOCK_SHARED
;
break
;
case
LOCK_EXCLUSIVE
:
r
=
lock_blocking
(
lockitem
->
l
.
lock_fd
);
if
(
!
r
)
lockitem
->
l
.
locktype
=
LOCK_EXCLUSIVE
;
break
;
case
LOCK_NONBLOCKING
:
r
=
lock_nonblocking
(
lockitem
->
l
.
lock_fd
);
if
(
r
==
-1
)
r
=
IMAP_MAILBOX_LOCKED
;
else
if
(
!
r
)
lockitem
->
l
.
locktype
=
LOCK_EXCLUSIVE
;
break
;
default
:
fatal
(
"unknown lock type"
,
EC_SOFTWARE
);
}
done
:
if
(
r
)
remove_lockitem
(
lockitem
);
else
*
mboxlockptr
=
&
lockitem
->
l
;
return
r
;
}
void
mboxname_release
(
struct
mboxlock
**
mboxlockptr
)
{
struct
mboxlocklist
*
lockitem
;
struct
mboxlock
*
lock
=
*
mboxlockptr
;
lockitem
=
find_lockitem
(
lock
->
name
);
assert
(
lockitem
&&
&
lockitem
->
l
==
lock
);
*
mboxlockptr
=
NULL
;
if
(
lockitem
->
nopen
>
1
)
{
lockitem
->
nopen
--
;
return
;
}
remove_lockitem
(
lockitem
);
}
/*
* Convert the external mailbox 'name' to an internal name.
* If 'userid' is non-null, it is the name of the current user.
* On success, results are placed in the buffer pointed to by
* 'result', the buffer must be of size MAX_MAILBOX_BUFFER to
* allow space for DELETED mailboxes and moving the domain from
* one end to the other and such. Yay flexibility.
*/
/* Handle conversion from the standard namespace to the internal namespace */
static
int
mboxname_tointernal
(
struct
namespace
*
namespace
,
const
char
*
name
,
const
char
*
userid
,
char
*
result
)
{
char
*
cp
;
char
*
atp
;
char
*
mbresult
;
int
userlen
,
domainlen
=
0
,
namelen
;
/* Blank the result, just in case */
result
[
0
]
=
'\0'
;
result
[
MAX_MAILBOX_BUFFER
-1
]
=
'\0'
;
userlen
=
userid
?
strlen
(
userid
)
:
0
;
namelen
=
strlen
(
name
);
if
(
config_virtdomains
)
{
if
(
userid
&&
(
cp
=
strrchr
(
userid
,
'@'
)))
{
/* user logged in as user@domain */
userlen
=
cp
-
userid
;
/* don't prepend default domain */
if
(
!
(
config_defdomain
&&
!
strcasecmp
(
config_defdomain
,
cp
+
1
)))
{
domainlen
=
strlen
(
cp
+
1
)
+
1
;
snprintf
(
result
,
MAX_MAILBOX_BUFFER
,
"%s!"
,
cp
+
1
);
}
}
if
((
cp
=
strrchr
(
name
,
'@'
)))
{
/* mailbox specified as mbox@domain */
namelen
=
cp
-
name
;
if
(
config_defdomain
&&
!
strcasecmp
(
config_defdomain
,
cp
+
1
))
{
if
(
domainlen
)
{
/* don't allow cross-domain access */
return
IMAP_MAILBOX_BADNAME
;
}
/* don't prepend default domain */
}
else
{
if
((
!
domainlen
&&
!
namespace
->
isadmin
)
||
(
domainlen
&&
strcasecmp
(
userid
+
userlen
,
cp
)))
{
/* don't allow cross-domain access
(except for global admin) */
return
IMAP_MAILBOX_BADNAME
;
}
domainlen
=
strlen
(
cp
+
1
)
+
1
;
snprintf
(
result
,
MAX_MAILBOX_BUFFER
,
"%s!"
,
cp
+
1
);
}
atp
=
strchr
(
name
,
'@'
);
if
(
atp
&&
atp
!=
cp
)
{
/* don't allow multiple '@' in name */
return
IMAP_MAILBOX_BADNAME
;
}
}
/* if no domain specified, we're in the default domain */
}
mbresult
=
result
+
domainlen
;
/* Personal (INBOX) namespace */
if
((
name
[
0
]
==
'i'
||
name
[
0
]
==
'I'
)
&&
!
strncasecmp
(
name
,
"inbox"
,
5
)
&&
(
namelen
==
5
||
name
[
5
]
==
namespace
->
hier_sep
))
{
if
(
!
userid
||
((
cp
=
strchr
(
userid
,
namespace
->
hier_sep
))
&&
(
cp
-
userid
<
userlen
)))
{
return
IMAP_MAILBOX_BADNAME
;
}
snprintf
(
mbresult
,
MAX_MAILBOX_BUFFER
-
domainlen
,
"user.%.*s%.*s"
,
userlen
,
userid
,
namelen
-5
,
name
+
5
);
/* Translate any separators in userid+mailbox */
mboxname_hiersep_tointernal
(
namespace
,
mbresult
+
5
+
userlen
,
0
);
}
else
{
/* Other Users & Shared namespace */
snprintf
(
mbresult
,
MAX_MAILBOX_BUFFER
-
domainlen
,
"%.*s"
,
namelen
,
name
);
/* Translate any separators in mailboxname */
mboxname_hiersep_tointernal
(
namespace
,
mbresult
,
0
);
}
if
(
result
[
MAX_MAILBOX_BUFFER
-1
]
!=
'\0'
)
{
syslog
(
LOG_ERR
,
"IOERROR: long mailbox name attempt: %s"
,
name
);
return
IMAP_MAILBOX_BADNAME
;
}
return
0
;
}
/* Handle conversion from the alternate namespace to the internal namespace */
static
int
mboxname_tointernal_alt
(
struct
namespace
*
namespace
,
const
char
*
name
,
const
char
*
userid
,
char
*
result
)
{
char
*
cp
;
int
userlen
,
domainlen
=
0
,
namelen
;
int
prefixlen
;
size_t
resultlen
;
/* Blank the result, just in case */
result
[
0
]
=
'\0'
;
userlen
=
userid
?
strlen
(
userid
)
:
0
;
namelen
=
strlen
(
name
);
if
(
config_virtdomains
)
{
if
(
userid
&&
(
cp
=
strchr
(
userid
,
'@'
)))
{
/* user logged in as user@domain */
userlen
=
cp
++
-
userid
;
if
(
!
(
config_defdomain
&&
!
strcasecmp
(
config_defdomain
,
cp
)))
{
/* don't prepend default domain */
domainlen
=
strlen
(
cp
)
+
1
;
if
(
domainlen
>
MAX_MAILBOX_NAME
)
return
IMAP_MAILBOX_BADNAME
;
sprintf
(
result
,
"%s!"
,
cp
);
}
}
if
((
cp
=
strrchr
(
name
,
'@'
)))
{
/* mailbox specified as mbox@domain */
namelen
=
cp
-
name
;
if
(
config_defdomain
&&
!
strcasecmp
(
config_defdomain
,
cp
+
1
))
{
if
(
domainlen
)
{
/* don't allow cross-domain access */
return
IMAP_MAILBOX_BADNAME
;
}
/* don't prepend default domain */
}
else
{
if
((
!
domainlen
&&
!
namespace
->
isadmin
)
||
(
domainlen
&&
strcasecmp
(
userid
+
userlen
,
cp
)))
{
/* don't allow cross-domain access
(except for global admin) */
return
IMAP_MAILBOX_BADNAME
;
}
domainlen
=
strlen
(
cp
+
1
)
+
1
;
if
(
domainlen
>
MAX_MAILBOX_NAME
)
return
IMAP_MAILBOX_BADNAME
;
sprintf
(
result
,
"%s!"
,
cp
+
1
);
}
}
/* if no domain specified, we're in the default domain */
}
result
+=
domainlen
;
/* Shared namespace */
prefixlen
=
strlen
(
namespace
->
prefix
[
NAMESPACE_SHARED
]);
if
(
prefixlen
==
0
)
return
IMAP_MAILBOX_BADNAME
;
if
(
!
strncmp
(
name
,
namespace
->
prefix
[
NAMESPACE_SHARED
],
prefixlen
-1
)
&&
(
namelen
==
prefixlen
-1
||
name
[
prefixlen
-1
]
==
namespace
->
hier_sep
))
{
if
(
namelen
==
prefixlen
-1
)
{
/* can't create folders using undelimited prefix */
return
IMAP_MAILBOX_BADNAME
;
}
if
(
domainlen
+
namelen
-
prefixlen
>
MAX_MAILBOX_NAME
)
{
return
IMAP_MAILBOX_BADNAME
;
}
sprintf
(
result
,
"%.*s"
,
namelen
-
prefixlen
,
name
+
prefixlen
);
/* Translate any separators in mailboxname */
mboxname_hiersep_tointernal
(
namespace
,
result
,
0
);
return
0
;
}
/* Other Users namespace */
prefixlen
=
strlen
(
namespace
->
prefix
[
NAMESPACE_USER
]);
if
(
prefixlen
==
0
)
return
IMAP_MAILBOX_BADNAME
;
if
(
!
strncmp
(
name
,
namespace
->
prefix
[
NAMESPACE_USER
],
prefixlen
-1
)
&&
(
namelen
==
prefixlen
-1
||
name
[
prefixlen
-1
]
==
namespace
->
hier_sep
))
{
if
(
namelen
==
prefixlen
-1
)
{
/* can't create folders using undelimited prefix */
return
IMAP_MAILBOX_BADNAME
;
}
if
(
domainlen
+
namelen
-
prefixlen
+
5
>
MAX_MAILBOX_NAME
)
{
return
IMAP_MAILBOX_BADNAME
;
}
sprintf
(
result
,
"user.%.*s"
,
namelen
-
prefixlen
,
name
+
prefixlen
);
/* Translate any separators in userid+mailbox */
mboxname_hiersep_tointernal
(
namespace
,
result
+
5
,
0
);
return
0
;
}
/* Personal (INBOX) namespace */
if
(
!
userid
||
((
cp
=
strchr
(
userid
,
namespace
->
hier_sep
))
&&
(
cp
-
userid
<
userlen
)))
{
return
IMAP_MAILBOX_BADNAME
;
}
if
(
domainlen
+
userlen
+
5
>
MAX_MAILBOX_NAME
)
{
return
IMAP_MAILBOX_BADNAME
;
}
sprintf
(
result
,
"user.%.*s"
,
userlen
,
userid
);
/* INBOX */
if
((
name
[
0
]
==
'i'
||
name
[
0
]
==
'I'
)
&&
!
strncasecmp
(
name
,
"inbox"
,
5
)
&&
(
namelen
==
5
||
name
[
5
]
==
namespace
->
hier_sep
))
{
if
(
name
[
5
]
==
namespace
->
hier_sep
)
{
/* can't create folders under INBOX */
return
IMAP_MAILBOX_BADNAME
;
}
return
0
;
}
resultlen
=
strlen
(
result
);
/* other personal folder */
if
(
domainlen
+
resultlen
+
6
+
namelen
>
MAX_MAILBOX_NAME
)
{
return
IMAP_MAILBOX_BADNAME
;
}
snprintf
(
result
+
resultlen
,
MAX_MAILBOX_BUFFER
-
resultlen
,
".%.*s"
,
namelen
,
name
);
/* Translate any separators in mailboxname */
mboxname_hiersep_tointernal
(
namespace
,
result
+
6
+
userlen
,
0
);
return
0
;
}
/*
* Convert the internal mailbox 'name' to an external name.
* If 'userid' is non-null, it is the name of the current user.
* On success, results are placed in the buffer pointed to by
* 'result', the buffer must be of size MAX_MAILBOX_BUFFER.
*/
/* Handle conversion from the internal namespace to the standard namespace */
static
int
mboxname_toexternal
(
struct
namespace
*
namespace
,
const
char
*
name
,
const
char
*
userid
,
char
*
result
)
{
char
*
domain
=
NULL
,
*
cp
;
size_t
domainlen
=
0
,
resultlen
;
/* Blank the result, just in case */
result
[
0
]
=
'\0'
;
if
(
strlen
(
name
)
>
MAX_MAILBOX_NAME
)
return
IMAP_MAILBOX_BADNAME
;
if
(
config_virtdomains
&&
(
cp
=
strchr
(
name
,
'!'
)))
{
domain
=
(
char
*
)
name
;
domainlen
=
cp
++
-
name
;
name
=
cp
;
/* don't use the domain if it matches the user's domain */
if
(
userid
&&
(
cp
=
strchr
(
userid
,
'@'
))
&&
(
strlen
(
++
cp
)
==
domainlen
)
&&
!
strncmp
(
domain
,
cp
,
domainlen
))
domain
=
NULL
;
}
strcpy
(
result
,
name
);
/* Translate any separators in mailboxname */
mboxname_hiersep_toexternal
(
namespace
,
result
,
0
);
resultlen
=
strlen
(
result
);
/* Append domain */
if
(
domain
)
{
if
(
resultlen
+
domainlen
+
1
>
MAX_MAILBOX_NAME
)
return
IMAP_MAILBOX_BADNAME
;
snprintf
(
result
+
resultlen
,
MAX_MAILBOX_BUFFER
-
resultlen
,
"@%.*s"
,
(
int
)
domainlen
,
domain
);
}
return
0
;
}
/* Handle conversion from the internal namespace to the alternate namespace */
static
int
mboxname_toexternal_alt
(
struct
namespace
*
namespace
,
const
char
*
name
,
const
char
*
userid
,
char
*
result
)
{
char
*
domain
;
size_t
userlen
,
resultlen
;
/* Blank the result, just in case */
result
[
0
]
=
'\0'
;
if
(
strlen
(
name
)
>
MAX_MAILBOX_NAME
)
return
IMAP_MAILBOX_BADNAME
;
if
(
!
userid
)
return
IMAP_MAILBOX_BADNAME
;
userlen
=
strlen
(
userid
);
if
(
config_virtdomains
&&
(
domain
=
strchr
(
userid
,
'@'
)))
{
size_t
domainlen
=
strlen
(
domain
);
userlen
=
domain
-
userid
;
if
(
!
strncmp
(
name
,
domain
+
1
,
domainlen
-1
)
&&
name
[
domainlen
-1
]
==
'!'
)
{
name
+=
domainlen
;
}
}
/* Personal (INBOX) namespace */
if
(
!
strncasecmp
(
name
,
"inbox"
,
5
)
&&
(
name
[
5
]
==
'\0'
||
name
[
5
]
==
'.'
))
{
if
(
name
[
5
]
==
'\0'
)
strcpy
(
result
,
name
);
else
strcpy
(
result
,
name
+
6
);
}
/* paranoia - this shouldn't be needed */
else
if
(
!
strncmp
(
name
,
"user."
,
5
)
&&
!
strncmp
(
name
+
5
,
userid
,
userlen
)
&&
(
name
[
5
+
userlen
]
==
'\0'
||
name
[
5
+
userlen
]
==
'.'
))
{
if
(
name
[
5
+
userlen
]
==
'\0'
)
strcpy
(
result
,
"INBOX"
);
else
strcpy
(
result
,
name
+
5
+
userlen
+
1
);
}
/* Other Users namespace */
else
if
(
!
strncmp
(
name
,
"user"
,
4
)
&&
(
name
[
4
]
==
'\0'
||
name
[
4
]
==
'.'
))
{
size_t
prefixlen
=
strlen
(
namespace
->
prefix
[
NAMESPACE_USER
]);
if
((
prefixlen
>
MAX_MAILBOX_NAME
)
||
((
name
[
4
]
==
'.'
)
&&
((
prefixlen
+
1
+
strlen
(
name
+
5
))
>
MAX_MAILBOX_NAME
)))
return
IMAP_MAILBOX_BADNAME
;
sprintf
(
result
,
"%.*s"
,
(
int
)
(
prefixlen
-1
),
namespace
->
prefix
[
NAMESPACE_USER
]);
resultlen
=
strlen
(
result
);
if
(
name
[
4
]
==
'.'
)
{
sprintf
(
result
+
resultlen
,
"%c%s"
,
namespace
->
hier_sep
,
name
+
5
);
}
}
/* Shared namespace */
else
{
/* special case: LIST/LSUB "" % */
if
(
!
strncmp
(
name
,
namespace
->
prefix
[
NAMESPACE_SHARED
],
strlen
(
namespace
->
prefix
[
NAMESPACE_SHARED
])
-1
))
{
strcpy
(
result
,
name
);
}
else
{
strcpy
(
result
,
namespace
->
prefix
[
NAMESPACE_SHARED
]);
strcat
(
result
,
name
);
}
}
/* Translate any separators in mailboxname */
mboxname_hiersep_toexternal
(
namespace
,
result
,
0
);
return
0
;
}
/*
* Create namespace based on config options.
*/
int
mboxname_init_namespace
(
struct
namespace
*
namespace
,
int
isadmin
)
{
const
char
*
prefix
;
assert
(
namespace
!=
NULL
);
namespace
->
isadmin
=
isadmin
;
namespace
->
hier_sep
=
config_getswitch
(
IMAPOPT_UNIXHIERARCHYSEP
)
?
'/'
:
'.'
;
namespace
->
isalt
=
!
isadmin
&&
config_getswitch
(
IMAPOPT_ALTNAMESPACE
);
if
(
namespace
->
isalt
)
{
/* alternate namespace */
strcpy
(
namespace
->
prefix
[
NAMESPACE_INBOX
],
""
);
prefix
=
config_getstring
(
IMAPOPT_USERPREFIX
);
if
(
!
prefix
||
strlen
(
prefix
)
==
0
||
strlen
(
prefix
)
>=
MAX_NAMESPACE_PREFIX
||
strchr
(
prefix
,
namespace
->
hier_sep
)
!=
NULL
)
return
IMAP_NAMESPACE_BADPREFIX
;
sprintf
(
namespace
->
prefix
[
NAMESPACE_USER
],
"%.*s%c"
,
MAX_NAMESPACE_PREFIX
-1
,
prefix
,
namespace
->
hier_sep
);
prefix
=
config_getstring
(
IMAPOPT_SHAREDPREFIX
);
if
(
!
prefix
||
strlen
(
prefix
)
==
0
||
strlen
(
prefix
)
>=
MAX_NAMESPACE_PREFIX
||
strchr
(
prefix
,
namespace
->
hier_sep
)
!=
NULL
||
!
strncmp
(
namespace
->
prefix
[
NAMESPACE_USER
],
prefix
,
strlen
(
prefix
)))
return
IMAP_NAMESPACE_BADPREFIX
;
sprintf
(
namespace
->
prefix
[
NAMESPACE_SHARED
],
"%.*s%c"
,
MAX_NAMESPACE_PREFIX
-1
,
prefix
,
namespace
->
hier_sep
);
namespace
->
mboxname_tointernal
=
mboxname_tointernal_alt
;
namespace
->
mboxname_toexternal
=
mboxname_toexternal_alt
;
namespace
->
mboxlist_findall
=
mboxlist_findall_alt
;
namespace
->
mboxlist_findsub
=
mboxlist_findsub_alt
;
}
else
{
/* standard namespace */
sprintf
(
namespace
->
prefix
[
NAMESPACE_INBOX
],
"%s%c"
,
"INBOX"
,
namespace
->
hier_sep
);
sprintf
(
namespace
->
prefix
[
NAMESPACE_USER
],
"%s%c"
,
"user"
,
namespace
->
hier_sep
);
strcpy
(
namespace
->
prefix
[
NAMESPACE_SHARED
],
""
);
namespace
->
mboxname_tointernal
=
mboxname_tointernal
;
namespace
->
mboxname_toexternal
=
mboxname_toexternal
;
namespace
->
mboxlist_findall
=
mboxlist_findall
;
namespace
->
mboxlist_findsub
=
mboxlist_findsub
;
}
return
0
;
}
/*
* Translate separator charactors in a mailboxname from its external
* representation to its internal representation '.'.
* If using the unixhierarchysep '/', all '.'s get translated to DOTCHAR.
*/
char
*
mboxname_hiersep_tointernal
(
struct
namespace
*
namespace
,
char
*
name
,
int
length
)
{
char
*
p
;
assert
(
namespace
!=
NULL
);
assert
(
namespace
->
hier_sep
==
'.'
||
namespace
->
hier_sep
==
'/'
);
if
(
!
length
)
length
=
strlen
(
name
);
if
(
namespace
->
hier_sep
==
'/'
)
{
/* change all '/'s to '.' and all '.'s to DOTCHAR */
for
(
p
=
name
;
*
p
&&
length
;
p
++
,
length
--
)
{
if
(
*
p
==
'/'
)
*
p
=
'.'
;
else
if
(
*
p
==
'.'
)
*
p
=
DOTCHAR
;
}
}
return
name
;
}
/*
* Translate separator charactors in a mailboxname from its internal
* representation '.' to its external representation.
* If using the unixhierarchysep '/', all DOTCHAR get translated to '.'.
*/
char
*
mboxname_hiersep_toexternal
(
struct
namespace
*
namespace
,
char
*
name
,
int
length
)
{
char
*
p
;
assert
(
namespace
!=
NULL
);
assert
(
namespace
->
hier_sep
==
'.'
||
namespace
->
hier_sep
==
'/'
);
if
(
!
length
)
length
=
strlen
(
name
);
if
(
namespace
->
hier_sep
==
'/'
)
{
/* change all '.'s to '/' and all DOTCHARs to '.' */
for
(
p
=
name
;
*
p
&&
length
;
p
++
,
length
--
)
{
if
(
*
p
==
'.'
)
*
p
=
'/'
;
else
if
(
*
p
==
DOTCHAR
)
*
p
=
'.'
;
}
}
return
name
;
}
/*
* Return nonzero if 'userid' owns the (internal) mailbox 'name'.
*/
int
mboxname_userownsmailbox
(
const
char
*
userid
,
const
char
*
name
)
{
struct
namespace
internal
=
{
'.'
,
0
,
0
,
{
"INBOX."
,
"user."
,
""
},
NULL
,
NULL
,
NULL
,
NULL
};
char
inboxname
[
MAX_MAILBOX_BUFFER
];
if
(
!
mboxname_tointernal
(
&
internal
,
"INBOX"
,
userid
,
inboxname
)
&&
!
strncmp
(
name
,
inboxname
,
strlen
(
inboxname
))
&&
(
name
[
strlen
(
inboxname
)]
==
'\0'
||
name
[
strlen
(
inboxname
)]
==
'.'
))
{
return
1
;
}
return
0
;
}
/*
* If (internal) mailbox 'name' is a user's mailbox (optionally INBOX),
* returns a pointer to the userid, otherwise returns NULL.
*/
char
*
mboxname_isusermailbox
(
const
char
*
name
,
int
isinbox
)
{
const
char
*
p
;
const
char
*
start
=
name
;
/* step past the domain part */
if
(
config_virtdomains
&&
(
p
=
strchr
(
start
,
'!'
)))
start
=
p
+
1
;
/* starts with "user." AND
* we don't care if it's an inbox OR
* there's no dots after the username
*/
if
(
!
strncmp
(
start
,
"user."
,
5
)
&&
(
!
isinbox
||
!
strchr
(
start
+
5
,
'.'
)))
return
(
char
*
)
start
+
5
;
else
return
NULL
;
}
/*
* If (internal) mailbox 'name' is a DELETED mailbox
* returns boolean
*/
int
mboxname_isdeletedmailbox
(
const
char
*
name
)
{
static
const
char
*
deletedprefix
=
NULL
;
static
int
deletedprefix_len
=
0
;
int
domainlen
=
0
;
char
*
p
;
if
(
!
deletedprefix
)
{
deletedprefix
=
config_getstring
(
IMAPOPT_DELETEDPREFIX
);
deletedprefix_len
=
strlen
(
deletedprefix
);
}
if
(
config_virtdomains
&&
(
p
=
strchr
(
name
,
'!'
)))
domainlen
=
p
-
name
+
1
;
return
((
!
strncmp
(
name
+
domainlen
,
deletedprefix
,
deletedprefix_len
)
&&
name
[
domainlen
+
deletedprefix_len
]
==
'.'
)
?
1
:
0
);
}
/*
* Translate (internal) inboxname into corresponding userid.
*/
char
*
mboxname_to_userid
(
const
char
*
mboxname
)
{
static
char
userid
[
MAX_MAILBOX_BUFFER
];
const
char
*
domain
=
NULL
,
*
cp
;
char
*
rp
;
int
domainlen
=
0
;
if
(
config_virtdomains
&&
(
cp
=
strchr
(
mboxname
,
'!'
)))
{
/* locate, save, and skip domain */
domain
=
mboxname
;
domainlen
=
cp
++
-
mboxname
;
}
else
{
cp
=
mboxname
;
}
/* not a user mailbox? */
if
(
strncmp
(
cp
,
"user."
,
5
))
return
NULL
;
/* skip "user." */
strcpy
(
userid
,
cp
+
5
);
/* find end of userid */
rp
=
strchr
(
userid
,
'.'
);
if
(
!
rp
)
rp
=
userid
+
strlen
(
userid
);
if
(
domain
)
{
/* append domain */
sprintf
(
rp
,
"@%.*s"
,
domainlen
,
domain
);
}
else
{
/* otherwise close off at end of userid anyway */
*
rp
=
'\0'
;
}
return
(
userid
);
}
/*
* Apply additional restrictions on netnews mailbox names.
* Cannot have all-numeric name components.
*/
int
mboxname_netnewscheck
(
const
char
*
name
)
{
int
c
;
int
sawnonnumeric
=
0
;
while
((
c
=
*
name
++
)
!=
0
)
{
switch
(
c
)
{
case
'.'
:
if
(
!
sawnonnumeric
)
return
IMAP_MAILBOX_BADNAME
;
sawnonnumeric
=
0
;
break
;
case
'0'
:
case
'1'
:
case
'2'
:
case
'3'
:
case
'4'
:
case
'5'
:
case
'6'
:
case
'7'
:
case
'8'
:
case
'9'
:
break
;
default
:
sawnonnumeric
=
1
;
break
;
}
}
if
(
!
sawnonnumeric
)
return
IMAP_MAILBOX_BADNAME
;
return
0
;
}
/*
* Apply site policy restrictions on mailbox names.
* Restrictions are hardwired for now.
*/
#define GOODCHARS " #$'+,-.0123456789:=@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"
int
mboxname_policycheck
(
const
char
*
name
)
{
unsigned
i
;
struct
glob
*
g
;
int
sawutf7
=
0
;
unsigned
c1
,
c2
,
c3
,
c4
,
c5
,
c6
,
c7
,
c8
;
int
ucs4
;
int
unixsep
;
unixsep
=
config_getswitch
(
IMAPOPT_UNIXHIERARCHYSEP
);
/* Skip policy check on mailbox created in delayed delete namespace
* assuming the mailbox existed before and was OK then.
* This should allow mailboxes that are extremely long to be
* deleted when delayed_delete is enabled.
* A thorough fix might remove the prefix and timestamp
* then continue with the check
*/
if
(
!
mboxname_isdeletedmailbox
(
name
))
{
if
(
strlen
(
name
)
>
MAX_MAILBOX_NAME
)
return
IMAP_MAILBOX_BADNAME
;
}
for
(
i
=
0
;
i
<
NUM_BADMBOXPATTERNS
;
i
++
)
{
g
=
glob_init
(
badmboxpatterns
[
i
],
0
);
if
(
GLOB_TEST
(
g
,
name
)
!=
-1
)
{
glob_free
(
&
g
);
return
IMAP_MAILBOX_BADNAME
;
}
glob_free
(
&
g
);
}
if
(
*
name
==
'~'
)
return
IMAP_MAILBOX_BADNAME
;
while
(
*
name
)
{
if
(
*
name
==
'&'
)
{
/* Modified UTF-7 */
name
++
;
while
(
*
name
!=
'-'
)
{
if
(
sawutf7
)
{
/* Two adjacent utf7 sequences */
return
IMAP_MAILBOX_BADNAME
;
}
if
((
c1
=
CHARMOD64
(
*
name
++
))
==
XX
||
(
c2
=
CHARMOD64
(
*
name
++
))
==
XX
||
(
c3
=
CHARMOD64
(
*
name
++
))
==
XX
)
{
/* Non-base64 character */
return
IMAP_MAILBOX_BADNAME
;
}
ucs4
=
(
c1
<<
10
)
|
(
c2
<<
4
)
|
(
c3
>>
2
);
if
((
ucs4
&
0xff80
)
==
0
||
(
ucs4
&
0xf800
)
==
0xd800
)
{
/* US-ASCII or multi-word character */
return
IMAP_MAILBOX_BADNAME
;
}
if
(
*
name
==
'-'
)
{
/* Trailing bits not zero */
if
(
c3
&
0x03
)
return
IMAP_MAILBOX_BADNAME
;
/* End of UTF-7 sequence */
break
;
}
if
((
c4
=
CHARMOD64
(
*
name
++
))
==
XX
||
(
c5
=
CHARMOD64
(
*
name
++
))
==
XX
||
(
c6
=
CHARMOD64
(
*
name
++
))
==
XX
)
{
/* Non-base64 character */
return
IMAP_MAILBOX_BADNAME
;
}
ucs4
=
((
c3
&
0x03
)
<<
14
)
|
(
c4
<<
8
)
|
(
c5
<<
2
)
|
(
c6
>>
4
);
if
((
ucs4
&
0xff80
)
==
0
||
(
ucs4
&
0xf800
)
==
0xd800
)
{
/* US-ASCII or multi-word character */
return
IMAP_MAILBOX_BADNAME
;
}
if
(
*
name
==
'-'
)
{
/* Trailing bits not zero */
if
(
c6
&
0x0f
)
return
IMAP_MAILBOX_BADNAME
;
/* End of UTF-7 sequence */
break
;
}
if
((
c7
=
CHARMOD64
(
*
name
++
))
==
XX
||
(
c8
=
CHARMOD64
(
*
name
++
))
==
XX
)
{
/* Non-base64 character */
return
IMAP_MAILBOX_BADNAME
;
}
ucs4
=
((
c6
&
0x0f
)
<<
12
)
|
(
c7
<<
6
)
|
c8
;
if
((
ucs4
&
0xff80
)
==
0
||
(
ucs4
&
0xf800
)
==
0xd800
)
{
/* US-ASCII or multi-word character */
return
IMAP_MAILBOX_BADNAME
;
}
}
if
(
name
[
-1
]
==
'&'
)
sawutf7
=
0
;
/* '&-' is sequence for '&' */
else
sawutf7
=
1
;
name
++
;
/* Skip over terminating '-' */
}
else
{
if
(
!
strchr
(
GOODCHARS
,
*
name
)
&&
/* If we're using unixhierarchysep, DOTCHAR is allowed */
!
(
unixsep
&&
*
name
==
DOTCHAR
))
return
IMAP_MAILBOX_BADNAME
;
name
++
;
sawutf7
=
0
;
}
}
return
0
;
}
void
mboxname_hash
(
char
*
buf
,
size_t
buf_len
,
const
char
*
root
,
const
char
*
name
)
{
const
char
*
idx
;
char
c
,
*
p
;
snprintf
(
buf
,
buf_len
,
"%s"
,
root
);
buf_len
-=
strlen
(
buf
);
buf
+=
strlen
(
buf
);
if
(
config_virtdomains
&&
(
p
=
strchr
(
name
,
'!'
)))
{
*
p
=
'\0'
;
/* split domain!user */
if
(
config_hashimapspool
)
{
c
=
(
char
)
dir_hash_c
(
name
,
config_fulldirhash
);
snprintf
(
buf
,
buf_len
,
"%s%c/%s"
,
FNAME_DOMAINDIR
,
c
,
name
);
}
else
{
snprintf
(
buf
,
buf_len
,
"%s%s"
,
FNAME_DOMAINDIR
,
name
);
}
*
p
++
=
'!'
;
/* reassemble domain!user */
name
=
p
;
buf_len
-=
strlen
(
buf
);
buf
+=
strlen
(
buf
);
}
if
(
config_hashimapspool
)
{
idx
=
strchr
(
name
,
'.'
);
if
(
idx
==
NULL
)
{
idx
=
name
;
}
else
{
idx
++
;
}
c
=
(
char
)
dir_hash_c
(
idx
,
config_fulldirhash
);
snprintf
(
buf
,
buf_len
,
"/%c/%s"
,
c
,
name
);
}
else
{
/* standard mailbox placement */
snprintf
(
buf
,
buf_len
,
"/%s"
,
name
);
}
/* change all '.'s to '/' */
for
(
p
=
buf
;
*
p
;
p
++
)
{
if
(
*
p
==
'.'
)
*
p
=
'/'
;
}
}
/* note: mboxname must be internal */
char
*
mboxname_datapath
(
const
char
*
partition
,
const
char
*
mboxname
,
unsigned
long
uid
)
{
static
char
pathresult
[
MAX_MAILBOX_PATH
+
1
];
const
char
*
root
;
root
=
config_partitiondir
(
partition
);
if
(
!
root
)
return
NULL
;
if
(
!
mboxname
)
{
strncpy
(
pathresult
,
root
,
MAX_MAILBOX_PATH
);
return
pathresult
;
}
mboxname_hash
(
pathresult
,
MAX_MAILBOX_PATH
,
root
,
mboxname
);
if
(
uid
)
{
int
len
=
strlen
(
pathresult
);
snprintf
(
pathresult
+
len
,
MAX_MAILBOX_PATH
-
len
,
"/%lu."
,
uid
);
}
pathresult
[
MAX_MAILBOX_PATH
]
=
'\0'
;
if
(
strlen
(
pathresult
)
==
MAX_MAILBOX_PATH
)
return
NULL
;
return
pathresult
;
}
char
*
mboxname_lockpath
(
const
char
*
mboxname
)
{
static
char
lockresult
[
MAX_MAILBOX_PATH
+
1
];
char
basepath
[
MAX_MAILBOX_PATH
+
1
];
const
char
*
root
=
config_getstring
(
IMAPOPT_MBOXNAME_LOCKPATH
);
int
len
;
if
(
!
root
)
{
snprintf
(
basepath
,
MAX_MAILBOX_PATH
,
"%s/lock"
,
config_dir
);
root
=
basepath
;
}
mboxname_hash
(
lockresult
,
MAX_MAILBOX_PATH
,
root
,
mboxname
);
len
=
strlen
(
lockresult
);
snprintf
(
lockresult
+
len
,
MAX_MAILBOX_PATH
-
len
,
"%s"
,
".lock"
);
lockresult
[
MAX_MAILBOX_PATH
]
=
'\0'
;
if
(
strlen
(
lockresult
)
==
MAX_MAILBOX_PATH
)
return
NULL
;
return
lockresult
;
}
char
*
mboxname_metapath
(
const
char
*
partition
,
const
char
*
mboxname
,
int
metafile
,
int
isnew
)
{
static
char
metaresult
[
MAX_MAILBOX_PATH
];
int
metaflag
=
0
;
const
char
*
root
=
NULL
;
const
char
*
filename
=
NULL
;
char
confkey
[
256
];
*
confkey
=
'\0'
;
switch
(
metafile
)
{
case
META_HEADER
:
snprintf
(
confkey
,
256
,
"metadir-header-%s"
,
partition
);
metaflag
=
IMAP_ENUM_METAPARTITION_FILES_HEADER
;
filename
=
FNAME_HEADER
;
break
;
case
META_INDEX
:
snprintf
(
confkey
,
256
,
"metadir-index-%s"
,
partition
);
metaflag
=
IMAP_ENUM_METAPARTITION_FILES_INDEX
;
filename
=
FNAME_INDEX
;
break
;
case
META_CACHE
:
snprintf
(
confkey
,
256
,
"metadir-cache-%s"
,
partition
);
metaflag
=
IMAP_ENUM_METAPARTITION_FILES_CACHE
;
filename
=
FNAME_CACHE
;
break
;
case
META_EXPUNGE
:
/* not movable, it's only old */
metaflag
=
IMAP_ENUM_METAPARTITION_FILES_EXPUNGE
;
filename
=
FNAME_EXPUNGE
;
break
;
case
META_SQUAT
:
snprintf
(
confkey
,
256
,
"metadir-squat-%s"
,
partition
);
metaflag
=
IMAP_ENUM_METAPARTITION_FILES_SQUAT
;
filename
=
FNAME_SQUAT
;
break
;
case
0
:
break
;
default
:
fatal
(
"Unknown meta file requested"
,
EC_SOFTWARE
);
}
if
(
*
confkey
)
root
=
config_getoverflowstring
(
confkey
,
NULL
);
if
(
!
root
&&
(
!
metaflag
||
(
config_metapartition_files
&
metaflag
)))
root
=
config_metapartitiondir
(
partition
);
if
(
!
root
)
root
=
config_partitiondir
(
partition
);
if
(
!
root
)
return
NULL
;
if
(
!
mboxname
)
{
strncpy
(
metaresult
,
root
,
MAX_MAILBOX_PATH
);
return
metaresult
;
}
mboxname_hash
(
metaresult
,
MAX_MAILBOX_PATH
,
root
,
mboxname
);
if
(
filename
)
{
int
len
=
strlen
(
metaresult
);
if
(
isnew
)
snprintf
(
metaresult
+
len
,
MAX_MAILBOX_PATH
-
len
,
"%s.NEW"
,
filename
);
else
snprintf
(
metaresult
+
len
,
MAX_MAILBOX_PATH
-
len
,
"%s"
,
filename
);
}
if
(
strlen
(
metaresult
)
>=
MAX_MAILBOX_PATH
)
return
NULL
;
return
metaresult
;
}
void
mboxname_todeleted
(
const
char
*
name
,
char
*
result
,
int
withtime
)
{
int
domainlen
=
0
;
char
*
p
;
const
char
*
deletedprefix
=
config_getstring
(
IMAPOPT_DELETEDPREFIX
);
strncpy
(
result
,
name
,
MAX_MAILBOX_BUFFER
);
if
(
config_virtdomains
&&
(
p
=
strchr
(
name
,
'!'
)))
domainlen
=
p
-
name
+
1
;
if
(
withtime
)
{
struct
timeval
tv
;
gettimeofday
(
&
tv
,
NULL
);
snprintf
(
result
+
domainlen
,
MAX_MAILBOX_BUFFER
-
domainlen
,
"%s.%s.%X"
,
deletedprefix
,
name
+
domainlen
,
(
unsigned
)
tv
.
tv_sec
);
}
else
{
snprintf
(
result
+
domainlen
,
MAX_MAILBOX_BUFFER
-
domainlen
,
"%s.%s"
,
deletedprefix
,
name
+
domainlen
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, Apr 24, 1:18 PM (3 d, 10 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18845097
Default Alt Text
mboxname.c (31 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline