Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117749743
index.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
52 KB
Referenced Files
None
Subscribers
None
index.c
View Options
/* index.c -- Routines for dealing with the index file in the imapd
*
* (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
<string.h>
#include
<time.h>
#include
<sys/types.h>
#include
<sys/stat.h>
#include
<netinet/in.h>
#include
<syslog.h>
#include
<com_err.h>
#include
<errno.h>
#include
"acl.h"
#include
"util.h"
#include
"assert.h"
#include
"sysexits.h"
#include
"imap_err.h"
#include
"mailbox.h"
#include
"imapd.h"
#include
"append.h"
#include
"charset.h"
#include
"xmalloc.h"
extern
int
errno
;
/* The index and cache files, mapped into memory */
static
char
*
index_base
;
static
unsigned
long
index_len
;
static
char
*
cache_base
;
static
unsigned
long
cache_len
;
static
unsigned
long
cache_end
;
/* Attributes of memory-mapped index file */
static
time_t
index_ino
;
static
unsigned
long
start_offset
;
static
unsigned
long
record_size
;
static
unsigned
recentuid
;
/* UID of last non-\Recent message */
static
unsigned
lastnotrecent
;
/* Msgno of last non-\Recent message */
static
time_t
*
flagreport
;
/* Array for each msgno of last_updated when
* FLAGS data reported to client.
* Zero if FLAGS data never reported */
static
char
*
seenflag
;
/* Array for each msgno, nonzero if \Seen */
static
int
flagalloced
=
-1
;
/* Allocated size of above two arrays */
static
int
examining
;
/* Nonzero if opened with EXAMINE command */
static
int
keepingseen
;
/* Nonzero if /Seen is meaningful */
static
unsigned
allseen
;
/* Last UID if all msgs /Seen last checkpoint */
struct
seen
*
seendb
;
/* Seen state database object */
static
char
*
seenuids
;
/* Sequence of UID's from last seen checkpoint */
/* Access macros for the memory-mapped index file data */
#define INDEX_OFFSET(msgno) (index_base+start_offset+(((msgno)-1)*record_size))
#define UID(msgno) ntohl(*((bit32 *)(INDEX_OFFSET(msgno)+OFFSET_UID)))
#define INTERNALDATE(msgno) ntohl(*((bit32 *)(INDEX_OFFSET(msgno)+OFFSET_INTERNALDATE)))
#define SENTDATE(msgno) ntohl(*((bit32 *)(INDEX_OFFSET(msgno)+OFFSET_SENTDATE)))
#define SIZE(msgno) ntohl(*((bit32 *)(INDEX_OFFSET(msgno)+OFFSET_SIZE)))
#define HEADER_SIZE(msgno) ntohl(*((bit32 *)(INDEX_OFFSET(msgno)+OFFSET_HEADER_SIZE)))
#define CONTENT_OFFSET(msgno) ntohl(*((bit32 *)(INDEX_OFFSET(msgno)+OFFSET_CONTENT_OFFSET)))
#define CACHE_OFFSET(msgno) ntohl(*((bit32 *)(INDEX_OFFSET(msgno)+OFFSET_CACHE_OFFSET)))
#define LAST_UPDATED(msgno) ntohl(*((bit32 *)(INDEX_OFFSET(msgno)+OFFSET_LAST_UPDATED)))
#define SYSTEM_FLAGS(msgno) ntohl(*((bit32 *)(INDEX_OFFSET(msgno)+OFFSET_SYSTEM_FLAGS)))
#define USER_FLAGS(msgno,i) ntohl(*((bit32 *)(INDEX_OFFSET(msgno)+OFFSET_USER_FLAGS+((i)*4))))
/* Access assistance macros for memory-mapped cache file data */
#define CACHE_ITEM_BIT32(ptr) (ntohl(*((bit32 *)(ptr))))
#define CACHE_ITEM_LEN(ptr) CACHE_ITEM_BIT32(ptr)
#define CACHE_ITEM_NEXT(ptr) ((ptr)+4+((3+CACHE_ITEM_LEN(ptr))&~3))
/* Forward declarations */
static
int
index_forsequence
();
static
int
index_listflags
();
static
int
index_fetchflags
(),
index_fetchreply
();
static
int
index_storeseen
(),
index_storeflag
();
static
int
index_search_evaluate
();
static
int
index_searchmsg
(),
index_searchheader
();
static
int
index_copysetup
();
struct
copyargs
{
struct
copymsg
*
copymsg
;
int
nummsg
;
int
msgalloc
;
};
/*
* A mailbox is about to be closed.
*/
index_closemailbox
(
mailbox
)
struct
mailbox
*
mailbox
;
{
if
(
seendb
)
{
index_checkseen
(
mailbox
,
1
,
0
,
imapd_exists
);
seen_close
(
seendb
);
seendb
=
0
;
}
if
(
index_len
)
{
map_free
(
&
index_base
,
&
index_len
);
map_free
(
&
cache_base
,
&
cache_len
);
index_len
=
cache_end
=
0
;
}
}
/*
* A new mailbox has been selected, map it into memory and do the
* initial CHECK.
*/
index_newmailbox
(
mailbox
,
examine_mode
)
struct
mailbox
*
mailbox
;
int
examine_mode
;
{
keepingseen
=
(
mailbox
->
myrights
&
ACL_SEEN
);
examining
=
examine_mode
;
allseen
=
0
;
recentuid
=
0
;
index_listflags
(
mailbox
);
imapd_exists
=
-1
;
index_check
(
mailbox
,
0
,
1
);
}
#define SLOP 50
/*
* Check for and report updates
*/
index_check
(
mailbox
,
usinguid
,
checkseen
)
struct
mailbox
*
mailbox
;
int
usinguid
;
int
checkseen
;
{
struct
stat
sbuf
;
int
newexists
,
oldexists
,
oldmsgno
,
msgno
,
nexpunge
,
i
,
r
;
struct
index_record
record
;
time_t
last_read
;
bit32
user_flags
[
MAX_USER_FLAGS
/
32
];
oldexists
=
imapd_exists
;
/* Check for expunge */
if
(
index_len
)
{
if
(
stat
(
FNAME_INDEX
+
1
,
&
sbuf
)
!=
0
)
{
if
(
errno
==
ENOENT
)
{
/* Mailbox has been deleted */
while
(
imapd_exists
--
)
{
prot_printf
(
imapd_out
,
"* 1 EXPUNGE
\r\n
"
);
}
mailbox
->
exists
=
0
;
imapd_exists
=
-1
;
if
(
seendb
)
{
seen_close
(
seendb
);
seendb
=
0
;
}
}
}
else
if
(
sbuf
.
st_ino
!=
mailbox
->
index_ino
)
{
if
(
mailbox_open_index
(
mailbox
))
{
fatal
(
"failed to reopen index file"
,
EX_IOERR
);
}
for
(
oldmsgno
=
msgno
=
1
;
oldmsgno
<=
imapd_exists
;
oldmsgno
++
,
msgno
++
)
{
if
(
msgno
<=
mailbox
->
exists
)
{
mailbox_read_index_record
(
mailbox
,
msgno
,
&
record
);
}
else
{
record
.
uid
=
mailbox
->
last_uid
+
1
;
}
nexpunge
=
0
;
while
(
oldmsgno
<=
imapd_exists
&&
UID
(
oldmsgno
)
<
record
.
uid
)
{
nexpunge
++
;
oldmsgno
++
;
}
if
(
nexpunge
)
{
bcopy
(
flagreport
+
msgno
+
nexpunge
,
flagreport
+
msgno
,
(
oldexists
-
msgno
-
nexpunge
+
1
)
*
sizeof
(
*
flagreport
));
bcopy
(
seenflag
+
msgno
+
nexpunge
,
seenflag
+
msgno
,
(
oldexists
-
msgno
-
nexpunge
+
1
)
*
sizeof
(
*
seenflag
));
oldexists
-=
nexpunge
;
while
(
nexpunge
--
)
{
prot_printf
(
imapd_out
,
"* %u EXPUNGE
\r\n
"
,
msgno
);
}
}
}
/* Force re-map of index/cache files */
map_free
(
&
index_base
,
&
index_len
);
map_free
(
&
cache_base
,
&
cache_len
);
cache_end
=
0
;
/* Force a * n EXISTS message */
imapd_exists
=
-1
;
}
else
if
(
sbuf
.
st_mtime
!=
mailbox
->
index_mtime
)
{
mailbox_read_index_header
(
mailbox
);
}
}
index_ino
=
mailbox
->
index_ino
;
start_offset
=
mailbox
->
start_offset
;
record_size
=
mailbox
->
record_size
;
newexists
=
mailbox
->
exists
;
/* Refresh the index and cache files */
map_refresh
(
fileno
(
mailbox
->
index
),
&
index_base
,
&
index_len
,
start_offset
+
newexists
*
record_size
,
"index"
,
mailbox
->
name
);
if
(
fstat
(
fileno
(
mailbox
->
cache
),
&
sbuf
)
==
-1
)
{
syslog
(
LOG_ERR
,
"IOERROR: stating cache file for %s: %m"
,
mailbox
->
name
);
fatal
(
"failed to stat cache file"
,
EX_IOERR
);
}
if
(
cache_end
<
sbuf
.
st_size
)
{
cache_end
=
sbuf
.
st_size
;
map_refresh
(
fileno
(
mailbox
->
cache
),
&
cache_base
,
&
cache_len
,
cache_end
,
"cache"
,
mailbox
->
name
);
}
/* If opening mailbox, get \Recent info */
if
(
oldexists
==
-1
&&
keepingseen
)
{
r
=
seen_open
(
mailbox
,
imapd_userid
,
&
seendb
);
if
(
!
r
)
{
r
=
seen_lockread
(
seendb
,
&
last_read
,
&
recentuid
,
&
seenuids
);
if
(
r
)
seen_close
(
seendb
);
}
if
(
r
)
{
seendb
=
0
;
prot_printf
(
imapd_out
,
"* OK %s: %s
\r\n
"
,
error_message
(
IMAP_NO_CHECKPRESERVE
),
error_message
(
r
));
}
else
{
/*
* Empty seenuids so that index_checkseen() will pick up the
* initial \Seen info. Leave the database locked.
*/
*
seenuids
=
'\0'
;
}
}
/* If opening mailbox or had an EXPUNGE, find where \Recent starts */
if
(
imapd_exists
==
-1
)
{
imapd_exists
=
newexists
;
lastnotrecent
=
index_finduid
(
recentuid
);
imapd_exists
=
-1
;
}
/* If EXISTS changed, report it */
if
(
newexists
!=
imapd_exists
)
{
/* Re-size flagreport and seenflag arrays if necessary */
if
(
newexists
>
flagalloced
)
{
flagalloced
=
newexists
+
SLOP
;
flagreport
=
(
time_t
*
)
xrealloc
((
char
*
)
flagreport
,
(
flagalloced
+
1
)
*
sizeof
(
time_t
));
seenflag
=
xrealloc
(
seenflag
,
flagalloced
+
1
);
}
/* Zero out array entry for newly arrived messages */
for
(
i
=
oldexists
+
1
;
i
<=
newexists
;
i
++
)
{
flagreport
[
i
]
=
0
;
seenflag
[
i
]
=
0
;
}
checkseen
=
1
;
imapd_exists
=
newexists
;
prot_printf
(
imapd_out
,
"* %u EXISTS
\r\n
* %u RECENT
\r\n
"
,
imapd_exists
,
imapd_exists
-
lastnotrecent
);
}
/* Check Flags */
if
(
checkseen
)
index_checkseen
(
mailbox
,
0
,
usinguid
,
oldexists
);
for
(
i
=
1
;
i
<=
imapd_exists
&&
seenflag
[
i
];
i
++
);
if
(
i
==
imapd_exists
+
1
)
allseen
=
mailbox
->
last_uid
;
if
(
oldexists
==
-1
)
{
if
(
imapd_exists
&&
i
<=
imapd_exists
)
{
prot_printf
(
imapd_out
,
"* OK [UNSEEN %u]
\r\n
"
,
i
);
}
prot_printf
(
imapd_out
,
"* OK [UIDVALIDITY %u]
\r\n
"
,
mailbox
->
uidvalidity
);
}
for
(
msgno
=
1
;
msgno
<=
oldexists
;
msgno
++
)
{
if
(
flagreport
[
msgno
]
&&
flagreport
[
msgno
]
<
LAST_UPDATED
(
msgno
))
{
for
(
i
=
0
;
i
<
MAX_USER_FLAGS
/
32
;
i
++
)
{
user_flags
[
i
]
=
USER_FLAGS
(
msgno
,
i
);
}
index_fetchflags
(
mailbox
,
msgno
,
SYSTEM_FLAGS
(
msgno
),
user_flags
,
LAST_UPDATED
(
msgno
));
if
(
usinguid
)
prot_printf
(
imapd_out
,
" UID %u"
,
UID
(
msgno
));
prot_printf
(
imapd_out
,
")
\r\n
"
);
}
}
}
/*
* Checkpoint the user's \Seen state
*/
#define SAVEGROW 200
index_checkseen
(
mailbox
,
quiet
,
usinguid
,
oldexists
)
struct
mailbox
*
mailbox
;
int
quiet
;
int
usinguid
;
int
oldexists
;
{
int
r
;
time_t
last_time
;
unsigned
last_uid
;
char
*
newseenuids
;
char
*
old
,
*
new
;
unsigned
oldnext
=
0
,
oldseen
=
0
;
unsigned
newnext
=
0
,
newseen
=
0
;
int
neweof
=
0
;
unsigned
msgno
,
uid
,
dirty
=
0
;
int
i
;
bit32
user_flags
[
MAX_USER_FLAGS
/
32
];
char
*
saveseenuids
,
*
save
;
int
savealloced
;
unsigned
start
,
newallseen
,
inrange
,
usecomma
;
if
(
!
keepingseen
||
!
seendb
)
return
;
if
(
imapd_exists
==
0
)
{
seen_unlock
(
seendb
);
return
;
}
/* Lock \Seen database and read current values */
r
=
seen_lockread
(
seendb
,
&
last_time
,
&
last_uid
,
&
newseenuids
);
if
(
r
)
{
prot_printf
(
imapd_out
,
"* OK %s: %s
\r\n
"
,
error_message
(
IMAP_NO_CHECKSEEN
),
error_message
(
r
));
return
;
}
/*
* Propagate changes in the database to the seenflag[] array
* and possibly to the client.
*/
old
=
seenuids
;
new
=
newseenuids
;
while
(
isdigit
(
*
old
))
oldnext
=
oldnext
*
10
+
*
old
++
-
'0'
;
while
(
isdigit
(
*
new
))
newnext
=
newnext
*
10
+
*
new
++
-
'0'
;
for
(
msgno
=
1
;
msgno
<=
imapd_exists
;
msgno
++
)
{
uid
=
UID
(
msgno
);
while
(
oldnext
<=
uid
)
{
if
(
*
old
!=
':'
&&
!
oldseen
&&
oldnext
==
uid
)
{
oldseen
=
1
;
break
;
}
else
{
oldseen
=
(
*
old
==
':'
);
oldnext
=
0
;
if
(
!*
old
)
oldnext
=
mailbox
->
last_uid
+
1
;
else
old
++
;
while
(
isdigit
(
*
old
))
oldnext
=
oldnext
*
10
+
*
old
++
-
'0'
;
oldnext
+=
oldseen
;
}
}
while
(
newnext
<=
uid
)
{
if
(
*
new
!=
':'
&&
!
newseen
&&
newnext
==
uid
)
{
newseen
=
1
;
break
;
}
else
{
newseen
=
(
*
new
==
':'
);
newnext
=
0
;
if
(
!*
new
)
{
newnext
=
mailbox
->
last_uid
+
1
;
neweof
++
;
}
else
new
++
;
while
(
isdigit
(
*
new
))
newnext
=
newnext
*
10
+
*
new
++
-
'0'
;
newnext
+=
newseen
;
}
}
if
(
oldseen
!=
newseen
)
{
if
(
seenflag
[
msgno
]
!=
newseen
)
{
seenflag
[
msgno
]
=
newseen
;
if
(
!
quiet
&&
msgno
<=
oldexists
&&
flagreport
[
msgno
])
{
for
(
i
=
0
;
i
<
MAX_USER_FLAGS
/
32
;
i
++
)
{
user_flags
[
i
]
=
USER_FLAGS
(
msgno
,
i
);
}
index_fetchflags
(
mailbox
,
msgno
,
SYSTEM_FLAGS
(
msgno
),
user_flags
,
LAST_UPDATED
(
msgno
));
if
(
usinguid
)
prot_printf
(
imapd_out
,
" UID %u"
,
UID
(
msgno
));
prot_printf
(
imapd_out
,
")
\r\n
"
);
}
}
}
else
if
(
seenflag
[
msgno
]
!=
newseen
)
{
dirty
++
;
}
}
if
(
!
examining
&&
oldexists
!=
imapd_exists
)
{
/* If just did a SELECT, record time of our reading the mailbox */
if
(
oldexists
==
-1
)
last_time
=
time
((
time_t
*
)
0
);
/* Update the \Recent high-water mark */
last_uid
=
mailbox
->
last_uid
;
dirty
++
;
}
/* If there's nothing to save back to the database, clean up and return */
if
(
!
dirty
)
{
seen_unlock
(
seendb
);
free
(
seenuids
);
seenuids
=
newseenuids
;
/* We might have deleted our last unseen message */
if
(
!
allseen
)
{
for
(
msgno
=
1
;
msgno
<=
imapd_exists
;
msgno
++
)
{
if
(
!
seenflag
[
msgno
])
break
;
}
if
(
msgno
==
imapd_exists
+
1
)
{
drop_seen
(
mailbox
->
name
,
imapd_userid
,
mailbox
->
last_uid
);
}
}
return
;
}
/* Build the seenuids string to save to the database */
start
=
1
;
inrange
=
1
;
newallseen
=
mailbox
->
last_uid
;
usecomma
=
0
;
savealloced
=
SAVEGROW
;
save
=
saveseenuids
=
xmalloc
(
savealloced
);
*
save
=
'\0'
;
for
(
msgno
=
1
;
msgno
<=
imapd_exists
;
msgno
++
)
{
uid
=
UID
(
msgno
);
if
(
seenflag
[
msgno
]
!=
inrange
)
{
newallseen
=
0
;
if
(
inrange
)
{
if
(
start
==
uid
-1
)
{
if
(
usecomma
++
)
*
save
++
=
','
;
sprintf
(
save
,
"%u"
,
start
);
save
+=
strlen
(
save
);
}
else
if
(
uid
>
1
)
{
if
(
usecomma
++
)
*
save
++
=
','
;
sprintf
(
save
,
"%u:"
,
start
);
save
+=
strlen
(
save
);
sprintf
(
save
,
"%u"
,
uid
-1
);
save
+=
strlen
(
save
);
}
inrange
=
0
;
}
else
{
start
=
uid
;
inrange
=
1
;
}
}
if
(
save
-
saveseenuids
>
savealloced
-
30
)
{
savealloced
+=
SAVEGROW
;
saveseenuids
=
xrealloc
(
saveseenuids
,
savealloced
);
save
=
saveseenuids
+
strlen
(
saveseenuids
);
}
}
/* Any messages between uid+1 and mailbox->last_uid get same disposition
* as uid
*/
uid
=
mailbox
->
last_uid
;
while
(
newnext
<=
uid
)
{
if
(
*
new
!=
':'
&&
!
newseen
&&
newnext
==
uid
)
{
newseen
=
1
;
break
;
}
else
{
newseen
=
(
*
new
==
':'
);
newnext
=
0
;
if
(
!*
new
)
{
newnext
=
mailbox
->
last_uid
+
1
;
neweof
++
;
}
else
new
++
;
while
(
isdigit
(
*
new
))
newnext
=
newnext
*
10
+
*
new
++
-
'0'
;
newnext
+=
newseen
;
}
}
if
(
inrange
)
{
/* Last message read. */
if
(
newseen
&&
newnext
>
uid
+
1
)
{
/* We parsed a range which went past uid. Include it in output. */
uid
=
newnext
-1
;
}
else
if
(
!
neweof
&&
!
newseen
&&
newnext
==
uid
+
1
)
{
/* We parsed ",N" where N is one past uid. Include it
* in the output range */
if
(
*
new
==
':'
)
{
/* There's a ":M" after the ",N". Parse/include that too. */
new
++
;
newnext
=
0
;
while
(
isdigit
(
*
new
))
newnext
=
newnext
*
10
+
*
new
++
-
'0'
;
}
uid
=
newnext
;
newseen
++
;
/* Forget we parsed ",N" */
}
if
(
!
start
&&
uid
>
1
)
start
=
1
;
if
(
usecomma
++
)
*
save
++
=
','
;
if
(
start
&&
start
!=
uid
)
{
sprintf
(
save
,
"%u:"
,
start
);
save
+=
strlen
(
save
);
}
sprintf
(
save
,
"%u"
,
uid
);
save
+=
strlen
(
save
);
if
(
!
neweof
&&
!
newseen
)
{
/* Parsed a lone number */
if
(
usecomma
++
)
*
save
++
=
','
;
sprintf
(
save
,
"%u"
,
newnext
);
save
+=
strlen
(
save
);
}
}
else
if
(
newseen
&&
newnext
>
uid
+
1
)
{
/* We parsed a range which went past uid. Include it in output */
if
(
usecomma
++
)
*
save
++
=
','
;
if
(
newnext
>
uid
+
2
)
{
sprintf
(
save
,
"%u:"
,
uid
+
1
);
save
+=
strlen
(
save
);
}
sprintf
(
save
,
"%u"
,
newnext
-1
);
save
+=
strlen
(
save
);
}
else
if
(
*
new
==
':'
)
{
/* Parsed first half of a range. Write it out */
if
(
usecomma
++
)
*
save
++
=
','
;
sprintf
(
save
,
"%u"
,
uid
+
1
);
save
+=
strlen
(
save
);
}
else
if
(
!
neweof
&&
!
newseen
)
{
/* Parsed a lone number */
if
(
usecomma
++
)
*
save
++
=
','
;
sprintf
(
save
,
"%u"
,
newnext
);
save
+=
strlen
(
save
);
}
if
(
*
new
)
{
if
(
save
-
saveseenuids
+
strlen
(
new
)
>=
savealloced
)
{
savealloced
+=
strlen
(
new
);
saveseenuids
=
xrealloc
(
saveseenuids
,
savealloced
);
save
=
saveseenuids
+
strlen
(
saveseenuids
);
}
strcpy
(
save
,
usecomma
?
new
:
new
+
1
);
}
/* Write the changes, clean up, and return */
r
=
seen_write
(
seendb
,
last_time
,
last_uid
,
saveseenuids
);
seen_unlock
(
seendb
);
free
(
seenuids
);
if
(
r
)
{
prot_printf
(
imapd_out
,
"* OK %s: %s
\r\n
"
,
error_message
(
IMAP_NO_CHECKSEEN
),
error_message
(
r
));
free
(
saveseenuids
);
seenuids
=
newseenuids
;
return
;
}
if
(
newallseen
)
drop_seen
(
mailbox
->
name
,
imapd_userid
,
mailbox
->
last_uid
);
else
if
(
allseen
==
mailbox
->
last_uid
)
{
drop_seen
(
mailbox
->
name
,
imapd_userid
,
0
);
}
free
(
newseenuids
);
seenuids
=
saveseenuids
;
}
/*
* Perform a FETCH-related command on a sequence.
*/
index_fetch
(
mailbox
,
sequence
,
usinguid
,
fetchargs
)
struct
mailbox
*
mailbox
;
char
*
sequence
;
int
usinguid
;
struct
fetchargs
*
fetchargs
;
{
index_forsequence
(
mailbox
,
sequence
,
usinguid
,
index_fetchreply
,
(
char
*
)
fetchargs
);
}
/*
* Perform a STORE command on a sequence
*/
int
index_store
(
mailbox
,
sequence
,
usinguid
,
storeargs
,
flag
,
nflags
)
struct
mailbox
*
mailbox
;
char
*
sequence
;
int
usinguid
;
struct
storeargs
*
storeargs
;
char
**
flag
;
int
nflags
;
{
int
i
,
r
,
userflag
,
emptyflag
;
int
writeheader
=
0
;
int
newflag
[
MAX_USER_FLAGS
];
long
myrights
=
mailbox
->
myrights
;
/* Handle simple case of just changing /Seen */
if
(
storeargs
->
operation
!=
STORE_REPLACE
&&
!
storeargs
->
system_flags
&&
!
nflags
)
{
if
(
!
storeargs
->
seen
)
return
0
;
/* Nothing to change */
if
(
!
(
myrights
&
ACL_SEEN
))
return
IMAP_PERMISSION_DENIED
;
storeargs
->
usinguid
=
usinguid
;
index_forsequence
(
mailbox
,
sequence
,
usinguid
,
index_storeseen
,
(
char
*
)
storeargs
);
return
0
;
}
mailbox_read_acl
(
mailbox
);
myrights
&=
mailbox
->
myrights
;
/* First pass at checking permission */
if
((
storeargs
->
seen
&&
!
(
myrights
&
ACL_SEEN
))
||
((
storeargs
->
system_flags
&
FLAG_DELETED
)
&&
!
(
myrights
&
ACL_DELETE
))
||
(((
storeargs
->
system_flags
&
~
FLAG_DELETED
)
||
nflags
)
&&
!
(
myrights
&
ACL_WRITE
)))
{
mailbox
->
myrights
=
myrights
;
return
IMAP_PERMISSION_DENIED
;
}
/* Check to see if we have to add new user flags */
for
(
userflag
=
0
;
userflag
<
MAX_USER_FLAGS
;
userflag
++
)
newflag
[
userflag
]
=
0
;
for
(
i
=
0
;
i
<
nflags
;
i
++
)
{
emptyflag
=
-1
;
for
(
userflag
=
0
;
userflag
<
MAX_USER_FLAGS
;
userflag
++
)
{
if
(
mailbox
->
flagname
[
userflag
])
{
if
(
!
strcasecmp
(
flag
[
i
],
mailbox
->
flagname
[
userflag
]))
break
;
}
else
if
(
!
newflag
[
userflag
]
&&
emptyflag
==
-1
)
{
emptyflag
=
userflag
;
}
}
if
(
userflag
==
MAX_USER_FLAGS
)
{
if
(
emptyflag
==
-1
)
{
return
IMAP_USERFLAG_EXHAUSTED
;
}
newflag
[
emptyflag
]
=
1
;
writeheader
++
;
}
}
/* Add the new user flags */
if
(
writeheader
)
{
r
=
mailbox_lock_header
(
mailbox
);
if
(
r
)
return
r
;
/*
* New flags might have been assigned since we last looked
* Do the assignment again.
*/
for
(
userflag
=
0
;
userflag
<
MAX_USER_FLAGS
;
userflag
++
)
newflag
[
userflag
]
=
0
;
for
(
i
=
0
;
i
<
nflags
;
i
++
)
{
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
;
}
}
if
(
userflag
==
MAX_USER_FLAGS
)
{
if
(
emptyflag
==
-1
)
{
mailbox_unlock_header
(
mailbox
);
mailbox
->
myrights
=
myrights
;
/* Undo the new assignments */
for
(
userflag
=
0
;
userflag
<
MAX_USER_FLAGS
;
userflag
++
)
{
if
(
newflag
[
userflag
]
&&
mailbox
->
flagname
[
userflag
])
{
free
(
mailbox
->
flagname
[
userflag
]);
mailbox
->
flagname
[
userflag
]
=
0
;
}
}
/* Tell client about new flags we read while looking */
index_listflags
(
mailbox
);
return
IMAP_USERFLAG_EXHAUSTED
;
}
mailbox
->
flagname
[
emptyflag
]
=
strsave
(
flag
[
i
]);
}
}
/* Tell client about new flags */
index_listflags
(
mailbox
);
r
=
mailbox_write_header
(
mailbox
);
mailbox_unlock_header
(
mailbox
);
mailbox
->
myrights
=
myrights
;
if
(
r
)
return
r
;
}
/* Not reading header anymore--can put back our working ACL */
mailbox
->
myrights
=
myrights
;
/* Now we know all user flags are in the mailbox header, find the bits */
for
(
i
=
0
;
i
<
nflags
;
i
++
)
{
for
(
userflag
=
0
;
userflag
<
MAX_USER_FLAGS
;
userflag
++
)
{
if
(
mailbox
->
flagname
[
userflag
])
{
if
(
!
strcasecmp
(
flag
[
i
],
mailbox
->
flagname
[
userflag
]))
break
;
}
}
assert
(
userflag
!=
MAX_USER_FLAGS
);
storeargs
->
user_flags
[
userflag
/
32
]
|=
1
<<
(
userflag
&
31
);
}
storeargs
->
update_time
=
time
((
time_t
*
)
0
);
storeargs
->
usinguid
=
usinguid
;
r
=
mailbox_lock_index
(
mailbox
);
if
(
r
)
return
r
;
r
=
index_forsequence
(
mailbox
,
sequence
,
usinguid
,
index_storeflag
,
(
char
*
)
storeargs
);
mailbox_unlock_index
(
mailbox
);
/* Refresh the index file, for systems without mmap() */
map_refresh
(
fileno
(
mailbox
->
index
),
&
index_base
,
&
index_len
,
start_offset
+
imapd_exists
*
record_size
,
"index"
,
mailbox
->
name
);
return
r
;
}
/*
* Performs a SEARCH command
*/
index_search
(
mailbox
,
searchargs
,
usinguid
)
struct
mailbox
*
mailbox
;
struct
searchargs
*
searchargs
;
int
usinguid
;
{
unsigned
msgno
;
FILE
*
msgfile
=
0
;
prot_printf
(
imapd_out
,
"* SEARCH"
);
for
(
msgno
=
1
;
msgno
<=
imapd_exists
;
msgno
++
)
{
if
(
index_search_evaluate
(
mailbox
,
searchargs
,
msgno
,
&
msgfile
))
{
prot_printf
(
imapd_out
,
" %u"
,
usinguid
?
UID
(
msgno
)
:
msgno
);
}
if
(
msgfile
)
{
fclose
(
msgfile
);
msgfile
=
0
;
}
}
prot_printf
(
imapd_out
,
"
\r\n
"
);
}
/*
* Performs a COPY command
*/
int
index_copy
(
mailbox
,
sequence
,
usinguid
,
name
)
struct
mailbox
*
mailbox
;
char
*
sequence
;
int
usinguid
;
char
*
name
;
{
static
struct
copyargs
copyargs
;
int
i
;
unsigned
long
totalsize
=
0
;
int
r
;
struct
mailbox
append_mailbox
;
copyargs
.
nummsg
=
0
;
index_forsequence
(
mailbox
,
sequence
,
usinguid
,
index_copysetup
,
(
char
*
)
&
copyargs
);
for
(
i
=
0
;
i
<
copyargs
.
nummsg
;
i
++
)
{
totalsize
+=
copyargs
.
copymsg
[
i
].
size
;
}
r
=
append_setup
(
&
append_mailbox
,
name
,
MAILBOX_FORMAT_NORMAL
,
ACL_INSERT
,
totalsize
);
if
(
r
)
return
r
;
r
=
append_copy
(
mailbox
,
&
append_mailbox
,
copyargs
.
nummsg
,
copyargs
.
copymsg
,
imapd_userid
);
mailbox_close
(
&
append_mailbox
);
return
r
;
}
/*
* Returns the msgno of the message with UID 'uid'.
* If no message with UID 'uid', returns the message with
* the higest UID not greater than 'uid'.
*/
int
index_finduid
(
uid
)
int
uid
;
{
int
low
=
1
,
high
=
imapd_exists
;
int
mid
,
miduid
;
while
(
low
<=
high
)
{
mid
=
(
high
-
low
)
/
2
+
low
;
miduid
=
UID
(
mid
);
if
(
miduid
==
uid
)
{
return
mid
;
}
else
if
(
miduid
>
uid
)
{
high
=
mid
-
1
;
}
else
{
low
=
mid
+
1
;
}
}
return
high
;
}
/*
* Call a function 'proc' on each message in 'sequence'. If 'usinguid'
* is nonzero, 'sequence' is interpreted as a sequence of UIDs instead
* of a sequence of msgnos. 'proc' is called with arguments 'mailbox',
* the msgno, and 'rock'. If any invocation of 'proc' returns nonzero,
* returns the first such returned value. Otherwise, returns zero.
*/
static
int
index_forsequence
(
mailbox
,
sequence
,
usinguid
,
proc
,
rock
)
struct
mailbox
*
mailbox
;
char
*
sequence
;
int
usinguid
;
int
(
*
proc
)();
char
*
rock
;
{
int
i
,
start
=
0
,
end
;
int
r
,
result
=
0
;
for
(;;)
{
if
(
isdigit
(
*
sequence
))
{
start
=
start
*
10
+
*
sequence
-
'0'
;
}
else
if
(
*
sequence
==
'*'
)
{
sequence
++
;
start
=
usinguid
?
UID
(
imapd_exists
)
:
imapd_exists
;
}
else
if
(
*
sequence
==
':'
)
{
end
=
0
;
sequence
++
;
while
(
isdigit
(
*
sequence
))
{
end
=
end
*
10
+
*
sequence
++
-
'0'
;
}
if
(
*
sequence
==
'*'
)
{
sequence
++
;
end
=
usinguid
?
UID
(
imapd_exists
)
:
imapd_exists
;
}
if
(
start
>
end
)
{
i
=
end
;
end
=
start
;
start
=
i
;
}
if
(
usinguid
)
{
i
=
index_finduid
(
start
);
if
(
!
i
||
start
!=
UID
(
i
))
i
++
;
start
=
i
;
end
=
index_finduid
(
end
);
}
if
(
start
<
1
)
start
=
1
;
if
(
end
>
imapd_exists
)
end
=
imapd_exists
;
for
(
i
=
start
;
i
<=
end
;
i
++
)
{
r
=
(
*
proc
)(
mailbox
,
i
,
rock
);
if
(
r
&&
!
result
)
result
=
r
;
}
start
=
0
;
if
(
!*
sequence
)
return
result
;
}
else
{
if
(
start
&&
usinguid
)
{
i
=
index_finduid
(
start
);
if
(
!
i
||
start
!=
UID
(
i
))
i
=
0
;
start
=
i
;
}
if
(
start
>
0
&&
start
<=
imapd_exists
)
{
r
=
(
*
proc
)(
mailbox
,
start
,
rock
);
if
(
r
&&
!
result
)
result
=
r
;
}
start
=
0
;
if
(
!*
sequence
)
return
result
;
}
sequence
++
;
}
}
/*
* Return nonzero iff 'num' is included in 'sequence'
*/
static
int
index_insequence
(
num
,
sequence
,
usinguid
)
int
num
;
char
*
sequence
;
int
usinguid
;
{
int
i
,
start
=
0
,
end
;
for
(;;)
{
if
(
isdigit
(
*
sequence
))
{
start
=
start
*
10
+
*
sequence
-
'0'
;
}
else
if
(
*
sequence
==
'*'
)
{
sequence
++
;
start
=
usinguid
?
UID
(
imapd_exists
)
:
imapd_exists
;
}
else
if
(
*
sequence
==
':'
)
{
end
=
0
;
sequence
++
;
while
(
isdigit
(
*
sequence
))
{
end
=
end
*
10
+
*
sequence
++
-
'0'
;
}
if
(
*
sequence
==
'*'
)
{
sequence
++
;
end
=
usinguid
?
UID
(
imapd_exists
)
:
imapd_exists
;
}
if
(
start
>
end
)
{
i
=
end
;
end
=
start
;
start
=
i
;
}
if
(
num
>=
start
&&
num
<=
end
)
return
1
;
start
=
0
;
if
(
!*
sequence
)
return
0
;
}
else
{
if
(
num
==
start
)
return
1
;
start
=
0
;
if
(
!*
sequence
)
return
0
;
}
sequence
++
;
}
}
/*
* Helper function to fetch data from a message file.
* Writes a quoted-string or literal containing data from
* 'msgfile', which is of format 'format', starting at 'offset'
* and containing 'size' octets. If 'start_octet' is nonzero, the data
* is further constrained by 'start_octet' and 'octet_count' as per
* the IMAP command PARTIAL.
*/
static
index_fetchmsg
(
msgfile
,
format
,
offset
,
size
,
start_octet
,
octet_count
)
FILE
*
msgfile
;
int
format
;
unsigned
offset
;
unsigned
size
;
unsigned
start_octet
;
unsigned
octet_count
;
{
char
buf
[
4096
],
*
p
;
int
n
;
/* partial fetch: adjust 'size', normalize 'start_octet' to be 0-based */
if
(
start_octet
)
{
if
(
size
<
start_octet
)
{
size
=
0
;
}
else
{
size
-=
start_octet
-
1
;
}
if
(
size
>
octet_count
)
size
=
octet_count
;
start_octet
--
;
}
/* If no data, output null quoted string */
if
(
!
msgfile
||
size
==
0
)
{
prot_printf
(
imapd_out
,
"
\"\"
"
);
return
;
}
/* Write size of literal */
prot_printf
(
imapd_out
,
"{%u}
\r\n
"
,
size
);
if
(
format
==
MAILBOX_FORMAT_NETNEWS
)
{
/* Have to fetch line-by-line, converting LF to CRLF */
fseek
(
msgfile
,
offset
,
0
);
while
(
size
)
{
if
(
!
fgets
(
buf
,
sizeof
(
buf
)
-1
,
msgfile
))
{
/* Read error, resynch client */
while
(
size
--
)
prot_putc
(
' '
,
imapd_out
);
return
;
}
p
=
buf
+
strlen
(
buf
);
if
(
p
[
-1
]
==
'\n'
)
{
p
[
-1
]
=
'\r'
;
*
p
++
=
'\n'
;
*
p
=
'\0'
;
}
n
=
p
-
buf
;
if
(
start_octet
>=
n
)
{
/* Skip over entire line */
start_octet
-=
n
;
}
else
{
/* Skip over (possibly zero) first part of line */
n
-=
start_octet
;
prot_write
(
imapd_out
,
buf
+
start_octet
,
n
);
start_octet
=
0
;
size
-=
n
;
}
}
}
else
{
/* Seek over PARTIAL constraint, do fetch in buf-size chunks */
offset
+=
start_octet
;
fseek
(
msgfile
,
offset
,
0
);
while
(
size
)
{
n
=
fread
(
buf
,
1
,
size
>
sizeof
(
buf
)
?
sizeof
(
buf
)
:
size
,
msgfile
);
if
(
n
==
0
)
{
/* Read error, resynch client */
while
(
size
--
)
prot_putc
(
' '
,
imapd_out
);
return
;
}
prot_write
(
imapd_out
,
buf
,
n
);
size
-=
n
;
}
}
}
/*
* Helper function to fetch a body section
*/
static
index_fetchsection
(
msgfile
,
format
,
section
,
cacheitem
,
start_octet
,
octet_count
)
FILE
*
msgfile
;
int
format
;
char
*
section
;
char
*
cacheitem
;
int
start_octet
;
int
octet_count
;
{
char
*
p
;
int
skip
;
cacheitem
+=
4
;
p
=
section
;
while
(
*
p
)
{
skip
=
0
;
while
(
isdigit
(
*
p
))
skip
=
skip
*
10
+
*
p
++
-
'0'
;
if
(
*
p
==
'.'
)
p
++
;
/* section 0 only allowed on tail */
if
(
!
skip
&&
*
p
)
goto
badpart
;
/* section number too large */
if
(
skip
>=
CACHE_ITEM_BIT32
(
cacheitem
))
goto
badpart
;
if
(
*
p
)
{
cacheitem
+=
CACHE_ITEM_BIT32
(
cacheitem
)
*
3
*
4
+
4
;
while
(
--
skip
)
{
if
(
CACHE_ITEM_BIT32
(
cacheitem
)
>
0
)
{
skip
+=
CACHE_ITEM_BIT32
(
cacheitem
)
-1
;
cacheitem
+=
CACHE_ITEM_BIT32
(
cacheitem
)
*
3
*
4
;
}
cacheitem
+=
4
;
}
}
}
cacheitem
+=
skip
*
3
*
4
+
4
;
if
(
CACHE_ITEM_BIT32
(
cacheitem
+
4
)
==
-1
)
goto
badpart
;
index_fetchmsg
(
msgfile
,
format
,
CACHE_ITEM_BIT32
(
cacheitem
),
CACHE_ITEM_BIT32
(
cacheitem
+
4
),
start_octet
,
octet_count
);
return
;
badpart
:
prot_printf
(
imapd_out
,
"NIL"
);
}
/*
* Helper function to read a header section into a static buffer
*/
static
char
*
index_readheader
(
msgfile
,
format
,
size
)
FILE
*
msgfile
;
int
format
;
unsigned
size
;
{
static
char
*
buf
;
static
int
bufsize
;
int
n
,
left
;
char
*
p
;
if
(
bufsize
<
size
+
2
)
{
bufsize
=
size
+
100
;
buf
=
xrealloc
(
buf
,
bufsize
);
}
if
(
format
==
MAILBOX_FORMAT_NETNEWS
)
{
left
=
size
;
p
=
buf
;
while
(
left
>
0
)
{
if
(
!
fgets
(
p
,
left
+
1
,
msgfile
))
{
*
p
=
'\0'
;
break
;
}
left
-=
strlen
(
p
);
p
=
p
+
strlen
(
p
);
if
(
p
[
-1
]
==
'\n'
)
{
p
[
-1
]
=
'\r'
;
*
p
++
=
'\n'
;
*
p
=
'\0'
;
left
--
;
}
}
}
else
{
n
=
fread
(
buf
,
1
,
size
,
msgfile
);
buf
[
n
]
=
'\0'
;
}
return
buf
;
}
/*
* Prune the header section in buf to include only those headers
* listed in headers or (if headers_not is non-empty) those headers
* not in headers_not.
*/
static
index_pruneheader
(
buf
,
headers
,
headers_not
)
char
*
buf
;
struct
strlist
*
headers
;
struct
strlist
*
headers_not
;
{
char
*
p
,
*
colon
,
*
nextheader
;
int
goodheader
;
char
*
endlastgood
=
buf
;
struct
strlist
*
l
;
p
=
buf
;
while
(
*
p
&&
*
p
!=
'\r'
)
{
colon
=
strchr
(
p
,
':'
);
if
(
colon
&&
headers_not
)
{
goodheader
=
1
;
for
(
l
=
headers_not
;
l
;
l
=
l
->
next
)
{
if
(
colon
-
p
==
strlen
(
l
->
s
)
&&
!
strncasecmp
(
p
,
l
->
s
,
colon
-
p
))
{
goodheader
=
0
;
break
;
}
}
}
else
{
goodheader
=
0
;
}
if
(
colon
)
{
for
(
l
=
headers
;
l
;
l
=
l
->
next
)
{
if
(
colon
-
p
==
strlen
(
l
->
s
)
&&
!
strncasecmp
(
p
,
l
->
s
,
colon
-
p
))
{
goodheader
=
1
;
break
;
}
}
}
nextheader
=
p
;
do
{
nextheader
=
strchr
(
nextheader
,
'\n'
);
if
(
nextheader
)
nextheader
++
;
else
nextheader
=
p
+
strlen
(
p
);
}
while
(
*
nextheader
==
' '
||
*
nextheader
==
'\t'
);
if
(
goodheader
)
{
if
(
endlastgood
!=
p
)
{
strcpy
(
endlastgood
,
p
);
nextheader
-=
p
-
endlastgood
;
}
endlastgood
=
nextheader
;
}
p
=
nextheader
;
}
*
endlastgood
=
'\0'
;
}
/*
* Handle a FETCH RFC822.HEADER.LINES or RFC822.HEADER.LINES.NOT
* that can't use the cacheheaders in cyrus.cache
*/
static
index_fetchheader
(
msgfile
,
format
,
size
,
headers
,
headers_not
)
FILE
*
msgfile
;
int
format
;
unsigned
size
;
struct
strlist
*
headers
;
struct
strlist
*
headers_not
;
{
char
*
buf
;
/* If no data, output null quoted string */
if
(
!
msgfile
)
{
prot_printf
(
imapd_out
,
"
\"\"
"
);
return
;
}
rewind
(
msgfile
);
buf
=
index_readheader
(
msgfile
,
format
,
size
);
index_pruneheader
(
buf
,
headers
,
headers_not
);
size
=
strlen
(
buf
);
prot_printf
(
imapd_out
,
"{%u}
\r\n
%s
\r\n
"
,
size
+
2
,
buf
);
}
/*
* Handle a FETCH RFC822.HEADER.LINES that can use the
* cacheheaders in cyrus.cache
*/
static
index_fetchcacheheader
(
msgno
,
headers
)
unsigned
msgno
;
struct
strlist
*
headers
;
{
static
char
*
buf
;
static
int
bufsize
;
char
*
cacheitem
;
unsigned
size
;
cacheitem
=
cache_base
+
CACHE_OFFSET
(
msgno
);
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip envelope */
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip bodystructure */
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip body */
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip section */
size
=
CACHE_ITEM_LEN
(
cacheitem
);
if
(
bufsize
<
size
+
2
)
{
bufsize
=
size
+
100
;
buf
=
xrealloc
(
buf
,
bufsize
);
}
bcopy
(
cacheitem
+
4
,
buf
,
size
);
buf
[
size
]
=
'\0'
;
index_pruneheader
(
buf
,
headers
,
0
);
size
=
strlen
(
buf
);
prot_printf
(
imapd_out
,
"{%u}
\r\n
%s
\r\n
"
,
size
+
2
,
buf
);
}
/*
* Send a * FLAGS response.
*/
static
int
index_listflags
(
mailbox
)
struct
mailbox
*
mailbox
;
{
int
i
;
int
cancreate
=
0
;
char
sepchar
=
'('
;
prot_printf
(
imapd_out
,
"* FLAGS (
\\
Answered
\\
Flagged
\\
Draft
\\
Deleted
\\
Seen"
);
for
(
i
=
0
;
i
<
MAX_USER_FLAGS
;
i
++
)
{
if
(
mailbox
->
flagname
[
i
])
{
prot_printf
(
imapd_out
,
" %s"
,
mailbox
->
flagname
[
i
]);
}
else
cancreate
++
;
}
prot_printf
(
imapd_out
,
")
\r\n
* OK [PERMANENTFLAGS "
);
if
(
mailbox
->
myrights
&
ACL_WRITE
)
{
prot_printf
(
imapd_out
,
"%c
\\
Answered
\\
Flagged
\\
Draft"
,
sepchar
);
sepchar
=
' '
;
}
if
(
mailbox
->
myrights
&
ACL_DELETE
)
{
prot_printf
(
imapd_out
,
"%c
\\
Deleted"
,
sepchar
);
sepchar
=
' '
;
}
if
(
mailbox
->
myrights
&
ACL_SEEN
)
{
prot_printf
(
imapd_out
,
"%c
\\
Seen"
,
sepchar
);
sepchar
=
' '
;
}
if
(
mailbox
->
myrights
&
ACL_WRITE
)
{
for
(
i
=
0
;
i
<
MAX_USER_FLAGS
;
i
++
)
{
if
(
mailbox
->
flagname
[
i
])
{
prot_printf
(
imapd_out
,
" %s"
,
mailbox
->
flagname
[
i
]);
}
}
if
(
cancreate
)
{
prot_printf
(
imapd_out
,
"
\\
*"
);
}
}
if
(
sepchar
==
'('
)
prot_printf
(
imapd_out
,
"("
);
prot_printf
(
imapd_out
,
")]
\r\n
"
);
}
/*
* Helper function to send * FETCH (FLAGS data.
* Does not send the terminating close paren or CRLF.
* Also sends preceeding * FLAGS if necessary.
*/
static
int
index_fetchflags
(
mailbox
,
msgno
,
system_flags
,
user_flags
,
last_updated
)
struct
mailbox
*
mailbox
;
unsigned
msgno
;
bit32
system_flags
;
bit32
user_flags
[
MAX_USER_FLAGS
/
32
];
time_t
last_updated
;
{
int
sepchar
=
'('
;
unsigned
flag
;
bit32
flagmask
;
for
(
flag
=
0
;
flag
<
MAX_USER_FLAGS
;
flag
++
)
{
if
((
flag
&
31
)
==
0
)
{
flagmask
=
user_flags
[
flag
/
32
];
}
if
(
!
mailbox
->
flagname
[
flag
]
&&
(
flagmask
&
(
1
<<
(
flag
&
31
))))
{
mailbox_read_header
(
mailbox
);
index_listflags
(
mailbox
);
break
;
}
}
prot_printf
(
imapd_out
,
"* %u FETCH (FLAGS "
,
msgno
);
if
(
msgno
>
lastnotrecent
)
{
prot_printf
(
imapd_out
,
"%c
\\
Recent"
,
sepchar
);
sepchar
=
' '
;
}
if
(
system_flags
&
FLAG_ANSWERED
)
{
prot_printf
(
imapd_out
,
"%c
\\
Answered"
,
sepchar
);
sepchar
=
' '
;
}
if
(
system_flags
&
FLAG_FLAGGED
)
{
prot_printf
(
imapd_out
,
"%c
\\
Flagged"
,
sepchar
);
sepchar
=
' '
;
}
if
(
system_flags
&
FLAG_DRAFT
)
{
prot_printf
(
imapd_out
,
"%c
\\
Draft"
,
sepchar
);
sepchar
=
' '
;
}
if
(
system_flags
&
FLAG_DELETED
)
{
prot_printf
(
imapd_out
,
"%c
\\
Deleted"
,
sepchar
);
sepchar
=
' '
;
}
if
(
seenflag
[
msgno
])
{
prot_printf
(
imapd_out
,
"%c
\\
Seen"
,
sepchar
);
sepchar
=
' '
;
}
for
(
flag
=
0
;
flag
<
MAX_USER_FLAGS
;
flag
++
)
{
if
((
flag
&
31
)
==
0
)
{
flagmask
=
user_flags
[
flag
/
32
];
}
if
(
mailbox
->
flagname
[
flag
]
&&
(
flagmask
&
(
1
<<
(
flag
&
31
))))
{
prot_printf
(
imapd_out
,
"%c%s"
,
sepchar
,
mailbox
->
flagname
[
flag
]);
sepchar
=
' '
;
}
}
if
(
sepchar
==
'('
)
prot_putc
(
'('
,
imapd_out
);
prot_putc
(
')'
,
imapd_out
);
flagreport
[
msgno
]
=
last_updated
;
}
/*
* Helper function to send requested * FETCH data for a message
*/
static
int
index_fetchreply
(
mailbox
,
msgno
,
rock
)
struct
mailbox
*
mailbox
;
int
msgno
;
char
*
rock
;
{
struct
fetchargs
*
fetchargs
=
(
struct
fetchargs
*
)
rock
;
int
fetchitems
=
fetchargs
->
fetchitems
;
FILE
*
msgfile
=
0
;
int
sepchar
;
int
i
;
bit32
user_flags
[
MAX_USER_FLAGS
/
32
];
char
*
cacheitem
;
struct
strlist
*
section
;
/* Open the message file if we're going to need it */
if
((
fetchitems
&
(
FETCH_HEADER
|
FETCH_TEXT
|
FETCH_RFC822
|
FETCH_UNCACHEDHEADER
))
||
fetchargs
->
bodysections
||
fetchargs
->
headers_not
)
{
msgfile
=
fopen
(
mailbox_message_fname
(
mailbox
,
UID
(
msgno
)),
"r"
);
if
(
!
msgfile
)
{
prot_printf
(
imapd_out
,
"* OK "
);
prot_printf
(
imapd_out
,
error_message
(
IMAP_NO_MSGGONE
),
msgno
);
prot_printf
(
imapd_out
,
"
\r\n
"
);
}
}
/* set the \Seen flag if necessary */
if
(
fetchitems
&
FETCH_SETSEEN
)
{
if
(
!
seenflag
[
msgno
]
&&
(
mailbox
->
myrights
&
ACL_SEEN
))
{
seenflag
[
msgno
]
=
1
;
fetchitems
|=
FETCH_FLAGS
;
}
}
if
(
fetchitems
&
FETCH_FLAGS
)
{
for
(
i
=
0
;
i
<
MAX_USER_FLAGS
/
32
;
i
++
)
{
user_flags
[
i
]
=
USER_FLAGS
(
msgno
,
i
);
}
index_fetchflags
(
mailbox
,
msgno
,
SYSTEM_FLAGS
(
msgno
),
user_flags
,
LAST_UPDATED
(
msgno
));
sepchar
=
' '
;
}
else
{
prot_printf
(
imapd_out
,
"* %u FETCH "
,
msgno
);
sepchar
=
'('
;
}
if
(
fetchitems
&
FETCH_UID
)
{
prot_printf
(
imapd_out
,
"%cUID %u"
,
sepchar
,
UID
(
msgno
));
sepchar
=
' '
;
}
if
(
fetchitems
&
FETCH_INTERNALDATE
)
{
time_t
msgdate
=
INTERNALDATE
(
msgno
);
struct
tm
*
tm
=
localtime
(
&
msgdate
);
long
gmtoff
=
gmtoff_of
(
tm
,
msgdate
);
int
gmtnegative
=
0
;
static
char
*
monthname
[]
=
{
"Jan"
,
"Feb"
,
"Mar"
,
"Apr"
,
"May"
,
"Jun"
,
"Jul"
,
"Aug"
,
"Sep"
,
"Oct"
,
"Nov"
,
"Dec"
};
char
datebuf
[
30
];
if
(
tm
->
tm_year
<
80
)
{
abort
();
}
if
(
gmtoff
<
0
)
{
gmtoff
=
-
gmtoff
;
gmtnegative
=
1
;
}
gmtoff
/=
60
;
sprintf
(
datebuf
,
"%2u-%s-%u %.2u:%.2u:%.2u %c%.2u%.2u"
,
tm
->
tm_mday
,
monthname
[
tm
->
tm_mon
],
tm
->
tm_year
+
1900
,
tm
->
tm_hour
,
tm
->
tm_min
,
tm
->
tm_sec
,
gmtnegative
?
'-'
:
'+'
,
gmtoff
/
60
,
gmtoff
%
60
);
prot_printf
(
imapd_out
,
"%cINTERNALDATE
\"
%s
\"
"
,
sepchar
,
datebuf
);
sepchar
=
' '
;
}
if
(
fetchitems
&
FETCH_SIZE
)
{
prot_printf
(
imapd_out
,
"%cRFC822.SIZE %u"
,
sepchar
,
SIZE
(
msgno
));
sepchar
=
' '
;
}
if
(
fetchitems
&
FETCH_ENVELOPE
)
{
prot_printf
(
imapd_out
,
"%cENVELOPE "
,
sepchar
);
sepchar
=
' '
;
cacheitem
=
cache_base
+
CACHE_OFFSET
(
msgno
);
prot_write
(
imapd_out
,
cacheitem
+
4
,
CACHE_ITEM_LEN
(
cacheitem
));
}
if
(
fetchitems
&
FETCH_BODYSTRUCTURE
)
{
prot_printf
(
imapd_out
,
"%cBODYSTRUCTURE "
,
sepchar
);
sepchar
=
' '
;
cacheitem
=
cache_base
+
CACHE_OFFSET
(
msgno
);
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip envelope */
prot_write
(
imapd_out
,
cacheitem
+
4
,
CACHE_ITEM_LEN
(
cacheitem
));
}
if
(
fetchitems
&
FETCH_BODY
)
{
prot_printf
(
imapd_out
,
"%cBODY "
,
sepchar
);
sepchar
=
' '
;
cacheitem
=
cache_base
+
CACHE_OFFSET
(
msgno
);
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip envelope */
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip bodystructure */
prot_write
(
imapd_out
,
cacheitem
+
4
,
CACHE_ITEM_LEN
(
cacheitem
));
}
if
(
fetchitems
&
FETCH_HEADER
)
{
prot_printf
(
imapd_out
,
"%cRFC822.HEADER "
,
sepchar
);
sepchar
=
' '
;
index_fetchmsg
(
msgfile
,
mailbox
->
format
,
0
,
HEADER_SIZE
(
msgno
),
fetchargs
->
start_octet
,
fetchargs
->
octet_count
);
}
else
if
((
fetchitems
&
FETCH_UNCACHEDHEADER
)
||
fetchargs
->
headers_not
)
{
prot_printf
(
imapd_out
,
"%cRFC822.HEADER "
,
sepchar
);
sepchar
=
' '
;
index_fetchheader
(
msgfile
,
mailbox
->
format
,
HEADER_SIZE
(
msgno
),
fetchargs
->
headers
,
fetchargs
->
headers_not
);
}
else
if
(
fetchargs
->
headers
)
{
prot_printf
(
imapd_out
,
"%cRFC822.HEADER "
,
sepchar
);
sepchar
=
' '
;
index_fetchcacheheader
(
msgno
,
fetchargs
->
headers
);
}
if
(
fetchitems
&
FETCH_TEXT
)
{
prot_printf
(
imapd_out
,
"%cRFC822.TEXT "
,
sepchar
);
sepchar
=
' '
;
index_fetchmsg
(
msgfile
,
mailbox
->
format
,
CONTENT_OFFSET
(
msgno
),
SIZE
(
msgno
)
-
HEADER_SIZE
(
msgno
),
fetchargs
->
start_octet
,
fetchargs
->
octet_count
);
}
if
(
fetchitems
&
FETCH_RFC822
)
{
prot_printf
(
imapd_out
,
"%cRFC822 "
,
sepchar
);
sepchar
=
' '
;
index_fetchmsg
(
msgfile
,
mailbox
->
format
,
0
,
SIZE
(
msgno
),
fetchargs
->
start_octet
,
fetchargs
->
octet_count
);
}
for
(
section
=
fetchargs
->
bodysections
;
section
;
section
=
section
->
next
)
{
prot_printf
(
imapd_out
,
"%cBODY[%s] "
,
sepchar
,
section
->
s
);
sepchar
=
' '
;
cacheitem
=
cache_base
+
CACHE_OFFSET
(
msgno
);
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip envelope */
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip bodystructure */
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip body */
index_fetchsection
(
msgfile
,
mailbox
->
format
,
section
->
s
,
cacheitem
,
fetchargs
->
start_octet
,
fetchargs
->
octet_count
);
}
prot_printf
(
imapd_out
,
")
\r\n
"
);
if
(
msgfile
)
fclose
(
msgfile
);
return
0
;
}
/*
* Helper function to perform a STORE command which only changes the
* \Seen flag.
*/
static
int
index_storeseen
(
mailbox
,
msgno
,
rock
)
struct
mailbox
*
mailbox
;
int
msgno
;
char
*
rock
;
{
struct
storeargs
*
storeargs
=
(
struct
storeargs
*
)
rock
;
int
val
=
(
storeargs
->
operation
==
STORE_ADD
)
?
1
:
0
;
int
i
;
bit32
user_flags
[
MAX_USER_FLAGS
/
32
];
if
(
seenflag
[
msgno
]
==
val
)
return
0
;
seenflag
[
msgno
]
=
val
;
if
(
storeargs
->
silent
)
return
0
;
for
(
i
=
0
;
i
<
MAX_USER_FLAGS
/
32
;
i
++
)
{
user_flags
[
i
]
=
USER_FLAGS
(
msgno
,
i
);
}
index_fetchflags
(
mailbox
,
msgno
,
SYSTEM_FLAGS
(
msgno
),
user_flags
,
LAST_UPDATED
(
msgno
));
if
(
storeargs
->
usinguid
)
{
prot_printf
(
imapd_out
,
" UID %u"
,
UID
(
msgno
));
}
prot_printf
(
imapd_out
,
")
\r\n
"
);
return
0
;
}
/*
* Helper function to perform a generalized STORE command
*/
static
int
index_storeflag
(
mailbox
,
msgno
,
rock
)
struct
mailbox
*
mailbox
;
int
msgno
;
char
*
rock
;
{
struct
storeargs
*
storeargs
=
(
struct
storeargs
*
)
rock
;
int
i
;
struct
index_record
record
;
int
uid
=
UID
(
msgno
);
int
low
=
1
,
high
=
mailbox
->
exists
;
int
mid
;
int
r
;
int
firsttry
=
1
;
int
seendirty
=
0
,
dirty
=
0
;
/* Change \Seen flag */
if
(
storeargs
->
operation
==
STORE_REPLACE
&&
(
mailbox
->
myrights
&
ACL_SEEN
))
{
if
(
seenflag
[
msgno
]
!=
storeargs
->
seen
)
seendirty
++
;
seenflag
[
msgno
]
=
storeargs
->
seen
;
}
else
if
(
storeargs
->
seen
)
{
i
=
(
storeargs
->
operation
==
STORE_ADD
)
?
1
:
0
;
if
(
seenflag
[
msgno
]
!=
i
)
seendirty
++
;
seenflag
[
msgno
]
=
i
;
}
/* Find index record */
while
(
low
<=
high
)
{
if
(
firsttry
&&
msgno
==
storeargs
->
last_msgno
+
1
)
{
/* Take "good" first guess */
mid
=
storeargs
->
last_found
+
1
;
if
(
mid
>
high
)
mid
=
high
;
}
else
{
mid
=
(
high
-
low
)
/
2
+
low
;
}
firsttry
=
0
;
r
=
mailbox_read_index_record
(
mailbox
,
mid
,
&
record
);
if
(
r
)
return
r
;
if
(
record
.
uid
==
uid
)
{
break
;
}
else
if
(
record
.
uid
>
uid
)
{
high
=
mid
-
1
;
}
else
{
low
=
mid
+
1
;
}
}
storeargs
->
last_msgno
=
msgno
;
storeargs
->
last_found
=
mid
;
if
(
low
>
high
)
{
/* Message was expunged. */
if
(
storeargs
->
usinguid
)
{
/* We're going to * n EXPUNGE it */
return
0
;
}
/* Fake setting the flags */
mid
=
0
;
storeargs
->
last_found
=
high
;
record
.
system_flags
=
SYSTEM_FLAGS
(
msgno
);
for
(
i
=
0
;
i
<
MAX_USER_FLAGS
/
32
;
i
++
)
{
record
.
user_flags
[
i
]
=
USER_FLAGS
(
msgno
,
i
);
}
}
if
(
storeargs
->
operation
==
STORE_REPLACE
)
{
if
(
!
(
mailbox
->
myrights
&
ACL_WRITE
))
{
record
.
system_flags
=
(
record
.
system_flags
&~
FLAG_DELETED
)
|
(
storeargs
->
system_flags
&
FLAG_DELETED
);
}
else
{
if
(
!
(
mailbox
->
myrights
&
ACL_DELETE
))
{
record
.
system_flags
=
(
record
.
system_flags
&
FLAG_DELETED
)
|
(
storeargs
->
system_flags
&~
FLAG_DELETED
);
}
else
{
record
.
system_flags
=
storeargs
->
system_flags
;
}
for
(
i
=
0
;
i
<
MAX_USER_FLAGS
/
32
;
i
++
)
{
record
.
user_flags
[
i
]
=
storeargs
->
user_flags
[
i
];
}
}
dirty
++
;
/* Don't try to be clever */
}
else
if
(
storeargs
->
operation
==
STORE_ADD
)
{
if
(
~
record
.
system_flags
&
storeargs
->
system_flags
)
dirty
++
;
record
.
system_flags
|=
storeargs
->
system_flags
;
for
(
i
=
0
;
i
<
MAX_USER_FLAGS
/
32
;
i
++
)
{
if
(
~
record
.
user_flags
[
i
]
&
storeargs
->
user_flags
[
i
])
dirty
++
;
record
.
user_flags
[
i
]
|=
storeargs
->
user_flags
[
i
];
}
}
else
{
/* STORE_REMOVE */
if
(
record
.
system_flags
&
storeargs
->
system_flags
)
dirty
++
;
record
.
system_flags
&=
~
storeargs
->
system_flags
;
for
(
i
=
0
;
i
<
MAX_USER_FLAGS
/
32
;
i
++
)
{
if
(
record
.
user_flags
[
i
]
&
storeargs
->
user_flags
[
i
])
dirty
++
;
record
.
user_flags
[
i
]
&=
~
storeargs
->
user_flags
[
i
];
}
}
if
(
dirty
||
seendirty
)
{
if
(
dirty
)
{
/* If .SILENT, assume client has updated their cache */
if
(
storeargs
->
silent
&&
flagreport
[
msgno
]
&&
flagreport
[
msgno
]
==
record
.
last_updated
)
{
flagreport
[
msgno
]
=
(
record
.
last_updated
>=
storeargs
->
update_time
)
?
record
.
last_updated
+
1
:
storeargs
->
update_time
;
}
record
.
last_updated
=
(
record
.
last_updated
>=
storeargs
->
update_time
)
?
record
.
last_updated
+
1
:
storeargs
->
update_time
;
}
if
(
!
storeargs
->
silent
)
{
index_fetchflags
(
mailbox
,
msgno
,
record
.
system_flags
,
record
.
user_flags
,
record
.
last_updated
);
if
(
storeargs
->
usinguid
)
{
prot_printf
(
imapd_out
,
" UID %u"
,
UID
(
msgno
));
}
prot_printf
(
imapd_out
,
")
\r\n
"
);
}
if
(
dirty
&&
mid
)
{
r
=
mailbox_write_index_record
(
mailbox
,
mid
,
&
record
);
if
(
r
)
return
r
;
}
}
return
0
;
}
/*
* Evaluate a searchargs structure on a msgno
*/
static
int
index_search_evaluate
(
mailbox
,
searchargs
,
msgno
,
msgfile
)
struct
mailbox
*
mailbox
;
struct
searchargs
*
searchargs
;
int
msgno
;
FILE
**
msgfile
;
{
int
i
;
struct
strlist
*
l
,
*
h
;
char
*
cacheitem
;
int
cachelen
;
struct
searchsub
*
s
;
if
(
searchargs
->
recent_set
&&
msgno
<=
lastnotrecent
)
return
0
;
if
(
searchargs
->
recent_unset
&&
msgno
>
lastnotrecent
)
return
0
;
if
(
searchargs
->
peruser_flags_set
&&
!
seenflag
[
msgno
])
return
0
;
if
(
searchargs
->
peruser_flags_unset
&&
seenflag
[
msgno
])
return
0
;
if
(
searchargs
->
smaller
&&
SIZE
(
msgno
)
>=
searchargs
->
smaller
)
return
0
;
if
(
searchargs
->
larger
&&
SIZE
(
msgno
)
<=
searchargs
->
larger
)
return
0
;
if
(
searchargs
->
after
&&
INTERNALDATE
(
msgno
)
<
searchargs
->
after
)
return
0
;
if
(
searchargs
->
before
&&
INTERNALDATE
(
msgno
)
>
searchargs
->
before
)
return
0
;
if
(
searchargs
->
sentafter
&&
SENTDATE
(
msgno
)
<
searchargs
->
sentafter
)
return
0
;
if
(
searchargs
->
sentbefore
&&
SENTDATE
(
msgno
)
>
searchargs
->
sentbefore
)
return
0
;
if
(
~
SYSTEM_FLAGS
(
msgno
)
&
searchargs
->
system_flags_set
)
return
0
;
if
(
SYSTEM_FLAGS
(
msgno
)
&
searchargs
->
system_flags_unset
)
return
0
;
for
(
i
=
0
;
i
<
MAX_USER_FLAGS
/
32
;
i
++
)
{
if
(
~
USER_FLAGS
(
msgno
,
i
)
&
searchargs
->
user_flags_set
[
i
])
return
0
;
if
(
USER_FLAGS
(
msgno
,
i
)
&
searchargs
->
user_flags_unset
[
i
])
return
0
;
}
for
(
l
=
searchargs
->
sequence
;
l
;
l
=
l
->
next
)
{
if
(
!
index_insequence
(
msgno
,
l
->
s
,
0
))
return
0
;
}
for
(
l
=
searchargs
->
uidsequence
;
l
;
l
=
l
->
next
)
{
if
(
!
index_insequence
(
UID
(
msgno
),
l
->
s
,
1
))
return
0
;
}
if
(
searchargs
->
from
||
searchargs
->
to
||
searchargs
->
cc
||
searchargs
->
bcc
||
searchargs
->
subject
)
{
cacheitem
=
cache_base
+
CACHE_OFFSET
(
msgno
);
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip envelope */
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip bodystructure */
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip body */
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip section */
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip cacheheaders */
cachelen
=
CACHE_ITEM_LEN
(
cacheitem
);
for
(
l
=
searchargs
->
from
;
l
;
l
=
l
->
next
)
{
if
(
!
charset_searchstring
(
l
->
s
,
l
->
p
,
cacheitem
+
4
,
cachelen
))
return
0
;
}
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip from */
cachelen
=
CACHE_ITEM_LEN
(
cacheitem
);
for
(
l
=
searchargs
->
to
;
l
;
l
=
l
->
next
)
{
if
(
!
charset_searchstring
(
l
->
s
,
l
->
p
,
cacheitem
+
4
,
cachelen
))
return
0
;
}
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip to */
cachelen
=
CACHE_ITEM_LEN
(
cacheitem
);
for
(
l
=
searchargs
->
cc
;
l
;
l
=
l
->
next
)
{
if
(
!
charset_searchstring
(
l
->
s
,
l
->
p
,
cacheitem
+
4
,
cachelen
))
return
0
;
}
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip cc */
cachelen
=
CACHE_ITEM_LEN
(
cacheitem
);
for
(
l
=
searchargs
->
bcc
;
l
;
l
=
l
->
next
)
{
if
(
!
charset_searchstring
(
l
->
s
,
l
->
p
,
cacheitem
+
4
,
cachelen
))
return
0
;
}
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip subject */
cachelen
=
CACHE_ITEM_LEN
(
cacheitem
);
for
(
l
=
searchargs
->
subject
;
l
;
l
=
l
->
next
)
{
if
(
!
charset_searchstring
(
l
->
s
,
l
->
p
,
cacheitem
+
4
,
cachelen
))
return
0
;
}
}
for
(
s
=
searchargs
->
sublist
;
s
;
s
=
s
->
next
)
{
if
(
index_search_evaluate
(
mailbox
,
s
->
sub1
,
msgno
,
msgfile
))
{
if
(
!
s
->
sub2
)
return
0
;
}
else
{
if
(
s
->
sub2
&&
!
index_search_evaluate
(
mailbox
,
s
->
sub2
,
msgno
,
msgfile
))
return
0
;
}
}
if
(
searchargs
->
body
||
searchargs
->
text
||
searchargs
->
header
)
{
if
(
!*
msgfile
)
{
*
msgfile
=
fopen
(
mailbox_message_fname
(
mailbox
,
UID
(
msgno
)),
"r"
);
if
(
!*
msgfile
)
return
0
;
}
h
=
searchargs
->
header_name
;
for
(
l
=
searchargs
->
header
;
l
;
(
l
=
l
->
next
),
(
h
=
h
->
next
))
{
if
(
!
index_searchheader
(
h
->
s
,
l
->
s
,
l
->
p
,
*
msgfile
,
mailbox
->
format
,
HEADER_SIZE
(
msgno
)))
return
0
;
}
cacheitem
=
cache_base
+
CACHE_OFFSET
(
msgno
);
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip envelope */
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip bodystructure */
cacheitem
=
CACHE_ITEM_NEXT
(
cacheitem
);
/* skip body */
for
(
l
=
searchargs
->
body
;
l
;
l
=
l
->
next
)
{
if
(
!
index_searchmsg
(
l
->
s
,
l
->
p
,
*
msgfile
,
mailbox
->
format
,
1
,
cacheitem
))
return
0
;
}
for
(
l
=
searchargs
->
text
;
l
;
l
=
l
->
next
)
{
if
(
!
index_searchmsg
(
l
->
s
,
l
->
p
,
*
msgfile
,
mailbox
->
format
,
0
,
cacheitem
))
return
0
;
}
}
return
1
;
}
/*
* Search part of a message for a substring
*/
static
int
index_searchmsg
(
substr
,
pat
,
msgfile
,
format
,
skipheader
,
cacheitem
)
char
*
substr
;
comp_pat
*
pat
;
FILE
*
msgfile
;
int
format
;
int
skipheader
;
char
*
cacheitem
;
{
int
partsleft
=
1
;
int
subparts
;
int
len
,
charset
,
encoding
;
char
*
p
;
cacheitem
+=
4
;
while
(
partsleft
--
)
{
subparts
=
CACHE_ITEM_BIT32
(
cacheitem
);
cacheitem
+=
4
;
if
(
subparts
)
{
partsleft
+=
subparts
-1
;
if
(
skipheader
)
{
skipheader
=
0
;
/* Only skip top-level message header */
}
else
{
len
=
CACHE_ITEM_BIT32
(
cacheitem
+
4
);
if
(
len
>
0
)
{
fseek
(
msgfile
,
CACHE_ITEM_BIT32
(
cacheitem
),
0
);
p
=
index_readheader
(
msgfile
,
format
,
len
);
p
=
charset_decode1522
(
p
);
if
(
charset_searchstring
(
substr
,
pat
,
p
,
strlen
(
p
)))
{
return
1
;
}
}
}
cacheitem
+=
3
*
4
;
while
(
--
subparts
)
{
len
=
CACHE_ITEM_BIT32
(
cacheitem
+
4
);
charset
=
CACHE_ITEM_BIT32
(
cacheitem
+
8
)
>>
16
;
encoding
=
CACHE_ITEM_BIT32
(
cacheitem
+
8
)
&
0xff
;
if
(
len
>
0
&&
charset
>=
0
&&
charset
<
0xffff
)
{
fseek
(
msgfile
,
CACHE_ITEM_BIT32
(
cacheitem
),
0
);
if
(
charset_searchfile
(
substr
,
pat
,
msgfile
,
format
==
MAILBOX_FORMAT_NETNEWS
,
len
,
charset
,
encoding
))
return
1
;
}
cacheitem
+=
3
*
4
;
}
}
}
return
0
;
}
/*
* Search named header of a message for a substring
*/
static
int
index_searchheader
(
name
,
substr
,
pat
,
msgfile
,
format
,
size
)
char
*
name
;
char
*
substr
;
comp_pat
*
pat
;
FILE
*
msgfile
;
int
format
;
int
size
;
{
char
*
p
;
static
struct
strlist
header
;
header
.
s
=
name
;
rewind
(
msgfile
);
p
=
index_readheader
(
msgfile
,
format
,
size
);
index_pruneheader
(
p
,
&
header
,
0
);
p
=
charset_decode1522
(
p
);
return
charset_searchstring
(
substr
,
pat
,
p
,
strlen
(
p
));
}
/*
* Helper function to set up arguments to append_copy()
*/
#define COPYARGSGROW 30
static
int
index_copysetup
(
mailbox
,
msgno
,
rock
)
struct
mailbox
*
mailbox
;
int
msgno
;
char
*
rock
;
{
struct
copyargs
*
copyargs
=
(
struct
copyargs
*
)
rock
;
int
flag
=
0
;
unsigned
userflag
;
bit32
flagmask
;
if
(
copyargs
->
nummsg
==
copyargs
->
msgalloc
)
{
copyargs
->
msgalloc
+=
COPYARGSGROW
;
copyargs
->
copymsg
=
(
struct
copymsg
*
)
xrealloc
((
char
*
)
copyargs
->
copymsg
,
copyargs
->
msgalloc
*
sizeof
(
struct
copymsg
));
}
copyargs
->
copymsg
[
copyargs
->
nummsg
].
uid
=
UID
(
msgno
);
copyargs
->
copymsg
[
copyargs
->
nummsg
].
internaldate
=
INTERNALDATE
(
msgno
);
copyargs
->
copymsg
[
copyargs
->
nummsg
].
sentdate
=
SENTDATE
(
msgno
);
copyargs
->
copymsg
[
copyargs
->
nummsg
].
size
=
SIZE
(
msgno
);
copyargs
->
copymsg
[
copyargs
->
nummsg
].
header_size
=
HEADER_SIZE
(
msgno
);
copyargs
->
copymsg
[
copyargs
->
nummsg
].
cache_begin
=
cache_base
+
CACHE_OFFSET
(
msgno
);
if
(
mailbox
->
format
!=
MAILBOX_FORMAT_NORMAL
)
{
/* Force copy and re-parse of message */
copyargs
->
copymsg
[
copyargs
->
nummsg
].
cache_len
=
0
;
}
else
if
(
msgno
<
imapd_exists
)
{
copyargs
->
copymsg
[
copyargs
->
nummsg
].
cache_len
=
CACHE_OFFSET
(
msgno
+
1
)
-
CACHE_OFFSET
(
msgno
);
}
else
{
copyargs
->
copymsg
[
copyargs
->
nummsg
].
cache_len
=
cache_end
-
CACHE_OFFSET
(
msgno
);
}
copyargs
->
copymsg
[
copyargs
->
nummsg
].
seen
=
seenflag
[
msgno
];
copyargs
->
copymsg
[
copyargs
->
nummsg
].
system_flags
=
SYSTEM_FLAGS
(
msgno
);
for
(
userflag
=
0
;
userflag
<
MAX_USER_FLAGS
;
userflag
++
)
{
if
((
userflag
&
31
)
==
0
)
{
flagmask
=
USER_FLAGS
(
msgno
,
userflag
/
32
);
}
if
(
!
mailbox
->
flagname
[
userflag
]
&&
(
flagmask
&
(
1
<<
(
userflag
&
31
))))
{
mailbox_read_header
(
mailbox
);
index_listflags
(
mailbox
);
break
;
}
}
for
(
userflag
=
0
;
userflag
<
MAX_USER_FLAGS
;
userflag
++
)
{
if
((
userflag
&
31
)
==
0
)
{
flagmask
=
USER_FLAGS
(
msgno
,
userflag
/
32
);
}
if
(
mailbox
->
flagname
[
userflag
]
&&
(
flagmask
&
(
1
<<
(
userflag
&
31
))))
{
copyargs
->
copymsg
[
copyargs
->
nummsg
].
flag
[
flag
++
]
=
mailbox
->
flagname
[
userflag
];
}
}
copyargs
->
copymsg
[
copyargs
->
nummsg
].
flag
[
flag
]
=
0
;
copyargs
->
nummsg
++
;
return
0
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, Apr 4, 1:46 AM (1 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18822008
Default Alt Text
index.c (52 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline