/**************************************************************************** ** $Id: smtp.cpp,v 1.10 2003/09/29 12:05:09 rom_vinot Exp $ ** ** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. ** Copyright (C) 1999-2003 Hiroyuki Yamamoto ** Copyright (C) 2003 Romain Vinot ** *****************************************************************************/ #include "smtp.h" #include #include #include #include #include #include #include #include using namespace std; Smtp::Smtp( const QString &smtp, const QString &p, const QString &f, const QString &r, QString &msg, bool auth, QString &log, QString &pass) : message(msg), from(f), rcpt(r), port(p), useAuthentification(auth), login(log), password(pass) { socket = new QSocket( this ); connect ( socket, SIGNAL( readyRead() ), this, SLOT( readyRead() ) ); dnsLookup = new QDns( smtp, QDns::A ); connect( dnsLookup, SIGNAL(resultsReady()), this, SLOT(dnsLookupHelper()) ); state=SMTP_READY; // TODO : Do we still need that ? QTimer::singleShot(20000, this, SLOT(problemTimeOut())); } Smtp::~Smtp() { delete t; delete socket; } void Smtp::doNothing() {} void Smtp::readyRead() { // SMTP is line-oriented if ( !socket->canReadLine() ) return; QString responseLine; do { responseLine = socket->readLine(); response += responseLine; } while( socket->canReadLine() && responseLine[3] != ' ' ); QString code = responseLine; code.truncate( 3); cout << "DEBUG: SMTP: " << responseLine; bool cont = false; if (code.length() < 3) { error("Bad SMTP response. The server is not standard."); return; } if (code=="504" || code=="503" || code=="501") { QString str=QString("An error occured during SMTP session:\n")+responseLine; error(str); return; } if (code=="535") { error("An error occured during authentification.\n"); return; } if (code[0]!='1' && code[0]!='2' && code[0]!='3') { QString str=QString("An error occured during SMTP session:\n") + responseLine; error(str); return; } if (responseLine[3] == '-') cont = true; else if (responseLine[3] != ' ' && responseLine[3] != '\0') { error("Bad SMTP response. The server is not standard."); return; } /* ignore all multiline responses except for EHLO */ if (cont && state != SMTP_EHLO) return; switch (state) { case SMTP_READY: case SMTP_CONNECTED: if (useAuthentification) { state = SMTP_EHLO; *t << "EHLO " << host << "\r\n"; } else { state = SMTP_HELO; *t << "HELO " << host << "\r\n"; } break; case SMTP_HELO: sendFrom(); break; case SMTP_EHLO: if (code=="250") { if (responseLine.find("AUTH",3)!=-1 && responseLine.find("LOGIN",8)!=-1) sendAuthLogin(); else { QMessageBox::about( qApp->activeWindow(), "QSpaceHulk, mail not sent", "SMTP AUTH not available. I will try to send the " "message without authentification"); sendFrom(); } } else sendFrom(); break; case SMTP_AUTH: receivedAuth(responseLine); break; case SMTP_AUTH_LOGIN_USER: state = SMTP_AUTH_LOGIN_PASS; if (code=="334") { char buf[8192]; Base64::Encode(buf, password.latin1(), password.length()); *t << buf << "\r\n"; } else // Server rejects AUTH *t << "*\r\n"; break; case SMTP_AUTH_LOGIN_PASS: sendFrom(); break; case SMTP_FROM: sendRcpt(); break; case SMTP_RCPT: sendData(); break; case SMTP_DATA: sendBodyData(); break; case SMTP_SEND_DATA: sendQuit(); break; case SMTP_QUIT: QMessageBox::about( qApp->activeWindow(), "QSpaceHulk, Mail send", "The mail has been send successfully.\n" "You don't need to send the file manually."); QTimer::singleShot( 0, this, SLOT(deleteMe()) ); break; case SMTP_ERROR: default: error("An error occured during SMTP session\n"); return; } } void Smtp::deleteMe() { delete this; } void Smtp::problemTimeOut() { /* Currently removed... QString msg = "A problem has occured : the mail has not yet been sent " "after 20 seconds.\n"; switch (state) { case Init : msg += "I'm not able to connect to your smtp server.\n"; break; case Mail: case Rcpt: msg += "I can connect to the machine but I can't send " "my message.\n"; break; case Data: case Body: msg += "The SMTP server responded but I have no answer" " after sending the message.\n"; break; default: break; } msg += "You should send manually the file " "already saved in your savegame directory."; QMessageBox::about( qApp->activeWindow(), "QSpacehulk, Mail not send", msg); QTimer::singleShot( 0, this, SLOT(deleteMe()) ); */ } void Smtp::error(QString msg) { msg += "\nYou must send the savegame file manually."; QMessageBox::about( qApp->activeWindow(), "QSpaceHulk, mail not sent", msg); QTimer::singleShot( 0, this, SLOT(deleteMe()) ); } void Smtp::dnsLookupHelper() { QValueList s = dnsLookup->addresses(); if ( s.isEmpty() && dnsLookup->isWorking() ) return; host = s.first().toString(); socket->connectToHost( host, port.toInt() ); t = new QTextStream( socket ); } void Smtp::sendFrom() { state = SMTP_FROM; if (from.find('<')!=-1) *t << "MAIL FROM: " << from << "\r\n"; else *t << "MAIL FROM: <" << from << ">\r\n"; } void Smtp::sendRcpt() { state = SMTP_RCPT; // You must always write the adress with "< >" or it will not be recognized. *t << "RCPT TO: <" << rcpt << ">\r\n"; } void Smtp::sendData() { state = SMTP_DATA; *t << "DATA\r\n"; } void Smtp::sendBodyData() { state = SMTP_SEND_DATA; *t << message << ".\r\n"; } void Smtp::sendQuit() { state = SMTP_QUIT; *t << "QUIT\r\n"; } void Smtp::sendAuthLogin() { state = SMTP_AUTH; *t << "AUTH LOGIN\r\n"; } void Smtp::receivedAuth(QString &responseLine) { state = SMTP_AUTH_LOGIN_USER; if (responseLine.find("334")==0) { char buf[8192]; Base64::Encode(buf, login.latin1(), login.length()); *t << buf << "\r\n"; } else // Server rejects AUTH *t << "*\r\n"; } /*********************************************************************************/ /* Utils : BASE64 */ /*********************************************************************************/ static const char base64char[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char base64val[128] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 }; #define BASE64VAL(c) (isascii(c) ? base64val[(int)(c)] : -1) void Base64::Encode(char *out, const char *in, int inlen) { const char *inp = in; char *outp = out; while (inlen >= 3) { *outp++ = base64char[(inp[0] >> 2) & 0x3f]; *outp++ = base64char[((inp[0] & 0x03) << 4) | ((inp[1] >> 4) & 0x0f)]; *outp++ = base64char[((inp[1] & 0x0f) << 2) | ((inp[2] >> 6) & 0x03)]; *outp++ = base64char[inp[2] & 0x3f]; inp += 3; inlen -= 3; } if (inlen > 0) { *outp++ = base64char[(inp[0] >> 2) & 0x3f]; if (inlen == 1) { *outp++ = base64char[(inp[0] & 0x03) << 4]; *outp++ = '='; } else { *outp++ = base64char[((inp[0] & 0x03) << 4) | ((inp[1] >> 4) & 0x0f)]; *outp++ = base64char[((inp[1] & 0x0f) << 2)]; } *outp++ = '='; } *outp = '\0'; } int Base64::Decode(char *out, const char *in, int inlen) { const char *inp = in; char *outp = out; char buf[4]; while ((inlen >= 4 || inlen < 0) && *inp != '\0') { buf[0] = *inp++; inlen--; if (BASE64VAL(buf[0]) == -1) break; buf[1] = *inp++; inlen--; if (BASE64VAL(buf[1]) == -1) break; buf[2] = *inp++; inlen--; if (buf[2] != '=' && BASE64VAL(buf[2]) == -1) break; buf[3] = *inp++; inlen--; if (buf[3] != '=' && BASE64VAL(buf[3]) == -1) break; *outp++ = ((BASE64VAL(buf[0]) << 2) & 0xfc) | ((BASE64VAL(buf[1]) >> 4) & 0x03); if (buf[2] != '=') { *outp++ = ((BASE64VAL(buf[1]) & 0x0f) << 4) | ((BASE64VAL(buf[2]) >> 2) & 0x0f); if (buf[3] != '=') { *outp++ = ((BASE64VAL(buf[2]) & 0x03) << 6) | (BASE64VAL(buf[3]) & 0x3f); } } } return outp - out; } // /*********************************************************************************/ // /* Utils : MD5 */ // /*********************************************************************************/ // /* Written by Ulrich Drepper , 1995. */ // /* heavily modified for GnuPG by */ // /* modified again for Sylpheed by 2001-02-11 */ // /* Test values: // * "" D4 1D 8C D9 8F 00 B2 04 E9 80 09 98 EC F8 42 7E // * "a" 0C C1 75 B9 C0 F1 B6 A8 31 C3 99 E2 69 77 26 61 // * "abc 90 01 50 98 3C D2 4F B0 D6 96 3F 7D 28 E1 7F 72 // * "message digest" F9 6B 69 7D 7C B7 93 8D 52 5A 2F 31 AA F1 61 D0 // */ // #include // #include // #include // #include // #include // #include "utils.h" // #include "md5.h" // /**************** // * Rotate a 32 bit integer by n bytes // */ // #define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) ) // void md5_init(MD5_CONTEXT *ctx) // { // ctx->A = 0x67452301; // ctx->B = 0xefcdab89; // ctx->C = 0x98badcfe; // ctx->D = 0x10325476; // ctx->nblocks = 0; // ctx->count = 0; // ctx->finalized = 0; // } // /* These are the four functions used in the four steps of the MD5 algorithm // and defined in the RFC 1321. The first function is a little bit optimized // (as found in Colin Plumbs public domain implementation). */ // /* #define FF(b, c, d) ((b & c) | (~b & d)) */ // #define FF(b, c, d) (d ^ (b & (c ^ d))) // #define FG(b, c, d) FF (d, b, c) // #define FH(b, c, d) (b ^ c ^ d) // #define FI(b, c, d) (c ^ (b | ~d)) // /**************** // * transform n*64 bytes // */ // static void transform(MD5_CONTEXT *ctx, const unsigned char *data) // { // u32 correct_words[16]; // u32 A = ctx->A; // u32 B = ctx->B; // u32 C = ctx->C; // u32 D = ctx->D; // u32 *cwp = correct_words; // #ifdef BIG_ENDIAN_HOST // { // int i; // unsigned char *p2, *p1; // for (i = 0, p1 = data, p2 = (unsigned char*)correct_words; // i < 16; i++, p2 += 4) { // p2[3] = *p1++; // p2[2] = *p1++; // p2[1] = *p1++; // p2[0] = *p1++; // } // } // #else // memcpy(correct_words, data, 64); // #endif /* #define OP(a, b, c, d, s, T) \ do { \ a += FF (b, c, d) + (*cwp++) + T; \ a = rol(a, s); \ a += b; \ } while (0) */ // /* Before we start, one word about the strange constants. // They are defined in RFC 1321 as // T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 // */ // /* Round 1. */ // OP (A, B, C, D, 7, 0xd76aa478); // OP (D, A, B, C, 12, 0xe8c7b756); // OP (C, D, A, B, 17, 0x242070db); // OP (B, C, D, A, 22, 0xc1bdceee); // OP (A, B, C, D, 7, 0xf57c0faf); // OP (D, A, B, C, 12, 0x4787c62a); // OP (C, D, A, B, 17, 0xa8304613); // OP (B, C, D, A, 22, 0xfd469501); // OP (A, B, C, D, 7, 0x698098d8); // OP (D, A, B, C, 12, 0x8b44f7af); // OP (C, D, A, B, 17, 0xffff5bb1); // OP (B, C, D, A, 22, 0x895cd7be); // OP (A, B, C, D, 7, 0x6b901122); // OP (D, A, B, C, 12, 0xfd987193); // OP (C, D, A, B, 17, 0xa679438e); // OP (B, C, D, A, 22, 0x49b40821); // #undef OP /* #define OP(f, a, b, c, d, k, s, T) \ do { \ a += f (b, c, d) + correct_words[k] + T; \ a = rol(a, s); \ a += b; \ } while (0) */ // /* Round 2. */ // OP (FG, A, B, C, D, 1, 5, 0xf61e2562); // OP (FG, D, A, B, C, 6, 9, 0xc040b340); // OP (FG, C, D, A, B, 11, 14, 0x265e5a51); // OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); // OP (FG, A, B, C, D, 5, 5, 0xd62f105d); // OP (FG, D, A, B, C, 10, 9, 0x02441453); // OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); // OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); // OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); // OP (FG, D, A, B, C, 14, 9, 0xc33707d6); // OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); // OP (FG, B, C, D, A, 8, 20, 0x455a14ed); // OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); // OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); // OP (FG, C, D, A, B, 7, 14, 0x676f02d9); // OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); // /* Round 3. */ // OP (FH, A, B, C, D, 5, 4, 0xfffa3942); // OP (FH, D, A, B, C, 8, 11, 0x8771f681); // OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); // OP (FH, B, C, D, A, 14, 23, 0xfde5380c); // OP (FH, A, B, C, D, 1, 4, 0xa4beea44); // OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); // OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); // OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); // OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); // OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); // OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); // OP (FH, B, C, D, A, 6, 23, 0x04881d05); // OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); // OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); // OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); // OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); // /* Round 4. */ // OP (FI, A, B, C, D, 0, 6, 0xf4292244); // OP (FI, D, A, B, C, 7, 10, 0x432aff97); // OP (FI, C, D, A, B, 14, 15, 0xab9423a7); // OP (FI, B, C, D, A, 5, 21, 0xfc93a039); // OP (FI, A, B, C, D, 12, 6, 0x655b59c3); // OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); // OP (FI, C, D, A, B, 10, 15, 0xffeff47d); // OP (FI, B, C, D, A, 1, 21, 0x85845dd1); // OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); // OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); // OP (FI, C, D, A, B, 6, 15, 0xa3014314); // OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); // OP (FI, A, B, C, D, 4, 6, 0xf7537e82); // OP (FI, D, A, B, C, 11, 10, 0xbd3af235); // OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); // OP (FI, B, C, D, A, 9, 21, 0xeb86d391); // /* Put checksum in context given as argument. */ // ctx->A += A; // ctx->B += B; // ctx->C += C; // ctx->D += D; // } // /* The routine updates the message-digest context to // * account for the presence of each of the characters inBuf[0..inLen-1] // * in the message whose digest is being computed. // */ // void md5_update(MD5_CONTEXT *hd, const unsigned char *inbuf, size_t inlen) // { // if (hd->count == 64) { /* flush the buffer */ // transform( hd, hd->buf ); // hd->count = 0; // hd->nblocks++; // } // if (!inbuf) // return; // if (hd->count) { // for (; inlen && hd->count < 64; inlen--) // hd->buf[hd->count++] = *inbuf++; // md5_update(hd, NULL, 0); // if (!inlen) // return; // } // while (inlen >= 64) { // transform(hd, inbuf); // hd->count = 0; // hd->nblocks++; // inlen -= 64; // inbuf += 64; // } // for (; inlen && hd->count < 64; inlen--) // hd->buf[hd->count++] = *inbuf++; // } // /* The routine final terminates the message-digest computation and // * ends with the desired message digest in mdContext->digest[0...15]. // * The handle is prepared for a new MD5 cycle. // * Returns 16 bytes representing the digest. // */ // static void do_final(MD5_CONTEXT *hd) // { // u32 t, msb, lsb; // unsigned char *p; // md5_update(hd, NULL, 0); /* flush */ // msb = 0; // t = hd->nblocks; // if ((lsb = t << 6) < t) /* multiply by 64 to make a byte count */ // msb++; // msb += t >> 26; // t = lsb; // if ((lsb = t + hd->count) < t) /* add the count */ // msb++; // t = lsb; // if ((lsb = t << 3) < t) /* multiply by 8 to make a bit count */ // msb++; // msb += t >> 29; // if (hd->count < 56) { /* enough room */ // hd->buf[hd->count++] = 0x80; /* pad */ // while(hd->count < 56) // hd->buf[hd->count++] = 0; /* pad */ // } else { /* need one extra block */ // hd->buf[hd->count++] = 0x80; /* pad character */ // while (hd->count < 64) // hd->buf[hd->count++] = 0; // md5_update(hd, NULL, 0); /* flush */ // memset(hd->buf, 0, 56); /* fill next block with zeroes */ // } // /* append the 64 bit count */ // hd->buf[56] = lsb ; // hd->buf[57] = lsb >> 8; // hd->buf[58] = lsb >> 16; // hd->buf[59] = lsb >> 24; // hd->buf[60] = msb ; // hd->buf[61] = msb >> 8; // hd->buf[62] = msb >> 16; // hd->buf[63] = msb >> 24; // transform(hd, hd->buf); // p = hd->buf; // #ifdef BIG_ENDIAN_HOST /* #define X(a) do { *p++ = hd->a ; *p++ = hd->a >> 8; \ *p++ = hd->a >> 16; *p++ = hd->a >> 24; } while(0) */ // #else /* little endian */ // /*#define X(a) do { *(u32*)p = hd->##a ; p += 4; } while(0)*/ // /* Unixware's cpp doesn't like the above construct so we do it his way: // * (reported by Allan Clark) */ // #define X(a) do { *(u32*)p = (*hd).a ; p += 4; } while(0) // #endif // X(A); // X(B); // X(C); // X(D); // #undef X // hd->finalized = 1; // } // void // md5_final(unsigned char *digest, MD5_CONTEXT *ctx) // { // if (!ctx->finalized) // do_final(ctx); // memcpy(digest, ctx->buf, 16); // } // /* // * Creates a MD5 digest in hex fomrat (lowercase letters) from the // * string S. hextdigest but be buffer of at lease 33 bytes! // */ // void md5_hex_digest(char *hexdigest, const unsigned char *s) // { // int i; // MD5_CONTEXT context; // unsigned char digest[16]; // md5_init(&context); // md5_update(&context, s, strlen(s)); // md5_final(digest, &context); // for (i = 0; i < 16; i++) // sprintf(hexdigest + 2 * i, "%02x", digest[i]); // } // /* // ** Function: md5_hmac // ** taken from the file rfc2104.txt // ** written by Martin Schaaf // */ // void md5_hmac(unsigned char *digest, // const unsigned char* text, int text_len, // const unsigned char* key, int key_len) // { // MD5_CONTEXT context; // unsigned char k_ipad[64]; /* inner padding - // * key XORd with ipad // */ // unsigned char k_opad[64]; /* outer padding - // * key XORd with opad // */ // /* unsigned char tk[16]; */ // int i; // /* start out by storing key in pads */ // memset(k_ipad, 0, sizeof k_ipad); // memset(k_opad, 0, sizeof k_opad); // if (key_len > 64) { // /* if key is longer than 64 bytes reset it to key=MD5(key) */ // MD5_CONTEXT tctx; // md5_init(&tctx); // md5_update(&tctx, key, key_len); // md5_final(k_ipad, &tctx); // md5_final(k_opad, &tctx); // } else { // memcpy(k_ipad, key, key_len); // memcpy(k_opad, key, key_len); // } // /* // * the HMAC_MD5 transform looks like: // * // * MD5(K XOR opad, MD5(K XOR ipad, text)) // * // * where K is an n byte key // * ipad is the byte 0x36 repeated 64 times // * opad is the byte 0x5c repeated 64 times // * and text is the data being protected // */ // /* XOR key with ipad and opad values */ // for (i = 0; i < 64; i++) { // k_ipad[i] ^= 0x36; // k_opad[i] ^= 0x5c; // } // /* // * perform inner MD5 // */ // md5_init(&context); /* init context for 1st // * pass */ // md5_update(&context, k_ipad, 64); /* start with inner pad */ // md5_update(&context, text, text_len); /* then text of datagram */ // md5_final(digest, &context); /* finish up 1st pass */ // /* // * perform outer MD5 // */ // md5_init(&context); /* init context for 2nd // * pass */ // md5_update(&context, k_opad, 64); /* start with outer pad */ // md5_update(&context, digest, 16); /* then results of 1st // * hash */ // md5_final(digest, &context); /* finish up 2nd pass */ // } // void md5_hex_hmac(char *hexdigest, // const unsigned char* text, int text_len, // const unsigned char* key, int key_len) // { // unsigned char digest[16]; // int i; // md5_hmac(digest, text, text_len, key, key_len); // for (i = 0; i < 16; i++) // sprintf(hexdigest + 2 * i, "%02x", digest[i]); // }