jirentabu / crashrpt

Automatically exported from code.google.com/p/crashrpt
0 stars 0 forks source link

Support multiple e-mail recipients #123

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
In some organizations it may be beneficial to distribute the crash reports to 
multiple recipients and it may be difficult to set up a mail group to bounce a 
single e-mail to multiple recipients.

Extend CrashSender to send crash reports to multiple recipients.

Necessary changes to make it work over smtp are following:

in BOOL CErrorReportSender::SendOverSMTP():

    // If the sender is not defined, use the first e-mail address from the recipient list.
    if (g_CrashInfo.GetReport(m_nCurReport).m_sEmailFrom.IsEmpty()) {
        // Force a copy of the string. Simple assignment just references the data of g_CrashInfo.m_sEmailTo. 
        // The copy string will be modified by strtok.
        CString copy((LPCTSTR)g_CrashInfo.m_sEmailTo, g_CrashInfo.m_sEmailTo.GetLength());
        TCHAR   separators[] = _T(";, ");
        TCHAR  *context      = 0;
        TCHAR  *to           = _tcstok_s(const_cast<LPTSTR>((LPCTSTR)copy), separators, &context);
        m_EmailMsg.m_sFrom   = (to == 0 || *to == 0) ? g_CrashInfo.m_sEmailTo : to;
    } else
        m_EmailMsg.m_sFrom = g_CrashInfo.GetReport(m_nCurReport).m_sEmailFrom;

