Page MenuHomePhorge

append.c
No OneTemporary

Authored By
Unknown
Size
26 KB
Referenced Files
None
Subscribers
None

append.c

/* append.c -- Routines for appending messages to a mailbox
* $Id: append.c,v 1.90 2002/04/02 17:16:13 leg 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 <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 "seen.h"
#include "retry.h"
struct stagemsg {
char fname[1024];
int num_parts;
char parts[1][MAX_MAILBOX_PATH];
};
static int append_addseen(struct mailbox *mailbox, const char *userid,
const char *msgrange);
static void addme(char **msgrange, int *alloced, long uid);
#define zero_index(i) { memset(&i, 0, sizeof(struct index_record)); }
/*
* Check to see if mailbox can be appended to
*
* 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)
*
*/
int append_check(const char *name, int format,
struct auth_state *auth_state,
long aclcheck, long quotacheck)
{
struct mailbox m;
int r;
int mbflags;
/* Is mailbox moved? */
r = mboxlist_detail(name, &mbflags, NULL, NULL, NULL, NULL);
if (!r) {
if(mbflags & MBTYPE_MOVING) return IMAP_MAILBOX_MOVED;
} else {
return r;
}
r = mailbox_open_header(name, auth_state, &m);
if (r) return r;
if ((m.myrights & aclcheck) != aclcheck) {
r = (m.myrights & ACL_LOOKUP) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
mailbox_close(&m);
return r;
}
r = mailbox_open_index(&m);
if (r) {
mailbox_close(&m);
return r;
}
if (m.format != format) {
mailbox_close(&m);
return IMAP_MAILBOX_NOTSUPPORTED;
}
r = mailbox_read_quota(&m.quota);
if (r) {
mailbox_close(&m);
return r;
}
if (m.quota.limit >= 0 && quotacheck >= 0 &&
m.quota.used + quotacheck >
((unsigned) m.quota.limit * QUOTA_UNITS)) {
mailbox_close(&m);
return IMAP_QUOTA_EXCEEDED;
}
mailbox_close(&m);
return 0;
}
/*
* 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,
const char *userid, 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 >
((unsigned) as->m.quota.limit * QUOTA_UNITS)) {
mailbox_close(&as->m);
return IMAP_QUOTA_EXCEEDED;
}
if (userid) {
strcpy(as->userid, userid);
} else {
as->userid[0] = '\0';
}
/* 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->seen_msgrange = NULL;
as->seen_alloced = 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,
unsigned long *uidvalidity,
unsigned long *start,
unsigned long *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 */
if (as->seen_msgrange && as->userid[0]) {
append_addseen(&as->m, as->userid, as->seen_msgrange);
}
if (as->seen_msgrange) {
free(as->seen_msgrange);
}
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);
if (as->seen_msgrange) {
free(as->seen_msgrange);
}
return r;
}
/*
* Return the number of stage msgs
*/
int append_stageparts(struct stagemsg *stagep)
{
if (!stagep) return 0;
return stagep->num_parts;
}
/*
* staging, to allow for single-instance store. initializes the stage
* with the file for the given mailboxname and returns the open file
* so it can double as the spool file
*/
FILE *append_newstage(const char *mailboxname, time_t internaldate,
struct stagemsg **stagep)
{
struct stagemsg *stage;
char stagedir[1024], stagefile[1024];
FILE *f;
assert(mailboxname != NULL);
assert(stagep != NULL);
stage = xmalloc(sizeof(struct stagemsg) +
5 * MAX_MAILBOX_PATH * sizeof(char));
sprintf(stage->fname, "%d-%d",(int) getpid(), (int) internaldate);
stage->num_parts = 5; /* room for 5 paths */
/* xxx check errors */
mboxlist_findstage(mailboxname, stagedir);
strcpy(stagefile, stagedir);
strcat(stagefile, stage->fname);
/* create this file and put it into stage->parts[0] */
f = fopen(stagefile, "w+");
if (!f) {
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);
*stagep = NULL;
return NULL;
}
strcpy(stage->parts[0], stagefile);
stage->parts[1][0] = '\0';
*stagep = stage;
return f;
}
/*
* 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,
struct stagemsg *stage)
{
struct mailbox *mailbox = &as->m;
struct index_record message_index;
char fname[MAX_MAILBOX_PATH];
FILE *destfile;
int i, r;
int userflag, emptyflag;
/* for staging */
char stagefile[1024];
int sp;
assert(stage != NULL && stage->parts[0][0] != '\0');
assert(mailbox->format == MAILBOX_FORMAT_NORMAL);
assert(size != 0);
zero_index(message_index);
/* xxx check errors */
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] */
/* create the new staging file */
r = mailbox_copyfile(stage->parts[0], stagefile);
if (r) {
/* maybe the directory doesn't exist? */
char stagedir[1024];
/* xxx check errors */
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);
r = mailbox_copyfile(stage->parts[0], stagefile);
}
}
if (r) {
/* oh well, we tried */
syslog(LOG_ERR, "IOERROR: creating message file %s: %m",
stagefile);
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) {
/* this will hopefully ensure that the link() actually happened
and makes sure that the file actually hits disk */
fsync(fileno(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")) {
addme(&as->seen_msgrange, &as->seen_alloced, message_index.uid);
}
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;
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 the 'userid' passed to append_setup controls 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)
{
struct mailbox *mailbox = &as->m;
struct index_record message_index;
char fname[MAX_MAILBOX_PATH];
FILE *destfile;
int i, r;
int userflag, emptyflag;
assert(mailbox->format == MAILBOX_FORMAT_NORMAL);
assert(size != 0);
zero_index(message_index);
/* 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++;
unlink(fname);
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")) {
addme(&as->seen_msgrange, &as->seen_alloced, message_index.uid);
}
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' passed to append_setup()
* 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)
{
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++) {
zero_index(message_index[msg]);
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) {
char fnamebuf[MAILBOX_FNAME_LEN];
mailbox_message_get_fname(mailbox, copymsg[msg].uid, fnamebuf);
/* Link/copy message file */
r = mailbox_copyfile(fnamebuf, 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;
unlink(fname);
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 %lu 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++;
/* should this message be marked \Seen? */
if (copymsg[msg].seen) {
addme(&as->seen_msgrange, &as->seen_alloced,
message_index[msg].uid);
}
}
/* 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;
}
/* add 'uid' to 'msgrange'. 'uid' should be larger than anything in
* 'msgrange'.
*/
static void addme(char **msgrange, int *alloced, long uid)
{
char *p;
int wasrange;
int len;
assert(msgrange != NULL);
len = *msgrange ? strlen(*msgrange) : 0;
if (*alloced < len + 40) {
*alloced += 40;
*msgrange = (char *) xrealloc(*msgrange, sizeof(char *) * (*alloced));
}
p = *msgrange;
if (!len) {
/* first time */
sprintf(*msgrange, "%ld", uid);
} else {
/* this is tricky if this is the second number we're adding */
wasrange = 0;
/* see what the last one is */
p = *msgrange + len - 1;
while (isdigit((int) *p) && p > *msgrange) p--;
/* second time, p == msgrange here */
if (*p == ':') wasrange = 1;
p++;
if (atoi(p) == uid - 1) {
if (!wasrange) {
p = *msgrange + len;
*p++ = ':';
} else {
/* we'll just overwrite the current number */
}
} else {
p = *msgrange + len;
*p++ = ',';
}
sprintf(p, "%ld", uid);
return;
}
}
/*
* Set the \Seen flag for 'userid' in 'mailbox' for the messages from
* 'msgrange'. the lowest msgrange must be larger than any previously
* seen message.
*/
static int append_addseen(struct mailbox *mailbox,
const char *userid,
const char *msgrange)
{
int r;
struct seen *seendb;
time_t last_read, last_change;
unsigned last_uid;
char *seenuids;
int last_seen;
char *tail, *p;
int oldlen;
int start;
/* what's the first uid in our new list? */
start = atoi(msgrange);
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;
oldlen = strlen(seenuids);
seenuids = xrealloc(seenuids, oldlen + strlen(msgrange) + 10);
tail = seenuids + oldlen;
/* 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++ = ',';
}
strcpy(p, msgrange);
r = seen_write(seendb, last_read, last_uid, time(NULL), seenuids);
seen_close(seendb);
free(seenuids);
return r;
}

File Metadata

Mime Type
text/x-c
Expires
Fri, Apr 24, 1:14 PM (5 d, 1 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18782394
Default Alt Text
append.c (26 KB)

Event Timeline