www.pudn.com > pop3.rar > MimeMessage.c
/****************************************************************** // MimeMessage.c // Function: 为CMailAdapter提供接口 // Author : wan ning // Date : 2006/11/17 // history : *******************************************************************/ #include#include #include #include #include #include "MimeMessage.h" #include "mimeb64.h" enum RecipClass { TO = 1, CC, BCC }; #define MAX_BUFFER_SIZE 1024 #define MIN_BUFFER_SIZE 128 #define CONTENT_TYPE_STRING "text/plain" #define CONTENT_TYPE_JPG "image/jpeg" #define CONTENT_TYPE_DEF "text/html" #define CONTENT_TYPE_MULTIPART "multipart/mixed" #define CONTENT_TYPE_OCTET_STREAM "application/octet-stream" #define CHAR_SET "gb2312" #define TRANSFER_ENCODING_7BIT "7bit" #define TRANSFER_ENCODING_B64 "base64" #define MAIL_PART_BOUNDARY "_.__.__TOL__Mailer__Part_Boundary_" #define ERROR_OPEN_FILE_FAIL "Can not open file" #define ERROR_MALLOC_FAILE "malloc failed" #define ERROR_EBADF "invalid file-handle" //全局变量,用于构造boundary int BoundaryLevel = 0; //-------------------------------------------------------------- // 功能:转换邮件为MIME编码字符串 // 参数: // IN LPVOID pMail 邮件结构指针 // OUT char* strMail MIME编码后邮件字符串指针 // OUT char* strError 错误信息提示 // 返回值:成功为TRUE,失败为FALSE // 备注:strMail要足够大,这里不检查溢出 //-------------------------------------------------------------- BOOL getMailString( LPVOID pMail, char *strMail, char *strError ) { BOOL bRet; assert( pMail!=NULL && strMail!=NULL && strError!=NULL ); // 初始化字符串strMail startHeader( strMail ); // 将邮件头部信息追加到字符串strMail prepareHeader( pMail, strMail ); /* endHeader( strMail ); */ // 将邮件体追加到字符串strMail bRet = prepareBody( pMail, strMail, strError ); return bRet; } //-------------------------------------------------------------- // 功能:分配MIME编码字符串所需的内存空间 // 参数: // IN LPVOID pMail 邮件结构指针 // OUT char* strError 错误信息提示 // 返回值:NULL 失败 ,其它,分配的内存空间的起始地址 //-------------------------------------------------------------- void * mallocMime( LPVOID pMail, char *strError ) { long lmimeStrLen = getMimeStrLen(pMail, strError); if ( lmimeStrLen != -1L ) { return malloc( lmimeStrLen ); } else { return NULL; } } //-------------------------------------------------------------- // 功能:释放MIME编码字符串所需的内存空间 // 参数: // IN char* strMail MIME编码后邮件字符串指针 // 返回值:空 //-------------------------------------------------------------- void freeMime( void *strMail ) { free( strMail ); } //-------------------------------------------------------------- // 功能:计算存放MIME编码字符串所需的大小,单位Byte // 参数: // IN LPVOID pMail 邮件结构指针 // OUT char* strError 错误信息提示 // 返回值:-1 失败,其它 存放MIME编码字符串所需的大小 // 备注:只要有一个附件打开失败都将返回-1 //-------------------------------------------------------------- long getMimeStrLen( LPVOID pMail, char *strError ) { #define HEADER_SIZE 1024 #define BODY_TAG 256 LPNOTE_DETAIL pMailBody = (LPNOTE_DETAIL)pMail; long ltotalSize = 0L; long lfileSize; long lmimeSize = 0L; int i; // 邮件首部大小设置为HEADER_SIZE ltotalSize += HEADER_SIZE; assert(pMail!=NULL && strError!=NULL); // 计算发/收信人地址所需的空间 ltotalSize += strlen( pMailBody->pcDisplaySender ); ltotalSize += strlen( pMailBody->pcDisplayTo ); ltotalSize += strlen( pMailBody->pcDisplayCc ); ltotalSize += strlen( pMailBody->pcDisplayBcc ); // 计算正文文本所需空间大小 /* // 正文文本以文件形式保存的情况 // 路径名为空时,表示没有邮件正文,不计算空间。 if ( pMailBody->pcBodyFilePathName != NULL ) { lfileSize = getByteFileSize( pMailBody->pcBodyFilePathName ); if ( -1L == lfileSize ) { sprintf( strError, "%s: %s", ERROR_OPEN_FILE_FAIL, pMailBody->pcBodyFilePathName ); return -1L; } else { lmimeSize += lfileSize; } } */ // 正文文本在内存中 if ( pMailBody->pcBodyContent[0] != '\0' ) { lmimeSize += strlen( pMailBody->pcBodyContent ); } // 计算标题所需空间大小 if ( pMailBody->pcSubject[0] != '\0' ) { lmimeSize += strlen( pMailBody->pcSubject ); } // 计算附件所需空间大小 if ( pMailBody->bHasAttach ) { for( i = 0; i < pMailBody->iAttachCount; i++ ) { // 附件的标记信息大小 ltotalSize += BODY_TAG; lfileSize = getByteFileSize( (pMailBody->lpAttach[i]).lpszPathName ); if ( -1L == lfileSize ) { sprintf( strError, "%s %d %s: %s", __FILE__, __LINE__, ERROR_OPEN_FILE_FAIL, (pMailBody->lpAttach[i]).lpszPathName ); return -1L; } else { lmimeSize += lfileSize; } } } // Base64编码后长度是原来的4/3 lmimeSize = ( lmimeSize*4 )/3+1; // 编码后每行的长度不能超过76,所以要加上每行回车换行所需的空间 ltotalSize += lmimeSize + (lmimeSize/BASE64_LINE_SIZE*2); return ltotalSize; } //-------------------------------------------------------------- // 功能:初始化邮件字符串为空串 // 参数: // IN/OUT char* strMail 邮件字符串指针 // 返回值:空 //-------------------------------------------------------------- static void startHeader( char *strMail ) { strMail[0] = '\0'; } //-------------------------------------------------------------- // 功能:邮件头部结束符 // 参数: // IN/OUT char* strMail 邮件字符串指针 // 返回值:空 //-------------------------------------------------------------- static void endHeader( char *strMail ) { strcat( strMail, "\r\n" ); } //-------------------------------------------------------------- // 功能:追加一个新行到邮件字符串 // 参数: // IN const char* plszNewLine // IN/OUT char* plHeader // 返回值:空 //-------------------------------------------------------------- static void addHeaderLine( char *plHeader, const char *plszNewLine ) { strcat( plHeader, plszNewLine ); strcat( plHeader, "\r\n" ); } //-------------------------------------------------------------- // 功能:处理邮件头部信息到字符串 // 参数: // IN LPVOID pMail 邮件结构指针 // OUT char* strMail MIME编码后邮件字符串指针 // 返回值:空 //-------------------------------------------------------------- static void prepareHeader( LPVOID pMail, char *strMail ) { LPNOTE_DETAIL pMailHeader = ( LPNOTE_DETAIL )pMail; char strImportance[sizeof(long)+1]; // 临时缓冲区 char sTemp[MAX_BUFFER_SIZE]={'\0'}; char sTemp2[MAX_BUFFER_SIZE]={'\0'}; /* char sTo[MAX_BUFFER_SIZE]={'\0'}; char sCc[MAX_BUFFER_SIZE]={'\0'}; char sBcc[MAX_BUFFER_SIZE]={'\0'}; */ struct tm *ptrTime; time_t lt; // 用于计算时区 TIME_ZONE_INFORMATION timezoneinfo; LONG lTimeDifferential = 0; LONG lHours = 0; LONG lMinutes = 0; unsigned long dwReturn; // From, To, Cc, Bcc if ( pMailHeader->pcDisplaySender[0] != '\0' ) { sprintf( sTemp, "From: %s", pMailHeader->pcDisplaySender ); addHeaderLine( strMail, sTemp ); } if ( pMailHeader->pcDisplayTo[0] != '\0' ) { sprintf( sTemp, "To: %s", pMailHeader->pcDisplayTo ); addHeaderLine( strMail, sTemp ); } if ( pMailHeader->pcDisplayCc[0] != '\0' ) { sprintf( sTemp, "Cc: %s", pMailHeader->pcDisplayCc ); addHeaderLine( strMail, sTemp ); } if ( pMailHeader->pcDisplayBcc[0] != '\0' ) { sprintf( sTemp, "Bcc: %s", pMailHeader->pcDisplayBcc ); addHeaderLine( strMail, sTemp ); } /* //收件人数量 int iRcvrCount = pMailHeader->iRcvrCount; //收件人 LPMAIL_USR lpRecipience; int i; // From: sprintf( sTemp, "From: \"%s\" <%s>", pMailHeader->lpSender->lpszName, pMailHeader->lpSender->lpszAddress ); addHeaderLine( strMail, sTemp ); // To/Cc/Bcc: for ( i = 0; i < iRcvrCount; i++ ) { //lpRecipience = getRecipient( pMailHeader, i ); lpRecipience = &pMailHeader->lpReceiver[i]; sprintf( sTemp, "\"%s\" <%s>",lpRecipience->lpszName, lpRecipience->lpszAddress ); switch ( lpRecipience->lRecipClass ) { case TO: if( '\0' == sTo[0] ) { sprintf( sTo, "To: %s", sTemp ); } else { strcat( sTo, ",\r\n\t" ); strcat( sTo, sTemp ); } break; case CC: if ( '\0' == sCc[0] ) { sprintf( sCc, "Cc: %s", sTemp ); } else { strcat( sCc, ",\r\n\t" ); strcat( sCc, sTemp ); } break; case BCC: if ('\0' == sBcc[0]) { sprintf( sBcc, "Bcc: %s", sTemp ); } else { strcat( sBcc, ",\r\n\t" ); strcat( sBcc, sTemp ); } break; default: break; } } //将收信人信息追加到字符串strMail if ( sTo[0] != '\0' ) { addHeaderLine( strMail, sTo ); } if ( sCc[0] != '\0' ) { addHeaderLine( strMail, sCc ); } if ( sBcc[0] != '\0' ) { addHeaderLine( strMail, sBcc ); } */ // Date: // Format: Mon, 01 Jun 98 01:10:30 GMT lt = time( NULL ); ptrTime = localtime( < ); strftime( sTemp2, MAX_BUFFER_SIZE, "Date: %a, %d %b %y %H:%M:%S ", ptrTime ); // 时区计算 dwReturn = GetTimeZoneInformation( &timezoneinfo ); switch ( dwReturn ) { case TIME_ZONE_ID_UNKNOWN: case TIME_ZONE_ID_STANDARD: lTimeDifferential = timezoneinfo.Bias + timezoneinfo.StandardBias; //Bias is in minutes--calculate the hours for HHMM format. lHours = -(lTimeDifferential/60); //Bias is in minutes--calculate the minutes for HHMM format. lMinutes = lTimeDifferential%60L; break; case TIME_ZONE_ID_DAYLIGHT: lTimeDifferential = timezoneinfo.Bias + timezoneinfo.DaylightBias; //Bias is in minutes--calculate the hours for HHMM format. //Need to take the additive inverse. //Bias is based on GMT=Local+Bias. //We need a differential based on GMT=Local-Bias. lHours = -(lTimeDifferential/60); //Bias is in minutes--calculate the minutes for HHMM format. lMinutes = lTimeDifferential%60L; break; case TIME_ZONE_ID_INVALID: assert( FALSE ); break; } sprintf( sTemp, "%s%+.2d%.2d", sTemp2, lHours, lMinutes ); addHeaderLine( strMail, sTemp ); // Subject: 将邮件标题转为Base64编码,并追加到strMail if ( pMailHeader->pcSubject[0] != '\0' ) { Base64Encode( (char *const)sTemp2, (const unsigned char*)pMailHeader->pcSubject, strlen(pMailHeader->pcSubject) ); sprintf( sTemp, "Subject: =?gb2312?B?%s?=", sTemp2 ); addHeaderLine( strMail, sTemp ); } /* // X-Mailer:表示信件是从哪个客户端发送出来的 sprintf( sTemp, "X-Mailer: %s", pMailHeader->XXXX ); addHeaderLine( strMail, sTemp ); */ // X-Priority: sprintf( sTemp, "X-Priority: %s", _ltoa(pMailHeader->lImportance, strImportance, 10) ); addHeaderLine( strMail, sTemp ); addHeaderLine( strMail, "MIME-Version: 1.0" ); } //-------------------------------------------------------------- // 功能:将邮件体信息追加到到字符串 // 参数: // IN LPVOID pMail 邮件结构指针 // OUT char* strMail MIME编码后邮件字符串指针 // OUT char* strError 错误信息提示 // 返回值:成功为TRUE,失败为FALSE //-------------------------------------------------------------- static BOOL prepareBody( LPVOID pMail, char *strMail, char *strError ) { LPNOTE_DETAIL pMailBody = (LPNOTE_DETAIL)pMail; BOOL bRet; LPMAIL_ATCH lpMailAttach; char sTemp[MIN_BUFFER_SIZE]={'\0'}; char strBoundaryLevel[sizeof(int)+1]; int i; if ( !pMailBody->bHasAttach ) // 不带带附件的邮件处理 { // 添加正文 bRet = appendText( pMail, strMail, strError ); } // 带附件的邮件处理 else { //Content-Type: multipart/mixed; boundary=_0_.__.__TOL__Mailer__Part_Boundary_ sprintf( sTemp, "Content-Type: %s;\r\n\tboundary=_%s%s\r\n", CONTENT_TYPE_MULTIPART, _itoa(BoundaryLevel, strBoundaryLevel, 10), MAIL_PART_BOUNDARY ); addHeaderLine( strMail, sTemp ); addHeaderLine( strMail, "This is a multi-part message in MIME format.\r\n" ); // 添加正文 insertBoundary( strMail ); bRet = appendText( pMail, strMail, strError ); // 如果正文添加失败,则不进行附件添加 // 当正文在字符串中保存时总返回TRUE if ( bRet ) { for ( i = 0; i < pMailBody->iAttachCount; i++ ) { // 添加附件 // lpMailAttach = getMailAttach(pMailBody, i); lpMailAttach = &pMailBody->lpAttach[i]; appendAttach( lpMailAttach, strMail, strError ); }// end for }// end if }// end else return bRet; } //-------------------------------------------------------------- // 功能:将邮件正文MIME编码并追加到字符串 // 参数: // IN LPVOID pMail 邮件结构指针 // OUT char* strMail MIME编码后邮件字符串指针 // OUT char* strError 错误信息提示 // 返回值:成功为TRUE,失败为FALSE //-------------------------------------------------------------- static BOOL appendText( LPVOID pMail, char *strMail, char *strError ) { char sTemp[MIN_BUFFER_SIZE]; int fbeginToEncode; /* // 正文以文件形式保存时 char *pcFilename = ( (LPNOTE_DETAIL)pMail )->pcBodyFilePathName; BOOL bRet; */ char *pcTextBody = ( (LPNOTE_DETAIL)pMail )->pcBodyContent; assert(pMail != NULL && strMail != NULL); // 没有邮件正文时不做处理 if ( '\0' == pcTextBody[0] ) { return TRUE; } // 设置SMTP正文头部参数 /* // 用于发送html格式的邮件 // Content-Type: text/html; charset="iso-8859-1" sprintf( sTemp, "Content-Type: %s; charset=\"%s\"", "text/html", "gb2312" ); */ // 用于发送文本格式的邮件 // Content-Type: text/plain; charset="gb2312" sprintf( sTemp, "Content-Type: %s; charset=\"%s\"", CONTENT_TYPE_STRING, CHAR_SET ); addHeaderLine( strMail, sTemp ); // Content-Transfer-Encoding: base64 sprintf( sTemp, "Content-Transfer-Encoding: %s\r\n", TRANSFER_ENCODING_B64 ); addHeaderLine( strMail, sTemp ); /* // 将指定文件中的邮件正文编码并追加到strMail bRet = appendFileBody(( (LPNOTE_DETAIL)pMail )->pcBodyFilePathName, strMail, strError ); */ // 将邮件正文紧接在strMail字符串的后面 fbeginToEncode = strlen( strMail ); Base64Encode( (char *const)(&strMail[fbeginToEncode]), (const unsigned char*)pcTextBody, strlen(pcTextBody) ); return TRUE; } //-------------------------------------------------------------- // 功能:获得一个收件人信息 // 参数: // IN LPVOID pMail 邮件结构指针 // 返回值:收件人结构体指针 //-------------------------------------------------------------- static LPMAIL_USR getRecipient( LPNOTE_DETAIL pMail, int i ) { return &pMail->lpReceiver[i]; } //-------------------------------------------------------------- // 功能:获得一个附件信息 // 参数: // IN LPVOID pMail 邮件结构指针 // 返回值:附件结构体指针 //-------------------------------------------------------------- static LPMAIL_ATCH getMailAttach( LPNOTE_DETAIL pMail, int i ) { return &pMail->lpAttach[i]; } //-------------------------------------------------------------- // 功能:得到Content-Type // 参数: // IN LPVOID pMail 邮件结构指针 // OUT sTemp 输出Content-Type字符串 // 返回值:成功TRUE,失败为FALSE //-------------------------------------------------------------- static BOOL getContentType(LPMAIL_ATCH lpMailAttach,char *sTemp) { char *szPostfix; int ch = '.'; BOOL bRet = FALSE; assert(lpMailAttach != NULL); //得到附件的后缀名 szPostfix = strrchr(lpMailAttach->lpszName,ch)+1; // 设置SMTP附件头部参数 // Content-Type: text/plain; file=XXXXX if((strcmp(szPostfix,"txt") == 0) || (strcmp(szPostfix,"TXT") == 0)) { sprintf( sTemp, "Content-Type: %s; file=%s", CONTENT_TYPE_STRING,lpMailAttach->lpszName ); bRet = TRUE; } else if((strcmp(szPostfix,"jpg") == 0) || (strcmp(szPostfix,"JPG") == 0)) { sprintf( sTemp, "Content-Type: %s; file=%s", CONTENT_TYPE_JPG,lpMailAttach->lpszName ); bRet = TRUE; } else { sprintf( sTemp, "Content-Type: %s; file=%s", CONTENT_TYPE_DEF,lpMailAttach->lpszName ); bRet = TRUE; } return bRet; } //-------------------------------------------------------------- // 功能:将附件追加到字符串 // 参数: // IN LPVOID pMail 邮件结构指针 // OUT char* strMail MIME编码后邮件字符串指针 // OUT char* strError 错误信息提示 // 返回值:成功为TRUE,失败为FALSE //-------------------------------------------------------------- static BOOL appendAttach( LPMAIL_ATCH lpMailAttach, char *strMail, char *strError ) { char sTemp[MIN_BUFFER_SIZE]; BOOL bRet = FALSE; insertBoundary( strMail ); if(!getContentType(lpMailAttach,sTemp)) return bRet; // 设置SMTP附件头部参数 // Content-Type: text/plain; file=XXXXX /*if(strcmp(szPostfix,"txt") == 0) { sprintf( sTemp, "Content-Type: %s; file=%s", CONTENT_TYPE_STRING,lpMailAttach->lpszName ); } else// Content-Type: application/octet-stream; file=XXXXX { sprintf( sTemp, "Content-Type: %s; file=%s", CONTENT_TYPE_OCTET_STREAM, lpMailAttach->lpszName ); } */ addHeaderLine( strMail, sTemp ); // Content-Transfer-Encoding: base64 sprintf( sTemp, "Content-Transfer-Encoding: %s", TRANSFER_ENCODING_B64 ); addHeaderLine( strMail, sTemp ); // Content-Disposition: attachment; filename=XXXXX sprintf( sTemp, "Content-Disposition: attachment; filename=%s\r\n", lpMailAttach->lpszName ); addHeaderLine( strMail, sTemp ); // 将指定文件中的附件编码并追加到strMail bRet = appendFileBody(lpMailAttach->lpszPathName, strMail, strError ); return bRet; } //-------------------------------------------------------------- // 功能:将指定路径文件的内容BASE64编码并追加到字符串 // 参数: // IN LPVOID pMail 邮件结构指针 // OUT char* strMail MIME编码后邮件字符串指针 // OUT char* strError 错误信息提示 // 返回值:成功为TRUE,失败为FALSE //-------------------------------------------------------------- static BOOL appendFileBody(char *plszfilename, char *strMail, char *strError ) { /* FILE *fp; unsigned char buf[MAX_BUFFER_SIZE]; size_t iflag; size_t iReadSize; //记录从文件中读出的长度 size_t iWriteSize; //记录Base64编码后的长度 assert(plszfilename!=NULL && strMail!=NULL); */ //以二进制方式“读打开” /* fp = fopen( plszfilename, "rb" ); if( NULL == fp ) { sprintf(strError, "%s: %s", ERROR_OPEN_FILE_FAIL, plszfilename); return FALSE; } //从strMail字符串的结束符的位置开始保存编码后的邮件正文 //iflag用于记录每次保存的开始位置 iflag = strlen( strMail ); while( !feof(fp) ) { iReadSize = fread( buf, sizeof(char), MAX_BUFFER_SIZE, fp ); //如果文件为空不进行编码转换. if ( 0 == iReadSize ) { continue; } iWriteSize = Base64Encode( (char *const)(&strMail[iflag]), (const unsigned char*)buf, iReadSize ); iflag +=iWriteSize; } fclose( fp ); */ long lfileSize; int handle; void *plbuf; int fbeginToEncode; assert(plszfilename!=NULL && strMail!=NULL); // 以二进制只读形式打开文件 handle = open( plszfilename, _O_BINARY ); if ( -1 == handle ) { sprintf( strError, "%s: %s", ERROR_OPEN_FILE_FAIL, plszfilename ); return FALSE; } // 获得文件大小,单位为byte lfileSize = filelength( handle ); // –1L to indicate an error, and an invalid handle sets errno to EBADF if ( -1L == lfileSize ) { sprintf( strError, "%s: %s", ERROR_EBADF, plszfilename ); close( handle ); return FALSE; } // 文件不为空时进行编码 if ( lfileSize > 0L ) { //分配合适的缓冲区存放文件的备份 plbuf = malloc( lfileSize + 1 ); if (NULL == plbuf) { sprintf( strError, "%s", ERROR_MALLOC_FAILE ); close( handle ); return FALSE; } read( handle, plbuf, lfileSize ); // 将缓冲区中的数据编码,编码后的数据紧接在strMail字符串的后面 fbeginToEncode = strlen( strMail ); Base64Encode( (char *const)(&strMail[fbeginToEncode]), (const unsigned char*)plbuf,lfileSize ); // 释放内存 free( plbuf ); } // 关闭文件 close( handle ); return TRUE; } //-------------------------------------------------------------- // 功能:为mime邮件添加边界标记 // 参数: // IN/OUT char* strMail MIME编码后邮件字符串指针 // 返回值:空 //-------------------------------------------------------------- static void insertBoundary( char* strMail ) { char sTemp[64]; char strBoundaryLevel[sizeof(int)+1]; char * lastTowChar = &strMail[strlen(strMail)-2]; // 保证在插入边界前,字符串的末尾为“\r\n” if ( (strcmp(lastTowChar, "\r\n")) != 0 ) { strcat(strMail, "\r\n"); } sprintf( sTemp, "\r\n--_%s%s\r\n", _itoa(BoundaryLevel, strBoundaryLevel, 10), MAIL_PART_BOUNDARY ); strcat(strMail, sTemp); } //-------------------------------------------------------------- // 功能:获得文件大小,单位为Byte // 参数: // IN char *fileName 文件路径名字符串 // 返回值:-1 文件打开失败 // 其它 以byte为单位的文件大小 //-------------------------------------------------------------- static long getByteFileSize( char *fileName ) { int handle; long lfileSize; // 获得文件句柄 handle = open( fileName, _O_BINARY ); // 当open返回-1时表示打开文件错误 if ( -1 == handle ) { return ( -1L ); } // filelength函数返回 –1L 表示: invalid file-handle lfileSize = filelength( handle ); // 关闭文件 close( handle ); return ( lfileSize ); } //end /* From: "wan ning" To: "william" Date: Thu, 16 Nov 06 16:20:28 +0800 Subject: =?gb2312?B?xOO6wyE=?= X-Priority: 0 MIME-Version: 1.0 Content-Type: multipart/mixed; boundary=_0_.__.__TOL__Mailer__Part_Boundary_ This is a multi-part message in MIME format. --_0_.__.__TOL__Mailer__Part_Boundary_ Content-Type: text/plain; charset="gb2312" Content-Transfer-Encoding: base64 PGhlYWQ+DQoJPHRpdGxlPg0KCQloZWxsbyx0ZXN0DQoJPC90aXRsZT4NCjwvaGVhZD4NCjxoMT7E 47rD1tC5+iENCjxoMj4gaGVsbG8sIHdvcmxkIQ0KPGJvZHk+DQo8L2JvZHk+ --_0_.__.__TOL__Mailer__Part_Boundary_ Content-Type: application/octet-stream; file=body.txt Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename=body.txt YWJjxOO6w6Oh1tC5+g== */