int CSmtpClient::SendEmailToRecipient(CString sSmtpServer, CEmailMessage* msg, 
AssyncNotification* scn)
{
    int status = 1;

    strconv_t strconv;  

    struct addrinfo *result = NULL;
    struct addrinfo *ptr = NULL;
    struct addrinfo hints;
    //Vojtech: Lines of the "To:" and "Cc:" lines, that will become part of the e-mail header.
    CString sBodyTo;

    int iResult = -1;  
    CString sPostServer;
    CString sServiceName;
    sServiceName.Format(_T("%d"), 
        m_sProxyServer.IsEmpty()?msg->m_nRecipientPort:m_nProxyPort);  
    SOCKET sock = INVALID_SOCKET;
    CString sMsg, str;
    std::set<CString>::iterator it;
    CString sStatusMsg;

    // Prepare message text
    CString sMessageText = msg->m_sText;
    sMessageText.Replace(_T("\n"),_T("\r\n"));
    sMessageText.Replace(_T("\r\n.\r\n"), _T("\r\n*\r\n"));
    LPCWSTR lpwszMessageText = strconv.t2w(sMessageText.GetBuffer(0));
    std::string sUTF8Text = UTF16toUTF8(lpwszMessageText);

    // Check that all attachments exist
    for(it=msg->m_aAttachments.begin(); it!=msg->m_aAttachments.end(); it++)
    {
        if(CheckAttachmentOK(*it)!=0)
        {
            sStatusMsg.Format(_T("Attachment not found: %s"), *it);
            scn->SetProgress(sStatusMsg, 1);
            return 2; // critical error
        }
    }

    sStatusMsg.Format(_T("Getting address info of %s port %s"), sSmtpServer, CString(sServiceName));
    scn->SetProgress(sStatusMsg, 1);

    int res = SOCKET_ERROR;
    char buf[1024]="";
    std::string sEncodedFileData;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;  

    LPCSTR lpszSmtpServer = strconv.t2a(sSmtpServer);
    LPCSTR lpszServiceName = strconv.t2a(sServiceName);
    iResult = getaddrinfo(lpszSmtpServer, lpszServiceName, &hints, &result);
    if(iResult!=0)
        goto exit;

    for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) 
    {
        if(scn->IsCancelled()) {status = 2; goto exit;}

        sStatusMsg.Format(_T("Creating socket"));
        scn->SetProgress(sStatusMsg, 1);

        sock = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
        if(sock==INVALID_SOCKET)
        {
            scn->SetProgress(_T("Socket creation failed."), 1);
            goto exit;
        }

        sStatusMsg.Format(_T("Connecting to SMTP server %s port %s"), sSmtpServer, CString(sServiceName));
        scn->SetProgress(sStatusMsg, 1);

        res = connect(sock, ptr->ai_addr, (int)ptr->ai_addrlen);
        if(res!=SOCKET_ERROR)
            break;     

        closesocket(sock);
    }

    if(res==SOCKET_ERROR)
        goto exit;

    sStatusMsg.Format(_T("Connected OK."));
    scn->SetProgress(sStatusMsg, 5);

    if(scn->IsCancelled()) {status = 2; goto exit;}

    scn->SetProgress(_T("Waiting for greeting message from SMTP server..."), 1);

    res = recv(sock, buf, 1024, 0);
    if(res==SOCKET_ERROR)
    {
        sStatusMsg.Format(_T("Failed to receive greeting message from SMTP server (recv code %d)."), res);
        scn->SetProgress(sStatusMsg, 1);
        goto exit;
    }

    if(220!=GetMessageCode(buf)) 
    {
        goto exit;
    }

    char response[1024]="";

    sStatusMsg.Format(_T("Sending HELO"));
    scn->SetProgress(sStatusMsg, 1);

    // send HELO
    res=SendMsg(scn, sock, _T("HELO CrashSender\r\n"), response, 1024);
    if(res!=250)
    {
        sStatusMsg = CString(response, 1024);
        scn->SetProgress(sStatusMsg, 0);    
        goto exit;
    }

    sStatusMsg.Format(_T("Sending sender and recipient information"));
    scn->SetProgress(sStatusMsg, 1);

    sMsg.Format(_T("MAIL FROM:<%s>\r\n"), msg->m_sFrom);
    res=SendMsg(scn, sock, sMsg, response, 1024);
    if(res!=250)
    {
        sStatusMsg = CString(response, 1024);
        scn->SetProgress(sStatusMsg, 0);    
        goto exit;
    }

    //Vojtech: Process multiple e-mail recipients.
    // E-mail addresses are separated by comma or semicolon.
    {
        // Force a copy of the string. Simple assignment just references the data of g_CrashInfo.m_sEmailTo. 
        // The copy string will be modified by strtok.
        CString copy         = msg->m_sTo;
        TCHAR   separators[] = _T(";, ");
        TCHAR  *context      = 0;
        TCHAR  *to           = _tcstok_s(const_cast<LPTSTR>((LPCTSTR)copy), separators, &context);
        bool    first        = true;
        while (to != 0) {
            sMsg.Format(first ? _T("To: <%s>\r\n") : _T("Cc: <%s>\r\n"), to);
            sBodyTo += sMsg;
            sMsg.Format(_T("RCPT TO:<%s>\r\n"), to);
            res=SendMsg(scn, sock, sMsg, response, 1024);
            if(res!=250)
            {
                sStatusMsg = CString(response, 1024);
                scn->SetProgress(sStatusMsg, 0);
                goto exit;
            }
            to=_tcstok_s(NULL, separators, &context);
            first=false;
        };
    }

    sStatusMsg.Format(_T("Start sending email data"));
    scn->SetProgress(sStatusMsg, 1);

    // Send DATA
    res=SendMsg(scn, sock, _T("DATA\r\n"), response, 1024);
    if(res!=354)
    {
        sStatusMsg = CString(response, 1024);
        scn->SetProgress(sStatusMsg, 0);    
        goto exit;
    }

    // Get current time
    time_t cur_time;
    time(&cur_time);
    char szDateTime[64] = "";

#if _MSC_VER >= 1400
    struct tm ltimeinfo;
    localtime_s(&ltimeinfo, &cur_time );
    strftime(szDateTime, 64,  "%a, %d %b %Y %H:%M:%S", &ltimeinfo);
#else
    struct tm* ltimeinfo = localtime(&cur_time );
    strftime(szDateTime, 64,  "%a, %d %b %Y %H:%M:%S", ltimeinfo);
