使用Indy9的IdSMTP/IdMessage发送邮件主题被截断的问题

时间:2022-06-01 19:00:47
【现象】Indy版本9.0.13,使用IdMessage封装邮件,用IdSMTP发送。当Subject长度大于45个字节时,收到邮件主题后面的被截断,并且以未解码的形式放到了邮件内容中。各位大侠有谁碰到类似问题?怎么处理?

【初步分析】确定是IdMessage生成邮件时截断了主题。用IdMessage->SaveToFile()获得的邮件如下:
////////////////////////////////////////////////////////////////
From: shen.js@163.com
Subject:
 =?GB2312?B?suLK1LLiytQxsuLK1LLiytQysuLK1LLiytQzsuLK1LLiytQ0suLK1LLiytQ1?=

 =?GB2312?B?suLK1LLiytQ2?=
To: shen.js@163.com
Content-Type: text/html
Date: Tue, 27 Apr 2004 17:35:38 +0800

test

////////////////////////////////////////////////////////////////
subject后面的第一行为主题的未截断部分,第二行为空行,第三行为主题的被截断部分,并且在收到的邮件中以乱码(就是上面的 =?GB2312?B?suLK1LLiytQ2?=)形式被放到了内容中,第四行的To: shen.js@163.com等也被放到了邮件内容中。

如果主题字节数少于45byte,则正常!

18 个解决方案

#1


的确是这样,估计是主题不允许太长。
我想,你应当给indy公司去个email.

#2


不清楚

#3


郁闷......

正在做的一个税务群发软件,主题一般都很长,要求主题不能被截断

哪位大侠知道除了indy9,其他indy版本是否也这么随意地截断主题啊?

或者有没有其他发邮件的组件好用?

//bow

#4


up

#5


今天又测试了一下,发现问题好像出在这里:IdMessage一定把Subject按MiME标准Encode,而接收邮件的服务器不能按MIME标准正确地Decode。

1)按rfc2047的规定,邮件主题Encode后的格式
Subject:
 =?GB2312?B?suLK1LLiytQxsuLK1LLiytQysuLK1LLiytQzsuLK1LLiytQ0suLK1LLiytQ1?=
 =?GB2312?B?suLK1LLiytQ2?=
是正确的。

2)如果在任何邮件服务器上手工自己给自己发邮件(不用自己的程序),不管主题多长都没问题,但是有一个区别,这种情况下收到的主题并没有被Encode!!!查看信件原文时可知。

因此,现在的问题变为:
使用IdMessage时,怎样能够不加密主题?试了NoEncode=true(缺省false),根本不起作用!

#6


请哪位大侠指点一下啊。使用IdMessage遇到的问题重新描述一下:

(1)当邮件主题是有中文时会对主题编码;

(2)当主题较长时,会像上面那样分割编码;

(3)而现在很多邮件服务器或邮件程序只会对主题分割后的第1段解码并作为邮件主题,其他部分放到邮件内容中,并且不解码

(4)使用任何邮箱(比如网易、新浪等)手工互相发送邮件,主题可以任意长度,因为它们发送时不对主题进行Base64编码

因此
(1)用IdMessage怎样才能不对主题进行编码?
(2)或用什么其他组件能完成这种功能?

再送100分求救!

//bow

#7


base64编码规则行长75,长了要截断换行

#8


老大,既然base64规定行长75,那为什么那些邮件服务器和邮件程序,包括foxmail,都只对主题的第1行解码,其他行就不理了呢?郁闷中.......

#9


用TIdMessage->SaveToFile()和LoadFromFile()测试发现:

主题第二行不解码的原因是:TIdMessage用Base64编码生成主题时,回车换行符'\0d\0a'重复了,即'\0d\0a\0d\0a'。只要去掉1个就能够正常显示了。

现在的问题是:TIdMessage好像没有提供方法去直接更改它生成的Email!只能读不能写!各位老大有什么办法没有啊?

#10


我也遇到同样问题
有些网站服务器并不对主题加密,比如tom,com

#11


我正想了解如何发送邮件,可否发送代码到我的邮箱:lzfhope@163.com

#12


我也想要一份
maplechen@163.com

#13


用IdSMTP和IdMessage组件发邮件的方法就是参考雨中漫步的经典范例^_^精华区有的
整个程序较大,就不发过来了。

#14


呵呵,把我的解决方法帖出来,虽然说有点土,但是管用。能不能把点数加给自己啊^_^

【问题】用Indy9的IdSMTP和IdMessage组件发送邮件,当邮件主题包含汉字并且长度较长时,会发生主题截断的现象,并且被截断的未解码的主题会放到邮件内容中

