3703 lines
141 KiB
C
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, <m);
|
|
|
|
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(¤t, 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);
|
|
}
|