#endif

    TIME_ZONE_INFORMATION tzi;
    GetTimeZoneInformation(&tzi);

    int diff_hours = -tzi.Bias/60;
    int diff_mins = abs(tzi.Bias%60);

    str.Format(_T("Date: %s %c%02d%02d\r\n"), strconv.a2t(szDateTime), diff_hours>=0?'+':'-', diff_hours, diff_mins);
    sMsg = str;
    str.Format(_T("From: <%s>\r\n"), msg->m_sFrom);
    sMsg  += str;
    sMsg += sBodyTo;
    str.Format(_T("Subject: %s\r\n"), msg->m_sSubject);
    sMsg += str;
    sMsg += "MIME-Version: 1.0\r\n";
    sMsg += "Content-Type: multipart/mixed; boundary=KkK170891tpbkKk__FV_KKKkkkjjwq\r\n";
    sMsg += "\r\n\r\n";
    res = SendMsg(scn, sock, sMsg);
    if(res!=sMsg.GetLength())
        goto exit;

    /* Message text */

    sStatusMsg.Format(_T("Sending message text"));
    scn->SetProgress(sStatusMsg, 15);

    sMsg =  "--KkK170891tpbkKk__FV_KKKkkkjjwq\r\n";
    sMsg += "Content-Type: text/plain; charset=UTF-8\r\n";
    sMsg += "\r\n";  
    sMsg += sUTF8Text.c_str();
    sMsg += "\r\n";
    res = SendMsg(scn, sock, sMsg);
    if(res!=sMsg.GetLength())
        goto exit;

    sStatusMsg.Format(_T("Sending attachments"));
    scn->SetProgress(sStatusMsg, 1);

    /* Attachments. */
    for(it=msg->m_aAttachments.begin(); it!=msg->m_aAttachments.end(); it++)
    {    
        CString sFileName = *it;
        sFileName.Replace('/', '\\');
        CString sDisplayName = sFileName.Mid(sFileName.ReverseFind('\\')+1);

        // Header
        sMsg =  "\r\n--KkK170891tpbkKk__FV_KKKkkkjjwq\r\n";
        sMsg += "Content-Type: application/octet-stream\r\n";
        sMsg += "Content-Transfer-Encoding: base64\r\n";
        sMsg += "Content-Disposition: attachment; filename=\"";
        sMsg += sDisplayName;
        sMsg += "\"\r\n";
        sMsg += "\r\n";
        res = SendMsg(scn, sock, sMsg);
        if(res!=sMsg.GetLength())
            goto exit;

        // Encode data
        LPBYTE buf = NULL;
        //int buf_len = 0;
        int nEncode=Base64EncodeAttachment(sFileName, sEncodedFileData);
        if(nEncode!=0)
        {
            sStatusMsg.Format(_T("Error BASE64-encoding attachment %s"), sFileName);
            scn->SetProgress(sStatusMsg, 1);
            goto exit;
        }

        // Send encoded data
        sMsg = sEncodedFileData.c_str();        
        res = SendMsg(scn, sock, sMsg);
        if(res!=sMsg.GetLength())
            goto exit;

        delete [] buf;
    }

    sMsg =  "\r\n--KkK170891tpbkKk__FV_KKKkkkjjwq--";
    res = SendMsg(scn, sock, sMsg);
    if(res!=sMsg.GetLength())
        goto exit;

    // End of message marker
    if(250!=SendMsg(scn, sock, _T("\r\n.\r\n"), response, 1024))
    {
        sStatusMsg = CString(response, 1024);
        scn->SetProgress(sStatusMsg, 0);    
        goto exit;
    }

    // quit
    if(221!=SendMsg(scn, sock, _T("QUIT\r\n"), response, 1024))
    {
        sStatusMsg = CString(response, 1024);
        scn->SetProgress(sStatusMsg, 0);    
        goto exit;
    }

    // OK.
    status = 0;

exit:

    if(scn->IsCancelled()) 
        status = 2;

    sStatusMsg.Format(_T("Finished with error code %d"), status);
    scn->SetProgress(sStatusMsg, 100, false);

    // Clean up
    closesocket(sock);
    freeaddrinfo(result);  
    return status;
}

Original issue reported on code.google.com by bubn...@gmail.com on 3 Jan 2012 at 11:44

GoogleCodeExporter commented 9 years ago

Original comment by zexspect...@gmail.com on 5 Jan 2012 at 8:26

GoogleCodeExporter commented 9 years ago
This issue was closed by revision r1339.

Original comment by zexspect...@gmail.com on 25 Aug 2012 at 7:41