【原因】Indy的IdMessage组件在生成待发送的邮件时,主题中有汉字时会按RFC2045~2047的base64编码规范对主题进行编码,base64要求编码后每行长度不能超过75(76)个字节;所以当主题过长时要分行。问题是IdMessage编码时,用了2对分行符<CR><LF><CR><LF>,而RFC规定<CR><LF><CR><LF>表示邮件中一节的结束,所以接收邮件的程序只会对第1行解码,其余的理解为邮件内容了。

【解决办法】因为IdMessage没有提供直接修改编码后的邮件的方法,所以用IdMessage的SaveFromStream()和LoadFromStream()以及TStringList和TMemoryStream来去掉Email的Header部分多余的<CR><LF>。
感觉此方法笨了些,贴出来抛砖引语,看还有什么其他简单的办法。当然直接修改Indy的源代码最好,但俺不会delphi。真不方便,sigh~

//---------------------------------------------------------------------------
int __fastcall SMTP_SendMail(TIdSMTP* Smtp,TIdMessage *MsgSend,const TMailInfo& MI,String& sMsg)
{
//发送邮件
//注:发送的SMTP属性通过SMTP_Setup()函数设置了
//参数:
//in:
//Smtp:使用的IdSmtp控件
//MsgSend:使用的IdMessage控件
//MI:要发送的邮件信息
// OUT:
// Msg 返回错误信息
//返回值
//0: 成功发送
//-1:发送失败,参见Msg信息
//-2:连接失败,参见Msg信息

    String s,sFile;
    TStringList * slst = new TStringList;
//清除,否则包含有上一条的信息
    MsgSend->Clear();
//是否要求收到回执
    if (MI.bReturnReciept)
    {//{We set the recipient to the From E-Mail address }
        MsgSend->ReceiptRecipient->Text = MsgSend->From->Text;
    }
    else
    {// {indicate that there is no receipt recipiant}
        MsgSend->ReceiptRecipient->Text = "";
    }
//发件人
    MsgSend->From->Text = MI.sFrom;
//收件人
    MsgSend->Recipients->EMailAddresses = MI.sTo; //{ To: header }
//邮件主题
    MsgSend->Subject = MI.sSubject; //{ Subject: header }
//抄送
    MsgSend->CCList->EMailAddresses = MI.sCc;// {CC}
//暗送
    MsgSend->BccList->EMailAddresses = MI.sBcc; //{BCC}
//邮件优先级别
    MsgSend->Priority = TIdMessagePriority(MI.tPriority);
//邮件内容类型
    MsgSend->ContentType = MI.sContType;
    MsgSend->Encoding = meUU;

//下面的一段代码解决这个bug
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////处理Header,去掉多余的CRLF

    TMemoryStream* pms = new TMemoryStream();
    MsgSend->SaveToStream(pms);
    slst->Clear();
    pms->Position = 0;
    slst->LoadFromStream(pms);
    for(int i=0;i<slst->Count;i++)
    {
        if(slst->Strings[i]=="")
        {
            slst->Delete(i);
        }
    }
    slst->Add(""); //IdMessage的LoadFromStream()规定Stream必须以//<CR><LF><CR><LF>结尾
    pms->Size = 0;
    pms->Position = 0;
    slst->SaveToStream(pms);
    pms->Position = 0;
    MsgSend->LoadFromStream(pms,true);
    delete pms;
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////

//邮件内容
    MsgSend->Body->Text = MI.sBody;
//附件
    if(MI.sAttachList.Length() > 0)
    {
        slst->Clear();
        slst->CommaText = MI.sAttachList;
        for(int i=0;i<slst->Count;i++)
        {
            MsgSend->MessageParts->Add();
            new TIdAttachment(MsgSend->MessageParts,slst->Strings[i]);//由第1句MsgSend->Clear()回收
        }
    }

    delete slst;
    sFile = s + "_2.eml";
//    MsgSend->SaveToFile(sFile);

//连接SMTP服务器
    if(!Smtp->Connected())
    {
        try
        {
            Smtp->Connect();
        }
        catch(Exception &e)
        {
            s = AnsiReplaceStr(e.Message,"\r\n","");
            sMsg = "Try Send: 连接SMTP服务器\'";
            sMsg += Smtp->Host;
            sMsg += "\'失败![错误信息]:";
            sMsg += s;
            return -2;
        }
    }
    if(Smtp->ClosedGracefully)
    {
        try
        {
            Smtp->Connect();
        }
        catch(Exception &e)
        {
            s = AnsiReplaceStr(e.Message,"\r\n","");
            sMsg = "Try Send: 连接SMTP服务器\'";
            sMsg += Smtp->Host;
            sMsg += "\'失败![错误信息]:";
            sMsg += s;
            return -2;
        }
    }
//发送邮件
    if(Smtp->Connected())
    {
        try
        {
            Smtp->Send(MsgSend);
        }
        catch(Exception &e)
        {
            s = AnsiReplaceStr(e.Message,"\r\n","");
            sMsg = "Try Send: 发送邮件失败!";
            sMsg += "From:";
            sMsg += MI.sFrom;
            sMsg += ",To:";
            sMsg += MI.sTo;
            sMsg += ",Suject=";
            sMsg += MI.sSubject;
            sMsg += ",Body=";
            sMsg += MI.sBody.SubString(1,30);
            sMsg += "......,";
            sMsg += " [错误信息]:";
            sMsg += s;
            try
            {
                Smtp->CheckForGracefulDisconnect(true);
            }
            catch(...)
            {
                return -2;
            }
            try
            {
                Smtp->CheckForDisconnect(true,false);
            }
            catch(...)
            {
                return -2;
            }
            return -1;
        }
    }
    else
    {
        sMsg = "Try Send: 连接SMTP服务器\'";
        sMsg += Smtp->Host;
        sMsg += "\'失败![错误信息]:连接断开!";
        return -2;
    }
//    if(m_Para.bDebug)
    {
        sMsg = "成功发送邮件 From:";
        sMsg += MI.sFrom;
        sMsg += ",To:";
        sMsg += MI.sTo;
        sMsg += ",Suject=";
        sMsg += MI.sSubject;
        sMsg += ",Body=";
        sMsg += MI.sBody.SubString(1,100);
        sMsg += "......";
    }
    return 0;
}

