#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include <string>
#include "msgid.h"
#include "options.h"
#include "c-client-header.h"
#include <cassert>
//////////////////////////////////////////////////////////////////////////
//
// A MsgId as in the msinfo file has one of the following forms:
//
// <abcd@x.y.z> HEADER_MSGID format
// <>, md5: <ABCD1234> MD5_MSGID format
//
//////////////////////////////////////////////////////////////////////////
extern options_t options;
#ifdef HAVE_MD5
#define MD5BLKLEN 64 /* MD5 block length */
#define MD5DIGLEN 16 /* MD5 digest length */
typedef struct {
unsigned long chigh; /* high 32bits of byte count */
unsigned long clow; /* low 32bits of byte count */
unsigned long state[4]; /* state (ABCD) */
unsigned char buf[MD5BLKLEN]; /* input buffer */
unsigned char *ptr; /* buffer position */
} MD5CONTEXT;
extern "C" {
void md5_init (MD5CONTEXT *ctx);
void md5_update (MD5CONTEXT *ctx,unsigned char *data,unsigned long len);
void md5_final (unsigned char *digest,MD5CONTEXT *ctx);
}
#endif // HAVE_MD5
//////////////////////////////////////////////////////////////////////////
//
MsgId::MsgId(ENVELOPE *envelope)
//
// Create a message id from an envelope
//
// depending on the options that are set we use one of the methods for
// generating a message identificator.
//
// Currently there are two methods:
//
// HEADER_MSGID - use the Message-ID header provided in the mailheader
// MD5_MSGID - make a md5 hash from the From, To, Subject, Date
// and Message-ID fields
//
//////////////////////////////////////////////////////////////////////////
{
string str;
switch( options.msgid_type) {
case (HEADER_MSGID) :
if ( envelope->message_id
&& strcmp( envelope->message_id, "") != 0
&& strcmp( envelope->message_id, "<>") != 0 )
*this = envelope->message_id;
else
*this = "<>"; // empty message-id
sanitize_message_id();
// some software produces empty Message-IDs f.ex. for draft emails
// which makes us unable to differentiaty between such messages
if ( *this == "<>") {
printf( "Warning: empty Message-ID header in message From: \"%s\" "
"Subject: \"%s\" - please consult the README\n",
(envelope->from && envelope->from->mailbox)
? envelope->from->mailbox : "",
envelope->subject ? envelope->subject : "");
}
break;
#ifdef HAVE_MD5
case (MD5_MSGID) :
char addr[4096];
if (envelope->date)
str = string((char*)envelope->date);
if (envelope->subject)
str += string(envelope->subject);
if (envelope->message_id)
str += string(envelope->message_id);
for (ADDRESS *a = envelope->from; a; a = a->next) {
*addr = '\0';
rfc822_address(addr,a);
str += string(addr);
}
for (ADDRESS *a = envelope->to; a; a = a->next) {
*addr = '\0';
rfc822_address(addr,a);
str += string(addr);
}
if (str.length() != 0) {
unsigned char bdigest[MD5DIGLEN];
MD5CONTEXT ctx;
char cdigest[2 * MD5DIGLEN + 2];
md5_init(&ctx);
md5_update(&ctx, (unsigned char *) str.c_str(), strlen(str.c_str()) );
md5_final(bdigest, &ctx);
for (int i = 0; i < MD5DIGLEN; i++)
sprintf(&cdigest[2 * i],"%02x",bdigest[i]);
cdigest[2 * MD5DIGLEN] = '\0';
*this = cdigest;
}
break;
#endif // HAVE_MD5
default :
assert(0); // this should not happen
break;
}
}
//////////////////////////////////////////////////////////////////////////
//
void MsgId::sanitize_message_id()
//
// Some mail software goes out of its way to produce braindammaged
// message-ids.
//
// This function attempts to bring it back to a sane form following RFC822
// that is "<blabla@somedomain>". Spaces inside the Message-ID are forbidden
// and are replaced by dots: ' '->'.'.
//
// Iif our msgid starts with '<' we find the corresponding '>' and throw
// everything after that away (let's hope there is no braindead sw that has
// significant msgid stuff _after_ the '>' or isn't using the '>' _inside_
// the msgid). If we don't find the ending '>' we just put a ">" at the end
// of the msgid.
//
//////////////////////////////////////////////////////////////////////////
{
int removed_blanks = 0;
int added_brackets = 0;
unsigned i;
if ((*this)[0] != '<') {
*this = '<'+ *this;
added_brackets = 1;
}
for (i=0; (i < this->length()) && ((*this)[i] != '>'); i++) {
if (isspace((*this)[i]) || iscntrl((*this)[i])) {
(*this)[i] = '.';
removed_blanks = 1;
}
}
// Have we reached end of string?
if (i == this->size()) {
// if there's no '>' we need to attach
if((*this)[i-1] != '>') {
*this = *this + '>';
added_brackets = 1;
}
}
// we've found a '>', so let's cut the rubbish that follows
else
*this = this->substr( 0, i+1);
if (options.report_braindammaged_msgids)
if (removed_blanks)
fprintf( stderr, "Warning: added brackets <> around message id %s\n",
this->c_str());
else if (added_brackets)
fprintf( stderr, "Warning: replaced blanks with . in message id %s\n",
this->c_str());
}
//////////////////////////////////////////////////////////////////////////
//
string MsgId::from_msinfo_format()
//
// Read from a msinfo entry the relevant msgid depending on which
// format is currently chosen
//
// Currently there are two formats:
//
// HEADER_MSGID - message id as in the Message-ID header in a mailheader
// MD5_MSGID - message id in md5 hash format
//
//////////////////////////////////////////////////////////////////////////
{
int start;
switch( options.msgid_type) {
case (HEADER_MSGID) :
return this->substr(0, this->find(">") + 1);
break;
#ifdef HAVE_MD5
case (MD5_MSGID) :
start = this->find(">, md5: <") + 9; // 9 == length of ">, md5: <"
return this->substr( start, this->find( ">", start) - start);
break;
#endif // HAVE_MD5
default :
assert(0); // this should not happen
break;
}
}
//////////////////////////////////////////////////////////////////////////
//
string MsgId::to_msinfo_format()
//
// Return the message id in msinfo format
//
// Currently there are two formats:
//
// HEADER_MSGID - message id as in the Message-ID header in a mailheader
// MD5_MSGID - message id in md5 hash format
//
//////////////////////////////////////////////////////////////////////////
{
switch( options.msgid_type) {
case (HEADER_MSGID) :
return *this;
break;
#ifdef HAVE_MD5
case (MD5_MSGID) :
return "<>, md5: <" + *this + ">";
break;
#endif // HAVE_MD5
default :
assert(0); // this should not happen
break;
}
}
//////////////////////////////////////////////////////////////////////////
//
bool MsgId::empty()
//
// Say whether Message-ID is empty
//
//
//////////////////////////////////////////////////////////////////////////
{
return string::empty() || *this == "<>";
}
syntax highlighted by Code2HTML, v. 0.9.1