/****************************************************************************
 * <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 "queue.h"
#include "mime.h"
#include "messages.h"
#include "domain.h"

#include <xpl.h>
#include <logger.h>
#include <mdb.h>
#include <nmap.h>
#include <nmlib.h>

#define PUSHCLIENTALLOC 20
#define REMOTENMAP_ALLOC_STEPS 10
#define MAX_PUSHCLIENTS_ERRORS 25

#if defined(SOLARIS) || defined(S390RH)
#define SPOOL_LOCK_ARRAY_MASK 0xFF000000
#else
#define SPOOL_LOCK_ARRAY_MASK 0x000000FF
#endif
#define UNUSED_SPOOL_LOCK_ID ((unsigned long)(1 << 31))

typedef struct _NMAPConnection {
    Connection *conn;
    unsigned long address;
    unsigned short port;
    BOOL error;
} NMAPConnection;
    

typedef struct _NMAPConnections {
    long used;
    long allocated;

    NMAPConnection *connections;
} NMAPConnections;

/* Globals */
MessageQueue Queue = { 0, };

static int HandleDSN(FILE *data, FILE *control);

#define MAX_CHARS_IN_PDBSEARCH    512

static int 
PDBSearch(char *doc, char *searchString)
{
    unsigned char *w[MAX_CHARS_IN_PDBSEARCH], *textStart, *textEnd, *text, *end;
    int wl[MAX_CHARS_IN_PDBSEARCH];
    unsigned char expression[MAX_CHARS_IN_PDBSEARCH];
    long word, words = 0, i,len;
    int found = 0;

    if (!doc || !(*doc)) {
        return(0);
    }

    len = strlen(searchString);
    if (len == 0) {
        return 0;
    }

    if (len < MAX_CHARS_IN_PDBSEARCH) {
        strcpy(expression, searchString);
    } else {
        len = sizeof(expression) - 1;
        strncpy(expression, searchString, len);
        expression[MAX_CHARS_IN_PDBSEARCH - 1] = '\0';
    }

    for (i = 0; i < len; i++) {
        expression[i] = toupper(expression[i]);
    }

    text = expression;
    while (*text && isspace(*text)) {
        text++;
    }

    if ((*text == '\"') && ((end = strchr(text + 1, '\"')) != NULL)) {
        w[words++] = text + 1;
        *end = '\0';
        text = end + 1;
    }
        
    while (*text && isspace(*text)) {
        text++;
    }

    if ((*text == '\0') && (!words))
        return 0;

    if (*text) {
        w[words++] = text;
    }

    for(i = text - expression; (i < len) && (words < MAX_CHARS_IN_PDBSEARCH) ;i++) {
        if (isspace(expression[i])) {
            if (!isspace(expression[i+1])) {
                w[words++] = expression + i + 1;
                expression[i] = '\0';
            } else {
                expression[i] = '\0';
            }
        }
    }

    for (i = 0; i < words; i++) {
        wl[i] = strlen(w[i]);
    }

    textStart = doc;
    textEnd = doc + strlen(doc) - 1;

    for (text = textStart; text != textEnd; text++) {
        for (word = 0; word < words; word++) {
            i = 0;
            while ((i != wl[word]) && (w[word][i] == toupper(text[i]))) {
                i++;
            }
            if (wl[word]==i) {
                found = 1;

                if ((text != textStart && !isspace(*(text - 1))) && ((text != textEnd) && !isspace(*(text + wl[word])))) {
                    goto NoMatch;
                }
            }
        }
NoMatch:
        ;
    }
    return found;
}

static BOOL 
InitSpoolEntryIDLocks(void)
{
    int i;
    int j;
    unsigned long *id;

    Queue.spoolLocks.entryIDs = (unsigned long *)MemMalloc(SPOOL_LOCK_ARRAY_SIZE * SPOOL_LOCK_IDARRAY_SIZE * sizeof(unsigned long));
    if (Queue.spoolLocks.entryIDs != NULL) {
        for (i = 0; i < SPOOL_LOCK_ARRAY_SIZE; i++) {
            XplOpenLocalSemaphore(Queue.spoolLocks.semaphores[i], 1);

            for (j = 0, id = &Queue.spoolLocks.entryIDs[i * SPOOL_LOCK_IDARRAY_SIZE]; j < SPOOL_LOCK_IDARRAY_SIZE; j++, id++) {
                *id = UNUSED_SPOOL_LOCK_ID;
            }
        }

        return(TRUE);
    }

    return(FALSE);
}

static void 
DeInitSpoolEntryIDLocks(void)
{
    register int    i;

    if (Queue.spoolLocks.entryIDs != NULL) {
        for (i = 0; i < SPOOL_LOCK_ARRAY_SIZE; i++) {
            XplCloseLocalSemaphore(Queue.spoolLocks.semaphores[i]);
        }

        MemFree(Queue.spoolLocks.entryIDs);
    }
    return;
}

static unsigned long * 
SpoolEntryIDLock(unsigned long id)
{
    register unsigned long *unused;
    register unsigned long *cur;
    register unsigned long *limit;

    if (Queue.spoolLocks.entryIDs != NULL) {
        unused = NULL;
        cur = &(Queue.spoolLocks.entryIDs[(SPOOL_LOCK_ARRAY_MASK & id) * SPOOL_LOCK_IDARRAY_SIZE]);
        limit = cur + SPOOL_LOCK_IDARRAY_SIZE;

        XplWaitOnLocalSemaphore(Queue.spoolLocks.semaphores[SPOOL_LOCK_ARRAY_MASK & id]);

        do {
            if (*cur != id) {
                if (*cur != UNUSED_SPOOL_LOCK_ID) {
                    continue;
                }

                if (unused != NULL) {
                    continue;
                }

                unused = cur;
                continue;
            }

            XplSignalLocalSemaphore(Queue.spoolLocks.semaphores[SPOOL_LOCK_ARRAY_MASK & id]);

            return(NULL);
        } while (++cur < limit);

        if (unused != NULL) {
            *unused = id;

            XplSignalLocalSemaphore(Queue.spoolLocks.semaphores[SPOOL_LOCK_ARRAY_MASK & id]);

            return(unused);
        }

        XplSignalLocalSemaphore(Queue.spoolLocks.semaphores[SPOOL_LOCK_ARRAY_MASK & id]);

        LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_PQE_LOCK_FULL, LOG_INFO, 0, NULL, NULL, id, 0, NULL, 0);

        XplConsolePrintf("bongoqueue: Unable to lock spool entry %x; table full.\r\n", (unsigned int)id);
    }

    return(NULL);
}

static void 
SpoolEntryIDUnlock(unsigned long *idLock)
{
    unsigned long id = *idLock;

    if ((Queue.spoolLocks.entryIDs != NULL) && (idLock != NULL)) {
        XplWaitOnLocalSemaphore(Queue.spoolLocks.semaphores[SPOOL_LOCK_ARRAY_MASK & id]);
        *idLock = UNUSED_SPOOL_LOCK_ID;
        XplSignalLocalSemaphore(Queue.spoolLocks.semaphores[SPOOL_LOCK_ARRAY_MASK & id]);
    }

    return;
}


static void
UpdatePushClientsRegistered(void) 
{
    int count;
    Queue.pushClientsRegistered[0] = TRUE;
    Queue.pushClientsRegistered[1] = TRUE;
    Queue.pushClientsRegistered[2] = FALSE;
    Queue.pushClientsRegistered[3] = FALSE;
    Queue.pushClientsRegistered[4] = FALSE;
    Queue.pushClientsRegistered[5] = FALSE;
    Queue.pushClientsRegistered[6] = TRUE;
    Queue.pushClientsRegistered[7] = TRUE;
    for (count = 0; count < Queue.pushClients.count; count++) {
        if (Queue.pushClients.array[count].queue < 8) {
            Queue.pushClientsRegistered[Queue.pushClients.array[count].queue] = TRUE;
        }
    }
}

static void
WriteQAgents(void) 
{
    FILE *handle;

    handle = fopen(Conf.queueClientsPath, "wb");
    if (handle) {
        fwrite(Queue.pushClients.array, sizeof(QueuePushClient), Queue.pushClients.count, handle);
        fclose(handle);
    }
}

static unsigned long
AddPushAgent(QueueClient *client, 
             unsigned long address, 
             int port, 
             int queue, 
             unsigned char *identifier)
{
    int count;
    QueuePushClient *temp;

    LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_ADD_QUEUE_AGENT, LOG_INFO, 0, NULL, NULL, XplHostToLittle(client->conn->socketAddress.sin_addr.s_addr), queue, &port, sizeof(port));

    XplMutexLock(Queue.pushClients.lock);

    for (count = 0; count < Queue.pushClients.count; count++) {
        if ((port == Queue.pushClients.array[count].port) && (address == Queue.pushClients.array[count].address) && (queue == Queue.pushClients.array[count].queue)) {
            XplMutexUnlock(Queue.pushClients.lock);
            return(count);
        }
    }

    if ((Queue.pushClients.count + 1) >= Queue.pushClients.allocated) {
        temp = MemRealloc(Queue.pushClients.array, (Queue.pushClients.allocated + PUSHCLIENTALLOC) * sizeof(QueuePushClient));
        if (temp) {
            Queue.pushClients.array = temp;
            Queue.pushClients.allocated += PUSHCLIENTALLOC;
        } else {
            LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_NMAP_OUT_OF_MEMORY, LOG_CRITICAL, 0, "Queue", identifier, (Queue.pushClients.allocated + PUSHCLIENTALLOC) * sizeof(QueuePushClient), 0, NULL, 0);

            XplConsolePrintf("bongoqueue: Out of memory processing user %s, mailbox %s; request size: %d bytes\r\n", "bongoqueue:AddPushAgent", 
                      identifier, (int)((Queue.pushClients.allocated + PUSHCLIENTALLOC) * sizeof(QueuePushClient)));
            return(-1);
        }
    }

    Queue.pushClients.array[Queue.pushClients.count].queue = queue;
    Queue.pushClients.array[Queue.pushClients.count].address = address;
    Queue.pushClients.array[Queue.pushClients.count].port = port;
    Queue.pushClients.array[Queue.pushClients.count].usageCount = 0;
    Queue.pushClients.array[Queue.pushClients.count].errorCount = 0;
    strcpy(Queue.pushClients.array[Queue.pushClients.count].identifier, identifier);
    Queue.pushClients.count++;

    UpdatePushClientsRegistered();
    WriteQAgents();

    XplMutexUnlock(Queue.pushClients.lock);
    return(Queue.pushClients.count);
}

static BOOL
RemovePushAgentIndex(int index, BOOL force)
{
    XplMutexLock(Queue.pushClients.lock);

    if (index < Queue.pushClients.count) {
        if (!force) {
            Queue.pushClients.array[index].errorCount++;
        }

        if (Queue.pushClients.array[index].usageCount > 1) {
            Queue.pushClients.array[index].usageCount--;

            XplMutexUnlock(Queue.pushClients.lock);

            return(FALSE);
        }

        if ((Queue.pushClients.array[index].errorCount > MAX_PUSHCLIENTS_ERRORS) || force) {
            if (force) {
                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_REREGISTER_QUEUE_AGENT, LOG_INFO, 0, NULL, NULL, XplHostToLittle(Queue.pushClients.array[index].address), Queue.pushClients.array[index].queue, &(Queue.pushClients.array[index].port), sizeof(Queue.pushClients.array[index].port));
            } else {
                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_REMOVED_QUEUE_AGENT, LOG_INFO, 0, NULL, NULL, XplHostToLittle(Queue.pushClients.array[index].address), Queue.pushClients.array[index].queue, &(Queue.pushClients.array[index].port),  sizeof(Queue.pushClients.array[index].port));
            }

            if (index < (Queue.pushClients.count - 1)) {
                memmove(&Queue.pushClients.array[index], &Queue.pushClients.array[index + 1], (Queue.pushClients.count - index - 1) * sizeof(QueuePushClient));
            }

            Queue.pushClients.count--;

            UpdatePushClientsRegistered();
            WriteQAgents();

            XplMutexUnlock(Queue.pushClients.lock);

            return(TRUE);
        }
    }

    XplMutexUnlock(Queue.pushClients.lock);

    return(FALSE);
}

static void
RemoveAllPushAgents(void)
{
    XplMutexLock(Queue.pushClients.lock);

    Queue.pushClients.count = 0;

    UpdatePushClientsRegistered();

    MemFree(Queue.pushClients.array);
    Queue.pushClients.array = NULL;

    XplMutexUnlock(Queue.pushClients.lock);

    return;
}


static BOOL
CheckIfDeliveryDeferred(void)
{
    struct tm tm;
    time_t tod=time(NULL);
    register unsigned char day;
    register unsigned char hour;

    localtime_r(&tod, &tm);
    day = tm.tm_wday;
    hour = tm.tm_hour;

    if ((hour < Conf.deferStart[day]) || (hour >= Conf.deferEnd[day])) {
        return(FALSE);
    }

    return(FALSE);
}

static int
GetNMAPConnection(NMAPConnections *list, 
                  struct sockaddr_in *address,
                  NMAPConnection **nmapRet,
                  BOOL *new)
{
    Connection *conn;
    int index;
    BOOL result;
    void *temp;
    char line[CONN_BUFSIZE + 1];
    
    for (index = 0; index < list->used; index++) {
        if ((list->connections[index].address == address->sin_addr.s_addr) && (list->connections[index].port == address->sin_port)) {
            *nmapRet = &list->connections[index];
            *new = FALSE;
            return 0;
        }
    }

    *new = TRUE;
    
    conn = NMAPConnect(NULL, address);
    if (conn == NULL) {
        XplConsolePrintf("bongoqueue: Couldn't connect to NMAP\r\n");
        return DELIVER_TRY_LATER;
    }
    
    result = NMAPAuthenticateToStore(conn, line, CONN_BUFSIZE);
    if (!result) {
        NMAPQuit(conn);
        ConnClose(conn, 0);
        ConnFree(conn);
        return DELIVER_TRY_LATER;
    }

    if ((list->used + 1) > list->allocated) {
        temp = MemRealloc(list->connections, (list->allocated + REMOTENMAP_ALLOC_STEPS) * sizeof(NMAPConnection));
        if (temp) {
            list->connections = (NMAPConnection *)temp;
        } else {
            NMAPQuit(conn);
            ConnClose(conn, 0);
            ConnFree(conn);
            return DELIVER_TRY_LATER;
        }
        
        list->allocated += REMOTENMAP_ALLOC_STEPS;
    }
    
    index = list->used++;
    list->connections[index].address = address->sin_addr.s_addr;
    list->connections[index].port = address->sin_port;
    list->connections[index].conn = conn;
    list->connections[index].error = FALSE;

    *nmapRet = &list->connections[index];
    return 0;
}

static int
DeliverToStore(NMAPConnections *list, 
               struct sockaddr_in *address,
               int type,
               char *from, 
               char *authFrom, 
               char *filename,
               FILE *fh,
               unsigned long count, 
               char *recipient, 
               char *mailbox,
               unsigned long messageFlags)
{
    NMAPConnection *nmap = NULL;
    int ccode = 0;
    unsigned char line[CONN_BUFSIZE + 1];
    BOOL new;

    if ((ccode = GetNMAPConnection(list, address, &nmap, &new)) < 0) {
        return DELIVER_TRY_LATER;
    }

    if (nmap->error) {
        return DELIVER_TRY_LATER;
    }

    if (new) {
        /* We've got the connection, now create the entry and get everything set up */
        if (address->sin_addr.s_addr == MsgGetHostIPAddress()) {
            if ((ccode = NMAPSendCommandF(nmap->conn, "DELIVER FILE %d %s %s %s\r\n", type, from, authFrom, filename)) == -1) {
                nmap->error = TRUE;
            }       
        } else {
            if ((ccode = ConnWriteF(nmap->conn, "DELIVER STREAM %d %s %s %d\r\n", type, from, authFrom, (int)count)) == -1 ||
                (ccode = ConnWriteFile(nmap->conn, fh)) == -1 ||
                (ccode = ConnFlush(nmap->conn)) == -1) {
                nmap->error = TRUE;

                fclose(fh);

                return DELIVER_TRY_LATER;
            }
        }

        ccode = NMAPReadAnswer(nmap->conn, line, CONN_BUFSIZE, TRUE);
        
        if (ccode != 2053) {
            nmap->error = TRUE;
            return DELIVER_TRY_LATER;
        }       
    }

    ccode = NMAPRunCommandF(nmap->conn, line, CONN_BUFSIZE, "%s %s %lu\r\n", 
                            recipient, mailbox, messageFlags);

    if (ccode == 1000) {
        return DELIVER_SUCCESS;
    } else {
        return atoi(line);
    }
}

static void 
EndStoreDelivery(NMAPConnections *list)
{
    int ccode;
    long index;
    unsigned char line[CONN_BUFSIZE + 1];

    for (index = 0; index < list->used; index++) {
        NMAPConnection *nmap = &list->connections[index];

        if (nmap) {
            if (!nmap->error &&
                ((ccode = ConnWrite(nmap->conn, "\r\n", 2)) != -1) && 
                ((ccode = ConnFlush(nmap->conn)) != -1)) {
                NMAPReadAnswer(nmap->conn, line, CONN_BUFSIZE, FALSE);
            }

            ConnClose(nmap->conn, 0);
            ConnFree(nmap->conn);
        } 
    }

    MemFree(list->connections);
}

static void
ProcessQueueEntryCleanUp(unsigned long *idLock, MIMEReportStruct *report)
{
    if (report) {
        FreeMIME(report);
    }

    if (idLock) {
        SpoolEntryIDUnlock(idLock);
    }

    XplSafeDecrement(Queue.activeWorkers);
}

