Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117749711
mailbox.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
70 KB
Referenced Files
None
Subscribers
None
mailbox.c
View Options
/* mailbox.c -- Mailbox manipulation routines
$Id: mailbox.c,v 1.127 2002/05/22 20:58:02 rjs3 Exp $
* 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.
*
*
*/
#include
<config.h>
#ifdef HAVE_UNISTD_H
#include
<unistd.h>
#endif
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<errno.h>
#include
<syslog.h>
#include
<sys/types.h>
#include
<fcntl.h>
#include
<netinet/in.h>
#include
<sys/un.h>
#include
<sys/socket.h>
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<ctype.h>
#include
<time.h>
#include
<com_err.h>
#ifdef HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#include
"assert.h"
#include
"imapconf.h"
#include
"acl.h"
#include
"map.h"
#include
"retry.h"
#include
"util.h"
#include
"lock.h"
#include
"exitcodes.h"
#include
"imap_err.h"
#include
"mailbox.h"
#include
"xmalloc.h"
#include
"mboxlist.h"
#include
"acapmbox.h"
#include
"seen.h"
#if 0
#include "acappush.h"
#endif
static
int
mailbox_doing_reconstruct
=
0
;
#define zeromailbox(m) { memset(&m, 0, sizeof(struct mailbox)); \
(m).header_fd = -1; \
(m).index_fd = -1; \
(m).cache_fd = -1; }
static
int
mailbox_calculate_flagcounts
(
struct
mailbox
*
mailbox
);
static
int
mailbox_upgrade_index
(
struct
mailbox
*
mailbox
);
/*
* Names of the headers we cache in the cyrus.cache file.
* Any changes to this list require corresponding changes to
* message_parse_headers() in message.c
*/
char
*
mailbox_cache_header_name
[]
=
{
/* "in-reply-to", in ENVELOPE */
"priority"
,
"references"
,
"resent-from"
,
"newsgroups"
,
"followup-to"
,
};
int
mailbox_num_cache_header
=
sizeof
(
mailbox_cache_header_name
)
/
sizeof
(
char
*
);
#if 0
/* acappush variables */
static int acappush_sock = -1;
static struct sockaddr_un acappush_remote;
static int acappush_remote_len = 0;
#endif
/* function to be used for notification of mailbox changes/updates */
static
mailbox_notifyproc_t
*
updatenotifier
=
NULL
;
/*
* Set the updatenotifier function
*/
void
mailbox_set_updatenotifier
(
mailbox_notifyproc_t
*
notifyproc
)
{
updatenotifier
=
notifyproc
;
}
/*
* Create connection to acappush
*/
int
mailbox_initialize
(
void
)
{
#if 0
int s;
int fdflags;
struct stat sbuf;
/* if not configured to do acap do nothing */
if (config_getstring("acap_server", NULL)==NULL) return 0;
if ((s = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
return IMAP_IOERROR;
}
acappush_remote.sun_family = AF_UNIX;
strcpy(acappush_remote.sun_path, config_dir);
strcat(acappush_remote.sun_path, FNAME_ACAPPUSH_SOCK);
acappush_remote_len = sizeof(acappush_remote.sun_family) +
strlen(acappush_remote.sun_path) + 1;
/* check that the socket exists */
if (stat(acappush_remote.sun_path, &sbuf) < 0) {
close(s);
return 0;
}
/* put us in non-blocking mode */
fdflags = fcntl(s, F_GETFD, 0);
if (fdflags != -1) fdflags = fcntl(s, F_SETFL, O_NONBLOCK | fdflags);
if (fdflags == -1) { close(s); return IMAP_IOERROR; }
acappush_sock = s;
#endif
return
0
;
}
/* create the unique identifier for a mailbox named 'name' with
* uidvalidity 'uidvalidity'. 'uniqueid' should be at least 17 bytes
* long. the unique identifier is just the mailbox name hashed to 32
* bits followed by the uid, both converted to hex.
*/
#define PRIME (2147484043UL)
void
mailbox_make_uniqueid
(
char
*
name
,
unsigned
long
uidvalidity
,
char
*
uniqueid
)
{
unsigned
long
hash
=
0
;
while
(
*
name
)
{
hash
*=
251
;
hash
+=
*
name
++
;
hash
%=
PRIME
;
}
sprintf
(
uniqueid
,
"%08lx%08lx"
,
hash
,
uidvalidity
);
}
/*
* Calculate relative filename for the message with UID 'uid'
* in 'mailbox'. 'out' must be at least MAILBOX_FNAME_LEN long.
*/
void
mailbox_message_get_fname
(
struct
mailbox
*
mailbox
,
unsigned
long
uid
,
char
*
out
)
{
assert
(
mailbox
->
format
!=
MAILBOX_FORMAT_NETNEWS
);
sprintf
(
out
,
"%lu."
,
uid
);
}
/*
* Maps in the content for the message with UID 'uid' in 'mailbox'.
* Returns map in 'basep' and 'lenp'
*/
int
mailbox_map_message
(
struct
mailbox
*
mailbox
,
int
iscurrentdir
,
unsigned
long
uid
,
const
char
**
basep
,
unsigned
long
*
lenp
)
{
int
msgfd
;
char
buf
[
4096
];
char
*
p
=
buf
;
struct
stat
sbuf
;
if
(
!
iscurrentdir
)
{
strcpy
(
buf
,
mailbox
->
path
);
p
=
buf
+
strlen
(
buf
);
*
p
++
=
'/'
;
}
sprintf
(
p
,
"%lu."
,
uid
);
msgfd
=
open
(
buf
,
O_RDONLY
,
0666
);
if
(
msgfd
==
-1
)
return
errno
;
if
(
fstat
(
msgfd
,
&
sbuf
)
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: fstat on %s: %m"
,
buf
);
fatal
(
"can't fstat message file"
,
EC_OSFILE
);
}
*
basep
=
0
;
*
lenp
=
0
;
map_refresh
(
msgfd
,
1
,
basep
,
lenp
,
sbuf
.
st_size
,
buf
,
mailbox
->
name
);
close
(
msgfd
);
return
0
;
}
/*
* Releases the buffer obtained from mailbox_map_message()
*/
void
mailbox_unmap_message
(
struct
mailbox
*
mailbox
__attribute__
((
unused
)),
unsigned
long
uid
__attribute__
((
unused
)),
const
char
**
basep
,
unsigned
long
*
lenp
)
{
map_free
(
basep
,
lenp
);
}
/*
* Set the "reconstruct" mode. Causes most errors to be ignored.
*/
void
mailbox_reconstructmode
()
{
mailbox_doing_reconstruct
=
1
;
}
/*
* Open and read the header of the mailbox with name 'name'
* The structure pointed to by 'mailbox' is initialized.
*/
int
mailbox_open_header
(
const
char
*
name
,
struct
auth_state
*
auth_state
,
struct
mailbox
*
mailbox
)
{
char
*
path
,
*
acl
;
int
r
;
r
=
mboxlist_lookup
(
name
,
&
path
,
&
acl
,
NULL
);
if
(
r
)
return
r
;
return
mailbox_open_header_path
(
name
,
path
,
acl
,
auth_state
,
mailbox
,
0
);
}
/*
* Open and read the header of the mailbox with name 'name'
* path 'path', and ACL 'acl'.
* The structure pointed to by 'mailbox' is initialized.
*/
int
mailbox_open_header_path
(
const
char
*
name
,
const
char
*
path
,
const
char
*
acl
,
struct
auth_state
*
auth_state
,
struct
mailbox
*
mailbox
,
int
suppresslog
)
{
char
fnamebuf
[
MAX_MAILBOX_PATH
];
struct
stat
sbuf
;
int
r
;
zeromailbox
(
*
mailbox
);
mailbox
->
quota
.
fd
=
-1
;
strcpy
(
fnamebuf
,
path
);
strcat
(
fnamebuf
,
FNAME_HEADER
);
mailbox
->
header_fd
=
open
(
fnamebuf
,
O_RDWR
,
0
);
if
(
mailbox
->
header_fd
==
-1
&&
!
mailbox_doing_reconstruct
)
{
if
(
!
suppresslog
)
{
syslog
(
LOG_ERR
,
"IOERROR: opening %s: %m"
,
fnamebuf
);
}
return
IMAP_IOERROR
;
}
if
(
mailbox
->
header_fd
!=
-1
)
{
if
(
fstat
(
mailbox
->
header_fd
,
&
sbuf
)
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: fstating %s: %m"
,
fnamebuf
);
fatal
(
"can't fstat header file"
,
EC_OSFILE
);
}
map_refresh
(
mailbox
->
header_fd
,
1
,
&
mailbox
->
header_base
,
&
mailbox
->
header_len
,
sbuf
.
st_size
,
"header"
,
name
);
mailbox
->
header_ino
=
sbuf
.
st_ino
;
}
mailbox
->
name
=
xstrdup
(
name
);
mailbox
->
path
=
xstrdup
(
path
);
mailbox
->
acl
=
xstrdup
(
acl
);
mailbox
->
myrights
=
cyrus_acl_myrights
(
auth_state
,
mailbox
->
acl
);
if
(
mailbox
->
header_fd
==
-1
)
return
0
;
r
=
mailbox_read_header
(
mailbox
);
if
(
r
&&
!
mailbox_doing_reconstruct
)
{
mailbox_close
(
mailbox
);
return
r
;
}
return
0
;
}
int
mailbox_open_locked
(
const
char
*
mbname
,
const
char
*
mbpath
,
const
char
*
mbacl
,
struct
auth_state
*
auth_state
,
struct
mailbox
*
mb
,
int
suppresslog
)
{
int
r
;
r
=
mailbox_open_header_path
(
mbname
,
mbpath
,
mbacl
,
auth_state
,
mb
,
suppresslog
);
if
(
r
)
return
r
;
/* now we have to close the mailbox if we fail */
r
=
mailbox_lock_header
(
mb
);
if
(
!
r
)
r
=
mailbox_open_index
(
mb
);
if
(
!
r
)
r
=
mailbox_lock_index
(
mb
);
if
(
r
)
mailbox_close
(
mb
);
return
r
;
}
#define MAXTRIES 60
/*
* Open the index and cache files for 'mailbox'. Also
* read the index header.
*/
int
mailbox_open_index
(
struct
mailbox
*
mailbox
)
{
char
fnamebuf
[
MAX_MAILBOX_PATH
];
bit32
index_gen
=
0
,
cache_gen
=
0
;
int
tries
=
0
;
if
(
mailbox
->
index_fd
!=
-1
)
{
close
(
mailbox
->
index_fd
);
map_free
(
&
mailbox
->
index_base
,
&
mailbox
->
index_len
);
}
if
(
mailbox
->
cache_fd
!=
-1
)
{
close
(
mailbox
->
cache_fd
);
map_free
(
&
mailbox
->
cache_base
,
&
mailbox
->
cache_len
);
}
do
{
strcpy
(
fnamebuf
,
mailbox
->
path
);
strcat
(
fnamebuf
,
FNAME_INDEX
);
mailbox
->
index_fd
=
open
(
fnamebuf
,
O_RDWR
,
0
);
if
(
mailbox
->
index_fd
!=
-1
)
{
map_refresh
(
mailbox
->
index_fd
,
0
,
&
mailbox
->
index_base
,
&
mailbox
->
index_len
,
MAP_UNKNOWN_LEN
,
"index"
,
mailbox
->
name
);
}
if
(
mailbox_doing_reconstruct
)
break
;
if
(
mailbox
->
index_fd
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: opening %s: %m"
,
fnamebuf
);
return
IMAP_IOERROR
;
}
strcpy
(
fnamebuf
,
mailbox
->
path
);
strcat
(
fnamebuf
,
FNAME_CACHE
);
mailbox
->
cache_fd
=
open
(
fnamebuf
,
O_RDWR
,
0
);
if
(
mailbox
->
cache_fd
!=
-1
)
{
struct
stat
sbuf
;
if
(
fstat
(
mailbox
->
cache_fd
,
&
sbuf
)
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: fstating %s: %m"
,
mailbox
->
name
);
fatal
(
"can't fstat cache file"
,
EC_OSFILE
);
}
mailbox
->
cache_size
=
sbuf
.
st_size
;
map_refresh
(
mailbox
->
cache_fd
,
0
,
&
mailbox
->
cache_base
,
&
mailbox
->
cache_len
,
mailbox
->
cache_size
,
"cache"
,
mailbox
->
name
);
}
if
(
mailbox
->
cache_fd
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: opening %s: %m"
,
fnamebuf
);
return
IMAP_IOERROR
;
}
/* Check generation number matches */
if
(
mailbox
->
index_len
<
4
||
mailbox
->
cache_len
<
4
)
{
return
IMAP_MAILBOX_BADFORMAT
;
}
index_gen
=
*
(
bit32
*
)
mailbox
->
index_base
;
cache_gen
=
*
(
bit32
*
)
mailbox
->
cache_base
;
if
(
index_gen
!=
cache_gen
)
{
close
(
mailbox
->
index_fd
);
map_free
(
&
mailbox
->
index_base
,
&
mailbox
->
index_len
);
close
(
mailbox
->
cache_fd
);
map_free
(
&
mailbox
->
cache_base
,
&
mailbox
->
cache_len
);
sleep
(
1
);
}
}
while
(
index_gen
!=
cache_gen
&&
tries
++
<
MAXTRIES
);
if
(
index_gen
!=
cache_gen
)
{
return
IMAP_MAILBOX_BADFORMAT
;
}
mailbox
->
generation_no
=
index_gen
;
return
mailbox_read_index_header
(
mailbox
);
}
/*
* Close the mailbox 'mailbox', freeing all associated resources.
*/
void
mailbox_close
(
struct
mailbox
*
mailbox
)
{
int
flag
;
close
(
mailbox
->
header_fd
);
map_free
(
&
mailbox
->
header_base
,
&
mailbox
->
header_len
);
if
(
mailbox
->
index_fd
!=
-1
)
{
close
(
mailbox
->
index_fd
);
map_free
(
&
mailbox
->
index_base
,
&
mailbox
->
index_len
);
}
if
(
mailbox
->
cache_fd
!=
-1
)
{
close
(
mailbox
->
cache_fd
);
map_free
(
&
mailbox
->
cache_base
,
&
mailbox
->
cache_len
);
}
if
(
mailbox
->
quota
.
fd
!=
-1
)
{
close
(
mailbox
->
quota
.
fd
);
}
free
(
mailbox
->
name
);
free
(
mailbox
->
path
);
free
(
mailbox
->
acl
);
free
(
mailbox
->
uniqueid
);
if
(
mailbox
->
quota
.
root
)
free
(
mailbox
->
quota
.
root
);
for
(
flag
=
0
;
flag
<
MAX_USER_FLAGS
;
flag
++
)
{
if
(
mailbox
->
flagname
[
flag
])
free
(
mailbox
->
flagname
[
flag
]);
}
zeromailbox
(
*
mailbox
);
mailbox
->
quota
.
fd
=
-1
;
}
/*
* Read the header of 'mailbox'
*/
int
mailbox_read_header
(
struct
mailbox
*
mailbox
)
{
int
flag
;
const
char
*
name
,
*
p
,
*
tab
,
*
eol
;
int
oldformat
=
0
;
/* Check magic number */
if
(
mailbox
->
header_len
<
sizeof
(
MAILBOX_HEADER_MAGIC
)
-1
||
strncmp
(
mailbox
->
header_base
,
MAILBOX_HEADER_MAGIC
,
sizeof
(
MAILBOX_HEADER_MAGIC
)
-1
))
{
return
IMAP_MAILBOX_BADFORMAT
;
}
/* Read quota file pathname */
p
=
mailbox
->
header_base
+
sizeof
(
MAILBOX_HEADER_MAGIC
)
-1
;
tab
=
memchr
(
p
,
'\t'
,
mailbox
->
header_len
-
(
p
-
mailbox
->
header_base
));
eol
=
memchr
(
p
,
'\n'
,
mailbox
->
header_len
-
(
p
-
mailbox
->
header_base
));
if
(
!
tab
||
tab
>
eol
||
!
eol
)
{
oldformat
=
1
;
if
(
!
eol
)
return
IMAP_MAILBOX_BADFORMAT
;
else
{
syslog
(
LOG_DEBUG
,
"mailbox '%s' has old cyrus.header"
,
mailbox
->
name
);
}
tab
=
eol
;
}
if
(
mailbox
->
quota
.
root
)
{
/* check if this is the same as what's there */
if
(
strlen
(
mailbox
->
quota
.
root
)
!=
(
size_t
)(
tab
-
p
)
||
strncmp
(
mailbox
->
quota
.
root
,
p
,
tab
-
p
)
!=
0
)
{
assert
(
mailbox
->
quota
.
lock_count
==
0
);
if
(
mailbox
->
quota
.
fd
!=
-1
)
{
close
(
mailbox
->
quota
.
fd
);
}
mailbox
->
quota
.
fd
=
-1
;
}
free
(
mailbox
->
quota
.
root
);
}
if
(
p
<
tab
)
{
mailbox
->
quota
.
root
=
xstrndup
(
p
,
tab
-
p
);
}
else
{
mailbox
->
quota
.
root
=
NULL
;
}
if
(
!
oldformat
)
{
/* read uniqueid */
p
=
tab
+
1
;
if
(
p
==
eol
)
return
IMAP_MAILBOX_BADFORMAT
;
mailbox
->
uniqueid
=
xstrndup
(
p
,
eol
-
p
);
}
else
{
/* uniqueid needs to be generated when we know the uidvalidity */
mailbox
->
uniqueid
=
NULL
;
}
/* Read names of user flags */
p
=
eol
+
1
;
eol
=
memchr
(
p
,
'\n'
,
mailbox
->
header_len
-
(
p
-
mailbox
->
header_base
));
if
(
!
eol
)
{
return
IMAP_MAILBOX_BADFORMAT
;
}
name
=
p
;
flag
=
0
;
while
(
name
<=
eol
&&
flag
<
MAX_USER_FLAGS
)
{
p
=
memchr
(
name
,
' '
,
eol
-
name
);
if
(
!
p
)
p
=
eol
;
if
(
mailbox
->
flagname
[
flag
])
free
(
mailbox
->
flagname
[
flag
]);
if
(
name
!=
p
)
{
mailbox
->
flagname
[
flag
++
]
=
xstrndup
(
name
,
p
-
name
);
}
else
{
mailbox
->
flagname
[
flag
++
]
=
NULL
;
}
name
=
p
+
1
;
}
while
(
flag
<
MAX_USER_FLAGS
)
{
if
(
mailbox
->
flagname
[
flag
])
free
(
mailbox
->
flagname
[
flag
]);
mailbox
->
flagname
[
flag
++
]
=
NULL
;
}
if
(
!
mailbox
->
uniqueid
)
{
char
buf
[
32
];
/* generate uniqueid */
mailbox_lock_header
(
mailbox
);
mailbox_open_index
(
mailbox
);
mailbox_make_uniqueid
(
mailbox
->
name
,
mailbox
->
uidvalidity
,
buf
);
mailbox
->
uniqueid
=
xstrdup
(
buf
);
mailbox_write_header
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
}
return
0
;
}
/*
* Read the acl out of the header of 'mailbox'
*/
int
mailbox_read_header_acl
(
struct
mailbox
*
mailbox
)
{
const
char
*
p
,
*
eol
;
/* Check magic number */
if
(
mailbox
->
header_len
<
sizeof
(
MAILBOX_HEADER_MAGIC
)
-1
||
strncmp
(
mailbox
->
header_base
,
MAILBOX_HEADER_MAGIC
,
sizeof
(
MAILBOX_HEADER_MAGIC
)
-1
))
{
return
IMAP_MAILBOX_BADFORMAT
;
}
/* Skip quota file pathname */
p
=
mailbox
->
header_base
+
sizeof
(
MAILBOX_HEADER_MAGIC
)
-1
;
eol
=
memchr
(
p
,
'\n'
,
mailbox
->
header_len
-
(
p
-
mailbox
->
header_base
));
if
(
!
eol
)
{
return
IMAP_MAILBOX_BADFORMAT
;
}
/* Skip names of user flags */
p
=
eol
+
1
;
eol
=
memchr
(
p
,
'\n'
,
mailbox
->
header_len
-
(
p
-
mailbox
->
header_base
));
if
(
!
eol
)
{
return
IMAP_MAILBOX_BADFORMAT
;
}
/* Read ACL */
p
=
eol
+
1
;
eol
=
memchr
(
p
,
'\n'
,
mailbox
->
header_len
-
(
p
-
mailbox
->
header_base
));
if
(
!
eol
)
{
return
IMAP_MAILBOX_BADFORMAT
;
}
free
(
mailbox
->
acl
);
mailbox
->
acl
=
xstrndup
(
p
,
eol
-
p
);
return
0
;
}
/*
* Read the the ACL for 'mailbox'.
*/
int
mailbox_read_acl
(
struct
mailbox
*
mailbox
,
struct
auth_state
*
auth_state
)
{
int
r
;
char
*
acl
;
r
=
mboxlist_lookup
(
mailbox
->
name
,
(
char
**
)
0
,
&
acl
,
NULL
);
if
(
r
)
return
r
;
free
(
mailbox
->
acl
);
mailbox
->
acl
=
xstrdup
(
acl
);
mailbox
->
myrights
=
cyrus_acl_myrights
(
auth_state
,
mailbox
->
acl
);
return
0
;
}
/*
* Read the header of the index file for mailbox
*/
int
mailbox_read_index_header
(
struct
mailbox
*
mailbox
)
{
struct
stat
sbuf
;
int
upgrade
=
0
;
if
(
mailbox
->
index_fd
==
-1
)
return
IMAP_MAILBOX_BADFORMAT
;
fstat
(
mailbox
->
index_fd
,
&
sbuf
);
mailbox
->
index_ino
=
sbuf
.
st_ino
;
mailbox
->
index_mtime
=
sbuf
.
st_mtime
;
mailbox
->
index_size
=
sbuf
.
st_size
;
map_refresh
(
mailbox
->
index_fd
,
0
,
&
mailbox
->
index_base
,
&
mailbox
->
index_len
,
sbuf
.
st_size
,
"index"
,
mailbox
->
name
);
if
(
mailbox
->
index_len
<
OFFSET_POP3_LAST_LOGIN
||
(
mailbox
->
index_len
<
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_START_OFFSET
)))))
{
return
IMAP_MAILBOX_BADFORMAT
;
}
if
(
mailbox_doing_reconstruct
)
{
mailbox
->
generation_no
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_GENERATION_NO
)));
}
mailbox
->
format
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_FORMAT
)));
mailbox
->
minor_version
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_MINOR_VERSION
)));
mailbox
->
start_offset
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_START_OFFSET
)));
mailbox
->
record_size
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_RECORD_SIZE
)));
mailbox
->
exists
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_EXISTS
)));
mailbox
->
last_appenddate
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_LAST_APPENDDATE
)));
mailbox
->
last_uid
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_LAST_UID
)));
mailbox
->
quota_mailbox_used
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_QUOTA_MAILBOX_USED
)));
if
(
mailbox
->
start_offset
<
OFFSET_POP3_LAST_LOGIN
+
sizeof
(
bit32
))
{
mailbox
->
pop3_last_login
=
0
;
}
else
{
mailbox
->
pop3_last_login
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_POP3_LAST_LOGIN
)));
}
if
(
mailbox
->
start_offset
<
OFFSET_UIDVALIDITY
+
sizeof
(
bit32
))
{
mailbox
->
uidvalidity
=
1
;
}
else
{
mailbox
->
uidvalidity
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_UIDVALIDITY
)));
}
if
(
mailbox
->
start_offset
<
OFFSET_FLAGGED
+
sizeof
(
bit32
))
{
/* calculate them now */
if
(
mailbox_calculate_flagcounts
(
mailbox
))
return
IMAP_IOERROR
;
upgrade
=
1
;
}
else
{
mailbox
->
deleted
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_DELETED
)));
mailbox
->
answered
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_ANSWERED
)));
mailbox
->
flagged
=
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_FLAGGED
)));
mailbox
->
dirty
=
0
;
}
if
(
mailbox
->
start_offset
<
OFFSET_POP3_NEW_UIDL
+
sizeof
(
bit32
))
{
mailbox
->
pop3_new_uidl
=
!
mailbox
->
exists
;
upgrade
=
1
;
}
else
{
mailbox
->
pop3_new_uidl
=
!
mailbox
->
exists
||
ntohl
(
*
((
bit32
*
)(
mailbox
->
index_base
+
OFFSET_POP3_NEW_UIDL
)));
}
if
(
upgrade
)
{
if
(
mailbox_upgrade_index
(
mailbox
))
return
IMAP_IOERROR
;
/* things might have been changed out from under us. reread */
return
mailbox_open_index
(
mailbox
);
}
if
(
!
mailbox_doing_reconstruct
&&
(
mailbox
->
minor_version
<
MAILBOX_MINOR_VERSION
))
{
return
IMAP_MAILBOX_BADFORMAT
;
}
return
0
;
}
/*
* Read an index record from a mailbox
*/
int
mailbox_read_index_record
(
mailbox
,
msgno
,
record
)
struct
mailbox
*
mailbox
;
unsigned
msgno
;
struct
index_record
*
record
;
{
unsigned
long
offset
;
unsigned
const
char
*
buf
;
int
n
;
offset
=
mailbox
->
start_offset
+
(
msgno
-1
)
*
mailbox
->
record_size
;
if
(
offset
+
INDEX_RECORD_SIZE
>
mailbox
->
index_len
)
{
syslog
(
LOG_ERR
,
"IOERROR: index record %u for %s past end of file"
,
msgno
,
mailbox
->
name
);
return
IMAP_IOERROR
;
}
buf
=
(
unsigned
char
*
)
mailbox
->
index_base
+
offset
;
record
->
uid
=
htonl
(
*
((
bit32
*
)(
buf
+
OFFSET_UID
)));
record
->
internaldate
=
htonl
(
*
((
bit32
*
)(
buf
+
OFFSET_INTERNALDATE
)));
record
->
sentdate
=
htonl
(
*
((
bit32
*
)(
buf
+
OFFSET_SENTDATE
)));
record
->
size
=
htonl
(
*
((
bit32
*
)(
buf
+
OFFSET_SIZE
)));
record
->
header_size
=
htonl
(
*
((
bit32
*
)(
buf
+
OFFSET_HEADER_SIZE
)));
record
->
content_offset
=
htonl
(
*
((
bit32
*
)(
buf
+
OFFSET_CONTENT_OFFSET
)));
record
->
cache_offset
=
htonl
(
*
((
bit32
*
)(
buf
+
OFFSET_CACHE_OFFSET
)));
record
->
last_updated
=
htonl
(
*
((
bit32
*
)(
buf
+
OFFSET_LAST_UPDATED
)));
record
->
system_flags
=
htonl
(
*
((
bit32
*
)(
buf
+
OFFSET_SYSTEM_FLAGS
)));
for
(
n
=
0
;
n
<
MAX_USER_FLAGS
/
32
;
n
++
)
{
record
->
user_flags
[
n
]
=
htonl
(
*
((
bit32
*
)(
buf
+
OFFSET_USER_FLAGS
+
4
*
n
)));
}
return
0
;
}
/*
* Open and read the quota file 'quota'
*/
int
mailbox_read_quota
(
quota
)
struct
quota
*
quota
;
{
const
char
*
p
,
*
eol
;
char
buf
[
4096
];
const
char
*
quota_base
=
0
;
unsigned
long
quota_len
=
0
;
if
(
!
quota
->
root
)
{
quota
->
used
=
0
;
quota
->
limit
=
-1
;
return
0
;
}
if
(
quota
->
fd
==
-1
)
{
mailbox_hash_quota
(
buf
,
quota
->
root
);
quota
->
fd
=
open
(
buf
,
O_RDWR
,
0
);
if
(
quota
->
fd
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: opening quota file %s: %m"
,
buf
);
return
IMAP_IOERROR
;
}
}
map_refresh
(
quota
->
fd
,
1
,
&
quota_base
,
&
quota_len
,
MAP_UNKNOWN_LEN
,
buf
,
0
);
p
=
quota_base
;
eol
=
memchr
(
p
,
'\n'
,
quota_len
-
(
p
-
quota_base
));
if
(
!
eol
)
{
map_free
(
&
quota_base
,
&
quota_len
);
return
IMAP_MAILBOX_BADFORMAT
;
}
quota
->
used
=
atol
(
p
);
p
=
eol
+
1
;
eol
=
memchr
(
p
,
'\n'
,
quota_len
-
(
p
-
quota_base
));
if
(
!
eol
)
{
map_free
(
&
quota_base
,
&
quota_len
);
return
IMAP_MAILBOX_BADFORMAT
;
}
quota
->
limit
=
atoi
(
p
);
map_free
(
&
quota_base
,
&
quota_len
);
return
0
;
}
/*
* Lock the header for 'mailbox'. Reread header if necessary.
*/
int
mailbox_lock_header
(
mailbox
)
struct
mailbox
*
mailbox
;
{
char
fnamebuf
[
MAX_MAILBOX_PATH
];
struct
stat
sbuf
;
const
char
*
lockfailaction
;
int
r
;
if
(
mailbox
->
header_lock_count
++
)
return
0
;
assert
(
mailbox
->
index_lock_count
==
0
);
assert
(
mailbox
->
quota
.
lock_count
==
0
);
assert
(
mailbox
->
seen_lock_count
==
0
);
strcpy
(
fnamebuf
,
mailbox
->
path
);
strcat
(
fnamebuf
,
FNAME_HEADER
);
r
=
lock_reopen
(
mailbox
->
header_fd
,
fnamebuf
,
&
sbuf
,
&
lockfailaction
);
if
(
r
)
{
mailbox
->
header_lock_count
--
;
syslog
(
LOG_ERR
,
"IOERROR: %s header for %s: %m"
,
lockfailaction
,
mailbox
->
name
);
return
IMAP_IOERROR
;
}
if
(
sbuf
.
st_ino
!=
mailbox
->
header_ino
)
{
map_free
(
&
mailbox
->
header_base
,
&
mailbox
->
header_len
);
map_refresh
(
mailbox
->
header_fd
,
1
,
&
mailbox
->
header_base
,
&
mailbox
->
header_len
,
sbuf
.
st_size
,
"header"
,
mailbox
->
name
);
mailbox
->
header_ino
=
sbuf
.
st_ino
;
r
=
mailbox_read_header
(
mailbox
);
if
(
r
&&
!
mailbox_doing_reconstruct
)
{
mailbox_unlock_header
(
mailbox
);
return
r
;
}
}
return
0
;
}
/*
* Lock the index file for 'mailbox'. Reread index file header if necessary.
*/
int
mailbox_lock_index
(
mailbox
)
struct
mailbox
*
mailbox
;
{
char
fnamebuf
[
MAX_MAILBOX_PATH
];
struct
stat
sbuffd
,
sbuffile
;
int
r
;
if
(
mailbox
->
index_lock_count
++
)
return
0
;
assert
(
mailbox
->
quota
.
lock_count
==
0
);
assert
(
mailbox
->
seen_lock_count
==
0
);
strcpy
(
fnamebuf
,
mailbox
->
path
);
strcat
(
fnamebuf
,
FNAME_INDEX
);
for
(;;)
{
r
=
lock_blocking
(
mailbox
->
index_fd
);
if
(
r
==
-1
)
{
mailbox
->
index_lock_count
--
;
syslog
(
LOG_ERR
,
"IOERROR: locking index for %s: %m"
,
mailbox
->
name
);
return
IMAP_IOERROR
;
}
fstat
(
mailbox
->
index_fd
,
&
sbuffd
);
r
=
stat
(
fnamebuf
,
&
sbuffile
);
if
(
r
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: stating index for %s: %m"
,
mailbox
->
name
);
mailbox_unlock_index
(
mailbox
);
return
IMAP_IOERROR
;
}
if
(
sbuffd
.
st_ino
==
sbuffile
.
st_ino
)
break
;
if
((
r
=
mailbox_open_index
(
mailbox
)))
{
return
r
;
}
}
r
=
mailbox_read_index_header
(
mailbox
);
if
(
r
&&
!
mailbox_doing_reconstruct
)
{
mailbox_unlock_index
(
mailbox
);
return
r
;
}
return
0
;
}
/*
* Place a POP lock on 'mailbox'.
*/
int
mailbox_lock_pop
(
mailbox
)
struct
mailbox
*
mailbox
;
{
int
r
=
-1
;
if
(
mailbox
->
pop_lock_count
++
)
return
0
;
r
=
lock_nonblocking
(
mailbox
->
cache_fd
);
if
(
r
==
-1
)
{
mailbox
->
pop_lock_count
--
;
if
(
errno
==
EWOULDBLOCK
||
errno
==
EAGAIN
||
errno
==
EACCES
)
{
return
IMAP_MAILBOX_POPLOCKED
;
}
syslog
(
LOG_ERR
,
"IOERROR: locking cache for %s: %m"
,
mailbox
->
name
);
return
IMAP_IOERROR
;
}
return
0
;
}
/*
* Lock the quota file 'quota'. Reread quota file if necessary.
*/
int
mailbox_lock_quota
(
quota
)
struct
quota
*
quota
;
{
char
quota_path
[
MAX_MAILBOX_PATH
];
struct
stat
sbuf
;
const
char
*
lockfailaction
;
int
r
;
/* assert(mailbox->header_lock_count != 0); */
if
(
quota
->
lock_count
++
)
return
0
;
/* assert(mailbox->seen_lock_count == 0); */
if
(
!
quota
->
root
)
{
quota
->
used
=
0
;
quota
->
limit
=
-1
;
return
0
;
}
mailbox_hash_quota
(
quota_path
,
quota
->
root
);
if
(
quota
->
fd
==
-1
)
{
quota
->
fd
=
open
(
quota_path
,
O_RDWR
,
0
);
if
(
quota
->
fd
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: opening quota file %s: %m"
,
quota_path
);
return
IMAP_IOERROR
;
}
}
r
=
lock_reopen
(
quota
->
fd
,
quota_path
,
&
sbuf
,
&
lockfailaction
);
if
(
r
==
-1
)
{
quota
->
lock_count
--
;
syslog
(
LOG_ERR
,
"IOERROR: %s quota %s: %m"
,
lockfailaction
,
quota
->
root
);
return
IMAP_IOERROR
;
}
return
mailbox_read_quota
(
quota
);
}
/*
* Release lock on the header for 'mailbox'
*/
void
mailbox_unlock_header
(
struct
mailbox
*
mailbox
)
{
assert
(
mailbox
->
header_lock_count
!=
0
);
if
(
--
mailbox
->
header_lock_count
==
0
)
{
lock_unlock
(
mailbox
->
header_fd
);
}
}
/*
* Release lock on the index file for 'mailbox'
*/
void
mailbox_unlock_index
(
mailbox
)
struct
mailbox
*
mailbox
;
{
assert
(
mailbox
->
index_lock_count
!=
0
);
if
(
--
mailbox
->
index_lock_count
==
0
)
{
lock_unlock
(
mailbox
->
index_fd
);
}
}
/*
* Release POP lock for 'mailbox'
*/
void
mailbox_unlock_pop
(
mailbox
)
struct
mailbox
*
mailbox
;
{
assert
(
mailbox
->
pop_lock_count
!=
0
);
if
(
--
mailbox
->
pop_lock_count
==
0
)
{
lock_unlock
(
mailbox
->
cache_fd
);
}
}
/*
* Release lock on the quota file 'quota'
*/
void
mailbox_unlock_quota
(
quota
)
struct
quota
*
quota
;
{
assert
(
quota
->
lock_count
!=
0
);
if
(
--
quota
->
lock_count
==
0
&&
quota
->
root
)
{
lock_unlock
(
quota
->
fd
);
}
}
/*
* Write the header file for 'mailbox'
*/
int
mailbox_write_header
(
struct
mailbox
*
mailbox
)
{
int
flag
;
FILE
*
newheader
;
int
newheader_fd
;
char
fnamebuf
[
MAX_MAILBOX_PATH
];
char
newfnamebuf
[
MAX_MAILBOX_PATH
];
struct
stat
sbuf
;
assert
(
mailbox
->
header_lock_count
!=
0
);
strcpy
(
fnamebuf
,
mailbox
->
path
);
strcat
(
fnamebuf
,
FNAME_HEADER
);
strcpy
(
newfnamebuf
,
fnamebuf
);
strcat
(
newfnamebuf
,
".NEW"
);
newheader
=
fopen
(
newfnamebuf
,
"w+"
);
if
(
!
newheader
)
{
syslog
(
LOG_ERR
,
"IOERROR: writing %s: %m"
,
newfnamebuf
);
return
IMAP_IOERROR
;
}
fputs
(
MAILBOX_HEADER_MAGIC
,
newheader
);
fprintf
(
newheader
,
"%s
\t
%s
\n
"
,
mailbox
->
quota
.
root
?
mailbox
->
quota
.
root
:
""
,
mailbox
->
uniqueid
);
for
(
flag
=
0
;
flag
<
MAX_USER_FLAGS
;
flag
++
)
{
if
(
mailbox
->
flagname
[
flag
])
{
fprintf
(
newheader
,
"%s "
,
mailbox
->
flagname
[
flag
]);
}
}
fprintf
(
newheader
,
"
\n
"
);
fprintf
(
newheader
,
"%s
\n
"
,
mailbox
->
acl
);
fflush
(
newheader
);
newheader_fd
=
dup
(
fileno
(
newheader
));
if
(
ferror
(
newheader
)
||
fsync
(
fileno
(
newheader
))
||
lock_blocking
(
newheader_fd
)
==
-1
||
rename
(
newfnamebuf
,
fnamebuf
)
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: writing %s: %m"
,
newfnamebuf
);
fclose
(
newheader
);
close
(
newheader_fd
);
unlink
(
newfnamebuf
);
return
IMAP_IOERROR
;
}
fclose
(
newheader
);
if
(
mailbox
->
header_fd
!=
-1
)
{
close
(
mailbox
->
header_fd
);
map_free
(
&
mailbox
->
header_base
,
&
mailbox
->
header_len
);
}
mailbox
->
header_fd
=
newheader_fd
;
if
(
fstat
(
mailbox
->
header_fd
,
&
sbuf
)
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: fstating %s: %m"
,
fnamebuf
);
fatal
(
"can't fstat header file"
,
EC_OSFILE
);
}
map_refresh
(
mailbox
->
header_fd
,
1
,
&
mailbox
->
header_base
,
&
mailbox
->
header_len
,
sbuf
.
st_size
,
"header"
,
mailbox
->
name
);
mailbox
->
header_ino
=
sbuf
.
st_ino
;
return
0
;
}
/*
* Write the index header for 'mailbox'
*/
int
mailbox_write_index_header
(
struct
mailbox
*
mailbox
)
{
char
buf
[
INDEX_HEADER_SIZE
];
unsigned
long
header_size
=
INDEX_HEADER_SIZE
;
int
n
;
assert
(
mailbox
->
index_lock_count
!=
0
);
if
(
updatenotifier
)
updatenotifier
(
mailbox
);
#if 0
if (acappush_sock != -1) {
acapmbdata_t acapdata;
/* fill in structure */
strcpy(acapdata.name, mailbox->name);
acapdata.uidvalidity = mailbox->uidvalidity;
acapdata.exists = mailbox->exists;
acapdata.deleted = mailbox->deleted;
acapdata.answered = mailbox->answered;
acapdata.flagged = mailbox->flagged;
/* send */
if (sendto(acappush_sock, &acapdata, 20+strlen(mailbox->name), 0,
(struct sockaddr *) &acappush_remote,
acappush_remote_len) == -1) {
syslog(LOG_ERR, "sending to acappush: %m");
}
}
#endif
*
((
bit32
*
)(
buf
+
OFFSET_GENERATION_NO
))
=
mailbox
->
generation_no
;
*
((
bit32
*
)(
buf
+
OFFSET_FORMAT
))
=
htonl
(
mailbox
->
format
);
*
((
bit32
*
)(
buf
+
OFFSET_MINOR_VERSION
))
=
htonl
(
mailbox
->
minor_version
);
*
((
bit32
*
)(
buf
+
OFFSET_START_OFFSET
))
=
htonl
(
mailbox
->
start_offset
);
*
((
bit32
*
)(
buf
+
OFFSET_RECORD_SIZE
))
=
htonl
(
mailbox
->
record_size
);
*
((
bit32
*
)(
buf
+
OFFSET_EXISTS
))
=
htonl
(
mailbox
->
exists
);
*
((
bit32
*
)(
buf
+
OFFSET_LAST_APPENDDATE
))
=
htonl
(
mailbox
->
last_appenddate
);
*
((
bit32
*
)(
buf
+
OFFSET_LAST_UID
))
=
htonl
(
mailbox
->
last_uid
);
*
((
bit32
*
)(
buf
+
OFFSET_QUOTA_MAILBOX_USED
))
=
htonl
(
mailbox
->
quota_mailbox_used
);
*
((
bit32
*
)(
buf
+
OFFSET_POP3_LAST_LOGIN
))
=
htonl
(
mailbox
->
pop3_last_login
);
*
((
bit32
*
)(
buf
+
OFFSET_UIDVALIDITY
))
=
htonl
(
mailbox
->
uidvalidity
);
*
((
bit32
*
)(
buf
+
OFFSET_DELETED
))
=
htonl
(
mailbox
->
deleted
);
*
((
bit32
*
)(
buf
+
OFFSET_ANSWERED
))
=
htonl
(
mailbox
->
answered
);
*
((
bit32
*
)(
buf
+
OFFSET_FLAGGED
))
=
htonl
(
mailbox
->
flagged
);
*
((
bit32
*
)(
buf
+
OFFSET_POP3_NEW_UIDL
))
=
htonl
(
mailbox
->
pop3_new_uidl
);
if
(
mailbox
->
start_offset
<
header_size
)
header_size
=
mailbox
->
start_offset
;
lseek
(
mailbox
->
index_fd
,
0
,
SEEK_SET
);
n
=
retry_write
(
mailbox
->
index_fd
,
buf
,
header_size
);
if
((
unsigned
long
)
n
!=
header_size
||
fsync
(
mailbox
->
index_fd
))
{
syslog
(
LOG_ERR
,
"IOERROR: writing index header for %s: %m"
,
mailbox
->
name
);
/* xxx can we unroll the acap send??? */
return
IMAP_IOERROR
;
}
return
0
;
}
/*
* Write an index record to a mailbox
* call fsync() on index_fd if 'sync' is true
*/
int
mailbox_write_index_record
(
struct
mailbox
*
mailbox
,
unsigned
msgno
,
struct
index_record
*
record
,
int
sync
)
{
int
n
;
char
buf
[
INDEX_RECORD_SIZE
];
*
((
bit32
*
)(
buf
+
OFFSET_UID
))
=
htonl
(
record
->
uid
);
*
((
bit32
*
)(
buf
+
OFFSET_INTERNALDATE
))
=
htonl
(
record
->
internaldate
);
*
((
bit32
*
)(
buf
+
OFFSET_SENTDATE
))
=
htonl
(
record
->
sentdate
);
*
((
bit32
*
)(
buf
+
OFFSET_SIZE
))
=
htonl
(
record
->
size
);
*
((
bit32
*
)(
buf
+
OFFSET_HEADER_SIZE
))
=
htonl
(
record
->
header_size
);
*
((
bit32
*
)(
buf
+
OFFSET_CONTENT_OFFSET
))
=
htonl
(
record
->
content_offset
);
*
((
bit32
*
)(
buf
+
OFFSET_CACHE_OFFSET
))
=
htonl
(
record
->
cache_offset
);
*
((
bit32
*
)(
buf
+
OFFSET_LAST_UPDATED
))
=
htonl
(
record
->
last_updated
);
*
((
bit32
*
)(
buf
+
OFFSET_SYSTEM_FLAGS
))
=
htonl
(
record
->
system_flags
);
for
(
n
=
0
;
n
<
MAX_USER_FLAGS
/
32
;
n
++
)
{
*
((
bit32
*
)(
buf
+
OFFSET_USER_FLAGS
+
4
*
n
))
=
htonl
(
record
->
user_flags
[
n
]);
}
n
=
lseek
(
mailbox
->
index_fd
,
mailbox
->
start_offset
+
(
msgno
-1
)
*
mailbox
->
record_size
,
SEEK_SET
);
if
(
n
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: seeking index record %u for %s: %m"
,
msgno
,
mailbox
->
name
);
return
IMAP_IOERROR
;
}
n
=
retry_write
(
mailbox
->
index_fd
,
buf
,
INDEX_RECORD_SIZE
);
if
(
n
!=
INDEX_RECORD_SIZE
||
(
sync
&&
fsync
(
mailbox
->
index_fd
)))
{
syslog
(
LOG_ERR
,
"IOERROR: writing index record %u for %s: %m"
,
msgno
,
mailbox
->
name
);
return
IMAP_IOERROR
;
}
return
0
;
}
/*
* Append a new record to the index file
* call fsync() on index_fd if 'sync' is true
*/
int
mailbox_append_index
(
struct
mailbox
*
mailbox
,
struct
index_record
*
record
,
unsigned
start
,
unsigned
num
,
int
sync
)
{
unsigned
i
;
int
j
,
len
,
n
;
char
*
buf
,
*
p
;
long
last_offset
;
assert
(
mailbox
->
index_lock_count
!=
0
);
if
(
mailbox
->
record_size
<
INDEX_RECORD_SIZE
)
{
return
IMAP_MAILBOX_BADFORMAT
;
}
len
=
num
*
mailbox
->
record_size
;
buf
=
xmalloc
(
len
);
memset
(
buf
,
0
,
len
);
for
(
i
=
0
;
i
<
num
;
i
++
)
{
p
=
buf
+
i
*
mailbox
->
record_size
;
*
((
bit32
*
)(
p
+
OFFSET_UID
))
=
htonl
(
record
[
i
].
uid
);
*
((
bit32
*
)(
p
+
OFFSET_INTERNALDATE
))
=
htonl
(
record
[
i
].
internaldate
);
*
((
bit32
*
)(
p
+
OFFSET_SENTDATE
))
=
htonl
(
record
[
i
].
sentdate
);
*
((
bit32
*
)(
p
+
OFFSET_SIZE
))
=
htonl
(
record
[
i
].
size
);
*
((
bit32
*
)(
p
+
OFFSET_HEADER_SIZE
))
=
htonl
(
record
[
i
].
header_size
);
*
((
bit32
*
)(
p
+
OFFSET_CONTENT_OFFSET
))
=
htonl
(
record
[
i
].
content_offset
);
*
((
bit32
*
)(
p
+
OFFSET_CACHE_OFFSET
))
=
htonl
(
record
[
i
].
cache_offset
);
*
((
bit32
*
)(
p
+
OFFSET_LAST_UPDATED
))
=
htonl
(
record
[
i
].
last_updated
);
*
((
bit32
*
)(
p
+
OFFSET_SYSTEM_FLAGS
))
=
htonl
(
record
[
i
].
system_flags
);
p
+=
OFFSET_USER_FLAGS
;
for
(
j
=
0
;
j
<
MAX_USER_FLAGS
/
32
;
j
++
,
p
+=
4
)
{
*
((
bit32
*
)
p
)
=
htonl
(
record
[
i
].
user_flags
[
j
]);
}
}
last_offset
=
mailbox
->
start_offset
+
start
*
mailbox
->
record_size
;
lseek
(
mailbox
->
index_fd
,
last_offset
,
SEEK_SET
);
n
=
retry_write
(
mailbox
->
index_fd
,
buf
,
len
);
free
(
buf
);
if
(
n
!=
len
||
(
sync
&&
fsync
(
mailbox
->
index_fd
)))
{
syslog
(
LOG_ERR
,
"IOERROR: appending index records for %s: %m"
,
mailbox
->
name
);
ftruncate
(
mailbox
->
index_fd
,
last_offset
);
return
IMAP_IOERROR
;
}
return
0
;
}
/*
* Write out the quota 'quota'
*/
int
mailbox_write_quota
(
quota
)
struct
quota
*
quota
;
{
int
r
;
char
quota_path
[
MAX_MAILBOX_PATH
];
char
new_quota_path
[
MAX_MAILBOX_PATH
];
FILE
*
newfile
;
int
newfd
;
assert
(
quota
->
lock_count
!=
0
);
if
(
!
quota
->
root
)
return
0
;
mailbox_hash_quota
(
quota_path
,
quota
->
root
);
strcpy
(
new_quota_path
,
quota_path
);
strcat
(
new_quota_path
,
".NEW"
);
newfile
=
fopen
(
new_quota_path
,
"w+"
);
if
(
!
newfile
)
{
syslog
(
LOG_ERR
,
"IOERROR: creating quota file %s: %m"
,
new_quota_path
);
return
IMAP_IOERROR
;
}
newfd
=
dup
(
fileno
(
newfile
));
r
=
lock_blocking
(
newfd
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"IOERROR: locking quota file %s: %m"
,
new_quota_path
);
fclose
(
newfile
);
close
(
newfd
);
return
IMAP_IOERROR
;
}
fprintf
(
newfile
,
"%lu
\n
%d
\n
"
,
quota
->
used
,
quota
->
limit
);
fflush
(
newfile
);
if
(
ferror
(
newfile
)
||
fsync
(
fileno
(
newfile
)))
{
syslog
(
LOG_ERR
,
"IOERROR: writing quota file %s: %m"
,
new_quota_path
);
fclose
(
newfile
);
close
(
newfd
);
return
IMAP_IOERROR
;
}
if
(
rename
(
new_quota_path
,
quota_path
))
{
syslog
(
LOG_ERR
,
"IOERROR: renaming quota file %s: %m"
,
quota_path
);
fclose
(
newfile
);
close
(
newfd
);
return
IMAP_IOERROR
;
}
fclose
(
newfile
);
if
(
quota
->
fd
!=
-1
)
{
close
(
quota
->
fd
);
quota
->
fd
=
-1
;
}
quota
->
fd
=
newfd
;
return
0
;
}
/*
* Remove the quota root 'quota'
*/
int
mailbox_delete_quota
(
quota
)
struct
quota
*
quota
;
{
char
quota_path
[
MAX_MAILBOX_PATH
];
assert
(
quota
->
lock_count
!=
0
);
if
(
!
quota
->
root
)
return
0
;
mailbox_hash_quota
(
quota_path
,
quota
->
root
);
unlink
(
quota_path
);
if
(
quota
->
fd
!=
-1
)
{
close
(
quota
->
fd
);
quota
->
fd
=
-1
;
}
free
(
quota
->
root
);
quota
->
root
=
0
;
return
0
;
}
/*
* Lock the index file for 'mailbox'.
* DON'T Reread index file header if necessary.
*/
static
int
mailbox_lock_index_for_upgrade
(
struct
mailbox
*
mailbox
)
{
char
fnamebuf
[
MAX_MAILBOX_PATH
];
struct
stat
sbuffd
,
sbuffile
;
int
r
;
if
(
mailbox
->
index_lock_count
++
)
return
0
;
assert
(
mailbox
->
quota
.
lock_count
==
0
);
assert
(
mailbox
->
seen_lock_count
==
0
);
strcpy
(
fnamebuf
,
mailbox
->
path
);
strcat
(
fnamebuf
,
FNAME_INDEX
);
for
(;;)
{
r
=
lock_blocking
(
mailbox
->
index_fd
);
if
(
r
==
-1
)
{
mailbox
->
index_lock_count
--
;
syslog
(
LOG_ERR
,
"IOERROR: locking index for %s: %m"
,
mailbox
->
name
);
return
IMAP_IOERROR
;
}
fstat
(
mailbox
->
index_fd
,
&
sbuffd
);
r
=
stat
(
fnamebuf
,
&
sbuffile
);
if
(
r
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: stating index for %s: %m"
,
mailbox
->
name
);
mailbox_unlock_index
(
mailbox
);
return
IMAP_IOERROR
;
}
if
(
sbuffd
.
st_ino
==
sbuffile
.
st_ino
)
break
;
if
((
r
=
mailbox_open_index
(
mailbox
)))
{
return
r
;
}
}
return
0
;
}
/*
* Upgrade the index header for 'mailbox'
*/
static
int
mailbox_upgrade_index
(
struct
mailbox
*
mailbox
)
{
int
r
;
unsigned
msgno
;
bit32
oldstart_offset
;
char
buf
[
INDEX_HEADER_SIZE
];
char
fnamebuf
[
MAX_MAILBOX_PATH
],
fnamebufnew
[
MAX_MAILBOX_PATH
];
FILE
*
newindex
;
char
*
fnametail
;
char
*
bufp
;
/* Lock files and open new index file */
r
=
mailbox_lock_header
(
mailbox
);
if
(
r
)
return
r
;
r
=
mailbox_lock_index_for_upgrade
(
mailbox
);
if
(
r
)
{
mailbox_unlock_header
(
mailbox
);
return
r
;
}
r
=
mailbox_lock_pop
(
mailbox
);
if
(
r
)
{
mailbox_unlock_index
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
return
r
;
}
strcpy
(
fnamebuf
,
mailbox
->
path
);
strcat
(
fnamebuf
,
FNAME_INDEX
);
strcat
(
fnamebuf
,
".NEW"
);
newindex
=
fopen
(
fnamebuf
,
"w+"
);
if
(
!
newindex
)
{
syslog
(
LOG_ERR
,
"IOERROR: creating %s: %m"
,
fnamebuf
);
goto
fail
;
}
/* change version number */
mailbox
->
minor_version
=
MAILBOX_MINOR_VERSION
;
/* save old start_offset; change start_offset */
oldstart_offset
=
mailbox
->
start_offset
;
mailbox
->
start_offset
=
INDEX_HEADER_SIZE
;
/* Write the new index header */
*
((
bit32
*
)(
buf
+
OFFSET_GENERATION_NO
))
=
mailbox
->
generation_no
;
*
((
bit32
*
)(
buf
+
OFFSET_FORMAT
))
=
htonl
(
mailbox
->
format
);
*
((
bit32
*
)(
buf
+
OFFSET_MINOR_VERSION
))
=
htonl
(
mailbox
->
minor_version
);
*
((
bit32
*
)(
buf
+
OFFSET_START_OFFSET
))
=
htonl
(
mailbox
->
start_offset
);
*
((
bit32
*
)(
buf
+
OFFSET_RECORD_SIZE
))
=
htonl
(
mailbox
->
record_size
);
*
((
bit32
*
)(
buf
+
OFFSET_EXISTS
))
=
htonl
(
mailbox
->
exists
);
*
((
bit32
*
)(
buf
+
OFFSET_LAST_APPENDDATE
))
=
htonl
(
mailbox
->
last_appenddate
);
*
((
bit32
*
)(
buf
+
OFFSET_LAST_UID
))
=
htonl
(
mailbox
->
last_uid
);
*
((
bit32
*
)(
buf
+
OFFSET_QUOTA_MAILBOX_USED
))
=
htonl
(
mailbox
->
quota_mailbox_used
);
*
((
bit32
*
)(
buf
+
OFFSET_POP3_LAST_LOGIN
))
=
htonl
(
mailbox
->
pop3_last_login
);
*
((
bit32
*
)(
buf
+
OFFSET_UIDVALIDITY
))
=
htonl
(
mailbox
->
uidvalidity
);
*
((
bit32
*
)(
buf
+
OFFSET_DELETED
))
=
htonl
(
mailbox
->
deleted
);
*
((
bit32
*
)(
buf
+
OFFSET_ANSWERED
))
=
htonl
(
mailbox
->
answered
);
*
((
bit32
*
)(
buf
+
OFFSET_FLAGGED
))
=
htonl
(
mailbox
->
flagged
);
*
((
bit32
*
)(
buf
+
OFFSET_POP3_NEW_UIDL
))
=
htonl
(
mailbox
->
pop3_new_uidl
);
fwrite
(
buf
,
1
,
INDEX_HEADER_SIZE
,
newindex
);
/* Write the rest of new index same as old */
for
(
msgno
=
1
;
msgno
<=
mailbox
->
exists
;
msgno
++
)
{
bufp
=
(
char
*
)
(
mailbox
->
index_base
+
oldstart_offset
+
(
msgno
-
1
)
*
mailbox
->
record_size
);
fwrite
(
bufp
,
mailbox
->
record_size
,
1
,
newindex
);
}
/* Ensure everything made it to disk */
fflush
(
newindex
);
if
(
ferror
(
newindex
)
||
fsync
(
fileno
(
newindex
)))
{
syslog
(
LOG_ERR
,
"IOERROR: writing index for %s: %m"
,
mailbox
->
name
);
goto
fail
;
}
strcpy
(
fnamebuf
,
mailbox
->
path
);
fnametail
=
fnamebuf
+
strlen
(
fnamebuf
);
strcpy
(
fnametail
,
FNAME_INDEX
);
strcpy
(
fnamebufnew
,
fnamebuf
);
strcat
(
fnamebufnew
,
".NEW"
);
if
(
rename
(
fnamebufnew
,
fnamebuf
))
{
syslog
(
LOG_ERR
,
"IOERROR: renaming index file for %s: %m"
,
mailbox
->
name
);
goto
fail
;
}
mailbox_unlock_pop
(
mailbox
);
mailbox_unlock_index
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
fclose
(
newindex
);
return
0
;
fail
:
mailbox_unlock_pop
(
mailbox
);
mailbox_unlock_index
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
return
IMAP_IOERROR
;
}
/*
* Calculate the number of messages in the mailbox with
* answered/deleted/flagged system flags
*/
static
int
mailbox_calculate_flagcounts
(
struct
mailbox
*
mailbox
)
{
int
r
;
unsigned
msgno
;
bit32
numansweredflag
=
0
;
bit32
numdeletedflag
=
0
;
bit32
numflaggedflag
=
0
;
struct
stat
sbuf
;
char
*
bufp
;
/* Lock files */
r
=
mailbox_lock_header
(
mailbox
);
if
(
r
)
return
r
;
r
=
mailbox_lock_index_for_upgrade
(
mailbox
);
if
(
r
)
{
mailbox_unlock_header
(
mailbox
);
return
r
;
}
r
=
mailbox_lock_pop
(
mailbox
);
if
(
r
)
{
mailbox_unlock_index
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
return
r
;
}
if
(
fstat
(
mailbox
->
cache_fd
,
&
sbuf
)
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: fstating %s: %m"
,
mailbox
->
name
);
fatal
(
"can't fstat cache file"
,
EC_OSFILE
);
}
mailbox
->
cache_size
=
sbuf
.
st_size
;
map_refresh
(
mailbox
->
cache_fd
,
0
,
&
mailbox
->
cache_base
,
&
mailbox
->
cache_len
,
mailbox
->
cache_size
,
"cache"
,
mailbox
->
name
);
/* for each message look at the system flags */
for
(
msgno
=
1
;
msgno
<=
mailbox
->
exists
;
msgno
++
)
{
bit32
sysflags
;
bufp
=
(
char
*
)
(
mailbox
->
index_base
+
mailbox
->
start_offset
+
(
msgno
-
1
)
*
mailbox
->
record_size
);
/* Sanity check */
if
(
*
((
bit32
*
)(
bufp
+
OFFSET_UID
))
==
0
)
{
syslog
(
LOG_ERR
,
"IOERROR: %s zero index record %u/%lu"
,
mailbox
->
name
,
msgno
,
mailbox
->
exists
);
mailbox_unlock_pop
(
mailbox
);
mailbox_unlock_index
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
return
IMAP_IOERROR
;
}
sysflags
=
ntohl
(
*
((
bit32
*
)(
bufp
+
OFFSET_SYSTEM_FLAGS
)));
if
(
sysflags
&
FLAG_ANSWERED
)
numansweredflag
++
;
if
(
sysflags
&
FLAG_DELETED
)
numdeletedflag
++
;
if
(
sysflags
&
FLAG_FLAGGED
)
numflaggedflag
++
;
}
mailbox
->
answered
=
numansweredflag
;
mailbox
->
deleted
=
numdeletedflag
;
mailbox
->
flagged
=
numflaggedflag
;
mailbox_unlock_pop
(
mailbox
);
mailbox_unlock_index
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
return
0
;
}
/*
* Perform an expunge operation on 'mailbox'. If 'iscurrentdir' is nonzero,
* the current directory is set to the mailbox directory. If nonzero, the
* function pointed to by 'decideproc' is called (with 'deciderock') to
* determine which messages to expunge. If 'decideproc' is a null pointer,
* then messages with the \Deleted flag are expunged.
*/
int
mailbox_expunge
(
mailbox
,
iscurrentdir
,
decideproc
,
deciderock
)
struct
mailbox
*
mailbox
;
int
iscurrentdir
;
mailbox_decideproc_t
*
decideproc
;
void
*
deciderock
;
{
int
r
,
n
;
char
fnamebuf
[
MAX_MAILBOX_PATH
],
fnamebufnew
[
MAX_MAILBOX_PATH
];
FILE
*
newindex
,
*
newcache
;
unsigned
long
*
deleted
;
unsigned
numdeleted
=
0
,
quotadeleted
=
0
;
unsigned
numansweredflag
=
0
;
unsigned
numdeletedflag
=
0
;
unsigned
numflaggedflag
=
0
;
unsigned
newexists
;
unsigned
newdeleted
;
unsigned
newanswered
;
unsigned
newflagged
;
char
*
buf
;
unsigned
msgno
;
int
lastmsgdeleted
=
1
;
unsigned
long
cachediff
=
0
;
unsigned
long
cachestart
=
sizeof
(
bit32
);
unsigned
long
cache_offset
;
struct
stat
sbuf
;
char
*
fnametail
;
/* Lock files and open new index/cache files */
r
=
mailbox_lock_header
(
mailbox
);
if
(
r
)
return
r
;
r
=
mailbox_lock_index
(
mailbox
);
if
(
r
)
{
mailbox_unlock_header
(
mailbox
);
return
r
;
}
r
=
mailbox_lock_pop
(
mailbox
);
if
(
r
)
{
mailbox_unlock_index
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
return
r
;
}
if
(
fstat
(
mailbox
->
cache_fd
,
&
sbuf
)
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: fstating %s: %m"
,
fnamebuf
);
/* xxx is fnamebuf initialized??? */
fatal
(
"can't fstat cache file"
,
EC_OSFILE
);
}
mailbox
->
cache_size
=
sbuf
.
st_size
;
map_refresh
(
mailbox
->
cache_fd
,
0
,
&
mailbox
->
cache_base
,
&
mailbox
->
cache_len
,
mailbox
->
cache_size
,
"cache"
,
mailbox
->
name
);
strcpy
(
fnamebuf
,
mailbox
->
path
);
strcat
(
fnamebuf
,
FNAME_INDEX
);
strcat
(
fnamebuf
,
".NEW"
);
newindex
=
fopen
(
fnamebuf
,
"w+"
);
if
(
!
newindex
)
{
syslog
(
LOG_ERR
,
"IOERROR: creating %s: %m"
,
fnamebuf
);
mailbox_unlock_pop
(
mailbox
);
mailbox_unlock_index
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
return
IMAP_IOERROR
;
}
strcpy
(
fnamebuf
,
mailbox
->
path
);
strcat
(
fnamebuf
,
FNAME_CACHE
);
strcat
(
fnamebuf
,
".NEW"
);
newcache
=
fopen
(
fnamebuf
,
"w+"
);
if
(
!
newcache
)
{
syslog
(
LOG_ERR
,
"IOERROR: creating %s: %m"
,
fnamebuf
);
fclose
(
newindex
);
mailbox_unlock_pop
(
mailbox
);
mailbox_unlock_index
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
return
IMAP_IOERROR
;
}
/* Allocate temporary buffers */
if
(
mailbox
->
exists
>
0
)
{
/* XXX kludge: not all mallocs return a valid pointer to 0 bytes;
some have the good sense to return 0 */
deleted
=
(
unsigned
long
*
)
xmalloc
(
mailbox
->
exists
*
sizeof
(
unsigned
long
));
}
else
{
deleted
=
0
;
}
buf
=
xmalloc
(
mailbox
->
start_offset
>
mailbox
->
record_size
?
mailbox
->
start_offset
:
mailbox
->
record_size
);
/* Copy over headers */
memcpy
(
buf
,
mailbox
->
index_base
,
mailbox
->
start_offset
);
(
*
(
bit32
*
)
buf
)
++
;
/* Increment generation number */
fwrite
(
buf
,
1
,
mailbox
->
start_offset
,
newindex
);
/* Grow the index header if necessary */
for
(
n
=
mailbox
->
start_offset
;
n
<
INDEX_HEADER_SIZE
;
n
++
)
{
if
(
n
==
OFFSET_UIDVALIDITY
+
3
)
{
putc
(
1
,
newindex
);
}
else
{
putc
(
0
,
newindex
);
}
}
fwrite
(
buf
,
1
,
sizeof
(
bit32
),
newcache
);
/* Copy over records for nondeleted messages */
for
(
msgno
=
1
;
msgno
<=
mailbox
->
exists
;
msgno
++
)
{
memcpy
(
buf
,
mailbox
->
index_base
+
mailbox
->
start_offset
+
(
msgno
-
1
)
*
mailbox
->
record_size
,
mailbox
->
record_size
);
/* Sanity check */
if
(
*
((
bit32
*
)(
buf
+
OFFSET_UID
))
==
0
)
{
syslog
(
LOG_ERR
,
"IOERROR: %s zero index record %u/%lu"
,
mailbox
->
name
,
msgno
,
mailbox
->
exists
);
goto
fail
;
}
if
(
decideproc
?
decideproc
(
mailbox
,
deciderock
,
buf
)
:
(
ntohl
(
*
((
bit32
*
)(
buf
+
OFFSET_SYSTEM_FLAGS
)))
&
FLAG_DELETED
))
{
bit32
sysflags
;
/* Remember UID and size */
deleted
[
numdeleted
++
]
=
ntohl
(
*
((
bit32
*
)(
buf
+
OFFSET_UID
)));
quotadeleted
+=
ntohl
(
*
((
bit32
*
)(
buf
+
OFFSET_SIZE
)));
/* figure out if deleted msg has system flags. update counts accordingly */
sysflags
=
ntohl
(
*
((
bit32
*
)(
buf
+
OFFSET_SYSTEM_FLAGS
)));
if
(
sysflags
&
FLAG_ANSWERED
)
numansweredflag
++
;
if
(
sysflags
&
FLAG_DELETED
)
numdeletedflag
++
;
if
(
sysflags
&
FLAG_FLAGGED
)
numflaggedflag
++
;
/* Copy over cache file data */
if
(
!
lastmsgdeleted
)
{
cache_offset
=
ntohl
(
*
((
bit32
*
)(
buf
+
OFFSET_CACHE_OFFSET
)));
fwrite
(
mailbox
->
cache_base
+
cachestart
,
1
,
cache_offset
-
cachestart
,
newcache
);
cachestart
=
cache_offset
;
lastmsgdeleted
=
1
;
}
}
else
{
cache_offset
=
ntohl
(
*
((
bit32
*
)(
buf
+
OFFSET_CACHE_OFFSET
)));
/* Set up for copying cache file data */
if
(
lastmsgdeleted
)
{
cachediff
+=
cache_offset
-
cachestart
;
cachestart
=
cache_offset
;
lastmsgdeleted
=
0
;
}
/* Fix up cache file offset */
*
((
bit32
*
)(
buf
+
OFFSET_CACHE_OFFSET
))
=
htonl
(
cache_offset
-
cachediff
);
fwrite
(
buf
,
1
,
mailbox
->
record_size
,
newindex
);
}
}
/* Copy over any remaining cache file data */
if
(
!
lastmsgdeleted
)
{
fwrite
(
mailbox
->
cache_base
+
cachestart
,
1
,
mailbox
->
cache_size
-
cachestart
,
newcache
);
}
/* Fix up information in index header */
rewind
(
newindex
);
n
=
fread
(
buf
,
1
,
mailbox
->
start_offset
,
newindex
);
if
((
unsigned
long
)
n
!=
mailbox
->
start_offset
)
{
syslog
(
LOG_ERR
,
"IOERROR: reading index header for %s: got %d of %ld"
,
mailbox
->
name
,
n
,
mailbox
->
start_offset
);
goto
fail
;
}
/* Fix up exists */
newexists
=
ntohl
(
*
((
bit32
*
)(
buf
+
OFFSET_EXISTS
)))
-
numdeleted
;
*
((
bit32
*
)(
buf
+
OFFSET_EXISTS
))
=
htonl
(
newexists
);
/* fix up other counts */
newanswered
=
ntohl
(
*
((
bit32
*
)(
buf
+
OFFSET_ANSWERED
)))
-
numansweredflag
;
*
((
bit32
*
)(
buf
+
OFFSET_ANSWERED
))
=
htonl
(
newanswered
);
newdeleted
=
ntohl
(
*
((
bit32
*
)(
buf
+
OFFSET_DELETED
)))
-
numdeletedflag
;
*
((
bit32
*
)(
buf
+
OFFSET_DELETED
))
=
htonl
(
newdeleted
);
newflagged
=
ntohl
(
*
((
bit32
*
)(
buf
+
OFFSET_FLAGGED
)))
-
numflaggedflag
;
*
((
bit32
*
)(
buf
+
OFFSET_FLAGGED
))
=
htonl
(
newflagged
);
/* Fix up quota_mailbox_used */
*
((
bit32
*
)(
buf
+
OFFSET_QUOTA_MAILBOX_USED
))
=
htonl
(
ntohl
(
*
((
bit32
*
)(
buf
+
OFFSET_QUOTA_MAILBOX_USED
)))
-
quotadeleted
);
/* Fix up start offset if necessary */
if
(
mailbox
->
start_offset
<
INDEX_HEADER_SIZE
)
{
*
((
bit32
*
)(
buf
+
OFFSET_START_OFFSET
))
=
htonl
(
INDEX_HEADER_SIZE
);
}
rewind
(
newindex
);
fwrite
(
buf
,
1
,
mailbox
->
start_offset
,
newindex
);
/* Ensure everything made it to disk */
fflush
(
newindex
);
fflush
(
newcache
);
if
(
ferror
(
newindex
)
||
ferror
(
newcache
)
||
fsync
(
fileno
(
newindex
))
||
fsync
(
fileno
(
newcache
)))
{
syslog
(
LOG_ERR
,
"IOERROR: writing index/cache for %s: %m"
,
mailbox
->
name
);
goto
fail
;
}
/* Record quota release */
r
=
mailbox_lock_quota
(
&
mailbox
->
quota
);
if
(
r
)
goto
fail
;
if
(
mailbox
->
quota
.
used
>=
quotadeleted
)
{
mailbox
->
quota
.
used
-=
quotadeleted
;
}
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"
,
quotadeleted
,
mailbox
->
quota
.
root
);
}
mailbox_unlock_quota
(
&
mailbox
->
quota
);
strcpy
(
fnamebuf
,
mailbox
->
path
);
fnametail
=
fnamebuf
+
strlen
(
fnamebuf
);
strcpy
(
fnametail
,
FNAME_INDEX
);
strcpy
(
fnamebufnew
,
fnamebuf
);
strcat
(
fnamebufnew
,
".NEW"
);
if
(
rename
(
fnamebufnew
,
fnamebuf
))
{
syslog
(
LOG_ERR
,
"IOERROR: renaming index file for %s: %m"
,
mailbox
->
name
);
goto
fail
;
}
strcpy
(
fnametail
,
FNAME_CACHE
);
strcpy
(
fnamebufnew
,
fnamebuf
);
strcat
(
fnamebufnew
,
".NEW"
);
if
(
rename
(
fnamebufnew
,
fnamebuf
))
{
syslog
(
LOG_CRIT
,
"CRITICAL IOERROR: renaming cache file for %s, need to reconstruct: %m"
,
mailbox
->
name
);
/* Fall through and delete message files anyway */
}
if
(
numdeleted
)
{
if
(
updatenotifier
)
updatenotifier
(
mailbox
);
#if 0
if (acappush_sock != -1) {
acapmbdata_t acapdata;
/* fill in structure */
strcpy(acapdata.name, mailbox->name);
acapdata.uidvalidity = mailbox->uidvalidity;
acapdata.exists = newexists;
acapdata.deleted = newdeleted;
acapdata.answered = newanswered;
acapdata.flagged = newflagged;
/* send */
if (sendto(acappush_sock, &acapdata, 20+strlen(mailbox->name), 0,
(struct sockaddr *) &acappush_remote,
acappush_remote_len) == -1) {
syslog(LOG_ERR, "Error sending to acappush: %m");
}
}
#endif
}
mailbox_unlock_pop
(
mailbox
);
mailbox_unlock_index
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
fclose
(
newindex
);
fclose
(
newcache
);
/* Delete message files */
*
fnametail
++
=
'/'
;
for
(
msgno
=
0
;
msgno
<
numdeleted
;
msgno
++
)
{
if
(
iscurrentdir
)
{
char
shortfnamebuf
[
MAILBOX_FNAME_LEN
];
mailbox_message_get_fname
(
mailbox
,
deleted
[
msgno
],
shortfnamebuf
);
unlink
(
shortfnamebuf
);
}
else
{
mailbox_message_get_fname
(
mailbox
,
deleted
[
msgno
],
fnametail
);
unlink
(
fnamebuf
);
}
}
free
(
buf
);
if
(
deleted
)
free
(
deleted
);
return
0
;
fail
:
free
(
buf
);
free
(
deleted
);
fclose
(
newindex
);
fclose
(
newcache
);
mailbox_unlock_pop
(
mailbox
);
mailbox_unlock_index
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
return
IMAP_IOERROR
;
}
/* find the mailbox 'name' 's quotaroot, and return it in 'start'.
'start' must be at least MAX_MAILBOX_PATH.
returns true if a quotaroot is found, 0 otherwise.
*/
int
mailbox_findquota
(
char
*
start
,
const
char
*
name
)
{
char
quota_path
[
MAX_MAILBOX_PATH
];
char
*
tail
;
struct
stat
sbuf
;
strcpy
(
start
,
name
);
lcase
(
start
);
mailbox_hash_quota
(
quota_path
,
start
);
while
(
stat
(
quota_path
,
&
sbuf
)
==
-1
)
{
tail
=
strrchr
(
start
,
'.'
);
if
(
!
tail
)
return
0
;
*
tail
=
'\0'
;
mailbox_hash_quota
(
quota_path
,
start
);
}
return
1
;
}
int
mailbox_create
(
const
char
*
name
,
char
*
path
,
const
char
*
acl
,
const
char
*
uniqueid
,
int
format
,
struct
mailbox
*
mailboxp
)
{
int
r
;
char
*
p
=
path
;
char
quota_root
[
MAX_MAILBOX_PATH
];
int
hasquota
;
char
fnamebuf
[
MAX_MAILBOX_PATH
];
struct
mailbox
mailbox
;
int
save_errno
;
int
n
;
const
char
*
lockfailaction
;
struct
stat
sbuf
;
while
((
p
=
strchr
(
p
+
1
,
'/'
)))
{
*
p
=
'\0'
;
if
(
mkdir
(
path
,
0755
)
==
-1
&&
errno
!=
EEXIST
)
{
save_errno
=
errno
;
if
(
stat
(
path
,
&
sbuf
)
==
-1
)
{
errno
=
save_errno
;
syslog
(
LOG_ERR
,
"IOERROR: creating directory %s: %m"
,
path
);
return
IMAP_IOERROR
;
}
}
*
p
=
'/'
;
}
if
(
mkdir
(
path
,
0755
)
==
-1
&&
errno
!=
EEXIST
)
{
save_errno
=
errno
;
if
(
stat
(
path
,
&
sbuf
)
==
-1
)
{
errno
=
save_errno
;
syslog
(
LOG_ERR
,
"IOERROR: creating directory %s: %m"
,
path
);
return
IMAP_IOERROR
;
}
}
zeromailbox
(
mailbox
);
mailbox
.
quota
.
fd
=
-1
;
hasquota
=
mailbox_findquota
(
quota_root
,
name
);
strcpy
(
fnamebuf
,
path
);
p
=
fnamebuf
+
strlen
(
fnamebuf
);
strcpy
(
p
,
FNAME_HEADER
);
mailbox
.
header_fd
=
open
(
fnamebuf
,
O_WRONLY
|
O_TRUNC
|
O_CREAT
,
0666
);
if
(
mailbox
.
header_fd
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: creating %s: %m"
,
fnamebuf
);
return
IMAP_IOERROR
;
}
/* Note that we are locking the mailbox here. Technically, this function
* can be called with a lock on the mailbox list. This would otherwise
* violate the locking semantics, but it is okay since the mailbox list
* changes have not been committed, and the mailbox we create here *can't*
* be opened by anyone else */
r
=
lock_reopen
(
mailbox
.
header_fd
,
fnamebuf
,
NULL
,
&
lockfailaction
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"IOERROR: %s header for new mailbox %s: %m"
,
lockfailaction
,
mailbox
.
name
);
mailbox_close
(
&
mailbox
);
return
IMAP_IOERROR
;
}
mailbox
.
header_lock_count
++
;
mailbox
.
name
=
xstrdup
(
name
);
mailbox
.
path
=
xstrdup
(
path
);
mailbox
.
acl
=
xstrdup
(
acl
);
strcpy
(
p
,
FNAME_INDEX
);
mailbox
.
index_fd
=
open
(
fnamebuf
,
O_WRONLY
|
O_TRUNC
|
O_CREAT
,
0666
);
if
(
mailbox
.
index_fd
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: creating %s: %m"
,
fnamebuf
);
mailbox_close
(
&
mailbox
);
return
IMAP_IOERROR
;
}
r
=
lock_reopen
(
mailbox
.
index_fd
,
fnamebuf
,
NULL
,
&
lockfailaction
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"IOERROR: %s index for new mailbox %s: %m"
,
lockfailaction
,
mailbox
.
name
);
mailbox_close
(
&
mailbox
);
return
IMAP_IOERROR
;
}
mailbox
.
index_lock_count
++
;
strcpy
(
p
,
FNAME_CACHE
);
mailbox
.
cache_fd
=
open
(
fnamebuf
,
O_WRONLY
|
O_TRUNC
|
O_CREAT
,
0666
);
if
(
mailbox
.
cache_fd
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: creating %s: %m"
,
fnamebuf
);
mailbox_close
(
&
mailbox
);
return
IMAP_IOERROR
;
}
if
(
hasquota
)
mailbox
.
quota
.
root
=
xstrdup
(
quota_root
);
mailbox
.
generation_no
=
0
;
mailbox
.
format
=
format
;
mailbox
.
minor_version
=
MAILBOX_MINOR_VERSION
;
mailbox
.
start_offset
=
INDEX_HEADER_SIZE
;
mailbox
.
record_size
=
INDEX_RECORD_SIZE
;
mailbox
.
exists
=
0
;
mailbox
.
last_appenddate
=
0
;
mailbox
.
last_uid
=
0
;
mailbox
.
quota_mailbox_used
=
0
;
mailbox
.
pop3_last_login
=
0
;
mailbox
.
uidvalidity
=
time
(
0
);
mailbox
.
deleted
=
0
;
mailbox
.
answered
=
0
;
mailbox
.
flagged
=
0
;
mailbox
.
pop3_new_uidl
=
1
;
if
(
!
uniqueid
)
{
mailbox
.
uniqueid
=
xmalloc
(
sizeof
(
char
)
*
32
);
mailbox_make_uniqueid
(
mailbox
.
name
,
mailbox
.
uidvalidity
,
mailbox
.
uniqueid
);
}
else
{
mailbox
.
uniqueid
=
xstrdup
(
uniqueid
);
}
r
=
mailbox_write_header
(
&
mailbox
);
if
(
!
r
)
r
=
mailbox_write_index_header
(
&
mailbox
);
if
(
!
r
)
{
n
=
retry_write
(
mailbox
.
cache_fd
,
(
char
*
)
&
mailbox
.
generation_no
,
4
);
if
(
n
!=
4
||
fsync
(
mailbox
.
cache_fd
))
{
syslog
(
LOG_ERR
,
"IOERROR: writing initial cache for %s: %m"
,
mailbox
.
name
);
r
=
IMAP_IOERROR
;
}
}
if
(
!
r
)
r
=
seen_create_mailbox
(
&
mailbox
);
if
(
mailboxp
)
{
*
mailboxp
=
mailbox
;
}
else
{
mailbox_close
(
&
mailbox
);
}
return
r
;
}
/*
* Delete and close the mailbox 'mailbox'. Closes 'mailbox' whether
* or not the deletion was successful. Requires a locked mailbox.
*/
int
mailbox_delete
(
struct
mailbox
*
mailbox
,
int
delete_quota_root
)
{
int
r
,
rquota
=
0
;
DIR
*
dirp
;
struct
dirent
*
f
;
char
buf
[
MAX_MAILBOX_PATH
];
char
*
tail
;
/* Ensure that we are locked */
if
(
!
mailbox
->
header_lock_count
)
return
IMAP_INTERNAL
;
rquota
=
mailbox_lock_quota
(
&
mailbox
->
quota
);
seen_delete_mailbox
(
mailbox
);
if
(
delete_quota_root
&&
!
rquota
)
{
mailbox_delete_quota
(
&
mailbox
->
quota
);
}
else
if
(
!
rquota
)
{
/* Free any quota being used by this mailbox */
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 %lu bytes in quota %s"
,
mailbox
->
quota_mailbox_used
,
mailbox
->
quota
.
root
);
}
mailbox_unlock_quota
(
&
mailbox
->
quota
);
}
/* remove all files in directory */
strcpy
(
buf
,
mailbox
->
path
);
tail
=
buf
+
strlen
(
buf
);
*
tail
++
=
'/'
;
dirp
=
opendir
(
mailbox
->
path
);
if
(
dirp
)
{
while
((
f
=
readdir
(
dirp
))
!=
NULL
)
{
if
(
f
->
d_name
[
0
]
==
'.'
&&
(
f
->
d_name
[
1
]
==
'\0'
||
(
f
->
d_name
[
1
]
==
'.'
&&
f
->
d_name
[
2
]
==
'\0'
)))
{
/* readdir() can return "." or "..", and I got a bug report
that SCO might blow the file system to smithereens if we
unlink(".."). Let's not do that. */
continue
;
}
strcpy
(
tail
,
f
->
d_name
);
(
void
)
unlink
(
buf
);
}
closedir
(
dirp
);
}
/* Remove empty directories, going up path */
tail
--
;
do
{
*
tail
=
'\0'
;
}
while
(
rmdir
(
buf
)
==
0
&&
(
tail
=
strrchr
(
buf
,
'/'
)));
mailbox_close
(
mailbox
);
return
0
;
}
/*
* Expunge decision proc used by mailbox_rename() to expunge all messages
* in INBOX
*/
static
int
expungeall
(
struct
mailbox
*
mailbox
__attribute__
((
unused
)),
void
*
rock
__attribute__
((
unused
)),
char
*
indexbuf
__attribute__
((
unused
)))
{
return
1
;
}
/* if 'isinbox' is set, we perform the funky RENAME INBOX INBOX.old
semantics, regardless of whether or not the name of the mailbox is
'user.foo'.*/
/* requires a LOCKED oldmailbox pointer */
int
mailbox_rename_copy
(
struct
mailbox
*
oldmailbox
,
const
char
*
newname
,
char
*
newpath
,
bit32
*
olduidvalidityp
,
bit32
*
newuidvalidityp
,
struct
mailbox
*
mailboxp
)
{
int
r
;
struct
mailbox
newmailbox
;
unsigned
int
flag
,
msgno
;
struct
index_record
record
;
char
oldfname
[
MAX_MAILBOX_PATH
],
newfname
[
MAX_MAILBOX_PATH
];
char
*
oldfnametail
,
*
newfnametail
;
assert
(
oldmailbox
->
header_lock_count
>
0
&&
oldmailbox
->
index_lock_count
>
0
);
/* Create new mailbox */
r
=
mailbox_create
(
newname
,
newpath
,
oldmailbox
->
acl
,
oldmailbox
->
uniqueid
,
oldmailbox
->
format
,
&
newmailbox
);
if
(
r
)
return
r
;
if
(
mailboxp
)
mailboxp
=
&
newmailbox
;
if
(
strcmp
(
oldmailbox
->
name
,
newname
)
==
0
)
{
/* Just moving mailboxes between partitions */
newmailbox
.
uidvalidity
=
oldmailbox
->
uidvalidity
;
}
if
(
olduidvalidityp
)
*
olduidvalidityp
=
oldmailbox
->
uidvalidity
;
if
(
newuidvalidityp
)
*
newuidvalidityp
=
newmailbox
.
uidvalidity
;
/* Copy flag names */
for
(
flag
=
0
;
flag
<
MAX_USER_FLAGS
;
flag
++
)
{
if
(
oldmailbox
->
flagname
[
flag
])
{
newmailbox
.
flagname
[
flag
]
=
xstrdup
(
oldmailbox
->
flagname
[
flag
]);
}
}
r
=
mailbox_write_header
(
&
newmailbox
);
if
(
r
)
{
mailbox_close
(
&
newmailbox
);
return
r
;
}
/* Check quota if necessary */
if
(
newmailbox
.
quota
.
root
)
{
r
=
mailbox_lock_quota
(
&
newmailbox
.
quota
);
if
(
!
oldmailbox
->
quota
.
root
||
strcmp
(
oldmailbox
->
quota
.
root
,
newmailbox
.
quota
.
root
)
!=
0
)
{
if
(
!
r
&&
newmailbox
.
quota
.
limit
>=
0
&&
newmailbox
.
quota
.
used
+
oldmailbox
->
quota_mailbox_used
>
((
unsigned
)
newmailbox
.
quota
.
limit
*
QUOTA_UNITS
))
{
r
=
IMAP_QUOTA_EXCEEDED
;
}
}
if
(
r
)
{
mailbox_close
(
&
newmailbox
);
return
r
;
}
}
strcpy
(
oldfname
,
oldmailbox
->
path
);
oldfnametail
=
oldfname
+
strlen
(
oldfname
);
strcpy
(
newfname
,
newmailbox
.
path
);
newfnametail
=
newfname
+
strlen
(
newfname
);
/* Copy over index/cache files */
strcpy
(
oldfnametail
,
FNAME_INDEX
);
strcpy
(
newfnametail
,
FNAME_INDEX
);
unlink
(
newfname
);
/* Make link() possible */
r
=
mailbox_copyfile
(
oldfname
,
newfname
);
strcpy
(
oldfnametail
,
FNAME_CACHE
);
strcpy
(
newfnametail
,
FNAME_CACHE
);
unlink
(
newfname
);
if
(
!
r
)
r
=
mailbox_copyfile
(
oldfname
,
newfname
);
if
(
r
)
{
mailbox_close
(
&
newmailbox
);
return
r
;
}
/* Re-open index file and store new uidvalidity */
close
(
newmailbox
.
index_fd
);
newmailbox
.
index_fd
=
dup
(
oldmailbox
->
index_fd
);
(
void
)
mailbox_read_index_header
(
&
newmailbox
);
newmailbox
.
generation_no
=
oldmailbox
->
generation_no
;
(
void
)
mailbox_write_index_header
(
&
newmailbox
);
/* Copy over message files */
oldfnametail
++
;
newfnametail
++
;
for
(
msgno
=
1
;
msgno
<=
oldmailbox
->
exists
;
msgno
++
)
{
r
=
mailbox_read_index_record
(
oldmailbox
,
msgno
,
&
record
);
if
(
r
)
break
;
mailbox_message_get_fname
(
oldmailbox
,
record
.
uid
,
oldfnametail
);
strcpy
(
newfnametail
,
oldfnametail
);
r
=
mailbox_copyfile
(
oldfname
,
newfname
);
if
(
r
)
break
;
}
if
(
!
r
)
r
=
seen_copy
(
oldmailbox
,
&
newmailbox
);
/* Record new quota usage */
if
(
!
r
&&
newmailbox
.
quota
.
root
)
{
newmailbox
.
quota
.
used
+=
oldmailbox
->
quota_mailbox_used
;
r
=
mailbox_write_quota
(
&
newmailbox
.
quota
);
mailbox_unlock_quota
(
&
newmailbox
.
quota
);
}
if
(
r
)
{
/* failure and back out */
for
(
msgno
=
1
;
msgno
<=
oldmailbox
->
exists
;
msgno
++
)
{
if
(
mailbox_read_index_record
(
oldmailbox
,
msgno
,
&
record
))
continue
;
mailbox_message_get_fname
(
oldmailbox
,
record
.
uid
,
newfnametail
);
(
void
)
unlink
(
newfname
);
}
}
return
r
;
}
/* Requires a locked mailbox */
int
mailbox_rename_finish
(
struct
mailbox
*
oldmailbox
,
struct
mailbox
*
newmailbox
,
int
isinbox
)
{
int
r
,
r2
;
if
(
isinbox
)
{
/* Expunge old mailbox */
r
=
mailbox_expunge
(
oldmailbox
,
0
,
expungeall
,
(
char
*
)
0
);
}
else
{
r
=
mailbox_delete
(
oldmailbox
,
0
);
}
if
(
r
&&
newmailbox
->
quota
.
root
)
{
r2
=
mailbox_lock_quota
(
&
(
newmailbox
->
quota
));
newmailbox
->
quota
.
used
+=
newmailbox
->
quota_mailbox_used
;
if
(
!
r2
)
{
r2
=
mailbox_write_quota
(
&
(
newmailbox
->
quota
));
mailbox_unlock_quota
(
&
(
newmailbox
->
quota
));
}
if
(
r2
)
{
syslog
(
LOG_ERR
,
"LOSTQUOTA: unable to record use of %lu bytes in quota %s"
,
newmailbox
->
quota_mailbox_used
,
newmailbox
->
quota
.
root
);
}
}
if
(
r
)
{
syslog
(
LOG_CRIT
,
"Rename Failure during mailbox_rename_finish (%s->%s), "
\
"potential leaked space (%s)"
,
oldmailbox
->
name
,
newmailbox
->
name
,
error_message
(
r
));
}
/* Ugh, we'd have to back all the way out at this point. */
/* xxx we leak space for now if something failed. */
return
r
;
}
/*
* Synchronize 'new' mailbox to 'old' mailbox.
*/
int
mailbox_sync
(
const
char
*
oldname
,
const
char
*
oldpath
,
const
char
*
oldacl
,
const
char
*
newname
,
char
*
newpath
,
int
docreate
,
bit32
*
olduidvalidityp
,
bit32
*
newuidvalidityp
,
struct
mailbox
*
mailboxp
)
{
int
r
,
r2
;
struct
mailbox
oldmailbox
,
newmailbox
;
unsigned
int
flag
,
oldmsgno
,
newmsgno
;
struct
index_record
oldrecord
,
newrecord
;
char
oldfname
[
MAX_MAILBOX_PATH
],
newfname
[
MAX_MAILBOX_PATH
];
char
*
oldfnametail
,
*
newfnametail
;
/* Open old mailbox and lock */
mailbox_open_header_path
(
oldname
,
oldpath
,
oldacl
,
0
,
&
oldmailbox
,
0
);
if
(
oldmailbox
.
format
==
MAILBOX_FORMAT_NETNEWS
)
{
mailbox_close
(
&
oldmailbox
);
return
IMAP_MAILBOX_NOTSUPPORTED
;
}
r
=
mailbox_lock_header
(
&
oldmailbox
);
if
(
!
r
)
r
=
mailbox_open_index
(
&
oldmailbox
);
if
(
!
r
)
r
=
mailbox_lock_index
(
&
oldmailbox
);
if
(
r
)
{
mailbox_close
(
&
oldmailbox
);
return
r
;
}
if
(
docreate
)
{
/* Create new mailbox */
r
=
mailbox_create
(
newname
,
newpath
,
oldmailbox
.
acl
,
oldmailbox
.
uniqueid
,
oldmailbox
.
format
,
&
newmailbox
);
}
else
{
/* Open new mailbox and lock */
r
=
mailbox_open_header_path
(
newname
,
newpath
,
oldacl
,
0
,
&
newmailbox
,
0
);
r
=
mailbox_lock_header
(
&
newmailbox
);
if
(
!
r
)
r
=
mailbox_open_index
(
&
newmailbox
);
if
(
!
r
)
r
=
mailbox_lock_index
(
&
newmailbox
);
if
(
r
)
{
mailbox_close
(
&
newmailbox
);
}
}
if
(
r
)
{
mailbox_close
(
&
oldmailbox
);
return
r
;
}
newmailbox
.
uidvalidity
=
oldmailbox
.
uidvalidity
;
if
(
olduidvalidityp
)
*
olduidvalidityp
=
oldmailbox
.
uidvalidity
;
if
(
newuidvalidityp
)
*
newuidvalidityp
=
newmailbox
.
uidvalidity
;
/* Copy flag names */
for
(
flag
=
0
;
flag
<
MAX_USER_FLAGS
;
flag
++
)
{
if
(
oldmailbox
.
flagname
[
flag
])
{
newmailbox
.
flagname
[
flag
]
=
xstrdup
(
oldmailbox
.
flagname
[
flag
]);
}
}
r
=
mailbox_write_header
(
&
newmailbox
);
if
(
r
)
{
mailbox_close
(
&
newmailbox
);
mailbox_close
(
&
oldmailbox
);
return
r
;
}
/* Check quota if necessary */
if
(
newmailbox
.
quota
.
root
)
{
r
=
mailbox_lock_quota
(
&
newmailbox
.
quota
);
if
(
!
oldmailbox
.
quota
.
root
||
strcmp
(
oldmailbox
.
quota
.
root
,
newmailbox
.
quota
.
root
)
!=
0
)
{
if
(
!
r
&&
newmailbox
.
quota
.
limit
>=
0
&&
newmailbox
.
quota
.
used
+
oldmailbox
.
quota_mailbox_used
>
((
unsigned
)
newmailbox
.
quota
.
limit
*
QUOTA_UNITS
))
{
r
=
IMAP_QUOTA_EXCEEDED
;
}
}
if
(
r
)
{
mailbox_close
(
&
newmailbox
);
mailbox_close
(
&
oldmailbox
);
return
r
;
}
}
strcpy
(
oldfname
,
oldmailbox
.
path
);
strcat
(
oldfname
,
"/"
);
oldfnametail
=
oldfname
+
strlen
(
oldfname
);
strcpy
(
newfname
,
newmailbox
.
path
);
strcat
(
newfname
,
"/"
);
newfnametail
=
newfname
+
strlen
(
newfname
);
/*
* Copy over new message files and delete expunged ones.
*
* We use the fact that UIDs are monotonically increasing to our
* advantage; we compare the UIDs from each mailbox in order, and:
*
* - if UID in "slave" mailbox < UID in "master" mailbox,
* then the message has been deleted from "master" since last sync,
* so delete it from "slave" and move on to next "slave" UID
* - if UID in "slave" mailbox == UID in "master" mailbox,
* then message is still current and we already have a copy,
* so move on to next UID in each mailbox
* - if UID in "master" mailbox > last UID in "slave" mailbox,
* then this is a new arrival in "master" since last sync,
* so copy it to "slave" and move on to next "master" UID
*/
newmsgno
=
1
;
for
(
oldmsgno
=
1
;
oldmsgno
<=
oldmailbox
.
exists
;
oldmsgno
++
)
{
r
=
mailbox_read_index_record
(
&
oldmailbox
,
oldmsgno
,
&
oldrecord
);
if
(
r
)
break
;
if
(
newmsgno
<=
newmailbox
.
exists
)
{
do
{
r
=
mailbox_read_index_record
(
&
newmailbox
,
newmsgno
,
&
newrecord
);
if
(
r
)
goto
fail
;
newmsgno
++
;
if
(
newrecord
.
uid
<
oldrecord
.
uid
)
{
/* message expunged since last sync - delete message file */
mailbox_message_get_fname
(
&
newmailbox
,
newrecord
.
uid
,
newfnametail
);
unlink
(
newfname
);
}
}
while
((
newrecord
.
uid
<
oldrecord
.
uid
)
&&
(
newmsgno
<=
newmailbox
.
exists
));
}
/* we check 'exists' instead of last UID in case of empty mailbox */
if
(
newmsgno
>
newmailbox
.
exists
)
{
/* message arrived since last sync - copy message file */
mailbox_message_get_fname
(
&
oldmailbox
,
oldrecord
.
uid
,
oldfnametail
);
strcpy
(
newfnametail
,
oldfnametail
);
r
=
mailbox_copyfile
(
oldfname
,
newfname
);
if
(
r
)
break
;
}
}
if
(
!
r
)
r
=
seen_copy
(
&
oldmailbox
,
&
newmailbox
);
if
(
!
r
)
{
/* Copy over index/cache files */
oldfnametail
--
;
newfnametail
--
;
strcpy
(
oldfnametail
,
FNAME_INDEX
);
strcpy
(
newfnametail
,
FNAME_INDEX
);
unlink
(
newfname
);
/* Make link() possible */
r
=
mailbox_copyfile
(
oldfname
,
newfname
);
strcpy
(
oldfnametail
,
FNAME_CACHE
);
strcpy
(
newfnametail
,
FNAME_CACHE
);
unlink
(
newfname
);
if
(
!
r
)
r
=
mailbox_copyfile
(
oldfname
,
newfname
);
if
(
r
)
{
mailbox_close
(
&
newmailbox
);
mailbox_close
(
&
oldmailbox
);
return
r
;
}
/* Re-open index file and store new uidvalidity */
close
(
newmailbox
.
index_fd
);
newmailbox
.
index_fd
=
dup
(
oldmailbox
.
index_fd
);
(
void
)
mailbox_read_index_header
(
&
newmailbox
);
newmailbox
.
generation_no
=
oldmailbox
.
generation_no
;
(
void
)
mailbox_write_index_header
(
&
newmailbox
);
}
/* Record new quota usage */
if
(
!
r
&&
newmailbox
.
quota
.
root
)
{
newmailbox
.
quota
.
used
+=
oldmailbox
.
quota_mailbox_used
;
r
=
mailbox_write_quota
(
&
newmailbox
.
quota
);
mailbox_unlock_quota
(
&
newmailbox
.
quota
);
}
if
(
r
)
goto
fail
;
if
(
r
&&
newmailbox
.
quota
.
root
)
{
r2
=
mailbox_lock_quota
(
&
newmailbox
.
quota
);
newmailbox
.
quota
.
used
+=
newmailbox
.
quota_mailbox_used
;
if
(
!
r2
)
{
r2
=
mailbox_write_quota
(
&
newmailbox
.
quota
);
mailbox_unlock_quota
(
&
newmailbox
.
quota
);
}
if
(
r2
)
{
syslog
(
LOG_ERR
,
"LOSTQUOTA: unable to record use of %lu bytes in quota %s"
,
newmailbox
.
quota_mailbox_used
,
newmailbox
.
quota
.
root
);
}
}
if
(
r
)
goto
fail
;
if
(
mailboxp
)
{
*
mailboxp
=
newmailbox
;
}
else
{
mailbox_close
(
&
newmailbox
);
}
return
0
;
fail
:
#if 0
for (msgno = 1; msgno <= oldmailbox.exists; msgno++) {
if (mailbox_read_index_record(&oldmailbox, msgno, &record)) continue;
mailbox_message_get_fname(&oldmailbox, record.uid, newfnametail);
(void) unlink(newfname);
}
#endif
mailbox_close
(
&
newmailbox
);
mailbox_close
(
&
oldmailbox
);
return
r
;
}
/*
* Copy (or link) the file 'from' to the file 'to'
*/
int
mailbox_copyfile
(
from
,
to
)
const
char
*
from
;
const
char
*
to
;
{
int
srcfd
,
destfd
;
struct
stat
sbuf
;
const
char
*
src_base
=
0
;
unsigned
long
src_size
=
0
;
int
n
;
if
(
link
(
from
,
to
)
==
0
)
return
0
;
if
(
errno
==
EEXIST
)
{
if
(
unlink
(
to
)
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: unlinking to recreate %s: %m"
,
to
);
return
IMAP_IOERROR
;
}
if
(
link
(
from
,
to
)
==
0
)
return
0
;
}
destfd
=
open
(
to
,
O_RDWR
|
O_TRUNC
|
O_CREAT
,
0666
);
if
(
destfd
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: creating %s: %m"
,
to
);
return
IMAP_IOERROR
;
}
srcfd
=
open
(
from
,
O_RDONLY
,
0666
);
if
(
srcfd
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: opening %s: %m"
,
from
);
close
(
destfd
);
return
IMAP_IOERROR
;
}
if
(
fstat
(
srcfd
,
&
sbuf
)
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: fstat on %s: %m"
,
from
);
close
(
srcfd
);
close
(
destfd
);
return
IMAP_IOERROR
;
}
map_refresh
(
srcfd
,
1
,
&
src_base
,
&
src_size
,
sbuf
.
st_size
,
from
,
0
);
n
=
retry_write
(
destfd
,
src_base
,
src_size
);
if
(
n
==
-1
||
fsync
(
destfd
))
{
map_free
(
&
src_base
,
&
src_size
);
close
(
srcfd
);
close
(
destfd
);
syslog
(
LOG_ERR
,
"IOERROR: writing %s: %m"
,
to
);
return
IMAP_IOERROR
;
}
map_free
(
&
src_base
,
&
src_size
);
close
(
srcfd
);
close
(
destfd
);
return
0
;
}
void
mailbox_hash_mbox
(
char
*
buf
,
const
char
*
root
,
const
char
*
name
)
{
const
char
*
idx
;
char
c
,
*
p
;
if
(
config_hashimapspool
)
{
idx
=
strchr
(
name
,
'.'
);
if
(
idx
==
NULL
)
{
idx
=
name
;
}
else
{
idx
++
;
}
c
=
(
char
)
dir_hash_c
(
idx
);
sprintf
(
buf
,
"%s/%c/%s"
,
root
,
c
,
name
);
}
else
{
/* standard mailbox placement */
sprintf
(
buf
,
"%s/%s"
,
root
,
name
);
}
/* change all '.'s to '/' */
for
(
p
=
buf
+
strlen
(
root
)
+
1
;
*
p
;
p
++
)
{
if
(
*
p
==
'.'
)
*
p
=
'/'
;
}
}
/* simple hash so it's easy to find these things in the filesystem;
our human time is worth more than efficiency */
void
mailbox_hash_quota
(
char
*
buf
,
const
char
*
qr
)
{
const
char
*
idx
;
char
c
;
idx
=
strchr
(
qr
,
'.'
);
/* skip past user. */
if
(
idx
==
NULL
)
{
idx
=
qr
;
}
else
{
idx
++
;
}
c
=
(
char
)
dir_hash_c
(
idx
);
sprintf
(
buf
,
"%s%s%c/%s"
,
config_dir
,
FNAME_QUOTADIR
,
c
,
qr
);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, Apr 4, 1:45 AM (1 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18822001
Default Alt Text
mailbox.c (70 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline