cpim-tester.cpp 12.3 KB
Newer Older
Ronan's avatar
Ronan committed
1
/*
Ronan's avatar
Ronan committed
2
 * cpim-tester.cpp
Ronan's avatar
Ronan committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * Copyright (C) 2017  Belledonne Communications SARL
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

Sylvain Berfini's avatar
Sylvain Berfini committed
19
#include "address/address.h"
Ronan's avatar
Ronan committed
20
#include "chat/chat-message/chat-message.h"
21
#include "chat/chat-room/basic-chat-room.h"
22 23
#include "chat/cpim/cpim.h"
#include "content/content-type.h"
24
#include "content/content.h"
25
#include "core/core.h"
jehan's avatar
jehan committed
26
#include "belr/grammarbuilder.h"
27 28
// TODO: Remove me later.
#include "private.h"
Ronan's avatar
Ronan committed
29 30

#include "liblinphone_tester.h"
Benjamin REIS's avatar
Benjamin REIS committed
31
#include "tester_utils.h"
Ronan's avatar
Ronan committed
32

Ronan's avatar
Ronan committed
33
// =============================================================================
Ronan's avatar
Ronan committed
34

Ronan's avatar
Ronan committed
35
using namespace std;
Ronan's avatar
Ronan committed
36

37
using namespace LinphonePrivate;
Ronan's avatar
Ronan committed
38

Ronan's avatar
Ronan committed
39
static void parse_minimal_message () {
40
	const string str = "Subject: the weather will be fine today\r\n"
Ronan's avatar
Ronan committed
41 42 43 44 45 46 47 48 49
		"\r\n"
		"Content-Type: text/plain; charset=utf-8\r\n"
		"\r\n";

	shared_ptr<const Cpim::Message> message = Cpim::Message::createFromString(str);
	if (!BC_ASSERT_PTR_NOT_NULL(message)) return;

	const string str2 = message->asString();
	BC_ASSERT_STRING_EQUAL(str2.c_str(), str.c_str());
50

51 52
	const string content = message->getContent();
	BC_ASSERT_STRING_EQUAL(content.c_str(), "");
Ronan's avatar
Ronan committed
53 54
}

Ronan's avatar
Ronan committed
55
static void set_generic_header_name () {
Ronan's avatar
Ronan committed
56 57 58 59 60 61 62 63
	const list<pair<string, bool> > entries = {
		// Reserved.
		{ "From", false },
		{ "To", false },
		{ "cc", false },
		{ "DateTime", false },
		{ "Subject", false },
		{ "NS", false },
64
		{ "Require", false }
Ronan's avatar
Ronan committed
65 66 67
	};

	for (const auto &entry : entries) {
68
		Cpim::GenericHeader genericHeader(entry.first, "");
Ronan's avatar
Ronan committed
69 70

		const string name = genericHeader.getName();
71
		BC_ASSERT_STRING_EQUAL(name.c_str(), "");
Ronan's avatar
Ronan committed
72 73 74
	}
}

Ronan's avatar
Ronan committed
75
static void check_core_header_names () {
76
	const list<pair<shared_ptr<Cpim::Header>, string> > entries = {
Ronan's avatar
Ronan committed
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
		{ make_shared<Cpim::FromHeader>(), "From" },
		{ make_shared<Cpim::ToHeader>(), "To" },
		{ make_shared<Cpim::CcHeader>(), "cc" },
		{ make_shared<Cpim::DateTimeHeader>(), "DateTime" },
		{ make_shared<Cpim::SubjectHeader>(), "Subject" },
		{ make_shared<Cpim::NsHeader>(), "NS" },
		{ make_shared<Cpim::RequireHeader>(), "Require" }
	};

	for (const auto &entry : entries) {
		const string name = entry.first->getName();
		BC_ASSERT_STRING_EQUAL(name.c_str(), entry.second.c_str());
	}
}

Ronan's avatar
Ronan committed
92
static void parse_rfc_example () {
93 94 95 96
	const string body = "<body>"
		"Here is the text of my message."
		"</body>";

97 98
	const string str = "From: \"MR SANDERS\"<im:piglet@100akerwood.com>\r\n"
		"To: \"Depressed Donkey\"<im:eeyore@100akerwood.com>\r\n"
Ronan's avatar
Ronan committed
99 100 101 102 103 104 105 106
		"DateTime: 2000-12-13T13:40:00-08:00\r\n"
		"Subject: the weather will be fine today\r\n"
		"Subject:;lang=fr beau temps prevu pour aujourd'hui\r\n"
		"NS: MyFeatures <mid:MessageFeatures@id.foo.com>\r\n"
		"Require: MyFeatures.VitalMessageOption\r\n"
		"MyFeatures.VitalMessageOption: Confirmation-requested\r\n"
		"MyFeatures.WackyMessageOption: Use-silly-font\r\n"
		"\r\n"
107
		"Content-Type: text/xml; charset=utf-8\r\n"
Ronan's avatar
Ronan committed
108
		"Content-ID: <1234567890@foo.com>\r\n"
109
		"\r\n" + body;
Ronan's avatar
Ronan committed
110 111 112 113 114 115

	shared_ptr<const Cpim::Message> message = Cpim::Message::createFromString(str);
	if (!BC_ASSERT_PTR_NOT_NULL(message)) return;

	const string str2 = message->asString();
	BC_ASSERT_STRING_EQUAL(str2.c_str(), str.c_str());
116

117 118
	string content = message->getContent();
	BC_ASSERT_STRING_EQUAL(content.c_str(), body.c_str());
119 120 121 122 123 124 125 126

	Cpim::Message::HeaderList list = message->getMessageHeaders();
	if (!BC_ASSERT_PTR_NOT_NULL(list)) return;
	BC_ASSERT_EQUAL(list->size(), 7, int, "%d");

	list = message->getMessageHeaders("MyFeatures");
	if (!BC_ASSERT_PTR_NOT_NULL(list)) return;
	BC_ASSERT_EQUAL(list->size(), 2, int, "%d");
Ronan's avatar
Ronan committed
127 128
}

Ronan's avatar
Ronan committed
129
static void parse_message_with_generic_header_parameters () {
130 131 132 133
	const string body = "<body>"
		"Here is the text of my message."
		"</body>";

134
	const string str = "From: \"MR SANDERS\"<im:piglet@100akerwood.com>\r\n"
Ronan's avatar
Ronan committed
135 136 137 138
		"Test:;aaa=bbb;yes=no CheckMe\r\n"
		"yaya: coucou\r\n"
		"yepee:;good=bad ugly\r\n"
		"\r\n"
139
		"Content-Type: text/xml; charset=utf-8\r\n"
Ronan's avatar
Ronan committed
140
		"Content-ID: <1234567890@foo.com>\r\n"
141
		"\r\n" + body;
Ronan's avatar
Ronan committed
142 143 144 145 146 147

	shared_ptr<const Cpim::Message> message = Cpim::Message::createFromString(str);
	if (!BC_ASSERT_PTR_NOT_NULL(message)) return;

	const string str2 = message->asString();
	BC_ASSERT_STRING_EQUAL(str2.c_str(), str.c_str());
148

149 150
	string content = message->getContent();
	BC_ASSERT_STRING_EQUAL(content.c_str(), body.c_str());
Ronan's avatar
Ronan committed
151 152
}

Ronan's avatar
Ronan committed
153
static void build_message () {
jehan's avatar
jehan committed
154

Ronan's avatar
Ronan committed
155 156 157
	Cpim::Message message;

	// Set message headers.
158
	Cpim::FromHeader fromHeader("im:piglet@100akerwood.com", "MR SANDERS");
Ronan's avatar
Ronan committed
159

160
	Cpim::ToHeader toHeader("im:eeyore@100akerwood.com", "Depressed Donkey");
Ronan's avatar
Ronan committed
161

162 163
	// 976686000 is 2000-12-13T13:40:00-08:00
	Cpim::DateTimeHeader dateTimeHeader(976686000);
164
	BC_ASSERT_EQUAL((int)dateTimeHeader.getTime(), 976686000, int, "%d");
Ronan's avatar
Ronan committed
165

166
	Cpim::SubjectHeader subjectHeader("the weather will be fine today");
Ronan's avatar
Ronan committed
167

168
	Cpim::SubjectHeader subjectWithLanguageHeader("beau temps prevu pour aujourd'hui", "fr");
Ronan's avatar
Ronan committed
169

170
	Cpim::NsHeader nsHeader("mid:MessageFeatures@id.foo.com", "MyFeatures");
Ronan's avatar
Ronan committed
171

172
	Cpim::RequireHeader requireHeader("MyFeatures.VitalMessageOption");
Ronan's avatar
Ronan committed
173

174
	Cpim::GenericHeader vitalMessageHeader("MyFeatures.VitalMessageOption", "Confirmation-requested");
Ronan's avatar
Ronan committed
175

176
	Cpim::GenericHeader wackyMessageHeader("MyFeatures.WackyMessageOption", "Use-silly-font");
Ronan's avatar
Ronan committed
177

178 179 180 181 182 183 184 185 186
	if (!BC_ASSERT_TRUE(message.addMessageHeader(fromHeader))) return;
	if (!BC_ASSERT_TRUE(message.addMessageHeader(toHeader))) return;
	if (!BC_ASSERT_TRUE(message.addMessageHeader(dateTimeHeader))) return;
	if (!BC_ASSERT_TRUE(message.addMessageHeader(subjectHeader))) return;
	if (!BC_ASSERT_TRUE(message.addMessageHeader(subjectWithLanguageHeader))) return;
	if (!BC_ASSERT_TRUE(message.addMessageHeader(nsHeader))) return;
	if (!BC_ASSERT_TRUE(message.addMessageHeader(requireHeader))) return;
	if (!BC_ASSERT_TRUE(message.addMessageHeader(vitalMessageHeader))) return;
	if (!BC_ASSERT_TRUE(message.addMessageHeader(wackyMessageHeader))) return;
Ronan's avatar
Ronan committed
187

188
	// Set Content headers.
189
    Cpim::GenericHeader contentTypeHeader("Content-Type", "text/xml; charset=utf-8");
190
	if (!BC_ASSERT_TRUE(message.addContentHeader(contentTypeHeader))) return;
191

192
	Cpim::GenericHeader contentIdHeader("Content-ID", "<1234567890@foo.com>");
193 194 195 196 197 198 199 200
	if (!BC_ASSERT_TRUE(message.addContentHeader(contentIdHeader))) return;

	// Add a wrong message header and a wrong content header
	Cpim::FromHeader wrongFromHeader("", "");
	if (!BC_ASSERT_FALSE(message.addMessageHeader(wrongFromHeader))) return;

	Cpim::GenericHeader wrongContentHeader("", "");
	if (!BC_ASSERT_FALSE(message.addContentHeader(wrongContentHeader))) return;
201 202

	const string content = "<body>"
Ronan's avatar
Ronan committed
203 204 205 206 207 208
		"Here is the text of my message."
		"</body>";

	if (!BC_ASSERT_TRUE(message.setContent(content))) return;

	const string strMessage = message.asString();
209 210
	const string expectedMessage = "From: \"MR SANDERS\"<im:piglet@100akerwood.com>\r\n"
		"To: \"Depressed Donkey\"<im:eeyore@100akerwood.com>\r\n"
211
		"DateTime: 2000-12-13T05:40:00Z\r\n"
Ronan's avatar
Ronan committed
212 213 214 215 216 217 218
		"Subject: the weather will be fine today\r\n"
		"Subject:;lang=fr beau temps prevu pour aujourd'hui\r\n"
		"NS: MyFeatures <mid:MessageFeatures@id.foo.com>\r\n"
		"Require: MyFeatures.VitalMessageOption\r\n"
		"MyFeatures.VitalMessageOption: Confirmation-requested\r\n"
		"MyFeatures.WackyMessageOption: Use-silly-font\r\n"
		"\r\n"
219
		"Content-Type: text/xml; charset=utf-8\r\n"
Ronan's avatar
Ronan committed
220 221 222 223 224 225 226 227 228
		"Content-ID: <1234567890@foo.com>\r\n"
		"\r\n"
		"<body>"
		"Here is the text of my message."
		"</body>";

	BC_ASSERT_STRING_EQUAL(strMessage.c_str(), expectedMessage.c_str());
}

229 230 231 232 233 234 235 236
static int fake_im_encryption_engine_process_incoming_message_cb (
	LinphoneImEncryptionEngine *engine,
	LinphoneChatRoom *room,
	LinphoneChatMessage *msg
) {
	// Encryption is the first receiving step, so this message should be CPIM.
	const string expected = ContentType::Cpim.asString();
	BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(msg), expected.c_str());
Sylvain Berfini's avatar
Sylvain Berfini committed
237 238 239
	return -1;
}

240 241 242 243 244 245 246 247
static int fake_im_encryption_engine_process_outgoing_message_cb (
	LinphoneImEncryptionEngine *engine,
	LinphoneChatRoom *room,
	LinphoneChatMessage *msg
) {
	// Encryption is the last sending step, so this message should be CPIM.
	const string expected = ContentType::Cpim.asString();
	BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(msg), expected.c_str());
Sylvain Berfini's avatar
Sylvain Berfini committed
248 249 250
	return -1;
}

251
static void cpim_chat_message_modifier_base (bool useMultipart) {
Sylvain Berfini's avatar
Sylvain Berfini committed
252 253
	LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
	LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc");
Sylvain Berfini's avatar
Sylvain Berfini committed
254

255
	// We use a fake encryption engine just to check the internal content type during the sending/receiving process.
Sylvain Berfini's avatar
Sylvain Berfini committed
256 257 258 259 260 261 262 263 264
	LinphoneImEncryptionEngine *marie_imee = linphone_im_encryption_engine_new();
	LinphoneImEncryptionEngineCbs *marie_cbs = linphone_im_encryption_engine_get_callbacks(marie_imee);
	LinphoneImEncryptionEngine *pauline_imee = linphone_im_encryption_engine_new();
	LinphoneImEncryptionEngineCbs *pauline_cbs = linphone_im_encryption_engine_get_callbacks(pauline_imee);
	linphone_im_encryption_engine_cbs_set_process_outgoing_message(marie_cbs, fake_im_encryption_engine_process_outgoing_message_cb);
	linphone_im_encryption_engine_cbs_set_process_incoming_message(pauline_cbs, fake_im_encryption_engine_process_incoming_message_cb);
	linphone_core_set_im_encryption_engine(marie->lc, marie_imee);
	linphone_core_set_im_encryption_engine(pauline->lc, pauline_imee);

265 266 267 268
	char *paulineUri = linphone_address_as_string_uri_only(pauline->identity);
	IdentityAddress paulineAddress(paulineUri);
	bctbx_free(paulineUri);

269
	shared_ptr<AbstractChatRoom> marieRoom = marie->lc->cppPtr->getOrCreateBasicChatRoom(paulineAddress);
Sylvain Berfini's avatar
Sylvain Berfini committed
270
	marieRoom->allowCpim(true);
271

272
	shared_ptr<ChatMessage> marieMessage = marieRoom->createChatMessage("Hello CPIM");
273
	if (useMultipart) {
Sylvain Berfini's avatar
Sylvain Berfini committed
274
		marieRoom->allowMultipart(true);
275 276 277
		Content *content = new Content();
		content->setContentType(ContentType::PlainText);
		content->setBody("Hello Part 2");
278
		marieMessage->addContent(content);
279
	}
280
	marieMessage->send();
Sylvain Berfini's avatar
Sylvain Berfini committed
281 282

	BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneMessageReceived,1));
283
	BC_ASSERT_TRUE(marieMessage->getInternalContent().getContentType().isEmpty()); // Internal content is cleaned after message is sent or received
284

Sylvain Berfini's avatar
Sylvain Berfini committed
285 286 287
	BC_ASSERT_PTR_NOT_NULL(pauline->stat.last_received_chat_message);
	if (pauline->stat.last_received_chat_message != NULL) {
		BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(pauline->stat.last_received_chat_message), "Hello CPIM");
288 289
		const string expected = ContentType::PlainText.asString();
		BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(pauline->stat.last_received_chat_message), expected.c_str());
Sylvain Berfini's avatar
Sylvain Berfini committed
290 291
	}

292 293 294
	marieMessage.reset();
	marieRoom.reset();

Sylvain Berfini's avatar
Sylvain Berfini committed
295 296 297
	linphone_im_encryption_engine_unref(marie_imee);
	linphone_im_encryption_engine_unref(pauline_imee);

Sylvain Berfini's avatar
Sylvain Berfini committed
298 299
	linphone_core_manager_destroy(marie);
	linphone_core_manager_destroy(pauline);
300 301
}

302
static void cpim_chat_message_modifier () {
303 304 305
	cpim_chat_message_modifier_base(FALSE);
}

306
static void cpim_chat_message_modifier_with_multipart_body () {
307 308 309
	cpim_chat_message_modifier_base(TRUE);
}

Ronan's avatar
Ronan committed
310 311 312 313 314 315
test_t cpim_tests[] = {
	TEST_NO_TAG("Parse minimal CPIM message", parse_minimal_message),
	TEST_NO_TAG("Set generic header name", set_generic_header_name),
	TEST_NO_TAG("Check core header names", check_core_header_names),
	TEST_NO_TAG("Parse RFC example", parse_rfc_example),
	TEST_NO_TAG("Parse Message with generic header parameters", parse_message_with_generic_header_parameters),
316
	TEST_NO_TAG("Build Message", build_message),
317
	TEST_NO_TAG("CPIM chat message modifier", cpim_chat_message_modifier),
318
	TEST_NO_TAG("CPIM chat message modifier with multipart body", cpim_chat_message_modifier_with_multipart_body)
Ronan's avatar
Ronan committed
319 320
};

jehan's avatar
jehan committed
321 322
static int suite_begin(void) {
	//Supposed to be done by platform helper, but in this case, we don't have it"
jehan's avatar
jehan committed
323
	belr::GrammarLoader::get().addPath(std::string(bc_tester_get_resource_dir_prefix()).append("/share/belr/grammars"));
jehan's avatar
jehan committed
324 325
	return 0;
}
Ronan's avatar
Ronan committed
326
test_suite_t cpim_test_suite = {
jehan's avatar
jehan committed
327
	"Cpim", suite_begin, NULL, liblinphone_tester_before_each, liblinphone_tester_after_each,
Ronan's avatar
Ronan committed
328 329
	sizeof(cpim_tests) / sizeof(cpim_tests[0]), cpim_tests
};