static BOOL
ProcessQueueEntry(unsigned char *entryIn)
{
    int i;
    int len;
    int queue;
    int count;
    int qDateLength = 0;
    int qFlagsLength = 0;
    int qIDLength = 0;
    int qAddressLength = 0;
    int qFromLength = 0;
    long lines = 0;
    unsigned long used;
    unsigned long dSize = 0;
    unsigned long entryID;
    unsigned long *idLock = NULL;
    unsigned char *ptr;
    unsigned char *ptr2 = NULL;
    unsigned char *cur;
    unsigned char *next;
    unsigned char *limit;
    unsigned char *qDate;
    unsigned char *qFlags;
    unsigned char *qID;
    unsigned char *qAddress;
    unsigned char *qFrom;
    unsigned char *qEnvelope = NULL;
    unsigned char path[XPL_MAX_PATH + 1] = "";
    unsigned char path2[XPL_MAX_PATH + 1];
    unsigned char line[CONN_BUFSIZE + 1] = "";
    unsigned char entry[15];
    BOOL keep = TRUE;
    BOOL bounce = FALSE;
    time_t date;
    struct sockaddr_in saddr;
    struct stat sb;
    FILE *fh;
    FILE *data;
    FILE *newFH;
    MIMEReportStruct *report = NULL;
    MDBValueStruct *vs;
    QueueClient *client;
    void *handle;

StartOver:
    if (!entryIn) {
        ProcessQueueEntryCleanUp(idLock, report);
        return(FALSE);
    }

    XplRenameThread(XplGetThreadID(), entryIn);

    line[0] = '\0';

    strcpy(entry, entryIn + 3);
    entryIn[3] = '\0';

    queue = atoi(entryIn);

    MemFree(entryIn);

    entryID = strtol(entry, NULL, 16);

    LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_QUEUE, LOGGER_EVENT_PQE_START, LOG_DEBUG, entryID, NULL, NULL, queue, entryID, NULL, 0);

    idLock = SpoolEntryIDLock(entryID);
    if (idLock) {
        sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue);
        fh = fopen(path, "r+b");
    } else {
        ProcessQueueEntryCleanUp(NULL, report);
        return(FALSE);
    }

    if (fh) {
        fgets(line, CONN_BUFSIZE, fh);
        date = atoi(line + 1);
        fclose(fh);
    } else {
        ProcessQueueEntryCleanUp(idLock, report);
        return(FALSE);
    }

    /* We've got pre and post processing off queue entries - this is pre */
    switch(queue) {
        case Q_INCOMING: {
            sb.st_size = -1;
            qDate = NULL;
            qFlags = NULL;
            qID = NULL;
            qAddress = NULL;
            qFrom = NULL;

            sprintf(path, "%s/w%s.%03d", Conf.spoolPath, entry, queue);
            newFH = fopen(path, "wb");

            sprintf(path, "%s/c%s.%03d",Conf.spoolPath, entry, queue);
            if (newFH 
                    && (stat(path, &sb) == 0) 
                    && (sb.st_size > 8) 
                    && ((qEnvelope = (unsigned char *)MemMalloc(sb.st_size + 1)) != NULL) 
                    && ((fh = fopen(path, "rb")) != NULL) 
                    && (fread(qEnvelope, sizeof(unsigned char), sb.st_size, fh) == (size_t)sb.st_size)) {
                /* Sort the control file as follows:
                    QUEUE_DATE
                    QUEUE_FLAGS
                    QUEUE_ID
                    QUEUE_ADDRESS
                    QUEUE_FROM

                   Followed by, in no particular order:
                    QUEUE_BOUNCE
                    QUEUE_RECIP_LOCAL
                    QUEUE_RECIP_MBOX_LOCAL
                    QUEUE_CALENDAR_LOCAL
                    QUEUE_RECIP_REMOTE
                    QUEUE_THIRD_PARTY
                */
                fclose(fh);

                qEnvelope[sb.st_size] = '\0';

                count = 0;
                cur = qEnvelope;
                limit = qEnvelope + sb.st_size;
                while (cur < limit) {
                    next = strchr(cur, '\n');
                    if (next) {
                        next++;
                    } else {
                        next = limit;
                    }

                    switch (*cur) {
                        case 0x0A:
                        case 0x0D: {
                            qFrom = NULL;
                            cur = limit;
                            break;
                        }

                        case QUEUE_DATE: {
                            qDate = cur;
                            qDateLength = next - cur;
                            break;
                        }

                        case QUEUE_FLAGS: {
                            qFlags = cur;
                            qFlagsLength = next - cur;
                            break;
                        }

                        case QUEUE_ID: {
                            qID = cur;
                            qIDLength = next - cur;
                            break;
                        }

                        case QUEUE_ADDRESS: {
                            qAddress = cur;
                            qAddressLength = next - cur;
                            break;
                        }

                        case QUEUE_FROM: {
                            qFrom = cur;
                            qFromLength = next - cur;
                            break;
                        }

                        case QUEUE_BOUNCE:
                        case QUEUE_CALENDAR_LOCAL:
                        case QUEUE_RECIP_LOCAL:
                        case QUEUE_RECIP_MBOX_LOCAL:
                        case QUEUE_RECIP_REMOTE:
                        case QUEUE_THIRD_PARTY: {
                            count++;
                            break;
                        }

                        default: {
                            break;
                        }
                    }

                    if (next < limit) {
                        next[-1] = '\n';
                    }

                    cur = next;
                }

                if (qDate && qFrom && count) {
                    fwrite(qDate, sizeof(unsigned char), qDateLength, newFH);

                    if (qFlags) {
                        fwrite(qFlags, sizeof(unsigned char), qFlagsLength, newFH);
                    } else {
                        fwrite(QUEUES_FLAGS"0\r\n", sizeof(unsigned char), 4, newFH);
                    }

                    if (qID) {
                        fwrite(qID, sizeof(unsigned char), qIDLength, newFH);
                    }

                    if (qAddress) {
                        fwrite(qAddress, sizeof(unsigned char), qAddressLength, newFH);
                    }

                    fwrite(qFrom, sizeof(unsigned char), qFromLength, newFH);
                } else {
                    /* fixme - if a new queue entry has at least QUEUE_FROM 
                       but no recipients we should bounce the message rather
                       than consuming it! */
                    fclose(newFH);

                    unlink(path);

                    sprintf(path, "%s/w%s.%03d",Conf.spoolPath, entry, queue);
                    unlink(path);

                    sprintf(path, "%s/d%s.msg",Conf.spoolPath, entry);
                    unlink(path);

                    MemFree(qEnvelope);

                    if ((handle = QDBHandleAlloc()) != NULL) {
                        QDBRemoveID(handle, entryID);

                        QDBHandleRelease(handle);
                    }

                    XplSafeDecrement(Queue.queuedLocal);
                    ProcessQueueEntryCleanUp(idLock, report);
                    return(TRUE);
                }

                vs = MDBCreateValueStruct(Agent.agent.directoryHandle, NULL);

                count = 0;
                cur = qEnvelope;
                while (cur < limit) {
                    next = strchr(cur, '\n');
                    if (next) {
                        ptr = next++;
                    } else {
                        ptr = NULL;
                        next = limit;
                    }

                    switch (*cur) {
                        case QUEUE_BOUNCE:
                        case QUEUE_CALENDAR_LOCAL: {
                            fwrite(cur, sizeof(unsigned char), next - cur, newFH);
                            break;
                        }

                        case QUEUE_RECIP_LOCAL:
                        case QUEUE_RECIP_MBOX_LOCAL: {
                            if (ptr) {
                                *ptr = '\0';
                            }

                            ptr2 = strchr(cur + 1, ' ');
                            if (ptr2) {
                                *ptr2 = '\0';
                            }

                            if (MsgFindObject(cur + 1, NULL, NULL, NULL, vs)) {
                                if (ptr2) {
                                    for (used = 0; (used < vs->Used); used++) {
                                        fprintf(newFH, "%c%s %s\n", *cur, vs->Value[used], ptr2 + 1);
                                        count++;
                                    }
                                } else {
                                    for (used = 0; (used < vs->Used); used++) {
                                        fprintf(newFH, "%c%s %s %d\r\n", *cur, vs->Value[used], vs->Value[used], DSN_FAILURE);
                                        count++;
                                    }
                                }

                                MDBFreeValues(vs);
                            } else {
                                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_QUEUE, LOGGER_EVENT_PQE_FIND_OBJECT_FAILED, LOG_INFO, entryID, cur + 1, "", queue, 0, NULL, 0);

                                if (ptr2) {
                                    *ptr2 = ' ';
                                }

                                if (ptr) {
                                    *ptr = '\n';
                                }

                                fwrite(cur, sizeof(unsigned char), next - cur, newFH);
                            }

                            break;
                        }

                        case QUEUE_RECIP_REMOTE: {
                            if (ptr) {
                                *ptr = '\0';
                            }

                            ptr2 = strchr(cur + 1, ' ');
                            if (ptr2) {
                                *ptr2 = '\0';
                            }

                            if (ptr2) {
                                fprintf(newFH, QUEUES_RECIP_REMOTE"%s %s\n", cur + 1, ptr2 + 1);
                            } else {
                                fprintf(newFH, QUEUES_RECIP_REMOTE"%s %s %d\r\n", cur + 1, cur + 1, DSN_FAILURE);
                            }

                            break;
                        }

                        case QUEUE_ADDRESS: 
                        case QUEUE_DATE: 
                        case QUEUE_FROM: 
                        case QUEUE_ID: 
                        case QUEUE_FLAGS: {
                            break;
                        }

                        case QUEUE_THIRD_PARTY:
                        default: {
                            fwrite(cur, sizeof(unsigned char), next - cur, newFH);
                            break;
                        }
                    }

                    cur = next;
                }

                MDBDestroyValueStruct(vs);

                MemFree(qEnvelope);

                fclose(newFH);

                unlink(path);

                sprintf(path2, "%s/w%s.%03d", Conf.spoolPath, entry, queue);
                rename(path2, path);

                break;
            }

            if (!newFH) { 
                sprintf(path, "%s/w%s.%03d",Conf.spoolPath, entry, queue);
                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_FILE_OPEN_FAILURE, LOG_CRITICAL, entryID, path, __FILE__, __LINE__, 0, NULL, 0);
            } else if (!qEnvelope) {
                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_NMAP_OUT_OF_MEMORY, LOG_CRITICAL, entryID, __FILE__, NULL, sb.st_size, 0, NULL, 0);
            } else if (!fh) {
                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_FILE_OPEN_FAILURE, LOG_CRITICAL, entryID, path, __FILE__, __LINE__, 0, NULL, 0);
            } else {
                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_FILE_OPEN_FAILURE, LOG_CRITICAL, entryID, path, __FILE__, __LINE__, 0, NULL, 0);
            }

            if (qEnvelope) {
                MemFree(qEnvelope);
            }

            if (fh) {
                fclose(fh);
            }

            if (newFH) {
                fclose(newFH);
            }

            LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_WRITE_ERROR, LOG_WARNING, 0, "Spool", "Queue", __LINE__, 0, NULL, 0);

            sprintf(path, "%s/w%s.%03d",Conf.spoolPath, entry, queue);
            unlink(path);

            ProcessQueueEntryCleanUp(idLock, report);
            return(TRUE);
        }

        /* It's in the deliver queue, check if it's local and deliver it, if not, hand it off to the agents!*/
        /* By way of design,this function also removes all entries w/o any receiver from the queue */
        case Q_OUTGOING: {
            if (date < time(NULL) - Conf.maxLinger) {
                /* We move it to the Q_RTS queue and the linger code there will bounce it for us */
                sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue);
                sprintf(path2, "%s/c%s.%03d", Conf.spoolPath, entry, Q_RTS);
                rename(path, path2);

                sprintf(path, "%03d%s", Q_RTS, entry);
                SpoolEntryIDUnlock(idLock);
                entryIn = MemStrdup(path);
                goto StartOver;
            }

            if (Conf.deferEnabled && CheckIfDeliveryDeferred()) {
                ProcessQueueEntryCleanUp(idLock, report);
                return(TRUE);
            }

            /* Fall-through to Q_DELIVER */
        }

        case Q_DELIVER: {
            int status;
            unsigned long messageFlags;
            unsigned long flags = DSN_FAILURE | DSN_HEADER | DSN_BODY;
            unsigned char *mailbox;
            unsigned char *preMailboxDelim = NULL;
            unsigned char *postMailboxDelim = NULL;
            unsigned char recipient[MAXEMAILNAMESIZE + 1];
            unsigned char sender[MAXEMAILNAMESIZE + 1];
            unsigned char authenticatedSender[MAXEMAILNAMESIZE + 1];
            unsigned char messageID[MAXEMAILNAMESIZE + 1];
            char dataFilename[XPL_MAX_PATH];
            NMAPConnections list = { 0, };

            data = NULL;
            saddr.sin_addr.s_addr = 0;

            sprintf(dataFilename, "d%s.msg", entry);

            if (!dSize) {
                if (stat(path, &sb)) {
                    ProcessQueueEntryCleanUp(idLock, report);
                    return(TRUE);
                }

                dSize = (unsigned long)sb.st_size;
            }

            keep = FALSE;
            bounce = FALSE;

            sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue);
            fh = fopen(path, "rb");

            sprintf(path, "%s/w%s.%03d", Conf.spoolPath, entry, queue);
            newFH = fopen(path, "wb");
            if (!fh || !newFH) {
                if (fh) {
                    fclose(fh);
                }

                if (newFH) {
                    fclose(newFH);
                }

                sprintf(path, "%s/w%s.%03d",Conf.spoolPath, entry, queue);
                unlink(path);

                ProcessQueueEntryCleanUp(idLock, report);
                return(TRUE);
            }

            while (!feof(fh) && !ferror(fh)) {
                if (fgets(line, CONN_BUFSIZE, fh)) {
                    CHOP_NEWLINE(line);

                    mailbox = "INBOX";
                    messageFlags = 0;

                    switch(line[0]) {
                        case QUEUE_FROM: {
                            /* sender  */
                            ptr = strchr(line + 1, ' ');
                            if (ptr) {
                                *ptr = '\0';
                                ptr2 = strchr(ptr + 1, ' ');
                            }

                            strncpy(sender, line + 1, MAXEMAILNAMESIZE);
                            if (ptr) {
                                if (ptr2) {
                                    *ptr2 = '\0';
                                }

                                strncpy(authenticatedSender, ptr + 1, MAXEMAILNAMESIZE);
                                if (ptr2) {
                                    strcpy(messageID, ptr2 + 1);
                                    *ptr2 = ' ';
                                }
                                *ptr = ' ';
                            } else {
                                authenticatedSender[0]='-';
                                authenticatedSender[1]='\0';
                            }
                    
                            /* LoggerEvent(NMAP.handle.logging, LOGGER_SUBSYSTEM_QUEUE, LOGGER_EVENT_PQE_FROM, LOG_DEBUG, entryID, entry, sender, queue, 0, MIME_TEXT_PLAIN, authenticatedSender? strlen(authenticatedSender) + 1: 1, authenticatedSender? (char *)authenticatedSender: "", NULL, 0); */
                    
                            fprintf(newFH, "%s\r\n", line);
                            break;
                        }

                        case QUEUE_CALENDAR_LOCAL: {
                            struct sockaddr_in    siaddr;

                            if (!data) {
                                data = fopen(path, "rb");
                                if (!data) {
                                    fclose(fh);
                                    sprintf(path, "%s/w%s.%03d",Conf.spoolPath, entry, queue);
                                    fclose(newFH);
                                    unlink(path);
                                    ProcessQueueEntryCleanUp(idLock, report);
                                    return(TRUE);
                                }
                            }

                            /* First arg is recipient name, second arg is calendar name; third would be flags */
                            ptr = strchr(line+1, ' ');
                            if (ptr) {
                                *ptr = '\0';
                                mailbox = ptr + 1;
                                ptr2 = strchr(ptr + 1, ' ');
                                if (ptr2) {
                                    *ptr2 = '\0';
                                    flags = atol(ptr2 + 1);
                                }
                            } else {
                                ptr2 = NULL;
                                mailbox = "MAIN";
                            }
                            strcpy(recipient, line + 1);
                            if (ptr) {
                                *ptr = ' ';
                            }

                            vs = MDBCreateValueStruct(Agent.agent.directoryHandle, NULL);
                            if (!MsgFindObject(recipient, NULL, NULL, &siaddr, vs)) {
                                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_QUEUE, LOGGER_EVENT_PQE_FIND_OBJECT_FAILED, LOG_WARNING, entryID, recipient, "", queue, 0, NULL, 0);

                                status = DELIVER_USER_UNKNOWN;
                            } else {
                                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_QUEUE, LOGGER_EVENT_PQE_REMOTE_NMAP, LOG_DEBUG, entryID, entry, line + 1, queue, XplHostToLittle(siaddr.sin_addr.s_addr), NULL, 0);
                                status = DeliverToStore(&list, &siaddr, NMAP_DOCTYPE_CAL, sender, authenticatedSender, dataFilename, data, dSize, recipient, mailbox, flags);
                                if (Agent.agent.state == BONGO_AGENT_STATE_STOPPING) {
                                    status = DELIVER_TRY_LATER;
                                }
                            }
                            MDBDestroyValueStruct(vs);

                            /* Restore our buffer */
                            if (ptr2) {
                                *ptr2 = ' ';
                            }

                            if (status<DELIVER_SUCCESS) {
                                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_QUEUE, LOGGER_EVENT_PQE_DELIVERY_FAILED, LOG_NOTICE, entryID, entry, NULL, queue, status, NULL, 0);
                                if (status==DELIVER_USER_UNKNOWN || status==DELIVER_INTERNAL_ERROR || status==DELIVER_QUOTA_EXCEEDED) {
                                    XplSafeIncrement(Queue.localDeliveryFailed);
                                    if (flags & DSN_FAILURE) {
                                        fprintf(newFH, QUEUES_BOUNCE"%s %s %lu %d\r\n", recipient, recipient, flags, status);
                                        bounce=TRUE;
                                    }
                                } else {
                                    fprintf(newFH,"%s\r\n",line);
                                    keep=TRUE; 
                                }
                            } else {
                                if (status==DELIVER_SUCCESS) {
                                    if (flags & DSN_SUCCESS) {
                                        fprintf(newFH, QUEUES_BOUNCE"%s %s %lu %d\r\n", recipient, recipient, flags, DELIVER_SUCCESS);
                                        bounce=TRUE;
                                    }
                                }
                            }

                            fseek(data, 0, SEEK_SET);
                            break;
                        }

                        case QUEUE_RECIP_MBOX_LOCAL: {    /* Local w/ Mailbox; syntax: MRecip ORecip flags MBox MsgFlags */
                            i = 0;
                            ptr=line;
                            while (ptr[0]!='\0') {
                                if (ptr[0]==' ') {
                                    i++;
                                    if (i > 2) {
                                        switch(i) {
                                            case 3: {
                                                preMailboxDelim = ptr;
                                                *preMailboxDelim = '\0';
                                                mailbox = ptr + 1;
                                                break;
                                            }
                                            case 4: {
                                                postMailboxDelim = ptr;
                                                *postMailboxDelim = '\0';
                                                messageFlags = atol(ptr + 1);
                                                break;
                                            }
                                        }
                                    }
                                }
                                ptr++;
                            }
                        }
                        /**** FALLTHROUGH ****/

                        case QUEUE_RECIP_LOCAL: { /* Local */
                            struct sockaddr_in siaddr;
                            if (!data) {
                                sprintf(path, "%s/d%s.msg", Conf.spoolPath, entry);
                                data = fopen(path, "rb");
                                if (!data) {
                                    fclose(fh);
                                    fclose(newFH);

                                    sprintf(path, "%s/w%s.%03d", Conf.spoolPath, entry, queue);
                                    unlink(path);

                                    ProcessQueueEntryCleanUp(idLock, report);
                                    return(TRUE);
                                }
                            }
                
                            ptr = strchr(line + 1, ' ');
                            if (ptr) {
                                *ptr = '\0';
                                ptr2 = strchr(ptr + 1, ' ');
                                if (ptr2) {
                                    *ptr2 = '\0';
                                    flags = atol(ptr2 + 1);
                                }
                            }

                            strcpy(recipient, line + 1);
                            if (ptr) {
                                *ptr = ' ';
                                if (ptr2)
                                    *ptr2 = ' ';
                            }

                            /* Attempt delivery, check if local or remote */
                            vs = MDBCreateValueStruct(Agent.agent.directoryHandle, NULL);
                            if (MsgFindObject(recipient, NULL, NULL, &siaddr, vs)) {
                                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_QUEUE, LOGGER_EVENT_PQE_REMOTE_NMAP, LOG_DEBUG, entryID, entry, line + 1, queue, XplHostToLittle(siaddr.sin_addr.s_addr), NULL, 0);
                                status = DeliverToStore(&list, &siaddr, NMAP_DOCTYPE_MAIL, sender, authenticatedSender, dataFilename, data, dSize, recipient, mailbox, messageFlags);

                                if (Agent.agent.state == BONGO_AGENT_STATE_STOPPING) {
                                    status = DELIVER_TRY_LATER;
                                }
                            } else {
                                status = DELIVER_USER_UNKNOWN;

                                /* This will forward the message to a remote domain */
                                XplRWReadLockAcquire(&Conf.lock);
                                if (Conf.forwardUndeliverableEnabled) {
                                    XplRWReadLockRelease(&Conf.lock);

                                    /* Handle NUA address */
                                    if ((ptr = strchr(recipient, '@')) != NULL) {
                                        *ptr = '%';
                                    }

                                    ptr = strchr(line, ' ');
                                    if (ptr) {
                                        XplRWReadLockAcquire(&Conf.lock);
                                        fprintf(newFH, "%c%s@%s%s\r\n", QUEUE_RECIP_REMOTE, recipient, Conf.forwardUndeliverableAddress, ptr);
                                        XplRWReadLockRelease(&Conf.lock);
                                    }

                                    /* We want success, but flags = 0 so we don't notify the sender (yet) */
                                    /* SMTP will take care of it if the remote system doesn support DSN */
                                    status = DELIVER_SUCCESS;
                                    flags = 0;
                                    keep = TRUE; /* recipient prevent it from getting removed */
                                } else {
                                    LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_QUEUE, LOGGER_EVENT_PQE_FIND_OBJECT_FAILED, LOG_INFO, entryID, recipient, (authenticatedSender && (authenticatedSender[0] != '-') && (authenticatedSender[1] != ' '))? authenticatedSender: sender, queue, saddr.sin_addr.s_addr, NULL, 0);

                                    XplRWReadLockRelease(&Conf.lock);
                                }
                            }

                            MDBDestroyValueStruct(vs);

                            if (status < DELIVER_SUCCESS) {
                                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_QUEUE, LOGGER_EVENT_PQE_DELIVERY_FAILED, LOG_WARNING, entryID, entry, NULL, queue, status, NULL, 0);
                                if ((status == DELIVER_USER_UNKNOWN) || (status == DELIVER_INTERNAL_ERROR) || (status == DELIVER_QUOTA_EXCEEDED)) {
                                    XplSafeIncrement(Queue.localDeliveryFailed);
                                    if (flags & DSN_FAILURE) {
                                        ptr = strchr(line, ' ');
                                        if (ptr) { /* ptr points to the origrecip */
                                            ptr = strchr(ptr + 1, ' ');
                                            if (ptr) { /* ptr points to flags */
                                                ptr = strchr(ptr + 1, ' ');
                                                if (ptr) { /* ptr points behind flags */
                                                    *ptr = '\0';
                                                    fprintf(newFH, QUEUES_BOUNCE"%s %d\r\n", line + 1, status);
                                                    *ptr = ' ';
                                                } else {
                                                    fprintf(newFH, QUEUES_BOUNCE"%s %d\r\n", line  +1, status);
                                                }
                                            }
                                        }

                                        bounce = TRUE;
                                    }
                                } else {
                                    if (preMailboxDelim) {
                                        *preMailboxDelim = ' ';
                                        if (postMailboxDelim) {
	                                        *postMailboxDelim = ' ';
                                        }
                                    }
                                    fprintf(newFH, "%s\r\n", line);
                                    keep = TRUE; 
                                }
                            } else {
                                if (status==DELIVER_SUCCESS) {
                                    if (flags & DSN_SUCCESS) {
                                        ptr = strchr(line, ' ');
                                        if (ptr) { /* ptr points to the origrecip */
                                            ptr = strchr(ptr + 1, ' ');
                                            if (ptr) { /* ptr points to flags */
                                                ptr = strchr(ptr + 1, ' ');
                                                if (ptr) { /* ptr points behind flags */
                                                    *ptr = '\0';
                                                    fprintf(newFH, QUEUES_BOUNCE"%s %d\r\n", line + 1, DELIVER_SUCCESS);
                                                    *ptr = ' ';
                                                } else {
                                                    fprintf(newFH, QUEUES_BOUNCE"%s %d\r\n", line + 1, DELIVER_SUCCESS);
                                                }
                                            }
                                        }

                                        bounce = TRUE;
                                    }
                                }
                            }

                            fseek(data, 0, SEEK_SET);
                            break;
                        }

                        case QUEUE_RECIP_REMOTE: {
                            /* Remote */
                            fprintf(newFH, "%s\r\n", line);
                            keep = TRUE;
                            break;
                        }

                        case QUEUE_BOUNCE: {
                            /* Have bounced entry */
                            fprintf(newFH, "%s\r\n", line);
                            bounce = TRUE;
                            break;                            
                        }

                        case QUEUE_ADDRESS: {
                            /* Address associated with 'Mail sender' */
                            fprintf(newFH, "%s\r\n", line);
                            saddr.sin_addr.s_addr = atol(line + 1);
                            break;
                        }

                        case QUEUE_DATE: {
                            fprintf(newFH, "%s\r\n", line);
                            break;
                        }

                        case QUEUE_FLAGS: {
                            fprintf(newFH, "%s\r\n", line);
                            break;
                        }

                        case QUEUE_THIRD_PARTY: {
                            fprintf(newFH, "%s\r\n", line);
                            break;
                        }

                        default:
                            LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_QUEUE, LOGGER_EVENT_PQE_FIND_OBJECT_FAILED, LOG_INFO, entryID, line, entry, queue, 0, NULL, 0);
                            fprintf(newFH, "%s\r\n", line);
                            break;
                    }
                }
            }
            if (list.allocated) {
                EndStoreDelivery(&list);
                memset(&list, 0, sizeof(NMAPConnections));
            }
            fclose(newFH);
            fclose(fh);
            if (bounce) {
                unsigned char    Path2[XPL_MAX_PATH+1];

                /* First, rename the work file into a control file */
                sprintf(path, "%s/c%s.%03d",Conf.spoolPath, entry, queue);
                unlink(path);
                sprintf(Path2, "%s/w%s.%03d",Conf.spoolPath, entry, queue);
                rename(Path2, path);

                if (!data) {
                    sprintf(path, "%s/d%s.msg",Conf.spoolPath, entry);
                    data=fopen(path,"rb");
                } else {
                    fseek(data, 0, SEEK_SET);    
                }

                sprintf(path, "%s/c%s.%03d",Conf.spoolPath, entry, queue);
                fh=fopen(path,"rb");
        
                if (fh && data && 0 == HandleDSN(data, fh)) {
                    /* Now bounce the thing */
                    fseek(fh, 0, SEEK_SET);

                    sprintf(path, "%s/w%s.%03d",Conf.spoolPath, entry, queue);
                    newFH=fopen(path, "wb");
                    if (newFH) {
                        /* Now remove the bounced entries */
                        while (!feof(fh) && !ferror(fh)) {
                            if (fgets(line, sizeof(line), fh)) {
                                switch(line[0]) {
                                    case QUEUE_BOUNCE: {
                                        break;
                                    }
                                    default: {
                                        fputs(line, newFH);
                                    }
                                }
                            }
                        }
                        fclose(newFH);
                    }
                    fclose(fh);
                } else {
                    if (fh) {
                        fclose(fh);
                    }
                    if (data) {
                        fclose(data);
                    }

                    LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_FILE_OPEN_FAILURE, LOG_CRITICAL, entryID, path, __FILE__, __LINE__, 0, NULL, 0);
                    ProcessQueueEntryCleanUp(idLock, report);
                    return(FALSE);
                }                
            }
            if (data) {
                fclose(data);
            }
            if (keep) {
                unsigned char    Path2[XPL_MAX_PATH+1];

                sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue);
                unlink(path);

                sprintf(Path2, "%s/w%s.%03d", Conf.spoolPath, entry, queue);
                rename(Path2, path);
            } else {
                sprintf(path, "%s/w%s.%03d", Conf.spoolPath, entry, queue);
                unlink(path);
        
                sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue);
                unlink(path);

                sprintf(path, "%s/d%s.msg", Conf.spoolPath, entry);
                unlink(path);

                XplSafeDecrement(Queue.queuedLocal);
                ProcessQueueEntryCleanUp(idLock, report);
                return(TRUE);
            }
        }
        default: {
            break;
        }
    }

    if (Agent.agent.state < BONGO_AGENT_STATE_STOPPING) {
        sprintf(path, "%s/d%s.msg", Conf.spoolPath, entry);
    } else {
        ProcessQueueEntryCleanUp(idLock, report);
        return(TRUE);
    }

    if (stat(path, &sb) == 0) {
        dSize = (unsigned long)sb.st_size;
    } else {
        ProcessQueueEntryCleanUp(idLock, report);
        return(TRUE);
    }


    for (used = 0; (used < (unsigned long)Queue.pushClients.count) && (Agent.agent.state < BONGO_AGENT_STATE_STOPPING); used++) {
        if ((Queue.pushClients.array[used].queue == queue) && (Queue.pushClients.array[used].errorCount <= MAX_PUSHCLIENTS_ERRORS)) {
            sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue);
            if ((stat(path, &sb) == 0) && ((fh = fopen(path, "rb")) != NULL)) {
                /* Count the number of lines */
                do {
                    if (fgets(line, CONN_BUFSIZE, fh) 
                            && ((line[0] == QUEUE_RECIP_REMOTE) || (line[0] == QUEUE_RECIP_LOCAL) || (line[0] == QUEUE_RECIP_MBOX_LOCAL))) {
                        lines++;
                    }
                } while (!feof(fh) && !ferror(fh));
            } else {
                ProcessQueueEntryCleanUp(idLock, report);
                return(TRUE);
            }

            fseek(fh, 0, SEEK_SET);

            client = QueueClientAlloc();
            memset(client, 0, sizeof(QueueClient));
            
            if (client && ((client->conn = ConnAlloc(TRUE)) != NULL)) {
                client->authorized = TRUE;
            } else {
                if (client) {
                    QueueClientFree(client);
                }

                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_OUT_OF_MEMORY, LOG_ERROR, entryID, __FILE__, NULL, sizeof(QueueClient), __LINE__, NULL, 0);
                if (fh) {
                    fclose(fh);
                }

                fh = NULL;
                continue;
            }

            /* fixme - reevaluate this section
               This works around connecting to ourselves if the qclients info is broken */
            ConnSocket(client->conn);
            memset(&saddr, 0, sizeof(struct sockaddr_in));
            saddr.sin_family = AF_INET;

            bind(client->conn->socket, (struct sockaddr *)&saddr, sizeof(saddr));

            len = sizeof(saddr);
            getsockname(client->conn->socket, (struct sockaddr *)&saddr, &len);

            Queue.pushClients.array[used].usageCount++;

            if (saddr.sin_port == Queue.pushClients.array[used].port) {
                if ((Queue.pushClients.array[used].address == 0x0100007F) || (Queue.pushClients.array[used].address == saddr.sin_addr.s_addr)) {
                    ConnClose(client->conn, 0);
                    ConnFree(client->conn);
                    client->conn = NULL;

                    if (RemovePushAgentIndex(used, TRUE)) {
                        used--;
                    }

                    QueueClientFree(client);

                    if (fh) {
                        fclose(fh);
                    }

                    fh = NULL;

                    ProcessQueueEntryCleanUp(idLock, report);
                    return(TRUE);
                }
            }

            /* Contacting waiting client */
            memset(&saddr, 0, sizeof(struct sockaddr_in));
            saddr.sin_family = AF_INET;
            saddr.sin_addr.s_addr = Queue.pushClients.array[used].address;
            saddr.sin_port = Queue.pushClients.array[used].port;

            len = connect(client->conn->socket, (struct sockaddr *)&saddr, sizeof(saddr));
            if (!len) {
                /* Non-blocking, w/o nagele */
                len = 1;
                setsockopt(client->conn->socket, IPPROTO_TCP, 1, (unsigned char *)&len, sizeof(len));

                Queue.pushClients.array[used].errorCount = 0;
            } else {
                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_QUEUE, LOGGER_EVENT_PQE_FAILED_CONNECT, LOG_WARNING, entryID, entry, NULL, queue, XplHostToLittle(saddr.sin_addr.s_addr), &(Queue.pushClients.array[used].port), sizeof(Queue.pushClients.array[used].port));

                ConnClose(client->conn, 0);
                ConnFree(client->conn);
                client->conn = NULL;

                if (RemovePushAgentIndex(used, FALSE)) {
                    used--;
                }

                QueueClientFree(client);

                if (fh) {
                    fclose(fh);
                }

                fh = NULL;

                ProcessQueueEntryCleanUp(idLock, report);
                return(TRUE);
            }

            /* We got a connection to the guy, tell him what to do */
            ConnWriteF(client->conn, "6020 %03d-%s %ld %ld %ld\r\n", queue, entry, (unsigned long)sb.st_size, dSize, lines);
            ConnWriteFile(client->conn, fh);

            fclose(fh);
            fh = NULL;

            sprintf(client->entry.workQueue, "%03d-%s", queue, entry);
            client->entry.workQueue[3] = '\0';

            ConnWrite(client->conn, "6021 Get busy!\r\n", 16);
            ConnFlush(client->conn);

            client->entry.report = report;

            if (!HandleCommand(client)) {
                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_QUEUE, LOGGER_EVENT_PQE_FAILED, LOG_WARNING, entryID, entry, NULL, queue, XplHostToLittle(saddr.sin_addr.s_addr), &(Queue.pushClients.array[used].port), sizeof(Queue.pushClients.array[used].port));
            }

            /* fixme - evaluate this section.
               Clean up behind us ? */
            if (client->entry.work) {
                if (Agent.flags & QUEUE_AGENT_DEBUG) {
                    XplConsolePrintf("bongoqueue: Ran into queue resend [C%s.%03d]!\r\n", entry, queue);
                    XplConsolePrintf("bongoqueue: Last command:%s\r\n", client->buffer);
                }

                fclose(client->entry.work);
                client->entry.work = NULL;

                sprintf(client->path,"%s/w%s.%03d", Conf.spoolPath, entry, queue);
                remove(client->path);
            }

            Queue.pushClients.array[used].usageCount--;

            if (client->conn) {
                ConnClose(client->conn, 0);
                ConnFree(client->conn);
                client->conn = NULL;
            }

            /* fixme - evaluate this section. */
            if (report == client->entry.report) {
                ;
            } else {
                if (report == NULL) {
                    report = client->entry.report;
                } else {
                    ProcessQueueEntryCleanUp(idLock, NULL);
                    return(FALSE);
                }
            }

            QueueClientFree(client);

            if (access(path, 0)) {
                XplSafeDecrement(Queue.queuedLocal);

                ProcessQueueEntryCleanUp(idLock, report);
                return(TRUE);
            }
        } else {
            if (Queue.pushClients.array[used].queue == queue) {
                if (RemovePushAgentIndex(used, FALSE)) {
                    used--;
                }
            }
        }
    }

    if (Agent.agent.state == BONGO_AGENT_STATE_STOPPING) {
        ProcessQueueEntryCleanUp(idLock, report);
        return(TRUE);
    }

    switch(queue) {
        case Q_INCOMING:
        case Q_INCOMING_CLEAN:
        case Q_FORWARD:
        case Q_RULE:
        case Q_FOUR:
        case Q_FIVE:
        case Q_DELIVER: {
            i = queue + 1;
            while(i < 8 && !Queue.pushClientsRegistered[i]) {
                i++;
            }

            sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue);
            sprintf(path2, "%s/c%s.%03d", Conf.spoolPath, entry, i);
            rename(path, path2);

            sprintf(path, "%03d%s", i, entry);
            SpoolEntryIDUnlock(idLock);

            entryIn = MemStrdup(path);
            goto StartOver;
            break;
        }

        case Q_OUTGOING: {
            /* Check if there are any recipients left */
            sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue);

            if ((handle = QDBHandleAlloc()) != NULL) {
               QDBRemoveID(handle, entryID);

               QDBHandleRelease(handle);
            }

            fh = fopen(path,"rb");
            keep = FALSE;
            if (fh) {
                do {
                    if (fgets(line, CONN_BUFSIZE, fh)!=NULL) {
                        switch(line[0]) {
                            case QUEUE_BOUNCE: {
                                bounce = TRUE;
                                break;
                            }

                            case QUEUE_CALENDAR_LOCAL:
                            case QUEUE_RECIP_LOCAL:
                            case QUEUE_RECIP_MBOX_LOCAL: {
                                keep = TRUE;
                                break;
                            }

                            case QUEUE_RECIP_REMOTE: {
                                int ccode;

                                ptr = strchr(line + 1, ' ');
                                if (ptr) {
                                    *ptr = '\0';
                                }

                                ptr2 = strchr(line + 1, '@');
                                if (ptr2) {
                                    if ((handle = QDBHandleAlloc()) != NULL) {
                                        ccode = QDBAdd(handle, ptr2 + 1, entryID, queue);
                                        if (ccode == -1) {
                                            LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_DATABASE, LOGGER_EVENT_DATABASE_INSERT_ERROR, LOG_ERROR, 0, ptr2 + 1, NULL, ccode, 0, NULL, 0);
                                        }

                                        QDBHandleRelease(handle);
                                    }
                                }

                                if (ptr) {
                                    *ptr = ' ';
                                }

                                keep = TRUE;
                                break;
                            }
                        }
                    }
                } while (!feof(fh) && !ferror(fh));
                fclose(fh);
            } else {
                keep = TRUE;
            }

            /* Note that we make sure to keep the content of path pointing to the control file; this way we only need one sprintf */
            if (bounce) {
                /* Call the bouncing code */
                sprintf(path2, "%s/d%s.msg", Conf.spoolPath, entry);
                data = fopen(path2, "rb");
                fh = fopen(path, "rb");

                if (fh && data && 0 == HandleDSN(data, fh)) {
                    fclose(data);

                    /* If we're not keeping the file, we can ignore its contents */
                    if (keep) {
                        sprintf(path2, "%s/w%s.%03d", Conf.spoolPath, entry, queue);
                        newFH = fopen(path2, "wb");
                        if (newFH) {
                            /* Now remove the bounced entries */
                            while (!feof(fh) && !ferror(fh)) {
                                if (fgets(line, CONN_BUFSIZE, fh)) {
                                    switch(line[0]) {
                                        case QUEUE_BOUNCE: {
                                            break;
                                        }

                                        default: {
                                            fputs(line, newFH);
                                            break;
                                        }
                                    }
                                }
                            }

                            fclose(fh);
                            fclose(newFH);

                            unlink(path);
                            rename(path2, path);
                        } else {
                            fclose(fh);
                        }
                    } else {
                        fclose(fh);
                    }
                } else {
                    if (fh) {
                        fclose(fh);
                    }

                    if (data) {
                        fclose(data);
                    }

                    LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_FILE_OPEN_FAILURE, LOG_CRITICAL, entryID, path, __FILE__, __LINE__, 0, NULL, 0);

                    ProcessQueueEntryCleanUp(idLock, report);
                    return(TRUE);
                }
            }

            if (!keep) {
                XplSafeDecrement(Queue.queuedLocal);

                unlink(path);

                sprintf(path, "%s/d%s.msg",Conf.spoolPath, entry);
                unlink(path);

                break;
            }

            break;
        }

        case Q_RTS: {
            if (date < time(NULL) - Conf.maxLinger) {
                if ((handle = QDBHandleAlloc()) != NULL) {
                    QDBRemoveID(handle, entryID);

                    QDBHandleRelease(handle);
                }

                sprintf(path, "%s/w%s.%03d", Conf.spoolPath, entry, queue);
                newFH = fopen(path, "wb");

                sprintf(path, "%s/d%s.msg", Conf.spoolPath, entry);
                data = fopen(path, "rb");

                sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue);
                fh = fopen(path, "rb");

                if (fh && newFH && data) {
                    XplSafeIncrement(Queue.remoteDeliveryFailed);

                    /* Write the new control file, containing the bounces */
                    while (!feof(fh) && !ferror(fh)) {
                        if (fgets(line, sizeof(line), fh)) {
                            switch(line[0]) {
                                case QUEUE_CALENDAR_LOCAL: {
                                    /* We don't bounce those yet */
                                    break;
                                }

                                case QUEUE_RECIP_LOCAL:
                                case QUEUE_RECIP_REMOTE:
                                case QUEUE_RECIP_MBOX_LOCAL: {
                                    ptr = strchr(line, ' ');
                                    if (ptr) {
                                        /* ptr points to the origrecip */
                                        ptr = strchr(ptr + 1, ' ');
                                        if (ptr) {
                                            /* ptr points to flags */
                                            ptr = strchr(ptr + 1, ' ');
                                            if (ptr) {
                                                /* ptr points behind flags */
                                                *ptr = '\0';
                                                fprintf(newFH, QUEUES_BOUNCE"%s %d\r\n", line + 1, DELIVER_TOO_LONG);
                                                *ptr = ' ';
                                            } else {
                                                fprintf(newFH, QUEUES_BOUNCE"%s %d\r\n", line + 1, DELIVER_TOO_LONG);
                                            }
                                        }
                                    }
                                    break;
                                }

                                default: {
                                    fputs(line, newFH);
                                    break;
                                }
                            }
                        }
                    }

                    fclose(newFH);
                    fclose(fh);

                    /* Still got path from above */
                    unlink(path);
                    sprintf(path2, "%s/w%s.%03d", Conf.spoolPath, entry, queue);
                    rename(path2, path);

                    fh = fopen(path, "rb");
                    if (fh) {
                        HandleDSN(data, fh);
                        fclose(fh);
                    } else {
                        LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_FILE_OPEN_FAILURE, LOG_CRITICAL, entryID, path, __FILE__, __LINE__, 0, NULL, 0);
                    }

                    fclose(data);

                    sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue);
                    unlink(path);

                    sprintf(path, "%s/d%s.msg", Conf.spoolPath, entry);
                    unlink(path);

                    XplSafeDecrement(Queue.queuedLocal);
                } else {
                    if (data) {
                        fclose(data);
                    }

                    if (fh) {
                        fclose(fh);
                    }

                    if (newFH) {
                        fclose(newFH);
                    }
                }

                ProcessQueueEntryCleanUp(idLock, report);
                return(TRUE);
            } else {
                count = 0;
                sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, Q_RTS);
                sprintf(path2, "%s/c%s.%03d", Conf.spoolPath, entry, Q_INCOMING);
                rename(path, path2);

                sprintf(path, "%03d%s", Q_INCOMING, entry);
            }

            SpoolEntryIDUnlock(idLock);
            entryIn = MemStrdup(path);
            goto StartOver;
            break;
        }

        case Q_DIGEST: {
            /* if message in here older than Conf.maxLinger, removed */
            break;
        }

        default: {
            break;
        }
    }

    ProcessQueueEntryCleanUp(idLock, report);

    return(TRUE);
}

