/**************************************************************************** * * 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. * ****************************************************************************/ #include #include "queue.h" #include "mime.h" #include "messages.h" #include "domain.h" #include #include #include #include #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 #define FOPEN_CHECK(handle, path, mode) fopen_check(&(handle), (path), (mode), __LINE__) FILE * fopen_check(FILE **handle, char *path, char *mode, int line) { LogAssertF(*handle == NULL, "File handle already open on line %d", line); *handle = fopen(path, mode); return *handle; } #define FCLOSE_CHECK(f) fclose_check(&(f), __LINE__) int fclose_check(FILE **fh, int line) { int ret; ret = fclose(*fh); if (ret == 0) { *fh = NULL; } else { LogFailureF("File close failed on line %d: %d", line, errno); } return ret; } #define UNLINK_CHECK(path) unlink_check((path), __LINE__) int unlink_check(char *path, int line) { int ret; ret = unlink(path); LogAssertF(ret == 0, "Unable to delete file %s on line %d: %d", path, line, errno); return ret; } #define RENAME_CHECK(oldpath, newpath) rename_check((oldpath), (newpath), __LINE__) int rename_check(const char *oldpath, const char *newpath, int line) { int ret; ret = rename(oldpath, newpath); LogAssertF(ret == 0, "Unable to rename %s to %s on line %d: %d", oldpath, newpath, line, errno); return ret; } 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]); Log(LOG_INFO, "Unable to lock spool entry %x, table full", (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; Log(LOG_INFO, "Adding client on host %s:%d to queue %d", LOGIP(client->conn->socketAddress), port, queue); 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 { LogFailureF("Out of memory processing mailbox %s", identifier); 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) { Log(LOG_INFO, "Reregistered queue agent"); } else { Log(LOG_INFO, "Removed queue agent"); } 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_CHECK(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 = NULL; FILE *data = NULL; FILE *newFH = NULL; MIMEReportStruct *report = NULL; // REMOVE-MDB 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); Log(LOG_DEBUG, "Processing entry %ld on queue %d", entryID, queue); idLock = SpoolEntryIDLock(entryID); if (idLock) { sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue); FOPEN_CHECK(fh, path, "r+b"); } else { ProcessQueueEntryCleanUp(NULL, report); return(FALSE); } if (fh) { fgets(line, CONN_BUFSIZE, fh); date = atoi(line + 1); FCLOSE_CHECK(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: { FILE *temp = NULL; sb.st_size = -1; qDate = NULL; qFlags = NULL; qID = NULL; qAddress = NULL; qFrom = NULL; sprintf(path, "%s/w%s.%03d", Conf.spoolPath, entry, queue); FOPEN_CHECK(newFH, 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) && ((temp = fopen(path, "rb")) != NULL) && (fread(qEnvelope, sizeof(unsigned char), sb.st_size, temp) == (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(temp); 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_CHECK(newFH); UNLINK_CHECK(path); sprintf(path, "%s/w%s.%03d",Conf.spoolPath, entry, queue); UNLINK_CHECK(path); sprintf(path, "%s/d%s.msg",Conf.spoolPath, entry); UNLINK_CHECK(path); MemFree(qEnvelope); if ((handle = QDBHandleAlloc()) != NULL) { QDBRemoveID(handle, entryID); QDBHandleRelease(handle); } XplSafeDecrement(Queue.queuedLocal); ProcessQueueEntryCleanUp(idLock, report); return(TRUE); } // REMOVE-MDB 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'; } // REMOVE-MDB #if 0 /* this adds lines to the envelope for each user -- expanding a group if needed */ 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); /* end of envelope rewrite section */ } else { #endif Log(LOG_INFO, "Entry %ld queue %d, can't find %s", entryID, queue, cur + 1); if (ptr2) { *ptr2 = ' '; } if (ptr) { *ptr = '\n'; } fwrite(cur, sizeof(unsigned char), next - cur, newFH); // REMOVE-MDB } 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; } // REMOVE-MDB MDBDestroyValueStruct(vs); MemFree(qEnvelope); FCLOSE_CHECK(newFH); UNLINK_CHECK(path); sprintf(path2, "%s/w%s.%03d", Conf.spoolPath, entry, queue); RENAME_CHECK(path2, path); break; } if (!newFH) { sprintf(path, "%s/w%s.%03d",Conf.spoolPath, entry, queue); LogFailureF("File open failure. Entry %ld, path %s", entryID, path); } else if (!qEnvelope) { LogFailureF("Out of memory. Entry %ld, size %ld", entryID, sb.st_size); } else if (!fh) { LogFailureF("File open failure. Entry %ld, path %s", entryID, path); } else { LogFailureF("Event file open failure. Entry %ld, path %s", entryID, path); } if (qEnvelope) { MemFree(qEnvelope); } if (fh) { FCLOSE_CHECK(fh); } if (newFH) { FCLOSE_CHECK(newFH); } Log(LOG_WARNING, "Write error in queue"); sprintf(path, "%s/w%s.%03d",Conf.spoolPath, entry, queue); UNLINK_CHECK(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_CHECK(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); FOPEN_CHECK(fh, path, "rb"); sprintf(path, "%s/w%s.%03d", Conf.spoolPath, entry, queue); FOPEN_CHECK(newFH, path, "wb"); if (!fh || !newFH) { if (fh) { FCLOSE_CHECK(fh); } if (newFH) { FCLOSE_CHECK(newFH); } sprintf(path, "%s/w%s.%03d",Conf.spoolPath, entry, queue); UNLINK_CHECK(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'; } fprintf(newFH, "%s\r\n", line); break; } case QUEUE_CALENDAR_LOCAL: { struct sockaddr_in siaddr; if (!data) { FOPEN_CHECK(data, path, "rb"); if (!data) { FCLOSE_CHECK(fh); sprintf(path, "%s/w%s.%03d",Conf.spoolPath, entry, queue); FCLOSE_CHECK(newFH); UNLINK_CHECK(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 = ' '; } // REMOVE-MDB #if 0 vs = MDBCreateValueStruct(Agent.agent.directoryHandle, NULL); if (!MsgFindObject(recipient, NULL, NULL, &siaddr, vs)) { Log(LOG_WARNING, "User %s unknown, entry %ld", recipient, entryID); status = DELIVER_USER_UNKNOWN; } else { #endif Log(LOG_DEBUG, "Delivering %s on queue %d to %s", entry, queue, line+1); 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; } // REMOVE-MDB } // MDBDestroyValueStruct(vs); /* Restore our buffer */ if (ptr2) { *ptr2 = ' '; } if (status 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); FOPEN_CHECK(data, path, "rb"); if (!data) { FCLOSE_CHECK(fh); FCLOSE_CHECK(newFH); sprintf(path, "%s/w%s.%03d", Conf.spoolPath, entry, queue); UNLINK_CHECK(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 */ // REMOVE-MDB #if 0 vs = MDBCreateValueStruct(Agent.agent.directoryHandle, NULL); if (MsgFindObject(recipient, NULL, NULL, &siaddr, vs)) { Log(LOG_DEBUG, "Deliver to store entry %s in queue %d for host %s", entry, queue, LOGIP(siaddr)); 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 { #endif 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 { Log(LOG_INFO, "Can't forward undeliverable entry %ld in queue %d for user %s on host %s", entry, queue, sender, LOGIP(saddr)); XplRWReadLockRelease(&Conf.lock); } // REMOVE-MDB } // REMOVE-MDB MDBDestroyValueStruct(vs); if (status < DELIVER_SUCCESS) { Log(LOG_WARNING, "Couldn't deliver entry %s on queue %d, status %d", entry, queue, status); 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: Log(LOG_INFO, "Unknown command: %s (entry: %ld)", line, entryID); fprintf(newFH, "%s\r\n", line); break; } } } if (list.allocated) { EndStoreDelivery(&list); memset(&list, 0, sizeof(NMAPConnections)); } FCLOSE_CHECK(newFH); FCLOSE_CHECK(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_CHECK(path); sprintf(Path2, "%s/w%s.%03d",Conf.spoolPath, entry, queue); RENAME_CHECK(Path2, path); if (!data) { sprintf(path, "%s/d%s.msg",Conf.spoolPath, entry); FOPEN_CHECK(data, path, "rb"); } else { fseek(data, 0, SEEK_SET); } sprintf(path, "%s/c%s.%03d",Conf.spoolPath, entry, queue); FOPEN_CHECK(fh, 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); FOPEN_CHECK(newFH, 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_CHECK(newFH); } FCLOSE_CHECK(fh); } else { if (fh) { FCLOSE_CHECK(fh); } if (data) { FCLOSE_CHECK(data); } Log(LOG_CRITICAL, "File open error for entry %ld, path: %s", entryID, path); ProcessQueueEntryCleanUp(idLock, report); return(FALSE); } } if (data) { FCLOSE_CHECK(data); } if (keep) { unsigned char Path2[XPL_MAX_PATH+1]; sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue); UNLINK_CHECK(path); sprintf(Path2, "%s/w%s.%03d", Conf.spoolPath, entry, queue); RENAME_CHECK(Path2, path); } else { sprintf(path, "%s/w%s.%03d", Conf.spoolPath, entry, queue); UNLINK_CHECK(path); sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue); UNLINK_CHECK(path); sprintf(path, "%s/d%s.msg", Conf.spoolPath, entry); UNLINK_CHECK(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) { FOPEN_CHECK(fh, path, "rb"); if (fh) { /* 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); } LogFailureF("Cannot allocate %d bytes memory (entry %ld)", sizeof(QueueClient), entryID); if (fh) { FCLOSE_CHECK(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_CHECK(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 { LogFailureF("Couldn't connect client %s", LOGIP(saddr)); ConnClose(client->conn, 0); ConnFree(client->conn); client->conn = NULL; if (RemovePushAgentIndex(used, FALSE)) { used--; } QueueClientFree(client); if (fh) { FCLOSE_CHECK(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_CHECK(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)) { LogFailureF("Couldn't handle command on entry %s for host %s", entry, LOGIP(saddr)); } /* 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_CHECK(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); } FOPEN_CHECK(fh, 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); LogAssertF(ccode != -1, "Database insert error: %s", ptr2 + 1); QDBHandleRelease(handle); } } if (ptr) { *ptr = ' '; } keep = TRUE; break; } } } } while (!feof(fh) && !ferror(fh)); FCLOSE_CHECK(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); FOPEN_CHECK(data, path2, "rb"); FOPEN_CHECK(fh, path, "rb"); if (fh && data && 0 == HandleDSN(data, fh)) { FCLOSE_CHECK(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); FOPEN_CHECK(newFH, 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_CHECK(fh); FCLOSE_CHECK(newFH); UNLINK_CHECK(path); RENAME_CHECK(path2, path); } else { FCLOSE_CHECK(fh); } } else { FCLOSE_CHECK(fh); } } else { if (fh) { FCLOSE_CHECK(fh); } if (data) { FCLOSE_CHECK(data); } LogFailureF("File open failure: entry %ld, path %s", entryID, path); ProcessQueueEntryCleanUp(idLock, report); return(TRUE); } } if (!keep) { XplSafeDecrement(Queue.queuedLocal); UNLINK_CHECK(path); sprintf(path, "%s/d%s.msg",Conf.spoolPath, entry); UNLINK_CHECK(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); FOPEN_CHECK(newFH, path, "wb"); sprintf(path, "%s/d%s.msg", Conf.spoolPath, entry); FOPEN_CHECK(data, path, "rb"); sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue); FOPEN_CHECK(fh, 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_CHECK(newFH); FCLOSE_CHECK(fh); /* Still got path from above */ UNLINK_CHECK(path); sprintf(path2, "%s/w%s.%03d", Conf.spoolPath, entry, queue); RENAME_CHECK(path2, path); FOPEN_CHECK(fh, path, "rb"); if (fh) { HandleDSN(data, fh); FCLOSE_CHECK(fh); } else { LogFailureF("Couldn't open path %s (%ld)", path, entryID); } FCLOSE_CHECK(data); sprintf(path, "%s/c%s.%03d", Conf.spoolPath, entry, queue); UNLINK_CHECK(path); sprintf(path, "%s/d%s.msg", Conf.spoolPath, entry); UNLINK_CHECK(path); XplSafeDecrement(Queue.queuedLocal); } else { if (data) { FCLOSE_CHECK(data); } if (fh) { FCLOSE_CHECK(fh); } if (newFH) { FCLOSE_CHECK(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_CHECK(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 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; // REMOVE-MDB 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 */ // REMOVE-MDB #if 0 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); } } #endif /* 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\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_CHECK(path); return -1; } if (!CreateDSNMessage(data, control, rtsData, rtsControl, FALSE)) { fclose(rtsControl); fclose(rtsData); sprintf(path, "%s/d%07lx.msg", Conf.spoolPath, id); UNLINK_CHECK(path); sprintf(path, "%s/c%07lx.%03d", Conf.spoolPath, id, Q_INCOMING); UNLINK_CHECK(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_CHECK(path); } } fclose(killFile); } sprintf(path, "%s/killfile", MsgGetDBFDir(NULL)); UNLINK_CHECK(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 - */ 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; // REMOVE-MDB #if 0 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); #endif } 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 - */ 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_CHECK(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_CHECK(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_CHECK(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 - */ 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 - */ 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 -[ ] */ 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[ ] */ 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_CHECK(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 - */ 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_CHECK(client->path); sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4); UNLINK_CHECK(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_CHECK(path); RENAME_CHECK(client->path, path); } else { if (stat(client->path, &sb) == 0) { /* Our new queue file exists, everything still ok */ RENAME_CHECK(client->path, path); } else { /* got to keep the old one */ UNLINK_CHECK(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 - */ 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 - */ 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 - */ 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 - */ 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 - */ 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_CHECK(client->path, path); ccode = ConnWrite(client->conn, MSG1000OK, sizeof(MSG1000OK) - 1); } else { sprintf(client->path, "%s/d%s.msg", Conf.spoolPath, ptr + 4); UNLINK_CHECK(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_CHECK(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_CHECK(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 - */ 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 [ ][ ] */ 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[ -] */ 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_CHECK(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; QueueClient *client = (QueueClient *)param; ptr = client->buffer + 12; /* QSRCH DOMAIN */ if ((*ptr++ == ' ') && (!isspace(*ptr))) { if ((handle = QDBHandleAlloc()) != NULL) { ccode = QDBSearchDomain(handle, ptr); if (!ccode) { // REMOVE-MDB 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 { LogFailureF("Couldn't find %s in QDB", ptr); ccode = ConnWrite(client->conn, MSG4261NODOMAIN, sizeof(MSG4261NODOMAIN) - 1); } } else { LogFailure("Out of memory"); ccode = ConnWrite(client->conn, MSG5230NOMEMORYERR, sizeof(MSG5230NOMEMORYERR) - 1); } 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 -
*/ 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 - */ 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 - */ 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 */ 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 [ ][ ] */ 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 */ 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 */ 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 */ 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[ ] */ 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 */ 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 */ 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 */ 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)); }