Page MenuHomePhorge

append.c
No OneTemporary

Authored By
Unknown
Size
16 KB
Referenced Files
None
Subscribers
None

append.c

/* append.c -- Routines for appending messages to a mailbox
*
* (C) Copyright 1994 by Carnegie Mellon University
*
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of CMU not be
* used in advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
*
* CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
* CMU 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 <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <syslog.h>
#include "acl.h"
#include "assert.h"
#include "imap_err.h"
#include "mailbox.h"
#include "append.h"
#include "xmalloc.h"
/*
* 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 'mailbox' is set up.
*
*/
int append_setup(mailbox, name, format, aclcheck, quotacheck)
struct mailbox *mailbox;
char *name;
int format;
long aclcheck;
long quotacheck;
{
int r;
r = mailbox_open_header(name, mailbox);
if (r) return r;
if ((mailbox->myrights & aclcheck) != aclcheck) {
r = (mailbox->myrights & ACL_LOOKUP) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
mailbox_close(mailbox);
return r;
}
r = mailbox_lock_header(mailbox);
if (r) {
mailbox_close(mailbox);
return r;
}
r = mailbox_open_index(mailbox);
if (r) {
mailbox_close(mailbox);
return r;
}
if (mailbox->format != format) {
mailbox_close(mailbox);
return IMAP_MAILBOX_NOTSUPPORTED;
}
r = mailbox_lock_index(mailbox);
if (r) {
mailbox_close(mailbox);
return r;
}
r = mailbox_lock_quota(mailbox);
if (r) {
mailbox_close(mailbox);
return r;
}
if (mailbox->quota_limit >= 0 && quotacheck >= 0 &&
mailbox->quota_used + quotacheck > mailbox->quota_limit * QUOTA_UNITS) {
return IMAP_QUOTA_EXCEEDED;
}
return 0;
}
/*
* Append to 'mailbox' from the stdio stream 'messagefile'.
* 'mailbox' must have been opened with append_setup().
* If 'size', is nonzero it the expected size of the message.
* If 'size' is zero, message may need LF to CRLF conversion.
* '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.
*/
int
append_fromstream(mailbox, messagefile, size, internaldate, flag, nflags,
userid)
struct mailbox *mailbox;
FILE *messagefile;
unsigned size;
time_t internaldate;
char **flag;
int nflags;
char *userid;
{
struct index_record message_index;
static struct index_record zero_index;
char fname[MAX_MAILBOX_PATH];
FILE *destfile;
int i, r;
long last_cacheoffset;
int setseen = 0, writeheader = 0;
int userflag, emptyflag;
assert(mailbox->format == MAILBOX_FORMAT_NORMAL);
/* Setup */
fseek(mailbox->cache, 0L, 2);
last_cacheoffset = ftell(mailbox->cache);
message_index = zero_index;
message_index.uid = mailbox->last_uid + 1;
message_index.last_updated = time(0);
message_index.internaldate = internaldate;
/* Create message file */
strcpy(fname, mailbox->path);
strcat(fname, "/");
strcat(fname, mailbox_message_fname(mailbox, message_index.uid));
destfile = fopen(fname, "w+");
if (!destfile) {
syslog(LOG_ERR, "IOERROR: creating message file %s: %m", fname);
return IMAP_IOERROR;
}
/* Copy and parse message */
if (size) {
r = message_copy_strict(messagefile, destfile, size);
}
else {
r = message_copy_byline(messagefile, destfile);
}
if (!r) r = message_parse(destfile, mailbox, &message_index);
fclose(destfile);
if (r) {
unlink(fname);
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;
}
}
else if (!strcmp(flag[i], "\\flagged")) {
if (mailbox->myrights & ACL_WRITE) {
message_index.system_flags |= FLAG_FLAGGED;
}
}
else if (!strcmp(flag[i], "\\answered")) {
if (mailbox->myrights & ACL_WRITE) {
message_index.system_flags |= FLAG_ANSWERED;
}
}
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] = strsave(flag[i]);
writeheader++;
}
if (userflag != MAX_USER_FLAGS) {
message_index.user_flags[userflag/32] |= 1<<(userflag&31);
}
}
}
/* Write out the header if we created a new user flag */
if (writeheader) {
r = mailbox_write_header(mailbox);
if (r) {
unlink(fname);
ftruncate(fileno(mailbox->cache), last_cacheoffset);
return r;
}
}
/* Write out index file entry */
r = mailbox_append_index(mailbox, &message_index, mailbox->exists, 1);
if (r) {
unlink(fname);
ftruncate(fileno(mailbox->cache), last_cacheoffset);
return r;
}
/* Calculate new index header information */
mailbox->exists++;
mailbox->last_uid = message_index.uid;
mailbox->last_appenddate = time(0);
mailbox->quota_mailbox_used += message_index.size;
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) {
unlink(fname);
ftruncate(fileno(mailbox->cache), last_cacheoffset);
/* We don't ftruncate index file. It doesn't matter */
return r;
}
/* Write out quota file */
mailbox->quota_used += message_index.size;
r = mailbox_write_quota(mailbox);
if (r) {
syslog(LOG_ERR,
"LOSTQUOTA: unable to record use of %d bytes in quota file %s",
message_index.size, mailbox->quota_path);
}
/* Set \Seen flag if necessary */
if (setseen && userid && (mailbox->myrights & ACL_SEEN)) {
append_addseen(mailbox, userid, message_index.uid, message_index.uid);
}
return 0;
}
/*
* Append to 'mailbox' the 'nummsg' messages listed in the array
* pointed to by 'copymsg'. 'mailbox' 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(mailbox, nummsg, copymsg, userid)
struct mailbox *mailbox;
int nummsg;
struct copymsg *copymsg;
char *userid;
{
int msg;
struct index_record *message_index;
static struct index_record zero_index;
unsigned long quota_usage = 0;
char fname[MAX_MAILBOX_PATH];
FILE *srcfile, *destfile;
int r;
long last_cacheoffset;
int writeheader = 0;
int flag, userflag, emptyflag;
int seenbegin;
assert(mailbox->format == MAILBOX_FORMAT_NORMAL);
if (!nummsg) return 0;
fseek(mailbox->cache, 0L, 2);
last_cacheoffset = ftell(mailbox->cache);
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 = mailbox->last_uid + 1 + msg;
message_index[msg].last_updated = time(0);
message_index[msg].internaldate = copymsg[msg].internaldate;
if (copymsg[msg].cache_len) {
/* Link/copy message file */
strcpy(fname, mailbox->path);
strcat(fname, "/");
strcat(fname, mailbox_message_fname(mailbox,
message_index[msg].uid));
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 = ftell(mailbox->cache);
fwrite(copymsg[msg].cache_begin, 1, copymsg[msg].cache_len,
mailbox->cache);
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;
}
else {
/*
* Have to copy the message, possibly converting LF to CR LF
* Then, we have to parse the message.
*/
destfile = fopen(fname, "w");
if (!destfile) {
syslog(LOG_ERR, "IOERROR: writing message file %s: %m", fname);
r = IMAP_IOERROR;
goto fail;
}
srcfile = fopen(mailbox_message_fname(mailbox, copymsg[msg].uid),
"r");
if (!srcfile) {
fclose(destfile);
syslog(LOG_ERR, "IOERROR: reading message file %s: %m",
mailbox_message_fname(mailbox, copymsg[msg].uid));
r = IMAP_IOERROR;
goto fail;
}
r = message_copy_byline(srcfile, destfile);
fclose(srcfile);
if (!r) r = message_parse(destfile, mailbox, &message_index[msg]);
fclose(destfile);
if (r) goto fail;
}
quota_usage += message_index[msg].size;
/* Handle any flags that need to be copied */
if (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 (mailbox->flagname[userflag]) {
if (!strcasecmp(copymsg[msg].flag[flag],
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] =
strsave(copymsg[msg].flag[flag]);
writeheader++;
}
if (userflag != MAX_USER_FLAGS) {
message_index[msg].user_flags[userflag/32] |=
1<<(userflag&31);
}
}
}
if (mailbox->myrights & ACL_DELETE) {
message_index[msg].system_flags |=
copymsg[msg].system_flags & FLAG_DELETED;
}
}
/* Write out the header if we created a new user flag */
if (writeheader) {
r = mailbox_write_header(mailbox);
if (r) goto fail;
}
/* Write out index file entries */
r = mailbox_append_index(mailbox, message_index, mailbox->exists, nummsg);
if (r) goto fail;
/* Calculate new index header information */
mailbox->exists += nummsg;
mailbox->last_uid += nummsg;
mailbox->last_appenddate = time(0);
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);
if (r) {
syslog(LOG_ERR,
"LOSTQUOTA: unable to record use of %d bytes in quota file %s",
quota_usage, mailbox->quota_path);
}
/* Set \Seen flags if necessary */
for (msg = seenbegin = 0; msg < nummsg; msg++) {
if (!seenbegin && !copymsg[msg].seen) continue;
if (seenbegin && copymsg[msg].seen) continue;
if (!seenbegin) {
if (mailbox->myrights & ACL_SEEN) {
seenbegin = msg+1;
}
}
else {
append_addseen(mailbox, userid, message_index[seenbegin-1].uid,
message_index[msg-1].uid);
seenbegin = 0;
}
}
if (seenbegin) {
append_addseen(mailbox, userid, message_index[seenbegin-1].uid,
message_index[nummsg-1].uid);
}
free(message_index);
return 0;
fail:
/* Remove all new message files */
for (msg = 0; msg < nummsg; msg++) {
strcpy(fname, mailbox->path);
strcat(fname, "/");
strcat(fname, mailbox_message_fname(mailbox, mailbox->last_uid + 1 + msg));
unlink(fname);
}
ftruncate(fileno(mailbox->cache), last_cacheoffset);
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 2 /* XXX 20 */
int
append_collectnews(mailbox, feeduid)
struct mailbox *mailbox;
unsigned long feeduid;
{
time_t curtime, internaldate;
struct index_record *message_index;
int size_message_index;
static struct index_record zero_index;
unsigned long quota_usage = 0;
int msg = 0;
int uid = mailbox->last_uid;
FILE *f;
int r;
long last_cacheoffset;
assert(mailbox->format == MAILBOX_FORMAT_NETNEWS);
if (feeduid < mailbox->last_uid) feeduid = mailbox->last_uid;
curtime = internaldate = time(0);
fseek(mailbox->cache, 0L, 2);
last_cacheoffset = ftell(mailbox->cache);
size_message_index = feeduid - mailbox->last_uid + COLLECTGROW;
message_index = (struct index_record *)
xmalloc(size_message_index * sizeof(struct index_record));
if (chdir(mailbox->path)) {
syslog(LOG_ERR, "IOERROR: changing dir to %s: %m", mailbox->path);
return IMAP_IOERROR;
}
/* Find and parse the new messages */
for (;;) {
uid++;
f = fopen(mailbox_message_fname(mailbox, uid), "r");
if (!f) {
if (uid < feeduid) continue;
break;
}
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(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);
return 0;
}
/* 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);
if (r) {
syslog(LOG_ERR,
"LOSTQUOTA: unable to record use of %d bytes in quota file %s",
quota_usage, mailbox->quota_path);
}
free(message_index);
return 0;
fail:
ftruncate(fileno(mailbox->cache), last_cacheoffset);
free(message_index);
return r;
}
/*
* Set the \Seen flag for 'userid' in 'mailbox' for the messages from
* 'start' to 'end', inclusively.
*/
static int append_addseen(mailbox, userid, start, end)
struct mailbox *mailbox;
char *userid;
int start;
int end;
{
int r;
struct seen *seendb;
time_t last_time;
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_time, &last_uid, &seenuids);
if (r) return r;
seenuids = xrealloc(seenuids, strlen(seenuids)+40);
tail = seenuids + strlen(seenuids);
/* Scan back to last uid */
while (tail > seenuids && isdigit(tail[-1])) tail--;
for (p = tail, last_seen=0; *p; p++) last_seen = last_seen * 10 + *p - '0';
if (last_seen == start-1) {
if (tail > seenuids && tail[-1] == ':') p = tail - 1;
*p++ = ':';
}
else {
if (p > seenuids) *p++ = ',';
if (start != end) {
sprintf(p, "%d:", start);
p += strlen(p);
}
}
sprintf(p, "%d", end);
r = seen_write(seendb, last_time, last_uid, seenuids);
seen_close(seendb);
free(seenuids);
return r;
}

File Metadata

Mime Type
text/x-c
Expires
Fri, Apr 24, 10:12 AM (3 d, 6 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18846627
Default Alt Text
append.c (16 KB)

Event Timeline