You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When sending password protected message, we must upload it to FES & format the Gmail message differently. You can study EncryptedMsgMailFormatter on browser extension for details not mentioned here.
The steps are (when sending a password-protected message through FES:
get a message token (reply token) from FES
encode a string that contains sender, recipient, subject, reply token, as follows: replyInfo = Str.base64urlUtfEncode(JSON.stringify({sender: "...", recipient: ["..."], subject: "...", token: "..."}))
format a div html element that contains . infoDiv = '<div style="display: none" class="cryptup_reply" cryptup-data="REPLY INFO STRING"></div>'
append the div to plaintext as follows: bodyWithReplyToken = newMsgData.plaintext + '\n\n' + infoDiv
construct a regular plain mime message using the above plain text + attachments: pgpMimeWithAttachments = Mime.encode(bodyWithReplyToken, { Subject: newMsg.subject }, attachments);
encrypt the encoded plain mime message ONLY FOR THE MESSAGE PASSWORD, no public keys. pwdEncryptedWithAttachments = encryptDataArmor(pgpMimeWithAttachments, newMsg.pwd, []); // encrypted only for pwd, not signed. This message will be uploaded, therefore it's only encrypted for message password.
upload resulting data to FES. msgUrl = this.view.acctServer.messageUpload(idToken, pwdEncryptedWithAttachments, replyToken, from, recipients, { renderUploadProgress(p, PercentageAccounting.FIRST_HALF) } ). Notice that the upload progress will be divided by two, so that instead of rendering 0-100%, we render 0-50% at this stage.
encrypt and format the final Gmail message. First encrypt body: encryptedTextFile = Core.encryptFile(newMsgData.plaintext, publicKeys) // no attachments, no infoDiv, no mime. Use public keys to encrypt as usual, as available. No password
then encrypt attachments encryptedAttachments = plainAttachments.map { Core.encryptFile($0, publicKeys) } // add .pgp to name if not done automatically
put all attachments into an array encryptedAttachmentsAndBodyAsFiles = [Attachment(data=encryptedTextFile, name=message.asc, mimeType=application/pgp-encrypted)] + encryptedAttachments
format message text to go into the email, which is in plain text. See method formatPwdEncryptedMsgBodyLink. emailAndLinkBody = await this.formatPwdEncryptedMsgBodyLink(msgUrl); - NOTE: skip the intro part from the linked code, we are not implementing this on mobile yet
now all data is encrypted manually. The password-encrypted one was uploaded to FES and we have URL, which was encoded into emailAndLinkBody. Attachments were encrypted separately. Compose it all into a plain message together: sendableMessageMime = Core.composeEmail(format=plain, emailAndLinkBody, encryptedAttachmentsAndBodyAsFiles)
finally you have mime data to send. Upload to gmail & use PercentageAccounting.SECOND_HALF, eg on browser extension (simplified):
// if this is a password-encrypted message, then we've already shown progress for uploading to backend// and this requests represents second half of uploadable effort. Else this represents all (no previous heavy requests)constprogressRepresents=isPasswordEncryptedMessage ? 'SECOND-HALF' : 'EVERYTHING';`
msgSentRes=awaitthis.view.emailProvider.msgSend(msg,(p)=>this.renderUploadProgress(p,progressRepresents));
You can start implementation with approximately this method:
privategetPwdMsgSendableBodyWithOnlineReplyMsgToken=async(authInfo: FcUuidAuth,newMsgData: NewMsgData): Promise<{bodyWithReplyToken: SendableMsgBody,replyToken: string}>=>{constrecipients=Array.prototype.concat.apply([],Object.values(newMsgData.recipients));try{constresponse=awaitthis.view.acctServer.messageToken(authInfo);constinfoDiv=Ui.e('div',{'style': 'display: none;','class': 'cryptup_reply','cryptup-data': Str.htmlAttrEncode({sender: newMsgData.from,recipient: Value.arr.withoutVal(Value.arr.withoutVal(recipients,newMsgData.from),this.acctEmail),subject: newMsgData.subject,token: response.replyToken,})});return{bodyWithReplyToken: {'text/plain': newMsgData.plaintext+'\n\n'+infoDiv,'text/html': newMsgData.plainhtml+'<br /><br />'+infoDiv},replyToken: response.replyToken};}catch(msgTokenErr){if(ApiErr.isAuthErr(msgTokenErr)){Settings.offerToLoginWithPopupShowModalOnErr(this.acctEmail);thrownewComposerResetBtnTrigger();}elseif(ApiErr.isNetErr(msgTokenErr)){throwmsgTokenErr;}// note - you don't need to re-implement this exactlythrowCatch.rewrapErr(msgTokenErr,'There was a token error sending this message. Please try again. Let us know at human@flowcrypt.com if this happens repeatedly.');}};
Rendering upload progress:
publicrenderUploadProgress=(progress: number|undefined,progressRepresents: 'FIRST-HALF'|'SECOND-HALF'|'EVERYTHING')=>{if(progress&&this.view.attachmentsModule.attachment.hasAttachment()){if(progressRepresents==='FIRST-HALF'){progress=Math.floor(progress/2);// show 0-50% instead of 0-100%}elseif(progressRepresents==='SECOND-HALF'){progress=Math.floor(50+progress/2);// show 50-100% instead of 0-100%}else{progress=Math.floor(progress);// show 0-100%}this.view.S.now('send_btn_text').text(`${SendBtnTexts.BTN_SENDING}${progress<100 ? `${progress}%` : ''}`);}};
privatesendablePwdMsg=async(newMsg: NewMsgData,pubs: PubkeyResult[],msgUrl: string,signingPrv?: Key)=>{// encoded as: PGP/MIME-like structure but with attachments as external files due to email size limit (encrypted for pubkeys only)constmsgBody=this.richtext ? {'text/plain': newMsg.plaintext,'text/html': newMsg.plainhtml} : {'text/plain': newMsg.plaintext};constpgpMimeNoAttachments=awaitMime.encode(msgBody,{Subject: newMsg.subject},[]);// no attachments, attached to email separatelyconst{data: pubEncryptedNoAttachments}=awaitthis.encryptDataArmor(Buf.fromUtfStr(pgpMimeNoAttachments),undefined,pubs,signingPrv);// encrypted only for pubsconstattachments=this.createPgpMimeAttachments(pubEncryptedNoAttachments).concat(awaitthis.view.attachmentsModule.attachment.collectEncryptAttachments(pubs));// encrypted only for pubsconstemailIntroAndLinkBody=awaitthis.formatPwdEncryptedMsgBodyLink(msgUrl);returnawaitSendableMsg.createPwdMsg(this.acctEmail,this.headers(newMsg),emailIntroAndLinkBody,attachments,{isDraft: this.isDraft});};
When sending password protected message, we must upload it to FES & format the Gmail message differently. You can study
EncryptedMsgMailFormatteron browser extension for details not mentioned here.The steps are (when sending a password-protected message through FES:
replyInfo = Str.base64urlUtfEncode(JSON.stringify({sender: "...", recipient: ["..."], subject: "...", token: "..."}))infoDiv = '<div style="display: none" class="cryptup_reply" cryptup-data="REPLY INFO STRING"></div>'bodyWithReplyToken = newMsgData.plaintext + '\n\n' + infoDivpgpMimeWithAttachments = Mime.encode(bodyWithReplyToken, { Subject: newMsg.subject }, attachments);pwdEncryptedWithAttachments = encryptDataArmor(pgpMimeWithAttachments, newMsg.pwd, []); // encrypted only for pwd, not signed. This message will be uploaded, therefore it's only encrypted for message password.msgUrl = this.view.acctServer.messageUpload(idToken, pwdEncryptedWithAttachments, replyToken, from, recipients, { renderUploadProgress(p, PercentageAccounting.FIRST_HALF) } ). Notice that the upload progress will be divided by two, so that instead of rendering 0-100%, we render 0-50% at this stage.encryptedTextFile = Core.encryptFile(newMsgData.plaintext, publicKeys) // no attachments, no infoDiv, no mime. Use public keys to encrypt as usual, as available. No passwordencryptedAttachments = plainAttachments.map { Core.encryptFile($0, publicKeys) }// add.pgpto name if not done automaticallyencryptedAttachmentsAndBodyAsFiles = [Attachment(data=encryptedTextFile, name=message.asc, mimeType=application/pgp-encrypted)] + encryptedAttachmentsformatPwdEncryptedMsgBodyLink.emailAndLinkBody = await this.formatPwdEncryptedMsgBodyLink(msgUrl);- NOTE: skip theintropart from the linked code, we are not implementing this on mobile yetemailAndLinkBody. Attachments were encrypted separately. Compose it all into a plain message together:sendableMessageMime = Core.composeEmail(format=plain, emailAndLinkBody, encryptedAttachmentsAndBodyAsFiles)PercentageAccounting.SECOND_HALF, eg on browser extension (simplified):You can start implementation with approximately this method:
Rendering upload progress:
Formatting plain part of Gmail message
Formatting final Gmail message: