GODT-1650: text/html sending tests

This commit is contained in:
James Houlahan
2022-10-04 16:27:38 +02:00
parent ba9368426c
commit c953b8030a
12 changed files with 535 additions and 165 deletions

3
go.mod
View File

@ -38,7 +38,7 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.0
github.com/urfave/cli/v2 v2.16.3
gitlab.protontech.ch/go/liteapi v0.32.1-0.20221004092920-6b728aed0d4d
gitlab.protontech.ch/go/liteapi v0.32.1-0.20221004164551-596cce482fb0
golang.org/x/exp v0.0.0-20220921164117-439092de6870
golang.org/x/net v0.1.0
golang.org/x/sys v0.1.0
@ -131,4 +131,5 @@ replace (
github.com/emersion/go-imap => github.com/ProtonMail/go-imap v0.0.0-20201228133358-4db68cea0cac
github.com/emersion/go-message => github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753
github.com/keybase/go-keychain => github.com/cuthix/go-keychain v0.0.0-20220405075754-31e7cee908fe
gitlab.protontech.ch/go/liteapi => ../liteapi
)

2
go.sum
View File

@ -463,8 +463,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0=
github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA=
gitlab.protontech.ch/go/liteapi v0.32.1-0.20221004092920-6b728aed0d4d h1:2CB6po0yWmgb0bVCylvQlQph6a6Hk/Uziq5eHg0ZCfo=
gitlab.protontech.ch/go/liteapi v0.32.1-0.20221004092920-6b728aed0d4d/go.mod h1:SVxEeF4uYYYpSlfeAj2ZqluVEP95pbZ8LyoieSxU0pM=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=

View File

