Hello,
But since you asked so nicely I did look into it and found that your
patch does
not apply to clamsmtp.
I attached a version of the patch that applies to clamsmtp. It also
includes minor other code changes, but that should not change
functionality.
Also you made some changes that don't really seem to be related to the
problem at hand, or why did you do for instance this:
- while((rc = getline(&line, &line_len, file)) != -1)
+ while(line = (fgets(buf + 1, buf_len - 1, file)))
I may be missing something obvious here, though, but I haven't looked
into clamsmtp's source code much.
getline reads whole lines, that is till '\n' or EOF appears, and
reallocates the buffer if it was not large enough, so that maybe the
buffer has a new address after a call of getline. But my method of
prepending a dot relies on buf and line to share the same memory region,
with just buf starting one byte earlier. Therefore I switched to fgets,
which always reads maximum one byte less than its size parameter.
Regards
Christoph
--- clamsmtp-1.10.orig/common/smtppass.c
+++ clamsmtp-1.10/common/smtppass.c
@@ -1389,16 +1389,34 @@ int sp_cache_data(spctx_t* ctx)
{
int r, count = 0;
const char* data;
+ int linestart;
+
+ linestart = 1;
while((r = sp_read_data(ctx, &data)) != 0)
{
- if(r < 0)
- return -1; /* Message already printed */
+ if(r < 0)
+ return -1; /* Message already printed */
+
+ /* SMTP RFCs say that servers must remove leading dots at the beginning
+ * of a line. We do that here.
+ */
+
+ if (linestart && (data[0] == '.'))
+ {
+ data++;
+ r--;
+ }
+
+ if (ctx->_crlf)
+ linestart = 1;
+ else
+ linestart = 0;
- count += r;
+ count += r;
- if((r = sp_write_data(ctx, data, r)) < 0)
- return -1; /* Message already printed */
+ if((r = sp_write_data(ctx, data, r)) < 0)
+ return -1; /* Message already printed */
}
/* End the caching */
@@ -1572,9 +1590,12 @@ int sp_done_data(spctx_t* ctx, const cha
int ret = 0;
char *line;
char header[MAX_HEADER_LENGTH] = "";
- size_t header_len, line_len;
+ size_t header_len;
int header_prepend = 0;
ssize_t rc;
+ size_t buf_len;
+ int linestart;
+ char *buf;
ASSERT(ctx->cachename[0]); /* Must still be around */
ASSERT(!ctx->cachefile); /* File must be closed */
@@ -1582,10 +1603,12 @@ int sp_done_data(spctx_t* ctx, const cha
memset(header, 0, sizeof(header));
/* Alloc line buffer */
- line_len = SP_LINE_LENGTH;
- if((line = (char *)malloc(line_len)) == NULL)
+ buf_len = SP_LINE_LENGTH;
+ if((buf = (char *)malloc(buf_len)) == NULL)
RETURN(-1);
+ buf[0] = '.';
+
/* Open the file */
file = fopen(ctx->cachename, "r");
if(file == NULL)
@@ -1631,17 +1654,24 @@ int sp_done_data(spctx_t* ctx, const cha
header[0] = '\0';
}
- /* Transfer actual file data */
- while((rc = getline(&line, &line_len, file)) != -1)
+ /* Transfer actual file data */
+ while(line = (fgets(buf + 1, buf_len - 1, file)))
{
- /*
- * If the line is <CRLF>.<CRLF> we need to change it so that
- * it doesn't end the email. We do this by adding a space.
- * This won't occur much in clamsmtpd, but proxsmtpd might
- * have filters that accidentally put this in.
- */
- if(strcmp(line, "." CRLF) == 0)
- strncpy(line, ". " CRLF, SP_LINE_LENGTH);
+ /* SMTP RFCs say that clients must prepend an additional dot
+ * to every line starting with a dot. We do that here.
+ */
+ if (linestart && (line[0] == '.'))
+ line = buf;
+
+ rc = strlen(line);
+
+ if (strstr(line, CRLF))
+ linestart = 1;
+ else
+ linestart = 0;
+
+ if(strcmp(line, "." CRLF) == 0)
+ strncpy(line, ". " CRLF, SP_LINE_LENGTH);
if(header[0] != '\0')
{
@@ -1683,10 +1713,10 @@ int sp_done_data(spctx_t* ctx, const cha
cleanup:
- if(line)
- free(line);
- if(file)
- fclose(file); /* read-only so no error check */
+ if(buf)
+ free(buf);
+ if(file)
+ fclose(file); /* read-only so no error check */
return ret;
}