Generate internationalized delivery status reports
Debian Trixie package bundle / packages (push) Successful in 15m22s
Debian Trixie package bundle / packages (push) Successful in 15m22s
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
add_executable(bongoqueue
|
||||
conf.c
|
||||
dsn-format.c
|
||||
domain.c
|
||||
queued.c
|
||||
mime.c
|
||||
@@ -15,3 +16,14 @@ target_link_libraries(bongoqueue
|
||||
)
|
||||
|
||||
install(TARGETS bongoqueue DESTINATION ${SBIN_INSTALL_DIR})
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_executable(queue-dsn-format-test
|
||||
tests/dsn-format-test.c
|
||||
dsn-format.c
|
||||
)
|
||||
target_include_directories(queue-dsn-format-test PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
add_test(NAME queue-dsn-format COMMAND queue-dsn-format-test)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/****************************************************************************
|
||||
* <Novell-copyright>
|
||||
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc.
|
||||
*
|
||||
* To contact Novell about this file by physical or electronic mail, you
|
||||
* may find current contact information at www.novell.com.
|
||||
* </Novell-copyright>
|
||||
****************************************************************************/
|
||||
|
||||
#include "dsn-format.h"
|
||||
|
||||
#include <nmap.h>
|
||||
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
|
||||
static int
|
||||
HexValue(unsigned char value)
|
||||
{
|
||||
if (value >= '0' && value <= '9') return value - '0';
|
||||
if (value >= 'A' && value <= 'F') return value - 'A' + 10;
|
||||
if (value >= 'a' && value <= 'f') return value - 'a' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
AppendUtf8(char *result, size_t result_size, size_t *used,
|
||||
unsigned int codepoint)
|
||||
{
|
||||
unsigned char encoded[4];
|
||||
size_t length;
|
||||
|
||||
if (codepoint <= 0x7fU) {
|
||||
encoded[0] = (unsigned char)codepoint;
|
||||
length = 1U;
|
||||
} else if (codepoint <= 0x7ffU) {
|
||||
encoded[0] = (unsigned char)(0xc0U | (codepoint >> 6U));
|
||||
encoded[1] = (unsigned char)(0x80U | (codepoint & 0x3fU));
|
||||
length = 2U;
|
||||
} else if (codepoint <= 0xffffU) {
|
||||
if (codepoint >= 0xd800U && codepoint <= 0xdfffU) return 0;
|
||||
encoded[0] = (unsigned char)(0xe0U | (codepoint >> 12U));
|
||||
encoded[1] = (unsigned char)(0x80U | ((codepoint >> 6U) & 0x3fU));
|
||||
encoded[2] = (unsigned char)(0x80U | (codepoint & 0x3fU));
|
||||
length = 3U;
|
||||
} else if (codepoint <= 0x10ffffU) {
|
||||
encoded[0] = (unsigned char)(0xf0U | (codepoint >> 18U));
|
||||
encoded[1] = (unsigned char)(0x80U | ((codepoint >> 12U) & 0x3fU));
|
||||
encoded[2] = (unsigned char)(0x80U | ((codepoint >> 6U) & 0x3fU));
|
||||
encoded[3] = (unsigned char)(0x80U | (codepoint & 0x3fU));
|
||||
length = 4U;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
if (length >= result_size - *used) return 0;
|
||||
memcpy(result + *used, encoded, length);
|
||||
*used += length;
|
||||
result[*used] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
DecodeUnitext(const char *address, char *result, size_t result_size)
|
||||
{
|
||||
const unsigned char *cursor = (const unsigned char *)address;
|
||||
size_t used = 0U;
|
||||
|
||||
while (*cursor != '\0') {
|
||||
if (cursor[0] == '\\' && cursor[1] == 'x' && cursor[2] == '{') {
|
||||
const unsigned char *digits = cursor + 3;
|
||||
unsigned int codepoint = 0U;
|
||||
size_t count = 0U;
|
||||
int digit;
|
||||
|
||||
while (count < 6U && (digit = HexValue(digits[count])) >= 0) {
|
||||
codepoint = (codepoint << 4U) | (unsigned int)digit;
|
||||
count++;
|
||||
}
|
||||
if (count < 2U || digits[count] != '}' || codepoint == 0U ||
|
||||
!AppendUtf8(result, result_size, &used, codepoint)) return 0;
|
||||
cursor = digits + count + 1U;
|
||||
} else {
|
||||
if (*cursor == '\\') return 0;
|
||||
if (used + 1U >= result_size) return 0;
|
||||
result[used++] = (char)*cursor++;
|
||||
result[used] = '\0';
|
||||
}
|
||||
}
|
||||
return used != 0U;
|
||||
}
|
||||
|
||||
int
|
||||
BongoDsnWriteBase64(FILE *input, FILE *output)
|
||||
{
|
||||
static const char alphabet[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
unsigned char input_buffer[57];
|
||||
char output_buffer[78];
|
||||
size_t count;
|
||||
|
||||
if (input == NULL || output == NULL) return 0;
|
||||
while ((count = fread(input_buffer, 1U, sizeof(input_buffer), input)) > 0U) {
|
||||
size_t input_offset = 0U;
|
||||
size_t output_offset = 0U;
|
||||
|
||||
while (input_offset < count) {
|
||||
size_t remaining = count - input_offset;
|
||||
unsigned int a = input_buffer[input_offset++];
|
||||
unsigned int b = remaining > 1U ? input_buffer[input_offset++] : 0U;
|
||||
unsigned int c = remaining > 2U ? input_buffer[input_offset++] : 0U;
|
||||
|
||||
output_buffer[output_offset++] = alphabet[a >> 2U];
|
||||
output_buffer[output_offset++] =
|
||||
alphabet[((a & 0x03U) << 4U) | (b >> 4U)];
|
||||
output_buffer[output_offset++] = remaining > 1U
|
||||
? alphabet[((b & 0x0fU) << 2U) | (c >> 6U)] : '=';
|
||||
output_buffer[output_offset++] = remaining > 2U
|
||||
? alphabet[c & 0x3fU] : '=';
|
||||
}
|
||||
output_buffer[output_offset++] = '\r';
|
||||
output_buffer[output_offset++] = '\n';
|
||||
if (fwrite(output_buffer, 1U, output_offset, output) != output_offset)
|
||||
return 0;
|
||||
}
|
||||
return !ferror(input) && !ferror(output);
|
||||
}
|
||||
|
||||
int
|
||||
BongoDsnAddress(const char *address, int international, char *result,
|
||||
size_t result_size)
|
||||
{
|
||||
const unsigned char *cursor;
|
||||
const char *type;
|
||||
int written;
|
||||
|
||||
if (result != NULL && result_size != 0U) result[0] = '\0';
|
||||
if (address == NULL || address[0] == '\0' || result == NULL ||
|
||||
result_size == 0U) return 0;
|
||||
if (strchr(address, ';') != NULL) {
|
||||
if (international && !strncasecmp(address, "utf-8;", 6U)) {
|
||||
char decoded[BONGO_DSN_ADDRESS_MAX];
|
||||
|
||||
if (DecodeUnitext(address + 6U, decoded, sizeof(decoded))) {
|
||||
written = snprintf(result, result_size, "utf-8;%s", decoded);
|
||||
return written > 0 && (size_t)written < result_size;
|
||||
}
|
||||
/* RFC 6533 requires malformed stored UTF-8 address values to be
|
||||
* copied without alteration rather than guessed at. */
|
||||
}
|
||||
written = snprintf(result, result_size, "%s", address);
|
||||
return written > 0 && (size_t)written < result_size;
|
||||
}
|
||||
type = "rfc822";
|
||||
for (cursor = (const unsigned char *)address; *cursor; cursor++) {
|
||||
if (*cursor >= 0x80U) {
|
||||
type = "utf-8";
|
||||
break;
|
||||
}
|
||||
}
|
||||
written = snprintf(result, result_size, "%s;%s", type, address);
|
||||
return written > 0 && (size_t)written < result_size;
|
||||
}
|
||||
|
||||
const char *
|
||||
BongoDsnStatus(int reason, const char **action)
|
||||
{
|
||||
const char *result;
|
||||
|
||||
if (action == NULL) return NULL;
|
||||
*action = "failed";
|
||||
switch (reason) {
|
||||
case DELIVER_SUCCESS:
|
||||
*action = "delivered";
|
||||
result = "2.0.0";
|
||||
break;
|
||||
case DELIVER_PENDING:
|
||||
*action = "delayed";
|
||||
result = "4.0.0";
|
||||
break;
|
||||
case DELIVER_TRY_LATER:
|
||||
case DELIVER_PROCESSING_ERROR:
|
||||
*action = "delayed";
|
||||
result = "4.3.0";
|
||||
break;
|
||||
case DELIVER_LOCKED:
|
||||
*action = "delayed";
|
||||
result = "4.2.0";
|
||||
break;
|
||||
case DELIVER_USER_UNKNOWN:
|
||||
result = "5.1.1";
|
||||
break;
|
||||
case DELIVER_HOST_UNKNOWN:
|
||||
case DELIVER_UNREACHABLE:
|
||||
result = "5.4.4";
|
||||
break;
|
||||
case DELIVER_BOGUS_NAME:
|
||||
result = "5.1.3";
|
||||
break;
|
||||
case DELIVER_REFUSED:
|
||||
result = "5.4.1";
|
||||
break;
|
||||
case DELIVER_HOPCOUNT_EXCEEDED:
|
||||
result = "5.4.6";
|
||||
break;
|
||||
case DELIVER_TIMEOUT:
|
||||
case DELIVER_TOO_LONG:
|
||||
result = "5.4.7";
|
||||
break;
|
||||
case DELIVER_QUOTA_EXCEEDED:
|
||||
result = "5.2.2";
|
||||
break;
|
||||
case DELIVER_BLOCKED:
|
||||
case DELIVER_AUTH_ERROR:
|
||||
case DELIVER_VIRUS_REJECT:
|
||||
result = "5.7.1";
|
||||
break;
|
||||
case DELIVER_INTERNAL_ERROR:
|
||||
result = "5.3.0";
|
||||
break;
|
||||
default:
|
||||
result = "5.0.0";
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/****************************************************************************
|
||||
* <Novell-copyright>
|
||||
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc.
|
||||
*
|
||||
* To contact Novell about this file by physical or electronic mail, you
|
||||
* may find current contact information at www.novell.com.
|
||||
* </Novell-copyright>
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef BONGO_QUEUE_DSN_FORMAT_H
|
||||
#define BONGO_QUEUE_DSN_FORMAT_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define BONGO_DSN_ADDRESS_MAX 4096U
|
||||
|
||||
int BongoDsnWriteBase64(FILE *input, FILE *output);
|
||||
int BongoDsnAddress(const char *address, int international, char *result,
|
||||
size_t result_size);
|
||||
const char *BongoDsnStatus(int reason, const char **action);
|
||||
|
||||
#endif
|
||||
+121
-37
@@ -29,6 +29,7 @@
|
||||
#include "queue.h"
|
||||
#include "mime.h"
|
||||
#include "messages.h"
|
||||
#include "dsn-format.h"
|
||||
|
||||
static BOOL
|
||||
QueueFormatBuffer(char *destination, size_t destinationSize,
|
||||
@@ -1882,6 +1883,8 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
int reason = 0;
|
||||
int oReason;
|
||||
unsigned long flags;
|
||||
unsigned long messageFlags = 0;
|
||||
unsigned long returnFlags = DSN_HEADER;
|
||||
unsigned long bodySize = 0;
|
||||
unsigned long maxBodySize;
|
||||
unsigned long received = 0;
|
||||
@@ -1892,13 +1895,14 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
char timeLine[80];
|
||||
char line[CONN_BUFSIZE + 1];
|
||||
char postmaster[MAXEMAILNAMESIZE + 1];
|
||||
char sender[MAXEMAILNAMESIZE + 1];
|
||||
char sender[MAXEMAILNAMESIZE + 1] = "";
|
||||
char aSender[MAXEMAILNAMESIZE + 1] = "";
|
||||
char recipient[MAXEMAILNAMESIZE + 1] = "";
|
||||
char oRecipient[MAXEMAILNAMESIZE + 1] = "";
|
||||
char envID[128] = "";
|
||||
BOOL mBounce=FALSE;
|
||||
BOOL header;
|
||||
BOOL international;
|
||||
time_t now;
|
||||
|
||||
/* Step 0, check if we want to bounce at all */
|
||||
@@ -1924,11 +1928,14 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
oReason = 0;
|
||||
do {
|
||||
if (fgets(line, CONN_BUFSIZE, control)) {
|
||||
CHOP_NEWLINE(line);
|
||||
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 (sscanf(line + 1, "%256s %256s %lu %d", recipient,
|
||||
oRecipient, &flags, &reason) == 4) {
|
||||
returnFlags |= flags;
|
||||
if (!mBounce && (oReason != 0) && (oReason != reason)) {
|
||||
mBounce = TRUE;
|
||||
reason = 0;
|
||||
@@ -1945,13 +1952,19 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
ptr2 = strchr(ptr+1,' ');
|
||||
if (ptr2) {
|
||||
*ptr2 = '\0';
|
||||
strncpy(envID, ptr2, 127);
|
||||
if (!QueueFormatBuffer(envID, sizeof(envID),
|
||||
"%s", ptr2 + 1))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
strcpy(aSender, ptr + 1);
|
||||
if (!QueueFormatBuffer(aSender, sizeof(aSender),
|
||||
"%s", ptr + 1))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
strcpy(sender, line + 1);
|
||||
if (!QueueFormatBuffer(sender, sizeof(sender), "%s",
|
||||
line + 1))
|
||||
return FALSE;
|
||||
if ((sender[0] == '-') && (sender[1] == '\0')) {
|
||||
/* We don't bounce bounces */
|
||||
return(FALSE);
|
||||
@@ -1964,6 +1977,14 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
received = atol(line + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
case QUEUE_FLAGS: {
|
||||
char *end = NULL;
|
||||
unsigned long parsed = strtoul(line + 1, &end, 10);
|
||||
|
||||
if (end != line + 1) messageFlags = parsed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!feof(control) && !ferror(control));
|
||||
@@ -1971,6 +1992,7 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
if (recipient[0] == '\0') {
|
||||
return(FALSE);
|
||||
}
|
||||
international = (messageFlags & MSG_FLAG_SMTPUTF8) != 0;
|
||||
|
||||
QueueFormat(postmaster, "%s@%s", BongoGlobals.postmaster, BongoGlobals.hostname);
|
||||
handling = Conf.bounceHandling;
|
||||
@@ -2046,7 +2068,9 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
fprintf(rtsData, "Precedence: bulk\r\n\r\nThis is a MIME-encapsulated message\r\n\r\n");
|
||||
|
||||
/* First section, human readable */
|
||||
fprintf(rtsData, "--%" PRIdMAX "-%lu-%s\r\nContent-type: text/plain; charset=US-ASCII\r\n\r\n", (intmax_t)now, (long unsigned int)XplGetThreadID(), BongoGlobals.hostname);
|
||||
fprintf(rtsData, "--%" PRIdMAX "-%lu-%s\r\nContent-type: text/plain; charset=%s\r\nContent-Transfer-Encoding: %s\r\n\r\n", (intmax_t)now, (long unsigned int)XplGetThreadID(), BongoGlobals.hostname,
|
||||
international ? "UTF-8" : "US-ASCII",
|
||||
international ? "8bit" : "7bit");
|
||||
|
||||
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);
|
||||
@@ -2081,7 +2105,10 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
ptr2 = strchr(ptr, ' ');
|
||||
if (ptr2) {
|
||||
*ptr2 = '\0';
|
||||
strcpy(oRecipient, ptr);
|
||||
if (!QueueFormatBuffer(oRecipient,
|
||||
sizeof(oRecipient), "%s",
|
||||
ptr))
|
||||
return FALSE;
|
||||
ptr2++;
|
||||
flags = (atol(ptr2) & DSN_FLAGS);
|
||||
ptr = strchr(ptr2, ' ');
|
||||
@@ -2093,7 +2120,11 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strcpy(oRecipient, ptr);
|
||||
if (!QueueFormatBuffer(oRecipient,
|
||||
sizeof(oRecipient), "%s",
|
||||
ptr)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2213,18 +2244,23 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
}
|
||||
} while (!feof(control) && !ferror(control));
|
||||
|
||||
/* Second section, computer readable */
|
||||
fprintf(rtsData, "--%" PRIdMAX "-%lu-%s\r\nContent-type: message/delivery-status\r\n\r\n", (intmax_t)now, (long unsigned int)XplGetThreadID(), BongoGlobals.hostname);
|
||||
/* Second section, computer readable. RFC 6533 explicitly permits a
|
||||
* 7-bit Content-Transfer-Encoding for the global status media type. */
|
||||
FILE *statusPart = international ? tmpfile() : rtsData;
|
||||
if (statusPart == NULL) return FALSE;
|
||||
if (!international) {
|
||||
fprintf(rtsData, "--%" PRIdMAX "-%lu-%s\r\nContent-type: message/delivery-status\r\n\r\n", (intmax_t)now, (long unsigned int)XplGetThreadID(), BongoGlobals.hostname);
|
||||
}
|
||||
|
||||
/* Per message fields */
|
||||
if (envID[0]!='\0') {
|
||||
fprintf(rtsData, "Original-Envelope-Id: %s\r\n", envID);
|
||||
fprintf(statusPart, "Original-Envelope-Id: %s\r\n", envID);
|
||||
}
|
||||
|
||||
fprintf(rtsData, "Reporting-MTA: dns; %s\r\n", BongoGlobals.hostname);
|
||||
fprintf(statusPart, "Reporting-MTA: dns; %s\r\n", BongoGlobals.hostname);
|
||||
|
||||
MsgGetRFC822Date(-1, received, timeLine);
|
||||
fprintf(rtsData, "Arrival-Date: %s\r\n", timeLine);
|
||||
fprintf(statusPart, "Arrival-Date: %s\r\n", timeLine);
|
||||
|
||||
/* Per recipient fields */
|
||||
/* Need to go through the file and enumerate all recipients */
|
||||
@@ -2246,7 +2282,12 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
ptr2 = strchr(ptr, ' ');
|
||||
if (ptr2) {
|
||||
*ptr2 = '\0';
|
||||
strcpy(oRecipient, ptr);
|
||||
if (!QueueFormatBuffer(oRecipient,
|
||||
sizeof(oRecipient), "%s",
|
||||
ptr)) {
|
||||
if (international) fclose(statusPart);
|
||||
return FALSE;
|
||||
}
|
||||
ptr2++;
|
||||
flags = (atol(ptr2) & DSN_FLAGS);
|
||||
ptr = strchr(ptr2, ' ');
|
||||
@@ -2254,35 +2295,65 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
reason = atol(ptr + 1);
|
||||
}
|
||||
} else {
|
||||
strcpy(oRecipient, ptr);
|
||||
if (!QueueFormatBuffer(oRecipient,
|
||||
sizeof(oRecipient), "%s",
|
||||
ptr)) {
|
||||
if (international) fclose(statusPart);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Each per recipient block in DSN requires CRLF */
|
||||
fprintf(rtsData, "\r\n");
|
||||
fprintf(statusPart, "\r\n");
|
||||
|
||||
if (oRecipient[0] != '\0') {
|
||||
fprintf(rtsData, "Original-recipient: %s\r\n", oRecipient);
|
||||
char dsnAddress[BONGO_DSN_ADDRESS_MAX];
|
||||
if (BongoDsnAddress(oRecipient, international, dsnAddress,
|
||||
sizeof(dsnAddress)))
|
||||
fprintf(statusPart, "Original-recipient: %s\r\n",
|
||||
dsnAddress);
|
||||
}
|
||||
|
||||
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");
|
||||
{
|
||||
char dsnAddress[BONGO_DSN_ADDRESS_MAX];
|
||||
if (!BongoDsnAddress(line + 1, international, dsnAddress,
|
||||
sizeof(dsnAddress))) {
|
||||
if (international) fclose(statusPart);
|
||||
return FALSE;
|
||||
}
|
||||
fprintf(statusPart, "Final-recipient: %s\r\n",
|
||||
dsnAddress);
|
||||
}
|
||||
{
|
||||
const char *action;
|
||||
const char *status = BongoDsnStatus(reason, &action);
|
||||
|
||||
fprintf(statusPart, "Action: %s\r\n", action);
|
||||
fprintf(statusPart, "Status: %s\r\n", status);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!feof(control) && !ferror(control));
|
||||
|
||||
if (international) {
|
||||
fprintf(rtsData, "--%" PRIdMAX "-%lu-%s\r\nContent-type: message/global-delivery-status\r\nContent-Transfer-Encoding: base64\r\n\r\n", (intmax_t)now, (long unsigned int)XplGetThreadID(), BongoGlobals.hostname);
|
||||
rewind(statusPart);
|
||||
if (!BongoDsnWriteBase64(statusPart, rtsData)) {
|
||||
fclose(statusPart);
|
||||
return FALSE;
|
||||
}
|
||||
fclose(statusPart);
|
||||
}
|
||||
|
||||
/* Third section, original message */
|
||||
fprintf(rtsData, "--%" PRIdMAX "-%lu-%s\r\nContent-type: message/rfc822\r\n\r\n", (intmax_t)now, (long unsigned int)XplGetThreadID(), BongoGlobals.hostname);
|
||||
FILE *returnPart = international ? tmpfile() : rtsData;
|
||||
if (returnPart == NULL) return FALSE;
|
||||
if (!international) {
|
||||
fprintf(rtsData, "--%" PRIdMAX "-%lu-%s\r\nContent-type: %s\r\n\r\n", (intmax_t)now, (long unsigned int)XplGetThreadID(), BongoGlobals.hostname,
|
||||
(returnFlags & DSN_BODY) ? "message/rfc822" :
|
||||
"text/rfc822-headers");
|
||||
}
|
||||
|
||||
header = TRUE;
|
||||
maxBodySize = -1;
|
||||
@@ -2292,21 +2363,21 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
if((line[0] == '\r') && (line[1] == '\n') && (line[2] == 0)) {
|
||||
header = FALSE;
|
||||
if (Conf.bounceMaxBodySize) {
|
||||
fprintf(rtsData, "\r\n<Body content limited to %lu bytes>\r\n", Conf.bounceMaxBodySize);
|
||||
fprintf(returnPart, "\r\n<Body content limited to %lu bytes>\r\n", Conf.bounceMaxBodySize);
|
||||
maxBodySize = Conf.bounceMaxBodySize;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & DSN_HEADER) {
|
||||
if (returnFlags & DSN_HEADER) {
|
||||
if ((line[0] == 'F') && (line[1] == 'r') && (line[4] == ' ')) {
|
||||
fwrite(">", sizeof(char), 1, rtsData);
|
||||
fwrite(">", sizeof(char), 1, returnPart);
|
||||
}
|
||||
|
||||
bodySize += fwrite(line, sizeof(char), strlen(line), rtsData);
|
||||
bodySize += fwrite(line, sizeof(char), strlen(line), returnPart);
|
||||
}
|
||||
} else {
|
||||
if (flags & DSN_BODY) {
|
||||
bodySize += fwrite(line, sizeof(char), strlen(line), rtsData);
|
||||
if (returnFlags & DSN_BODY) {
|
||||
bodySize += fwrite(line, sizeof(char), strlen(line), returnPart);
|
||||
if (bodySize > maxBodySize) {
|
||||
break;
|
||||
}
|
||||
@@ -2315,8 +2386,16 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & DSN_BODY)) {
|
||||
fprintf(rtsData, "-- Message body has been omitted --\r\n");
|
||||
if (international) {
|
||||
fprintf(rtsData, "--%" PRIdMAX "-%lu-%s\r\nContent-type: %s\r\nContent-Transfer-Encoding: base64\r\n\r\n", (intmax_t)now, (long unsigned int)XplGetThreadID(), BongoGlobals.hostname,
|
||||
(returnFlags & DSN_BODY) ? "message/global" :
|
||||
"message/global-headers");
|
||||
rewind(returnPart);
|
||||
if (!BongoDsnWriteBase64(returnPart, rtsData)) {
|
||||
fclose(returnPart);
|
||||
return FALSE;
|
||||
}
|
||||
fclose(returnPart);
|
||||
}
|
||||
|
||||
/* End of message */
|
||||
@@ -2324,6 +2403,11 @@ CreateDSNMessage(FILE *data, FILE *control, FILE *rtsData, FILE *rtsControl, BOO
|
||||
|
||||
/* Now create the control file */
|
||||
fprintf(rtsControl, "D%" PRIdMAX "\r\n", (intmax_t)now);
|
||||
if (international) {
|
||||
fprintf(rtsControl, "%c%lu\r\n", QUEUE_FLAGS,
|
||||
(unsigned long)(MSG_FLAG_SMTPUTF8 |
|
||||
MSG_FLAG_ENCODING_8BITM));
|
||||
}
|
||||
fprintf(rtsControl, "F- -\r\n");
|
||||
|
||||
XplRWReadLockAcquire(&Conf.lock);
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/****************************************************************************
|
||||
* <Novell-copyright>
|
||||
* Copyright (c) 2001 Novell, Inc. All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public License
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, contact Novell, Inc.
|
||||
*
|
||||
* To contact Novell about this file by physical or electronic mail, you
|
||||
* may find current contact information at www.novell.com.
|
||||
* </Novell-copyright>
|
||||
****************************************************************************/
|
||||
|
||||
#include "dsn-format.h"
|
||||
|
||||
#include <nmap.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static void
|
||||
TestBase64(void)
|
||||
{
|
||||
FILE *input = tmpfile();
|
||||
FILE *output = tmpfile();
|
||||
char result[64];
|
||||
size_t length;
|
||||
|
||||
assert(input != NULL && output != NULL);
|
||||
assert(fwrite("Bongo UTF-8: \303\274", 1U,
|
||||
strlen("Bongo UTF-8: \303\274"), input) ==
|
||||
strlen("Bongo UTF-8: \303\274"));
|
||||
rewind(input);
|
||||
assert(BongoDsnWriteBase64(input, output));
|
||||
rewind(output);
|
||||
length = fread(result, 1U, sizeof(result) - 1U, output);
|
||||
result[length] = '\0';
|
||||
assert(!strcmp(result, "Qm9uZ28gVVRGLTg6IMO8\r\n"));
|
||||
fclose(input);
|
||||
fclose(output);
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
char address[128];
|
||||
const char *action;
|
||||
|
||||
TestBase64();
|
||||
assert(BongoDsnAddress("user@example.test", 0, address,
|
||||
sizeof(address)));
|
||||
assert(!strcmp(address, "rfc822;user@example.test"));
|
||||
assert(BongoDsnAddress("m\303\274ller@example.test", 1, address,
|
||||
sizeof(address)));
|
||||
assert(!strcmp(address, "utf-8;m\303\274ller@example.test"));
|
||||
assert(BongoDsnAddress("UTF-8;m\\x{FC}ller@example.test", 1, address,
|
||||
sizeof(address)));
|
||||
assert(!strcmp(address, "utf-8;m\303\274ller@example.test"));
|
||||
assert(BongoDsnAddress("UTF-8;m\\x{FC}ller@example.test", 0, address,
|
||||
sizeof(address)));
|
||||
assert(!strcmp(address, "UTF-8;m\\x{FC}ller@example.test"));
|
||||
assert(BongoDsnAddress("utf-8;bad\\x{D800}@example.test", 1, address,
|
||||
sizeof(address)));
|
||||
assert(!strcmp(address, "utf-8;bad\\x{D800}@example.test"));
|
||||
assert(!strcmp(BongoDsnStatus(DELIVER_SUCCESS, &action), "2.0.0"));
|
||||
assert(!strcmp(action, "delivered"));
|
||||
assert(!strcmp(BongoDsnStatus(DELIVER_USER_UNKNOWN, &action), "5.1.1"));
|
||||
assert(!strcmp(action, "failed"));
|
||||
assert(!strcmp(BongoDsnStatus(DELIVER_TRY_LATER, &action), "4.3.0"));
|
||||
assert(!strcmp(action, "delayed"));
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user