Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117888243
append.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
25 KB
Referenced Files
None
Subscribers
None
append.c
View Options
/* append.c -- Routines for appending messages to a mailbox
* $Id: append.c,v 1.68 2000/04/11 03:35:02 leg Exp $
*
# Copyright 1998 Carnegie Mellon University
#
# No warranties, either expressed or implied, are made regarding the
# operation, use, or results of the software.
#
# Permission to use, copy, modify and distribute this software and its
# documentation is hereby granted for non-commercial purposes only
# provided that this copyright notice appears in all copies and in
# supporting documentation.
#
# Permission is also granted to Internet Service Providers and others
# entities to use the software for internal purposes.
#
# The distribution, modification or sale of a product which uses or is
# based on the software, in whole or in part, for commercial purposes or
# benefits requires specific, additional permission from:
#
# 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
*
*/
#include
<config.h>
#ifdef HAVE_UNISTD_H
#include
<unistd.h>
#endif
#include
<stdio.h>
#include
<ctype.h>
#include
<string.h>
#include
<sys/types.h>
#include
<syslog.h>
#include
<sys/stat.h>
#include
"acl.h"
#include
"assert.h"
#include
"imap_err.h"
#include
"mailbox.h"
#include
"message.h"
#include
"append.h"
#include
"imapconf.h"
#include
"prot.h"
#include
"xmalloc.h"
#include
"mboxlist.h"
#include
"acapmbox.h"
#include
"seen.h"
#include
"retry.h"
struct
stagemsg
{
unsigned
long
size
;
time_t
internaldate
;
char
fname
[
1024
];
int
num_parts
;
char
parts
[
1
][
MAX_MAILBOX_PATH
];
};
static
int
append_addseen
(
struct
mailbox
*
mailbox
,
const
char
*
userid
,
unsigned
start
,
unsigned
end
);
static
struct
index_record
zero_index
;
/*
* Open a mailbox for appending
*
* Arguments:
* name - name of mailbox directory
* format - mailbox must be of this format
* aclcheck - user must have these rights on mailbox ACL
* quotacheck - mailbox must have this much quota left
* (-1 means don't care about quota)
*
* On success, the struct pointed to by 'as' is set up.
*
*/
int
append_setup
(
struct
appendstate
*
as
,
const
char
*
name
,
int
format
,
struct
auth_state
*
auth_state
,
long
aclcheck
,
long
quotacheck
)
{
int
r
;
r
=
mailbox_open_header
(
name
,
auth_state
,
&
as
->
m
);
if
(
r
)
return
r
;
if
((
as
->
m
.
myrights
&
aclcheck
)
!=
aclcheck
)
{
r
=
(
as
->
m
.
myrights
&
ACL_LOOKUP
)
?
IMAP_PERMISSION_DENIED
:
IMAP_MAILBOX_NONEXISTENT
;
mailbox_close
(
&
as
->
m
);
return
r
;
}
r
=
mailbox_lock_header
(
&
as
->
m
);
if
(
r
)
{
mailbox_close
(
&
as
->
m
);
return
r
;
}
r
=
mailbox_open_index
(
&
as
->
m
);
if
(
r
)
{
mailbox_close
(
&
as
->
m
);
return
r
;
}
if
(
as
->
m
.
format
!=
format
)
{
mailbox_close
(
&
as
->
m
);
return
IMAP_MAILBOX_NOTSUPPORTED
;
}
r
=
mailbox_lock_index
(
&
as
->
m
);
if
(
r
)
{
mailbox_close
(
&
as
->
m
);
return
r
;
}
r
=
mailbox_lock_quota
(
&
as
->
m
.
quota
);
if
(
r
)
{
mailbox_close
(
&
as
->
m
);
return
r
;
}
if
(
as
->
m
.
quota
.
limit
>=
0
&&
quotacheck
>=
0
&&
as
->
m
.
quota
.
used
+
quotacheck
>
as
->
m
.
quota
.
limit
*
QUOTA_UNITS
)
{
mailbox_close
(
&
as
->
m
);
return
IMAP_QUOTA_EXCEEDED
;
}
/* zero out metadata */
as
->
orig_cache_len
=
as
->
m
.
cache_len
;
as
->
nummsg
=
as
->
numanswered
=
as
->
numdeleted
=
as
->
numflagged
=
0
;
as
->
quota_used
=
0
;
as
->
writeheader
=
0
;
as
->
s
=
APPEND_READY
;
return
0
;
}
/* may return non-zero, indicating that the entire append has failed
and the mailbox is probably in an inconsistent state. */
int
append_commit
(
struct
appendstate
*
as
,
int
*
uidvalidity
,
int
*
start
,
int
*
num
)
{
int
r
=
0
;
if
(
as
->
s
==
APPEND_DONE
)
return
0
;
as
->
s
=
APPEND_DONE
;
if
(
start
)
*
start
=
as
->
m
.
last_uid
+
1
;
if
(
num
)
*
num
=
as
->
nummsg
;
if
(
uidvalidity
)
*
uidvalidity
=
as
->
m
.
uidvalidity
;
/* write out the header if we created new user flags */
if
(
as
->
writeheader
&&
(
r
=
mailbox_write_header
(
&
as
->
m
)))
{
append_abort
(
as
);
return
r
;
}
/* flush the new index records */
if
(
fsync
(
as
->
m
.
index_fd
))
{
syslog
(
LOG_ERR
,
"IOERROR: writing index records for %s: %m"
,
as
->
m
.
name
);
append_abort
(
as
);
return
IMAP_IOERROR
;
}
/* Flush out the cache file data */
if
(
fsync
(
as
->
m
.
cache_fd
))
{
syslog
(
LOG_ERR
,
"IOERROR: writing cache file for %s: %m"
,
as
->
m
.
name
);
append_abort
(
as
);
return
IMAP_IOERROR
;
}
/* Calculate new index header information */
as
->
m
.
exists
+=
as
->
nummsg
;
as
->
m
.
last_uid
+=
as
->
nummsg
;
as
->
m
.
answered
+=
as
->
numanswered
;
as
->
m
.
deleted
+=
as
->
numdeleted
;
as
->
m
.
flagged
+=
as
->
numflagged
;
as
->
m
.
last_appenddate
=
time
(
0
);
as
->
m
.
quota_mailbox_used
+=
as
->
quota_used
;
if
(
as
->
m
.
minor_version
>
MAILBOX_MINOR_VERSION
)
{
as
->
m
.
minor_version
=
MAILBOX_MINOR_VERSION
;
}
/* Write out index header & synchronize to disk.
this writes to acappush too. */
r
=
mailbox_write_index_header
(
&
as
->
m
);
if
(
r
)
{
append_abort
(
as
);
return
r
;
}
/* Write out quota file */
as
->
m
.
quota
.
used
+=
as
->
quota_used
;
r
=
mailbox_write_quota
(
&
as
->
m
.
quota
);
if
(
r
)
{
syslog
(
LOG_ERR
,
"LOSTQUOTA: unable to record use of %u bytes in quota file %s"
,
as
->
quota_used
,
as
->
m
.
quota
.
root
);
}
/* set seen state */
/* erg, non-trivial */
mailbox_unlock_quota
(
&
as
->
m
.
quota
);
mailbox_unlock_index
(
&
as
->
m
);
mailbox_unlock_header
(
&
as
->
m
);
mailbox_close
(
&
as
->
m
);
return
0
;
}
/* may return non-zero, indicating an internal error of some sort. */
int
append_abort
(
struct
appendstate
*
as
)
{
int
r
=
0
;
int
uid
;
if
(
as
->
s
==
APPEND_DONE
)
return
0
;
as
->
s
=
APPEND_DONE
;
/* unlink message files that were created */
for
(
uid
=
as
->
m
.
last_uid
+
1
;
uid
<=
as
->
m
.
last_uid
+
as
->
nummsg
;
uid
++
)
{
char
fname
[
MAX_MAILBOX_PATH
];
/* Create message file */
strcpy
(
fname
,
as
->
m
.
path
);
strcat
(
fname
,
"/"
);
mailbox_message_get_fname
(
&
as
->
m
,
uid
,
fname
+
strlen
(
fname
));
if
(
unlink
(
fname
)
<
0
)
{
/* hmmm, never got appended? */
/* r = IMAP_IOERROR; */
}
}
/* truncate the cache */
ftruncate
(
as
->
m
.
cache_fd
,
as
->
orig_cache_len
);
/* unlock mailbox */
mailbox_unlock_quota
(
&
as
->
m
.
quota
);
mailbox_unlock_index
(
&
as
->
m
);
mailbox_unlock_header
(
&
as
->
m
);
mailbox_close
(
&
as
->
m
);
return
r
;
}
/*
* staging, to allow for single-instance store. the complication here
* is multiple partitions.
*/
int
append_fromstage
(
struct
appendstate
*
as
,
struct
protstream
*
messagefile
,
unsigned
long
size
,
time_t
internaldate
,
const
char
**
flag
,
int
nflags
,
const
char
*
userid
,
struct
stagemsg
**
stagep
)
{
struct
mailbox
*
mailbox
=
&
as
->
m
;
struct
index_record
message_index
=
zero_index
;
char
fname
[
MAX_MAILBOX_PATH
];
FILE
*
destfile
;
int
i
,
r
;
int
setseen
=
0
;
int
userflag
,
emptyflag
;
/* for staging */
struct
stagemsg
*
stage
;
char
stagefile
[
1024
];
FILE
*
f
;
int
sp
;
assert
(
stagep
!=
NULL
);
assert
(
mailbox
->
format
==
MAILBOX_FORMAT_NORMAL
);
assert
(
size
!=
0
);
if
(
!*
stagep
)
{
/* create a new stage */
stage
=
xmalloc
(
sizeof
(
struct
stagemsg
)
+
5
*
MAX_MAILBOX_PATH
*
sizeof
(
char
));
stage
->
size
=
size
;
stage
->
internaldate
=
internaldate
;
sprintf
(
stage
->
fname
,
"%d-%d"
,(
int
)
getpid
(),
(
int
)
internaldate
);
stage
->
num_parts
=
5
;
/* room for 5 paths */
stage
->
parts
[
0
][
0
]
=
'\0'
;
}
else
{
stage
=
*
stagep
;
/* reuse existing stage */
}
mboxlist_findstage
(
mailbox
->
name
,
stagefile
);
strcat
(
stagefile
,
stage
->
fname
);
sp
=
0
;
while
(
stage
->
parts
[
sp
][
0
]
!=
'\0'
)
{
if
(
!
strcmp
(
stagefile
,
stage
->
parts
[
sp
]))
break
;
sp
++
;
}
if
(
stage
->
parts
[
sp
][
0
]
==
'\0'
)
{
/* ok, create this file and add put it into stage->parts[sp] */
f
=
fopen
(
stagefile
,
"w+"
);
if
(
!
f
)
{
char
stagedir
[
1024
];
mboxlist_findstage
(
mailbox
->
name
,
stagedir
);
if
(
mkdir
(
stagedir
,
0755
)
!=
0
)
{
syslog
(
LOG_ERR
,
"couldn't create stage directory: %s: %m"
,
stagedir
);
}
else
{
syslog
(
LOG_NOTICE
,
"created stage directory %s"
,
stagedir
);
f
=
fopen
(
stagefile
,
"w+"
);
}
}
if
(
!
f
)
{
syslog
(
LOG_ERR
,
"IOERROR: creating message file %s: %m"
,
stagefile
);
return
IMAP_IOERROR
;
}
r
=
message_copy_strict
(
messagefile
,
f
,
size
);
fclose
(
f
);
if
(
r
)
{
unlink
(
stagefile
);
return
r
;
}
if
(
sp
==
stage
->
num_parts
)
{
/* need more room */
stage
->
num_parts
+=
5
;
stage
=
xrealloc
(
stage
,
sizeof
(
struct
stagemsg
)
+
stage
->
num_parts
*
MAX_MAILBOX_PATH
*
sizeof
(
char
));
}
strcpy
(
stage
->
parts
[
sp
],
stagefile
);
stage
->
parts
[
sp
+
1
][
0
]
=
'\0'
;
}
/* stage->parts[sp] contains the message and is on the same partition
as the mailbox we're looking at */
/* Setup */
message_index
.
uid
=
mailbox
->
last_uid
+
as
->
nummsg
+
1
;
message_index
.
last_updated
=
time
(
0
);
message_index
.
internaldate
=
internaldate
;
lseek
(
mailbox
->
cache_fd
,
0L
,
SEEK_END
);
/* Create message file */
as
->
nummsg
++
;
strcpy
(
fname
,
mailbox
->
path
);
strcat
(
fname
,
"/"
);
mailbox_message_get_fname
(
mailbox
,
message_index
.
uid
,
fname
+
strlen
(
fname
));
r
=
mailbox_copyfile
(
stage
->
parts
[
sp
],
fname
);
destfile
=
fopen
(
fname
,
"r"
);
if
(
!
r
&&
destfile
)
{
/* ok, we've successfully created the file */
r
=
message_parse_file
(
destfile
,
mailbox
,
&
message_index
);
}
if
(
destfile
)
fclose
(
destfile
);
if
(
r
)
{
append_abort
(
as
);
return
r
;
}
/* Handle flags the user wants to set in the message */
for
(
i
=
0
;
i
<
nflags
;
i
++
)
{
if
(
!
strcmp
(
flag
[
i
],
"
\\
seen"
))
setseen
++
;
else
if
(
!
strcmp
(
flag
[
i
],
"
\\
deleted"
))
{
if
(
mailbox
->
myrights
&
ACL_DELETE
)
{
message_index
.
system_flags
|=
FLAG_DELETED
;
as
->
numdeleted
++
;
}
}
else
if
(
!
strcmp
(
flag
[
i
],
"
\\
draft"
))
{
if
(
mailbox
->
myrights
&
ACL_WRITE
)
{
message_index
.
system_flags
|=
FLAG_DRAFT
;
}
}
else
if
(
!
strcmp
(
flag
[
i
],
"
\\
flagged"
))
{
if
(
mailbox
->
myrights
&
ACL_WRITE
)
{
message_index
.
system_flags
|=
FLAG_FLAGGED
;
as
->
numflagged
++
;
}
}
else
if
(
!
strcmp
(
flag
[
i
],
"
\\
answered"
))
{
if
(
mailbox
->
myrights
&
ACL_WRITE
)
{
message_index
.
system_flags
|=
FLAG_ANSWERED
;
as
->
numanswered
++
;
}
}
else
if
(
mailbox
->
myrights
&
ACL_WRITE
)
{
/* User flag */
emptyflag
=
-1
;
for
(
userflag
=
0
;
userflag
<
MAX_USER_FLAGS
;
userflag
++
)
{
if
(
mailbox
->
flagname
[
userflag
])
{
if
(
!
strcasecmp
(
flag
[
i
],
mailbox
->
flagname
[
userflag
]))
break
;
}
else
if
(
emptyflag
==
-1
)
emptyflag
=
userflag
;
}
/* Flag is not defined--create it */
if
(
userflag
==
MAX_USER_FLAGS
&&
emptyflag
!=
-1
)
{
userflag
=
emptyflag
;
mailbox
->
flagname
[
userflag
]
=
xstrdup
(
flag
[
i
]);
as
->
writeheader
++
;
}
if
(
userflag
!=
MAX_USER_FLAGS
)
{
message_index
.
user_flags
[
userflag
/
32
]
|=
1
<<
(
userflag
&
31
);
}
}
}
/* Write out index file entry */
r
=
mailbox_append_index
(
mailbox
,
&
message_index
,
mailbox
->
exists
+
as
->
nummsg
-
1
,
1
,
0
);
if
(
r
)
{
append_abort
(
as
);
return
r
;
}
/* ok, we've successfully added a message */
as
->
quota_used
+=
message_index
.
size
;
*
stagep
=
stage
;
return
0
;
}
int
append_removestage
(
struct
stagemsg
*
stage
)
{
int
i
;
if
(
stage
==
NULL
)
return
0
;
i
=
0
;
while
(
stage
->
parts
[
i
][
0
]
!=
'\0'
)
{
/* unlink the staging file */
if
(
unlink
(
stage
->
parts
[
i
])
!=
0
)
{
syslog
(
LOG_ERR
,
"IOERROR: error unlinking file %s: %m"
,
stage
->
parts
[
i
]);
}
i
++
;
}
free
(
stage
);
return
0
;
}
/*
* Append to 'mailbox' from the prot stream 'messagefile'.
* 'mailbox' must have been opened with append_setup().
* 'size' is the expected size of the message.
* 'internaldate' specifies the internaldate for the new message.
* 'flags' contains the names of the 'nflags' flags that the
* user wants to set in the message. If the '\Seen' flag is
* in 'flags', then 'userid' contains the name of the user whose
* \Seen flag gets set.
*
* The message is not committed to the mailbox (nor is the mailbox
* unlocked) until append_commit() is called. multiple
* append_onefromstream()s can be aborted by calling append_abort().
*/
int
append_fromstream
(
struct
appendstate
*
as
,
struct
protstream
*
messagefile
,
unsigned
long
size
,
time_t
internaldate
,
const
char
**
flag
,
int
nflags
,
const
char
*
userid
)
{
struct
mailbox
*
mailbox
=
&
as
->
m
;
struct
index_record
message_index
=
zero_index
;
char
fname
[
MAX_MAILBOX_PATH
];
FILE
*
destfile
;
int
i
,
r
;
int
setseen
=
0
;
int
userflag
,
emptyflag
;
assert
(
mailbox
->
format
==
MAILBOX_FORMAT_NORMAL
);
assert
(
size
!=
0
);
/* Setup */
message_index
.
uid
=
mailbox
->
last_uid
+
as
->
nummsg
+
1
;
message_index
.
last_updated
=
time
(
0
);
message_index
.
internaldate
=
internaldate
;
lseek
(
mailbox
->
cache_fd
,
0L
,
SEEK_END
);
/* Create message file */
strcpy
(
fname
,
mailbox
->
path
);
strcat
(
fname
,
"/"
);
mailbox_message_get_fname
(
mailbox
,
message_index
.
uid
,
fname
+
strlen
(
fname
));
as
->
nummsg
++
;
destfile
=
fopen
(
fname
,
"w+"
);
if
(
!
destfile
)
{
syslog
(
LOG_ERR
,
"IOERROR: creating message file %s: %m"
,
fname
);
append_abort
(
as
);
return
IMAP_IOERROR
;
}
/* Copy and parse message */
r
=
message_copy_strict
(
messagefile
,
destfile
,
size
);
if
(
!
r
)
{
r
=
message_parse_file
(
destfile
,
mailbox
,
&
message_index
);
}
fclose
(
destfile
);
if
(
r
)
{
append_abort
(
as
);
return
r
;
}
/* Handle flags the user wants to set in the message */
for
(
i
=
0
;
i
<
nflags
;
i
++
)
{
if
(
!
strcmp
(
flag
[
i
],
"
\\
seen"
))
setseen
++
;
else
if
(
!
strcmp
(
flag
[
i
],
"
\\
deleted"
))
{
if
(
mailbox
->
myrights
&
ACL_DELETE
)
{
message_index
.
system_flags
|=
FLAG_DELETED
;
as
->
numdeleted
++
;
}
}
else
if
(
!
strcmp
(
flag
[
i
],
"
\\
draft"
))
{
if
(
mailbox
->
myrights
&
ACL_WRITE
)
{
message_index
.
system_flags
|=
FLAG_DRAFT
;
}
}
else
if
(
!
strcmp
(
flag
[
i
],
"
\\
flagged"
))
{
if
(
mailbox
->
myrights
&
ACL_WRITE
)
{
message_index
.
system_flags
|=
FLAG_FLAGGED
;
as
->
numflagged
++
;
}
}
else
if
(
!
strcmp
(
flag
[
i
],
"
\\
answered"
))
{
if
(
mailbox
->
myrights
&
ACL_WRITE
)
{
message_index
.
system_flags
|=
FLAG_ANSWERED
;
as
->
numanswered
++
;
}
}
else
if
(
mailbox
->
myrights
&
ACL_WRITE
)
{
/* User flag */
emptyflag
=
-1
;
for
(
userflag
=
0
;
userflag
<
MAX_USER_FLAGS
;
userflag
++
)
{
if
(
mailbox
->
flagname
[
userflag
])
{
if
(
!
strcasecmp
(
flag
[
i
],
mailbox
->
flagname
[
userflag
]))
break
;
}
else
if
(
emptyflag
==
-1
)
emptyflag
=
userflag
;
}
/* Flag is not defined--create it */
if
(
userflag
==
MAX_USER_FLAGS
&&
emptyflag
!=
-1
)
{
userflag
=
emptyflag
;
mailbox
->
flagname
[
userflag
]
=
xstrdup
(
flag
[
i
]);
as
->
writeheader
++
;
}
if
(
userflag
!=
MAX_USER_FLAGS
)
{
message_index
.
user_flags
[
userflag
/
32
]
|=
1
<<
(
userflag
&
31
);
}
}
}
/* Write out index file entry; if we abort later, it's not
important */
r
=
mailbox_append_index
(
mailbox
,
&
message_index
,
mailbox
->
exists
+
as
->
nummsg
-
1
,
1
,
0
);
if
(
r
)
{
append_abort
(
as
);
return
r
;
}
/* ok, we've successfully added a message */
as
->
quota_used
+=
message_index
.
size
;
return
0
;
}
/*
* Append to 'append_mailbox' ('as') the 'nummsg' messages from the mailbox
* 'mailbox' listed in the array pointed to by 'copymsg'. 'as'
* must have been opened with append_setup(). If the '\Seen' flag is
* to be set anywhere then 'userid' contains the name of the user
* whose \Seen flag gets set.
*/
int
append_copy
(
struct
mailbox
*
mailbox
,
struct
appendstate
*
as
,
int
nummsg
,
struct
copymsg
*
copymsg
,
const
char
*
userid
)
{
struct
mailbox
*
append_mailbox
=
&
as
->
m
;
int
msg
;
struct
index_record
*
message_index
;
char
fname
[
MAX_MAILBOX_PATH
];
const
char
*
src_base
;
unsigned
long
src_size
;
const
char
*
startline
,
*
endline
;
FILE
*
destfile
;
int
r
,
n
;
int
flag
,
userflag
,
emptyflag
;
assert
(
append_mailbox
->
format
==
MAILBOX_FORMAT_NORMAL
);
if
(
!
nummsg
)
{
append_abort
(
as
);
return
0
;
}
lseek
(
append_mailbox
->
cache_fd
,
0L
,
SEEK_END
);
message_index
=
(
struct
index_record
*
)
xmalloc
(
nummsg
*
sizeof
(
struct
index_record
));
/* Copy/link all files and cache info */
for
(
msg
=
0
;
msg
<
nummsg
;
msg
++
)
{
message_index
[
msg
]
=
zero_index
;
message_index
[
msg
].
uid
=
append_mailbox
->
last_uid
+
1
+
as
->
nummsg
;
message_index
[
msg
].
last_updated
=
time
(
0
);
message_index
[
msg
].
internaldate
=
copymsg
[
msg
].
internaldate
;
as
->
nummsg
++
;
strcpy
(
fname
,
append_mailbox
->
path
);
strcat
(
fname
,
"/"
);
mailbox_message_get_fname
(
append_mailbox
,
message_index
[
msg
].
uid
,
fname
+
strlen
(
fname
));
if
(
copymsg
[
msg
].
cache_len
)
{
/* Link/copy message file */
r
=
mailbox_copyfile
(
mailbox_message_fname
(
mailbox
,
copymsg
[
msg
].
uid
),
fname
);
if
(
r
)
goto
fail
;
/* Write out cache info, copy other info */
message_index
[
msg
].
cache_offset
=
lseek
(
append_mailbox
->
cache_fd
,
0L
,
SEEK_CUR
);
message_index
[
msg
].
sentdate
=
copymsg
[
msg
].
sentdate
;
message_index
[
msg
].
size
=
copymsg
[
msg
].
size
;
message_index
[
msg
].
header_size
=
copymsg
[
msg
].
header_size
;
message_index
[
msg
].
content_offset
=
copymsg
[
msg
].
header_size
;
n
=
retry_write
(
append_mailbox
->
cache_fd
,
copymsg
[
msg
].
cache_begin
,
copymsg
[
msg
].
cache_len
);
if
(
n
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: writing cache file for %s: %m"
,
append_mailbox
->
name
);
r
=
IMAP_IOERROR
;
goto
fail
;
}
}
else
{
/*
* Have to copy the message, possibly converting LF to CR LF
* Then, we have to parse the message.
*/
r
=
0
;
destfile
=
fopen
(
fname
,
"w+"
);
if
(
!
destfile
)
{
syslog
(
LOG_ERR
,
"IOERROR: writing message file %s: %m"
,
fname
);
r
=
IMAP_IOERROR
;
goto
fail
;
}
if
(
mailbox_map_message
(
mailbox
,
0
,
copymsg
[
msg
].
uid
,
&
src_base
,
&
src_size
)
!=
0
)
{
fclose
(
destfile
);
syslog
(
LOG_ERR
,
"IOERROR: opening message file %u of %s: %m"
,
copymsg
[
msg
].
uid
,
mailbox
->
name
);
r
=
IMAP_IOERROR
;
goto
fail
;
}
startline
=
src_base
;
while
(
(
endline
=
memchr
(
startline
,
'\n'
,
src_size
-
(
startline
-
src_base
)))
)
{
fwrite
(
startline
,
1
,
(
endline
-
startline
),
destfile
);
if
(
endline
==
startline
||
endline
[
-1
]
!=
'\r'
)
{
putc
(
'\r'
,
destfile
);
}
putc
(
'\n'
,
destfile
);
startline
=
endline
+
1
;
}
fwrite
(
startline
,
1
,
src_size
-
(
startline
-
src_base
),
destfile
);
fflush
(
destfile
);
if
(
ferror
(
destfile
)
||
fsync
(
fileno
(
destfile
)))
{
syslog
(
LOG_ERR
,
"IOERROR: writing message: %m"
);
r
=
IMAP_IOERROR
;
}
mailbox_unmap_message
(
mailbox
,
copymsg
[
msg
].
uid
,
&
src_base
,
&
src_size
);
if
(
!
r
)
r
=
message_parse_file
(
destfile
,
append_mailbox
,
&
message_index
[
msg
]);
fclose
(
destfile
);
if
(
r
)
goto
fail
;
}
as
->
quota_used
+=
message_index
[
msg
].
size
;
/* Handle any flags that need to be copied */
if
(
append_mailbox
->
myrights
&
ACL_WRITE
)
{
message_index
[
msg
].
system_flags
=
copymsg
[
msg
].
system_flags
&
~
FLAG_DELETED
;
for
(
flag
=
0
;
copymsg
[
msg
].
flag
[
flag
];
flag
++
)
{
emptyflag
=
-1
;
for
(
userflag
=
0
;
userflag
<
MAX_USER_FLAGS
;
userflag
++
)
{
if
(
append_mailbox
->
flagname
[
userflag
])
{
if
(
!
strcasecmp
(
copymsg
[
msg
].
flag
[
flag
],
append_mailbox
->
flagname
[
userflag
]))
break
;
}
else
if
(
emptyflag
==
-1
)
emptyflag
=
userflag
;
}
/* Flag is not defined--create it */
if
(
userflag
==
MAX_USER_FLAGS
&&
emptyflag
!=
-1
)
{
userflag
=
emptyflag
;
append_mailbox
->
flagname
[
userflag
]
=
xstrdup
(
copymsg
[
msg
].
flag
[
flag
]);
as
->
writeheader
++
;
}
if
(
userflag
!=
MAX_USER_FLAGS
)
{
message_index
[
msg
].
user_flags
[
userflag
/
32
]
|=
1
<<
(
userflag
&
31
);
}
}
}
if
(
append_mailbox
->
myrights
&
ACL_DELETE
)
{
message_index
[
msg
].
system_flags
|=
copymsg
[
msg
].
system_flags
&
FLAG_DELETED
;
}
if
(
message_index
[
msg
].
system_flags
&
FLAG_DELETED
)
as
->
numdeleted
++
;
if
(
message_index
[
msg
].
system_flags
&
FLAG_ANSWERED
)
as
->
numanswered
++
;
if
(
message_index
[
msg
].
system_flags
&
FLAG_FLAGGED
)
as
->
numflagged
++
;
}
/* Write out index file entries */
r
=
mailbox_append_index
(
append_mailbox
,
message_index
,
append_mailbox
->
exists
+
as
->
nummsg
-
nummsg
,
nummsg
,
0
);
fail
:
if
(
r
)
append_abort
(
as
);
free
(
message_index
);
return
r
;
}
/*
* Append the to 'mailbox' the index/cache entries for the netnews
* articles which have recently arrived. Articles up to and including
* 'feeduid', as well as any existing consecutive articles after 'feeduid'
* are appended.
*/
#define COLLECTGROW 50
int
append_collectnews
(
struct
appendstate
*
as
,
const
char
*
group
,
unsigned
long
feeduid
)
{
#if 0
char newspath[4096], *end_newspath;
time_t curtime, internaldate;
struct index_record *message_index;
int size_message_index;
unsigned long quota_usage = 0;
int msg = 0;
int uid = mailbox->last_uid;
FILE *f;
int r;
long last_cacheoffset;
struct stat sbuf;
assert(mailbox->format == MAILBOX_FORMAT_NETNEWS);
if (feeduid < mailbox->last_uid) feeduid = mailbox->last_uid;
curtime = internaldate = time(0);
last_cacheoffset = lseek(mailbox->cache_fd, 0L, SEEK_END);
size_message_index = feeduid - mailbox->last_uid + COLLECTGROW;
message_index = (struct index_record *)
xmalloc(size_message_index * sizeof(struct index_record));
if (config_newsspool) {
strcpy(newspath, config_newsspool);
end_newspath = newspath + strlen(newspath);
if (end_newspath == newspath || end_newspath[-1] != '/') {
*end_newspath++ = '/';
}
strcpy(end_newspath, group);
while (*end_newspath) {
if (*end_newspath == '.') *end_newspath = '/';
end_newspath++;
}
if (chdir(newspath)) {
syslog(LOG_ERR, "IOERROR: changing dir to %s: %m", newspath);
mailbox_unlock_quota(&mailbox->quota);
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
return IMAP_IOERROR;
}
}
else if (chdir(mailbox->path)) {
syslog(LOG_ERR, "IOERROR: changing dir to %s: %m", mailbox->path);
mailbox_unlock_quota(&mailbox->quota);
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
return IMAP_IOERROR;
}
/* Find and parse the new messages */
for (;;) {
uid++;
sprintf(newspath, "%u", uid);
f = fopen(newspath, "r");
if (!f) {
if (uid < feeduid) continue;
break;
}
if (fstat(fileno(f), &sbuf)) {
fclose(f);
continue;
}
if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
/* This is in theory a subnewsgroup and should be left alone. */
fclose(f);
continue;
}
if (msg == size_message_index) {
size_message_index += COLLECTGROW;
message_index = (struct index_record *)
xrealloc((char *)message_index,
size_message_index * sizeof(struct index_record));
}
message_index[msg] = zero_index;
message_index[msg].uid = uid;
message_index[msg].last_updated = curtime;
message_index[msg].internaldate = internaldate++;
r = message_parse_file(f, mailbox, &message_index[msg]);
fclose(f);
if (r) goto fail;
quota_usage += message_index[msg].size;
msg++;
}
/* Didn't find anything to append */
if (msg == 0) {
free(message_index);
mailbox_unlock_quota(&mailbox->quota);
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
return 0;
}
/* Flush out the cache file data */
if (fsync(mailbox->cache_fd)) {
syslog(LOG_ERR, "IOERROR: writing cache file for %s: %m",
mailbox->name);
r = IMAP_IOERROR;
goto fail;
}
/* Write out index file entries */
r = mailbox_append_index(mailbox, message_index, mailbox->exists, msg);
if (r) goto fail;
/* Calculate new index header information */
mailbox->exists += msg;
mailbox->last_uid = uid-1;
mailbox->last_appenddate = internaldate-1;
mailbox->quota_mailbox_used += quota_usage;
if (mailbox->minor_version > MAILBOX_MINOR_VERSION) {
mailbox->minor_version = MAILBOX_MINOR_VERSION;
}
/* Write out index header */
r = mailbox_write_index_header(mailbox);
if (r) goto fail;
/* Write out quota file */
mailbox->quota.used += quota_usage;
r = mailbox_write_quota(&mailbox->quota);
if (r) {
syslog(LOG_ERR,
"LOSTQUOTA: unable to record use of %u bytes in quota file %s",
quota_usage, mailbox->quota.root);
}
free(message_index);
mailbox_unlock_quota(&mailbox->quota);
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
return 0;
fail:
ftruncate(mailbox->cache_fd, last_cacheoffset);
free(message_index);
mailbox_unlock_quota(&mailbox->quota);
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
return r;
#endif
return
0
;
}
/*
* Set the \Seen flag for 'userid' in 'mailbox' for the messages from
* 'start' to 'end', inclusively.
*/
static
int
append_addseen
(
struct
mailbox
*
mailbox
,
const
char
*
userid
,
unsigned
start
,
unsigned
end
)
{
int
r
;
struct
seen
*
seendb
;
time_t
last_read
,
last_change
;
unsigned
last_uid
;
char
*
seenuids
;
int
last_seen
;
char
*
tail
,
*
p
;
r
=
seen_open
(
mailbox
,
userid
,
&
seendb
);
if
(
r
)
return
r
;
r
=
seen_lockread
(
seendb
,
&
last_read
,
&
last_uid
,
&
last_change
,
&
seenuids
);
if
(
r
)
return
r
;
seenuids
=
xrealloc
(
seenuids
,
strlen
(
seenuids
)
+
40
);
tail
=
seenuids
+
strlen
(
seenuids
);
/* Scan back to last uid */
while
(
tail
>
seenuids
&&
isdigit
((
int
)
tail
[
-1
]))
tail
--
;
for
(
p
=
tail
,
last_seen
=
0
;
*
p
;
p
++
)
last_seen
=
last_seen
*
10
+
*
p
-
'0'
;
if
(
last_seen
&&
last_seen
>=
start
-1
)
{
if
(
tail
>
seenuids
&&
tail
[
-1
]
==
':'
)
p
=
tail
-
1
;
*
p
++
=
':'
;
}
else
{
if
(
p
>
seenuids
)
*
p
++
=
','
;
if
(
start
!=
end
)
{
sprintf
(
p
,
"%u:"
,
start
);
p
+=
strlen
(
p
);
}
}
sprintf
(
p
,
"%u"
,
end
);
r
=
seen_write
(
seendb
,
last_read
,
last_uid
,
time
((
time_t
*
)
0
),
seenuids
);
seen_close
(
seendb
);
free
(
seenuids
);
return
r
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Mon, Apr 6, 3:14 AM (3 w, 17 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18832158
Default Alt Text
append.c (25 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline