#include "config.h" #include #include #include #include "msgid.h" #include "options.h" #include "c-client-header.h" #include ////////////////////////////////////////////////////////////////////////// // // A MsgId as in the msinfo file has one of the following forms: // // HEADER_MSGID format // <>, md5: 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 "". 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 == "<>"; }