#15


呵呵我用的是BCB.感觉还是用delphi好啊,可以直接改VCL,sigh

#16


我也在写收发邮件,如果有人愿意交流请加我的QQ:84560912或MSN:sunseave@hotmail.com

#17


我也遇见类似的问题了,不过不是发生邮件发送过程中,而是接受过程中。
从某些服务器接收的邮件标题无法还原为汉字,直接把base64编码格式显示出来了,如下:
=?GB2312?B?suLK1LLiytQxsuLK1LLiytQysuLK1LLiytQzsuLK1LLiytQ0suLK1LLiytQ1?=
看来也是标题过长的原因?
现在仍然束手无策

#18


如果像楼上某位搂主所言,那倒不如直接空格替换中间的某段字符。
如:
Subject:
 =?GB2312?B?suLK1LLiytQxsuLK1LLiytQysuLK1LLiytQzsuLK1LLiytQ0suLK1LLiytQ1?=

 =?GB2312?B?suLK1LLiytQ2?=
用空格替换如下字符串,就可以了
 ?=

 =?GB2312?B?
我试过的,替换后,有一个空格同样能正确解码。

#1


的确是这样,估计是主题不允许太长。
我想,你应当给indy公司去个email.

#2


不清楚

#3


郁闷......

正在做的一个税务群发软件,主题一般都很长,要求主题不能被截断

哪位大侠知道除了indy9,其他indy版本是否也这么随意地截断主题啊?

或者有没有其他发邮件的组件好用?

//bow

#4


up

#5


今天又测试了一下,发现问题好像出在这里:IdMessage一定把Subject按MiME标准Encode,而接收邮件的服务器不能按MIME标准正确地Decode。

1)按rfc2047的规定,邮件主题Encode后的格式
Subject:
 =?GB2312?B?suLK1LLiytQxsuLK1LLiytQysuLK1LLiytQzsuLK1LLiytQ0suLK1LLiytQ1?=
 =?GB2312?B?suLK1LLiytQ2?=
是正确的。

2)如果在任何邮件服务器上手工自己给自己发邮件(不用自己的程序),不管主题多长都没问题,但是有一个区别,这种情况下收到的主题并没有被Encode!!!查看信件原文时可知。

因此,现在的问题变为:
使用IdMessage时,怎样能够不加密主题?试了NoEncode=true(缺省false),根本不起作用!

#6


请哪位大侠指点一下啊。使用IdMessage遇到的问题重新描述一下:

(1)当邮件主题是有中文时会对主题编码;

(2)当主题较长时,会像上面那样分割编码;

(3)而现在很多邮件服务器或邮件程序只会对主题分割后的第1段解码并作为邮件主题,其他部分放到邮件内容中,并且不解码