static BOOL 
CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOOL force)
{
    int reason = 0;
    int oReason;
    unsigned long flags;
    unsigned long bodySize = 0;
    unsigned long maxBodySize;
    unsigned long received = 0;
    unsigned long handling;
    unsigned char *ptr;
    unsigned char *ptr2;
    unsigned char *transcript;
    unsigned char timeLine[80];
    unsigned char line[CONN_BUFSIZE + 1];
    unsigned char dn[MDB_MAX_OBJECT_CHARS + 1];
    unsigned char postmaster[MAXEMAILNAMESIZE + 1];
    unsigned char sender[MAXEMAILNAMESIZE + 1];
    unsigned char aSender[MAXEMAILNAMESIZE + 1] = "";
    unsigned char recipient[MAXEMAILNAMESIZE + 1] = "";
    unsigned char oRecipient[MAXEMAILNAMESIZE + 1] = "";
    unsigned char envID[128] = "";
    BOOL mBounce=FALSE;
    BOOL header;
    time_t now;
    MDBValueStruct *vs;

    /* Step 0, check if we want to bounce at all */
    now = time(NULL);

    /* Spam block */
    if (Conf.bounceBlockSpam && !force) {
        if (Queue.lastBounce >= now - Conf.bounceInterval) {
            Queue.lastBounce = now;
            Queue.bounceCount++;
            if (Queue.bounceCount > Conf.bounceMax) {
                /* We don't want the bounce, probably spam */
                XplSafeIncrement(Queue.totalSpam);
                return(FALSE);
            }
        } else {
            Queue.lastBounce = now;
            Queue.bounceCount = 0;
        }
    }

    /* Step 1, grab info from original control file */
    oReason = 0;
    do {
        if (fgets(line, CONN_BUFSIZE, control)) {
            switch(line[0]) {
                case QUEUE_BOUNCE: {
                    /* Syntax: BRecip ORecip DSN failure reason transcript */
                    oReason = reason;
                    if (sscanf(line + 1, "%s %s %lu %d", recipient, oRecipient, &flags, &reason) == 4) {
                        if (!mBounce && (oReason != 0) && (oReason != reason)) {
                            mBounce = TRUE;
                            reason = 0;
                        }
                    }

                    break;
                }

                case QUEUE_FROM: {
                    ptr = strchr(line + 1,' ');
                    if (ptr) {
                        *ptr = '\0';
                        ptr2 = strchr(ptr+1,' ');
                        if (ptr2) {
                            *ptr2 = '\0';
                            strncpy(envID, ptr2, 127);
                        }

                        strcpy(aSender, ptr + 1);
                    }

                    strcpy(sender, line + 1);
                    if ((sender[0] == '-') && (sender[1] == '\0')) {
                        /* We don't bounce bounces */
                        return(FALSE);
                    }

                    break;
                }

                case QUEUE_DATE: {
                    received = atol(line + 1);
                    break;
                }
            }
        }
    } while (!feof(control) && !ferror(control));

    if (recipient[0] == '\0') {
        return(FALSE);
    }

    sprintf(postmaster, "%s@%s", Conf.postMaster, Conf.officialName);
    handling = Conf.bounceHandling;

    /* We're guaranteed to have a recipient */
    ptr = strchr(recipient, '@');
    if (ptr) {
        if (MsgDomainExists(ptr + 1, dn)) {
            vs = MDBCreateValueStruct(Agent.agent.directoryHandle, NULL);
            if (MDBRead(dn, MSGSRV_A_POSTMASTER, vs) > 0) {
                ptr2 = strrchr(vs->Value[0], '\\');
                if (ptr2) {
                    sprintf(postmaster, "%s@%s", ptr2 + 1, ptr + 1);
                }

                MDBFreeValues(vs);
            }

	    if (MDBRead(dn, MSGSRV_A_BOUNCE_RETURN, vs)>0) {
		if (atol(vs->Value[0])) {
		    handling |= BOUNCE_RETURN;
		} else {
		    handling &= ~BOUNCE_RETURN;
		}
	    }
	    MDBFreeValues(vs);
	    if (MDBRead(dn, MSGSRV_A_BOUNCE_CC_POSTMASTER, vs)>0) {
		if (atol(vs->Value[0])) {
		    handling |= BOUNCE_CC_POSTMASTER;
		} else {
		    handling &= ~BOUNCE_CC_POSTMASTER;
		}
	    }
	    MDBFreeValues(vs);
            MDBDestroyValueStruct(vs);
        }
    }

    /* Step 2, create the bounce; we've got all information */
    MsgGetRFC822Date(-1, 0, timeLine);

    fprintf(rtsData, "Date: %s\r\n", timeLine);
    fprintf(rtsData, "From: Mail Delivery System <%s>\r\n", postmaster);
    fprintf(rtsData, "Message-Id: <%lu-%lu@%s>\r\n", now, (long unsigned int)XplGetThreadID(), Conf.officialName);
    fprintf(rtsData, "To: <%s>\r\n", sender);
    fprintf(rtsData, "MIME-Version: 1.0\r\nContent-Type: multipart/report; report-type=delivery-status;\r\n\tboundary=\"%lu-%lu-%s\"\r\n", now, (long unsigned int)XplGetThreadID(), Conf.officialName);
    switch (reason) {
        case DELIVER_FAILURE:           fprintf(rtsData, "Subject: Returned mail: Delivery failure\r\n");               break;
        case DELIVER_HOST_UNKNOWN:      fprintf(rtsData, "Subject: Returned mail: Host unknown\r\n");                   break;
        case DELIVER_BOGUS_NAME:        fprintf(rtsData, "Subject: Returned mail: Unrecognized address\r\n");           break;
        case DELIVER_TIMEOUT:           fprintf(rtsData, "Subject: Returned mail: Remote host unreachable\r\n");        break;
        case DELIVER_REFUSED:           fprintf(rtsData, "Subject: Returned mail: Delivery refused\r\n");               break;
        case DELIVER_UNREACHABLE:       fprintf(rtsData, "Subject: Returned mail: Remote host unreachable\r\n");        break;
        case DELIVER_LOCKED:            fprintf(rtsData, "Subject: Returned mail: Mailbox locked\r\n");                 break;
        case DELIVER_USER_UNKNOWN:      fprintf(rtsData, "Subject: Returned mail: User unknown\r\n");                   break;
        case DELIVER_HOPCOUNT_EXCEEDED: fprintf(rtsData, "Subject: Returned mail: Too many hops\r\n");                  break;
        case DELIVER_TRY_LATER:         fprintf(rtsData, "Subject: Returned mail: Delivery failure\r\n");               break;
        case DELIVER_INTERNAL_ERROR:    fprintf(rtsData, "Subject: Returned mail: Unspecified error\r\n");              break;
        case DELIVER_TOO_LONG:          fprintf(rtsData, "Subject: Returned mail: Delivery time exceeded\r\n");         break;
        case DELIVER_QUOTA_EXCEEDED:    fprintf(rtsData, "Subject: Returned mail: Quota exceeded\r\n");                 break;
        case DELIVER_BLOCKED:           fprintf(rtsData, "Subject: Returned mail: Message Blocked\r\n");                break;
        case DELIVER_VIRUS_REJECT:      fprintf(rtsData, "Subject: Returned mail: Possible Virus Infection\r\n");       break;
        default:
        case DELIVER_SUCCESS:
        case 0:                         fprintf(rtsData, "Subject: Returned mail: Delivery Status Notification\r\n");   break;
    }

    fprintf(rtsData, "Precedence: bulk\r\n\r\nThis is a MIME-encapsulated message\r\n\r\n");

    /* First section, human readable */
    fprintf(rtsData, "--%lu-%lu-%s\r\nContent-type: text/plain; charset=US-ASCII\r\n\r\n", now, (long unsigned int)XplGetThreadID(), Conf.officialName);

    MsgGetRFC822Date(-1, received, timeLine);
    fprintf(rtsData, "The original message was received %s\r\nfrom %s\r\n\r\n", timeLine, aSender[0]!='\0' ? aSender : sender);

    if (!mBounce) {
        if (reason==DELIVER_SUCCESS) {
            fprintf(rtsData, "   ----- The following address(es) have been delivered -----\r\n");
        } else if (reason==DELIVER_PENDING) {
            fprintf(rtsData, "   ----- The following address(es) have temporary problems -----\r\n");
        } else {
            fprintf(rtsData, "   ----- The following address(es) had permanent fatal errors -----\r\n");
        }
    }

    /* Need to go through the file and enumerate all recipients */
    fseek(control, 0, SEEK_SET);
    do {
        if (fgets(line, CONN_BUFSIZE, control)) {
            if (line[0] == QUEUE_BOUNCE) {
                CHOP_NEWLINE(line);

                transcript = NULL;
                flags = DSN_FAILURE | DSN_HEADER;
                reason = DELIVER_INTERNAL_ERROR;
                oRecipient[0] = '\0';

                ptr = strchr(line + 1, ' ');
                if (ptr) {
                    /* Grab the original recipient */
                    *ptr='\0';
                    ptr++;
                    ptr2 = strchr(ptr, ' ');
                    if (ptr2) {
                        *ptr2 = '\0';
                        strcpy(oRecipient, ptr);
                        ptr2++;
                        flags = (atol(ptr2) & DSN_FLAGS);
                        ptr = strchr(ptr2, ' ');
                        if (ptr) {
                            reason = atol(ptr + 1);
                            ptr2 = strchr(ptr + 1, ' ');
                            if (ptr2) {
                                transcript = ptr2 + 1;
                            }
                        }
                    } else {
                        strcpy(oRecipient, ptr);
                    }
                }

                /* Now print the status for each */
                if (oRecipient[0] != '\0') {
                    if (reason != DELIVER_SUCCESS) {
                        fprintf(rtsData, "<%s>; originally to %s (unrecoverable error)\r\n", line + 1, (char *)oRecipient);
                    } else {
                        fprintf(rtsData, "<%s>; originally to %s\r\n", line + 1, (char *)oRecipient);
                    }
                } else if (reason != DELIVER_SUCCESS) {
                    fprintf(rtsData, "<%s> (unrecoverable error)\r\n", line + 1);
                } else {
                    fprintf(rtsData, "<%s>\r\n", line + 1);
                }

                switch (reason) {
                    case DELIVER_HOST_UNKNOWN: {
                        ptr = strchr(recipient, '@');
                        if (ptr) {
                            fprintf(rtsData, "  \tThe host '%s' is unknown or could not be looked up\r\n", ptr + 1);
                        } else {
                            fprintf(rtsData, "  \tThe host '%s' is unknown or could not be looked up\r\n", recipient);
                        }

                        break;
                    }

                    case DELIVER_TOO_LONG:
                    case DELIVER_TIMEOUT: {
                        fprintf(rtsData, "  \tThe mail system tried to deliver the message for the last %d days,\r\n\tbut was not able to successfully do so.\r\n",(int)Conf.maxLinger / 86400);
                        break;
                    }

                    case DELIVER_REFUSED: {
                        ptr = strchr(recipient, '@');
                        fprintf(rtsData, "  \tThe mail exchanger for domain '%s' refused to talk to us.\r\n", ptr+1);
                        break;
                    }

                    case DELIVER_UNREACHABLE: {
                        ptr = strchr(recipient, '@');
                        fprintf(rtsData, "  \tThe mail exchanger for domain '%s' was not reachable.\r\n", ptr+1);
                        break;
                    }

                    case DELIVER_USER_UNKNOWN: {
                        ptr = strchr(recipient, '@');
                        if (ptr) {
                            *ptr = '\0';
                            fprintf(rtsData, "  \tThe recipient '%s' is unknown at host '%s'\r\n", recipient, ptr + 1);
                            *ptr = '@';
                        } else {
                            fprintf(rtsData, "  \tThe recipient '%s' is unknown\r\n", recipient);
                        }

                        break;
                    }

                    case DELIVER_HOPCOUNT_EXCEEDED: {
                        fprintf(rtsData, "  \tThe message was routed through too many hosts.\r\n\tThis usually indicates a mail loop.\r\n");
                        break;
                    }

                    case DELIVER_QUOTA_EXCEEDED: {
                        if (Conf.quotaMessage && (Conf.quotaMessage[0] != '\0')) {
                            fprintf(rtsData, Conf.quotaMessage);
                        } else {
                            fprintf(rtsData, DEFAULT_QUOTA_MESSAGE);
                        }                              
                        break;
                    }

                    case DELIVER_SUCCESS: {
                        fprintf(rtsData, "  \tThe message has been successfully delivered.\r\n");
                        break;
                    }

                    case DELIVER_LOCKED:
                    case DELIVER_FAILURE:
                    case DELIVER_TRY_LATER:
                    case DELIVER_INTERNAL_ERROR:
                    default: {
                        fprintf(rtsData, "  \tThe mail system encountered a delivery failure, code %d.\r\n", reason);
                        fprintf(rtsData, "  \tThis failure could be due to circumstances out of its control,\r\n");
                        fprintf(rtsData, "  \tplease check the transcript for details\r\n");
                        break;
                    }
                }

                if (transcript) {
                    fprintf(rtsData, "  \t  ----- transcript of session follows -----\r\n");
                    ptr = transcript;
                    while ((ptr = strchr(ptr, '\\'))!=NULL) {
                        switch(*(ptr + 1)) {
                            case 'r': {
                                memmove(ptr, ptr + 1, strlen(ptr));
                                *ptr = '\r';
                                break;
                            }

                            case 'n': {
                                memmove(ptr, ptr + 1, strlen(ptr));
                                *ptr = '\n';
                                break;
                            }
                        }

                        ptr++;
                    }

                    fprintf(rtsData, "%s", transcript);
                }

                fprintf(rtsData, "\r\n");
            }
        }
    } while (!feof(control) && !ferror(control));

    /* Second section, computer readable */
    fprintf(rtsData, "--%lu-%lu-%s\r\nContent-type: message/delivery-status\r\n\r\n", now, (long unsigned int)XplGetThreadID(), Conf.officialName);

    /* Per message fields */
    if (envID[0]!='\0') {
        fprintf(rtsData, "Original-Envelope-Id: %s\r\n", envID);
    }

    fprintf(rtsData, "Reporting-MTA: dns; %s\r\n", Conf.officialName);

    MsgGetRFC822Date(-1, received, timeLine);
    fprintf(rtsData, "Arrival-Date: %s\r\n", timeLine);

    /* Per recipient fields */
    /* Need to go through the file and enumerate all recipients */
    fseek(control, 0, SEEK_SET);
    do {
        if (fgets(line, CONN_BUFSIZE, control)) {
            if (line[0] == QUEUE_BOUNCE) {
                CHOP_NEWLINE(line);

                flags = DSN_FAILURE | DSN_HEADER;
                reason = DELIVER_INTERNAL_ERROR;
                oRecipient[0] = '\0';

                ptr = strchr(line + 1, ' ');
                if (ptr) {
                    /* Grab the original recipient */
                    *ptr = '\0';
                    ptr++;
                    ptr2 = strchr(ptr, ' ');
                    if (ptr2) {
                        *ptr2 = '\0';
                        strcpy(oRecipient, ptr);
                        ptr2++;
                        flags = (atol(ptr2) & DSN_FLAGS);
                        ptr = strchr(ptr2, ' ');
                        if (ptr) {
                            reason = atol(ptr + 1);
                        }
                    } else {
                        strcpy(oRecipient, ptr);
                    }
                }

                /* Each per recipient block in DSN requires CRLF */
                fprintf(rtsData, "\r\n"); 

                if (oRecipient[0] != '\0') {
                    fprintf(rtsData, "Original-recipient: %s\r\n", oRecipient);
                }

                fprintf(rtsData, "Final-recipient: %s\r\n", line + 1);
                if (reason == DELIVER_SUCCESS) {
                    fprintf(rtsData, "Action: delivered\r\n");
                    fprintf(rtsData, "Status: 2.0.0\r\n");
                } else if (reason == DELIVER_PENDING) {
                    fprintf(rtsData, "Action: delayed\r\n");
                    fprintf(rtsData, "Status: 4.0.0\r\n");
                } else {
                    /* fixme - this could be smarter, use the status code */
                    fprintf(rtsData, "Action: failed\r\n");
                    fprintf(rtsData, "Status: 5.0.0\r\n");
                }
            }
        }
    } while (!feof(control) && !ferror(control));

    /* Third section, original message */
    fprintf(rtsData, "--%lu-%lu-%s\r\nContent-type: message/rfc822\r\n\r\n", now, (long unsigned int)XplGetThreadID(), Conf.officialName);

    header = TRUE;
    maxBodySize = -1;
    while (!feof(data) && !ferror(data)) {
        if ((ptr = fgets(line, CONN_BUFSIZE, data)) != NULL) {
            if (header) {
                if((line[0] == '\r') && (line[1] == '\n') && (line[2] == 0)) {
                    header = FALSE;
                    if (Conf.bounceMaxBodySize) {
                        fprintf(rtsData, "\r\n<Body content limited to %lu bytes>\r\n", Conf.bounceMaxBodySize);
                        maxBodySize = Conf.bounceMaxBodySize;
                    }
                }

                if (flags & DSN_HEADER) {
                    if ((line[0] == 'F') && (line[1] == 'r') && (line[4] == ' ')) {
                        fwrite(">", sizeof(unsigned char), 1, rtsData);
                    }

                    bodySize += fwrite(line, sizeof(unsigned char), strlen(line), rtsData);
                }
            } else {
                if (flags & DSN_BODY) {
                    bodySize += fwrite(line, sizeof(unsigned char), strlen(line), rtsData);
                    if (bodySize > maxBodySize) {
                        break;
                    }
                }
            }
        }
    }

    if (!(flags & DSN_BODY)) {
        fprintf(rtsData, "-- Message body has been omitted --\r\n");
    }

    /* End of message */
    fprintf(rtsData, "\r\n--%lu-%lu-%s--\r\n", now, (long unsigned int)XplGetThreadID(), Conf.officialName);

    /* Now create the control file */
    fprintf(rtsControl, "D%lu\r\n", now);
    fprintf(rtsControl, "F- -\r\n");

    XplRWReadLockAcquire(&Conf.lock);

    if (Conf.bounceHandling & BOUNCE_RETURN) {
        fprintf(rtsControl, QUEUES_RECIP_REMOTE"%s %s 0\r\n", sender, sender);
    }

    if (Conf.bounceHandling & BOUNCE_CC_POSTMASTER) {
        fprintf(rtsControl, QUEUES_RECIP_REMOTE"%s %s 0\r\n", postmaster, postmaster);
    }

    XplRWReadLockRelease(&Conf.lock);

    return(TRUE);
}

