Files
bongo/src/agents/imap/fetch.c
T
2026-07-19 18:25:32 +02:00

3703 lines
141 KiB
C

/****************************************************************************
* <Novell-copyright>
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail, you
* may find current contact information at www.novell.com.
* </Novell-copyright>
****************************************************************************/
#include <config.h>
#include <xpl.h>
#include <logger.h>
#include <msgapi.h>
#include "imapd.h"
#include "binary.h"
BongoKeywordIndex *FetchFlagsAttIndex = NULL;
BongoKeywordIndex *FetchFlagsSoloIndex = NULL;
BongoKeywordIndex *FetchFlagsSectionIndex = NULL;
static long FetchFlagResponderBinary(void *param1, void *param2, void *param3);
char TopLevelMime[] = "Content-Type: message/rfc822\r\nContent-Tranfer-Encoding: 7bit\r\n";
unsigned long TopLevelMimeLen = sizeof(TopLevelMime) - 1;
static long
WriteClientBytes(ImapSession *session, const void *data, uint64_t length)
{
const unsigned char *cursor = data;
if (length > SIZE_MAX) return STATUS_FETCH_LIMIT;
while (length) {
int chunk = length > INT_MAX ? INT_MAX : (int)length;
if (ConnWrite(session->client.conn, cursor, chunk) != chunk)
return STATUS_ABORT;
cursor += chunk;
length -= (uint64_t)chunk;
}
return STATUS_CONTINUE;
}
static long
TransferNMAPDocument(ImapSession *session, uint64_t length)
{
while (length) {
int chunk = length > INT_MAX ? INT_MAX : (int)length;
if (ConnReadToConn(session->store.conn, session->client.conn, chunk) !=
chunk) return STATUS_ABORT;
length -= (uint64_t)chunk;
}
return NMAPReadCrLf(session->store.conn) == 2 ? STATUS_CONTINUE :
STATUS_ABORT;
}
static BOOL
FindRFC822Header(char *Header, char *SearchFor, char **Buffer, int BufSize)
{
char *NL, *ptr, *ptr2, *ptr3;
BOOL KeepGoing=TRUE;
int len;
ptr=Header;
while (KeepGoing) {
NL=strchr(ptr,0x0a);
if (NL) {
/* Check for line continuation, if yes, whack CR/LF and keep going */
if (IsWhiteSpace(*(NL+1))) {
*NL=' ';
NL[1]=' ';
if (*(NL-1)==0x0d) {
*(NL-1)=' ';
}
continue;
} else {
*NL='\0';
}
}
ptr2=strchr(ptr, ':');
if (ptr2) {
*ptr2='\0';
if (XplStrCaseCmp(ptr, SearchFor)==0) {
ptr3=ptr2+1;
while (IsWhiteSpace(*ptr3))
ptr3++;
len=strlen(ptr3);
if (!BufSize) {
*Buffer=MemMalloc(len+1);
if (!*Buffer)
return(FALSE);
} else {
if (len>BufSize) {
return(FALSE);
}
}
strncpy(*Buffer, ptr3, len);
(*Buffer)[len]='\0';
if ((ptr3=strrchr(*Buffer,0x0d))!=NULL)
*ptr3='\0';
if ((ptr3=strrchr(*Buffer,0x0a))!=NULL)
*ptr3='\0';
*ptr2=':';
if (NL)
*NL=0x0a;
// while ((ptr=strchr(*Buffer, '"'))!=0)
// memmove(ptr, ptr+1, strlen(ptr+1)+1);
return(TRUE);
}
*ptr2=':';
}
if (NL) {
*NL=0x0a;
ptr=NL+1;
} else {
KeepGoing=FALSE;
}
}
return(FALSE);
}
static long
SendRFC822Address(ImapSession *session, char *Address, char *Answer)
{
long ccode = 0;
RFC822AddressStruct *Current;
RFC822AddressStruct *Rfc822AddressList = NULL;
unsigned long Len;
UNUSED_PARAMETER(Answer);
RFC822ParseAddressList(&Rfc822AddressList, Address, ".MissingHostName.");
if (Rfc822AddressList) {
if (ConnWrite(session->client.conn, "(", 1) != -1) {
Current = Rfc822AddressList;
while (Current) {
if (Current->Personal) {
Len = strlen(Current->Personal);
if ((ccode = ConnWriteF(session->client.conn, "({%lu}\r\n", Len) != -1) &&
(ccode = ConnWrite(session->client.conn, Current->Personal, Len) != -1) &&
(ccode = ConnWrite(session->client.conn, " ", 1)) != -1) {
;
} else {
break;
}
} else {
if ((ccode = ConnWrite(session->client.conn, "(NIL ", 5)) != -1) {
;
} else {
break;
}
}
if (Current->ADL) {
if (((ccode = ConnWrite(session->client.conn, "\"", 1)) != -1) &&
((ccode = ConnWrite(session->client.conn, Current->ADL, strlen(Current->ADL))) != -1) &&
((ccode = ConnWrite(session->client.conn, "\" ", 2)) != -1)) {
;
} else {
break;
}
} else {
if ((ccode = ConnWrite(session->client.conn, "NIL ", 4)) != -1) {
;
} else {
break;
}
}
if (Current->Mailbox) {
if (((ccode = ConnWrite(session->client.conn, "\"", 1)) != -1) &&
((ccode = ConnWrite(session->client.conn, Current->Mailbox, strlen(Current->Mailbox))) != -1) &&
((ccode = ConnWrite(session->client.conn, "\" ", 2)) != -1)) {
;
} else {
break;
}
} else {
if ((ccode = ConnWrite(session->client.conn, "NIL ", 4)) != -1) {
;
} else {
break;
}
}
if (Current->Host) {
if (((ccode = ConnWrite(session->client.conn, "\"", 1)) != -1) &&
((ccode = ConnWrite(session->client.conn, Current->Host, strlen(Current->Host))) != -1) &&
((ccode = ConnWrite(session->client.conn, "\")", 2)) != -1)) {
;
} else {
break;
}
} else {
if ((ccode = ConnWrite(session->client.conn, "NIL)", 4)) != -1) {
;
} else {
break;
}
}
Current = Current->Next;
}
RFC822FreeAddressList(Rfc822AddressList);
if (ccode != -1) {
if (ConnWrite(session->client.conn, ") ", 2) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
RFC822FreeAddressList(Rfc822AddressList);
return(STATUS_ABORT);
}
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
static size_t
MakeRFC822Header(char *Header, size_t headerSize)
{
char *NL, *ptr, *ptr2, *start=Header;
BOOL StripLine, KeepGoing=TRUE;
ptr=Header;
Header[headerSize]='\0';
while (KeepGoing) {
StripLine=FALSE;
NL=strchr(ptr,0x0a);
if (NL)
*NL='\0';
if ((*ptr!='\t') && (*ptr!=' ')) {
ptr2=strchr(ptr, ':');
if (ptr2) {
*ptr2='\0';
if (strchr(ptr,' '))
StripLine=TRUE;
*ptr2=':';
} else {
if (NL)
StripLine=TRUE;
}
}
if (NL) {
*NL=0x0a;
ptr=NL+1;
} else {
KeepGoing=FALSE;
}
if (StripLine) {
if (NL) {
memmove(start, ptr, strlen(ptr)+1);
start=NL-ptr+start+1;
ptr=start;
} else {
*ptr='\0';
}
} else {
start=ptr;
if ((*ptr==0x0d) || (*ptr==0x0a))
KeepGoing=FALSE;
}
}
return strlen(Header);
}
__inline static char *
GetSubHeader(char *header, unsigned long headerSize, char **fields, unsigned long fieldCount, BOOL not, unsigned long *newHeaderLen)
{
char *newHeader;
char *start;
char *colon;
char *newLine;
char *dest;
unsigned long i;
unsigned long lineLen;
BOOL keepLine;
newHeader = MemMalloc(headerSize);
if (newHeader) {
start = header;
dest = newHeader;
*newHeaderLen = 0;
do {
colon = strchr(start, ':');
if (colon) {
*colon = '\0';
i = 0;
do {
if (XplStrCaseCmp(start, fields[i]) != 0) {
i++;
if (i < fieldCount) {
continue;
}
/* this is not a match */
keepLine = not;
break;
}
/* this line is a match */
keepLine = !not;
break;
} while (TRUE);
*colon = ':';
newLine = start;
do {
newLine = strchr(newLine, '\n');
if (newLine) {
if (IsWhiteSpace(*(newLine + 1))) {
newLine +=2;
continue;
}
}
break;
} while (TRUE);
if (newLine) {
newLine++;
if (keepLine) {
lineLen = newLine - start;
memcpy(dest + *newHeaderLen, start, lineLen);
*newHeaderLen += lineLen;
}
start = newLine;
continue;
}
}
newLine = strchr(start, '\n');
if (newLine) {
start = newLine + 1;
continue;
}
break;
} while(TRUE);
if (*newHeaderLen > 0) {
return(newHeader);
}
*newHeader = '\0';
return(newHeader);
}
return(NULL);
}
static BOOL
ParseMIMEAtom(const char **cursor, char *output, size_t output_size)
{
const char *start;
size_t length;
while (**cursor == ' ') (*cursor)++;
start = *cursor;
while (**cursor && **cursor != ' ') (*cursor)++;
length = (size_t)(*cursor - start);
if (!length || !output_size) return FALSE;
if (length >= output_size) length = output_size - 1;
memcpy(output, start, length);
output[length] = '\0';
return TRUE;
}
static BOOL
ParseMIMEUInt64(const char **cursor, uint64_t *value)
{
char *end;
uintmax_t parsed;
while (**cursor == ' ') (*cursor)++;
if (!isdigit((unsigned char)**cursor)) return FALSE;
errno = 0;
parsed = strtoumax(*cursor, &end, 10);
if (errno == ERANGE || end == *cursor || parsed > UINT64_MAX) return FALSE;
if (*end && *end != ' ') return FALSE;
*cursor = end;
*value = (uint64_t)parsed;
return TRUE;
}
static BOOL
ParseMIMEDLine(const char *mime, char *type, size_t type_size,
char *subtype, size_t subtype_size, char *charset,
size_t charset_size, char *encoding, size_t encoding_size,
char *name, size_t name_size, uint64_t *header_start,
uint64_t *header_size, uint64_t *start_pos, uint64_t *size,
uint64_t *message_header_size, uint64_t *lines)
{
const char *cursor = mime;
const char *name_start;
size_t name_length;
if (!mime || !type || !subtype || !charset || !encoding || !name ||
!header_start || !header_size || !start_pos || !size ||
!message_header_size || !lines) return FALSE;
if (!ParseMIMEAtom(&cursor, type, type_size + 1) ||
!ParseMIMEAtom(&cursor, subtype, subtype_size + 1) ||
!ParseMIMEAtom(&cursor, charset, charset_size + 1) ||
!ParseMIMEAtom(&cursor, encoding, encoding_size + 1)) return FALSE;
while (*cursor == ' ') cursor++;
if (*cursor++ != '"') return FALSE;
name_start = cursor;
while (*cursor && *cursor != '"') cursor++;
if (*cursor != '"' || !name_size) return FALSE;
name_length = (size_t)(cursor - name_start);
if (name_length > name_size) name_length = name_size;
memcpy(name, name_start, name_length);
name[name_length] = '\0';
cursor++;
if (!ParseMIMEUInt64(&cursor, header_start) ||
!ParseMIMEUInt64(&cursor, header_size) ||
!ParseMIMEUInt64(&cursor, start_pos) ||
!ParseMIMEUInt64(&cursor, size) ||
!ParseMIMEUInt64(&cursor, message_header_size) ||
!ParseMIMEUInt64(&cursor, lines)) return FALSE;
while (*cursor == ' ') cursor++;
return *cursor == '\0';
}
__inline static void
MessageDetailsFree(MessageDetail *messageDetail)
{
if (messageDetail->header) {
MemFree(messageDetail->header);
messageDetail->header = NULL;
}
if (messageDetail->mimeInfo) {
g_array_free(messageDetail->mimeInfo, TRUE);
messageDetail->mimeInfo = NULL;
}
}
__inline static void
FreeFetchFlags(FetchStruct *FetchRequest)
{
unsigned long i;
FetchFlagStruct *flag;
for (i = 0; i < FetchRequest->flagCount; i++) {
flag = &(FetchRequest->flag[i]);
if (!(flag->hasAllocated)) {
continue;
}
if (flag->hasAllocated & ALLOCATED_FIELDS) {
MemFree(flag->fieldList.fieldRequest);
}
if (!(flag->hasAllocated & ALLOCATED_FIELD_POINTERS)) {
;
} else {
MemFree(flag->fieldList.field);
}
if (!(flag->hasAllocated & ALLOCATED_PART_NUMBERS)) {
;
} else {
MemFree(flag->bodyPart.part);
}
}
return;
}
__inline static void
FreeFetchResourcesSuccess(FetchStruct *FetchRequest)
{
if (FetchRequest->messageSet) {
MemFree(FetchRequest->messageSet);
}
if (!(FetchRequest->hasAllocated)) {
return;
}
FreeFetchFlags(FetchRequest);
if (!(FetchRequest->hasAllocated & ALLOCATED_FLAGS)) {
return;
}
MemFree(FetchRequest->flag);
return;
}
__inline static void
FreeFetchResourcesFailure(FetchStruct *FetchRequest)
{
MessageDetailsFree(&(FetchRequest->messageDetail));
FreeFetchResourcesSuccess(FetchRequest);
return;
}
__inline static long
ReadBodyPart(ImapSession *session, char **Part, unsigned long ID, unsigned long Start, unsigned long Size)
{
size_t count;
long ccode;
*Part = MemMalloc(Size + 1);
if (*Part) {
if (Size) {
/* Request the body part*/
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %lu %lu\r\n", session->folder.selected.message[ID].guid, Start, Size) != -1) {
if ((ccode = NMAPReadPropertyValueLength(session->store.conn, "nmap.document", &count)) == 2001) {
if (NMAPReadCount(session->store.conn, *Part, count) == (int)count) {
if (NMAPReadCrLf(session->store.conn) == 2) {
return(STATUS_CONTINUE);
}
}
MemFree(*Part);
*Part = NULL;
return(STATUS_ABORT);
}
MemFree(*Part);
*Part = NULL;
return(CheckForNMAPCommError(ccode));
}
MemFree(*Part);
*Part = NULL;
return(STATUS_NMAP_COMM_ERROR);
}
**Part = '\0';
return(STATUS_CONTINUE);
}
return(STATUS_MEMORY_ERROR);
}
__inline static long
SendEnvelope(ImapSession *session, char *Header, char *Answer)
{
char *component = NULL;
long ccode;
unsigned long len;
/* this function is memory intensive */
if (ConnWrite(session->client.conn, "(", 1) != -1) {
/* First comes the date, just as text */
if (FindRFC822Header(Header, "Date", &component, 0)) {
len = strlen(component);
if ((ConnWriteF(session->client.conn, "{%d}\r\n", (int)len) != -1) &&
(ConnWrite(session->client.conn, component, len) != -1) &&
(ConnWrite(session->client.conn, " ", 1) != -1)) {
MemFree(component);
component = NULL;
} else {
MemFree(component);
return(STATUS_ABORT);
}
} else {
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
;
} else {
return(STATUS_ABORT);
}
}
/* now env_subject */
if (FindRFC822Header(Header, "Subject", &component, 0)) {
len=strlen(component);
if ((ConnWriteF(session->client.conn, "{%d}\r\n", (int)len) != -1) &&
(ConnWrite(session->client.conn, component, len) != -1) &&
(ConnWrite(session->client.conn, " ", 1) != -1)) {
MemFree(component);
component = NULL;
} else {
MemFree(component);
return(STATUS_ABORT);
}
} else {
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
;
} else {
return(STATUS_ABORT);
}
}
/* now env_from */
if (FindRFC822Header(Header, "From", &component, 0)) {
if ((ccode = SendRFC822Address(session, component, Answer)) == STATUS_CONTINUE) {
MemFree(component);
component = NULL;
} else {
MemFree(component);
return(ccode);
}
} else {
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
;
} else {
return(STATUS_ABORT);
}
}
/* now env_sender */
if (FindRFC822Header(Header, "Sender", &component, 0)) {
if ((ccode = SendRFC822Address(session, component, Answer)) == STATUS_CONTINUE) {
MemFree(component);
component = NULL;
} else {
MemFree(component);
return(ccode);
}
} else {
if (FindRFC822Header(Header, "From", &component, 0)) {
if ((ccode = SendRFC822Address(session, component, Answer)) == STATUS_CONTINUE) {
MemFree(component);
component = NULL;
} else {
MemFree(component);
return(ccode);
}
} else {
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
;
} else {
return(STATUS_ABORT);
}
}
}
/* now env_in_reply_to */
if (FindRFC822Header(Header, "Reply-To", &component, 0)) {
if ((ccode = SendRFC822Address(session, component, Answer)) == STATUS_CONTINUE) {
MemFree(component);
component = NULL;
} else {
MemFree(component);
return(ccode);
}
} else {
if (FindRFC822Header(Header, "From", &component, 0)) {
if ((ccode = SendRFC822Address(session, component, Answer)) == STATUS_CONTINUE) {
MemFree(component);
component = NULL;
} else {
MemFree(component);
return(ccode);
}
} else {
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
;
} else {
return(STATUS_ABORT);
}
}
}
/* now env_to */
if (FindRFC822Header(Header, "To", &component, 0)) {
if ((ccode = SendRFC822Address(session, component, Answer)) == STATUS_CONTINUE) {
MemFree(component);
component = NULL;
} else {
MemFree(component);
return(ccode);
}
} else {
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
;
} else {
return(STATUS_ABORT);
}
}
/* now env_cc */
if (FindRFC822Header(Header, "Cc", &component, 0)) {
if ((ccode = SendRFC822Address(session, component, Answer)) == STATUS_CONTINUE) {
MemFree(component);
component = NULL;
} else {
MemFree(component);
return(ccode);
}
} else {
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
;
} else {
return(STATUS_ABORT);
}
}
/* now env_bcc */
if (FindRFC822Header(Header, "Bcc", &component, 0)) {
if ((ccode = SendRFC822Address(session, component, Answer)) == STATUS_CONTINUE) {
MemFree(component);
component = NULL;
} else {
MemFree(component);
return(ccode);
}
} else {
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
;
} else {
return(STATUS_ABORT);
}
}
/* now env_in_reply_to */
if (FindRFC822Header(Header, "In-Reply-To", &component, 0)) {
char *ptr = component;
int inRepToLen = strlen(component);
/* Break up reply to session, Answer may not be large enough */
/* to hold all of the recipients in the reply to field. */
do {
if (*ptr == '"' || *ptr == '\\' || (unsigned char)*ptr < 0x20 ||
(unsigned char)*ptr > 0x7f) {
if ((ConnWriteF(session->client.conn, "{%lu}\r\n", (long unsigned int)inRepToLen) != -1) &&
(ConnWrite(session->client.conn, component, inRepToLen) != -1) &&
(ConnWrite(session->client.conn, " ", 1) != -1)) {
break;
} else {
MemFree(component);
return(STATUS_ABORT);
}
}
if (*(++ptr)) {
continue;
}
if ((ConnWrite(session->client.conn, "\"", 1) != -1) &&
(ConnWrite(session->client.conn, component, inRepToLen) != -1) &&
(ConnWrite(session->client.conn, "\" ", 2) != -1)) {
break;
} else {
MemFree(component);
return(STATUS_ABORT);
}
} while (TRUE);
MemFree(component);
component = NULL;
} else {
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
;
} else {
return(STATUS_ABORT);
}
}
/* now env_message_id */
if (FindRFC822Header(Header, "Message-Id", &component, 0)) {
if ((ConnWrite(session->client.conn, "\"", 1) != -1) &&
(ConnWrite(session->client.conn, component, strlen(component)) != -1) &&
(ConnWrite(session->client.conn, "\"", 1) != -1)) {
MemFree(component);
component = NULL;
} else {
MemFree(component);
return(STATUS_ABORT);
}
} else {
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
;
} else {
return(STATUS_ABORT);
}
}
if (ConnWrite(session->client.conn, ")", 1) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderUid(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param3);
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
if (ConnWriteF(session->client.conn, "UID %lu", (long)FetchRequest->message->uid) != -1) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderFlags(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param3);
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
char addSpace[2];
long ccode;
ccode = ConnWrite(session->client.conn, "FLAGS (", strlen("FLAGS ("));
addSpace[0] = '\0';
addSpace[1] = '\0';
if ((FetchRequest->message->flags & STORE_MSG_FLAG_ANSWERED) && (ccode != -1)) {
ccode = ConnWriteF(session->client.conn, "\\Answered");
addSpace[0]=' ';
}
if ((FetchRequest->message->flags & STORE_MSG_FLAG_DELETED) && (ccode != -1)) {
ccode = ConnWriteF(session->client.conn, "%s\\Deleted",addSpace);
addSpace[0]=' ';
}
if ((FetchRequest->message->flags & STORE_MSG_FLAG_RECENT) && (ccode != -1)) {
ccode = ConnWriteF(session->client.conn, "%s\\Recent", addSpace);
addSpace[0]=' ';
}
if ((FetchRequest->message->flags & STORE_MSG_FLAG_SEEN) && (ccode != -1)) {
ccode = ConnWriteF(session->client.conn, "%s\\Seen", addSpace);
addSpace[0]=' ';
}
if ((FetchRequest->message->flags & STORE_MSG_FLAG_DRAFT) && (ccode != -1)) {
ccode = ConnWriteF(session->client.conn, "%s\\Draft", addSpace);
addSpace[0]=' ';
}
if ((FetchRequest->message->flags & STORE_MSG_FLAG_FLAGGED) && (ccode != -1)) {
ccode = ConnWriteF(session->client.conn, "%s\\Flagged", addSpace);
addSpace[0]=' ';
}
if (ccode != -1) {
ccode = ConnWrite(session->client.conn, ")", strlen(")"));
if (ccode != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderEnvelope(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param3);
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
if (ConnWrite(session->client.conn, "ENVELOPE ", strlen("ENVELOPE ")) != -1) {
return(SendEnvelope(session, FetchRequest->messageDetail.header, session->store.response));
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderXSender(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param3);
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
long ccode;
return STATUS_CONTINUE;
/* FIXME - the store agent does not provide this property yet */
ccode = NMAPGetTextProperty(session->store.conn, FetchRequest->message->guid, "nmap.mail.authenticatedsender", session->store.response, sizeof(session->store.response));
if (ccode == 3245) {
if (ConnWrite(session->client.conn, "XSENDER {0}\r\n", 13) != -1) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
if (ccode == 2001) {
unsigned long authLen;
authLen = strlen(session->store.response);
if (!strchr(session->store.response, '@')) {
size_t hostlen = strlen(BongoGlobals.hostname);
if ((ConnWriteF(session->client.conn, "XSENDER {%lu}\r\n", authLen + 1 + hostlen) != -1) &&
(ConnWrite(session->client.conn, session->store.response, authLen) != -1) &&
(ConnWrite(session->client.conn, "@", 1) != -1) &&
(ConnWrite(session->client.conn, BongoGlobals.hostname, hostlen) != -1)) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
if ((ConnWriteF(session->client.conn, "XSENDER {%lu}\r\n", authLen) != -1) &&
(ConnWrite(session->client.conn, session->store.response, authLen) != -1)) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
return(CheckForNMAPCommError(ccode));
}
static long
FetchFlagResponderInternalDate(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
struct tm ltm;
time_t internalDate;
UNUSED_PARAMETER(param3);
internalDate = (time_t)FetchRequest->message->internalDate;
gmtime_r(&internalDate, &ltm);
if (ConnWriteF(session->client.conn, "INTERNALDATE \"%02d-%3s-%04d %02d:%02d:%02d +0000\"", ltm.tm_mday, Imap.command.months[ltm.tm_mon], 1900+ltm.tm_year, ltm.tm_hour, ltm.tm_min, ltm.tm_sec) != -1) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyStructure(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
char **multiPartTypeList;
uint64_t *rfcSizeList;
long ccode;
char Type[MIME_TYPE_LEN+1];
char Subtype[MIME_SUBTYPE_LEN+1];
char Charset[MIME_CHARSET_LEN+1];
char Encoding[MIME_ENCODING_LEN+1];
char Name[MIME_NAME_LEN+1];
uint64_t mimeOffset;
uint64_t mimeLen;
uint64_t SPos;
uint64_t Lines;
uint64_t Size;
unsigned long MPCount;
unsigned long RFCCount;
uint64_t partHeaderSize;
unsigned long MultiPartTypeListLen = 20;
unsigned long RFCSizeListLen = 20;
unsigned long mimeResponseLine = 0;
UNUSED_PARAMETER(param3);
multiPartTypeList = MemMalloc(sizeof(char *)*MultiPartTypeListLen);
if (multiPartTypeList) {
MPCount = 0;
rfcSizeList = MemMalloc(sizeof(*rfcSizeList) * RFCSizeListLen);
if (rfcSizeList) {
RFCCount=0;
if (FetchRequest->flags & F_BODY) {
ccode = ConnWrite(session->client.conn, "BODY ", strlen("BODY "));
} else {
ccode = ConnWrite(session->client.conn, "BODYSTRUCTURE ", strlen("BODYSTRUCTURE "));
}
if (ccode != -1) {
do {
char *mime_string = g_array_index(FetchRequest->messageDetail.mimeInfo,
char *, mimeResponseLine);
switch(atol(mime_string)) {
case 2002: {
ParseMIMEDLine(mime_string + 5,
Type, MIME_TYPE_LEN,
Subtype, MIME_SUBTYPE_LEN,
Charset, MIME_CHARSET_LEN,
Encoding, MIME_ENCODING_LEN,
Name, MIME_NAME_LEN,
&mimeOffset,
&mimeLen,
&SPos,
&Size,
&partHeaderSize,
&Lines);
/* First, set the default values if nothing is specified by the message */
if (Type[0]=='-') {
strcpy(Type, "TEXT");
strcpy(Subtype, "PLAIN");
}
if (Encoding[0]=='-') {
strcpy(Encoding, "7BIT");
}
/************
**MULTIPART**
************/
if (XplStrCaseCmp(Type, "Multipart")==0) {
if (MPCount < MultiPartTypeListLen) {
multiPartTypeList[MPCount++] = MemStrdup(Subtype);
} else {
char *tmp;
MultiPartTypeListLen <<= 1; /* Double the Size */
tmp = MemRealloc(multiPartTypeList, sizeof(char *) * MultiPartTypeListLen);
if (tmp) {
multiPartTypeList = (char **)tmp;
multiPartTypeList[MPCount++] = MemStrdup(Subtype);
} else {
while (MPCount > 0) {
MPCount--;
MemFree(multiPartTypeList[MPCount]);
}
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_MEMORY_ERROR);
}
}
if (ConnWrite(session->client.conn, "(", 1) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
/************
** TEXT **
************/
} else if (XplStrCaseCmp(Type, "Text")==0) {
if (Charset[0]=='-') {
strcpy(Charset, "US-ASCII");
}
if (ConnWriteF(session->client.conn, "(\"%s\" \"%s\" (\"CHARSET\" \"%s\") NIL NIL \"%s\" %" PRIu64 " %" PRIu64 ")", Type, Subtype, Charset, Encoding, Size, Lines) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
/************
** MESSAGE **
************/
} else if ((XplStrCaseCmp(Type, "Message")==0) && (XplStrCaseCmp(Subtype, "RFC822")==0)) {
char *PartHeader;
/* we have to print the basic stuff, then get the headers, print the envelope */
/* and then continue in the loop */
if (RFCCount < RFCSizeListLen) {
rfcSizeList[RFCCount++] = Lines;
} else {
char *tmp;
RFCSizeListLen <<= 1; /* Double the Size */
tmp = MemRealloc(rfcSizeList, sizeof(*rfcSizeList) * RFCSizeListLen);
if (tmp) {
rfcSizeList = (uint64_t *)tmp;
rfcSizeList[RFCCount++] = Lines;
} else {
while (MPCount > 0) {
MPCount--;
MemFree(multiPartTypeList[MPCount]);
}
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_MEMORY_ERROR);
}
}
/* Body Type & Subtype */
if (ConnWriteF(session->client.conn, "(\"%s\" \"%s\" ", Type, Subtype) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
/* Body parameter parentesized list */
if ((Charset[0]!='-') && (Name[0]!='\0')) {
char *langPtr;
if ((Name[0] != '*') || (Name[1] != '=') || ((langPtr = strchr(Name + 2, '\'')) == NULL) || ((langPtr = strchr(langPtr + 1, '\'')) == NULL)) {
if (ConnWriteF(session->client.conn, "(\"CHARSET\" \"%s\" \"NAME\" \"%s\") ", Charset, Name) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
} else {
/* this is an rfc2231 name */
if (ConnWriteF(session->client.conn, "(\"CHARSET\" \"%s\" \"NAME*\" \"%s\") ", Charset, Name + 2) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
}
} else if (Charset[0]!='-') {
if (ConnWriteF(session->client.conn, "(\"CHARSET\" \"%s\") ", Charset) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
} else if (Name[0]!='\0') {
char *langPtr;
if ((Name[0] != '*') || (Name[1] != '=') || ((langPtr = strchr(Name + 2, '\'')) == NULL) || ((langPtr = strchr(langPtr + 1, '\'')) == NULL)) {
if (ConnWriteF(session->client.conn, "(\"NAME\" \"%s\") ", Name) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
} else {
/* this is an rfc2231 name */
if (ConnWriteF(session->client.conn, "(\"NAME*\" \"%s\") ", Name + 2) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
}
} else {
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
}
/* Body ID & Body description */
if (ConnWrite(session->client.conn, "NIL NIL ", 8) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
/* Body encoding && Size */
if (ConnWriteF(session->client.conn, "\"%s\" %" PRIu64 " ", Encoding, Size) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
/* Grab the envelope */
if ((ccode = ReadBodyPart(session, &PartHeader, FetchRequest->messageDetail.sequenceNumber, SPos, partHeaderSize)) == STATUS_CONTINUE) {
ccode = SendEnvelope(session, PartHeader, session->store.response);
MemFree(PartHeader);
if (ccode == STATUS_CONTINUE) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(ccode);
}
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(ccode);
}
/************
** DEFAULT **
************/
} else {
/* Body Type & Subtype */
if (ConnWriteF(session->client.conn, "(\"%s\" \"%s\" ", Type, Subtype) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
/* Body parameter parentesized list */
if ((Charset[0]!='-') && (Name[0]!='\0')) {
char *langPtr;
if ((Name[0] != '*') || (Name[1] != '=') || ((langPtr = strchr(Name + 2, '\'')) == NULL) || ((langPtr = strchr(langPtr + 1, '\'')) == NULL)) {
if (ConnWriteF(session->client.conn, "(\"CHARSET\" \"%s\" \"NAME\" \"%s\") ", Charset, Name) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
} else {
/* This is an rfc2231 encoded name */
if (ConnWriteF(session->client.conn, "(\"CHARSET\" \"%s\" \"NAME*\" \"%s\") ", Charset, Name + 2) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
}
} else if (Charset[0]!='-') {
if (ConnWriteF(session->client.conn, "(\"CHARSET\" \"%s\") ", Charset) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
} else if (Name[0]!='\0') {
char *langPtr;
if ((Name[0] != '*') || (Name[1] != '=') || ((langPtr = strchr(Name + 2, '\'')) == NULL) || ((langPtr = strchr(langPtr + 1, '\'')) == NULL)) {
if (ConnWriteF(session->client.conn, "(\"NAME\" \"%s\") ", Name) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
} else {
/* This is an rfc2231 encoded name */
if (ConnWriteF(session->client.conn, "(\"NAME*\" \"%s\") ", Name + 2) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
}
} else {
if (ConnWrite(session->client.conn, "NIL ", 4) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
}
/* Body ID & Body description */
if (ConnWrite(session->client.conn, "NIL NIL ", 8) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
/* Body encoding && Size */
if (ConnWriteF(session->client.conn, "\"%s\" %" PRIu64 ")", Encoding, Size) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
}
break;
}
case 2003: {
if (MPCount > 0) {
MPCount--;
if (ConnWriteF(session->client.conn, " \"%s\")", multiPartTypeList[MPCount]) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
MemFree(multiPartTypeList[MPCount]);
multiPartTypeList[MPCount] = NULL;
}
break;
}
case 2004: {
RFCCount--;
if (ConnWriteF(session->client.conn, " %" PRIu64 ")", rfcSizeList[RFCCount]) != -1) {
;
} else {
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
break;
}
}
mimeResponseLine++;
if (mimeResponseLine < FetchRequest->messageDetail.mimeInfo->len) {
continue;
}
break;
} while (TRUE);
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_CONTINUE);
}
MemFree(multiPartTypeList);
MemFree(rfcSizeList);
return(STATUS_ABORT);
}
MemFree(multiPartTypeList);
}
return(STATUS_MEMORY_ERROR);
}
static long
FetchFlagResponderRfc822Text(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param3);
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
long ccode;
uint64_t count;
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %" PRIu64 "\r\n", FetchRequest->message->guid, FetchRequest->message->headerSize) != -1) {
if ((ccode = NMAPReadPropertyValueLength64(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, "RFC822.TEXT {%" PRIu64 "}\r\n", count) != -1)
return TransferNMAPDocument(session, count);
return STATUS_ABORT;
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
static long
FetchFlagResponderRfc822Size(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param3);
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
if(ConnWriteF(session->client.conn, "RFC822.SIZE %" PRIu64, FetchRequest->message->size) != -1) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderRfc822Header(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param3);
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
if (ConnWriteF(session->client.conn, "RFC822.HEADER {%" PRIu64 "}\r\n", FetchRequest->message->headerSize) != -1)
return WriteClientBytes(session, FetchRequest->messageDetail.header,
FetchRequest->message->headerSize);
return(STATUS_ABORT);
}
static long
FetchFlagResponderRfc822(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param3);
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
long ccode;
uint64_t count;
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %" PRIu64 "\r\n", FetchRequest->message->guid, FetchRequest->message->headerSize) != -1) {
if ((ccode = NMAPReadPropertyValueLength64(session->store.conn, "nmap.document", &count)) == 2001) {
if (count > UINT64_MAX - FetchRequest->message->headerSize)
return STATUS_FETCH_LIMIT;
if (ConnWriteF(session->client.conn, "RFC822 {%" PRIu64 "}\r\n", count + FetchRequest->message->headerSize) != -1 &&
WriteClientBytes(session, FetchRequest->messageDetail.header,
FetchRequest->message->headerSize) == STATUS_CONTINUE)
return TransferNMAPDocument(session, count);
return STATUS_ABORT;
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
static long
FetchFlagResponderBody(void *param1, void *param2, void *param3)
{
return(FetchFlagResponderBodyStructure(param1, param2, param3));
}
static long
FetchFlagResponderBodyHeader(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param3);
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
if (ConnWriteF(session->client.conn, "BODY[HEADER] {%" PRIu64 "}\r\n", FetchRequest->message->headerSize) != -1)
return WriteClientBytes(session, FetchRequest->messageDetail.header,
FetchRequest->message->headerSize);
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyHeaderPartial(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
if (flag->partial.start < FetchRequest->message->headerSize) {
uint64_t available = FetchRequest->message->headerSize - flag->partial.start;
uint64_t length = flag->partial.len < available ? flag->partial.len : available;
if (flag->partial.start > SIZE_MAX) return STATUS_FETCH_LIMIT;
if (ConnWriteF(session->client.conn, "BODY[HEADER]<%" PRIu64 "> {%" PRIu64 "}\r\n", flag->partial.start, length) != -1)
return WriteClientBytes(session,
FetchRequest->messageDetail.header +
(size_t)flag->partial.start, length);
return STATUS_ABORT;
}
if (ConnWriteF(session->client.conn, "BODY[HEADER]<%" PRIu64 "> {0}\r\n", flag->partial.start) != -1) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyText(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param3);
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
long ccode;
uint64_t count;
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %" PRIu64 "\r\n", FetchRequest->message->guid, FetchRequest->message->headerSize) != -1) {
if ((ccode = NMAPReadPropertyValueLength64(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, "BODY[TEXT] {%" PRIu64 "}\r\n", count) != -1)
return TransferNMAPDocument(session, count);
return STATUS_ABORT;
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
static long
FetchFlagResponderBodyTextPartial(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
uint64_t count;
long ccode;
if (flag->partial.start < FetchRequest->message->bodySize) {
uint64_t available = FetchRequest->message->bodySize - flag->partial.start;
uint64_t length = flag->partial.len < available ? flag->partial.len : available;
if (FetchRequest->message->headerSize > UINT64_MAX - flag->partial.start)
return STATUS_FETCH_LIMIT;
ccode = NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %" PRIu64 " %" PRIu64 "\r\n", FetchRequest->message->guid, FetchRequest->message->headerSize + flag->partial.start, length);
if (ccode != -1) {
if ((ccode = NMAPReadPropertyValueLength64(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, "BODY[TEXT]<%" PRIu64 "> {%" PRIu64 "}\r\n", flag->partial.start, count) != -1)
return TransferNMAPDocument(session, count);
return STATUS_ABORT;
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
if (ConnWriteF(session->client.conn, "BODY[TEXT]<%" PRIu64 "> {0}\r\n", flag->partial.start) != -1) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyBoth(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param3);
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
long ccode;
uint64_t count;
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 "\r\n", FetchRequest->message->guid) != -1) {
if ((ccode = NMAPReadPropertyValueLength64(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, "BODY[] {%" PRIu64 "}\r\n", count) != -1)
return TransferNMAPDocument(session, count);
return STATUS_ABORT;
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
static long
FetchFlagResponderBodyBothPartial(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
long ccode;
uint64_t count;
if (flag->partial.start < FetchRequest->message->size) {
uint64_t available = FetchRequest->message->size - flag->partial.start;
uint64_t length = flag->partial.len < available ? flag->partial.len : available;
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %" PRIu64 " %" PRIu64 "\r\n", FetchRequest->message->guid, flag->partial.start, length) != -1) {
if ((ccode = NMAPReadPropertyValueLength64(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, "BODY[]<%" PRIu64 "> {%" PRIu64 "}\r\n", flag->partial.start, count) != -1)
return TransferNMAPDocument(session, count);
return STATUS_ABORT;
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
if (ConnWriteF(session->client.conn, "BODY[]<%" PRIu64 "> {0}\r\n", flag->partial.start) != -1) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyMime(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param2);
UNUSED_PARAMETER(param3);
ImapSession *session = (ImapSession *)param1;
if (ConnWriteF(session->client.conn, "BODY[MIME] {%lu}\r\n", TopLevelMimeLen) != -1) {
if (ConnWrite(session->client.conn, TopLevelMime, TopLevelMimeLen) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyMimePartial(void *param1, void *param2, void *param3)
{
UNUSED_PARAMETER(param2);
ImapSession *session = (ImapSession *)param1;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
if (flag->partial.start < TopLevelMimeLen) {
if ((flag->partial.start + flag->partial.len) < TopLevelMimeLen) {
if (ConnWriteF(session->client.conn, "BODY[MIME]<%lu> {%lu}\r\n", flag->partial.start, flag->partial.len) != -1) {
if (ConnWrite(session->client.conn, TopLevelMime + flag->partial.start, flag->partial.len) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
if (ConnWriteF(session->client.conn, "BODY[MIME]<%lu> {%lu}\r\n", flag->partial.start, TopLevelMimeLen - flag->partial.start) != -1) {
if (ConnWrite(session->client.conn, TopLevelMime + flag->partial.start, TopLevelMimeLen - flag->partial.start) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
if (ConnWriteF(session->client.conn, "BODY[MIME]<%lu> {0}\r\n", flag->partial.start) != -1) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyHeaderFields(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
long ccode;
unsigned long j;
char *resultHeader;
unsigned long resultHeaderLen;
if (!flag->fieldList.skip) {
ccode = ConnWrite(session->client.conn, "BODY[HEADER.FIELDS (", strlen("BODY[HEADER.FIELDS ("));
} else {
ccode = ConnWrite(session->client.conn, "BODY[HEADER.FIELDS.NOT (", strlen("BODY[HEADER.FIELDS.NOT ("));
}
if (ccode != -1) {
/* We go one less to have an easy way of knowing if to put a space */
for (j = 0; j < flag->fieldList.count - 1; j++) {
if (ConnWriteF(session->client.conn, "\"%s\" ", flag->fieldList.field[j]) != -1) {
continue;
}
return(STATUS_ABORT);
}
if (ConnWriteF(session->client.conn, "\"%s\")]", flag->fieldList.field[j]) != -1) {
resultHeader = GetSubHeader(FetchRequest->messageDetail.header, FetchRequest->message->headerSize, flag->fieldList.field, flag->fieldList.count, flag->fieldList.skip, &resultHeaderLen);
if (resultHeader) {
if (!flag->isPartial) {
if (resultHeaderLen > 0) {
if (ConnWriteF(session->client.conn, " {%lu}\r\n", resultHeaderLen + 2) != -1) {
if (ConnWrite(session->client.conn, resultHeader, resultHeaderLen) != -1) {
if (ConnWrite(session->client.conn, "\r\n", 2) != -1) {
MemFree(resultHeader);
return(STATUS_CONTINUE);
}
}
}
MemFree(resultHeader);
return(STATUS_ABORT);
}
if (ConnWrite(session->client.conn, " {0}\r\n", sizeof(" {0}\r\n") - 1) != -1) {
MemFree(resultHeader);
return(STATUS_CONTINUE);
}
MemFree(resultHeader);
return(STATUS_ABORT);
}
if (resultHeaderLen > 0) {
if (flag->partial.start < resultHeaderLen) {
if ((flag->partial.start + flag->partial.len) < resultHeaderLen) {
;
} else {
flag->partial.len = resultHeaderLen - flag->partial.start;
}
} else {
flag->partial.start = 0;
flag->partial.len = 0;
}
if (flag->partial.len > 0) {
if ((ccode = ConnWriteF(session->client.conn, "<%lu> {%lu}\r\n", flag->partial.start, flag->partial.len)) != -1) {
ccode = ConnWrite(session->client.conn, resultHeader + flag->partial.start, flag->partial.len);
}
} else {
ccode = ConnWriteF(session->client.conn, "<%lu> {0}\r\n", flag->partial.start);
}
MemFree(resultHeader);
if (ccode != -1) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
if (ConnWriteF(session->client.conn, "<%lu> {0}\r\n", flag->partial.start) != -1) {
MemFree(resultHeader);
return(STATUS_CONTINUE);
}
MemFree(resultHeader);
return(STATUS_ABORT);
}
return(STATUS_MEMORY_ERROR);
}
}
return(STATUS_ABORT);
}
__inline static BOOL
HasPart(GArray *mimeInfo, BodyPartRequestStruct *request)
{
char type[MIME_TYPE_LEN+1];
char subtype[MIME_SUBTYPE_LEN+1];
char encoding[MIME_ENCODING_LEN+1];
char dummy[MIME_NAME_LEN+1];
uint64_t dummy64;
long depthChange = 0;
BOOL haveRfc822 = TRUE;
unsigned long mimeResponseLine = 0;
unsigned long currentDepth = 0;
unsigned long matchDepth = 0;
unsigned long matchDepthPart = 0;
do {
char *mime_string = g_array_index(mimeInfo, char *, mimeResponseLine);
switch (atol(mime_string)) {
case 2002: {
ParseMIMEDLine(mime_string + 5,
type, MIME_TYPE_LEN,
subtype, MIME_SUBTYPE_LEN,
dummy, MIME_NAME_LEN,
encoding, MIME_ENCODING_LEN,
dummy, MIME_NAME_LEN,
&(request->mimeOffset),
&(request->mimeLen),
&(request->partOffset),
&(request->partLen),
&(request->messageHeaderLen),
&dummy64);
if (toupper(type[0]) == 'M') {
if (XplStrCaseCmp(type, "MULTIPART") == 0) {
if (!haveRfc822) {
if (currentDepth == matchDepth) {
matchDepthPart++;
}
depthChange = 1;
} else {
depthChange = 0;
}
haveRfc822 = FALSE;
} else if (XplStrCaseCmp(type, "MESSAGE") == 0) {
if (!haveRfc822) {
if (currentDepth == matchDepth) {
matchDepthPart++;
}
depthChange = 1;
haveRfc822 = TRUE;
} else {
depthChange = 0;
}
} else {
if (currentDepth == matchDepth) {
matchDepthPart++;
}
haveRfc822 = FALSE;
depthChange = 0;
}
} else {
if (currentDepth == matchDepth) {
matchDepthPart++;
}
depthChange = 0;
haveRfc822 = FALSE;
}
/* Check to see if this is the part we are looking for */
if (currentDepth == matchDepth) {
if (matchDepthPart == request->part[matchDepth]) {
matchDepth++;
if (matchDepth == request->depth) {
/* We found the part!!!! */
request->isMessage =
XplStrCaseCmp(type, "MESSAGE") == 0 &&
(XplStrCaseCmp(subtype, "RFC822") == 0 ||
XplStrCaseCmp(subtype, "GLOBAL") == 0);
request->isMultipart =
XplStrCaseCmp(type, "MULTIPART") == 0;
request->isText = XplStrCaseCmp(type, "TEXT") == 0;
snprintf(request->encoding,
sizeof(request->encoding), "%s", encoding);
return(TRUE);
}
matchDepthPart = 0;
} else if (matchDepthPart > request->part[matchDepth]) {
/* Not Found! Send Nothing */
return(FALSE);
}
} else if (currentDepth < matchDepth) {
/* Not Found! Send Nothing */
return(FALSE);
}
break;
}
case 2003:
case 2004: {
if (currentDepth > 0) {
depthChange = -1;
} else {
depthChange = 0;
}
break;
}
}
mimeResponseLine++;
if (mimeResponseLine < mimeInfo->len) {
if (depthChange == 0) {
continue;
}
if (depthChange > 0) {
currentDepth++;
if (currentDepth == matchDepth) {
matchDepthPart = 0;
}
continue;
}
currentDepth--;
continue;
}
/* Not Found; Send Nothing */
break;
} while(TRUE);
return(FALSE);
}
static int
BinaryFileSink(const unsigned char *data, size_t length, void *context)
{
FILE *file = context;
return length == 0 || fwrite(data, 1, length, file) == length;
}
static void
BinaryResultFree(FetchFlagStruct *flag)
{
if (flag->binaryFile) fclose(flag->binaryFile);
flag->binaryFile = NULL;
flag->binaryLength = 0;
flag->binaryPrepared = FALSE;
flag->binaryMissing = FALSE;
flag->binaryContainsNul = FALSE;
}
static void
BinaryResultsFree(FetchStruct *request)
{
unsigned long i;
for (i = 0; i < request->flagCount; i++)
BinaryResultFree(&request->flag[i]);
}
static long
PrepareBinaryResult(ImapSession *session, FetchStruct *request,
FetchFlagStruct *flag)
{
IMAPBinaryDecoder decoder;
unsigned char buffer[16384];
const char *encoding = "7BIT";
uint64_t offset = 0;
uint64_t encoded_length = request->message->size;
uint64_t response_length;
uint64_t remaining;
long ccode;
BOOL is_text = FALSE;
BinaryResultFree(flag);
if (flag->bodyPart.depth) {
if (!HasPart(request->messageDetail.mimeInfo, &flag->bodyPart)) {
flag->binaryMissing = TRUE;
flag->binaryPrepared = TRUE;
return STATUS_CONTINUE;
}
if (flag->bodyPart.isMultipart || flag->bodyPart.isMessage)
return STATUS_BINARY_SECTION;
offset = flag->bodyPart.partOffset;
encoded_length = flag->bodyPart.partLen;
encoding = flag->bodyPart.encoding;
is_text = flag->bodyPart.isText;
}
if (!IMAPBinaryDecoderInit(&decoder, encoding, is_text))
return STATUS_UNKNOWN_CTE;
flag->binaryFile = tmpfile();
if (!flag->binaryFile) return STATUS_ABORT;
if (NMAPSendCommandF(session->store.conn,
"READ %" PRIx64 " %" PRIu64 " %" PRIu64 "\r\n",
request->message->guid, offset,
encoded_length) == -1) {
BinaryResultFree(flag);
return STATUS_NMAP_COMM_ERROR;
}
ccode = NMAPReadPropertyValueLength64(session->store.conn,
"nmap.document",
&response_length);
if (ccode != 2001) {
BinaryResultFree(flag);
return CheckForNMAPCommError(ccode);
}
remaining = response_length;
while (remaining) {
int chunk = remaining > sizeof(buffer) ? (int)sizeof(buffer) :
(int)remaining;
if (NMAPReadCount(session->store.conn, buffer, chunk) != chunk ||
!IMAPBinaryDecoderWrite(&decoder, buffer, (size_t)chunk,
remaining == (uint64_t)chunk,
BinaryFileSink, flag->binaryFile)) {
BinaryResultFree(flag);
return STATUS_ABORT;
}
remaining -= (uint64_t)chunk;
}
if (response_length == 0 &&
!IMAPBinaryDecoderWrite(&decoder, NULL, 0, TRUE, BinaryFileSink,
flag->binaryFile)) {
BinaryResultFree(flag);
return STATUS_ABORT;
}
if (NMAPReadCrLf(session->store.conn) != 2 ||
fflush(flag->binaryFile) != 0) {
BinaryResultFree(flag);
return STATUS_ABORT;
}
if (flag->binarySize && decoder.output_length > UINT32_MAX) {
BinaryResultFree(flag);
return STATUS_FETCH_LIMIT;
}
flag->binaryLength = decoder.output_length;
flag->binaryContainsNul = decoder.contains_nul;
flag->binaryPrepared = TRUE;
return STATUS_CONTINUE;
}
static long
PrepareBinaryResults(ImapSession *session, FetchStruct *request)
{
unsigned long i;
for (i = 0; i < request->flagCount; i++) {
long ccode;
if (!request->flag[i].binarySize &&
!(request->flags & F_BINARY)) continue;
if (request->flag[i].responder != FetchFlagResponderBinary)
continue;
ccode = PrepareBinaryResult(session, request, &request->flag[i]);
if (ccode != STATUS_CONTINUE) {
BinaryResultsFree(request);
return ccode;
}
}
return STATUS_CONTINUE;
}
static BOOL
BinarySeek(FILE *file, uint64_t offset)
{
if (offset > INT64_MAX) return FALSE;
clearerr(file);
return fseeko(file, (off_t)offset, SEEK_SET) == 0;
}
static BOOL
BinaryRangeContainsNul(FetchFlagStruct *flag, uint64_t start,
uint64_t length, BOOL *contains_nul)
{
unsigned char buffer[16384];
uint64_t remaining = length;
*contains_nul = FALSE;
if (!flag->binaryContainsNul || length == 0) return TRUE;
if (!BinarySeek(flag->binaryFile, start)) return FALSE;
while (remaining) {
size_t chunk = remaining > sizeof(buffer) ? sizeof(buffer) :
(size_t)remaining;
size_t count = fread(buffer, 1, chunk, flag->binaryFile);
size_t i;
if (count != chunk) return FALSE;
for (i = 0; i < count; i++) {
if (buffer[i] == '\0') {
*contains_nul = TRUE;
return TRUE;
}
}
remaining -= count;
}
return TRUE;
}
static long
BinaryWriteRange(ImapSession *session, FetchFlagStruct *flag,
uint64_t start, uint64_t length)
{
unsigned char buffer[16384];
uint64_t remaining = length;
if (length && !BinarySeek(flag->binaryFile, start)) return STATUS_ABORT;
while (remaining) {
int chunk = remaining > sizeof(buffer) ? (int)sizeof(buffer) :
(int)remaining;
if (fread(buffer, 1, (size_t)chunk, flag->binaryFile) !=
(size_t)chunk ||
ConnWrite(session->client.conn, buffer, chunk) != chunk)
return STATUS_ABORT;
remaining -= (uint64_t)chunk;
}
return STATUS_CONTINUE;
}
static long
FetchFlagResponderBinary(void *param1, void *param2, void *param3)
{
ImapSession *session = param1;
FetchFlagStruct *flag = param3;
uint64_t start = 0;
uint64_t length;
unsigned long i;
BOOL contains_nul;
UNUSED_PARAMETER(param2);
if (!flag->binaryPrepared) return STATUS_BUG;
if (ConnWrite(session->client.conn,
flag->binarySize ? "BINARY.SIZE[" : "BINARY[",
flag->binarySize ? 12 : 7) == -1) return STATUS_ABORT;
for (i = 0; i < flag->bodyPart.depth; i++) {
if (i && ConnWrite(session->client.conn, ".", 1) == -1)
return STATUS_ABORT;
if (ConnWriteF(session->client.conn, "%lu", flag->bodyPart.part[i]) == -1)
return STATUS_ABORT;
}
if (ConnWrite(session->client.conn, "]", 1) == -1) return STATUS_ABORT;
if (flag->binarySize)
return ConnWriteF(session->client.conn, " %" PRIu64,
flag->binaryMissing ? 0 : flag->binaryLength) == -1 ?
STATUS_ABORT : STATUS_CONTINUE;
if (flag->binaryMissing)
return ConnWrite(session->client.conn, " NIL", 4) == -1 ?
STATUS_ABORT : STATUS_CONTINUE;
length = flag->binaryLength;
if (flag->isPartial) {
start = flag->partial.start;
if (start >= flag->binaryLength) {
length = 0;
} else {
uint64_t available = flag->binaryLength - start;
length = flag->partial.len < available ? flag->partial.len :
available;
}
if (ConnWriteF(session->client.conn, "<%" PRIu64 ">", start) == -1)
return STATUS_ABORT;
}
if (!BinaryRangeContainsNul(flag, start, length, &contains_nul))
return STATUS_ABORT;
if (ConnWriteF(session->client.conn, contains_nul ? " ~{%" PRIu64 "}\r\n" :
" {%" PRIu64 "}\r\n",
length) == -1) return STATUS_ABORT;
return BinaryWriteRange(session, flag, start, length);
}
static long
FetchFlagResponderBodyPartNumber(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
long ccode;
size_t count;
unsigned long i;
if (ConnWriteF(session->client.conn, "BODY[%lu", flag->bodyPart.part[0]) != -1) {
for (i = 1; i < flag->bodyPart.depth; i++) {
if (ConnWriteF(session->client.conn, ".%lu", flag->bodyPart.part[i]) != -1) {
continue;
}
return(STATUS_ABORT);
}
if (HasPart(FetchRequest->messageDetail.mimeInfo, &(flag->bodyPart))) {
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %lu %lu\r\n", FetchRequest->message->guid, flag->bodyPart.partOffset, flag->bodyPart.partLen) != -1) {
if ((ccode = NMAPReadPropertyValueLength(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, "] {%lu}\r\n", (long)count) != -1) {
if (ConnReadToConn(session->store.conn, session->client.conn, count) == (int)count) {
if (NMAPReadCrLf(session->store.conn) == 2) {
return(STATUS_CONTINUE);
}
}
}
return(STATUS_ABORT);
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
if (ConnWrite(session->client.conn, "] {0}\r\n", strlen("] {0}\r\n")) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyPartNumberPartial(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
long ccode;
size_t count;
unsigned long i;
long resultLen;
if (ConnWriteF(session->client.conn, "BODY[%lu", flag->bodyPart.part[0]) != -1) {
for (i = 1; i < flag->bodyPart.depth; i++) {
if (ConnWriteF(session->client.conn, ".%lu", flag->bodyPart.part[i]) != -1) {
continue;
}
return(STATUS_ABORT);
}
if (HasPart(FetchRequest->messageDetail.mimeInfo, &(flag->bodyPart))) {
if (flag->partial.start < flag->bodyPart.partLen) {
if ((flag->partial.start + flag->partial.len) < flag->bodyPart.partLen) {
/* all in range */
resultLen = flag->partial.len;
} else {
/* len out of range */
resultLen = flag->bodyPart.partLen - flag->partial.start;
}
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %lu %lu\r\n", FetchRequest->message->guid, flag->bodyPart.partOffset + flag->partial.start, resultLen) != -1) {
if ((ccode = NMAPReadPropertyValueLength(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, "]<%lu> {%lu}\r\n", flag->partial.start, (long)count) != -1) {
if (ConnReadToConn(session->store.conn, session->client.conn, count) == (int)count) {
if (NMAPReadCrLf(session->store.conn) == 2) {
return(STATUS_CONTINUE);
}
}
}
return(STATUS_ABORT);
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
/* start out of range */
}
if (ConnWriteF(session->client.conn, "]<%lu> {0}\r\n", flag->partial.start) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyPartText(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
long ccode;
size_t count;
unsigned long i;
if (ConnWriteF(session->client.conn, "BODY[%lu", flag->bodyPart.part[0]) != -1) {
for (i = 1; i < flag->bodyPart.depth; i++) {
if (ConnWriteF(session->client.conn, ".%lu", flag->bodyPart.part[i]) != -1) {
continue;
}
return(STATUS_ABORT);
}
if (HasPart(FetchRequest->messageDetail.mimeInfo, &(flag->bodyPart)) && flag->bodyPart.isMessage) {
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %lu %lu\r\n", FetchRequest->message->guid, flag->bodyPart.partOffset + flag->bodyPart.messageHeaderLen, flag->bodyPart.partLen - flag->bodyPart.messageHeaderLen) != -1) {
if ((ccode = NMAPReadPropertyValueLength(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, ".TEXT] {%lu}\r\n", (long)count) != -1) {
if (ConnReadToConn(session->store.conn, session->client.conn, count) == (int)count) {
if (NMAPReadCrLf(session->store.conn) == 2) {
return(STATUS_CONTINUE);
}
}
}
return(STATUS_ABORT);
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
if (ConnWrite(session->client.conn, ".TEXT] {0}\r\n", strlen(".TEXT] {0}\r\n")) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyPartTextPartial(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
long ccode;
size_t count;
unsigned long i;
long resultLen;
unsigned long textStart;
unsigned long textLen;
if (ConnWriteF(session->client.conn, "BODY[%lu", flag->bodyPart.part[0]) != -1) {
for (i = 1; i < flag->bodyPart.depth; i++) {
if (ConnWriteF(session->client.conn, ".%lu", flag->bodyPart.part[i]) != -1) {
continue;
}
return(STATUS_ABORT);
}
if (HasPart(FetchRequest->messageDetail.mimeInfo, &(flag->bodyPart)) && flag->bodyPart.isMessage) {
textStart = flag->bodyPart.partOffset + flag->bodyPart.messageHeaderLen;
textLen = flag->bodyPart.partLen - flag->bodyPart.messageHeaderLen;
if (flag->partial.start < textLen) {
if ((flag->partial.start + flag->partial.len) < textLen) {
/* all in range */
resultLen = flag->partial.len;
} else {
/* len out of range */
resultLen = textLen - flag->partial.start;
}
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %lu %lu\r\n", FetchRequest->message->guid, textStart + flag->partial.start, resultLen) != -1) {
if ((ccode = NMAPReadPropertyValueLength(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, ".TEXT]<%lu> {%lu}\r\n", flag->partial.start, (long)count) != -1) {
if (ConnReadToConn(session->store.conn, session->client.conn, count) == (int)count) {
if (NMAPReadCrLf(session->store.conn) == 2) {
return(STATUS_CONTINUE);
}
}
}
return(STATUS_ABORT);
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
/* start out of range */
}
if (ConnWriteF(session->client.conn, ".TEXT]<%lu> {0}\r\n", flag->partial.start) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyPartHeader(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
long ccode;
size_t count;
unsigned long i;
if (ConnWriteF(session->client.conn, "BODY[%lu", flag->bodyPart.part[0]) != -1) {
for (i = 1; i < flag->bodyPart.depth; i++) {
if (ConnWriteF(session->client.conn, ".%lu", flag->bodyPart.part[i]) != -1) {
continue;
}
return(STATUS_ABORT);
}
if (HasPart(FetchRequest->messageDetail.mimeInfo, &(flag->bodyPart)) && flag->bodyPart.isMessage ) {
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %lu %lu\r\n", FetchRequest->message->guid, flag->bodyPart.partOffset, flag->bodyPart.messageHeaderLen) != -1) {
if ((ccode = NMAPReadPropertyValueLength(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, ".HEADER] {%lu}\r\n", (long)count) != -1) {
if (ConnReadToConn(session->store.conn, session->client.conn, count) == (int)count) {
if (NMAPReadCrLf(session->store.conn) == 2) {
return(STATUS_CONTINUE);
}
}
}
return(STATUS_ABORT);
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
if (ConnWrite(session->client.conn, ".HEADER] {0}\r\n", strlen(".HEADER] {0}\r\n")) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyPartHeaderPartial(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
long ccode;
size_t count;
unsigned long i;
long resultLen;
if (ConnWriteF(session->client.conn, "BODY[%lu", flag->bodyPart.part[0]) != -1) {
for (i = 1; i < flag->bodyPart.depth; i++) {
if (ConnWriteF(session->client.conn, ".%lu", flag->bodyPart.part[i]) != -1) {
continue;
}
return(STATUS_ABORT);
}
if (HasPart(FetchRequest->messageDetail.mimeInfo, &(flag->bodyPart)) && flag->bodyPart.isMessage) {
if (flag->partial.start < flag->bodyPart.messageHeaderLen) {
if ((flag->partial.start + flag->partial.len) < flag->bodyPart.messageHeaderLen) {
/* all in range */
resultLen = flag->partial.len;
} else {
/* len out of range */
resultLen = flag->bodyPart.messageHeaderLen - flag->partial.start;
}
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %lu %lu\r\n", FetchRequest->message->guid, flag->bodyPart.partOffset + flag->partial.start, resultLen) != -1) {
if ((ccode = NMAPReadPropertyValueLength(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, ".HEADER]<%lu> {%lu}\r\n", flag->partial.start, (long)count) != -1) {
if (ConnReadToConn(session->store.conn, session->client.conn, count) == (int)count) {
if (NMAPReadCrLf(session->store.conn) == 2) {
return(STATUS_CONTINUE);
}
}
}
return(STATUS_ABORT);
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
/* start out of range */
}
if (ConnWriteF(session->client.conn, ".HEADER]<%lu> {0}\r\n", flag->partial.start) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyPartHeaderFields(void *param1, void *param2, void *param3)
{
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
long ccode;
unsigned long j;
char *fullHeader;
long fullHeaderLen;
char *resultHeader;
unsigned long resultHeaderLen;
if (ConnWriteF(session->client.conn, "BODY[%lu", flag->bodyPart.part[0]) != -1) {
for (j = 1; j < flag->bodyPart.depth; j++) {
if (ConnWriteF(session->client.conn, ".%lu", flag->bodyPart.part[j]) != -1) {
continue;
}
return(STATUS_ABORT);
}
} else {
return(STATUS_ABORT);
}
if (!(flag->fieldList.skip)) {
ccode = ConnWrite(session->client.conn, ".HEADER.FIELDS (", strlen(".HEADER.FIELDS ("));
} else {
ccode = ConnWrite(session->client.conn, ".HEADER.FIELDS.NOT (", strlen(".HEADER.FIELDS.NOT ("));
}
if (ccode != -1) {
for (j = 0; j < flag->fieldList.count - 1; j++) {
if (ConnWriteF(session->client.conn, "\"%s\" ", flag->fieldList.field[j]) != -1) {
continue;
}
return(STATUS_ABORT);
}
} else {
return(STATUS_ABORT);
}
if (ConnWriteF(session->client.conn, "\"%s\")]", flag->fieldList.field[j]) != -1) {
;
} else {
return(STATUS_ABORT);
}
if (HasPart(FetchRequest->messageDetail.mimeInfo, &(flag->bodyPart)) && flag->bodyPart.isMessage) {
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %lu %lu\r\n", FetchRequest->message->guid, flag->bodyPart.partOffset, flag->bodyPart.messageHeaderLen) != -1) {
ccode = NMAPReadResponse(session->store.conn, session->store.response, sizeof(session->store.response), TRUE);
if (ccode == 2001) {
fullHeaderLen = atoi(session->store.response);
fullHeader = MemMalloc(fullHeaderLen + 1);
if (fullHeader) {
if (NMAPReadCount(session->store.conn, fullHeader, fullHeaderLen) == fullHeaderLen) {
if (NMAPReadCrLf(session->store.conn) == 2) {
fullHeader[fullHeaderLen] = '0';
resultHeader = GetSubHeader(fullHeader, fullHeaderLen, flag->fieldList.field, flag->fieldList.count, flag->fieldList.skip, &resultHeaderLen);
MemFree(fullHeader);
if (resultHeader) {
if (!flag->isPartial) {
if (resultHeaderLen > 0) {
if (ConnWriteF(session->client.conn, " {%lu}\r\n", resultHeaderLen) != -1) {
if (ConnWrite(session->client.conn, resultHeader, resultHeaderLen) != -1) {
MemFree(resultHeader);
return(STATUS_CONTINUE);
}
}
MemFree(resultHeader);
return(STATUS_ABORT);
}
if (ConnWrite(session->client.conn, " {0}\r\n", sizeof(" {0}\r\n") - 1) != -1) {
MemFree(resultHeader);
return(STATUS_CONTINUE);
}
MemFree(resultHeader);
return(STATUS_ABORT);
}
if (resultHeaderLen > 0) {
if (flag->partial.start < resultHeaderLen) {
if ((flag->partial.start + flag->partial.len) < resultHeaderLen) {
if (ConnWriteF(session->client.conn, "<%lu> {%lu}\r\n", flag->partial.start, flag->partial.len) != -1) {
if (ConnWrite(session->client.conn, resultHeader + flag->partial.start, flag->partial.len) != -1) {
MemFree(resultHeader);
return(STATUS_CONTINUE);
}
}
MemFree(resultHeader);
return(STATUS_ABORT);
}
if (ConnWriteF(session->client.conn, "<%lu> {%lu}\r\n", flag->partial.start, resultHeaderLen - flag->partial.start) != -1) {
if (ConnWrite(session->client.conn, resultHeader + flag->partial.start, resultHeaderLen - flag->partial.start) != -1) {
MemFree(resultHeader);
return(STATUS_CONTINUE);
}
}
MemFree(resultHeader);
return(STATUS_ABORT);
}
}
if (ConnWriteF(session->client.conn, "<%lu> {0}\r\n", flag->partial.start) != -1) {
MemFree(resultHeader);
return(STATUS_CONTINUE);
}
MemFree(resultHeader);
return(STATUS_ABORT);
}
return(STATUS_MEMORY_ERROR);
}
}
MemFree(fullHeader);
return(STATUS_ABORT);
}
return(STATUS_MEMORY_ERROR);
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
if (!flag->isPartial) {
if (ConnWrite(session->client.conn, " {0}\r\n", strlen(" {0}\r\n")) != -1) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
if (ConnWriteF(session->client.conn, "<%lu> {0}\r\n", flag->partial.start) != -1) {
return(STATUS_CONTINUE);
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyPartMime(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
long ccode;
size_t count;
unsigned long i;
if (ConnWriteF(session->client.conn, "BODY[%lu", flag->bodyPart.part[0]) != -1) {
for (i = 1; i < flag->bodyPart.depth; i++) {
if (ConnWriteF(session->client.conn, ".%lu", flag->bodyPart.part[i]) != -1) {
continue;
}
return(STATUS_ABORT);
}
if (HasPart(FetchRequest->messageDetail.mimeInfo, &(flag->bodyPart))) {
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %lu %lu\r\n", FetchRequest->message->guid, flag->bodyPart.mimeOffset, flag->bodyPart.mimeLen) != -1) {
if ((ccode = NMAPReadPropertyValueLength(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, ".MIME] {%lu}\r\n", (long)count) != -1) {
if (ConnReadToConn(session->store.conn, session->client.conn, count) == (int)count) {
if (NMAPReadCrLf(session->store.conn) == 2) {
return(STATUS_CONTINUE);
}
}
}
return(STATUS_ABORT);
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
if (ConnWrite(session->client.conn, ".MIME] {0}\r\n", strlen(".MIME] {0}\r\n")) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyPartMimePartial(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
FetchStruct *FetchRequest = (FetchStruct *)param2;
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
long ccode;
size_t count;
unsigned long i;
if (ConnWriteF(session->client.conn, "BODY[%lu", flag->bodyPart.part[0]) != -1) {
for (i = 1; i < flag->bodyPart.depth; i++) {
if (ConnWriteF(session->client.conn, ".%lu", flag->bodyPart.part[i]) != -1) {
continue;
}
return(STATUS_ABORT);
}
if (HasPart(FetchRequest->messageDetail.mimeInfo, &(flag->bodyPart)) && (flag->partial.start < flag->bodyPart.mimeLen)) {
if ((flag->partial.start + flag->partial.len) < flag->bodyPart.mimeLen) {
count = flag->partial.len;
} else {
count = flag->bodyPart.mimeLen - flag->partial.start;
}
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " %lu %lu\r\n", FetchRequest->message->guid, flag->bodyPart.mimeOffset + flag->partial.start, (long)count) != -1) {
if ((ccode = NMAPReadPropertyValueLength(session->store.conn, "nmap.document", &count)) == 2001) {
if (ConnWriteF(session->client.conn, ".MIME] {%lu}\r\n", (long)count) != -1) {
if (ConnReadToConn(session->store.conn, session->client.conn, count) == (int)count) {
if (NMAPReadCrLf(session->store.conn) == 2) {
return(STATUS_CONTINUE);
}
}
}
return(STATUS_ABORT);
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
if (ConnWrite(session->client.conn, ".MIME] {0}\r\n", strlen(".MIME] {0}\r\n")) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
static long
FetchFlagResponderBodyPart(void *param1, void *param2, void *param3)
{
FetchFlagStruct *flag = (FetchFlagStruct *)param3;
return(flag->bodyPart.responder(param1, param2, param3));
}
static long
FetchFlagResponderFast(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
long ccode;
if ((ccode = FetchFlagResponderFlags(param1, param2, param3)) == STATUS_CONTINUE) {
if ((ccode = ConnWrite(session->client.conn, " ", 1)) != -1) {
if ((ccode = FetchFlagResponderInternalDate(param1, param2, param3)) == STATUS_CONTINUE) {
if ((ccode = ConnWrite(session->client.conn, " ", 1)) != -1) {
return(FetchFlagResponderRfc822Size(param1, param2, param3));
}
}
}
}
return(ccode);
}
static long
FetchFlagResponderFull(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
long ccode;
if ((ccode = FetchFlagResponderFlags(param1, param2, param3)) == STATUS_CONTINUE) {
if ((ccode = ConnWrite(session->client.conn, " ", 1)) != -1) {
if ((ccode = FetchFlagResponderInternalDate(param1, param2, param3)) == STATUS_CONTINUE) {
if ((ccode = ConnWrite(session->client.conn, " ", 1)) != -1) {
if ((ccode = FetchFlagResponderRfc822Size(param1, param2, param3)) == STATUS_CONTINUE) {
if ((ccode = ConnWrite(session->client.conn, " ", 1)) != -1) {
if ((ccode = FetchFlagResponderEnvelope(param1, param2, param3)) == STATUS_CONTINUE) {
if ((ccode = ConnWrite(session->client.conn, " ", 1)) != -1) {
return(FetchFlagResponderBodyStructure(param1, param2, param3));
}
}
}
}
}
}
}
}
return(ccode);
}
static long
FetchFlagResponderAll(void *param1, void *param2, void *param3)
{
ImapSession *session = (ImapSession *)param1;
long ccode;
if ((ccode = FetchFlagResponderFlags(param1, param2, param3)) == STATUS_CONTINUE) {
if ((ccode = ConnWrite(session->client.conn, " ", 1)) != -1) {
if ((ccode = FetchFlagResponderInternalDate(param1, param2, param3)) == STATUS_CONTINUE) {
if ((ccode = ConnWrite(session->client.conn, " ", 1)) != -1) {
if ((ccode = FetchFlagResponderRfc822Size(param1, param2, param3)) == STATUS_CONTINUE) {
if ((ccode = ConnWrite(session->client.conn, " ", 1)) != -1) {
return(FetchFlagResponderEnvelope(param1, param2, param3));
}
}
}
}
}
}
return(ccode);
}
static unsigned long
ParseFetchFlagPartial(char **parentPtr, FetchFlagStruct *flag)
{
char *ptr = *parentPtr;
char *end;
uintmax_t start;
uintmax_t length;
if (!isdigit((unsigned char)*ptr)) return F_PARSE_ERROR;
errno = 0;
start = strtoumax(ptr, &end, 10);
if (errno == ERANGE || end == ptr || start > UINT64_MAX || *end != '.')
return F_PARSE_ERROR;
ptr = end + 1;
if (!isdigit((unsigned char)*ptr) || *ptr == '0') return F_PARSE_ERROR;
errno = 0;
length = strtoumax(ptr, &end, 10);
if (errno == ERANGE || end == ptr || length > UINT64_MAX ||
length == 0 || *end != '>') return F_PARSE_ERROR;
flag->partial.start = (uint64_t)start;
flag->partial.len = (uint64_t)length;
flag->isPartial = TRUE;
*parentPtr = end + 1;
return STATUS_CONTINUE;
}
__inline static unsigned long
ParseHeaderFields(char **parentPtr, FetchFlagStruct *flag)
{
char *start;
char *end;
char *current;
char *fieldBuffer;
unsigned long fieldBufferLen;
unsigned long spaceCount;
unsigned char len;
unsigned long fieldCount;
unsigned long ccode;
end = strchr(*parentPtr, ')');
if (end) {
fieldBufferLen = end - *parentPtr;
fieldBuffer = MemMalloc(fieldBufferLen + 1);
if (fieldBuffer) {
flag->hasAllocated |= ALLOCATED_FIELDS;
memcpy(fieldBuffer, *parentPtr, fieldBufferLen);
fieldBuffer[fieldBufferLen] = '\0';
start = fieldBuffer;
end = fieldBuffer + fieldBufferLen;
current = start;
spaceCount = 0;
do {
if (*current != ' ') {
current++;
continue;
}
spaceCount++;
current++;
} while (current < end);
flag->fieldList.fieldRequest = fieldBuffer;
if (spaceCount < FETCH_FIELD_ALLOC_THRESHOLD) {
flag->fieldList.field = &(flag->fieldList.fieldEmbedded[0]);
} else {
flag->fieldList.field = MemCalloc((spaceCount + 1), sizeof(char *));
if (flag->fieldList.field) {
flag->hasAllocated |= ALLOCATED_FIELD_POINTERS;
} else {
return(F_SYSTEM_ERROR);
}
}
fieldCount = 0;
current = start;
do {
if (*current == '"') {
current++;
start = current;
do {
if (current < end) {
if (*current != '"') {
current++;
continue;
}
if (*(current - 1) != '\\') {
len = current - start;
*current = '\0';
current++;
break;
}
current++;
continue;
}
return(F_PARSE_ERROR);
} while (TRUE);
} else {
start = current;
do {
if (current < end) {
if (*current != ' ') {
current++;
continue;
}
}
len = current - start;
break;
} while (TRUE);
}
/* we've got a header name */
if (len > 0) {
flag->fieldList.field[fieldCount] = start;
fieldCount++;
flag->fieldList.count = fieldCount;
if (current < end) {
if (*current == ' ') {
*current = '\0';
current++;
continue;
}
return(F_PARSE_ERROR);
}
break;
}
return(F_PARSE_ERROR);
} while (current < end);
/* we have fields; lets go back to the original buffer and see what comes after the fields */
current = *parentPtr + fieldBufferLen + 1;
if (*current == ']') {
current++;
if (*current != '<') {
flag->isPartial = FALSE;
*parentPtr = current;
return(0);
}
current++;
ccode = ParseFetchFlagPartial(&current, flag);
if (ccode == 0) {
*parentPtr = current;
return(0);
}
return(ccode);
}
return(F_PARSE_ERROR);
}
return(F_SYSTEM_ERROR);
}
return(F_PARSE_ERROR);
}
__inline static unsigned long
ParseFetchFlagHeaderFields(char **parentPtr, FetchFlagStruct *flag)
{
return(ParseHeaderFields(parentPtr, flag));
}
__inline static unsigned long
ParseFetchFlagHeaderFieldsNot(char **parentPtr, FetchFlagStruct *flag)
{
flag->fieldList.skip = TRUE;
return(ParseHeaderFields(parentPtr, flag));
}
static FetchFlag FetchFlagsSection[] = {
{"TEXT]", sizeof("TEXT]") - 1, F_BODY_TEXT, NULL, FetchFlagResponderBodyPartText},
{"TEXT]<", sizeof("TEXT]<") - 1, F_BODY_TEXT, ParseFetchFlagPartial, FetchFlagResponderBodyPartTextPartial},
{"MIME]", sizeof("MIME]") - 1, F_BODY_MIME, NULL, FetchFlagResponderBodyPartMime},
{"MIME]<", sizeof("MIME]<") - 1, F_BODY_MIME, ParseFetchFlagPartial, FetchFlagResponderBodyPartMimePartial},
{"HEADER]", sizeof("HEADER]") - 1, F_BODY_HEADER, NULL, FetchFlagResponderBodyPartHeader},
{"HEADER]<", sizeof("HEADER]<") - 1, F_BODY_HEADER, ParseFetchFlagPartial, FetchFlagResponderBodyPartHeaderPartial},
{"HEADER.FIELDS (", sizeof("HEADER.FIELDS (") - 1, F_BODY_HEADER_FIELDS, ParseFetchFlagHeaderFields, FetchFlagResponderBodyPartHeaderFields},
{"HEADER.FIELDS.NOT (", sizeof("HEADER.FIELDS.NOT (") - 1, F_BODY_HEADER_FIELDS_NOT, ParseFetchFlagHeaderFieldsNot, FetchFlagResponderBodyPartHeaderFields},
{NULL, 0, 0, NULL, NULL}
};
static unsigned long
ParseFetchFlagBodyPart(char **parentPtr, FetchFlagStruct *flag)
{
char *ptr;
unsigned long *tmpPart;
long sectionId;
unsigned long ccode;
ptr = *parentPtr;
flag->bodyPart.part = (unsigned long *)&(flag->bodyPart.partEmbedded);
do {
if (isdigit(*ptr)) {
if (flag->bodyPart.depth < PART_DEPTH_ALLOC_THRESHOLD) {
/* No allocation needed; use space in the structure */
;
} else if (flag->bodyPart.depth > PART_DEPTH_ALLOC_THRESHOLD) {
/* Grow the existing allocated structure */
tmpPart = MemRealloc(flag->bodyPart.part, sizeof(unsigned long) * (flag->bodyPart.depth + 1));
if (tmpPart) {
flag->bodyPart.part = tmpPart;
} else {
return(F_SYSTEM_ERROR);
}
} else {
/* switch from embedded space to allocated space */
tmpPart = MemMalloc(sizeof(unsigned long) * (PART_DEPTH_ALLOC_THRESHOLD + 1));
if (tmpPart) {
flag->hasAllocated |= ALLOCATED_PART_NUMBERS;
/* copy the data from embedded array to allocated array */
memcpy(tmpPart, flag->bodyPart.part, sizeof(unsigned long) * PART_DEPTH_ALLOC_THRESHOLD);
flag->bodyPart.part = tmpPart;
} else {
return(F_SYSTEM_ERROR);
}
}
{
char *partEnd;
uintmax_t part = strtoumax(ptr, &partEnd, 10);
if (partEnd == ptr || part > UINT32_MAX)
return(F_PARSE_ERROR);
flag->bodyPart.part[flag->bodyPart.depth] =
(unsigned long)part;
flag->bodyPart.depth++;
ptr = partEnd;
}
if (*ptr == '.') {
ptr++;
continue;
}
if (*ptr == ']') {
ptr++;
if (*ptr != '<') {
flag->bodyPart.responder = FetchFlagResponderBodyPartNumber;
*parentPtr = ptr;
return(0);
}
ptr++;
ccode = ParseFetchFlagPartial(&ptr, flag);
if (ccode == 0) {
flag->bodyPart.responder = FetchFlagResponderBodyPartNumberPartial;
*parentPtr = ptr;
return(0);
}
return(ccode);
}
return(F_PARSE_ERROR);
}
sectionId = BongoKeywordBegins(FetchFlagsSectionIndex, ptr);
if (sectionId != -1) {
ptr += FetchFlagsSection[sectionId].nameLen;
flag->bodyPart.responder = FetchFlagsSection[sectionId].responder;
if (FetchFlagsSection[sectionId].parser == NULL) {
*parentPtr = ptr;
return(0);
}
ccode = FetchFlagsSection[sectionId].parser(&ptr, flag);
if (ccode == 0) {
*parentPtr = ptr;
return(0);
}
return(ccode);
}
return(F_PARSE_ERROR);
} while(TRUE);
}
static BOOL
AppendBinaryPart(FetchFlagStruct *flag, unsigned long part)
{
unsigned long *new_parts;
if (flag->bodyPart.depth < PART_DEPTH_ALLOC_THRESHOLD) {
flag->bodyPart.part = flag->bodyPart.partEmbedded;
} else if (flag->bodyPart.depth == PART_DEPTH_ALLOC_THRESHOLD) {
new_parts = MemMalloc(sizeof(*new_parts) *
(PART_DEPTH_ALLOC_THRESHOLD + 1));
if (!new_parts) return FALSE;
memcpy(new_parts, flag->bodyPart.partEmbedded,
sizeof(*new_parts) * PART_DEPTH_ALLOC_THRESHOLD);
flag->bodyPart.part = new_parts;
flag->hasAllocated |= ALLOCATED_PART_NUMBERS;
} else {
new_parts = MemRealloc(flag->bodyPart.part,
sizeof(*new_parts) *
(flag->bodyPart.depth + 1));
if (!new_parts) return FALSE;
flag->bodyPart.part = new_parts;
}
flag->bodyPart.part[flag->bodyPart.depth++] = part;
return TRUE;
}
static unsigned long
ParseFetchFlagBinaryCommon(char **parent_ptr, FetchFlagStruct *flag,
BOOL size_only)
{
char *ptr = *parent_ptr;
flag->binarySize = size_only;
flag->bodyPart.part = flag->bodyPart.partEmbedded;
if (*ptr != ']') {
for (;;) {
char *end;
uintmax_t part;
if (*ptr < '1' || *ptr > '9') return F_PARSE_ERROR;
errno = 0;
part = strtoumax(ptr, &end, 10);
if (errno == ERANGE || end == ptr || part > UINT32_MAX)
return F_PARSE_ERROR;
if (!AppendBinaryPart(flag, (unsigned long)part))
return F_SYSTEM_ERROR;
ptr = end;
if (*ptr == '.') {
ptr++;
continue;
}
if (*ptr != ']') return F_PARSE_ERROR;
break;
}
}
ptr++;
if (*ptr == '<') {
unsigned long result;
if (size_only) return F_PARSE_ERROR;
ptr++;
result = ParseFetchFlagPartial(&ptr, flag);
if (result != STATUS_CONTINUE) return result;
}
*parent_ptr = ptr;
return STATUS_CONTINUE;
}
static unsigned long
ParseFetchFlagBinary(char **parent_ptr, FetchFlagStruct *flag)
{
return ParseFetchFlagBinaryCommon(parent_ptr, flag, FALSE);
}
static unsigned long
ParseFetchFlagBinarySize(char **parent_ptr, FetchFlagStruct *flag)
{
return ParseFetchFlagBinaryCommon(parent_ptr, flag, TRUE);
}
#define FETCH_ATT_FLAGS \
{"BINARY.PEEK[", sizeof("BINARY.PEEK[") - 1, F_BINARY, ParseFetchFlagBinary, FetchFlagResponderBinary}, \
{"BINARY.SIZE[", sizeof("BINARY.SIZE[") - 1, F_BINARY_SIZE, ParseFetchFlagBinarySize, FetchFlagResponderBinary}, \
{"BINARY[", sizeof("BINARY[") - 1, F_BINARY | F_BODY_SEEN, ParseFetchFlagBinary, FetchFlagResponderBinary}, \
{"UID", sizeof("UID") - 1, F_UID, NULL, FetchFlagResponderUid}, \
{"FLAGS", sizeof("FLAGS") - 1, F_FLAGS, NULL, FetchFlagResponderFlags}, \
{"ENVELOPE", sizeof("ENVELOPE") - 1, F_ENVELOPE, NULL, FetchFlagResponderEnvelope}, \
{"XSENDER", sizeof("XSENDER") - 1, F_XSENDER, NULL, FetchFlagResponderXSender}, \
{"INTERNALDATE", sizeof("INTERNALDATE") - 1, F_INTERNALDATE, NULL, FetchFlagResponderInternalDate}, \
{"BODYSTRUCTURE", sizeof("BODYSTRUCTURE") - 1, F_BODYSTRUCTURE, NULL, FetchFlagResponderBodyStructure}, \
{"RFC822.TEXT", sizeof("RFC822.TEXT") - 1, F_RFC822_TEXT | F_BODY_SEEN, NULL, FetchFlagResponderRfc822Text}, \
{"RFC822.SIZE", sizeof("RFC822.SIZE") - 1, F_RFC822_SIZE, NULL, FetchFlagResponderRfc822Size}, \
{"RFC822.HEADER", sizeof("RFC822.HEADER") - 1, F_RFC822_HEADER, NULL, FetchFlagResponderRfc822Header}, \
{"RFC822", sizeof("RFC822") - 1, F_RFC822_BOTH | F_BODY_SEEN, NULL, FetchFlagResponderRfc822}, \
{"BODY", sizeof("BODY") - 1, F_BODY, NULL, FetchFlagResponderBody}, \
{"BODY[HEADER]", sizeof("BODY[HEADER]") - 1, F_BODY_HEADER | F_BODY_SEEN, NULL, FetchFlagResponderBodyHeader}, \
{"BODY[HEADER]<", sizeof("BODY[HEADER]<") - 1, F_BODY_HEADER_PARTIAL | F_BODY_SEEN, ParseFetchFlagPartial, FetchFlagResponderBodyHeaderPartial}, \
{"BODY[TEXT]", sizeof("BODY[TEXT]") - 1, F_BODY_TEXT | F_BODY_SEEN, NULL, FetchFlagResponderBodyText}, \
{"BODY[TEXT]<", sizeof("BODY[TEXT]<") - 1, F_BODY_TEXT_PARTIAL | F_BODY_SEEN, ParseFetchFlagPartial, FetchFlagResponderBodyTextPartial}, \
{"BODY[]", sizeof("BODY[]") - 1, F_BODY_BOTH | F_BODY_SEEN, NULL, FetchFlagResponderBodyBoth}, \
{"BODY[]<", sizeof("BODY[]<") - 1, F_BODY_BOTH_PARTIAL | F_BODY_SEEN, ParseFetchFlagPartial, FetchFlagResponderBodyBothPartial}, \
{"BODY[MIME]", sizeof("BODY[MIME]") - 1, F_BODY_MIME | F_BODY_SEEN, NULL, FetchFlagResponderBodyMime}, \
{"BODY[MIME]<", sizeof("BODY[MIME]<") - 1, F_BODY_MIME_PARTIAL | F_BODY_SEEN, ParseFetchFlagPartial, FetchFlagResponderBodyMimePartial}, \
{"BODY[HEADER.FIELDS (", sizeof("BODY[HEADER.FIELDS (") - 1, F_BODY_HEADER_FIELDS | F_BODY_SEEN, ParseFetchFlagHeaderFields, FetchFlagResponderBodyHeaderFields}, \
{"BODY[HEADER.FIELDS.NOT (", sizeof("BODY[HEADER.FIELDS.NOT (") - 1, F_BODY_HEADER_FIELDS_NOT | F_BODY_SEEN, ParseFetchFlagHeaderFieldsNot, FetchFlagResponderBodyHeaderFields}, \
{"BODY[", sizeof("BODY[") - 1, F_BODY_PART | F_BODY_SEEN, ParseFetchFlagBodyPart, FetchFlagResponderBodyPart}, \
{"BODY.PEEK[TEXT]", sizeof("BODY.PEEK[TEXT]") - 1, F_BODY_TEXT, NULL, FetchFlagResponderBodyText}, \
{"BODY.PEEK[TEXT]<", sizeof("BODY.PEEK[TEXT]<") - 1, F_BODY_TEXT_PARTIAL, ParseFetchFlagPartial, FetchFlagResponderBodyTextPartial}, \
{"BODY.PEEK[HEADER]", sizeof("BODY.PEEK[HEADER]") - 1, F_BODY_HEADER, NULL, FetchFlagResponderBodyHeader}, \
{"BODY.PEEK[HEADER]<", sizeof("BODY.PEEK[HEADER]<") - 1, F_BODY_HEADER | F_BODY_HEADER_PARTIAL, ParseFetchFlagPartial, FetchFlagResponderBodyHeaderPartial}, \
{"BODY.PEEK[]", sizeof("BODY.PEEK[]") - 1, F_BODY_BOTH, NULL, FetchFlagResponderBodyBoth}, \
{"BODY.PEEK[]<", sizeof("BODY.PEEK[]<") - 1, F_BODY_BOTH | F_BODY_BOTH_PARTIAL, ParseFetchFlagPartial, FetchFlagResponderBodyBothPartial}, \
{"BODY.PEEK[MIME]", sizeof("BODY.PEEK[MIME]") - 1, F_BODY_MIME, NULL, FetchFlagResponderBodyMime}, \
{"BODY.PEEK[MIME]<", sizeof("BODY.PEEK[MIME]<") - 1, F_BODY_MIME | F_BODY_MIME_PARTIAL, ParseFetchFlagPartial, FetchFlagResponderBodyMimePartial}, \
{"BODY.PEEK[HEADER.FIELDS (", sizeof("BODY.PEEK[HEADER.FIELDS (") - 1, F_BODY_HEADER_FIELDS, ParseFetchFlagHeaderFields, FetchFlagResponderBodyHeaderFields}, \
{"BODY.PEEK[HEADER.FIELDS.NOT (", sizeof("BODY.PEEK[HEADER.FIELDS.NOT (") - 1, F_BODY_HEADER_FIELDS_NOT, ParseFetchFlagHeaderFieldsNot, FetchFlagResponderBodyHeaderFields}, \
{"BODY.PEEK[", sizeof("BODY.PEEK[") - 1, F_BODY_PART, ParseFetchFlagBodyPart, FetchFlagResponderBodyPart},
static FetchFlag FetchFlagsSolo[] = {
{"FAST", sizeof("FAST") - 1, F_FLAGS | F_INTERNALDATE | F_RFC822_SIZE, NULL, FetchFlagResponderFast},
{"FULL", sizeof("FULL") - 1, F_FLAGS | F_INTERNALDATE | F_RFC822_SIZE | F_ENVELOPE | F_BODYSTRUCTURE, NULL, FetchFlagResponderFull},
{"ALL", sizeof("ALL") - 1, F_FLAGS | F_INTERNALDATE | F_RFC822_SIZE | F_ENVELOPE, NULL, FetchFlagResponderAll},
FETCH_ATT_FLAGS
{NULL, 0, 0, NULL, NULL}
};
static FetchFlag FetchFlagsAtt[] = {
FETCH_ATT_FLAGS
{NULL, 0, 0, NULL, NULL}
};
__inline static void
RemoveLastFlag(FetchStruct *FetchRequest)
{
FetchRequest->flagCount--;
}
__inline static FetchFlagStruct *
AddNewFlag(FetchStruct *FetchRequest, FetchResponder responder)
{
FetchFlagStruct *newFlag;
FetchFlagStruct *tmpFlag;
if (FetchRequest->flagCount < FETCH_FLAG_ALLOC_THRESHOLD) {
FetchRequest->flag = FetchRequest->flagEmbedded;
newFlag = &(FetchRequest->flag[FetchRequest->flagCount]);
newFlag->responder = responder;
FetchRequest->flagCount++;
return(newFlag);
}
if (FetchRequest->flagCount > FETCH_FLAG_ALLOC_THRESHOLD) {
tmpFlag = MemRealloc(FetchRequest->flag, sizeof(FetchFlagStruct) * (FetchRequest->flagCount + 1));
if (tmpFlag) {
FetchRequest->flag = tmpFlag;
newFlag = &(FetchRequest->flag[FetchRequest->flagCount]);
memset(newFlag, 0, sizeof(FetchFlagStruct));
newFlag->responder = responder;
FetchRequest->flagCount++;
return(newFlag);
}
return(NULL);
}
tmpFlag = MemMalloc(sizeof(FetchFlagStruct) * (FETCH_FLAG_ALLOC_THRESHOLD + 1));
if (tmpFlag) {
FetchRequest->hasAllocated |= ALLOCATED_FLAGS;
memcpy(tmpFlag, FetchRequest->flagEmbedded, sizeof(FetchFlagStruct) * FETCH_FLAG_ALLOC_THRESHOLD);
FetchRequest->flag = tmpFlag;
newFlag = &(FetchRequest->flag[FetchRequest->flagCount]);
memset(newFlag, 0, sizeof(FetchFlagStruct));
newFlag->responder = responder;
FetchRequest->flagCount++;
return(newFlag);
}
return(NULL);
}
__inline static long
ParseFetchArguments(ImapSession *session, char *ptr, FetchStruct *FetchRequest)
{
char *Items;
long allFlags = FetchRequest->flags;
long flagID;
FetchFlag flagInfo;
FetchFlagStruct *currentFlag;
long ccode;
/* Grab the range */
ccode = GrabArgument(session, &ptr, &FetchRequest->messageSet);
if (ccode == STATUS_CONTINUE) {
if (FetchRequest->messageSet) {
if (*ptr == ' ') {
ptr++;
if (*ptr != '\0') {
/* parse the fetch flags */
if (*ptr == '(') {
/* we have parens so we can have one or more 'fetch-att' flags */
ccode = GrabArgument(session, &ptr, &Items);
if (ccode == STATUS_CONTINUE) {
ptr = Items - 1;
do {
ptr++;
flagID = BongoKeywordBegins(FetchFlagsAttIndex, ptr);
if (flagID != -1) {
flagInfo = FetchFlagsAtt[flagID];
if ((flagInfo.value &
(F_BINARY | F_BINARY_SIZE)) &&
!(session->client.enabledCapabilities &
IMAP_ENABLE_REV2)) {
MemFree(Items);
return F_PARSE_ERROR;
}
ptr += flagInfo.nameLen;
currentFlag = AddNewFlag(FetchRequest, flagInfo.responder);
if (currentFlag) {
if (flagInfo.parser == NULL) {
/* make sure uid only gets added once; it gets added automatically for uid fetch requests */
if ((!(flagInfo.value & F_UID)) || (!(allFlags & F_UID))) {
allFlags |= flagInfo.value;
} else {
RemoveLastFlag(FetchRequest);
}
continue;
}
allFlags |= flagInfo.value;
ccode = flagInfo.parser(&ptr, currentFlag);
FetchRequest->hasAllocated |= currentFlag->hasAllocated;
if (ccode == 0) {
continue;
}
return(ccode);
}
return(F_SYSTEM_ERROR);
}
MemFree(Items);
return(F_PARSE_ERROR);
} while (*ptr == ' ');
if (*ptr == '\0') {
MemFree(Items);
return(allFlags);
}
MemFree(Items);
}
return(F_PARSE_ERROR);
}
/* there are no parens so there can only be one macro or one 'fetch-att' flag */
flagID = BongoKeywordBegins(FetchFlagsSoloIndex, ptr);
if (flagID != -1) {
flagInfo = FetchFlagsSolo[flagID];
if ((flagInfo.value & (F_BINARY | F_BINARY_SIZE)) &&
!(session->client.enabledCapabilities &
IMAP_ENABLE_REV2))
return F_PARSE_ERROR;
ptr += flagInfo.nameLen;
currentFlag = AddNewFlag(FetchRequest, flagInfo.responder);
if (currentFlag) {
if (flagInfo.parser == NULL) {
if (*ptr == '\0') {
if ((!(flagInfo.value & F_UID)) || (!(allFlags & F_UID))) {
allFlags |= flagInfo.value;
} else {
RemoveLastFlag(FetchRequest);
}
return(allFlags);
}
return(F_PARSE_ERROR);
}
allFlags |= flagInfo.value;
ccode = flagInfo.parser(&ptr, currentFlag);
FetchRequest->hasAllocated |= currentFlag->hasAllocated;
if (ccode == 0) {
if (*ptr == '\0') {
return(allFlags);
}
return(F_PARSE_ERROR);
}
return(ccode);
}
return(F_SYSTEM_ERROR);
}
}
}
}
return(F_PARSE_ERROR);
}
return(ccode);
}
__inline static long
SetMessageSeenFlag(ImapSession *session, FetchStruct *FetchRequest)
{
long ccode;
unsigned long newFlags;
char *ptr;
if (NMAPSendCommandF(session->store.conn, "FLAG %" PRIx64 " +%lu\r\n", FetchRequest->message->guid, (unsigned long)STORE_MSG_FLAG_SEEN) != -1) {
ccode = NMAPReadResponse(session->store.conn, session->store.response, sizeof(session->store.response), TRUE);
if (ccode == 1000) {
if (!(FetchRequest->flags & F_FLAGS)) {
/* This fetch request does allow flags responses, but we have to wait for later to send them to the client */
ptr = strchr(session->store.response, ' ');
if (ptr) {
newFlags = atol(ptr + 1);
RememberFlags(session->folder.selected.events.flags, FetchRequest->message->guid, newFlags);
}
}
return(STATUS_CONTINUE);
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
__inline static long
GetMessageHeader(ImapSession *session, FetchStruct *FetchRequest)
{
int ccode;
size_t count;
/* Request the header */
if (NMAPSendCommandF(session->store.conn, "READ %" PRIx64 " 0 %" PRIu64 "\r\n", FetchRequest->message->guid, FetchRequest->message->headerSize) != -1) {
if ((ccode = NMAPReadPropertyValueLength(session->store.conn, "nmap.document", &count)) == 2001) {
if (count > INT_MAX) {
return STATUS_FETCH_LIMIT;
}
FetchRequest->message->headerSize = count;
FetchRequest->messageDetail.header = MemMalloc(FetchRequest->message->headerSize + 1);
if (FetchRequest->messageDetail.header) {
if ((ccode = NMAPReadCount(session->store.conn, FetchRequest->messageDetail.header, FetchRequest->message->headerSize)) > 0) {
if ((size_t)ccode == count) {
if (NMAPReadCrLf(session->store.conn) == 2) {
FetchRequest->messageDetail.header[FetchRequest->message->headerSize] = '\0';
FetchRequest->message->headerSize = MakeRFC822Header(
FetchRequest->messageDetail.header, count);
return(0);
}
}
}
}
return(STATUS_ABORT);
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_NMAP_COMM_ERROR);
}
__inline static long
GetMimeInfo(ImapSession *session, FetchStruct *FetchRequest)
{
long ccode;
if (FetchRequest->messageDetail.mimeInfo == NULL) {
FetchRequest->messageDetail.mimeInfo = g_array_new(FALSE, FALSE, sizeof(char *));
if (! FetchRequest->messageDetail.mimeInfo) {
return(STATUS_MEMORY_ERROR);
}
} else {
g_array_free(FetchRequest->messageDetail.mimeInfo, TRUE);
}
if (NMAPSendCommandF(session->store.conn, "MIME %" PRIx64 "\r\n", FetchRequest->message->guid) != -1) {
ccode = NMAPReadResponse(session->store.conn, session->store.response, sizeof(session->store.response), FALSE);
while(ccode != 1000) {
if ((ccode > 2001) && (ccode < 2005)) {
char *entry;
size_t len = sizeof(char) * (strlen(session->store.response) + 1);
entry = MemMalloc(len);
memcpy(entry, session->store.response, len);
g_array_append_val(FetchRequest->messageDetail.mimeInfo, entry);
ccode = NMAPReadResponse(session->store.conn, session->store.response, sizeof(session->store.response), FALSE);
continue;
}
return(CheckForNMAPCommError(ccode));
}
return(STATUS_CONTINUE);
}
return(STATUS_NMAP_COMM_ERROR);
}
__inline static long
SendResponseForFetchFlags(ImapSession *session, FetchStruct *FetchRequest)
{
long ccode;
unsigned long i;
if (ConnWriteF(session->client.conn, "* %lu FETCH (", FetchRequest->messageDetail.sequenceNumber + 1) != -1) {
if (FetchRequest->flagCount > 0) {
i = 0;
do {
if ((ccode = FetchRequest->flag[i].responder(session, FetchRequest, &(FetchRequest->flag[i]))) == STATUS_CONTINUE) {
i++;
if (i < FetchRequest->flagCount) {
if (ConnWrite(session->client.conn, " ", 1) != -1) {
continue;
}
return(STATUS_ABORT);
}
break;
}
return(ccode);
} while(TRUE);
}
if (ConnWrite(session->client.conn, ")\r\n", 3) != -1) {
return(STATUS_CONTINUE);
}
}
return(STATUS_ABORT);
}
__inline static long
MessageDetailsGet(ImapSession *session, FetchStruct *FetchRequest)
{
long ccode;
BOOL needLegacyUtf8Check =
!IMAP_UTF8_ENABLED(session->client.enabledCapabilities) &&
(FetchRequest->flags & F_UTF8_SENSITIVE);
if ((FetchRequest->flags & F_NEED_HEADER) || needLegacyUtf8Check) {
if ((ccode = GetMessageHeader(session, FetchRequest)) == 0) {
if (needLegacyUtf8Check && IMAPMessageHeaderContainsEightBit(
(const unsigned char *)FetchRequest->messageDetail.header,
FetchRequest->message->headerSize))
return STATUS_UTF8_REQUIRED;
} else {
return(ccode);
}
}
if (FetchRequest->flags & F_NEED_MIME) {
if ((ccode = GetMimeInfo(session, FetchRequest)) == STATUS_CONTINUE) {
;
} else {
return(ccode);
}
}
return(STATUS_CONTINUE);
}
__inline static long
SetSeenFlag(ImapSession *session, FetchStruct *FetchRequest)
{
/* Set the flags - got to do this here so the flags include the change - see the rfc */
if ((FetchRequest->flags & F_BODY_SEEN) && !session->folder.selected.readOnly) {
return(SetMessageSeenFlag(session, FetchRequest));
}
return(STATUS_CONTINUE);
}
__inline static long
SendResponseForMessage(ImapSession *session, FetchStruct *FetchRequest)
{
long ccode;
ccode = MessageDetailsGet(session, FetchRequest);
if (ccode == STATUS_CONTINUE) {
if (FetchRequest->flags & (F_BINARY | F_BINARY_SIZE))
ccode = PrepareBinaryResults(session, FetchRequest);
if (ccode == STATUS_CONTINUE &&
(ccode = SetSeenFlag(session, FetchRequest)) == STATUS_CONTINUE)
ccode = SendResponseForFetchFlags(session, FetchRequest);
}
BinaryResultsFree(FetchRequest);
MessageDetailsFree(&(FetchRequest->messageDetail));
return(ccode);
}
__inline static BOOL
MessageStillExists(FetchStruct *FetchRequest)
{
if (!(FetchRequest->message->flags & STORE_MSG_FLAG_PURGED)) {
return(TRUE);
}
return(FALSE);
}
__inline static long
SendResponseForMessageRange(ImapSession *session, FetchStruct *FetchRequest, unsigned long rangeStart, unsigned long rangeEnd, BOOL *purgedMessage)
{
long ccode;
unsigned long currentMessage;
currentMessage = rangeStart;
ccode = STATUS_CONTINUE;
do {
FetchRequest->message = &(session->folder.selected.message[currentMessage]);
FetchRequest->messageDetail.sequenceNumber = currentMessage;
if (MessageStillExists(FetchRequest)) {
if ((ccode = SendResponseForMessage(session, FetchRequest)) == STATUS_CONTINUE) {
currentMessage++;
continue;
}
break;
}
currentMessage++;
*purgedMessage = TRUE;
} while (currentMessage <= rangeEnd);
return(ccode);
}
int
ImapCommandFetch(void *param)
{
ImapSession *session = (ImapSession *)param;
FetchStruct FetchRequest;
char *resolvedSet = NULL;
char *nextRange;
unsigned long rangeStart;
unsigned long rangeEnd;
int ccode;
BOOL purgedMessage = FALSE;
BOOL emptySet = FALSE;
if ((ccode = CheckState(session, STATE_SELECTED)) == STATUS_CONTINUE) {
/* rfc 2180 discourages purge notifications during the store command */
if ((ccode = EventsSend(session, STORE_EVENT_NEW | STORE_EVENT_FLAG)) == STATUS_CONTINUE) {
memset(&FetchRequest, 0, sizeof(FetchRequest));
FetchRequest.flags = ParseFetchArguments(session, session->command.buffer + 6, &FetchRequest);
if (!(FetchRequest.flags & F_ERROR)) {
ccode = ImapSearchResolveMessageSet(
session, FetchRequest.messageSet, FALSE,
&resolvedSet, &emptySet);
if (ccode != STATUS_CONTINUE) {
FreeFetchResourcesFailure(&FetchRequest);
return SendError(session->client.conn,
session->command.tag, "FETCH", ccode);
}
if (resolvedSet) {
MemFree(FetchRequest.messageSet);
FetchRequest.messageSet = resolvedSet;
resolvedSet = NULL;
}
if (emptySet) {
FreeFetchResourcesSuccess(&FetchRequest);
return SendOk(session, "FETCH");
}
nextRange = FetchRequest.messageSet;
do {
ccode = GetMessageRange(session->folder.selected.message, session->folder.selected.messageCount, &(nextRange), &rangeStart, &rangeEnd, FALSE);
if (ccode == STATUS_CONTINUE) {
if ((ccode = SendResponseForMessageRange(session, &FetchRequest, rangeStart, rangeEnd, &purgedMessage)) == STATUS_CONTINUE) {
continue;
}
}
FreeFetchResourcesFailure(&FetchRequest);
return(SendError(session->client.conn, session->command.tag, "FETCH", ccode));
} while (nextRange);
FreeFetchResourcesSuccess(&FetchRequest);
if (!purgedMessage) {
return(SendOk(session, "FETCH"));
}
return(SendError(session->client.conn, session->command.tag, "FETCH", STATUS_REQUESTED_MESSAGE_NO_LONGER_EXISTS));
}
if (!(FetchRequest.flags & F_SYSTEM_ERROR)) {
FreeFetchResourcesFailure(&FetchRequest);
return(SendError(session->client.conn, session->command.tag, "FETCH", STATUS_INVALID_ARGUMENT));
}
FreeFetchResourcesFailure(&FetchRequest);
return(SendError(session->client.conn, session->command.tag, "FETCH", STATUS_MEMORY_ERROR));
}
}
return(SendError(session->client.conn, session->command.tag, "FETCH", ccode));
}
int
ImapCommandUidFetch(void *param)
{
ImapSession *session = (ImapSession *)param;
FetchStruct FetchRequest;
char *resolvedSet = NULL;
int ccode;
char *nextRange;
unsigned long rangeStart;
unsigned long rangeEnd;
BOOL purgedMessage = FALSE;
BOOL emptySet = FALSE;
FetchFlagStruct *firstFlag;
if ((ccode = CheckState(session, STATE_SELECTED)) == STATUS_CONTINUE) {
/* rfc 2180 discourages purge notifications during the store command */
if ((ccode = EventsSend(session, STORE_EVENT_NEW | STORE_EVENT_FLAG)) == STATUS_CONTINUE) {
memmove(session->command.buffer, session->command.buffer + 4, strlen(session->command.buffer + 4) + 1);
memset(&FetchRequest, 0, sizeof(FetchRequest));
/* Respond with uid even if it has not been explicitly requested */
FetchRequest.flags = F_UID;
firstFlag = AddNewFlag(&FetchRequest, FetchFlagResponderUid);
if (firstFlag) {
FetchRequest.flags = ParseFetchArguments(session, session->command.buffer + 6, &FetchRequest);
if (!(FetchRequest.flags & F_ERROR)) {
ccode = ImapSearchResolveMessageSet(
session, FetchRequest.messageSet, TRUE,
&resolvedSet, &emptySet);
if (ccode != STATUS_CONTINUE) {
FreeFetchResourcesFailure(&FetchRequest);
return SendError(session->client.conn,
session->command.tag, "UID FETCH",
ccode);
}
if (resolvedSet) {
MemFree(FetchRequest.messageSet);
FetchRequest.messageSet = resolvedSet;
resolvedSet = NULL;
}
if (emptySet) {
FreeFetchResourcesSuccess(&FetchRequest);
return SendOk(session, "UID FETCH");
}
nextRange = FetchRequest.messageSet;
do {
ccode = GetMessageRange(session->folder.selected.message, session->folder.selected.messageCount, &(nextRange), &rangeStart, &rangeEnd, TRUE);
if (ccode == STATUS_CONTINUE) {
ccode = SendResponseForMessageRange(session, &FetchRequest, rangeStart, rangeEnd, &purgedMessage);
if (ccode == STATUS_CONTINUE) {
continue;
}
}
if (ccode == STATUS_UID_NOT_FOUND) {
continue;
}
FreeFetchResourcesFailure(&FetchRequest);
return(SendError(session->client.conn, session->command.tag, "UID FETCH", ccode));
} while (nextRange);
FreeFetchResourcesSuccess(&FetchRequest);
if (!purgedMessage) {
return(SendOk(session, "UID FETCH"));
}
return(SendError(session->client.conn, session->command.tag, "UID FETCH", STATUS_REQUESTED_MESSAGE_NO_LONGER_EXISTS));
}
if (!(FetchRequest.flags & F_SYSTEM_ERROR)) {
FreeFetchResourcesFailure(&FetchRequest);
return(SendError(session->client.conn, session->command.tag, "UID FETCH", STATUS_INVALID_ARGUMENT));
}
FreeFetchResourcesFailure(&FetchRequest);
}
return(SendError(session->client.conn, session->command.tag, "UID FETCH", STATUS_MEMORY_ERROR));
}
}
return(SendError(session->client.conn, session->command.tag, "UID FETCH", ccode));
}
void
CommandFetchCleanup(void)
{
BongoKeywordIndexFree(FetchFlagsAttIndex);
BongoKeywordIndexFree(FetchFlagsSoloIndex);
BongoKeywordIndexFree(FetchFlagsSectionIndex);
}
BOOL
CommandFetchInit(void)
{
BongoKeywordIndexCreateFromTable(FetchFlagsAttIndex, FetchFlagsAtt, .name, TRUE);
if (FetchFlagsAttIndex != NULL) {
BongoKeywordIndexCreateFromTable(FetchFlagsSoloIndex, FetchFlagsSolo, .name, TRUE);
if (FetchFlagsSoloIndex != NULL) {
BongoKeywordIndexCreateFromTable(FetchFlagsSectionIndex, FetchFlagsSection, .name, TRUE);
if (FetchFlagsSectionIndex) {
return(TRUE);
}
BongoKeywordIndexFree(FetchFlagsSoloIndex);
}
BongoKeywordIndexFree(FetchFlagsAttIndex);
}
return(FALSE);
}