IOS邮箱开发

(一) - 邮箱收发协议

什么是POP3、SMTP和IMAP协议

电子邮件工作原理


POP3

POP3是Post Office Protocol 3的简称,即邮局协议的第3个版本,它规定怎样将个人计算机连接到Internet的邮件服务器和下载电子邮件的电子协议。它是因特网电子邮件的第一个离线协议标准,POP3允许用户从服务器上把邮件存储到本地主机(即自己的计算机)上,同时删除保存在邮件服务器上的邮件,而POP3服务器则是遵循POP3协议的接收邮件服务器,用来接收电子邮件的。(与IMAP有什么区别?)

SMTP

SMTP 的全称是“Simple Mail Transfer Protocol”,即简单邮件传输协议。它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。SMTP 服务器就是遵循 SMTP 协议的发送邮件服务器。   SMTP 认证,简单地说就是要求必须在提供了账户名和密码之后才可以登录 SMTP 服务器,这就使得那些垃圾邮件的散播者无可乘之机。   增加 SMTP 认证的目的是为了使用户避免受到垃圾邮件的侵扰。

IMAP

IMAP全称是Internet Mail Access Protocol,即交互式邮件存取协议,它是跟POP3类似邮件访问标准协议之一。不同的是,开启了IMAP后,您在电子邮件客户端收取的邮件仍然保留在服务器上,同时在客户端上的操作都会反馈到服务器上,如:删除邮件,标记已读等,服务器上的邮件也会做相应的动作。所以无论从浏览器登录邮箱或者客户端软件登录邮箱,看到的邮件以及状态都是一致的。(与POP3有什么区别?

网易163免费邮箱相关服务器信息:

163免费邮客户端设置的POP3、SMTP、IMAP地址

网易邮箱已经默认开启POP3/SMTP/IMAP服务,方便你通过客户端更好地收发邮件,如果关闭可以通过以下方式开启:

请登录163邮箱,点击页面正上方的“设置”,再点击左侧上“POP3/SMTP/IMAP”,其中“开启SMTP服务”是系统默认勾选开启的。您可勾选图中另两个选项,点击确定,即可开启成功。不勾选图中两个选项,点击确定,可关闭成功。** **

什么是POP3、SMTP和IMAP?

(二) - 各大主流邮箱服务器信息和端口号

网易163免费邮箱相关服务器信息:
服务器名称 服务器地址 SSL协议端口号 非SSL协议端口号
IMAP imap.163.com 993 143
SMTP smtp.163.com 465/994 25
POP3 pop.163.com 995 110
网易163企业邮箱相关服务器信息:(免费企业邮箱的smtp服务器名及端口号为:smtp.ym.163.com / 25)
服务器名称 服务器地址 SSL协议端口号 非SSL协议端口号
IMAP imap.qiye.163.com 993 143
SMTP smtp.qiye.163.com 994 25
POP3 pop.qiye.163.com 995 110

网易126免费邮箱相关服务器信息:
服务器名称 服务器地址 SSL协议端口号 非SSL协议端口号
IMAP imap.126.com 993 143
SMTP smtp.126.com 465/994 25
POP3 pop.126.com 995 110

腾讯QQ免费邮箱相关服务器信息:
服务器名称 服务器地址 SSL协议端口号 非SSL协议端口号
IMAP imap.qq.com 993 143
SMTP smtp.qq.com 465/587 25
POP3 pop.qq.com 995 110

腾讯QQ企业邮箱相关服务器信息:
服务器名称 服务器地址 SSL协议端口号 非SSL协议端口号
IMAP imap.exmail.qq.com 993 143
SMTP smtp.exmail.qq.com 465/587 25
POP3 pop.exmail.qq.com 995 110

谷歌Gmail邮箱相关服务器信息:
服务器名称 服务器地址 SSL协议端口号 非SSL协议端口号
IMAP imap.gmail.com 993 143
SMTP smtp.gmail.com 465 25
POP3 pop.gmail.com 995 110

Exchange邮箱相关服务器信息:(待确定)
服务器名称 服务器地址 域 SSL
Exchange ex.xx.com xx.com

邮箱默认配置:
服务器名称 服务器地址 SSL协议端口号 非SSL协议端口号
IMAP imap.xx.com 993 143
SMTP smtp.xx.com 465 25
POP3 pop.xx.com 995 110

(三) - MailCore2 详细使用

简介
MailCore是一个第三方的邮件SDK,支持POP和IMAP 方式接收邮件,以及smtp邮件发送。
安装

  1. 从github上拉取代码
    git clone https://github.com/MailCore/mailcore2/

  2. 添加到项目中
    把build-mac/mailcore2.xcodeproj拖进工程

  3. 在项目中添加静态库链接
    选中工程--TARGETS--Build Phases
    在 Link Binary With Libraries 中添加:

libMailCore-ios.a  
CFNetwork.framework  
Security.framework  

在 Target Dependencies 中选择:

static maincore2 ios

  1. 设置Flags
    选中工程--TARGETS--Build Setting
    在 Other Linker Flags 中设置

-lctemplate-ios -letpan-ios -lxml2 -lsasl2 -liconv -ltidy -lz -lc++ -stdlib=libc++ -ObjC -lresolv

在 C++ Standard Library 中设置

libc++

然后选择相应 Target 编译。
SMTP(Simple Mail Transfer Protocol)

  1. 先创建MCOSMTPSession,配置smtp邮箱参数
MCOSMTPSession *smtpSession = [[MCOSMTPSession alloc] init];  
smtpSession.hostname = @"smtp.exmail.qq.com";  
smtpSession.port = 587;  
smtpSession.username = @"hello@qq.com";  
smtpSession.password = @"passward";  
smtpSession.connectionType = MCOConnectionTypeStartTLS;  
  
MCOSMTPOperation *smtpOperation = [self.smtpSession loginOperation];  
[smtpOperation start:^(NSError * error) {  
    if (error == nil) {  
        NSLog(@"login account successed");  
    } else {  
        NSLog(@"login account failure: %@", error);  
    }  
}];  
  1. 使用MCOMessageBuilder构建邮件体的发送内容
// 构建邮件体的发送内容  
MCOMessageBuilder *messageBuilder = [[MCOMessageBuilder alloc] init];  
messageBuilder.header.from = [MCOAddress addressWithDisplayName:@"张三" mailbox:@"111111@qq.com"];   // 发送人  
messageBuilder.header.to = @[[MCOAddress addressWithMailbox:@"222222@qq.com"]];       // 收件人(多人)  
messageBuilder.header.cc = @[[MCOAddress addressWithMailbox:@"@333333qq.com"]];      // 抄送(多人)  
messageBuilder.header.bcc = @[[MCOAddress addressWithMailbox:@"444444@qq.com"]];    // 密送(多人)  
messageBuilder.header.subject = @"测试邮件";    // 邮件标题  
messageBuilder.textBody = @"hello world";           // 邮件正文  
  
/*  
 如果邮件是回复或者转发,原邮件中往往有附件以及正文中有其他图片资源, 
 如果有需要你可将原文原封不动的也带过去,这里发送的正文就可以如下配置  
 */  
NSString * bodyHtml = @"<p>我是原邮件正文</p>";  
NSString *body = @"我是邮件回复的内容";  
NSMutableString*fullBodyHtml = [NSMutableString stringWithFormat:@"%@<br/>-------------原始邮件-------------<br/>%@",[body stringByReplacingOccurrencesOfString:@"\n"withString:@"<br/>"],bodyHtml];  
[messageBuilder setHTMLBody:fullBodyHtml];  
  
// 添加正文里的附加资源  
NSArray *inattachments = msgPaser.htmlInlineAttachments;  
for (MCOAttachment*attachmentininattachments) {  
    [messageBuilder addRelatedAttachment:attachment];    //添加html正文里的附加资源(图片)  
}  
  
// 添加邮件附件  
for (MCOAttachment*attachmentinattachments) {  
    [builder addAttachment:attachment];    //添加附件  
}  
  1. 将构建好的邮件发送出去
// 发送邮件  
NSData * rfc822Data =[messageBuilder data];  
MCOSMTPSendOperation *sendOperation = [self.smtpSession sendOperationWithData:rfc822Data];  
[sendOperation start:^(NSError *error) {  
    if (error == nil) {  
        NSLog(@"send message successed");  
    } else {  
        NSLog(@"send message failure: %@", error);  
    }  
}];  

IMAP(Internet Mail Access Protocol)
  1. 先创建MCOIMAPSession,配置imap邮箱参数
MCOIMAPSession *imapSession = [[MCOIMAPSession alloc] init];  
imapSession.hostname = hostname;  
imapSession.port = port;  
imapSession.username = username;  
imapSession.password = passward;  
imapSession.connectionType = MCOConnectionTypeTLS;  
  
MCOIMAPOperation *imapOperation = [imapSession checkAccountOperation];  
[imapOperation start:^(NSError * __nullable error) {  
    if (error == nil) {  
        NSLog(@"login account successed\n");  
          
        // 在这里获取邮件,获取文件夹信息  
        [self loadIMAPFolder];  
    } else {  
        NSLog(@"login account failure: %@\n", error);  
    }  
}];  
  1. 获取文件夹目录
MCOIMAPFetchFoldersOperation *imapFetchFolderOp = [imapSession fetchAllFoldersOperation];  
[imapFetchFolderOp start:^(NSError * error, NSArray * folders) {  
    [_folderList addObjectsFromArray:folders];  
    [_folderTableView reloadData];  
}];  
  1. 获取收件箱邮件
MCOIMAPMessagesRequestKind requestKind = (MCOIMAPMessagesRequestKind)  
(MCOIMAPMessagesRequestKindHeaders | MCOIMAPMessagesRequestKindStructure |  
 MCOIMAPMessagesRequestKindInternalDate | MCOIMAPMessagesRequestKindHeaderSubject |  
 MCOIMAPMessagesRequestKindFlags);  
  
// 获取收件箱信息(包含邮件总数等信息)  
NSString *folderName = @"INBOX";  
MCOIMAPFolderInfoOperation * folderInfoOperation = [imapSession folderInfoOperation:folderName];  
[folderInfoOperation start:^(NSError *error, MCOIMAPFolderInfo * info) {  
      
    NSLog(@"total number: %d", info.messageCount);  
      
    NSInteger numberOfMessages = 10;  
    numberOfMessages -= 1;  
    MCOIndexSet *numbers = [MCOIndexSet indexSetWithRange:MCORangeMake([info messageCount] - numberOfMessages, numberOfMessages)];  
      
    MCOIMAPFetchMessagesOperation *imapMessagesFetchOp = [imapSession fetchMessagesByNumberOperationWithFolder:folderName  
                                                                                                                                                                  requestKind:requestKind  
                                                                                                                                                                        numbers:numbers];  
      
    // 异步获取邮件  
    [imapMessagesFetchOp start:^(NSError *error, NSArray *messages, MCOIndexSet *vanishedMessages) {  
        [_messageList addObjectsFromArray:messages];  
        [_messageTableView reloadData];  
    }];  
}];  
  1. 获取邮件头
// 拿出一个邮件MCOIMAPMessage(里面包含邮件头等信息)  
MCOIMAPMessage *message = _messageList [0];  
  
// 使用MCOIMAPMessageRenderingOperation来获得邮件概要信息  
NSString *uidKey = [NSString stringWithFormat:@"%d", message.uid];  
  
MCOIMAPMessageRenderingOperation *messageRenderingOperation = [imapSession plainTextBodyRenderingOperationWithMessage:message folder:@"INBOX"];  
[messageRenderingOperation start:^(NSString * plainTextBodyString,NSError * error) {  
      
    // plainTextBodyString为邮件的正文文本信息  
      
}];  
  1. 获取邮件内容,用MCOMessageParser解析邮件
MCOIMAPFetchContentOperation * fetchContentOp = [self.imapSession fetchMessageOperationWithFolder:@"INBOX" uid:[message uid]];  
[fetchContentOp start:^(NSError * error, NSData * data) {  
   if ([error code] != MCOErrorNone) {  
       return;  
   }  
     
   // 解析邮件内容  
   MCOMessageParser * msgPareser = [MCOMessageParser messageParserWithData:data];  
   NSString *content = [msgPareser htmlRenderingWithDelegate:self];  

POP(Post Office Protocol)
  1. 先创建MCOIMAPSession,配置imap邮箱参数
MCOPOPSession *popSession = [[MCOPOPSession alloc] init];  
popSession.hostname = @"smtp.exmail.qq.com";  
popSession.port = 995;  
popSession.username = @"hello@qq.com";  
popSession.password = @"passward";  
popSession.connectionType = MCOConnectionTypeTLS;  
  
// 登录邮箱  
MCOPOPOperation *popOperation = [popSession checkAccountOperation];  
[popOperation start:^(NSError * __nullable error) {  
    if (error == nil) {  
        NSLog(@"login account successed");  
    } else {  
        NSLog(@"login account failure: %@", error);  
    }  
}];  
  1. 获取邮件信息(同IMAP)
  2. 关于MCOMessageParser

不管是pop还是IMAP获取的邮件,最后都要得到MCOMessageParser来操作邮件内容。

// MCOMessageHeader包含了邮件标题,时间等头信息  
MCOMessageHeader *header = msgPaser.header;  
  
// 获得邮件正文的HTML内容,一般使用webView加载  
NSString * bodyHtml = [msgPaser htmlBodyRendering];  
  
// 获取附件(多个)  
NSMutableArray *attachments = [[NSMutableArray alloc] initWithArray:_msgPaser.attachments];  
  
// 拿到一个附件MCOAttachment,可从中得到文件名,文件内容data  

MCOAttachment *attachment = attachments[0];  

将附件写入本地

// 获取文件路径

NSString *tmpDirectory = NSTemporaryDirectory();  
NSString *filePath = [tmpDirectorystringByAppendingPathComponent : attachment.filename ];  
[attachmentData writeToFile:filePathatomically:YES];  

当邮件正文有图片,在webview中如何加载图片,可通过注入js的方式修改html
定义脚本:

static NSString * mainJavascript = @"\ 
var imageElements = function() {\ 
var imageNodes = document.getElementsByTagName('img');\ 
return [].slice.call(imageNodes);\ 
};\ 
\ 
var findCIDImageURL = function() {\ 
var images = imageElements();\ 
\ 
var imgLinks = [];\ 
for (var i = 0; i < images.length; i++) {\ 
var url = images[i].getAttribute('src');\ 
if (url.indexOf('cid:') == 0 || url.indexOf('x-mailcore-image:') == 0)\ 
imgLinks.push(url);\ 
}\ 
return JSON.stringify(imgLinks);\ 
};\ 
\ 
var replaceImageSrc = function(info) {\ 
var images = imageElements();\ 
\ 
for (var i = 0; i < images.length; i++) {\ 
var url = images[i].getAttribute('src');\ 
if (url.indexOf(info.URLKey) == 0) {\ 
images[i].setAttribute('src', info.LocalPathKey);\ 
break;\ 
}\ 
}\ 
};\ 
";  

修改刚才获取的html内容

MCOMessageParser * msgPareser = [MCOMessageParser messageParserWithData:data];  
NSString *content = [msgPareser htmlRenderingWithDelegate:self];  
  
NSMutableString * html = [NSMutableString string];  
[html appendFormat:@"<html><head><script>%@</script></head>"  
 @"<body>%@</body><iframe src='x-mailcore-msgviewloaded:' style='width: 0px; height: 0px; border: none;'>"  
 @"</iframe></html>", mainJavascript, content];  
[_webView loadHTMLString:content baseURL:nil];  

设置webView代理

#pragma mark - webview  
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {  
      
    NSURLRequest *responseRequest = [self webView:webView resource:nil willSendRequest:request redirectResponse:nil fromDataSource:nil];  
      
    if(responseRequest == request) {  
        return YES;  
    } else {  
        [webView loadRequest:responseRequest];  
        return NO;  
    }  
}  
  
- (NSURLRequest *)webView:(UIWebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(id)dataSource {  
      
    if ([[[request URL] scheme] isEqualToString:@"x-mailcore-msgviewloaded"]) {  
        [self _loadImages];  
    }  
    return request;  
}  
  
// 加载网页中的图片  
- (void) _loadImages {  
    NSString * result = [webView stringByEvaluatingJavaScriptFromString:@"findCIDImageURL()"];  
    NSLog(@"-----加载网页中的图片-----");  
    NSLog(@"%@", result);  
      
    if (result == nil || [result isEqualToString:@""]) {  
        return;  
    }  
      
    NSData * data = [result dataUsingEncoding:NSUTF8StringEncoding];  
    NSError *error = nil;  
    NSArray * imagesURLStrings = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];  
      
    for (NSString * urlString in imagesURLStrings) {  
        MCOAbstractPart * part = nil;  
        NSURL *url = [NSURL URLWithString:urlString];  
          
        if ([MCOCIDURLProtocol isCID:url]) {  
            part = [self _partForCIDURL:url];  
        }  
        else if ([MCOCIDURLProtocol isXMailcoreImage:url]) {  
            NSString * specifier = [url resourceSpecifier];  
            NSString * partUniqueID = specifier;  
            part = [self _partForUniqueID:partUniqueID];  
        }  
          
        if (part == nil) {  
            continue;  
        }  
          
        NSString * partUniqueID = [part uniqueID];  
        MCOAttachment * attachment = (MCOAttachment *) [msgPaser partForUniqueID:partUniqueID];  
        NSData * data =[attachment data];  
          
        if (data!=nil) {  
              
            //获取文件路径  
            NSString *tmpDirectory =NSTemporaryDirectory();  
            NSString *filePath=[tmpDirectory stringByAppendingPathComponent : attachment.filename ];  
              
            NSFileManager *fileManger=[NSFileManager defaultManager];  
            if (![fileManger fileExistsAtPath:filePath]) {  
                //不存在就去请求加载  
                NSData *attachmentData=[attachment data];  
                [attachmentData writeToFile:filePath atomically:YES];  
                NSLog(@"资源:%@已经下载至%@", attachment.filename,filePath);  
            }  
              
            NSURL * cacheURL = [NSURL fileURLWithPath:filePath];  
            NSDictionary * args =@{@"URLKey": urlString,@"LocalPathKey": cacheURL.absoluteString};  
              
            NSString * jsonString = [self _jsonEscapedStringFromDictionary:args];  
            NSString * replaceScript = [NSString stringWithFormat:@"replaceImageSrc(%@)", jsonString];  
            [webView stringByEvaluatingJavaScriptFromString:replaceScript];  
        }  
    }  
}  
  
- (NSString *)_jsonEscapedStringFromDictionary:(NSDictionary *)dictionary {  
      
    NSData * json = [NSJSONSerialization dataWithJSONObject:dictionary options:0 error:nil];  
    NSString * jsonString = [[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding];  
    return jsonString;  
}  
  
- (NSURL *) _cacheJPEGImageData:(NSData *)imageData withFilename:(NSString *)filename {  
      
    NSString * path = [[NSTemporaryDirectory()stringByAppendingPathComponent:filename]stringByAppendingPathExtension:@"jpg"];  
    [imageData writeToFile:path atomically:YES];  
    return [NSURL fileURLWithPath:path];  
}  
  
- (MCOAbstractPart *) _partForCIDURL:(NSURL *)url {  
    return [_message partForContentID:[url resourceSpecifier]];  
}  
  
- (MCOAbstractPart *) _partForUniqueID:(NSString *)partUniqueID {  
    return [_message partForUniqueID:partUniqueID];  
}  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容