static int
HandleDSN(FILE *data, FILE *control)
{
    unsigned long id;
    unsigned long *idLock;
    unsigned char path[XPL_MAX_PATH + 1];
    FILE *rtsControl;
    FILE *rtsData;
    XplThreadID threadID;

    XplMutexLock(Queue.queueIDLock);
    id = Queue.queueID++ & ((1 << 28) - 1);
    XplMutexUnlock(Queue.queueIDLock);

    sprintf(path, "%s/c%07lx.%03d", Conf.spoolPath, id, Q_INCOMING);

    /* Create control file */
    rtsControl = fopen(path,"wb");
    if (!rtsControl) {
        fprintf (stderr, "could not open rtsControl\n");
        return -1;
    }

    idLock = SpoolEntryIDLock(id);

    /* Create data file */
    sprintf(path, "%s/d%07lx.msg", Conf.spoolPath, id);
    rtsData = fopen(path, "wb");
    if (!rtsData) {
        fprintf (stderr, "could not open rtsData\n");
        fclose(rtsControl);
        sprintf(path, "%s/d%07lx.msg", Conf.spoolPath, id);
        unlink(path);
        return -1;
    }

    if (!CreateDSNMessage(data, control, rtsData, rtsControl, FALSE)) {
        fclose(rtsControl);
        fclose(rtsData);

        sprintf(path, "%s/d%07lx.msg", Conf.spoolPath, id);
        unlink(path);

        sprintf(path, "%s/c%07lx.%03d", Conf.spoolPath, id, Q_INCOMING);
        unlink(path);

        SpoolEntryIDUnlock(idLock);
        return 0;
    }

    XplSafeIncrement(Queue.queuedLocal);

    fclose(rtsControl);
    fclose(rtsData);

    SpoolEntryIDUnlock(idLock);
    sprintf(path, "%03d%lx",Q_INCOMING, id);

    if (XplSafeRead(Queue.activeWorkers) < Conf.maxConcurrentWorkers) {
        XplBeginCountedThread(&threadID, ProcessQueueEntry, STACKSPACE_Q, MemStrdup(path), id, Queue.activeWorkers);
    }

    return 0;
}