(4)使用任何邮箱(比如网易、新浪等)手工互相发送邮件,主题可以任意长度,因为它们发送时不对主题进行Base64编码

因此
(1)用IdMessage怎样才能不对主题进行编码?
(2)或用什么其他组件能完成这种功能?

再送100分求救!

//bow

#7


base64编码规则行长75,长了要截断换行

#8


老大,既然base64规定行长75,那为什么那些邮件服务器和邮件程序,包括foxmail,都只对主题的第1行解码,其他行就不理了呢?郁闷中.......

#9


用TIdMessage->SaveToFile()和LoadFromFile()测试发现:

主题第二行不解码的原因是:TIdMessage用Base64编码生成主题时,回车换行符'\0d\0a'重复了,即'\0d\0a\0d\0a'。只要去掉1个就能够正常显示了。

现在的问题是:TIdMessage好像没有提供方法去直接更改它生成的Email!只能读不能写!各位老大有什么办法没有啊?

#10


我也遇到同样问题
有些网站服务器并不对主题加密,比如tom,com

#11


我正想了解如何发送邮件,可否发送代码到我的邮箱:lzfhope@163.com

#12


我也想要一份
maplechen@163.com

#13


用IdSMTP和IdMessage组件发邮件的方法就是参考雨中漫步的经典范例^_^精华区有的
整个程序较大,就不发过来了。

#14


呵呵,把我的解决方法帖出来,虽然说有点土,但是管用。能不能把点数加给自己啊^_^

【问题】用Indy9的IdSMTP和IdMessage组件发送邮件,当邮件主题包含汉字并且长度较长时,会发生主题截断的现象,并且被截断的未解码的主题会放到邮件内容中

【原因】Indy的IdMessage组件在生成待发送的邮件时,主题中有汉字时会按RFC2045~2047的base64编码规范对主题进行编码,base64要求编码后每行长度不能超过75(76)个字节;所以当主题过长时要分行。问题是IdMessage编码时,用了2对分行符<CR><LF><CR><LF>,而RFC规定<CR><LF><CR><LF>表示邮件中一节的结束,所以接收邮件的程序只会对第1行解码,其余的理解为邮件内容了。

【解决办法】因为IdMessage没有提供直接修改编码后的邮件的方法,所以用IdMessage的SaveFromStream()和LoadFromStream()以及TStringList和TMemoryStream来去掉Email的Header部分多余的<CR><LF>。
感觉此方法笨了些,贴出来抛砖引语,看还有什么其他简单的办法。当然直接修改Indy的源代码最好,但俺不会delphi。真不方便,sigh~

