1、k9mail源码分析privatevoidfinishAutoSetup() String email = mEmailView.getText().toString(); String password = mPasswordView.getText().toString();String emailParts = splitEmail(email); String user = emailParts0; String domain = emailParts1; URI incomingUri = null; URI outgoingUri = null;try String userEnc
2、 = URLEncoder.encode(user, UTF-8); String passwordEnc = URLEncoder.encode(password, UTF-8); String incomingUsername = mProvider.incomingUsernameTemplate;incomingUsername = incomingUsername.replaceAll($email, email);incomingUsername = incomingUsername.replaceAll($user, userEnc);incomingUsername = inc
3、omingUsername.replaceAll($domain, domain); URI incomingUriTemplate = mProvider.incomingUriTemplate;incomingUri = new URI(incomingUriTemplate.getScheme(), incomingUsername + : + passwordEnc, incomingUriTemplate.getHost(), incomingUriTemplate.getPort(), null,null, null); String outgoingUsername = mPro
4、vider.outgoingUsernameTemplate; URI outgoingUriTemplate = mProvider.outgoingUriTemplate;if (outgoingUsername != null) outgoingUsername = outgoingUsername.replaceAll($email, email);outgoingUsername = outgoingUsername.replaceAll($user, userEnc);outgoingUsername = outgoingUsername.replaceAll($domain, d
5、omain);outgoingUri = new URI(outgoingUriTemplate.getScheme(), outgoingUsername + : + passwordEnc, outgoingUriTemplate.getHost(), outgoingUriTemplate.getPort(), null,null, null); else outgoingUri = new URI(outgoingUriTemplate.getScheme(),null, outgoingUriTemplate.getHost(), outgoingUriTemplate.getPor
6、t(), null,null, null); if (mAccount = null) mAccount = Preferences.getPreferences(this).newAccount(); mAccount.setName(getOwnerName();mAccount.setEmail(email);mAccount.setStoreUri(incomingUri.toString();mAccount.setTransportUri(outgoingUri.toString();mAccount.setDraftsFolderName(getString(R.string.s
7、pecial_mailbox_name_drafts);mAccount.setTrashFolderName(getString(R.string.special_mailbox_name_trash);mAccount.setArchiveFolderName(getString(R.string.special_mailbox_name_archive);/ Yahoo! has a special folder for Spam, called Bulk Mail.if (incomingUriTemplate.getHost().toLowerCase().endsWith() mA
8、ccount.setSpamFolderName(Bulk Mail); else mAccount.setSpamFolderName(getString(R.string.special_mailbox_name_spam); mAccount.setSentFolderName(getString(R.string.special_mailbox_name_sent);if (incomingUri.toString().startsWith(imap) mAccount.setDeletePolicy(Account.DELETE_POLICY_ON_DELETE); elseif (
9、incomingUri.toString().startsWith(pop3) mAccount.setDeletePolicy(Account.DELETE_POLICY_NEVER); / Check incoming here. Then check outgoing in onActivityResult()AccountSetupCheckSettings.actionCheckSettings(this, mAccount, CheckDirection.INCOMING); catch (UnsupportedEncodingExceptionenc) / This really
10、 shouldnt happen since the encoding is hardcoded to UTF-8Log.e(K9.LOG_TAG, Couldnt urlencode username or password., enc); catch (URISyntaxException use) /* * If there is some problem with the URI we give up and go on to * manual setup. */onManualSetup(); MIME邮件的构成MIME消息(邮件,对应 k9mail 的MimeMessage类),由
11、消息头(对应 k9mai的MimeHeader类)和消息体(body对应k9mail的Body 接口)两大部分组成。邮件头中不允许出现空行。邮件头包含了发件人、收件人、主题、时间、MIME 版本、邮件内容的类型等重要信息。每条信息称为一个域,由域名后加“: ”和信息的内容构成,可以是一行,较长的也可以占用多行。域的首行必须“顶头”写,即左边不能有空白字符(空格和制表符);续行则必须以空白字符打头,且第一个空白字符不是信息本身固有的,解码时要过滤掉。邮件头信息中各个字段及其含义的说明如下表:邮件体(body)指的是邮件的内容,它的类型由邮件头的“Content-Type”域指出,常见的简单类型有
12、 text/plain(纯文本)和 text/html(超文本)。multipart 类型是 MIME 邮件的精髓,对应 k9mail 的MultiPart类。邮件体被分为多个段或者多个部分,对应 k9mail 的 Part 接口,每个段又包含段头和段体两部分,这两部分之间也以空行分隔。常见的 multipart 类型有三种: multipart/mixed, multipart/related 和multipart/alternative。从它们的名称,不难推知这些类型各自的含义和用处。它们之间的层次关系如下图所示,从中可以看出,如果在邮件中要添加附件,必须定义 multipart/mixe
13、d 段;如果存在内嵌资源,至少要定义 multipart/related 段;如果纯文本与超文本共存,至少要定义 multipart/alternative 段。为什么说是“至少”?举个例子说,如果只有纯文本与超文本正文,那么在邮件头中将类型扩大化,定义为 multipart/related,甚至 multipart/mixed,都是允许的。k-9-4.804srccomfsckk9mail地址:address.java地址和通讯录名字之间的转换身份认证:Authentication.java函数作用:计算对CRAM-MD5认证机制的响应,给用户提供认证信息和服务器提供随机数。package
14、com.fsck.k9.mail;importjava.security.MessageDigest;import com.fsck.k9.mail.filter.Base64;import com.fsck.k9.mail.filter.Hex;publicclassAuthentication privatestaticfinalStringUS_ASCII = US-ASCII;/* * Computes the response for CRAM-MD5 authentication mechanism given the user credentials and * the serv
15、er-provided nonce. * * param username Theusername. * param password The password. * param b64Nonce Thenonce as base64-encoded string.* return The CRAM-MD5 response. * * throwsAuthenticationFailedExceptionIf something went wrong. * * see Authentication#computeCramMd5Bytes(String, String, byte) */publ
16、icstaticString computeCramMd5(String username, String password, String b64Nonce)throwsAuthenticationFailedException try byte b64NonceBytes = b64Nonce.getBytes(US_ASCII);byte b64CRAM = computeCramMd5Bytes(username, password, b64NonceBytes);returnnewString(b64CRAM, US_ASCII); catch (AuthenticationFail
17、edException e) throwe; catch (Exception e) thrownewAuthenticationFailedException(This shouldnt happen, e); /* * Computes the response for CRAM-MD5 authentication mechanism given the user credentials and * the server-provided nonce. * * param username Theusername. * param password The password. * par
18、am b64Nonce Thenonce as base64-encoded byte array.* return The CRAM-MD5 response as byte array. * * throwsAuthenticationFailedExceptionIf something went wrong. * * seeRFC 2195 */publicstaticbyte computeCramMd5Bytes(String username, String password, byte b64Nonce)throwsAuthenticationFailedException t
19、ry byte nonce = Base64.decodeBase64(b64Nonce);byte secretBytes = password.getBytes(US_ASCII);MessageDigest md = MessageDigest.getInstance(MD5);if (secretBytes.length 64) secretBytes = md.digest(secretBytes); byte ipad = newbyte64;byte opad = newbyte64;System.arraycopy(secretBytes, 0, ipad, 0, secret
20、Bytes.length);System.arraycopy(secretBytes, 0, opad, 0, secretBytes.length);for (inti = 0; iipad.length; i+) ipadi = 0x36;for (inti = 0; iopad.length; i+) opadi = 0x5c;md.update(ipad);byte firstPass = md.digest(nonce);md.update(opad);byte result = md.digest(firstPass);StringplainCRAM = username + +
21、newString(Hex.encodeHex(result);byte b64CRAM = Base64.encodeBase64(plainCRAM.getBytes(US_ASCII);return b64CRAM; catch (Exception e) thrownewAuthenticationFailedException(Something went wrong during CRAM-MD5 computation, e); k-9-.fsck.k9.mail.transport;SmtpTransport.javaWebDavTransport.javak-9-4.804s
22、rccomfsckk9mailtransportimapImapSettings.javacom.fsck.k9:Account.java这是个实体类,也是 android 平台上 MVC 模式中的 Model 类,它除了封装Email 账户的信息外,还被设计用于保存账户的各种设置,包括账户身份认证设置 Identity、字体设置FontSizes、通知设置NotificationSetting和邮件收发地址、草稿箱、垃圾箱、总是密送到的账户、反垃圾用的文件夹、各种网络(3g、WiFi等)连接下是否启用压缩等,这些数据使用android平台的SharedPreferences保存,Shared
23、Preferences是平台下除了SQLite 外另外一种方便好用的数据持久化方式,应该是 android 平台上最简单的读写外部数据的方法,特别适用于保存客户端不同用户的个性化设置信息。一个账户用一个 UUID 定义,可以通过mUuid属性的值来区分两个账户。Accou类实现了接口BaseAccount,这个接口定义了获取、设置 Email 账户的及其描述的 String 类型的数据,还定义了获取账户 UUID 值的方法。通过 Account 可以获得账户对应的LocalStore实例,然后可以进一步获得该账户在 SQLite 数据库里面保存的一切信息,包括邮件文件夹、邮件等,还可以获得远程
24、邮件服务器的代理(RemoteStore,包括ImapStore、 Pop3Store 和WebDavStore,这些组件封装了对远程服务器的访问),并进一步获得其内部类(例如ImapStore的内部类ImapFolder),实现对远程服务器上相应对象的操作。Account 的类型怎么区分的?通过 Account 类的属性mStoreUri,不同类型的 Account 的mStoreUri的值以不同的前缀开头,k9mail 分别使用 pop3、imap和webdav表示相应的三种 Email 账户类型,该属性的值的形式如下:imap:/PLAIN:帐号:密码:143/1%7C这些数据变化后,k
25、9mail会调用Account 类的save方法通过SharedPreferences.Editor把这些数据保存到 xml 配置文件中。该类存储了一个账号的所有信息。Account stores all of the settings for a single account defined by the user. It is able to saveand delete itself given a Preferences to work with. Each account is defined by a UUID.(1)Account的默认信息:收发件箱、删除策略、网络类型、消息类型
26、、键值、颜色、排序类、数据库ID等等(2)protectedAccount(Context context)设置账户的基本信息:(3)private intpickColor(Context context)* Pick a nice Android guidelines color if we havent used them all yet.(4)protectedAccount(Preferences preferences, String uuid)this.mUuid = uuid;loadAccount(preferences); (5)private synchronized v
27、oidloadAccount(Preferences preferences)* Load stored settings for this account.(6)protected synchronized voiddelete(Preferences preferences)删除一个账户(7)publicstaticintfindNewAccountNumber(ListaccountNumbers) 为新账户建立一个AccountNumber(8)publicstatic ListgetExistingAccountNumbers(Preferencespreferences)获得存在的
28、所有帐户的AccountNumber列表(9)publicstaticintgenerateAccountNumber(Preferences preferences) 生成一个AccountNumber(10)publicvoid move(Preferences preferences, booleanmoveUp) 移动账户的位置,上移或下移一位(11)publicsynchronizedvoid save(Preferences preferences)保存账户的配置信息。当账户被创建时,我们会为其分配一个唯一的AccountNumber(12)public voidresetVisi
29、bleLimits()重置可以显示的账户的个数(13)publicAccountStatsgetStats(Context context) throwsMessagingException获得帐户状态(14)颜色相关的函数publicsynchronizedvoidsetChipColor(int color) publicsynchronizedvoidcacheChips()publicColorChipgetCheckmarkChip() returnmCheckmarkChip; publicsynchronizedintgetChipColor() returnmChipColor
30、; publicColorChipgenerateColorChip(booleanmessageRead, booleantoMe, booleanccMe, booleanfromMe, booleanmessageFlagged)publicColorChipgenerateColorChip() returnnewColorChip(mChipColor, false, ColorChip.CIRCULAR); (15)public String getUuid() returnmUuid; 获得当前账户的Uuid(16)public Uri getContentUri() returnUri.parse(content:/accounts/ + getUuid(); 获得当前账户的ContentUri(16)publicsynchronized String getStoreUri() returnmStoreUri; 获得当前账户的StoreUri(17)publicsynchronizedvoidsetStoreUri(String storeUri) this.mStoreUri = storeUri;设置当前账户的Store
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1