static void
CheckQueue(void *queueIn)
{
    int ccode;
    int burst;
    int count;
    unsigned long found;
    unsigned long handled;
    unsigned char path[XPL_MAX_PATH + 1];
    time_t now;
    XplDir *dirP;
    XplDir *dirEntry;
    XplThreadID id;
    BOOL flushing;

    XplRenameThread(XplGetThreadID(), "Message-queue Monitor");

    for (count = 0; (count < 60) && (Agent.agent.state < BONGO_AGENT_STATE_STOPPING); count++) {
        XplDelay(1000);
    }

    burst = 0;
    while (Agent.agent.state < BONGO_AGENT_STATE_STOPPING) {
        found=0;
        handled=0;

        flushing = Queue.flushNeeded;
        Queue.restartNeeded = Queue.flushNeeded = FALSE;

        if (flushing) {
            XplConsolePrintf ("bongoqueue: flushing the queue\n");
        }

        if (Agent.flags & QUEUE_AGENT_DEBUG) {
            XplConsolePrintf("bongoqueue: Restarting queue:");
        }

        if (!flushing) {
            now = time(NULL) - Conf.queueInterval + 60;
        }

        dirP = XplOpenDir(Conf.spoolPath);
        if (dirP == NULL) {
            goto delayQueue;
        }

        while (Agent.agent.state < BONGO_AGENT_STATE_STOPPING) {
            dirEntry = XplReadDir(dirP);
            if (!dirEntry) {
                break;
            }

            /* We don't want to work on .IN [incoming] files */
            if (strlen(dirEntry->d_nameDOS) != 12 ||
                toupper(dirEntry->d_nameDOS[0]) != 'C'||
                !isdigit(dirEntry->d_nameDOS[9])) {
                continue;
            }

            found++;
            sprintf(path, "%.3s%.7s", dirEntry->d_nameDOS + 9, dirEntry->d_nameDOS + 1);
                
            /* skip recently-touched messages that are in the outgoing
             * queue (queue 7) */
            if (!flushing && path[2] == '7' && XplCalendarTime(dirEntry->d_cdatetime) > (unsigned long)now) {
                continue;
            }

            /* wait for an empty queue slot */
            while (Agent.agent.state < BONGO_AGENT_STATE_STOPPING &&
                   XplSafeRead(Queue.activeWorkers) >= Conf.maxConcurrentWorkers) {
                XplDelay(250);
            }

            handled++;
            XplBeginCountedThread(&id, ProcessQueueEntry, STACKSPACE_Q, MemStrdup(path), ccode, Queue.activeWorkers);
            
            if (++burst > 32) {
                burst = 0;
                XplDelay(55);
            }
        }

        XplCloseDir(dirP);
        if (Agent.flags & QUEUE_AGENT_DEBUG) {
            XplConsolePrintf(" Entries found: %4lu, handled:%4lu, %srestart\r\n", found, handled, Queue.restartNeeded ? "Have " : "No ");
        }

        if (flushing) {
            XplConsolePrintf ("bongoqueue: finished flushing the queue\n");
        }

    delayQueue:
        for (count = 0; 
             count < Conf.queueInterval &&
                 Agent.agent.state < BONGO_AGENT_STATE_STOPPING &&
                 !Queue.restartNeeded && !Queue.flushNeeded;
             count++) {
            XplDelay(1000);
        }
    }

    XplSafeDecrement(Queue.activeWorkers);

#if VERBOSE
    XplConsolePrintf("bongoqueue: Message queue monitor done.\r\n");
#endif

    XplExitThread(EXIT_THREAD, 0);

    return;
}

BOOL
QueueInit(void) 
{
    if (!InitSpoolEntryIDLocks()) {
        XplConsolePrintf(AGENT_NAME ": Couldn't create spool locks.\r\n");
        return FALSE;
    }    

    XplMutexInit(Queue.pushClients.lock);
    XplMutexInit(Queue.queueIDLock);

    XplSafeWrite(Queue.queuedLocal, 0);
    XplSafeWrite(Queue.activeWorkers, 0);

    Queue.queueID = time(NULL) & ((1 << 28) - 1);

    /* initialize the push agents registered array in case no agents
     * connect to us */
    RemoveAllPushAgents();

    return TRUE;
}

void
QueueShutdown(void) 
{
#if VERBOSE
    XplConsolePrintf("bongoqueue: Writing queue agent list\r\n");
#endif
    RemoveAllPushAgents();

    QDBShutdown();

    DeInitSpoolEntryIDLocks();
    
    XplMutexDestroy(Queue.pushClients.lock);
    XplMutexDestroy(Queue.queueIDLock);
}