//---------------------------------------------------------------------------
int __fastcall SMTP_SendMail(TIdSMTP* Smtp,TIdMessage *MsgSend,const TMailInfo& MI,String& sMsg)
{
//发送邮件
//注:发送的SMTP属性通过SMTP_Setup()函数设置了
//参数:
//in:
//Smtp:使用的IdSmtp控件
//MsgSend:使用的IdMessage控件
//MI:要发送的邮件信息
// OUT:
// Msg 返回错误信息
//返回值
//0: 成功发送
//-1:发送失败,参见Msg信息
//-2:连接失败,参见Msg信息

    String s,sFile;
    TStringList * slst = new TStringList;
//清除,否则包含有上一条的信息
    MsgSend->Clear();
//是否要求收到回执
    if (MI.bReturnReciept)
    {//{We set the recipient to the From E-Mail address }
        MsgSend->ReceiptRecipient->Text = MsgSend->From->Text;
    }
    else
    {// {indicate that there is no receipt recipiant}
        MsgSend->ReceiptRecipient->Text = "";
    }
//发件人
    MsgSend->From->Text = MI.sFrom;
//收件人
    MsgSend->Recipients->EMailAddresses = MI.sTo; //{ To: header }
//邮件主题
    MsgSend->Subject = MI.sSubject; //{ Subject: header }
//抄送
    MsgSend->CCList->EMailAddresses = MI.sCc;// {CC}
//暗送
    MsgSend->BccList->EMailAddresses = MI.sBcc; //{BCC}
//邮件优先级别
    MsgSend->Priority = TIdMessagePriority(MI.tPriority);
//邮件内容类型
    MsgSend->ContentType = MI.sContType;
    MsgSend->Encoding = meUU;

//下面的一段代码解决这个bug
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////处理Header,去掉多余的CRLF

    TMemoryStream* pms = new TMemoryStream();
    MsgSend->SaveToStream(pms);
    slst->Clear();
    pms->Position = 0;
    slst->LoadFromStream(pms);
    for(int i=0;i<slst->Count;i++)
    {
        if(slst->Strings[i]=="")
        {
            slst->Delete(i);
        }
    }
    slst->Add(""); //IdMessage的LoadFromStream()规定Stream必须以//<CR><LF><CR><LF>结尾
    pms->Size = 0;
    pms->Position = 0;
    slst->SaveToStream(pms);
    pms->Position = 0;
    MsgSend->LoadFromStream(pms,true);
    delete pms;
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////

//邮件内容
    MsgSend->Body->Text = MI.sBody;
//附件
    if(MI.sAttachList.Length() > 0)
    {
        slst->Clear();
        slst->CommaText = MI.sAttachList;
        for(int i=0;i<slst->Count;i++)
        {
            MsgSend->MessageParts->Add();
            new TIdAttachment(MsgSend->MessageParts,slst->Strings[i]);//由第1句MsgSend->Clear()回收
        }
    }

    delete slst;
    sFile = s + "_2.eml";
//    MsgSend->SaveToFile(sFile);

//连接SMTP服务器
    if(!Smtp->Connected())
    {
        try
        {
            Smtp->Connect();
        }
        catch(Exception &e)
        {
            s = AnsiReplaceStr(e.Message,"\r\n","");
            sMsg = "Try Send: 连接SMTP服务器\'";
            sMsg += Smtp->Host;
            sMsg += "\'失败![错误信息]:";
            sMsg += s;
            return -2;
        }
    }
    if(Smtp->ClosedGracefully)
    {
        try
        {
            Smtp->Connect();
        }
        catch(Exception &e)
        {
            s = AnsiReplaceStr(e.Message,"\r\n","");
            sMsg = "Try Send: 连接SMTP服务器\'";
            sMsg += Smtp->Host;
            sMsg += "\'失败![错误信息]:";
            sMsg += s;
            return -2;
        }
    }
//发送邮件
    if(Smtp->Connected())
    {
        try
        {
            Smtp->Send(MsgSend);
        }
        catch(Exception &e)
        {
            s = AnsiReplaceStr(e.Message,"\r\n","");
            sMsg = "Try Send: 发送邮件失败!";
            sMsg += "From:";
            sMsg += MI.sFrom;
            sMsg += ",To:";
            sMsg += MI.sTo;
            sMsg += ",Suject=";
            sMsg += MI.sSubject;
            sMsg += ",Body=";
            sMsg += MI.sBody.SubString(1,30);
            sMsg += "......,";
            sMsg += " [错误信息]:";
            sMsg += s;
            try
            {
                Smtp->CheckForGracefulDisconnect(true);
            }
            catch(...)
            {
                return -2;
            }
            try
            {
                Smtp->CheckForDisconnect(true,false);
            }
            catch(...)
            {
                return -2;
            }
            return -1;
        }
    }
    else
    {
        sMsg = "Try Send: 连接SMTP服务器\'";
        sMsg += Smtp->Host;
        sMsg += "\'失败![错误信息]:连接断开!";
        return -2;
    }
//    if(m_Para.bDebug)
    {
        sMsg = "成功发送邮件 From:";
        sMsg += MI.sFrom;
        sMsg += ",To:";
        sMsg += MI.sTo;
        sMsg += ",Suject=";
        sMsg += MI.sSubject;
        sMsg += ",Body=";
        sMsg += MI.sBody.SubString(1,100);
        sMsg += "......";
    }
    return 0;
}

#15


呵呵我用的是BCB.感觉还是用delphi好啊,可以直接改VCL,sigh

#16


我也在写收发邮件,如果有人愿意交流请加我的QQ:84560912或MSN:sunseave@hotmail.com

#17


我也遇见类似的问题了,不过不是发生邮件发送过程中,而是接受过程中。
从某些服务器接收的邮件标题无法还原为汉字,直接把base64编码格式显示出来了,如下:
=?GB2312?B?suLK1LLiytQxsuLK1LLiytQysuLK1LLiytQzsuLK1LLiytQ0suLK1LLiytQ1?=
看来也是标题过长的原因?
现在仍然束手无策

#18


如果像楼上某位搂主所言,那倒不如直接空格替换中间的某段字符。
如:
Subject:
 =?GB2312?B?suLK1LLiytQxsuLK1LLiytQysuLK1LLiytQzsuLK1LLiytQ0suLK1LLiytQ1?=

 =?GB2312?B?suLK1LLiytQ2?=
用空格替换如下字符串,就可以了
 ?=

 =?GB2312?B?
我试过的,替换后,有一个空格同样能正确解码。