其实邮件解码很简单,很多人愿意自己解码,但是为什么不用现成的工具呢?给大家推荐一款十分方便的邮件解码源码——Gmime
Gmime从2002年开始做了,到现在已经有了很多版本支持大部分的smtp和HTTP,但是也有他不完美的地方,比方说charset解码的流数据大小不准确,比如说最新版本gmime2.6.20编译编不过去(这个问题确实让我很恶心,我几乎把gmime所有依赖的glib等库全编了一遍,但是还是不好用),我用的版本是gmime-2.6.7。在我们的产品上已经用了一段时间,还是不错的,不过对于Http下使用还得自己改下源代码。
下面进入正题说下GMIME如何用,GMIME有个文档,很详细的介绍了函数功能:gmime-2.6.7\gmime-2.6.7\docs\reference\html
因为最开始我没看到,所以研究了半天没搞明白所给的实例中代码如何用,使用者请仔细看下他提供的函数接口,并比对着gmime-2.6.7\gmime-2.6.7\examples下的例子仔细看下,打开工程可以在gmime-2.6.7\gmime-2.6.7\build\vs2008 用vs2008打开并全局查找比对例子如何用,当然不是所有函数接口都有案例的。
下面给几个比较关键接口实现,比较简单网上有基本不需要自己改(请仔细阅读gmime提供的文档和案例)
获取body部分,由于获取body时要使用g_mime_stream_read 而这个函数采用的流读取,只能对流中的内容读取一次,每次读取的内容仅限4096(修改源码也没用,就算修改了也没效果,亲身测试),所以要采取循环读取的方法。
以下是这部分的关键代码:
std::string CMailParser::GetBody(){
std::string data;
int iRe=0,ichar=0,iencode =0;
GMimePart * mime_part = (GMimePart*)g_mime_message_get_body_ex(this->m_MimeMessage);
if( NULL==mime_part )
{
return "error" ;
}
GMimeContentEncoding ReEncoding = g_mime_part_get_content_encoding(mime_part);
char* szCharset = (char*)g_mime_object_get_content_type_parameter((GMimeObject*)mime_part,"charset");
GMimeDataWrapper *dataWrap = g_mime_part_get_content_object(mime_part);
GMimeStream * gmime_stream= g_mime_data_wrapper_get_stream(dataWrap);
//encoding转码
GMimeFilter * pAttFilter = g_mime_filter_basic_new(ReEncoding,FALSE);
GMimeStream* pFilterStream = g_mime_stream_filter_new(gmime_stream);
iencode = g_mime_stream_filter_add (GMIME_STREAM_FILTER (pFilterStream), pAttFilter);
//charset转码
gint64 size = g_mime_stream_length(pFilterStream);
g_object_unref ( pAttFilter );
if (pFilterStream && size >0)
{
char* szTemp=NULL;
szTemp = (char*)malloc( size * 3 );
char* current =szTemp;
memset(szTemp,0,size*3);
while ( (current-szTemp) < size)
{
iRe = g_mime_stream_read(pFilterStream,current,size );
int iLength= strlen(szTemp);
if(iRe>0)
current += iRe;
else
break;
}
size = current-szTemp;
if (size >0)
{
if(szCharset != NULL)
{
char * pUtf8Buff = NULL;
pUtf8Buff = GetUtf8String((const byte*)szTemp, size );
data.assign(pUtf8Buff,strlen(pUtf8Buff));
if( pUtf8Buff !=NULL )
{
free( pUtf8Buff );
}
if(szTemp != NULL)
{
free(szTemp);
szTemp = NULL;
}
}
else
{
data.assign(szTemp,size);
if(szTemp != NULL)
{
free(szTemp);
szTemp = NULL;
}
}
}
g_mime_stream_filter_remove((GMimeStreamFilter*)pFilterStream,ichar);
g_mime_stream_filter_remove((GMimeStreamFilter*)pFilterStream,iencode);
g_object_unref (pFilterStream);
//g_object_unref (gmime_stream);
}
else
{
g_mime_stream_filter_remove((GMimeStreamFilter*)pFilterStream,ichar);
g_mime_stream_filter_remove((GMimeStreamFilter*)pFilterStream,iencode);
g_object_unref (pFilterStream);
//g_object_unref (gmime_stream);
return "error";
}
return data;
}
gmime还有个很关键的点就是计数器,很多东西GMIME会用GLIB生成在内部进行加1,出来的时候要unref一下,这个得自己看代码查,如果unref次数多于ref错误会导致警报,如果ref次数多于unref次数可能导致内存泄露,这就得使用者自己查了~~
(天融信dlp研发工程师)