int 
CreateQueueThreads(BOOL failed)
{
    int i;
    unsigned long count;
    unsigned long total;
    unsigned long current;
    unsigned char path[XPL_MAX_PATH + 1];
    FILE *control;
    FILE *killFile;
    XplDir *dirP;
    XplDir *dirEntry;
    XplThreadID id;

    if (QDBStartup(2, 64) != 0) {
        return(-1);
    }

    if (failed) {
        XplConsolePrintf("bongoqueue: System not shut down properly, verifying queue integrity.\r\n");
    }
#if VERBOSE
    else {
        XplConsolePrintf("bongoqueue: Verifying queue integrity, quick mode.\r\n");
    }
#endif

    sprintf(path, "%s", Conf.spoolPath);

    dirP = XplOpenDir(path);

    sprintf(path, "%s/fragfile", MsgGetDBFDir(NULL));
    killFile = fopen(path, "wb");
    if (!killFile) {
        if (dirP) {
            XplCloseDir(dirP);
            dirP = NULL;
        }
    }

    XplSafeWrite(Queue.activeWorkers, Conf.maxSequentialWorkers + 1);

    total = 0;
    count = 0;

    /* Count and clean the queue */
    if (dirP) {
        while ((dirEntry = XplReadDir(dirP)) != NULL) {
            if (dirEntry->d_attr & XPL_A_SUBDIR) {
                continue;
            }

            switch(dirEntry->d_nameDOS[0] & 0xDF) {
                case 'C': {        
                    if ((dirEntry->d_nameDOS[strlen(dirEntry->d_nameDOS)-1] & 0xDF) == 'N') {
                        /* Check for *.*N */
                        fprintf(killFile, "%s/c%s\r\n", Conf.spoolPath, dirEntry->d_nameDOS + 1);
                        fprintf(killFile, "%s/d%s\r\n", Conf.spoolPath, dirEntry->d_nameDOS + 1);

                        count += 2;
                    } else if ((dirEntry->d_nameDOS[strlen(dirEntry->d_nameDOS)-1] & 0xDF) == 'K') {
                        /* Check for *.LCK */
                        fprintf(killFile, "%s/%s\r\n", Conf.spoolPath, dirEntry->d_nameDOS);

                        count++;
                    } else if (failed) {
                        sprintf(path, "%s/d%s", Conf.spoolPath, dirEntry->d_nameDOS + 1);
                        sprintf(path + strlen(path) - 3, "msg");
                        if (access(path, 0) == 0) {
                            /* Check for matching D file */
                            if (dirEntry->d_size <= 0) {
                                /* Check for sparse file */
                                fprintf(killFile, "%s/c%s\r\n", Conf.spoolPath, dirEntry->d_nameDOS + 1);
                                fprintf(killFile, "%s/d%s\r\n", Conf.spoolPath, dirEntry->d_nameDOS + 1);

                                count += 2;
                            } else {
                                /* Find the highest number and update Queue.queueID */
                                if (Queue.queueID < ((unsigned long)strtol(dirEntry->d_nameDOS + 1, NULL, 16) + 1)) {
                                    Queue.queueID = (unsigned long)strtol(dirEntry->d_nameDOS + 1, NULL, 16) + 1;
                                }
                                XplSafeIncrement(Queue.queuedLocal);
                            }
                        } else {
                            /* No matching D file */
                            fprintf(killFile, "%s/c%s\r\n", Conf.spoolPath, dirEntry->d_nameDOS + 1);
                            count++;
                        }
                    } else {
                        /* Find the highest number and update Queue.queueID */
                        if (Queue.queueID < ((unsigned long)strtol(dirEntry->d_nameDOS + 1, NULL, 16) + 1)) {
                            Queue.queueID = strtol(dirEntry->d_nameDOS + 1, NULL, 16) + 1;
                        }
                        XplSafeIncrement(Queue.queuedLocal);
                    }

                    break;
                }

                case 'D': {
                    if (dirEntry->d_size <=0 ) {
                        /* Check for sparse file */
                        fprintf(killFile, "%s/c%s\r\n", Conf.spoolPath, dirEntry->d_nameDOS + 1);
                        fprintf(killFile, "%s/d%s\r\n", Conf.spoolPath, dirEntry->d_nameDOS + 1);

                        count += 2;
                    } else if (failed) {
                        sprintf(path, "%s/c%s", Conf.spoolPath, dirEntry->d_nameDOS + 1);
                        for (i = 0; i < 10; i++) {
                            sprintf(path + strlen(path) - 3, "%03d", i);
                            if (access(path, 0)==0) {
                                break;
                            }
                        }
                    
                        if (i == 10) {
                            /* Check for matching C file */
                            fprintf(killFile, "%s/d%s\r\n", Conf.spoolPath, dirEntry->d_nameDOS + 1);

                            count++;
                        }
                    }

                    break;
                }

                case 'W': {
                    /* Remove work file */
                    fprintf(killFile, "%s/%s\r\n", Conf.spoolPath, dirEntry->d_nameDOS);

                    count++;
                    break;
                }

                default: {
                    fprintf(killFile, "%s/%s\r\n", Conf.spoolPath, dirEntry->d_nameDOS);

                    count++;
                    break;
                }
            }
        }

        XplCloseDir(dirP);
    }

    sprintf(path, "%s", Conf.spoolPath);
    dirP = XplOpenDir(path);

    if (killFile) {
        fclose(killFile);
        killFile=NULL;
    }

#if VERBOSE
    XplConsolePrintf("bongoqueue: Queue integrity check complete, now cleaning irrelevant entries.\r\n");
#endif

    sprintf(path, "%s/fragfile", MsgGetDBFDir(NULL));
    killFile=fopen(path, "rb");
    if (!killFile) {
        XplConsolePrintf("bongoqueue: Could not re-open killfile.\r\n");
    } else {
        current = 0;
        while (!feof(killFile) && !ferror(killFile)) {
            if (fgets(path, sizeof(path), killFile)) {
                current++;
                CHOP_NEWLINE(path);

                unlink(path);
            }
        }

        fclose(killFile);
    }

    sprintf(path, "%s/killfile", MsgGetDBFDir(NULL));
    unlink(path);

    XplCloseDir(dirP);

    if (failed) {
        sprintf(path, "%s", Conf.spoolPath);

        dirP = XplOpenDir(path);
        if (dirP) {
            current = 0;
            total -= count;
            if (total == 0) {
                total = 100;
            }

            while ((dirEntry=XplReadDir(dirP)) != NULL) {
                if (dirEntry->d_attr & XPL_A_SUBDIR) {
                    continue;
                }

                current++;
                if ((dirEntry->d_nameDOS[0] & 0xDF) == 'C' ) {
                    sprintf(path, "%s/c%s", Conf.spoolPath, dirEntry->d_nameDOS + 1);
                    control = fopen(path, "r+b");
                    if (!control) {
                        continue;
                    }

                    fseek(control, dirEntry->d_size - 2, SEEK_SET);
                    fwrite("\r\n", 2, 1, control);
                    fclose(control);
                }
            }

            XplCloseDir(dirP);
        }
    }

#if VERBOSE
    XplConsolePrintf("bongoqueue: Queue integrity check complete, starting Queue Monitor [%d].\r\n", XplSafeRead(Queue.queuedLocal));
#endif

    XplSafeWrite(Queue.activeWorkers, 0);

    XplBeginCountedThread(&id, CheckQueue, STACKSPACE_Q, NULL, i, Agent.activeThreads);

    return(0);
}

int 
CommandQaddm(void *param)
{
    int ccode;
    int result;
    unsigned long flags = 0;
    unsigned char *sender;
    unsigned char *authSender;
    unsigned char *recipient;
    unsigned char *mailbox;
    unsigned char *queue;
    unsigned char *ptr;
    unsigned char *ptr2;
    struct stat sb;
    FILE *data;
    QueueClient *client = (QueueClient *)param;

    if (Agent.flags & QUEUE_AGENT_DISK_SPACE_LOW) {
        return(ConnWrite(client->conn, MSG5221SPACELOW, sizeof(MSG5221SPACELOW) - 1));
    }

    ptr = client->buffer + 5;

    /* QADDM <queue>-<id> <sender> <authSender> <recipient> <mailbox> <flags> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && ((sender = strchr(ptr + 4, ' ')) != NULL) 
            && ((authSender = strchr(sender + 1, ' ')) != NULL) 
            && ((recipient = strchr(authSender + 1, ' ')) != NULL) 
            && ((mailbox = strchr(recipient + 1, ' ')) != NULL) 
            && ((ptr2 = strchr(mailbox + 1, ' ')) != NULL)) {
        ptr[3] = '\0';

        *sender++ = '\0';
        *authSender++ = '\0';
        *recipient++ = '\0';
        *mailbox++ = '\0';
        *ptr2++ = '\0';

        queue = ptr;

        ptr += 4;

        flags = atol(ptr2);
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    result = DELIVER_FAILURE;
    
    sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr);
    if ((stat(client->path, &sb) == 0) && ((data = fopen(client->path, "rb")) != NULL)) {
        NMAPConnections list = { 0, };
        struct sockaddr_in saddr;
        MDBValueStruct *vs;

        vs = MDBCreateValueStruct(Agent.agent.directoryHandle, NULL);

        if (MsgFindObject(recipient, NULL, NULL, &saddr, vs)) {
            result = DeliverToStore(&list, &saddr, NMAP_DOCTYPE_MAIL, sender, authSender, client->path, data, sb.st_size, recipient, mailbox, 0);
            EndStoreDelivery(&list);
        }
        MDBDestroyValueStruct(vs);
        
    } else {
        return(ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1));
    }

    fclose(data);
    data = NULL;

    if (result==DELIVER_SUCCESS) {
        ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
    } else if (result==DELIVER_QUOTA_EXCEEDED) {
        ccode = ConnWrite(client->conn, MSG5220QUOTAERR, sizeof(MSG5220QUOTAERR) - 1);
    } else {
        ccode = ConnWrite(client->conn, MSG4120BOXLOCKED, sizeof(MSG4120BOXLOCKED) - 1);
    }

    return(ccode);
}

int 
CommandQaddq(void *param)
{
    unsigned long len;
    unsigned long start;
    unsigned long length;
    unsigned char *ptr;
    unsigned char *ptr2;
    unsigned char *ptr3;
    FILE *data;
    QueueClient *client = (QueueClient *)param;

    start = 0;
    length = 0;

    if (!client->entry.data) {
        return(ConnWrite(client->conn, MSG4260NOQENTRY, sizeof(MSG4260NOQENTRY) - 1));
    }

    if (Agent.flags & QUEUE_AGENT_DISK_SPACE_LOW) {
        return(ConnWrite(client->conn, MSG5221SPACELOW, sizeof(MSG5221SPACELOW) - 1));
    }

    client->entry.target = 0;

    ptr = client->buffer + 5;

    /* QADDQ <queue>-<id> <start> <length> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && ((ptr2 = strchr(ptr + 4, ' ')) != NULL) 
            && ((ptr3 = strchr(ptr2 + 1, ' ')) != NULL)) {
        ptr[3] = '\0';

        *ptr2++ = '\0';
        *ptr3++ = '\0';

        start = atol(ptr2);
        length = atol(ptr3);
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    /* fixme - LockQueueEntry?
    LockQueueEntry(ptr+4, atoi(ptr)); */

    sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4);
    data = fopen(client->path, "rb");
    if (data) {
        fseek(data, start, SEEK_SET);
    } else {
        /* fixme - UnlockQueueEntry?
        UnlockQueueEntry(ptr+4, atoi(ptr)); */
        return(ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1));
    }

    while (!feof(data) && !ferror(data) && (length != 0)) {
        if (length > CONN_BUFSIZE) {
            len = fread(client->line, sizeof(unsigned char), CONN_BUFSIZE, data);
        } else {
            len = fread(client->line, sizeof(unsigned char), length, data);
        }

        if (len) {
            fwrite(client->line, sizeof(unsigned char), len, client->entry.data);
            length -= len;
        }
    }

    fclose(data);

    /* fixme - UnlockQueueEntry
    UnlockQueueEntry(ptr+4, atoi(ptr)); */

    return(ConnWrite(client->conn, MSG1000ENTRYMADE, sizeof(MSG1000ENTRYMADE) - 1));
}

int 
CommandQabrt(void *param)
{
    int ccode;
    QueueClient *client = (QueueClient *)param;

    if (client->entry.control) {
        fclose(client->entry.control);
        client->entry.control = NULL;
        
        sprintf(client->path,"%s/c%07lx.in", Conf.spoolPath, client->entry.id);
        unlink(client->path);
    }
    
    if (client->entry.data) {
        fclose(client->entry.data);
        client->entry.data = NULL;
        
        sprintf(client->path,"%s/d%07lx.msg",Conf.spoolPath, client->entry.id);
        unlink(client->path);
    }
    if (client->entry.work) {
        fclose(client->entry.work);
        client->entry.work = NULL;
        
        if (client->entry.workQueue[0] != '\0') {
            sprintf(client->path,"%s/w%s.%s",Conf.spoolPath, client->entry.workQueue + 4, client->entry.workQueue);
            unlink(client->path);
        }
    }
    
    client->entry.id = 0;
    
    ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
    return(ccode);
}

int 
CommandQbody(void *param)
{
    int ccode;
    unsigned long count = 0;
    unsigned char *ptr;
    struct stat sb;
    FILE *data;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 5;

    /* QBODY <queue>-<id> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4])) {
        ptr[3] = '\0';
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    sprintf(client->path,"%s/d%s.msg", Conf.spoolPath, ptr + 4);
    if ((stat(client->path, &sb) == 0) && ((data = fopen(client->path, "rb")) != NULL)) {
        while (!feof(data) && !ferror(data)) {
            if (fgets(client->line, CONN_BUFSIZE, data) != NULL) {
                /* Note that for the QBODY command we count the blank line, unlike in QHEAD */
                count += strlen(client->line);
                if ((client->line[0] != '\r') || (client->line[1] != '\n')) {
                    continue;
                }

                break;
            }
        }

        if (((ccode = ConnWriteF(client->conn, "2023 %lu Message body follows\r\n", sb.st_size - count)) != -1) 
                && ((ccode = ConnWriteFromFile(client->conn, data, count)) != -1)) {
            ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
        }

        fclose(data);
    } else {
        ccode = ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1);
    }

    return(ccode);
}

int 
CommandQbraw(void *param)
{
    int ccode;
    unsigned long start;
    unsigned long size;
    unsigned long count = 0;
    unsigned char *ptr;
    unsigned char *ptr2;
    unsigned char *ptr3;
    struct stat sb;
    FILE *data;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 5;

    /* QBRAW <queue>-<id> <start> <length> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4])) 
            && ((ptr2 = strchr(ptr + 5, ' ')) != NULL) 
            && ((ptr3 = strchr(ptr2 + 1, ' ')) != NULL)) {
        ptr[3] = '\0';
        *ptr2++ = '\0';
        *ptr3++ = '\0';

        start = atol(ptr2);
        size = atol(ptr3);
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4);
    if ((stat(client->path, &sb) == 0) && ((data = fopen(client->path, "rb")) != NULL)) {
        while (!feof(data) && !ferror(data)) {
            if (fgets(client->line, CONN_BUFSIZE, data) != NULL) {
                count += strlen(client->line);
                if ((client->line[0] != '\r') || (client->line[1] != '\n')) {
                    continue;
                }

                break;
            }
        }
    } else {
        return(ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1));
    }

    if (start) {
        fseek(data, start, SEEK_CUR);
    }

    if (size > (sb.st_size - count - start)) {
        size = sb.st_size - count - start;
    }

    if (((ccode = ConnWriteF(client->conn, "2021 %lu Partial body follows\r\n", size)) != -1) 
            && ((ccode = ConnWriteFromFile(client->conn, data, size)) != -1)) {
        ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
    }

    fclose(data);

    return(ccode);
}

int 
CommandQcopy(void *param)
{
    unsigned long id;
    unsigned long len;
    unsigned long target;
    unsigned char *ptr;
    unsigned char *ptr2;
    FILE *source;
    QueueClient *client = (QueueClient *)param;

    if (client->entry.data || client->entry.control) {
        return(ConnWrite(client->conn, MSG4226QUEUEOPEN, sizeof(MSG4226QUEUEOPEN) - 1));
    }

    if (Agent.flags & QUEUE_AGENT_DISK_SPACE_LOW) {
        return(ConnWrite(client->conn, MSG5221SPACELOW, sizeof(MSG5221SPACELOW) - 1));
    }

    target = 0;
    ptr = client->buffer + 5;

    /* QCOPY <queue>-<id>[ <target>] */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4]))) {
        ptr[3] = '\0';
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    ptr2 = strchr(ptr + 5, ' ');
    if (ptr2) {
        *ptr2++ = '\0';

        if (*ptr2) {
            target = atol(ptr2);
        } else {
            return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
        }
    }

    if (target <= 999) {
        client->entry.target = target;
    } else {
        client->entry.target = 0;

        return(ConnWrite(client->conn, MSG4000OUTOFRANGE, sizeof(MSG4000OUTOFRANGE) - 1));
    }

    /* fixme - LockQueueEntry?
    LockQueueEntry(ptr+4, atoi(ptr)); */
    sprintf(client->path, "%s/d%s.msg",Conf.spoolPath, ptr + 4);
    source = fopen(client->path, "rb");
    if (source) {
        XplMutexLock(Queue.queueIDLock);
        id = Queue.queueID++ & ((1 << 28) - 1);
        XplMutexUnlock(Queue.queueIDLock);
    } else {
        /* fixme - UnlockQueueEntry?
        UnlockQueueEntry(ptr+4, atoi(ptr)); */

        return(ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1));
    }

    client->entry.id = id;

    sprintf(client->path, "%s/c%07lx.in", Conf.spoolPath, id);
    client->entry.control = fopen(client->path, "wb");
    if (client->entry.control) {
        fprintf(client->entry.control, QUEUES_DATE"%lu\r\n", time(NULL));
    } else {
        fclose(source);

        return(ConnWrite(client->conn, MSG5221SPACELOW, sizeof(MSG5221SPACELOW) - 1));
    }

    sprintf(client->path, "%s/d%07lx.msg", Conf.spoolPath, id);
    client->entry.data = fopen(client->path, "wb");

    while (!feof(source) && !ferror(source)) {
        len = fread(client->line, sizeof(unsigned char), CONN_BUFSIZE, source);
        if (len) {
            fwrite(client->line, sizeof(unsigned char), len, client->entry.data);
        }
    }

    fclose(source);

    /* fixme - UnlockQueueEntry?
    UnlockQueueEntry(ptr+4, atoi(ptr)); */

    return(ConnWrite(client->conn, MSG1000ENTRYMADE, sizeof(MSG1000ENTRYMADE) - 1));
}

int 
CommandQcrea(void *param)
{
    unsigned long id;
    unsigned long target;
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    if (Agent.flags & QUEUE_AGENT_DISK_SPACE_LOW) {
        return(ConnWrite(client->conn, MSG5221SPACELOW, sizeof(MSG5221SPACELOW) - 1));
    }

    ptr = client->buffer + 5;

    /* FIXME: disallow consequtive qcrea commands by checking client->entry.control */

    /* QCREA[ <target>] */
    if (*ptr == '\0') {
        target = 0;
    } else if ((*ptr++ == ' ') && (isdigit(*ptr))) {
        target = atol(ptr);
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    if (target <= 999) {
        client->entry.target = target;
    } else {
        client->entry.target = 0;

        return(ConnWrite(client->conn, MSG4000OUTOFRANGE, sizeof(MSG4000OUTOFRANGE) - 1));
    }

    XplMutexLock(Queue.queueIDLock);
    id = Queue.queueID++ & ((1 << 28) - 1);
    XplMutexUnlock(Queue.queueIDLock);

    sprintf(client->path, "%s/c%07lx.in", Conf.spoolPath, id);
    client->entry.control = fopen(client->path, "wb");
    if (client->entry.control) {
        fprintf(client->entry.control, QUEUES_DATE"%lu\r\n", time(NULL));
    } else {
        return(ConnWrite(client->conn, MSG5221SPACELOW, sizeof(MSG5221SPACELOW) - 1));
    }

    sprintf(client->path, "%s/d%07lx.msg", Conf.spoolPath, id);
    client->entry.data = fopen(client->path, "wb");
    if (client->entry.data) {
        client->entry.id = id;
    } else {
        fclose(client->entry.control);
        sprintf(client->path, "%s/c%07lx.in", Conf.spoolPath, id);
        unlink(client->path);

        return(ConnWrite(client->conn, MSG5221SPACELOW, sizeof(MSG5221SPACELOW) - 1));
    }

    /* We leave the handles open */
    return(ConnWrite(client->conn, MSG1000ENTRYMADE, sizeof(MSG1000ENTRYMADE) - 1));
}

int 
CommandQdele(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 5;

    /* QDELE <queue>-<id> */
    if ((*ptr++ == ' ')
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4]))) {
        ptr[3] = '\0';
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    /* fixme - LockQueueEntry?
    LockQueueEntry(ptr+4, atoi(ptr)); */

    sprintf(client->path, "%s/c%s.%s", Conf.spoolPath, ptr + 4, ptr);
    unlink(client->path);

    sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4);
    unlink(client->path);

    /* FIXME: close out the file handles? */

    /* fixme - UnlockQueueEntry?
    UnlockQueueEntry(ptr+4, atoi(ptr)); */

    return(ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1));
}

int 
CommandQdone(void *param)
{
    int ccode;
    unsigned char path[XPL_MAX_PATH + 1];
    struct stat sb;
    struct stat sb1;
    struct stat sb2;
    QueueClient *client = (QueueClient *)param;

    /* QDONE */
    
    if (client->entry.work) {
        fclose(client->entry.work);
        client->entry.work = NULL;
        if (client->entry.workQueue[0] != '\0') {
            sprintf(client->path, "%s/w%s.%s", Conf.spoolPath, client->entry.workQueue + 4, client->entry.workQueue);
            sprintf(path, "%s/c%s.%s", Conf.spoolPath, client->entry.workQueue + 4, client->entry.workQueue);
            if ((stat(client->path, &sb1) == 0) && (stat(path, &sb2) == 0)) {
                unlink(path);
                rename(client->path, path);
            } else {
                if (stat(client->path, &sb) == 0) {
                    /* Our new queue file exists, everything still ok */
                    rename(client->path, path);
                } else {
                    /* got to keep the old one */
                    unlink(client->path);
                }
            }
            
            client->entry.workQueue[0] = '\0';
        }
    }
    
    ccode = ConnWrite(client->conn, MSG1000RQWATCHMODE, sizeof(MSG1000RQWATCHMODE) - 1);

    client->done = TRUE;

    return(ccode);
}

int 
CommandQdspc(void *param)
{
    int ccode;
    unsigned long freeBlocks;
    int bytesPerBlock;
    QueueClient *client = (QueueClient *)param;

    /* QDSPC */

    bytesPerBlock = XplGetDiskBlocksize(Conf.spoolPath);
    freeBlocks = XplGetDiskspaceFree(Conf.spoolPath);

    if (freeBlocks > (unsigned long)(LONG_MAX / bytesPerBlock)) {
        ccode = ConnWriteF(client->conn, MSG1000SPACE_AVAIL, LONG_MAX);
    } else if ((freeBlocks * bytesPerBlock) < Conf.minimumFree) {
        ccode = ConnWriteF(client->conn, MSG1000SPACE_AVAIL, 0L);
    } else {
        ccode = ConnWriteF(client->conn, MSG1000SPACE_AVAIL, (freeBlocks * bytesPerBlock) - Conf.minimumFree);
    }

    return(ccode);      
}

int 
CommandQgrep(void *param)
{
    int ccode;
    int length;
    char *field;
    unsigned char *ptr;
    BOOL found = FALSE;
    FILE *data;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 5;

    /* QGREP <queue>-<id> <field> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4])) 
            && ((field = strchr(ptr + 5, ' ')) != NULL)) {
        ptr[3] = '\0';
        *field++ = '\0';
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4);
    data = fopen(client->path, "rb");
    if (data) {
        length = strlen(field);
    } else {
        return(ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1));
    }

    ccode = 0;
    while ((ccode != -1) && !feof(data) && !ferror(data)) {
        if (fgets(client->line, CONN_BUFSIZE, data) != NULL) { 
            if ((client->line[0] != '\r') || (client->line[1] != '\n')) {
                if (!found) {
                    if (XplStrNCaseCmp(client->line, field, length) == 0) {
                        ccode = ConnWriteF(client->conn, "2002-%s", client->line);

                        found = TRUE;
                    }
                } else if (isspace(client->line[0])) {
                    ccode = ConnWriteF(client->conn, "2002-%s", client->line);
                } else {
                    /* Found the field, and no additional header lines belong to it
                       Don't break yet as the search should continue for multiple lines 
                       with the same field */
                    found = FALSE;
                }

                continue;
            }

            break;
        }
    }

    fclose(data);

    if (ccode != -1) {
        ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
    }

    return(ccode);
}

int 
CommandQhead(void *param)
{
    int ccode;
    unsigned long count = 0;
    unsigned char *ptr;
    FILE *data;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 5;

    /* QHEAD <queue>-<id> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4]))) {
        ptr[3] = '\0';
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    sprintf(client->path,"%s/d%s.msg", Conf.spoolPath, ptr+4);
    data = fopen(client->path, "rb");
    if (data) {
        while (!feof(data) && !ferror(data)) {
            if (fgets(client->line, CONN_BUFSIZE, data) != NULL) {
                if ((client->line[0] != '\r') || (client->line[1] != '\n')) {
                    count += strlen(client->line);
                    continue;
                }

                break;
            }
        }
    } else {
        return(ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1));
    }

    fseek(data, 0, SEEK_SET);

    if (((ccode = ConnWriteF(client->conn, "2023 %lu Message header follows\r\n", count)) != -1) 
            && ((ccode = ConnWriteFromFile(client->conn, data, count)) != -1)) {
        ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
    }

    fclose(data);

    return(ccode);
}

int 
CommandQinfo(void *param)
{
    unsigned long count = 0;
    unsigned char *ptr;
    struct stat sb;
    FILE *data;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 5;

    /* QINFO <queue>-<id> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4]))) {
        ptr[3] = '\0';
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4);
    if ((stat(client->path, &sb) == 0) && ((data = fopen(client->path, "rb")) != NULL)) {
        while (!feof(data) && !ferror(data)) {
            if (fgets(client->line, CONN_BUFSIZE, data) != NULL) {
                if ((client->line[0] != '\r') || (client->line[1] != '\n')) {
                    count += strlen(client->line);
                    continue;
                }

                break;
            }
        }
    } else {
        return(ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1));
    }

    fclose(data);

    return(ConnWriteF(client->conn, "2001 %s-%s %lu %lu %lu 0 0 0 0\r\n",
        ptr, ptr + 4, /* ID */
        sb.st_size, /* Size */
        count, /* HeadSize */
        sb.st_size - count)); /* BodySize */
}

int
CommandQmodFrom(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 9;

    if (!client->entry.work) {
        sprintf(client->path, "%s/w%s.%s", Conf.spoolPath, client->entry.workQueue + 4, client->entry.workQueue);
        client->entry.work = fopen(client->path, "wb");
        if (!client->entry.work) {
            return(0);
        }
    }

    if ((*ptr++ == ' ') 
            && (*ptr) 
            && (!isspace(*ptr))) {
        fprintf(client->entry.work, QUEUES_FROM"%s\r\n", ptr);
    }

    return(0);
}

int
CommandQmodFlags(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 10;

    if (!client->entry.work) {
        sprintf(client->path, "%s/w%s.%s", Conf.spoolPath, client->entry.workQueue + 4, client->entry.workQueue);
        client->entry.work = fopen(client->path, "wb");
        if (!client->entry.work) {
            return(0);
        }
    }

    if ((*ptr++ == ' ') 
            && (*ptr) 
            && (!isspace(*ptr))) {
        fprintf(client->entry.work, QUEUES_FLAGS"%s\r\n", ptr);
    }

    return(0);
}

int
CommandQmodLocal(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 10;

    if (!client->entry.work) {
        sprintf(client->path, "%s/w%s.%s", Conf.spoolPath, client->entry.workQueue + 4, client->entry.workQueue);
        client->entry.work = fopen(client->path, "wb");
        if (!client->entry.work) {
            return(0);
        }
    }

    if ((*ptr++ == ' ') 
            && (*ptr) 
            && (!isspace(*ptr))) {
        fprintf(client->entry.work, QUEUES_RECIP_LOCAL"%s\r\n", ptr);
    }

    return(0);
}

int
CommandQmodMailbox(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 12;

    if (!client->entry.work) {
        sprintf(client->path, "%s/w%s.%s", Conf.spoolPath, client->entry.workQueue + 4, client->entry.workQueue);
        client->entry.work = fopen(client->path, "wb");
        if (!client->entry.work) {
            return(0);
        }
    }

    if ((*ptr++ == ' ') 
            && (*ptr) 
            && (!isspace(*ptr))) {
        fprintf(client->entry.work, QUEUES_RECIP_MBOX_LOCAL"%s\r\n", ptr);
    }

    return(0);
}

int
CommandQmodRaw(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 8;

    if (!client->entry.work) {
        sprintf(client->path, "%s/w%s.%s", Conf.spoolPath, client->entry.workQueue + 4, client->entry.workQueue);
        client->entry.work = fopen(client->path, "wb");
        if (!client->entry.work) {
            return(0);
        }
    }

    if ((*ptr++ == ' ') 
            && (*ptr) 
            && (!isspace(*ptr))) {
        fprintf(client->entry.work, "%s\r\n", ptr);
    }

    return(0);
}

int
CommandQmodTo(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 7;

    if (!client->entry.work) {
        sprintf(client->path, "%s/w%s.%s", Conf.spoolPath, client->entry.workQueue + 4, client->entry.workQueue);
        client->entry.work = fopen(client->path, "wb");
        if (!client->entry.work) {
            return(0);
        }
    }

    if ((*ptr++ == ' ') 
            && (*ptr) 
            && (!isspace(*ptr))) {
        fprintf(client->entry.work,QUEUES_RECIP_REMOTE"%s\r\n", ptr);
    }

    return(0);
}

int 
CommandQmime(void *param)
{
    int ccode;
    long size;
    unsigned long i;
    unsigned char *ptr;
    struct stat sb;
    FILE *data;
    MIMEReportStruct *report;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 5;

    /* QMIME <queue>-<id> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4]))) {
        ptr[3] = '\0';
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    if (!client->entry.report) {
        /* Find the message size */
        sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4);
        if ((stat(client->path, &sb) == 0) && ((data = fopen(client->path, "rb")) != NULL)) {
            /* Find the start of the body */
            while (!feof(data) && !ferror(data)) {
                if (fgets(client->line, CONN_BUFSIZE, data) != NULL) {
                    i = 0;
                    while (client->line[i] == '\r') {
                        i++;
                    }

                    if (client->line[i] != '\n') {
                        continue;
                    }

                    break;
                }
            }

            size = ftell(data);
            if (size > -1) {
                ;
            } else {
                size = 0;
            }

            fseek(data, 0, SEEK_SET);

            report = ParseMIME(data, size, sb.st_size, -1, client->line, CONN_BUFSIZE);
            if (report) {
                client->entry.report = report;

                ccode = SendMIME(client->conn, report);
            } else {
                /* fixme - Make a Parsing error message */                                        
                ccode = ConnWrite(client->conn, MSG5230NOMEMORYERR, sizeof(MSG5230NOMEMORYERR) - 1);
            }

            fclose(data);
        } else {
            ccode = ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1);
        }
    } else {
        ccode = SendMIME(client->conn, client->entry.report);
    }
    return(ccode);

}

int 
CommandQmove(void *param)
{
    int ccode;
    unsigned char *ptr;
    unsigned char *ptr2;
    unsigned char path[XPL_MAX_PATH + 1];
    struct stat sb;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 5;    

    /* QMOVE <queue>-<id> <queue> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4])) 
            && ((ptr2 = strchr(ptr + 5, ' ')) != NULL)) {
        ptr[3] = '\0';
        *ptr2++ = '\0';
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    sprintf(client->path, "%s/c%s.%s", Conf.spoolPath, ptr + 4, ptr);
    if (stat(client->path, &sb) == 0) {
        sprintf(path, "%s/c%s.%03d", Conf.spoolPath, ptr + 4, atoi(ptr));

        rename(client->path, path);

        ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
    } else {
        sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4);
        unlink(client->path);

        ccode = ConnWrite(client->conn, MSG4224NOENTRY, sizeof(MSG4224NOENTRY) - 1);
    }

    return(ccode);
}

int 
CommandQrcp(void *param)
{
    int ccode;
    unsigned long id;
    unsigned char *ptr;
    unsigned char path[XPL_MAX_PATH + 1];
    FILE *source;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 4;

    if (client->entry.control && client->entry.data && client->entry.id) {
        fclose(client->entry.data);
        client->entry.data = NULL;

        fclose(client->entry.control);
        client->entry.control = NULL;
    } else {
        return(ConnWrite(client->conn, MSG4000CANTUNLOCKENTRY, sizeof(MSG4000CANTUNLOCKENTRY) - 1));
    }

    /*
        Close the current queue entry, copy the data file, come out as if 
        a QCREA was just performed and then run the now old entry.

        QRCP */
    if (*ptr++ == '\0') {
        XplMutexLock(Queue.queueIDLock);
        id = Queue.queueID++ & ((1 << 28) - 1);
        XplMutexUnlock(Queue.queueIDLock);
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    sprintf(client->path, "%s/c%07lx.in", Conf.spoolPath, id);
    client->entry.control = fopen(client->path,"wb");
    if (client->entry.control) {
        fprintf(client->entry.control, QUEUES_DATE"%lu\r\n", time(NULL));

        sprintf(client->path, "%s/d%07lx.msg", Conf.spoolPath, id);
        client->entry.data = fopen(client->path, "wb");
        if (client->entry.data) {
            sprintf(client->path, "%s/d%07lx.msg", Conf.spoolPath, client->entry.id);
            source = fopen(client->path, "rb");

            while (!feof(source) && !ferror(source)) {
                ccode = fread(client->line, sizeof(unsigned char), CONN_BUFSIZE, source);
                if (ccode) {
                    fwrite(client->line, sizeof(unsigned char), ccode, client->entry.data);
                }
            }

            fclose(source);

            sprintf(client->path,"%s/c%07lx.in",Conf.spoolPath, client->entry.id);
            sprintf(path, "%s/c%07lx.%03ld", Conf.spoolPath, client->entry.id, client->entry.target);
            rename(client->path, path);

            sprintf(client->path, "%03ld%lx", client->entry.target, client->entry.id);

            if (XplSafeRead(Queue.activeWorkers) < Conf.maxConcurrentWorkers) {
                XplThreadID threadID;
                XplBeginCountedThread(&threadID, ProcessQueueEntry, STACKSPACE_Q, MemStrdup(client->path), ccode, Queue.activeWorkers);
            } else if (XplSafeRead(Queue.activeWorkers) < Conf.maxSequentialWorkers) {
                ConnFlush(client->conn);

                XplSafeIncrement(Queue.activeWorkers);

                ProcessQueueEntry(MemStrdup(client->path));
            } else {
                Queue.restartNeeded = TRUE;
            }

            client->entry.id = id;
            ccode = ConnWriteF(client->conn, "1000 %03ld-%lx OK\r\n", client->entry.target, client->entry.id);

            XplSafeIncrement(Queue.queuedLocal);

            return(ccode);
        }

        fclose(client->entry.control);
        client->entry.control = NULL;

        sprintf(client->path, "%s/c%07lx.in", Conf.spoolPath, id);
        unlink(client->path);
    }

    sprintf(client->path, "%s/c%07lx.in", Conf.spoolPath, client->entry.id);
    client->entry.control = fopen(path, "a+b");

    sprintf(client->path,"%s/d%07lx.msg", Conf.spoolPath, client->entry.id);
    client->entry.data = fopen(path, "a+b");

    if (client->entry.data && client->entry.control) {
        ccode = ConnWrite(client->conn, "1000 Didn't copy; keeping the old entry\r\n", 41);
    } else {
        if (client->entry.data) {
            fclose(client->entry.data);
            client->entry.data = NULL;
        }

        if (client->entry.control) {
            fclose(client->entry.control);
            client->entry.data = NULL;
        }

        ccode = ConnWrite(client->conn, MSG5221SPACELOW, sizeof(MSG5221SPACELOW) - 1);
    }

    return(ccode);
}

int 
CommandQretr(void *param)
{
    int ccode;
    unsigned char *ptr;
    unsigned char *ptr2;
    struct stat sb;
    FILE *data = NULL;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 5;

    /* QRETR <queue>-<id> <INFO | MESSAGE> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4])) 
            && ((ptr2 = strchr(ptr + 5, ' ')) != NULL) 
            && (ptr2[1]) 
            && (!isspace(ptr2[1]))) {
        ptr[3] = '\0';
        *ptr2++ = '\0';
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    if (XplStrCaseCmp(ptr2, "INFO") == 0) {
        sprintf(client->path, "%s/c07%s.%s", Conf.spoolPath, ptr + 4, ptr);
        if ((stat(client->path, &sb) == 0) 
                && ((data = fopen(client->path, "rb")) != NULL) 
                && sb.st_size) {
            ccode = ConnWriteF(client->conn, "2022 %lu Info follows\r\n", sb.st_size);
        } else {
            if (data) {
                fclose(data);
            }

            return(ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1));
        }
    } else if (XplStrCaseCmp(ptr2, "MESSAGE") == 0) {
        sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4);
        if ((stat(client->path, &sb) == 0) && ((data = fopen(client->path, "rb")) != NULL)) {
            ccode = ConnWriteF(client->conn, "2023 %lu Message follows\r\n", sb.st_size);
        } else {
            if (data) {
                fclose(data);
            }

            return(ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1));
        }
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    if ((ccode != -1) 
            && ((ccode = ConnWriteFromFile(client->conn, data, sb.st_size)) != -1)) {
        ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
    }

    fclose(data);

    return(ccode);
}

int 
CommandQrts(void *param)
{
    unsigned char *recipient;
    unsigned char *oRecipient;
    unsigned char *flags;
    QueueClient *client = (QueueClient *)param;


    /* fixme - the Queue Return To Sender command handler is being used inconsistently. */
    if (client->entry.workQueue[0] == '\0') {
        return(ConnWrite(client->conn, MSG3012BADQSTATE, sizeof(MSG3012BADQSTATE) - 1));
    }

    recipient = client->buffer + 4;

    if (!client->entry.work) {
        sprintf(client->path, "%s/w%s.%s", Conf.spoolPath, client->entry.workQueue + 4, client->entry.workQueue);
        client->entry.work = fopen(client->path, "wb");
        if (!client->entry.work) {
            return(0);
        }
    }

    /* QRETR <recipient> <original address> <routing envelope flags>[ <delivery state>][ <transcript>] */
    if ((*recipient++ == ' ') 
            && (!isspace(*recipient)) 
            && ((oRecipient = strchr(recipient, ' ')) != NULL) 
            && (oRecipient[1]) 
            && (!isspace(oRecipient[1])) 
            && ((flags = strchr(oRecipient + 1, ' ')) != NULL) 
            && (flags[1]) 
            && (!isspace(flags[1]))) {
        fprintf(client->entry.work, QUEUES_BOUNCE"%s\r\n", recipient);
    }

    return(0);
}

int 
CommandQrun(void *param)
{
    int ccode;
    unsigned long target;
    unsigned long id;
    unsigned char *ptr;
    unsigned char path[XPL_MAX_PATH + 1];
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 4;

    /* QRUN[ <queue>-<id>] */
    if (*ptr == '\0') {
        if (client->entry.control && client->entry.data && client->entry.id) {
            fclose(client->entry.data);
            client->entry.data = NULL;

            fclose(client->entry.control);
            client->entry.control = NULL;
        } else {
            return(ConnWrite(client->conn, MSG4000CANTUNLOCKENTRY, sizeof(MSG4000CANTUNLOCKENTRY) - 1));
        }

        sprintf(client->path,"%s/c%07lx.in",Conf.spoolPath, client->entry.id);
        sprintf(path, "%s/c%07lx.%03ld", Conf.spoolPath, client->entry.id, client->entry.target);
        rename(client->path, path);

        XplSafeIncrement(Queue.queuedLocal);

        ccode = ConnWriteF(client->conn, "1000 %03ld-%lx OK\r\n", client->entry.target, client->entry.id);

        sprintf(client->path, "%03ld%07lx", client->entry.target, client->entry.id);
        client->entry.id = 0;
    } else if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4]))) {
        ptr[3] = '\0';

        id = strtol(ptr + 4, NULL, 16);
        target = atoi(ptr);

        ccode = ConnWriteF(client->conn, "1000 %03ld-%lx OK\r\n", target, id);

        sprintf(client->path, "%03ld%07lx", target, id);
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    if (XplSafeRead(Queue.activeWorkers) < Conf.maxConcurrentWorkers) {
        XplThreadID threadID;
        XplBeginCountedThread(&threadID, ProcessQueueEntry, STACKSPACE_Q, MemStrdup(client->path), ccode, Queue.activeWorkers);
    } else if (XplSafeRead(Queue.activeWorkers) < Conf.maxSequentialWorkers) {
        ConnFlush(client->conn);

        XplSafeIncrement(Queue.activeWorkers);

        ProcessQueueEntry(MemStrdup(client->path));
    } else {
        Queue.restartNeeded = TRUE;
    }

    return(ccode);
}