@ -1,11 +1,27 @@
package tests
import (
"encoding/json"
"reflect"
"github.com/bradenaw/juniper/xslices"
)
func ToAny(v any) any {
b, err := json.Marshal(v)
if err != nil {
panic(err)
}
var a any
if err := json.Unmarshal(b, &a); err != nil {
panic(err)
}
return a
}
func IsSub(outer, inner any) bool {
if outer == nil && inner != nil {
return IsSub(reflect.Zero(reflect.TypeOf(inner)).Interface(), inner)
@ -30,10 +46,6 @@ func IsSub(outer, inner any) bool {
return false
}
if len(inner) != len(outer) {
return false
}
return isSubSlice(outer, inner)
default:
@ -69,6 +81,10 @@ func isSubMap(outer, inner map[string]any) bool {
}
func isSubSlice(outer, inner []any) bool {
if len(inner) != len(outer) {
return false
}
for _, v := range inner {
if xslices.IndexFunc(outer, func(outer any) bool {
return IsSub(outer, v)

View File

@ -5,7 +5,7 @@ Feature: IMAP get mailbox info
| name | type |
| one | folder |
And the address "user@pm.me" of account "user@pm.me" has the following messages in "one":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And bridge starts

View File

@ -6,7 +6,7 @@ Feature: IMAP copy messages
| mbox | folder |
| label | label |
And the address "user@pm.me" of account "user@pm.me" has the following messages in "Inbox":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
| jane.doe@mail.com | name@pm.me | bar | true |
And bridge starts
@ -17,37 +17,37 @@ Feature: IMAP copy messages
Scenario: Copy message to label
When IMAP client "1" copies the message with subject "foo" from "INBOX" to "Labels/label"
Then IMAP client "1" sees the following messages in "INBOX":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
| jane.doe@mail.com | name@pm.me | bar | true |
And IMAP client "1" sees the following messages in "Labels/label":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
Scenario: Copy all messages to label
When IMAP client "1" copies all messages from "INBOX" to "Labels/label"
Then IMAP client "1" sees the following messages in "INBOX":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
| jane.doe@mail.com | name@pm.me | bar | true |
And IMAP client "1" sees the following messages in "Labels/label":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
| jane.doe@mail.com | name@pm.me | bar | true |
Scenario: Copy message to folder does move
When IMAP client "1" copies the message with subject "foo" from "INBOX" to "Folders/mbox"
Then IMAP client "1" eventually sees the following messages in "INBOX":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| jane.doe@mail.com | name@pm.me | bar | true |
And IMAP client "1" sees the following messages in "Folders/mbox":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
Scenario: Copy all messages to folder does move
When IMAP client "1" copies all messages from "INBOX" to "Folders/mbox"
Then IMAP client "1" sees the following messages in "Folders/mbox":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
| jane.doe@mail.com | name@pm.me | bar | true |
And IMAP client "1" eventually sees 0 messages in "INBOX"
@ -55,7 +55,7 @@ Feature: IMAP copy messages
Scenario: Copy message from Inbox to Sent is not possible
When IMAP client "1" copies the message with subject "foo" from "INBOX" to "Sent"
Then IMAP client "1" eventually sees the following messages in "INBOX":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| john.doe@mail.com | user@pm.me | foo | false |
| jane.doe@mail.com | name@pm.me | bar | true |
And IMAP client "1" eventually sees 0 messages in "Sent"

View File

@ -0,0 +1,344 @@
Feature: SMTP sending of plain messages
Background:
Given there exists an account with username "user@pm.me" and password "password"
And there exists an account with username "bridgetest@protonmail.com" and password "password"
And there exists an account with username "bridgetest2@protonmail.com" and password "password"
And bridge starts
And the user logs in with username "user@pm.me" and password "password"
And user "user@pm.me" connects and authenticates SMTP client "1"
Scenario: HTML message to external account
When SMTP client "1" sends the following message from "user@pm.me" to "pm.bridge.qa@gmail.com":
"""
From: Bridge Test <user@pm.me>
To: External Bridge <pm.bridge.qa@gmail.com>
Subject: HTML text external
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=utf-8
In-Reply-To: <base64hashOfSomeMessage@protonmail.internalid>
<html><body>This is body of <b>HTML mail</b> without attachment<body></html>
"""
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | HTML text external |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
"Message": {
"Subject": "HTML text external",
"Sender": {
"Name": "Bridge Test"
},
"ToList": [
{
"Address": "pm.bridge.qa@gmail.com",
"Name": "External Bridge"
}
],
"CCList": [],
"BCCList": [],
"MIMEType": "text/html"
}
}
"""
Scenario: HTML message with inline image to external account
When SMTP client "1" sends the following message from "user@pm.me" to "pm.bridge.qa@gmail.com":
"""
From: Bridge Test <user@pm.me>
To: External Bridge <pm.bridge.qa@gmail.com>
Subject: Html Inline External
Content-Disposition: inline
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.5.0
MIME-Version: 1.0
Content-Language: en-US
Content-Type: multipart/related; boundary="------------61FA22A41A3F46E8E90EF528"
This is a multi-part message in MIME format.
--------------61FA22A41A3F46E8E90EF528
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<p><br>
</p>
<p>Behold! An inline <img moz-do-not-send="false"
src="cid:part1.D96BFAE9.E2E1CAE3@protonmail.com" alt=""
width="24" height="24"><br>
</p>
</body>
</html>
--------------61FA22A41A3F46E8E90EF528
Content-Type: image/gif; name="email-action-left.gif"
Content-Transfer-Encoding: base64
Content-ID: <part1.D96BFAE9.E2E1CAE3@protonmail.com>
Content-Disposition: inline; filename="email-action-left.gif"
R0lGODlhGAAYANUAACcsKOHs4kppTH6tgYWxiIq0jTVENpG5lDI/M7bRuEaJSkqOTk2RUU+P
U16lYl+lY2iva262cXS6d3rDfYLNhWeeamKTZGSVZkNbRGqhbOPt4////+7u7qioqFZWVlNT
UyIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAGAAYAAAG
/8CNcLjRJAqVRqNSSGiI0GFgoKhar4NAdHioMhyRCYUyiTgY1cOWUH1ILgIDAGAQXCSPKgHa
XUAyGCCCg4IYGRALCmpCAVUQFgiEkiAIFhBVWhtUDxmRk5IIGXkDRQoMEoGfHpIYEmhGCg4X
nyAdHB+SFw4KRwoRArQdG7eEAhEKSAoTBoIdzs/Cw7iCBhMKSQoUAIJbQ8QgABQKStnbIN1C
3+HjFcrMtdDO6dMg1dcFvsCfwt+CxsgJYs3a10+QLl4aTKGitYpQq1eaFHDyREtQqFGMHEGq
SMkSJi4K/ACiZQiRIihsJL6JM6fOnTwK9kTpYgqMGDJm0JzsNuWKTw0FWdANMYJECRMnW4IA
ADs=
--------------61FA22A41A3F46E8E90EF528--
"""
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | Html Inline External |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
"Message": {
"Subject": "Html Inline External",
"Sender": {
"Name": "Bridge Test"
},
"ToList": [
{
"Address": "pm.bridge.qa@gmail.com",
"Name": "External Bridge"
}
],
"CCList": [],
"BCCList": [],
"MIMEType": "text/html"
}
}
"""
Scenario: HTML message with alternative inline to internal account
When SMTP client "1" sends the following message from "user@pm.me" to "bridgetest@protonmail.com":
"""
From: Bridge Test <user@pm.me>
To: Internal Bridge <bridgetest@protonmail.com>
Subject: Html Inline Alternative Internal
Content-Disposition: inline
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.5.0
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="------------5A259F4DE164B5ADA313F644"
Content-Language: en-US
This is a multi-part message in MIME format.
--------------5A259F4DE164B5ADA313F644
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit
Behold! An inline
--------------5A259F4DE164B5ADA313F644
Content-Type: multipart/related; boundary="------------61FA22A41A3F46E8E90EF528"
--------------61FA22A41A3F46E8E90EF528
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<p><br>
</p>
<p>Behold! An inline <img moz-do-not-send="false"
src="cid:part1.D96BFAE9.E2E1CAE3@protonmail.com" alt=""
width="24" height="24"><br>
</p>
</body>
</html>
--------------61FA22A41A3F46E8E90EF528
Content-Type: image/gif; name="email-action-left.gif"
Content-Transfer-Encoding: base64
Content-ID: <part1.D96BFAE9.E2E1CAE3@protonmail.com>
Content-Disposition: inline; filename="email-action-left.gif"
R0lGODlhGAAYANUAACcsKOHs4kppTH6tgYWxiIq0jTVENpG5lDI/M7bRuEaJSkqOTk2RUU+P
U16lYl+lY2iva262cXS6d3rDfYLNhWeeamKTZGSVZkNbRGqhbOPt4////+7u7qioqFZWVlNT
UyIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAGAAYAAAG
/8CNcLjRJAqVRqNSSGiI0GFgoKhar4NAdHioMhyRCYUyiTgY1cOWUH1ILgIDAGAQXCSPKgHa
XUAyGCCCg4IYGRALCmpCAVUQFgiEkiAIFhBVWhtUDxmRk5IIGXkDRQoMEoGfHpIYEmhGCg4X
nyAdHB+SFw4KRwoRArQdG7eEAhEKSAoTBoIdzs/Cw7iCBhMKSQoUAIJbQ8QgABQKStnbIN1C
3+HjFcrMtdDO6dMg1dcFvsCfwt+CxsgJYs3a10+QLl4aTKGitYpQq1eaFHDyREtQqFGMHEGq
SMkSJi4K/ACiZQiRIihsJL6JM6fOnTwK9kTpYgqMGDJm0JzsNuWKTw0FWdANMYJECRMnW4IA
ADs=
--------------61FA22A41A3F46E8E90EF528--
--------------5A259F4DE164B5ADA313F644--
"""
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject |
| user@pm.me | bridgetest@protonmail.com | Html Inline Alternative Internal |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
"Message": {
"Subject": "Html Inline Alternative Internal",
"Sender": {
"Name": "Bridge Test"
},
"ToList": [
{
"Address": "bridgetest@protonmail.com",
"Name": "Internal Bridge"
}
],
"CCList": [],
"BCCList": [],
"MIMEType": "text/html"
}
}
"""
Scenario: HTML message with alternative inline to external account
When SMTP client "1" sends the following message from "user@pm.me" to "pm.bridge.qa@gmail.com":
"""
From: Bridge Test <user@pm.me>
To: External Bridge <pm.bridge.qa@gmail.com>
Subject: Html Inline Alternative External
Content-Disposition: inline
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.5.0
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="------------5A259F4DE164B5ADA313F644"
Content-Language: en-US
This is a multi-part message in MIME format.
--------------5A259F4DE164B5ADA313F644
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit
Behold! An inline
--------------5A259F4DE164B5ADA313F644
Content-Type: multipart/related; boundary="------------61FA22A41A3F46E8E90EF528"
--------------61FA22A41A3F46E8E90EF528
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<p><br>
</p>
<p>Behold! An inline <img moz-do-not-send="false"
src="cid:part1.D96BFAE9.E2E1CAE3@protonmail.com" alt=""
width="24" height="24"><br>
</p>
</body>
</html>
--------------61FA22A41A3F46E8E90EF528
Content-Type: image/gif; name="email-action-left.gif"
Content-Transfer-Encoding: base64
Content-ID: <part1.D96BFAE9.E2E1CAE3@protonmail.com>
Content-Disposition: inline; filename="email-action-left.gif"
R0lGODlhGAAYANUAACcsKOHs4kppTH6tgYWxiIq0jTVENpG5lDI/M7bRuEaJSkqOTk2RUU+P
U16lYl+lY2iva262cXS6d3rDfYLNhWeeamKTZGSVZkNbRGqhbOPt4////+7u7qioqFZWVlNT
UyIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAGAAYAAAG
/8CNcLjRJAqVRqNSSGiI0GFgoKhar4NAdHioMhyRCYUyiTgY1cOWUH1ILgIDAGAQXCSPKgHa
XUAyGCCCg4IYGRALCmpCAVUQFgiEkiAIFhBVWhtUDxmRk5IIGXkDRQoMEoGfHpIYEmhGCg4X
nyAdHB+SFw4KRwoRArQdG7eEAhEKSAoTBoIdzs/Cw7iCBhMKSQoUAIJbQ8QgABQKStnbIN1C
3+HjFcrMtdDO6dMg1dcFvsCfwt+CxsgJYs3a10+QLl4aTKGitYpQq1eaFHDyREtQqFGMHEGq
SMkSJi4K/ACiZQiRIihsJL6JM6fOnTwK9kTpYgqMGDJm0JzsNuWKTw0FWdANMYJECRMnW4IA
ADs=
--------------61FA22A41A3F46E8E90EF528--
--------------5A259F4DE164B5ADA313F644--
"""
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | Html Inline Alternative External |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
"Message": {
"Subject": "Html Inline Alternative External",
"Sender": {
"Name": "Bridge Test"
},
"ToList": [
{
"Address": "pm.bridge.qa@gmail.com",
"Name": "External Bridge"
}
],
"CCList": [],
"BCCList": [],
"MIMEType": "text/html"
}
}
"""
Scenario: HTML message with extremely long line (greater than default 2000 line limit) to external account
When SMTP client "1" sends the following message from "user@pm.me" to "pm.bridge.qa@gmail.com":
"""
From: Bridge Test <user@pm.me>
To: External Bridge <pm.bridge.qa@gmail.com>
Subject: HTML text external
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html; charset=utf-8
In-Reply-To: <base64hashOfSomeMessage@protonmail.internalid>
<html><body>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<body></html>
"""
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | HTML text external |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
"Message": {
"Subject": "HTML text external",
"Sender": {
"Name": "Bridge Test"
},
"ToList": [
{
"Address": "pm.bridge.qa@gmail.com",
"Name": "External Bridge"
}
],
"CCList": [],
"BCCList": [],
"MIMEType": "text/html"
}
}
"""

View File

@ -19,8 +19,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | bridgetest@protonmail.com | | false |
| from | to | subject |
| user@pm.me | bridgetest@protonmail.com | |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -54,8 +54,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | pm.bridge.qa@gmail.com | | false |
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -92,8 +92,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | bridgetest@protonmail.com | Plain text internal | false |
| from | to | subject |
| user@pm.me | bridgetest@protonmail.com | Plain text internal |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -130,8 +130,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | pm.bridge.qa@gmail.com | Plain text external | false |
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | Plain text external |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -168,8 +168,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | pm.bridge.qa@gmail.com | Plain text no charset external | false |
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | Plain text no charset external |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -209,8 +209,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | pm.bridge.qa@gmail.com | Plain text no charset external | false |
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | Plain text no charset external |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -245,8 +245,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | subject | unread |
| user@pm.me | pm.bridge.qa@gmail.com | Plain, no charset, no content, external | false |
| from | to | subject |
| user@pm.me | pm.bridge.qa@gmail.com | Plain, no charset, no content, external |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{
@ -285,8 +285,8 @@ Feature: SMTP sending of plain messages
Then it succeeds
When user "user@pm.me" connects and authenticates IMAP client "1"
Then IMAP client "1" eventually sees the following messages in "Sent":
| sender | recipient | cc | subject | unread |
| user@pm.me | bridgetest@protonmail.com | bridgetest2@protonmail.com | RCPT-CC test | false |
| from | to | cc | subject |
| user@pm.me | bridgetest@protonmail.com | bridgetest2@protonmail.com | RCPT-CC test |
And the body in the "POST" request to "/mail/v4/messages" is:
"""
{

View File

@ -7,11 +7,11 @@ Feature: Address mode
| one | folder |
| two | folder |
And the address "user@pm.me" of account "user@pm.me" has the following messages in "one":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And the address "alias@pm.me" of account "user@pm.me" has the following messages in "two":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
And bridge starts
@ -21,30 +21,30 @@ Feature: Address mode
Scenario: The user is in combined mode
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
Then IMAP client "1" sees the following messages in "Folders/one":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And IMAP client "1" sees the following messages in "Folders/two":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
And IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
Then IMAP client "2" sees the following messages in "Folders/one":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And IMAP client "2" sees the following messages in "Folders/two":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
And IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
@ -55,22 +55,22 @@ Feature: Address mode
And user "user@pm.me" finishes syncing
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
Then IMAP client "1" sees the following messages in "Folders/one":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And IMAP client "1" sees 0 messages in "Folders/two"
And IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
Then IMAP client "2" sees 0 messages in "Folders/one"
And IMAP client "2" sees the following messages in "Folders/two":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
And IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
@ -81,14 +81,14 @@ Feature: Address mode
And user "user@pm.me" finishes syncing
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
Then IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
Then IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
@ -97,14 +97,14 @@ Feature: Address mode
Scenario: The user adds an address while in combined mode
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
Then IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
Then IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
@ -113,7 +113,7 @@ Feature: Address mode
And bridge sends an address created event for user "user@pm.me"
When user "user@pm.me" connects and authenticates IMAP client "3" with address "other@pm.me"
Then IMAP client "3" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
@ -124,12 +124,12 @@ Feature: Address mode
And user "user@pm.me" finishes syncing
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
And IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
And IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
Given the account "user@pm.me" has additional address "other@pm.me"
@ -140,14 +140,14 @@ Feature: Address mode
Scenario: The user deletes an address while in combined mode
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
Then IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
Then IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
| c@pm.me | c@pm.me | three | true |
@ -162,12 +162,12 @@ Feature: Address mode
And user "user@pm.me" finishes syncing
When user "user@pm.me" connects and authenticates IMAP client "1" with address "user@pm.me"
And IMAP client "1" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
When user "user@pm.me" connects and authenticates IMAP client "2" with address "alias@pm.me"
And IMAP client "2" sees the following messages in "All Mail":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| c@pm.me | c@pm.me | three | true |
| d@pm.me | d@pm.me | four | false |
Given the account "user@pm.me" no longer has additional address "alias@pm.me"

View File

@ -7,11 +7,11 @@ Feature: Bridge can fully sync an account
| two | folder |
| three | label |
And the address "user@pm.me" of account "user@pm.me" has the following messages in "one":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And the address "user@pm.me" of account "user@pm.me" has the following messages in "two":
| sender | recipient | subject | unread |
| from | to | subject | unread |
| a@pm.me | a@pm.me | one | true |
| b@pm.me | b@pm.me | two | false |
And bridge starts

View File

@ -127,15 +127,16 @@ func (s *scenario) imapClientSeesTheFollowingMailboxInfo(clientID string, table
return err
}
haveMailboxes := xslices.Map(status, func(info *imap.MailboxStatus) Mailbox {
return Mailbox{
Name: info.Name,
Total: int(info.Messages),
Unread: int(info.Unseen),
}
haveMailboxes := xslices.Map(status, func(status *imap.MailboxStatus) Mailbox {
return newMailboxFromIMAP(status)
})
return matchMailboxes(haveMailboxes, table)
wantMailboxes, err := unmarshalTable[Mailbox](table)
if err != nil {
return err
}
return matchMailboxes(haveMailboxes, wantMailboxes)
}
func (s *scenario) imapClientEventuallySeesTheFollowingMailboxInfo(clientID string, table *godog.Table) error {
@ -159,14 +160,15 @@ func (s *scenario) imapClientSeesTheFollowingMailboxInfoForMailbox(clientID, mai
})
haveMailboxes := xslices.Map(status, func(info *imap.MailboxStatus) Mailbox {
return Mailbox{
Name: info.Name,
Total: int(info.Messages),
Unread: int(info.Unseen),
}
return newMailboxFromIMAP(info)
})
return matchMailboxes(haveMailboxes, table)
wantMailboxes, err := unmarshalTable[Mailbox](table)
if err != nil {
return err
}
return matchMailboxes(haveMailboxes, wantMailboxes)
}
func (s *scenario) imapClientSeesTheFollowingMailboxes(clientID string, table *godog.Table) error {
@ -280,31 +282,15 @@ func (s *scenario) imapClientSeesTheFollowingMessagesInMailbox(clientID, mailbox
}
haveMessages := xslices.Map(fetch, func(msg *imap.Message) Message {
message := Message{
Subject: msg.Envelope.Subject,
Unread: slices.Contains(msg.Flags, imap.SeenFlag),
}
if len(msg.Envelope.From) > 0 {
message.From = msg.Envelope.From[0].Address()
}
if len(msg.Envelope.To) > 0 {
message.To = msg.Envelope.To[0].Address()
}
if len(msg.Envelope.Cc) > 0 {
message.CC = msg.Envelope.Cc[0].Address()
}
if len(msg.Envelope.Bcc) > 0 {
message.BCC = msg.Envelope.Bcc[0].Address()
}
return message
return newMessageFromIMAP(msg)
})
return matchMessages(haveMessages, table)
wantMessages, err := unmarshalTable[Message](table)
if err != nil {
return err
}
return matchMessages(haveMessages, wantMessages)
}
func (s *scenario) imapClientEventuallySeesTheFollowingMessagesInMailbox(clientID, mailbox string, table *godog.Table) error {

View File

@ -6,82 +6,85 @@ import (
"strconv"
"time"
"github.com/bradenaw/juniper/xslices"
"github.com/cucumber/godog"
"github.com/cucumber/messages-go/v16"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/emersion/go-imap"
"golang.org/x/exp/slices"
)
type Message struct {
Subject string `bdd:"subject"`
From string `bdd:"sender"`
To string `bdd:"recipient"`
From string `bdd:"from"`
To string `bdd:"to"`
CC string `bdd:"cc"`
BCC string `bdd:"bcc"`
Unread bool `bdd:"unread"`
}
func newMessageFromRow(header, row *messages.PickleTableRow) Message {
var msg Message
if err := unmarshalRow(header, row, &msg); err != nil {
panic(err)
func newMessageFromIMAP(msg *imap.Message) Message {
message := Message{
Subject: msg.Envelope.Subject,
Unread: slices.Contains(msg.Flags, imap.SeenFlag),
}
return msg
if len(msg.Envelope.From) > 0 {
message.From = msg.Envelope.From[0].Address()
}
if len(msg.Envelope.To) > 0 {
message.To = msg.Envelope.To[0].Address()
}
if len(msg.Envelope.Cc) > 0 {
message.CC = msg.Envelope.Cc[0].Address()
}
if len(msg.Envelope.Bcc) > 0 {
message.BCC = msg.Envelope.Bcc[0].Address()
}
return message
}
func matchMessages(have []Message, want *godog.Table) error {
if want := parseMessages(want); !cmp.Equal(want, have, cmpopts.SortSlices(func(a, b Message) bool { return a.Subject < b.Subject })) {
return fmt.Errorf("want: %v, have: %v", want, have)
func matchMessages(have, want []Message) error {
if !IsSub(ToAny(have), ToAny(want)) {
return fmt.Errorf("missing messages: %v", want)
}
return nil
}
func parseMessages(table *godog.Table) []Message {
header := table.Rows[0]
return xslices.Map(table.Rows[1:], func(row *messages.PickleTableRow) Message {
return newMessageFromRow(header, row)
})
}
type Mailbox struct {
Name string `bdd:"name"`
Total int `bdd:"total"`
Unread int `bdd:"unread"`
}
func newMailboxFromRow(header, row *messages.PickleTableRow) Mailbox {
var mbox Mailbox
if err := unmarshalRow(header, row, &mbox); err != nil {
panic(err)
func newMailboxFromIMAP(status *imap.MailboxStatus) Mailbox {
return Mailbox{
Name: status.Name,
Total: int(status.Messages),
Unread: int(status.Unseen),
}
return mbox
}
func matchMailboxes(have []Mailbox, want *godog.Table) error {
if want := parseMailboxes(want); !cmp.Equal(want, have, cmpopts.SortSlices(func(a, b Mailbox) bool { return a.Name < b.Name })) {
return fmt.Errorf("want: %v, have: %v", want, have)
func matchMailboxes(have, want []Mailbox) error {
slices.SortFunc(have, func(a, b Mailbox) bool {
return a.Name < b.Name
})
slices.SortFunc(want, func(a, b Mailbox) bool {
return a.Name < b.Name
})
if !IsSub(want, have) {
return fmt.Errorf("missing messages: %v", want)
}
return nil
}
func parseMailboxes(table *godog.Table) []Mailbox {
header := table.Rows[0]
return xslices.Map(table.Rows[1:], func(row *messages.PickleTableRow) Mailbox {
return newMailboxFromRow(header, row)
})
}
func eventually(condition func() error, waitFor, tick time.Duration) error {
ch := make(chan error, 1)
@ -111,14 +114,24 @@ func eventually(condition func() error, waitFor, tick time.Duration) error {
}
}
func getCellValue(header, row *messages.PickleTableRow, name string) (string, bool) {
for idx, cell := range header.Cells {
if cell.Value == name {
return row.Cells[idx].Value, true
}
func unmarshalTable[T any](table *messages.PickleTable) ([]T, error) {
if len(table.Rows) == 0 {
return nil, fmt.Errorf("empty table")
}
return "", false
var res []T
for _, row := range table.Rows[1:] {
var v T
if err := unmarshalRow(table.Rows[0], row, &v); err != nil {
return nil, err
}
res = append(res, v)
}
return res, nil
}
func unmarshalRow(header, row *messages.PickleTableRow, v any) error {
@ -152,6 +165,16 @@ func unmarshalRow(header, row *messages.PickleTableRow, v any) error {
return nil
}
func getCellValue(header, row *messages.PickleTableRow, name string) (string, bool) {
for idx, cell := range header.Cells {
if cell.Value == name {
return row.Cells[idx].Value, true
}
}
return "", false
}
func mustParseInt(s string) int {
i, err := strconv.Atoi(s)
if err != nil {

View File

@ -8,9 +8,7 @@ import (
"time"
"github.com/ProtonMail/gluon/rfc822"
"github.com/bradenaw/juniper/xslices"
"github.com/cucumber/godog"
"github.com/cucumber/messages-go/v16"
"github.com/google/uuid"
"gitlab.protontech.ch/go/liteapi"
"golang.org/x/exp/slices"
@ -82,29 +80,28 @@ func (s *scenario) theAccountHasCustomLabels(username string, count int) error {
}
func (s *scenario) theAccountHasTheFollowingCustomMailboxes(username string, table *godog.Table) error {
type mailbox struct {
name string
typ liteapi.LabelType
type CustomMailbox struct {
Name string `bdd:"name"`
Type string `bdd:"type"`
}
wantMailboxes := xslices.Map(table.Rows[1:], func(row *messages.PickleTableRow) mailbox {
var mailboxType liteapi.LabelType
switch row.Cells[1].Value {
case "folder":
mailboxType = liteapi.LabelTypeFolder
case "label":
mailboxType = liteapi.LabelTypeLabel
wantMailboxes, err := unmarshalTable[CustomMailbox](table)
if err != nil {
return err
}
return mailbox{
name: row.Cells[0].Value,
typ: mailboxType,
}
})
for _, wantMailbox := range wantMailboxes {
if _, err := s.t.api.CreateLabel(s.t.getUserID(username), wantMailbox.name, wantMailbox.typ); err != nil {
var labelType liteapi.LabelType
switch wantMailbox.Type {
case "folder":
labelType = liteapi.LabelTypeFolder
case "label":
labelType = liteapi.LabelTypeLabel
}
if _, err := s.t.api.CreateLabel(s.t.getUserID(username), wantMailbox.Name, labelType); err != nil {
return err
}
}
@ -117,7 +114,12 @@ func (s *scenario) theAddressOfAccountHasTheFollowingMessagesInMailbox(address,
addrID := s.t.getUserAddrID(userID, address)
mboxID := s.t.getMBoxID(userID, mailbox)
for _, wantMessage := range parseMessages(table) {
wantMessages, err := unmarshalTable[Message](table)
if err != nil {
return err
}
for _, wantMessage := range wantMessages {
if _, err := s.t.api.CreateMessage(
userID,
addrID,
@ -125,8 +127,8 @@ func (s *scenario) theAddressOfAccountHasTheFollowingMessagesInMailbox(address,
wantMessage.Subject,
&mail.Address{Address: wantMessage.From},
[]*mail.Address{{Address: wantMessage.To}},
[]*mail.Address{},
[]*mail.Address{},
[]*mail.Address{{Address: wantMessage.CC}},
[]*mail.Address{{Address: wantMessage.BCC}},
"some body goes here",
rfc822.TextPlain,
wantMessage.Unread,