int 
CommandQsrchDomain(void *param)
{
    int ccode;
    unsigned long used;
    unsigned char *ptr;
    void *handle = NULL;
    MDBValueStruct *vs = NULL;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 12;

    /* QSRCH DOMAIN <domain> */
    if ((*ptr++ == ' ') && (!isspace(*ptr))) {
        if (((vs = MDBCreateValueStruct(Agent.agent.directoryHandle, NULL)) != NULL) 
                && ((handle = QDBHandleAlloc()) != NULL)) {
            ccode = QDBSearchDomain(handle, ptr, vs);
            if (!ccode) {
                for (used = 0; (ccode != -1) && (used < vs->Used); used++) {
                    ccode = ConnWriteF(client->conn, "2001-007-%s\r\n", vs->Value[used]);
                }

                if (ccode != -1) {
                    ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
                }
            } else {
                LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_DATABASE, LOGGER_EVENT_DATABASE_FIND_ERROR, LOG_ERROR, 0, ptr, NULL, ccode, 0, NULL, 0);
                ccode = ConnWrite(client->conn, MSG4261NODOMAIN, sizeof(MSG4261NODOMAIN) - 1);
            }
        } else {
            LoggerEvent(Agent.agent.loggingHandle, LOGGER_SUBSYSTEM_GENERAL, LOGGER_EVENT_NMAP_OUT_OF_MEMORY, LOG_CRITICAL, 0, client->buffer, NULL, sizeof(MDBValueStruct), 0, NULL, 0);
            ccode = ConnWrite(client->conn, MSG5230NOMEMORYERR, sizeof(MSG5230NOMEMORYERR) - 1);
        }

        if (vs) {
            MDBDestroyValueStruct(vs);
        }

        if (handle) {
            QDBHandleRelease(handle);
        }
    } else {
        ccode = ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1);
    }

    return(ccode);
}

int 
CommandQsrchHeader(void *param)
{
    int ccode;
    int length;
    char *field;
    char *content;
    unsigned char *ptr;
    BOOL fieldFound=FALSE;
    BOOL contentFound=FALSE;
    FILE *data;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 12;

    /* QSRCH HEADER <queue>-<id> <header field> <header content> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4])) 
            && ((field = strchr(ptr + 5, ' ')) != NULL) 
            && (field[1]) 
            && (!isspace(field[1]))) {
        ptr[3] = '\0';
        *field++ = '\0';

        content = strchr(field, ' ');
        if (content) {
            *content++ = '\0';
        }

        length = strlen(field);
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }


    sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4);
    data = fopen(client->path, "rb");
    if (data) {
        while (!feof(data) && !ferror(data)) {
            if (fgets(client->line, CONN_BUFSIZE, data) != NULL) {
                if ((client->line[0] != '\r') || client->line[1] != '\n') {
                    if (!fieldFound) {
                        if (XplStrNCaseCmp(client->line, field, length) != 0) {
                            continue;
                        }

                        fieldFound = TRUE;
                        if (!content || PDBSearch(client->line + length, content)) {
                            contentFound = TRUE;
                            break;
                        }
                    } else if (isspace(client->line[0])) {
                        if (PDBSearch(client->line + length, content) == FALSE) {
                            continue;
                        }

                        contentFound=TRUE;
                        break;
                    }

                    /* fixme - compared with other NMAP searching commands 
                       this implementation stops after finding the first
                       matching field (regardless of any optional content
                       match.

                       Should this be changed? */
                }

                break;
            }
        }

        fclose(data);
    } else {
        return(ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1));
    }

    if (contentFound) {
        ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
    } else {
        ccode = ConnWrite(client->conn, MSG4262NOTFOUND, sizeof(MSG4262NOTFOUND) - 1);
    }

    return(ccode);
}

int 
CommandQsrchBody(void *param)
{
    int ccode;
    int length;
    char *content;
    unsigned char *ptr;
    BOOL found = FALSE;
    FILE *data;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 10;

    /* QSRCH BODY <queue>-<id> <search string> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4])) 
            && ((content = strchr(ptr + 5, ' ')) != NULL) 
            && (content[1])) {
        ptr[3] = '\0';
        *content++ = '\0';

        length = strlen(content);
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4);
    data = fopen(client->path, "rb");
    if (data) {
        while (!feof(data) && !ferror(data)) {
            if (fgets(client->line, CONN_BUFSIZE, data) != NULL) {
                if ((client->line[0] != '\r') || (client->line[1] != '\n')) {
                    continue;
                }

                break;
            }
        }

        /* fixme - this doesn't search across buffer bondaries! */
        while (!feof(data) && !ferror(data)) {
            if (fread(client->line, sizeof(unsigned char), CONN_BUFSIZE, data) > 0) {
                if (PDBSearch(client->line, content) == FALSE) {
                    continue;
                }
                found = TRUE;
                break;
            }
        }

        fclose(data);
    } else {
        return(ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1));
    }


    if (found) {
        ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
    } else {
        ccode = ConnWrite(client->conn, MSG4262NOTFOUND, sizeof(MSG4262NOTFOUND) - 1);
    }

    return(ccode);
}

int 
CommandQsrchBraw(void *param)
{
    int ccode;
    int read;
    int length;
    unsigned long start;
    unsigned long end;
    unsigned char *ptr;
    unsigned char *startPtr;
    unsigned char *endPtr;
    unsigned char *content;
    BOOL found = FALSE;
    FILE *data;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 10;

    /* QSRCH BRAW <queue>-<id> <start> <end> <search string> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr)) 
            && (ptr[0]) 
            && (ptr[1]) 
            && (ptr[2]) 
            && (ptr[3] == '-') 
            && (ptr[4]) 
            && (!isspace(ptr[4])) 
            && ((startPtr = strchr(ptr + 5, ' ')) != NULL) 
            && (startPtr[1]) 
            && (isdigit(startPtr[1])) 
            && ((endPtr = strchr(startPtr + 1, ' ')) != NULL) 
            && (endPtr[1]) 
            && (isdigit(endPtr[1])) 
            && ((content = strchr(endPtr + 1, ' ')) != NULL)) {
        ptr[3] = '\0';
        *startPtr++ = '\0';
        *endPtr++ = '\0';
        *content++ = '\0';

        start = atol(startPtr);
        end = atol(endPtr);

        length = strlen(content);
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4);
    data = fopen(client->path, "rb");
    if (data) {
        while (!feof(data) && !ferror(data)) {
            if (fgets(client->line, CONN_BUFSIZE, data) != NULL) {
                if ((client->line[0] != '\r') || (client->line[1] != '\n')) {
                    continue;
                }

                break;
            }
        }

        /* Seek to the start position */
        fseek(data, start, SEEK_CUR);

        /* fixme - this doesn't search across buffer bondaries! */
        while ((end > 0) && !feof(data) && !ferror(data)) {
            if (end >= CONN_BUFSIZE) {
                read = fread(client->line, sizeof(unsigned char), CONN_BUFSIZE, data);
            } else {
                read = fread(client->line, sizeof(unsigned char), end, data);
            }

            if (read) {
                end -= read;

                if (PDBSearch(client->line, content) == FALSE) {
                    continue;
                }

                found = TRUE;
                break;
            }

        }

        fclose(data);
    } else {
        return(ConnWrite(client->conn, MSG4224CANTREAD, sizeof(MSG4224CANTREAD) - 1));
    }

    if (found) {
        ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
    } else {
        ccode = ConnWrite(client->conn, MSG4262NOTFOUND, sizeof(MSG4262NOTFOUND) - 1);
    }

    return(ccode);
}

int 
CommandQstorAddress(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    if (!client->entry.data || !client->entry.control) {
        return(ConnWrite(client->conn, MSG4260NOQENTRY, sizeof(MSG4260NOQENTRY) - 1));
    }

    ptr = client->buffer + 13;

    /* QSTOR ADDRESS <value> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr))) {
        fprintf(client->entry.control, QUEUES_ADDRESS"%s\r\n", ptr);

        return(ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1));
    }

    return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
}

int 
CommandQstorCal(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    if (!client->entry.data || !client->entry.control) {
        return(ConnWrite(client->conn, MSG4260NOQENTRY, sizeof(MSG4260NOQENTRY) - 1));
    }

    ptr = client->buffer + 9;

    /* QSTOR CAL <recipient>[ <calendar name>][ <routing envelope flags>] */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr))) {
        fprintf(client->entry.control, QUEUES_CALENDAR_LOCAL"%s\r\n", ptr);

        return(ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1));
    }

    return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
}

int 
CommandQstorFlags(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    if (!client->entry.data || !client->entry.control) {
        return(ConnWrite(client->conn, MSG4260NOQENTRY, sizeof(MSG4260NOQENTRY) - 1));
    }

    ptr = client->buffer + 11;

    /* QSTOR FLAGS <value> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr))) {
        fprintf(client->entry.control, QUEUES_FLAGS"%s\r\n", ptr);

        return(ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1));
    }

    return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
}

int 
CommandQstorFrom(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    if (!client->entry.data || !client->entry.control) {
        return(ConnWrite(client->conn, MSG4260NOQENTRY, sizeof(MSG4260NOQENTRY) - 1));
    }

    ptr = client->buffer + 10;
    /* QSTOR FROM <sender> <authenticated sender | -> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr))) {
        fprintf(client->entry.control, QUEUES_FROM"%s\r\n", ptr);

        return(ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1));
    }

    return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
}

int 
CommandQstorLocal(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    if (!client->entry.data || !client->entry.control) {
        return(ConnWrite(client->conn, MSG4260NOQENTRY, sizeof(MSG4260NOQENTRY) - 1));
    }

   ptr = client->buffer + 11;

    /* QSTOR LOCAL <recipient> <original recipient> <routing envelope flags> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr))) {
        fprintf(client->entry.control, QUEUES_RECIP_LOCAL"%s\r\n", ptr);

        return(ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1));
    }

    return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
}

int 
CommandQstorMessage(void *param)
{
    int ccode;
    long count;
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;
    unsigned char TimeBuf[80];

    if (!client->entry.data || !client->entry.control) {
        return(ConnWrite(client->conn, MSG4260NOQENTRY, sizeof(MSG4260NOQENTRY) - 1));
    }

    ptr = client->buffer + 13;

    /* QSTOR MESSAGE[ <size>] */
    if (*ptr == '\0') {
        count = 0;
    } else if ((*ptr++ == ' ') && (isdigit(*ptr))) {
        count = atol(ptr);
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    MsgGetRFC822Date(-1, 0, TimeBuf);
    fprintf(client->entry.data,
            "Received: from %d.%d.%d.%d [%d.%d.%d.%d] by %s\r\n\twith NMAP (%s); %s\r\n",
            client->conn->socketAddress.sin_addr.s_net,
            client->conn->socketAddress.sin_addr.s_host,
            client->conn->socketAddress.sin_addr.s_lh,
            client->conn->socketAddress.sin_addr.s_impno,

            client->conn->socketAddress.sin_addr.s_net,
            client->conn->socketAddress.sin_addr.s_host,
            client->conn->socketAddress.sin_addr.s_lh,
            client->conn->socketAddress.sin_addr.s_impno,

            Agent.agent.officialName,
            AGENT_NAME,
            TimeBuf);

    if (count) {
        ccode = ConnReadToFile(client->conn, client->entry.data, count);
    } else {
        ccode = ConnReadToFileUntilEOS(client->conn, client->entry.data);
    }

    if (ccode != -1) {
        ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1);
    }

    return(ccode);
}

int 
CommandQstorRaw(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    if (!client->entry.data || !client->entry.control) {
        return(ConnWrite(client->conn, MSG4260NOQENTRY, sizeof(MSG4260NOQENTRY) - 1));
    }

    ptr = client->buffer + 9;

    /* QSTOR RAW <value> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr))) {
        fprintf(client->entry.control,"%s\r\n", ptr);

        return(ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1));
    }

    return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
}

int 
CommandQstorTo(void *param)
{
    unsigned char *ptr;
    QueueClient *client = (QueueClient *)param;

    if (!client->entry.data || !client->entry.control) {
        return(ConnWrite(client->conn, MSG4260NOQENTRY, sizeof(MSG4260NOQENTRY) - 1));
    }

    ptr = client->buffer + 8;

    /* QSTOR TO <value> */
    if ((*ptr++ == ' ') 
            && (!isspace(*ptr))) {
        fprintf(client->entry.control,QUEUES_RECIP_REMOTE"%s\r\n", ptr);

        return(ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1));
    }

    return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
}

int 
CommandQwait(void *param)
{
    int ccode;
    int port;
    int queue;
    int i;
    unsigned char *ptr;
    unsigned char *identifier;
    QueueClient *client = (QueueClient *)param;

    ptr = client->buffer + 5;
    
    /* QWAIT <queue> <port> <identifier> */
    if ((*ptr++ == ' ') 
            && ((ptr = strchr(ptr, ' ')) != NULL) 
            && (ptr[1]) 
            && (isdigit(ptr[1])) 
            && ((identifier = strchr(ptr + 1, ' ')) != NULL) 
            && (identifier[1]) 
            && (!isspace(identifier[1])) 
            && (strlen(identifier + 1) < MDB_MAX_OBJECT_CHARS)) {
        *ptr++ = '\0';
        *identifier++ = '\0';

        queue = atol(client->buffer + 6);
        port = atol(ptr);
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    if (port) {
        XplSafeIncrement(Queue.numServicedAgents);
        ccode = ConnWrite(client->conn, MSG1000QWATCHMODE, sizeof(MSG1000QWATCHMODE) - 1);
    } else {
        return(ConnWrite(client->conn, MSG3010BADARGC, sizeof(MSG3010BADARGC) - 1));
    }

    if (ccode != -1) {
        for (i = 0; i < Queue.pushClients.count; i++) {
            if (strcmp(Queue.pushClients.array[i].identifier, identifier) == 0) {
                if (RemovePushAgentIndex(i, TRUE)) {
                    i--;
                }
            }
        }

        if ((MsgGetHostIPAddress() != MsgGetAgentBindIPAddress())
            && (!client->conn->socketAddress.sin_addr.s_addr || (client->conn->socketAddress.sin_addr.s_addr == inet_addr("127.0.0.1")) || (client->conn->socketAddress.sin_addr.s_addr == MsgGetHostIPAddress()))) {
            AddPushAgent(client, inet_addr("127.0.0.1"), htons(port), queue, identifier);
        } else {
            AddPushAgent(client, client->conn->socketAddress.sin_addr.s_addr, htons(port), queue, identifier);
        }

        client->done = TRUE;
    }

    return(ccode);
}

int
CommandQflush(void *param)
{
    QueueClient *client = (QueueClient *)param;

    Queue.flushNeeded = TRUE;

    return (ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1));
}
